blob: cb5d4cb8242fc22597144b8b3b1b829daa20cf8e [file] [log] [blame]
Jason Monke9789282016-11-09 08:59:56 -05001/*
Jason Monk340b0e52017-03-08 14:57:56 -05002 * Copyright (C) 2017 The Android Open Source Project
Jason Monke9789282016-11-09 08:59:56 -05003 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
Jason Monk340b0e52017-03-08 14:57:56 -050015package android.testing;
Jason Monke9789282016-11-09 08:59:56 -050016
Jason Monk9abca5e2016-11-11 16:18:14 -050017import android.content.BroadcastReceiver;
18import android.content.ComponentCallbacks;
Jason Monk3cfedd72016-12-09 09:31:37 -050019import android.content.ComponentName;
Jason Monke9789282016-11-09 08:59:56 -050020import android.content.ContentProviderClient;
Jason Monke9789282016-11-09 08:59:56 -050021import android.content.Context;
22import android.content.ContextWrapper;
Jason Monk9abca5e2016-11-11 16:18:14 -050023import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.ServiceConnection;
Jason Monk26bc8992017-01-04 14:17:47 -050026import android.content.pm.PackageManager;
Jason Monk3cfedd72016-12-09 09:31:37 -050027import android.content.res.Resources;
Jason Monk9abca5e2016-11-11 16:18:14 -050028import android.os.Handler;
Jason Monk3cfedd72016-12-09 09:31:37 -050029import android.os.IBinder;
Jason Monk9abca5e2016-11-11 16:18:14 -050030import android.os.UserHandle;
Jason Monke9789282016-11-09 08:59:56 -050031import android.provider.Settings;
Jason Monk3cfedd72016-12-09 09:31:37 -050032import android.util.ArrayMap;
Jason Monkaa573e92017-01-27 17:00:29 -050033import android.view.LayoutInflater;
Jason Monke9789282016-11-09 08:59:56 -050034
Jason Monk340b0e52017-03-08 14:57:56 -050035import org.junit.rules.TestRule;
36import org.junit.rules.TestWatcher;
37import org.junit.runner.Description;
38import org.junit.runners.model.Statement;
Jason Monk9abca5e2016-11-11 16:18:14 -050039
Jason Monk340b0e52017-03-08 14:57:56 -050040/**
41 * A ContextWrapper with utilities specifically designed to make Testing easier.
42 *
43 * <ul>
44 * <li>System services can be mocked out with {@link #addMockSystemService}</li>
45 * <li>Service binding can be mocked out with {@link #addMockService}</li>
46 * <li>Settings support {@link TestableSettings}</li>
47 * <li>Has support for {@link LeakCheck} for services and receivers</li>
48 * </ul>
49 *
50 * <p>TestableContext should be defined as a rule on your test so it can clean up after itself.
51 * Like the following:</p>
52 * <pre class="prettyprint">
53 * {@literal
54 * @Rule
55 * private final TestableContext mContext = new TestableContext(InstrumentationRegister.getContext());
56 * }
57 * </pre>
58 */
59public class TestableContext extends ContextWrapper implements TestRule {
Jason Monke9789282016-11-09 08:59:56 -050060
Jason Monk340b0e52017-03-08 14:57:56 -050061 private final TestableContentResolver mTestableContentResolver;
62 private final TestableSettings mSettingsProvider;
Jason Monke9789282016-11-09 08:59:56 -050063
Jason Monk3cfedd72016-12-09 09:31:37 -050064 private ArrayMap<String, Object> mMockSystemServices;
65 private ArrayMap<ComponentName, IBinder> mMockServices;
66 private ArrayMap<ServiceConnection, ComponentName> mActiveServices;
67
Jason Monk26bc8992017-01-04 14:17:47 -050068 private PackageManager mMockPackageManager;
Jason Monk340b0e52017-03-08 14:57:56 -050069 private LeakCheck.Tracker mReceiver;
70 private LeakCheck.Tracker mService;
71 private LeakCheck.Tracker mComponent;
Jason Monk9abca5e2016-11-11 16:18:14 -050072
Jason Monk340b0e52017-03-08 14:57:56 -050073 public TestableContext(Context base) {
74 this(base, null);
75 }
76
77 public TestableContext(Context base, LeakCheck check) {
Jason Monke9789282016-11-09 08:59:56 -050078 super(base);
Jason Monk340b0e52017-03-08 14:57:56 -050079 mTestableContentResolver = new TestableContentResolver(base);
Jason Monke9789282016-11-09 08:59:56 -050080 ContentProviderClient settings = base.getContentResolver()
81 .acquireContentProviderClient(Settings.AUTHORITY);
Jason Monk340b0e52017-03-08 14:57:56 -050082 mSettingsProvider = TestableSettings.getFakeSettingsProvider(settings,
83 mTestableContentResolver);
84 mTestableContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider.getProvider());
85 mReceiver = check != null ? check.getTracker("receiver") : null;
86 mService = check != null ? check.getTracker("service") : null;
87 mComponent = check != null ? check.getTracker("component") : null;
Jason Monke9789282016-11-09 08:59:56 -050088 }
89
Jason Monk26bc8992017-01-04 14:17:47 -050090 public void setMockPackageManager(PackageManager mock) {
91 mMockPackageManager = mock;
92 }
93
94 @Override
95 public PackageManager getPackageManager() {
96 if (mMockPackageManager != null) {
97 return mMockPackageManager;
98 }
99 return super.getPackageManager();
100 }
101
Jason Monk3cfedd72016-12-09 09:31:37 -0500102 @Override
103 public Resources getResources() {
104 return super.getResources();
105 }
106
Adrian Roos91250682017-02-06 14:48:15 -0800107 public <T> void addMockSystemService(Class<T> service, T mock) {
108 addMockSystemService(getSystemServiceName(service), mock);
109 }
110
Jason Monk3cfedd72016-12-09 09:31:37 -0500111 public void addMockSystemService(String name, Object service) {
Jason Monk340b0e52017-03-08 14:57:56 -0500112 if (mMockSystemServices == null) mMockSystemServices = new ArrayMap<>();
Jason Monk3cfedd72016-12-09 09:31:37 -0500113 mMockSystemServices.put(name, service);
114 }
115
116 public void addMockService(ComponentName component, IBinder service) {
Jason Monk340b0e52017-03-08 14:57:56 -0500117 if (mMockServices == null) mMockServices = new ArrayMap<>();
Jason Monk3cfedd72016-12-09 09:31:37 -0500118 mMockServices.put(component, service);
119 }
120
Jason Monk3cfedd72016-12-09 09:31:37 -0500121 @Override
122 public Object getSystemService(String name) {
123 if (mMockSystemServices != null && mMockSystemServices.containsKey(name)) {
124 return mMockSystemServices.get(name);
125 }
Jason Monkaa573e92017-01-27 17:00:29 -0500126 if (name.equals(LAYOUT_INFLATER_SERVICE)) {
127 return getBaseContext().getSystemService(LayoutInflater.class).cloneInContext(this);
128 }
Jason Monk3cfedd72016-12-09 09:31:37 -0500129 return super.getSystemService(name);
130 }
131
Jason Monk340b0e52017-03-08 14:57:56 -0500132 public TestableSettings getSettingsProvider() {
Jason Monke9789282016-11-09 08:59:56 -0500133 return mSettingsProvider;
134 }
135
136 @Override
Jason Monk340b0e52017-03-08 14:57:56 -0500137 public TestableContentResolver getContentResolver() {
138 return mTestableContentResolver;
Jason Monke9789282016-11-09 08:59:56 -0500139 }
140
141 @Override
142 public Context getApplicationContext() {
143 // Return this so its always a TestableContext.
144 return this;
145 }
Jason Monk9abca5e2016-11-11 16:18:14 -0500146
147 @Override
148 public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
149 if (mReceiver != null) mReceiver.getLeakInfo(receiver).addAllocation(new Throwable());
150 return super.registerReceiver(receiver, filter);
151 }
152
153 @Override
154 public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
155 String broadcastPermission, Handler scheduler) {
156 if (mReceiver != null) mReceiver.getLeakInfo(receiver).addAllocation(new Throwable());
157 return super.registerReceiver(receiver, filter, broadcastPermission, scheduler);
158 }
159
160 @Override
161 public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
162 IntentFilter filter, String broadcastPermission, Handler scheduler) {
163 if (mReceiver != null) mReceiver.getLeakInfo(receiver).addAllocation(new Throwable());
164 return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
165 scheduler);
166 }
167
168 @Override
169 public void unregisterReceiver(BroadcastReceiver receiver) {
170 if (mReceiver != null) mReceiver.getLeakInfo(receiver).clearAllocations();
171 super.unregisterReceiver(receiver);
172 }
173
174 @Override
175 public boolean bindService(Intent service, ServiceConnection conn, int flags) {
176 if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
Jason Monk3cfedd72016-12-09 09:31:37 -0500177 if (checkMocks(service.getComponent(), conn)) return true;
Jason Monk9abca5e2016-11-11 16:18:14 -0500178 return super.bindService(service, conn, flags);
179 }
180
181 @Override
182 public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
183 Handler handler, UserHandle user) {
184 if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
Jason Monk3cfedd72016-12-09 09:31:37 -0500185 if (checkMocks(service.getComponent(), conn)) return true;
Jason Monk9abca5e2016-11-11 16:18:14 -0500186 return super.bindServiceAsUser(service, conn, flags, handler, user);
187 }
188
189 @Override
190 public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
191 UserHandle user) {
192 if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
Jason Monk3cfedd72016-12-09 09:31:37 -0500193 if (checkMocks(service.getComponent(), conn)) return true;
Jason Monk9abca5e2016-11-11 16:18:14 -0500194 return super.bindServiceAsUser(service, conn, flags, user);
195 }
196
Jason Monk3cfedd72016-12-09 09:31:37 -0500197 private boolean checkMocks(ComponentName component, ServiceConnection conn) {
198 if (mMockServices != null && component != null && mMockServices.containsKey(component)) {
Jason Monk340b0e52017-03-08 14:57:56 -0500199 if (mActiveServices == null) mActiveServices = new ArrayMap<>();
Jason Monk3cfedd72016-12-09 09:31:37 -0500200 mActiveServices.put(conn, component);
201 conn.onServiceConnected(component, mMockServices.get(component));
202 return true;
203 }
204 return false;
205 }
206
Jason Monk9abca5e2016-11-11 16:18:14 -0500207 @Override
208 public void unbindService(ServiceConnection conn) {
209 if (mService != null) mService.getLeakInfo(conn).clearAllocations();
Jason Monk3cfedd72016-12-09 09:31:37 -0500210 if (mActiveServices != null && mActiveServices.containsKey(conn)) {
211 conn.onServiceDisconnected(mActiveServices.get(conn));
212 mActiveServices.remove(conn);
213 return;
214 }
Jason Monk9abca5e2016-11-11 16:18:14 -0500215 super.unbindService(conn);
216 }
217
Jason Monk3cfedd72016-12-09 09:31:37 -0500218 public boolean isBound(ComponentName component) {
219 return mActiveServices != null && mActiveServices.containsValue(component);
220 }
221
Jason Monk9abca5e2016-11-11 16:18:14 -0500222 @Override
223 public void registerComponentCallbacks(ComponentCallbacks callback) {
224 if (mComponent != null) mComponent.getLeakInfo(callback).addAllocation(new Throwable());
225 super.registerComponentCallbacks(callback);
226 }
227
228 @Override
229 public void unregisterComponentCallbacks(ComponentCallbacks callback) {
230 if (mComponent != null) mComponent.getLeakInfo(callback).clearAllocations();
231 super.unregisterComponentCallbacks(callback);
232 }
Jason Monk49fa0162017-01-11 09:21:56 -0500233
Jason Monk340b0e52017-03-08 14:57:56 -0500234 @Override
235 public Statement apply(Statement base, Description description) {
236 return new TestWatcher() {
237 @Override
238 protected void succeeded(Description description) {
239 mSettingsProvider.clearOverrides();
240 }
Jason Monk49fa0162017-01-11 09:21:56 -0500241
Jason Monk340b0e52017-03-08 14:57:56 -0500242 @Override
243 protected void failed(Throwable e, Description description) {
244 mSettingsProvider.clearOverrides();
245 }
246 }.apply(base, description);
Jason Monk49fa0162017-01-11 09:21:56 -0500247 }
Jason Monke9789282016-11-09 08:59:56 -0500248}