• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
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 
15 package android.testing;
16 
17 import android.content.BroadcastReceiver;
18 import android.content.ComponentCallbacks;
19 import android.content.ComponentName;
20 import android.content.ContentProviderClient;
21 import android.content.Context;
22 import android.content.ContextWrapper;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.content.ServiceConnection;
26 import android.content.pm.PackageManager;
27 import android.content.res.Resources;
28 import android.os.Handler;
29 import android.os.IBinder;
30 import android.os.UserHandle;
31 import android.provider.Settings;
32 import android.util.ArrayMap;
33 import android.view.LayoutInflater;
34 
35 import org.junit.rules.TestRule;
36 import org.junit.rules.TestWatcher;
37 import org.junit.runner.Description;
38 import org.junit.runners.model.Statement;
39 
40 /**
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 TestableSettingsProvider}</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  */
59 public class TestableContext extends ContextWrapper implements TestRule {
60 
61     private final TestableContentResolver mTestableContentResolver;
62     private final TestableSettingsProvider mSettingsProvider;
63 
64     private ArrayMap<String, Object> mMockSystemServices;
65     private ArrayMap<ComponentName, IBinder> mMockServices;
66     private ArrayMap<ServiceConnection, ComponentName> mActiveServices;
67 
68     private PackageManager mMockPackageManager;
69     private LeakCheck.Tracker mReceiver;
70     private LeakCheck.Tracker mService;
71     private LeakCheck.Tracker mComponent;
72 
TestableContext(Context base)73     public TestableContext(Context base) {
74         this(base, null);
75     }
76 
TestableContext(Context base, LeakCheck check)77     public TestableContext(Context base, LeakCheck check) {
78         super(base);
79         mTestableContentResolver = new TestableContentResolver(base);
80         ContentProviderClient settings = base.getContentResolver()
81                 .acquireContentProviderClient(Settings.AUTHORITY);
82         mSettingsProvider = TestableSettingsProvider.getFakeSettingsProvider(settings);
83         mTestableContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider);
84         mReceiver = check != null ? check.getTracker("receiver") : null;
85         mService = check != null ? check.getTracker("service") : null;
86         mComponent = check != null ? check.getTracker("component") : null;
87     }
88 
setMockPackageManager(PackageManager mock)89     public void setMockPackageManager(PackageManager mock) {
90         mMockPackageManager = mock;
91     }
92 
93     @Override
getPackageManager()94     public PackageManager getPackageManager() {
95         if (mMockPackageManager != null) {
96             return mMockPackageManager;
97         }
98         return super.getPackageManager();
99     }
100 
101     @Override
getResources()102     public Resources getResources() {
103         return super.getResources();
104     }
105 
addMockSystemService(Class<T> service, T mock)106     public <T> void addMockSystemService(Class<T> service, T mock) {
107         addMockSystemService(getSystemServiceName(service), mock);
108     }
109 
addMockSystemService(String name, Object service)110     public void addMockSystemService(String name, Object service) {
111         if (mMockSystemServices == null) mMockSystemServices = new ArrayMap<>();
112         mMockSystemServices.put(name, service);
113     }
114 
addMockService(ComponentName component, IBinder service)115     public void addMockService(ComponentName component, IBinder service) {
116         if (mMockServices == null) mMockServices = new ArrayMap<>();
117         mMockServices.put(component, service);
118     }
119 
120     @Override
getSystemService(String name)121     public Object getSystemService(String name) {
122         if (mMockSystemServices != null && mMockSystemServices.containsKey(name)) {
123             return mMockSystemServices.get(name);
124         }
125         if (name.equals(LAYOUT_INFLATER_SERVICE)) {
126             return getBaseContext().getSystemService(LayoutInflater.class).cloneInContext(this);
127         }
128         return super.getSystemService(name);
129     }
130 
getSettingsProvider()131     TestableSettingsProvider getSettingsProvider() {
132         return mSettingsProvider;
133     }
134 
135     @Override
getContentResolver()136     public TestableContentResolver getContentResolver() {
137         return mTestableContentResolver;
138     }
139 
140     @Override
getApplicationContext()141     public Context getApplicationContext() {
142         // Return this so its always a TestableContext.
143         return this;
144     }
145 
146     @Override
registerReceiver(BroadcastReceiver receiver, IntentFilter filter)147     public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
148         if (mReceiver != null) mReceiver.getLeakInfo(receiver).addAllocation(new Throwable());
149         return super.registerReceiver(receiver, filter);
150     }
151 
152     @Override
registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler)153     public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
154             String broadcastPermission, Handler scheduler) {
155         if (mReceiver != null) mReceiver.getLeakInfo(receiver).addAllocation(new Throwable());
156         return super.registerReceiver(receiver, filter, broadcastPermission, scheduler);
157     }
158 
159     @Override
registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user, IntentFilter filter, String broadcastPermission, Handler scheduler)160     public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
161             IntentFilter filter, String broadcastPermission, Handler scheduler) {
162         if (mReceiver != null) mReceiver.getLeakInfo(receiver).addAllocation(new Throwable());
163         return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
164                 scheduler);
165     }
166 
167     @Override
unregisterReceiver(BroadcastReceiver receiver)168     public void unregisterReceiver(BroadcastReceiver receiver) {
169         if (mReceiver != null) mReceiver.getLeakInfo(receiver).clearAllocations();
170         super.unregisterReceiver(receiver);
171     }
172 
173     @Override
bindService(Intent service, ServiceConnection conn, int flags)174     public boolean bindService(Intent service, ServiceConnection conn, int flags) {
175         if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
176         if (checkMocks(service.getComponent(), conn)) return true;
177         return super.bindService(service, conn, flags);
178     }
179 
180     @Override
bindServiceAsUser(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user)181     public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
182             Handler handler, UserHandle user) {
183         if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
184         if (checkMocks(service.getComponent(), conn)) return true;
185         return super.bindServiceAsUser(service, conn, flags, handler, user);
186     }
187 
188     @Override
bindServiceAsUser(Intent service, ServiceConnection conn, int flags, UserHandle user)189     public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
190             UserHandle user) {
191         if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
192         if (checkMocks(service.getComponent(), conn)) return true;
193         return super.bindServiceAsUser(service, conn, flags, user);
194     }
195 
checkMocks(ComponentName component, ServiceConnection conn)196     private boolean checkMocks(ComponentName component, ServiceConnection conn) {
197         if (mMockServices != null && component != null && mMockServices.containsKey(component)) {
198             if (mActiveServices == null) mActiveServices = new ArrayMap<>();
199             mActiveServices.put(conn, component);
200             conn.onServiceConnected(component, mMockServices.get(component));
201             return true;
202         }
203         return false;
204     }
205 
206     @Override
unbindService(ServiceConnection conn)207     public void unbindService(ServiceConnection conn) {
208         if (mService != null) mService.getLeakInfo(conn).clearAllocations();
209         if (mActiveServices != null && mActiveServices.containsKey(conn)) {
210             conn.onServiceDisconnected(mActiveServices.get(conn));
211             mActiveServices.remove(conn);
212             return;
213         }
214         super.unbindService(conn);
215     }
216 
isBound(ComponentName component)217     public boolean isBound(ComponentName component) {
218         return mActiveServices != null && mActiveServices.containsValue(component);
219     }
220 
221     @Override
registerComponentCallbacks(ComponentCallbacks callback)222     public void registerComponentCallbacks(ComponentCallbacks callback) {
223         if (mComponent != null) mComponent.getLeakInfo(callback).addAllocation(new Throwable());
224         super.registerComponentCallbacks(callback);
225     }
226 
227     @Override
unregisterComponentCallbacks(ComponentCallbacks callback)228     public void unregisterComponentCallbacks(ComponentCallbacks callback) {
229         if (mComponent != null) mComponent.getLeakInfo(callback).clearAllocations();
230         super.unregisterComponentCallbacks(callback);
231     }
232 
233     @Override
apply(Statement base, Description description)234     public Statement apply(Statement base, Description description) {
235         return new TestWatcher() {
236             @Override
237             protected void succeeded(Description description) {
238                 mSettingsProvider.clearValuesAndCheck(TestableContext.this);
239             }
240 
241             @Override
242             protected void failed(Throwable e, Description description) {
243                 mSettingsProvider.clearValuesAndCheck(TestableContext.this);
244             }
245         }.apply(base, description);
246     }
247 }
248