ContextFixture.java revision 6bfc71d2b5340f6274b3e63926a7068e364fc9ff
1/* 2 * Copyright (C) 2016 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 com.android.internal.telephony; 18 19import com.google.common.collect.ArrayListMultimap; 20import com.google.common.collect.Multimap; 21 22import org.mockito.MockitoAnnotations; 23import org.mockito.invocation.InvocationOnMock; 24import org.mockito.stubbing.Answer; 25 26import android.app.AlarmManager; 27import android.app.AppOpsManager; 28import android.app.NotificationManager; 29import android.content.BroadcastReceiver; 30import android.content.ComponentName; 31import android.content.ContentProvider; 32import android.content.ContentResolver; 33import android.content.ContentValues; 34import android.content.Context; 35import android.content.Intent; 36import android.content.IntentFilter; 37import android.content.ServiceConnection; 38import android.content.SharedPreferences; 39import android.content.pm.PackageManager; 40import android.content.pm.ResolveInfo; 41import android.content.pm.ServiceInfo; 42import android.content.res.Configuration; 43import android.content.res.Resources; 44import android.database.Cursor; 45import android.net.ConnectivityManager; 46import android.net.Uri; 47import android.net.wifi.WifiManager; 48import android.os.Bundle; 49import android.os.Handler; 50import android.os.IInterface; 51import android.os.PersistableBundle; 52import android.os.UserHandle; 53import android.os.UserManager; 54import android.preference.PreferenceManager; 55import android.provider.Settings; 56import android.provider.Telephony; 57import android.telephony.CarrierConfigManager; 58import android.telephony.SubscriptionManager; 59import android.telephony.TelephonyManager; 60import android.test.mock.MockContentProvider; 61import android.test.mock.MockContentResolver; 62import android.test.mock.MockContext; 63import android.util.Log; 64 65import java.util.ArrayList; 66import java.util.Arrays; 67import java.util.Collection; 68import java.util.HashMap; 69import java.util.List; 70import java.util.Locale; 71import java.util.Map; 72 73import static org.mockito.Matchers.anyBoolean; 74import static org.mockito.Matchers.anyString; 75import static org.mockito.Mockito.any; 76import static org.mockito.Mockito.anyInt; 77import static org.mockito.Mockito.doAnswer; 78import static org.mockito.Mockito.doReturn; 79import static org.mockito.Mockito.eq; 80import static org.mockito.Mockito.mock; 81import static org.mockito.Mockito.spy; 82import static org.mockito.Mockito.when; 83 84/** 85 * Controls a test {@link Context} as would be provided by the Android framework to an 86 * {@code Activity}, {@code Service} or other system-instantiated component. 87 * 88 * Contains Fake<Component> classes like FakeContext for components that require complex and 89 * reusable stubbing. Others can be mocked using Mockito functions in tests or constructor/public 90 * methods of this class. 91 */ 92public class ContextFixture implements TestFixture<Context> { 93 private static final String TAG = "ContextFixture"; 94 95 public class FakeContentProvider extends MockContentProvider { 96 @Override 97 public int delete(Uri uri, String selection, String[] selectionArgs) { 98 return 0; 99 } 100 101 @Override 102 public Uri insert(Uri uri, ContentValues values) { 103 return null; 104 } 105 106 @Override 107 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 108 String sortOrder) { 109 return null; 110 } 111 112 @Override 113 public Bundle call(String method, String request, Bundle args) { 114 return null; 115 } 116 } 117 118 private final HashMap<String, Object> mSystemServices = new HashMap<String, Object>(); 119 120 public void setSystemService(String name, Object service) { 121 synchronized(mSystemServices) { 122 mSystemServices.put(name, service); 123 } 124 } 125 126 public class FakeContext extends MockContext { 127 @Override 128 public PackageManager getPackageManager() { 129 return mPackageManager; 130 } 131 132 @Override 133 public boolean bindService( 134 Intent serviceIntent, 135 ServiceConnection connection, 136 int flags) { 137 if (mServiceByServiceConnection.containsKey(connection)) { 138 throw new RuntimeException("ServiceConnection already bound: " + connection); 139 } 140 IInterface service = mServiceByComponentName.get(serviceIntent.getComponent()); 141 if (service == null) { 142 throw new RuntimeException("ServiceConnection not found: " 143 + serviceIntent.getComponent()); 144 } 145 mServiceByServiceConnection.put(connection, service); 146 connection.onServiceConnected(serviceIntent.getComponent(), service.asBinder()); 147 return true; 148 } 149 150 @Override 151 public void unbindService( 152 ServiceConnection connection) { 153 IInterface service = mServiceByServiceConnection.remove(connection); 154 if (service == null) { 155 throw new RuntimeException("ServiceConnection not found: " + connection); 156 } 157 connection.onServiceDisconnected(mComponentNameByService.get(service)); 158 } 159 160 @Override 161 public Object getSystemService(String name) { 162 switch (name) { 163 case Context.TELEPHONY_SERVICE: 164 return mTelephonyManager; 165 case Context.APP_OPS_SERVICE: 166 return mAppOpsManager; 167 case Context.NOTIFICATION_SERVICE: 168 return mNotificationManager; 169 case Context.USER_SERVICE: 170 return mUserManager; 171 case Context.CARRIER_CONFIG_SERVICE: 172 return mCarrierConfigManager; 173 case Context.POWER_SERVICE: 174 // PowerManager is a final class so cannot be mocked, return real service 175 return TestApplication.getAppContext().getSystemService(name); 176 case Context.TELEPHONY_SUBSCRIPTION_SERVICE: 177 return mSubscriptionManager; 178 case Context.WIFI_SERVICE: 179 return mWifiManager; 180 case Context.ALARM_SERVICE: 181 return mAlarmManager; 182 case Context.CONNECTIVITY_SERVICE: 183 return mConnectivityManager; 184 default: 185 synchronized(mSystemServices) { 186 return mSystemServices.get(name); 187 } 188 } 189 } 190 191 @Override 192 public int getUserId() { 193 return 0; 194 } 195 196 @Override 197 public Resources getResources() { 198 return mResources; 199 } 200 201 @Override 202 public String getOpPackageName() { 203 return "com.android.internal.telephony"; 204 } 205 206 @Override 207 public ContentResolver getContentResolver() { 208 return mContentResolver; 209 } 210 211 @Override 212 public void unregisterReceiver(BroadcastReceiver receiver) { 213 } 214 215 @Override 216 public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { 217 Intent result = null; 218 synchronized (mBroadcastReceiversByAction) { 219 for (int i = 0 ; i < filter.countActions(); i++) { 220 mBroadcastReceiversByAction.put(filter.getAction(i), receiver); 221 if (result == null) { 222 result = mStickyBroadcastByAction.get(filter.getAction(i)); 223 } 224 } 225 } 226 227 return result; 228 } 229 230 @Override 231 public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, 232 String broadcastPermission, Handler scheduler) { 233 return registerReceiver(receiver, filter); 234 } 235 236 @Override 237 public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user, 238 IntentFilter filter, String broadcastPermission, Handler scheduler) { 239 return registerReceiver(receiver, filter); 240 } 241 242 @Override 243 public void sendBroadcast(Intent intent) { 244 logd("sendBroadcast called for " + intent.getAction()); 245 synchronized (mBroadcastReceiversByAction) { 246 for (BroadcastReceiver broadcastReceiver : 247 mBroadcastReceiversByAction.get(intent.getAction())) { 248 broadcastReceiver.onReceive(mContext, intent); 249 } 250 } 251 } 252 253 @Override 254 public void sendBroadcast(Intent intent, String receiverPermission) { 255 logd("sendBroadcast called for " + intent.getAction()); 256 sendBroadcast(intent); 257 } 258 259 @Override 260 public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, 261 String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, 262 int initialCode, String initialData, Bundle initialExtras) { 263 logd("sendOrderedBroadcastAsUser called for " + intent.getAction()); 264 sendBroadcast(intent); 265 if (resultReceiver != null) { 266 synchronized (mOrderedBroadcastReceivers) { 267 mOrderedBroadcastReceivers.put(intent, resultReceiver); 268 } 269 } 270 } 271 272 @Override 273 public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, 274 String receiverPermission, int appOp, BroadcastReceiver resultReceiver, 275 Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { 276 logd("sendOrderedBroadcastAsUser called for " + intent.getAction()); 277 sendBroadcast(intent); 278 if (resultReceiver != null) { 279 synchronized (mOrderedBroadcastReceivers) { 280 mOrderedBroadcastReceivers.put(intent, resultReceiver); 281 } 282 } 283 } 284 285 @Override 286 public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, 287 String receiverPermission, int appOp, Bundle options, 288 BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, 289 String initialData, Bundle initialExtras) { 290 logd("sendOrderedBroadcastAsUser called for " + intent.getAction()); 291 sendBroadcast(intent); 292 if (resultReceiver != null) { 293 synchronized (mOrderedBroadcastReceivers) { 294 mOrderedBroadcastReceivers.put(intent, resultReceiver); 295 } 296 } 297 } 298 299 @Override 300 public void sendStickyBroadcast(Intent intent) { 301 logd("sendStickyBroadcast called for " + intent.getAction()); 302 synchronized (mBroadcastReceiversByAction) { 303 sendBroadcast(intent); 304 mStickyBroadcastByAction.put(intent.getAction(), intent); 305 } 306 } 307 308 @Override 309 public void sendStickyBroadcastAsUser(Intent intent, UserHandle ignored) { 310 logd("sendStickyBroadcastAsUser called for " + intent.getAction()); 311 sendStickyBroadcast(intent); 312 } 313 314 @Override 315 public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) 316 throws PackageManager.NameNotFoundException { 317 return this; 318 } 319 320 @Override 321 public void enforceCallingOrSelfPermission(String permission, String message) { 322 // Don't bother enforcing anything in mock. 323 } 324 325 @Override 326 public SharedPreferences getSharedPreferences(String name, int mode) { 327 return mSharedPreferences; 328 } 329 330 @Override 331 public String getPackageName() { 332 return "com.android.internal.telephony"; 333 } 334 335 public boolean testMethod() { 336 return true; 337 } 338 339 public int testMethod1() { 340 return 0; 341 } 342 } 343 344 private final Multimap<String, ComponentName> mComponentNamesByAction = 345 ArrayListMultimap.create(); 346 private final Map<ComponentName, IInterface> mServiceByComponentName = 347 new HashMap<ComponentName, IInterface>(); 348 private final Map<ComponentName, ServiceInfo> mServiceInfoByComponentName = 349 new HashMap<ComponentName, ServiceInfo>(); 350 private final Map<IInterface, ComponentName> mComponentNameByService = 351 new HashMap<IInterface, ComponentName>(); 352 private final Map<ServiceConnection, IInterface> mServiceByServiceConnection = 353 new HashMap<ServiceConnection, IInterface>(); 354 private final Multimap<String, BroadcastReceiver> mBroadcastReceiversByAction = 355 ArrayListMultimap.create(); 356 private final HashMap<String, Intent> mStickyBroadcastByAction = 357 new HashMap<String, Intent>(); 358 private final Multimap<Intent, BroadcastReceiver> mOrderedBroadcastReceivers = 359 ArrayListMultimap.create(); 360 361 // The application context is the most important object this class provides to the system 362 // under test. 363 private final Context mContext = spy(new FakeContext()); 364 365 // We then create a spy on the application context allowing standard Mockito-style 366 // when(...) logic to be used to add specific little responses where needed. 367 368 private final Resources mResources = mock(Resources.class); 369 private final PackageManager mPackageManager = mock(PackageManager.class); 370 private final TelephonyManager mTelephonyManager = mock(TelephonyManager.class); 371 private final AppOpsManager mAppOpsManager = mock(AppOpsManager.class); 372 private final NotificationManager mNotificationManager = mock(NotificationManager.class); 373 private final UserManager mUserManager = mock(UserManager.class); 374 private final CarrierConfigManager mCarrierConfigManager = mock(CarrierConfigManager.class); 375 private final SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class); 376 private final AlarmManager mAlarmManager = mock(AlarmManager.class); 377 private final ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class); 378 private final WifiManager mWifiManager = mock(WifiManager.class); 379 380 private final ContentProvider mContentProvider = spy(new FakeContentProvider()); 381 382 private final Configuration mConfiguration = new Configuration(); 383 private final SharedPreferences mSharedPreferences = PreferenceManager. 384 getDefaultSharedPreferences(TestApplication.getAppContext()); 385 private final MockContentResolver mContentResolver = new MockContentResolver(); 386 private final PersistableBundle mBundle = new PersistableBundle(); 387 388 public ContextFixture() { 389 MockitoAnnotations.initMocks(this); 390 391 doAnswer(new Answer<List<ResolveInfo>>() { 392 @Override 393 public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable { 394 return doQueryIntentServices( 395 (Intent) invocation.getArguments()[0], 396 (Integer) invocation.getArguments()[1]); 397 } 398 }).when(mPackageManager).queryIntentServices((Intent) any(), anyInt()); 399 400 doAnswer(new Answer<List<ResolveInfo>>() { 401 @Override 402 public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable { 403 return doQueryIntentServices( 404 (Intent) invocation.getArguments()[0], 405 (Integer) invocation.getArguments()[1]); 406 } 407 }).when(mPackageManager).queryIntentServicesAsUser((Intent) any(), anyInt(), anyInt()); 408 409 doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt()); 410 411 mConfiguration.locale = Locale.US; 412 doReturn(mConfiguration).when(mResources).getConfiguration(); 413 414 mContentResolver.addProvider(Settings.System.CONTENT_URI.getAuthority(), mContentProvider); 415 } 416 417 @Override 418 public Context getTestDouble() { 419 return mContext; 420 } 421 422 public void putResource(int id, final String value) { 423 when(mResources.getText(eq(id))).thenReturn(value); 424 when(mResources.getString(eq(id))).thenReturn(value); 425 when(mResources.getString(eq(id), any())).thenAnswer(new Answer<String>() { 426 @Override 427 public String answer(InvocationOnMock invocation) { 428 Object[] args = invocation.getArguments(); 429 return String.format(value, Arrays.copyOfRange(args, 1, args.length)); 430 } 431 }); 432 } 433 434 public void putBooleanResource(int id, boolean value) { 435 when(mResources.getBoolean(eq(id))).thenReturn(value); 436 } 437 438 public void putStringArrayResource(int id, String[] values) { 439 doReturn(values).when(mResources).getStringArray(eq(id)); 440 } 441 442 public void putIntArrayResource(int id, int[] values) { 443 doReturn(values).when(mResources).getIntArray(eq(id)); 444 } 445 446 public PersistableBundle getCarrierConfigBundle() { 447 return mBundle; 448 } 449 450 private void addService(String action, ComponentName name, IInterface service) { 451 mComponentNamesByAction.put(action, name); 452 mServiceByComponentName.put(name, service); 453 mComponentNameByService.put(service, name); 454 } 455 456 private List<ResolveInfo> doQueryIntentServices(Intent intent, int flags) { 457 List<ResolveInfo> result = new ArrayList<ResolveInfo>(); 458 for (ComponentName componentName : mComponentNamesByAction.get(intent.getAction())) { 459 ResolveInfo resolveInfo = new ResolveInfo(); 460 resolveInfo.serviceInfo = mServiceInfoByComponentName.get(componentName); 461 result.add(resolveInfo); 462 } 463 return result; 464 } 465 466 public void sendBroadcastToOrderedBroadcastReceivers() { 467 synchronized (mOrderedBroadcastReceivers) { 468 // having a map separate from mOrderedBroadcastReceivers is helpful here as onReceive() 469 // call within the loop may lead to sendOrderedBroadcast() which can add to 470 // mOrderedBroadcastReceivers 471 Collection<Map.Entry<Intent, BroadcastReceiver>> map = 472 mOrderedBroadcastReceivers.entries(); 473 for (Map.Entry<Intent, BroadcastReceiver> entry : map) { 474 entry.getValue().onReceive(mContext, entry.getKey()); 475 mOrderedBroadcastReceivers.remove(entry.getKey(), entry.getValue()); 476 } 477 } 478 } 479 480 private static void logd(String s) { 481 Log.d(TAG, s); 482 } 483} 484