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.AndroidTestCase; 20 import android.view.Choreographer; 21 22 public class ChoreographerTest extends AndroidTestCase { 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 = Choreographer.getInstance(); 29 30 public void testFrameDelay() { 31 assertTrue(Choreographer.getFrameDelay() > 0); 32 33 long oldFrameDelay = Choreographer.getFrameDelay(); 34 long newFrameDelay = oldFrameDelay * 2; 35 Choreographer.setFrameDelay(newFrameDelay); 36 assertEquals(newFrameDelay, Choreographer.getFrameDelay()); 37 38 Choreographer.setFrameDelay(oldFrameDelay); 39 } 40 41 public void testPostCallbackWithoutDelayEventuallyRunsCallbacks() { 42 MockRunnable addedCallback1 = new MockRunnable(); 43 MockRunnable addedCallback2 = new MockRunnable(); 44 MockRunnable removedCallback = new MockRunnable(); 45 try { 46 // Add and remove a few callbacks. 47 mChoreographer.postCallback( 48 Choreographer.CALLBACK_ANIMATION, addedCallback1, null); 49 mChoreographer.postCallback( 50 Choreographer.CALLBACK_ANIMATION, addedCallback2, null); 51 mChoreographer.postCallback( 52 Choreographer.CALLBACK_ANIMATION, removedCallback, null); 53 mChoreographer.removeCallbacks( 54 Choreographer.CALLBACK_ANIMATION, removedCallback, null); 55 56 // Sleep for a couple of frames. 57 sleep(NOMINAL_VSYNC_PERIOD * 3); 58 59 // We expect the remaining callbacks to have been invoked once. 60 assertEquals(1, addedCallback1.invocationCount); 61 assertEquals(1, addedCallback2.invocationCount); 62 assertEquals(0, removedCallback.invocationCount); 63 64 // If we post a callback again, then it should be invoked again. 65 mChoreographer.postCallback( 66 Choreographer.CALLBACK_ANIMATION, addedCallback1, null); 67 sleep(NOMINAL_VSYNC_PERIOD * 3); 68 69 assertEquals(2, addedCallback1.invocationCount); 70 assertEquals(1, addedCallback2.invocationCount); 71 assertEquals(0, removedCallback.invocationCount); 72 73 // If the token matches, the the callback should be removed. 74 mChoreographer.postCallback( 75 Choreographer.CALLBACK_ANIMATION, addedCallback1, null); 76 mChoreographer.postCallback( 77 Choreographer.CALLBACK_ANIMATION, removedCallback, TOKEN); 78 mChoreographer.removeCallbacks( 79 Choreographer.CALLBACK_ANIMATION, null, TOKEN); 80 sleep(NOMINAL_VSYNC_PERIOD * 3); 81 assertEquals(3, addedCallback1.invocationCount); 82 assertEquals(0, removedCallback.invocationCount); 83 84 // If the action and token matches, then the callback should be removed. 85 // If only the token matches, then the callback should not be removed. 86 mChoreographer.postCallback( 87 Choreographer.CALLBACK_ANIMATION, addedCallback1, TOKEN); 88 mChoreographer.postCallback( 89 Choreographer.CALLBACK_ANIMATION, removedCallback, TOKEN); 90 mChoreographer.removeCallbacks( 91 Choreographer.CALLBACK_ANIMATION, removedCallback, TOKEN); 92 sleep(NOMINAL_VSYNC_PERIOD * 3); 93 assertEquals(4, addedCallback1.invocationCount); 94 assertEquals(0, removedCallback.invocationCount); 95 } finally { 96 mChoreographer.removeCallbacks( 97 Choreographer.CALLBACK_ANIMATION, addedCallback1, null); 98 mChoreographer.removeCallbacks( 99 Choreographer.CALLBACK_ANIMATION, addedCallback2, null); 100 mChoreographer.removeCallbacks( 101 Choreographer.CALLBACK_ANIMATION, removedCallback, null); 102 } 103 } 104 105 public void testPostCallbackWithDelayEventuallyRunsCallbacksAfterDelay() { 106 MockRunnable addedCallback = new MockRunnable(); 107 MockRunnable removedCallback = new MockRunnable(); 108 try { 109 // Add and remove a few callbacks. 110 mChoreographer.postCallbackDelayed( 111 Choreographer.CALLBACK_ANIMATION, addedCallback, null, DELAY_PERIOD); 112 mChoreographer.postCallbackDelayed( 113 Choreographer.CALLBACK_ANIMATION, removedCallback, null, DELAY_PERIOD); 114 mChoreographer.removeCallbacks( 115 Choreographer.CALLBACK_ANIMATION, removedCallback, null); 116 117 // Sleep for a couple of frames. 118 sleep(NOMINAL_VSYNC_PERIOD * 3); 119 120 // The callbacks should not have been invoked yet because of the delay. 121 assertEquals(0, addedCallback.invocationCount); 122 assertEquals(0, removedCallback.invocationCount); 123 124 // Sleep for the rest of the delay time. 125 sleep(DELAY_PERIOD); 126 127 // We expect the remaining callbacks to have been invoked. 128 assertEquals(1, addedCallback.invocationCount); 129 assertEquals(0, removedCallback.invocationCount); 130 131 // If the token matches, the the callback should be removed. 132 mChoreographer.postCallbackDelayed( 133 Choreographer.CALLBACK_ANIMATION, addedCallback, null, DELAY_PERIOD); 134 mChoreographer.postCallbackDelayed( 135 Choreographer.CALLBACK_ANIMATION, removedCallback, TOKEN, DELAY_PERIOD); 136 mChoreographer.removeCallbacks( 137 Choreographer.CALLBACK_ANIMATION, null, TOKEN); 138 sleep(NOMINAL_VSYNC_PERIOD * 3 + DELAY_PERIOD); 139 assertEquals(2, addedCallback.invocationCount); 140 assertEquals(0, removedCallback.invocationCount); 141 142 // If the action and token matches, then the callback should be removed. 143 // If only the token matches, then the callback should not be removed. 144 mChoreographer.postCallbackDelayed( 145 Choreographer.CALLBACK_ANIMATION, addedCallback, TOKEN, DELAY_PERIOD); 146 mChoreographer.postCallbackDelayed( 147 Choreographer.CALLBACK_ANIMATION, removedCallback, TOKEN, DELAY_PERIOD); 148 mChoreographer.removeCallbacks( 149 Choreographer.CALLBACK_ANIMATION, removedCallback, TOKEN); 150 sleep(NOMINAL_VSYNC_PERIOD * 3 + DELAY_PERIOD); 151 assertEquals(3, addedCallback.invocationCount); 152 assertEquals(0, removedCallback.invocationCount); 153 } finally { 154 mChoreographer.removeCallbacks( 155 Choreographer.CALLBACK_ANIMATION, addedCallback, null); 156 mChoreographer.removeCallbacks( 157 Choreographer.CALLBACK_ANIMATION, removedCallback, null); 158 } 159 } 160 161 public void testPostCallbackThrowsIfRunnableIsNull() { 162 try { 163 mChoreographer.postCallback( 164 Choreographer.CALLBACK_ANIMATION, null, TOKEN); 165 fail("Expected IllegalArgumentException"); 166 } catch (IllegalArgumentException ex) { 167 // expected 168 } 169 } 170 171 public void testPostCallbackDelayedThrowsIfRunnableIsNull() { 172 try { 173 mChoreographer.postCallbackDelayed( 174 Choreographer.CALLBACK_ANIMATION, null, TOKEN, DELAY_PERIOD); 175 fail("Expected IllegalArgumentException"); 176 } catch (IllegalArgumentException ex) { 177 // expected 178 } 179 } 180 181 public void testPostFrameCallbackWithoutDelayEventuallyRunsFrameCallbacks() { 182 MockFrameCallback addedFrameCallback1 = new MockFrameCallback(); 183 MockFrameCallback addedFrameCallback2 = new MockFrameCallback(); 184 MockFrameCallback removedFrameCallback = new MockFrameCallback(); 185 try { 186 // Add and remove a few callbacks. 187 long postTimeNanos = System.nanoTime(); 188 mChoreographer.postFrameCallback(addedFrameCallback1); 189 mChoreographer.postFrameCallback(addedFrameCallback2); 190 mChoreographer.postFrameCallback(removedFrameCallback); 191 mChoreographer.removeFrameCallback(removedFrameCallback); 192 193 // Sleep for a couple of frames. 194 sleep(NOMINAL_VSYNC_PERIOD * 3); 195 196 // We expect the remaining callbacks to have been invoked once. 197 assertEquals(1, addedFrameCallback1.invocationCount); 198 assertEquals(1, addedFrameCallback2.invocationCount); 199 assertEquals(0, removedFrameCallback.invocationCount); 200 assertTimeDeltaLessThan(addedFrameCallback1.frameTimeNanos - postTimeNanos, 201 NOMINAL_VSYNC_PERIOD * 3 * NANOS_PER_MS); 202 assertTimeDeltaLessThan(addedFrameCallback2.frameTimeNanos - postTimeNanos, 203 NOMINAL_VSYNC_PERIOD * 3 * NANOS_PER_MS); 204 assertTimeDeltaLessThan(Math.abs(addedFrameCallback2.frameTimeNanos 205 - addedFrameCallback1.frameTimeNanos), NOMINAL_VSYNC_PERIOD * NANOS_PER_MS); 206 207 // If we post a callback again, then it should be invoked again. 208 postTimeNanos = System.nanoTime(); 209 mChoreographer.postFrameCallback(addedFrameCallback1); 210 sleep(NOMINAL_VSYNC_PERIOD * 3); 211 212 assertEquals(2, addedFrameCallback1.invocationCount); 213 assertEquals(1, addedFrameCallback2.invocationCount); 214 assertEquals(0, removedFrameCallback.invocationCount); 215 assertTimeDeltaLessThan(addedFrameCallback1.frameTimeNanos - postTimeNanos, 216 NOMINAL_VSYNC_PERIOD * 3 * NANOS_PER_MS); 217 } finally { 218 mChoreographer.removeFrameCallback(addedFrameCallback1); 219 mChoreographer.removeFrameCallback(addedFrameCallback2); 220 mChoreographer.removeFrameCallback(removedFrameCallback); 221 } 222 } 223 224 public void testPostFrameCallbackWithDelayEventuallyRunsFrameCallbacksAfterDelay() { 225 MockFrameCallback addedFrameCallback = new MockFrameCallback(); 226 MockFrameCallback removedFrameCallback = new MockFrameCallback(); 227 try { 228 // Add and remove a few callbacks. 229 long postTimeNanos = System.nanoTime(); 230 mChoreographer.postFrameCallbackDelayed(addedFrameCallback, DELAY_PERIOD); 231 mChoreographer.postFrameCallbackDelayed(removedFrameCallback, DELAY_PERIOD); 232 mChoreographer.removeFrameCallback(removedFrameCallback); 233 234 // Sleep for a couple of frames. 235 sleep(NOMINAL_VSYNC_PERIOD * 3); 236 237 // The callbacks should not have been invoked yet because of the delay. 238 assertEquals(0, addedFrameCallback.invocationCount); 239 assertEquals(0, removedFrameCallback.invocationCount); 240 241 // Sleep for the rest of the delay time. 242 sleep(DELAY_PERIOD); 243 244 // We expect the remaining callbacks to have been invoked. 245 assertEquals(1, addedFrameCallback.invocationCount); 246 assertEquals(0, removedFrameCallback.invocationCount); 247 assertTimeDeltaLessThan(addedFrameCallback.frameTimeNanos - postTimeNanos, 248 (NOMINAL_VSYNC_PERIOD * 3 + DELAY_PERIOD) * NANOS_PER_MS); 249 } finally { 250 mChoreographer.removeFrameCallback(addedFrameCallback); 251 mChoreographer.removeFrameCallback(removedFrameCallback); 252 } 253 } 254 255 private void assertTimeDeltaLessThan(long deltaNanos, long thresholdNanos) { 256 if (deltaNanos >= thresholdNanos) { 257 fail("Expected time delta less than " + thresholdNanos + " nanos, actually " 258 + " was " + deltaNanos + " nanos."); 259 } 260 } 261 262 public void testPostFrameCallbackThrowsIfCallbackIsNull() { 263 try { 264 mChoreographer.postFrameCallback(null); 265 fail("Expected IllegalArgumentException"); 266 } catch (IllegalArgumentException ex) { 267 // expected 268 } 269 } 270 271 public void testPostFrameCallbackDelayedThrowsIfCallbackIsNull() { 272 try { 273 mChoreographer.postFrameCallbackDelayed(null, DELAY_PERIOD); 274 fail("Expected IllegalArgumentException"); 275 } catch (IllegalArgumentException ex) { 276 // expected 277 } 278 } 279 280 public void testRemoveFrameCallbackThrowsIfCallbackIsNull() { 281 try { 282 mChoreographer.removeFrameCallback(null); 283 fail("Expected IllegalArgumentException"); 284 } catch (IllegalArgumentException ex) { 285 // expected 286 } 287 } 288 289 private void sleep(long time) { 290 try { 291 Thread.sleep(time); 292 } catch (InterruptedException e) { 293 fail(e.getMessage()); 294 } 295 } 296 297 private static final class MockRunnable implements Runnable { 298 public int invocationCount; 299 300 @Override 301 public void run() { 302 invocationCount += 1; 303 } 304 } 305 306 private static final class MockFrameCallback implements Choreographer.FrameCallback { 307 public long frameTimeNanos; 308 public int invocationCount; 309 310 @Override 311 public void doFrame(long frameTimeNanos) { 312 this.frameTimeNanos = frameTimeNanos; 313 invocationCount += 1; 314 } 315 } 316 } 317