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();
     }