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