• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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