diff options
author | Vadim Tryshev <vadimt@google.com> | 2018-05-31 14:33:09 -0700 |
---|---|---|
committer | Vadim Tryshev <vadimt@google.com> | 2018-06-18 14:29:34 -0700 |
commit | 5a7d701d555c433a945ee102e94f431eb9c53767 (patch) | |
tree | 85bc1e538a2f51179b6887d945e2ec49338c648e | |
parent | 3871e7268607f446ed25bf475d13e230e51c64c3 (diff) | |
download | android_packages_apps_Trebuchet-5a7d701d555c433a945ee102e94f431eb9c53767.tar.gz android_packages_apps_Trebuchet-5a7d701d555c433a945ee102e94f431eb9c53767.tar.bz2 android_packages_apps_Trebuchet-5a7d701d555c433a945ee102e94f431eb9c53767.zip |
UNREVIEWED: TAPL prototype for in-lab flake test
These sources didn't pass a normal CR, and mustn't be used in prod. The
goal of landing them is to start in-lab flakiness tests ASAP. A normal
code review will follow after landing cleanup CLs.
Bug: 110103162
Test: Running TaplTests suite
Change-Id: I051591c9af7822d4cbae4261e66563f1ccc1cfcc
-rw-r--r-- | tests/tapl/com/android/launcher3/tapl/AllAppsFromHome.java | 142 | ||||
-rw-r--r-- | tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java | 63 | ||||
-rw-r--r-- | tests/tapl/com/android/launcher3/tapl/AppIcon.java | 53 | ||||
-rw-r--r-- | tests/tapl/com/android/launcher3/tapl/Home.java | 187 | ||||
-rw-r--r-- | tests/tapl/com/android/launcher3/tapl/Launcher.java | 303 | ||||
-rw-r--r-- | tests/tapl/com/android/launcher3/tapl/Overview.java | 111 | ||||
-rw-r--r-- | tests/tapl/com/android/launcher3/tapl/OverviewTask.java | 65 | ||||
-rw-r--r-- | tests/tapl/com/android/launcher3/tapl/Widgets.java | 65 |
8 files changed, 989 insertions, 0 deletions
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromHome.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromHome.java new file mode 100644 index 000000000..02f8183c8 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromHome.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2018 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.launcher3.tapl; + +import android.support.annotation.NonNull; +import android.support.test.uiautomator.BySelector; +import android.support.test.uiautomator.Direction; +import android.support.test.uiautomator.UiObject2; + +/** + * Operations on AllApps opened from Home. + */ +public final class AllAppsFromHome { + private static final int MAX_SCROLL_ATTEMPTS = 40; + private static final int MIN_INTERACT_SIZE = 100; + private static final int FLING_SPEED = 12000; + + private final Launcher mLauncher; + private final int mHeight; + + AllAppsFromHome(Launcher launcher) { + mLauncher = launcher; + final UiObject2 allAppsContainer = assertState(); + mHeight = allAppsContainer.getVisibleBounds().height(); + } + + /** + * Asserts that we are in all apps. + * + * @return All apps container. + */ + @NonNull + private UiObject2 assertState() { + return mLauncher.assertState(Launcher.State.ALL_APPS); + } + + /** + * Finds an icon. Fails if the icon doesn't exist. Scrolls the app list when needed to make + * sure the icon is visible. + * + * @param appName name of the app. + * @return The app. + */ + @NonNull + public AppIcon getAppIcon(String appName) { + final UiObject2 allAppsContainer = assertState(); + final BySelector appIconSelector = AppIcon.getAppIconSelector(appName); + if (!allAppsContainer.hasObject(appIconSelector)) { + scrollBackToBeginning(); + int attempts = 0; + while (!allAppsContainer.hasObject(appIconSelector) && + allAppsContainer.scroll(Direction.DOWN, 0.8f)) { + mLauncher.assertTrue("Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS, + ++attempts <= MAX_SCROLL_ATTEMPTS); + assertState(); + } + } + assertState(); + + final UiObject2 appIcon = mLauncher.getObjectInContainer(allAppsContainer, appIconSelector); + ensureIconVisible(appIcon, allAppsContainer); + return new AppIcon(mLauncher, appIcon); + } + + private void scrollBackToBeginning() { + final UiObject2 allAppsContainer = assertState(); + + int attempts = 0; + allAppsContainer.setGestureMargins(5, 500, 5, 5); + + while (allAppsContainer.scroll(Direction.UP, 0.5f)) { + mLauncher.waitForIdle(); + assertState(); + + mLauncher.assertTrue("Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS, + ++attempts <= MAX_SCROLL_ATTEMPTS); + } + + mLauncher.waitForIdle(); + assertState(); + } + + private void ensureIconVisible(UiObject2 appIcon, UiObject2 allAppsContainer) { + final int appHeight = appIcon.getVisibleBounds().height(); + if (appHeight < MIN_INTERACT_SIZE) { + // Try to figure out how much percentage of the container needs to be scrolled in order + // to reveal the app icon to have the MIN_INTERACT_SIZE + final float pct = Math.max(((float) (MIN_INTERACT_SIZE - appHeight)) / mHeight, 0.2f); + allAppsContainer.scroll(Direction.DOWN, pct); + mLauncher.waitForIdle(); + assertState(); + } + } + + /** + * Flings forward (down) and waits the fling's end. + */ + public void flingForward() { + final UiObject2 allAppsContainer = assertState(); + // Start the gesture in the center to avoid starting at elements near the top. + allAppsContainer.setGestureMargins(0, 0, 0, mHeight / 2); + allAppsContainer.fling(Direction.DOWN, FLING_SPEED); + assertState(); + } + + /** + * Flings backward (up) and waits the fling's end. + */ + public void flingBackward() { + final UiObject2 allAppsContainer = assertState(); + // Start the gesture in the center, for symmetry with forward. + allAppsContainer.setGestureMargins(0, mHeight / 2, 0, 0); + allAppsContainer.fling(Direction.UP, FLING_SPEED); + assertState(); + } + + /** + * Gets the UI object for AllApps. + * Used by NexusLauncherStrategy.openAllApps(). No one else should use it. + * + * @return container object. + */ + @Deprecated + @NonNull + public UiObject2 getObjectDeprecated() { + return assertState(); + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java new file mode 100644 index 000000000..cba708621 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2018 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.launcher3.tapl; + +import android.graphics.Point; +import android.support.annotation.NonNull; +import android.support.test.uiautomator.UiObject2; + +/** + * Operations on AllApps opened from Overview. + * Scroll gestures that are OK for {@link AllAppsFromHome} may close it, so they are not supported. + */ +public final class AllAppsFromOverview { + private final Launcher mLauncher; + + AllAppsFromOverview(Launcher launcher) { + mLauncher = launcher; + assertState(); + } + + /** + * Asserts that we are in all apps. + * + * @return All apps container. + */ + @NonNull + private UiObject2 assertState() { + return mLauncher.assertState(Launcher.State.ALL_APPS); + } + + /** + * Swipes down to switch back to Overview whence we came from. + * + * @return the overview panel. + */ + @NonNull + public Overview switchBackToOverview() { + final UiObject2 allAppsContainer = assertState(); + // Swipe from the search box to the bottom. + final UiObject2 qsb = mLauncher.waitForObjectInContainer( + allAppsContainer, "search_container_all_apps"); + final Point start = qsb.getVisibleCenter(); + final int endY = (int) (mLauncher.getDevice().getDisplayHeight() * 0.6); + mLauncher.swipe(start.x, start.y, start.x, endY, (endY - start.y) / 100); // 100 px/step + + return new Overview(mLauncher); + } + +} diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java new file mode 100644 index 000000000..73a74f2dd --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 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.launcher3.tapl; + +import android.support.test.uiautomator.By; +import android.support.test.uiautomator.BySelector; +import android.support.test.uiautomator.UiObject2; +import android.support.test.uiautomator.Until; +import android.widget.TextView; + +/** + * App icon, whether in all apps or in workspace/ + */ +public final class AppIcon { + private final Launcher mLauncher; + private final UiObject2 mIcon; + + AppIcon(Launcher launcher, UiObject2 icon) { + mLauncher = launcher; + mIcon = icon; + } + + static BySelector getAppIconSelector(String appName) { + return By.clazz(TextView.class).text(appName).pkg(Launcher.LAUNCHER_PKG); + } + + /** + * Clicks the icon to launch its app. + */ + public void launch() { + mLauncher.assertTrue("Launching an app didn't open a new window: " + mIcon.getText(), + mIcon.clickAndWait(Until.newWindow(), Launcher.APP_LAUNCH_TIMEOUT_MS)); + mLauncher.assertState(Launcher.State.BACKGROUND); + } + + UiObject2 getIcon() { + return mIcon; + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/Home.java b/tests/tapl/com/android/launcher3/tapl/Home.java new file mode 100644 index 000000000..0ec1a644e --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/Home.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2018 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.launcher3.tapl; + +import static junit.framework.TestCase.assertTrue; + +import android.graphics.Point; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.test.uiautomator.Direction; +import android.support.test.uiautomator.UiObject2; +import android.view.KeyEvent; + +/** + * Operations on the home screen. + */ +public final class Home { + + private final Launcher mLauncher; + private final UiObject2 mHotseat; + private final int ICON_DRAG_SPEED = 2000; + + Home(Launcher launcher) { + mLauncher = launcher; + assertState(); + mHotseat = launcher.waitForLauncherObject("hotseat"); + } + + /** + * Asserts that we are in home. + * + * @return Workspace. + */ + @NonNull + private UiObject2 assertState() { + return mLauncher.assertState(Launcher.State.HOME); + } + + /** + * Swipes up or presses the square button to switch to Overview. + * + * @return the Overview panel object. + */ + @NonNull + public Overview switchToOverview() { + assertState(); + if (mLauncher.isSwipeUpEnabled()) { + final int height = mLauncher.getDevice().getDisplayHeight(); + final UiObject2 navBar = mLauncher.getSystemUiObject("navigation_bar_frame"); + + // Swipe from nav bar to 2/3rd down the screen. + mLauncher.swipe( + navBar.getVisibleBounds().centerX(), navBar.getVisibleBounds().centerY(), + navBar.getVisibleBounds().centerX(), height * 2 / 3, + (navBar.getVisibleBounds().centerY() - height * 2 / 3) / 100); // 100 px/step + } else { + mLauncher.getSystemUiObject("recent_apps").click(); + } + + return new Overview(mLauncher); + } + + /** + * Swipes up to All Apps. + * + * @return the App Apps object. + */ + @NonNull + public AllAppsFromHome switchToAllApps() { + assertState(); + if (mLauncher.isSwipeUpEnabled()) { + int midX = mLauncher.getDevice().getDisplayWidth() / 2; + int height = mLauncher.getDevice().getDisplayHeight(); + // Swipe from 6/7ths down the screen to 1/7th down the screen. + mLauncher.swipe( + midX, + height * 6 / 7, + midX, + height / 7, + (height * 2 / 3) / 100); // 100 px/step + } else { + // Swipe from the hotseat to near the top, e.g. 10% of the screen. + final UiObject2 hotseat = mHotseat; + final Point start = hotseat.getVisibleCenter(); + final int endY = (int) (mLauncher.getDevice().getDisplayHeight() * 0.1f); + mLauncher.swipe( + start.x, + start.y, + start.x, + endY, + (start.y - endY) / 100); // 100 px/step + } + + return new AllAppsFromHome(mLauncher); + } + + /** + * Returns an icon for the app, if currently visible. + * + * @param appName name of the app + * @return app icon, if found, null otherwise. + */ + @Nullable + public AppIcon tryGetWorkspaceAppIcon(String appName) { + final UiObject2 workspace = assertState(); + final UiObject2 icon = workspace.findObject(AppIcon.getAppIconSelector(appName)); + return icon != null ? new AppIcon(mLauncher, icon) : null; + } + + /** + * Ensures that workspace is scrollable. If it's not, drags an icon icons from hotseat to the + * second screen. + */ + public void ensureWorkspaceIsScrollable() { + final UiObject2 workspace = assertState(); + if (!isWorkspaceScrollable(workspace)) { + dragIconToNextScreen(getHotseatAppIcon("Messages"), workspace); + } + assertTrue("Home screen workspace didn't become scrollable", + isWorkspaceScrollable(workspace)); + } + + private boolean isWorkspaceScrollable(UiObject2 workspace) { + return workspace.isScrollable(); + } + + @NonNull + private AppIcon getHotseatAppIcon(String appName) { + return new AppIcon(mLauncher, mLauncher.getObjectInContainer( + mHotseat, AppIcon.getAppIconSelector(appName))); + } + + private void dragIconToNextScreen(AppIcon app, UiObject2 workspace) { + final Point dest = new Point( + mLauncher.getDevice().getDisplayWidth(), workspace.getVisibleBounds().centerY()); + app.getIcon().drag(dest, ICON_DRAG_SPEED); + assertState(); + } + + /** + * Flings to get to screens on the right. Waits for scrolling and a possible overscroll + * recoil to complete. + */ + public void flingForward() { + final UiObject2 workspace = assertState(); + workspace.fling(Direction.RIGHT); + mLauncher.waitForIdle(); + assertState(); + } + + /** + * Flings to get to screens on the left. Waits for scrolling and a possible overscroll + * recoil to complete. + */ + public void flingBackward() { + final UiObject2 workspace = assertState(); + workspace.fling(Direction.LEFT); + mLauncher.waitForIdle(); + assertState(); + } + + /** + * Opens widgets container by pressing Ctrl+W. + * + * @return the widgets container. + */ + @NonNull + public Widgets openAllWidgets() { + assertState(); + mLauncher.getDevice().pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON); + return new Widgets(mLauncher); + } +}
\ No newline at end of file diff --git a/tests/tapl/com/android/launcher3/tapl/Launcher.java b/tests/tapl/com/android/launcher3/tapl/Launcher.java new file mode 100644 index 000000000..5201dc8ba --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/Launcher.java @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2018 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.launcher3.tapl; + +import static com.android.systemui.shared.system.SettingsCompat.SWIPE_UP_SETTING_NAME; + +import android.content.res.Resources; +import android.os.RemoteException; +import android.provider.Settings; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.test.InstrumentationRegistry; +import android.support.test.uiautomator.By; +import android.support.test.uiautomator.BySelector; +import android.support.test.uiautomator.UiDevice; +import android.support.test.uiautomator.UiObject2; +import android.support.test.uiautomator.Until; +import android.util.Log; + +import org.junit.Assert; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * The main tapl object. The only object that can be explicitly constructed by the using code. It + * produces all other objects. + */ +public final class Launcher { + + private static final String WORKSPACE_RES_ID = "workspace"; + private static final String APPS_RES_ID = "apps_view"; + private static final String OVERVIEW_RES_ID = "overview_panel"; + private static final String WIDGETS_RES_ID = "widgets_list_view"; + + enum State {HOME, ALL_APPS, OVERVIEW, WIDGETS, BACKGROUND} + + static final String LAUNCHER_PKG = "com.google.android.apps.nexuslauncher"; + static final int APP_LAUNCH_TIMEOUT_MS = 10000; + private static final int UI_OBJECT_WAIT_TIMEOUT_MS = 10000; + private static final String SWIPE_UP_SETTING_AVAILABLE_RES_NAME = + "config_swipe_up_gesture_setting_available"; + private static final String SWIPE_UP_ENABLED_DEFAULT_RES_NAME = + "config_swipe_up_gesture_default"; + private static final String SYSTEMUI_PACKAGE = "com.android.systemui"; + private static final String TAG = "tapl.Launcher"; + private final UiDevice mDevice; + private final boolean mSwipeUpEnabled; + + /** + * Constructs the root of TAPL hierarchy. You get all other object from it. + */ + public Launcher(UiDevice device) { + mDevice = device; + final boolean swipeUpEnabledDefault = + !getSystemBooleanRes(SWIPE_UP_SETTING_AVAILABLE_RES_NAME) || + getSystemBooleanRes(SWIPE_UP_ENABLED_DEFAULT_RES_NAME); + mSwipeUpEnabled = Settings.Secure.getInt( + InstrumentationRegistry.getTargetContext().getContentResolver(), + SWIPE_UP_SETTING_NAME, + swipeUpEnabledDefault ? 1 : 0) == 1; + } + + private boolean getSystemBooleanRes(String resName) { + final Resources res = Resources.getSystem(); + final int resId = res.getIdentifier(resName, "bool", "android"); + assertTrue("Resource not found: " + resName, resId != 0); + return res.getBoolean(resId); + } + + private void dumpViewHierarchy() { + final ByteArrayOutputStream stream = new ByteArrayOutputStream(); + try { + mDevice.dumpWindowHierarchy(stream); + stream.flush(); + stream.close(); + for (String line : stream.toString().split("\\r?\\n")) { + Log.e(TAG, line.trim()); + } + } catch (IOException e) { + Log.e(TAG, "error dumping XML to logcat", e); + } + } + + void fail(String message) { + dumpViewHierarchy(); + Assert.fail(message); + } + + void assertTrue(String message, boolean condition) { + if (!condition) { + fail(message); + } + } + + void assertNotNull(String message, Object object) { + assertTrue(message, object != null); + } + + private void failEquals(String message, Object actual) { + String formatted = "Values should be different. "; + if (message != null) { + formatted = message + ". "; + } + + formatted += "Actual: " + actual; + fail(formatted); + } + + void assertNotEquals(String message, int unexpected, int actual) { + if (unexpected == actual) { + failEquals(message, actual); + } + } + + boolean isSwipeUpEnabled() { + return mSwipeUpEnabled; + } + + UiObject2 assertState(State state) { + switch (state) { + case HOME: { + //waitUntilGone(APPS_RES_ID); + waitUntilGone(OVERVIEW_RES_ID); + waitUntilGone(WIDGETS_RES_ID); + return waitForLauncherObject(WORKSPACE_RES_ID); + } + case WIDGETS: { + waitUntilGone(WORKSPACE_RES_ID); + waitUntilGone(APPS_RES_ID); + waitUntilGone(OVERVIEW_RES_ID); + return waitForLauncherObject(WIDGETS_RES_ID); + } + case ALL_APPS: { + waitUntilGone(OVERVIEW_RES_ID); + waitUntilGone(WORKSPACE_RES_ID); + waitUntilGone(WIDGETS_RES_ID); + return waitForLauncherObject(APPS_RES_ID); + } + case OVERVIEW: { + //waitForLauncherObject(APPS_RES_ID); + waitUntilGone(WORKSPACE_RES_ID); + waitUntilGone(WIDGETS_RES_ID); + return waitForLauncherObject(OVERVIEW_RES_ID); + } + case BACKGROUND: { + waitUntilGone(WORKSPACE_RES_ID); + waitUntilGone(APPS_RES_ID); + waitUntilGone(OVERVIEW_RES_ID); + waitUntilGone(WIDGETS_RES_ID); + return null; + } + default: + fail("Invalid state: " + state); + return null; + } + } + + /** + * Presses nav bar home button. + * + * @return the Home object. + */ + public Home pressHome() { + getSystemUiObject("home").click(); + return getHome(); + } + + /** + * Gets the Home object if the current state is "active home", i.e. workspace. Fails if the + * launcher is not in that state. + * + * @return Home object. + */ + @NonNull + public Home getHome() { + return new Home(this); + } + + /** + * Gets the Widgets object if the current state is showing all widgets. Fails if the launcher is + * not in that state. + * + * @return Widgets object. + */ + @NonNull + public Widgets getAllWidgets() { + return new Widgets(this); + } + + /** + * Gets the Overview object if the current state is showing the overview panel. Fails if the + * launcher is not in that state. + * + * @return Overview object. + */ + @NonNull + public Overview getOverview() { + return new Overview(this); + } + + /** + * Gets the All Apps object if the current state is showing the all apps panel. Fails if the + * launcher is not in that state. + * + * @return All Aps object. + */ + @NonNull + public AllAppsFromHome getAllApps() { + return new AllAppsFromHome(this); + } + + /** + * Gets the All Apps object if the current state is showing the all apps panel. Returns null if + * the launcher is not in that state. + * + * @return All Aps object or null. + */ + @Nullable + public AllAppsFromHome tryGetAllApps() { + return tryGetLauncherObject(APPS_RES_ID) != null ? getAllApps() : null; + } + + private void waitUntilGone(String resId) { +// assertTrue("Unexpected launcher object visible: " + resId, +// mDevice.wait(Until.gone(getLauncherObjectSelector(resId)), +// UI_OBJECT_WAIT_TIMEOUT_MS)); + } + + @NonNull + UiObject2 getSystemUiObject(String resId) { + try { + mDevice.wakeUp(); + } catch (RemoteException e) { + fail("Failed to wake up the device: " + e); + } + final UiObject2 object = mDevice.findObject(By.res(SYSTEMUI_PACKAGE, resId)); + assertNotNull("Can't find a systemui object with id: " + resId, object); + return object; + } + + @NonNull + UiObject2 getObjectInContainer(UiObject2 container, BySelector selector) { + final UiObject2 object = container.findObject(selector); + assertNotNull("Can't find an object with selector: " + selector, object); + return object; + } + + @Nullable + private UiObject2 tryGetLauncherObject(String resName) { + return mDevice.findObject(getLauncherObjectSelector(resName)); + } + + @NonNull + UiObject2 waitForObjectInContainer(UiObject2 container, String resName) { + final UiObject2 object = container.wait( + Until.findObject(getLauncherObjectSelector(resName)), + UI_OBJECT_WAIT_TIMEOUT_MS); + assertNotNull("Can find a launcher object id: " + resName + " in container: " + + container.getResourceName(), object); + return object; + } + + @NonNull + UiObject2 waitForLauncherObject(String resName) { + final UiObject2 object = mDevice.wait(Until.findObject(getLauncherObjectSelector(resName)), + UI_OBJECT_WAIT_TIMEOUT_MS); + assertNotNull("Can find a launcher object; id: " + resName, object); + return object; + } + + static BySelector getLauncherObjectSelector(String resName) { + return By.res(LAUNCHER_PKG, resName); + } + + @NonNull + UiDevice getDevice() { + return mDevice; + } + + void swipe(int startX, int startY, int endX, int endY, int steps) { + mDevice.swipe(startX, startY, endX, endY, steps); + waitForIdle(); + } + + void waitForIdle() { + mDevice.waitForIdle(); + } +}
\ No newline at end of file diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java new file mode 100644 index 000000000..225165571 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/Overview.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2018 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.launcher3.tapl; + +import android.graphics.Point; +import android.support.annotation.NonNull; +import android.support.test.uiautomator.Direction; +import android.support.test.uiautomator.UiObject2; + +import java.util.Collections; +import java.util.List; + +/** + * Overview pane. + */ +public final class Overview { + private static final int DEFAULT_FLING_SPEED = 15000; + + private final Launcher mLauncher; + + Overview(Launcher launcher) { + mLauncher = launcher; + assertState(); + } + + /** + * Asserts that we are in overview. + * + * @return Overview panel. + */ + @NonNull + private UiObject2 assertState() { + return mLauncher.assertState(Launcher.State.OVERVIEW); + } + + /** + * Flings forward (left) and waits the fling's end. + */ + public void flingForward() { + final UiObject2 overview = assertState(); + overview.fling(Direction.LEFT, DEFAULT_FLING_SPEED); + mLauncher.waitForIdle(); + assertState(); + } + + /** + * Flings backward (right) and waits the fling's end. + */ + public void flingBackward() { + final UiObject2 overview = assertState(); + overview.fling(Direction.RIGHT, DEFAULT_FLING_SPEED); + mLauncher.waitForIdle(); + assertState(); + } + + /** + * Gets the current task in the carousel, or fails if the carousel is empty. + * + * @return the task in the middle of the visible tasks list. + */ + @NonNull + public OverviewTask getCurrentTask() { + assertState(); + final List<UiObject2> taskViews = mLauncher.getDevice().findObjects( + Launcher.getLauncherObjectSelector("snapshot")); + mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size()); + + // taskViews contains up to 3 task views: the 'main' (having the widest visible + // part) one in the center, and parts of its right and left siblings. Find the + // main task view by its width. + final UiObject2 widestTask = Collections.max(taskViews, + (t1, t2) -> Integer.compare(t1.getVisibleBounds().width(), + t2.getVisibleBounds().width())); + + return new OverviewTask(mLauncher, widestTask); + } + + /** + * Swipes up to All Apps. + * + * @return the App Apps object. + */ + @NonNull + public AllAppsFromOverview switchToAllApps() { + assertState(); + + // Swipe from the hotseat to near the top, e.g. 10% of the screen. + final UiObject2 predictionRow = mLauncher.waitForLauncherObject( + "prediction_row"); + final Point start = predictionRow.getVisibleCenter(); + final int endY = (int) (mLauncher.getDevice().getDisplayHeight() * 0.1f); + mLauncher.swipe( + start.x, start.y, start.x, endY, (start.y - endY) / 100); // 100 px/step + + return new AllAppsFromOverview(mLauncher); + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java new file mode 100644 index 000000000..68d308251 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2018 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.launcher3.tapl; + +import android.support.test.uiautomator.Direction; +import android.support.test.uiautomator.UiObject2; +import android.support.test.uiautomator.Until; + +/** + * A recent task in the overview panel carousel. + */ +public final class OverviewTask { + private final Launcher mLauncher; + private final UiObject2 mTask; + + OverviewTask(Launcher launcher, UiObject2 task) { + mLauncher = launcher; + assertState(); + mTask = task; + } + + /** + * Asserts that we are in overview. + * + * @return Overview panel. + */ + private void assertState() { + mLauncher.assertState(Launcher.State.OVERVIEW); + } + + /** + * Swipes the task up. + */ + public void dismiss() { + assertState(); + // Dismiss the task via flinging it up. + mTask.fling(Direction.DOWN); + mLauncher.waitForIdle(); + } + + /** + * Clicks at the task. + */ + public void open() { + assertState(); + mLauncher.assertTrue("Launching task didn't open a new window: " + + mTask.getParent().getContentDescription(), + mTask.clickAndWait(Until.newWindow(), Launcher.APP_LAUNCH_TIMEOUT_MS)); + mLauncher.assertState(Launcher.State.BACKGROUND); + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java new file mode 100644 index 000000000..7a5198a80 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2018 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.launcher3.tapl; + +import android.support.annotation.NonNull; +import android.support.test.uiautomator.Direction; +import android.support.test.uiautomator.UiObject2; + +/** + * All widgets container. + */ +public final class Widgets { + private static final int FLING_SPEED = 12000; + + private final Launcher mLauncher; + + Widgets(Launcher launcher) { + mLauncher = launcher; + assertState(); + } + + /** + * Flings forward (down) and waits the fling's end. + */ + public void flingForward() { + final UiObject2 widgetsContainer = assertState(); + widgetsContainer.fling(Direction.DOWN, FLING_SPEED); + mLauncher.waitForIdle(); + assertState(); + } + + /** + * Flings backward (up) and waits the fling's end. + */ + public void flingBackward() { + final UiObject2 widgetsContainer = assertState(); + widgetsContainer.fling(Direction.UP, FLING_SPEED); + mLauncher.waitForIdle(); + assertState(); + } + + /** + * Asserts that we are in widgets. + * + * @return Widgets container. + */ + @NonNull + private UiObject2 assertState() { + return mLauncher.assertState(Launcher.State.WIDGETS); + } +} |