blob: fcb9d55e8360f69a5e3c9481161e4e2a8a79bd8d [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package android.test;
18
19import android.app.Application;
20import android.app.Service;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.os.IBinder;
25import android.os.RemoteException;
26import android.test.mock.MockApplication;
27
28import java.lang.reflect.Field;
29import 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 */
71public abstract class ServiceTestCase<T extends Service> extends AndroidTestCase {
72
73 Class<T> mServiceClass;
74
75 private Context mSystemContext;
76 private Application mApplication;
77
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 */
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
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 */
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 */
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 */
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 */
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
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 */
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 */
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 */
272 public Context getSystemContext() {
273 return mSystemContext;
274 }
275
276 public void testServiceTestCaseSetUpProperly() throws Exception {
277 setupService();
278 assertNotNull("service should be launched successfully", mService);
279 }
280}