| // Copyright 2024 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "libpmt/pmt.h" |
| |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include <base/files/file_path.h> |
| #include <base/files/file_util.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| namespace pmt { |
| namespace { |
| |
| using testing::Return; |
| |
| static constexpr Guid kId = 0x130671b2; |
| static constexpr Guid kId1 = 0x130670b2; |
| static constexpr Guid kId2 = 0x1a067102; |
| static constexpr Guid kId3 = 0x1a067002; |
| static constexpr char kTelemDataDir[] = "testdata/pmt.xml"; |
| static constexpr char kTelemDataPath[] = "testdata/test_telem_data"; |
| static constexpr size_t kTelemDataPathSize = 3352; |
| |
| class DataInterfaceMock : public PmtDataInterface { |
| public: |
| MOCK_METHOD(std::vector<Guid>, DetectDevices, ()); |
| MOCK_CONST_METHOD0(GetMetadataMappingsFile, base::FilePath()); |
| MOCK_CONST_METHOD1(IsValid, bool(Guid guid)); |
| MOCK_CONST_METHOD1(GetTelemetryFile, |
| const std::optional<base::FilePath>(Guid guid)); |
| MOCK_CONST_METHOD1(GetTelemetrySize, const size_t(Guid guid)); |
| }; |
| |
| class pmtTest : public ::testing::Test { |
| protected: |
| void SetUp() override { |
| // Will be deleted by the unique_ptr. |
| data_mock_ = new DataInterfaceMock(); |
| dec_data_mock_ = new DataInterfaceMock(); |
| pmt_ = std::make_unique<PmtCollector>( |
| std::unique_ptr<PmtDataInterface>(data_mock_)); |
| pmt_dec_ = std::make_unique<PmtDecoder>( |
| std::unique_ptr<PmtDataInterface>(dec_data_mock_)); |
| } |
| |
| DataInterfaceMock* data_mock_; |
| DataInterfaceMock* dec_data_mock_; |
| std::unique_ptr<PmtCollector> pmt_; |
| std::unique_ptr<PmtDecoder> pmt_dec_; |
| }; |
| |
| TEST_F(pmtTest, GuidDetection) { |
| std::vector<Guid> good_id = {kId}; |
| EXPECT_CALL(*data_mock_, DetectDevices).WillOnce(Return(good_id)); |
| auto result = pmt_->DetectDevices(); |
| ASSERT_EQ(result.size(), 1); |
| ASSERT_EQ(result[0], kId); |
| |
| std::vector<Guid> no_id = {}; |
| EXPECT_CALL(*data_mock_, DetectDevices).WillOnce(Return(no_id)); |
| result = pmt_->DetectDevices(); |
| ASSERT_EQ(result.size(), 0); |
| } |
| |
| TEST_F(pmtTest, SetupCollectionWithNoGuids) { |
| std::vector<Guid> guids = {}; |
| auto result = pmt_->SetUpCollection(guids); |
| |
| ASSERT_EQ(result, -EINVAL); |
| } |
| |
| TEST_F(pmtTest, SetupCollectionWithInvalidGuid) { |
| std::vector<Guid> guids = {kId, kId2}; |
| // Simulate that the second ID is invalid. |
| EXPECT_CALL(*data_mock_, IsValid(kId)).WillOnce(Return(true)); |
| EXPECT_CALL(*data_mock_, IsValid(kId2)).WillOnce(Return(false)); |
| auto result = pmt_->SetUpCollection(guids); |
| |
| ASSERT_EQ(result, -EINVAL); |
| } |
| |
| TEST_F(pmtTest, SetupCollectionWithMissingTelemetryFile) { |
| std::vector<Guid> guids = {kId, kId2}; |
| const int data_size = 100; |
| std::optional<base::FilePath> no_telem_file; |
| |
| EXPECT_CALL(*data_mock_, IsValid(kId)).WillOnce(Return(true)); |
| EXPECT_CALL(*data_mock_, IsValid(kId2)).WillOnce(Return(true)); |
| EXPECT_CALL(*data_mock_, GetTelemetrySize).WillOnce(Return(data_size)); |
| EXPECT_CALL(*data_mock_, GetTelemetryFile).WillOnce(Return(no_telem_file)); |
| auto result = pmt_->SetUpCollection(guids); |
| |
| ASSERT_EQ(result, -EBADF); |
| } |
| |
| TEST_F(pmtTest, SetupCollectionWithInvalidTelemetryFile) { |
| std::vector<Guid> guids = {kId, kId2}; |
| const int data_size = 100; |
| base::FilePath telem_data_path("invalid_file"); |
| |
| EXPECT_CALL(*data_mock_, IsValid(kId)).WillOnce(Return(true)); |
| EXPECT_CALL(*data_mock_, IsValid(kId2)).WillOnce(Return(true)); |
| EXPECT_CALL(*data_mock_, GetTelemetrySize).WillOnce(Return(data_size)); |
| EXPECT_CALL(*data_mock_, GetTelemetryFile).WillOnce(Return(telem_data_path)); |
| auto result = pmt_->SetUpCollection(guids); |
| |
| ASSERT_EQ(result, -EBADF); |
| } |
| |
| TEST_F(pmtTest, SetupCollection) { |
| std::vector<Guid> guids = {kId, kId2}; |
| const int data_size_id = 100, data_size_id2 = 200; |
| base::FilePath telem_data_path(kTelemDataPath); |
| EXPECT_CALL(*data_mock_, IsValid(kId)).WillOnce(Return(true)); |
| EXPECT_CALL(*data_mock_, IsValid(kId2)).WillOnce(Return(true)); |
| EXPECT_CALL(*data_mock_, GetTelemetrySize(kId)) |
| .WillOnce(Return(data_size_id)); |
| EXPECT_CALL(*data_mock_, GetTelemetrySize(kId2)) |
| .WillOnce(Return(data_size_id2)); |
| EXPECT_CALL(*data_mock_, GetTelemetryFile(kId)) |
| .WillOnce(Return(telem_data_path)); |
| EXPECT_CALL(*data_mock_, GetTelemetryFile(kId2)) |
| .WillOnce(Return(telem_data_path)); |
| auto result = pmt_->SetUpCollection(guids); |
| |
| ASSERT_EQ(result, 0); |
| ASSERT_EQ(pmt_->GetData()->devices_size(), 2); |
| ASSERT_EQ(pmt_->GetData()->devices(0).guid(), kId); |
| ASSERT_EQ(pmt_->GetData()->devices(0).data().size(), data_size_id); |
| ASSERT_EQ(pmt_->GetData()->devices(1).guid(), kId2); |
| ASSERT_EQ(pmt_->GetData()->devices(1).data().size(), data_size_id2); |
| |
| // Second call should fail because we're set up already. |
| result = pmt_->SetUpCollection(guids); |
| ASSERT_EQ(result, -EBUSY); |
| } |
| |
| TEST_F(pmtTest, CleanUpCollection) { |
| // If nothing is setup clean should fail. |
| auto result = pmt_->CleanUpCollection(); |
| ASSERT_EQ(result, -ENOENT); |
| ASSERT_EQ(pmt_->GetData(), nullptr); |
| |
| // First set up the collection. |
| std::vector<Guid> guids = {kId, kId2}; |
| const int data_size_id = 100, data_size_id2 = 200; |
| base::FilePath telem_data_path(kTelemDataPath); |
| EXPECT_CALL(*data_mock_, IsValid(kId)).WillOnce(Return(true)); |
| EXPECT_CALL(*data_mock_, IsValid(kId2)).WillOnce(Return(true)); |
| EXPECT_CALL(*data_mock_, GetTelemetrySize(kId)) |
| .WillOnce(Return(data_size_id)); |
| EXPECT_CALL(*data_mock_, GetTelemetrySize(kId2)) |
| .WillOnce(Return(data_size_id2)); |
| EXPECT_CALL(*data_mock_, GetTelemetryFile(kId)) |
| .WillOnce(Return(telem_data_path)); |
| EXPECT_CALL(*data_mock_, GetTelemetryFile(kId2)) |
| .WillOnce(Return(telem_data_path)); |
| result = pmt_->SetUpCollection(guids); |
| |
| // Check if the setup is correct. |
| ASSERT_EQ(result, 0); |
| ASSERT_NE(pmt_->GetData(), nullptr); |
| ASSERT_EQ(pmt_->GetData()->devices_size(), 2); |
| ASSERT_EQ(pmt_->GetData()->devices(0).guid(), kId); |
| ASSERT_EQ(pmt_->GetData()->devices(0).data().size(), data_size_id); |
| ASSERT_EQ(pmt_->GetData()->devices(1).guid(), kId2); |
| ASSERT_EQ(pmt_->GetData()->devices(1).data().size(), data_size_id2); |
| // Now clean up. |
| result = pmt_->CleanUpCollection(); |
| ASSERT_EQ(pmt_->GetData(), nullptr); |
| ASSERT_EQ(result, 0); |
| |
| // Now set it up again, it should work. |
| // First set up the collection. |
| EXPECT_CALL(*data_mock_, IsValid(kId)).WillOnce(Return(true)); |
| EXPECT_CALL(*data_mock_, IsValid(kId2)).WillOnce(Return(true)); |
| EXPECT_CALL(*data_mock_, GetTelemetrySize(kId)) |
| .WillOnce(Return(data_size_id)); |
| EXPECT_CALL(*data_mock_, GetTelemetrySize(kId2)) |
| .WillOnce(Return(data_size_id2)); |
| EXPECT_CALL(*data_mock_, GetTelemetryFile(kId)) |
| .WillOnce(Return(telem_data_path)); |
| EXPECT_CALL(*data_mock_, GetTelemetryFile(kId2)) |
| .WillOnce(Return(telem_data_path)); |
| result = pmt_->SetUpCollection(guids); |
| |
| // Check if the setup is correct. |
| ASSERT_EQ(result, 0); |
| ASSERT_EQ(pmt_->GetData()->devices_size(), 2); |
| ASSERT_EQ(pmt_->GetData()->devices(0).guid(), kId); |
| ASSERT_EQ(pmt_->GetData()->devices(0).data().size(), data_size_id); |
| ASSERT_EQ(pmt_->GetData()->devices(1).guid(), kId2); |
| ASSERT_EQ(pmt_->GetData()->devices(1).data().size(), data_size_id2); |
| } |
| |
| TEST_F(pmtTest, CollectionSetupIsSortedByGuid) { |
| std::vector<Guid> guids = {kId, kId1, kId2, kId3}; |
| const int data_size = 100; |
| base::FilePath telem_data_path(kTelemDataPath); |
| EXPECT_CALL(*data_mock_, IsValid(kId)).WillOnce(Return(true)); |
| EXPECT_CALL(*data_mock_, IsValid(kId1)).WillOnce(Return(true)); |
| EXPECT_CALL(*data_mock_, IsValid(kId2)).WillOnce(Return(true)); |
| EXPECT_CALL(*data_mock_, IsValid(kId3)).WillOnce(Return(true)); |
| EXPECT_CALL(*data_mock_, GetTelemetrySize).WillRepeatedly(Return(data_size)); |
| EXPECT_CALL(*data_mock_, GetTelemetryFile) |
| .WillRepeatedly(Return(telem_data_path)); |
| auto result = pmt_->SetUpCollection(guids); |
| |
| ASSERT_EQ(result, 0); |
| ASSERT_EQ(pmt_->GetData()->devices_size(), 4); |
| ASSERT_EQ(pmt_->GetData()->devices(0).guid(), kId1); |
| ASSERT_EQ(pmt_->GetData()->devices(1).guid(), kId); |
| ASSERT_EQ(pmt_->GetData()->devices(2).guid(), kId3); |
| ASSERT_EQ(pmt_->GetData()->devices(3).guid(), kId2); |
| } |
| |
| TEST_F(pmtTest, TakeSnapshot) { |
| std::vector<Guid> guids = {kId}; |
| const int data_size = kTelemDataPathSize; |
| base::FilePath telem_data_path(kTelemDataPath); |
| EXPECT_CALL(*data_mock_, IsValid(kId)).WillOnce(Return(true)); |
| EXPECT_CALL(*data_mock_, GetTelemetrySize(kId)).WillOnce(Return(data_size)); |
| EXPECT_CALL(*data_mock_, GetTelemetryFile).WillOnce(Return(telem_data_path)); |
| |
| auto result = pmt_->SetUpCollection(guids); |
| ASSERT_EQ(result, 0); |
| |
| auto res = pmt_->TakeSnapshot(); |
| ASSERT_EQ(res, 0); |
| |
| ASSERT_EQ(pmt_->GetData()->devices_size(), 1); |
| ASSERT_EQ(pmt_->GetData()->devices(0).guid(), kId); |
| ASSERT_EQ(pmt_->GetData()->devices(0).data().size(), data_size); |
| |
| // Verify that the contents are the same. |
| std::string expected_telem_data; |
| ASSERT_TRUE(base::ReadFileToString(telem_data_path, &expected_telem_data)); |
| ASSERT_EQ(pmt_->GetData()->devices(0).data(), expected_telem_data); |
| } |
| |
| TEST_F(pmtTest, TakeSnapshotHandleEOF) { |
| std::vector<Guid> guids = {kId}; |
| const int data_size = kTelemDataPathSize + 1; |
| base::FilePath telem_data_path(kTelemDataPath); |
| EXPECT_CALL(*data_mock_, IsValid).WillOnce(Return(true)); |
| EXPECT_CALL(*data_mock_, GetTelemetrySize).WillOnce(Return(data_size)); |
| EXPECT_CALL(*data_mock_, GetTelemetryFile).WillOnce(Return(telem_data_path)); |
| auto result = pmt_->SetUpCollection(guids); |
| ASSERT_EQ(result, 0); |
| |
| result = pmt_->TakeSnapshot(); |
| ASSERT_EQ(result, -EIO); |
| } |
| |
| TEST_F(pmtTest, TakeSnapshotHandleBadFilePath) { |
| std::vector<Guid> guids = {kId}; |
| const int data_size = kTelemDataPathSize; |
| base::FilePath telem_data_path("bad/path"); |
| EXPECT_CALL(*data_mock_, IsValid).WillOnce(Return(true)); |
| EXPECT_CALL(*data_mock_, GetTelemetrySize).WillOnce(Return(data_size)); |
| EXPECT_CALL(*data_mock_, GetTelemetryFile).WillOnce(Return(telem_data_path)); |
| auto result = pmt_->SetUpCollection(guids); |
| ASSERT_EQ(result, -EBADF); |
| } |
| |
| TEST_F(pmtTest, HandleTakesnapshotBeforeSetup) { |
| auto result = pmt_->TakeSnapshot(); |
| ASSERT_EQ(result, -EPERM); |
| } |
| |
| TEST_F(pmtTest, DecodingDetectMetadata) { |
| base::FilePath pmt_xml_path(kTelemDataDir); |
| EXPECT_CALL(*dec_data_mock_, GetMetadataMappingsFile()) |
| .WillOnce(Return(pmt_xml_path)); |
| auto result = pmt_dec_->DetectMetadata(); |
| ASSERT_EQ(result.size(), 1); |
| ASSERT_EQ(result[0], 0x130670b2); |
| } |
| |
| TEST_F(pmtTest, DecodingDetectMetadataBadFilePath) { |
| base::FilePath pmt_xml_path("bad/path"); |
| EXPECT_CALL(*dec_data_mock_, GetMetadataMappingsFile()) |
| .WillOnce(Return(pmt_xml_path)); |
| auto result = pmt_dec_->DetectMetadata(); |
| ASSERT_EQ(result.size(), 0); |
| } |
| |
| TEST_F(pmtTest, DecodingSetupMetadata) { |
| base::FilePath pmt_xml_path(kTelemDataDir); |
| EXPECT_CALL(*dec_data_mock_, GetMetadataMappingsFile()) |
| .WillRepeatedly(Return(pmt_xml_path)); |
| auto result = pmt_dec_->DetectMetadata(); |
| ASSERT_EQ(result.size(), 1); |
| ASSERT_EQ(result[0], 0x130670b2); |
| auto setup_done = pmt_dec_->SetUpDecoding({0x130670b2}); |
| ASSERT_EQ(setup_done, 0); |
| |
| setup_done = pmt_dec_->SetUpDecoding({0x130670b2}); |
| ASSERT_EQ(setup_done, -EBUSY); |
| } |
| |
| TEST_F(pmtTest, DecodingSetupMetadataUnsupportedGuid) { |
| base::FilePath pmt_xml_path(kTelemDataDir); |
| EXPECT_CALL(*dec_data_mock_, GetMetadataMappingsFile()) |
| .WillRepeatedly(Return(pmt_xml_path)); |
| auto result = pmt_dec_->DetectMetadata(); |
| ASSERT_EQ(result.size(), 1); |
| ASSERT_EQ(result[0], 0x130670b2); |
| auto setup_done = pmt_dec_->SetUpDecoding({0xcafebabe}); |
| ASSERT_EQ(setup_done, -EINVAL); |
| |
| // Next setup with a proper GUID should succeed. |
| setup_done = pmt_dec_->SetUpDecoding({0x130670b2}); |
| ASSERT_EQ(setup_done, 0); |
| } |
| |
| TEST_F(pmtTest, DecodingCleanup) { |
| base::FilePath pmt_xml_path(kTelemDataDir); |
| EXPECT_CALL(*dec_data_mock_, GetMetadataMappingsFile()) |
| .WillRepeatedly(Return(pmt_xml_path)); |
| |
| auto setup_done = pmt_dec_->CleanUpDecoding(); |
| ASSERT_EQ(setup_done, -ENOENT); |
| |
| auto result = pmt_dec_->DetectMetadata(); |
| ASSERT_EQ(result.size(), 1); |
| ASSERT_EQ(result[0], 0x130670b2); |
| |
| setup_done = pmt_dec_->CleanUpDecoding(); |
| ASSERT_EQ(setup_done, -ENOENT); |
| |
| setup_done = pmt_dec_->SetUpDecoding({0x130670b2}); |
| ASSERT_EQ(setup_done, 0); |
| |
| setup_done = pmt_dec_->CleanUpDecoding(); |
| ASSERT_EQ(setup_done, 0); |
| } |
| |
| } // namespace |
| } // namespace pmt |