| /* | |
| * Copyright (C) 2008 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| package android.test; | |
| import android.app.Application; | |
| import android.app.Service; | |
| import android.content.Context; | |
| import android.content.Intent; | |
| import android.os.IBinder; | |
| import android.test.mock.MockApplication; | |
| import android.test.mock.MockService; | |
| import java.util.Random; | |
| /** | |
| * This test case provides a framework in which you can test Service classes in | |
| * a controlled environment. It provides basic support for the lifecycle of a | |
| * Service, and hooks with which you can inject various dependencies and control | |
| * the environment in which your Service is tested. | |
| * | |
| * <div class="special reference"> | |
| * <h3>Developer Guides</h3> | |
| * <p>For more information about application testing, read the | |
| * <a href="{@docRoot}guide/topics/testing/index.html">Testing</a> developer guide.</p> | |
| * </div> | |
| * | |
| * <p><b>Lifecycle Support.</b> | |
| * A Service is accessed with a specific sequence of | |
| * calls, as described in the | |
| * <a href="http://developer.android.com/guide/topics/fundamentals/services.html">Services</a> | |
| * document. In order to support the lifecycle of a Service, | |
| * <code>ServiceTestCase</code> enforces this protocol: | |
| * | |
| * <ul> | |
| * <li> | |
| * The {@link #setUp()} method is called before each test method. The base implementation | |
| * gets the system context. If you override <code>setUp()</code>, you must call | |
| * <code>super.setUp()</code> as the first statement in your override. | |
| * </li> | |
| * <li> | |
| * The test case waits to call {@link android.app.Service#onCreate()} until one of your | |
| * test methods calls {@link #startService} or {@link #bindService}. This gives you an | |
| * opportunity to set up or adjust any additional framework or test logic before you test | |
| * the running service. | |
| * </li> | |
| * <li> | |
| * When one of your test methods calls {@link #startService ServiceTestCase.startService()} | |
| * or {@link #bindService ServiceTestCase.bindService()}, the test case calls | |
| * {@link android.app.Service#onCreate() Service.onCreate()} and then calls either | |
| * {@link android.app.Service#startService(Intent) Service.startService(Intent)} or | |
| * {@link android.app.Service#bindService(Intent, ServiceConnection, int) | |
| * Service.bindService(Intent, ServiceConnection, int)}, as appropriate. It also stores | |
| * values needed to track and support the lifecycle. | |
| * </li> | |
| * <li> | |
| * After each test method finishes, the test case calls the {@link #tearDown} method. This | |
| * method stops and destroys the service with the appropriate calls, depending on how the | |
| * service was started. If you override <code>tearDown()</code>, your must call the | |
| * <code>super.tearDown()</code> as the last statement in your override. | |
| * </li> | |
| * </ul> | |
| * | |
| * <p> | |
| * <strong>Dependency Injection.</strong> | |
| * A service has two inherent dependencies, its {@link android.content.Context Context} and its | |
| * associated {@link android.app.Application Application}. The ServiceTestCase framework | |
| * allows you to inject modified, mock, or isolated replacements for these dependencies, and | |
| * thus perform unit tests with controlled dependencies in an isolated environment. | |
| * </p> | |
| * <p> | |
| * By default, the test case is injected with a full system context and a generic | |
| * {@link android.test.mock.MockApplication MockApplication} object. You can inject | |
| * alternatives to either of these by invoking | |
| * {@link AndroidTestCase#setContext(Context) setContext()} or | |
| * {@link #setApplication setApplication()}. You must do this <em>before</em> calling | |
| * startService() or bindService(). The test framework provides a | |
| * number of alternatives for Context, including | |
| * {@link android.test.mock.MockContext MockContext}, | |
| * {@link android.test.RenamingDelegatingContext RenamingDelegatingContext}, | |
| * {@link android.content.ContextWrapper ContextWrapper}, and | |
| * {@link android.test.IsolatedContext}. | |
| * | |
| * @deprecated Use | |
| * <a href="{@docRoot}reference/android/support/test/rule/ServiceTestRule.html"> | |
| * ServiceTestRule</a> instead. New tests should be written using the | |
| * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>. | |
| */ | |
| @Deprecated | |
| public abstract class ServiceTestCase<T extends Service> extends AndroidTestCase { | |
| Class<T> mServiceClass; | |
| private Context mSystemContext; | |
| private Application mApplication; | |
| /** | |
| * Constructor | |
| * @param serviceClass The type of the service under test. | |
| */ | |
| public ServiceTestCase(Class<T> serviceClass) { | |
| mServiceClass = serviceClass; | |
| } | |
| private T mService; | |
| private boolean mServiceAttached = false; | |
| private boolean mServiceCreated = false; | |
| private boolean mServiceStarted = false; | |
| private boolean mServiceBound = false; | |
| private Intent mServiceIntent = null; | |
| private int mServiceId; | |
| /** | |
| * @return An instance of the service under test. This instance is created automatically when | |
| * a test calls {@link #startService} or {@link #bindService}. | |
| */ | |
| public T getService() { | |
| return mService; | |
| } | |
| /** | |
| * Gets the current system context and stores it. | |
| * | |
| * Extend this method to do your own test initialization. If you do so, you | |
| * must call <code>super.setUp()</code> as the first statement in your override. The method is | |
| * called before each test method is executed. | |
| */ | |
| @Override | |
| protected void setUp() throws Exception { | |
| super.setUp(); | |
| // get the real context, before the individual tests have a chance to muck with it | |
| mSystemContext = getContext(); | |
| } | |
| /** | |
| * Creates the service under test and attaches all injected dependencies | |
| * (Context, Application) to it. This is called automatically by {@link #startService} or | |
| * by {@link #bindService}. | |
| * If you need to call {@link AndroidTestCase#setContext(Context) setContext()} or | |
| * {@link #setApplication setApplication()}, do so before calling this method. | |
| */ | |
| protected void setupService() { | |
| mService = null; | |
| try { | |
| mService = mServiceClass.newInstance(); | |
| } catch (Exception e) { | |
| assertNotNull(mService); | |
| } | |
| if (getApplication() == null) { | |
| setApplication(new MockApplication()); | |
| } | |
| MockService.attachForTesting( | |
| mService, getContext(), mServiceClass.getName(), getApplication()); | |
| assertNotNull(mService); | |
| mServiceId = new Random().nextInt(); | |
| mServiceAttached = true; | |
| } | |
| /** | |
| * Starts the service under test, in the same way as if it were started by | |
| * {@link android.content.Context#startService(Intent) Context.startService(Intent)} with | |
| * an {@link android.content.Intent} that identifies a service. | |
| * If you use this method to start the service, it is automatically stopped by | |
| * {@link #tearDown}. | |
| * | |
| * @param intent An Intent that identifies a service, of the same form as the Intent passed to | |
| * {@link android.content.Context#startService(Intent) Context.startService(Intent)}. | |
| */ | |
| protected void startService(Intent intent) { | |
| if (!mServiceAttached) { | |
| setupService(); | |
| } | |
| assertNotNull(mService); | |
| if (!mServiceCreated) { | |
| mService.onCreate(); | |
| mServiceCreated = true; | |
| } | |
| mService.onStartCommand(intent, 0, mServiceId); | |
| mServiceStarted = true; | |
| } | |
| /** | |
| * <p> | |
| * Starts the service under test, in the same way as if it were started by | |
| * {@link android.content.Context#bindService(Intent, ServiceConnection, int) | |
| * Context.bindService(Intent, ServiceConnection, flags)} with an | |
| * {@link android.content.Intent} that identifies a service. | |
| * </p> | |
| * <p> | |
| * Notice that the parameters are different. You do not provide a | |
| * {@link android.content.ServiceConnection} object or the flags parameter. Instead, | |
| * you only provide the Intent. The method returns an object whose type is a | |
| * subclass of {@link android.os.IBinder}, or null if the method fails. An IBinder | |
| * object refers to a communication channel between the application and | |
| * the service. The flag is assumed to be {@link android.content.Context#BIND_AUTO_CREATE}. | |
| * </p> | |
| * <p> | |
| * See <a href="{@docRoot}guide/components/aidl.html">Designing a Remote Interface | |
| * Using AIDL</a> for more information about the communication channel object returned | |
| * by this method. | |
| * </p> | |
| * Note: To be able to use bindService in a test, the service must implement getService() | |
| * method. An example of this is in the ApiDemos sample application, in the | |
| * LocalService demo. | |
| * | |
| * @param intent An Intent object of the form expected by | |
| * {@link android.content.Context#bindService}. | |
| * | |
| * @return An object whose type is a subclass of IBinder, for making further calls into | |
| * the service. | |
| */ | |
| protected IBinder bindService(Intent intent) { | |
| if (!mServiceAttached) { | |
| setupService(); | |
| } | |
| assertNotNull(mService); | |
| if (!mServiceCreated) { | |
| mService.onCreate(); | |
| mServiceCreated = true; | |
| } | |
| // no extras are expected by unbind | |
| mServiceIntent = intent.cloneFilter(); | |
| IBinder result = mService.onBind(intent); | |
| mServiceBound = true; | |
| return result; | |
| } | |
| /** | |
| * Makes the necessary calls to stop (or unbind) the service under test, and | |
| * calls onDestroy(). Ordinarily this is called automatically (by {@link #tearDown}, but | |
| * you can call it directly from your test in order to check for proper shutdown behavior. | |
| */ | |
| protected void shutdownService() { | |
| if (mServiceStarted) { | |
| mService.stopSelf(); | |
| mServiceStarted = false; | |
| } else if (mServiceBound) { | |
| mService.onUnbind(mServiceIntent); | |
| mServiceBound = false; | |
| } | |
| if (mServiceCreated) { | |
| mService.onDestroy(); | |
| mServiceCreated = false; | |
| } | |
| } | |
| /** | |
| * <p> | |
| * Shuts down the service under test. Ensures all resources are cleaned up and | |
| * garbage collected before moving on to the next test. This method is called after each | |
| * test method. | |
| * </p> | |
| * <p> | |
| * Subclasses that override this method must call <code>super.tearDown()</code> as their | |
| * last statement. | |
| * </p> | |
| * | |
| * @throws Exception | |
| */ | |
| @Override | |
| protected void tearDown() throws Exception { | |
| shutdownService(); | |
| mService = null; | |
| // Scrub out members - protects against memory leaks in the case where someone | |
| // creates a non-static inner class (thus referencing the test case) and gives it to | |
| // someone else to hold onto | |
| scrubClass(ServiceTestCase.class); | |
| super.tearDown(); | |
| } | |
| /** | |
| * Sets the application that is used during the test. If you do not call this method, | |
| * a new {@link android.test.mock.MockApplication MockApplication} object is used. | |
| * | |
| * @param application The Application object that is used by the service under test. | |
| * | |
| * @see #getApplication() | |
| */ | |
| public void setApplication(Application application) { | |
| mApplication = application; | |
| } | |
| /** | |
| * Returns the Application object in use by the service under test. | |
| * | |
| * @return The application object. | |
| * | |
| * @see #setApplication | |
| */ | |
| public Application getApplication() { | |
| return mApplication; | |
| } | |
| /** | |
| * Returns the real system context that is saved by {@link #setUp()}. Use it to create | |
| * mock or other types of context objects for the service under test. | |
| * | |
| * @return A normal system context. | |
| */ | |
| public Context getSystemContext() { | |
| return mSystemContext; | |
| } | |
| /** | |
| * Tests that {@link #setupService()} runs correctly and issues an | |
| * {@link junit.framework.Assert#assertNotNull(String, Object)} if it does. | |
| * You can override this test method if you wish. | |
| * | |
| * @throws Exception | |
| */ | |
| public void testServiceTestCaseSetUpProperly() throws Exception { | |
| setupService(); | |
| assertNotNull("service should be launched successfully", mService); | |
| } | |
| } |