Commit a535bde8 authored by Yaron Friedman's avatar Yaron Friedman Committed by Chromium LUCI CQ

[Flaky tests] De-flake PowerBroadcastReceiverTests

1. Migrate tests to robolectric unit tests.
2. Add one integration test to confirm it's initialized

Change-Id: I8e89487a7ece9cbef330caec6dae0e7bae25d44f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2639647
Commit-Queue: Yaron Friedman <yfriedman@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#845220}
parent c072c8c7
......@@ -4,6 +4,7 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/ChromeBackupAgentTest.java",
"junit/src/org/chromium/chrome/browser/DeferredStartupHandlerTest.java",
"junit/src/org/chromium/chrome/browser/DelayedScreenLockIntentHandlerTest.java",
"junit/src/org/chromium/chrome/browser/PowerBroadcastReceiverTest.java",
"junit/src/org/chromium/chrome/browser/ShadowIdleHandlerAwareMessageQueue.java",
"junit/src/org/chromium/chrome/browser/ShortcutHelperTest.java",
"junit/src/org/chromium/chrome/browser/about_settings/AboutSettingsBridgeTest.java",
......
......@@ -30,7 +30,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/NavigationPopupTest.java",
"javatests/src/org/chromium/chrome/browser/PopularUrlsTest.java",
"javatests/src/org/chromium/chrome/browser/PopupTest.java",
"javatests/src/org/chromium/chrome/browser/PowerBroadcastReceiverTest.java",
"javatests/src/org/chromium/chrome/browser/PowerBroadcastReceiverIntegrationTest.java",
"javatests/src/org/chromium/chrome/browser/ProcessIsolationTest.java",
"javatests/src/org/chromium/chrome/browser/ReachedCodeProfilerTest.java",
"javatests/src/org/chromium/chrome/browser/RestoreHistogramTest.java",
......
......@@ -34,21 +34,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
public class PowerBroadcastReceiver extends BroadcastReceiver {
private final AtomicBoolean mIsRegistered = new AtomicBoolean(false);
private PowerManagerHelper mPowerManagerHelper;
private ServiceRunnable mServiceRunnable;
/**
* Stubs out interaction with the PowerManager.
*/
@VisibleForTesting
static class PowerManagerHelper {
/** @return whether the screen is on or not. */
public boolean isScreenOn(Context context) {
PowerManager powerManager =
(PowerManager) context.getSystemService(Context.POWER_SERVICE);
return powerManager.isInteractive();
}
}
private ServiceRunnable mServiceRunnable = new ServiceRunnable();
/**
* Defines a set of actions to perform when the conditions are met.
......@@ -81,7 +67,7 @@ public class PowerBroadcastReceiver extends BroadcastReceiver {
public void post() {
if (mState == State.POSTED) return;
setState(State.POSTED);
mHandler.postDelayed(this, getDelayToRun());
mHandler.postDelayed(this, MS_DELAY_TO_RUN);
}
public void cancel() {
......@@ -110,14 +96,6 @@ public class PowerBroadcastReceiver extends BroadcastReceiver {
OmahaBase.onForegroundSessionStart(context);
}
public long getDelayToRun() {
return MS_DELAY_TO_RUN;
}
}
public PowerBroadcastReceiver() {
mServiceRunnable = new ServiceRunnable();
mPowerManagerHelper = new PowerManagerHelper();
}
/** See {@link ChromeApplication#onForegroundSessionStart()}. */
......@@ -125,7 +103,10 @@ public class PowerBroadcastReceiver extends BroadcastReceiver {
ThreadUtils.assertOnUiThread();
assert Looper.getMainLooper() == Looper.myLooper();
if (mPowerManagerHelper.isScreenOn(ContextUtils.getApplicationContext())) {
PowerManager powerManager =
(PowerManager) ContextUtils.getApplicationContext().getSystemService(
Context.POWER_SERVICE);
if (powerManager.isInteractive()) {
mServiceRunnable.post();
} else {
registerReceiver();
......@@ -186,12 +167,4 @@ public class PowerBroadcastReceiver extends BroadcastReceiver {
mServiceRunnable.cancel();
mServiceRunnable = runnable;
}
/**
* Sets the PowerManagerHelper that will be used to check if the screen is on.
*/
@VisibleForTesting
void setPowerManagerHelperForTests(PowerManagerHelper helper) {
mPowerManagerHelper = helper;
}
}
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser;
import androidx.test.filters.SmallTest;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.PowerBroadcastReceiver.ServiceRunnable;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import java.util.concurrent.TimeoutException;
/** Test suite for verifying PowerBroadcastReceiver is initialized. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class PowerBroadcastReceiverIntegrationTest {
@Rule
public final ChromeTabbedActivityTestRule mActivityTestRule =
new ChromeTabbedActivityTestRule();
@Test
@SmallTest
@Feature({"Omaha", "Sync"})
public void testEnsurePowerBroadcastReceiverRegisteredWhenLaunched() throws Exception {
final CallbackHelper callback = new CallbackHelper();
PowerBroadcastReceiver receiver = TestThreadUtils.runOnUiThreadBlocking(
()
-> ChromeActivitySessionTracker.getInstance()
.getPowerBroadcastReceiverForTesting());
receiver.setServiceRunnableForTests(new ServiceRunnable() {
@Override
public void post() {
callback.notifyCalled();
}
});
mActivityTestRule.startMainActivityOnBlankPage();
try {
callback.waitForFirst();
} catch (TimeoutException e) {
Assert.fail("PowerBroadReceiver never initialized");
}
}
}
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser;
import android.content.Context;
import android.content.Intent;
import android.support.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.FlakyTest;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.util.ChromeApplicationTestUtils;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
/**
* Tests for the PowerBroadcastReceiver.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class PowerBroadcastReceiverTest {
@Rule
public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
private static final long MS_INTERVAL = 1000;
private static final long MS_RUNNABLE_DELAY = 2500;
private static final long MS_TIMEOUT = 5000;
private static final MockPowerManagerHelper sScreenOff = new MockPowerManagerHelper(false);
private static final MockPowerManagerHelper sScreenOn = new MockPowerManagerHelper(true);
/** Mocks out PowerBroadcastReceiver.ServiceRunnable. */
private static class MockServiceRunnable extends PowerBroadcastReceiver.ServiceRunnable {
public CallbackHelper postHelper = new CallbackHelper();
public CallbackHelper cancelHelper = new CallbackHelper();
public CallbackHelper runHelper = new CallbackHelper();
public CallbackHelper runActionsHelper = new CallbackHelper();
@Override
public void setState(@State int state) {
super.setState(state);
if (state == State.POSTED) {
postHelper.notifyCalled();
} else if (state == State.CANCELED) {
cancelHelper.notifyCalled();
} else if (state == State.COMPLETED) {
runHelper.notifyCalled();
}
}
@Override
public long getDelayToRun() {
return MS_RUNNABLE_DELAY;
}
@Override
public void runActions() {
// Don't let the real services start.
runActionsHelper.notifyCalled();
}
}
/** Mocks out PowerBroadcastReceiver.PowerManagerHelper. */
private static class MockPowerManagerHelper extends PowerBroadcastReceiver.PowerManagerHelper {
private final boolean mScreenIsOn;
public MockPowerManagerHelper(boolean screenIsOn) {
mScreenIsOn = screenIsOn;
}
@Override
public boolean isScreenOn(Context context) {
return mScreenIsOn;
}
}
private MockServiceRunnable mRunnable;
private PowerBroadcastReceiver mReceiver;
@Before
public void setUp() throws Exception {
mActivityTestRule.startMainActivityFromLauncher();
mReceiver = TestThreadUtils.runOnUiThreadBlocking(
() -> ChromeActivitySessionTracker.getInstance()
.getPowerBroadcastReceiverForTesting());
// Set up our mock runnable.
mRunnable = new MockServiceRunnable();
mReceiver.setServiceRunnableForTests(mRunnable);
// Initially claim that the screen is on.
mReceiver.setPowerManagerHelperForTests(sScreenOn);
}
/**
* Check if the runnable is posted and run while the screen is on.
*/
@Test
@MediumTest
@Feature({"Omaha", "Sync"})
public void testRunnableRunsWithScreenOn() throws Exception {
// Pause & resume to post the runnable.
ChromeApplicationTestUtils.fireHomeScreenIntent(InstrumentationRegistry.getTargetContext());
int postCount = mRunnable.postHelper.getCallCount();
int runCount = mRunnable.runHelper.getCallCount();
ChromeApplicationTestUtils.launchChrome(InstrumentationRegistry.getTargetContext());
// Relaunching Chrome should cause the runnable to trigger.
mRunnable.postHelper.waitForCallback(postCount, 1);
mRunnable.runHelper.waitForCallback(runCount, 1);
Assert.assertEquals(0, mRunnable.cancelHelper.getCallCount());
Assert.assertFalse("Still listening for power broadcasts.", mReceiver.isRegistered());
}
/**
* Check that the runnable gets posted and canceled when Main is sent to the background.
*/
@Test
@FlakyTest(message = "https://crbug.com/579363")
@MediumTest
@Feature({"Omaha", "Sync"})
public void testRunnableGetsCanceled() throws Exception {
// Pause & resume to post the runnable.
ChromeApplicationTestUtils.fireHomeScreenIntent(InstrumentationRegistry.getTargetContext());
int postCount = mRunnable.postHelper.getCallCount();
ChromeApplicationTestUtils.launchChrome(InstrumentationRegistry.getTargetContext());
mRunnable.postHelper.waitForCallback(postCount, 1);
Assert.assertEquals(0, mRunnable.runHelper.getCallCount());
Assert.assertEquals(0, mRunnable.cancelHelper.getCallCount());
// Pause before the runnable has a chance to run. Should cause the runnable to be canceled.
int cancelCount = mRunnable.cancelHelper.getCallCount();
ChromeApplicationTestUtils.fireHomeScreenIntent(InstrumentationRegistry.getTargetContext());
mRunnable.cancelHelper.waitForCallback(cancelCount, 1);
Assert.assertEquals(0, mRunnable.runHelper.getCallCount());
Assert.assertFalse("Still listening for power broadcasts.", mReceiver.isRegistered());
}
/**
* Check that the runnable gets run only while the screen is on.
*/
@Test
@MediumTest
@Feature({"Omaha", "Sync"})
@FlakyTest(message = "https://crbug.com/587138")
public void testRunnableGetsRunWhenScreenIsOn() throws Exception {
// Claim the screen is off.
mReceiver.setPowerManagerHelperForTests(sScreenOff);
// Pause & resume. Because the screen is off, nothing should happen.
ChromeApplicationTestUtils.fireHomeScreenIntent(InstrumentationRegistry.getTargetContext());
ChromeApplicationTestUtils.launchChrome(InstrumentationRegistry.getTargetContext());
Assert.assertTrue("Isn't waiting for power broadcasts.", mReceiver.isRegistered());
Assert.assertEquals(0, mRunnable.postHelper.getCallCount());
Assert.assertEquals(0, mRunnable.runHelper.getCallCount());
Assert.assertEquals(0, mRunnable.cancelHelper.getCallCount());
// Pretend to turn the screen on.
int postCount = mRunnable.postHelper.getCallCount();
int runCount = mRunnable.runHelper.getCallCount();
mReceiver.setPowerManagerHelperForTests(sScreenOn);
Intent intent = new Intent(Intent.ACTION_SCREEN_ON);
mReceiver.onReceive(InstrumentationRegistry.getTargetContext(), intent);
// The runnable should run now that the screen is on.
mRunnable.postHelper.waitForCallback(postCount, 1);
mRunnable.runHelper.waitForCallback(runCount, 1);
Assert.assertEquals(0, mRunnable.cancelHelper.getCallCount());
Assert.assertFalse("Still listening for power broadcasts.", mReceiver.isRegistered());
}
}
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.MediumTest;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowPowerManager;
import org.chromium.base.ActivityState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.PowerBroadcastReceiver.ServiceRunnable.State;
/**
* Tests for the PowerBroadcastReceiver.
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class PowerBroadcastReceiverTest {
@Mock
private Activity mActivity;
@Spy
private PowerBroadcastReceiver.ServiceRunnable mRunnable;
private PowerBroadcastReceiver mReceiver;
private ShadowPowerManager mShadowPowerManager;
@Before
public void setUp() throws Exception {
Context appContext = ApplicationProvider.getApplicationContext();
MockitoAnnotations.initMocks(this);
mShadowPowerManager =
Shadows.shadowOf((PowerManager) appContext.getSystemService(Context.POWER_SERVICE));
mReceiver = new PowerBroadcastReceiver();
mReceiver.setServiceRunnableForTests(mRunnable);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.CREATED);
// Initially claim that the screen is on.
mShadowPowerManager.setIsInteractive(true);
}
private void startSession() {
mReceiver.onForegroundSessionStart();
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STARTED);
}
private void pauseSession() {
mReceiver.onForegroundSessionEnd();
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.PAUSED);
}
/**
* Check if the runnable is posted and run while the screen is on.
*/
@Test
@MediumTest
@Feature({"Omaha", "Sync"})
public void testRunnableRunsWithScreenOn() throws Exception {
startSession();
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
pauseSession();
verify(mRunnable, times(1)).setState(State.POSTED);
verify(mRunnable, times(1)).setState(State.COMPLETED);
verify(mRunnable, times(0)).setState(State.CANCELED);
verify(mRunnable, times(1)).runActions();
Assert.assertFalse("Still listening for power broadcasts.", mReceiver.isRegistered());
}
/**
* Check that the runnable gets posted and canceled when Main is sent to the background.
*/
@Test
@Feature({"Omaha", "Sync"})
public void testRunnableGetsCanceled() throws Exception {
startSession();
pauseSession();
// Pause happened before the runnable has a chance to run.
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
verify(mRunnable, times(1)).setState(State.POSTED);
verify(mRunnable, times(0)).setState(State.COMPLETED);
verify(mRunnable, times(1)).setState(State.CANCELED);
verify(mRunnable, times(0)).runActions();
Assert.assertFalse("Still listening for power broadcasts.", mReceiver.isRegistered());
}
/**
* Check that the runnable gets run only while the screen is on.
*/
@Test
@Feature({"Omaha", "Sync"})
public void testRunnableGetsRunWhenScreenIsOn() throws Exception {
// Claim the screen is off.
mShadowPowerManager.setIsInteractive(false);
// Because the screen is off, nothing should happen.
startSession();
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
Assert.assertTrue("Isn't waiting for power broadcasts.", mReceiver.isRegistered());
verify(mRunnable, times(0)).setState(State.POSTED);
verify(mRunnable, times(0)).setState(State.COMPLETED);
verify(mRunnable, times(0)).setState(State.CANCELED);
verify(mRunnable, times(0)).runActions();
// Pretend to turn the screen on.
mShadowPowerManager.setIsInteractive(true);
Intent intent = new Intent(Intent.ACTION_SCREEN_ON);
mReceiver.onReceive(ApplicationProvider.getApplicationContext(), intent);
// Now the event should be processed.
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
verify(mRunnable, times(1)).setState(State.POSTED);
verify(mRunnable, times(1)).setState(State.COMPLETED);
verify(mRunnable, times(0)).setState(State.CANCELED);
verify(mRunnable, times(1)).runActions();
Assert.assertFalse("Still listening for power broadcasts.", mReceiver.isRegistered());
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment