| // Copyright 2020 The Chromium OS 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 <memory> |
| #include <utility> |
| |
| #include <base/run_loop.h> |
| #include <base/test/task_environment.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <mojo/core/embedder/embedder.h> |
| #include <mojo/public/cpp/bindings/binding.h> |
| #include <mojo/public/cpp/bindings/interface_request.h> |
| #include <power_manager/proto_bindings/power_supply_properties.pb.h> |
| |
| #include "diagnostics/common/system/fake_powerd_adapter.h" |
| #include "diagnostics/cros_healthd/events/power_events_impl.h" |
| #include "diagnostics/cros_healthd/system/mock_context.h" |
| #include "mojo/cros_healthd_events.mojom.h" |
| |
| namespace diagnostics { |
| |
| namespace { |
| |
| namespace mojo_ipc = ::chromeos::cros_healthd::mojom; |
| |
| using ::testing::Invoke; |
| using ::testing::StrictMock; |
| |
| class MockCrosHealthdPowerObserver : public mojo_ipc::CrosHealthdPowerObserver { |
| public: |
| MockCrosHealthdPowerObserver( |
| mojo_ipc::CrosHealthdPowerObserverRequest request) |
| : binding_{this /* impl */, std::move(request)} { |
| DCHECK(binding_.is_bound()); |
| } |
| MockCrosHealthdPowerObserver(const MockCrosHealthdPowerObserver&) = delete; |
| MockCrosHealthdPowerObserver& operator=(const MockCrosHealthdPowerObserver&) = |
| delete; |
| |
| MOCK_METHOD(void, OnAcInserted, (), (override)); |
| MOCK_METHOD(void, OnAcRemoved, (), (override)); |
| MOCK_METHOD(void, OnOsSuspend, (), (override)); |
| MOCK_METHOD(void, OnOsResume, (), (override)); |
| |
| private: |
| mojo::Binding<mojo_ipc::CrosHealthdPowerObserver> binding_; |
| }; |
| |
| } // namespace |
| |
| // Tests for the PowerEventsImpl class. |
| class PowerEventsImplTest : public testing::Test { |
| protected: |
| PowerEventsImplTest() = default; |
| PowerEventsImplTest(const PowerEventsImplTest&) = delete; |
| PowerEventsImplTest& operator=(const PowerEventsImplTest&) = delete; |
| |
| void SetUp() override { |
| ASSERT_TRUE(mock_context_.Initialize()); |
| |
| // Before any observers have been added, we shouldn't have subscribed to |
| // powerd_adapter. |
| ASSERT_FALSE(fake_adapter()->HasPowerObserver(&power_events_impl_)); |
| |
| mojo_ipc::CrosHealthdPowerObserverPtr observer_ptr; |
| mojo_ipc::CrosHealthdPowerObserverRequest observer_request( |
| mojo::MakeRequest(&observer_ptr)); |
| observer_ = std::make_unique<StrictMock<MockCrosHealthdPowerObserver>>( |
| std::move(observer_request)); |
| power_events_impl_.AddObserver(std::move(observer_ptr)); |
| // Now that an observer has been added, we should have subscribed to |
| // powerd_adapter. |
| ASSERT_TRUE(fake_adapter()->HasPowerObserver(&power_events_impl_)); |
| } |
| |
| PowerEventsImpl* power_events_impl() { return &power_events_impl_; } |
| |
| FakePowerdAdapter* fake_adapter() { |
| return mock_context_.fake_powerd_adapter(); |
| } |
| |
| MockCrosHealthdPowerObserver* mock_observer() { return observer_.get(); } |
| |
| base::test::TaskEnvironment* task_environment() { return &task_environment_; } |
| |
| void DestroyMojoObserver() { |
| observer_.reset(); |
| |
| // Make sure |power_events_impl_| gets a chance to observe the connection |
| // error. |
| task_environment_.RunUntilIdle(); |
| } |
| |
| private: |
| base::test::TaskEnvironment task_environment_; |
| MockContext mock_context_; |
| std::unique_ptr<StrictMock<MockCrosHealthdPowerObserver>> observer_; |
| PowerEventsImpl power_events_impl_{&mock_context_}; |
| }; |
| |
| // Test that we can receive AC inserted events from powerd's AC proto. |
| TEST_F(PowerEventsImplTest, ReceiveAcInsertedEventFromAcProto) { |
| base::RunLoop run_loop; |
| EXPECT_CALL(*mock_observer(), OnAcInserted()).WillOnce(Invoke([&]() { |
| run_loop.Quit(); |
| })); |
| |
| power_manager::PowerSupplyProperties power_supply; |
| power_supply.set_external_power(power_manager::PowerSupplyProperties::AC); |
| fake_adapter()->EmitPowerSupplyPollSignal(power_supply); |
| |
| run_loop.Run(); |
| } |
| |
| // Test that we can receive AC inserted events from powerd's USB proto. |
| TEST_F(PowerEventsImplTest, ReceiveAcInsertedEventFromUsbProto) { |
| base::RunLoop run_loop; |
| EXPECT_CALL(*mock_observer(), OnAcInserted()).WillOnce(Invoke([&]() { |
| run_loop.Quit(); |
| })); |
| |
| power_manager::PowerSupplyProperties power_supply; |
| power_supply.set_external_power(power_manager::PowerSupplyProperties::USB); |
| fake_adapter()->EmitPowerSupplyPollSignal(power_supply); |
| |
| run_loop.Run(); |
| } |
| |
| // Test that we can receive AC removed events. |
| TEST_F(PowerEventsImplTest, ReceiveAcRemovedEvent) { |
| base::RunLoop run_loop; |
| EXPECT_CALL(*mock_observer(), OnAcRemoved()).WillOnce(Invoke([&]() { |
| run_loop.Quit(); |
| })); |
| |
| power_manager::PowerSupplyProperties power_supply; |
| power_supply.set_external_power( |
| power_manager::PowerSupplyProperties::DISCONNECTED); |
| fake_adapter()->EmitPowerSupplyPollSignal(power_supply); |
| |
| run_loop.Run(); |
| } |
| |
| // Test that we can receive OS suspend events from suspend imminent signals. |
| TEST_F(PowerEventsImplTest, ReceiveOsSuspendEventFromSuspendImminent) { |
| base::RunLoop run_loop; |
| EXPECT_CALL(*mock_observer(), OnOsSuspend()).WillOnce(Invoke([&]() { |
| run_loop.Quit(); |
| })); |
| |
| power_manager::SuspendImminent suspend_imminent; |
| fake_adapter()->EmitSuspendImminentSignal(suspend_imminent); |
| |
| run_loop.Run(); |
| } |
| |
| // Test that we can receive OS suspend events from dark suspend imminent |
| // signals. |
| TEST_F(PowerEventsImplTest, ReceiveOsSuspendEventFromDarkSuspendImminent) { |
| base::RunLoop run_loop; |
| EXPECT_CALL(*mock_observer(), OnOsSuspend()).WillOnce(Invoke([&]() { |
| run_loop.Quit(); |
| })); |
| |
| power_manager::SuspendImminent suspend_imminent; |
| fake_adapter()->EmitDarkSuspendImminentSignal(suspend_imminent); |
| |
| run_loop.Run(); |
| } |
| |
| // Test that we can receive OS resume events. |
| TEST_F(PowerEventsImplTest, ReceiveOsResumeEvent) { |
| base::RunLoop run_loop; |
| EXPECT_CALL(*mock_observer(), OnOsResume()).WillOnce(Invoke([&]() { |
| run_loop.Quit(); |
| })); |
| |
| power_manager::SuspendDone suspend_done; |
| fake_adapter()->EmitSuspendDoneSignal(suspend_done); |
| |
| run_loop.Run(); |
| } |
| |
| // Test that powerd events without external power are ignored. |
| TEST_F(PowerEventsImplTest, IgnorePayloadWithoutExternalPower) { |
| power_manager::PowerSupplyProperties power_supply; |
| fake_adapter()->EmitPowerSupplyPollSignal(power_supply); |
| |
| task_environment()->RunUntilIdle(); |
| } |
| |
| // Test that multiple of the same powerd events in a row are only reported once. |
| TEST_F(PowerEventsImplTest, MultipleIdenticalPayloadsReportedOnlyOnce) { |
| base::RunLoop run_loop; |
| EXPECT_CALL(*mock_observer(), OnAcRemoved()).WillOnce(Invoke([&]() { |
| run_loop.Quit(); |
| })); |
| |
| // Make the first call, which should be reported. |
| power_manager::PowerSupplyProperties power_supply; |
| power_supply.set_external_power( |
| power_manager::PowerSupplyProperties::DISCONNECTED); |
| fake_adapter()->EmitPowerSupplyPollSignal(power_supply); |
| |
| run_loop.Run(); |
| |
| // A second identical call should be ignored. |
| fake_adapter()->EmitPowerSupplyPollSignal(power_supply); |
| |
| task_environment()->RunUntilIdle(); |
| |
| // Changing the type of external power should again be reported. |
| base::RunLoop run_loop2; |
| EXPECT_CALL(*mock_observer(), OnAcInserted()).WillOnce(Invoke([&]() { |
| run_loop2.Quit(); |
| })); |
| |
| power_supply.set_external_power(power_manager::PowerSupplyProperties::AC); |
| fake_adapter()->EmitPowerSupplyPollSignal(power_supply); |
| |
| run_loop2.Run(); |
| } |
| |
| // Test that PowerEvents unsubscribes to PowerdAdapter when PowerEvents loses |
| // all of its mojo observers. |
| TEST_F(PowerEventsImplTest, UnsubscribeFromPowerdAdapterWhenAllObserversLost) { |
| DestroyMojoObserver(); |
| |
| // Emit an event, so that PowerEventsImpl has a chance to check for any |
| // remaining mojo observers. |
| power_manager::SuspendDone suspend_done; |
| fake_adapter()->EmitSuspendDoneSignal(suspend_done); |
| |
| EXPECT_FALSE(fake_adapter()->HasPowerObserver(power_events_impl())); |
| } |
| |
| } // namespace diagnostics |