blob: 3bc48e9c3944f3e59b5f1dc0d795a3a9f040d696 [file] [log] [blame]
// 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 <memory>
#include <utility>
#include <vector>
#include "base/strings/string_piece.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "chrome/browser/upgrade_detector/build_state.h"
#include "chrome/browser/upgrade_detector/installed_version_monitor.h"
#include "chrome/browser/upgrade_detector/mock_build_state_observer.h"
#include "components/version_info/version_info.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::AllOf;
using ::testing::ByMove;
using ::testing::Eq;
using ::testing::IsFalse;
using ::testing::IsTrue;
using ::testing::Property;
using ::testing::Return;
namespace {
constexpr base::StringPiece kPollTypeHistogramName("UpgradeDetector.PollType");
class FakeMonitor final : public InstalledVersionMonitor {
public:
FakeMonitor() = default;
// Simulate that either a change was detected (|error| is false) or that an
// error occurred (|error| is true).
void Notify(bool error) { callback_.Run(error); }
// InstalledVersionMonitor:
void Start(Callback callback) override { callback_ = std::move(callback); }
private:
Callback callback_;
};
} // namespace
class InstalledVersionPollerTest : public ::testing::Test {
protected:
InstalledVersionPollerTest() { build_state_.AddObserver(&mock_observer_); }
~InstalledVersionPollerTest() override {
build_state_.RemoveObserver(&mock_observer_);
}
// Returns a version somewhat higher than the running version.
static base::Version GetUpgradeVersion() {
std::vector<uint32_t> components = version_info::GetVersion().components();
components[3] += 2;
return base::Version(components);
}
// Returns a version between the running version and the upgrade version
// above.
static base::Version GetCriticalVersion() {
std::vector<uint32_t> components = version_info::GetVersion().components();
components[3] += 1;
return base::Version(components);
}
// Returns a version lower than the running version.
static base::Version GetRollbackVersion() {
std::vector<uint32_t> components = version_info::GetVersion().components();
components[0] -= 1;
return base::Version(components);
}
// Returns an InstalledAndCriticalVersion instance indicating no update.
static InstalledAndCriticalVersion MakeNoUpdateVersions() {
return InstalledAndCriticalVersion(version_info::GetVersion());
}
// Returns an InstalledAndCriticalVersion instance indicating an upgrade.
static InstalledAndCriticalVersion MakeUpgradeVersions() {
return InstalledAndCriticalVersion(GetUpgradeVersion());
}
// Returns an InstalledAndCriticalVersion instance indicating an upgrade with
// a critical version.
static InstalledAndCriticalVersion MakeCriticalUpgradeVersions() {
return InstalledAndCriticalVersion(GetUpgradeVersion(),
GetCriticalVersion());
}
// Returns an InstalledAndCriticalVersion instance with an invalid version.
static InstalledAndCriticalVersion MakeErrorVersions() {
return InstalledAndCriticalVersion(base::Version());
}
// Returns an InstalledAndCriticalVersion instance for a version rollback.
static InstalledAndCriticalVersion MakeRollbackVersions() {
return InstalledAndCriticalVersion(GetRollbackVersion());
}
std::unique_ptr<InstalledVersionMonitor> MakeMonitor() {
EXPECT_FALSE(fake_monitor_);
auto monitor = std::make_unique<FakeMonitor>();
fake_monitor_ = monitor.get();
return monitor;
}
void TriggerMonitor() {
ASSERT_NE(fake_monitor_, nullptr);
fake_monitor_->Notify(false);
}
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
::testing::StrictMock<MockBuildStateObserver> mock_observer_;
BuildState build_state_;
FakeMonitor* fake_monitor_ = nullptr;
};
// Tests that a poll returning the current version does not update the
// BuildState.
TEST_F(InstalledVersionPollerTest, TestNoUpdate) {
base::MockRepeatingCallback<InstalledAndCriticalVersion()> callback;
EXPECT_CALL(callback, Run()).WillOnce(Return(ByMove(MakeNoUpdateVersions())));
InstalledVersionPoller poller(&build_state_, callback.Get(), MakeMonitor(),
task_environment_.GetMockTickClock());
task_environment_.RunUntilIdle();
::testing::Mock::VerifyAndClearExpectations(&callback);
::testing::Mock::VerifyAndClearExpectations(&mock_observer_);
// A second poll with the same version likewise does nothing.
EXPECT_CALL(callback, Run()).WillOnce(Return(ByMove(MakeNoUpdateVersions())));
task_environment_.FastForwardBy(
InstalledVersionPoller::kDefaultPollingInterval);
::testing::Mock::VerifyAndClearExpectations(&callback);
::testing::Mock::VerifyAndClearExpectations(&mock_observer_);
}
// Tests that a poll with an update is reported to the BuildState.
TEST_F(InstalledVersionPollerTest, TestUpgrade) {
base::MockRepeatingCallback<InstalledAndCriticalVersion()> callback;
// No update the first time.
EXPECT_CALL(callback, Run()).WillOnce(Return(ByMove(MakeNoUpdateVersions())));
InstalledVersionPoller poller(&build_state_, callback.Get(), MakeMonitor(),
task_environment_.GetMockTickClock());
task_environment_.RunUntilIdle();
::testing::Mock::VerifyAndClearExpectations(&callback);
::testing::Mock::VerifyAndClearExpectations(&mock_observer_);
// Followed by an update, which is reported.
EXPECT_CALL(callback, Run()).WillOnce(Return(ByMove(MakeUpgradeVersions())));
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>(GetUpgradeVersion()))),
Property(&BuildState::critical_version, IsFalse()))));
task_environment_.FastForwardBy(
InstalledVersionPoller::kDefaultPollingInterval);
::testing::Mock::VerifyAndClearExpectations(&callback);
::testing::Mock::VerifyAndClearExpectations(&mock_observer_);
// Followed by the same update, which is not reported.
EXPECT_CALL(callback, Run()).WillOnce(Return(ByMove(MakeUpgradeVersions())));
task_environment_.FastForwardBy(
InstalledVersionPoller::kDefaultPollingInterval);
::testing::Mock::VerifyAndClearExpectations(&callback);
::testing::Mock::VerifyAndClearExpectations(&mock_observer_);
}
// Tests that a poll with an update is reported to the BuildState and that a
// subsequent poll back to the original version is also reported.
TEST_F(InstalledVersionPollerTest, TestUpgradeThenDowngrade) {
base::MockRepeatingCallback<InstalledAndCriticalVersion()> callback;
// An update is found.
EXPECT_CALL(callback, Run()).WillOnce(Return(ByMove(MakeUpgradeVersions())));
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>(GetUpgradeVersion()))),
Property(&BuildState::critical_version, IsFalse()))));
InstalledVersionPoller poller(&build_state_, callback.Get(), MakeMonitor(),
task_environment_.GetMockTickClock());
task_environment_.RunUntilIdle();
::testing::Mock::VerifyAndClearExpectations(&callback);
::testing::Mock::VerifyAndClearExpectations(&mock_observer_);
// Which is then reverted back to the running version.
EXPECT_CALL(callback, Run()).WillOnce(Return(ByMove(MakeNoUpdateVersions())));
EXPECT_CALL(
mock_observer_,
OnUpdate(AllOf(
Eq(&build_state_),
Property(&BuildState::update_type, Eq(BuildState::UpdateType::kNone)),
Property(&BuildState::installed_version, IsFalse()),
Property(&BuildState::critical_version, IsFalse()))));
task_environment_.FastForwardBy(
InstalledVersionPoller::kDefaultPollingInterval);
::testing::Mock::VerifyAndClearExpectations(&callback);
::testing::Mock::VerifyAndClearExpectations(&mock_observer_);
}
// Tests that a poll with a critical update is reported to the BuildState.
TEST_F(InstalledVersionPollerTest, TestCriticalUpgrade) {
base::MockRepeatingCallback<InstalledAndCriticalVersion()> callback;
EXPECT_CALL(callback, Run())
.WillOnce(Return(ByMove(MakeCriticalUpgradeVersions())));
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>(GetUpgradeVersion()))),
Property(&BuildState::critical_version, IsTrue()),
Property(&BuildState::critical_version,
Eq(base::Optional<base::Version>(GetCriticalVersion()))))));
InstalledVersionPoller poller(&build_state_, callback.Get(), MakeMonitor(),
task_environment_.GetMockTickClock());
task_environment_.RunUntilIdle();
::testing::Mock::VerifyAndClearExpectations(&callback);
::testing::Mock::VerifyAndClearExpectations(&mock_observer_);
}
// Tests that a poll that failed to find a version reports an update anyway.
TEST_F(InstalledVersionPollerTest, TestMissingVersion) {
base::MockRepeatingCallback<InstalledAndCriticalVersion()> callback;
EXPECT_CALL(callback, Run()).WillOnce(Return(ByMove(MakeErrorVersions())));
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()))));
InstalledVersionPoller poller(&build_state_, callback.Get(), MakeMonitor(),
task_environment_.GetMockTickClock());
task_environment_.RunUntilIdle();
::testing::Mock::VerifyAndClearExpectations(&callback);
::testing::Mock::VerifyAndClearExpectations(&mock_observer_);
}
// Tests that a version downgrade (a rollback) is reported as such.
TEST_F(InstalledVersionPollerTest, TestRollback) {
base::MockRepeatingCallback<InstalledAndCriticalVersion()> callback;
EXPECT_CALL(callback, Run()).WillOnce(Return(ByMove(MakeRollbackVersions())));
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>(GetRollbackVersion()))),
Property(&BuildState::critical_version, IsFalse()))));
InstalledVersionPoller poller(&build_state_, callback.Get(), MakeMonitor(),
task_environment_.GetMockTickClock());
task_environment_.RunUntilIdle();
::testing::Mock::VerifyAndClearExpectations(&callback);
::testing::Mock::VerifyAndClearExpectations(&mock_observer_);
}
// Tests that a modification in the monitored location triggers a poll.
TEST_F(InstalledVersionPollerTest, TestMonitor) {
// Provide a GetInstalledVersionCallback that always reports no update, and
// don't make any noise about it being called.
::testing::NiceMock<
base::MockRepeatingCallback<InstalledAndCriticalVersion()>>
callback;
ON_CALL(callback, Run()).WillByDefault([]() {
return MakeNoUpdateVersions();
});
InstalledVersionPoller poller(&build_state_, callback.Get(), MakeMonitor(),
task_environment_.GetMockTickClock());
task_environment_.RunUntilIdle();
// Poke the monitor so that it announces a change.
TriggerMonitor();
::testing::Mock::VerifyAndClearExpectations(&callback);
// Expect a poll in ten seconds.
EXPECT_CALL(callback, Run());
task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(10));
}
// Tests that no metrics are reported if no poll finds an update.
TEST_F(InstalledVersionPollerTest, NoOpNoMetrics) {
base::HistogramTester histogram_tester;
base::MockRepeatingCallback<InstalledAndCriticalVersion()> callback;
EXPECT_CALL(callback, Run())
.WillOnce(Return(ByMove(MakeNoUpdateVersions())))
.WillOnce(Return(ByMove(MakeNoUpdateVersions())))
.WillOnce(Return(ByMove(MakeNoUpdateVersions())));
InstalledVersionPoller poller(&build_state_, callback.Get(), MakeMonitor(),
task_environment_.GetMockTickClock());
// First no-op: the startup task.
task_environment_.RunUntilIdle();
// Second no-op: the periodic task.
task_environment_.FastForwardBy(
InstalledVersionPoller::kDefaultPollingInterval);
// Third no-op: the monitor task.
TriggerMonitor();
task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(10));
histogram_tester.ExpectTotalCount(kPollTypeHistogramName, 0);
}
// Tests that the PollType metric is recorded when the startup poll finds an
// update.
TEST_F(InstalledVersionPollerTest, StartupMetrics) {
base::HistogramTester histogram_tester;
base::MockRepeatingCallback<InstalledAndCriticalVersion()> callback;
EXPECT_CALL(callback, Run()).WillOnce(Return(ByMove(MakeUpgradeVersions())));
EXPECT_CALL(mock_observer_, OnUpdate(_));
InstalledVersionPoller poller(&build_state_, callback.Get(), MakeMonitor(),
task_environment_.GetMockTickClock());
// Run the startup task.
task_environment_.RunUntilIdle();
histogram_tester.ExpectUniqueSample(kPollTypeHistogramName, 0, 1);
}
// Tests that the PollType metric is recorded when the monitor poll finds an
// update.
TEST_F(InstalledVersionPollerTest, MonitorMetrics) {
base::HistogramTester histogram_tester;
base::MockRepeatingCallback<InstalledAndCriticalVersion()> callback;
EXPECT_CALL(callback, Run())
.WillOnce(Return(ByMove(MakeNoUpdateVersions())))
.WillOnce(Return(ByMove(MakeUpgradeVersions())));
EXPECT_CALL(mock_observer_, OnUpdate(_));
InstalledVersionPoller poller(&build_state_, callback.Get(), MakeMonitor(),
task_environment_.GetMockTickClock());
// Run the startup task.
task_environment_.RunUntilIdle();
// The monitor finds a change.
TriggerMonitor();
task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(10));
histogram_tester.ExpectUniqueSample(kPollTypeHistogramName, 1, 1);
}
// Tests that the PollType metric is recorded when the periodic poll finds an
// update.
TEST_F(InstalledVersionPollerTest, PeriodicMetrics) {
base::HistogramTester histogram_tester;
base::MockRepeatingCallback<InstalledAndCriticalVersion()> callback;
EXPECT_CALL(callback, Run())
.WillOnce(Return(ByMove(MakeNoUpdateVersions())))
.WillOnce(Return(ByMove(MakeUpgradeVersions())));
EXPECT_CALL(mock_observer_, OnUpdate(_));
InstalledVersionPoller poller(&build_state_, callback.Get(), MakeMonitor(),
task_environment_.GetMockTickClock());
// Run the startup task.
task_environment_.RunUntilIdle();
// Run the periodic task.
task_environment_.FastForwardBy(
InstalledVersionPoller::kDefaultPollingInterval);
histogram_tester.ExpectUniqueSample(kPollTypeHistogramName, 2, 1);
}
// Tests that the PollType metric is recorded only once even in case of multiple
// polls.
TEST_F(InstalledVersionPollerTest, MetricsOnlyOnce) {
base::HistogramTester histogram_tester;
base::MockRepeatingCallback<InstalledAndCriticalVersion()> callback;
EXPECT_CALL(callback, Run())
.WillOnce(Return(ByMove(MakeUpgradeVersions())))
.WillOnce(Return(ByMove(MakeUpgradeVersions())));
EXPECT_CALL(mock_observer_, OnUpdate(_));
InstalledVersionPoller poller(&build_state_, callback.Get(), MakeMonitor(),
task_environment_.GetMockTickClock());
// Run the startup task.
task_environment_.RunUntilIdle();
// Run the periodic task.
task_environment_.FastForwardBy(
InstalledVersionPoller::kDefaultPollingInterval);
// Only the startup poll is recorded.
histogram_tester.ExpectUniqueSample(kPollTypeHistogramName, 0, 1);
}