blob: 0d452e9afdf9d8ff19f4f39b4e89edc2210d4ca2 [file] [log] [blame]
// Copyright 2015 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.
#include "chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_win.h"
#include <initializer_list>
#include <set>
#include <tuple>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/synchronization/lock.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/version.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/component_updater/sw_reporter_installer_win.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_controller_win.h"
#include "chrome/browser/safe_browsing/chrome_cleaner/srt_client_info_win.h"
#include "chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/test_launcher_utils.h"
#include "components/component_updater/pref_names.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_service.h"
#include "components/safe_browsing/common/safe_browsing_prefs.h"
#include "components/variations/variations_params_manager.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace safe_browsing {
namespace {
using ::testing::_;
using ::testing::DoAll;
using ::testing::Eq;
using ::testing::InvokeWithoutArgs;
using ::testing::Return;
using ::testing::SaveArg;
constexpr char kSRTPromptGroup[] = "SRTGroup";
class Waiter {
public:
Waiter() = default;
~Waiter() = default;
void Wait() {
while (!Done())
base::RunLoop().RunUntilIdle();
}
bool Done() {
base::AutoLock lock(lock_);
return done_;
}
void Signal() {
base::AutoLock lock(lock_);
done_ = true;
}
private:
bool done_ = false;
base::Lock lock_;
};
// Parameters for this test:
// - bool: whether the policy is managed or not.
// - bool: if managed, whether the policy is forced on or not.
using ReporterRunnerPolicyTestParams = std::tuple<bool, bool>;
enum class ReporterRunnerPolicy {
kNoPolicy,
kEnabled,
kDisabled,
};
class ReporterRunnerPolicyTest
: public InProcessBrowserTest,
public ::testing::WithParamInterface<ReporterRunnerPolicy> {
public:
ReporterRunnerPolicyTest() {
policy_ = GetParam();
component_updater::SetRegisterSwReporterComponentCallbackForTesting(
base::BindOnce(&ReporterRunnerPolicyTest::ComponentRegistered,
base::Unretained(this)));
}
void WaitForComponentRegistration() { waiter_.Wait(); }
protected:
ReporterRunnerPolicy policy_;
private:
void SetUpCommandLine(base::CommandLine* command_line) override {}
// Make sure the component will be installed during the test.
void SetUpDefaultCommandLine(base::CommandLine* command_line) override {
base::CommandLine default_command_line(base::CommandLine::NO_PROGRAM);
InProcessBrowserTest::SetUpDefaultCommandLine(&default_command_line);
test_launcher_utils::RemoveCommandLineSwitch(
default_command_line, switches::kDisableComponentUpdate, command_line);
}
void SetUpInProcessBrowserTestFixture() override {
EXPECT_CALL(policy_provider_, IsInitializationComplete(_))
.WillRepeatedly(Return(true));
policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
&policy_provider_);
// Setup polices as needed.
if (policy_ != ReporterRunnerPolicy::kNoPolicy) {
bool is_enabled = policy_ == ReporterRunnerPolicy::kEnabled;
policy::PolicyMap policies;
policies.Set(policy::key::kChromeCleanupEnabled,
policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
policy::POLICY_SOURCE_PLATFORM,
std::make_unique<base::Value>(is_enabled), nullptr);
policy_provider_.UpdateChromePolicy(policies);
}
}
void ComponentRegistered() { waiter_.Signal(); }
policy::MockConfigurationPolicyProvider policy_provider_;
Waiter waiter_;
DISALLOW_COPY_AND_ASSIGN(ReporterRunnerPolicyTest);
};
IN_PROC_BROWSER_TEST_P(ReporterRunnerPolicyTest, CheckComponent) {
WaitForComponentRegistration();
// If the cleaner is disabled by policy, there should be no reporter
// component installed. Otherwise it should be installed.
std::vector<std::string> component_ids =
g_browser_process->component_updater()->GetComponentIDs();
bool sw_component_registered = base::ContainsValue(
component_ids, component_updater::kSwReporterComponentId);
ASSERT_EQ(policy_ != ReporterRunnerPolicy::kDisabled,
sw_component_registered);
}
INSTANTIATE_TEST_SUITE_P(PolicyControl,
ReporterRunnerPolicyTest,
::testing::Values(ReporterRunnerPolicy::kNoPolicy,
ReporterRunnerPolicy::kEnabled,
ReporterRunnerPolicy::kDisabled));
// The state of the the enterprise policy to use during tests.
enum class PolicyState {
kNoLogs, // Don't set the policy for logs.
kLogsForceEnable, // Force the logs to be enabled.
kLogsForceDisable, // Force the logs to be disabled.
};
// Parameters for this test:
// - SwReporterInvocationType invocation_type_: identifies the type of
// invocation being tested.
// - const char* old_seed_: The old "Seed" Finch parameter saved in prefs.
// - const char* incoming_seed_: The new "Seed" Finch parameter.
// - PolicyState policy_state_: The state of logs reporting enterprise policy.
using ReporterRunnerTestParams =
std::tuple<SwReporterInvocationType, const char*, const char*, PolicyState>;
class ReporterRunnerTest
: public InProcessBrowserTest,
public SwReporterTestingDelegate,
public ::testing::WithParamInterface<ReporterRunnerTestParams> {
public:
ReporterRunnerTest() {
std::tie(invocation_type_, old_seed_, incoming_seed_, policy_state_) =
GetParam();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
variations::testing::VariationParamsManager::AppendVariationParams(
kSRTPromptTrial, kSRTPromptGroup, {{"Seed", incoming_seed_}},
command_line);
}
void SetUpInProcessBrowserTestFixture() override {
SetSwReporterTestingDelegate(this);
EXPECT_CALL(policy_provider_, IsInitializationComplete(_))
.WillRepeatedly(Return(true));
policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
&policy_provider_);
switch (policy_state_) {
case PolicyState::kNoLogs:
break;
case PolicyState::kLogsForceEnable:
case PolicyState::kLogsForceDisable: {
bool is_enabled = policy_state_ == PolicyState::kLogsForceEnable;
policy::PolicyMap policies;
policies.Set(policy::key::kChromeCleanupReportingEnabled,
policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
policy::POLICY_SOURCE_PLATFORM,
std::make_unique<base::Value>(is_enabled), nullptr);
policy_provider_.UpdateChromePolicy(policies);
EXPECT_CALL(mock_chrome_cleaner_controller_, logs_enabled(_))
.WillRepeatedly(Return(is_enabled));
break;
}
}
}
void SetUpOnMainThread() override {
// SetDateInLocalState calculates a time as Now() minus an offset. Move the
// simulated clock ahead far enough that this calculation won't underflow.
FastForwardBy(
base::TimeDelta::FromDays(kDaysBetweenSuccessfulSwReporterRuns * 2));
ClearLastTimeSentReport();
chrome::FindLastActive()->profile()->GetPrefs()->SetString(
prefs::kSwReporterPromptSeed, old_seed_);
}
void TearDownInProcessBrowserTestFixture() override {
SetSwReporterTestingDelegate(nullptr);
}
// Records that the reporter was launched with the parameters given in
// |invocation|.
int LaunchReporter(const SwReporterInvocation& invocation) override {
++reporter_launch_count_;
reporter_launch_parameters_.push_back(invocation);
if (first_launch_callback_)
std::move(first_launch_callback_).Run();
return exit_code_to_report_;
}
// Returns the test's idea of the current time.
base::Time Now() const override { return now_; }
ChromeCleanerController* GetCleanerController() override {
return &mock_chrome_cleaner_controller_;
}
void CreateChromeCleanerDialogController() override {
dialog_controller_created_ = true;
}
void FastForwardBy(base::TimeDelta delta) { now_ += delta; }
// Returns a task runner to use when launching the reporter (which is
// normally a blocking action).
base::TaskRunner* BlockingTaskRunner() const override {
// Use the test's main task runner so that we don't need to pump another
// message loop. Since the test calls LaunchReporter instead of actually
// doing a blocking reporter launch, it doesn't matter that the task runner
// doesn't have the MayBlock trait.
return base::ThreadTaskRunnerHandle::Get().get();
}
void ResetReporterRuns(int exit_code_to_report) {
exit_code_to_report_ = exit_code_to_report;
reporter_launch_count_ = 0;
reporter_launch_parameters_.clear();
dialog_controller_created_ = false;
}
SwReporterInvocation CreateInvocation(const base::FilePath& exe_path) {
return SwReporterInvocation(base::CommandLine(exe_path))
.WithSupportedBehaviours(
SwReporterInvocation::BEHAVIOURS_ENABLED_BY_DEFAULT);
}
SwReporterInvocationSequence CreateInvocationSequence(
const SwReporterInvocationSequence::Queue& container) {
SwReporterInvocationSequence sequence(base::Version("1.2.3"));
sequence.mutable_container() = container;
return sequence;
}
// Schedules a single reporter to run.
void RunSingleReporterAndWait(int exit_code_to_report,
SwReporterInvocationResult expected_result) {
RunReporterQueueAndWait(exit_code_to_report, expected_result,
SwReporterInvocationSequence::Queue(
{CreateInvocation(base::FilePath())}));
}
// Schedules a queue of reporters to run.
void RunReporterQueueAndWait(
int exit_code_to_report,
SwReporterInvocationResult expected_result,
const SwReporterInvocationSequence::Queue& container) {
ResetReporterRuns(exit_code_to_report);
ON_CALL(mock_chrome_cleaner_controller_, state())
.WillByDefault(Return(ChromeCleanerController::State::kIdle));
if (expected_result != SwReporterInvocationResult::kNotScheduled) {
EXPECT_CALL(mock_chrome_cleaner_controller_, OnReporterSequenceStarted())
.Times(1);
}
Waiter on_sequence_done;
EXPECT_CALL(mock_chrome_cleaner_controller_,
OnReporterSequenceDone(Eq(expected_result)))
.WillOnce(InvokeWithoutArgs(&on_sequence_done, &Waiter::Signal));
auto invocations = CreateInvocationSequence(container);
MaybeStartSwReporter(invocation_type_, std::move(invocations));
on_sequence_done.Wait();
}
// Sets |path| in the local state to a date corresponding to |days| days ago.
void SetDateInLocalState(const std::string& path, int days) {
PrefService* local_state = g_browser_process->local_state();
local_state->SetInt64(
path, (Now() - base::TimeDelta::FromDays(days)).ToInternalValue());
}
// Sets local state for last time the software reporter ran to |days| days
// ago.
void SetDaysSinceLastTriggered(int days) {
SetDateInLocalState(prefs::kSwReporterLastTimeTriggered, days);
}
// Sets local state for last time the software reporter sent logs to |days|
// days ago.
void SetLastTimeSentReport(int days) {
SetDateInLocalState(prefs::kSwReporterLastTimeSentReport, days);
}
// Clears local state for last time the software reporter sent logs. This
// prevents potential false positives that could arise from state not
// properly cleaned between successive tests.
void ClearLastTimeSentReport() {
PrefService* local_state = g_browser_process->local_state();
local_state->ClearPref(prefs::kSwReporterLastTimeSentReport);
}
// Retrieves the timestamp of the last time the software reporter sent logs.
int64_t GetLastTimeSentReport() {
const PrefService* local_state = g_browser_process->local_state();
DCHECK(local_state->HasPrefPath(prefs::kSwReporterLastTimeSentReport));
return local_state->GetInt64(prefs::kSwReporterLastTimeSentReport);
}
void EnableSBExtendedReporting() {
Browser* browser = chrome::FindLastActive();
DCHECK(browser);
Profile* profile = browser->profile();
SetExtendedReportingPref(profile->GetPrefs(), true);
}
// Expects that the reporter has been launched |expected_launch_count| times,
// and TriggerPrompt will be called if and only if |expect_prompt| is true.
void ExpectNumReporterLaunches(int expected_launch_count,
bool expect_prompt) {
EXPECT_EQ(expected_launch_count, reporter_launch_count_);
EXPECT_EQ(expect_prompt, dialog_controller_created_);
}
// Expects that the reporter has been launched once with each path in
// |expected_launch_paths|, and TriggerPrompt will be called if and only
// if |expect_prompt| is true.
void ExpectReporterLaunches(
const std::vector<base::FilePath>& expected_launch_paths,
bool expect_prompt) {
ExpectNumReporterLaunches(expected_launch_paths.size(), expect_prompt);
EXPECT_EQ(expected_launch_paths.size(), reporter_launch_parameters_.size());
for (size_t i = 0; i < expected_launch_paths.size() &&
i < reporter_launch_parameters_.size();
++i) {
EXPECT_EQ(expected_launch_paths[i],
reporter_launch_parameters_[i].command_line().GetProgram());
}
}
void ExpectLastTimeSentReportNotSet() {
PrefService* local_state = g_browser_process->local_state();
EXPECT_FALSE(
local_state->HasPrefPath(prefs::kSwReporterLastTimeSentReport));
}
void ExpectLastReportSentInTheLastHour() {
const PrefService* local_state = g_browser_process->local_state();
const base::Time now = Now();
const base::Time last_time_sent_logs = base::Time::FromInternalValue(
local_state->GetInt64(prefs::kSwReporterLastTimeSentReport));
// Checks if the last time sent logs is set as no more than one hour ago,
// which should be enough time if the execution does not fail.
EXPECT_LT(now - base::TimeDelta::FromHours(1), last_time_sent_logs);
EXPECT_GE(now, last_time_sent_logs);
}
// Returns true if it is expected that the test will enable logging.
bool ExpectLogging(bool expect_logging_by_default) {
bool expect_logging = expect_logging_by_default;
if (policy_state_ == PolicyState::kLogsForceDisable ||
invocation_type_ ==
SwReporterInvocationType::kUserInitiatedWithLogsDisallowed) {
expect_logging = false;
} else if (invocation_type_ ==
SwReporterInvocationType::kUserInitiatedWithLogsAllowed) {
expect_logging = true;
}
return expect_logging;
}
// Expects |invocation|'s command line to contain all the switches required
// for reporter logging if and only if |expect_switches| is true.
void ExpectLoggingSwitches(const SwReporterInvocation& invocation,
bool expect_switches) {
static const std::set<std::string> logging_switches{
chrome_cleaner::kExtendedSafeBrowsingEnabledSwitch,
chrome_cleaner::kChromeVersionSwitch,
chrome_cleaner::kChromeChannelSwitch};
const base::CommandLine::SwitchMap& invocation_switches =
invocation.command_line().GetSwitches();
// Checks if switches that enable logging on the reporter are present on
// the invocation if and only if logging is allowed.
for (const std::string& logging_switch : logging_switches) {
EXPECT_EQ(expect_switches, invocation_switches.find(logging_switch) !=
invocation_switches.end());
}
}
void ExpectReporterLoggingHappenedInTheLastHour(
const SwReporterInvocation& invocation,
bool expect_logging) {
ExpectLoggingSwitches(invocation, expect_logging);
if (expect_logging)
ExpectLastReportSentInTheLastHour();
}
void FutureExpectReporterLoggingHappenedInTheLastHour(
base::OnceCallback<const SwReporterInvocation&()> get_invocation,
bool expect_logging) {
ExpectReporterLoggingHappenedInTheLastHour(std::move(get_invocation).Run(),
expect_logging);
}
void ExpectNoReporterLoggingWithLastTimeSentReportNotSet(
const SwReporterInvocation& invocation) {
ExpectLoggingSwitches(invocation, false);
ExpectLastTimeSentReportNotSet();
}
void ExpectNoReporterLoggingWithLastTimeSentReportSet(
const SwReporterInvocation& invocation,
int64_t last_time_sent_logs) {
ExpectLoggingSwitches(invocation, false);
EXPECT_EQ(last_time_sent_logs, GetLastTimeSentReport());
}
void FutureExpectNoReporterLoggingWithLastTimeSentReportSet(
base::OnceCallback<const SwReporterInvocation&()> get_invocation,
int64_t last_time_sent_logs) {
ExpectNoReporterLoggingWithLastTimeSentReportSet(
std::move(get_invocation).Run(), last_time_sent_logs);
}
void ExpectReporterLoggingBehavior(const SwReporterInvocation& invocation,
bool expect_logging_by_default,
int64_t last_time_sent_logs) {
bool expect_logging = ExpectLogging(expect_logging_by_default);
if (expect_logging) {
ExpectReporterLoggingHappenedInTheLastHour(invocation, expect_logging);
} else if (last_time_sent_logs == -1) {
ExpectNoReporterLoggingWithLastTimeSentReportNotSet(invocation);
} else {
ExpectNoReporterLoggingWithLastTimeSentReportSet(invocation,
last_time_sent_logs);
}
}
bool LogsDisabledByUser() {
return invocation_type_ ==
SwReporterInvocationType::kUserInitiatedWithLogsDisallowed;
}
void ExpectResultOnSequenceDone(SwReporterInvocationResult expected_result,
base::OnceClosure closure,
SwReporterInvocationResult result) {
EXPECT_EQ(expected_result, result);
std::move(closure).Run();
}
OnReporterSequenceDone ExpectResultOnSequenceDoneCallback(
SwReporterInvocationResult expected_result,
base::OnceClosure closure) {
return base::BindOnce(&ReporterRunnerTest::ExpectResultOnSequenceDone,
base::Unretained(this), expected_result,
base::Passed(&closure));
}
bool PromptDialogShouldBeShown(SwReporterInvocationType invocation_type) {
return !IsUserInitiated(invocation_type) &&
(incoming_seed_.empty() || (incoming_seed_ != old_seed_));
}
// Tests that a sequence of type |invocation_type1|, once scheduled, will not
// allow a sequence of type |invocation_type2| be scheduled.
void TestSequenceNotScheduled(SwReporterInvocationType invocation_type1,
SwReporterInvocationType invocation_type2) {
const base::FilePath path1(L"path1");
const base::FilePath path2(L"path2");
ResetReporterRuns(chrome_cleaner::kSwReporterNothingFound);
ON_CALL(mock_chrome_cleaner_controller_, state())
.WillByDefault(Return(ChromeCleanerController::State::kIdle));
EXPECT_CALL(mock_chrome_cleaner_controller_, OnReporterSequenceStarted())
.Times(1);
// Launch two sequences that will be run in the following order:
// - The first sequence (of type |invocation_type1|) starts and waits to
// proceed;
// - The second sequence (of type |invocation_type2|) is scheduled and this
// test is blocked until all sequences complete;
// - The second sequence can't be scheduled and its invocation will not
// run, because the first sequence is already running; it will then be
// deleted and will release the first sequence that is waiting to
// proceed;
// - The first sequence, once unblocked, will finish and unblock this test;
// - At the end, check if only the invocation in the first sequence (path1)
// was run.
Waiter first_sequence_done;
Waiter second_sequence_done;
EXPECT_CALL(
mock_chrome_cleaner_controller_,
OnReporterSequenceDone(Eq(SwReporterInvocationResult::kNothingFound)))
.WillOnce(
DoAll(InvokeWithoutArgs(&second_sequence_done, &Waiter::Wait),
InvokeWithoutArgs(&first_sequence_done, &Waiter::Signal)));
SwReporterInvocationSequence::Queue invocations1({CreateInvocation(path1)});
MaybeStartSwReporter(invocation_type1,
CreateInvocationSequence(invocations1));
EXPECT_CALL(
mock_chrome_cleaner_controller_,
OnReporterSequenceDone(Eq(SwReporterInvocationResult::kNotScheduled)))
.WillOnce(InvokeWithoutArgs(&second_sequence_done, &Waiter::Signal));
SwReporterInvocationSequence::Queue invocations2({CreateInvocation(path2)});
MaybeStartSwReporter(invocation_type2,
CreateInvocationSequence(invocations2));
first_sequence_done.Wait();
ExpectReporterLaunches({path1}, /*expect_prompt=*/false);
}
base::Time now_;
// Test parameters.
SwReporterInvocationType invocation_type_;
std::string old_seed_;
std::string incoming_seed_;
PolicyState policy_state_;
bool dialog_controller_created_ = false;
int reporter_launch_count_ = 0;
std::vector<SwReporterInvocation> reporter_launch_parameters_;
int exit_code_to_report_ = kReporterNotLaunchedExitCode;
// Using NiceMock to suppress warnings about uninteresting calls.
::testing::NiceMock<policy::MockConfigurationPolicyProvider> policy_provider_;
// Using NiceMock to suppress warnings about uninteresting calls to state().
::testing::NiceMock<MockChromeCleanerController>
mock_chrome_cleaner_controller_;
// A callback to invoke when the first reporter of a queue is launched. This
// can be used to perform actions in the middle of a queue of reporters which
// all launch on the same mock clock tick.
base::OnceClosure first_launch_callback_;
base::test::ScopedFeatureList scoped_feature_list_;
private:
DISALLOW_COPY_AND_ASSIGN(ReporterRunnerTest);
};
} // namespace
IN_PROC_BROWSER_TEST_P(ReporterRunnerTest, NothingFound) {
RunSingleReporterAndWait(chrome_cleaner::kSwReporterNothingFound,
SwReporterInvocationResult::kNothingFound);
ExpectNumReporterLaunches(/*expected_launch_count=*/1,
/*expect_prompt=*/false);
}
IN_PROC_BROWSER_TEST_P(ReporterRunnerTest, CleanupNeeded) {
bool cleanup_offered =
IsUserInitiated(invocation_type_) ||
(incoming_seed_.empty() || (incoming_seed_ != old_seed_));
SwReporterInvocation scan_invocation = CreateInvocation(base::FilePath());
if (cleanup_offered) {
EXPECT_CALL(mock_chrome_cleaner_controller_, Scan(_))
.WillOnce(SaveArg<0>(&scan_invocation));
}
RunSingleReporterAndWait(
chrome_cleaner::kSwReporterCleanupNeeded,
cleanup_offered ? SwReporterInvocationResult::kCleanupToBeOffered
: SwReporterInvocationResult::kCleanupNotOffered);
ExpectNumReporterLaunches(
/*expected_launch_count=*/1,
/*expect_prompt=*/PromptDialogShouldBeShown(invocation_type_));
if (cleanup_offered) {
EXPECT_EQ(invocation_type_ ==
SwReporterInvocationType::kUserInitiatedWithLogsAllowed,
scan_invocation.cleaner_logs_upload_enabled());
EXPECT_EQ(invocation_type_ == SwReporterInvocationType::kPeriodicRun
? chrome_cleaner::ChromePromptValue::kPrompted
: chrome_cleaner::ChromePromptValue::kUserInitiated,
scan_invocation.chrome_prompt());
}
}
IN_PROC_BROWSER_TEST_P(ReporterRunnerTest, RanRecently) {
constexpr int kDaysLeft = 1;
SetDaysSinceLastTriggered(kDaysBetweenSuccessfulSwReporterRuns - kDaysLeft);
if (IsUserInitiated(invocation_type_)) {
RunSingleReporterAndWait(chrome_cleaner::kSwReporterNothingFound,
SwReporterInvocationResult::kNothingFound);
ExpectNumReporterLaunches(/*expected_launch_count=*/1,
/*expect_prompt=*/false);
} else {
RunSingleReporterAndWait(chrome_cleaner::kSwReporterNothingFound,
SwReporterInvocationResult::kNotScheduled);
ExpectNumReporterLaunches(/*expected_launch_count=*/0,
/*expect_prompt=*/false);
}
}
IN_PROC_BROWSER_TEST_P(ReporterRunnerTest, FailureToLaunch) {
RunSingleReporterAndWait(kReporterNotLaunchedExitCode,
SwReporterInvocationResult::kProcessFailedToLaunch);
ExpectNumReporterLaunches(/*expected_launch_count=*/1,
/*expect_prompt=*/false);
}
IN_PROC_BROWSER_TEST_P(ReporterRunnerTest, GeneralFailure) {
constexpr int kFailureExitCode = 1;
RunSingleReporterAndWait(kFailureExitCode,
SwReporterInvocationResult::kGeneralFailure);
ExpectNumReporterLaunches(/*expected_launch_count=*/1,
/*expect_prompt=*/false);
}
IN_PROC_BROWSER_TEST_P(ReporterRunnerTest, MultipleLaunches) {
constexpr int kAFewDays = 3;
const base::FilePath path1(L"path1");
const base::FilePath path2(L"path2");
SwReporterInvocationSequence::Queue invocations(
{CreateInvocation(path1), CreateInvocation(path2)});
{
SCOPED_TRACE("Launch 2 times");
SetDaysSinceLastTriggered(kDaysBetweenSuccessfulSwReporterRuns);
RunReporterQueueAndWait(chrome_cleaner::kSwReporterNothingFound,
SwReporterInvocationResult::kNothingFound,
invocations);
ExpectReporterLaunches({path1, path2}, /*expect_prompt=*/false);
}
// Schedule a launch with 2 invocations, then another with the same 2.
{
SCOPED_TRACE("Launch 2 times with retry");
FastForwardBy(
base::TimeDelta::FromDays(kDaysBetweenSuccessfulSwReporterRuns));
RunReporterQueueAndWait(chrome_cleaner::kSwReporterNothingFound,
SwReporterInvocationResult::kNothingFound,
invocations);
ExpectReporterLaunches({path1, path2}, /*expect_prompt=*/false);
if (IsUserInitiated(invocation_type_)) {
// If user initiated, the second invocation sequence should run again.
RunReporterQueueAndWait(chrome_cleaner::kSwReporterNothingFound,
SwReporterInvocationResult::kNothingFound,
invocations);
ExpectReporterLaunches({path1, path2}, /*expect_prompt=*/false);
} else {
// For periodic runs, the second invocation sequence shouldn't run again,
// since the first sequence has just finished.
RunReporterQueueAndWait(chrome_cleaner::kSwReporterNothingFound,
SwReporterInvocationResult::kNotScheduled,
invocations);
ExpectReporterLaunches({}, /*expect_prompt=*/false);
}
}
{
SCOPED_TRACE("Attempt to launch after a few days");
FastForwardBy(base::TimeDelta::FromDays(kAFewDays));
if (IsUserInitiated(invocation_type_)) {
RunReporterQueueAndWait(chrome_cleaner::kSwReporterNothingFound,
SwReporterInvocationResult::kNothingFound,
invocations);
ExpectReporterLaunches({path1, path2}, /*expect_prompt=*/false);
} else {
RunReporterQueueAndWait(chrome_cleaner::kSwReporterNothingFound,
SwReporterInvocationResult::kNotScheduled,
invocations);
ExpectReporterLaunches({}, /*expect_prompt=*/false);
}
FastForwardBy(base::TimeDelta::FromDays(
kDaysBetweenSuccessfulSwReporterRuns - kAFewDays));
RunReporterQueueAndWait(chrome_cleaner::kSwReporterNothingFound,
SwReporterInvocationResult::kNothingFound,
invocations);
ExpectReporterLaunches({path1, path2}, /*expect_prompt=*/false);
}
// Second launch should not occur after a failure.
{
SCOPED_TRACE("Launch multiple times with failure");
FastForwardBy(
base::TimeDelta::FromDays(kDaysBetweenSuccessfulSwReporterRuns));
RunReporterQueueAndWait(kReporterNotLaunchedExitCode,
SwReporterInvocationResult::kProcessFailedToLaunch,
invocations);
ExpectReporterLaunches({path1}, /*expect_prompt=*/false);
}
}
IN_PROC_BROWSER_TEST_P(ReporterRunnerTest,
ReporterLogging_NoSBExtendedReporting) {
RunSingleReporterAndWait(chrome_cleaner::kSwReporterNothingFound,
SwReporterInvocationResult::kNothingFound);
ExpectNumReporterLaunches(/*expected_launch_count=*/1,
/*expect_prompt=*/false);
ExpectReporterLoggingBehavior(reporter_launch_parameters_.front(),
/*expect_logging_by_default=*/false,
/*last_time_sent_logs=*/-1);
}
IN_PROC_BROWSER_TEST_P(ReporterRunnerTest, ReporterLogging_EnabledFirstRun) {
EnableSBExtendedReporting();
// Note: don't set last time sent logs in the local state.
// SBER is enabled and there is no record in the local state of the last time
// logs have been sent, so we should send logs in this run.
RunSingleReporterAndWait(chrome_cleaner::kSwReporterNothingFound,
SwReporterInvocationResult::kNothingFound);
ExpectNumReporterLaunches(/*expected_launch_count=*/1,
/*expect_prompt=*/false);
ExpectReporterLoggingBehavior(reporter_launch_parameters_.front(),
/*expect_logging_by_default=*/true,
/*last_time_sent_logs=*/-1);
}
IN_PROC_BROWSER_TEST_P(ReporterRunnerTest,
ReporterLogging_EnabledNoRecentLogging) {
// SBER is enabled and last time logs were sent was more than
// |kDaysBetweenReporterLogsSent| day ago, so we should send logs in this run.
EnableSBExtendedReporting();
SetLastTimeSentReport(kDaysBetweenReporterLogsSent + 3);
int64_t last_time_sent_logs = GetLastTimeSentReport();
RunSingleReporterAndWait(chrome_cleaner::kSwReporterNothingFound,
SwReporterInvocationResult::kNothingFound);
ExpectNumReporterLaunches(/*expected_launch_count=*/1,
/*expect_prompt=*/false);
ExpectReporterLoggingBehavior(reporter_launch_parameters_.front(),
/*expect_logging_by_default=*/true,
last_time_sent_logs);
}
IN_PROC_BROWSER_TEST_P(ReporterRunnerTest,
ReporterLogging_EnabledRecentlyLogged) {
// SBER is enabled, but logs have been sent less than
// |kDaysBetweenReporterLogsSent| day ago, so we shouldn't send any logs in
// this run.
EnableSBExtendedReporting();
SetLastTimeSentReport(kDaysBetweenReporterLogsSent - 1);
int64_t last_time_sent_logs = GetLastTimeSentReport();
RunSingleReporterAndWait(chrome_cleaner::kSwReporterNothingFound,
SwReporterInvocationResult::kNothingFound);
ExpectNumReporterLaunches(/*expected_launch_count=*/1,
/*expect_prompt=*/false);
ExpectReporterLoggingBehavior(reporter_launch_parameters_.front(),
/*expect_logging_by_default=*/false,
last_time_sent_logs);
}
IN_PROC_BROWSER_TEST_P(ReporterRunnerTest, ReporterLogging_MultipleLaunches) {
EnableSBExtendedReporting();
SetLastTimeSentReport(kDaysBetweenReporterLogsSent + 3);
int64_t last_time_sent_logs = GetLastTimeSentReport();
const base::FilePath path1(L"path1");
const base::FilePath path2(L"path2");
SwReporterInvocationSequence::Queue invocations(
{CreateInvocation(path1), CreateInvocation(path2)});
auto get_first_invocation = base::BindOnce(
[](const std::vector<SwReporterInvocation>* params)
-> const SwReporterInvocation& { return params->front(); },
&reporter_launch_parameters_);
if (LogsDisabledByUser()) {
first_launch_callback_ = base::BindOnce(
&ReporterRunnerTest::
FutureExpectNoReporterLoggingWithLastTimeSentReportSet,
base::Unretained(this), base::Passed(&get_first_invocation),
last_time_sent_logs);
} else {
bool expect_logging = ExpectLogging(true);
first_launch_callback_ = base::BindOnce(
&ReporterRunnerTest::FutureExpectReporterLoggingHappenedInTheLastHour,
base::Unretained(this), base::Passed(&get_first_invocation),
expect_logging);
}
RunReporterQueueAndWait(chrome_cleaner::kSwReporterNothingFound,
SwReporterInvocationResult::kNothingFound,
invocations);
// SBER is enabled and last time logs were sent was more than
// |kDaysBetweenReporterLogsSent| day ago, so we should send logs in this run.
{
SCOPED_TRACE("first launch");
ExpectReporterLaunches({path1, path2}, /*expect_prompt=*/false);
}
// Logs should also be sent for the next run, even though LastTimeSentReport
// is now recent, because the run is part of the same set of invocations.
{
SCOPED_TRACE("second launch");
ExpectReporterLoggingBehavior(reporter_launch_parameters_[1],
/*expect_logging_by_default=*/true,
last_time_sent_logs);
}
}
IN_PROC_BROWSER_TEST_P(ReporterRunnerTest,
PeriodicRunsNotScheduledIfAlreadyRunning) {
TestSequenceNotScheduled(invocation_type_,
SwReporterInvocationType::kPeriodicRun);
}
IN_PROC_BROWSER_TEST_P(ReporterRunnerTest,
UserInitiatedRunsNotScheduledIfAlreadyRunning) {
// No need to test for periodic runs, since the corresponding test cases are
// already exercised by PeriodicRunsNotScheduledIfAlreadyRunning.
if (!IsUserInitiated(invocation_type_))
return;
TestSequenceNotScheduled(
invocation_type_,
SwReporterInvocationType::kUserInitiatedWithLogsDisallowed);
}
// Make the invocation type test parameter printable.
std::ostream& operator<<(std::ostream& out,
SwReporterInvocationType invocation_type) {
switch (invocation_type) {
case SwReporterInvocationType::kUnspecified:
return out << "Unspecified";
case SwReporterInvocationType::kPeriodicRun:
return out << "PeriodicRun";
case SwReporterInvocationType::kUserInitiatedWithLogsDisallowed:
return out << "UserInitiatedWithLogsDisallowed";
case SwReporterInvocationType::kUserInitiatedWithLogsAllowed:
return out << "UserInitiatedWithLogsAllowed";
default:
NOTREACHED();
return out << "UnknownSwReporterInvocationType";
}
}
// Make the invocation type test parameter printable.
std::ostream& operator<<(std::ostream& out, PolicyState policy_state) {
switch (policy_state) {
case PolicyState::kNoLogs:
return out << "NoLogs";
case PolicyState::kLogsForceEnable:
return out << "LogsEnabled";
case PolicyState::kLogsForceDisable:
return out << "LogsDisabled";
default:
NOTREACHED();
return out << "UnknownPolicyState";
}
}
// ::testing::PrintToStringParamName does not format tuples as a valid test
// name, so this functor can be used to get each element in the tuple
// explicitly and format them using the above operator<< overrides.
struct ReporterRunTestParamsToString {
std::string operator()(
const ::testing::TestParamInfo<ReporterRunnerTestParams>& info) const {
std::ostringstream param_name;
param_name << std::get<0>(info.param) << "_";
const std::string old_seed(std::get<1>(info.param));
param_name << (old_seed.empty() ? "EmptySeed" : old_seed) << "_";
const std::string incoming_seed(std::get<2>(info.param));
param_name << (incoming_seed.empty() ? "EmptySeed" : incoming_seed) << "_";
param_name << std::get<3>(info.param);
return param_name.str();
}
};
// Tests without an enterprise policy set.
INSTANTIATE_TEST_SUITE_P(
NoEnterprisePolicy,
ReporterRunnerTest,
::testing::Combine(
::testing::Values(
SwReporterInvocationType::kPeriodicRun,
SwReporterInvocationType::kUserInitiatedWithLogsDisallowed,
SwReporterInvocationType::kUserInitiatedWithLogsAllowed),
::testing::Values("", "Seed1"), // old_seed_
::testing::Values("", "Seed1", "Seed2"), // incoming_seed_
::testing::Values(PolicyState::kNoLogs)),
ReporterRunTestParamsToString());
// Tests with enterprise policies forcing reporting to be either enabled
// or disabled.
INSTANTIATE_TEST_SUITE_P(
EnterprisePolicy,
ReporterRunnerTest,
::testing::Combine(
::testing::Values(
SwReporterInvocationType::kPeriodicRun,
SwReporterInvocationType::kUserInitiatedWithLogsDisallowed,
SwReporterInvocationType::kUserInitiatedWithLogsAllowed),
::testing::Values("Seed1"), // old_seed_
::testing::Values("Seed2"), // incoming_seed_
::testing::Values(PolicyState::kLogsForceEnable,
PolicyState::kLogsForceDisable)),
ReporterRunTestParamsToString());
} // namespace safe_browsing