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 static org.junit.Assert.assertEquals;
     20 import static org.junit.Assert.assertTrue;
     21 import static org.junit.Assert.fail;
     22 import static org.mockito.Mockito.mock;
     23 import static org.mockito.Mockito.timeout;
     24 import static org.mockito.Mockito.times;
     25 import static org.mockito.Mockito.verify;
     26 import static org.mockito.Mockito.verifyZeroInteractions;
     27 
     28 import android.os.SystemClock;
     29 import android.view.Choreographer;
     30 
     31 import androidx.test.annotation.UiThreadTest;
     32 import androidx.test.filters.MediumTest;
     33 import androidx.test.runner.AndroidJUnit4;
     34 
     35 import org.junit.Before;
     36 import org.junit.Test;
     37 import org.junit.runner.RunWith;
     38 import org.mockito.ArgumentCaptor;
     39 
     40 @MediumTest
     41 @RunWith(AndroidJUnit4.class)
     42 public class ChoreographerTest {
     43     private static final long NOMINAL_VSYNC_PERIOD = 16;
     44     private static final long DELAY_PERIOD = NOMINAL_VSYNC_PERIOD * 5;
     45     private static final long NANOS_PER_MS = 1000000;
     46     private static final Object TOKEN = new Object();
     47 
     48     private Choreographer mChoreographer;
     49 
     50     @UiThreadTest
     51     @Before
     52     public void setup() {
     53         mChoreographer = Choreographer.getInstance();
     54     }
     55 
     56     @Test
     57     public void testFrameDelay() {
     58         assertTrue(Choreographer.getFrameDelay() > 0);
     59 
     60         long oldFrameDelay = Choreographer.getFrameDelay();
     61         long newFrameDelay = oldFrameDelay * 2;
     62         Choreographer.setFrameDelay(newFrameDelay);
     63         assertEquals(newFrameDelay, Choreographer.getFrameDelay());
     64 
     65         Choreographer.setFrameDelay(oldFrameDelay);
     66     }
     67 
     68     @Test
     69     public void testPostCallbackWithoutDelay() {
     70         final Runnable addedCallback1 = mock(Runnable.class);
     71         final Runnable addedCallback2 = mock(Runnable.class);
     72         final Runnable removedCallback = mock(Runnable.class);
     73         try {
     74             // Add and remove a few callbacks.
     75             mChoreographer.postCallback(
     76                     Choreographer.CALLBACK_ANIMATION, addedCallback1, null);
     77             mChoreographer.postCallback(
     78                     Choreographer.CALLBACK_ANIMATION, addedCallback2, null);
     79             mChoreographer.postCallback(
     80                     Choreographer.CALLBACK_ANIMATION, removedCallback, null);
     81             mChoreographer.removeCallbacks(
     82                     Choreographer.CALLBACK_ANIMATION, removedCallback, null);
     83 
     84             // We expect the remaining callbacks to have been invoked once.
     85             verify(addedCallback1, timeout(NOMINAL_VSYNC_PERIOD * 30).times(1)).run();
     86             verify(addedCallback2, timeout(NOMINAL_VSYNC_PERIOD * 30).times(1)).run();
     87             verifyZeroInteractions(removedCallback);
     88 
     89             // If we post a callback again, then it should be invoked again.
     90             mChoreographer.postCallback(
     91                     Choreographer.CALLBACK_ANIMATION, addedCallback1, null);
     92 
     93             verify(addedCallback1, timeout(NOMINAL_VSYNC_PERIOD * 30).times(2)).run();
     94             verify(addedCallback2, times(1)).run();
     95             verifyZeroInteractions(removedCallback);
     96 
     97             // If the token matches, the the callback should be removed.
     98             mChoreographer.postCallback(
     99                     Choreographer.CALLBACK_ANIMATION, addedCallback1, null);
    100             mChoreographer.postCallback(
    101                     Choreographer.CALLBACK_ANIMATION, removedCallback, TOKEN);
    102             mChoreographer.removeCallbacks(
    103                     Choreographer.CALLBACK_ANIMATION, null, TOKEN);
    104             verify(addedCallback1, timeout(NOMINAL_VSYNC_PERIOD * 30).times(3)).run();
    105             verifyZeroInteractions(removedCallback);
    106 
    107             // If the action and token matches, then the callback should be removed.
    108             // If only the token matches, then the callback should not be removed.
    109             mChoreographer.postCallback(
    110                     Choreographer.CALLBACK_ANIMATION, addedCallback1, TOKEN);
    111             mChoreographer.postCallback(
    112                     Choreographer.CALLBACK_ANIMATION, removedCallback, TOKEN);
    113             mChoreographer.removeCallbacks(
    114                     Choreographer.CALLBACK_ANIMATION, removedCallback, TOKEN);
    115             verify(addedCallback1, timeout(NOMINAL_VSYNC_PERIOD * 30).times(4)).run();
    116             verifyZeroInteractions(removedCallback);
    117         } finally {
    118             mChoreographer.removeCallbacks(
    119                     Choreographer.CALLBACK_ANIMATION, addedCallback1, null);
    120             mChoreographer.removeCallbacks(
    121                     Choreographer.CALLBACK_ANIMATION, addedCallback2, null);
    122             mChoreographer.removeCallbacks(
    123                     Choreographer.CALLBACK_ANIMATION, removedCallback, null);
    124         }
    125     }
    126 
    127     @Test
    128     public void testPostCallbackWithDelay() {
    129         final Runnable addedCallback = mock(Runnable.class);
    130         final Runnable removedCallback = mock(Runnable.class);
    131         try {
    132             // Add and remove a few callbacks.
    133             mChoreographer.postCallbackDelayed(
    134                     Choreographer.CALLBACK_ANIMATION, addedCallback, null, DELAY_PERIOD);
    135             mChoreographer.postCallbackDelayed(
    136                     Choreographer.CALLBACK_ANIMATION, removedCallback, null, DELAY_PERIOD);
    137             mChoreographer.removeCallbacks(
    138                     Choreographer.CALLBACK_ANIMATION, removedCallback, null);
    139 
    140             // Sleep for a couple of frames.
    141             SystemClock.sleep(NOMINAL_VSYNC_PERIOD * 3);
    142 
    143             // The callbacks should not have been invoked yet because of the delay.
    144             verifyZeroInteractions(addedCallback);
    145             verifyZeroInteractions(removedCallback);
    146 
    147             // We expect the remaining callbacks to have been invoked.
    148             verify(addedCallback, timeout(DELAY_PERIOD * 3).times(1)).run();
    149             verifyZeroInteractions(removedCallback);
    150 
    151             // If the token matches, the the callback should be removed.
    152             mChoreographer.postCallbackDelayed(
    153                     Choreographer.CALLBACK_ANIMATION, addedCallback, null, DELAY_PERIOD);
    154             mChoreographer.postCallbackDelayed(
    155                     Choreographer.CALLBACK_ANIMATION, removedCallback, TOKEN, DELAY_PERIOD);
    156             mChoreographer.removeCallbacks(
    157                     Choreographer.CALLBACK_ANIMATION, null, TOKEN);
    158             verify(addedCallback, timeout(DELAY_PERIOD * 3).times(2)).run();
    159             verifyZeroInteractions(removedCallback);
    160 
    161             // If the action and token matches, then the callback should be removed.
    162             // If only the token matches, then the callback should not be removed.
    163             mChoreographer.postCallbackDelayed(
    164                     Choreographer.CALLBACK_ANIMATION, addedCallback, TOKEN, DELAY_PERIOD);
    165             mChoreographer.postCallbackDelayed(
    166                     Choreographer.CALLBACK_ANIMATION, removedCallback, TOKEN, DELAY_PERIOD);
    167             mChoreographer.removeCallbacks(
    168                     Choreographer.CALLBACK_ANIMATION, removedCallback, TOKEN);
    169             verify(addedCallback, timeout(DELAY_PERIOD * 3).times(3)).run();
    170             verifyZeroInteractions(removedCallback);
    171         } finally {
    172             mChoreographer.removeCallbacks(
    173                     Choreographer.CALLBACK_ANIMATION, addedCallback, null);
    174             mChoreographer.removeCallbacks(
    175                     Choreographer.CALLBACK_ANIMATION, removedCallback, null);
    176         }
    177     }
    178 
    179     @Test(expected=IllegalArgumentException.class)
    180     public void testPostNullCallback() {
    181         mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, null, TOKEN);
    182     }
    183 
    184     @Test(expected=IllegalArgumentException.class)
    185     public void testPostNullCallbackDelayed() {
    186         mChoreographer.postCallbackDelayed( Choreographer.CALLBACK_ANIMATION, null, TOKEN,
    187                 DELAY_PERIOD);
    188     }
    189 
    190     @Test
    191     public void testPostFrameCallbackWithoutDelay() {
    192         final Choreographer.FrameCallback addedFrameCallback1 =
    193                 mock(Choreographer.FrameCallback.class);
    194         final Choreographer.FrameCallback addedFrameCallback2 =
    195                 mock(Choreographer.FrameCallback.class);
    196         final Choreographer.FrameCallback removedFrameCallback =
    197                 mock(Choreographer.FrameCallback.class);
    198         try {
    199             // Add and remove a few callbacks.
    200             long postTimeNanos = System.nanoTime();
    201             mChoreographer.postFrameCallback(addedFrameCallback1);
    202             mChoreographer.postFrameCallback(addedFrameCallback2);
    203             mChoreographer.postFrameCallback(removedFrameCallback);
    204             mChoreographer.removeFrameCallback(removedFrameCallback);
    205 
    206             // We expect the remaining callbacks to have been invoked once.
    207             ArgumentCaptor<Long> frameTimeNanosCaptor1 = ArgumentCaptor.forClass(Long.class);
    208             ArgumentCaptor<Long> frameTimeNanosCaptor2 = ArgumentCaptor.forClass(Long.class);
    209             verify(addedFrameCallback1, timeout(NOMINAL_VSYNC_PERIOD * 10).times(1))
    210                     .doFrame(frameTimeNanosCaptor1.capture());
    211             verify(addedFrameCallback2, times(1)).doFrame(frameTimeNanosCaptor2.capture());
    212             verifyZeroInteractions(removedFrameCallback);
    213 
    214             assertTimeDeltaLessThan(frameTimeNanosCaptor1.getValue() - postTimeNanos,
    215                     NOMINAL_VSYNC_PERIOD * 10 * NANOS_PER_MS);
    216             assertTimeDeltaLessThan(frameTimeNanosCaptor2.getValue() - postTimeNanos,
    217                     NOMINAL_VSYNC_PERIOD * 10 * NANOS_PER_MS);
    218             assertTimeDeltaLessThan(
    219                     Math.abs(frameTimeNanosCaptor2.getValue() - frameTimeNanosCaptor1.getValue()),
    220                     NOMINAL_VSYNC_PERIOD * NANOS_PER_MS);
    221 
    222             // If we post a callback again, then it should be invoked again.
    223             postTimeNanos = System.nanoTime();
    224             mChoreographer.postFrameCallback(addedFrameCallback1);
    225 
    226             verify(addedFrameCallback1, timeout(NOMINAL_VSYNC_PERIOD * 10).times(2))
    227                     .doFrame(frameTimeNanosCaptor1.capture());
    228             verify(addedFrameCallback2, times(1)).doFrame(frameTimeNanosCaptor2.capture());
    229             verifyZeroInteractions(removedFrameCallback);
    230             assertTimeDeltaLessThan(frameTimeNanosCaptor1.getAllValues().get(1) - postTimeNanos,
    231                     NOMINAL_VSYNC_PERIOD * 10 * NANOS_PER_MS);
    232         } finally {
    233             mChoreographer.removeFrameCallback(addedFrameCallback1);
    234             mChoreographer.removeFrameCallback(addedFrameCallback2);
    235             mChoreographer.removeFrameCallback(removedFrameCallback);
    236         }
    237     }
    238 
    239     @Test
    240     public void testPostFrameCallbackWithDelay() {
    241         final Choreographer.FrameCallback addedFrameCallback =
    242                 mock(Choreographer.FrameCallback.class);
    243         final Choreographer.FrameCallback removedFrameCallback =
    244                 mock(Choreographer.FrameCallback.class);
    245         try {
    246             // Add and remove a few callbacks.
    247             long postTimeNanos = System.nanoTime();
    248             mChoreographer.postFrameCallbackDelayed(addedFrameCallback, DELAY_PERIOD);
    249             mChoreographer.postFrameCallbackDelayed(removedFrameCallback, DELAY_PERIOD);
    250             mChoreographer.removeFrameCallback(removedFrameCallback);
    251 
    252             // Sleep for a couple of frames.
    253             SystemClock.sleep(NOMINAL_VSYNC_PERIOD * 3);
    254 
    255             // The callbacks should not have been invoked yet because of the delay.
    256             verifyZeroInteractions(addedFrameCallback);
    257             verifyZeroInteractions(removedFrameCallback);
    258 
    259             // We expect the remaining callbacks to have been invoked.
    260             ArgumentCaptor<Long> frameTimeNanosCaptor = ArgumentCaptor.forClass(Long.class);
    261             verify(addedFrameCallback, timeout(DELAY_PERIOD * 3).times(1))
    262                     .doFrame(frameTimeNanosCaptor.capture());
    263             verifyZeroInteractions(removedFrameCallback);
    264             assertTimeDeltaLessThan(frameTimeNanosCaptor.getValue() - postTimeNanos,
    265                     (NOMINAL_VSYNC_PERIOD * 10 + DELAY_PERIOD) * NANOS_PER_MS);
    266         } finally {
    267             mChoreographer.removeFrameCallback(addedFrameCallback);
    268             mChoreographer.removeFrameCallback(removedFrameCallback);
    269         }
    270     }
    271 
    272     private void assertTimeDeltaLessThan(long deltaNanos, long thresholdNanos) {
    273         if (deltaNanos >= thresholdNanos) {
    274             fail("Expected time delta less than " + thresholdNanos + " nanos, actually "
    275                     + " was " + deltaNanos + " nanos.");
    276         }
    277     }
    278 
    279     @Test(expected=IllegalArgumentException.class)
    280     public void testPostNullFrameCallback() {
    281         mChoreographer.postFrameCallback(null);
    282     }
    283 
    284     @Test(expected=IllegalArgumentException.class)
    285     public void testPostNullFrameCallbackDelayed() {
    286         mChoreographer.postFrameCallbackDelayed(null, DELAY_PERIOD);
    287     }
    288 
    289     @Test(expected=IllegalArgumentException.class)
    290     public void testRemoveNullFrameCallback() {
    291         mChoreographer.removeFrameCallback(null);
    292     }
    293 }
    294