blob: aeef1e4a91d7239d554d268e4b42a6204ad7645a [file] [log] [blame]
// Copyright 2020 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 <memory>
#include <string>
#include <utility>
#include <base/check.h>
#include <base/containers/span.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/optional.h>
#include <base/strings/stringprintf.h>
#include <base/test/scoped_chromeos_version_info.h>
#include <gtest/gtest.h>
#include "diagnostics/common/file_test_utils.h"
#include "diagnostics/common/mojo_type_utils.h"
#include "diagnostics/cros_healthd/fetchers/system_fetcher.h"
#include "diagnostics/cros_healthd/fetchers/system_fetcher_constants.h"
#include "diagnostics/cros_healthd/system/mock_context.h"
namespace diagnostics {
namespace {
namespace mojo_ipc = ::chromeos::cros_healthd::mojom;
template <typename T>
base::Optional<std::string> GetMockValue(const T& value) {
return value;
}
base::Optional<std::string> GetMockValue(
const mojo_ipc::NullableUint64Ptr& ptr) {
if (ptr)
return std::to_string(ptr->value);
return base::nullopt;
}
class SystemUtilsTest : public BaseFileTest {
protected:
SystemUtilsTest() = default;
SystemUtilsTest(const SystemUtilsTest&) = delete;
SystemUtilsTest& operator=(const SystemUtilsTest&) = delete;
void SetUp() override {
SetTestRoot(mock_context_.root_dir());
expected_system_info_ = mojo_ipc::SystemInfoV2::New();
auto& vpd_info = expected_system_info_->vpd_info;
vpd_info = mojo_ipc::VpdInfo::New();
vpd_info->activate_date = "2020-40";
vpd_info->mfg_date = "2019-01-01";
vpd_info->model_name = "XX ModelName 007 XY";
vpd_info->region = "us";
vpd_info->serial_number = "8607G03EDF";
vpd_info->sku_number = "ABCD&^A";
auto& dmi_info = expected_system_info_->dmi_info;
dmi_info = mojo_ipc::DmiInfo::New();
dmi_info->bios_vendor = "Google";
dmi_info->bios_version = "Google_BoardName.12200.68.0";
dmi_info->board_name = "BoardName";
dmi_info->board_vendor = "Google";
dmi_info->board_version = "rev1234";
dmi_info->chassis_vendor = "Google";
dmi_info->chassis_type = mojo_ipc::NullableUint64::New(9);
dmi_info->product_family = "FooFamily";
dmi_info->product_name = "BarProductName";
dmi_info->product_version = "rev1234";
dmi_info->sys_vendor = "Google";
auto& os_info = expected_system_info_->os_info;
os_info = mojo_ipc::OsInfo::New();
os_info->code_name = "CodeName";
os_info->marketing_name = "Latitude 1234 Chromebook Enterprise";
os_info->boot_mode = mojo_ipc::BootMode::kCrosSecure;
auto& os_version = os_info->os_version;
os_version = mojo_ipc::OsVersion::New();
os_version->release_milestone = "87";
os_version->build_number = "13544";
os_version->patch_number = "59.0";
os_version->release_channel = "stable-channel";
SetSystemInfo(expected_system_info_);
SetHasSkuNumber(true);
}
void SetSystemInfo(const mojo_ipc::SystemInfoV2Ptr& system_info) {
SetVpdInfo(system_info->vpd_info);
SetDmiInfo(system_info->dmi_info);
SetOsInfo(system_info->os_info);
}
void SetVpdInfo(const mojo_ipc::VpdInfoPtr& vpd_info) {
const auto& ro = kRelativePathVpdRo;
const auto& rw = kRelativePathVpdRw;
if (vpd_info.is_null()) {
// Delete the whole vpd dir.
UnsetPath(base::FilePath(ro).DirName());
return;
}
SetMockFile({rw, kFileNameActivateDate}, vpd_info->activate_date);
SetMockFile({ro, kFileNameMfgDate}, vpd_info->mfg_date);
SetMockFile({ro, kFileNameModelName}, vpd_info->model_name);
SetMockFile({ro, kFileNameRegion}, vpd_info->region);
SetMockFile({ro, kFileNameSerialNumber}, vpd_info->serial_number);
SetMockFile({ro, kFileNameSkuNumber}, vpd_info->sku_number);
}
void SetDmiInfo(const mojo_ipc::DmiInfoPtr& dmi_info) {
const auto& dmi = kRelativePathDmiInfo;
if (dmi_info.is_null()) {
UnsetPath(dmi);
return;
}
SetMockFile({dmi, kFileNameBiosVendor}, dmi_info->bios_vendor);
SetMockFile({dmi, kFileNameBiosVersion}, dmi_info->bios_version);
SetMockFile({dmi, kFileNameBoardName}, dmi_info->board_name);
SetMockFile({dmi, kFileNameBoardVendor}, dmi_info->board_vendor);
SetMockFile({dmi, kFileNameBoardVersion}, dmi_info->board_version);
SetMockFile({dmi, kFileNameChassisVendor}, dmi_info->chassis_vendor);
SetMockFile({dmi, kFileNameChassisType}, dmi_info->chassis_type);
SetMockFile({dmi, kFileNameProductFamily}, dmi_info->product_family);
SetMockFile({dmi, kFileNameProductName}, dmi_info->product_name);
SetMockFile({dmi, kFileNameProductVersion}, dmi_info->product_version);
SetMockFile({dmi, kFileNameSysVendor}, dmi_info->sys_vendor);
}
void SetOsInfo(const mojo_ipc::OsInfoPtr& os_info) {
ASSERT_FALSE(os_info.is_null());
mock_context_.fake_system_config()->SetMarketingName(
os_info->marketing_name);
mock_context_.fake_system_config()->SetCodeName(os_info->code_name);
SetOsVersion(os_info->os_version);
SetBootMode(os_info->boot_mode);
}
void SetOsVersion(const mojo_ipc::OsVersionPtr& os_version) {
PopulateLsbRelease(base::StringPrintf(
"CHROMEOS_RELEASE_CHROME_MILESTONE=%s\n"
"CHROMEOS_RELEASE_BUILD_NUMBER=%s\n"
"CHROMEOS_RELEASE_PATCH_NUMBER=%s\n"
"CHROMEOS_RELEASE_TRACK=%s\n",
os_version->release_milestone.c_str(), os_version->build_number.c_str(),
os_version->patch_number.c_str(), os_version->release_channel.c_str()));
}
void SetBootMode(const mojo_ipc::BootMode& boot_mode) {
base::Optional<std::string> proc_cmd;
base::Optional<std::string> efi_secure;
switch (boot_mode) {
case mojo_ipc::BootMode::kCrosSecure:
proc_cmd = "Foo Bar=1 cros_secure Foo Bar=1";
break;
case mojo_ipc::BootMode::kCrosEfi:
efi_secure = std::string{0x0};
proc_cmd = "Foo Bar=1 cros_efi Foo Bar=1";
break;
case mojo_ipc::BootMode::kCrosEfiSecure:
efi_secure = std::string{0x1};
proc_cmd = "Foo Bar=1 cros_efi Foo Bar=1";
break;
case mojo_ipc::BootMode::kCrosLegacy:
proc_cmd = "Foo Bar=1 cros_legacy Foo Bar=1";
break;
case mojo_ipc::BootMode::kUnknown:
proc_cmd = "Foo Bar=1 Foo Bar=1";
break;
}
SetMockFile(kFilePathProcCmdline, proc_cmd);
SetMockFile(kFileUEFISecurityBoot, efi_secure);
}
// Sets the mock file with |value|. If the |value| is omitted, deletes the
// file.
template <typename T>
void SetMockFile(const PathType& path, const T& value) {
auto mock = GetMockValue(value);
if (mock) {
SetFile(path, mock.value());
} else {
UnsetPath(path);
}
}
void SetHasSkuNumber(bool val) {
mock_context_.fake_system_config()->SetHasSkuNumber(val);
}
void PopulateLsbRelease(const std::string& lsb_release) {
// Deletes the old instance to release the global lock before creating a new
// instance.
chromeos_version_.reset();
chromeos_version_ = std::make_unique<base::test::ScopedChromeOSVersionInfo>(
lsb_release, base::Time::Now());
}
void ExpectFetchSystemInfo() {
auto system_result = system_fetcher_.FetchSystemInfoV2();
ASSERT_FALSE(system_result.is_null());
ASSERT_FALSE(system_result->is_error());
ASSERT_TRUE(system_result->is_system_info_v2());
auto res = std::move(system_result->get_system_info_v2());
EXPECT_EQ(res, expected_system_info_)
<< GetDiffString(res, expected_system_info_);
auto system_result_old = system_fetcher_.FetchSystemInfo();
ASSERT_FALSE(system_result_old.is_null());
ASSERT_FALSE(system_result_old->is_error());
ASSERT_TRUE(system_result_old->is_system_info());
EXPECT_EQ(system_result_old->get_system_info(),
SystemFetcher::ConvertToSystemInfo(expected_system_info_));
}
void ExpectFetchProbeError(const mojo_ipc::ErrorType& expected) {
auto system_result = system_fetcher_.FetchSystemInfo();
ASSERT_TRUE(system_result->is_error());
EXPECT_EQ(system_result->get_error()->type, expected);
}
protected:
mojo_ipc::SystemInfoV2Ptr expected_system_info_;
private:
MockContext mock_context_;
SystemFetcher system_fetcher_{&mock_context_};
base::FilePath relative_vpd_rw_dir_;
base::FilePath relative_vpd_ro_dir_;
base::FilePath relative_dmi_info_path_;
std::unique_ptr<base::test::ScopedChromeOSVersionInfo> chromeos_version_;
};
// Template for testing the missing field of vpd/dmi.
#define TEST_MISSING_FIELD(info, field) \
TEST_F(SystemUtilsTest, TestNo_##info##_##field) { \
expected_system_info_->info->field = base::nullopt; \
SetSystemInfo(expected_system_info_); \
ExpectFetchSystemInfo(); \
}
TEST_F(SystemUtilsTest, TestFetchSystemInfo) {
ExpectFetchSystemInfo();
}
TEST_F(SystemUtilsTest, TestNoVpdDir) {
expected_system_info_->vpd_info = nullptr;
SetSystemInfo(expected_system_info_);
SetHasSkuNumber(true);
ExpectFetchProbeError(mojo_ipc::ErrorType::kFileReadError);
SetHasSkuNumber(false);
ExpectFetchSystemInfo();
}
TEST_F(SystemUtilsTest, TestNoSkuNumber) {
// Sku number file exists.
SetSystemInfo(expected_system_info_);
// Ensure that there is no sku number returned even if sku number exists.
SetHasSkuNumber(false);
expected_system_info_->vpd_info->sku_number = base::nullopt;
ExpectFetchSystemInfo();
// Sku number file doesn't exist.
SetSystemInfo(expected_system_info_);
ExpectFetchSystemInfo();
// Sku number file doesn't exist but should have.
SetHasSkuNumber(true);
ExpectFetchProbeError(mojo_ipc::ErrorType::kFileReadError);
}
TEST_MISSING_FIELD(vpd_info, activate_date);
TEST_MISSING_FIELD(vpd_info, region);
TEST_MISSING_FIELD(vpd_info, mfg_date);
TEST_MISSING_FIELD(vpd_info, serial_number);
TEST_MISSING_FIELD(vpd_info, model_name);
TEST_F(SystemUtilsTest, TestNoSysDevicesVirtualDmiId) {
expected_system_info_->dmi_info = nullptr;
// Delete the whole directory |kRelativePathDmiInfo|.
UnsetPath(kRelativePathDmiInfo);
ExpectFetchSystemInfo();
}
TEST_MISSING_FIELD(dmi_info, bios_vendor);
TEST_MISSING_FIELD(dmi_info, bios_version);
TEST_MISSING_FIELD(dmi_info, board_name);
TEST_MISSING_FIELD(dmi_info, board_vendor);
TEST_MISSING_FIELD(dmi_info, board_version);
TEST_MISSING_FIELD(dmi_info, chassis_vendor);
TEST_MISSING_FIELD(dmi_info, product_family);
TEST_MISSING_FIELD(dmi_info, product_name);
TEST_MISSING_FIELD(dmi_info, product_version);
TEST_MISSING_FIELD(dmi_info, sys_vendor);
TEST_F(SystemUtilsTest, TestNoChassisType) {
expected_system_info_->dmi_info->chassis_type = nullptr;
SetSystemInfo(expected_system_info_);
ExpectFetchSystemInfo();
}
TEST_F(SystemUtilsTest, TestBadChassisType) {
// Overwrite the contents of |kChassisTypeFileName| with a chassis_type value
// that cannot be parsed into an unsigned integer.
std::string bad_chassis_type = "bad chassis type";
SetMockFile({kRelativePathDmiInfo, kFileNameChassisType}, bad_chassis_type);
ExpectFetchProbeError(mojo_ipc::ErrorType::kParseError);
}
TEST_F(SystemUtilsTest, TestNoOsVersion) {
PopulateLsbRelease("");
ExpectFetchProbeError(mojo_ipc::ErrorType::kFileReadError);
}
TEST_F(SystemUtilsTest, TestBadOsVersion) {
PopulateLsbRelease(
"Milestone\n"
"CHROMEOS_RELEASE_BUILD_NUMBER=1\n"
"CHROMEOS_RELEASE_PATCH_NUMBER=2\n"
"CHROMEOS_RELEASE_TRACK=3\n");
ExpectFetchProbeError(mojo_ipc::ErrorType::kFileReadError);
}
TEST_F(SystemUtilsTest, TestBootMode) {
expected_system_info_->os_info->boot_mode = mojo_ipc::BootMode::kCrosSecure;
SetSystemInfo(expected_system_info_);
ExpectFetchSystemInfo();
expected_system_info_->os_info->boot_mode = mojo_ipc::BootMode::kCrosEfi;
SetSystemInfo(expected_system_info_);
ExpectFetchSystemInfo();
expected_system_info_->os_info->boot_mode = mojo_ipc::BootMode::kCrosLegacy;
SetSystemInfo(expected_system_info_);
ExpectFetchSystemInfo();
expected_system_info_->os_info->boot_mode = mojo_ipc::BootMode::kUnknown;
SetSystemInfo(expected_system_info_);
ExpectFetchSystemInfo();
expected_system_info_->os_info->boot_mode =
mojo_ipc::BootMode::kCrosEfiSecure;
SetSystemInfo(expected_system_info_);
ExpectFetchSystemInfo();
}
} // namespace
} // namespace diagnostics