Commit 429fac25 authored by Greg Thompson's avatar Greg Thompson Committed by Commit Bot

Introduce BuildState in UpgradeDetector.

This CL introduces a new model for the state of the build and
controllers to drive it for desktop Chrome and Chrome OS. In its
introduction here, the model holds whether or not an update is
available, its version, the "critical" version (if set), and whether or
not the new version is a rollback.

This CL does not activate the new model or controllers in the browser. A
subsequent CL will give the browser a process-wide BuildState instance,
a single controller to update the BuildState when an update is
available, and make UpgradeDetector an observer of the process's
BuildState.

BUG=1043624

Change-Id: I50be6b313ee152f2351e823fbd8637aac0c5c52a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2017520
Commit-Queue: Greg Thompson <grt@chromium.org>
Reviewed-by: default avatarJulian Pastarmov <pastarmovj@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#737757}
parent 74a51a35
...@@ -3689,6 +3689,9 @@ jumbo_static_library("browser") { ...@@ -3689,6 +3689,9 @@ jumbo_static_library("browser") {
"themes/theme_service_factory.h", "themes/theme_service_factory.h",
"themes/theme_syncable_service.cc", "themes/theme_syncable_service.cc",
"themes/theme_syncable_service.h", "themes/theme_syncable_service.h",
"upgrade_detector/build_state.cc",
"upgrade_detector/build_state.h",
"upgrade_detector/build_state_observer.h",
"upgrade_detector/upgrade_detector.cc", "upgrade_detector/upgrade_detector.cc",
"upgrade_detector/upgrade_detector.h", "upgrade_detector/upgrade_detector.h",
"upgrade_detector/upgrade_observer.h", "upgrade_detector/upgrade_observer.h",
...@@ -3866,6 +3869,8 @@ jumbo_static_library("browser") { ...@@ -3866,6 +3869,8 @@ jumbo_static_library("browser") {
"task_manager/providers/vm/vm_process_task_provider.h", "task_manager/providers/vm/vm_process_task_provider.h",
"task_manager/sampling/arc_shared_sampler.cc", "task_manager/sampling/arc_shared_sampler.cc",
"task_manager/sampling/arc_shared_sampler.h", "task_manager/sampling/arc_shared_sampler.h",
"upgrade_detector/installed_version_updater_chromeos.cc",
"upgrade_detector/installed_version_updater_chromeos.h",
"upgrade_detector/upgrade_detector_chromeos.cc", "upgrade_detector/upgrade_detector_chromeos.cc",
"upgrade_detector/upgrade_detector_chromeos.h", "upgrade_detector/upgrade_detector_chromeos.h",
] ]
...@@ -4120,6 +4125,7 @@ jumbo_static_library("browser") { ...@@ -4120,6 +4125,7 @@ jumbo_static_library("browser") {
"shell_integration_linux.h", "shell_integration_linux.h",
"themes/theme_service_aura_linux.cc", "themes/theme_service_aura_linux.cc",
"themes/theme_service_aura_linux.h", "themes/theme_service_aura_linux.h",
"upgrade_detector/get_installed_version_linux.cc",
] ]
if (use_dbus) { if (use_dbus) {
...@@ -4273,6 +4279,12 @@ jumbo_static_library("browser") { ...@@ -4273,6 +4279,12 @@ jumbo_static_library("browser") {
"signin/signin_global_error.h", "signin/signin_global_error.h",
"signin/signin_global_error_factory.cc", "signin/signin_global_error_factory.cc",
"signin/signin_global_error_factory.h", "signin/signin_global_error_factory.h",
"upgrade_detector/get_installed_version.cc",
"upgrade_detector/get_installed_version.h",
"upgrade_detector/get_installed_version_mac.mm",
"upgrade_detector/get_installed_version_win.cc",
"upgrade_detector/installed_version_poller.cc",
"upgrade_detector/installed_version_poller.h",
"upgrade_detector/upgrade_detector_impl.cc", "upgrade_detector/upgrade_detector_impl.cc",
"upgrade_detector/upgrade_detector_impl.h", "upgrade_detector/upgrade_detector_impl.h",
] ]
......
// Copyright 2020 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/upgrade_detector/build_state.h"
#include <tuple>
#include <utility>
#include "chrome/browser/upgrade_detector/build_state_observer.h"
BuildState::BuildState() = default;
BuildState::~BuildState() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void BuildState::SetUpdate(
UpdateType update_type,
const base::Version& installed_version,
const base::Optional<base::Version>& critical_version) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(update_type != UpdateType::kNone ||
(!installed_version.IsValid() && !critical_version.has_value()));
base::Optional<base::Version> new_installed_version;
if (installed_version.IsValid())
new_installed_version = installed_version;
// Update state and notify observers only in case of a change in state.
if (std::tie(update_type_, installed_version_, critical_version_) !=
std::tie(update_type, new_installed_version, critical_version)) {
update_type_ = update_type;
installed_version_ = std::move(new_installed_version);
critical_version_ = critical_version;
NotifyObserversOnUpdate();
}
}
void BuildState::AddObserver(BuildStateObserver* observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observers_.AddObserver(observer);
}
void BuildState::RemoveObserver(const BuildStateObserver* observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observers_.RemoveObserver(observer);
}
void BuildState::NotifyObserversOnUpdate() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto& observer : observers_)
observer.OnUpdate(this);
}
// Copyright 2020 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.
#ifndef CHROME_BROWSER_UPGRADE_DETECTOR_BUILD_STATE_H_
#define CHROME_BROWSER_UPGRADE_DETECTOR_BUILD_STATE_H_
#include "base/observer_list.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
#include "base/version.h"
class BuildStateObserver;
// The state of the browser or device build. This class is not thread safe --
// all access must take place on the UI thread.
class BuildState {
public:
enum class UpdateType {
// No new version ready for use.
kNone = 0,
// A bump from the running version to a newer one (i.e., a typical update).
kNormalUpdate = 1,
// Rollback to an older version via administrative action.
kEnterpriseRollback = 2,
// Rollback to an older version via a switch to a more stable channel.
// (Chrome OS only.)
kChannelSwitchRollback = 3,
};
BuildState();
BuildState(const BuildState&) = delete;
BuildState& operator=(const BuildState&) = delete;
~BuildState();
// Returns the current update type if an update has been detected, or kNone if
// one has not been detected. In the latter case, neither installed_version()
// nor critical_version() is relevant.
UpdateType update_type() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return update_type_;
}
// If update_type() is not kNone, returns the discovered version or no value
// if an error occurred while determining the installed version. A returned
// value may be numerically higher or lower than the currently running build.
// Note: On Chrome OS, this is the system version number rather than the
// browser version number.
const base::Optional<base::Version>& installed_version() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return installed_version_;
}
// If update_type() is not kNone, returns the optional critical version,
// indicating a minimum version that must be running. A running version lower
// than this must be updated as soon as possible. (Windows only.)
const base::Optional<base::Version>& critical_version() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return critical_version_;
}
// Sets the update properties. Observers are notified if the given properties
// differ from the instance's previous properties.
void SetUpdate(UpdateType update_type,
const base::Version& installed_version,
const base::Optional<base::Version>& critical_version);
void AddObserver(BuildStateObserver* observer);
void RemoveObserver(const BuildStateObserver* observer);
private:
void NotifyObserversOnUpdate();
SEQUENCE_CHECKER(sequence_checker_);
base::ObserverList<BuildStateObserver, /*check_empty=*/true> observers_;
UpdateType update_type_ = UpdateType::kNone;
base::Optional<base::Version> installed_version_;
base::Optional<base::Version> critical_version_;
};
#endif // CHROME_BROWSER_UPGRADE_DETECTOR_BUILD_STATE_H_
// Copyright 2020 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.
#ifndef CHROME_BROWSER_UPGRADE_DETECTOR_BUILD_STATE_OBSERVER_H_
#define CHROME_BROWSER_UPGRADE_DETECTOR_BUILD_STATE_OBSERVER_H_
#include "base/observer_list_types.h"
class BuildState;
// An observer of changes to the browser's BuildState.
class BuildStateObserver : public base::CheckedObserver {
public:
// Called when the installed version of the browser changes. For example, when
// an update is installed on the machine and a restart is needed for it to
// become the running version. |build_state| is the browser's state.
virtual void OnUpdate(const BuildState* build_state) = 0;
};
#endif // CHROME_BROWSER_UPGRADE_DETECTOR_BUILD_STATE_OBSERVER_H_
// Copyright 2020 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/upgrade_detector/build_state.h"
#include "base/optional.h"
#include "base/version.h"
#include "chrome/browser/upgrade_detector/mock_build_state_observer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::AllOf;
using ::testing::Eq;
using ::testing::IsFalse;
using ::testing::IsTrue;
using ::testing::Property;
class BuildStateTest : public ::testing::Test {
protected:
BuildStateTest() { build_state_.AddObserver(&mock_observer_); }
~BuildStateTest() override { build_state_.RemoveObserver(&mock_observer_); }
MockBuildStateObserver& mock_observer() { return mock_observer_; }
BuildState& build_state() { return build_state_; }
private:
MockBuildStateObserver mock_observer_;
BuildState build_state_;
};
// Observers are not notified when there's no update.
TEST_F(BuildStateTest, SetUpdateNoUpdate) {
EXPECT_EQ(build_state().update_type(), BuildState::UpdateType::kNone);
build_state().SetUpdate(BuildState::UpdateType::kNone, base::Version(),
base::nullopt);
::testing::Mock::VerifyAndClearExpectations(&mock_observer());
EXPECT_EQ(build_state().update_type(), BuildState::UpdateType::kNone);
EXPECT_FALSE(build_state().installed_version().has_value());
EXPECT_FALSE(build_state().critical_version().has_value());
}
// Observers are notified upon update when the version couldn't be fetched.
TEST_F(BuildStateTest, SetUpdateWithNoVersion) {
EXPECT_CALL(
mock_observer(),
OnUpdate(AllOf(Eq(&build_state()),
Property(&BuildState::update_type,
Eq(BuildState::UpdateType::kNormalUpdate)),
Property(&BuildState::installed_version, IsFalse()),
Property(&BuildState::critical_version, IsFalse()))));
build_state().SetUpdate(BuildState::UpdateType::kNormalUpdate,
base::Version(), base::nullopt);
::testing::Mock::VerifyAndClearExpectations(&mock_observer());
}
// Observers are notified upon update and the version is held.
TEST_F(BuildStateTest, SetUpdateWithVersion) {
const base::Version expected_version("1.2.3.4");
EXPECT_CALL(mock_observer(),
OnUpdate(AllOf(
Eq(&build_state()),
Property(&BuildState::update_type,
Eq(BuildState::UpdateType::kNormalUpdate)),
Property(&BuildState::installed_version, IsTrue()),
Property(&BuildState::installed_version,
Eq(base::Optional<base::Version>(expected_version))),
Property(&BuildState::critical_version, IsFalse()))));
build_state().SetUpdate(BuildState::UpdateType::kNormalUpdate,
expected_version, base::nullopt);
::testing::Mock::VerifyAndClearExpectations(&mock_observer());
}
// Observers are notified upon update with a critical version and both versions
// are held.
TEST_F(BuildStateTest, SetUpdateWithCritical) {
const base::Version expected_version("1.2.3.4");
const base::Version expected_critical("1.2.3.3");
EXPECT_CALL(
mock_observer(),
OnUpdate(AllOf(
Eq(&build_state()),
Property(&BuildState::update_type,
Eq(BuildState::UpdateType::kNormalUpdate)),
Property(&BuildState::installed_version, IsTrue()),
Property(&BuildState::installed_version,
Eq(base::Optional<base::Version>(expected_version))),
Property(&BuildState::critical_version, IsTrue()),
Property(&BuildState::critical_version,
Eq(base::Optional<base::Version>(expected_critical))))));
build_state().SetUpdate(BuildState::UpdateType::kNormalUpdate,
expected_version, expected_critical);
::testing::Mock::VerifyAndClearExpectations(&mock_observer());
}
// Observers are only notified once for duplicate calls.
TEST_F(BuildStateTest, TwoUpdatesOnceNotification) {
const base::Version expected_version("1.2.3.4");
EXPECT_CALL(mock_observer(), OnUpdate(&build_state()));
build_state().SetUpdate(BuildState::UpdateType::kNormalUpdate,
expected_version, base::nullopt);
build_state().SetUpdate(BuildState::UpdateType::kNormalUpdate,
expected_version, base::nullopt);
::testing::Mock::VerifyAndClearExpectations(&mock_observer());
}
// Copyright 2020 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/upgrade_detector/get_installed_version.h"
#include <utility>
InstalledAndCriticalVersion::InstalledAndCriticalVersion(
base::Version the_installed_version)
: installed_version(std::move(the_installed_version)) {}
InstalledAndCriticalVersion::InstalledAndCriticalVersion(
base::Version the_installed_version,
base::Version the_critical_version)
: installed_version(std::move(the_installed_version)),
critical_version(std::move(the_critical_version)) {}
InstalledAndCriticalVersion::InstalledAndCriticalVersion(
InstalledAndCriticalVersion&& other) noexcept
: installed_version(std::move(other.installed_version)),
critical_version(std::move(other.critical_version)) {}
InstalledAndCriticalVersion::~InstalledAndCriticalVersion() = default;
// Copyright 2020 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.
#ifndef CHROME_BROWSER_UPGRADE_DETECTOR_GET_INSTALLED_VERSION_H_
#define CHROME_BROWSER_UPGRADE_DETECTOR_GET_INSTALLED_VERSION_H_
#include "base/optional.h"
#include "base/version.h"
struct InstalledAndCriticalVersion {
explicit InstalledAndCriticalVersion(base::Version the_installed_version);
InstalledAndCriticalVersion(base::Version the_installed_version,
base::Version the_critical_version);
InstalledAndCriticalVersion(InstalledAndCriticalVersion&& other) noexcept;
InstalledAndCriticalVersion& operator=(InstalledAndCriticalVersion&& other) =
default;
~InstalledAndCriticalVersion();
// The installed version (not necessarily the one that is running). May be
// invalid in case of failure reading the installed version.
base::Version installed_version;
// An optional critical version, indicating a minimum version that must be
// running. A running version lower than this is presumed to have a critical
// flaw sufficiently important that it must be updated as soon as possible.
base::Optional<base::Version> critical_version;
};
// A platform-specific function that returns the currently installed version and
// an optional critical version (Windows only as of this writing). This function
// may block the thread on which it runs.
InstalledAndCriticalVersion GetInstalledVersion();
#endif // CHROME_BROWSER_UPGRADE_DETECTOR_GET_INSTALLED_VERSION_H_
// Copyright 2020 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/upgrade_detector/get_installed_version.h"
#include <string>
#include <utility>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/process/launch.h"
#include "base/strings/string_util.h"
#include "chrome/common/chrome_switches.h"
// On Linux, the Chrome binary may have been replaced by an update. Ask it to
// report its version.
InstalledAndCriticalVersion GetInstalledVersion() {
base::CommandLine command_line(*base::CommandLine::ForCurrentProcess());
command_line.AppendSwitch(switches::kProductVersion);
base::Version installed_version;
std::string reply;
if (base::GetAppOutput(command_line, &reply)) {
installed_version =
base::Version(base::TrimWhitespaceASCII(reply, base::TRIM_ALL));
}
// Failure may be a result of invoking a Chrome that predates the introduction
// of the --product-version switch in https://crrev.com/48795 (6.0.424.0).
DLOG_IF(ERROR, !installed_version.IsValid())
<< "Failed to get current file version";
return InstalledAndCriticalVersion(std::move(installed_version));
}
// Copyright 2020 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/upgrade_detector/get_installed_version.h"
#include <stdio.h>
#include <string>
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/strings/string_number_conversions.h"
#include "components/version_info/version_info.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
namespace {
constexpr char kChildModeSwitch[] =
"get-installed-version-linux-unittest-child-mode";
enum class ChildMode {
kNoVersion = 0,
kProcessError = 1,
kWithMonkey = 2,
kWithVersion = 3,
};
ChildMode GetChildMode() {
const auto switch_value =
base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(
kChildModeSwitch);
int mode_int = 0;
base::StringToInt(switch_value, &mode_int);
return static_cast<ChildMode>(mode_int);
}
} // namespace
// A function run in a child process to print the desired version to stdout,
// just like Chrome does.
MULTIPROCESS_TEST_MAIN(GetProductVersionInChildProc) {
switch (GetChildMode()) {
case ChildMode::kNoVersion:
// Return successful process exit without printing anything.
return 0;
case ChildMode::kProcessError:
// Return a bad exit code without printing anything.
break;
case ChildMode::kWithMonkey:
// Print something unexpected and report success.
printf("monkey, monkey, monkey");
return 0;
case ChildMode::kWithVersion:
// Print the current version and report success.
printf("%s\n", version_info::GetVersionNumber().c_str());
return 0;
}
return 1;
}
// A multi process test that exercises the Linux implementation of
// GetInstalledVersion.
class GetInstalledVersionLinuxTest : public ::testing::Test {
protected:
GetInstalledVersionLinuxTest()
: original_command_line_(*base::CommandLine::ForCurrentProcess()) {}
~GetInstalledVersionLinuxTest() override {
*base::CommandLine::ForCurrentProcess() = original_command_line_;
}
// Adds switches to the current process command line so that when
// GetInstalledVersion relaunches the current process, it has the proper
// child proc switch to lead to GetProductVersionInChildProc above and the
// mode switch to tell the child how to behave.
void AddChildCommandLineSwitches(ChildMode child_mode) {
auto* command_line = base::CommandLine::ForCurrentProcess();
command_line->AppendSwitchASCII(switches::kTestChildProcess,
"GetProductVersionInChildProc");
command_line->AppendSwitchASCII(
kChildModeSwitch, base::NumberToString(static_cast<int>(child_mode)));
}
private:
// The original process command line; saved during construction and restored
// during destruction.
const base::CommandLine original_command_line_;
};
// Tests that an empty instance is returned when the child process reports
// nothing.
TEST_F(GetInstalledVersionLinuxTest, NoVersion) {
AddChildCommandLineSwitches(ChildMode::kNoVersion);
InstalledAndCriticalVersion versions = GetInstalledVersion();
EXPECT_FALSE(versions.installed_version.IsValid());
EXPECT_FALSE(versions.critical_version.has_value());
}
// Tests that an empty instance is returned when the child process exits with an
// error.
TEST_F(GetInstalledVersionLinuxTest, ProcessError) {
AddChildCommandLineSwitches(ChildMode::kProcessError);
InstalledAndCriticalVersion versions = GetInstalledVersion();
ASSERT_FALSE(versions.installed_version.IsValid());
EXPECT_FALSE(versions.critical_version.has_value());
}
// Tests that an empty instance is returned when the child process reports a
// monkey.
TEST_F(GetInstalledVersionLinuxTest, WithMonkey) {
AddChildCommandLineSwitches(ChildMode::kWithMonkey);
InstalledAndCriticalVersion versions = GetInstalledVersion();
ASSERT_FALSE(versions.installed_version.IsValid());
EXPECT_FALSE(versions.critical_version.has_value());
}
// Tests that the expected instance is returned when the child process reports a
// valid version.
TEST_F(GetInstalledVersionLinuxTest, WithVersion) {
AddChildCommandLineSwitches(ChildMode::kWithVersion);
InstalledAndCriticalVersion versions = GetInstalledVersion();
ASSERT_TRUE(versions.installed_version.IsValid());
EXPECT_EQ(versions.installed_version, version_info::GetVersion());
EXPECT_FALSE(versions.critical_version.has_value());
}
// Copyright 2020 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/upgrade_detector/get_installed_version.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/mac/keystone_glue.h"
InstalledAndCriticalVersion GetInstalledVersion() {
return InstalledAndCriticalVersion(base::Version(
base::UTF16ToASCII(keystone_glue::CurrentlyInstalledVersion())));
}
// Copyright 2020 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/upgrade_detector/get_installed_version.h"
#include <utility>
#include "chrome/installer/util/install_util.h"
InstalledAndCriticalVersion GetInstalledVersion() {
base::Version installed_version =
InstallUtil::GetChromeVersion(!InstallUtil::IsPerUserInstall());
if (installed_version.IsValid()) {
base::Version critical_version = InstallUtil::GetCriticalUpdateVersion();
if (critical_version.IsValid()) {
return InstalledAndCriticalVersion(std::move(installed_version),
std::move(critical_version));
}
}
return InstalledAndCriticalVersion(std::move(installed_version));
}
// Copyright 2020 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/upgrade_detector/get_installed_version.h"
#include <stdint.h>
#include <vector>
#include "base/strings/utf_string_conversions.h"
#include "base/test/test_reg_util_win.h"
#include "base/win/registry.h"
#include "chrome/install_static/install_util.h"
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/install_util.h"
#include "components/version_info/version_info.h"
#include "testing/gtest/include/gtest/gtest.h"
class GetInstalledVersionWinTest : public ::testing::Test {
protected:
void SetUp() override {
ASSERT_NO_FATAL_FAILURE(
registry_overrides_.OverrideRegistry(HKEY_CURRENT_USER));
}
void SetInstalledVersion(const base::Version& version) {
base::win::RegKey key(HKEY_CURRENT_USER,
install_static::GetClientsKeyPath().c_str(),
KEY_SET_VALUE);
ASSERT_TRUE(key.Valid());
ASSERT_EQ(key.WriteValue(google_update::kRegVersionField,
base::ASCIIToUTF16(version.GetString()).c_str()),
ERROR_SUCCESS);
}
void SetCriticalVersion(const base::Version& version) {
base::win::RegKey key(HKEY_CURRENT_USER,
install_static::GetClientsKeyPath().c_str(),
KEY_SET_VALUE);
ASSERT_TRUE(key.Valid());
ASSERT_EQ(key.WriteValue(google_update::kRegCriticalVersionField,
base::ASCIIToUTF16(version.GetString()).c_str()),
ERROR_SUCCESS);
}
private:
registry_util::RegistryOverrideManager registry_overrides_;
};
// Tests that an empty instance is returned when no version is in the registry.
TEST_F(GetInstalledVersionWinTest, NoVersion) {
InstalledAndCriticalVersion versions = GetInstalledVersion();
EXPECT_FALSE(versions.installed_version.IsValid());
EXPECT_FALSE(versions.critical_version.has_value());
}
// Tests that the product version in the registry is returned.
TEST_F(GetInstalledVersionWinTest, WithVersion) {
ASSERT_NO_FATAL_FAILURE(SetInstalledVersion(version_info::GetVersion()));
InstalledAndCriticalVersion versions = GetInstalledVersion();
ASSERT_TRUE(versions.installed_version.IsValid());
EXPECT_EQ(versions.installed_version, version_info::GetVersion());
EXPECT_FALSE(versions.critical_version.has_value());
}
// Tests that the product version and critical version in the registry are
// returned.
TEST_F(GetInstalledVersionWinTest, WithCriticalVersion) {
std::vector<uint32_t> components = version_info::GetVersion().components();
--components[0];
base::Version critical_version = base::Version(std::move(components));
ASSERT_NO_FATAL_FAILURE(SetInstalledVersion(version_info::GetVersion()));
ASSERT_NO_FATAL_FAILURE(SetCriticalVersion(critical_version));
InstalledAndCriticalVersion versions = GetInstalledVersion();
ASSERT_TRUE(versions.installed_version.IsValid());
EXPECT_EQ(versions.installed_version, version_info::GetVersion());
ASSERT_TRUE(versions.critical_version.has_value());
ASSERT_TRUE(versions.critical_version.value().IsValid());
EXPECT_EQ(versions.critical_version.value(), critical_version);
}
// Copyright 2020 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/upgrade_detector/installed_version_poller.h"
#include <stdint.h>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/post_task.h"
#include "chrome/browser/upgrade_detector/build_state.h"
#include "chrome/browser/upgrade_detector/get_installed_version.h"
#include "chrome/common/chrome_switches.h"
#include "components/version_info/version_info.h"
namespace {
bool g_disabled_for_testing = false;
// Bits in a testing options bitfield.
constexpr uint32_t kSimulateUpgrade = 0x01;
constexpr uint32_t kSimulateCriticalUpdate = 0x02;
// Returns a bitfield of the testing options that are selected via command line
// switches, or 0 if no options are selected.
uint32_t GetTestingOptions() {
uint32_t testing_options = 0;
const base::CommandLine& cmd_line = *base::CommandLine::ForCurrentProcess();
if (cmd_line.HasSwitch(switches::kSimulateUpgrade))
testing_options |= kSimulateUpgrade;
// Simulating a critical update is a superset of simulating an upgrade.
if (cmd_line.HasSwitch(switches::kSimulateCriticalUpdate))
testing_options |= (kSimulateUpgrade | kSimulateCriticalUpdate);
return testing_options;
}
// A GetInstalledVersionCallback implementation used when a regular or a
// critical update is simulated via --simulate-upgrade or
// --simulate-critical-update.
InstalledAndCriticalVersion SimulateGetInstalledVersion(
uint32_t testing_options) {
DCHECK_NE(0U, testing_options);
std::vector<uint32_t> components = version_info::GetVersion().components();
components[3] += 2;
InstalledAndCriticalVersion result((base::Version(components)));
if ((testing_options & kSimulateCriticalUpdate) != 0) {
--components[3];
result.critical_version.emplace(std::move(components));
}
return result;
}
// Returns the callback to get the installed version. Use of any testing option
// on the process command line results in use of the simulation function.
InstalledVersionPoller::GetInstalledVersionCallback
GetGetInstalledVersionCallback() {
const uint32_t testing_options = GetTestingOptions();
return testing_options ? base::BindRepeating(&SimulateGetInstalledVersion,
testing_options)
: base::BindRepeating(&GetInstalledVersion);
}
// Returns the polling interval specified by --check-for-update-interval, or
// the default interval.
base::TimeDelta GetPollingInterval() {
const base::CommandLine& cmd_line = *base::CommandLine::ForCurrentProcess();
const std::string seconds_str =
cmd_line.GetSwitchValueASCII(switches::kCheckForUpdateIntervalSec);
int seconds;
if (!seconds_str.empty() && base::StringToInt(seconds_str, &seconds))
return base::TimeDelta::FromSeconds(seconds);
return InstalledVersionPoller::kDefaultPollingInterval;
}
} // namespace
// InstalledVersionPoller::ScopedDisableForTesting ----------------------------
InstalledVersionPoller::ScopedDisableForTesting::ScopedDisableForTesting() {
g_disabled_for_testing = true;
}
InstalledVersionPoller::ScopedDisableForTesting::~ScopedDisableForTesting() {
g_disabled_for_testing = false;
}
// InstalledVersionPoller ------------------------------------------------------
// static
const base::TimeDelta InstalledVersionPoller::kDefaultPollingInterval =
base::TimeDelta::FromHours(2);
InstalledVersionPoller::InstalledVersionPoller(BuildState* build_state)
: InstalledVersionPoller(build_state,
GetGetInstalledVersionCallback(),
nullptr) {}
InstalledVersionPoller::InstalledVersionPoller(
BuildState* build_state,
GetInstalledVersionCallback get_installed_version,
const base::TickClock* tick_clock)
: build_state_(build_state),
get_installed_version_(std::move(get_installed_version)),
timer_(FROM_HERE,
GetPollingInterval(),
base::BindRepeating(&InstalledVersionPoller::Poll,
base::Unretained(this)),
tick_clock) {
// Make the first check in the background without delay. Suppress this if
// polling is disabled for testing. This prevents all polling from taking
// place since the result of poll N kicks off poll N+1.
if (!g_disabled_for_testing)
Poll();
}
InstalledVersionPoller::~InstalledVersionPoller() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void InstalledVersionPoller::Poll() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Run the version getter in the background. Get the result back via a weak
// pointer so that the result is dropped on the floor should this instance be
// destroyed while polling.
base::PostTaskAndReplyWithResult(
FROM_HERE,
{base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN, base::MayBlock(),
base::ThreadPool()},
base::BindOnce(get_installed_version_),
base::BindOnce(&InstalledVersionPoller::OnInstalledVersion,
weak_ptr_factory_.GetWeakPtr()));
}
void InstalledVersionPoller::OnInstalledVersion(
InstalledAndCriticalVersion versions) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Consider either an invalid version or a higher version a normal update.
BuildState::UpdateType update_type = BuildState::UpdateType::kNormalUpdate;
if (versions.installed_version.IsValid()) {
switch (versions.installed_version.CompareTo(version_info::GetVersion())) {
case -1:
update_type = BuildState::UpdateType::kEnterpriseRollback;
break;
case 0:
update_type = BuildState::UpdateType::kNone;
break;
}
}
if (update_type == BuildState::UpdateType::kNone) {
// The discovered version matches the current version, so report that no
// update is available.
build_state_->SetUpdate(update_type, base::Version(), base::nullopt);
} else {
// Either the installed version could not be discovered (invalid installed
// version) or differs from the running version. Report it accordingly.
build_state_->SetUpdate(update_type, versions.installed_version,
versions.critical_version);
}
// Poll again after the polling interval passes.
timer_.Reset();
}
// Copyright 2020 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.
#ifndef CHROME_BROWSER_UPGRADE_DETECTOR_INSTALLED_VERSION_POLLER_H_
#define CHROME_BROWSER_UPGRADE_DETECTOR_INSTALLED_VERSION_POLLER_H_
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/upgrade_detector/get_installed_version.h"
class BuildState;
namespace base {
class TickClock;
} // namespace base
// Periodically polls for the installed version of the browser in the
// background. Polling begins automatically after construction and continues
// until destruction. The discovered version is provided to the given BuildState
// on each poll.
class InstalledVersionPoller {
public:
// The default polling interval. This may be overridden for testing by
// specifying some number of seconds via --check-for-update-interval=seconds.
static const base::TimeDelta kDefaultPollingInterval;
// Constructs an instance that will poll upon construction and periodically
// thereafter.
explicit InstalledVersionPoller(BuildState* build_state);
// A type of callback that is run (in the background) to get the currently
// installed version of the browser.
using GetInstalledVersionCallback =
base::RepeatingCallback<InstalledAndCriticalVersion()>;
// A constructor for tests that provide a mock source of time and a mock
// version getter.
InstalledVersionPoller(BuildState* build_state,
GetInstalledVersionCallback get_installed_version,
const base::TickClock* tick_clock);
InstalledVersionPoller(const InstalledVersionPoller&) = delete;
InstalledVersionPoller& operator=(const InstalledVersionPoller&) = delete;
~InstalledVersionPoller();
class ScopedDisableForTesting {
public:
ScopedDisableForTesting(ScopedDisableForTesting&&) noexcept = default;
ScopedDisableForTesting& operator=(ScopedDisableForTesting&&) = default;
~ScopedDisableForTesting();
private:
friend class InstalledVersionPoller;
ScopedDisableForTesting();
};
// Returns an object that prevents any newly created instances of
// InstalledVersionPoller from doing any work for the duration of its
// lifetime.
static ScopedDisableForTesting MakeScopedDisableForTesting() {
return ScopedDisableForTesting();
}
private:
// Initiates a poll in the background.
void Poll();
void OnInstalledVersion(InstalledAndCriticalVersion installed_version);
SEQUENCE_CHECKER(sequence_checker_);
BuildState* const build_state_;
const GetInstalledVersionCallback get_installed_version_;
base::RetainingOneShotTimer timer_;
base::WeakPtrFactory<InstalledVersionPoller> weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_UPGRADE_DETECTOR_INSTALLED_VERSION_POLLER_H_
// Copyright 2020 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/upgrade_detector/installed_version_updater_chromeos.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "chrome/browser/upgrade_detector/build_state.h"
#include "chromeos/dbus/dbus_thread_manager.h"
namespace {
// The reason of the rollback used in the UpgradeDetector.RollbackReason
// histogram.
enum class RollbackReason {
kToMoreStableChannel = 0,
kEnterpriseRollback = 1,
kMaxValue = kEnterpriseRollback,
};
} // namespace
InstalledVersionUpdater::InstalledVersionUpdater(BuildState* build_state)
: build_state_(build_state) {
chromeos::DBusThreadManager::Get()->GetUpdateEngineClient()->AddObserver(
this);
}
InstalledVersionUpdater::~InstalledVersionUpdater() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
chromeos::DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(
this);
}
void InstalledVersionUpdater::UpdateStatusChanged(
const update_engine::StatusResult& status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (status.current_operation() !=
update_engine::Operation::UPDATED_NEED_REBOOT) {
return;
}
// Cancel a potential channel request from a previous status update.
weak_ptr_factory_.InvalidateWeakPtrs();
current_channel_.reset();
target_channel_.reset();
if (status.is_enterprise_rollback()) {
// Powerwash will be required. Determine what kind of notification to show
// based on the current and target channels.
// StatusResult::new_version is the new Chrome OS system version number.
new_version_ = status.new_version();
auto* client = chromeos::DBusThreadManager::Get()->GetUpdateEngineClient();
client->GetChannel(/*get_current_channel=*/false,
base::BindOnce(&InstalledVersionUpdater::OnChannel,
weak_ptr_factory_.GetWeakPtr(), false));
client->GetChannel(/*get_current_channel=*/true,
base::BindOnce(&InstalledVersionUpdater::OnChannel,
weak_ptr_factory_.GetWeakPtr(), true));
} else {
// Not going to an earlier version; no powerwash or rollback message is
// required.
new_version_.reset();
build_state_->SetUpdate(BuildState::UpdateType::kNormalUpdate,
base::Version(status.new_version()), base::nullopt);
}
}
void InstalledVersionUpdater::OnChannel(bool is_current_channel,
const std::string& channel_name) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Preserve this channel name.
(is_current_channel ? current_channel_ : target_channel_) = channel_name;
// Nothing else to do if still waiting on the other value.
if (!current_channel_.has_value() || !target_channel_.has_value())
return;
// Set the UpdateType based on whether or not the device is moving to a more
// stable channel.
// TODO(crbug.com/864672): Use is_rollback from the update engine so that
// admin-driven channel changes appear as enterprise rollbacks rather than
// channel switches.
BuildState::UpdateType update_type =
chromeos::UpdateEngineClient::IsTargetChannelMoreStable(
current_channel_.value(), target_channel_.value())
? BuildState::UpdateType::kChannelSwitchRollback
: BuildState::UpdateType::kEnterpriseRollback;
base::UmaHistogramEnumeration(
"UpgradeDetector.RollbackReason",
update_type == BuildState::UpdateType::kChannelSwitchRollback
? RollbackReason::kToMoreStableChannel
: RollbackReason::kEnterpriseRollback);
LOG(WARNING) << "Device is rolling back, will require powerwash. Reason: "
<< (update_type ==
BuildState::UpdateType::kChannelSwitchRollback)
<< ", current_channel: " << current_channel_.value()
<< ", target_channel: " << target_channel_.value();
build_state_->SetUpdate(update_type, base::Version(new_version_.value()),
base::nullopt);
// All done; clear state.
new_version_.reset();
current_channel_.reset();
target_channel_.reset();
}
// Copyright 2020 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.
#ifndef CHROME_BROWSER_UPGRADE_DETECTOR_INSTALLED_VERSION_UPDATER_CHROMEOS_H_
#define CHROME_BROWSER_UPGRADE_DETECTOR_INSTALLED_VERSION_UPDATER_CHROMEOS_H_
#include <string>
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
#include "chromeos/dbus/update_engine_client.h"
class BuildState;
// Observes the system UpdateEngineClient for updates that require a device
// restart. Update information is pushed to the given BuildState as it happens.
class InstalledVersionUpdater : public chromeos::UpdateEngineClient::Observer {
public:
explicit InstalledVersionUpdater(BuildState* build_state);
InstalledVersionUpdater(const InstalledVersionUpdater&) = delete;
InstalledVersionUpdater& operator=(const InstalledVersionUpdater&) = delete;
~InstalledVersionUpdater() override;
// chromeos::UpdateEngineClient::Observer:
void UpdateStatusChanged(const update_engine::StatusResult& status) override;
private:
// A callback run upon fetching either the current or target channel name
// following a rollback.
void OnChannel(bool is_current_channel, const std::string& channel_name);
SEQUENCE_CHECKER(sequence_checker_);
BuildState* const build_state_;
// Holds the current channel following an enterprise rollback while waiting
// for the target channel to be retrieved.
base::Optional<std::string> current_channel_;
// Holds the target channel following an enterprise rollback while waiting for
// the current channel to be retrieved.
base::Optional<std::string> target_channel_;
// Holds a new version received from the update engine client following an
// enterprise rollback while the current and target channels are being
// retrieved.
base::Optional<std::string> new_version_;
base::WeakPtrFactory<InstalledVersionUpdater> weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_UPGRADE_DETECTOR_INSTALLED_VERSION_UPDATER_CHROMEOS_H_
// Copyright 2020 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/upgrade_detector/installed_version_updater_chromeos.h"
#include <memory>
#include <utility>
#include "base/test/task_environment.h"
#include "chrome/browser/upgrade_detector/build_state.h"
#include "chrome/browser/upgrade_detector/mock_build_state_observer.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_update_engine_client.h"
#include "chromeos/dbus/update_engine/update_engine.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::AllOf;
using ::testing::Eq;
using ::testing::IsFalse;
using ::testing::IsTrue;
using ::testing::Property;
class InstalledVersionUpdaterTest : public ::testing::Test {
protected:
InstalledVersionUpdaterTest() {
// Create the fake update engine client, hold a pointer to it, and hand
// ownership of it off to the DBus thread manager.
auto fake_update_engine_client =
std::make_unique<chromeos::FakeUpdateEngineClient>();
fake_update_engine_client_ = fake_update_engine_client.get();
chromeos::DBusThreadManager::GetSetterForTesting()->SetUpdateEngineClient(
std::move(fake_update_engine_client));
build_state_.AddObserver(&mock_observer_);
}
~InstalledVersionUpdaterTest() override {
build_state_.RemoveObserver(&mock_observer_);
// Be kind; rewind.
chromeos::DBusThreadManager::Shutdown();
}
void NotifyStatusChanged(update_engine::StatusResult status) {
fake_update_engine_client_->NotifyObserversThatStatusChanged(status);
}
base::test::TaskEnvironment task_environment_;
::testing::StrictMock<MockBuildStateObserver> mock_observer_;
BuildState build_state_;
private:
chromeos::FakeUpdateEngineClient* fake_update_engine_client_; // Not owned.
};
// Tests that an unrelated status change notification does not push data to the
// BuildState.
TEST_F(InstalledVersionUpdaterTest, UnrelatedStatus) {
InstalledVersionUpdater installed_version_updater(&build_state_);
update_engine::StatusResult status;
status.set_current_operation(update_engine::NEED_PERMISSION_TO_UPDATE);
NotifyStatusChanged(std::move(status));
}
// Tests that an update notifies the BuildState appropriately.
TEST_F(InstalledVersionUpdaterTest, Update) {
InstalledVersionUpdater installed_version_updater(&build_state_);
update_engine::StatusResult status;
const std::string new_version("1.2.3");
status.set_current_operation(update_engine::UPDATED_NEED_REBOOT);
status.set_new_version(new_version);
EXPECT_CALL(
mock_observer_,
OnUpdate(AllOf(Eq(&build_state_),
Property(&BuildState::update_type,
Eq(BuildState::UpdateType::kNormalUpdate)),
Property(&BuildState::installed_version, IsTrue()),
Property(&BuildState::installed_version,
Eq(base::Optional<base::Version>(
base::Version(new_version)))),
Property(&BuildState::critical_version, IsFalse()))));
NotifyStatusChanged(std::move(status));
}
// Tests that a rollback without channel change notifies the BuildState
// appropriately.
TEST_F(InstalledVersionUpdaterTest, Rollback) {
InstalledVersionUpdater installed_version_updater(&build_state_);
update_engine::StatusResult status;
const std::string new_version("1.2.3");
status.set_current_operation(update_engine::UPDATED_NEED_REBOOT);
status.set_new_version(new_version);
status.set_is_enterprise_rollback(true);
EXPECT_CALL(
mock_observer_,
OnUpdate(AllOf(Eq(&build_state_),
Property(&BuildState::update_type,
Eq(BuildState::UpdateType::kEnterpriseRollback)),
Property(&BuildState::installed_version, IsTrue()),
Property(&BuildState::installed_version,
Eq(base::Optional<base::Version>(
base::Version(new_version)))),
Property(&BuildState::critical_version, IsFalse()))));
NotifyStatusChanged(std::move(status));
task_environment_.RunUntilIdle();
}
// Copyright 2020 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/upgrade_detector/mock_build_state_observer.h"
MockBuildStateObserver::MockBuildStateObserver() = default;
MockBuildStateObserver::~MockBuildStateObserver() = default;
// Copyright 2020 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.
#ifndef CHROME_BROWSER_UPGRADE_DETECTOR_MOCK_BUILD_STATE_OBSERVER_H_
#define CHROME_BROWSER_UPGRADE_DETECTOR_MOCK_BUILD_STATE_OBSERVER_H_
#include "chrome/browser/upgrade_detector/build_state_observer.h"
#include "testing/gmock/include/gmock/gmock.h"
class MockBuildStateObserver : public BuildStateObserver {
public:
MockBuildStateObserver();
~MockBuildStateObserver() override;
MOCK_METHOD(void, OnUpdate, (const BuildState*), (override));
};
#endif // CHROME_BROWSER_UPGRADE_DETECTOR_MOCK_BUILD_STATE_OBSERVER_H_
...@@ -3560,6 +3560,10 @@ test("unit_tests") { ...@@ -3560,6 +3560,10 @@ test("unit_tests") {
"../browser/ui/webui/favicon_source_unittest.cc", "../browser/ui/webui/favicon_source_unittest.cc",
"../browser/ui/webui/ntp/cookie_controls_handler_unittest.cc", "../browser/ui/webui/ntp/cookie_controls_handler_unittest.cc",
"../browser/ui/webui/signin/sync_confirmation_handler_unittest.cc", "../browser/ui/webui/signin/sync_confirmation_handler_unittest.cc",
"../browser/upgrade_detector/build_state_unittest.cc",
"../browser/upgrade_detector/get_installed_version_win_unittest.cc",
"../browser/upgrade_detector/mock_build_state_observer.cc",
"../browser/upgrade_detector/mock_build_state_observer.h",
"../browser/webauthn/authenticator_request_scheduler_unittest.cc", "../browser/webauthn/authenticator_request_scheduler_unittest.cc",
"../browser/webauthn/chrome_authenticator_request_delegate_unittest.cc", "../browser/webauthn/chrome_authenticator_request_delegate_unittest.cc",
"../test/pixel/browser_skia_gold_pixel_diff_unittest.cc", "../test/pixel/browser_skia_gold_pixel_diff_unittest.cc",
...@@ -4834,6 +4838,7 @@ test("unit_tests") { ...@@ -4834,6 +4838,7 @@ test("unit_tests") {
sources += [ sources += [
"../browser/shell_integration_linux_unittest.cc", "../browser/shell_integration_linux_unittest.cc",
"../browser/ui/input_method/input_method_engine_unittest.cc", "../browser/ui/input_method/input_method_engine_unittest.cc",
"../browser/upgrade_detector/get_installed_version_linux_unittest.cc",
] ]
} }
if (use_gtk) { if (use_gtk) {
...@@ -5212,12 +5217,15 @@ test("unit_tests") { ...@@ -5212,12 +5217,15 @@ test("unit_tests") {
"../browser/ui/views/sharing/sharing_dialog_view_unittest.cc", "../browser/ui/views/sharing/sharing_dialog_view_unittest.cc",
"../browser/ui/webui/signin/signin_create_profile_handler_unittest.cc", "../browser/ui/webui/signin/signin_create_profile_handler_unittest.cc",
"../browser/ui/webui/signin/signin_error_handler_unittest.cc", "../browser/ui/webui/signin/signin_error_handler_unittest.cc",
"../browser/upgrade_detector/installed_version_poller_unittest.cc",
"../browser/upgrade_detector/upgrade_detector_impl_unittest.cc", "../browser/upgrade_detector/upgrade_detector_impl_unittest.cc",
] ]
} }
if (is_chromeos) { if (is_chromeos) {
sources += sources += [
[ "../browser/upgrade_detector/upgrade_detector_chromeos_unittest.cc" ] "../browser/upgrade_detector/installed_version_updater_chromeos_unittest.cc",
"../browser/upgrade_detector/upgrade_detector_chromeos_unittest.cc",
]
} }
if (toolkit_views) { if (toolkit_views) {
deps += [ deps += [
......
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