// 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 "base/power_monitor/battery_state_sampler.h" #include #include #include "base/power_monitor/power_monitor_buildflags.h" #include "base/power_monitor/sampling_event_source.h" #include "base/run_loop.h" #include "base/task/sequenced_task_runner.h" #include "base/test/bind.h" #include "base/test/gtest_util.h" #include "base/test/power_monitor_test_utils.h" #include "base/test/task_environment.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { class TestBatteryLevelProvider : public BatteryLevelProvider { public: TestBatteryLevelProvider() = default; ~TestBatteryLevelProvider() override = default; void GetBatteryState(OnceCallback&)> callback) override { DCHECK(!battery_states_.empty()); auto next_battery_state = std::move(battery_states_.front()); battery_states_.pop(); std::move(callback).Run(next_battery_state); } void PushBatteryState(const std::optional& battery_state) { battery_states_.push(battery_state); } protected: std::queue> battery_states_; }; class TestBatteryLevelProviderAsync : public TestBatteryLevelProvider { public: TestBatteryLevelProviderAsync() = default; ~TestBatteryLevelProviderAsync() override = default; void GetBatteryState(OnceCallback&)> callback) override { DCHECK(!battery_states_.empty()); auto next_battery_state = std::move(battery_states_.front()); battery_states_.pop(); SequencedTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindLambdaForTesting([callback = std::move(callback), battery_state = next_battery_state]() mutable { std::move(callback).Run(battery_state); })); } }; // A test observer that exposes the last battery state received. class TestObserver : public BatteryStateSampler::Observer { public: TestObserver() = default; ~TestObserver() override = default; void OnBatteryStateSampled( const std::optional& battery_state) override { battery_state_ = battery_state; } std::optional battery_state() const { return battery_state_; } private: std::optional battery_state_; }; // Those test battery states just need to be different. The actual values don't // matter. const std::optional kTestBatteryState1 = BatteryLevelProvider::BatteryState{ .battery_count = 1, .is_external_power_connected = false, .current_capacity = 10000, .full_charged_capacity = 10000, .charge_unit = BatteryLevelProvider::BatteryLevelUnit::kMWh}; const std::optional kTestBatteryState2 = BatteryLevelProvider::BatteryState{ .battery_count = 1, .is_external_power_connected = false, .current_capacity = 5000, .full_charged_capacity = 10000, .charge_unit = BatteryLevelProvider::BatteryLevelUnit::kMWh}; const std::optional kTestBatteryState3 = BatteryLevelProvider::BatteryState{ .battery_count = 1, .is_external_power_connected = true, .current_capacity = 2000, .full_charged_capacity = 10000, .charge_unit = BatteryLevelProvider::BatteryLevelUnit::kMWh}; bool operator==(const BatteryLevelProvider::BatteryState& lhs, const BatteryLevelProvider::BatteryState& rhs) { return std::tie(lhs.battery_count, lhs.is_external_power_connected, lhs.current_capacity, lhs.full_charged_capacity, lhs.charge_unit) == std::tie(rhs.battery_count, rhs.is_external_power_connected, rhs.current_capacity, rhs.full_charged_capacity, rhs.charge_unit); } bool operator!=(const BatteryLevelProvider::BatteryState& lhs, const BatteryLevelProvider::BatteryState& rhs) { return !(lhs == rhs); } TEST(BatteryStateSamplerTest, GlobalInstance) { #if BUILDFLAG(HAS_BATTERY_LEVEL_PROVIDER_IMPL) || BUILDFLAG(IS_CHROMEOS_ASH) // Get() DCHECKs on platforms with a battery level provider if it's called // without being initialized. ChromeOS behaves the same because it has a // `BatteryLevelProvider`, but it doesn't live in base so it doesn't exist in // this test. EXPECT_DCHECK_DEATH(BatteryStateSampler::Get()); #else // Get() returns null if the sampler doesn't exist on platforms without a // battery level provider. EXPECT_FALSE(BatteryStateSampler::Get()); #endif auto battery_level_provider = std::make_unique(); battery_level_provider->PushBatteryState(kTestBatteryState1); // Create the sampler. auto battery_state_sampler = std::make_unique( std::make_unique(), std::move(battery_level_provider)); // Now the getter works. EXPECT_TRUE(BatteryStateSampler::Get()); // Can't create a second sampler. EXPECT_DCHECK_DEATH({ BatteryStateSampler another_battery_state_sampler( std::make_unique(), std::make_unique()); }); battery_state_sampler.reset(); // The sampler no longer exists. #if BUILDFLAG(HAS_BATTERY_LEVEL_PROVIDER_IMPL) || BUILDFLAG(IS_CHROMEOS_ASH) EXPECT_DCHECK_DEATH(BatteryStateSampler::Get()); #else EXPECT_FALSE(BatteryStateSampler::Get()); #endif } TEST(BatteryStateSamplerTest, InitialSample) { auto sampling_event_source = std::make_unique(); auto battery_level_provider = std::make_unique(); // Push the initial battery state that will be queried by the sampler. battery_level_provider->PushBatteryState(kTestBatteryState1); BatteryStateSampler battery_state_sampler(std::move(sampling_event_source), std::move(battery_level_provider)); TestObserver observer; battery_state_sampler.AddObserver(&observer); EXPECT_EQ(observer.battery_state(), kTestBatteryState1); } TEST(BatteryStateSamplerTest, MultipleSamples) { auto battery_level_provider = std::make_unique(); // Push the initial battery state and the first 3 samples after it. battery_level_provider->PushBatteryState(kTestBatteryState1); battery_level_provider->PushBatteryState(kTestBatteryState2); battery_level_provider->PushBatteryState(kTestBatteryState3); battery_level_provider->PushBatteryState(kTestBatteryState1); auto sampling_event_source = std::make_unique(); auto* sampling_event_source_ptr = sampling_event_source.get(); BatteryStateSampler battery_state_sampler(std::move(sampling_event_source), std::move(battery_level_provider)); TestObserver observer; battery_state_sampler.AddObserver(&observer); EXPECT_EQ(observer.battery_state(), kTestBatteryState1); // Simulate 2 events to trigger 2 more samples. sampling_event_source_ptr->SimulateEvent(); EXPECT_EQ(observer.battery_state(), kTestBatteryState2); sampling_event_source_ptr->SimulateEvent(); EXPECT_EQ(observer.battery_state(), kTestBatteryState3); battery_state_sampler.RemoveObserver(&observer); // Simulate 1 event and ensure the observer does not receive the new state. sampling_event_source_ptr->SimulateEvent(); EXPECT_NE(observer.battery_state(), kTestBatteryState1); } TEST(BatteryStateSamplerTest, MultipleObservers) { auto battery_level_provider = std::make_unique(); // Push the initial battery state and the first sample after it. battery_level_provider->PushBatteryState(kTestBatteryState1); battery_level_provider->PushBatteryState(kTestBatteryState2); auto sampling_event_source = std::make_unique(); auto* sampling_event_source_ptr = sampling_event_source.get(); BatteryStateSampler battery_state_sampler(std::move(sampling_event_source), std::move(battery_level_provider)); // Add 2 observers. TestObserver observer1; battery_state_sampler.AddObserver(&observer1); EXPECT_EQ(observer1.battery_state(), kTestBatteryState1); TestObserver observer2; battery_state_sampler.AddObserver(&observer2); EXPECT_EQ(observer2.battery_state(), kTestBatteryState1); // Simulate an event to get another sample. sampling_event_source_ptr->SimulateEvent(); EXPECT_EQ(observer1.battery_state(), kTestBatteryState2); EXPECT_EQ(observer2.battery_state(), kTestBatteryState2); } // Tests that if sampling the battery state is asynchronous (like it is on // Windows), the sampler will correctly notify new observers when the first // sample arrives. TEST(BatteryStateSamplerTest, InitialSample_Async) { test::SingleThreadTaskEnvironment task_environment; auto battery_level_provider = std::make_unique(); // Push the initial battery state. battery_level_provider->PushBatteryState(kTestBatteryState1); auto sampling_event_source = std::make_unique(); // Creating the sampler starts the first async sample. BatteryStateSampler battery_state_sampler(std::move(sampling_event_source), std::move(battery_level_provider)); TestObserver observer; battery_state_sampler.AddObserver(&observer); EXPECT_EQ(observer.battery_state(), std::nullopt); RunLoop().RunUntilIdle(); EXPECT_EQ(observer.battery_state(), kTestBatteryState1); } } // namespace base