1 /* 2 * Copyright (C) 2023 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.activity.lifecycle; 18 19 import static android.server.wm.StateLogger.log; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertFalse; 23 import static org.junit.Assert.assertTrue; 24 import static org.junit.Assert.fail; 25 26 import android.app.Activity; 27 import android.util.Pair; 28 29 import java.util.ArrayList; 30 import java.util.Arrays; 31 import java.util.List; 32 33 /** Util class that verifies correct event and state transition sequences. */ 34 public class TransitionVerifier { 35 36 private static final Class CALLBACK_TRACKING_CLASS = CallbackTrackingActivity.class; 37 private static final Class CONFIG_CHANGE_HANDLING_CLASS = 38 LifecycleConfigChangeHandlingActivity.class; 39 assertLaunchSequence( Class<? extends Activity> activityClass, EventLog eventLog, String... expectedSubsequentEvents)40 static void assertLaunchSequence( 41 Class<? extends Activity> activityClass, 42 EventLog eventLog, 43 String... expectedSubsequentEvents) { 44 final List<String> observedTransitions = eventLog.getActivityLog(activityClass); 45 log("Observed sequence: " + observedTransitions); 46 final String errorMessage = errorDuringTransition(activityClass, "launch"); 47 48 final List<String> launchSequence = getLaunchSequence(activityClass); 49 final List<String> expectedTransitions; 50 expectedTransitions = 51 new ArrayList<>(launchSequence.size() + expectedSubsequentEvents.length); 52 expectedTransitions.addAll(launchSequence); 53 expectedTransitions.addAll(Arrays.asList(expectedSubsequentEvents)); 54 assertEquals(errorMessage, expectedTransitions, observedTransitions); 55 } 56 getLaunchSequence(Class<? extends Activity> activityClass)57 public static List<String> getLaunchSequence(Class<? extends Activity> activityClass) { 58 return CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass) 59 ? Arrays.asList( 60 LifecycleConstants.ON_CREATE, 61 LifecycleConstants.ON_START, 62 LifecycleConstants.ON_POST_CREATE, 63 LifecycleConstants.ON_RESUME, 64 LifecycleConstants.ON_TOP_POSITION_GAINED) 65 : Arrays.asList( 66 LifecycleConstants.ON_CREATE, 67 LifecycleConstants.ON_START, 68 LifecycleConstants.ON_RESUME); 69 } 70 getLaunchAndDestroySequence(Class<? extends Activity> activityClass)71 static List<String> getLaunchAndDestroySequence(Class<? extends Activity> activityClass) { 72 final List<String> expectedTransitions = new ArrayList<>(); 73 expectedTransitions.addAll(getLaunchSequence(activityClass)); 74 expectedTransitions.addAll(getResumeToDestroySequence(activityClass)); 75 return expectedTransitions; 76 } 77 assertLaunchSequence( Class<? extends Activity> launchingActivity, Class<? extends Activity> existingActivity, EventLog eventLog, boolean launchingIsTranslucent)78 static void assertLaunchSequence( 79 Class<? extends Activity> launchingActivity, 80 Class<? extends Activity> existingActivity, 81 EventLog eventLog, 82 boolean launchingIsTranslucent) { 83 final boolean includingCallbacks; 84 if (CALLBACK_TRACKING_CLASS.isAssignableFrom(launchingActivity) 85 && CALLBACK_TRACKING_CLASS.isAssignableFrom(existingActivity)) { 86 includingCallbacks = true; 87 } else if (!CALLBACK_TRACKING_CLASS.isAssignableFrom(launchingActivity) 88 && !CALLBACK_TRACKING_CLASS.isAssignableFrom(existingActivity)) { 89 includingCallbacks = false; 90 } else { 91 throw new IllegalArgumentException( 92 "Mixed types of callback tracking not supported. " 93 + "Both activities must support or not support callback tracking " 94 + "simultaneously"); 95 } 96 97 final List<Pair<String, String>> observedTransitions = eventLog.getLog(); 98 log("Observed sequence: " + observedTransitions); 99 final String errorMessage = errorDuringTransition(launchingActivity, "launch"); 100 101 final List<Pair<String, String>> expectedTransitions = new ArrayList<>(); 102 // First top position will be lost 103 if (includingCallbacks) { 104 expectedTransitions.add( 105 transition(existingActivity, LifecycleConstants.ON_TOP_POSITION_LOST)); 106 } 107 // Next the existing activity is paused and the next one is launched 108 expectedTransitions.add(transition(existingActivity, LifecycleConstants.ON_PAUSE)); 109 expectedTransitions.add(transition(launchingActivity, LifecycleConstants.ON_CREATE)); 110 expectedTransitions.add(transition(launchingActivity, LifecycleConstants.ON_START)); 111 if (includingCallbacks) { 112 expectedTransitions.add( 113 transition(launchingActivity, LifecycleConstants.ON_POST_CREATE)); 114 } 115 expectedTransitions.add(transition(launchingActivity, LifecycleConstants.ON_RESUME)); 116 if (includingCallbacks) { 117 expectedTransitions.add( 118 transition(launchingActivity, LifecycleConstants.ON_TOP_POSITION_GAINED)); 119 } 120 if (!launchingIsTranslucent) { 121 expectedTransitions.add(transition(existingActivity, LifecycleConstants.ON_STOP)); 122 } 123 124 assertEquals(errorMessage, expectedTransitions, observedTransitions); 125 } 126 assertLaunchAndStopSequence( Class<? extends Activity> activityClass, EventLog eventLog)127 static void assertLaunchAndStopSequence( 128 Class<? extends Activity> activityClass, EventLog eventLog) { 129 assertLaunchAndStopSequence(activityClass, eventLog, false /* onTop */); 130 } 131 assertLaunchAndStopSequence( Class<? extends Activity> activityClass, EventLog eventLog, boolean onTop)132 static void assertLaunchAndStopSequence( 133 Class<? extends Activity> activityClass, EventLog eventLog, boolean onTop) { 134 final List<String> observedTransitions = eventLog.getActivityLog(activityClass); 135 log("Observed sequence: " + observedTransitions); 136 final String errorMessage = errorDuringTransition(activityClass, "launch and stop"); 137 138 final boolean includeCallbacks = CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass); 139 140 final List<String> expectedTransitions = 141 new ArrayList<>( 142 Arrays.asList(LifecycleConstants.ON_CREATE, LifecycleConstants.ON_START)); 143 if (includeCallbacks) { 144 expectedTransitions.add(LifecycleConstants.ON_POST_CREATE); 145 } 146 expectedTransitions.add(LifecycleConstants.ON_RESUME); 147 if (includeCallbacks && onTop) { 148 expectedTransitions.addAll( 149 Arrays.asList( 150 LifecycleConstants.ON_TOP_POSITION_GAINED, 151 LifecycleConstants.ON_TOP_POSITION_LOST)); 152 } 153 expectedTransitions.addAll( 154 Arrays.asList(LifecycleConstants.ON_PAUSE, LifecycleConstants.ON_STOP)); 155 assertEquals(errorMessage, expectedTransitions, observedTransitions); 156 } 157 assertLaunchAndPauseSequence( Class<? extends Activity> activityClass, EventLog eventLog)158 static void assertLaunchAndPauseSequence( 159 Class<? extends Activity> activityClass, EventLog eventLog) { 160 final List<String> observedTransitions = eventLog.getActivityLog(activityClass); 161 log("Observed sequence: " + observedTransitions); 162 final String errorMessage = errorDuringTransition(activityClass, "launch and pause"); 163 164 final List<String> expectedTransitions = 165 Arrays.asList( 166 LifecycleConstants.ON_CREATE, 167 LifecycleConstants.ON_START, 168 LifecycleConstants.ON_RESUME, 169 LifecycleConstants.ON_PAUSE); 170 assertEquals(errorMessage, expectedTransitions, observedTransitions); 171 } 172 assertRestartSequence(Class<? extends Activity> activityClass, EventLog eventLog)173 static void assertRestartSequence(Class<? extends Activity> activityClass, EventLog eventLog) { 174 final List<String> observedTransitions = eventLog.getActivityLog(activityClass); 175 log("Observed sequence: " + observedTransitions); 176 final String errorMessage = errorDuringTransition(activityClass, "restart"); 177 178 final List<String> expectedTransitions = 179 Arrays.asList(LifecycleConstants.ON_RESTART, LifecycleConstants.ON_START); 180 assertEquals(errorMessage, expectedTransitions, observedTransitions); 181 } 182 assertRestartAndResumeSequence( Class<? extends Activity> activityClass, EventLog eventLog)183 static void assertRestartAndResumeSequence( 184 Class<? extends Activity> activityClass, EventLog eventLog) { 185 final List<String> observedTransitions = eventLog.getActivityLog(activityClass); 186 log("Observed sequence: " + observedTransitions); 187 final String errorMessage = errorDuringTransition(activityClass, "restart and pause"); 188 189 final List<String> expectedTransitions = 190 Arrays.asList( 191 LifecycleConstants.ON_RESTART, 192 LifecycleConstants.ON_START, 193 LifecycleConstants.ON_RESUME); 194 assertEquals(errorMessage, expectedTransitions, observedTransitions); 195 } 196 197 /** 198 * TODO(b/192274045): In Automotive, we tolerate superfluous lifecycle events between the first 199 * lifecycle events and the last one until any discrepancy between ActivityManager and Keyguard 200 * state is resolved. 201 */ assertRestartAndResumeSubSequence( Class<? extends Activity> activityClass, EventLog eventLog)202 static void assertRestartAndResumeSubSequence( 203 Class<? extends Activity> activityClass, EventLog eventLog) { 204 final List<String> observedTransitions = eventLog.getActivityLog(activityClass); 205 log("Observed sequence: " + observedTransitions); 206 207 final List<Pair<String, String>> expectedTransitions = 208 Arrays.asList( 209 transition(activityClass, LifecycleConstants.ON_RESTART), 210 transition(activityClass, LifecycleConstants.ON_START), 211 transition(activityClass, LifecycleConstants.ON_RESUME)); 212 213 assertOrder(eventLog, expectedTransitions, "restart and resume"); 214 } 215 assertRecreateAndResumeSequence( Class<? extends Activity> activityClass, EventLog eventLog)216 static void assertRecreateAndResumeSequence( 217 Class<? extends Activity> activityClass, EventLog eventLog) { 218 final List<String> observedTransitions = eventLog.getActivityLog(activityClass); 219 log("Observed sequence: " + observedTransitions); 220 final String errorMessage = errorDuringTransition(activityClass, "recreateA and pause"); 221 222 final List<String> expectedTransitions = 223 Arrays.asList( 224 LifecycleConstants.ON_DESTROY, 225 LifecycleConstants.ON_CREATE, 226 LifecycleConstants.ON_START, 227 LifecycleConstants.ON_RESUME); 228 assertEquals(errorMessage, expectedTransitions, observedTransitions); 229 } 230 assertLaunchAndDestroySequence( Class<? extends Activity> activityClass, EventLog eventLog)231 static void assertLaunchAndDestroySequence( 232 Class<? extends Activity> activityClass, EventLog eventLog) { 233 final List<String> observedTransitions = eventLog.getActivityLog(activityClass); 234 log("Observed sequence: " + observedTransitions); 235 final String errorMessage = errorDuringTransition(activityClass, "launch and destroy"); 236 237 final List<String> expectedTransitions = 238 Arrays.asList( 239 LifecycleConstants.ON_CREATE, 240 LifecycleConstants.ON_START, 241 LifecycleConstants.ON_RESUME, 242 LifecycleConstants.ON_PAUSE, 243 LifecycleConstants.ON_STOP, 244 LifecycleConstants.ON_DESTROY); 245 assertEquals(errorMessage, expectedTransitions, observedTransitions); 246 } 247 assertResumeToDestroySequence( Class<? extends Activity> activityClass, EventLog eventLog)248 static void assertResumeToDestroySequence( 249 Class<? extends Activity> activityClass, EventLog eventLog) { 250 final List<String> observedTransitions = eventLog.getActivityLog(activityClass); 251 log("Observed sequence: " + observedTransitions); 252 final String errorMessage = errorDuringTransition(activityClass, "launch and destroy"); 253 254 final List<String> expectedTransitions = getResumeToDestroySequence(activityClass); 255 assertEquals(errorMessage, expectedTransitions, observedTransitions); 256 } 257 getResumeToDestroySequence(Class<? extends Activity> activityClass)258 static List<String> getResumeToDestroySequence(Class<? extends Activity> activityClass) { 259 return CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass) 260 ? Arrays.asList( 261 LifecycleConstants.ON_TOP_POSITION_LOST, 262 LifecycleConstants.ON_PAUSE, 263 LifecycleConstants.ON_STOP, 264 LifecycleConstants.ON_DESTROY) 265 : Arrays.asList( 266 LifecycleConstants.ON_PAUSE, 267 LifecycleConstants.ON_STOP, 268 LifecycleConstants.ON_DESTROY); 269 } 270 assertResumeToStopSequence( Class<? extends Activity> activityClass, EventLog eventLog)271 static void assertResumeToStopSequence( 272 Class<? extends Activity> activityClass, EventLog eventLog) { 273 final List<String> observedTransitions = eventLog.getActivityLog(activityClass); 274 log("Observed sequence: " + observedTransitions); 275 final String errorMessage = errorDuringTransition(activityClass, "resumed to stopped"); 276 final boolean includeCallbacks = CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass); 277 278 final List<String> expectedTransitions = new ArrayList<>(); 279 if (includeCallbacks) { 280 expectedTransitions.add(LifecycleConstants.ON_TOP_POSITION_LOST); 281 } 282 expectedTransitions.add(LifecycleConstants.ON_PAUSE); 283 expectedTransitions.add(LifecycleConstants.ON_STOP); 284 285 assertEquals(errorMessage, expectedTransitions, observedTransitions); 286 } 287 assertStopToResumeSequence( Class<? extends Activity> activityClass, EventLog eventLog)288 static void assertStopToResumeSequence( 289 Class<? extends Activity> activityClass, EventLog eventLog) { 290 final List<String> observedTransitions = eventLog.getActivityLog(activityClass); 291 log("Observed sequence: " + observedTransitions); 292 final String errorMessage = errorDuringTransition(activityClass, "stopped to resumed"); 293 final boolean includeCallbacks = CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass); 294 295 final List<String> expectedTransitions = 296 new ArrayList<>( 297 Arrays.asList( 298 LifecycleConstants.ON_RESTART, 299 LifecycleConstants.ON_START, 300 LifecycleConstants.ON_RESUME)); 301 if (includeCallbacks) { 302 expectedTransitions.add(LifecycleConstants.ON_TOP_POSITION_GAINED); 303 } 304 305 assertEquals(errorMessage, expectedTransitions, observedTransitions); 306 } 307 308 /** 309 * TODO(b/192274045): In Automotive, we tolerate superfluous lifecycle events between the first 310 * lifecycle events and the last one until any discrepancy between ActivityManager and Keyguard 311 * state is resolved. 312 */ assertStopToResumeSubSequence( Class<? extends Activity> activityClass, EventLog eventLog)313 static void assertStopToResumeSubSequence( 314 Class<? extends Activity> activityClass, EventLog eventLog) { 315 final List<String> observedTransitions = eventLog.getActivityLog(activityClass); 316 log("Observed sequence: " + observedTransitions); 317 final boolean includeCallbacks = CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass); 318 319 final List<Pair<String, String>> expectedTransitions = 320 new ArrayList<>( 321 Arrays.asList( 322 transition(activityClass, LifecycleConstants.ON_RESTART), 323 transition(activityClass, LifecycleConstants.ON_START), 324 transition(activityClass, LifecycleConstants.ON_RESUME))); 325 if (includeCallbacks) { 326 expectedTransitions.add( 327 transition(activityClass, LifecycleConstants.ON_TOP_POSITION_GAINED)); 328 } 329 330 assertOrder(eventLog, expectedTransitions, "stop and resume"); 331 } 332 assertRelaunchSequence( Class<? extends Activity> activityClass, EventLog eventLog, String startState)333 static void assertRelaunchSequence( 334 Class<? extends Activity> activityClass, EventLog eventLog, String startState) { 335 final List<String> expectedTransitions = getRelaunchSequence(startState); 336 assertSequence(activityClass, eventLog, expectedTransitions, "relaunch"); 337 } 338 getRelaunchSequence(String startState)339 static List<String> getRelaunchSequence(String startState) { 340 final List<String> expectedTransitions; 341 if (startState.equals(LifecycleConstants.ON_PAUSE)) { 342 expectedTransitions = 343 Arrays.asList( 344 LifecycleConstants.ON_STOP, 345 LifecycleConstants.ON_DESTROY, 346 LifecycleConstants.ON_CREATE, 347 LifecycleConstants.ON_START, 348 LifecycleConstants.ON_RESUME, 349 LifecycleConstants.ON_PAUSE); 350 } else if (startState.equals(LifecycleConstants.ON_STOP)) { 351 expectedTransitions = 352 Arrays.asList( 353 LifecycleConstants.ON_DESTROY, 354 LifecycleConstants.ON_CREATE, 355 LifecycleConstants.ON_START, 356 LifecycleConstants.ON_RESUME, 357 LifecycleConstants.ON_PAUSE, 358 LifecycleConstants.ON_STOP); 359 } else if (startState.equals(LifecycleConstants.ON_RESUME)) { 360 expectedTransitions = 361 Arrays.asList( 362 LifecycleConstants.ON_PAUSE, 363 LifecycleConstants.ON_STOP, 364 LifecycleConstants.ON_DESTROY, 365 LifecycleConstants.ON_CREATE, 366 LifecycleConstants.ON_START, 367 LifecycleConstants.ON_RESUME); 368 } else if (startState.equals(LifecycleConstants.ON_TOP_POSITION_GAINED)) { 369 // Looks like we're tracking the callbacks here 370 expectedTransitions = 371 Arrays.asList( 372 LifecycleConstants.ON_TOP_POSITION_LOST, 373 LifecycleConstants.ON_PAUSE, 374 LifecycleConstants.ON_STOP, 375 LifecycleConstants.ON_DESTROY, 376 LifecycleConstants.ON_CREATE, 377 LifecycleConstants.ON_START, 378 LifecycleConstants.ON_POST_CREATE, 379 LifecycleConstants.ON_RESUME, 380 LifecycleConstants.ON_TOP_POSITION_GAINED); 381 } else { 382 throw new IllegalArgumentException("Start state not supported: " + startState); 383 } 384 return expectedTransitions; 385 } 386 getSplitScreenTransitionSequence(Class<? extends Activity> activityClass)387 static List<String> getSplitScreenTransitionSequence(Class<? extends Activity> activityClass) { 388 // Minimized-dock is not a policy requirement and but SysUI-specific concept, so we here 389 // don't expect a trailing ON_PAUSE. 390 return CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass) 391 ? CONFIG_CHANGE_HANDLING_CLASS.isAssignableFrom(activityClass) 392 ? Arrays.asList( 393 LifecycleConstants.ON_MULTI_WINDOW_MODE_CHANGED, 394 LifecycleConstants.ON_TOP_POSITION_LOST) 395 : Arrays.asList( 396 LifecycleConstants.ON_TOP_POSITION_LOST, 397 LifecycleConstants.ON_PAUSE, 398 LifecycleConstants.ON_STOP, 399 LifecycleConstants.ON_DESTROY, 400 LifecycleConstants.ON_CREATE, 401 LifecycleConstants.ON_START, 402 LifecycleConstants.ON_POST_CREATE, 403 LifecycleConstants.ON_RESUME, 404 LifecycleConstants.ON_TOP_POSITION_GAINED, 405 LifecycleConstants.ON_TOP_POSITION_LOST) 406 : Arrays.asList( 407 LifecycleConstants.ON_PAUSE, 408 LifecycleConstants.ON_STOP, 409 LifecycleConstants.ON_DESTROY, 410 LifecycleConstants.ON_CREATE, 411 LifecycleConstants.ON_START, 412 LifecycleConstants.ON_RESUME); 413 } 414 415 // TODO(b/149338177): Remove this workaround once test passes with TestTaskOrganizer not to 416 // depend on minimized dock feature which is not policy requirement, but SysUI-specific. 417 /** 418 * Returns the result of appending "leave from minimized dock" transitions to given transitions 419 * to "consume" these activity callbacks. 420 */ appendMinimizedDockTransitionTrail(List<String> transitions)421 static List<String> appendMinimizedDockTransitionTrail(List<String> transitions) { 422 final List<String> newTransitions = new ArrayList<>(transitions); 423 newTransitions.addAll( 424 Arrays.asList(LifecycleConstants.ON_PAUSE, LifecycleConstants.ON_RESUME)); 425 426 return newTransitions; 427 } 428 assertSequence( Class<? extends Activity> activityClass, EventLog eventLog, List<String> expectedTransitions, String transition)429 static void assertSequence( 430 Class<? extends Activity> activityClass, 431 EventLog eventLog, 432 List<String> expectedTransitions, 433 String transition) { 434 final List<String> observedTransitions = eventLog.getActivityLog(activityClass); 435 log("Observed sequence: " + observedTransitions); 436 final String errorMessage = errorDuringTransition(activityClass, transition); 437 438 assertEquals(errorMessage, expectedTransitions, observedTransitions); 439 } 440 441 /** 442 * Assert that the observed transitions of a particular activity happened in expected order. 443 * There may be more observed transitions than in the expected array, only their order matters. 444 * 445 * <p>Use this method when there is no need to verify the entire sequence, only that some 446 * transitions happened after another. 447 */ assertOrder( EventLog eventLog, Class<? extends Activity> activityClass, List<String> expectedTransitionsOrder, String transition)448 static void assertOrder( 449 EventLog eventLog, 450 Class<? extends Activity> activityClass, 451 List<String> expectedTransitionsOrder, 452 String transition) { 453 List<Pair<String, String>> expectedTransitions = new ArrayList<>(); 454 for (String callback : expectedTransitionsOrder) { 455 expectedTransitions.add(transition(activityClass, callback)); 456 } 457 assertOrder(eventLog, expectedTransitions, transition); 458 } 459 460 /** 461 * Assert that the observed transitions happened in expected order. There may be more observed 462 * transitions than in the expected array, only their order matters. 463 * 464 * <p>Use this method when there is no need to verify the entire sequence, only that some 465 * transitions happened after another. 466 */ assertOrder( EventLog eventLog, List<Pair<String, String>> expectedTransitionsOrder, String transition)467 public static void assertOrder( 468 EventLog eventLog, 469 List<Pair<String, String>> expectedTransitionsOrder, 470 String transition) { 471 String result = checkOrderAndReturnError(eventLog, expectedTransitionsOrder, transition); 472 if (result != null) { 473 fail(result); 474 } 475 } 476 477 /** 478 * Same as {@link #assertOrder(EventLog, List, String)}, but returns the String with error if it 479 * occurs. Otherwise returns {@code null} 480 */ checkOrderAndReturnError( EventLog eventLog, List<Pair<String, String>> expectedTransitionsOrder, String transition)481 public static String checkOrderAndReturnError( 482 EventLog eventLog, 483 List<Pair<String, String>> expectedTransitionsOrder, 484 String transition) { 485 final List<Pair<String, String>> observedTransitions = eventLog.getLog(); 486 int nextObservedPosition = 0; 487 for (Pair<String, String> expectedTransition : expectedTransitionsOrder) { 488 while (nextObservedPosition < observedTransitions.size() 489 && !observedTransitions.get(nextObservedPosition).equals(expectedTransition)) { 490 nextObservedPosition++; 491 } 492 if (nextObservedPosition == observedTransitions.size()) { 493 return "Transition wasn't observed in the expected position: " 494 + expectedTransition 495 + " during transition: " 496 + transition; 497 } 498 } 499 return null; 500 } 501 502 /** Assert that a transition was observer, no particular order. */ assertTransitionObserved( EventLog eventLog, Pair<String, String> expectedTransition, String transition)503 public static void assertTransitionObserved( 504 EventLog eventLog, Pair<String, String> expectedTransition, String transition) { 505 assertTrue( 506 "Transition " + expectedTransition + " must be observed during " + transition, 507 eventLog.getLog().contains(expectedTransition)); 508 } 509 510 /** 511 * Same as {@link #checkOrderAndReturnError(EventLog, List, String)}, but returns {@code false} 512 * if the order does not match. Otherwise returns {@code true} 513 */ checkOrder( EventLog eventLog, List<Pair<String, String>> expectedTransitionsOrder)514 public static boolean checkOrder( 515 EventLog eventLog, List<Pair<String, String>> expectedTransitionsOrder) { 516 String result = checkOrderAndReturnError(eventLog, expectedTransitionsOrder, null); 517 return result == null; 518 } 519 520 /** Assert that a transition was not observer, no particular order. */ assertTransitionNotObserved( EventLog eventLog, Pair<String, String> expectedTransition, String transition)521 static void assertTransitionNotObserved( 522 EventLog eventLog, Pair<String, String> expectedTransition, String transition) { 523 assertFalse( 524 "Transition " + expectedTransition + " must not be observed during " + transition, 525 eventLog.getLog().contains(expectedTransition)); 526 } 527 assertEmptySequence( Class<? extends Activity> activityClass, EventLog eventLog, String transition)528 static void assertEmptySequence( 529 Class<? extends Activity> activityClass, EventLog eventLog, String transition) { 530 assertSequence(activityClass, eventLog, new ArrayList<>(), transition); 531 } 532 533 /** Assert that a lifecycle sequence matches one of the possible variants. */ assertSequenceMatchesOneOf( Class<? extends Activity> activityClass, EventLog eventLog, List<List<String>> expectedTransitions, String transition)534 static void assertSequenceMatchesOneOf( 535 Class<? extends Activity> activityClass, 536 EventLog eventLog, 537 List<List<String>> expectedTransitions, 538 String transition) { 539 final List<String> observedTransitions = eventLog.getActivityLog(activityClass); 540 log("Observed sequence: " + observedTransitions); 541 final String errorMessage = errorDuringTransition(activityClass, transition); 542 543 boolean oneOfExpectedSequencesObserved = false; 544 for (List<String> transitionVariant : expectedTransitions) { 545 if (transitionVariant.equals(observedTransitions)) { 546 oneOfExpectedSequencesObserved = true; 547 break; 548 } 549 } 550 assertTrue( 551 errorMessage 552 + "\nObserved transitions: " 553 + observedTransitions 554 + "\nExpected one of: " 555 + expectedTransitions, 556 oneOfExpectedSequencesObserved); 557 } 558 559 /** Assert the entire sequence for all involved activities. */ assertEntireSequence( List<Pair<String, String>> expectedTransitions, EventLog eventLog, String message)560 static void assertEntireSequence( 561 List<Pair<String, String>> expectedTransitions, EventLog eventLog, String message) { 562 final List<Pair<String, String>> observedTransitions = eventLog.getLog(); 563 assertEquals(message, expectedTransitions, observedTransitions); 564 } 565 transition( Class<? extends Activity> activityClass, String state)566 public static Pair<String, String> transition( 567 Class<? extends Activity> activityClass, String state) { 568 return new Pair<>(activityClass.getCanonicalName(), state); 569 } 570 transition(String owner, String state)571 public static Pair<String, String> transition(String owner, String state) { 572 return new Pair<>(owner, state); 573 } 574 errorDuringTransition( Class<? extends Activity> activityClass, String transition)575 private static String errorDuringTransition( 576 Class<? extends Activity> activityClass, String transition) { 577 return "Failed verification during moving activity: " 578 + activityClass.getCanonicalName() 579 + " through transition: " 580 + transition; 581 } 582 } 583