1/* 2 * Copyright (C) 2008 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 android.test; 18 19import android.app.Application; 20import android.app.Service; 21import android.content.ComponentName; 22import android.content.Context; 23import android.content.Intent; 24import android.os.IBinder; 25import android.os.RemoteException; 26import android.test.mock.MockApplication; 27 28import java.lang.reflect.Field; 29import java.util.Random; 30 31/** 32 * This test case provides a framework in which you can test Service classes in 33 * a controlled environment. It provides basic support for the lifecycle of a 34 * Service, and hooks with which you can inject various dependencies and control 35 * the environment in which your Service is tested. 36 * 37 * <p><b>Lifecycle Support.</b> 38 * A Service is accessed with a specific sequence of 39 * calls, as documented in the section 40 * <a href="http://developer.android.com/guide/topics/fundamentals.html#servlife"> 41 * Service lifecycle</a> in the Developer Guide. In order to support the lifecycle of a Service, 42 * <code>ServiceTestCase</code> enforces this protocol: 43 * 44 * <ul> 45 * <li> 46 * The {@link #setUp()} method is called before each test method. The base implementation 47 * gets the system context. If you override <code>setUp()</code>, you must call 48 * <code>super.setUp()</code> as the first statement in your override. 49 * </li> 50 * <li> 51 * The test case waits to call {@link android.app.Service#onCreate()} until one of your 52 * test methods calls {@link #startService} or {@link #bindService}. This gives you an 53 * opportunity to set up or adjust any additional framework or test logic before you test 54 * the running service. 55 * </li> 56 * <li> 57 * When one of your test methods calls {@link #startService ServiceTestCase.startService()} 58 * or {@link #bindService ServiceTestCase.bindService()}, the test case calls 59 * {@link android.app.Service#onCreate() Service.onCreate()} and then calls either 60 * {@link android.app.Service#startService(Intent) Service.startService(Intent)} or 61 * {@link android.app.Service#bindService(Intent, ServiceConnection, int) 62 * Service.bindService(Intent, ServiceConnection, int)}, as appropriate. It also stores 63 * values needed to track and support the lifecycle. 64 * </li> 65 * <li> 66 * After each test method finishes, the test case calls the {@link #tearDown} method. This 67 * method stops and destroys the service with the appropriate calls, depending on how the 68 * service was started. If you override <code>tearDown()</code>, your must call the 69 * <code>super.tearDown()</code> as the last statement in your override. 70 * </li> 71 * </ul> 72 * 73 * <p> 74 * <strong>Dependency Injection.</strong> 75 * A service has two inherent dependencies, its {@link android.content.Context Context} and its 76 * associated {@link android.app.Application Application}. The ServiceTestCase framework 77 * allows you to inject modified, mock, or isolated replacements for these dependencies, and 78 * thus perform unit tests with controlled dependencies in an isolated environment. 79 * </p> 80 * <p> 81 * By default, the test case is injected with a full system context and a generic 82 * {@link android.test.mock.MockApplication MockApplication} object. You can inject 83 * alternatives to either of these by invoking 84 * {@link AndroidTestCase#setContext(Context) setContext()} or 85 * {@link #setApplication setApplication()}. You must do this <em>before</em> calling 86 * startService() or bindService(). The test framework provides a 87 * number of alternatives for Context, including 88 * {link android.test.mock.MockContext MockContext}, 89 * {@link android.test.RenamingDelegatingContext RenamingDelegatingContext}, 90 * {@link android.content.ContextWrapper ContextWrapper}, and 91 * {@link android.test.IsolatedContext}. 92 */ 93public abstract class ServiceTestCase<T extends Service> extends AndroidTestCase { 94 95 Class<T> mServiceClass; 96 97 private Context mSystemContext; 98 private Application mApplication; 99 100 /** 101 * Constructor 102 * @param serviceClass The type of the service under test. 103 */ 104 public ServiceTestCase(Class<T> serviceClass) { 105 mServiceClass = serviceClass; 106 } 107 108 private T mService; 109 private boolean mServiceAttached = false; 110 private boolean mServiceCreated = false; 111 private boolean mServiceStarted = false; 112 private boolean mServiceBound = false; 113 private Intent mServiceIntent = null; 114 private int mServiceId; 115 116 /** 117 * @return An instance of the service under test. This instance is created automatically when 118 * a test calls {@link #startService} or {@link #bindService}. 119 */ 120 public T getService() { 121 return mService; 122 } 123 124 /** 125 * Gets the current system context and stores it. 126 * 127 * Extend this method to do your own test initialization. If you do so, you 128 * must call <code>super.setUp()</code> as the first statement in your override. The method is 129 * called before each test method is executed. 130 */ 131 @Override 132 protected void setUp() throws Exception { 133 super.setUp(); 134 135 // get the real context, before the individual tests have a chance to muck with it 136 mSystemContext = getContext(); 137 138 } 139 140 /** 141 * Creates the service under test and attaches all injected dependencies 142 * (Context, Application) to it. This is called automatically by {@link #startService} or 143 * by {@link #bindService}. 144 * If you need to call {@link AndroidTestCase#setContext(Context) setContext()} or 145 * {@link #setApplication setApplication()}, do so before calling this method. 146 */ 147 protected void setupService() { 148 mService = null; 149 try { 150 mService = mServiceClass.newInstance(); 151 } catch (Exception e) { 152 assertNotNull(mService); 153 } 154 if (getApplication() == null) { 155 setApplication(new MockApplication()); 156 } 157 mService.attach( 158 getContext(), 159 null, // ActivityThread not actually used in Service 160 mServiceClass.getName(), 161 null, // token not needed when not talking with the activity manager 162 getApplication(), 163 null // mocked services don't talk with the activity manager 164 ); 165 166 assertNotNull(mService); 167 168 mServiceId = new Random().nextInt(); 169 mServiceAttached = true; 170 } 171 172 /** 173 * Starts the service under test, in the same way as if it were started by 174 * {@link android.content.Context#startService(Intent) Context.startService(Intent)} with 175 * an {@link android.content.Intent} that identifies a service. 176 * If you use this method to start the service, it is automatically stopped by 177 * {@link #tearDown}. 178 * 179 * @param intent An Intent that identifies a service, of the same form as the Intent passed to 180 * {@link android.content.Context#startService(Intent) Context.startService(Intent)}. 181 */ 182 protected void startService(Intent intent) { 183 if (!mServiceAttached) { 184 setupService(); 185 } 186 assertNotNull(mService); 187 188 if (!mServiceCreated) { 189 mService.onCreate(); 190 mServiceCreated = true; 191 } 192 mService.onStartCommand(intent, 0, mServiceId); 193 194 mServiceStarted = true; 195 } 196 197 /** 198 * <p> 199 * Starts the service under test, in the same way as if it were started by 200 * {@link android.content.Context#bindService(Intent, ServiceConnection, int) 201 * Context.bindService(Intent, ServiceConnection, flags)} with an 202 * {@link android.content.Intent} that identifies a service. 203 * </p> 204 * <p> 205 * Notice that the parameters are different. You do not provide a 206 * {@link android.content.ServiceConnection} object or the flags parameter. Instead, 207 * you only provide the Intent. The method returns an object whose type is a 208 * subclass of {@link android.os.IBinder}, or null if the method fails. An IBinder 209 * object refers to a communication channel between the application and 210 * the service. The flag is assumed to be {@link android.content.Context#BIND_AUTO_CREATE}. 211 * </p> 212 * <p> 213 * See <a href="{@docRoot}guide/developing/tools/aidl.html">Designing a Remote Interface 214 * Using AIDL</a> for more information about the communication channel object returned 215 * by this method. 216 * </p> 217 * Note: To be able to use bindService in a test, the service must implement getService() 218 * method. An example of this is in the ApiDemos sample application, in the 219 * LocalService demo. 220 * 221 * @param intent An Intent object of the form expected by 222 * {@link android.content.Context#bindService}. 223 * 224 * @return An object whose type is a subclass of IBinder, for making further calls into 225 * the service. 226 */ 227 protected IBinder bindService(Intent intent) { 228 if (!mServiceAttached) { 229 setupService(); 230 } 231 assertNotNull(mService); 232 233 if (!mServiceCreated) { 234 mService.onCreate(); 235 mServiceCreated = true; 236 } 237 // no extras are expected by unbind 238 mServiceIntent = intent.cloneFilter(); 239 IBinder result = mService.onBind(intent); 240 241 mServiceBound = true; 242 return result; 243 } 244 245 /** 246 * Makes the necessary calls to stop (or unbind) the service under test, and 247 * calls onDestroy(). Ordinarily this is called automatically (by {@link #tearDown}, but 248 * you can call it directly from your test in order to check for proper shutdown behavior. 249 */ 250 protected void shutdownService() { 251 if (mServiceStarted) { 252 mService.stopSelf(); 253 mServiceStarted = false; 254 } else if (mServiceBound) { 255 mService.onUnbind(mServiceIntent); 256 mServiceBound = false; 257 } 258 if (mServiceCreated) { 259 mService.onDestroy(); 260 } 261 } 262 263 /** 264 * <p> 265 * Shuts down the service under test. Ensures all resources are cleaned up and 266 * garbage collected before moving on to the next test. This method is called after each 267 * test method. 268 * </p> 269 * <p> 270 * Subclasses that override this method must call <code>super.tearDown()</code> as their 271 * last statement. 272 * </p> 273 * 274 * @throws Exception 275 */ 276 @Override 277 protected void tearDown() throws Exception { 278 shutdownService(); 279 mService = null; 280 281 // Scrub out members - protects against memory leaks in the case where someone 282 // creates a non-static inner class (thus referencing the test case) and gives it to 283 // someone else to hold onto 284 scrubClass(ServiceTestCase.class); 285 286 super.tearDown(); 287 } 288 289 /** 290 * Sets the application that is used during the test. If you do not call this method, 291 * a new {@link android.test.mock.MockApplication MockApplication} object is used. 292 * 293 * @param application The Application object that is used by the service under test. 294 * 295 * @see #getApplication() 296 */ 297 public void setApplication(Application application) { 298 mApplication = application; 299 } 300 301 /** 302 * Returns the Application object in use by the service under test. 303 * 304 * @return The application object. 305 * 306 * @see #setApplication 307 */ 308 public Application getApplication() { 309 return mApplication; 310 } 311 312 /** 313 * Returns the real system context that is saved by {@link #setUp()}. Use it to create 314 * mock or other types of context objects for the service under test. 315 * 316 * @return A normal system context. 317 */ 318 public Context getSystemContext() { 319 return mSystemContext; 320 } 321 322 /** 323 * Tests that {@link #setupService()} runs correctly and issues an 324 * {@link junit.framework.Assert#assertNotNull(String, Object)} if it does. 325 * You can override this test method if you wish. 326 * 327 * @throws Exception 328 */ 329 public void testServiceTestCaseSetUpProperly() throws Exception { 330 setupService(); 331 assertNotNull("service should be launched successfully", mService); 332 } 333} 334