blob: 64d66590f149fa163c5748c8d2f11531f723e596 [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 <inttypes.h>
#include <cstdint>
#include <cstdlib>
#include <string>
#include <utility>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/run_loop.h>
#include <base/strings/stringprintf.h>
#include <base/test/task_environment.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "diagnostics/cros_healthd/executor/mock_executor_adapter.h"
#include "diagnostics/cros_healthd/fetchers/fan_fetcher.h"
#include "diagnostics/cros_healthd/system/mock_context.h"
#include "mojo/cros_healthd_executor.mojom.h"
#include "mojo/cros_healthd_probe.mojom.h"
namespace diagnostics {
namespace {
namespace executor_ipc = chromeos::cros_healthd_executor::mojom;
namespace mojo_ipc = chromeos::cros_healthd::mojom;
using ::testing::_;
using ::testing::DoAll;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::StrictMock;
using ::testing::WithArg;
// Test values for fan speed.
constexpr uint32_t kFirstFanSpeedRpm = 2255;
constexpr uint32_t kSecondFanSpeedRpm = 1263;
constexpr uint64_t kOverflowingValue = 0xFFFFFFFFFF;
// Saves |response| to |response_destination|.
void OnGetFanSpeedResponseReceived(mojo_ipc::FanResultPtr* response_destination,
base::Closure quit_closure,
mojo_ipc::FanResultPtr response) {
*response_destination = std::move(response);
quit_closure.Run();
}
} // namespace
class FanUtilsTest : public ::testing::Test {
protected:
FanUtilsTest() = default;
void SetUp() override {
ASSERT_TRUE(mock_context_.Initialize());
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
ASSERT_TRUE(
base::CreateDirectory(GetTempDirPath().Append(kRelativeCrosEcPath)));
}
const base::FilePath& GetTempDirPath() const {
DCHECK(temp_dir_.IsValid());
return temp_dir_.GetPath();
}
MockExecutorAdapter* mock_executor() { return mock_context_.mock_executor(); }
mojo_ipc::FanResultPtr FetchFanInfo() {
base::RunLoop run_loop;
mojo_ipc::FanResultPtr result;
fan_fetcher_.FetchFanInfo(GetTempDirPath(),
base::BindOnce(&OnGetFanSpeedResponseReceived,
&result, run_loop.QuitClosure()));
run_loop.Run();
return result;
}
private:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY};
MockContext mock_context_;
FanFetcher fan_fetcher_{&mock_context_};
base::ScopedTempDir temp_dir_;
};
// Test that fan information can be fetched successfully.
TEST_F(FanUtilsTest, FetchFanInfo) {
// Set the mock executor response.
EXPECT_CALL(*mock_executor(), GetFanSpeed(_))
.WillOnce(WithArg<0>(
Invoke([](executor_ipc::Executor::GetFanSpeedCallback callback) {
executor_ipc::ProcessResult result;
result.return_code = EXIT_SUCCESS;
result.out =
base::StringPrintf("Fan 0 RPM: %u\nFan 1 RPM: %u\n",
kFirstFanSpeedRpm, kSecondFanSpeedRpm);
std::move(callback).Run(result.Clone());
})));
auto fan_result = FetchFanInfo();
ASSERT_TRUE(fan_result->is_fan_info());
const auto& fan_info = fan_result->get_fan_info();
ASSERT_EQ(fan_info.size(), 2);
EXPECT_EQ(fan_info[0]->speed_rpm, kFirstFanSpeedRpm);
EXPECT_EQ(fan_info[1]->speed_rpm, kSecondFanSpeedRpm);
}
// Test that no fan information is returned for a device that has no fan.
TEST_F(FanUtilsTest, NoFan) {
// Set the mock executor response.
EXPECT_CALL(*mock_executor(), GetFanSpeed(_))
.WillOnce(WithArg<0>(
Invoke([](executor_ipc::Executor::GetFanSpeedCallback callback) {
executor_ipc::ProcessResult result;
result.return_code = EXIT_SUCCESS;
result.out = "";
std::move(callback).Run(result.Clone());
})));
auto fan_result = FetchFanInfo();
ASSERT_TRUE(fan_result->is_fan_info());
EXPECT_EQ(fan_result->get_fan_info().size(), 0);
}
// Test that the executor failing to collect fan speed fails gracefully and
// returns a ProbeError.
TEST_F(FanUtilsTest, CollectFanSpeedFailure) {
// Set the mock executor response.
EXPECT_CALL(*mock_executor(), GetFanSpeed(_))
.WillOnce(WithArg<0>(
Invoke([](executor_ipc::Executor::GetFanSpeedCallback callback) {
executor_ipc::ProcessResult result;
result.return_code = EXIT_FAILURE;
result.err = "Some error happened!";
std::move(callback).Run(result.Clone());
})));
auto fan_result = FetchFanInfo();
ASSERT_TRUE(fan_result->is_error());
EXPECT_EQ(fan_result->get_error()->type,
chromeos::cros_healthd::mojom::ErrorType::kSystemUtilityError);
}
// Test that fan speed is set to 0 RPM when a fan stalls.
TEST_F(FanUtilsTest, FanStalled) {
// Set the mock executor response.
EXPECT_CALL(*mock_executor(), GetFanSpeed(_))
.WillOnce(WithArg<0>(
Invoke([](executor_ipc::Executor::GetFanSpeedCallback callback) {
executor_ipc::ProcessResult result;
result.return_code = EXIT_SUCCESS;
result.out = base::StringPrintf("Fan 0 stalled!\nFan 1 RPM: %u\n",
kSecondFanSpeedRpm);
std::move(callback).Run(result.Clone());
})));
auto fan_result = FetchFanInfo();
ASSERT_TRUE(fan_result->is_fan_info());
const auto& fan_info = fan_result->get_fan_info();
ASSERT_EQ(fan_info.size(), 2);
EXPECT_EQ(fan_info[0]->speed_rpm, 0);
EXPECT_EQ(fan_info[1]->speed_rpm, kSecondFanSpeedRpm);
}
// Test that failing to match a line of output to the fan speed regex fails
// gracefully and returns a ProbeError.
TEST_F(FanUtilsTest, BadLine) {
// Set the mock executor response.
EXPECT_CALL(*mock_executor(), GetFanSpeed(_))
.WillOnce(WithArg<0>(
Invoke([](executor_ipc::Executor::GetFanSpeedCallback callback) {
executor_ipc::ProcessResult result;
result.return_code = EXIT_SUCCESS;
result.out = base::StringPrintf("Fan 0 RPM: bad\nFan 1 RPM: %u\n",
kSecondFanSpeedRpm);
std::move(callback).Run(result.Clone());
})));
auto fan_result = FetchFanInfo();
ASSERT_TRUE(fan_result->is_error());
EXPECT_EQ(fan_result->get_error()->type,
chromeos::cros_healthd::mojom::ErrorType::kParseError);
}
// Test that failing to convert the first fan speed string to an integer fails
// gracefully and returns a ProbeError.
TEST_F(FanUtilsTest, BadValue) {
// Set the mock executor response.
EXPECT_CALL(*mock_executor(), GetFanSpeed(_))
.WillOnce(WithArg<0>(
Invoke([](executor_ipc::Executor::GetFanSpeedCallback callback) {
executor_ipc::ProcessResult result;
result.return_code = EXIT_SUCCESS;
result.out = base::StringPrintf("Fan 0 RPM: -115\nFan 1 RPM: %u\n",
kSecondFanSpeedRpm);
std::move(callback).Run(result.Clone());
})));
auto fan_result = FetchFanInfo();
ASSERT_TRUE(fan_result->is_error());
EXPECT_EQ(fan_result->get_error()->type,
chromeos::cros_healthd::mojom::ErrorType::kParseError);
}
// Test that no fan info is fetched for a device that does not have a Google EC.
TEST_F(FanUtilsTest, NoGoogleEc) {
base::FilePath root_dir = GetTempDirPath();
ASSERT_TRUE(
base::DeletePathRecursively(root_dir.Append(kRelativeCrosEcPath)));
auto fan_result = FetchFanInfo();
ASSERT_TRUE(fan_result->is_fan_info());
EXPECT_EQ(fan_result->get_fan_info().size(), 0);
}
// Test that overflowing fan speed integer values from ectool are handled
// gracefully.
TEST_F(FanUtilsTest, OverflowingFanSpeedValue) {
// Set the mock executor response.
EXPECT_CALL(*mock_executor(), GetFanSpeed(_))
.WillOnce(WithArg<0>(
Invoke([](executor_ipc::Executor::GetFanSpeedCallback callback) {
executor_ipc::ProcessResult result;
result.return_code = EXIT_SUCCESS;
result.out =
base::StringPrintf("Fan 0 RPM: %u\nFan 1 RPM: %" PRId64 "\n",
kFirstFanSpeedRpm, kOverflowingValue);
std::move(callback).Run(result.Clone());
})));
auto fan_result = FetchFanInfo();
ASSERT_TRUE(fan_result->is_error());
EXPECT_EQ(fan_result->get_error()->type,
chromeos::cros_healthd::mojom::ErrorType::kParseError);
}
} // namespace diagnostics