0

shimless: Implement basic state transitions.

- Implement GetCurrentState, GetNextState, GetPrevState
 - Add templated GetNextStateGeneric so all next state methods can
   share.
 - Allow toMojom for rmad::RmadState::STATE_NOT_SET for case when
   RMA is not active

Bug: 1198187
Test: tools/autotest.py -C out/Default --run_all \
 ash/content/shimless_rma/backend/shimless_rma_service_unittest.cc
      tools/autotest.py -C out/Default --run_all \
 ash/content/shimless_rma/mojom/shimless_rma_mojom_traits_unittest.cc

Change-Id: I9f7a1794c158ddef8f8fca62917363a1f613d044
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2925976
Commit-Queue: Gavin Dodd <gavindodd@google.com>
Reviewed-by: Zentaro Kavanagh <zentaro@chromium.org>
Reviewed-by: Alex Gough <ajgo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#894423}
This commit is contained in:
Gavin Dodd
2021-06-21 22:04:27 +00:00
committed by Chromium LUCI CQ
parent b13a3cd338
commit b9a8837a90
7 changed files with 369 additions and 55 deletions

@ -18,6 +18,7 @@ test("ash_content_unittests") {
deps = [
"//ash/content/scanning:unit_tests",
"//ash/content/shimless_rma/backend:unit_tests",
"//ash/content/shimless_rma/mojom:unit_tests",
"//ash/content/shortcut_customization_ui/backend:unit_tests",
"//base",

@ -16,3 +16,25 @@ static_library("backend") {
"//chromeos/dbus/rmad:rmad_proto",
]
}
source_set("unit_tests") {
testonly = true
sources = [ "shimless_rma_service_unittest.cc" ]
deps = [
":backend",
"//ash/content/shimless_rma/mojom",
"//base",
"//base/test:test_support",
"//chromeos/dbus/rmad",
"//chromeos/dbus/rmad:rmad_proto",
"//content/test:test_support",
"//services/data_decoder/public/cpp:test_support",
"//services/device/public/cpp:test_support",
"//testing/gtest",
"//ui/gfx",
"//ui/shell_dialogs",
"//ui/webui",
]
}

@ -4,12 +4,22 @@
#include "ash/content/shimless_rma/backend/shimless_rma_service.h"
#include "ash/content/shimless_rma/mojom/shimless_rma_mojom_traits.h"
#include "base/bind.h"
#include "chromeos/dbus/rmad/rmad_client.h"
namespace ash {
namespace shimless_rma {
namespace {
using StateTraits =
mojo::EnumTraits<mojom::RmaState, rmad::RmadState::StateCase>;
using ErrorTraits = mojo::EnumTraits<mojom::RmadErrorCode, rmad::RmadErrorCode>;
} // namespace
// TODO(gavindodd): Declare an observer class and register an instance when
// the mojom interface is created.
@ -17,14 +27,21 @@ ShimlessRmaService::ShimlessRmaService() {}
ShimlessRmaService::~ShimlessRmaService() = default;
void ShimlessRmaService::GetCurrentState(GetCurrentStateCallback callback) {
chromeos::RmadClient::Get()->GetCurrentState(base::BindOnce(
&ShimlessRmaService::OnGetStateResponse<GetCurrentStateCallback>,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
// TODO(crbug.com/1218180): For development only. Remove when all state
// specific functions implemented.
void ShimlessRmaService::GetNextState(GetNextStateCallback callback) {
GetNextStateGeneric(std::move(callback));
}
void ShimlessRmaService::GetPrevState(GetPrevStateCallback callback) {
chromeos::RmadClient::Get()->TransitionPreviousState(base::BindOnce(
&ShimlessRmaService::OnGetStateResponse<GetPrevStateCallback>,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ShimlessRmaService::AbortRma(AbortRmaCallback callback) {
@ -156,5 +173,39 @@ void ShimlessRmaService::BindInterface(
receiver_.Bind(std::move(pending_receiver));
}
template <class Callback>
void ShimlessRmaService::GetNextStateGeneric(Callback callback) {
chromeos::RmadClient::Get()->TransitionNextState(
state_proto_,
base::BindOnce(&ShimlessRmaService::OnGetStateResponse<Callback>,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
template <class Callback>
void ShimlessRmaService::OnGetStateResponse(
Callback callback,
absl::optional<rmad::GetStateReply> response) {
if (!response) {
LOG(ERROR) << "Failed to call rmadClient";
std::move(callback).Run(mojom::RmaState::kUnknown,
mojom::RmadErrorCode::kRequestInvalid);
return;
}
const mojom::RmaState state =
StateTraits::ToMojom(response->state().state_case());
if (!mojom::IsKnownEnumValue(state)) {
LOG(ERROR) << "rmadClient returned unknown state " << state;
std::move(callback).Run(mojom::RmaState::kUnknown,
mojom::RmadErrorCode::kTransitionFailed);
}
state_proto_ = response->state();
if (response->error() != rmad::RMAD_ERROR_OK) {
LOG(ERROR) << "rmadClient returned error " << response->error();
std::move(callback).Run(state, ErrorTraits::ToMojom(response->error()));
return;
}
std::move(callback).Run(state, mojom::RmadErrorCode::kOk);
}
} // namespace shimless_rma
} // namespace ash

@ -12,6 +12,7 @@
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote_set.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace ash {
namespace shimless_rma {
@ -92,6 +93,14 @@ class ShimlessRmaService : public mojom::ShimlessRmaService {
mojo::PendingReceiver<mojom::ShimlessRmaService> pending_receiver);
private:
template <class Callback>
void GetNextStateGeneric(Callback callback);
template <class Callback>
void OnGetStateResponse(Callback callback,
absl::optional<rmad::GetStateReply> response);
rmad::RmadState state_proto_;
mojo::Receiver<mojom::ShimlessRmaService> receiver_{this};
// Note: This should remain the last member so it'll be destroyed and

@ -0,0 +1,277 @@
// Copyright 2021 The Chromium 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 "ash/content/shimless_rma/backend/shimless_rma_service.h"
#include <map>
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "chromeos/dbus/rmad/fake_rmad_client.h"
#include "chromeos/dbus/rmad/rmad_client.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ash {
namespace shimless_rma {
class ShimlessRmaServiceTest : public testing::Test {
public:
ShimlessRmaServiceTest() = default;
~ShimlessRmaServiceTest() override { base::RunLoop().RunUntilIdle(); }
void SetUp() override {
shimless_rma_provider_ = std::make_unique<ShimlessRmaService>();
chromeos::RmadClient::InitializeFake();
rmad_client_ = chromeos::RmadClient::Get();
}
void TearDown() override { chromeos::RmadClient::Shutdown(); }
rmad::RmadState* CreateState(rmad::RmadState::StateCase state_case) {
rmad::RmadState* state = new rmad::RmadState();
switch (state_case) {
case rmad::RmadState::kWelcome:
state->set_allocated_welcome(new rmad::WelcomeState());
break;
case rmad::RmadState::kSelectNetwork:
state->set_allocated_select_network(new rmad::SelectNetworkState());
break;
case rmad::RmadState::kUpdateChrome:
state->set_allocated_update_chrome(new rmad::UpdateChromeState());
break;
case rmad::RmadState::kComponentsRepair:
state->set_allocated_components_repair(
new rmad::ComponentsRepairState());
break;
case rmad::RmadState::kDeviceDestination:
state->set_allocated_device_destination(
new rmad::DeviceDestinationState());
break;
case rmad::RmadState::kWpDisableMethod:
state->set_allocated_wp_disable_method(
new rmad::WriteProtectDisableMethodState());
break;
case rmad::RmadState::kWpDisableRsu:
state->set_allocated_wp_disable_rsu(
new rmad::WriteProtectDisableRsuState());
break;
case rmad::RmadState::kWpDisablePhysical:
state->set_allocated_wp_disable_physical(
new rmad::WriteProtectDisablePhysicalState());
break;
case rmad::RmadState::kWpDisableComplete:
state->set_allocated_wp_disable_complete(
new rmad::WriteProtectDisableCompleteState());
break;
case rmad::RmadState::kUpdateRoFirmware:
state->set_allocated_update_ro_firmware(
new rmad::UpdateRoFirmwareState());
break;
case rmad::RmadState::kRestock:
state->set_allocated_restock(new rmad::RestockState());
break;
case rmad::RmadState::kUpdateDeviceInfo:
state->set_allocated_update_device_info(
new rmad::UpdateDeviceInfoState());
break;
case rmad::RmadState::kCalibrateComponents:
state->set_allocated_calibrate_components(
new rmad::CalibrateComponentsState());
break;
case rmad::RmadState::kProvisionDevice:
state->set_allocated_provision_device(new rmad::ProvisionDeviceState());
break;
case rmad::RmadState::kWpEnablePhysical:
state->set_allocated_wp_enable_physical(
new rmad::WriteProtectEnablePhysicalState());
break;
case rmad::RmadState::kFinalize:
state->set_allocated_finalize(new rmad::FinalizeState());
break;
default:
assert(false);
}
EXPECT_EQ(state->state_case(), state_case);
return state;
}
rmad::GetStateReply CreateStateReply(rmad::RmadState::StateCase state,
rmad::RmadErrorCode error) {
rmad::GetStateReply reply;
reply.set_allocated_state(CreateState(state));
reply.set_error(error);
return reply;
}
chromeos::FakeRmadClient* fake_rmad_client_() {
return google::protobuf::down_cast<chromeos::FakeRmadClient*>(rmad_client_);
}
protected:
std::unique_ptr<ShimlessRmaService> shimless_rma_provider_;
chromeos::RmadClient* rmad_client_ = nullptr; // Unowned convenience pointer.
private:
base::test::TaskEnvironment task_environment_;
};
TEST_F(ShimlessRmaServiceTest, GetCurrentState) {
std::vector<rmad::GetStateReply> fake_states;
fake_states.push_back(
CreateStateReply(rmad::RmadState::kWelcome, rmad::RMAD_ERROR_OK));
fake_states.push_back(
CreateStateReply(rmad::RmadState::kSelectNetwork, rmad::RMAD_ERROR_OK));
fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
base::RunLoop run_loop;
shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
[&](mojom::RmaState state, mojom::RmadErrorCode error) {
EXPECT_EQ(state, mojom::RmaState::kWelcomeScreen);
EXPECT_EQ(error, mojom::RmadErrorCode::kOk);
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(ShimlessRmaServiceTest, GetCurrentStateNoRma) {
std::vector<rmad::GetStateReply> fake_states;
fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
base::RunLoop run_loop;
shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
[&](mojom::RmaState state, mojom::RmadErrorCode error) {
EXPECT_EQ(state, mojom::RmaState::kUnknown);
EXPECT_EQ(error, mojom::RmadErrorCode::kRmaNotRequired);
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(ShimlessRmaServiceTest, GetNextState) {
std::vector<rmad::GetStateReply> fake_states;
fake_states.push_back(
CreateStateReply(rmad::RmadState::kWelcome, rmad::RMAD_ERROR_OK));
fake_states.push_back(
CreateStateReply(rmad::RmadState::kSelectNetwork, rmad::RMAD_ERROR_OK));
fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
base::RunLoop run_loop;
shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
[&](mojom::RmaState state, mojom::RmadErrorCode error) {
EXPECT_EQ(state, mojom::RmaState::kWelcomeScreen);
EXPECT_EQ(error, mojom::RmadErrorCode::kOk);
}));
run_loop.RunUntilIdle();
shimless_rma_provider_->GetNextState(base::BindLambdaForTesting(
[&](mojom::RmaState state, mojom::RmadErrorCode error) {
EXPECT_EQ(state, mojom::RmaState::kConfigureNetwork);
EXPECT_EQ(error, mojom::RmadErrorCode::kOk);
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(ShimlessRmaServiceTest, GetNextStateWithoutCurrentStateInvalid) {
std::vector<rmad::GetStateReply> fake_states;
fake_states.push_back(
CreateStateReply(rmad::RmadState::kWelcome, rmad::RMAD_ERROR_OK));
fake_states.push_back(
CreateStateReply(rmad::RmadState::kSelectNetwork, rmad::RMAD_ERROR_OK));
fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
base::RunLoop run_loop;
shimless_rma_provider_->GetNextState(base::BindLambdaForTesting(
[&](mojom::RmaState state, mojom::RmadErrorCode error) {
EXPECT_EQ(state, mojom::RmaState::kWelcomeScreen);
EXPECT_EQ(error, mojom::RmadErrorCode::kRequestInvalid);
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(ShimlessRmaServiceTest, GetNextStateWithNoNextStateFails) {
std::vector<rmad::GetStateReply> fake_states = {
CreateStateReply(rmad::RmadState::kWelcome, rmad::RMAD_ERROR_OK)};
fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
base::RunLoop run_loop;
shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
[&](mojom::RmaState state, mojom::RmadErrorCode error) {
EXPECT_EQ(state, mojom::RmaState::kWelcomeScreen);
EXPECT_EQ(error, mojom::RmadErrorCode::kOk);
}));
run_loop.RunUntilIdle();
shimless_rma_provider_->GetNextState(base::BindLambdaForTesting(
[&](mojom::RmaState state, mojom::RmadErrorCode error) {
EXPECT_EQ(state, mojom::RmaState::kWelcomeScreen);
EXPECT_EQ(error, mojom::RmadErrorCode::kTransitionFailed);
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(ShimlessRmaServiceTest, GetPrevState) {
std::vector<rmad::GetStateReply> fake_states;
fake_states.push_back(
CreateStateReply(rmad::RmadState::kWelcome, rmad::RMAD_ERROR_OK));
fake_states.push_back(
CreateStateReply(rmad::RmadState::kSelectNetwork, rmad::RMAD_ERROR_OK));
fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
base::RunLoop run_loop;
shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
[&](mojom::RmaState state, mojom::RmadErrorCode error) {
EXPECT_EQ(state, mojom::RmaState::kWelcomeScreen);
EXPECT_EQ(error, mojom::RmadErrorCode::kOk);
}));
run_loop.RunUntilIdle();
shimless_rma_provider_->GetNextState(base::BindLambdaForTesting(
[&](mojom::RmaState state, mojom::RmadErrorCode error) {
EXPECT_EQ(state, mojom::RmaState::kConfigureNetwork);
EXPECT_EQ(error, mojom::RmadErrorCode::kOk);
}));
run_loop.RunUntilIdle();
shimless_rma_provider_->GetPrevState(base::BindLambdaForTesting(
[&](mojom::RmaState state, mojom::RmadErrorCode error) {
EXPECT_EQ(state, mojom::RmaState::kWelcomeScreen);
EXPECT_EQ(error, mojom::RmadErrorCode::kOk);
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(ShimlessRmaServiceTest, GetPrevStateWithoutCurrentStateFails) {
std::vector<rmad::GetStateReply> fake_states;
fake_states.push_back(
CreateStateReply(rmad::RmadState::kWelcome, rmad::RMAD_ERROR_OK));
fake_states.push_back(
CreateStateReply(rmad::RmadState::kSelectNetwork, rmad::RMAD_ERROR_OK));
fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
base::RunLoop run_loop;
shimless_rma_provider_->GetPrevState(base::BindLambdaForTesting(
[&](mojom::RmaState state, mojom::RmadErrorCode error) {
EXPECT_EQ(state, mojom::RmaState::kWelcomeScreen);
EXPECT_EQ(error, mojom::RmadErrorCode::kTransitionFailed);
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(ShimlessRmaServiceTest, GetPrevStateWithNoPrevStateFails) {
std::vector<rmad::GetStateReply> fake_states = {
CreateStateReply(rmad::RmadState::kWelcome, rmad::RMAD_ERROR_OK)};
fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
base::RunLoop run_loop;
shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
[&](mojom::RmaState state, mojom::RmadErrorCode error) {
EXPECT_EQ(state, mojom::RmaState::kWelcomeScreen);
EXPECT_EQ(error, mojom::RmadErrorCode::kOk);
}));
run_loop.RunUntilIdle();
shimless_rma_provider_->GetPrevState(base::BindLambdaForTesting(
[&](mojom::RmaState state, mojom::RmadErrorCode error) {
EXPECT_EQ(state, mojom::RmaState::kWelcomeScreen);
EXPECT_EQ(error, mojom::RmadErrorCode::kTransitionFailed);
run_loop.Quit();
}));
run_loop.Run();
}
} // namespace shimless_rma
} // namespace ash

@ -71,7 +71,6 @@ MojomRmaState EnumTraits<MojomRmaState, ProtoRmadState>::ToMojom(
return MojomRmaState::kRepairComplete;
case ProtoRmadState::STATE_NOT_SET:
NOTREACHED();
return MojomRmaState::kUnknown;
}
NOTREACHED();

@ -35,22 +35,6 @@ void TestProtoToMojo(
}
}
template <typename MojoEnum, typename ProtoEnum, size_t N>
void TestNoMojoToProto(
const base::fixed_flat_map<MojoEnum, ProtoEnum, N>& enums) {
// The mojo enum is not sparse.
EXPECT_EQ(enums.size(), static_cast<size_t>(MojoEnum::kMaxValue));
#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
// This test hits a NOTREACHED so it is a release mode only test.
for (auto enum_pair : enums) {
ProtoEnum mojo_to_proto;
EXPECT_FALSE((mojo::EnumTraits<MojoEnum, ProtoEnum>::FromMojom(
enum_pair.first, &mojo_to_proto)));
}
#endif
}
template <typename MojoEnum, typename ProtoEnum, size_t N>
void TestMojoToProto(
const base::fixed_flat_map<MojoEnum, ProtoEnum, N>& enums) {
@ -66,22 +50,6 @@ void TestMojoToProto(
}
}
// Zero value can only be tested when DCHECK is off due to hitting NOTREACHED.
template <typename MojoEnum, typename ProtoEnum>
void TestZeroValue(MojoEnum mojoZero, ProtoEnum protoZero) {
EXPECT_EQ(static_cast<int32_t>(mojoZero), 0);
EXPECT_EQ(static_cast<int32_t>(protoZero), 0);
#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
// This test hits a NOTREACHED so it is a release mode only test.
EXPECT_EQ(mojoZero,
(mojo::EnumTraits<MojoEnum, ProtoEnum>::ToMojom(protoZero)));
ProtoEnum mojo_to_proto;
EXPECT_FALSE((mojo::EnumTraits<MojoEnum, ProtoEnum>::FromMojom(
mojoZero, &mojo_to_proto)));
#endif
}
} // namespace
class ShimlessRmaMojoToProtoTest : public testing::Test {
@ -123,10 +91,16 @@ TEST_F(ShimlessRmaMojoToProtoTest, StatesMatch) {
rmad::RmadState::kWpEnablePhysical},
{mojom::RmaState::kRepairComplete, rmad::RmadState::kFinalize}});
TestZeroValue(mojom::RmaState::kUnknown, rmad::RmadState::STATE_NOT_SET);
// rmad::RmadState::STATE_NOT_SET is used when RMA is not active so the
// toMojo conversion is reachable, unlike other enums.
EXPECT_EQ(static_cast<int32_t>(mojom::RmaState::kUnknown), 0);
EXPECT_EQ(static_cast<int32_t>(rmad::RmadState::STATE_NOT_SET), 0);
// This test hits a NOTREACHED so it is a release mode only test.
EXPECT_EQ(
mojom::RmaState::kUnknown,
(mojo::EnumTraits<mojom::RmaState, rmad::RmadState::StateCase>::ToMojom(
rmad::RmadState::STATE_NOT_SET)));
TestProtoToMojo(enums);
TestNoMojoToProto(enums);
}
TEST_F(ShimlessRmaMojoToProtoTest, ErrorsMatch) {
@ -200,11 +174,7 @@ TEST_F(ShimlessRmaMojoToProtoTest, ErrorsMatch) {
{mojom::RmadErrorCode::kCannotCancelRma,
rmad::RmadErrorCode::RMAD_ERROR_CANNOT_CANCEL_RMA}});
TestZeroValue(mojom::RmadErrorCode::kNotSet,
rmad::RmadErrorCode::RMAD_ERROR_NOT_SET);
TestProtoToMojo(enums);
TestNoMojoToProto(enums);
}
TEST_F(ShimlessRmaMojoToProtoTest, RepairComponentsMatch) {
@ -224,9 +194,6 @@ TEST_F(ShimlessRmaMojoToProtoTest, RepairComponentsMatch) {
{mojom::ComponentType::kThumbReader,
rmad::ComponentRepairState::RMAD_COMPONENT_THUMB_READER}});
TestZeroValue(mojom::ComponentType::kComponentUnknown,
rmad::ComponentRepairState::RMAD_COMPONENT_UNKNOWN);
TestProtoToMojo(enums);
TestMojoToProto(enums);
}
@ -242,9 +209,6 @@ TEST_F(ShimlessRmaMojoToProtoTest, RepairStatesMatch) {
{mojom::ComponentRepairState::kMissing,
rmad::ComponentRepairState::RMAD_REPAIR_MISSING}});
TestZeroValue(mojom::ComponentRepairState::kRepairUnknown,
rmad::ComponentRepairState::RMAD_REPAIR_UNKNOWN);
TestProtoToMojo(enums);
TestMojoToProto(enums);
}
@ -257,12 +221,7 @@ TEST_F(ShimlessRmaMojoToProtoTest, CalibrationComponentsMatch) {
rmad::CalibrateComponentsState::
RMAD_CALIBRATION_COMPONENT_ACCELEROMETER}});
TestZeroValue(
mojom::CalibrationComponent::kCalibrateUnknown,
rmad::CalibrateComponentsState::RMAD_CALIBRATION_COMPONENT_UNKNOWN);
TestProtoToMojo(enums);
TestNoMojoToProto(enums);
}
TEST_F(ShimlessRmaMojoToProtoTest, ProvisioningStepsMatch) {
@ -274,11 +233,7 @@ TEST_F(ShimlessRmaMojoToProtoTest, ProvisioningStepsMatch) {
{mojom::ProvisioningStep::kProvisioningComplete,
rmad::ProvisionDeviceState::RMAD_PROVISIONING_STEP_COMPLETE}});
TestZeroValue(mojom::ProvisioningStep::kProvisioningUnknown,
rmad::ProvisionDeviceState::RMAD_PROVISIONING_STEP_UNKNOWN);
TestProtoToMojo(enums);
TestNoMojoToProto(enums);
}
} // namespace shimless_rma