From 8213206c6ea8be2060db1ddf8acc18e9bcf06e95 Mon Sep 17 00:00:00 2001 From: Adam Momtaz Date: Mon, 24 Sep 2012 18:06:29 -0700 Subject: [PATCH] Added UiAutomator samples to SDK Verified tests are building OK. Change-Id: I838e44e84b3d9e2e503bff4ed2b95887996d8801 --- build/sdk.atree | 1 + samples/UiAutomator/Android.mk | 30 ++++ samples/UiAutomator/README | 31 ++++ samples/UiAutomator/project.properties | 1 + .../uiautomator/demos/LaunchSettings.java | 97 +++++++++++ .../uiautomator/demos/LogBuildNumber.java | 162 ++++++++++++++++++ .../uiautomator/demos/SetTwoMinuteAlarm.java | 149 ++++++++++++++++ 7 files changed, 471 insertions(+) create mode 100644 samples/UiAutomator/Android.mk create mode 100644 samples/UiAutomator/README create mode 100644 samples/UiAutomator/project.properties create mode 100644 samples/UiAutomator/src/com/android/test/uiautomator/demos/LaunchSettings.java create mode 100644 samples/UiAutomator/src/com/android/test/uiautomator/demos/LogBuildNumber.java create mode 100644 samples/UiAutomator/src/com/android/test/uiautomator/demos/SetTwoMinuteAlarm.java diff --git a/build/sdk.atree b/build/sdk.atree index e0c25bad4..fe9c3cf57 100644 --- a/build/sdk.atree +++ b/build/sdk.atree @@ -193,6 +193,7 @@ development/samples/TicTacToeLib samples/${PLATFORM_NAME}/TicTacTo development/samples/TicTacToeMain samples/${PLATFORM_NAME}/TicTacToeMain development/samples/TtsEngine samples/${PLATFORM_NAME}/TtsEngine development/samples/ToyVpn samples/${PLATFORM_NAME}/ToyVpn +development/samples/UiAutomator samples/${PLATFORM_NAME}/UiAutomator development/samples/USB/MissileLauncher samples/${PLATFORM_NAME}/USB/MissileLauncher development/samples/USB/AdbTest samples/${PLATFORM_NAME}/USB/AdbTest development/samples/VoiceRecognitionService samples/${PLATFORM_NAME}/VoiceRecognitionService diff --git a/samples/UiAutomator/Android.mk b/samples/UiAutomator/Android.mk new file mode 100644 index 000000000..54191fab8 --- /dev/null +++ b/samples/UiAutomator/Android.mk @@ -0,0 +1,30 @@ +#Copyright (C) 2012 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +local_target_dir := $(TARGET_OUT_DATA)/local/tmp +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_MODULE_TAGS := optional + +LOCAL_MODULE := uiautomator.samples + +LOCAL_JAVA_LIBRARIES := uiautomator.core + +LOCAL_MODULE_PATH := $(local_target_dir) + +include $(BUILD_JAVA_LIBRARY) diff --git a/samples/UiAutomator/README b/samples/UiAutomator/README new file mode 100644 index 000000000..d9c080954 --- /dev/null +++ b/samples/UiAutomator/README @@ -0,0 +1,31 @@ +----------- LaunchSettings Demo ----------- + + This demos how we read content-description to properly open the + All Apps view and select and application to launch. Then we will + use the package name to verify that the current window is actually + from the expected package + + To run this demo you must build it first and push it on your device: + # adb push uiautomator.samples.jar /data/local/tmp + # adb shell uiautomator runtest uiautomator.samples.jar -c com.android.test.uiautomator.demos.LaunchSettings + +---------- LogBuildNumber Demo ------------ + + This demos how we can scroll list views and verify data in list view + items. Here we do the following: + + Launch Settings + + Select the About + + Read the Build string + + To run this demo you must build it first and push it on your device: + # adb push uiautomator.samples.jar /data/local/tmp + # adb shell uiautomator runtest uiautomator.samples.jar -c com.android.test.uiautomator.demos.LogBuildNumber + +---------- SetTwoMinuteAlarm Demo --------- + + Test demonstrates using the UiAutomator APIs to set an alarm to + go off in 2 minutes + + To run this demo you must build it first and push it on your device: + # adb push uiautomator.samples.jar /data/local/tmp + # adb shell uiautomator runtest uiautomator.samples.jar -c com.android.test.uiautomator.demos.SetTwoMinuteAlarm diff --git a/samples/UiAutomator/project.properties b/samples/UiAutomator/project.properties new file mode 100644 index 000000000..86f3b8234 --- /dev/null +++ b/samples/UiAutomator/project.properties @@ -0,0 +1 @@ +target=android-16 diff --git a/samples/UiAutomator/src/com/android/test/uiautomator/demos/LaunchSettings.java b/samples/UiAutomator/src/com/android/test/uiautomator/demos/LaunchSettings.java new file mode 100644 index 000000000..112da52a3 --- /dev/null +++ b/samples/UiAutomator/src/com/android/test/uiautomator/demos/LaunchSettings.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.test.uiautomator.demos; + +import com.android.uiautomator.core.UiObject; +import com.android.uiautomator.core.UiObjectNotFoundException; +import com.android.uiautomator.core.UiScrollable; +import com.android.uiautomator.core.UiSelector; +import com.android.uiautomator.testrunner.UiAutomatorTestCase; + +/** + * This demos how we read content-description to properly open the + * All Apps view and select and application to launch. Then we will + * use the package name to verify that the current window is actually + * from the expected package + */ +public class LaunchSettings extends UiAutomatorTestCase { + + public void testDemo() throws UiObjectNotFoundException { + // Good practice to start from a common place + getUiDevice().pressHome(); + + // When we use the uiautomatorviewer in the DSK/tools we find that this + // button has the following content-description + UiObject allAppsButton = new UiObject(new UiSelector().description("Apps")); + + // ** NOTE ** + // Any operation on a UiObject that is not currently present on the display + // will result in a UiObjectNotFoundException to be thrown. If we want to + // first check if the object exists we can use allAppsButton.exists() which + // return boolean. + // ** NOTE ** + + //The operation below expects the click will result a new window. + allAppsButton.clickAndWaitForNewWindow(); + + // On the this view, we expect two tabs, one for APPS and another for + // WIDGETS. We will want to click the APPS just so we're sure apps and + // not widgets is our current display + UiObject appsTab = new UiObject(new UiSelector().text("Apps")); + + // ** NOTE ** + // The above operation assumes that there is only one text "Apps" in the + // current view. If not, then the first "Apps" is selected. But if we + // want to be certain that we click on the tab "Apps", we can use the + // uiautomatorview from the SDK/tools and see if we can further narrow the + // selector. Comment the line above and uncomment the one bellow to use + // the more specific selector. + // ** NOTE ** + + // This creates a selector hierarchy where the first selector is for the + // TabWidget and the second will search only inside the layout of TabWidget + // To use this instead, uncomment the lines bellow and comment the above appsTab + //UiSelector appsTabSelector = + // new UiSelector().className(android.widget.TabWidget.class.getName()) + // .childSelector(new UiSelector().text("Apps")); + //UiObject appsTab = new UiObject(appsTabSelector); + + + // The operation below we only cause a content change so a click() is good + appsTab.click(); + + // Since our device may have many apps on it spanning multiple views, we + // may need to scroll to find our app. Here we use UiScrollable to help. + // We declare the object with a selector to a scrollable view. Since in this + // case the firt scrollable view happens to be the one containing our apps + // list, we should be ok. + UiScrollable appViews = new UiScrollable(new UiSelector().scrollable(true)); + // swipe horizontally when searching (default is vertical) + appViews.setAsHorizontalList(); + + // the appsViews will perform horizontal scrolls to find the Settings app + UiObject settingsApp = appViews.getChildByText( + new UiSelector().className(android.widget.TextView.class.getName()), "Settings"); + settingsApp.clickAndWaitForNewWindow(); + + // create a selector for anything on the display and check if the package name + // is the expected one + UiObject settingsValidation = + new UiObject(new UiSelector().packageName("com.android.settings")); + + assertTrue("Unable to detect Settings", settingsValidation.exists()); + } +} diff --git a/samples/UiAutomator/src/com/android/test/uiautomator/demos/LogBuildNumber.java b/samples/UiAutomator/src/com/android/test/uiautomator/demos/LogBuildNumber.java new file mode 100644 index 000000000..22e4fdb57 --- /dev/null +++ b/samples/UiAutomator/src/com/android/test/uiautomator/demos/LogBuildNumber.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.test.uiautomator.demos; + +import android.util.Log; + +import com.android.uiautomator.core.UiObject; +import com.android.uiautomator.core.UiObjectNotFoundException; +import com.android.uiautomator.core.UiScrollable; +import com.android.uiautomator.core.UiSelector; +import com.android.uiautomator.testrunner.UiAutomatorTestCase; + +/** + * This demos how we can scroll list views and verify data in list view + * items. Here we do the following: + * + */ +public class LogBuildNumber extends UiAutomatorTestCase { + public static final String LOG_TAG = LogBuildNumber.class.getSimpleName(); + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + /** + * For the purpose of this demo, we're declaring the Launcher signatures here. + * It may be more appropriate to declare signatures and methods related + * to Launcher in their own reusable Launcher helper file. + */ + public static class LauncherHelper { + public static final UiSelector ALL_APPS_BUTTON = new UiSelector().description("Apps"); + public static final UiSelector LAUNCHER_CONTAINER = new UiSelector().scrollable(true); + public static final UiSelector LAUNCHER_ITEM = + new UiSelector().className(android.widget.TextView.class.getName()); + } + + /** + * For the purpose of this demo, we're declaring the Settings signatures here. + * It may be more appropriate to declare signatures and methods related + * to Settings in their own reusable Settings helper file. + */ + public static class SettingsHelper { + public static final UiSelector LIST_VIEW = + new UiSelector().className(android.widget.ListView.class.getName()); + public static final UiSelector LIST_VIEW_ITEM = + new UiSelector().className(android.widget.LinearLayout.class.getName()); + } + + /** + * Script starts here + * @throws UiObjectNotFoundException + */ + public void testDemo() throws UiObjectNotFoundException { + // The following code is documented in the LaunchSettings demo. For detailed description + // of how this code works, look at the demo LaunchSettings + + // Good to start from here + getUiDevice().pressHome(); + + // open the All Apps view + UiObject allAppsButton = new UiObject(LauncherHelper.ALL_APPS_BUTTON); + allAppsButton.click(); + + // clicking the APPS tab + UiSelector appsTabSelector = + new UiSelector().className(android.widget.TabWidget.class.getName()) + .childSelector(new UiSelector().text("Apps")); + UiObject appsTab = new UiObject(appsTabSelector); + appsTab.click(); + + // Clicking the Settings + UiScrollable allAppsScreen = new UiScrollable(LauncherHelper.LAUNCHER_CONTAINER); + allAppsScreen.setAsHorizontalList(); + UiObject settingsApp = + allAppsScreen.getChildByText(LauncherHelper.LAUNCHER_ITEM, "Settings"); + settingsApp.click(); + + // Now we will select the settings we need to work with. To make this operation a little + // more generic we will put it in a function. We will try it as a phone first then as a + // tablet if phone is not our device type + if (!selectSettingsFor("About phone")) + selectSettingsFor("About tablet"); + + // Now we need to read the Build number text and return it + String buildNum = getAboutItem("Build number"); + + // Log it - Use adb logcat to view the results + Log.i(LOG_TAG, "Build = " + buildNum); + } + + /** + * Select a settings items and perform scroll if needed to find it. + * @param name + */ + private boolean selectSettingsFor(String name) { + try { + UiScrollable appsSettingsList = new UiScrollable(SettingsHelper.LIST_VIEW); + UiObject obj = appsSettingsList.getChildByText(SettingsHelper.LIST_VIEW_ITEM, name); + obj.click(); + } catch (UiObjectNotFoundException e) { + return false; + } + return true; + } + + /** + * This function will detect the presence of 2 or 1 list view display fragments and + * targets the correct list view for the About item details + * @param item + * @return the details string of an about item entry + * @throws UiObjectNotFoundException + */ + private String getAboutItem(String item) throws UiObjectNotFoundException { + // try accessing the second list view if one exists else we will assume the + // device is displaying a single list view + UiScrollable aboutSettingsList = new UiScrollable(SettingsHelper.LIST_VIEW.instance(1)); + if (!aboutSettingsList.exists()) + aboutSettingsList = new UiScrollable(SettingsHelper.LIST_VIEW.instance(0)); + + // the returned aboutItem will be pointing at the node matching the + // SettingsOsr.LIST_VIEW_ITEM. So, aboutItem is a container of widgets where one + // actually contains the text (item) we're looking for. + UiObject aboutItem = aboutSettingsList.getChildByText(SettingsHelper.LIST_VIEW_ITEM, item); + + // Since aboutItem contains the text widgets for the requested details, we're assuming + // here that the param 'item' refers to the label and the second text is the value for it. + UiObject txt = aboutItem.getChild( + new UiSelector().className(android.widget.TextView.class.getName()).instance(1)); + + // This is interesting. Since aboutItem is returned pointing a the layout containing the + // test values, we know it is visible else an exception would've been thrown. However, + // we're not certain that the instance(1) or second text view inside this layout is + // in fact fully visible and not off the screen. + if (!txt.exists()) + aboutSettingsList.scrollForward(); // scroll it into view + + return txt.getText(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } +} diff --git a/samples/UiAutomator/src/com/android/test/uiautomator/demos/SetTwoMinuteAlarm.java b/samples/UiAutomator/src/com/android/test/uiautomator/demos/SetTwoMinuteAlarm.java new file mode 100644 index 000000000..6f970a7d5 --- /dev/null +++ b/samples/UiAutomator/src/com/android/test/uiautomator/demos/SetTwoMinuteAlarm.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.test.uiautomator.demos; + +import android.widget.TextView; + +import com.android.uiautomator.core.UiObject; +import com.android.uiautomator.core.UiObjectNotFoundException; +import com.android.uiautomator.core.UiScrollable; +import com.android.uiautomator.core.UiSelector; +import com.android.uiautomator.testrunner.UiAutomatorTestCase; + +/** + * Test demonstrates using the UiAutomator APIs to set an alarm to + * go off in 2 minutes + */ +public class SetTwoMinuteAlarm extends UiAutomatorTestCase { + + /** + * For the purpose of this demo, we're declaring the Launcher signatures here. + * It may be more appropriate to declare signatures and methods related + * to Launcher in their own reusable Launcher helper file. + */ + public static class LauncherHelper { + public static final UiSelector ALL_APPS_BUTTON = new UiSelector().description("Apps"); + public static final UiSelector LAUNCHER_CONTAINER = new UiSelector().scrollable(true); + public static final UiSelector LAUNCHER_ITEM = + new UiSelector().className(android.widget.TextView.class.getName()); + } + + /** + * Set an alarm 2 minutes from now and verify it goes off. Also check the notification + * shades for an alarm. (Crude test but it demos the use of Android resources) + * @throws UiObjectNotFoundException + */ + public void testDemo() throws UiObjectNotFoundException { + // The following code is documented in the LaunchSettings demo. For detailed description + // of how this code works, look at the demo LaunchSettings + + // Good to start from here + getUiDevice().pressHome(); + + // open the All Apps view + UiObject allAppsButton = new UiObject(LauncherHelper.ALL_APPS_BUTTON); + allAppsButton.click(); + + // clicking the APPS tab + UiSelector appsTabSelector = + new UiSelector().className(android.widget.TabWidget.class.getName()) + .childSelector(new UiSelector().text("Apps")); + UiObject appsTab = new UiObject(appsTabSelector); + appsTab.click(); + + // Clicking the Settings + UiScrollable allAppsScreen = new UiScrollable(LauncherHelper.LAUNCHER_CONTAINER); + allAppsScreen.setAsHorizontalList(); + UiObject clockApp = + allAppsScreen.getChildByText(LauncherHelper.LAUNCHER_ITEM, "Clock"); + clockApp.click(); + + // Set an alarm to go off in about 2 minutes + setAlarm(2); + + // wait for the alarm alert dialog + UiObject alarmAlert = + new UiObject(new UiSelector().packageName("com.google.android.deskclock") + .className(TextView.class.getName()).text("Alarm")); + + assertTrue("Timeout while waiting for alarm to go off", + alarmAlert.waitForExists(2 * 60 * 1000)); + + clickByText("Dismiss"); + } + + /** + * Helper function to set an alarm + * @param minutesFromNow + * @throws UiObjectNotFoundException + */ + private void setAlarm(int minutesFromNow) throws UiObjectNotFoundException { + UiObject setAlarm = new UiObject(new UiSelector().textStartsWith("Alarm set")); + if (!setAlarm.exists()) + setAlarm = new UiObject(new UiSelector().textStartsWith("Set alarm")); + setAlarm.click(); + + // let's add an alarm + clickByDescription("Add alarm"); + // let's set the time + clickByText("Time"); + + // we want the minutes only + UiSelector minuteAreaSelector = new UiSelector().className( + android.widget.NumberPicker.class.getName()).instance(1); + UiSelector minuteIncreaseButtonSelector = minuteAreaSelector.childSelector( + new UiSelector().className(android.widget.Button.class.getName()).instance(1)); + + // increment minutes a couple of times + for (int x = 0; x < minutesFromNow; x++) + new UiObject(minuteIncreaseButtonSelector).click(); + clickByText("Done"); + + // few confirmations to click thru + UiObject doneButton = new UiObject(new UiSelector().text("Done")); + UiObject okButton = new UiObject(new UiSelector().text("OK")); + // working around some inconsistencies in phone vs tablet UI + if (doneButton.exists()) { + doneButton.click(); + } else { + okButton.click(); // let it fail if neither exists + } + + // we're done. Let's return to home screen + clickByText("Done"); + getUiDevice().pressHome(); + } + + /** + * Helper to click on objects that match the content-description text + * @param text + * @throws UiObjectNotFoundException + */ + private void clickByDescription(String text) throws UiObjectNotFoundException { + UiObject obj = new UiObject(new UiSelector().description(text)); + obj.clickAndWaitForNewWindow(); + } + + /** + * Helper to click on object that match the text value + * @param text + * @throws UiObjectNotFoundException + */ + private void clickByText(String text) throws UiObjectNotFoundException { + UiObject obj = new UiObject(new UiSelector().text(text)); + obj.clickAndWaitForNewWindow(); + } +} \ No newline at end of file