| // Copyright 2019 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/enterprise_reporting/report_scheduler.h" |
| |
| #include <utility> |
| |
| #include "base/test/gmock_callback_support.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "chrome/browser/enterprise_reporting/prefs.h" |
| #include "chrome/browser/enterprise_reporting/request_timer.h" |
| #include "chrome/browser/policy/fake_browser_dm_token_storage.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/test/base/scoped_testing_local_state.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "components/policy/core/common/cloud/mock_cloud_policy_client.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ::base::test::RunOnceCallback; |
| using ::testing::_; |
| using ::testing::Invoke; |
| using ::testing::WithArgs; |
| |
| namespace em = enterprise_management; |
| |
| namespace enterprise_reporting { |
| namespace { |
| constexpr char kDMToken[] = "dm_token"; |
| constexpr char kClientId[] = "client_id"; |
| const int kDefaultUploadInterval = 24; |
| } // namespace |
| class FakeRequestTimer : public RequestTimer { |
| public: |
| FakeRequestTimer() = default; |
| ~FakeRequestTimer() override = default; |
| void Start(const base::Location& posted_from, |
| base::TimeDelta first_delay, |
| base::TimeDelta repeat_delay, |
| base::RepeatingClosure user_task) override { |
| first_delay_ = first_delay; |
| repeat_delay_ = repeat_delay; |
| user_task_ = user_task; |
| is_running_ = true; |
| } |
| |
| void Stop() override { is_running_ = false; } |
| |
| void Reset() override { is_running_ = true; } |
| |
| void Fire() { |
| EXPECT_TRUE(is_running_); |
| user_task_.Run(); |
| is_running_ = false; |
| } |
| |
| bool is_running() { return is_running_; } |
| |
| base::TimeDelta first_delay() { return first_delay_; } |
| |
| base::TimeDelta repeat_delay() { return repeat_delay_; } |
| |
| private: |
| base::TimeDelta first_delay_; |
| base::TimeDelta repeat_delay_; |
| base::RepeatingClosure user_task_; |
| bool is_running_ = false; |
| DISALLOW_COPY_AND_ASSIGN(FakeRequestTimer); |
| }; |
| |
| ACTION_P(ScheduleGeneratorCallback, request_number) { |
| ReportGenerator::Requests requests; |
| for (int i = 0; i < request_number; i++) |
| requests.push(std::make_unique<em::ChromeDesktopReportRequest>()); |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(arg0), std::move(requests))); |
| } |
| |
| class MockReportGenerator : public ReportGenerator { |
| public: |
| void Generate(ReportCallback callback) override { OnGenerate(callback); } |
| MOCK_METHOD1(OnGenerate, void(ReportCallback& callback)); |
| }; |
| |
| class MockReportUploader : public ReportUploader { |
| public: |
| MockReportUploader() : ReportUploader(nullptr, 0) {} |
| ~MockReportUploader() override = default; |
| void SetRequestAndUpload(Requests requests, |
| ReportCallback callback) override { |
| OnSetRequestAndUpload(requests, callback); |
| } |
| MOCK_METHOD2(OnSetRequestAndUpload, |
| void(Requests& requests, ReportCallback& callback)); |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(MockReportUploader); |
| }; |
| |
| class ReportSchedulerTest : public ::testing::Test { |
| public: |
| ReportSchedulerTest() |
| : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME), |
| local_state_(TestingBrowserProcess::GetGlobal()) {} |
| ~ReportSchedulerTest() override = default; |
| void SetUp() override { |
| scoped_feature_list_.InitAndEnableFeature( |
| features::kEnterpriseReportingInBrowser); |
| RegisterPrefs(local_state_.Get()->registry()); |
| client_ptr_ = std::make_unique<policy::MockCloudPolicyClient>(); |
| client_ = client_ptr_.get(); |
| timer_ptr_ = std::make_unique<FakeRequestTimer>(); |
| timer_ = timer_ptr_.get(); |
| generator_ptr_ = std::make_unique<MockReportGenerator>(); |
| generator_ = generator_ptr_.get(); |
| uploader_ptr_ = std::make_unique<MockReportUploader>(); |
| uploader_ = uploader_ptr_.get(); |
| Init(true, kDMToken, kClientId); |
| } |
| |
| void Init(bool policy_enabled, |
| const std::string& dm_token, |
| const std::string& client_id) { |
| ToggleCloudReport(policy_enabled); |
| storage_.SetDMToken(dm_token); |
| storage_.SetClientId(client_id); |
| } |
| |
| void CreateScheduler() { |
| scheduler_ = std::make_unique<ReportScheduler>(std::move(client_ptr_), |
| std::move(timer_ptr_), |
| std::move(generator_ptr_)); |
| scheduler_->SetReportUploaderForTesting(std::move(uploader_ptr_)); |
| } |
| |
| void SetLastUploadInHour(int gap) { |
| previous_set_last_upload_timestamp_ = |
| base::Time::Now() - base::TimeDelta::FromHours(gap); |
| local_state_.Get()->SetTime(kLastUploadTimestamp, |
| previous_set_last_upload_timestamp_); |
| } |
| |
| void ToggleCloudReport(bool enabled) { |
| local_state_.Get()->SetManagedPref(prefs::kCloudReportingEnabled, |
| std::make_unique<base::Value>(enabled)); |
| } |
| |
| // If lastUploadTimestamp is updated recently, it should be updated as Now(). |
| // Otherwise, it should be same as previous set timestamp. |
| void ExpectLastUploadTimestampUpdated(bool is_updated) { |
| auto current_last_upload_timestamp = |
| local_state_.Get()->GetTime(kLastUploadTimestamp); |
| if (is_updated) { |
| EXPECT_EQ(base::Time::Now(), current_last_upload_timestamp); |
| } else { |
| EXPECT_EQ(previous_set_last_upload_timestamp_, |
| current_last_upload_timestamp); |
| } |
| } |
| |
| ReportGenerator::Requests CreateRequests(int number) { |
| ReportGenerator::Requests requests; |
| for (int i = 0; i < number; i++) |
| requests.push(std::make_unique<em::ChromeDesktopReportRequest>()); |
| return requests; |
| } |
| |
| base::test::ScopedFeatureList scoped_feature_list_; |
| base::test::TaskEnvironment task_environment_; |
| ScopedTestingLocalState local_state_; |
| |
| std::unique_ptr<ReportScheduler> scheduler_; |
| policy::MockCloudPolicyClient* client_; |
| FakeRequestTimer* timer_; |
| MockReportGenerator* generator_; |
| MockReportUploader* uploader_; |
| policy::FakeBrowserDMTokenStorage storage_; |
| base::Time previous_set_last_upload_timestamp_; |
| |
| private: |
| std::unique_ptr<policy::MockCloudPolicyClient> client_ptr_; |
| std::unique_ptr<FakeRequestTimer> timer_ptr_; |
| std::unique_ptr<MockReportGenerator> generator_ptr_; |
| std::unique_ptr<MockReportUploader> uploader_ptr_; |
| DISALLOW_COPY_AND_ASSIGN(ReportSchedulerTest); |
| }; |
| |
| TEST_F(ReportSchedulerTest, NoReportWithoutPolicy) { |
| Init(false, kDMToken, kClientId); |
| CreateScheduler(); |
| EXPECT_FALSE(timer_->is_running()); |
| } |
| |
| TEST_F(ReportSchedulerTest, NoReportWithoutDMToken) { |
| Init(true, "", kClientId); |
| CreateScheduler(); |
| EXPECT_FALSE(timer_->is_running()); |
| } |
| |
| TEST_F(ReportSchedulerTest, NoReportWithoutClientId) { |
| Init(true, kDMToken, ""); |
| CreateScheduler(); |
| EXPECT_FALSE(timer_->is_running()); |
| } |
| |
| TEST_F(ReportSchedulerTest, UploadReportSucceeded) { |
| EXPECT_CALL(*client_, SetupRegistration(kDMToken, kClientId, _)); |
| EXPECT_CALL(*generator_, OnGenerate(_)) |
| .WillOnce(WithArgs<0>(ScheduleGeneratorCallback(1))); |
| EXPECT_CALL(*uploader_, OnSetRequestAndUpload(_, _)) |
| .WillOnce(RunOnceCallback<1>(ReportUploader::kSuccess)); |
| |
| CreateScheduler(); |
| EXPECT_TRUE(timer_->is_running()); |
| |
| timer_->Fire(); |
| |
| // timer is paused until the report is finished. |
| EXPECT_FALSE(timer_->is_running()); |
| |
| // Run pending task. |
| task_environment_.FastForwardBy(base::TimeDelta()); |
| |
| // Next report is scheduled. |
| EXPECT_TRUE(timer_->is_running()); |
| ExpectLastUploadTimestampUpdated(true); |
| |
| ::testing::Mock::VerifyAndClearExpectations(client_); |
| ::testing::Mock::VerifyAndClearExpectations(generator_); |
| } |
| |
| TEST_F(ReportSchedulerTest, UploadReportTransientError) { |
| EXPECT_CALL(*client_, SetupRegistration(kDMToken, kClientId, _)); |
| EXPECT_CALL(*generator_, OnGenerate(_)) |
| .WillOnce(WithArgs<0>(ScheduleGeneratorCallback(1))); |
| EXPECT_CALL(*uploader_, OnSetRequestAndUpload(_, _)) |
| .WillOnce(RunOnceCallback<1>(ReportUploader::kTransientError)); |
| |
| CreateScheduler(); |
| EXPECT_TRUE(timer_->is_running()); |
| |
| timer_->Fire(); |
| |
| // timer is paused until the report is finished. |
| EXPECT_FALSE(timer_->is_running()); |
| |
| // Run pending task. |
| task_environment_.FastForwardBy(base::TimeDelta()); |
| |
| // Next report is scheduled. |
| EXPECT_TRUE(timer_->is_running()); |
| ExpectLastUploadTimestampUpdated(true); |
| |
| ::testing::Mock::VerifyAndClearExpectations(client_); |
| ::testing::Mock::VerifyAndClearExpectations(generator_); |
| } |
| |
| TEST_F(ReportSchedulerTest, UploadReportPersistentError) { |
| EXPECT_CALL(*client_, SetupRegistration(kDMToken, kClientId, _)) |
| .WillOnce(WithArgs<0>( |
| Invoke(client_, &policy::MockCloudPolicyClient::SetDMToken))); |
| EXPECT_CALL(*generator_, OnGenerate(_)) |
| .WillOnce(WithArgs<0>(ScheduleGeneratorCallback(1))); |
| EXPECT_CALL(*uploader_, OnSetRequestAndUpload(_, _)) |
| .WillOnce(RunOnceCallback<1>(ReportUploader::kPersistentError)); |
| |
| CreateScheduler(); |
| EXPECT_TRUE(timer_->is_running()); |
| |
| timer_->Fire(); |
| |
| // timer is paused until the report is finished. |
| EXPECT_FALSE(timer_->is_running()); |
| |
| // Run pending task. |
| task_environment_.FastForwardBy(base::TimeDelta()); |
| |
| // Next report is not scheduled. |
| EXPECT_FALSE(timer_->is_running()); |
| ExpectLastUploadTimestampUpdated(false); |
| |
| // Turn off and on reporting to resume. |
| ToggleCloudReport(false); |
| ToggleCloudReport(true); |
| EXPECT_TRUE(timer_->is_running()); |
| |
| ::testing::Mock::VerifyAndClearExpectations(client_); |
| ::testing::Mock::VerifyAndClearExpectations(generator_); |
| } |
| |
| TEST_F(ReportSchedulerTest, NoReportGenerate) { |
| EXPECT_CALL(*client_, SetupRegistration(kDMToken, kClientId, _)) |
| .WillOnce(WithArgs<0>( |
| Invoke(client_, &policy::MockCloudPolicyClient::SetDMToken))); |
| EXPECT_CALL(*generator_, OnGenerate(_)) |
| .WillOnce(WithArgs<0>(ScheduleGeneratorCallback(0))); |
| EXPECT_CALL(*uploader_, OnSetRequestAndUpload(_, _)).Times(0); |
| |
| CreateScheduler(); |
| EXPECT_TRUE(timer_->is_running()); |
| |
| timer_->Fire(); |
| |
| // timer is paused until the report is finished. |
| EXPECT_FALSE(timer_->is_running()); |
| |
| // Run pending task. |
| task_environment_.FastForwardBy(base::TimeDelta()); |
| |
| // Next report is not scheduled. |
| EXPECT_FALSE(timer_->is_running()); |
| ExpectLastUploadTimestampUpdated(false); |
| |
| // Turn off and on reporting to resume. |
| ToggleCloudReport(false); |
| ToggleCloudReport(true); |
| EXPECT_TRUE(timer_->is_running()); |
| |
| ::testing::Mock::VerifyAndClearExpectations(client_); |
| ::testing::Mock::VerifyAndClearExpectations(generator_); |
| } |
| |
| TEST_F(ReportSchedulerTest, TimerDelayWithLastUploadTimestamp) { |
| int gap = 10; |
| SetLastUploadInHour(gap); |
| |
| EXPECT_CALL(*client_, SetupRegistration(kDMToken, kClientId, _)); |
| |
| CreateScheduler(); |
| EXPECT_TRUE(timer_->is_running()); |
| |
| EXPECT_EQ(base::TimeDelta::FromHours(kDefaultUploadInterval - gap), |
| timer_->first_delay()); |
| EXPECT_EQ(base::TimeDelta::FromHours(kDefaultUploadInterval), |
| timer_->repeat_delay()); |
| |
| ::testing::Mock::VerifyAndClearExpectations(client_); |
| ::testing::Mock::VerifyAndClearExpectations(generator_); |
| } |
| |
| TEST_F(ReportSchedulerTest, TimerDelayWithoutLastUploadTimestamp) { |
| EXPECT_CALL(*client_, SetupRegistration(kDMToken, kClientId, _)); |
| |
| CreateScheduler(); |
| EXPECT_TRUE(timer_->is_running()); |
| |
| EXPECT_GT(base::TimeDelta(), timer_->first_delay()); |
| EXPECT_EQ(base::TimeDelta::FromHours(kDefaultUploadInterval), |
| timer_->repeat_delay()); |
| |
| ::testing::Mock::VerifyAndClearExpectations(client_); |
| } |
| |
| TEST_F(ReportSchedulerTest, |
| ReportingIsDisabledWhileNewReportIsScheduledButNotPosted) { |
| EXPECT_CALL(*client_, SetupRegistration(kDMToken, kClientId, _)); |
| |
| CreateScheduler(); |
| EXPECT_TRUE(timer_->is_running()); |
| |
| ToggleCloudReport(false); |
| |
| // Next report is not scheduled. |
| EXPECT_FALSE(timer_->is_running()); |
| ExpectLastUploadTimestampUpdated(false); |
| |
| ::testing::Mock::VerifyAndClearExpectations(client_); |
| ::testing::Mock::VerifyAndClearExpectations(generator_); |
| } |
| |
| TEST_F(ReportSchedulerTest, ReportingIsDisabledWhileNewReportIsPosted) { |
| EXPECT_CALL(*client_, SetupRegistration(kDMToken, kClientId, _)); |
| EXPECT_CALL(*generator_, OnGenerate(_)) |
| .WillOnce(WithArgs<0>(ScheduleGeneratorCallback(1))); |
| EXPECT_CALL(*uploader_, OnSetRequestAndUpload(_, _)) |
| .WillOnce(RunOnceCallback<1>(ReportUploader::kSuccess)); |
| |
| CreateScheduler(); |
| EXPECT_TRUE(timer_->is_running()); |
| |
| timer_->Fire(); |
| ToggleCloudReport(false); |
| EXPECT_FALSE(timer_->is_running()); |
| |
| // Run pending task. |
| task_environment_.FastForwardBy(base::TimeDelta()); |
| |
| ExpectLastUploadTimestampUpdated(true); |
| // Next report is not scheduled. |
| EXPECT_FALSE(timer_->is_running()); |
| |
| ::testing::Mock::VerifyAndClearExpectations(client_); |
| ::testing::Mock::VerifyAndClearExpectations(generator_); |
| } |
| |
| } // namespace enterprise_reporting |