blob: 2496b8807ffcdcaf9846ec8a14616b1c307f2586 [file] [log] [blame]
// 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(const SystemFilesServiceTest&) = delete;
SystemFilesServiceTest& operator=(const SystemFilesServiceTest&) = delete;
~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_;
};
// 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(
const SystemFilesServiceFileLocationTest&) = delete;
SystemFilesServiceFileLocationTest& operator=(
const SystemFilesServiceFileLocationTest&) = delete;
~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(); }
};
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;
SystemFilesServiceDirectoryLocationTest(
const SystemFilesServiceDirectoryLocationTest&) = delete;
SystemFilesServiceDirectoryLocationTest& operator=(
const SystemFilesServiceDirectoryLocationTest&) = delete;
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(); }
};
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