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