• 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 by 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  * Every Service is designed to be accessed within a specific sequence of
39  * calls.  <insert link to Service lifecycle doc here>.
40  * In order to support the lifecycle of a Service, this test case will make the
41  * following calls at the following times.
42  *
43  * <ul><li>The test case will not call onCreate() until your test calls
44  * {@link #startService} or {@link #bindService}.  This gives you a chance
45  * to set up or adjust any additional framework or test logic before
46  * onCreate().</li>
47  * <li>When your test calls {@link #startService} or {@link #bindService}
48  * the test case will call onCreate(), and then call the corresponding entry point in your service.
49  * It will record any parameters or other support values necessary to support the lifecycle.</li>
50  * <li>After your test completes, the test case {@link #tearDown} function is
51  * automatically called, and it will stop and destroy your service with the appropriate
52  * calls (depending on how your test invoked the service.)</li>
53  * </ul>
54  *
55  * <p><b>Dependency Injection.</b>
56  * Every service has two inherent dependencies, the {@link android.content.Context Context} in
57  * which it runs, and the {@link android.app.Application Application} with which it is associated.
58  * This framework allows you to inject modified, mock, or isolated replacements for these
59  * dependencies, and thus perform a true unit test.
60  *
61  * <p>If simply run your tests as-is, your Service will be injected with a fully-functional
62  * Context, and a generic {@link android.test.mock.MockApplication MockApplication} object.
63  * You can create and inject alternatives to either of these by calling
64  * {@link AndroidTestCase#setContext(Context) setContext()} or
65  * {@link #setApplication setApplication()}.  You must do this <i>before</i> calling
66  * startService() or bindService().  The test framework provides a
67  * number of alternatives for Context, including {link android.test.mock.MockContext MockContext},
68  * {@link android.test.RenamingDelegatingContext RenamingDelegatingContext}, and
69  * {@link android.content.ContextWrapper ContextWrapper}.
70  */
71 public abstract class ServiceTestCase<T extends Service> extends AndroidTestCase {
72 
73     Class<T> mServiceClass;
74 
75     private Context mSystemContext;
76     private Application mApplication;
77 
ServiceTestCase(Class<T> serviceClass)78     public ServiceTestCase(Class<T> serviceClass) {
79         mServiceClass = serviceClass;
80     }
81 
82     private T mService;
83     private boolean mServiceAttached = false;
84     private boolean mServiceCreated = false;
85     private boolean mServiceStarted = false;
86     private boolean mServiceBound = false;
87     private Intent mServiceIntent = null;
88     private int mServiceId;
89 
90     /**
91      * @return Returns the actual service under test.
92      */
getService()93     public T getService() {
94         return mService;
95     }
96 
97     /**
98      * This will do the work to instantiate the Service under test.  After this, your test
99      * code must also start and stop the service.
100      */
101     @Override
setUp()102     protected void setUp() throws Exception {
103         super.setUp();
104 
105         // get the real context, before the individual tests have a chance to muck with it
106         mSystemContext = getContext();
107 
108     }
109 
110     /**
111      * Create the service under test and attach all injected dependencies (Context, Application) to
112      * it.  This will be called automatically by {@link #startService} or by {@link #bindService}.
113      * If you wish to call {@link AndroidTestCase#setContext(Context) setContext()} or
114      * {@link #setApplication setApplication()}, you must do so  before calling this function.
115      */
setupService()116     protected void setupService() {
117         mService = null;
118         try {
119             mService = mServiceClass.newInstance();
120         } catch (Exception e) {
121             assertNotNull(mService);
122         }
123         if (getApplication() == null) {
124             setApplication(new MockApplication());
125         }
126         mService.attach(
127                 getContext(),
128                 null,               // ActivityThread not actually used in Service
129                 mServiceClass.getName(),
130                 null,               // token not needed when not talking with the activity manager
131                 getApplication(),
132                 null                // mocked services don't talk with the activity manager
133                 );
134 
135         assertNotNull(mService);
136 
137         mServiceId = new Random().nextInt();
138         mServiceAttached = true;
139     }
140 
141     /**
142      * Start the service under test, in the same way as if it was started by
143      * {@link android.content.Context#startService Context.startService()}, providing the
144      * arguments it supplied.  If you use this method to start the service, it will automatically
145      * be stopped by {@link #tearDown}.
146      *
147      * @param intent The Intent as if supplied to {@link android.content.Context#startService}.
148      */
startService(Intent intent)149     protected void startService(Intent intent) {
150         assertFalse(mServiceStarted);
151         assertFalse(mServiceBound);
152 
153         if (!mServiceAttached) {
154             setupService();
155         }
156         assertNotNull(mService);
157 
158         if (!mServiceCreated) {
159             mService.onCreate();
160             mServiceCreated = true;
161         }
162         mService.onStart(intent, mServiceId);
163 
164         mServiceStarted = true;
165     }
166 
167     /**
168      * Start the service under test, in the same way as if it was started by
169      * {@link android.content.Context#bindService Context.bindService()}, providing the
170      * arguments it supplied.
171      *
172      * Return the communication channel to the service.  May return null if
173      * clients can not bind to the service.  The returned
174      * {@link android.os.IBinder} is usually for a complex interface
175      * that has been <a href="{@docRoot}guide/developing/tools/aidl.html">described using
176      * aidl</a>.
177      *
178      * Note:  In order to test with this interface, your service must implement a getService()
179      * method, as shown in samples.ApiDemos.app.LocalService.
180 
181      * @param intent The Intent as if supplied to {@link android.content.Context#bindService}.
182      *
183      * @return Return an IBinder for making further calls into the Service.
184      */
bindService(Intent intent)185     protected IBinder bindService(Intent intent) {
186         assertFalse(mServiceStarted);
187         assertFalse(mServiceBound);
188 
189         if (!mServiceAttached) {
190             setupService();
191         }
192         assertNotNull(mService);
193 
194         if (!mServiceCreated) {
195             mService.onCreate();
196             mServiceCreated = true;
197         }
198         // no extras are expected by unbind
199         mServiceIntent = intent.cloneFilter();
200         IBinder result = mService.onBind(intent);
201 
202         mServiceBound = true;
203         return result;
204     }
205 
206     /**
207      * This will make the necessary calls to stop (or unbind) the Service under test, and
208      * call onDestroy().  Ordinarily this will be called automatically (by {@link #tearDown}, but
209      * you can call it directly from your test in order to check for proper shutdown behaviors.
210      */
shutdownService()211     protected void shutdownService() {
212         if (mServiceStarted) {
213             mService.stopSelf();
214             mServiceStarted = false;
215         } else if (mServiceBound) {
216             mService.onUnbind(mServiceIntent);
217             mServiceBound = false;
218         }
219         if (mServiceCreated) {
220             mService.onDestroy();
221         }
222     }
223 
224     /**
225      * Shuts down the Service under test.  Also makes sure all resources are cleaned up and
226      * garbage collected before moving on to the next
227      * test.  Subclasses that override this method should make sure they call super.tearDown()
228      * at the end of the overriding method.
229      *
230      * @throws Exception
231      */
232     @Override
tearDown()233     protected void tearDown() throws Exception {
234         shutdownService();
235         mService = null;
236 
237         // Scrub out members - protects against memory leaks in the case where someone
238         // creates a non-static inner class (thus referencing the test case) and gives it to
239         // someone else to hold onto
240         scrubClass(ServiceTestCase.class);
241 
242         super.tearDown();
243     }
244 
245     /**
246      * Set the application for use during the test.  If your test does not call this function,
247      * a new {@link android.test.mock.MockApplication MockApplication} object will be generated.
248      *
249      * @param application The Application object that will be injected into the Service under test.
250      */
setApplication(Application application)251     public void setApplication(Application application) {
252         mApplication = application;
253     }
254 
255     /**
256      * Return the Application object being used by the Service under test.
257      *
258      * @return Returns the application object.
259      *
260      * @see #setApplication
261      */
getApplication()262     public Application getApplication() {
263         return mApplication;
264     }
265 
266     /**
267      * Return a real (not mocked or instrumented) system Context that can be used when generating
268      * Mock or other Context objects for your Service under test.
269      *
270      * @return Returns a reference to a normal Context.
271      */
getSystemContext()272     public Context getSystemContext() {
273         return mSystemContext;
274     }
275 
testServiceTestCaseSetUpProperly()276     public void testServiceTestCaseSetUpProperly() throws Exception {
277         setupService();
278         assertNotNull("service should be launched successfully", mService);
279     }
280 }
281