blob: f95a633c213647387a090dd9734cfdbb132e7088 [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 <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/macros.h>
#include <base/process/process_handle.h>
#include <base/test/simple_test_tick_clock.h>
#include <base/time/time.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "diagnostics/routines/diag_process_adapter.h"
#include "diagnostics/routines/urandom/urandom.h"
#include "wilco_dtc_supportd.pb.h" // NOLINT(build/include)
using testing::_;
using testing::DoAll;
using testing::Return;
using testing::SetArgPointee;
using testing::StrictMock;
namespace diagnostics {
namespace {
constexpr int kLengthSeconds = 100;
constexpr int kPercentCompleted = 43;
const std::vector<std::string> ConstructUrandomArgs() {
std::vector<std::string> args;
args.push_back("/usr/libexec/diagnostics/urandom"); // Executable path.
args.push_back("--time_delta_ms=" +
std::to_string(kLengthSeconds * 1000)); // Length in ms.
args.push_back(
"--urandom_path=/dev/urandom"); // Path to the urandom dev node.
return args;
}
class MockDiagProcessAdapter : public DiagProcessAdapter {
public:
MOCK_CONST_METHOD1(GetStatus,
base::TerminationStatus(const base::ProcessHandle&));
MOCK_METHOD2(StartProcess,
bool(const std::vector<std::string>& args,
base::ProcessHandle* handle));
MOCK_METHOD1(KillProcess, bool(const base::ProcessHandle&));
};
} // namespace
class UrandomRoutineTest : public testing::Test {
protected:
UrandomRoutineTest() {
auto new_tick_clock = std::make_unique<base::SimpleTestTickClock>();
tick_clock_ = new_tick_clock.get();
auto new_mock_adapter =
std::make_unique<StrictMock<MockDiagProcessAdapter>>();
mock_adapter_ = new_mock_adapter.get();
params_.set_length_seconds(kLengthSeconds);
routine_ = std::make_unique<UrandomRoutine>(
params_, std::move(new_tick_clock), std::move(new_mock_adapter));
}
UrandomRoutine* routine() { return routine_.get(); }
base::SimpleTestTickClock* tick_clock() { return tick_clock_; }
grpc_api::GetRoutineUpdateResponse* response() { return &response_; }
StrictMock<MockDiagProcessAdapter>* mock_adapter() { return mock_adapter_; }
void RunRoutineWithTerminationStatus(base::TerminationStatus status) {
routine_->Start();
PopulateStatusUpdateForRunningRoutine(status);
}
void RunRoutineToPercent(int percent) {
routine_->Start();
tick_clock_->Advance(
base::TimeDelta::FromSeconds(percent * kLengthSeconds / 100));
PopulateStatusUpdateForRunningRoutine(
base::TERMINATION_STATUS_STILL_RUNNING);
}
void RunRoutinePastCompletion() {
routine_->Start();
tick_clock_->Advance(base::TimeDelta::FromSeconds(kLengthSeconds + 1));
PopulateStatusUpdateForRunningRoutine(
base::TERMINATION_STATUS_STILL_RUNNING);
}
void PopulateStatusUpdateForRunningRoutine(base::TerminationStatus status) {
EXPECT_CALL(*mock_adapter_, GetStatus(_)).WillOnce(Return(status));
routine_->PopulateStatusUpdate(&response_, true);
}
void PopulateStatusUpdate() {
routine_->PopulateStatusUpdate(&response_, true);
}
private:
StrictMock<MockDiagProcessAdapter>* mock_adapter_; // Owned by |routine_|.
base::SimpleTestTickClock* tick_clock_; // Owned by |routine_|.
std::unique_ptr<UrandomRoutine> routine_;
grpc_api::GetRoutineUpdateResponse response_;
grpc_api::UrandomRoutineParameters params_;
DISALLOW_COPY_AND_ASSIGN(UrandomRoutineTest);
};
// Test that the urandom routine fails if the urandom executable fails to start.
TEST_F(UrandomRoutineTest, ExeFailsToStart) {
EXPECT_CALL(*mock_adapter(), StartProcess(ConstructUrandomArgs(), _))
.WillOnce(Return(false));
routine()->Start();
PopulateStatusUpdate();
EXPECT_EQ(response()->status_message(), kUrandomFailedToLaunchProcessMessage);
EXPECT_EQ(response()->status(), grpc_api::ROUTINE_STATUS_ERROR);
}
// Test that the urandom routine fails if the executable fails.
TEST_F(UrandomRoutineTest, ExeReturnsFailure) {
EXPECT_CALL(*mock_adapter(), StartProcess(ConstructUrandomArgs(), _))
.WillOnce(DoAll(SetArgPointee<1>(base::GetCurrentProcessHandle()),
Return(true)));
RunRoutineWithTerminationStatus(
base::TERMINATION_STATUS_ABNORMAL_TERMINATION);
EXPECT_EQ(response()->status_message(), kUrandomRoutineFailedMessage);
EXPECT_EQ(response()->status(), grpc_api::ROUTINE_STATUS_FAILED);
}
// Test that the urandom routine passes if the executable behaves as expected.
TEST_F(UrandomRoutineTest, NormalOperation) {
EXPECT_CALL(*mock_adapter(), StartProcess(ConstructUrandomArgs(), _))
.WillOnce(DoAll(SetArgPointee<1>(base::GetCurrentProcessHandle()),
Return(true)));
RunRoutineWithTerminationStatus(base::TERMINATION_STATUS_STILL_RUNNING);
EXPECT_EQ(response()->status_message(), kUrandomProcessRunningMessage);
EXPECT_EQ(response()->status(), grpc_api::ROUTINE_STATUS_RUNNING);
tick_clock()->Advance(base::TimeDelta::FromSeconds(kLengthSeconds));
PopulateStatusUpdateForRunningRoutine(
base::TERMINATION_STATUS_NORMAL_TERMINATION);
EXPECT_EQ(response()->status_message(), kUrandomRoutineSucceededMessage);
EXPECT_EQ(response()->status(), grpc_api::ROUTINE_STATUS_PASSED);
}
// Test that we can calculate the progress percentage correctly.
TEST_F(UrandomRoutineTest, ProgressPercentage) {
EXPECT_CALL(*mock_adapter(), StartProcess(ConstructUrandomArgs(), _))
.WillOnce(DoAll(SetArgPointee<1>(base::GetCurrentProcessHandle()),
Return(true)));
RunRoutineToPercent(kPercentCompleted);
EXPECT_EQ(response()->status(), grpc_api::ROUTINE_STATUS_RUNNING);
EXPECT_EQ(response()->progress_percent(), kPercentCompleted);
EXPECT_CALL(*mock_adapter(), GetStatus(_))
.WillOnce(Return(base::TERMINATION_STATUS_STILL_RUNNING));
EXPECT_CALL(*mock_adapter(), KillProcess(_)).WillOnce(Return(true));
}
// Test that we report the progress percent of a long-running test correctly.
TEST_F(UrandomRoutineTest, LongRunningProgressPercentage) {
EXPECT_CALL(*mock_adapter(), StartProcess(ConstructUrandomArgs(), _))
.WillOnce(DoAll(SetArgPointee<1>(base::GetCurrentProcessHandle()),
Return(true)));
RunRoutinePastCompletion();
EXPECT_EQ(response()->status(), grpc_api::ROUTINE_STATUS_RUNNING);
EXPECT_EQ(response()->progress_percent(), kUrandomLongRunningProgress);
EXPECT_CALL(*mock_adapter(), GetStatus(_))
.WillOnce(Return(base::TERMINATION_STATUS_STILL_RUNNING));
EXPECT_CALL(*mock_adapter(), KillProcess(_)).WillOnce(Return(true));
}
} // namespace diagnostics