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