1/*
2 * Copyright (C) 2013 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 */
16package com.android.uiautomator.tests.cts;
17
18import android.content.Intent;
19import android.content.ComponentName;
20import android.graphics.Point;
21import android.graphics.Rect;
22import android.os.RemoteException;
23import android.os.SystemClock;
24import android.util.Log;
25
26import com.android.uiautomator.core.UiCollection;
27import com.android.uiautomator.core.UiDevice;
28import com.android.uiautomator.core.UiObject;
29import com.android.uiautomator.core.UiObjectNotFoundException;
30import com.android.uiautomator.core.UiScrollable;
31import com.android.uiautomator.core.UiSelector;
32import com.android.uiautomator.core.UiWatcher;
33import com.android.uiautomator.testrunner.UiAutomatorTestCase;
34
35import java.io.File;
36import java.io.IOException;
37
38/**
39 * Sanity test uiautomator functionality on target device.
40 */
41public class CtsUiAutomatorTest extends UiAutomatorTestCase {
42    private static final String LOG_TAG = CtsUiAutomatorTest.class.getSimpleName();
43    private static final String[] LIST_SCROLL_TESTS = new String[] {
44            "Test 17", "Test 11", "Test 20", "Test 35"
45    };
46    private static final String PKG_NAME = "com.android.uiautomator.tests.cts.testapp";
47
48    // Maximum wait for key object to become visible
49    private static final int WAIT_EXIST_TIMEOUT = 5 * 1000;
50
51    private static final String SCREEN_SHOT_FILE_PATH_NAME = "ctsScreenShot";
52
53    @Override
54    protected void setUp() throws Exception {
55        super.setUp();
56        // Make sure the test app is always running
57        UiDevice.getInstance().waitForIdle();
58        if (!new UiObject(new UiSelector().packageName(PKG_NAME)).exists()) {
59            Intent intent = new Intent(Intent.ACTION_MAIN)
60                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
61                    .setComponent(new ComponentName(PKG_NAME, PKG_NAME + ".MainActivity"));
62            getInstrumentation().getContext().startActivity(intent);
63        }
64    }
65
66    /*
67     * Items in the listScrollTests array should be spread out such that a
68     * scroll is required to reach each item at each of the far ends.
69     */
70    public void testListScrollAndSelect() throws UiObjectNotFoundException {
71        UiScrollable listView = new UiScrollable(
72                new UiSelector().className(android.widget.ListView.class.getName()));
73
74        // on single fragment display
75        if (!listView.exists())
76            UiDevice.getInstance().pressBack();
77
78        for (String test : LIST_SCROLL_TESTS) {
79            openTest(test);
80            verifyTestDetailsExists(test);
81        }
82    }
83
84    /**
85     * Test erasing of multi word text in edit field and input of new text. Test
86     * verifying input text using a complex UiSelector
87     *
88     * @throws UiObjectNotFoundException
89     */
90    public void testTextEraseAndInput() throws UiObjectNotFoundException {
91        String testText = "Android Ui Automator Input Text";
92        openTest("Test 1");
93
94        UiObject editText = new UiObject(new UiSelector().className(android.widget.EditText.class
95                .getName()));
96        editText.setText(testText);
97
98        UiObject submitButton = new UiObject(new UiSelector()
99                .className(android.widget.Button.class.getName()).clickable(true)
100                .textStartsWith("Submit"));
101        submitButton.click();
102
103        UiObject result = new UiObject(new UiSelector().className(
104                android.widget.LinearLayout.class.getName()).childSelector(
105                (new UiSelector().className(android.widget.ScrollView.class.getName())
106                        .childSelector(new UiSelector().className(android.widget.TextView.class
107                                .getName())))));
108
109        if (!testText.equals(result.getText())) {
110            throw new UiObjectNotFoundException("Test text: " + testText);
111        }
112
113        getObjectByText("OK").click();
114    }
115
116    /**
117     * Select each of the buttons by using only the content description property
118     *
119     * @throws UiObjectNotFoundException
120     */
121    public void testSelectByContentDescription() throws UiObjectNotFoundException {
122        openTest("Test 2");
123        getObjectByDescription("Button 1").click();
124        verifyDialogActionResults("Button 1");
125        getObjectByDescription("Button 2").click();
126        verifyDialogActionResults("Button 2");
127        getObjectByDescription("Button 3").click();
128        verifyDialogActionResults("Button 3");
129    }
130
131    /**
132     * Select each of the buttons by using only the text property
133     *
134     * @throws UiObjectNotFoundException
135     */
136    public void testSelectByText() throws UiObjectNotFoundException {
137        openTest("Test 2");
138        getObjectByText("Button 1").click();
139        verifyDialogActionResults("Button 1");
140        getObjectByText("Button 2").click();
141        verifyDialogActionResults("Button 2");
142        getObjectByText("Button 3").click();
143        verifyDialogActionResults("Button 3");
144    }
145
146    /**
147     * Select each of the buttons by using only the index property
148     *
149     * @throws UiObjectNotFoundException
150     */
151    public void testSelectByIndex() throws UiObjectNotFoundException {
152        openTest("Test 2");
153        getObjectByIndex(android.widget.Button.class.getName(), 0).click();
154        verifyDialogActionResults("Button 1");
155        getObjectByIndex(android.widget.Button.class.getName(), 1).click();
156        verifyDialogActionResults("Button 2");
157        getObjectByIndex(android.widget.Button.class.getName(), 2).click();
158        verifyDialogActionResults("Button 3");
159    }
160
161    /**
162     * Select each of the buttons by using only the instance number
163     *
164     * @throws UiObjectNotFoundException
165     */
166    public void testSelectByInstance() throws UiObjectNotFoundException {
167        openTest("Test 2");
168        getObjectByInstance(android.widget.Button.class.getName(), 0).click();
169        verifyDialogActionResults("Button 1");
170        getObjectByInstance(android.widget.Button.class.getName(), 1).click();
171        verifyDialogActionResults("Button 2");
172        getObjectByInstance(android.widget.Button.class.getName(), 2).click();
173        verifyDialogActionResults("Button 3");
174    }
175
176    /**
177     * Test when a node's state is changed due to an action, it is updated in the accessibility
178     * hierarchy.
179     *
180     * @throws UiObjectNotFoundException
181     */
182    public void testSelectAfterContentChanged() throws UiObjectNotFoundException {
183        openTest("Test 2");
184        UiObject dynaButton = getObjectByText("Before");
185        dynaButton.click();
186        assertTrue("Button state change is not refreshed in accessibility hierarchy",
187                getObjectByText("After").exists());
188    }
189
190    /**
191     * Test opening the options menu using the soft buttons
192     *
193     * @throws UiObjectNotFoundException
194     * @throws InterruptedException
195     * @throws IOException
196     */
197    public void testDeviceSoftKeys() throws UiObjectNotFoundException, IOException,
198            InterruptedException {
199        openTest("Test 2");
200        UiDevice device = UiDevice.getInstance();
201        device.pressMenu();
202        getObjectByText("Finish").click();
203        verifyDialogActionResults("Finish");
204
205        // Back button
206        openTest("Test 1");
207        UiObject editText = new UiObject(new UiSelector().className(android.widget.EditText.class
208                .getName()));
209        editText.setText("Android Geppetto Test Application");
210
211        UiObject submitButton = new UiObject(new UiSelector()
212                .className(android.widget.Button.class.getName()).clickable(true)
213                .textStartsWith("Submit"));
214        submitButton.click();
215
216        // Text from the popup dialog
217        UiObject result = new UiObject(new UiSelector().textContains("geppetto"));
218
219        // Back button test to dismiss the dialog
220        assertTrue("Wait for exist must return true", result.waitForExists(2000));
221        device.pressBack();
222        result.waitUntilGone(1000);
223        assertFalse("Wait for exist must return false after press back", result.exists());
224
225        // Home button test
226        openTest("Test 5");
227        String pkgName = device.getCurrentPackageName();
228        assertTrue("CTS test app must be running", pkgName.equals(PKG_NAME));
229        device.pressHome();
230        boolean gone = new UiObject(new UiSelector().packageName(PKG_NAME)).waitUntilGone(5000);
231        assertTrue("CTS test app still visble after pressing home", gone);
232    }
233
234    /**
235     * This view is in constant update generating window content changed events.
236     * The test will read the time displayed and exhaust each wait for idle
237     * timeout until it read and sets the text back into the edit field and
238     * presses submit. A dialog box should pop up with the time it took since
239     * reading the value until pressing submit.
240     *
241     * @throws UiObjectNotFoundException
242     */
243    public void testWaitForIdleTimeout() throws UiObjectNotFoundException {
244        openTest("Test 3");
245        UiObject clk = new UiObject(new UiSelector().descriptionStartsWith("Performance "));
246
247        // First default wait for idle timeout assumed to be 10 seconds
248        String txtTime = clk.getText();
249        UiObject edit = new UiObject(new UiSelector().className(android.widget.EditText.class
250                .getName()));
251
252        // Second default wait for idle timeout assumed to be 10 seconds.
253        // Total ~20.
254        edit.setText(txtTime);
255
256        // Third default wait for idle timeout assumed to be 10 seconds.
257        // Total ~30.
258        getObjectByText("Submit").click();
259
260        // The value read should have value between 30 and 60 seconds indicating
261        // that the internal default timeouts for wait-for-idle is in acceptable
262        // range.
263        UiObject readTime = new UiObject(new UiSelector().className(
264                android.widget.TextView.class.getName()).instance(1));
265        String timeDiff = readTime.getText();
266        Log.i(LOG_TAG, "Sync time: " + timeDiff);
267
268        getObjectByText("OK").click();
269
270        int totalDelay = Integer.parseInt(timeDiff);
271
272        // Cumulative waits in this test should add up to at minimum 30 seconds
273        assertFalse("Timeout for wait-for-idle is too short. Expecting minimum 30 seconds",
274                totalDelay < 30 * 1000);
275
276        // allow for tolerance in time measurements due to differences between
277        // device speeds
278        assertFalse("Timeout for wait-for-idle is too long. Expecting maximum 60 seconds",
279                totalDelay > 60 * 1000);
280    }
281
282    /**
283     * This view is in constant update generating window content changed events.
284     * This test uses the soft key presses and clicks while the background
285     * screen is constantly updating causing a constant busy state.
286     *
287     * @throws UiObjectNotFoundException
288     */
289    public void testVerifyMenuClicks() throws UiObjectNotFoundException {
290        openTest("Test 3");
291        UiDevice.getInstance().pressMenu();
292        new UiObject(new UiSelector().text("Submit")).click();
293        verifyDialogActionResults("Submit");
294        UiDevice.getInstance().pressMenu();
295        new UiObject(new UiSelector().text("Exit")).click();
296        verifyDialogActionResults("Exit");
297    }
298
299    /**
300     * Verifies swipeRight, swipeLeft and raw swipe APIs perform as expected.
301     *
302     * @throws UiObjectNotFoundException
303     */
304    public void testSwipes() throws UiObjectNotFoundException {
305        openTest("Test 4");
306        UiObject textView = new UiObject(new UiSelector().textContains("["));
307
308        textView.swipeLeft(10);
309        assertTrue("UiObject swipe left 1->2", "[ 2 ]".equals(textView.getText()));
310
311        textView.swipeLeft(10);
312        assertTrue("UiObject swipe left 2->3", "[ 3 ]".equals(textView.getText()));
313
314        textView.swipeLeft(10);
315        assertTrue("UiObject swipe left 3->4", "[ 4 ]".equals(textView.getText()));
316
317        textView.swipeRight(10);
318        assertTrue("UiObject swipe right 3<-4", "[ 3 ]".equals(textView.getText()));
319
320        textView.swipeRight(10);
321        assertTrue("UiObject swipe right 2<-3", "[ 2 ]".equals(textView.getText()));
322
323        textView.swipeRight(10);
324        assertTrue("UiObject swipe right 1<-2", "[ 1 ]".equals(textView.getText()));
325
326        Rect tb = textView.getBounds();
327        UiDevice.getInstance().swipe(tb.right - 20, tb.centerY(), tb.left + 20, tb.centerY(), 50);
328
329        SystemClock.sleep(100);
330        assertTrue("UiDevice raw swipe 1->2", "[ 2 ]".equals(textView.getText()));
331    }
332
333    /**
334     * Creates a complex selector
335     *
336     * @throws UiObjectNotFoundException
337     */
338    public void testComplexSelectors() throws UiObjectNotFoundException {
339        openTest("Test 5");
340        UiSelector frameLayout = new UiSelector().className(android.widget.FrameLayout.class
341                .getName());
342        UiSelector gridLayout = new UiSelector().className(android.widget.GridLayout.class
343                .getName());
344        UiSelector toggleButton = new UiSelector().className(android.widget.ToggleButton.class
345                .getName());
346        UiObject button = new UiObject(frameLayout.childSelector(gridLayout).childSelector(
347                toggleButton));
348
349        assertTrue("Toggle button value should be OFF", "OFF".equals(button.getText()));
350        button.click();
351        assertTrue("Toggle button value should be ON", "ON".equals(button.getText()));
352        button.click();
353        assertTrue("Toggle button value should be OFF", "OFF".equals(button.getText()));
354    }
355
356    /**
357     * Test when an object does not exist, an exception is thrown
358     * @throws UiObjectNotFoundException
359     */
360    public void testExceptionObjectNotFound() throws UiObjectNotFoundException {
361        UiSelector selector = new UiSelector().text("Nothing should be found");
362        UiSelector child = new UiSelector().className("Nothing");
363        UiObject obj = new UiObject(selector.childSelector(child));
364
365        assertFalse("Object is reported as existing", obj.exists());
366
367        try {
368            obj.click();
369        } catch (UiObjectNotFoundException e) {
370            return;
371        }
372        assertTrue("Exception not thrown for Object not found", false);
373    }
374
375    /**
376     * Verifies the UiWatcher registration and trigger function
377     *
378     * @throws UiObjectNotFoundException
379     */
380    public void testUiWatcher() throws UiObjectNotFoundException {
381        openTest("Test 5");
382        UiDevice device = UiDevice.getInstance();
383        device.registerWatcher("Artificial crash", new UiWatcher() {
384
385            @Override
386            public boolean checkForCondition() {
387                if (new UiObject(new UiSelector().packageName("android")).exists()) {
388                    try {
389                        // Expecting a localized OK button
390                        new UiObject(new UiSelector().className(
391                                android.widget.Button.class.getName()).enabled(true)).click();
392                    } catch (UiObjectNotFoundException e) {
393                    }
394                    return true;
395                }
396                return false;
397            }
398        });
399
400        // Causes a runtime exception to be thrown
401        getObjectByText("Button").click();
402
403        // Fake doing something while the exception is being displayed
404        SystemClock.sleep(2000);
405        device.runWatchers();
406        assertTrue("UiWatcher not triggered", device.hasAnyWatcherTriggered());
407    }
408
409    /**
410     * Verifies the 'checked' property of both UiSelector and UiObject
411     *
412     * @throws UiObjectNotFoundException
413     */
414    public void testSelectorChecked() throws UiObjectNotFoundException {
415        openTest("Test 5");
416        UiObject checkboxChecked = new UiObject(new UiSelector().className(
417                android.widget.CheckBox.class.getName()).checked(true));
418        UiObject checkboxNotChecked = new UiObject(new UiSelector().className(
419                android.widget.CheckBox.class.getName()).checked(false));
420
421        checkboxNotChecked.click();
422        assertTrue("Checkbox should be checked", checkboxChecked.isChecked());
423        checkboxChecked.click();
424        assertFalse("Checkbox should be unchecked", checkboxNotChecked.isChecked());
425    }
426
427    /**
428     * Verifies the 'Clickable' property of both the UiSelector and UiObject
429     *
430     * @throws UiObjectNotFoundException
431     */
432    public void testSelectorClickable() throws UiObjectNotFoundException {
433        openTest("Test 5");
434        UiSelector clickableCheckbox = new UiSelector().clickable(true).className(
435                android.widget.CheckBox.class.getName());
436        UiSelector notClickableProgress = new UiSelector().clickable(false).className(
437                android.widget.ProgressBar.class.getName());
438
439        assertTrue("Selector clickable", new UiObject(clickableCheckbox).isClickable());
440        assertFalse("Selector not clickable", new UiObject(notClickableProgress).isClickable());
441    }
442
443    /**
444     * Verifies the 'focusable' property of both UiSelector and UiObject
445     *
446     * @throws UiObjectNotFoundException
447     */
448    public void testSelectorFocusable() throws UiObjectNotFoundException {
449        openTest("Test 5");
450        UiSelector mainLayout = new UiSelector().description("Widgets Collection");
451        UiSelector focusableCheckbox = mainLayout.childSelector(new UiSelector().className(
452                android.widget.CheckBox.class.getName()).focusable(true));
453        UiSelector notFocusableSpinner = mainLayout.childSelector(new UiSelector().className(
454                android.widget.Spinner.class.getName()).focusable(false));
455
456        assertTrue("Selector focusable", new UiObject(focusableCheckbox).isFocusable());
457        assertFalse("Selector not focusable", new UiObject(notFocusableSpinner).isFocusable());
458    }
459
460    /**
461     * Verifies the 'DescriptionContains' property of UiSelector
462     *
463     * @throws UiObjectNotFoundException
464     */
465    public void testSelectorDescriptionContains() throws UiObjectNotFoundException {
466        openTest("Test 5");
467        UiSelector progressDescriptionContains = new UiSelector().descriptionContains("%");
468        assertTrue("Selector descriptionContains", "Progress is 50 %".equals(new UiObject(
469                progressDescriptionContains).getContentDescription()));
470    }
471
472    /**
473     * Verifies the 'DescriptionStarts' property of UiSelector
474     *
475     * @throws UiObjectNotFoundException
476     */
477    public void testSelectorDescriptionStarts() throws UiObjectNotFoundException {
478        openTest("Test 5");
479        UiSelector progressDescriptionStart = new UiSelector().descriptionStartsWith("progress");
480        assertTrue("Selector descriptionStart", "Progress is 50 %".equals(new UiObject(
481                progressDescriptionStart).getContentDescription()));
482    }
483
484    /**
485     * Verifies the 'Enabled' property of both UiSelector and UiObject
486     *
487     * @throws UiObjectNotFoundException
488     */
489    public void testSelectorEnabled() throws UiObjectNotFoundException {
490        openTest("Test 5");
491        UiSelector mainLayout = new UiSelector().description("Widgets Collection");
492        UiSelector buttonDisabled = mainLayout.childSelector(new UiSelector().className(
493                android.widget.Button.class.getName()).enabled(false));
494        UiSelector buttonEnabled = mainLayout.childSelector(new UiSelector().className(
495                android.widget.Button.class.getName()).enabled(true));
496
497        assertFalse("Selector enabled false", new UiObject(buttonDisabled).isEnabled());
498        assertTrue("Selector enabled true", new UiObject(buttonEnabled).isEnabled());
499    }
500
501    /**
502     * Verifies the UiCollection object child counting by object pattern
503     *
504     * @throws UiObjectNotFoundException
505     */
506    public void testCollectionCount() throws UiObjectNotFoundException {
507        openTest("Test 5");
508        UiCollection collection = new UiCollection(
509                new UiSelector().description("Widgets Collection"));
510        assertTrue("Collection layout not found", collection.waitForExists(WAIT_EXIST_TIMEOUT));
511
512        assertTrue("Collection count",
513                collection.getChildCount(new UiSelector().clickable(true)) == 6);
514    }
515
516    /**
517     * Verifies the UiCollection can find an object by text and returning by
518     * pattern
519     *
520     * @throws UiObjectNotFoundException
521     */
522    public void testCollectionGetChildByText() throws UiObjectNotFoundException {
523        openTest("Test 5");
524        UiCollection collection = new UiCollection(
525                new UiSelector().description("Widgets Collection"));
526        assertTrue("Collection layout not found", collection.waitForExists(WAIT_EXIST_TIMEOUT));
527
528        UiObject item = collection.getChildByText(
529                new UiSelector().className(android.widget.Button.class.getName()), "Button");
530
531        assertTrue("Collection get child by text", "Button".equals(item.getText()));
532    }
533
534    /**
535     * Verifies the UiCollection can find an object by instance and returning by
536     * pattern
537     *
538     * @throws UiObjectNotFoundException
539     */
540    public void testCollectionGetChildByInstance() throws UiObjectNotFoundException {
541        openTest("Test 5");
542        UiCollection collection = new UiCollection(
543                new UiSelector().description("Widgets Collection"));
544        assertTrue("Collection layout not found", collection.waitForExists(WAIT_EXIST_TIMEOUT));
545
546        // find the second button
547        UiObject item = collection.getChildByInstance(
548                new UiSelector().className(android.widget.Button.class.getName()), 1);
549
550        assertTrue("Collection get child by instance", "Button".equals(item.getText()));
551    }
552
553    /**
554     * Verifies the UiCollection can find an object by description and returning
555     * by pattern
556     *
557     * @throws UiObjectNotFoundException
558     */
559    public void testCollectionGetChildByDescription() throws UiObjectNotFoundException {
560        openTest("Test 5");
561        UiCollection collection = new UiCollection(
562                new UiSelector().description("Widgets Collection"));
563        assertTrue("Collection layout not found", collection.waitForExists(WAIT_EXIST_TIMEOUT));
564
565        UiObject item = collection.getChildByDescription(
566                new UiSelector().className(android.widget.Button.class.getName()),
567                "Description for Button");
568
569        assertTrue("Collection get child by description", "Button".equals(item.getText()));
570    }
571
572    /**
573     * Test Orientation APIs by causing rotations and verifying current state
574     *
575     * @throws RemoteException
576     * @throws UiObjectNotFoundException
577     * @since API Level 17
578     */
579    public void testRotation() throws RemoteException, UiObjectNotFoundException {
580        openTest("Test 5");
581        UiDevice device = UiDevice.getInstance();
582
583        device.setOrientationLeft();
584        device.waitForIdle(); // isNaturalOrientation is not waiting for idle
585        SystemClock.sleep(1000);
586        assertFalse("Device orientation should not be natural", device.isNaturalOrientation());
587
588        device.setOrientationNatural();
589        device.waitForIdle(); // isNaturalOrientation is not waiting for idle
590        SystemClock.sleep(1000);
591        assertTrue("Device orientation should be natural", device.isNaturalOrientation());
592
593        device.setOrientationRight();
594        device.waitForIdle(); // isNaturalOrientation is not waiting for idle
595        SystemClock.sleep(1000);
596        assertFalse("Device orientation should not be natural", device.isNaturalOrientation());
597
598        device.setOrientationNatural();
599    }
600
601    /**
602     * Reads the current device's product name. Since it is not possible to predetermine the
603     * would be value, the check verifies that the value is not null and not empty.
604     *
605     * @since API Level 17
606     */
607    public void testGetProductName() {
608        String name = UiDevice.getInstance().getProductName();
609        assertFalse("Product name check returned empty string", name.isEmpty());
610    }
611
612    /**
613     * Select each of the buttons by using only regex text
614     *
615     * @throws UiObjectNotFoundException
616     * @since API Level 17
617     */
618    public void testSelectByTextMatch() throws UiObjectNotFoundException {
619        openTest("Test 2");
620        getObjectByTextMatch(".*n\\s1$").click();
621        verifyDialogActionResults("Button 1");
622        getObjectByTextMatch(".*n\\s2$").click();
623        verifyDialogActionResults("Button 2");
624        getObjectByTextMatch(".*n\\s3$").click();
625        verifyDialogActionResults("Button 3");
626    }
627
628    /**
629     * Select each of the buttons by using only regex content-description
630     *
631     * @throws UiObjectNotFoundException
632     * @since API Level 17
633     */
634    public void testSelectByDescriptionMatch() throws UiObjectNotFoundException {
635        openTest("Test 2");
636        getObjectByDescriptionMatch(".*n\\s1$").click();
637        verifyDialogActionResults("Button 1");
638        getObjectByDescriptionMatch(".*n\\s2$").click();
639        verifyDialogActionResults("Button 2");
640        getObjectByDescriptionMatch(".*n\\s3$").click();
641        verifyDialogActionResults("Button 3");
642    }
643
644    /**
645     * Select each of the buttons by using only regex class name
646     *
647     * @throws UiObjectNotFoundException
648     * @since API Level 17
649     */
650    public void testSelectByClassMatch() throws UiObjectNotFoundException {
651        openTest("Test 5");
652        UiObject tgl = getObjectByClassMatch(".*ToggleButton$", 0);
653        String tglValue = tgl.getText();
654        tgl.click();
655
656        assertFalse("Matching class by Regex failed", tglValue.equals(tgl.getText()));
657    }
658
659    /**
660     * Select each of the buttons by using only class type
661     *
662     * @throws UiObjectNotFoundException
663     * @since API Level 17
664     */
665    public void testSelectByClassType() throws UiObjectNotFoundException {
666        openTest("Test 5");
667        UiObject tgl = getObjectByClass(android.widget.ToggleButton.class, 0);
668        String tglValue = tgl.getText();
669        tgl.click();
670
671        assertFalse("Matching class by class type failed", tglValue.equals(tgl.getText()));
672    }
673
674    /**
675     * Test the coordinates of 3 buttons side by side verifying vertical and
676     * horizontal coordinates.
677     *
678     * @throws UiObjectNotFoundException
679     * @since API Level 17
680     */
681    public void testGetVisibleBounds() throws UiObjectNotFoundException {
682        openTest("Test 2");
683        Rect rect1 = getObjectByText("Button 1").getVisibleBounds();
684        Rect rect2 = getObjectByText("Button 2").getVisibleBounds();
685        Rect rect3 = getObjectByText("Button 3").getVisibleBounds();
686
687        assertTrue("X coordinate check failed",
688                rect1.left < rect2.left && rect2.right < rect3.right);
689        assertTrue("Y coordinate check failed",
690                rect1.top == rect2.top && rect2.bottom == rect3.bottom);
691    }
692
693   /**
694     * Tests the LongClick functionality in the API
695     *
696     * @throws UiObjectNotFoundException
697     * @since API Level 17
698     */
699    public void testSelectorLongClickable() throws UiObjectNotFoundException {
700        openTest("Test 2");
701        getObjectByText("Button 1").longClick();
702        verifyDialogActionResults("Longclick Button 1");
703    }
704
705    /**
706     * Test the UiSelector's long-clickable property
707     *
708     * @throws UiObjectNotFoundException
709     * @since API Level 17
710     */
711    public void testSelectorLongClickableProperty() throws UiObjectNotFoundException {
712        openTest("Test 2");
713        UiObject button3 = new UiObject(new UiSelector().className(
714                android.widget.Button.class).longClickable(true).instance(2));
715        button3.longClick();
716        verifyDialogActionResults("Longclick Button 3");
717    }
718
719    /**
720     * Takes a screen shot of the current display and checks if the file is
721     * created and is not zero size.
722     *
723     * @since API Level 17
724     */
725    public void testTakeScreenShots() {
726        File storePath = getInstrumentation().getContext().getFileStreamPath(SCREEN_SHOT_FILE_PATH_NAME);
727        getUiDevice().takeScreenshot(storePath);
728
729        assertTrue("Screenshot file not detected in store", storePath.exists());
730        assertTrue("Zero size for screenshot file", storePath.length() > 0);
731    }
732
733    /**
734     * Verifies the 'Resource-Id' property of UiSelector
735     *
736     * @throws UiObjectNotFoundException
737     * @since API Level 18
738     */
739    public void testSelectorResourceId() throws UiObjectNotFoundException {
740        openTest("Test 5");
741        UiSelector toggleSelector =
742                new UiSelector().resourceId("com.android.uiautomator.tests.cts.testapp:id/test_5_toggleButton");
743        UiObject toggleButton = new UiObject(toggleSelector);
744        assertTrue("Object with selector resource-id not found", toggleButton.exists());
745        assertTrue("Incorrect object for selector resource-id returned",
746                "OFF".equals(toggleButton.getText()) || "ON".equals(toggleButton.getText()));
747    }
748
749    /**
750     * Verify the UiSelector property resourceIdMatches
751     *
752     * @throws UiObjectNotFoundException
753     * @since API Level 18
754     */
755    public void testSelectorResourceIdMatches() throws UiObjectNotFoundException {
756        openTest("Test 2");
757        new UiObject(new UiSelector().resourceIdMatches("(?i).*button.*").instance(2)).click();
758        verifyDialogActionResults("Button 3");
759        new UiObject(new UiSelector().resourceIdMatches("(?i).*button1.*")).click();
760        verifyDialogActionResults("Button 1");
761    }
762
763    /**
764     * Performs a pinch out from the center of a view to its edges and listens to
765     * the motion events to make sure the starting and ending points of both pointers
766     * are correct.
767     *
768     * @throws UiObjectNotFoundException
769     * @since API Level 18
770     */
771    public void testPinchOut() throws UiObjectNotFoundException {
772        openTest("Test 12");
773
774        UiObject screen = new UiObject(
775                new UiSelector().description("Details View"));
776
777        // get the current view dimensions
778        Rect screenRect = screen.getBounds();
779
780        // perform the pinch for 100% of the view dimensions starting form
781        // the center out to the edges.
782        screen.pinchOut(100, 30);
783
784        // dialog with the detected pointers motion coordinates is displayed.
785        UiObject results = new UiObject(new UiSelector().className(
786                android.widget.ScrollView.class).childSelector(new UiSelector().className(
787                        android.widget.TextView.class)));
788        String allPointers = results.getText();
789        new UiObject(new UiSelector().text("OK")).click(); // dismiss dialog
790
791        // parse pointer 1
792        Point p1s = parsePointerCoordinates(allPointers, 0, 0); // start
793        Point p1e = parsePointerCoordinates(allPointers, 0, 1); // end
794        // parse pointer 2
795        Point p2s = parsePointerCoordinates(allPointers, 1, 0); // start
796        Point p2e = parsePointerCoordinates(allPointers, 1, 1); // end
797
798        assertTrue("All Y axis coordinates for pointer 1 must be the same", p1s.y == p1e.y);
799        assertTrue("All Y axis coordinates for pointer 2 must be the same", p2s.y == p2e.y);
800        assertTrue("All Y axis coordinates for both pointers must be the same", p1s.y == p2s.y);
801        assertTrue("Pinch must be in center of target view", p2s.y == screenRect.centerY());
802
803        assertTrue("Touch-down X coordinate for pointer 1 is invalid",
804                withinMarginOfError(0.1f, screenRect.centerX(), p1s.x));
805
806        assertTrue("Touch-down X coordinate for pointer 2 is invalid",
807                withinMarginOfError(0.1f, screenRect.centerX(), p2s.x));
808
809        assertTrue("Touch-up X coordinate for pointer 1 is invalid",
810                withinMarginOfError(0.1f, screenRect.centerX() - screenRect.left,
811                        screenRect.centerX() - p1e.x));
812
813        assertTrue("Touch-up X coordinate for pointer 2 is invalid",
814                withinMarginOfError(0.1f, screenRect.right, p2e.x));
815    }
816
817    /**
818     * Performs a pinch in from the edges of a view to its center and listens to
819     * the motion events to make sure the starting and ending points of both pointers
820     * are correct.
821     *
822     * @throws UiObjectNotFoundException
823     * @since API Level 18
824     */
825    public void testPinchIn() throws UiObjectNotFoundException {
826        openTest("Test 12");
827
828        UiObject screen = new UiObject(
829                new UiSelector().description("Details View"));
830
831        // get the current view dimensions
832        Rect screenRect = screen.getBounds();
833
834        // perform the pinch for 100% of the view dimensions starting form
835        // the edges in towards the center.
836        screen.pinchIn(100, 30);
837
838        // dialog with the detected pointers motion coordinates is displayed.
839        UiObject results = new UiObject(new UiSelector().className(
840                android.widget.ScrollView.class).childSelector(new UiSelector().className(
841                        android.widget.TextView.class)));
842        String allPointers = results.getText();
843        new UiObject(new UiSelector().text("OK")).click(); // dismiss dialog
844
845        // parse pointer 1
846        Point p1s = parsePointerCoordinates(allPointers, 0, 0); // start
847        Point p1e = parsePointerCoordinates(allPointers, 0, 1); // end
848        // parse pointer 2
849        Point p2s = parsePointerCoordinates(allPointers, 1, 0); // start
850        Point p2e = parsePointerCoordinates(allPointers, 1, 1); // end
851
852        assertTrue("All Y axis coordinates for pointer 1 must be the same", p1s.y == p1e.y);
853        assertTrue("All Y axis coordinates for pointer 2 must be the same", p2s.y == p2e.y);
854        assertTrue("All Y axis coordinates for both pointers must be the same", p1s.y == p2s.y);
855        assertTrue("Pinch must be in center of target view", p2s.y == screenRect.centerY());
856
857        assertTrue("Touch-down X coordinate for pointer 1 is invalid",
858                withinMarginOfError(0.1f, screenRect.centerX() - screenRect.left,
859                        screenRect.centerX() -  p1s.x));
860
861        assertTrue("Touch-down X coordinate for pointer 2 is invalid",
862                withinMarginOfError(0.1f, screenRect.right, p2s.x));
863
864        assertTrue("Touch-up X coordinate for pointer 1 is invalid",
865                withinMarginOfError(0.1f, screenRect.centerX(), p1e.x));
866
867        assertTrue("Touch-up X coordinate for pointer 2 is invalid",
868                withinMarginOfError(0.1f, screenRect.centerX(), p2e.x));
869    }
870
871    /**
872     * Performs a drag and drop operation from one UiObject to another UiObject
873     *
874     * @throws UiObjectNotFoundException
875     * @since API Level 18
876     */
877    public void testDragToObject() throws UiObjectNotFoundException {
878        openTest("Test 5");
879
880        UiObject imageButton = new UiObject(new UiSelector().description("Image button"));
881        UiObject starsBar = new UiObject(new UiSelector().className(android.widget.RatingBar.class));
882
883        Rect starsBarRect = starsBar.getBounds();
884        Rect imageButtonRect = imageButton.getBounds();
885        imageButton.dragTo(starsBar, 30);
886
887        // dialog with the detected pointers motion coordinates is displayed.
888        UiObject results = new UiObject(new UiSelector().className(
889                android.widget.ScrollView.class).childSelector(new UiSelector().className(
890                        android.widget.TextView.class)));
891        String allPointers = results.getText();
892        new UiObject(new UiSelector().text("OK")).click(); // dismiss dialog
893
894        // parse pointer 1
895        Point p1s = parsePointerCoordinates(allPointers, 0, 0); // start
896        Point p1e = parsePointerCoordinates(allPointers, 0, 1); // end
897
898        assertTrue("Invalid touch starting.X reported",
899                withinMarginOfError(0.05f, imageButtonRect.centerX(), p1s.x));
900        assertTrue("Invalid touch starting.Y reported",
901                withinMarginOfError(0.05f, imageButtonRect.centerY(), p1s.y));
902        assertTrue("Invalid touch ending.X reported",
903                withinMarginOfError(0.05f, starsBarRect.centerX(), p1e.x));
904        assertTrue("Invalid touch ending.Y reported",
905                withinMarginOfError(0.05f, starsBarRect.centerY(), p1e.y));
906    }
907
908    /**
909     * Performs a drag and drop operation from one UiObject to a specified coordinates
910     *
911     * @throws UiObjectNotFoundException
912     * @since API Level 18
913     */
914   public void testDragToCoordinates() throws UiObjectNotFoundException {
915       openTest("Test 5");
916
917       UiObject imageButton = new UiObject(new UiSelector().description("Image button"));
918       UiObject starsBar = new UiObject(new UiSelector().className(android.widget.RatingBar.class));
919
920       Rect starsBarRect = starsBar.getBounds();
921       Rect imageButtonRect = imageButton.getBounds();
922       imageButton.dragTo(starsBarRect.centerX(), starsBarRect.centerY(), 30);
923
924       // dialog with the detected pointers motion coordinates is displayed.
925       UiObject results = new UiObject(new UiSelector().className(
926               android.widget.ScrollView.class).childSelector(new UiSelector().className(
927                       android.widget.TextView.class)));
928       String allPointers = results.getText();
929       new UiObject(new UiSelector().text("OK")).click(); // dismiss dialog
930
931       // parse pointer 1
932       Point p1s = parsePointerCoordinates(allPointers, 0, 0); // start
933       Point p1e = parsePointerCoordinates(allPointers, 0, 1); // end
934
935       assertTrue("Invalid touch starting.X reported",
936               withinMarginOfError(0.05f, imageButtonRect.centerX(), p1s.x));
937       assertTrue("Invalid touch starting.Y reported",
938               withinMarginOfError(0.05f, imageButtonRect.centerY(), p1s.y));
939       assertTrue("Invalid touch ending.X reported",
940               withinMarginOfError(0.05f, starsBarRect.centerX(), p1e.x));
941       assertTrue("Invalid touch ending.Y reported",
942               withinMarginOfError(0.05f, starsBarRect.centerY(), p1e.y));
943   }
944
945   /**
946    * Detect if actual value is within the allowable margin of error of the expected value.
947    *
948    * Used essentially with actual values that may vary from the expected values such in the
949    * cases of touch and pinch and touch and swipe where the starting or ending points may
950    * not exactly match the expected value.
951    *
952    * @param marginPrecent is values between 0 and 1
953    * @param expected
954    * @param actual
955    * @return true if actual is within the allowed range from expected
956    */
957   private boolean withinMarginOfError(float marginPrecent, int expected, int actual) {
958       int m = (int) (marginPrecent * expected);
959       return actual >= expected - m && actual <= expected + m;
960   }
961
962   /**
963     * Parses a string containing starting to ending coordinates of one or more pointers.
964     *
965     * @param allPointers is a raw string with coordinates from all detected pointers
966     * @param pointerNumber is the desired pointer to be parsed
967     * @param edge is the 0 for the start or 1 for the end of the swipe
968     * @return Point containing the start or end coordinates of the specified pointer number
969     */
970    private Point parsePointerCoordinates(String allPointers, int pointerNumber, int edge) {
971        String pointers[] = allPointers.split("\n");
972        String coordinates = pointers[pointerNumber].split(":")[edge];
973        String xy[] = coordinates.split(",");
974        return new Point(Integer.parseInt(xy[0]), Integer.parseInt(xy[1]));
975    }
976
977    /**
978     * Private helper to open test views. Also covers UiScrollable tests
979     *
980     * @param name
981     * @throws UiObjectNotFoundException
982     */
983    private void openTest(String name) throws UiObjectNotFoundException {
984        try {
985            UiDevice.getInstance().setOrientationNatural();
986        } catch (RemoteException e) {
987            // will catch it in its own test. For now try to put the device
988            // in its natural orientation prior to each test
989        }
990        UiScrollable listView = new UiScrollable(
991                new UiSelector().className(android.widget.ListView.class.getName()));
992
993        // on single fragment display
994        if (!listView.exists())
995            UiDevice.getInstance().pressBack();
996
997        UiObject testItem = listView.getChildByText(
998                new UiSelector().className(android.widget.TextView.class.getName()), name);
999
1000        testItem.click();
1001    }
1002
1003    private void verifyTestDetailsExists(String name) throws UiObjectNotFoundException {
1004        // verify that we're at the right test
1005        new UiObject(new UiSelector().description("Details").text(name)).getText();
1006    }
1007
1008    private UiObject getObjectByText(String txt) {
1009        return new UiObject(new UiSelector().text(txt));
1010    }
1011
1012    private UiObject getObjectByTextMatch(String regex) {
1013        return new UiObject(new UiSelector().textMatches(regex));
1014    }
1015
1016    private UiObject getObjectByDescriptionMatch(String regex) {
1017        return new UiObject(new UiSelector().descriptionMatches(regex));
1018    }
1019
1020    private UiObject getObjectByDescription(String txt) {
1021        return new UiObject(new UiSelector().description(txt));
1022    }
1023
1024    private UiObject getObjectByClassMatch(String regex, int instance) {
1025        return new UiObject(new UiSelector().classNameMatches(regex).instance(instance));
1026    }
1027
1028    private <T> UiObject getObjectByClass(Class<T> type, int instance) {
1029        return new UiObject(new UiSelector().className(type).instance(instance));
1030    }
1031
1032    private UiObject getObjectByIndex(String className, int index) {
1033        return new UiObject(new UiSelector().className(className).index(index));
1034    }
1035
1036    private UiObject getObjectByInstance(String className, int instance) {
1037        return new UiObject(new UiSelector().className(className).instance(instance));
1038    }
1039
1040    private void verifyDialogActionResults(String txt) throws UiObjectNotFoundException {
1041        if (!getObjectByText("Action results").exists() || !getObjectByText(txt).exists()) {
1042            throw new UiObjectNotFoundException(txt);
1043        }
1044        getObjectByText("OK").click();
1045    }
1046}
1047