blob: b1c6ceee3417c094a57bd40d15efca6e26c7281d [file] [log] [blame]
Jesse Wilsone3424552011-05-16 18:30:05 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package tests.io;
18
Elliott Hughes5d930ca2014-04-23 17:53:37 -070019import android.system.ErrnoException;
20import android.system.OsConstants;
Jesse Wilsone3424552011-05-16 18:30:05 -070021import java.lang.reflect.InvocationHandler;
22import java.lang.reflect.InvocationTargetException;
23import java.lang.reflect.Method;
24import java.lang.reflect.Proxy;
Jesse Wilsone5eb80e2011-05-18 16:06:39 -070025import java.util.ArrayDeque;
26import java.util.Deque;
Jesse Wilsone3424552011-05-16 18:30:05 -070027import java.util.HashMap;
Jesse Wilsone3424552011-05-16 18:30:05 -070028import java.util.Map;
Jesse Wilsone3424552011-05-16 18:30:05 -070029import libcore.io.Libcore;
30import libcore.io.Os;
Jesse Wilsone3424552011-05-16 18:30:05 -070031
32/**
33 * A mocking interceptor that wraps another {@link Os} to add faults. This can
34 * be useful to test otherwise hard-to-test scenarios such as a full disk.
35 */
36public final class MockOs {
Jesse Wilsone5eb80e2011-05-18 16:06:39 -070037 private final InheritableThreadLocal<Map<String, Deque<InvocationHandler>>> handlers
38 = new InheritableThreadLocal<Map<String, Deque<InvocationHandler>>>() {
39 @Override protected Map<String, Deque<InvocationHandler>> initialValue() {
40 return new HashMap<String, Deque<InvocationHandler>>();
41 }
42 };
43
44 private Os delegate;
45 private final InvocationHandler delegateHandler = new InvocationHandler() {
46 @Override public Object invoke(Object o, Method method, Object[] args) throws Throwable {
47 try {
48 return method.invoke(delegate, args);
49 } catch (InvocationTargetException e) {
50 throw e.getCause();
51 }
Jesse Wilsone3424552011-05-16 18:30:05 -070052 }
53 };
54
55 private final InvocationHandler invocationHandler = new InvocationHandler() {
56 @Override public Object invoke(Object proxy, Method method, Object[] args)
57 throws Throwable {
Jesse Wilsone5eb80e2011-05-18 16:06:39 -070058 InvocationHandler handler = getHandlers(method.getName()).poll();
59 if (handler == null) {
60 handler = delegateHandler;
Jesse Wilsone3424552011-05-16 18:30:05 -070061 }
Jesse Wilsone5eb80e2011-05-18 16:06:39 -070062 return handler.invoke(proxy, method, args);
Jesse Wilsone3424552011-05-16 18:30:05 -070063 }
64 };
65
66 private final Os mockOs = (Os) Proxy.newProxyInstance(MockOs.class.getClassLoader(),
67 new Class[] { Os.class }, invocationHandler);
Jesse Wilsone3424552011-05-16 18:30:05 -070068
69 public void install() {
70 if (delegate != null) {
Jesse Wilson6f778cc2011-05-17 16:02:01 -070071 throw new IllegalStateException("MockOs already installed!");
Jesse Wilsone3424552011-05-16 18:30:05 -070072 }
73 delegate = Libcore.os;
74 Libcore.os = mockOs;
75 }
76
77 public void uninstall() {
78 if (delegate == null) {
Jesse Wilson6f778cc2011-05-17 16:02:01 -070079 throw new IllegalStateException("MockOs not installed!");
Jesse Wilsone3424552011-05-16 18:30:05 -070080 }
81 Libcore.os = delegate;
82 }
83
Jesse Wilsone5eb80e2011-05-18 16:06:39 -070084 /**
85 * Returns the invocation handlers to handle upcoming invocations of
86 * {@code methodName}. If empty, calls will be handled by the delegate.
87 */
88 public Deque<InvocationHandler> getHandlers(String methodName) {
89 Map<String, Deque<InvocationHandler>> threadFaults = handlers.get();
90 Deque<InvocationHandler> result = threadFaults.get(methodName);
91 if (result == null) {
92 result = new ArrayDeque<InvocationHandler>();
93 threadFaults.put(methodName, result);
94 }
95 return result;
96 }
97
98 /**
99 * Enqueues the specified number of normal operations. Useful to delay
100 * faults.
101 */
102 public void enqueueNormal(String methodName, int count) {
103 Deque<InvocationHandler> handlers = getHandlers(methodName);
104 for (int i = 0; i < count; i++) {
105 handlers.add(delegateHandler);
106 }
107 }
108
Jesse Wilsone3424552011-05-16 18:30:05 -0700109 public void enqueueFault(String methodName) {
110 enqueueFault(methodName, OsConstants.EIO);
111 }
112
Jesse Wilsone5eb80e2011-05-18 16:06:39 -0700113 public void enqueueFault(String methodName, final int errno) {
114 getHandlers(methodName).add(new InvocationHandler() {
Elliott Hughes9b510df2011-05-27 16:48:46 -0700115 @Override public Object invoke(Object proxy, Method method, Object[] args) throws ErrnoException {
Jesse Wilsone5eb80e2011-05-18 16:06:39 -0700116 throw new ErrnoException(method.getName(), errno);
117 }
118 });
Jesse Wilsone3424552011-05-16 18:30:05 -0700119 }
120}