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