blob: 807782bb8e33788793d2cafe9935a6d7ad161d01 [file] [log] [blame]
// 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.offlinepages;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.chromium.base.ActivityState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.BaseSwitches;
import org.chromium.base.Callback;
import org.chromium.base.CommandLine;
import org.chromium.base.SysUtils;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.background_task_scheduler.NativeBackgroundTask;
import org.chromium.chrome.test.support.DisableHistogramsRule;
import org.chromium.components.background_task_scheduler.BackgroundTask;
import org.chromium.components.background_task_scheduler.BackgroundTaskScheduler;
import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
import org.chromium.components.background_task_scheduler.TaskIds;
import org.chromium.components.background_task_scheduler.TaskInfo;
import org.chromium.components.background_task_scheduler.TaskParameters;
import org.chromium.net.ConnectionType;
/**
* Unit tests for OfflineBackgroundTask.
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE, shadows = {ShadowDeviceConditions.class})
public class OfflineBackgroundTaskTest {
private static final boolean REQUIRE_POWER = true;
private static final boolean REQUIRE_UNMETERED = true;
private static final boolean POWER_CONNECTED = true;
private static final boolean POWER_SAVE_MODE_ON = true;
private static final boolean METERED = true;
private static final int MINIMUM_BATTERY_LEVEL = 33;
private static final String IS_LOW_END_DEVICE_SWITCH =
"--" + BaseSwitches.ENABLE_LOW_END_DEVICE_MODE;
@Rule
public DisableHistogramsRule mDisableHistogramsRule = new DisableHistogramsRule();
private Bundle mTaskExtras;
private long mTestTime;
private TriggerConditions mTriggerConditions =
new TriggerConditions(!REQUIRE_POWER, MINIMUM_BATTERY_LEVEL, REQUIRE_UNMETERED);
private DeviceConditions mDeviceConditions = new DeviceConditions(!POWER_CONNECTED,
MINIMUM_BATTERY_LEVEL + 5, ConnectionType.CONNECTION_3G, !POWER_SAVE_MODE_ON, !METERED);
private Activity mTestActivity;
@Mock
private BackgroundSchedulerProcessor mBackgroundSchedulerProcessor;
@Mock
private BackgroundTaskScheduler mTaskScheduler;
@Mock
private BackgroundTask.TaskFinishedCallback mTaskFinishedCallback;
@Mock
private Callback<Boolean> mInternalBooleanCallback;
@Captor
private ArgumentCaptor<TaskInfo> mTaskInfo;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
BackgroundTaskSchedulerFactory.setSchedulerForTesting(mTaskScheduler);
doReturn(true)
.when(mTaskScheduler)
.schedule(eq(RuntimeEnvironment.application), mTaskInfo.capture());
ShadowDeviceConditions.setCurrentConditions(mDeviceConditions);
// Set up background scheduler processor mock.
BackgroundSchedulerProcessor.setInstanceForTesting(mBackgroundSchedulerProcessor);
// Build a bundle with trigger conditions.
mTaskExtras = new Bundle();
TaskExtrasPacker.packTimeInBundle(mTaskExtras);
TaskExtrasPacker.packTriggerConditionsInBundle(mTaskExtras, mTriggerConditions);
// Run tests as a low-end device.
CommandLine.init(new String[] {"testcommand", IS_LOW_END_DEVICE_SWITCH});
// Set up single, stopped Activity.
mTestActivity = new Activity();
ApplicationStatus.onStateChangeForTesting(mTestActivity, ActivityState.CREATED);
ApplicationStatus.onStateChangeForTesting(mTestActivity, ActivityState.STOPPED);
}
@After
public void tearDown() throws Exception {
// Clean up static state for subsequent Robolectric tests.
CommandLine.reset();
SysUtils.resetForTesting();
}
private void setupScheduledProcessingWithResult(boolean result) {
doReturn(result)
.when(mBackgroundSchedulerProcessor)
.startScheduledProcessing(
any(DeviceConditions.class), ArgumentMatchers.<Callback<Boolean>>any());
}
@Test
@Feature({"OfflinePages"})
public void testCheckConditions_BatteryConditions_LowBattery_NoPower() {
// Setup low battery conditions with no power connected.
DeviceConditions deviceConditionsLowBattery =
new DeviceConditions(!POWER_CONNECTED, MINIMUM_BATTERY_LEVEL - 1,
ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, !METERED);
ShadowDeviceConditions.setCurrentConditions(deviceConditionsLowBattery);
// Verify that conditions for processing are not met.
assertFalse(
OfflineBackgroundTask.checkConditions(RuntimeEnvironment.application, mTaskExtras));
// Check impact on starting before native loaded.
TaskParameters params = TaskParameters.create(TaskIds.OFFLINE_PAGES_BACKGROUND_JOB_ID)
.addExtras(mTaskExtras)
.build();
int result = new OfflineBackgroundTask().onStartTaskBeforeNativeLoaded(
RuntimeEnvironment.application, params, mTaskFinishedCallback);
assertEquals(NativeBackgroundTask.StartBeforeNativeResult.RESCHEDULE, result);
// Task finished can only gets called from the native part, when async processing starts.
verify(mTaskFinishedCallback, times(0)).taskFinished(anyBoolean());
}
@Test
@Feature({"OfflinePages"})
public void testCheckConditions_BatteryConditions_LowBattery_WithPower() {
// Set battery percentage below minimum level, but connect power.
DeviceConditions deviceConditionsPowerConnected =
new DeviceConditions(POWER_CONNECTED, MINIMUM_BATTERY_LEVEL - 1,
ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, !METERED);
ShadowDeviceConditions.setCurrentConditions(deviceConditionsPowerConnected);
// Now verify that same battery level, with power connected, will pass the conditions.
assertTrue(
OfflineBackgroundTask.checkConditions(RuntimeEnvironment.application, mTaskExtras));
// Check impact on starting before native loaded.
TaskParameters params = TaskParameters.create(TaskIds.OFFLINE_PAGES_BACKGROUND_JOB_ID)
.addExtras(mTaskExtras)
.build();
int result = new OfflineBackgroundTask().onStartTaskBeforeNativeLoaded(
RuntimeEnvironment.application, params, mTaskFinishedCallback);
assertEquals(NativeBackgroundTask.StartBeforeNativeResult.LOAD_NATIVE, result);
// Task finished can only gets called from the native part, when async processing starts.
verify(mTaskFinishedCallback, times(0)).taskFinished(anyBoolean());
}
@Test
@Feature({"OfflinePages"})
public void testCheckConditions_OnLowEndDevice_ActivityStarted() {
// Transition the test Activity to a running state.
ApplicationStatus.onStateChangeForTesting(mTestActivity, ActivityState.STARTED);
// Verify that conditions for processing are not met.
assertFalse(
OfflineBackgroundTask.checkConditions(RuntimeEnvironment.application, mTaskExtras));
// Check impact on starting before native loaded.
TaskParameters params = TaskParameters.create(TaskIds.OFFLINE_PAGES_BACKGROUND_JOB_ID)
.addExtras(mTaskExtras)
.build();
int result = new OfflineBackgroundTask().onStartTaskBeforeNativeLoaded(
RuntimeEnvironment.application, params, mTaskFinishedCallback);
assertEquals(NativeBackgroundTask.StartBeforeNativeResult.RESCHEDULE, result);
// Task finished can only gets called from the native part, when async processing starts.
verify(mTaskFinishedCallback, times(0)).taskFinished(anyBoolean());
}
@Test
@Feature({"OfflinePages"})
public void testCheckConditions_OnLowEndDevice_ActivityStopped() {
// Switch activity state to stopped.
ApplicationStatus.onStateChangeForTesting(mTestActivity, ActivityState.STOPPED);
// Now verify that condition check passes when Activity is stopped.
assertTrue(
OfflineBackgroundTask.checkConditions(RuntimeEnvironment.application, mTaskExtras));
// Check impact on starting before native loaded.
TaskParameters params = TaskParameters.create(TaskIds.OFFLINE_PAGES_BACKGROUND_JOB_ID)
.addExtras(mTaskExtras)
.build();
int result = new OfflineBackgroundTask().onStartTaskBeforeNativeLoaded(
RuntimeEnvironment.application, params, mTaskFinishedCallback);
assertEquals(NativeBackgroundTask.StartBeforeNativeResult.LOAD_NATIVE, result);
// Task finished can only gets called from the native part, when async processing starts.
verify(mTaskFinishedCallback, times(0)).taskFinished(anyBoolean());
}
@Test
@Feature({"OfflinePages"})
public void testOnStartTaskWithNative_BackupScheduleIfExecutingTask() {
setupScheduledProcessingWithResult(true);
TaskParameters params = TaskParameters.create(TaskIds.OFFLINE_PAGES_BACKGROUND_JOB_ID)
.addExtras(mTaskExtras)
.build();
new OfflineBackgroundTask().onStartTaskWithNative(
RuntimeEnvironment.application, params, mTaskFinishedCallback);
verify(mTaskScheduler, times(1))
.schedule(eq(RuntimeEnvironment.application), any(TaskInfo.class));
// Task is running at this point, hence no callback issued.
verify(mTaskFinishedCallback, times(0)).taskFinished(anyBoolean());
}
@Test
@Feature({"OfflinePages"})
public void testOnStartTaskWithNative_RescheduleThroughCallbackWhenRunning() {
setupScheduledProcessingWithResult(false);
TaskParameters params = TaskParameters.create(TaskIds.OFFLINE_PAGES_BACKGROUND_JOB_ID)
.addExtras(mTaskExtras)
.build();
new OfflineBackgroundTask().onStartTaskWithNative(
RuntimeEnvironment.application, params, mTaskFinishedCallback);
verify(mTaskScheduler, times(0)).schedule(any(Context.class), any(TaskInfo.class));
// Task started async processing after native load, but processing refused to progress,
// hence task finished called with reschedule request.
verify(mTaskFinishedCallback, times(1)).taskFinished(eq(true));
}
@Test
@Feature({"OfflinePages"})
public void testStartBackgroundRequests() {
setupScheduledProcessingWithResult(true);
assertTrue(OfflineBackgroundTask.startScheduledProcessing(mBackgroundSchedulerProcessor,
RuntimeEnvironment.application, mTaskExtras, mInternalBooleanCallback));
// Check with BackgroundSchedulerProcessor that processing started.
verify(mBackgroundSchedulerProcessor, times(1))
.startScheduledProcessing(eq(mDeviceConditions), eq(mInternalBooleanCallback));
}
@Test
@Feature({"OfflinePages"})
public void testStartBackgroundRequestsNotStarted() {
// Processing will not be started here.
setupScheduledProcessingWithResult(false);
assertFalse(OfflineBackgroundTask.startScheduledProcessing(mBackgroundSchedulerProcessor,
RuntimeEnvironment.application, mTaskExtras, mInternalBooleanCallback));
// Check with BackgroundSchedulerProcessor that it did not start.
verify(mBackgroundSchedulerProcessor, times(1))
.startScheduledProcessing(eq(mDeviceConditions), eq(mInternalBooleanCallback));
}
}