Merge "Adding tests that would reliably reproduce shifted search." into ub-launcher3-master
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
index b11260e..95be188 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -22,6 +22,8 @@
import static android.view.MotionEvent.ACTION_UP;
import static android.view.MotionEvent.INVALID_POINTER_ID;
+import static com.android.launcher3.util.RaceConditionTracker.ENTER;
+import static com.android.launcher3.util.RaceConditionTracker.EXIT;
import static com.android.systemui.shared.system.ActivityManagerWrapper
.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
@@ -47,6 +49,7 @@
import android.view.WindowManager;
import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.util.RaceConditionTracker;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.util.RemoteAnimationTargetSet;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -70,6 +73,7 @@
public class OtherActivityTouchConsumer extends ContextWrapper implements TouchConsumer {
private static final long LAUNCHER_DRAW_TIMEOUT_MS = 150;
+ public static final String DOWN_EVT = "OtherActivityTouchConsumer.DOWN";
private final SparseArray<RecentsAnimationState> mAnimationStates = new SparseArray<>();
private final RunningTaskInfo mRunningTask;
@@ -135,6 +139,7 @@
mTouchInteractionLog.addMotionEvent(ev);
switch (ev.getActionMasked()) {
case ACTION_DOWN: {
+ RaceConditionTracker.onEvent(DOWN_EVT, ENTER);
TraceHelper.beginSection("TouchInt");
mActivePointerId = ev.getPointerId(0);
mDownPos.set(ev.getX(), ev.getY());
@@ -151,6 +156,7 @@
Display display = getSystemService(WindowManager.class).getDefaultDisplay();
mDisplayRotation = display.getRotation();
WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
+ RaceConditionTracker.onEvent(DOWN_EVT, EXIT);
break;
}
case ACTION_POINTER_UP: {
diff --git a/quickstep/tests/src/com/android/quickstep/RaceConditionsTests.java b/quickstep/tests/src/com/android/quickstep/RaceConditionsTests.java
new file mode 100644
index 0000000..f132b12
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/RaceConditionsTests.java
@@ -0,0 +1,72 @@
+/*
+ * 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.quickstep;
+
+import static com.android.launcher3.util.RaceConditionTracker.enterEvt;
+import static com.android.launcher3.util.RaceConditionTracker.exitEvt;
+
+import android.content.Intent;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.util.RaceConditionReproducer;
+import com.android.quickstep.QuickStepOnOffRule.Mode;
+import com.android.quickstep.QuickStepOnOffRule.QuickstepOnOff;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class RaceConditionsTests extends AbstractQuickStepTest {
+ private void runTest(String... eventSequence) {
+ final RaceConditionReproducer eventProcessor = new RaceConditionReproducer(eventSequence);
+
+ // Destroy Launcher activity.
+ executeOnLauncher(launcher -> {
+ if (launcher != null) {
+ launcher.finish();
+ }
+ });
+ waitForLauncherCondition(
+ "Launcher still active", launcher -> launcher == null, DEFAULT_UI_TIMEOUT);
+
+ // Start an activity where we'll press home.
+ startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+
+ // The test action.
+ eventProcessor.startIteration();
+ mLauncher.pressHome();
+ eventProcessor.finishIteration();
+ }
+
+ @Test
+ @QuickstepOnOff(mode = Mode.ON)
+ public void testPressHomeToStartLauncher() {
+ runTest(enterEvt(Launcher.ON_CREATE_EVT),
+ exitEvt(Launcher.ON_CREATE_EVT),
+ enterEvt(OtherActivityTouchConsumer.DOWN_EVT),
+ exitEvt(OtherActivityTouchConsumer.DOWN_EVT));
+
+ runTest(enterEvt(OtherActivityTouchConsumer.DOWN_EVT),
+ exitEvt(OtherActivityTouchConsumer.DOWN_EVT),
+ enterEvt(Launcher.ON_CREATE_EVT),
+ exitEvt(Launcher.ON_CREATE_EVT));
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTests.java b/quickstep/tests/src/com/android/quickstep/TaplTests.java
index 701229c..6a1123e 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTests.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTests.java
@@ -24,9 +24,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import android.app.Instrumentation;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.util.Log;
@@ -66,7 +64,6 @@
@LargeTest
@RunWith(AndroidJUnit4.class)
public class TaplTests extends AbstractQuickStepTest {
- private static final int WAIT_TIME_MS = 60000;
private static final String TAG = "TaplTests";
private static int sScreenshotCount = 0;
@@ -112,13 +109,6 @@
waitForResumed("Launcher internal state is still Background");
}
- private String resolveSystemApp(String category) {
- return getInstrumentation().getContext().getPackageManager().resolveActivity(
- new Intent(Intent.ACTION_MAIN).addCategory(category),
- PackageManager.MATCH_SYSTEM_ONLY).
- activityInfo.packageName;
- }
-
private boolean isInState(LauncherState state) {
if (!TestHelpers.isInLauncherProcess()) return true;
return getFromLauncher(launcher -> launcher.getStateManager().getState() == state);
@@ -143,17 +133,6 @@
return !launcher.hasBeenResumed();
}
- private void startAppFast(String packageName) {
- final Instrumentation instrumentation = getInstrumentation();
- final Intent intent = instrumentation.getContext().getPackageManager().
- getLaunchIntentForPackage(packageName);
- intent.addCategory(Intent.CATEGORY_LAUNCHER);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- instrumentation.getTargetContext().startActivity(intent);
- assertTrue(packageName + " didn't start",
- mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), WAIT_TIME_MS));
- }
-
private void startTestApps() throws Exception {
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_MESSAGING));
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
@@ -338,7 +317,8 @@
assertNotNull("overview.getCurrentTask() returned null (1)", task);
assertNotNull("OverviewTask.open returned null", task.open());
assertTrue("Contacts app didn't open from Overview", mDevice.wait(Until.hasObject(
- By.pkg(resolveSystemApp(Intent.CATEGORY_APP_CONTACTS)).depth(0)), WAIT_TIME_MS));
+ By.pkg(resolveSystemApp(Intent.CATEGORY_APP_CONTACTS)).depth(0)),
+ LONG_WAIT_TIME_MS));
executeOnLauncher(launcher -> assertTrue(
"Launcher activity is the top activity; expecting another activity to be the top "
+ "one",
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index f8b44d0..9125a98 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -19,6 +19,7 @@
import static android.content.pm.ActivityInfo.CONFIG_LOCALE;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+
import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
import static com.android.launcher3.LauncherState.ALL_APPS;
@@ -26,6 +27,8 @@
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.logging.LoggerUtils.newTarget;
+import static com.android.launcher3.util.RaceConditionTracker.ENTER;
+import static com.android.launcher3.util.RaceConditionTracker.EXIT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -71,16 +74,18 @@
import android.view.animation.OvershootInterpolator;
import android.widget.Toast;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.PropertyListBuilder;
-import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompatVO;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragView;
@@ -116,6 +121,7 @@
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.PendingRequestArgs;
+import com.android.launcher3.util.RaceConditionTracker;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
@@ -143,8 +149,6 @@
import java.util.List;
import java.util.Set;
-import androidx.annotation.Nullable;
-
/**
* Default launcher application.
*/
@@ -184,6 +188,7 @@
private static final String RUNTIME_STATE_PENDING_ACTIVITY_RESULT = "launcher.activity_result";
// Type: SparseArray<Parcelable>
private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel";
+ public static final String ON_CREATE_EVT = "Launcher.onCreate";
private LauncherStateManager mStateManager;
@@ -255,6 +260,7 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
+ RaceConditionTracker.onEvent(ON_CREATE_EVT, ENTER);
if (DEBUG_STRICT_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
@@ -349,6 +355,7 @@
mRotationHelper.initialize();
TraceHelper.endSection("Launcher-onCreate");
+ RaceConditionTracker.onEvent(ON_CREATE_EVT, EXIT);
}
@Override
diff --git a/src/com/android/launcher3/util/RaceConditionTracker.java b/src/com/android/launcher3/util/RaceConditionTracker.java
index 8eafb55..8b06787 100644
--- a/src/com/android/launcher3/util/RaceConditionTracker.java
+++ b/src/com/android/launcher3/util/RaceConditionTracker.java
@@ -22,6 +22,9 @@
* orders.
*/
public class RaceConditionTracker {
+ public final static boolean ENTER = true;
+ public final static boolean EXIT = false;
+
public interface EventProcessor {
void onEvent(String eventName);
}
@@ -35,4 +38,22 @@
public static void onEvent(String eventName) {
if (sEventProcessor != null) sEventProcessor.onEvent(eventName);
}
+
+ public static void onEvent(String eventName, boolean isEnter) {
+ if (sEventProcessor != null) {
+ sEventProcessor.onEvent(enterExitEvt(eventName, isEnter));
+ }
+ }
+
+ public static String enterExitEvt(String eventName, boolean isEnter) {
+ return eventName + ":" + (isEnter ? "enter" : "exit");
+ }
+
+ public static String enterEvt(String eventName) {
+ return enterExitEvt(eventName, ENTER);
+ }
+
+ public static String exitEvt(String eventName) {
+ return enterExitEvt(eventName, EXIT);
+ }
}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index bc5aaee..338fd85 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -17,6 +17,7 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.app.Instrumentation;
@@ -25,11 +26,13 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.LauncherActivityInfo;
+import android.content.pm.PackageManager;
import android.os.Process;
import android.os.RemoteException;
import android.view.Surface;
import androidx.test.InstrumentationRegistry;
+import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.Direction;
import androidx.test.uiautomator.UiDevice;
@@ -77,6 +80,7 @@
public static final long SHORT_UI_TIMEOUT= 300;
public static final long DEFAULT_UI_TIMEOUT = 10000;
+ protected static final int LONG_WAIT_TIME_MS = 60000;
protected MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
protected final UiDevice mDevice;
@@ -325,4 +329,22 @@
return intent == null ? null : (Intent) intent.getParcelableExtra(Intent.EXTRA_INTENT);
}
}
+
+ protected void startAppFast(String packageName) {
+ final Instrumentation instrumentation = getInstrumentation();
+ final Intent intent = instrumentation.getContext().getPackageManager().
+ getLaunchIntentForPackage(packageName);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ instrumentation.getTargetContext().startActivity(intent);
+ assertTrue(packageName + " didn't start",
+ mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), LONG_WAIT_TIME_MS));
+ }
+
+ protected String resolveSystemApp(String category) {
+ return getInstrumentation().getContext().getPackageManager().resolveActivity(
+ new Intent(Intent.ACTION_MAIN).addCategory(category),
+ PackageManager.MATCH_SYSTEM_ONLY).
+ activityInfo.packageName;
+ }
}
diff --git a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java b/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
index c4350de..316e40d 100644
--- a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
+++ b/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
@@ -43,6 +43,11 @@
* executing events in previously unseen order. It does it by postponing execution of threads that
* would lead to an already seen sequence.
*
+ * If an event A occurs before event B in the sequence, this is how execution order looks like:
+ * Events: ... A ... B ...
+ * Events and instructions, guaranteed order:
+ * (instructions executed prior to A) A ... B (instructions executed after B)
+ *
* Each iteration has 3 parts (phases).
* Phase 1. Picking a previously seen event subsequence that we believe can have previously unseen
* continuations. Reproducing this sequence by pausing threads that would lead to other sequences.
@@ -178,6 +183,10 @@
mReproString = reproString;
}
+ public RaceConditionReproducer(String... reproSequence) {
+ this(String.join("|", reproSequence));
+ }
+
public synchronized String getCurrentSequenceString() {
return mCurrentSequence.toString();
}