Merge "TAPL: Verifying some interactions with system" into ub-launcher3-master
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 428e647..ab6393a 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -100,6 +100,7 @@
@PortraitLandscape
public void testOverview() throws Exception {
startTestApps();
+ // mLauncher.pressHome() also tests an important case of pressing home while in background.
Overview overview = mLauncher.pressHome().switchToOverview();
assertTrue("Launcher internal state didn't switch to Overview",
isInState(LauncherState.OVERVIEW));
diff --git a/src/com/android/launcher3/testing/TestLogging.java b/src/com/android/launcher3/testing/TestLogging.java
new file mode 100644
index 0000000..024110d
--- /dev/null
+++ b/src/com/android/launcher3/testing/TestLogging.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 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.testing;
+
+import android.util.Log;
+
+import com.android.launcher3.Utilities;
+
+public final class TestLogging {
+ public static synchronized void recordEvent(String event) {
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.TAPL_EVENTS_TAG, event);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 929315a..01c207f 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -31,6 +31,7 @@
public static final int QUICK_SWITCH_STATE_ORDINAL = 4;
public static final int ALL_APPS_STATE_ORDINAL = 5;
public static final int BACKGROUND_APP_STATE_ORDINAL = 6;
+ public static final String TAPL_EVENTS_TAG = "TaplEvents";
public static String stateOrdinalToString(int ordinal) {
switch (ordinal) {
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index ba1bfa5..bee7853 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -33,6 +33,7 @@
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
+import com.android.launcher3.testing.TestLogging;
/**
* Class to handle long-clicks on workspace items and start drag as a result.
@@ -46,6 +47,7 @@
ItemLongClickListener::onAllAppsItemLongClick;
private static boolean onWorkspaceItemLongClick(View v) {
+ TestLogging.recordEvent("onWorkspaceItemLongClick");
Launcher launcher = Launcher.getLauncher(v.getContext());
if (!canStartDrag(launcher)) return false;
if (!launcher.isInState(NORMAL) && !launcher.isInState(OVERVIEW)) return false;
@@ -75,6 +77,7 @@
}
private static boolean onAllAppsItemLongClick(View v) {
+ TestLogging.recordEvent("onAllAppsItemLongClick");
Launcher launcher = Launcher.getLauncher(v.getContext());
if (!canStartDrag(launcher)) return false;
// When we have exited all apps or are in transition, disregard long clicks
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index 66fdc94..61ce046 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -25,7 +25,6 @@
import android.graphics.PointF;
import android.graphics.Rect;
-import android.util.Log;
import android.view.GestureDetector;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
@@ -37,10 +36,9 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
-import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.views.OptionsPopupView;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -168,6 +166,7 @@
@Override
public void onLongPress(MotionEvent event) {
+ TestLogging.recordEvent("Workspace.longPress");
if (mLongPressState == STATE_REQUESTED) {
if (canHandleLongPress()) {
mLongPressState = STATE_PENDING_PARENT_INFORM;
@@ -178,9 +177,6 @@
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
Action.Direction.NONE, ContainerType.WORKSPACE,
mWorkspace.getCurrentPage());
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
- Log.d(TestProtocol.PERMANENT_DIAG_TAG, "Opening options popup on long press");
- }
OptionsPopupView.showDefaultOptions(mLauncher, mTouchDownPoint.x, mTouchDownPoint.y);
} else {
cancelLongPress();
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 6cae43d..3758cb8 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -35,6 +35,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -90,6 +91,7 @@
@Override
public boolean onLongClick(View v) {
+ TestLogging.recordEvent("Widgets.onLongClick");
if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
if (v instanceof WidgetCell) {
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 2da6344..bf68f71 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -22,10 +22,15 @@
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
+import java.util.regex.Pattern;
+
/**
* App icon, whether in all apps or in workspace/
*/
public final class AppIcon extends Launchable {
+
+ private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onAllAppsItemLongClick");
+
AppIcon(LauncherInstrumentation launcher, UiObject2 icon) {
super(launcher, icon);
}
@@ -43,6 +48,11 @@
}
@Override
+ protected void addExpectedEventsForLongClick() {
+ mLauncher.expectEvent(LONG_CLICK_EVENT);
+ }
+
+ @Override
protected String getLongPressIndicator() {
return "deep_shortcuts_container";
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
index ba9c10e..597be90 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
@@ -34,6 +34,10 @@
}
@Override
+ protected void addExpectedEventsForLongClick() {
+ }
+
+ @Override
protected String getLongPressIndicator() {
return "drop_target_bar";
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index 6881197..9327cb3 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -69,18 +69,24 @@
* Drags an object to the center of homescreen.
*/
public void dragToWorkspace() {
- final Point launchableCenter = getObject().getVisibleCenter();
- final Point displaySize = mLauncher.getRealDisplaySize();
- final int width = displaySize.x / 2;
- Workspace.dragIconToWorkspace(
- mLauncher,
- this,
- new Point(
- launchableCenter.x >= width ?
- launchableCenter.x - width / 2 : launchableCenter.x + width / 2,
- displaySize.y / 2),
- getLongPressIndicator());
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ final Point launchableCenter = getObject().getVisibleCenter();
+ final Point displaySize = mLauncher.getRealDisplaySize();
+ final int width = displaySize.x / 2;
+ addExpectedEventsForLongClick();
+ Workspace.dragIconToWorkspace(
+ mLauncher,
+ this,
+ new Point(
+ launchableCenter.x >= width
+ ? launchableCenter.x - width / 2
+ : launchableCenter.x + width / 2,
+ displaySize.y / 2),
+ getLongPressIndicator());
+ }
}
+ protected abstract void addExpectedEventsForLongClick();
+
protected abstract String getLongPressIndicator();
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 95c4997..196c6b7 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -79,6 +79,8 @@
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
@@ -92,6 +94,13 @@
private static final int GESTURE_STEP_MS = 16;
private static long START_TIME = System.currentTimeMillis();
+ static final Pattern LOG_TIME = Pattern.compile(
+ "[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]\\.[0-9][0-9][0-9]");
+
+ static final Pattern EVENT_LOG_ENTRY = Pattern.compile(
+ "(?<time>[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]\\.[0-9][0-9][0-9])"
+ + ".*" + TestProtocol.TAPL_EVENTS_TAG + ": (?<event>.*)");
+
// Types for launcher containers that the user is interacting with. "Background" is a
// pseudo-container corresponding to inactive launcher covered by another app.
public enum ContainerType {
@@ -146,6 +155,11 @@
private Consumer<ContainerType> mOnSettledStateAction;
+ // Not null when we are collecting expected events to compare with actual ones.
+ private List<Pattern> mExpectedEvents;
+
+ private String mTimeBeforeFirstLogEvent;
+
/**
* Constructs the root of TAPL hierarchy. You get all other objects from it.
*/
@@ -299,8 +313,11 @@
public void checkForAnomaly() {
final String anomalyMessage = getAnomalyMessage();
if (anomalyMessage != null) {
- failWithSystemHealth(
- "Tests are broken by a non-Launcher system error: " + anomalyMessage);
+ String message = "Tests are broken by a non-Launcher system error: " + anomalyMessage;
+ log("Hierarchy dump for: " + message);
+ dumpViewHierarchy();
+
+ Assert.fail(formatSystemHealthMessage(message));
}
}
@@ -339,7 +356,7 @@
mOnSettledStateAction = onSettledStateAction;
}
- private String getSystemHealthMessage() {
+ private String formatSystemHealthMessage(String message) {
final String testPackage = getContext().getPackageName();
mInstrumentation.getUiAutomation().grantRuntimePermission(
@@ -347,30 +364,34 @@
mInstrumentation.getUiAutomation().grantRuntimePermission(
testPackage, "android.permission.PACKAGE_USAGE_STATS");
- return mSystemHealthSupplier != null
+ final String systemHealth = mSystemHealthSupplier != null
? mSystemHealthSupplier.apply(START_TIME)
: TestHelpers.getSystemHealthMessage(getContext(), START_TIME);
+
+ if (systemHealth != null) {
+ return message
+ + ",\nperhaps linked to system health problems:\n<<<<<<<<<<<<<<<<<<\n"
+ + systemHealth + "\n>>>>>>>>>>>>>>>>>>";
+ }
+
+ return message;
}
private void fail(String message) {
checkForAnomaly();
- failWithSystemHealth("http://go/tapl : " + getContextDescription() + message +
- " (visible state: " + getVisibleStateMessage() + ")");
- }
-
- private void failWithSystemHealth(String message) {
- final String systemHealth = getSystemHealthMessage();
- if (systemHealth != null) {
- message = message
- + ", perhaps because of system health problems:\n<<<<<<<<<<<<<<<<<<\n"
- + systemHealth + "\n>>>>>>>>>>>>>>>>>>";
- }
-
+ message = "http://go/tapl : " + getContextDescription() + message
+ + " (visible state: " + getVisibleStateMessage() + ")";
log("Hierarchy dump for: " + message);
dumpViewHierarchy();
- Assert.fail(message);
+ final String eventMismatch = getEventMismatchMessage();
+
+ if (eventMismatch != null) {
+ message = message + ",\nhaving produced wrong events:\n " + eventMismatch;
+ }
+
+ Assert.fail(formatSystemHealthMessage(message));
}
private String getContextDescription() {
@@ -582,7 +603,7 @@
try (LauncherInstrumentation.Closable c = addContextLayer(action)) {
mDevice.waitForIdle();
runToState(
- () -> waitForSystemUiObject("home").click(),
+ waitForSystemUiObject("home")::click,
NORMAL_STATE_ORDINAL,
!hasLauncherObject(WORKSPACE_RES_ID)
&& (hasLauncherObject(APPS_RES_ID)
@@ -1099,4 +1120,104 @@
}
return tasks;
}
+
+ private List<String> getEvents() {
+ final ArrayList<String> events = new ArrayList<>();
+ try {
+ final String logcatTimeParameter =
+ mTimeBeforeFirstLogEvent != null ? " -t " + mTimeBeforeFirstLogEvent : "";
+ final String logcatEvents = mDevice.executeShellCommand(
+ "logcat -d --pid=" + getPid() + logcatTimeParameter
+ + " -s " + TestProtocol.TAPL_EVENTS_TAG);
+ final Matcher matcher = EVENT_LOG_ENTRY.matcher(logcatEvents);
+ while (matcher.find()) {
+ final String eventTime = matcher.group("time");
+ if (eventTime.equals(mTimeBeforeFirstLogEvent)) continue;
+
+ events.add(matcher.group("event"));
+ }
+ return events;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void startRecordingEvents() {
+ Assert.assertTrue("Already recording events", mExpectedEvents == null);
+ mExpectedEvents = new ArrayList<>();
+
+ try {
+ final String lastLogLine =
+ mDevice.executeShellCommand("logcat -d --pid=" + getPid() + " -t 1");
+ final Matcher matcher = LOG_TIME.matcher(lastLogLine);
+ mTimeBeforeFirstLogEvent = matcher.find() ? matcher.group().replaceAll(" ", "") : null;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void stopRecordingEvents() {
+ mExpectedEvents = null;
+ }
+
+ Closable eventsCheck() {
+ // Entering events check block.
+ startRecordingEvents();
+
+ return () -> {
+ // Leaving events check block.
+ if (mExpectedEvents == null) {
+ return; // There was a failure. Noo need to report another one.
+ }
+
+ // Wait until Launcher generates expected number of events.
+ final long endTime = SystemClock.uptimeMillis() + WAIT_TIME_MS;
+ while (SystemClock.uptimeMillis() < endTime
+ && getEvents().size() < mExpectedEvents.size()) {
+ SystemClock.sleep(100);
+ }
+
+ final String message = getEventMismatchMessage();
+ if (message != null) {
+ Assert.fail(formatSystemHealthMessage(
+ "http://go/tapl : unexpected event sequence: " + message));
+ }
+ };
+ }
+
+ void expectEvent(Pattern expected) {
+ if (mExpectedEvents != null) mExpectedEvents.add(expected);
+ }
+
+ private String getEventMismatchMessage() {
+ if (mExpectedEvents == null) return null;
+
+ try {
+ final List<String> actual = getEvents();
+
+ for (int i = 0; i < mExpectedEvents.size(); ++i) {
+ if (i >= actual.size()) {
+ return formatEventMismatchMessage("too few actual events", actual, i);
+ }
+ if (!mExpectedEvents.get(i).matcher(actual.get(i)).find()) {
+ return formatEventMismatchMessage("mismatched event", actual, i);
+ }
+ }
+
+ if (actual.size() > mExpectedEvents.size()) {
+ return formatEventMismatchMessage(
+ "too many actual events", actual, mExpectedEvents.size());
+ }
+ } finally {
+ stopRecordingEvents();
+ }
+
+ return null;
+ }
+
+ private String formatEventMismatchMessage(String message, List<String> actual, int position) {
+ return message + ", pos=" + position
+ + ", expected=" + mExpectedEvents
+ + ", actual" + actual;
+ }
}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java
index 1b6d8c4..e0db16d 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widget.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widget.java
@@ -18,10 +18,15 @@
import androidx.test.uiautomator.UiObject2;
+import java.util.regex.Pattern;
+
/**
* Widget in workspace or a widget list.
*/
public final class Widget extends Launchable {
+
+ private static final Pattern LONG_CLICK_EVENT = Pattern.compile("Widgets.onLongClick");
+
Widget(LauncherInstrumentation launcher, UiObject2 icon) {
super(launcher, icon);
}
@@ -30,4 +35,9 @@
protected String getLongPressIndicator() {
return "drop_target_bar";
}
+
+ @Override
+ protected void addExpectedEventsForLongClick() {
+ mLauncher.expectEvent(LONG_CLICK_EVENT);
+ }
}