• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
ServiceTestCase(Class<T> serviceClass)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      */
getService()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
setUp()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      */
setupService()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      */
startService(Intent intent)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      */
bindService(Intent intent)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      */
shutdownService()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
tearDown()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      */
setApplication(Application application)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      */
getApplication()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      */
getSystemContext()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      */
testServiceTestCaseSetUpProperly()335     public void testServiceTestCaseSetUpProperly() throws Exception {
336         setupService();
337         assertNotNull("service should be launched successfully", mService);
338     }
339 }
340