blob: bdb24fd6bb2132f9de8645b654be73da05e1606a [file] [log] [blame]
// Copyright 2021 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 <base/bind.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/memory/scoped_refptr.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "rmad/rmad_interface_impl.h"
#include "rmad/state_handler/mock_state_handler.h"
#include "rmad/state_handler/state_handler_manager.h"
#include "rmad/utils/json_store.h"
using testing::_;
using testing::NiceMock;
using testing::Return;
using testing::ReturnRef;
namespace rmad {
constexpr char kJsonStoreFileName[] = "json_store_file";
constexpr char kCurrentStateSetJson[] = R"({"state_history": [ 1 ]})";
constexpr char kCurrentStateNotSetJson[] = "{}";
constexpr char kCurrentStateInvalidStateJson[] = R"("state_history": [0])";
constexpr char kCurrentStateWithRepeatableHistoryJson[] =
R"({"state_history": [ 1, 4 ]})";
constexpr char kCurrentStateWithUnrepeatableHistoryJson[] =
R"({"state_history": [ 1, 4, 5 ]})";
constexpr char kCurrentStateWithUnsupportedStateJson[] =
R"({"state_history": [ 1, 4, 6 ]})";
constexpr char kInitializeCurrentStateFailJson[] =
R"({"state_history": [ 1 ]})";
constexpr char kInitializeNextStateFailJson[] =
R"({"state_history": [ 1, 4 ]})";
constexpr char kInitializePreviousStateFailJson[] =
R"({"state_history": [ 1, 4 ]})";
constexpr char kInvalidJson[] = R"(alfkjklsfsgdkjnbknd^^)";
class RmadInterfaceImplTest : public testing::Test {
public:
RmadInterfaceImplTest() {
welcome_proto_.set_allocated_welcome(new WelcomeState());
components_repair_proto_.set_allocated_components_repair(
new ComponentsRepairState());
device_destination_proto_.set_allocated_device_destination(
new DeviceDestinationState());
}
base::FilePath CreateInputFile(std::string filename,
const char* str,
int size) {
base::FilePath file_path = temp_dir_.GetPath().AppendASCII(filename);
base::WriteFile(file_path, str, size);
return file_path;
}
scoped_refptr<BaseStateHandler> CreateMockHandler(
scoped_refptr<JsonStore> json_store,
const RmadState& state,
bool is_repeatable,
RmadErrorCode initialize_error,
RmadState::StateCase next_state) {
auto mock_handler =
base::MakeRefCounted<NiceMock<MockStateHandler>>(json_store);
RmadState::StateCase state_case = state.state_case();
ON_CALL(*mock_handler, GetStateCase()).WillByDefault(Return(state_case));
ON_CALL(*mock_handler, GetState()).WillByDefault(ReturnRef(state));
ON_CALL(*mock_handler, IsRepeatable()).WillByDefault(Return(is_repeatable));
ON_CALL(*mock_handler, InitializeState())
.WillByDefault(Return(initialize_error));
ON_CALL(*mock_handler, GetNextStateCase(_))
.WillByDefault(Return(BaseStateHandler::GetNextStateCaseReply{
.error = RMAD_ERROR_OK, .state_case = next_state}));
return mock_handler;
}
std::unique_ptr<StateHandlerManager> CreateStateHandlerManagerWithHandlers(
scoped_refptr<JsonStore> json_store,
std::vector<scoped_refptr<BaseStateHandler>> mock_handlers) {
auto state_handler_manager =
std::make_unique<StateHandlerManager>(json_store);
// TODO(gavindodd): Work out how to create RmadState objects and have them
// scoped to the test.
for (auto mock_handler : mock_handlers) {
state_handler_manager->RegisterStateHandler(mock_handler);
}
return state_handler_manager;
}
std::unique_ptr<StateHandlerManager> CreateStateHandlerManager(
scoped_refptr<JsonStore> json_store) {
// TODO(gavindodd): Work out how to create RmadState objects and have them
// scoped to the test.
std::vector<scoped_refptr<BaseStateHandler>> mock_handlers;
mock_handlers.push_back(CreateMockHandler(json_store, welcome_proto_, true,
RMAD_ERROR_OK,
RmadState::kComponentsRepair));
mock_handlers.push_back(
CreateMockHandler(json_store, components_repair_proto_, true,
RMAD_ERROR_OK, RmadState::kDeviceDestination));
mock_handlers.push_back(
CreateMockHandler(json_store, device_destination_proto_, false,
RMAD_ERROR_OK, RmadState::kWpDisableMethod));
return CreateStateHandlerManagerWithHandlers(json_store, mock_handlers);
}
std::unique_ptr<StateHandlerManager> CreateStateHandlerManagerMissingHandler(
scoped_refptr<JsonStore> json_store) {
// TODO(gavindodd): Work out how to create RmadState objects and have them
// scoped to the test.
std::vector<scoped_refptr<BaseStateHandler>> mock_handlers;
mock_handlers.push_back(CreateMockHandler(json_store, welcome_proto_, true,
RMAD_ERROR_OK,
RmadState::kComponentsRepair));
return CreateStateHandlerManagerWithHandlers(json_store, mock_handlers);
}
std::unique_ptr<StateHandlerManager>
CreateStateHandlerManagerInitializeStateFail(
scoped_refptr<JsonStore> json_store) {
std::vector<scoped_refptr<BaseStateHandler>> mock_handlers;
mock_handlers.push_back(CreateMockHandler(json_store, welcome_proto_, true,
RMAD_ERROR_MISSING_COMPONENT,
RmadState::kComponentsRepair));
mock_handlers.push_back(
CreateMockHandler(json_store, components_repair_proto_, true,
RMAD_ERROR_OK, RmadState::kDeviceDestination));
mock_handlers.push_back(CreateMockHandler(
json_store, device_destination_proto_, false,
RMAD_ERROR_DEVICE_INFO_INVALID, RmadState::kWpDisableMethod));
return CreateStateHandlerManagerWithHandlers(json_store, mock_handlers);
}
protected:
void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
RmadState welcome_proto_;
RmadState components_repair_proto_;
RmadState device_destination_proto_;
base::ScopedTempDir temp_dir_;
};
TEST_F(RmadInterfaceImplTest, GetCurrentState_Set) {
base::FilePath json_store_file_path =
CreateInputFile(kJsonStoreFileName, kCurrentStateSetJson,
std::size(kCurrentStateSetJson) - 1);
auto json_store = base::MakeRefCounted<JsonStore>(json_store_file_path);
RmadInterfaceImpl rmad_interface(json_store,
CreateStateHandlerManager(json_store));
auto callback = [](const GetStateReply& reply) {
EXPECT_EQ(RMAD_ERROR_OK, reply.error());
EXPECT_EQ(RmadState::kWelcome, reply.state().state_case());
EXPECT_EQ(false, reply.can_go_back());
EXPECT_EQ(true, reply.can_abort());
};
rmad_interface.GetCurrentState(base::BindRepeating(callback));
}
TEST_F(RmadInterfaceImplTest, GetCurrentState_NotInRma) {
base::FilePath json_store_file_path =
temp_dir_.GetPath().AppendASCII("missing.json");
auto json_store = base::MakeRefCounted<JsonStore>(json_store_file_path);
RmadInterfaceImpl rmad_interface(json_store,
CreateStateHandlerManager(json_store));
auto callback = [](const GetStateReply& reply) {
EXPECT_EQ(RMAD_ERROR_RMA_NOT_REQUIRED, reply.error());
EXPECT_EQ(RmadState::STATE_NOT_SET, reply.state().state_case());
};
rmad_interface.GetCurrentState(base::BindRepeating(callback));
}
// If we are in RMA and the json is not valid the state is reset to the initial
// state, which is the welcome state and an error is set.
TEST_F(RmadInterfaceImplTest, GetCurrentState_NotSet) {
base::FilePath json_store_file_path =
CreateInputFile(kJsonStoreFileName, kCurrentStateNotSetJson,
std::size(kCurrentStateNotSetJson) - 1);
auto json_store = base::MakeRefCounted<JsonStore>(json_store_file_path);
RmadInterfaceImpl rmad_interface(json_store,
CreateStateHandlerManager(json_store));
auto callback = [](const GetStateReply& reply) {
EXPECT_EQ(RMAD_ERROR_OK, reply.error());
EXPECT_EQ(RmadState::kWelcome, reply.state().state_case());
EXPECT_EQ(false, reply.can_go_back());
EXPECT_EQ(true, reply.can_abort());
};
rmad_interface.GetCurrentState(base::BindRepeating(callback));
}
TEST_F(RmadInterfaceImplTest, GetCurrentState_WithHistory) {
base::FilePath json_store_file_path = CreateInputFile(
kJsonStoreFileName, kCurrentStateWithRepeatableHistoryJson,
std::size(kCurrentStateWithRepeatableHistoryJson) - 1);
auto json_store = base::MakeRefCounted<JsonStore>(json_store_file_path);
RmadInterfaceImpl rmad_interface(json_store,
CreateStateHandlerManager(json_store));
auto callback = [](const GetStateReply& reply) {
EXPECT_EQ(RMAD_ERROR_OK, reply.error());
EXPECT_EQ(RmadState::kComponentsRepair, reply.state().state_case());
EXPECT_EQ(true, reply.can_go_back());
EXPECT_EQ(true, reply.can_abort());
};
rmad_interface.GetCurrentState(base::BindRepeating(callback));
}
TEST_F(RmadInterfaceImplTest, GetCurrentState_WithUnsupportedState) {
base::FilePath json_store_file_path =
CreateInputFile(kJsonStoreFileName, kCurrentStateWithUnsupportedStateJson,
std::size(kCurrentStateWithUnsupportedStateJson) - 1);
auto json_store = base::MakeRefCounted<JsonStore>(json_store_file_path);
RmadInterfaceImpl rmad_interface(json_store,
CreateStateHandlerManager(json_store));
auto callback = [](const GetStateReply& reply) {
EXPECT_EQ(RMAD_ERROR_OK, reply.error());
EXPECT_EQ(RmadState::kComponentsRepair, reply.state().state_case());
EXPECT_EQ(true, reply.can_go_back());
EXPECT_EQ(true, reply.can_abort());
};
// TODO(gavindodd): Use mock log to check for expected error.
rmad_interface.GetCurrentState(base::BindRepeating(callback));
}
TEST_F(RmadInterfaceImplTest, GetCurrentState_InvalidState) {
base::FilePath json_store_file_path =
CreateInputFile(kJsonStoreFileName, kCurrentStateInvalidStateJson,
std::size(kCurrentStateInvalidStateJson) - 1);
auto json_store = base::MakeRefCounted<JsonStore>(json_store_file_path);
RmadInterfaceImpl rmad_interface(json_store,
CreateStateHandlerManager(json_store));
auto callback = [](const GetStateReply& reply) {
EXPECT_EQ(RMAD_ERROR_OK, reply.error());
EXPECT_EQ(RmadState::kWelcome, reply.state().state_case());
EXPECT_EQ(false, reply.can_go_back());
EXPECT_EQ(true, reply.can_abort());
};
rmad_interface.GetCurrentState(base::BindRepeating(callback));
}
TEST_F(RmadInterfaceImplTest, GetCurrentState_InvalidJson) {
base::FilePath json_store_file_path = CreateInputFile(
kJsonStoreFileName, kInvalidJson, std::size(kInvalidJson) - 1);
auto json_store = base::MakeRefCounted<JsonStore>(json_store_file_path);
RmadInterfaceImpl rmad_interface(json_store,
CreateStateHandlerManager(json_store));
auto callback = [](const GetStateReply& reply) {
EXPECT_EQ(RMAD_ERROR_OK, reply.error());
EXPECT_EQ(RmadState::kWelcome, reply.state().state_case());
EXPECT_EQ(false, reply.can_go_back());
EXPECT_EQ(true, reply.can_abort());
};
rmad_interface.GetCurrentState(base::BindRepeating(callback));
}
TEST_F(RmadInterfaceImplTest, GetCurrentState_InitializeStateFail) {
base::FilePath json_store_file_path =
CreateInputFile(kJsonStoreFileName, kInitializeCurrentStateFailJson,
std::size(kInitializeCurrentStateFailJson) - 1);
auto json_store = base::MakeRefCounted<JsonStore>(json_store_file_path);
RmadInterfaceImpl rmad_interface(
json_store, CreateStateHandlerManagerInitializeStateFail(json_store));
auto callback = [](const GetStateReply& reply) {
EXPECT_EQ(RMAD_ERROR_MISSING_COMPONENT, reply.error());
EXPECT_EQ(RmadState::STATE_NOT_SET, reply.state().state_case());
};
rmad_interface.GetCurrentState(base::BindRepeating(callback));
}
TEST_F(RmadInterfaceImplTest, TransitionNextState) {
base::FilePath json_store_file_path =
CreateInputFile(kJsonStoreFileName, kCurrentStateSetJson,
std::size(kCurrentStateSetJson) - 1);
auto json_store = base::MakeRefCounted<JsonStore>(json_store_file_path);
RmadInterfaceImpl rmad_interface(json_store,
CreateStateHandlerManager(json_store));
EXPECT_EQ(true, rmad_interface.CanAbort());
TransitionNextStateRequest request;
auto callback1 = [](const GetStateReply& reply) {
EXPECT_EQ(RMAD_ERROR_OK, reply.error());
EXPECT_EQ(RmadState::kComponentsRepair, reply.state().state_case());
EXPECT_EQ(true, reply.can_go_back());
EXPECT_EQ(true, reply.can_abort());
};
rmad_interface.TransitionNextState(request, base::BindRepeating(callback1));
EXPECT_EQ(true, rmad_interface.CanAbort());
auto callback2 = [](const GetStateReply& reply) {
EXPECT_EQ(RMAD_ERROR_OK, reply.error());
EXPECT_EQ(RmadState::kDeviceDestination, reply.state().state_case());
EXPECT_EQ(false, reply.can_go_back());
EXPECT_EQ(false, reply.can_abort());
};
rmad_interface.TransitionNextState(request, base::BindRepeating(callback2));
EXPECT_EQ(false, rmad_interface.CanAbort());
}
TEST_F(RmadInterfaceImplTest, TransitionNextState_MissingHandler) {
base::FilePath json_store_file_path =
CreateInputFile(kJsonStoreFileName, kCurrentStateSetJson,
std::size(kCurrentStateSetJson) - 1);
auto json_store = base::MakeRefCounted<JsonStore>(json_store_file_path);
RmadInterfaceImpl rmad_interface(
json_store, CreateStateHandlerManagerMissingHandler(json_store));
TransitionNextStateRequest request;
auto callback = [](const GetStateReply& reply) {
FAIL() << "Unexpected call to callback";
};
EXPECT_DEATH(rmad_interface.TransitionNextState(
request, base::BindRepeating(callback)),
"No registered state handler");
}
TEST_F(RmadInterfaceImplTest, TransitionNextState_InitializeNextStateFail) {
base::FilePath json_store_file_path =
CreateInputFile(kJsonStoreFileName, kInitializeNextStateFailJson,
std::size(kInitializeNextStateFailJson) - 1);
auto json_store = base::MakeRefCounted<JsonStore>(json_store_file_path);
RmadInterfaceImpl rmad_interface(
json_store, CreateStateHandlerManagerInitializeStateFail(json_store));
TransitionNextStateRequest request;
auto callback = [](const GetStateReply& reply) {
EXPECT_EQ(RMAD_ERROR_DEVICE_INFO_INVALID, reply.error());
EXPECT_EQ(RmadState::kComponentsRepair, reply.state().state_case());
EXPECT_EQ(true, reply.can_go_back());
EXPECT_EQ(true, reply.can_abort());
};
rmad_interface.TransitionNextState(request, base::BindRepeating(callback));
}
TEST_F(RmadInterfaceImplTest, TransitionPreviousState) {
base::FilePath json_store_file_path = CreateInputFile(
kJsonStoreFileName, kCurrentStateWithRepeatableHistoryJson,
std::size(kCurrentStateWithRepeatableHistoryJson) - 1);
auto json_store = base::MakeRefCounted<JsonStore>(json_store_file_path);
RmadInterfaceImpl rmad_interface(json_store,
CreateStateHandlerManager(json_store));
auto callback = [](const GetStateReply& reply) {
EXPECT_EQ(RMAD_ERROR_OK, reply.error());
EXPECT_EQ(RmadState::kWelcome, reply.state().state_case());
EXPECT_EQ(false, reply.can_go_back());
EXPECT_EQ(true, reply.can_abort());
};
rmad_interface.TransitionPreviousState(base::BindRepeating(callback));
}
TEST_F(RmadInterfaceImplTest, TransitionPreviousState_NoHistory) {
base::FilePath json_store_file_path =
CreateInputFile(kJsonStoreFileName, kCurrentStateSetJson,
std::size(kCurrentStateSetJson) - 1);
auto json_store = base::MakeRefCounted<JsonStore>(json_store_file_path);
RmadInterfaceImpl rmad_interface(json_store,
CreateStateHandlerManager(json_store));
auto callback = [](const GetStateReply& reply) {
EXPECT_EQ(RMAD_ERROR_TRANSITION_FAILED, reply.error());
EXPECT_EQ(RmadState::kWelcome, reply.state().state_case());
EXPECT_EQ(false, reply.can_go_back());
EXPECT_EQ(true, reply.can_abort());
};
rmad_interface.TransitionPreviousState(base::BindRepeating(callback));
}
TEST_F(RmadInterfaceImplTest, TransitionPreviousState_MissingHandler) {
base::FilePath json_store_file_path = CreateInputFile(
kJsonStoreFileName, kCurrentStateWithRepeatableHistoryJson,
std::size(kCurrentStateWithRepeatableHistoryJson) - 1);
auto json_store = base::MakeRefCounted<JsonStore>(json_store_file_path);
RmadInterfaceImpl rmad_interface(
json_store, CreateStateHandlerManagerMissingHandler(json_store));
auto callback = [](const GetStateReply& reply) {
EXPECT_EQ(RMAD_ERROR_TRANSITION_FAILED, reply.error());
EXPECT_EQ(RmadState::kWelcome, reply.state().state_case());
EXPECT_EQ(false, reply.can_go_back());
EXPECT_EQ(true, reply.can_abort());
};
rmad_interface.TransitionPreviousState(base::BindRepeating(callback));
}
TEST_F(RmadInterfaceImplTest,
TransitionPreviousState_InitializePreviousStateFail) {
base::FilePath json_store_file_path =
CreateInputFile(kJsonStoreFileName, kInitializePreviousStateFailJson,
std::size(kInitializePreviousStateFailJson) - 1);
auto json_store = base::MakeRefCounted<JsonStore>(json_store_file_path);
RmadInterfaceImpl rmad_interface(
json_store, CreateStateHandlerManagerInitializeStateFail(json_store));
auto callback = [](const GetStateReply& reply) {
EXPECT_EQ(RMAD_ERROR_MISSING_COMPONENT, reply.error());
EXPECT_EQ(RmadState::kComponentsRepair, reply.state().state_case());
EXPECT_EQ(true, reply.can_go_back());
EXPECT_EQ(true, reply.can_abort());
};
rmad_interface.TransitionPreviousState(base::BindRepeating(callback));
}
TEST_F(RmadInterfaceImplTest, AbortRma) {
base::FilePath json_store_file_path = CreateInputFile(
kJsonStoreFileName, kCurrentStateWithRepeatableHistoryJson,
std::size(kCurrentStateWithRepeatableHistoryJson) - 1);
auto json_store = base::MakeRefCounted<JsonStore>(json_store_file_path);
RmadInterfaceImpl rmad_interface(json_store,
CreateStateHandlerManager(json_store));
auto callback = [](const AbortRmaReply& reply) {
EXPECT_EQ(RMAD_ERROR_RMA_NOT_REQUIRED, reply.error());
};
rmad_interface.AbortRma(base::BindRepeating(callback));
}
TEST_F(RmadInterfaceImplTest, AbortRma_NoHistory) {
base::FilePath json_store_file_path =
CreateInputFile(kJsonStoreFileName, kCurrentStateSetJson,
std::size(kCurrentStateSetJson) - 1);
auto json_store = base::MakeRefCounted<JsonStore>(json_store_file_path);
RmadInterfaceImpl rmad_interface(json_store,
CreateStateHandlerManager(json_store));
auto callback = [](const AbortRmaReply& reply) {
EXPECT_EQ(RMAD_ERROR_RMA_NOT_REQUIRED, reply.error());
};
rmad_interface.AbortRma(base::BindRepeating(callback));
}
TEST_F(RmadInterfaceImplTest, AbortRma_Failed) {
base::FilePath json_store_file_path = CreateInputFile(
kJsonStoreFileName, kCurrentStateWithUnrepeatableHistoryJson,
std::size(kCurrentStateWithUnrepeatableHistoryJson) - 1);
auto json_store = base::MakeRefCounted<JsonStore>(json_store_file_path);
RmadInterfaceImpl rmad_interface(json_store,
CreateStateHandlerManager(json_store));
auto callback = [](const AbortRmaReply& reply) {
EXPECT_EQ(RMAD_ERROR_ABORT_FAILED, reply.error());
};
rmad_interface.AbortRma(base::BindRepeating(callback));
}
} // namespace rmad