Commit d8720340 authored by Becky Zhou's avatar Becky Zhou Committed by Commit Bot

[Dark] Add unit test for GlobalNightModeStateController

Bug: 948443
Change-Id: I2fe90e283fad46f5d2fecc71abd61392074c9af7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1601933
Commit-Queue: Becky Zhou <huayinz@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#659214}
parent 68cc21c5
......@@ -882,6 +882,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/nfc/BeamController.java",
"java/src/org/chromium/chrome/browser/nfc/BeamProvider.java",
"java/src/org/chromium/chrome/browser/night_mode/GlobalNightModeStateController.java",
"java/src/org/chromium/chrome/browser/night_mode/GlobalNightModeStateProviderHolder.java",
"java/src/org/chromium/chrome/browser/night_mode/NightModeMetrics.java",
"java/src/org/chromium/chrome/browser/night_mode/NightModeStateProvider.java",
"java/src/org/chromium/chrome/browser/night_mode/NightModeUtils.java",
......
......@@ -113,6 +113,8 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/notifications/NotificationTriggerBackgroundTaskTest.java",
"junit/src/org/chromium/chrome/browser/notifications/NotificationTriggerSchedulerTest.java",
"junit/src/org/chromium/chrome/browser/notifications/channels/ChannelDefinitionsTest.java",
"junit/src/org/chromium/chrome/browser/night_mode/GlobalNightModeStateControllerTest.java",
"junit/src/org/chromium/chrome/browser/night_mode/GlobalNightModeStateProviderHolderTest.java",
"junit/src/org/chromium/chrome/browser/ntp/TitleUtilTest.java",
"junit/src/org/chromium/chrome/browser/ntp/cards/ContentSuggestionsUnitTestUtils.java",
"junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java",
......
......@@ -12,7 +12,7 @@ import android.support.annotation.Nullable;
import android.support.annotation.StyleRes;
import android.support.v7.app.AppCompatActivity;
import org.chromium.chrome.browser.night_mode.GlobalNightModeStateController;
import org.chromium.chrome.browser.night_mode.GlobalNightModeStateProviderHolder;
import org.chromium.chrome.browser.night_mode.NightModeStateProvider;
import org.chromium.chrome.browser.night_mode.NightModeUtils;
......@@ -90,7 +90,7 @@ public class ChromeBaseAppCompatActivity
* of this class.
*/
protected NightModeStateProvider createNightModeStateProvider() {
return GlobalNightModeStateController.getInstance();
return GlobalNightModeStateProviderHolder.getInstance();
}
/**
......
......@@ -22,20 +22,18 @@ import org.chromium.base.ApplicationState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.ContextUtils;
import org.chromium.base.ObserverList;
import org.chromium.chrome.browser.ChromeBaseAppCompatActivity;
import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
import org.chromium.chrome.browser.preferences.themes.ThemePreferences;
import org.chromium.chrome.browser.util.FeatureUtilities;
/**
* Maintains and provides the night mode state for the entire application.
*/
public class GlobalNightModeStateController implements NightModeStateProvider,
SystemNightModeMonitor.Observer,
ApplicationStatus.ApplicationStateListener {
private static GlobalNightModeStateController sInstance;
class GlobalNightModeStateController implements NightModeStateProvider,
SystemNightModeMonitor.Observer,
ApplicationStatus.ApplicationStateListener {
private final ObserverList<Observer> mObservers = new ObserverList<>();
private final SystemNightModeMonitor mSystemNightModeMonitor;
private final ChromePreferenceManager mChromePreferenceManager;
/**
* Whether night mode is enabled throughout the entire app. If null, night mode is not
......@@ -53,24 +51,17 @@ public class GlobalNightModeStateController implements NightModeStateProvider,
private boolean mIsStarted;
/**
* @return The {@link GlobalNightModeStateController} that maintains the night mode state for
* the entire application. Note that UI widgets should always get the
* {@link NightModeStateProvider} from the {@link ChromeBaseAppCompatActivity} they are
* attached to, because the night mode state can be overridden at the activity level.
* Should not directly instantiate unless for testing purpose. Use
* {@link GlobalNightModeStateProviderHolder#getInstance()} instead.
* @param systemNightModeMonitor The {@link SystemNightModeMonitor} that maintains the system
* night mode state.
* @param chromePreferenceManager The {@link ChromePreferenceManager} that maintains shared
* preferences.
*/
public static GlobalNightModeStateController getInstance() {
if (sInstance == null) {
sInstance = new GlobalNightModeStateController();
}
return sInstance;
}
private GlobalNightModeStateController() {
if (!NightModeUtils.isNightModeSupported() || !FeatureUtilities.isNightModeAvailable()) {
// Always stay in light mode if night mode is not available.
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
return;
}
GlobalNightModeStateController(@NonNull SystemNightModeMonitor systemNightModeMonitor,
@NonNull ChromePreferenceManager chromePreferenceManager) {
mSystemNightModeMonitor = systemNightModeMonitor;
mChromePreferenceManager = chromePreferenceManager;
mPreferenceObserver = key -> {
if (TextUtils.equals(key, UI_THEME_SETTING_KEY)) updateNightMode();
......@@ -131,8 +122,8 @@ public class GlobalNightModeStateController implements NightModeStateProvider,
ContextUtils.getApplicationContext().registerReceiver(mPowerModeReceiver,
new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
}
SystemNightModeMonitor.getInstance().addObserver(this);
ChromePreferenceManager.getInstance().addObserver(mPreferenceObserver);
mSystemNightModeMonitor.addObserver(this);
mChromePreferenceManager.addObserver(mPreferenceObserver);
updateNightMode();
}
......@@ -144,16 +135,14 @@ public class GlobalNightModeStateController implements NightModeStateProvider,
if (mPowerModeReceiver != null) {
ContextUtils.getApplicationContext().unregisterReceiver(mPowerModeReceiver);
}
SystemNightModeMonitor.getInstance().removeObserver(this);
ChromePreferenceManager.getInstance().removeObserver(mPreferenceObserver);
mSystemNightModeMonitor.removeObserver(this);
mChromePreferenceManager.removeObserver(mPreferenceObserver);
}
private void updateNightMode() {
final int themeSetting =
ChromePreferenceManager.getInstance().readInt(UI_THEME_SETTING_KEY);
final int themeSetting = mChromePreferenceManager.readInt(UI_THEME_SETTING_KEY);
final boolean newNightModeOn = themeSetting == ThemePreferences.ThemeSetting.SYSTEM_DEFAULT
&& (mPowerSaveModeOn
|| SystemNightModeMonitor.getInstance().isSystemNightModeOn())
&& (mPowerSaveModeOn || mSystemNightModeMonitor.isSystemNightModeOn())
|| themeSetting == ThemePreferences.ThemeSetting.DARK;
if (mNightModeOn != null && newNightModeOn == mNightModeOn) return;
......
// Copyright 2019 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.night_mode;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatDelegate;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.browser.ChromeBaseAppCompatActivity;
import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
import org.chromium.chrome.browser.util.FeatureUtilities;
/**
* Holds an instance of {@link NightModeStateProvider} that provides night mode state for the entire
* application.
*/
public class GlobalNightModeStateProviderHolder {
private static NightModeStateProvider sInstance;
/**
* Created when night mode is not available or not supported.
*/
private static class DummyNightModeStateProvider implements NightModeStateProvider {
private DummyNightModeStateProvider() {
// Always stay in light mode if night mode is not available.
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
@Override
public boolean isInNightMode() {
return false;
}
@Override
public void addObserver(@NonNull Observer observer) {}
@Override
public void removeObserver(@NonNull Observer observer) {}
}
/**
* @return The {@link NightModeStateProvider} that maintains the night mode state for the entire
* application. Note that UI widgets should always get the
* {@link NightModeStateProvider} from the {@link ChromeBaseAppCompatActivity} they are
* attached to, because the night mode state can be overridden at the activity level.
*/
public static NightModeStateProvider getInstance() {
if (sInstance == null) {
if (!NightModeUtils.isNightModeSupported()
|| !FeatureUtilities.isNightModeAvailable()) {
sInstance = new DummyNightModeStateProvider();
} else {
sInstance = new GlobalNightModeStateController(SystemNightModeMonitor.getInstance(),
ChromePreferenceManager.getInstance());
}
}
return sInstance;
}
@VisibleForTesting
static void resetInstanceForTesting() {
sInstance = null;
}
}
......@@ -8,6 +8,7 @@ import android.content.res.Configuration;
import org.chromium.base.ContextUtils;
import org.chromium.base.ObserverList;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.browser.ChromeApplication;
/**
......@@ -76,4 +77,9 @@ public class SystemNightModeMonitor {
mSystemNightModeOn =
(uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
}
@VisibleForTesting
void notifyObserversForTesting() {
for (Observer observer : mObservers) observer.onSystemNightModeChanged();
}
}
......@@ -14,6 +14,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.UserManager;
import android.speech.RecognizerIntent;
import android.support.annotation.Nullable;
import org.chromium.base.CommandLine;
import org.chromium.base.ContextUtils;
......@@ -535,6 +536,15 @@ public class FeatureUtilities {
return sIsNightModeAvailable;
}
/**
* Toggles whether the night mode experiment is enabled for testing. Should be reset back to
* null after the test has finished.
*/
@VisibleForTesting
public static void setNightModeAvailableForTesting(@Nullable Boolean available) {
sIsNightModeAvailable = available;
}
/**
* Cache whether or not night mode is available for custom tabs (i.e. night mode experiment is
* enabled), so the value is immediately available on next start-up.
......
// Copyright 2019 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.night_mode;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import static org.chromium.base.ApplicationState.HAS_RUNNING_ACTIVITIES;
import static org.chromium.base.ApplicationState.HAS_STOPPED_ACTIVITIES;
import static org.chromium.chrome.browser.preferences.ChromePreferenceManager.UI_THEME_SETTING_KEY;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowPowerManager;
import org.chromium.base.ContextUtils;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
import org.chromium.chrome.browser.preferences.themes.ThemePreferences;
/**
* Unit tests for {@link GlobalNightModeStateController}.
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class GlobalNightModeStateControllerTest {
@Mock
private NightModeStateProvider.Observer mObserver;
private GlobalNightModeStateController mGlobalNightModeStateController;
private SystemNightModeMonitor mSystemNightModeMonitor;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mSystemNightModeMonitor = spy(SystemNightModeMonitor.getInstance());
mGlobalNightModeStateController = new GlobalNightModeStateController(
mSystemNightModeMonitor, ChromePreferenceManager.getInstance());
mGlobalNightModeStateController.onApplicationStateChange(HAS_RUNNING_ACTIVITIES);
// Night mode is disabled by default.
assertFalse(GlobalNightModeStateProviderHolder.getInstance().isInNightMode());
}
@After
public void tearDown() throws Exception {
ChromePreferenceManager.getInstance().removeKey(UI_THEME_SETTING_KEY);
}
@Test
public void testUpdateNightMode_PowerSaveMode() {
// Enable power save mode and verify night mode is enabled.
setIsPowerSaveMode(true);
assertTrue(mGlobalNightModeStateController.isInNightMode());
// Disable power save mode and verify night mode is disabled.
setIsPowerSaveMode(false);
assertFalse(mGlobalNightModeStateController.isInNightMode());
}
@Test
public void testUpdateNightMode_SystemNightMode() {
// Enable system night mode and verify night mode is enabled.
setSystemNightMode(true);
assertTrue(mGlobalNightModeStateController.isInNightMode());
// Disable system night mode and verify night mode is disabled.
setSystemNightMode(false);
assertFalse(mGlobalNightModeStateController.isInNightMode());
}
@Test
public void testUpdateNightMode_Preference() {
// Set preference to dark theme and verify night mode is enabled.
ChromePreferenceManager.getInstance().writeInt(
UI_THEME_SETTING_KEY, ThemePreferences.ThemeSetting.DARK);
assertTrue(mGlobalNightModeStateController.isInNightMode());
// Set preference to light theme and verify night mode is disabled.
ChromePreferenceManager.getInstance().writeInt(
UI_THEME_SETTING_KEY, ThemePreferences.ThemeSetting.LIGHT);
assertFalse(mGlobalNightModeStateController.isInNightMode());
// Regardless of power save mode and system night mode, night mode is disabled with light
// theme preference.
setIsPowerSaveMode(true);
assertFalse(mGlobalNightModeStateController.isInNightMode());
setSystemNightMode(true);
assertFalse(mGlobalNightModeStateController.isInNightMode());
}
@Test
public void testStopAndRestart() {
// Simulate to stop listening to night mode state changes. Verify that night mode state is
// not changed.
mGlobalNightModeStateController.onApplicationStateChange(HAS_STOPPED_ACTIVITIES);
setIsPowerSaveMode(true);
assertFalse(mGlobalNightModeStateController.isInNightMode());
setSystemNightMode(true);
assertFalse(mGlobalNightModeStateController.isInNightMode());
ChromePreferenceManager.getInstance().writeInt(
UI_THEME_SETTING_KEY, ThemePreferences.ThemeSetting.DARK);
assertFalse(mGlobalNightModeStateController.isInNightMode());
// Simulate to start listening to night mode state changes. Verify that
// 1. Night mode state is updated after #start().
// 2. Night mode state is updated on power save mode, system night mode, or preference
// changes.
mGlobalNightModeStateController.onApplicationStateChange(HAS_RUNNING_ACTIVITIES);
assertTrue(mGlobalNightModeStateController.isInNightMode());
ChromePreferenceManager.getInstance().writeInt(
UI_THEME_SETTING_KEY, ThemePreferences.ThemeSetting.SYSTEM_DEFAULT);
assertTrue(mGlobalNightModeStateController.isInNightMode());
setIsPowerSaveMode(false);
assertTrue(mGlobalNightModeStateController.isInNightMode());
setSystemNightMode(false);
assertFalse(mGlobalNightModeStateController.isInNightMode());
}
@Test
public void testObserver() {
mGlobalNightModeStateController.addObserver(mObserver);
// Verify that observer is called on night mode state changed from false to true.
ChromePreferenceManager.getInstance().writeInt(
UI_THEME_SETTING_KEY, ThemePreferences.ThemeSetting.DARK);
assertTrue(mGlobalNightModeStateController.isInNightMode());
verify(mObserver, times(1)).onNightModeStateChanged();
// Verify that observer is not called when night mode state is not changed.
setIsPowerSaveMode(true);
assertTrue(mGlobalNightModeStateController.isInNightMode());
verify(mObserver, times(1)).onNightModeStateChanged();
// Verify that observer is not called when night mode state is not changed.
setIsPowerSaveMode(false);
assertTrue(mGlobalNightModeStateController.isInNightMode());
verify(mObserver, times(1)).onNightModeStateChanged();
// Verify that observer is called when set to light theme.
ChromePreferenceManager.getInstance().writeInt(
UI_THEME_SETTING_KEY, ThemePreferences.ThemeSetting.LIGHT);
assertFalse(mGlobalNightModeStateController.isInNightMode());
verify(mObserver, times(2)).onNightModeStateChanged();
// Verify that observer is not called after it is removed.
mGlobalNightModeStateController.removeObserver(mObserver);
ChromePreferenceManager.getInstance().writeInt(
UI_THEME_SETTING_KEY, ThemePreferences.ThemeSetting.DARK);
assertTrue(mGlobalNightModeStateController.isInNightMode());
verify(mObserver, times(2)).onNightModeStateChanged();
}
/**
* Simulates setting power save mode, and notifies the change.
* @param isPowerSaveMode Whether power save mode is enabled or not.
*/
private void setIsPowerSaveMode(boolean isPowerSaveMode) {
PowerManager powerManager = (PowerManager) RuntimeEnvironment.application.getSystemService(
Context.POWER_SERVICE);
ShadowPowerManager shadowPowerManager = shadowOf(powerManager);
shadowPowerManager.setIsPowerSaveMode(isPowerSaveMode);
ContextUtils.getApplicationContext().sendBroadcast(
new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
}
/**
* Simulates setting system night mode, and notifies the change.
* @param isSystemNightModeOn Whether system night mode is enabled or not.
*/
private void setSystemNightMode(boolean isSystemNightModeOn) {
when(mSystemNightModeMonitor.isSystemNightModeOn()).thenReturn(isSystemNightModeOn);
mSystemNightModeMonitor.notifyObserversForTesting();
}
}
// Copyright 2019 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.night_mode;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.chromium.chrome.browser.preferences.ChromePreferenceManager.UI_THEME_SETTING_KEY;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
import org.chromium.chrome.browser.preferences.themes.ThemePreferences;
import org.chromium.chrome.browser.util.FeatureUtilities;
/**
* Unit tests for {@link GlobalNightModeStateProviderHolder}.
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class GlobalNightModeStateProviderHolderTest {
@After
public void tearDown() throws Exception {
FeatureUtilities.setNightModeAvailableForTesting(null);
GlobalNightModeStateProviderHolder.resetInstanceForTesting();
ChromePreferenceManager.getInstance().removeKey(UI_THEME_SETTING_KEY);
}
@Test
public void testNightModeNotAvailable() {
FeatureUtilities.setNightModeAvailableForTesting(false);
// Verify that night mode is disabled.
assertFalse(GlobalNightModeStateProviderHolder.getInstance().isInNightMode());
// Verify that night mode cannot be enabled.
ChromePreferenceManager.getInstance().writeInt(
UI_THEME_SETTING_KEY, ThemePreferences.ThemeSetting.DARK);
assertFalse(GlobalNightModeStateProviderHolder.getInstance().isInNightMode());
}
@Test
public void testNightModeAvailable() {
// Verify that the instance is a GlobalNightModeStateController. Other tests are covered
// in GlobalNightModeStateControllerTest.java.
FeatureUtilities.setNightModeAvailableForTesting(true);
assertTrue(GlobalNightModeStateProviderHolder.getInstance()
instanceof GlobalNightModeStateController);
}
}
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