blob: 208e99b3f53223d94de4d6b0df5932c9674812a8 [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.
#include "chrome/browser/component_updater/sw_reporter_installer_win.h"
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/json/json_reader.h"
#include "base/macros.h"
#include "base/metrics/field_trial.h"
#include "base/strings/strcat.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_reg_util_win.h"
#include "base/values.h"
#include "base/version.h"
#include "base/win/registry.h"
#include "chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_win.h"
#include "chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.h"
#include "components/chrome_cleaner/public/constants/constants.h"
#include "components/component_updater/mock_component_updater_service.h"
#include "components/variations/variations_params_manager.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace component_updater {
namespace {
constexpr char kErrorHistogramName[] = "SoftwareReporter.ExperimentErrors";
constexpr char kExperimentTag[] = "experiment_tag";
constexpr char kMissingTag[] = "missing_tag";
using safe_browsing::SwReporterInvocation;
using safe_browsing::SwReporterInvocationSequence;
using ::testing::_;
using ::testing::AtLeast;
using ::testing::ReturnRef;
using Events = update_client::UpdateClient::Observer::Events;
} // namespace
class SwReporterInstallerTest : public ::testing::Test {
public:
SwReporterInstallerTest()
: on_component_ready_callback_(
base::Bind(&SwReporterInstallerTest::SwReporterComponentReady,
base::Unretained(this))),
default_version_("1.2.3"),
default_path_(L"C:\\full\\path\\to\\download") {}
protected:
void SwReporterComponentReady(SwReporterInvocationSequence&& invocations) {
ASSERT_TRUE(extracted_invocations_.container().empty());
extracted_invocations_ = std::move(invocations);
}
base::FilePath MakeTestFilePath(const base::FilePath& path) const {
return path.Append(L"software_reporter_tool.exe");
}
void CreateFeatureWithoutTag() {
std::map<std::string, std::string> params;
CreateFeatureWithParams(params);
}
void CreateFeatureWithTag(const std::string& tag) {
std::map<std::string, std::string> params{{"reporter_omaha_tag", tag}};
CreateFeatureWithParams(params);
}
void CreateFeatureWithParams(
const std::map<std::string, std::string>& params) {
// Assign the given variation params to the experiment group until
// |variations_| goes out of scope when the test exits. This will also
// create a FieldTrial for this group and associate the params with the
// feature. For the test just re-use the feature name as the trial name.
variations_ = std::make_unique<variations::testing::VariationParamsManager>(
"SwReporterInstallerTestTrialName", params,
/*associated_features=*/
std::set<std::string>{
safe_browsing::kChromeCleanupDistributionFeature.name});
}
void ExpectAttributesWithTag(const SwReporterInstallerPolicy& policy,
const std::string& tag) {
update_client::InstallerAttributes attributes =
policy.GetInstallerAttributes();
EXPECT_EQ(1U, attributes.size());
EXPECT_EQ(tag, attributes["tag"]);
}
void ExpectEmptyAttributes(const SwReporterInstallerPolicy& policy) const {
update_client::InstallerAttributes attributes =
policy.GetInstallerAttributes();
EXPECT_TRUE(attributes.empty());
}
// Expects that the SwReporter was launched exactly once, with a session-id
// switch.
void ExpectDefaultInvocation() const {
EXPECT_EQ(default_version_, extracted_invocations_.version());
ASSERT_EQ(1U, extracted_invocations_.container().size());
const SwReporterInvocation& invocation =
extracted_invocations_.container().front();
EXPECT_EQ(MakeTestFilePath(default_path_),
invocation.command_line().GetProgram());
EXPECT_EQ(1U, invocation.command_line().GetSwitches().size());
EXPECT_FALSE(invocation.command_line()
.GetSwitchValueASCII(chrome_cleaner::kSessionIdSwitch)
.empty());
EXPECT_TRUE(invocation.command_line().GetArgs().empty());
EXPECT_TRUE(invocation.suffix().empty());
EXPECT_EQ(SwReporterInvocation::BEHAVIOURS_ENABLED_BY_DEFAULT,
invocation.supported_behaviours());
}
// Expects that the SwReporter was launched exactly once, with the given
// |expected_suffix|, a session-id, and one |expected_additional_argument| on
// the command-line. (|expected_additional_argument| mainly exists to test
// that arguments are included at all, so there is no need to test for
// combinations of multiple arguments and switches in this function.)
void ExpectInvocationFromManifest(
const std::string& expected_suffix,
const base::string16& expected_additional_argument) {
EXPECT_EQ(default_version_, extracted_invocations_.version());
ASSERT_EQ(1U, extracted_invocations_.container().size());
const SwReporterInvocation& invocation =
extracted_invocations_.container().front();
EXPECT_EQ(MakeTestFilePath(default_path_),
invocation.command_line().GetProgram());
EXPECT_FALSE(invocation.command_line()
.GetSwitchValueASCII(chrome_cleaner::kSessionIdSwitch)
.empty());
if (expected_suffix.empty()) {
EXPECT_EQ(1U, invocation.command_line().GetSwitches().size());
EXPECT_TRUE(invocation.suffix().empty());
} else {
EXPECT_EQ(2U, invocation.command_line().GetSwitches().size());
EXPECT_EQ(expected_suffix, invocation.command_line().GetSwitchValueASCII(
chrome_cleaner::kRegistrySuffixSwitch));
EXPECT_EQ(expected_suffix, invocation.suffix());
}
if (expected_additional_argument.empty()) {
EXPECT_TRUE(invocation.command_line().GetArgs().empty());
} else {
EXPECT_EQ(1U, invocation.command_line().GetArgs().size());
EXPECT_EQ(expected_additional_argument,
invocation.command_line().GetArgs()[0]);
}
EXPECT_EQ(0U, invocation.supported_behaviours());
histograms_.ExpectTotalCount(kErrorHistogramName, 0);
}
// Expects that the SwReporter was launched with the given |expected_suffix|,
// |expected_engine|, and |expected_behaviours|, as part of a series of
// multiple invocations.
void ConsumeAndCheckExperimentFromManifestInSeries(
const std::string& expected_suffix,
const std::string& expected_engine,
SwReporterInvocation::Behaviours expected_behaviours,
std::string* out_session_id) {
SCOPED_TRACE("Invocation with suffix " + expected_suffix);
SwReporterInvocation invocation =
extracted_invocations_.container().front();
extracted_invocations_.mutable_container().pop();
EXPECT_EQ(MakeTestFilePath(default_path_),
invocation.command_line().GetProgram());
// There should be one switch added from the manifest, plus registry-suffix
// and session-id added automatically.
EXPECT_EQ(3U, invocation.command_line().GetSwitches().size());
EXPECT_EQ(expected_engine,
invocation.command_line().GetSwitchValueASCII("engine"));
EXPECT_EQ(expected_suffix, invocation.command_line().GetSwitchValueASCII(
chrome_cleaner::kRegistrySuffixSwitch));
*out_session_id = invocation.command_line().GetSwitchValueASCII(
chrome_cleaner::kSessionIdSwitch);
EXPECT_FALSE(out_session_id->empty());
ASSERT_TRUE(invocation.command_line().GetArgs().empty());
EXPECT_EQ(expected_suffix, invocation.suffix());
EXPECT_EQ(expected_behaviours, invocation.supported_behaviours());
}
void ExpectLaunchError() {
// The SwReporter should not be launched, and an error should be logged.
EXPECT_TRUE(extracted_invocations_.container().empty());
histograms_.ExpectUniqueSample(kErrorHistogramName,
SW_REPORTER_EXPERIMENT_ERROR_BAD_PARAMS, 1);
}
std::unique_ptr<variations::testing::VariationParamsManager> variations_;
base::test::ScopedFeatureList scoped_feature_list_;
base::HistogramTester histograms_;
// |ComponentReady| asserts that it is run on the UI thread, so we must
// create test threads before calling it.
content::TestBrowserThreadBundle threads_;
// Bound callback to the |SwReporterComponentReady| method.
OnComponentReadyCallback on_component_ready_callback_;
// Default parameters for |ComponentReady|.
base::Version default_version_;
base::FilePath default_path_;
// Invocations captured by |SwReporterComponentReady|.
SwReporterInvocationSequence extracted_invocations_;
private:
DISALLOW_COPY_AND_ASSIGN(SwReporterInstallerTest);
};
TEST_F(SwReporterInstallerTest, MissingManifest) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
ExpectEmptyAttributes(policy);
policy.ComponentReady(default_version_, default_path_,
std::make_unique<base::DictionaryValue>());
ExpectDefaultInvocation();
}
TEST_F(SwReporterInstallerTest, MissingTag) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
CreateFeatureWithoutTag();
ExpectAttributesWithTag(policy, kMissingTag);
histograms_.ExpectUniqueSample(kErrorHistogramName,
SW_REPORTER_EXPERIMENT_ERROR_BAD_TAG, 1);
}
TEST_F(SwReporterInstallerTest, InvalidTag) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
CreateFeatureWithTag("tag with invalid whitespace chars");
ExpectAttributesWithTag(policy, kMissingTag);
histograms_.ExpectUniqueSample(kErrorHistogramName,
SW_REPORTER_EXPERIMENT_ERROR_BAD_TAG, 1);
}
TEST_F(SwReporterInstallerTest, TagTooLong) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
std::string tag_too_long(500, 'x');
CreateFeatureWithTag(tag_too_long);
ExpectAttributesWithTag(policy, kMissingTag);
histograms_.ExpectUniqueSample(kErrorHistogramName,
SW_REPORTER_EXPERIMENT_ERROR_BAD_TAG, 1);
}
TEST_F(SwReporterInstallerTest, EmptyTag) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
CreateFeatureWithTag("");
ExpectAttributesWithTag(policy, kMissingTag);
histograms_.ExpectUniqueSample(kErrorHistogramName,
SW_REPORTER_EXPERIMENT_ERROR_BAD_TAG, 1);
}
TEST_F(SwReporterInstallerTest, ValidTag) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
CreateFeatureWithTag(kExperimentTag);
ExpectAttributesWithTag(policy, kExperimentTag);
}
TEST_F(SwReporterInstallerTest, SingleInvocation) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
static constexpr char kTestManifest[] =
"{\"launch_params\": ["
" {"
" \"arguments\": [\"--engine=experimental\", \"random argument\"],"
" \"suffix\": \"TestSuffix\","
" \"prompt\": false"
" }"
"]}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
// The SwReporter should be launched once with the given arguments.
EXPECT_EQ(default_version_, extracted_invocations_.version());
ASSERT_EQ(1U, extracted_invocations_.container().size());
const SwReporterInvocation& invocation =
extracted_invocations_.container().front();
EXPECT_EQ(MakeTestFilePath(default_path_),
invocation.command_line().GetProgram());
EXPECT_EQ(3U, invocation.command_line().GetSwitches().size());
EXPECT_EQ("experimental",
invocation.command_line().GetSwitchValueASCII("engine"));
EXPECT_EQ("TestSuffix", invocation.command_line().GetSwitchValueASCII(
chrome_cleaner::kRegistrySuffixSwitch));
EXPECT_FALSE(invocation.command_line()
.GetSwitchValueASCII(chrome_cleaner::kSessionIdSwitch)
.empty());
ASSERT_EQ(1U, invocation.command_line().GetArgs().size());
EXPECT_EQ(L"random argument", invocation.command_line().GetArgs()[0]);
EXPECT_EQ("TestSuffix", invocation.suffix());
EXPECT_EQ(0U, invocation.supported_behaviours());
histograms_.ExpectTotalCount(kErrorHistogramName, 0);
}
TEST_F(SwReporterInstallerTest, MultipleInvocations) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
static constexpr char kTestManifest[] =
"{\"launch_params\": ["
" {"
" \"arguments\": [\"--engine=experimental\"],"
" \"suffix\": \"TestSuffix\","
" \"prompt\": false,"
" \"allow-reporter-logs\": true"
" },"
" {"
" \"arguments\": [\"--engine=second\"],"
" \"suffix\": \"SecondSuffix\","
" \"prompt\": true,"
" \"allow-reporter-logs\": false"
" },"
" {"
" \"arguments\": [\"--engine=third\"],"
" \"suffix\": \"ThirdSuffix\""
" },"
" {"
" \"arguments\": [\"--engine=fourth\"],"
" \"suffix\": \"FourthSuffix\","
" \"prompt\": true,"
" \"allow-reporter-logs\": true"
" }"
"]}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
// The SwReporter should be launched four times with the given arguments.
EXPECT_EQ(default_version_, extracted_invocations_.version());
ASSERT_EQ(4U, extracted_invocations_.container().size());
std::string out_session_id;
ConsumeAndCheckExperimentFromManifestInSeries("TestSuffix", "experimental",
/*supported_behaviours=*/0,
&out_session_id);
const std::string first_session_id(out_session_id);
ConsumeAndCheckExperimentFromManifestInSeries(
"SecondSuffix", "second", SwReporterInvocation::BEHAVIOUR_TRIGGER_PROMPT,
&out_session_id);
EXPECT_EQ(first_session_id, out_session_id);
ConsumeAndCheckExperimentFromManifestInSeries("ThirdSuffix", "third", 0U,
&out_session_id);
EXPECT_EQ(first_session_id, out_session_id);
ConsumeAndCheckExperimentFromManifestInSeries(
"FourthSuffix", "fourth", SwReporterInvocation::BEHAVIOUR_TRIGGER_PROMPT,
&out_session_id);
EXPECT_EQ(first_session_id, out_session_id);
histograms_.ExpectTotalCount(kErrorHistogramName, 0);
}
TEST_F(SwReporterInstallerTest, MissingSuffix) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
static constexpr char kTestManifest[] =
"{\"launch_params\": ["
" {"
" \"arguments\": [\"random argument\"]"
" }"
"]}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
ExpectLaunchError();
}
TEST_F(SwReporterInstallerTest, EmptySuffix) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
static constexpr char kTestManifest[] =
"{\"launch_params\": ["
" {"
" \"suffix\": \"\","
" \"arguments\": [\"random argument\"]"
" }"
"]}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
ExpectInvocationFromManifest("", L"random argument");
}
TEST_F(SwReporterInstallerTest, MissingSuffixAndArgs) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
static constexpr char kTestManifest[] =
"{\"launch_params\": ["
" {"
" }"
"]}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
ExpectLaunchError();
}
TEST_F(SwReporterInstallerTest, EmptySuffixAndArgs) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
static constexpr char kTestManifest[] =
"{\"launch_params\": ["
" {"
" \"suffix\": \"\","
" \"arguments\": []"
" }"
"]}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
ExpectInvocationFromManifest("", L"");
}
TEST_F(SwReporterInstallerTest, EmptySuffixAndArgsWithEmptyString) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
static constexpr char kTestManifest[] =
"{\"launch_params\": ["
" {"
" \"suffix\": \"\","
" \"arguments\": [\"\"]"
" }"
"]}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
ExpectInvocationFromManifest("", L"");
}
TEST_F(SwReporterInstallerTest, MissingArguments) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
static constexpr char kTestManifest[] =
"{\"launch_params\": ["
" {"
" \"suffix\": \"TestSuffix\""
" }"
"]}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
ExpectLaunchError();
}
TEST_F(SwReporterInstallerTest, EmptyArguments) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
static constexpr char kTestManifest[] =
"{\"launch_params\": ["
" {"
" \"suffix\": \"TestSuffix\","
" \"arguments\": []"
" }"
"]}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
ExpectInvocationFromManifest("TestSuffix", L"");
}
TEST_F(SwReporterInstallerTest, EmptyArgumentsWithEmptyString) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
static constexpr char kTestManifest[] =
"{\"launch_params\": ["
" {"
" \"suffix\": \"TestSuffix\","
" \"arguments\": [\"\"]"
" }"
"]}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
ExpectInvocationFromManifest("TestSuffix", L"");
}
TEST_F(SwReporterInstallerTest, EmptyManifest) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
static constexpr char kTestManifest[] = "{}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
ExpectDefaultInvocation();
}
TEST_F(SwReporterInstallerTest, EmptyLaunchParams) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
static constexpr char kTestManifest[] = "{\"launch_params\": []}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
ExpectDefaultInvocation();
}
TEST_F(SwReporterInstallerTest, BadSuffix) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
static constexpr char kTestManifest[] =
"{\"launch_params\": ["
" {"
" \"arguments\": [\"--engine=experimental\"],"
" \"suffix\": \"invalid whitespace characters\""
" }"
"]}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
// The SwReporter should not be launched, and an error should be logged.
EXPECT_TRUE(extracted_invocations_.container().empty());
histograms_.ExpectUniqueSample(kErrorHistogramName,
SW_REPORTER_EXPERIMENT_ERROR_BAD_PARAMS, 1);
}
TEST_F(SwReporterInstallerTest, SuffixTooLong) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
static constexpr char kTestManifest[] =
"{\"launch_params\": ["
" {"
" \"arguments\": [\"--engine=experimental\"],"
" \"suffix\": \"%s\""
" }"
"]}";
std::string suffix_too_long(500, 'x');
std::string manifest =
base::StringPrintf(kTestManifest, suffix_too_long.c_str());
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(manifest)));
// The SwReporter should not be launched, and an error should be logged.
EXPECT_TRUE(extracted_invocations_.container().empty());
histograms_.ExpectUniqueSample(kErrorHistogramName,
SW_REPORTER_EXPERIMENT_ERROR_BAD_PARAMS, 1);
}
TEST_F(SwReporterInstallerTest, BadTypesInManifest_ArgumentsIsNotAList) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
// This has a string instead of a list for "arguments".
static constexpr char kTestManifest[] =
"{\"launch_params\": ["
" {"
" \"arguments\": \"--engine=experimental\","
" \"suffix\": \"TestSuffix\""
" }"
"]}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
// The SwReporter should not be launched, and an error should be logged.
EXPECT_TRUE(extracted_invocations_.container().empty());
histograms_.ExpectUniqueSample(kErrorHistogramName,
SW_REPORTER_EXPERIMENT_ERROR_BAD_PARAMS, 1);
}
TEST_F(SwReporterInstallerTest, BadTypesInManifest_InvocationParamsIsNotAList) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
// This has the invocation parameters as direct children of "launch_params",
// instead of using a list.
static constexpr char kTestManifest[] =
"{\"launch_params\": "
" {"
" \"arguments\": [\"--engine=experimental\"],"
" \"suffix\": \"TestSuffix\""
" }"
"}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
// The SwReporter should not be launched, and an error should be logged.
EXPECT_TRUE(extracted_invocations_.container().empty());
histograms_.ExpectUniqueSample(kErrorHistogramName,
SW_REPORTER_EXPERIMENT_ERROR_BAD_PARAMS, 1);
}
TEST_F(SwReporterInstallerTest, BadTypesInManifest_SuffixIsAList) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
// This has a list for suffix as well as for arguments.
static constexpr char kTestManifest[] =
"{\"launch_params\": ["
" {"
" \"arguments\": [\"--engine=experimental\"],"
" \"suffix\": [\"TestSuffix\"]"
" }"
"]}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
// The SwReporter should not be launched, and an error should be logged.
EXPECT_TRUE(extracted_invocations_.container().empty());
histograms_.ExpectUniqueSample(kErrorHistogramName,
SW_REPORTER_EXPERIMENT_ERROR_BAD_PARAMS, 1);
}
TEST_F(SwReporterInstallerTest, BadTypesInManifest_PromptIsNotABoolean) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
// This has an int instead of a bool for prompt.
static constexpr char kTestManifest[] =
"{\"launch_params\": ["
" {"
" \"arguments\": [\"--engine=experimental\"],"
" \"suffix\": \"TestSuffix\","
" \"prompt\": 1"
" }"
"]}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
// The SwReporter should not be launched, and an error should be logged.
EXPECT_TRUE(extracted_invocations_.container().empty());
histograms_.ExpectUniqueSample(kErrorHistogramName,
SW_REPORTER_EXPERIMENT_ERROR_BAD_PARAMS, 1);
}
TEST_F(SwReporterInstallerTest, BadTypesInManifest_LaunchParamsIsScalar) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
static constexpr char kTestManifest[] = "{\"launch_params\": 0}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
// The SwReporter should not be launched, and an error should be logged.
EXPECT_TRUE(extracted_invocations_.container().empty());
histograms_.ExpectUniqueSample(kErrorHistogramName,
SW_REPORTER_EXPERIMENT_ERROR_BAD_PARAMS, 1);
}
TEST_F(SwReporterInstallerTest, BadTypesInManifest_LaunchParamsIsDict) {
SwReporterInstallerPolicy policy(on_component_ready_callback_);
static constexpr char kTestManifest[] = "{\"launch_params\": {}}";
policy.ComponentReady(
default_version_, default_path_,
base::DictionaryValue::From(base::JSONReader::Read(kTestManifest)));
// The SwReporter should not be launched, and an error should be logged.
EXPECT_TRUE(extracted_invocations_.container().empty());
histograms_.ExpectUniqueSample(kErrorHistogramName,
SW_REPORTER_EXPERIMENT_ERROR_BAD_PARAMS, 1);
}
class SwReporterOnDemandFetcherTest : public ::testing::Test,
public OnDemandUpdater {
public:
SwReporterOnDemandFetcherTest() = default;
void SetUp() override {
EXPECT_CALL(mock_cus_, AddObserver(_)).Times(1);
EXPECT_CALL(mock_cus_, GetOnDemandUpdater()).WillOnce(ReturnRef(*this));
EXPECT_CALL(mock_cus_, RemoveObserver(_)).Times(AtLeast(1));
}
void CreateOnDemandFetcherAndVerifyExpectations(bool can_be_updated) {
component_can_be_updated_ = can_be_updated;
fetcher_ = std::make_unique<SwReporterOnDemandFetcher>(
&mock_cus_,
base::BindOnce(&SwReporterOnDemandFetcherTest::SetErrorCallbackCalled,
base::Unretained(this)));
}
// OnDemandUpdater implementation:
void OnDemandUpdate(const std::string& crx_id,
Priority priority,
Callback callback) override {
ASSERT_EQ(kSwReporterComponentId, crx_id);
ASSERT_EQ(Priority::FOREGROUND, priority);
on_demand_update_called_ = true;
// |OnDemandUpdate| is called immediately on |SwReporterOnDemandFetcher|
// creation, before |fetcher_| has been assigned the newly created object.
// Post a task to guarantee that |fetcher_| is initialized.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
component_can_be_updated_
? &SwReporterOnDemandFetcherTest::FireComponentUpdateEvents
: &SwReporterOnDemandFetcherTest::FireComponentNotUpdatedEvents,
base::Unretained(this)));
}
protected:
::testing::StrictMock<MockComponentUpdateService> mock_cus_;
std::unique_ptr<SwReporterOnDemandFetcher> fetcher_;
bool on_demand_update_called_ = false;
private:
void FireComponentUpdateEvents() {
fetcher_->OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
kSwReporterComponentId);
fetcher_->OnEvent(Events::COMPONENT_UPDATE_FOUND, kSwReporterComponentId);
fetcher_->OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
kSwReporterComponentId);
fetcher_->OnEvent(Events::COMPONENT_UPDATE_READY, kSwReporterComponentId);
fetcher_->OnEvent(Events::COMPONENT_UPDATED, kSwReporterComponentId);
EXPECT_FALSE(error_callback_called_);
}
void FireComponentNotUpdatedEvents() {
fetcher_->OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
kSwReporterComponentId);
fetcher_->OnEvent(Events::COMPONENT_UPDATE_ERROR, kSwReporterComponentId);
EXPECT_TRUE(error_callback_called_);
}
void SetErrorCallbackCalled() { error_callback_called_ = true; }
bool component_can_be_updated_ = false;
bool error_callback_called_ = false;
content::TestBrowserThreadBundle threads_;
DISALLOW_COPY_AND_ASSIGN(SwReporterOnDemandFetcherTest);
};
TEST_F(SwReporterOnDemandFetcherTest, TestUpdateSuccess) {
CreateOnDemandFetcherAndVerifyExpectations(true);
EXPECT_TRUE(on_demand_update_called_);
}
TEST_F(SwReporterOnDemandFetcherTest, TestUpdateFailure) {
CreateOnDemandFetcherAndVerifyExpectations(false);
EXPECT_TRUE(on_demand_update_called_);
}
class SwReporterInstallerHistogramTest : public ::testing::Test {
protected:
void SetUp() override {
ASSERT_NO_FATAL_FAILURE(
registry_override_manager_.OverrideRegistry(HKEY_CURRENT_USER));
const base::string16 cleaner_key_name =
base::StrCat({chrome_cleaner::kSoftwareRemovalToolRegistryKey, L"\\",
chrome_cleaner::kCleanerSubKey});
cleaner_key_ = std::make_unique<base::win::RegKey>(
HKEY_CURRENT_USER, cleaner_key_name.c_str(),
KEY_QUERY_VALUE | KEY_SET_VALUE);
ASSERT_TRUE(cleaner_key_->Valid());
}
base::HistogramTester& histograms() { return histograms_; }
base::win::RegKey& cleaner_key() { return *cleaner_key_; }
// TODO(crbug.com/872824): use chrome_cleaner::RegistryLogger::WriteStartTime
// once it moves to components/chrome_cleaner
void WriteStartTime(base::Time start_time) {
const int64_t serialized =
start_time.ToDeltaSinceWindowsEpoch().InMicroseconds();
ASSERT_EQ(ERROR_SUCCESS, cleaner_key().WriteValue(
chrome_cleaner::kStartTimeValueName,
&serialized, sizeof(serialized), REG_QWORD));
}
// TODO(crbug.com/872824): use chrome_cleaner::RegistryLogger::WriteEndTime
// once it moves to components/chrome_cleaner
void WriteEndTime(base::Time end_time) {
const int64_t serialized =
end_time.ToDeltaSinceWindowsEpoch().InMicroseconds();
ASSERT_EQ(ERROR_SUCCESS, cleaner_key().WriteValue(
chrome_cleaner::kEndTimeValueName, &serialized,
sizeof(serialized), REG_QWORD));
}
private:
base::HistogramTester histograms_;
registry_util::RegistryOverrideManager registry_override_manager_;
std::unique_ptr<base::win::RegKey> cleaner_key_;
};
TEST_F(SwReporterInstallerHistogramTest, WithStartAndEndTimes) {
const base::Time start_time =
base::Time::Now() - base::TimeDelta::FromHours(1);
const base::Time end_time = start_time + base::TimeDelta::FromSeconds(10);
WriteStartTime(start_time);
WriteEndTime(end_time);
ReportUMAForLastCleanerRun();
histograms().ExpectUniqueSample("SoftwareReporter.Cleaner.HasCompleted",
1 /* SRT_COMPLETED_YES */, 1);
histograms().ExpectUniqueSample("SoftwareReporter.Cleaner.RunningTime",
(end_time - start_time).InMilliseconds(), 1);
}
} // namespace component_updater