Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.view.cts;
     18 
     19 import android.test.InstrumentationTestCase;
     20 import android.view.Choreographer;
     21 
     22 public class ChoreographerTest extends InstrumentationTestCase {
     23     private static final long NOMINAL_VSYNC_PERIOD = 16;
     24     private static final long DELAY_PERIOD = NOMINAL_VSYNC_PERIOD * 5;
     25     private static final long NANOS_PER_MS = 1000000;
     26     private static final Object TOKEN = new Object();
     27 
     28     private Choreographer mChoreographer;
     29 
     30     @Override
     31     protected void setUp() throws Exception {
     32         super.setUp();
     33         getInstrumentation().runOnMainSync(new Runnable() {
     34             @Override
     35             public void run() {
     36                 mChoreographer = Choreographer.getInstance();
     37             }
     38         });
     39     }
     40 
     41     public void testFrameDelay() {
     42         assertTrue(Choreographer.getFrameDelay() > 0);
     43 
     44         long oldFrameDelay = Choreographer.getFrameDelay();
     45         long newFrameDelay = oldFrameDelay * 2;
     46         Choreographer.setFrameDelay(newFrameDelay);
     47         assertEquals(newFrameDelay, Choreographer.getFrameDelay());
     48 
     49         Choreographer.setFrameDelay(oldFrameDelay);
     50     }
     51 
     52     public void testPostCallbackWithoutDelayEventuallyRunsCallbacks() {
     53         MockRunnable addedCallback1 = new MockRunnable();
     54         MockRunnable addedCallback2 = new MockRunnable();
     55         MockRunnable removedCallback = new MockRunnable();
     56         try {
     57             // Add and remove a few callbacks.
     58             mChoreographer.postCallback(
     59                     Choreographer.CALLBACK_ANIMATION, addedCallback1, null);
     60             mChoreographer.postCallback(
     61                     Choreographer.CALLBACK_ANIMATION, addedCallback2, null);
     62             mChoreographer.postCallback(
     63                     Choreographer.CALLBACK_ANIMATION, removedCallback, null);
     64             mChoreographer.removeCallbacks(
     65                     Choreographer.CALLBACK_ANIMATION, removedCallback, null);
     66 
     67             // Sleep for a couple of frames.
     68             sleep(NOMINAL_VSYNC_PERIOD * 3);
     69 
     70             // We expect the remaining callbacks to have been invoked once.
     71             assertEquals(1, addedCallback1.invocationCount);
     72             assertEquals(1, addedCallback2.invocationCount);
     73             assertEquals(0, removedCallback.invocationCount);
     74 
     75             // If we post a callback again, then it should be invoked again.
     76             mChoreographer.postCallback(
     77                     Choreographer.CALLBACK_ANIMATION, addedCallback1, null);
     78             sleep(NOMINAL_VSYNC_PERIOD * 3);
     79 
     80             assertEquals(2, addedCallback1.invocationCount);
     81             assertEquals(1, addedCallback2.invocationCount);
     82             assertEquals(0, removedCallback.invocationCount);
     83 
     84             // If the token matches, the the callback should be removed.
     85             mChoreographer.postCallback(
     86                     Choreographer.CALLBACK_ANIMATION, addedCallback1, null);
     87             mChoreographer.postCallback(
     88                     Choreographer.CALLBACK_ANIMATION, removedCallback, TOKEN);
     89             mChoreographer.removeCallbacks(
     90                     Choreographer.CALLBACK_ANIMATION, null, TOKEN);
     91             sleep(NOMINAL_VSYNC_PERIOD * 3);
     92             assertEquals(3, addedCallback1.invocationCount);
     93             assertEquals(0, removedCallback.invocationCount);
     94 
     95             // If the action and token matches, then the callback should be removed.
     96             // If only the token matches, then the callback should not be removed.
     97             mChoreographer.postCallback(
     98                     Choreographer.CALLBACK_ANIMATION, addedCallback1, TOKEN);
     99             mChoreographer.postCallback(
    100                     Choreographer.CALLBACK_ANIMATION, removedCallback, TOKEN);
    101             mChoreographer.removeCallbacks(
    102                     Choreographer.CALLBACK_ANIMATION, removedCallback, TOKEN);
    103             sleep(NOMINAL_VSYNC_PERIOD * 3);
    104             assertEquals(4, addedCallback1.invocationCount);
    105             assertEquals(0, removedCallback.invocationCount);
    106         } finally {
    107             mChoreographer.removeCallbacks(
    108                     Choreographer.CALLBACK_ANIMATION, addedCallback1, null);
    109             mChoreographer.removeCallbacks(
    110                     Choreographer.CALLBACK_ANIMATION, addedCallback2, null);
    111             mChoreographer.removeCallbacks(
    112                     Choreographer.CALLBACK_ANIMATION, removedCallback, null);
    113         }
    114     }
    115 
    116     public void testPostCallbackWithDelayEventuallyRunsCallbacksAfterDelay() {
    117         MockRunnable addedCallback = new MockRunnable();
    118         MockRunnable removedCallback = new MockRunnable();
    119         try {
    120             // Add and remove a few callbacks.
    121             mChoreographer.postCallbackDelayed(
    122                     Choreographer.CALLBACK_ANIMATION, addedCallback, null, DELAY_PERIOD);
    123             mChoreographer.postCallbackDelayed(
    124                     Choreographer.CALLBACK_ANIMATION, removedCallback, null, DELAY_PERIOD);
    125             mChoreographer.removeCallbacks(
    126                     Choreographer.CALLBACK_ANIMATION, removedCallback, null);
    127 
    128             // Sleep for a couple of frames.
    129             sleep(NOMINAL_VSYNC_PERIOD * 3);
    130 
    131             // The callbacks should not have been invoked yet because of the delay.
    132             assertEquals(0, addedCallback.invocationCount);
    133             assertEquals(0, removedCallback.invocationCount);
    134 
    135             // Sleep for the rest of the delay time.
    136             sleep(DELAY_PERIOD);
    137 
    138             // We expect the remaining callbacks to have been invoked.
    139             assertEquals(1, addedCallback.invocationCount);
    140             assertEquals(0, removedCallback.invocationCount);
    141 
    142             // If the token matches, the the callback should be removed.
    143             mChoreographer.postCallbackDelayed(
    144                     Choreographer.CALLBACK_ANIMATION, addedCallback, null, DELAY_PERIOD);
    145             mChoreographer.postCallbackDelayed(
    146                     Choreographer.CALLBACK_ANIMATION, removedCallback, TOKEN, DELAY_PERIOD);
    147             mChoreographer.removeCallbacks(
    148                     Choreographer.CALLBACK_ANIMATION, null, TOKEN);
    149             sleep(NOMINAL_VSYNC_PERIOD * 3 + DELAY_PERIOD);
    150             assertEquals(2, addedCallback.invocationCount);
    151             assertEquals(0, removedCallback.invocationCount);
    152 
    153             // If the action and token matches, then the callback should be removed.
    154             // If only the token matches, then the callback should not be removed.
    155             mChoreographer.postCallbackDelayed(
    156                     Choreographer.CALLBACK_ANIMATION, addedCallback, TOKEN, DELAY_PERIOD);
    157             mChoreographer.postCallbackDelayed(
    158                     Choreographer.CALLBACK_ANIMATION, removedCallback, TOKEN, DELAY_PERIOD);
    159             mChoreographer.removeCallbacks(
    160                     Choreographer.CALLBACK_ANIMATION, removedCallback, TOKEN);
    161             sleep(NOMINAL_VSYNC_PERIOD * 3 + DELAY_PERIOD);
    162             assertEquals(3, addedCallback.invocationCount);
    163             assertEquals(0, removedCallback.invocationCount);
    164         } finally {
    165             mChoreographer.removeCallbacks(
    166                     Choreographer.CALLBACK_ANIMATION, addedCallback, null);
    167             mChoreographer.removeCallbacks(
    168                     Choreographer.CALLBACK_ANIMATION, removedCallback, null);
    169         }
    170     }
    171 
    172     public void testPostCallbackThrowsIfRunnableIsNull() {
    173         try {
    174             mChoreographer.postCallback(
    175                     Choreographer.CALLBACK_ANIMATION, null, TOKEN);
    176             fail("Expected IllegalArgumentException");
    177         } catch (IllegalArgumentException ex) {
    178             // expected
    179         }
    180     }
    181 
    182     public void testPostCallbackDelayedThrowsIfRunnableIsNull() {
    183         try {
    184             mChoreographer.postCallbackDelayed(
    185                     Choreographer.CALLBACK_ANIMATION, null, TOKEN, DELAY_PERIOD);
    186             fail("Expected IllegalArgumentException");
    187         } catch (IllegalArgumentException ex) {
    188             // expected
    189         }
    190     }
    191 
    192     public void testPostFrameCallbackWithoutDelayEventuallyRunsFrameCallbacks() {
    193         MockFrameCallback addedFrameCallback1 = new MockFrameCallback();
    194         MockFrameCallback addedFrameCallback2 = new MockFrameCallback();
    195         MockFrameCallback removedFrameCallback = new MockFrameCallback();
    196         try {
    197             // Add and remove a few callbacks.
    198             long postTimeNanos = System.nanoTime();
    199             mChoreographer.postFrameCallback(addedFrameCallback1);
    200             mChoreographer.postFrameCallback(addedFrameCallback2);
    201             mChoreographer.postFrameCallback(removedFrameCallback);
    202             mChoreographer.removeFrameCallback(removedFrameCallback);
    203 
    204             // Sleep for a couple of frames.
    205             sleep(NOMINAL_VSYNC_PERIOD * 3);
    206 
    207             // We expect the remaining callbacks to have been invoked once.
    208             assertEquals(1, addedFrameCallback1.invocationCount);
    209             assertEquals(1, addedFrameCallback2.invocationCount);
    210             assertEquals(0, removedFrameCallback.invocationCount);
    211             assertTimeDeltaLessThan(addedFrameCallback1.frameTimeNanos - postTimeNanos,
    212                     NOMINAL_VSYNC_PERIOD * 3 * NANOS_PER_MS);
    213             assertTimeDeltaLessThan(addedFrameCallback2.frameTimeNanos - postTimeNanos,
    214                     NOMINAL_VSYNC_PERIOD * 3 * NANOS_PER_MS);
    215             assertTimeDeltaLessThan(Math.abs(addedFrameCallback2.frameTimeNanos
    216                     - addedFrameCallback1.frameTimeNanos), NOMINAL_VSYNC_PERIOD * NANOS_PER_MS);
    217 
    218             // If we post a callback again, then it should be invoked again.
    219             postTimeNanos = System.nanoTime();
    220             mChoreographer.postFrameCallback(addedFrameCallback1);
    221             sleep(NOMINAL_VSYNC_PERIOD * 3);
    222 
    223             assertEquals(2, addedFrameCallback1.invocationCount);
    224             assertEquals(1, addedFrameCallback2.invocationCount);
    225             assertEquals(0, removedFrameCallback.invocationCount);
    226             assertTimeDeltaLessThan(addedFrameCallback1.frameTimeNanos - postTimeNanos,
    227                     NOMINAL_VSYNC_PERIOD * 3 * NANOS_PER_MS);
    228         } finally {
    229             mChoreographer.removeFrameCallback(addedFrameCallback1);
    230             mChoreographer.removeFrameCallback(addedFrameCallback2);
    231             mChoreographer.removeFrameCallback(removedFrameCallback);
    232         }
    233     }
    234 
    235     public void testPostFrameCallbackWithDelayEventuallyRunsFrameCallbacksAfterDelay() {
    236         MockFrameCallback addedFrameCallback = new MockFrameCallback();
    237         MockFrameCallback removedFrameCallback = new MockFrameCallback();
    238         try {
    239             // Add and remove a few callbacks.
    240             long postTimeNanos = System.nanoTime();
    241             mChoreographer.postFrameCallbackDelayed(addedFrameCallback, DELAY_PERIOD);
    242             mChoreographer.postFrameCallbackDelayed(removedFrameCallback, DELAY_PERIOD);
    243             mChoreographer.removeFrameCallback(removedFrameCallback);
    244 
    245             // Sleep for a couple of frames.
    246             sleep(NOMINAL_VSYNC_PERIOD * 3);
    247 
    248             // The callbacks should not have been invoked yet because of the delay.
    249             assertEquals(0, addedFrameCallback.invocationCount);
    250             assertEquals(0, removedFrameCallback.invocationCount);
    251 
    252             // Sleep for the rest of the delay time.
    253             sleep(DELAY_PERIOD);
    254 
    255             // We expect the remaining callbacks to have been invoked.
    256             assertEquals(1, addedFrameCallback.invocationCount);
    257             assertEquals(0, removedFrameCallback.invocationCount);
    258             assertTimeDeltaLessThan(addedFrameCallback.frameTimeNanos - postTimeNanos,
    259                     (NOMINAL_VSYNC_PERIOD * 3 + DELAY_PERIOD) * NANOS_PER_MS);
    260         } finally {
    261             mChoreographer.removeFrameCallback(addedFrameCallback);
    262             mChoreographer.removeFrameCallback(removedFrameCallback);
    263         }
    264     }
    265 
    266     private void assertTimeDeltaLessThan(long deltaNanos, long thresholdNanos) {
    267         if (deltaNanos >= thresholdNanos) {
    268             fail("Expected time delta less than " + thresholdNanos + " nanos, actually "
    269                     + " was " + deltaNanos + " nanos.");
    270         }
    271     }
    272 
    273     public void testPostFrameCallbackThrowsIfCallbackIsNull() {
    274         try {
    275             mChoreographer.postFrameCallback(null);
    276             fail("Expected IllegalArgumentException");
    277         } catch (IllegalArgumentException ex) {
    278             // expected
    279         }
    280     }
    281 
    282     public void testPostFrameCallbackDelayedThrowsIfCallbackIsNull() {
    283         try {
    284             mChoreographer.postFrameCallbackDelayed(null, DELAY_PERIOD);
    285             fail("Expected IllegalArgumentException");
    286         } catch (IllegalArgumentException ex) {
    287             // expected
    288         }
    289     }
    290 
    291     public void testRemoveFrameCallbackThrowsIfCallbackIsNull() {
    292         try {
    293             mChoreographer.removeFrameCallback(null);
    294             fail("Expected IllegalArgumentException");
    295         } catch (IllegalArgumentException ex) {
    296             // expected
    297         }
    298     }
    299 
    300     private void sleep(long time) {
    301         try {
    302             Thread.sleep(time);
    303         } catch (InterruptedException e) {
    304             fail(e.getMessage());
    305         }
    306     }
    307 
    308     private static final class MockRunnable implements Runnable {
    309         public int invocationCount;
    310 
    311         @Override
    312         public void run() {
    313             invocationCount += 1;
    314         }
    315     }
    316 
    317     private static final class MockFrameCallback implements Choreographer.FrameCallback {
    318         public long frameTimeNanos;
    319         public int invocationCount;
    320 
    321         @Override
    322         public void doFrame(long frameTimeNanos) {
    323             this.frameTimeNanos = frameTimeNanos;
    324             invocationCount += 1;
    325         }
    326     }
    327 }
    328