TestScreenManager.java

/*
 * Copyright 2020 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 androidx.car.app.testing;

import static java.util.Objects.requireNonNull;

import android.annotation.SuppressLint;

import androidx.annotation.NonNull;
import androidx.car.app.OnScreenResultListener;
import androidx.car.app.Screen;
import androidx.car.app.ScreenManager;
import androidx.car.app.utils.CollectionUtils;
import androidx.lifecycle.Lifecycle.State;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * The {@link ScreenManager} that is used for testing.
 *
 * <p>This class will track the following usages of the {@link ScreenManager} throughout your test:
 *
 * <ul>
 *   <li>All the {@link Screen}s pushed via {@link ScreenManager#push}, or {@link
 *       ScreenManager#pushForResult}.
 *   <li>All the {@link Screen}s removed via, {@link ScreenManager#pop}, {@link
 *       ScreenManager#popTo}, or {@link ScreenManager#remove}.
 * </ul>
 */
public class TestScreenManager extends ScreenManager {
    private final List<Screen> mScreensPushed = new ArrayList<>();
    private final List<Screen> mScreensRemoved = new ArrayList<>();

    /**
     * Resets the values tracked by this {@link TestScreenManager} and the {@link Screen} stack.
     */
    public void reset() {
        getScreenStack().clear();
        mScreensPushed.clear();
        mScreensRemoved.clear();
    }

    /**
     * Returns all the {@link Screen}s pushed via {@link ScreenManager#push}, and {@link
     * ScreenManager#pushForResult}.
     *
     * <p>The screens are stored in the order in which they were pushed, where the first screen
     * in the list is the first screen that was pushed.
     *
     * <p>The screens will be stored until {@link #reset} is called.
     */
    @NonNull
    public List<Screen> getScreensPushed() {
        return CollectionUtils.unmodifiableCopy(mScreensPushed);
    }

    /**
     * Returns all the {@link Screen}s removed via {@link ScreenManager#pop}, {@link
     * ScreenManager#popTo}, and {@link ScreenManager#remove}.
     *
     * <p>The screens are stored in the order in which they were removed, where the first screen
     * in the list, is the first screen that was removed.
     *
     * <p>The screens will be stored until {@link #reset} is called.
     */
    @NonNull
    public List<Screen> getScreensRemoved() {
        return CollectionUtils.unmodifiableCopy(mScreensRemoved);
    }

    /** Returns {@code true} if the {@link Screen} stack has any screens in it. */
    public boolean hasScreens() {
        return !getScreensInStack().isEmpty();
    }

    @Override
    public void push(@NonNull Screen screen) {
        mScreensPushed.add(requireNonNull(screen));
        super.push(screen);
    }

    @SuppressLint("ExecutorRegistration")
    @Override
    public void pushForResult(
            @NonNull Screen screen, @NonNull OnScreenResultListener onScreenResultListener) {
        mScreensPushed.add(screen);
        super.pushForResult(screen, onScreenResultListener);
    }

    @Override
    public void pop() {
        Screen top = getTop();
        super.pop();

        if (!top.equals(getTop())) {
            mScreensRemoved.add(top);
        }
    }

    @Override
    public void popTo(@NonNull String marker) {
        Set<Screen> screensBefore = getScreensInStack();
        super.popTo(marker);
        Set<Screen> screensAfter = getScreensInStack();

        for (Screen screen : screensBefore) {
            if (!screensAfter.contains(screen)) {
                mScreensRemoved.add(screen);
            }
        }
    }

    @Override
    public void popToRoot() {
        Set<Screen> screensBefore = getScreensInStack();
        super.popToRoot();
        Set<Screen> screensAfter = getScreensInStack();

        for (Screen screen : screensBefore) {
            if (!screensAfter.contains(screen)) {
                mScreensRemoved.add(screen);
            }
        }
    }

    @Override
    public void remove(@NonNull Screen screen) {
        super.remove(screen);
        if (screen.getLifecycle().getCurrentState() == State.DESTROYED) {
            mScreensRemoved.add(screen);
        }
    }

    private Set<Screen> getScreensInStack() {
        return new HashSet<>(getScreenStack());
    }

    TestScreenManager(TestCarContext testCarContext) {
        super(testCarContext, testCarContext.getLifecycleOwner().mRegistry);
    }
}