blob: a289682cbfb46255467903b37c859f30212a4875 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package android.server.am.lifecycle;
import static android.server.am.StateLogger.log;
import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_CREATE;
import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_DESTROY;
import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED;
import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_PAUSE;
import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_POST_CREATE;
import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_RESTART;
import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_RESUME;
import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_START;
import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_STOP;
import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_TOP_POSITION_GAINED;
import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_TOP_POSITION_LOST;
import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.PRE_ON_CREATE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.app.Activity;
import android.server.am.lifecycle.ActivityLifecycleClientTestBase.CallbackTrackingActivity;
import android.server.am.lifecycle.LifecycleLog.ActivityCallback;
import android.util.Pair;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/** Util class that verifies correct activity state transition sequences. */
class LifecycleVerifier {
private static final Class CALLBACK_TRACKING_CLASS = CallbackTrackingActivity.class;
static void assertLaunchSequence(Class<? extends Activity> activityClass,
LifecycleLog lifecycleLog) {
final List<ActivityCallback> observedTransitions =
lifecycleLog.getActivityLog(activityClass);
log("Observed sequence: " + observedTransitions);
final String errorMessage = errorDuringTransition(activityClass, "launch");
final List<ActivityCallback> expectedTransitions = getLaunchSequence(activityClass);
assertEquals(errorMessage, expectedTransitions, observedTransitions);
}
public static List<ActivityCallback> getLaunchSequence(
Class<? extends Activity> activityClass) {
return CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass)
? Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME,
ON_TOP_POSITION_GAINED)
: Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME);
}
static void assertLaunchSequence(Class<? extends Activity> launchingActivity,
Class<? extends Activity> existingActivity, LifecycleLog lifecycleLog,
boolean launchingIsTranslucent) {
final boolean includingCallbacks;
if (CALLBACK_TRACKING_CLASS.isAssignableFrom(launchingActivity)
&& CALLBACK_TRACKING_CLASS.isAssignableFrom(existingActivity)) {
includingCallbacks = true;
} else if (!CALLBACK_TRACKING_CLASS.isAssignableFrom(launchingActivity)
&& !CALLBACK_TRACKING_CLASS.isAssignableFrom(existingActivity)) {
includingCallbacks = false;
} else {
throw new IllegalArgumentException("Mixed types of callback tracking not supported. "
+ "Both activities must support or not support callback tracking "
+ "simultaneously");
}
final List<Pair<String, ActivityCallback>> observedTransitions = lifecycleLog.getLog();
log("Observed sequence: " + observedTransitions);
final String errorMessage = errorDuringTransition(launchingActivity, "launch");
final List<Pair<String, ActivityCallback>> expectedTransitions = new ArrayList<>();
// First top position will be lost
if (includingCallbacks) {
expectedTransitions.add(transition(existingActivity, ON_TOP_POSITION_LOST));
}
// Next the existing activity is paused and the next one is launched
expectedTransitions.add(transition(existingActivity, ON_PAUSE));
expectedTransitions.add(transition(launchingActivity, PRE_ON_CREATE));
expectedTransitions.add(transition(launchingActivity, ON_CREATE));
expectedTransitions.add(transition(launchingActivity, ON_START));
if (includingCallbacks) {
expectedTransitions.add(transition(launchingActivity, ON_POST_CREATE));
}
expectedTransitions.add(transition(launchingActivity, ON_RESUME));
if (includingCallbacks) {
expectedTransitions.add(transition(launchingActivity, ON_TOP_POSITION_GAINED));
}
if (!launchingIsTranslucent) {
expectedTransitions.add(transition(existingActivity, ON_STOP));
}
assertEquals(errorMessage, expectedTransitions, observedTransitions);
}
static void assertLaunchAndStopSequence(Class<? extends Activity> activityClass,
LifecycleLog lifecycleLog) {
assertLaunchAndStopSequence(activityClass, lifecycleLog,
false /* onTop */);
}
static void assertLaunchAndStopSequence(Class<? extends Activity> activityClass,
LifecycleLog lifecycleLog, boolean onTop) {
final List<ActivityCallback> observedTransitions =
lifecycleLog.getActivityLog(activityClass);
log("Observed sequence: " + observedTransitions);
final String errorMessage = errorDuringTransition(activityClass, "launch and stop");
final boolean includeCallbacks = CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass);
final List<ActivityCallback> expectedTransitions = new ArrayList<>();
expectedTransitions.addAll(Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START));
if (includeCallbacks) {
expectedTransitions.add(ON_POST_CREATE);
}
expectedTransitions.add(ON_RESUME);
if (includeCallbacks && onTop) {
expectedTransitions.addAll(Arrays.asList(ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST));
}
expectedTransitions.addAll(Arrays.asList(ON_PAUSE, ON_STOP));
assertEquals(errorMessage, expectedTransitions, observedTransitions);
}
static void assertLaunchAndPauseSequence(Class<? extends Activity> activityClass,
LifecycleLog lifecycleLog) {
final List<ActivityCallback> observedTransitions =
lifecycleLog.getActivityLog(activityClass);
log("Observed sequence: " + observedTransitions);
final String errorMessage = errorDuringTransition(activityClass, "launch and pause");
final List<ActivityCallback> expectedTransitions =
Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME, ON_PAUSE);
assertEquals(errorMessage, expectedTransitions, observedTransitions);
}
static void assertRestartSequence(Class<? extends Activity> activityClass,
LifecycleLog lifecycleLog) {
final List<ActivityCallback> observedTransitions =
lifecycleLog.getActivityLog(activityClass);
log("Observed sequence: " + observedTransitions);
final String errorMessage = errorDuringTransition(activityClass, "restart");
final List<ActivityCallback> expectedTransitions =
Arrays.asList(ON_RESTART, ON_START);
assertEquals(errorMessage, expectedTransitions, observedTransitions);
}
static void assertRestartAndResumeSequence(Class<? extends Activity> activityClass,
LifecycleLog lifecycleLog) {
final List<ActivityCallback> observedTransitions =
lifecycleLog.getActivityLog(activityClass);
log("Observed sequence: " + observedTransitions);
final String errorMessage = errorDuringTransition(activityClass, "restart and pause");
final List<ActivityCallback> expectedTransitions =
Arrays.asList(ON_RESTART, ON_START, ON_RESUME);
assertEquals(errorMessage, expectedTransitions, observedTransitions);
}
static void assertRecreateAndResumeSequence(Class<? extends Activity> activityClass,
LifecycleLog lifecycleLog) {
final List<ActivityCallback> observedTransitions =
lifecycleLog.getActivityLog(activityClass);
log("Observed sequence: " + observedTransitions);
final String errorMessage = errorDuringTransition(activityClass, "recreateA and pause");
final List<ActivityCallback> expectedTransitions =
Arrays.asList(ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME);
assertEquals(errorMessage, expectedTransitions, observedTransitions);
}
static void assertLaunchAndDestroySequence(Class<? extends Activity> activityClass,
LifecycleLog lifecycleLog) {
final List<ActivityCallback> observedTransitions =
lifecycleLog.getActivityLog(activityClass);
log("Observed sequence: " + observedTransitions);
final String errorMessage = errorDuringTransition(activityClass, "launch and destroy");
final List<ActivityCallback> expectedTransitions = Arrays.asList(PRE_ON_CREATE, ON_CREATE,
ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY);
assertEquals(errorMessage, expectedTransitions, observedTransitions);
}
static void assertResumeToDestroySequence(Class<? extends Activity> activityClass,
LifecycleLog lifecycleLog) {
final List<ActivityCallback> observedTransitions =
lifecycleLog.getActivityLog(activityClass);
log("Observed sequence: " + observedTransitions);
final String errorMessage = errorDuringTransition(activityClass, "launch and destroy");
final List<ActivityCallback> expectedTransitions =
getResumeToDestroySequence(activityClass);
assertEquals(errorMessage, expectedTransitions, observedTransitions);
}
static List<ActivityCallback> getResumeToDestroySequence(
Class<? extends Activity> activityClass) {
return CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass)
? Arrays.asList(ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, ON_DESTROY)
: Arrays.asList(ON_PAUSE, ON_STOP, ON_DESTROY);
}
static void assertResumeToStopSequence(Class<? extends Activity> activityClass,
LifecycleLog lifecycleLog) {
final List<ActivityCallback> observedTransitions =
lifecycleLog.getActivityLog(activityClass);
log("Observed sequence: " + observedTransitions);
final String errorMessage = errorDuringTransition(activityClass, "resumed to stopped");
final boolean includeCallbacks = CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass);
final List<ActivityCallback> expectedTransitions = new ArrayList<>();
if (includeCallbacks) {
expectedTransitions.add(ON_TOP_POSITION_LOST);
}
expectedTransitions.add(ON_PAUSE);
expectedTransitions.add(ON_STOP);
assertEquals(errorMessage, expectedTransitions, observedTransitions);
}
static void assertStopToResumeSequence(Class<? extends Activity> activityClass,
LifecycleLog lifecycleLog) {
final List<ActivityCallback> observedTransitions =
lifecycleLog.getActivityLog(activityClass);
log("Observed sequence: " + observedTransitions);
final String errorMessage = errorDuringTransition(activityClass, "stopped to resumed");
final boolean includeCallbacks = CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass);
final List<ActivityCallback> expectedTransitions = new ArrayList<>(
Arrays.asList(ON_RESTART, ON_START, ON_RESUME));
if (includeCallbacks) {
expectedTransitions.add(ON_TOP_POSITION_GAINED);
}
assertEquals(errorMessage, expectedTransitions, observedTransitions);
}
static void assertRelaunchSequence(Class<? extends Activity> activityClass,
LifecycleLog lifecycleLog, ActivityCallback startState) {
final List<ActivityCallback> expectedTransitions = getRelaunchSequence(startState);
assertSequence(activityClass, lifecycleLog, expectedTransitions, "relaunch");
}
static List<ActivityCallback> getRelaunchSequence(ActivityCallback startState) {
final List<ActivityCallback> expectedTransitions;
if (startState == ON_PAUSE) {
expectedTransitions = Arrays.asList(
ON_STOP, ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME, ON_PAUSE);
} else if (startState == ON_STOP) {
expectedTransitions = Arrays.asList(
ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP);
} else if (startState == ON_RESUME) {
expectedTransitions = Arrays.asList(
ON_PAUSE, ON_STOP, ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME);
} else if (startState == ON_TOP_POSITION_GAINED) {
// Looks like we're tracking the callbacks here
expectedTransitions = Arrays.asList(
ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, ON_DESTROY, PRE_ON_CREATE, ON_CREATE,
ON_START, ON_POST_CREATE, ON_RESUME, ON_TOP_POSITION_GAINED);
} else {
throw new IllegalArgumentException("Start state not supported: " + startState);
}
return expectedTransitions;
}
static List<ActivityCallback> getSplitScreenTransitionSequence(
Class<? extends Activity> activityClass) {
return CALLBACK_TRACKING_CLASS.isAssignableFrom(activityClass)
? Arrays.asList(ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, ON_DESTROY, PRE_ON_CREATE,
ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, ON_TOP_POSITION_GAINED,
ON_TOP_POSITION_LOST, ON_PAUSE, ON_MULTI_WINDOW_MODE_CHANGED)
: Arrays.asList(ON_PAUSE, ON_STOP, ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START,
ON_RESUME, ON_PAUSE);
}
static void assertSequence(Class<? extends Activity> activityClass, LifecycleLog lifecycleLog,
List<ActivityCallback> expectedTransitions, String transition) {
final List<ActivityCallback> observedTransitions =
lifecycleLog.getActivityLog(activityClass);
log("Observed sequence: " + observedTransitions);
final String errorMessage = errorDuringTransition(activityClass, transition);
assertEquals(errorMessage, expectedTransitions, observedTransitions);
}
/**
* Assert that the observed transitions of a particular activity happened in expected order.
* There may be more observed transitions than in the expected array, only their order matters.
*
* Use this method when there is no need to verify the entire sequence, only that some
* transitions happened after another.
*/
static void assertOrder(LifecycleLog lifecycleLog, Class<? extends Activity> activityClass,
List<LifecycleLog.ActivityCallback> expectedTransitionsOrder, String transition) {
List<Pair<String, LifecycleLog.ActivityCallback>> expectedTransitions = new ArrayList<>();
for (LifecycleLog.ActivityCallback callback : expectedTransitionsOrder) {
expectedTransitions.add(transition(activityClass, callback));
}
assertOrder(lifecycleLog, expectedTransitions, transition);
}
/**
* Assert that the observed transitions happened in expected order. There may be more observed
* transitions than in the expected array, only their order matters.
*
* Use this method when there is no need to verify the entire sequence, only that some
* transitions happened after another.
*/
static void assertOrder(LifecycleLog lifecycleLog,
List<Pair<String, LifecycleLog.ActivityCallback>> expectedTransitionsOrder,
String transition) {
final List<Pair<String, LifecycleLog.ActivityCallback>> observedTransitions =
lifecycleLog.getLog();
int nextObservedPosition = 0;
for (Pair<String, LifecycleLog.ActivityCallback> expectedTransition
: expectedTransitionsOrder) {
while (nextObservedPosition < observedTransitions.size()
&& !observedTransitions.get(nextObservedPosition).equals(expectedTransition))
{
nextObservedPosition++;
}
if (nextObservedPosition == observedTransitions.size()) {
fail("Transition wasn't observed in the expected position: " + expectedTransition
+ " during transition: " + transition);
}
}
}
/**
* Assert that a transition was observer, no particular order.
*/
static void assertTransitionObserved(LifecycleLog lifecycleLog,
Pair<String, LifecycleLog.ActivityCallback> expectedTransition, String transition) {
assertTrue("Transition " + expectedTransition + " must be observed during " + transition,
lifecycleLog.getLog().contains(expectedTransition));
}
static void assertEmptySequence(Class<? extends Activity> activityClass,
LifecycleLog lifecycleLog, String transition) {
assertSequence(activityClass, lifecycleLog, new ArrayList<>(), transition);
}
/** Assert that a lifecycle sequence matches one of the possible variants. */
static void assertSequenceMatchesOneOf(Class<? extends Activity> activityClass,
LifecycleLog lifecycleLog, List<List<ActivityCallback>> expectedTransitions,
String transition) {
final List<ActivityCallback> observedTransitions =
lifecycleLog.getActivityLog(activityClass);
log("Observed sequence: " + observedTransitions);
final String errorMessage = errorDuringTransition(activityClass, transition);
boolean oneOfExpectedSequencesObserved = false;
for (List<ActivityCallback> transitionVariant : expectedTransitions) {
if (transitionVariant.equals(observedTransitions)) {
oneOfExpectedSequencesObserved = true;
break;
}
}
assertTrue(errorMessage + "\nObserved transitions: " + observedTransitions
+ "\nExpected one of: " + expectedTransitions,
oneOfExpectedSequencesObserved);
}
/** Assert the entire sequence for all involved activities. */
static void assertEntireSequence(
List<Pair<String, LifecycleLog.ActivityCallback>> expectedTransitions,
LifecycleLog lifecycleLog, String message) {
final List<Pair<String, LifecycleLog.ActivityCallback>> observedTransitions =
lifecycleLog.getLog();
assertEquals(message, expectedTransitions, observedTransitions);
}
static Pair<String, ActivityCallback> transition(Class<? extends Activity> activityClass,
ActivityCallback state) {
return new Pair<>(activityClass.getCanonicalName(), state);
}
private static String errorDuringTransition(Class<? extends Activity> activityClass,
String transition) {
return "Failed verification during moving activity: " + activityClass.getCanonicalName()
+ " through transition: " + transition;
}
}