Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1d070021 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Use FakeTimer in test to avoid flakiness."

parents 67abf64e 802978fd
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -119,7 +119,7 @@ public class ScreenFlashNotificationColorDialogFragment extends DialogFragment i
        synchronized (this) {
            if (mTimer != null) mTimer.cancel();

            mTimer = new Timer();
            mTimer = createTimer();
            if (mIsPreview) {
                mTimer.schedule(getStopTask(), 0);
                startDelay = BETWEEN_STOP_AND_START_DELAY_MS;
@@ -176,4 +176,8 @@ public class ScreenFlashNotificationColorDialogFragment extends DialogFragment i
        getContext().sendBroadcast(stopIntent);
        mIsPreview = false;
    }

    Timer createTimer() {
        return new Timer();
    }
}
+80 −35
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;

import com.android.settings.R;
import com.android.settings.testutils.FakeTimer;

import org.junit.Before;
import org.junit.Test;
@@ -49,9 +50,12 @@ import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.shadows.ShadowContextWrapper;
import org.robolectric.util.ReflectionHelpers;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.function.Consumer;

@RunWith(RobolectricTestRunner.class)
public class ScreenFlashNotificationColorDialogFragmentTest {
@@ -68,9 +72,8 @@ public class ScreenFlashNotificationColorDialogFragmentTest {
        mShadowContextWrapper = shadowOf(fragmentActivity);

        mCurrentColor = ROSE.mColorInt;
        mDialogFragment = ScreenFlashNotificationColorDialogFragment.getInstance(
                mCurrentColor, selectedColor -> mCurrentColor = selectedColor
        );
        mDialogFragment = createFragment();

        mDialogFragment.show(fragmentActivity.getSupportFragmentManager(), "test");

        mAlertDialog = (AlertDialog) mDialogFragment.getDialog();
@@ -91,16 +94,19 @@ public class ScreenFlashNotificationColorDialogFragmentTest {
    }

    @Test
    public void clickNeutral_assertStartPreview() throws InterruptedException {
    public void clickNeutral_assertStartPreview() {
        performClickOnDialog(BUTTON_NEUTRAL);
        Thread.sleep(100);
        getTimerFromFragment().runOneTask();

        Intent captured = getLastCapturedIntent();
        assertThat(captured.getAction()).isEqualTo(ACTION_FLASH_NOTIFICATION_START_PREVIEW);
        assertThat(captured.getIntExtra(EXTRA_FLASH_NOTIFICATION_PREVIEW_TYPE, TYPE_SHORT_PREVIEW))
                .isEqualTo(TYPE_LONG_PREVIEW);
        assertThat(captured.getIntExtra(EXTRA_FLASH_NOTIFICATION_PREVIEW_COLOR, Color.TRANSPARENT))
                .isEqualTo(ROSE.mColorInt);
        assertStartPreview(ROSE.mColorInt);
    }

    @Test
    public void clickNeutral_flushAllScheduledTasks_assertStopPreview() {
        performClickOnDialog(BUTTON_NEUTRAL);
        getTimerFromFragment().runAllTasks();

        assertStopPreview();
    }

    @Test
@@ -116,51 +122,47 @@ public class ScreenFlashNotificationColorDialogFragmentTest {
    }

    @Test
    public void clickNeutralAndPause_assertStopPreview() throws InterruptedException {
    public void clickNeutralAndPause_assertStopPreview() {
        performClickOnDialog(BUTTON_NEUTRAL);
        Thread.sleep(100);
        getTimerFromFragment().runOneTask();
        mDialogFragment.onPause();
        Thread.sleep(100);

        assertThat(getLastCapturedIntent().getAction())
                .isEqualTo(ACTION_FLASH_NOTIFICATION_STOP_PREVIEW);
        assertStopPreview();
    }

    @Test
    public void clickNeutralAndClickNegative_assertStopPreview() throws InterruptedException {
    public void clickNeutralAndClickNegative_assertStopPreview() {
        performClickOnDialog(BUTTON_NEUTRAL);
        Thread.sleep(100);
        getTimerFromFragment().runOneTask();
        performClickOnDialog(BUTTON_NEGATIVE);
        Thread.sleep(100);

        assertThat(getLastCapturedIntent().getAction())
                .isEqualTo(ACTION_FLASH_NOTIFICATION_STOP_PREVIEW);
        assertStopPreview();
    }

    @Test
    public void clickNeutralAndClickPositive_assertStopPreview() throws InterruptedException {
    public void clickNeutralAndClickPositive_assertStopPreview() {
        performClickOnDialog(BUTTON_NEUTRAL);
        Thread.sleep(100);
        getTimerFromFragment().runOneTask();
        performClickOnDialog(BUTTON_POSITIVE);
        Thread.sleep(100);

        assertThat(getLastCapturedIntent().getAction())
                .isEqualTo(ACTION_FLASH_NOTIFICATION_STOP_PREVIEW);
        assertStopPreview();
    }

    @Test
    public void clickNeutralAndClickColor_assertStartPreview() throws InterruptedException {
    public void clickNeutralAndClickColor_assertStartPreview() {
        performClickOnDialog(BUTTON_NEUTRAL);
        Thread.sleep(100);
        getTimerFromFragment().runOneTask();
        checkColorButton(CYAN);
        Thread.sleep(500);
        // When changing the color while the preview is running, the fragment will schedule three
        // tasks: stop the current preview, start the new preview, stop the new preview
        int numOfPendingTasks = getTimerFromFragment().numOfPendingTasks();
        // Run all the pending tasks except the last one
        while (numOfPendingTasks > 1) {
            getTimerFromFragment().runOneTask();
            numOfPendingTasks--;
        }

        Intent captured = getLastCapturedIntent();
        assertThat(captured.getAction()).isEqualTo(ACTION_FLASH_NOTIFICATION_START_PREVIEW);
        assertThat(captured.getIntExtra(EXTRA_FLASH_NOTIFICATION_PREVIEW_TYPE, TYPE_SHORT_PREVIEW))
                .isEqualTo(TYPE_LONG_PREVIEW);
        assertThat(captured.getIntExtra(EXTRA_FLASH_NOTIFICATION_PREVIEW_COLOR, Color.TRANSPARENT))
                .isEqualTo(CYAN.mColorInt);
        assertStartPreview(CYAN.mColorInt);
    }

    @Test
@@ -168,6 +170,7 @@ public class ScreenFlashNotificationColorDialogFragmentTest {
        checkColorButton(AZURE);
        performClickOnDialog(BUTTON_NEGATIVE);

        assertThat(getTimerFromFragment()).isNull();
        assertThat(mCurrentColor).isEqualTo(ROSE.mColorInt);
    }

@@ -193,4 +196,46 @@ public class ScreenFlashNotificationColorDialogFragmentTest {
        final int size = capturedIntents.size();
        return capturedIntents.get(size - 1);
    }

    private ScreenFlashNotificationColorDialogFragment createFragment() {
        ScreenFlashNotificationColorDialogFragmentWithFakeTimer fragment =
                new ScreenFlashNotificationColorDialogFragmentWithFakeTimer();
        ReflectionHelpers.setField(fragment, "mCurrentColor", mCurrentColor);
        ReflectionHelpers.setField(fragment, "mConsumer",
                (Consumer<Integer>) selectedColor -> mCurrentColor = selectedColor);

        return fragment;
    }

    private FakeTimer getTimerFromFragment() {
        return (FakeTimer) ReflectionHelpers.getField(mDialogFragment, "mTimer");
    }

    private void assertStartPreview(int color) {
        Intent captured = getLastCapturedIntent();
        assertThat(captured.getAction()).isEqualTo(ACTION_FLASH_NOTIFICATION_START_PREVIEW);
        assertThat(captured.getIntExtra(EXTRA_FLASH_NOTIFICATION_PREVIEW_TYPE, TYPE_SHORT_PREVIEW))
                .isEqualTo(TYPE_LONG_PREVIEW);
        assertThat(captured.getIntExtra(EXTRA_FLASH_NOTIFICATION_PREVIEW_COLOR, Color.TRANSPARENT))
                .isEqualTo(color);
    }

    private void assertStopPreview() {
        assertThat(getTimerFromFragment().numOfPendingTasks()).isEqualTo(0);
        assertThat(getLastCapturedIntent().getAction())
                .isEqualTo(ACTION_FLASH_NOTIFICATION_STOP_PREVIEW);
    }

    /**
     * A {@link ScreenFlashNotificationColorDialogFragment} that uses a fake timer so that it won't
     * create unmanageable timer threads during test.
     */
    public static class ScreenFlashNotificationColorDialogFragmentWithFakeTimer extends
            ScreenFlashNotificationColorDialogFragment {

        @Override
        Timer createTimer() {
            return new FakeTimer();
        }
    }
}
+81 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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 com.android.settings.testutils;

import java.util.PriorityQueue;
import java.util.Timer;
import java.util.TimerTask;

/**
 * A fake {@link Timer} that doesn't create a TimerThread which is hard to manage in test.
 */
public class FakeTimer extends Timer {
    private final PriorityQueue<ScheduledTimerTask> mQueue = new PriorityQueue<>();

    public FakeTimer() {
    }

    @Override
    public void cancel() {
        mQueue.clear();
    }

    @Override
    public void schedule(TimerTask task, long delay) {
        mQueue.offer(new ScheduledTimerTask(System.currentTimeMillis() + delay, task));
    }

    /**
     * Runs the first task in the queue if there's any.
     */
    public void runOneTask() {
        if (mQueue.size() > 0) {
            mQueue.poll().mTask.run();
        }
    }

    /**
     * Runs all the queued tasks in order.
     */
    public void runAllTasks() {
        while (mQueue.size() > 0) {
            mQueue.poll().mTask.run();
        }
    }

    /**
     * Returns number of pending tasks in the timer
     */
    public int numOfPendingTasks() {
        return mQueue.size();
    }

    private static class ScheduledTimerTask implements Comparable<ScheduledTimerTask> {
        final long mTimeToRunInMillisSeconds;
        final TimerTask mTask;

        ScheduledTimerTask(long timeToRunInMilliSeconds, TimerTask task) {
            this.mTimeToRunInMillisSeconds = timeToRunInMilliSeconds;
            this.mTask = task;
        }

        @Override
        public int compareTo(ScheduledTimerTask other) {
            return Long.compare(this.mTimeToRunInMillisSeconds, other.mTimeToRunInMillisSeconds);
        }
    }
}