| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/aggregation_service/report_scheduler_timer.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/callback.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/test/gmock_move_support.h" |
| #include "base/test/task_environment.h" |
| #include "base/time/time.h" |
| #include "content/public/browser/network_service_instance.h" |
| #include "services/network/public/mojom/network_change_manager.mojom.h" |
| #include "services/network/test/test_network_connection_tracker.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| using testing::_; |
| using testing::InSequence; |
| using testing::Invoke; |
| |
| using Checkpoint = testing::MockFunction<void(int step)>; |
| |
| const base::Time kExampleTime = base::Time::FromJavaTime(1652984901234); |
| |
| class MockReportSchedulerTimerDelegate : public ReportSchedulerTimer::Delegate { |
| public: |
| MOCK_METHOD(void, |
| GetNextReportTime, |
| (base::OnceCallback<void(absl::optional<base::Time>)>, |
| base::Time), |
| (override)); |
| MOCK_METHOD(void, OnReportingTimeReached, (base::Time), (override)); |
| |
| MOCK_METHOD(void, |
| AdjustOfflineReportTimes, |
| (base::OnceCallback<void(absl::optional<base::Time>)>), |
| (override)); |
| }; |
| |
| class ReportSchedulerTimerTest : public testing::Test { |
| public: |
| ReportSchedulerTimerTest() |
| : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) { |
| auto timer_delegate = std::make_unique<MockReportSchedulerTimerDelegate>(); |
| timer_delegate_ = timer_delegate.get(); |
| timer_ = std::make_unique<ReportSchedulerTimer>(std::move(timer_delegate)); |
| } |
| |
| void SetUp() override { |
| SetNetworkConnectionTrackerForTesting( |
| network::TestNetworkConnectionTracker::GetInstance()); |
| } |
| |
| protected: |
| base::test::TaskEnvironment task_environment_; |
| raw_ptr<MockReportSchedulerTimerDelegate> timer_delegate_; |
| std::unique_ptr<ReportSchedulerTimer> timer_; |
| }; |
| |
| TEST_F(ReportSchedulerTimerTest, SetTimer_FiredAtAppropriateTime) { |
| Checkpoint checkpoint; |
| { |
| InSequence seq; |
| |
| EXPECT_CALL(*timer_delegate_, OnReportingTimeReached).Times(0); |
| |
| EXPECT_CALL(checkpoint, Call(1)); |
| |
| EXPECT_CALL(*timer_delegate_, OnReportingTimeReached); |
| EXPECT_CALL(*timer_delegate_, GetNextReportTime); // Called via Refresh(). |
| } |
| |
| timer_->MaybeSet(kExampleTime); |
| |
| base::TimeDelta fast_forward_required = kExampleTime - base::Time::Now(); |
| task_environment_.FastForwardBy(fast_forward_required - base::Seconds(1)); |
| |
| checkpoint.Call(1); |
| |
| task_environment_.FastForwardBy(base::Seconds(1)); |
| } |
| |
| TEST_F(ReportSchedulerTimerTest, MultipleSetTimers_FiredAtAppropriateTime) { |
| Checkpoint checkpoint; |
| base::OnceCallback<void(absl::optional<base::Time>)> saved_cb_1; |
| base::OnceCallback<void(absl::optional<base::Time>)> saved_cb_2; |
| |
| { |
| InSequence seq; |
| |
| EXPECT_CALL(*timer_delegate_, OnReportingTimeReached).Times(0); |
| |
| EXPECT_CALL(checkpoint, Call(1)); |
| |
| EXPECT_CALL(*timer_delegate_, OnReportingTimeReached); |
| EXPECT_CALL(*timer_delegate_, GetNextReportTime) |
| .WillOnce(MoveArg<0>(&saved_cb_1)); // Called via Refresh(). |
| |
| EXPECT_CALL(checkpoint, Call(2)); |
| |
| EXPECT_CALL(*timer_delegate_, OnReportingTimeReached).Times(0); |
| |
| EXPECT_CALL(checkpoint, Call(3)); |
| |
| EXPECT_CALL(*timer_delegate_, OnReportingTimeReached); |
| EXPECT_CALL(*timer_delegate_, GetNextReportTime) |
| .WillOnce(MoveArg<0>(&saved_cb_2)); // Called via Refresh(). |
| |
| EXPECT_CALL(checkpoint, Call(4)); |
| } |
| // Test both simultaneous and non-simultaneous times. |
| const base::Time kReportTimes[] = {kExampleTime, kExampleTime, |
| kExampleTime + base::Hours(1)}; |
| |
| for (base::Time report_time : kReportTimes) { |
| timer_->MaybeSet(report_time); |
| } |
| |
| checkpoint.Call(1); |
| |
| task_environment_.FastForwardBy(kExampleTime - base::Time::Now()); |
| |
| checkpoint.Call(2); |
| |
| std::move(saved_cb_1).Run(kExampleTime + base::Hours(1)); |
| |
| checkpoint.Call(3); |
| |
| task_environment_.FastForwardBy(base::Hours(1)); |
| |
| checkpoint.Call(4); |
| |
| // Nothing should happen if no reports are left. |
| std::move(saved_cb_2).Run(absl::nullopt); |
| } |
| |
| TEST_F(ReportSchedulerTimerTest, Refresh_CallsGetNextReportTime) { |
| EXPECT_CALL(*timer_delegate_, GetNextReportTime); |
| timer_->Refresh(); |
| } |
| |
| TEST_F(ReportSchedulerTimerTest, NetworkChange) { |
| Checkpoint checkpoint; |
| { |
| InSequence seq; |
| |
| EXPECT_CALL(*timer_delegate_, OnReportingTimeReached).Times(0); |
| EXPECT_CALL(checkpoint, Call(1)); |
| EXPECT_CALL(*timer_delegate_, AdjustOfflineReportTimes); |
| } |
| |
| timer_->MaybeSet(kExampleTime); |
| |
| // Go offline |
| network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType( |
| network::mojom::ConnectionType::CONNECTION_NONE); |
| |
| task_environment_.FastForwardBy(kExampleTime - base::Time::Now()); |
| |
| checkpoint.Call(1); |
| |
| // Go back online. |
| network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType( |
| network::mojom::ConnectionType::CONNECTION_WIFI); |
| // Ensure that the network connection observers have been notified before |
| // this call returns. |
| task_environment_.RunUntilIdle(); |
| } |
| |
| } // namespace |
| |
| } // namespace content |