Be ultra stable, and go waaaaay faster

Thread.sleep() does not a flake-free test make, eliminate it.
And since we aren't sleeping, go 10x+ faster.

Bug: 38270821
Test: this
Change-Id: I94530edbd740f5901233b92eadc43ed9c18a9bc4
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 3fdd96e..5f3e99f 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -8,5 +8,6 @@
                       tests/tests/text/
                       tests/tests/graphics/
                       tests/tests/transition/
+                      tests/tests/uirendering/
                       tests/tests/view/
                       tests/tests/widget/
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/EdgeEffectTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/EdgeEffectTests.java
index fe79329..2f0ed64 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/EdgeEffectTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/EdgeEffectTests.java
@@ -15,10 +15,11 @@
  */
 package android.uirendering.cts.testclasses;
 
+import static android.uirendering.cts.util.MockVsyncHelper.nextFrame;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 import static org.mockito.Matchers.anyFloat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -29,12 +30,11 @@
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.uirendering.cts.R;
 import android.uirendering.cts.bitmapverifiers.PerPixelBitmapVerifier;
 import android.uirendering.cts.util.BitmapAsserter;
+import android.uirendering.cts.util.MockVsyncHelper;
 import android.view.ContextThemeWrapper;
 import android.widget.EdgeEffect;
 
@@ -45,7 +45,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 
-@MediumTest
+@SmallTest
 @RunWith(AndroidJUnit4.class)
 public class EdgeEffectTests {
 
@@ -190,30 +190,27 @@
     // validates changes to the alpha of draw commands produced by EdgeEffect
     // over the course of an animation
     private void verifyAlpha(EdgeEffectInitializer initializer, AlphaVerifier alphaVerifier) {
-        Canvas canvas = mock(Canvas.class);
-        ArgumentCaptor<Paint> captor = ArgumentCaptor.forClass(Paint.class);
-        EdgeEffect edgeEffect = new EdgeEffect(getContext());
-        edgeEffect.setSize(200, 200);
-        initializer.initialize(edgeEffect);
-        edgeEffect.draw(canvas);
-        verify(canvas).drawCircle(anyFloat(), anyFloat(), anyFloat(), captor.capture());
-        int oldAlpha = captor.getValue().getAlpha();
-        for (int i = 0; i < 3; i++) {
-            try {
-                Thread.sleep(20);
-            } catch (InterruptedException e) {
-                fail();
-            }
-            canvas = mock(Canvas.class);
+        MockVsyncHelper.runOnVsyncThread(() -> {
+            Canvas canvas = mock(Canvas.class);
+            ArgumentCaptor<Paint> captor = ArgumentCaptor.forClass(Paint.class);
+            EdgeEffect edgeEffect = new EdgeEffect(getContext());
+            edgeEffect.setSize(200, 200);
+            initializer.initialize(edgeEffect);
             edgeEffect.draw(canvas);
             verify(canvas).drawCircle(anyFloat(), anyFloat(), anyFloat(), captor.capture());
-            int newAlpha = captor.getValue().getAlpha();
-            alphaVerifier.verify(oldAlpha, newAlpha);
-            oldAlpha = newAlpha;
-        }
+            int oldAlpha = captor.getValue().getAlpha();
+            for (int i = 0; i < 3; i++) {
+                nextFrame();
+                canvas = mock(Canvas.class);
+                edgeEffect.draw(canvas);
+                verify(canvas).drawCircle(anyFloat(), anyFloat(), anyFloat(), captor.capture());
+                int newAlpha = captor.getValue().getAlpha();
+                alphaVerifier.verify(oldAlpha, newAlpha);
+                oldAlpha = newAlpha;
+            }
+        });
     }
 
-    @LargeTest
     @Test
     public void testOnAbsorb() {
         verifyAlpha(edgeEffect -> {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/util/MockVsyncHelper.java b/tests/tests/uirendering/src/android/uirendering/cts/util/MockVsyncHelper.java
new file mode 100644
index 0000000..4109cc2
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/util/MockVsyncHelper.java
@@ -0,0 +1,75 @@
+/*
+ * 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 android.uirendering.cts.util;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.view.animation.AnimationUtils;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+public class MockVsyncHelper {
+
+    private static ExecutorService sExecutor = Executors.newSingleThreadExecutor();
+    private static Future<Thread> sExecutorThread;
+
+    static {
+        // We can't wait on the future here because the lambda cannot be executed until
+        // the class has finished loading
+        sExecutorThread = sExecutor.submit(() -> {
+            AnimationUtils.lockAnimationClock(16);
+            return Thread.currentThread();
+        });
+    }
+
+    private static boolean isOnExecutorThread() {
+        try {
+            return Thread.currentThread().equals(sExecutorThread.get());
+        } catch (InterruptedException | ExecutionException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void nextFrame() {
+        assertTrue("nextFrame() must be called inside #unOnVsyncThread block",
+                isOnExecutorThread());
+        AnimationUtils.lockAnimationClock(AnimationUtils.currentAnimationTimeMillis() + 16);
+    }
+
+    public static void runOnVsyncThread(CallableVoid callable) {
+        assertFalse("Cannot runOnVsyncThread inside #runOnVsyncThread block",
+                isOnExecutorThread());
+        try {
+            sExecutor.submit(() -> {
+                callable.call();
+                return (Void) null;
+            }).get();
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        } catch (ExecutionException e) {
+            SneakyThrow.sneakyThrow(e.getCause() != null ? e.getCause() : e);
+        }
+    }
+
+    public interface CallableVoid {
+        void call() throws Exception;
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/util/SneakyThrow.java b/tests/tests/uirendering/src/android/uirendering/cts/util/SneakyThrow.java
new file mode 100644
index 0000000..d4f09bd
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/util/SneakyThrow.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 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 android.uirendering.cts.util;
+
+/**
+ * Provides a hacky method that always throws {@code t} even if {@code t} is a checked exception.
+ * and is not declared to be thrown.
+ *
+ * See
+ * http://www.mail-archive.com/javaposse@googlegroups.com/msg05984.html
+ */
+public class SneakyThrow {
+    /**
+     * A hacky method that always throws {@code t} even if {@code t} is a checked exception,
+     * and is not declared to be thrown.
+     */
+    public static void sneakyThrow(Throwable t) {
+        SneakyThrow.<RuntimeException>sneakyThrow_(t);
+    }
+
+    private static <T extends Throwable> void sneakyThrow_(Throwable t) throws T {
+        throw (T) t;
+    }
+}