| // Copyright 2018 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 <fcntl.h> |
| #include <poll.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include <cstdint> |
| #include <tuple> |
| |
| #include <base/barrier_closure.h> |
| #include <base/bind.h> |
| #include <base/files/file_util.h> |
| #include <base/files/scoped_file.h> |
| #include <base/files/scoped_temp_dir.h> |
| #include <base/run_loop.h> |
| #include <base/test/task_environment.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "diagnostics/wilco_dtc_supportd/ec_constants.h" |
| #include "diagnostics/wilco_dtc_supportd/telemetry/ec_service.h" |
| #include "diagnostics/wilco_dtc_supportd/telemetry/ec_service_test_utils.h" |
| #include "mojo/wilco_dtc_supportd.mojom.h" |
| |
| namespace diagnostics { |
| |
| namespace { |
| |
| using testing::_; |
| using testing::Invoke; |
| using testing::StrictMock; |
| |
| using EcEvent = EcService::EcEvent; |
| using EcEventReason = EcService::EcEvent::Reason; |
| using MojoEvent = chromeos::wilco_dtc_supportd::mojom::WilcoDtcSupportdEvent; |
| |
| // Tests for EcEvent. |
| // |
| // This is a parametrized test with the following parameters: |
| // * |source_ec_event| - the ec event subject to test. |
| // * |expected_event_reason| - the expected reason of the EC event. |
| class EcEventTest |
| : public testing::Test, |
| public testing::WithParamInterface<std::tuple<EcEvent, EcEventReason>> { |
| protected: |
| const EcEvent& source_ec_event() const { return std::get<0>(GetParam()); } |
| |
| EcEventReason expected_event_reason() const { |
| return std::get<1>(GetParam()); |
| } |
| }; |
| |
| // Tests that |EcEvent::GetReason| correctly extracts reason from the EC event. |
| TEST_P(EcEventTest, GetReason) { |
| EXPECT_EQ(source_ec_event().GetReason(), expected_event_reason()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| _, |
| EcEventTest, |
| testing::Values( |
| std::make_tuple(kEcEventNonWilcoCharger, |
| EcEventReason::kNonWilcoCharger), |
| std::make_tuple(kEcEventBatteryAuth, EcEventReason::kBatteryAuth), |
| std::make_tuple(kEcEventDockDisplay, EcEventReason::kDockDisplay), |
| std::make_tuple(kEcEventDockThunderbolt, |
| EcEventReason::kDockThunderbolt), |
| std::make_tuple(kEcEventIncompatibleDock, |
| EcEventReason::kIncompatibleDock), |
| std::make_tuple(kEcEventDockError, EcEventReason::kDockError), |
| std::make_tuple(kEcEventNonSysNotification, |
| EcEventReason::kNonSysNotification), |
| std::make_tuple(kEcEventAcAdapterNoFlags, |
| EcEventReason::kSysNotification), |
| std::make_tuple(kEcEventChargerNoFlags, |
| EcEventReason::kSysNotification), |
| std::make_tuple(kEcEventUsbCNoFlags, EcEventReason::kSysNotification), |
| std::make_tuple(kEcEventNonWilcoChargerBadSubType, |
| EcEventReason::kSysNotification))); |
| |
| class MockEcServiceObserver : public EcService::Observer { |
| public: |
| MOCK_METHOD(void, OnEcEvent, (const EcEvent&), (override)); |
| }; |
| |
| class EcServiceTest : public testing::Test { |
| protected: |
| EcServiceTest() = default; |
| |
| void SetUp() override { |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| service()->set_root_dir_for_testing(temp_dir_.GetPath()); |
| service()->set_event_fd_events_for_testing(POLLIN); |
| service()->AddObserver(&observer_); |
| EXPECT_TRUE(service()->HasObserver(&observer_)); |
| } |
| |
| void TearDown() override { |
| service()->RemoveObserver(&observer_); |
| EXPECT_FALSE(service()->HasObserver(&observer_)); |
| base::RunLoop run_loop; |
| service_.ShutDown(run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| |
| void CreateEcEventFile() { |
| base::FilePath file_path = ec_event_file_path(); |
| ASSERT_TRUE(base::CreateDirectory(file_path.DirName())); |
| ASSERT_EQ(mkfifo(file_path.value().c_str(), 0600), 0); |
| } |
| |
| base::FilePath ec_event_file_path() { |
| return temp_dir_.GetPath().Append(kEcEventFilePath); |
| } |
| |
| // Must be open only after |service_.Start()| call. Otherwise, it will |
| // block thread. |
| void InitFifoWriteEnd() { |
| ASSERT_EQ(fifo_write_end_.get(), -1); |
| fifo_write_end_.reset(open(ec_event_file_path().value().c_str(), O_WRONLY)); |
| ASSERT_NE(fifo_write_end_.get(), -1); |
| } |
| |
| void EmitEcEventAndSetObserverExpectations( |
| const EcEvent& ec_event, const base::RepeatingClosure& callback) { |
| ASSERT_EQ(write(fifo_write_end_.get(), &ec_event, sizeof(ec_event)), |
| sizeof(ec_event)); |
| |
| EXPECT_CALL(observer_, OnEcEvent(ec_event)) |
| .WillOnce( |
| Invoke([callback](const EcEvent& ec_event) { callback.Run(); })); |
| } |
| |
| EcService* service() { return &service_; } |
| |
| private: |
| base::test::TaskEnvironment task_environment_{ |
| base::test::TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY}; |
| StrictMock<MockEcServiceObserver> observer_; |
| EcService service_; |
| |
| base::ScopedTempDir temp_dir_; |
| |
| base::ScopedFD fifo_write_end_; |
| }; |
| |
| TEST_F(EcServiceTest, Start) { |
| CreateEcEventFile(); |
| ASSERT_TRUE(service()->Start()); |
| } |
| |
| TEST_F(EcServiceTest, StartFailure) { |
| ASSERT_FALSE(service()->Start()); |
| } |
| |
| // Tests for the EcService class that started successfully. |
| class StartedEcServiceTest : public EcServiceTest { |
| protected: |
| void SetUp() override { |
| ASSERT_NO_FATAL_FAILURE(EcServiceTest::SetUp()); |
| CreateEcEventFile(); |
| ASSERT_TRUE(service()->Start()); |
| InitFifoWriteEnd(); |
| } |
| }; |
| |
| TEST_F(StartedEcServiceTest, ReadEvent) { |
| base::RunLoop run_loop; |
| const uint16_t data[] = {0xaaaa, 0xbbbb, 0xcccc, 0xdddd, 0xeeee, 0xffff}; |
| EmitEcEventAndSetObserverExpectations( |
| EcEvent(0x8888, static_cast<EcEvent::Type>(0x9999), data), |
| run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| |
| TEST_F(StartedEcServiceTest, ReadManyEvent) { |
| base::RunLoop run_loop; |
| base::RepeatingClosure callback = base::BarrierClosure( |
| 2 /* num_closures */, run_loop.QuitClosure() /* done closure */); |
| const uint16_t data1[] = {0xaaaa, 0xbbbb, 0xcccc, 0xdddd, 0xeeee, 0xffff}; |
| EmitEcEventAndSetObserverExpectations( |
| EcEvent(0x8888, static_cast<EcEvent::Type>(0x9999), data1), callback); |
| const uint16_t data2[] = {0x0000, 0x1111, 0x2222, 0x3333, 0x4444, 0x5555}; |
| EmitEcEventAndSetObserverExpectations( |
| EcEvent(0x6666, static_cast<EcEvent::Type>(0x7777), data2), callback); |
| run_loop.Run(); |
| } |
| |
| } // namespace |
| |
| } // namespace diagnostics |