1/*
2 * Copyright (C) 2010 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 com.android.example.spinner.test;
18
19import com.android.example.spinner.SpinnerActivity;
20
21import android.app.Instrumentation;
22import android.test.ActivityInstrumentationTestCase2;
23import android.test.UiThreadTest;
24import android.view.KeyEvent;
25import android.widget.Spinner;
26import android.widget.SpinnerAdapter;
27import android.widget.TextView;
28
29/*
30 * Tests the example application Spinner. Uses the instrumentation test class
31 * ActivityInstrumentationTestCase2 as its base class. The tests include
32 *   - test initial conditions
33 *   - test the UI
34 *   - state management - preserving state after the app is shut down and restarted, preserving
35 *     state after the app is hidden (paused) and re-displayed (resumed)
36 *
37 * Demonstrates the use of JUnit setUp() and assert() methods.
38 */
39public class SpinnerActivityTest extends ActivityInstrumentationTestCase2<SpinnerActivity> {
40
41    // Number of items in the spinner's backing mLocalAdapter
42
43    public static final int ADAPTER_COUNT = 9;
44
45    // The location of Saturn in the backing mLocalAdapter array (0-based)
46
47    public static final int TEST_POSITION = 5;
48
49    // Set the initial position of the spinner to zero
50
51    public static final int INITIAL_POSITION = 0;
52
53    // The initial position corresponds to Mercury
54
55    public static final String INITIAL_SELECTION = "Mercury";
56
57    // Test values of position and selection for the testStateDestroy test
58
59    public static final int TEST_STATE_DESTROY_POSITION = 2;
60    public static final String TEST_STATE_DESTROY_SELECTION = "Earth";
61
62    // Test values of position and selection for the testStatePause test
63
64    public static final int TEST_STATE_PAUSE_POSITION = 4;
65    public static final String TEST_STATE_PAUSE_SELECTION = "Jupiter";
66
67    // The Application object for the application under test
68
69    private SpinnerActivity mActivity;
70
71    // String displayed in the spinner in the app under test
72
73    private String mSelection;
74
75    // The currently selected position in the spinner in the app under test
76
77    private int mPos;
78
79    /*
80     *  The Spinner object in the app under test. Used with instrumentation to control the
81     *  app under test.
82     */
83
84    private Spinner mSpinner;
85
86    /*
87     * The data backing the Spinner in the app under test.
88     */
89
90    private SpinnerAdapter mPlanetData;
91
92    /*
93     * Constructor for the test class. Required by Android test classes. The constructor
94     * must call the super constructor, providing the Android package name of the app under test
95     * and the Java class name of the activity in that application that handles the MAIN intent.
96     */
97    public SpinnerActivityTest() {
98
99        super("com.android.example.spinner", SpinnerActivity.class);
100    }
101
102    /*
103     * Sets up the test environment before each test.
104     * @see android.test.ActivityInstrumentationTestCase2#setUp()
105     */
106    @Override
107    protected void setUp() throws Exception {
108
109        /*
110         * Call the super constructor (required by JUnit)
111         */
112
113        super.setUp();
114
115        /*
116         * prepare to send key events to the app under test by turning off touch mode.
117         * Must be done before the first call to getActivity()
118         */
119
120        setActivityInitialTouchMode(false);
121
122        /*
123         * Start the app under test by starting its main activity. The test runner already knows
124         * which activity this is from the call to the super constructor, as mentioned
125         * previously. The tests can now use instrumentation to directly access the main
126         * activity through mActivity.
127         */
128        mActivity = getActivity();
129
130        /*
131         * Get references to objects in the application under test. These are
132         * tested to ensure that the app under test has initialized correctly.
133         */
134
135        mSpinner = (Spinner)mActivity.findViewById(com.android.example.spinner.R.id.Spinner01);
136
137        mPlanetData = mSpinner.getAdapter();
138
139    }
140
141    /*
142     * Tests the initial values of key objects in the app under test, to ensure the initial
143     * conditions make sense. If one of these is not initialized correctly, then subsequent
144     * tests are suspect and should be ignored.
145     */
146
147    public void testPreconditions() {
148
149        /*
150         *  An example of an initialization test. Assert that the item select listener in
151         *  the main Activity is not null (has been set to a valid callback)
152         */
153        assertTrue(mSpinner.getOnItemSelectedListener() != null);
154
155        /*
156         * Test that the spinner's backing mLocalAdapter was initialized correctly.
157         */
158
159        assertTrue(mPlanetData != null);
160
161        /*
162         *  Also ensure that the backing mLocalAdapter has the correct number of entries.
163         */
164
165        assertEquals(mPlanetData.getCount(), ADAPTER_COUNT);
166    }
167
168    /*
169     * Tests the UI of the main activity. Sends key events (keystrokes) to the UI, then checks
170     * if the resulting spinner state is consistent with the attempted selection.
171     */
172    public void testSpinnerUI() {
173
174        /*
175         * Request focus for the spinner widget in the application under test,
176         * and set its initial position. This code interacts with the app's View
177         *  so it has to run on the app's thread not the test's thread.
178         *
179         * To do this, pass the necessary code to the application with
180         * runOnUiThread(). The parameter is an anonymous Runnable object that
181         * contains the Java statements put in it by its run() method.
182         */
183        mActivity.runOnUiThread(
184            new Runnable() {
185                public void run() {
186                    mSpinner.requestFocus();
187                    mSpinner.setSelection(INITIAL_POSITION);
188                }
189            }
190        );
191
192        // Activate the spinner by clicking the center keypad key
193
194        this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
195
196        // send 5 down arrow keys to the spinner
197
198        for (int i = 1; i <= TEST_POSITION; i++) {
199
200            this.sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
201        }
202
203        // select the item at the current spinner position
204
205        this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
206
207        // get the position of the selected item
208
209        mPos = mSpinner.getSelectedItemPosition();
210
211        /*
212         * from the spinner's data mLocalAdapter, get the object at the selected position
213         * (this is a String value)
214         */
215
216        mSelection = (String)mSpinner.getItemAtPosition(mPos);
217
218        /*
219         * Get the TextView widget that displays the result of selecting an item from the spinner
220         */
221
222        TextView resultView =
223                (TextView) mActivity.findViewById(com.android.example.spinner.R.id.SpinnerResult);
224
225        // Get the String value in the EditText object
226
227        String resultText = (String) resultView.getText();
228
229        /*
230         * Confirm that the EditText contains the same value as the data in the mLocalAdapter
231         */
232
233        assertEquals(resultText,mSelection);
234    }
235
236    /*
237     *  Tests that the activity under test maintains the spinner state when the activity halts
238     *  and then restarts (for example, if the device reboots). Sets the spinner to a
239     *  certain state, calls finish() on the activity, restarts the activity, and then
240     *  checks that the spinner has the same state.
241     *
242     */
243
244    public void testStateDestroy() {
245
246        /*
247         * Set the position and value of the spinner in the Activity. The test runner's
248         * instrumentation enables this by running the test app and the main app in the same
249         * process.
250         */
251
252
253        mActivity.setSpinnerPosition(TEST_STATE_DESTROY_POSITION);
254
255        mActivity.setSpinnerSelection(TEST_STATE_DESTROY_SELECTION);
256
257        // Halt the Activity by calling Activity.finish() on it
258
259        mActivity.finish();
260
261        // Restart the activity by calling ActivityInstrumentationTestCase2.getActivity()
262
263        mActivity = this.getActivity();
264
265        /*
266         * Get the current position and selection from the activity.
267         */
268
269        int currentPosition = mActivity.getSpinnerPosition();
270        String currentSelection = mActivity.getSpinnerSelection();
271
272        // test that they are the same.
273
274        assertEquals(TEST_STATE_DESTROY_POSITION, currentPosition);
275
276        assertEquals(TEST_STATE_DESTROY_SELECTION, currentSelection);
277    }
278
279    /*
280     * Tests that the activity under test maintains the spinner's state when the activity is
281     * paused and then resumed.
282     *
283     * Calls the activity's onResume() method. Changes the spinner's state by
284     * altering the activity's View. This means the test must run
285     * on the UI Thread. All the statements in the test method may be run on
286     * that thread, so instead of using the runOnUiThread() method, the
287     * @UiThreadTest is used.
288     */
289    @UiThreadTest
290
291    public void testStatePause() {
292
293        /*
294         * Get the instrumentation object for this application. This object
295         * does all the instrumentation work for the test runner
296         */
297
298        Instrumentation instr = this.getInstrumentation();
299
300        /*
301         * Set the activity's fields for the position and value of the spinner
302         */
303
304        mActivity.setSpinnerPosition(TEST_STATE_PAUSE_POSITION);
305
306        mActivity.setSpinnerSelection(TEST_STATE_PAUSE_SELECTION);
307
308        /*
309         *  Use the instrumentation to onPause() on the currently running Activity.
310         *  This analogous to calling finish() in the testStateDestroy() method.
311         *  This way demonstrates using the test class' instrumentation.
312         */
313
314        instr.callActivityOnPause(mActivity);
315
316        /*
317         * Set the spinner to a test position
318         */
319
320        mActivity.setSpinnerPosition(0);
321
322        mActivity.setSpinnerSelection("");
323
324        /*
325         * Call the activity's onResume() method. This forces the activity
326         * to restore its state.
327         */
328
329        instr.callActivityOnResume(mActivity);
330
331        /*
332         * Get the current state of the spinner
333         */
334
335        int currentPosition = mActivity.getSpinnerPosition();
336
337        String currentSelection = mActivity.getSpinnerSelection();
338
339        assertEquals(TEST_STATE_PAUSE_POSITION,currentPosition);
340        assertEquals(TEST_STATE_PAUSE_SELECTION,currentSelection);
341  }
342
343}
344