Home | History | Annotate | Download | only in test
      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