blob: 31bb9aea31ce8de235fbf66faa3da130266d8874 [file] [log] [blame]
/*
* Copyright (C) 2022 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.wm.lifecycle;
import static org.junit.Assert.fail;
import android.app.Activity;
import android.util.Pair;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BooleanSupplier;
/**
* Gets notified about activity lifecycle updates and provides blocking mechanism to wait until
* expected activity states are reached.
*/
public class LifecycleTracker implements LifecycleLog.LifecycleTrackerCallback {
private static final int TIMEOUT = 5 * 1000;
private LifecycleLog mLifecycleLog;
public LifecycleTracker(LifecycleLog lifecycleLog) {
mLifecycleLog = lifecycleLog;
mLifecycleLog.setLifecycleTracker(this);
}
void waitAndAssertActivityStates(
Pair<Class<? extends Activity>, String>[] activityCallbacks) {
final boolean waitResult = waitForConditionWithTimeout(
() -> pendingCallbacks(activityCallbacks).isEmpty());
if (!waitResult) {
fail("Expected lifecycle states not achieved: " + pendingCallbacks(activityCallbacks));
}
}
void waitAndAssertActivityCurrentState(Class<? extends Activity> activityClass,
String expectedState) {
final boolean waitResult = waitForConditionWithTimeout(() -> {
List<String> activityLog = mLifecycleLog.getActivityLog(activityClass);
String currentState = activityLog.get(activityLog.size() - 1);
return expectedState.equals(currentState);
});
if (!waitResult) {
fail("Lifecycle state did not settle with the expected current state of "
+ expectedState + " : " + mLifecycleLog.getActivityLog(activityClass));
}
}
/**
* Waits for a specific sequence of events to happen.
* When there is a possibility of some lifecycle state happening more than once in a sequence,
* it is better to use this method instead of {@link #waitAndAssertActivityStates(Pair[])}.
* Otherwise we might stop tracking too early.
*/
public void waitForActivityTransitions(Class<? extends Activity> activityClass,
List<String> expectedTransitions) {
waitForConditionWithTimeout(
() -> mLifecycleLog.getActivityLog(activityClass).equals(expectedTransitions));
}
@Override
public synchronized void onActivityLifecycleChanged() {
notify();
}
/** Get a list of activity states that were not reached yet. */
private List<Pair<Class<? extends Activity>, String>> pendingCallbacks(
Pair<Class<? extends Activity>, String>[] activityCallbacks) {
final List<Pair<Class<? extends Activity>, String>> notReachedActivityCallbacks =
new ArrayList<>();
for (Pair<Class<? extends Activity>, String> callbackPair : activityCallbacks) {
final Class<? extends Activity> activityClass = callbackPair.first;
final List<String> transitionList =
mLifecycleLog.getActivityLog(activityClass);
if (transitionList.isEmpty()
|| !transitionList.get(transitionList.size() - 1).equals(callbackPair.second)) {
// The activity either hasn't got any state transitions yet or the current state is
// not the one we expect.
notReachedActivityCallbacks.add(callbackPair);
}
}
return notReachedActivityCallbacks;
}
public boolean waitForConditionWithTimeout(BooleanSupplier waitCondition) {
return waitForConditionWithTimeout(waitCondition, TIMEOUT);
}
/** Blocking call to wait for a condition to become true with max timeout. */
private synchronized boolean waitForConditionWithTimeout(BooleanSupplier waitCondition,
long timeoutMs) {
final long timeout = System.currentTimeMillis() + timeoutMs;
while (!waitCondition.getAsBoolean()) {
final long waitMs = timeout - System.currentTimeMillis();
if (waitMs <= 0) {
// Timeout expired.
return false;
}
try {
wait(timeoutMs);
} catch (InterruptedException e) {
// Weird, let's retry.
}
}
return true;
}
}