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