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