| // Copyright 2020 The Fuchsia 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 "src/developer/forensics/feedback_data/main_service.h" |
| |
| #include <fuchsia/feedback/cpp/fidl.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <lib/syslog/logger.h> |
| |
| #include <memory> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "src/developer/forensics/feedback_data/constants.h" |
| #include "src/developer/forensics/feedback_data/system_log_recorder/encoding/production_encoding.h" |
| #include "src/developer/forensics/feedback_data/system_log_recorder/encoding/version.h" |
| #include "src/developer/forensics/feedback_data/system_log_recorder/reader.h" |
| #include "src/developer/forensics/testing/log_message.h" |
| #include "src/developer/forensics/testing/stubs/cobalt_logger_factory.h" |
| #include "src/developer/forensics/testing/unit_test_fixture.h" |
| #include "src/developer/forensics/utils/log_format.h" |
| #include "src/lib/files/directory.h" |
| #include "src/lib/files/file.h" |
| #include "src/lib/files/path.h" |
| |
| namespace forensics { |
| namespace feedback_data { |
| namespace { |
| |
| using fuchsia::feedback::ComponentDataRegisterSyncPtr; |
| using fuchsia::feedback::DataProviderControllerSyncPtr; |
| using fuchsia::feedback::DataProviderSyncPtr; |
| using fuchsia::feedback::DeviceIdProviderSyncPtr; |
| using inspect::testing::ChildrenMatch; |
| using inspect::testing::NameMatches; |
| using inspect::testing::NodeMatches; |
| using inspect::testing::PropertyList; |
| using inspect::testing::StringIs; |
| using inspect::testing::UintIs; |
| using testing::BuildLogMessage; |
| using ::testing::Contains; |
| using ::testing::IsEmpty; |
| using ::testing::UnorderedElementsAreArray; |
| |
| std::string MakeFilepath(const std::string& dir, const size_t file_num) { |
| return files::JoinPath(dir, std::to_string(file_num)); |
| } |
| |
| const std::vector<std::string> kCurrentLogFilePaths = { |
| MakeFilepath(kCurrentLogsDir, 0), MakeFilepath(kCurrentLogsDir, 1), |
| MakeFilepath(kCurrentLogsDir, 2), MakeFilepath(kCurrentLogsDir, 3), |
| MakeFilepath(kCurrentLogsDir, 4), MakeFilepath(kCurrentLogsDir, 5), |
| MakeFilepath(kCurrentLogsDir, 6), MakeFilepath(kCurrentLogsDir, 7), |
| }; |
| |
| class MainServiceTest : public UnitTestFixture { |
| public: |
| void SetUp() { |
| FX_CHECK(files::CreateDirectory(kCurrentLogsDir)); |
| SetUpCobaltServer(std::make_unique<stubs::CobaltLoggerFactory>()); |
| RunLoopUntilIdle(); |
| } |
| |
| void TearDown() override { |
| FX_CHECK(files::DeletePath(kPreviousLogsFilePath, /*recursive=*/true)); |
| FX_CHECK(files::DeletePath(kCurrentLogsDir, /*recursive=*/true)); |
| FX_CHECK(files::DeletePath(files::JoinPath("/data/", kBootIdFileName), /*recursive=*/true)); |
| FX_CHECK(files::DeletePath(files::JoinPath("/tmp/", kBootIdFileName), /*recursive=*/true)); |
| } |
| |
| protected: |
| void CreateMainService(const bool is_first_instance) { |
| main_service_ = |
| MainService::TryCreate(dispatcher(), services(), &InspectRoot(), is_first_instance); |
| } |
| |
| void WriteFile(const std::string& filepath, const std::string& content) { |
| FX_CHECK(files::WriteFile(filepath, content.c_str(), content.size())); |
| } |
| |
| std::string ReadFile(const std::string& filepath) { |
| std::string content; |
| FX_CHECK(files::ReadFileToString(filepath, &content)); |
| return content; |
| } |
| |
| std::unique_ptr<MainService> main_service_; |
| }; |
| |
| MATCHER_P2(MatchesCobaltEvent, expected_type, expected_metric_id, "") { |
| return arg.type == expected_type && arg.metric_id == expected_metric_id; |
| } |
| |
| TEST_F(MainServiceTest, MovesPreviousBootLogs) { |
| std::string previous_log_contents = ""; |
| for (const auto& filepath : kCurrentLogFilePaths) { |
| auto encoder = system_log_recorder::ProductionEncoder(); |
| const std::string str = Format(BuildLogMessage(FX_LOG_INFO, "Log for file: " + filepath)); |
| previous_log_contents = previous_log_contents + str; |
| WriteFile(filepath, encoder.Encode(str)); |
| } |
| |
| CreateMainService(/*is_first_instance=*/true); |
| RunLoopUntilIdle(); |
| |
| EXPECT_FALSE(files::IsDirectory(kCurrentLogsDir)); |
| EXPECT_EQ(previous_log_contents, ReadFile(kPreviousLogsFilePath)); |
| |
| // Verify the event type and metric_id. |
| EXPECT_THAT(ReceivedCobaltEvents(), |
| UnorderedElementsAreArray({ |
| MatchesCobaltEvent(cobalt::EventType::kCount, |
| cobalt_registry::kPreviousBootLogCompressionRatioMetricId), |
| })); |
| } |
| |
| TEST_F(MainServiceTest, NoMovesPreviousBootLogsAfterFirstInstance) { |
| std::string previous_log_contents = ""; |
| for (const auto& filepath : kCurrentLogFilePaths) { |
| auto encoder = system_log_recorder::ProductionEncoder(); |
| const std::string str = Format(BuildLogMessage(FX_LOG_INFO, "Log for file: " + filepath)); |
| previous_log_contents = previous_log_contents + str; |
| WriteFile(filepath, encoder.Encode(str)); |
| } |
| |
| CreateMainService(/*is_first_instance=*/false); |
| RunLoopUntilIdle(); |
| |
| EXPECT_TRUE(files::IsDirectory(kCurrentLogsDir)); |
| // We check that nothing has been moved to /tmp. |
| EXPECT_FALSE(files::IsFile(kPreviousLogsFilePath)); |
| |
| // We check that the content of /cache is still the same. |
| for (const auto& filepath : kCurrentLogFilePaths) { |
| auto encoder = system_log_recorder::ProductionEncoder(); |
| const std::string str = Format(BuildLogMessage(FX_LOG_INFO, "Log for file: " + filepath)); |
| EXPECT_EQ(encoder.Encode(str), ReadFile(filepath)); |
| } |
| |
| // Verify no event was sent to cobalt. |
| EXPECT_THAT(ReceivedCobaltEvents(), IsEmpty()); |
| } |
| |
| TEST_F(MainServiceTest, MovesPreviousBootIdAndCreatesCurrentBootId) { |
| const std::string previous_boot_id = "previous_boot_id"; |
| WriteFile(files::JoinPath("/data/", kBootIdFileName), previous_boot_id); |
| |
| CreateMainService(/*is_first_instance=*/true); |
| |
| EXPECT_EQ(ReadFile(files::JoinPath("/tmp/", kBootIdFileName)), previous_boot_id); |
| EXPECT_THAT(ReadFile(files::JoinPath("/data/", kBootIdFileName)), Not(IsEmpty())); |
| EXPECT_NE(ReadFile(files::JoinPath("/data/", kBootIdFileName)), previous_boot_id); |
| } |
| |
| TEST_F(MainServiceTest, NoMovesPreviousIdAfterFirstInstance) { |
| const std::string previous_boot_id = "previous_boot_id"; |
| WriteFile(files::JoinPath("/data/", kBootIdFileName), previous_boot_id); |
| |
| CreateMainService(/*is_first_instance=*/false); |
| |
| EXPECT_EQ(ReadFile(files::JoinPath("/data/", kBootIdFileName)), previous_boot_id); |
| } |
| |
| TEST_F(MainServiceTest, CheckInspect) { |
| CreateMainService(/*is_first_instance=*/true); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(UnorderedElementsAreArray({ |
| AllOf(NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(UnorderedElementsAreArray({ |
| NodeMatches(AllOf(NameMatches("fuchsia.feedback.ComponentDataRegister"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 0u), |
| UintIs("current_num_connections", 0u), |
| })))), |
| NodeMatches(AllOf(NameMatches("fuchsia.feedback.DataProvider"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 0u), |
| UintIs("current_num_connections", 0u), |
| })))), |
| NodeMatches(AllOf(NameMatches("fuchsia.feedback.DataProviderController"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 0u), |
| UintIs("current_num_connections", 0u), |
| })))), |
| NodeMatches(AllOf(NameMatches("fuchsia.feedback.DeviceIdProvider"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 0u), |
| UintIs("current_num_connections", 0u), |
| })))), |
| }))), |
| }))); |
| } |
| |
| TEST_F(MainServiceTest, ComponentDataRegister_CheckInspect) { |
| CreateMainService(/*is_first_instance=*/true); |
| ComponentDataRegisterSyncPtr data_register_1; |
| main_service_->HandleComponentDataRegisterRequest(data_register_1.NewRequest()); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf(NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches( |
| AllOf(NameMatches("fuchsia.feedback.ComponentDataRegister"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 1u), |
| UintIs("current_num_connections", 1u), |
| })))))))))); |
| |
| ComponentDataRegisterSyncPtr data_register_2; |
| main_service_->HandleComponentDataRegisterRequest(data_register_2.NewRequest()); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf(NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches( |
| AllOf(NameMatches("fuchsia.feedback.ComponentDataRegister"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 2u), |
| UintIs("current_num_connections", 2u), |
| })))))))))); |
| |
| data_register_1.Unbind(); |
| RunLoopUntilIdle(); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf(NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches( |
| AllOf(NameMatches("fuchsia.feedback.ComponentDataRegister"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 2u), |
| UintIs("current_num_connections", 1u), |
| })))))))))); |
| |
| ComponentDataRegisterSyncPtr data_register_3; |
| main_service_->HandleComponentDataRegisterRequest(data_register_3.NewRequest()); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf(NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches( |
| AllOf(NameMatches("fuchsia.feedback.ComponentDataRegister"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 3u), |
| UintIs("current_num_connections", 2u), |
| })))))))))); |
| |
| data_register_2.Unbind(); |
| data_register_3.Unbind(); |
| RunLoopUntilIdle(); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf(NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches( |
| AllOf(NameMatches("fuchsia.feedback.ComponentDataRegister"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 3u), |
| UintIs("current_num_connections", 0u), |
| })))))))))); |
| } |
| |
| TEST_F(MainServiceTest, DataProvider_CheckInspect) { |
| CreateMainService(/*is_first_instance=*/true); |
| DataProviderSyncPtr data_provider_1; |
| main_service_->HandleDataProviderRequest(data_provider_1.NewRequest()); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf( |
| NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches(AllOf(NameMatches("fuchsia.feedback.DataProvider"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 1u), |
| UintIs("current_num_connections", 1u), |
| })))))))))); |
| |
| DataProviderSyncPtr data_provider_2; |
| main_service_->HandleDataProviderRequest(data_provider_2.NewRequest()); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf( |
| NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches(AllOf(NameMatches("fuchsia.feedback.DataProvider"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 2u), |
| UintIs("current_num_connections", 2u), |
| })))))))))); |
| |
| data_provider_1.Unbind(); |
| RunLoopUntilIdle(); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf( |
| NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches(AllOf(NameMatches("fuchsia.feedback.DataProvider"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 2u), |
| UintIs("current_num_connections", 1u), |
| })))))))))); |
| |
| DataProviderSyncPtr data_provider_3; |
| main_service_->HandleDataProviderRequest(data_provider_3.NewRequest()); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf( |
| NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches(AllOf(NameMatches("fuchsia.feedback.DataProvider"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 3u), |
| UintIs("current_num_connections", 2u), |
| })))))))))); |
| |
| data_provider_2.Unbind(); |
| data_provider_3.Unbind(); |
| RunLoopUntilIdle(); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf( |
| NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches(AllOf(NameMatches("fuchsia.feedback.DataProvider"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 3u), |
| UintIs("current_num_connections", 0u), |
| })))))))))); |
| } |
| |
| TEST_F(MainServiceTest, DataProviderController_CheckInspect) { |
| CreateMainService(/*is_first_instance=*/true); |
| DataProviderControllerSyncPtr data_provider_controller_1; |
| main_service_->HandleDataProviderControllerRequest(data_provider_controller_1.NewRequest()); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf(NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches( |
| AllOf(NameMatches("fuchsia.feedback.DataProviderController"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 1u), |
| UintIs("current_num_connections", 1u), |
| })))))))))); |
| |
| DataProviderControllerSyncPtr data_provider_controller_2; |
| main_service_->HandleDataProviderControllerRequest(data_provider_controller_2.NewRequest()); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf(NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches( |
| AllOf(NameMatches("fuchsia.feedback.DataProviderController"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 2u), |
| UintIs("current_num_connections", 2u), |
| })))))))))); |
| |
| data_provider_controller_1.Unbind(); |
| RunLoopUntilIdle(); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf(NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches( |
| AllOf(NameMatches("fuchsia.feedback.DataProviderController"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 2u), |
| UintIs("current_num_connections", 1u), |
| })))))))))); |
| |
| DataProviderControllerSyncPtr data_provider_controller_3; |
| main_service_->HandleDataProviderControllerRequest(data_provider_controller_3.NewRequest()); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf(NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches( |
| AllOf(NameMatches("fuchsia.feedback.DataProviderController"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 3u), |
| UintIs("current_num_connections", 2u), |
| })))))))))); |
| |
| data_provider_controller_2.Unbind(); |
| data_provider_controller_3.Unbind(); |
| RunLoopUntilIdle(); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf(NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches( |
| AllOf(NameMatches("fuchsia.feedback.DataProviderController"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 3u), |
| UintIs("current_num_connections", 0u), |
| })))))))))); |
| } |
| |
| TEST_F(MainServiceTest, DeviceIdProvider_CheckInspect) { |
| CreateMainService(/*is_first_instance=*/true); |
| DeviceIdProviderSyncPtr device_id_provider_1; |
| main_service_->HandleDeviceIdProviderRequest(device_id_provider_1.NewRequest()); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf( |
| NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches(AllOf(NameMatches("fuchsia.feedback.DeviceIdProvider"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 1u), |
| UintIs("current_num_connections", 1u), |
| })))))))))); |
| |
| DeviceIdProviderSyncPtr device_id_provider_2; |
| main_service_->HandleDeviceIdProviderRequest(device_id_provider_2.NewRequest()); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf( |
| NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches(AllOf(NameMatches("fuchsia.feedback.DeviceIdProvider"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 2u), |
| UintIs("current_num_connections", 2u), |
| })))))))))); |
| |
| device_id_provider_1.Unbind(); |
| RunLoopUntilIdle(); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf( |
| NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches(AllOf(NameMatches("fuchsia.feedback.DeviceIdProvider"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 2u), |
| UintIs("current_num_connections", 1u), |
| })))))))))); |
| |
| DeviceIdProviderSyncPtr device_id_provider_3; |
| main_service_->HandleDeviceIdProviderRequest(device_id_provider_3.NewRequest()); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf( |
| NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches(AllOf(NameMatches("fuchsia.feedback.DeviceIdProvider"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 3u), |
| UintIs("current_num_connections", 2u), |
| })))))))))); |
| |
| device_id_provider_2.Unbind(); |
| device_id_provider_3.Unbind(); |
| RunLoopUntilIdle(); |
| EXPECT_THAT( |
| InspectTree(), |
| ChildrenMatch(Contains(AllOf( |
| NodeMatches(NameMatches("fidl")), |
| ChildrenMatch(Contains(NodeMatches(AllOf(NameMatches("fuchsia.feedback.DeviceIdProvider"), |
| PropertyList(UnorderedElementsAreArray({ |
| UintIs("total_num_connections", 3u), |
| UintIs("current_num_connections", 0u), |
| })))))))))); |
| } |
| |
| } // namespace |
| } // namespace feedback_data |
| } // namespace forensics |