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  * <p><b>Lifecycle Support.</b>
     38  * A Service is accessed with a specific sequence of
     39  * calls, as described in the
     40  * <a href="http://developer.android.com/guide/topics/fundamentals/services.html">Services</a>
     41  * document. 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  */
     93 public 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