1 /* 2 * Copyright (C) 2018 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.server.wm.lifecycle; 18 19 import static android.server.wm.StateLogger.log; 20 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_CREATE; 21 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_DESTROY; 22 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED; 23 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_PAUSE; 24 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_POST_CREATE; 25 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_RESTART; 26 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_RESUME; 27 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_START; 28 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_STOP; 29 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_TOP_POSITION_GAINED; 30 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_TOP_POSITION_LOST; 31 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.PRE_ON_CREATE; 32 33 import static org.junit.Assert.assertEquals; 34 import static org.junit.Assert.assertTrue; 35 import static org.junit.Assert.fail; 36 37 import android.app.Activity; 38 import android.server.wm.lifecycle.ActivityLifecycleClientTestBase.CallbackTrackingActivity; 39 import android.server.wm.lifecycle.LifecycleLog.ActivityCallback; 40 import android.util.Pair; 41 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 import java.util.List; 45 46 /** Util class that verifies correct activity state transition sequences. */ 47 class LifecycleVerifier { 48 49 private static final Class CALLBACK_TRACKING_CLASS = CallbackTrackingActivity.class; 50 assertLaunchSequence(Class<? extends Activity> activityClass, LifecycleLog lifecycleLog, LifecycleLog.ActivityCallback... expectedSubsequentEvents)51 static void assertLaunchSequence(Class<? extends Activity> activityClass, 52 LifecycleLog lifecycleLog, LifecycleLog.ActivityCallback... expectedSubsequentEvents) { 53 final List<LifecycleLog.ActivityCallback> observedTransitions = 54 lifecycleLog.getActivityLog(activityClass); 55 log("Observed sequence: " + observedTransitions); 56 final String errorMessage = errorDuringTransition(activityClass, "launch"); 57 58 final List<LifecycleLog.ActivityCallback> launchSequence = getLaunchSequence(activityClass); 59 final List<LifecycleLog.ActivityCallback> expectedTransitions; 60 expectedTransitions = new ArrayList<>(launchSequence.size() + 61 expectedSubsequentEvents.length); 62 expectedTransitions.addAll(launchSequence); 63 expectedTransitions.addAll(Arrays.asList(expectedSubsequentEvents)); 64 assertEquals(errorMessage, expectedTransitions, observedTransitions); 65 } 66 getLaunchSequence( Class<? extends Activity> activityClass)67 public static List<LifecycleLog.ActivityCallback> getLaunchSequence( 68 Class<? extends Activity> activityClass) { 69 return CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass) 70 ? Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, 71 ON_TOP_POSITION_GAINED) 72 : Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME); 73 } 74 assertLaunchSequence(Class<? extends Activity> launchingActivity, Class<? extends Activity> existingActivity, LifecycleLog lifecycleLog, boolean launchingIsTranslucent)75 static void assertLaunchSequence(Class<? extends Activity> launchingActivity, 76 Class<? extends Activity> existingActivity, LifecycleLog lifecycleLog, 77 boolean launchingIsTranslucent) { 78 final boolean includingCallbacks; 79 if (CALLBACK_TRACKING_CLASS.isAssignableFrom(launchingActivity) 80 && CALLBACK_TRACKING_CLASS.isAssignableFrom(existingActivity)) { 81 includingCallbacks = true; 82 } else if (!CALLBACK_TRACKING_CLASS.isAssignableFrom(launchingActivity) 83 && !CALLBACK_TRACKING_CLASS.isAssignableFrom(existingActivity)) { 84 includingCallbacks = false; 85 } else { 86 throw new IllegalArgumentException("Mixed types of callback tracking not supported. " 87 + "Both activities must support or not support callback tracking " 88 + "simultaneously"); 89 } 90 91 92 final List<Pair<String, ActivityCallback>> observedTransitions = lifecycleLog.getLog(); 93 log("Observed sequence: " + observedTransitions); 94 final String errorMessage = errorDuringTransition(launchingActivity, "launch"); 95 96 final List<Pair<String, ActivityCallback>> expectedTransitions = new ArrayList<>(); 97 // First top position will be lost 98 if (includingCallbacks) { 99 expectedTransitions.add(transition(existingActivity, ON_TOP_POSITION_LOST)); 100 } 101 // Next the existing activity is paused and the next one is launched 102 expectedTransitions.add(transition(existingActivity, ON_PAUSE)); 103 expectedTransitions.add(transition(launchingActivity, PRE_ON_CREATE)); 104 expectedTransitions.add(transition(launchingActivity, ON_CREATE)); 105 expectedTransitions.add(transition(launchingActivity, ON_START)); 106 if (includingCallbacks) { 107 expectedTransitions.add(transition(launchingActivity, ON_POST_CREATE)); 108 } 109 expectedTransitions.add(transition(launchingActivity, ON_RESUME)); 110 if (includingCallbacks) { 111 expectedTransitions.add(transition(launchingActivity, ON_TOP_POSITION_GAINED)); 112 } 113 if (!launchingIsTranslucent) { 114 expectedTransitions.add(transition(existingActivity, ON_STOP)); 115 } 116 117 assertEquals(errorMessage, expectedTransitions, observedTransitions); 118 } 119 assertLaunchAndStopSequence(Class<? extends Activity> activityClass, LifecycleLog lifecycleLog)120 static void assertLaunchAndStopSequence(Class<? extends Activity> activityClass, 121 LifecycleLog lifecycleLog) { 122 assertLaunchAndStopSequence(activityClass, lifecycleLog, 123 false /* onTop */); 124 } 125 assertLaunchAndStopSequence(Class<? extends Activity> activityClass, LifecycleLog lifecycleLog, boolean onTop)126 static void assertLaunchAndStopSequence(Class<? extends Activity> activityClass, 127 LifecycleLog lifecycleLog, boolean onTop) { 128 final List<ActivityCallback> observedTransitions = 129 lifecycleLog.getActivityLog(activityClass); 130 log("Observed sequence: " + observedTransitions); 131 final String errorMessage = errorDuringTransition(activityClass, "launch and stop"); 132 133 final boolean includeCallbacks = CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass); 134 135 final List<ActivityCallback> expectedTransitions = new ArrayList<>(); 136 expectedTransitions.addAll(Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START)); 137 if (includeCallbacks) { 138 expectedTransitions.add(ON_POST_CREATE); 139 } 140 expectedTransitions.add(ON_RESUME); 141 if (includeCallbacks && onTop) { 142 expectedTransitions.addAll(Arrays.asList(ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST)); 143 } 144 expectedTransitions.addAll(Arrays.asList(ON_PAUSE, ON_STOP)); 145 assertEquals(errorMessage, expectedTransitions, observedTransitions); 146 } 147 assertLaunchAndPauseSequence(Class<? extends Activity> activityClass, LifecycleLog lifecycleLog)148 static void assertLaunchAndPauseSequence(Class<? extends Activity> activityClass, 149 LifecycleLog lifecycleLog) { 150 final List<ActivityCallback> observedTransitions = 151 lifecycleLog.getActivityLog(activityClass); 152 log("Observed sequence: " + observedTransitions); 153 final String errorMessage = errorDuringTransition(activityClass, "launch and pause"); 154 155 final List<ActivityCallback> expectedTransitions = 156 Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME, ON_PAUSE); 157 assertEquals(errorMessage, expectedTransitions, observedTransitions); 158 } 159 assertRestartSequence(Class<? extends Activity> activityClass, LifecycleLog lifecycleLog)160 static void assertRestartSequence(Class<? extends Activity> activityClass, 161 LifecycleLog lifecycleLog) { 162 final List<LifecycleLog.ActivityCallback> observedTransitions = 163 lifecycleLog.getActivityLog(activityClass); 164 log("Observed sequence: " + observedTransitions); 165 final String errorMessage = errorDuringTransition(activityClass, "restart"); 166 167 final List<LifecycleLog.ActivityCallback> expectedTransitions = 168 Arrays.asList(ON_RESTART, ON_START); 169 assertEquals(errorMessage, expectedTransitions, observedTransitions); 170 } 171 assertRestartAndResumeSequence(Class<? extends Activity> activityClass, LifecycleLog lifecycleLog)172 static void assertRestartAndResumeSequence(Class<? extends Activity> activityClass, 173 LifecycleLog lifecycleLog) { 174 final List<LifecycleLog.ActivityCallback> observedTransitions = 175 lifecycleLog.getActivityLog(activityClass); 176 log("Observed sequence: " + observedTransitions); 177 final String errorMessage = errorDuringTransition(activityClass, "restart and pause"); 178 179 final List<LifecycleLog.ActivityCallback> expectedTransitions = 180 Arrays.asList(ON_RESTART, ON_START, ON_RESUME); 181 assertEquals(errorMessage, expectedTransitions, observedTransitions); 182 } 183 assertRecreateAndResumeSequence(Class<? extends Activity> activityClass, LifecycleLog lifecycleLog)184 static void assertRecreateAndResumeSequence(Class<? extends Activity> activityClass, 185 LifecycleLog lifecycleLog) { 186 final List<LifecycleLog.ActivityCallback> observedTransitions = 187 lifecycleLog.getActivityLog(activityClass); 188 log("Observed sequence: " + observedTransitions); 189 final String errorMessage = errorDuringTransition(activityClass, "recreateA and pause"); 190 191 final List<LifecycleLog.ActivityCallback> expectedTransitions = 192 Arrays.asList(ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME); 193 assertEquals(errorMessage, expectedTransitions, observedTransitions); 194 } 195 assertLaunchAndDestroySequence(Class<? extends Activity> activityClass, LifecycleLog lifecycleLog)196 static void assertLaunchAndDestroySequence(Class<? extends Activity> activityClass, 197 LifecycleLog lifecycleLog) { 198 final List<LifecycleLog.ActivityCallback> observedTransitions = 199 lifecycleLog.getActivityLog(activityClass); 200 log("Observed sequence: " + observedTransitions); 201 final String errorMessage = errorDuringTransition(activityClass, "launch and destroy"); 202 203 final List<LifecycleLog.ActivityCallback> expectedTransitions = Arrays.asList( 204 PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY); 205 assertEquals(errorMessage, expectedTransitions, observedTransitions); 206 } 207 assertResumeToDestroySequence(Class<? extends Activity> activityClass, LifecycleLog lifecycleLog)208 static void assertResumeToDestroySequence(Class<? extends Activity> activityClass, 209 LifecycleLog lifecycleLog) { 210 final List<LifecycleLog.ActivityCallback> observedTransitions = 211 lifecycleLog.getActivityLog(activityClass); 212 log("Observed sequence: " + observedTransitions); 213 final String errorMessage = errorDuringTransition(activityClass, "launch and destroy"); 214 215 final List<LifecycleLog.ActivityCallback> expectedTransitions = 216 getResumeToDestroySequence(activityClass); 217 assertEquals(errorMessage, expectedTransitions, observedTransitions); 218 } 219 getResumeToDestroySequence( Class<? extends Activity> activityClass)220 static List<LifecycleLog.ActivityCallback> getResumeToDestroySequence( 221 Class<? extends Activity> activityClass) { 222 return CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass) 223 ? Arrays.asList(ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, ON_DESTROY) 224 : Arrays.asList(ON_PAUSE, ON_STOP, ON_DESTROY); 225 } 226 assertResumeToStopSequence(Class<? extends Activity> activityClass, LifecycleLog lifecycleLog)227 static void assertResumeToStopSequence(Class<? extends Activity> activityClass, 228 LifecycleLog lifecycleLog) { 229 final List<LifecycleLog.ActivityCallback> observedTransitions = 230 lifecycleLog.getActivityLog(activityClass); 231 log("Observed sequence: " + observedTransitions); 232 final String errorMessage = errorDuringTransition(activityClass, "resumed to stopped"); 233 final boolean includeCallbacks = CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass); 234 235 final List<LifecycleLog.ActivityCallback> expectedTransitions = new ArrayList<>(); 236 if (includeCallbacks) { 237 expectedTransitions.add(ON_TOP_POSITION_LOST); 238 } 239 expectedTransitions.add(ON_PAUSE); 240 expectedTransitions.add(ON_STOP); 241 242 assertEquals(errorMessage, expectedTransitions, observedTransitions); 243 } 244 assertStopToResumeSequence(Class<? extends Activity> activityClass, LifecycleLog lifecycleLog)245 static void assertStopToResumeSequence(Class<? extends Activity> activityClass, 246 LifecycleLog lifecycleLog) { 247 final List<LifecycleLog.ActivityCallback> observedTransitions = 248 lifecycleLog.getActivityLog(activityClass); 249 log("Observed sequence: " + observedTransitions); 250 final String errorMessage = errorDuringTransition(activityClass, "stopped to resumed"); 251 final boolean includeCallbacks = CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass); 252 253 final List<LifecycleLog.ActivityCallback> expectedTransitions = new ArrayList<>( 254 Arrays.asList(ON_RESTART, ON_START, ON_RESUME)); 255 if (includeCallbacks) { 256 expectedTransitions.add(ON_TOP_POSITION_GAINED); 257 } 258 259 assertEquals(errorMessage, expectedTransitions, observedTransitions); 260 } 261 assertRelaunchSequence(Class<? extends Activity> activityClass, LifecycleLog lifecycleLog, LifecycleLog.ActivityCallback startState)262 static void assertRelaunchSequence(Class<? extends Activity> activityClass, 263 LifecycleLog lifecycleLog, LifecycleLog.ActivityCallback startState) { 264 final List<LifecycleLog.ActivityCallback> expectedTransitions = getRelaunchSequence(startState); 265 assertSequence(activityClass, lifecycleLog, expectedTransitions, "relaunch"); 266 } 267 getRelaunchSequence( LifecycleLog.ActivityCallback startState)268 static List<LifecycleLog.ActivityCallback> getRelaunchSequence( 269 LifecycleLog.ActivityCallback startState) { 270 final List<LifecycleLog.ActivityCallback> expectedTransitions; 271 if (startState == ON_PAUSE) { 272 expectedTransitions = Arrays.asList( 273 ON_STOP, ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME, ON_PAUSE); 274 } else if (startState == ON_STOP) { 275 expectedTransitions = Arrays.asList( 276 ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP); 277 } else if (startState == ON_RESUME) { 278 expectedTransitions = Arrays.asList( 279 ON_PAUSE, ON_STOP, ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME); 280 } else if (startState == ON_TOP_POSITION_GAINED) { 281 // Looks like we're tracking the callbacks here 282 expectedTransitions = Arrays.asList( 283 ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, ON_DESTROY, PRE_ON_CREATE, ON_CREATE, 284 ON_START, ON_POST_CREATE, ON_RESUME, ON_TOP_POSITION_GAINED); 285 } else { 286 throw new IllegalArgumentException("Start state not supported: " + startState); 287 } 288 return expectedTransitions; 289 } 290 getSplitScreenTransitionSequence( Class<? extends Activity> activityClass)291 static List<LifecycleLog.ActivityCallback> getSplitScreenTransitionSequence( 292 Class<? extends Activity> activityClass) { 293 return CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass) 294 ? Arrays.asList( 295 ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, ON_DESTROY, PRE_ON_CREATE, 296 ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, ON_TOP_POSITION_GAINED, 297 ON_TOP_POSITION_LOST, ON_PAUSE, ON_MULTI_WINDOW_MODE_CHANGED) 298 : Arrays.asList( 299 ON_PAUSE, ON_STOP, ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, 300 ON_RESUME, ON_PAUSE); 301 } 302 assertSequence(Class<? extends Activity> activityClass, LifecycleLog lifecycleLog, List<ActivityCallback> expectedTransitions, String transition)303 static void assertSequence(Class<? extends Activity> activityClass, LifecycleLog lifecycleLog, 304 List<ActivityCallback> expectedTransitions, String transition) { 305 final List<ActivityCallback> observedTransitions = 306 lifecycleLog.getActivityLog(activityClass); 307 log("Observed sequence: " + observedTransitions); 308 final String errorMessage = errorDuringTransition(activityClass, transition); 309 310 assertEquals(errorMessage, expectedTransitions, observedTransitions); 311 } 312 313 /** 314 * Assert that the observed transitions of a particular activity happened in expected order. 315 * There may be more observed transitions than in the expected array, only their order matters. 316 * 317 * Use this method when there is no need to verify the entire sequence, only that some 318 * transitions happened after another. 319 */ assertOrder(LifecycleLog lifecycleLog, Class<? extends Activity> activityClass, List<ActivityCallback> expectedTransitionsOrder, String transition)320 static void assertOrder(LifecycleLog lifecycleLog, Class<? extends Activity> activityClass, 321 List<ActivityCallback> expectedTransitionsOrder, String transition) { 322 List<Pair<String, ActivityCallback>> expectedTransitions = new ArrayList<>(); 323 for (ActivityCallback callback : expectedTransitionsOrder) { 324 expectedTransitions.add(transition(activityClass, callback)); 325 } 326 assertOrder(lifecycleLog, expectedTransitions, transition); 327 } 328 329 /** 330 * Assert that the observed transitions happened in expected order. There may be more observed 331 * transitions than in the expected array, only their order matters. 332 * 333 * Use this method when there is no need to verify the entire sequence, only that some 334 * transitions happened after another. 335 */ assertOrder(LifecycleLog lifecycleLog, List<Pair<String, ActivityCallback>> expectedTransitionsOrder, String transition)336 static void assertOrder(LifecycleLog lifecycleLog, 337 List<Pair<String, ActivityCallback>> expectedTransitionsOrder, 338 String transition) { 339 final List<Pair<String, ActivityCallback>> observedTransitions = lifecycleLog.getLog(); 340 int nextObservedPosition = 0; 341 for (Pair<String, ActivityCallback> expectedTransition 342 : expectedTransitionsOrder) { 343 while (nextObservedPosition < observedTransitions.size() 344 && !observedTransitions.get(nextObservedPosition).equals(expectedTransition)) 345 { 346 nextObservedPosition++; 347 } 348 if (nextObservedPosition == observedTransitions.size()) { 349 fail("Transition wasn't observed in the expected position: " + expectedTransition 350 + " during transition: " + transition); 351 } 352 } 353 } 354 355 /** 356 * Assert that a transition was observer, no particular order. 357 */ assertTransitionObserved(LifecycleLog lifecycleLog, Pair<String, ActivityCallback> expectedTransition, String transition)358 static void assertTransitionObserved(LifecycleLog lifecycleLog, 359 Pair<String, ActivityCallback> expectedTransition, String transition) { 360 assertTrue("Transition " + expectedTransition + " must be observed during " + transition, 361 lifecycleLog.getLog().contains(expectedTransition)); 362 } 363 assertEmptySequence(Class<? extends Activity> activityClass, LifecycleLog lifecycleLog, String transition)364 static void assertEmptySequence(Class<? extends Activity> activityClass, 365 LifecycleLog lifecycleLog, String transition) { 366 assertSequence(activityClass, lifecycleLog, new ArrayList<>(), transition); 367 } 368 369 /** Assert that a lifecycle sequence matches one of the possible variants. */ assertSequenceMatchesOneOf(Class<? extends Activity> activityClass, LifecycleLog lifecycleLog, List<List<ActivityCallback>> expectedTransitions, String transition)370 static void assertSequenceMatchesOneOf(Class<? extends Activity> activityClass, 371 LifecycleLog lifecycleLog, List<List<ActivityCallback>> expectedTransitions, 372 String transition) { 373 final List<ActivityCallback> observedTransitions = 374 lifecycleLog.getActivityLog(activityClass); 375 log("Observed sequence: " + observedTransitions); 376 final String errorMessage = errorDuringTransition(activityClass, transition); 377 378 boolean oneOfExpectedSequencesObserved = false; 379 for (List<ActivityCallback> transitionVariant : expectedTransitions) { 380 if (transitionVariant.equals(observedTransitions)) { 381 oneOfExpectedSequencesObserved = true; 382 break; 383 } 384 } 385 assertTrue(errorMessage + "\nObserved transitions: " + observedTransitions 386 + "\nExpected one of: " + expectedTransitions, 387 oneOfExpectedSequencesObserved); 388 } 389 390 /** Assert the entire sequence for all involved activities. */ assertEntireSequence( List<Pair<String, ActivityCallback>> expectedTransitions, LifecycleLog lifecycleLog, String message)391 static void assertEntireSequence( 392 List<Pair<String, ActivityCallback>> expectedTransitions, 393 LifecycleLog lifecycleLog, String message) { 394 final List<Pair<String, ActivityCallback>> observedTransitions = lifecycleLog.getLog(); 395 assertEquals(message, expectedTransitions, observedTransitions); 396 } 397 transition(Class<? extends Activity> activityClass, ActivityCallback state)398 static Pair<String, ActivityCallback> transition(Class<? extends Activity> activityClass, 399 ActivityCallback state) { 400 return new Pair<>(activityClass.getCanonicalName(), state); 401 } 402 errorDuringTransition(Class<? extends Activity> activityClass, String transition)403 private static String errorDuringTransition(Class<? extends Activity> activityClass, 404 String transition) { 405 return "Failed verification during moving activity: " + activityClass.getCanonicalName() 406 + " through transition: " + transition; 407 } 408 } 409