blob: c027db05d740facd90f331058af5c187a0323509 [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/routines/subproc_routine.h"
#include <utility>
#include <vector>
#include <base/macros.h>
#include <base/command_line.h>
#include <base/test/simple_test_tick_clock.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "diagnostics/routines/diag_process_adapter.h"
namespace diagnostics {
namespace {
using ::testing::_;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::StrictMock;
using ::testing::Test;
using ::testing::AtMost;
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&));
};
class SubprocRoutineTest : public Test {
protected:
SubprocRoutineTest() {
auto mock_adapter_ptr =
std::make_unique<StrictMock<MockDiagProcessAdapter>>();
mock_adapter_ = mock_adapter_ptr.get();
auto tick_clock_ptr = std::make_unique<base::SimpleTestTickClock>();
tick_clock_ = tick_clock_ptr.get();
// We never actually run subprocesses in this unit test, because this module
// is not actually responsible for process invocation, and we trust the
// DiagProcessAdapter to do things appropriately.
auto command_line = base::CommandLine({"/dev/null"});
// For testing purposes, we will always pretend that subprocs are predicted
// to take 10s.
int predicted_duration_in_seconds = 10;
routine_ = std::make_unique<SubprocRoutine>(
std::move(mock_adapter_ptr), std::move(tick_clock_ptr), command_line,
predicted_duration_in_seconds);
}
SubprocRoutine* routine() { return routine_.get(); }
StrictMock<MockDiagProcessAdapter>* mock_adapter() { return mock_adapter_; }
base::SimpleTestTickClock* tick_clock() { return tick_clock_; }
void RunRoutineWithTerminationStatus(base::TerminationStatus status) {
routine_->Start();
PopulateStatusUpdateForRunningRoutine(status);
}
void RunRoutinePastCompletion() {
routine_->Start();
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<SubprocRoutine> routine_;
grpc_api::GetRoutineUpdateResponse response_;
grpc_api::UrandomRoutineParameters params_;
DISALLOW_COPY_AND_ASSIGN(SubprocRoutineTest);
};
TEST_F(SubprocRoutineTest, InvokeSubprocWithSuccess) {
EXPECT_CALL(*mock_adapter(), StartProcess(_, _))
.WillOnce(DoAll(SetArgPointee<1>(base::GetCurrentProcessHandle()),
Return(true)));
EXPECT_CALL(*mock_adapter(), GetStatus(_))
.WillOnce(Return(base::TERMINATION_STATUS_NORMAL_TERMINATION));
routine()->Start();
grpc_api::GetRoutineUpdateResponse response;
routine()->PopulateStatusUpdate(&response, false);
EXPECT_EQ(response.progress_percent(), 100);
EXPECT_EQ(response.status_message(), kSubprocRoutineSucceededMessage);
EXPECT_EQ(response.status(), grpc_api::ROUTINE_STATUS_PASSED);
}
TEST_F(SubprocRoutineTest, InvokeSubprocWithFailure) {
EXPECT_CALL(*mock_adapter(), StartProcess(_, _))
.WillOnce(DoAll(SetArgPointee<1>(base::GetCurrentProcessHandle()),
Return(true)));
EXPECT_CALL(*mock_adapter(), GetStatus(_))
.WillOnce(Return(base::TERMINATION_STATUS_ABNORMAL_TERMINATION));
routine()->Start();
grpc_api::GetRoutineUpdateResponse response;
routine()->PopulateStatusUpdate(&response, false);
EXPECT_EQ(response.progress_percent(), 100);
EXPECT_EQ(response.status_message(), kSubprocRoutineFailedMessage);
EXPECT_EQ(response.status(), grpc_api::ROUTINE_STATUS_FAILED);
}
TEST_F(SubprocRoutineTest, FailInvokingSubproc) {
EXPECT_CALL(*mock_adapter(), StartProcess(_, _)).WillOnce(Return(false));
routine()->Start();
EXPECT_EQ(routine()->GetStatus(), grpc_api::ROUTINE_STATUS_FAILED_TO_START);
}
TEST_F(SubprocRoutineTest, TestHalfProgressPercent) {
EXPECT_CALL(*mock_adapter(), StartProcess(_, _))
.WillOnce(DoAll(SetArgPointee<1>(base::GetCurrentProcessHandle()),
Return(true)));
EXPECT_CALL(*mock_adapter(), GetStatus(_))
.Times(AtMost(2))
.WillOnce(Return(base::TERMINATION_STATUS_STILL_RUNNING))
.WillOnce(Return(base::TERMINATION_STATUS_NORMAL_TERMINATION));
routine()->Start();
tick_clock()->Advance(base::TimeDelta::FromSeconds(5));
grpc_api::GetRoutineUpdateResponse response;
routine()->PopulateStatusUpdate(&response, false);
EXPECT_EQ(response.progress_percent(), 50);
EXPECT_EQ(response.status_message(), kSubprocRoutineProcessRunningMessage);
EXPECT_EQ(response.status(), grpc_api::ROUTINE_STATUS_RUNNING);
}
TEST_F(SubprocRoutineTest, TestHalfProgressThenCancel) {
EXPECT_CALL(*mock_adapter(), StartProcess(_, _))
.WillOnce(DoAll(SetArgPointee<1>(base::GetCurrentProcessHandle()),
Return(true)));
EXPECT_CALL(*mock_adapter(), KillProcess(_)).Times(AtMost(1));
EXPECT_CALL(*mock_adapter(), GetStatus(_))
.Times(AtMost(4))
.WillOnce(Return(base::TERMINATION_STATUS_STILL_RUNNING))
.WillOnce(Return(base::TERMINATION_STATUS_STILL_RUNNING))
.WillOnce(Return(base::TERMINATION_STATUS_STILL_RUNNING))
.WillOnce(Return(base::TERMINATION_STATUS_ABNORMAL_TERMINATION));
routine()->Start();
tick_clock()->Advance(base::TimeDelta::FromSeconds(5));
grpc_api::GetRoutineUpdateResponse response;
routine()->PopulateStatusUpdate(&response, false);
EXPECT_EQ(response.progress_percent(), 50);
EXPECT_EQ(response.status_message(), kSubprocRoutineProcessRunningMessage);
EXPECT_EQ(response.status(), grpc_api::ROUTINE_STATUS_RUNNING);
routine()->Cancel();
tick_clock()->Advance(base::TimeDelta::FromSeconds(1));
routine()->PopulateStatusUpdate(&response, false);
EXPECT_EQ(response.progress_percent(), 50);
EXPECT_EQ(response.status_message(), kSubprocRoutineProcessCancellingMessage);
EXPECT_EQ(response.status(), grpc_api::ROUTINE_STATUS_CANCELLING);
// Now the process should appear dead
routine()->PopulateStatusUpdate(&response, false);
EXPECT_EQ(response.progress_percent(), 50);
EXPECT_EQ(response.status_message(), kSubprocRoutineCancelled);
EXPECT_EQ(response.status(), grpc_api::ROUTINE_STATUS_CANCELLED);
}
} // namespace
} // namespace diagnostics