| // Copyright 2019 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 "diagnostics/wilco_dtc_supportd/telemetry/system_files_service_impl.h" |
| |
| #include <algorithm> |
| #include <iterator> |
| #include <memory> |
| #include <string> |
| #include <tuple> |
| |
| #include <base/files/file_path.h> |
| #include <base/files/scoped_temp_dir.h> |
| #include <base/macros.h> |
| #include <gtest/gtest.h> |
| #include <gmock/gmock.h> |
| |
| #include "diagnostics/common/file_test_utils.h" |
| #include "diagnostics/wilco_dtc_supportd/telemetry/system_files_service.h" |
| |
| using testing::AnyOf; |
| using testing::StrEq; |
| |
| namespace { |
| std::string FakeFileContents() { |
| constexpr char kFakeFileContents[] = "test1 \0 Œ test2/test3 \0 Ö"; |
| |
| return std::string(std::begin(kFakeFileContents), |
| std::end(kFakeFileContents)); |
| } |
| } // namespace |
| |
| namespace diagnostics { |
| |
| // Tests for SystemFilesService |
| class SystemFilesServiceTest : public testing::Test { |
| public: |
| SystemFilesServiceTest() = default; |
| ~SystemFilesServiceTest() override = default; |
| |
| void SetUp() override { |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| |
| system_files_service_.set_root_dir_for_testing(temp_dir_.GetPath()); |
| } |
| |
| protected: |
| base::ScopedTempDir temp_dir_; |
| SystemFilesServiceImpl system_files_service_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(SystemFilesServiceTest); |
| }; |
| |
| // Test that GetFileDump() returns false when the file doesn't exist. |
| TEST_F(SystemFilesServiceTest, NonExistingFile) { |
| auto file_dump = |
| system_files_service_.GetFileDump(SystemFilesService::File::kProcMeminfo); |
| EXPECT_FALSE(file_dump); |
| } |
| |
| // Test that GetFileDump() returns the requested file data when the file |
| // exists. |
| TEST_F(SystemFilesServiceTest, SimpleFile) { |
| const base::FilePath kTestPath("proc/meminfo"); |
| |
| base::FilePath abs_file = temp_dir_.GetPath().Append(kTestPath); |
| |
| ASSERT_TRUE(WriteFileAndCreateParentDirs(abs_file, FakeFileContents())); |
| |
| const auto file_dump = |
| system_files_service_.GetFileDump(SystemFilesService::File::kProcMeminfo); |
| |
| EXPECT_TRUE(file_dump); |
| EXPECT_EQ(file_dump.value().path, abs_file); |
| EXPECT_EQ(file_dump.value().canonical_path, abs_file); |
| EXPECT_EQ(file_dump.value().contents, FakeFileContents()); |
| } |
| |
| // Test that GetFileDump() returns the requested file data when the file |
| // exists and is a symlink. |
| TEST_F(SystemFilesServiceTest, Symlink) { |
| const base::FilePath kTestPath("proc/meminfo"); |
| const base::FilePath kTestLinkPath("proc/loadavg"); |
| |
| base::FilePath abs_file = temp_dir_.GetPath().Append(kTestPath); |
| base::FilePath abs_link = temp_dir_.GetPath().Append(kTestLinkPath); |
| |
| ASSERT_TRUE( |
| WriteFileAndCreateSymbolicLink(abs_file, FakeFileContents(), abs_link)); |
| |
| const auto file_dump = |
| system_files_service_.GetFileDump(SystemFilesService::File::kProcLoadavg); |
| |
| EXPECT_TRUE(file_dump); |
| EXPECT_EQ(file_dump.value().path, abs_link); |
| EXPECT_EQ(file_dump.value().canonical_path, abs_file); |
| EXPECT_EQ(file_dump.value().contents, FakeFileContents()); |
| } |
| |
| // Test that GetVpdField() returns false when the VPD field doesn't exist. |
| TEST_F(SystemFilesServiceTest, NoVpdField) { |
| EXPECT_FALSE(system_files_service_.GetVpdField( |
| SystemFilesService::VpdField::kSerialNumber)); |
| } |
| |
| // Test that GetVpdField() returns false when the VPD field contains non ASCII |
| // symbols. |
| TEST_F(SystemFilesServiceTest, NonASCIIVpdField) { |
| constexpr char kNonASCIIContent[] = "test1 Œ test2 不 test3"; |
| |
| base::FilePath abs_file = |
| temp_dir_.GetPath().Append("run/wilco_dtc/vpd_fields/system_id"); |
| |
| ASSERT_TRUE(WriteFileAndCreateParentDirs(abs_file, kNonASCIIContent)); |
| |
| EXPECT_FALSE(system_files_service_.GetVpdField( |
| SystemFilesService::VpdField::kSystemId)); |
| } |
| |
| // Test that GetVpdField() returns false when the VPD field is empty. |
| TEST_F(SystemFilesServiceTest, EmptyVpdField) { |
| base::FilePath abs_file = |
| temp_dir_.GetPath().Append("run/wilco_dtc/vpd_fields/model_name"); |
| |
| ASSERT_TRUE(WriteFileAndCreateParentDirs(abs_file, "")); |
| |
| EXPECT_FALSE(system_files_service_.GetVpdField( |
| SystemFilesService::VpdField::kModelName)); |
| } |
| |
| // Test that GetVpdField() returns the requested trimmed VPD field when the VPD |
| // field exists. |
| TEST_F(SystemFilesServiceTest, TrimmedVpdField) { |
| base::FilePath abs_file = |
| temp_dir_.GetPath().Append("run/wilco_dtc/vpd_fields/ActivateDate"); |
| |
| ASSERT_TRUE( |
| WriteFileAndCreateParentDirs(abs_file, "\n \t 20 Apr 2020 \t\t \n\n")); |
| |
| auto vpd_field = system_files_service_.GetVpdField( |
| SystemFilesService::VpdField::kActivateDate); |
| |
| EXPECT_TRUE(vpd_field); |
| EXPECT_EQ(vpd_field.value(), "20 Apr 2020"); |
| } |
| |
| // Test that GetDirectoryDump() returns false when the directory doesn't |
| // exist. |
| TEST_F(SystemFilesServiceTest, NonExistingDirectory) { |
| EXPECT_FALSE(system_files_service_.GetDirectoryDump( |
| SystemFilesService::Directory::kSysFirmwareDmiTables)); |
| } |
| |
| // Test that GetDirectoryDump() returns a single file when called on a |
| // directory containing a single file. |
| TEST_F(SystemFilesServiceTest, SingleFileInDirectory) { |
| const base::FilePath kDirPath("sys/firmware/dmi/tables/"); |
| const base::FilePath kFilePath("testf"); |
| |
| base::FilePath abs_path = |
| temp_dir_.GetPath().Append(kDirPath).Append(kFilePath); |
| |
| ASSERT_TRUE(WriteFileAndCreateParentDirs(abs_path, FakeFileContents())); |
| |
| auto file_dumps = system_files_service_.GetDirectoryDump( |
| SystemFilesService::Directory::kSysFirmwareDmiTables); |
| |
| ASSERT_TRUE(file_dumps); |
| ASSERT_EQ(file_dumps.value().size(), 1); |
| |
| EXPECT_EQ(file_dumps.value()[0]->path, abs_path); |
| EXPECT_EQ(file_dumps.value()[0]->canonical_path, abs_path); |
| EXPECT_EQ(file_dumps.value()[0]->contents, FakeFileContents()); |
| } |
| |
| // Test that GetDirectoryDump returns correct file dumps for files in nested |
| // directories. |
| TEST_F(SystemFilesServiceTest, GetFileInNestedDirectory) { |
| const base::FilePath kDirPath("sys/firmware/dmi/tables/"); |
| const base::FilePath kFilePath("a.testf"); |
| const base::FilePath kNestedPath("b.testn/testf"); |
| |
| std::string nested_file_contents = FakeFileContents() + " nested"; |
| |
| base::FilePath abs_file_path = |
| temp_dir_.GetPath().Append(kDirPath).Append(kFilePath); |
| base::FilePath abs_nested_path = |
| temp_dir_.GetPath().Append(kDirPath).Append(kNestedPath); |
| |
| ASSERT_TRUE( |
| WriteFileAndCreateParentDirs(abs_nested_path, nested_file_contents)); |
| ASSERT_TRUE(WriteFileAndCreateParentDirs(abs_file_path, FakeFileContents())); |
| |
| auto file_dumps = system_files_service_.GetDirectoryDump( |
| SystemFilesService::Directory::kSysFirmwareDmiTables); |
| |
| ASSERT_TRUE(file_dumps); |
| ASSERT_EQ(file_dumps.value().size(), 2); |
| |
| // We need to sort to ensure ordering for the next checks as file listing |
| // is not deterministic |
| std::sort(file_dumps.value().begin(), file_dumps.value().end(), |
| [](const std::unique_ptr<SystemFilesService::FileDump>& a, |
| const std::unique_ptr<SystemFilesService::FileDump>& b) { |
| return a->path.value() < b->path.value(); |
| }); |
| |
| EXPECT_EQ(file_dumps.value()[0]->path, abs_file_path); |
| EXPECT_EQ(file_dumps.value()[0]->canonical_path, abs_file_path); |
| EXPECT_EQ(file_dumps.value()[0]->contents, FakeFileContents()); |
| |
| EXPECT_EQ(file_dumps.value()[1]->path, abs_nested_path); |
| EXPECT_EQ(file_dumps.value()[1]->canonical_path, abs_nested_path); |
| EXPECT_EQ(file_dumps.value()[1]->contents, nested_file_contents); |
| } |
| |
| TEST_F(SystemFilesServiceTest, ShouldFollowSymlink) { |
| const base::FilePath kLinkPath("sys/class/net/"); |
| const base::FilePath kFilePath("a.testf"); |
| |
| base::ScopedTempDir other_dir; |
| ASSERT_TRUE(other_dir.CreateUniqueTempDir()); |
| |
| base::FilePath abs_file_path = other_dir.GetPath().Append(kFilePath); |
| base::FilePath abs_link_path = |
| temp_dir_.GetPath().Append(kLinkPath).Append(kFilePath); |
| |
| ASSERT_TRUE(WriteFileAndCreateSymbolicLink(abs_file_path, FakeFileContents(), |
| abs_link_path)); |
| |
| auto file_dumps = system_files_service_.GetDirectoryDump( |
| SystemFilesService::Directory::kSysClassNetwork); |
| |
| ASSERT_TRUE(file_dumps); |
| ASSERT_EQ(file_dumps.value().size(), 1); |
| |
| EXPECT_EQ(file_dumps.value()[0]->path, abs_link_path); |
| EXPECT_EQ(file_dumps.value()[0]->canonical_path, abs_file_path); |
| EXPECT_EQ(file_dumps.value()[0]->contents, FakeFileContents()); |
| } |
| |
| // Test that GetDirectoryDump() returns an empty result when given a directory |
| // with only a cyclic symlink. |
| TEST_F(SystemFilesServiceTest, CyclicSymLink) { |
| const base::FilePath kDirPath("sys/class/net/"); |
| const base::FilePath kLinkPath = |
| base::FilePath("sys/firmware/dmi/tables/link"); |
| |
| base::FilePath abs_path = |
| temp_dir_.GetPath().Append(kDirPath).Append(kLinkPath); |
| |
| ASSERT_TRUE(CreateCyclicSymbolicLink(abs_path)); |
| |
| auto file_dumps = system_files_service_.GetDirectoryDump( |
| SystemFilesService::Directory::kSysClassNetwork); |
| |
| ASSERT_TRUE(file_dumps); |
| EXPECT_EQ(file_dumps.value().size(), 0); |
| } |
| |
| // Test that GetDirectoryDump() returns a single result when given a directory |
| // containing a file and a symlink to that same file. |
| TEST_F(SystemFilesServiceTest, DuplicateSymLink) { |
| const base::FilePath kDirPath("sys/class/net/"); |
| const base::FilePath kFilePath("testf"); |
| const base::FilePath kLinkPath("linkf"); |
| |
| base::FilePath abs_file_path = |
| temp_dir_.GetPath().Append(kDirPath).Append(kFilePath); |
| base::FilePath abs_link_path = |
| temp_dir_.GetPath().Append(kDirPath).Append(kLinkPath); |
| |
| ASSERT_TRUE(WriteFileAndCreateSymbolicLink(abs_file_path, FakeFileContents(), |
| abs_link_path)); |
| |
| auto file_dumps = system_files_service_.GetDirectoryDump( |
| SystemFilesService::Directory::kSysClassNetwork); |
| |
| ASSERT_TRUE(file_dumps); |
| ASSERT_EQ(file_dumps.value().size(), 1); |
| |
| // The non-canonical path could be either |abs_file_path| or |
| // |abs_link_path|. Dumping a directory uses base::FileIterator, |
| // whose order is not guaranteed. |
| EXPECT_THAT( |
| file_dumps.value()[0]->path.value(), |
| AnyOf(StrEq(abs_file_path.value()), StrEq(abs_link_path.value()))); |
| EXPECT_EQ(file_dumps.value()[0]->canonical_path, abs_file_path); |
| EXPECT_EQ(file_dumps.value()[0]->contents, FakeFileContents()); |
| } |
| |
| TEST_F(SystemFilesServiceTest, ShouldNotFollowSymlink) { |
| const base::FilePath kLinkPath("sys/firmware/dmi/tables/"); |
| const base::FilePath kFilePath("a.testf"); |
| |
| base::ScopedTempDir other_dir; |
| ASSERT_TRUE(other_dir.CreateUniqueTempDir()); |
| |
| base::FilePath abs_file_path = other_dir.GetPath().Append(kFilePath); |
| base::FilePath abs_link_path = |
| temp_dir_.GetPath().Append(kLinkPath).Append(kFilePath); |
| |
| ASSERT_TRUE(WriteFileAndCreateSymbolicLink(abs_file_path, FakeFileContents(), |
| abs_link_path)); |
| |
| auto file_dumps = system_files_service_.GetDirectoryDump( |
| SystemFilesService::Directory::kSysFirmwareDmiTables); |
| |
| ASSERT_TRUE(file_dumps); |
| EXPECT_EQ(file_dumps.value().size(), 0); |
| } |
| |
| // Ensure Location mapping is valid |
| class SystemFilesServiceFileLocationTest |
| : public SystemFilesServiceTest, |
| public testing::WithParamInterface< |
| std::tuple<SystemFilesService::File, std::string>> { |
| public: |
| SystemFilesServiceFileLocationTest() = default; |
| ~SystemFilesServiceFileLocationTest() override = default; |
| |
| protected: |
| SystemFilesService::File GetLocationParam() const { |
| return std::get<0>(GetParam()); |
| } |
| |
| const std::string& GetPathParam() const { return std::get<1>(GetParam()); } |
| |
| base::FilePath GetAbsoluteTestFilePath() const { |
| return temp_dir_.GetPath().Append(GetPathParam()); |
| } |
| const std::string& GetTestFileContents() const { return GetPathParam(); } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(SystemFilesServiceFileLocationTest); |
| }; |
| |
| TEST_P(SystemFilesServiceFileLocationTest, Path) { |
| EXPECT_EQ(system_files_service_.GetPathForFile(GetLocationParam()).value(), |
| GetPathParam()); |
| } |
| |
| TEST_P(SystemFilesServiceFileLocationTest, Dump) { |
| ASSERT_TRUE(WriteFileAndCreateParentDirs(GetAbsoluteTestFilePath(), |
| GetTestFileContents())); |
| |
| const auto file_dump = system_files_service_.GetFileDump(GetLocationParam()); |
| |
| EXPECT_TRUE(file_dump); |
| EXPECT_EQ(file_dump.value().path, GetAbsoluteTestFilePath()); |
| EXPECT_EQ(file_dump.value().canonical_path, GetAbsoluteTestFilePath()); |
| EXPECT_EQ(file_dump.value().contents, GetTestFileContents()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| , |
| SystemFilesServiceFileLocationTest, |
| testing::Values( |
| std::make_tuple(SystemFilesService::File::kProcUptime, "proc/uptime"), |
| std::make_tuple(SystemFilesService::File::kProcMeminfo, "proc/meminfo"), |
| std::make_tuple(SystemFilesService::File::kProcLoadavg, "proc/loadavg"), |
| std::make_tuple(SystemFilesService::File::kProcStat, "proc/stat"), |
| std::make_tuple(SystemFilesService::File::kProcNetNetstat, |
| "proc/net/netstat"), |
| std::make_tuple(SystemFilesService::File::kProcNetDev, "proc/net/dev"), |
| std::make_tuple(SystemFilesService::File::kProcDiskstats, |
| "proc/diskstats"), |
| std::make_tuple(SystemFilesService::File::kProcCpuinfo, "proc/cpuinfo"), |
| std::make_tuple(SystemFilesService::File::kProcVmstat, "proc/vmstat"))); |
| |
| class SystemFilesServiceDirectoryLocationTest |
| : public SystemFilesServiceTest, |
| public testing::WithParamInterface< |
| std::tuple<SystemFilesService::Directory, std::string>> { |
| public: |
| SystemFilesServiceDirectoryLocationTest() = default; |
| |
| protected: |
| SystemFilesService::Directory GetLocationParam() const { |
| return std::get<0>(GetParam()); |
| } |
| |
| const std::string& GetPathParam() const { return std::get<1>(GetParam()); } |
| |
| base::FilePath GetAbsoluteTestFilePath() const { |
| return temp_dir_.GetPath().Append(GetPathParam()).Append("f"); |
| } |
| const std::string& GetTestFileContents() const { return GetPathParam(); } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(SystemFilesServiceDirectoryLocationTest); |
| }; |
| |
| TEST_P(SystemFilesServiceDirectoryLocationTest, Path) { |
| EXPECT_EQ( |
| system_files_service_.GetPathForDirectory(GetLocationParam()).value(), |
| GetPathParam()); |
| } |
| |
| TEST_P(SystemFilesServiceDirectoryLocationTest, Dump) { |
| ASSERT_TRUE(WriteFileAndCreateParentDirs(GetAbsoluteTestFilePath(), |
| GetTestFileContents())); |
| |
| auto file_dumps = system_files_service_.GetDirectoryDump(GetLocationParam()); |
| |
| ASSERT_TRUE(file_dumps); |
| ASSERT_EQ(file_dumps.value().size(), 1); |
| |
| EXPECT_EQ(file_dumps.value()[0]->path, GetAbsoluteTestFilePath()); |
| EXPECT_EQ(file_dumps.value()[0]->canonical_path, GetAbsoluteTestFilePath()); |
| EXPECT_EQ(file_dumps.value()[0]->contents, GetTestFileContents()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| , |
| SystemFilesServiceDirectoryLocationTest, |
| testing::Values( |
| std::make_tuple(SystemFilesService::Directory::kProcAcpiButton, |
| "proc/acpi/button/"), |
| std::make_tuple(SystemFilesService::Directory::kSysClassHwmon, |
| "sys/class/hwmon/"), |
| std::make_tuple(SystemFilesService::Directory::kSysClassThermal, |
| "sys/class/thermal/"), |
| std::make_tuple(SystemFilesService::Directory::kSysFirmwareDmiTables, |
| "sys/firmware/dmi/tables/"), |
| std::make_tuple(SystemFilesService::Directory::kSysClassPowerSupply, |
| "sys/class/power_supply/"), |
| std::make_tuple(SystemFilesService::Directory::kSysClassBacklight, |
| "sys/class/backlight/"), |
| std::make_tuple(SystemFilesService::Directory::kSysClassNetwork, |
| "sys/class/net/"), |
| std::make_tuple(SystemFilesService::Directory::kSysDevicesSystemCpu, |
| "sys/devices/system/cpu/"))); |
| |
| class SystemFilesServiceVpdFieldTest |
| : public SystemFilesServiceTest, |
| public testing::WithParamInterface< |
| std::tuple<SystemFilesService::VpdField, std::string>> { |
| public: |
| SystemFilesServiceVpdFieldTest() = default; |
| ~SystemFilesServiceVpdFieldTest() override = default; |
| |
| SystemFilesServiceVpdFieldTest(const SystemFilesServiceVpdFieldTest&) = |
| delete; |
| SystemFilesServiceVpdFieldTest& operator=( |
| const SystemFilesServiceVpdFieldTest&) = delete; |
| |
| protected: |
| SystemFilesService::VpdField GetVpdFieldParam() const { |
| return std::get<0>(GetParam()); |
| } |
| |
| const std::string& GetPathParam() const { return std::get<1>(GetParam()); } |
| |
| base::FilePath GetAbsoluteTestFilePath() const { |
| return temp_dir_.GetPath().Append(GetPathParam()); |
| } |
| const std::string& GetTestFileContents() const { return GetPathParam(); } |
| }; |
| |
| TEST_P(SystemFilesServiceVpdFieldTest, Path) { |
| EXPECT_EQ( |
| system_files_service_.GetPathForVpdField(GetVpdFieldParam()).value(), |
| GetPathParam()); |
| } |
| |
| TEST_P(SystemFilesServiceVpdFieldTest, Dump) { |
| ASSERT_TRUE(WriteFileAndCreateParentDirs(GetAbsoluteTestFilePath(), |
| GetTestFileContents())); |
| |
| auto vpd_field = system_files_service_.GetVpdField(GetVpdFieldParam()); |
| |
| EXPECT_TRUE(vpd_field); |
| EXPECT_EQ(vpd_field.value(), GetTestFileContents()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| , |
| SystemFilesServiceVpdFieldTest, |
| testing::Values(std::make_tuple(SystemFilesService::VpdField::kActivateDate, |
| "run/wilco_dtc/vpd_fields/ActivateDate"), |
| std::make_tuple(SystemFilesService::VpdField::kAssetId, |
| "run/wilco_dtc/vpd_fields/asset_id"), |
| std::make_tuple(SystemFilesService::VpdField::kMfgDate, |
| "run/wilco_dtc/vpd_fields/mfg_date"), |
| std::make_tuple(SystemFilesService::VpdField::kModelName, |
| "run/wilco_dtc/vpd_fields/model_name"), |
| std::make_tuple(SystemFilesService::VpdField::kSerialNumber, |
| "run/wilco_dtc/vpd_fields/serial_number"), |
| std::make_tuple(SystemFilesService::VpdField::kSkuNumber, |
| "run/wilco_dtc/vpd_fields/sku_number"), |
| std::make_tuple(SystemFilesService::VpdField::kSystemId, |
| "run/wilco_dtc/vpd_fields/system_id"), |
| std::make_tuple(SystemFilesService::VpdField::kUuid, |
| "run/wilco_dtc/vpd_fields/uuid_id"))); |
| |
| } // namespace diagnostics |