blob: 787315a0212acb00294b02a372bc336ce118d5e8 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/borealis/infra/state_manager.h"
#include <memory>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/test/task_environment.h"
#include "chrome/browser/ash/borealis/infra/expected.h"
#include "chrome/browser/ash/borealis/testing/callback_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace borealis {
namespace {
template <typename F>
using CallbackFactory = StrictCallbackFactory<F>;
struct Foo {
std::string msg;
};
struct Bar {
std::string msg;
};
struct Baz {
std::string msg;
};
class MockStateManager : public BorealisStateManager<Foo, Bar, Baz> {
public:
MOCK_METHOD(std::unique_ptr<OnTransition>, GetOnTransition, (), ());
MOCK_METHOD(std::unique_ptr<OffTransition>, GetOffTransition, (), ());
MOCK_METHOD(Bar, GetIsTurningOffError, (), ());
MOCK_METHOD(Baz, GetIsTurningOnError, (), ());
};
class NonCompletingOnTransition : public MockStateManager::OnTransition {
void Start(std::unique_ptr<MockStateManager::OffState> in) override {}
};
class SucceedingOnTransition : public MockStateManager::OnTransition {
void Start(std::unique_ptr<MockStateManager::OffState> in) override {
Succeed(std::make_unique<Foo>());
}
};
class FailingOnTransition : public MockStateManager::OnTransition {
void Start(std::unique_ptr<MockStateManager::OffState> in) override {
Fail(Bar{.msg = "failure turning on"});
}
};
class NonCompletingOffTransition : public MockStateManager::OffTransition {
void Start(std::unique_ptr<Foo> in) override {}
};
class SucceedingOffTransition : public MockStateManager::OffTransition {
void Start(std::unique_ptr<Foo> in) override { Succeed(nullptr); }
};
class FailingOffTransition : public MockStateManager::OffTransition {
void Start(std::unique_ptr<Foo> in) override {
Fail(Baz{.msg = "failure turning off"});
}
};
TEST(BorealisStateManagerTest, DefaultStateIsOff) {
testing::StrictMock<MockStateManager> state_manager;
CallbackFactory<MockStateManager::WhenOff> on_callback_handler;
// State managers are created in the "Off" state, so we don't need to
// transition there.
EXPECT_CALL(state_manager, GetOffTransition).Times(0);
EXPECT_CALL(on_callback_handler, Call(testing::Eq(absl::nullopt)));
state_manager.TurnOff(on_callback_handler.BindOnce());
}
TEST(BorealisStateManagerTest, CanBeTurnedOnAndOff) {
base::test::SingleThreadTaskEnvironment task_environment;
testing::StrictMock<MockStateManager> state_manager;
CallbackFactory<MockStateManager::WhenOn> on_callback_handler;
CallbackFactory<MockStateManager::WhenOff> off_callback_handler;
EXPECT_CALL(state_manager, GetOnTransition).WillOnce(testing::Invoke([]() {
return std::make_unique<SucceedingOnTransition>();
}));
EXPECT_CALL(state_manager, GetOffTransition).WillOnce(testing::Invoke([]() {
return std::make_unique<SucceedingOffTransition>();
}));
EXPECT_CALL(on_callback_handler, Call(testing::_))
.WillOnce(testing::Invoke(
[](Expected<Foo*, Bar> result) { EXPECT_TRUE(result); }));
EXPECT_CALL(off_callback_handler, Call(testing::Eq(absl::nullopt)));
state_manager.TurnOn(on_callback_handler.BindOnce());
task_environment.RunUntilIdle();
state_manager.TurnOff(off_callback_handler.BindOnce());
task_environment.RunUntilIdle();
}
TEST(BorealisStateManagerTest, CanHandleMultipleCallbacks) {
base::test::SingleThreadTaskEnvironment task_environment;
testing::StrictMock<MockStateManager> state_manager;
CallbackFactory<MockStateManager::WhenOn> on_callback_handler;
EXPECT_CALL(state_manager, GetOnTransition).WillOnce(testing::Invoke([]() {
return std::make_unique<SucceedingOnTransition>();
}));
state_manager.TurnOn(on_callback_handler.BindOnce());
state_manager.TurnOn(on_callback_handler.BindOnce());
// The above two callbacks will not be run until the sequence gets a chance to
// execute. We assure this by making the expectations after them.
EXPECT_CALL(on_callback_handler, Call(testing::_))
.Times(3)
.WillRepeatedly(testing::Invoke(
[](Expected<Foo*, Bar> result) { EXPECT_TRUE(result); }));
// The two callbacks will have a chance to run now.
task_environment.RunUntilIdle();
state_manager.TurnOn(on_callback_handler.BindOnce());
}
TEST(BorealisStateManagerTest, TurnOffRejectedWhileTurningOn) {
testing::StrictMock<MockStateManager> state_manager;
CallbackFactory<MockStateManager::WhenOff> off_callback_handler;
EXPECT_CALL(state_manager, GetOnTransition).WillOnce(testing::Invoke([]() {
return std::make_unique<NonCompletingOnTransition>();
}));
EXPECT_CALL(state_manager, GetIsTurningOnError)
.WillOnce(testing::Return(Baz{.msg = "rejected"}));
EXPECT_CALL(off_callback_handler, Call(testing::_))
.WillOnce(testing::Invoke([](absl::optional<Baz> err) {
ASSERT_TRUE(err.has_value());
EXPECT_EQ(err->msg, "rejected");
}));
state_manager.TurnOn(base::DoNothing());
state_manager.TurnOff(off_callback_handler.BindOnce());
}
TEST(BorealisStateManagerTest, TurnOnRejectedWhileTurningOff) {
base::test::SingleThreadTaskEnvironment task_environment;
testing::StrictMock<MockStateManager> state_manager;
CallbackFactory<MockStateManager::WhenOn> on_callback_handler;
EXPECT_CALL(state_manager, GetOnTransition).WillOnce(testing::Invoke([]() {
return std::make_unique<SucceedingOnTransition>();
}));
EXPECT_CALL(state_manager, GetOffTransition).WillOnce(testing::Invoke([]() {
return std::make_unique<NonCompletingOffTransition>();
}));
EXPECT_CALL(state_manager, GetIsTurningOffError)
.WillOnce(testing::Return(Bar{.msg = "blocked"}));
EXPECT_CALL(on_callback_handler, Call(testing::_))
.WillOnce(testing::Invoke([](Expected<Foo*, Bar> result) {
EXPECT_TRUE(result.Unexpected());
EXPECT_EQ(result.Error().msg, "blocked");
}));
state_manager.TurnOn(base::DoNothing());
task_environment.RunUntilIdle();
state_manager.TurnOff(base::DoNothing());
state_manager.TurnOn(on_callback_handler.BindOnce());
}
TEST(BorealisStateManagerTest, FailureToTurnOnProducesAnErrorAndResultsInOff) {
base::test::SingleThreadTaskEnvironment task_environment;
testing::StrictMock<MockStateManager> state_manager;
CallbackFactory<MockStateManager::WhenOn> on_callback_handler;
EXPECT_CALL(state_manager, GetOnTransition).WillOnce(testing::Invoke([]() {
return std::make_unique<FailingOnTransition>();
}));
EXPECT_CALL(on_callback_handler, Call(testing::_))
.WillOnce(testing::Invoke(
[](Expected<Foo*, Bar> result) { EXPECT_FALSE(result); }));
state_manager.TurnOn(on_callback_handler.BindOnce());
task_environment.RunUntilIdle();
// Additional call to turn off requires no transition, because the state is
// off.
CallbackFactory<MockStateManager::WhenOff> off_callback_handler;
EXPECT_CALL(off_callback_handler, Call(testing::Eq(absl::nullopt)));
state_manager.TurnOff(off_callback_handler.BindOnce());
}
TEST(BorealisStateManagerTest, FailureToTurnOffProducesErrorButDoesTurnOff) {
base::test::SingleThreadTaskEnvironment task_environment;
testing::StrictMock<MockStateManager> state_manager;
CallbackFactory<MockStateManager::WhenOn> on_callback_handler;
CallbackFactory<MockStateManager::WhenOff> off_callback_handler;
EXPECT_CALL(state_manager, GetOnTransition).WillOnce(testing::Invoke([]() {
return std::make_unique<SucceedingOnTransition>();
}));
EXPECT_CALL(state_manager, GetOffTransition).WillOnce(testing::Invoke([]() {
return std::make_unique<FailingOffTransition>();
}));
EXPECT_CALL(on_callback_handler, Call(testing::_))
.WillOnce(testing::Invoke(
[](Expected<Foo*, Bar> result) { EXPECT_TRUE(result); }));
EXPECT_CALL(off_callback_handler,
Call(testing::Not(testing::Eq(absl::nullopt))));
state_manager.TurnOn(on_callback_handler.BindOnce());
task_environment.RunUntilIdle();
state_manager.TurnOff(off_callback_handler.BindOnce());
task_environment.RunUntilIdle();
// Additional call to turn off requires no transition, because the state is
// off.
EXPECT_CALL(off_callback_handler, Call(testing::Eq(absl::nullopt)));
state_manager.TurnOff(off_callback_handler.BindOnce());
}
} // namespace
} // namespace borealis