Add tests for frame callbacks.

Change-Id: Ife3be8e153ad314a7c8ad1d386d14d46443bf73b
diff --git a/tests/tests/view/src/android/view/cts/ChoreographerTest.java b/tests/tests/view/src/android/view/cts/ChoreographerTest.java
index 9376f0c..3a34b2c 100644
--- a/tests/tests/view/src/android/view/cts/ChoreographerTest.java
+++ b/tests/tests/view/src/android/view/cts/ChoreographerTest.java
@@ -22,6 +22,7 @@
 public class ChoreographerTest extends AndroidTestCase {
     private static final long NOMINAL_VSYNC_PERIOD = 16;
     private static final long DELAY_PERIOD = NOMINAL_VSYNC_PERIOD * 5;
+    private static final long NANOS_PER_MS = 1000000;
     private static final Object TOKEN = new Object();
 
     private Choreographer mChoreographer = Choreographer.getInstance();
@@ -45,8 +46,8 @@
             // Add and remove a few callbacks.
             mChoreographer.postCallback(
                     Choreographer.CALLBACK_ANIMATION, addedCallback1, null);
-            mChoreographer.postCallbackDelayed(
-                    Choreographer.CALLBACK_ANIMATION, addedCallback2, null, 0);
+            mChoreographer.postCallback(
+                    Choreographer.CALLBACK_ANIMATION, addedCallback2, null);
             mChoreographer.postCallback(
                     Choreographer.CALLBACK_ANIMATION, removedCallback, null);
             mChoreographer.removeCallbacks(
@@ -177,6 +178,114 @@
         }
     }
 
+    public void testPostFrameCallbackWithoutDelayEventuallyRunsFrameCallbacks() {
+        MockFrameCallback addedFrameCallback1 = new MockFrameCallback();
+        MockFrameCallback addedFrameCallback2 = new MockFrameCallback();
+        MockFrameCallback removedFrameCallback = new MockFrameCallback();
+        try {
+            // Add and remove a few callbacks.
+            long postTimeNanos = System.nanoTime();
+            mChoreographer.postFrameCallback(addedFrameCallback1);
+            mChoreographer.postFrameCallback(addedFrameCallback2);
+            mChoreographer.postFrameCallback(removedFrameCallback);
+            mChoreographer.removeFrameCallback(removedFrameCallback);
+
+            // Sleep for a couple of frames.
+            sleep(NOMINAL_VSYNC_PERIOD * 3);
+
+            // We expect the remaining callbacks to have been invoked once.
+            assertEquals(1, addedFrameCallback1.invocationCount);
+            assertEquals(1, addedFrameCallback2.invocationCount);
+            assertEquals(0, removedFrameCallback.invocationCount);
+            assertTimeDeltaLessThan(addedFrameCallback1.frameTimeNanos - postTimeNanos,
+                    NOMINAL_VSYNC_PERIOD * 3 * NANOS_PER_MS);
+            assertTimeDeltaLessThan(addedFrameCallback2.frameTimeNanos - postTimeNanos,
+                    NOMINAL_VSYNC_PERIOD * 3 * NANOS_PER_MS);
+            assertTimeDeltaLessThan(Math.abs(addedFrameCallback2.frameTimeNanos
+                    - addedFrameCallback1.frameTimeNanos), NOMINAL_VSYNC_PERIOD * NANOS_PER_MS);
+
+            // If we post a callback again, then it should be invoked again.
+            postTimeNanos = System.nanoTime();
+            mChoreographer.postFrameCallback(addedFrameCallback1);
+            sleep(NOMINAL_VSYNC_PERIOD * 3);
+
+            assertEquals(2, addedFrameCallback1.invocationCount);
+            assertEquals(1, addedFrameCallback2.invocationCount);
+            assertEquals(0, removedFrameCallback.invocationCount);
+            assertTimeDeltaLessThan(addedFrameCallback1.frameTimeNanos - postTimeNanos,
+                    NOMINAL_VSYNC_PERIOD * 3 * NANOS_PER_MS);
+        } finally {
+            mChoreographer.removeFrameCallback(addedFrameCallback1);
+            mChoreographer.removeFrameCallback(addedFrameCallback2);
+            mChoreographer.removeFrameCallback(removedFrameCallback);
+        }
+    }
+
+    public void testPostFrameCallbackWithDelayEventuallyRunsFrameCallbacksAfterDelay() {
+        MockFrameCallback addedFrameCallback = new MockFrameCallback();
+        MockFrameCallback removedFrameCallback = new MockFrameCallback();
+        try {
+            // Add and remove a few callbacks.
+            long postTimeNanos = System.nanoTime();
+            mChoreographer.postFrameCallbackDelayed(addedFrameCallback, DELAY_PERIOD);
+            mChoreographer.postFrameCallbackDelayed(removedFrameCallback, DELAY_PERIOD);
+            mChoreographer.removeFrameCallback(removedFrameCallback);
+
+            // Sleep for a couple of frames.
+            sleep(NOMINAL_VSYNC_PERIOD * 3);
+
+            // The callbacks should not have been invoked yet because of the delay.
+            assertEquals(0, addedFrameCallback.invocationCount);
+            assertEquals(0, removedFrameCallback.invocationCount);
+
+            // Sleep for the rest of the delay time.
+            sleep(DELAY_PERIOD);
+
+            // We expect the remaining callbacks to have been invoked.
+            assertEquals(1, addedFrameCallback.invocationCount);
+            assertEquals(0, removedFrameCallback.invocationCount);
+            assertTimeDeltaLessThan(addedFrameCallback.frameTimeNanos - postTimeNanos,
+                    (NOMINAL_VSYNC_PERIOD * 3 + DELAY_PERIOD) * NANOS_PER_MS);
+        } finally {
+            mChoreographer.removeFrameCallback(addedFrameCallback);
+            mChoreographer.removeFrameCallback(removedFrameCallback);
+        }
+    }
+
+    private void assertTimeDeltaLessThan(long deltaNanos, long thresholdNanos) {
+        if (deltaNanos >= thresholdNanos) {
+            fail("Expected time delta less than " + thresholdNanos + " nanos, actually "
+                    + " was " + deltaNanos + " nanos.");
+        }
+    }
+
+    public void testPostFrameCallbackThrowsIfCallbackIsNull() {
+        try {
+            mChoreographer.postFrameCallback(null);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException ex) {
+            // expected
+        }
+    }
+
+    public void testPostFrameCallbackDelayedThrowsIfCallbackIsNull() {
+        try {
+            mChoreographer.postFrameCallbackDelayed(null, DELAY_PERIOD);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException ex) {
+            // expected
+        }
+    }
+
+    public void testRemoveFrameCallbackThrowsIfCallbackIsNull() {
+        try {
+            mChoreographer.removeFrameCallback(null);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException ex) {
+            // expected
+        }
+    }
+
     private void sleep(long time) {
         try {
             Thread.sleep(time);
@@ -193,4 +302,15 @@
             invocationCount += 1;
         }
     }
+
+    private static final class MockFrameCallback implements Choreographer.FrameCallback {
+        public long frameTimeNanos;
+        public int invocationCount;
+
+        @Override
+        public void doFrame(long frameTimeNanos) {
+            this.frameTimeNanos = frameTimeNanos;
+            invocationCount += 1;
+        }
+    }
 }