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
17package android.test;
18
19import java.lang.reflect.Field;
20
21import android.app.Application;
22import android.app.Instrumentation;
23import android.content.Context;
24
25/**
26 * This test case provides a framework in which you can test Application classes in
27 * a controlled environment.  It provides basic support for the lifecycle of a
28 * Application, and hooks by which you can inject various dependencies and control
29 * the environment in which your Application is tested.
30 *
31 * <p><b>Lifecycle Support.</b>
32 * Every Application is designed to be accessed within a specific sequence of
33 * method calls (see {@link android.app.Application} for more details).
34 * In order to support the lifecycle of a Application, this test case will make the
35 * following calls at the following times.
36 *
37 * <ul><li>The test case will not call onCreate() until your test calls
38 * {@link #createApplication()}.  This gives you a chance
39 * to set up or adjust any additional framework or test logic before
40 * onCreate().</li>
41 * <li>After your test completes, the test case {@link #tearDown} method is
42 * automatically called, and it will stop & destroy your application by calling its
43 * onDestroy() method.</li>
44 * </ul>
45 *
46 * <p><b>Dependency Injection.</b>
47 * Every Application has one inherent dependency, the {@link android.content.Context Context} in
48 * which it runs.
49 * This framework allows you to inject a modified, mock, or isolated replacement for this
50 * dependencies, and thus perform a true unit test.
51 *
52 * <p>If simply run your tests as-is, your Application will be injected with a fully-functional
53 * Context.
54 * You can create and inject alternative types of Contexts by calling
55 * {@link AndroidTestCase#setContext(Context) setContext()}.  You must do this <i>before</i> calling
56 * {@link #createApplication()}.  The test framework provides a
57 * number of alternatives for Context, including {@link android.test.mock.MockContext MockContext},
58 * {@link android.test.RenamingDelegatingContext RenamingDelegatingContext}, and
59 * {@link android.content.ContextWrapper ContextWrapper}.
60 */
61public abstract class ApplicationTestCase<T extends Application> extends AndroidTestCase {
62
63    Class<T> mApplicationClass;
64
65    private Context mSystemContext;
66
67    public ApplicationTestCase(Class<T> applicationClass) {
68        mApplicationClass = applicationClass;
69    }
70
71    private T mApplication;
72    private boolean mAttached = false;
73    private boolean mCreated = false;
74
75    /**
76     * @return Returns the actual Application under test.
77     */
78    public T getApplication() {
79        return mApplication;
80    }
81
82    /**
83     * This will do the work to instantiate the Application under test.  After this, your test
84     * code must also start and stop the Application.
85     */
86    @Override
87    protected void setUp() throws Exception {
88        super.setUp();
89
90        // get the real context, before the individual tests have a chance to muck with it
91        mSystemContext = getContext();
92    }
93
94    /**
95     * Load and attach the application under test.
96     */
97    private void setupApplication() {
98        mApplication = null;
99        try {
100            mApplication = (T) Instrumentation.newApplication(mApplicationClass, getContext());
101        } catch (Exception e) {
102            assertNotNull(mApplication);
103        }
104        mAttached = true;
105    }
106
107    /**
108     * Start the Application under test, in the same way as if it was started by the system.
109     * If you use this method to start the Application, it will automatically
110     * be stopped by {@link #tearDown}.  If you wish to inject a specialized Context for your
111     * test, by calling {@link AndroidTestCase#setContext(Context) setContext()},
112     * you must do so  before calling this method.
113     */
114    final protected void createApplication() {
115        assertFalse(mCreated);
116
117        if (!mAttached) {
118            setupApplication();
119        }
120        assertNotNull(mApplication);
121
122        mApplication.onCreate();
123        mCreated = true;
124    }
125
126    /**
127     * This will make the necessary calls to terminate the Application under test (it will
128     * call onTerminate().  Ordinarily this will be called automatically (by {@link #tearDown}, but
129     * you can call it directly from your test in order to check for proper shutdown behaviors.
130     */
131    final protected void terminateApplication() {
132        if (mCreated) {
133            mApplication.onTerminate();
134        }
135    }
136
137    /**
138     * Shuts down the Application under test.  Also makes sure all resources are cleaned up and
139     * garbage collected before moving on to the next
140     * test.  Subclasses that override this method should make sure they call super.tearDown()
141     * at the end of the overriding method.
142     *
143     * @throws Exception
144     */
145    @Override
146    protected void tearDown() throws Exception {
147        terminateApplication();
148        mApplication = null;
149
150        // Scrub out members - protects against memory leaks in the case where someone
151        // creates a non-static inner class (thus referencing the test case) and gives it to
152        // someone else to hold onto
153        scrubClass(ApplicationTestCase.class);
154
155        super.tearDown();
156    }
157
158    /**
159     * Return a real (not mocked or instrumented) system Context that can be used when generating
160     * Mock or other Context objects for your Application under test.
161     *
162     * @return Returns a reference to a normal Context.
163     */
164    public Context getSystemContext() {
165        return mSystemContext;
166    }
167
168    /**
169     * This test simply confirms that the Application class can be instantiated properly.
170     *
171     * @throws Exception
172     */
173    final public void testApplicationTestCaseSetUpProperly() throws Exception {
174        setupApplication();
175        assertNotNull("Application class could not be instantiated successfully", mApplication);
176    }
177}
178