#include "device/gamepad/gamepad_service.h"
#include <string.h>
#include <memory>
#include <optional>
#include "base/barrier_closure.h"
#include "base/compiler_specific.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "device/gamepad/gamepad_consumer.h"
#include "device/gamepad/gamepad_test_helpers.h"
#include "device/gamepad/public/cpp/gamepad_features.h"
#include "device/gamepad/simulated_gamepad_data_fetcher.h"
#include "device/gamepad/simulated_gamepad_params.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace device {
namespace {
using ::base::test::InvokeFuture;
using ::base::test::RunClosure;
using ::base::test::TestFuture;
constexpr int kNumberOfGamepads = Gamepads::kItemsLengthCap;
}
class MockGamepadConsumer : public GamepadConsumer {
public:
MockGamepadConsumer() {
EXPECT_CALL(*this, OnGamepadConnected).Times(0);
EXPECT_CALL(*this, OnGamepadDisconnected).Times(0);
EXPECT_CALL(*this, OnGamepadRawInputChanged).Times(0);
}
MockGamepadConsumer(MockGamepadConsumer&) = delete;
MockGamepadConsumer& operator=(MockGamepadConsumer&) = delete;
~MockGamepadConsumer() override = default;
MOCK_METHOD2(OnGamepadConnected, void(uint32_t, const Gamepad&));
MOCK_METHOD2(OnGamepadDisconnected, void(uint32_t, const Gamepad&));
MOCK_METHOD2(OnGamepadRawInputChanged, void(uint32_t, const Gamepad&));
};
class GamepadServiceTest : public testing::Test {
public:
GamepadServiceTest(const GamepadServiceTest&) = delete;
GamepadServiceTest& operator=(const GamepadServiceTest&) = delete;
protected:
GamepadServiceTest() {
UNSAFE_TODO(memset(&test_data_, 0, sizeof(test_data_)));
test_data_.items[0].buttons_length = 1;
}
~GamepadServiceTest() override = default;
GamepadService* service() const { return service_; }
void SetUp() override {
service_ = new GamepadService(CreateTestDataFetcher());
service_->SetSanitizationEnabled(false);
}
virtual std::unique_ptr<GamepadDataFetcher> CreateTestDataFetcher() {
auto fetcher = std::make_unique<MockGamepadDataFetcher>(test_data_);
fetcher_ = fetcher.get();
return fetcher;
}
void TearDown() override {
fetcher_ = nullptr;
service_->Terminate();
service_ = nullptr;
GamepadService::SetInstance(nullptr);
}
MockGamepadConsumer* CreateConsumer() {
consumers_.push_back(std::make_unique<MockGamepadConsumer>());
return consumers_.back().get();
}
void SetPadsConnected(int connected_count) {
for (int i = 0; i < kNumberOfGamepads; ++i)
test_data_.items[i].connected = (i < connected_count);
fetcher_->SetTestData(test_data_);
}
void SimulateUserGesture(bool has_gesture) {
test_data_.items[0].buttons[0].value = has_gesture ? 1.0f : 0.0f;
test_data_.items[0].buttons[0].pressed = has_gesture ? true : false;
fetcher_->SetTestData(test_data_);
}
void SimulatePageReload(GamepadConsumer* consumer) {
EXPECT_TRUE(service_->ConsumerBecameInactive(consumer));
EXPECT_TRUE(service_->ConsumerBecameActive(consumer));
}
void WaitForData() {
fetcher_->WaitForDataReadAndCallbacksIssued();
base::RunLoop().RunUntilIdle();
}
private:
base::test::SingleThreadTaskEnvironment task_environment_;
raw_ptr<MockGamepadDataFetcher> fetcher_ = nullptr;
raw_ptr<GamepadService> service_ = nullptr;
std::vector<std::unique_ptr<MockGamepadConsumer>> consumers_;
Gamepads test_data_;
};
TEST_F(GamepadServiceTest, ConnectionsTest) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
WaitForData();
{
base::RunLoop loop;
auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer, OnGamepadConnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
SimulateUserGesture(true);
SetPadsConnected(kNumberOfGamepads);
loop.Run();
}
{
base::RunLoop loop;
auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer, OnGamepadDisconnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
SetPadsConnected(0);
loop.Run();
}
}
TEST_F(GamepadServiceTest, ConnectionThenGestureTest) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
WaitForData();
SetPadsConnected(kNumberOfGamepads);
WaitForData();
{
base::RunLoop loop;
auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer, OnGamepadConnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
SimulateUserGesture(true);
loop.Run();
}
}
TEST_F(GamepadServiceTest, ReloadTest) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
WaitForData();
SetPadsConnected(kNumberOfGamepads);
WaitForData();
SimulatePageReload(consumer);
WaitForData();
{
base::RunLoop loop;
auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer, OnGamepadConnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
SimulateUserGesture(true);
loop.Run();
}
{
base::RunLoop loop;
auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer, OnGamepadConnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
SimulatePageReload(consumer);
loop.Run();
}
}
TEST_F(GamepadServiceTest, SecondConsumerGestureTest) {
auto* consumer1 = CreateConsumer();
auto* consumer2 = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer1));
WaitForData();
{
base::RunLoop loop;
auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer1, OnGamepadConnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
SetPadsConnected(kNumberOfGamepads);
SimulateUserGesture(true);
loop.Run();
}
SimulateUserGesture(false);
EXPECT_TRUE(service()->ConsumerBecameActive(consumer2));
WaitForData();
{
base::RunLoop loop;
auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer2, OnGamepadConnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
SetPadsConnected(kNumberOfGamepads);
SimulateUserGesture(true);
loop.Run();
}
}
TEST_F(GamepadServiceTest, ConnectWhileInactiveTest) {
auto* consumer1 = CreateConsumer();
auto* consumer2 = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer1));
EXPECT_TRUE(service()->ConsumerBecameActive(consumer2));
{
base::RunLoop loop;
auto barrier =
base::BarrierClosure(2 * kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer1, OnGamepadConnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
EXPECT_CALL(*consumer2, OnGamepadConnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
SimulateUserGesture(true);
SetPadsConnected(kNumberOfGamepads);
loop.Run();
}
{
base::RunLoop loop;
auto barrier =
base::BarrierClosure(2 * kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer1, OnGamepadDisconnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
EXPECT_CALL(*consumer2, OnGamepadDisconnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
SetPadsConnected(0);
loop.Run();
}
EXPECT_TRUE(service()->ConsumerBecameInactive(consumer2));
WaitForData();
{
base::RunLoop loop;
auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer1, OnGamepadConnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
SetPadsConnected(kNumberOfGamepads);
loop.Run();
}
{
base::RunLoop loop;
auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer2, OnGamepadConnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
EXPECT_TRUE(service()->ConsumerBecameActive(consumer2));
loop.Run();
}
}
TEST_F(GamepadServiceTest, DISABLED_ConnectAndDisconnectWhileInactiveTest) {
auto* consumer1 = CreateConsumer();
auto* consumer2 = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer1));
EXPECT_TRUE(service()->ConsumerBecameActive(consumer2));
{
base::RunLoop loop;
auto barrier = base::BarrierClosure(2, loop.QuitClosure());
EXPECT_CALL(*consumer1, OnGamepadConnected)
.Times(1)
.WillRepeatedly(RunClosure(barrier));
EXPECT_CALL(*consumer2, OnGamepadConnected)
.Times(1)
.WillRepeatedly(RunClosure(barrier));
SetPadsConnected(1);
SimulateUserGesture(true);
loop.Run();
}
{
base::RunLoop loop;
auto barrier = base::BarrierClosure(2, loop.QuitClosure());
EXPECT_CALL(*consumer1, OnGamepadDisconnected)
.Times(1)
.WillRepeatedly(RunClosure(barrier));
EXPECT_CALL(*consumer2, OnGamepadDisconnected)
.Times(1)
.WillRepeatedly(RunClosure(barrier));
SetPadsConnected(0);
loop.Run();
}
EXPECT_TRUE(service()->ConsumerBecameInactive(consumer2));
WaitForData();
{
base::RunLoop loop;
auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer1, OnGamepadConnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
SetPadsConnected(kNumberOfGamepads);
loop.Run();
}
{
base::RunLoop loop;
auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer1, OnGamepadDisconnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
SetPadsConnected(0);
loop.Run();
}
EXPECT_TRUE(service()->ConsumerBecameActive(consumer2));
WaitForData();
}
TEST_F(GamepadServiceTest, DISABLED_DisconnectWhileInactiveTest) {
auto* consumer1 = CreateConsumer();
auto* consumer2 = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer1));
EXPECT_TRUE(service()->ConsumerBecameActive(consumer2));
{
base::RunLoop loop;
auto barrier =
base::BarrierClosure(2 * kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer1, OnGamepadConnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
EXPECT_CALL(*consumer2, OnGamepadConnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
SetPadsConnected(kNumberOfGamepads);
SimulateUserGesture(true);
loop.Run();
}
EXPECT_TRUE(service()->ConsumerBecameInactive(consumer2));
WaitForData();
{
base::RunLoop loop;
auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer1, OnGamepadDisconnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
SetPadsConnected(0);
loop.Run();
}
{
base::RunLoop loop;
auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer2, OnGamepadDisconnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
EXPECT_TRUE(service()->ConsumerBecameActive(consumer2));
loop.Run();
}
}
TEST_F(GamepadServiceTest, DisconnectAndConnectWhileInactiveTest) {
auto* consumer1 = CreateConsumer();
auto* consumer2 = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer1));
EXPECT_TRUE(service()->ConsumerBecameActive(consumer2));
{
base::RunLoop loop;
auto barrier =
base::BarrierClosure(2 * kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer1, OnGamepadConnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
EXPECT_CALL(*consumer2, OnGamepadConnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
SetPadsConnected(kNumberOfGamepads);
SimulateUserGesture(true);
loop.Run();
}
EXPECT_TRUE(service()->ConsumerBecameInactive(consumer2));
WaitForData();
{
base::RunLoop loop;
auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer1, OnGamepadDisconnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
SetPadsConnected(0);
loop.Run();
}
{
base::RunLoop loop;
auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer1, OnGamepadConnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
SetPadsConnected(kNumberOfGamepads);
loop.Run();
}
{
base::RunLoop loop;
auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
EXPECT_CALL(*consumer2, OnGamepadConnected)
.Times(kNumberOfGamepads)
.WillRepeatedly(RunClosure(barrier));
EXPECT_TRUE(service()->ConsumerBecameActive(consumer2));
loop.Run();
}
}
TEST_F(GamepadServiceTest, ActiveConsumerBecameActive) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
EXPECT_FALSE(service()->ConsumerBecameActive(consumer));
}
TEST_F(GamepadServiceTest, InactiveConsumerBecameInactive) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
EXPECT_TRUE(service()->ConsumerBecameInactive(consumer));
EXPECT_FALSE(service()->ConsumerBecameInactive(consumer));
}
TEST_F(GamepadServiceTest, UnregisteredConsumerBecameInactive) {
auto* consumer = CreateConsumer();
EXPECT_FALSE(service()->ConsumerBecameInactive(consumer));
}
TEST_F(GamepadServiceTest, RemoveUnregisteredConsumer) {
auto* consumer = CreateConsumer();
EXPECT_FALSE(service()->RemoveConsumer(consumer));
}
class GamepadServiceSimulationTest : public GamepadServiceTest {
public:
std::unique_ptr<GamepadDataFetcher> CreateTestDataFetcher() override {
auto fetcher = std::make_unique<SimulatedGamepadDataFetcher>();
fetcher->SetOnPollForTesting(base::BindRepeating(
&GamepadServiceSimulationTest::OnPoll, base::Unretained(this)));
return fetcher;
}
void OnPoll() {
if (poll_loop_.has_value()) {
poll_loop_.value().Quit();
}
}
void WaitForPoll() {
poll_loop_.emplace();
poll_loop_.value().Run();
poll_loop_.reset();
}
protected:
base::UnguessableToken SetupRawInputGamepad(MockGamepadConsumer* consumer,
SimulatedGamepadParams params) {
params.name = "Raw input test gamepad";
auto token = service()->AddSimulatedGamepad(std::move(params));
TestFuture<uint32_t, const Gamepad&> connected_future;
EXPECT_CALL(*consumer, OnGamepadConnected)
.WillOnce(InvokeFuture(connected_future));
service()->SimulateButtonInput(token, 0, 1.0,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
EXPECT_EQ(connected_future.Get<0>(), 0u);
EXPECT_TRUE(connected_future.Get<1>().connected);
return token;
}
void CleanupRawInputGamepad(MockGamepadConsumer* consumer,
base::UnguessableToken token) {
TestFuture<uint32_t, const Gamepad&> disconnected_future;
EXPECT_CALL(*consumer, OnGamepadDisconnected)
.WillOnce(InvokeFuture(disconnected_future));
service()->RemoveSimulatedGamepad(token);
EXPECT_EQ(disconnected_future.Get<0>(), 0u);
EXPECT_FALSE(disconnected_future.Get<1>().connected);
}
private:
std::optional<base::RunLoop> poll_loop_;
};
TEST_F(GamepadServiceSimulationTest, ConnectDisconnect) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.name = "1 button";
params.button_bounds = {std::nullopt};
auto token = service()->AddSimulatedGamepad(std::move(params));
TestFuture<uint32_t, const Gamepad&> connected_future;
EXPECT_CALL(*consumer, OnGamepadConnected)
.WillOnce(InvokeFuture(connected_future));
service()->SimulateButtonInput(token, 0, 1.0,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
EXPECT_EQ(connected_future.Get<0>(), 0u);
EXPECT_TRUE(connected_future.Get<1>().connected);
TestFuture<uint32_t, const Gamepad&> disconnected_future;
EXPECT_CALL(*consumer, OnGamepadDisconnected)
.WillOnce(InvokeFuture(disconnected_future));
service()->RemoveSimulatedGamepad(token);
EXPECT_EQ(disconnected_future.Get<0>(), 0u);
EXPECT_FALSE(disconnected_future.Get<1>().connected);
}
TEST_F(GamepadServiceSimulationTest, ConnectDisconnectMultiple) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params1;
params1.name = "1 button";
params1.button_bounds = {std::nullopt};
auto token1 = service()->AddSimulatedGamepad(std::move(params1));
TestFuture<uint32_t, const Gamepad&> connected_future1;
EXPECT_CALL(*consumer, OnGamepadConnected)
.WillOnce(InvokeFuture(connected_future1));
service()->SimulateButtonInput(token1, 0, 1.0,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token1);
EXPECT_EQ(connected_future1.Get<0>(), 0u);
EXPECT_EQ(connected_future1.Get<1>().buttons_length, 1u);
SimulatedGamepadParams params2;
params2.name = "2 buttons";
params2.button_bounds = {std::nullopt, std::nullopt};
TestFuture<uint32_t, const Gamepad&> connected_future2;
EXPECT_CALL(*consumer, OnGamepadConnected)
.WillOnce(InvokeFuture(connected_future2));
auto token2 = service()->AddSimulatedGamepad(std::move(params2));
EXPECT_NE(token1, token2);
EXPECT_EQ(connected_future2.Get<0>(), 1u);
EXPECT_EQ(connected_future2.Get<1>().buttons_length, 2u);
TestFuture<uint32_t, const Gamepad&> disconnected_future1;
EXPECT_CALL(*consumer, OnGamepadDisconnected)
.WillOnce(InvokeFuture(disconnected_future1));
service()->RemoveSimulatedGamepad(token1);
EXPECT_EQ(disconnected_future1.Get<0>(), 0u);
EXPECT_EQ(disconnected_future1.Get<1>().buttons_length, 1u);
TestFuture<uint32_t, const Gamepad&> disconnected_future2;
EXPECT_CALL(*consumer, OnGamepadDisconnected)
.WillOnce(InvokeFuture(disconnected_future2));
service()->RemoveSimulatedGamepad(token2);
EXPECT_EQ(disconnected_future2.Get<0>(), 1u);
EXPECT_EQ(disconnected_future2.Get<1>().buttons_length, 2u);
}
TEST_F(GamepadServiceSimulationTest, SimulateButtonInput) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.name = "1 button";
params.button_bounds = {GamepadLogicalBounds(0.0, 255.0)};
auto token = service()->AddSimulatedGamepad(std::move(params));
TestFuture<uint32_t, const Gamepad&> connected_future;
EXPECT_CALL(*consumer, OnGamepadConnected)
.WillOnce(InvokeFuture(connected_future));
service()->SimulateButtonInput(token, 0, 255.0,
true, true);
service()->SimulateInputFrame(token);
EXPECT_EQ(connected_future.Get<0>(), 0u);
const Gamepad& connected_gamepad = connected_future.Get<1>();
EXPECT_EQ(connected_gamepad.buttons_length, 1u);
EXPECT_EQ(connected_gamepad.buttons[0].value, 1.0);
EXPECT_TRUE(connected_gamepad.buttons[0].pressed);
EXPECT_TRUE(connected_gamepad.buttons[0].touched);
service()->SimulateButtonInput(token, 0, 0.0,
false, false);
service()->SimulateInputFrame(token);
WaitForPoll();
TestFuture<uint32_t, const Gamepad&> disconnected_future;
EXPECT_CALL(*consumer, OnGamepadDisconnected)
.WillOnce(InvokeFuture(disconnected_future));
service()->RemoveSimulatedGamepad(token);
EXPECT_EQ(disconnected_future.Get<0>(), 0u);
const Gamepad& disconnected_gamepad = disconnected_future.Get<1>();
EXPECT_EQ(disconnected_gamepad.buttons_length, 1u);
EXPECT_EQ(disconnected_gamepad.buttons[0].value, 0.0);
EXPECT_FALSE(disconnected_gamepad.buttons[0].pressed);
EXPECT_FALSE(disconnected_gamepad.buttons[0].touched);
}
TEST_F(GamepadServiceSimulationTest, SimulateAxisInput) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.name = "1 axis 1 button";
params.axis_bounds = {GamepadLogicalBounds(0.0, 255.0)};
params.button_bounds = {std::nullopt};
auto token = service()->AddSimulatedGamepad(std::move(params));
TestFuture<uint32_t, const Gamepad&> connected_future;
EXPECT_CALL(*consumer, OnGamepadConnected)
.WillOnce(InvokeFuture(connected_future));
service()->SimulateButtonInput(token, 0, 1.0,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
EXPECT_EQ(connected_future.Get<0>(), 0u);
service()->SimulateAxisInput(token, 0, 255.0);
service()->SimulateInputFrame(token);
WaitForPoll();
TestFuture<uint32_t, const Gamepad&> disconnected_future;
EXPECT_CALL(*consumer, OnGamepadDisconnected)
.WillOnce(InvokeFuture(disconnected_future));
service()->RemoveSimulatedGamepad(token);
EXPECT_EQ(disconnected_future.Get<0>(), 0u);
const Gamepad& disconnected_gamepad = disconnected_future.Get<1>();
EXPECT_EQ(disconnected_gamepad.axes_length, 1u);
EXPECT_EQ(disconnected_gamepad.axes[0], 1.0);
}
TEST_F(GamepadServiceSimulationTest, SimulateTouchInput) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.name = "1 button with touchpad";
params.button_bounds = {std::nullopt};
params.touch_surface_bounds = {std::nullopt};
auto token = service()->AddSimulatedGamepad(std::move(params));
TestFuture<uint32_t, const Gamepad&> connected_future;
EXPECT_CALL(*consumer, OnGamepadConnected)
.WillOnce(InvokeFuture(connected_future));
service()->SimulateButtonInput(token, 0, 1.0,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
EXPECT_EQ(connected_future.Get<0>(), 0u);
auto touch_id =
service()->SimulateTouchInput(token, 0, 0.0,
0.0);
EXPECT_TRUE(touch_id.has_value());
service()->SimulateInputFrame(token);
WaitForPoll();
TestFuture<uint32_t, const Gamepad&> disconnected_future;
EXPECT_CALL(*consumer, OnGamepadDisconnected)
.WillOnce(InvokeFuture(disconnected_future));
service()->RemoveSimulatedGamepad(token);
EXPECT_EQ(disconnected_future.Get<0>(), 0u);
const Gamepad& disconnected_gamepad = disconnected_future.Get<1>();
EXPECT_EQ(disconnected_gamepad.touch_events_length, 1u);
EXPECT_EQ(disconnected_gamepad.touch_events[0].touch_id, touch_id.value());
EXPECT_EQ(disconnected_gamepad.touch_events[0].surface_id, 0);
EXPECT_EQ(disconnected_gamepad.touch_events[0].x, 0.0);
EXPECT_EQ(disconnected_gamepad.touch_events[0].y, 0.0);
EXPECT_FALSE(disconnected_gamepad.touch_events[0].has_surface_dimensions);
}
TEST_F(GamepadServiceSimulationTest, SimulateTouchMove) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.name = "1 button with touchpad";
params.button_bounds = {std::nullopt};
params.touch_surface_bounds = {std::nullopt};
auto token = service()->AddSimulatedGamepad(std::move(params));
TestFuture<uint32_t, const Gamepad&> connected_future;
EXPECT_CALL(*consumer, OnGamepadConnected)
.WillOnce(InvokeFuture(connected_future));
service()->SimulateButtonInput(token, 0, 1.0,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
EXPECT_EQ(connected_future.Get<0>(), 0u);
auto touch_id =
service()->SimulateTouchInput(token, 0, 0.0,
0.0);
ASSERT_TRUE(touch_id.has_value());
service()->SimulateInputFrame(token);
service()->SimulateTouchMove(token, touch_id.value(), 1.0,
1.0);
service()->SimulateInputFrame(token);
WaitForPoll();
TestFuture<uint32_t, const Gamepad&> disconnected_future;
EXPECT_CALL(*consumer, OnGamepadDisconnected)
.WillOnce(InvokeFuture(disconnected_future));
service()->RemoveSimulatedGamepad(token);
EXPECT_EQ(disconnected_future.Get<0>(), 0u);
const Gamepad& disconnected_gamepad = disconnected_future.Get<1>();
EXPECT_EQ(disconnected_gamepad.touch_events_length, 1u);
EXPECT_EQ(disconnected_gamepad.touch_events[0].touch_id, touch_id.value());
EXPECT_EQ(disconnected_gamepad.touch_events[0].x, 1.0);
EXPECT_EQ(disconnected_gamepad.touch_events[0].y, 1.0);
}
TEST_F(GamepadServiceSimulationTest, SimulateTouchEnd) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.name = "1 button with touchpad";
params.button_bounds = {std::nullopt};
params.touch_surface_bounds = {std::nullopt};
auto token = service()->AddSimulatedGamepad(std::move(params));
TestFuture<uint32_t, const Gamepad&> connected_future;
EXPECT_CALL(*consumer, OnGamepadConnected)
.WillOnce(InvokeFuture(connected_future));
service()->SimulateButtonInput(token, 0, 1.0,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
EXPECT_EQ(connected_future.Get<0>(), 0u);
auto touch_id0 =
service()->SimulateTouchInput(token, 0, 0.0,
0.0);
ASSERT_TRUE(touch_id0.has_value());
auto touch_id1 =
service()->SimulateTouchInput(token, 0, 0.0,
1.0);
ASSERT_TRUE(touch_id1.has_value());
service()->SimulateInputFrame(token);
service()->SimulateTouchEnd(token, touch_id0.value());
service()->SimulateInputFrame(token);
WaitForPoll();
TestFuture<uint32_t, const Gamepad&> disconnected_future;
EXPECT_CALL(*consumer, OnGamepadDisconnected)
.WillOnce(InvokeFuture(disconnected_future));
service()->RemoveSimulatedGamepad(token);
EXPECT_EQ(disconnected_future.Get<0>(), 0u);
const Gamepad& disconnected_gamepad = disconnected_future.Get<1>();
EXPECT_EQ(disconnected_gamepad.touch_events_length, 1u);
EXPECT_EQ(disconnected_gamepad.touch_events[0].touch_id, touch_id1.value());
}
TEST_F(GamepadServiceSimulationTest, Vibration) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.name = "1 button with vibration";
params.button_bounds = {std::nullopt};
params.vibration = {GamepadHapticEffectType::kDualRumble};
auto token = service()->AddSimulatedGamepad(std::move(params));
TestFuture<uint32_t, const Gamepad&> connected_future;
EXPECT_CALL(*consumer, OnGamepadConnected)
.WillOnce(InvokeFuture(connected_future));
service()->SimulateButtonInput(token, 0, 1.0,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
EXPECT_EQ(connected_future.Get<0>(), 0u);
EXPECT_TRUE(connected_future.Get<1>().vibration_actuator.not_null);
EXPECT_EQ(connected_future.Get<1>().vibration_actuator.type,
GamepadHapticActuatorType::kDualRumble);
TestFuture<mojom::GamepadHapticsResult> play_future;
auto effect_params = mojom::GamepadEffectParameters::New();
effect_params->strong_magnitude = 1.0;
effect_params->weak_magnitude = 1.0;
service()->PlayVibrationEffectOnce(
0,
mojom::GamepadHapticEffectType::GamepadHapticEffectTypeDualRumble,
std::move(effect_params), play_future.GetCallback());
EXPECT_EQ(play_future.Get(),
mojom::GamepadHapticsResult::GamepadHapticsResultComplete);
TestFuture<mojom::GamepadHapticsResult> reset_future;
service()->ResetVibrationActuator(0,
reset_future.GetCallback());
EXPECT_EQ(reset_future.Get(),
mojom::GamepadHapticsResult::GamepadHapticsResultComplete);
TestFuture<uint32_t, const Gamepad&> disconnected_future;
EXPECT_CALL(*consumer, OnGamepadDisconnected)
.WillOnce(InvokeFuture(disconnected_future));
service()->RemoveSimulatedGamepad(token);
EXPECT_EQ(disconnected_future.Get<0>(), 0u);
}
TEST_F(GamepadServiceSimulationTest, TokenNotFound) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.name = "1 button";
params.button_bounds = {std::nullopt};
auto token = service()->AddSimulatedGamepad(std::move(params));
TestFuture<uint32_t, const Gamepad&> connected_future;
EXPECT_CALL(*consumer, OnGamepadConnected)
.WillOnce(InvokeFuture(connected_future));
service()->SimulateButtonInput(token, 0, 1.0,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
EXPECT_EQ(connected_future.Get<0>(), 0u);
EXPECT_CALL(*consumer, OnGamepadConnected).Times(0);
EXPECT_CALL(*consumer, OnGamepadDisconnected).Times(0);
auto random_token = base::UnguessableToken::Create();
service()->RemoveSimulatedGamepad(random_token);
service()->SimulateAxisInput(random_token, 0,
0.0);
service()->SimulateButtonInput(
random_token, 0, 0.0,
std::nullopt, std::nullopt);
EXPECT_EQ(std::nullopt, service()->SimulateTouchInput(
random_token, 0, 0.0,
0.0));
service()->SimulateTouchMove(random_token, 0, 0.0,
0.0);
service()->SimulateTouchEnd(random_token, 0);
service()->SimulateInputFrame(random_token);
}
TEST_F(GamepadServiceSimulationTest, DISABLED_RemoveGamepadTwice) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.name = "1 button";
params.button_bounds = {std::nullopt};
auto token = service()->AddSimulatedGamepad(std::move(params));
TestFuture<uint32_t, const Gamepad&> connected_future;
EXPECT_CALL(*consumer, OnGamepadConnected)
.WillOnce(InvokeFuture(connected_future));
service()->SimulateButtonInput(token, 0, 1.0,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
EXPECT_EQ(connected_future.Get<0>(), 0u);
TestFuture<uint32_t, const Gamepad&> disconnected_future;
EXPECT_CALL(*consumer, OnGamepadDisconnected)
.WillOnce(InvokeFuture(disconnected_future));
service()->RemoveSimulatedGamepad(token);
EXPECT_EQ(disconnected_future.Get<0>(), 0u);
EXPECT_CALL(*consumer, OnGamepadDisconnected).Times(0);
service()->RemoveSimulatedGamepad(token);
}
TEST_F(GamepadServiceSimulationTest, InvalidButtonIndex) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.name = "1 button";
params.button_bounds = {GamepadLogicalBounds(0.0, 255.0)};
auto token = service()->AddSimulatedGamepad(std::move(params));
TestFuture<uint32_t, const Gamepad&> connected_future;
EXPECT_CALL(*consumer, OnGamepadConnected)
.WillOnce(InvokeFuture(connected_future));
service()->SimulateButtonInput(token, 0, 255.0,
true, true);
service()->SimulateInputFrame(token);
EXPECT_EQ(connected_future.Get<0>(), 0u);
const Gamepad& connected_gamepad = connected_future.Get<1>();
EXPECT_EQ(connected_gamepad.buttons_length, 1u);
EXPECT_EQ(connected_gamepad.buttons[0].value, 1.0);
EXPECT_TRUE(connected_gamepad.buttons[0].pressed);
EXPECT_TRUE(connected_gamepad.buttons[0].touched);
service()->SimulateButtonInput(token, 12345, 0.0,
false, false);
service()->SimulateInputFrame(token);
WaitForPoll();
TestFuture<uint32_t, const Gamepad&> disconnected_future;
EXPECT_CALL(*consumer, OnGamepadDisconnected)
.WillOnce(InvokeFuture(disconnected_future));
service()->RemoveSimulatedGamepad(token);
EXPECT_EQ(disconnected_future.Get<0>(), 0u);
}
TEST_F(GamepadServiceSimulationTest, InvalidAxisIndex) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.name = "1 axis 1 button";
params.axis_bounds = {GamepadLogicalBounds(0.0, 255.0)};
params.button_bounds = {std::nullopt};
auto token = service()->AddSimulatedGamepad(std::move(params));
TestFuture<uint32_t, const Gamepad&> connected_future;
EXPECT_CALL(*consumer, OnGamepadConnected)
.WillOnce(InvokeFuture(connected_future));
service()->SimulateButtonInput(token, 0, 1.0,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
EXPECT_EQ(connected_future.Get<0>(), 0u);
service()->SimulateAxisInput(token, 12345, 255.0);
service()->SimulateInputFrame(token);
WaitForPoll();
TestFuture<uint32_t, const Gamepad&> disconnected_future;
EXPECT_CALL(*consumer, OnGamepadDisconnected)
.WillOnce(InvokeFuture(disconnected_future));
service()->RemoveSimulatedGamepad(token);
EXPECT_EQ(disconnected_future.Get<0>(), 0u);
const Gamepad& disconnected_gamepad = disconnected_future.Get<1>();
EXPECT_EQ(disconnected_gamepad.axes_length, 1u);
}
TEST_F(GamepadServiceSimulationTest, TouchIdNotFound) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.name = "1 button with touchpad";
params.button_bounds = {std::nullopt};
params.touch_surface_bounds = {std::nullopt};
auto token = service()->AddSimulatedGamepad(std::move(params));
TestFuture<uint32_t, const Gamepad&> connected_future;
EXPECT_CALL(*consumer, OnGamepadConnected)
.WillOnce(InvokeFuture(connected_future));
service()->SimulateButtonInput(token, 0, 1.0,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
EXPECT_EQ(connected_future.Get<0>(), 0u);
auto touch_id =
service()->SimulateTouchInput(token, 0, 0.0,
0.0);
ASSERT_TRUE(touch_id.has_value());
service()->SimulateInputFrame(token);
static constexpr uint32_t kFakeTouchId = 42;
ASSERT_NE(touch_id.value(), kFakeTouchId);
service()->SimulateTouchMove(token, kFakeTouchId, 1.0,
1.0);
service()->SimulateInputFrame(token);
service()->SimulateTouchEnd(token, kFakeTouchId);
service()->SimulateInputFrame(token);
WaitForPoll();
TestFuture<uint32_t, const Gamepad&> disconnected_future;
EXPECT_CALL(*consumer, OnGamepadDisconnected)
.WillOnce(InvokeFuture(disconnected_future));
service()->RemoveSimulatedGamepad(token);
EXPECT_EQ(disconnected_future.Get<0>(), 0u);
const Gamepad& disconnected_gamepad = disconnected_future.Get<1>();
EXPECT_EQ(disconnected_gamepad.touch_events_length, 1u);
EXPECT_EQ(disconnected_gamepad.touch_events[0].touch_id, touch_id.value());
EXPECT_EQ(disconnected_gamepad.touch_events[0].x, 0.0);
EXPECT_EQ(disconnected_gamepad.touch_events[0].y, 0.0);
}
TEST_F(GamepadServiceSimulationTest, SurfaceIdNotFound) {
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.name = "1 button with touchpad";
params.button_bounds = {std::nullopt};
params.touch_surface_bounds = {std::nullopt};
auto token = service()->AddSimulatedGamepad(std::move(params));
TestFuture<uint32_t, const Gamepad&> connected_future;
EXPECT_CALL(*consumer, OnGamepadConnected)
.WillOnce(InvokeFuture(connected_future));
service()->SimulateButtonInput(token, 0, 1.0,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
EXPECT_EQ(connected_future.Get<0>(), 0u);
static constexpr uint32_t kFakeSurfaceId = 42;
auto touch_id =
service()->SimulateTouchInput(token, kFakeSurfaceId, 1.0,
1.0);
ASSERT_FALSE(touch_id.has_value());
WaitForPoll();
TestFuture<uint32_t, const Gamepad&> disconnected_future;
EXPECT_CALL(*consumer, OnGamepadDisconnected)
.WillOnce(InvokeFuture(disconnected_future));
service()->RemoveSimulatedGamepad(token);
EXPECT_EQ(disconnected_future.Get<0>(), 0u);
const Gamepad& disconnected_gamepad = disconnected_future.Get<1>();
EXPECT_EQ(disconnected_gamepad.touch_events_length, 0u);
}
TEST_F(GamepadServiceSimulationTest, RawInputChangeDetectionButton) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
features::kGamepadRawInputChangeEvent);
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.name = "Raw input test gamepad";
params.button_bounds = {std::nullopt};
auto token = SetupRawInputGamepad(consumer, std::move(params));
TestFuture<uint32_t, const Gamepad&> future;
EXPECT_CALL(*consumer, OnGamepadRawInputChanged)
.WillOnce(InvokeFuture(future));
service()->SimulateButtonInput(token, 0, 0.5,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
const auto [id, gamepad] = future.Take();
EXPECT_EQ(gamepad.buttons_length, 1u);
EXPECT_EQ(gamepad.buttons[0].value, 0.5);
CleanupRawInputGamepad(consumer, token);
}
TEST_F(GamepadServiceSimulationTest, RawInputChangeDetectionAxis) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
features::kGamepadRawInputChangeEvent);
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.name = "Raw input test gamepad";
params.button_bounds = {std::nullopt};
params.axis_bounds = {GamepadLogicalBounds(-1.0, 1.0)};
params.touch_surface_bounds = {std::nullopt};
auto token = SetupRawInputGamepad(consumer, std::move(params));
TestFuture<uint32_t, const Gamepad&> future;
EXPECT_CALL(*consumer, OnGamepadRawInputChanged)
.WillOnce(InvokeFuture(future));
service()->SimulateAxisInput(token, 0, 0.5);
service()->SimulateInputFrame(token);
const auto [id, gamepad] = future.Take();
EXPECT_EQ(gamepad.axes_length, 1u);
EXPECT_EQ(gamepad.axes[0], 0.5);
CleanupRawInputGamepad(consumer, token);
}
TEST_F(GamepadServiceSimulationTest, RawInputChangeDetectionTouch) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
features::kGamepadRawInputChangeEvent);
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.name = "Raw input test gamepad";
params.button_bounds = {std::nullopt};
params.axis_bounds = {};
params.touch_surface_bounds = {std::nullopt};
auto token = SetupRawInputGamepad(consumer, std::move(params));
std::optional<uint32_t> touch_id;
{
TestFuture<uint32_t, const Gamepad&> future;
EXPECT_CALL(*consumer, OnGamepadRawInputChanged)
.WillOnce(InvokeFuture(future));
touch_id = service()->SimulateTouchInput(token, 0,
0.3,
0.7);
ASSERT_TRUE(touch_id.has_value());
service()->SimulateInputFrame(token);
const auto [id, gamepad] = future.Take();
EXPECT_EQ(gamepad.touch_events_length, 1u);
EXPECT_EQ(gamepad.touch_events[0].touch_id, touch_id.value());
EXPECT_FLOAT_EQ(gamepad.touch_events[0].x, 0.3f);
EXPECT_FLOAT_EQ(gamepad.touch_events[0].y, 0.7f);
}
WaitForPoll();
{
TestFuture<uint32_t, const Gamepad&> future;
EXPECT_CALL(*consumer, OnGamepadRawInputChanged)
.WillOnce(InvokeFuture(future));
service()->SimulateTouchMove(token, touch_id.value(), 0.6,
0.4);
service()->SimulateInputFrame(token);
const auto [id, gamepad] = future.Take();
EXPECT_EQ(gamepad.touch_events_length, 1u);
EXPECT_EQ(gamepad.touch_events[0].touch_id, touch_id.value());
EXPECT_FLOAT_EQ(gamepad.touch_events[0].x, 0.6f);
EXPECT_FLOAT_EQ(gamepad.touch_events[0].y, 0.4f);
}
WaitForPoll();
{
TestFuture<uint32_t, const Gamepad&> future;
EXPECT_CALL(*consumer, OnGamepadRawInputChanged)
.WillOnce(InvokeFuture(future));
service()->SimulateTouchEnd(token, touch_id.value());
service()->SimulateInputFrame(token);
const auto [id, gamepad] = future.Take();
EXPECT_EQ(gamepad.touch_events_length, 0u);
}
WaitForPoll();
CleanupRawInputGamepad(consumer, token);
}
TEST_F(GamepadServiceSimulationTest,
RawInputChangeDetectionMultipleInputTypes) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
features::kGamepadRawInputChangeEvent);
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.button_bounds = {std::nullopt, std::nullopt};
params.axis_bounds = {GamepadLogicalBounds(-1.0, 1.0)};
params.touch_surface_bounds = {std::nullopt};
auto token = SetupRawInputGamepad(consumer, std::move(params));
std::optional<uint32_t> touch_id;
base::RunLoop loop;
TestFuture<uint32_t, const Gamepad&> future;
EXPECT_CALL(*consumer, OnGamepadRawInputChanged)
.WillOnce(InvokeFuture(future));
service()->SimulateButtonInput(token, 1, 0.8,
std::nullopt,
std::nullopt);
service()->SimulateAxisInput(token, 0, 0.5);
touch_id = service()->SimulateTouchInput(token, 0,
0.2,
0.9);
ASSERT_TRUE(touch_id.has_value());
service()->SimulateInputFrame(token);
const auto [id, gamepad] = future.Take();
EXPECT_EQ(gamepad.buttons_length, 2u);
EXPECT_EQ(gamepad.buttons[1].value, 0.8);
EXPECT_EQ(gamepad.axes_length, 1u);
EXPECT_EQ(gamepad.axes[0], 0.5);
EXPECT_EQ(gamepad.touch_events_length, 1u);
EXPECT_EQ(gamepad.touch_events[0].touch_id, touch_id.value());
EXPECT_FLOAT_EQ(gamepad.touch_events[0].x, 0.2f);
EXPECT_FLOAT_EQ(gamepad.touch_events[0].y, 0.9f);
CleanupRawInputGamepad(consumer, token);
}
TEST_F(GamepadServiceSimulationTest, RawInputChangeRequiresUserGesture) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
features::kGamepadRawInputChangeEvent);
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.name = "1 button";
params.button_bounds = {std::nullopt};
auto token = service()->AddSimulatedGamepad(std::move(params));
EXPECT_CALL(*consumer, OnGamepadRawInputChanged).Times(0);
EXPECT_CALL(*consumer, OnGamepadConnected).Times(0);
service()->SimulateButtonInput(token, 0, 1.0,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
service()->SimulateButtonInput(token, 0, 0.5,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
TestFuture<uint32_t, const Gamepad&> connected_future;
EXPECT_CALL(*consumer, OnGamepadConnected)
.WillOnce(InvokeFuture(connected_future));
service()->SimulateButtonInput(token, 0, 1.0,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
EXPECT_EQ(connected_future.Get<0>(), 0u);
WaitForPoll();
TestFuture<uint32_t, const Gamepad&> input_future;
EXPECT_CALL(*consumer, OnGamepadRawInputChanged)
.WillOnce(InvokeFuture(input_future));
service()->SimulateButtonInput(token, 0, 0.7,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
const auto [id, gamepad] = input_future.Take();
EXPECT_EQ(gamepad.buttons[0].value, 0.7);
CleanupRawInputGamepad(consumer, token);
}
TEST_F(GamepadServiceSimulationTest, RawInputChangeDisabledByFeatureFlag) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(
features::kGamepadRawInputChangeEvent);
auto* consumer = CreateConsumer();
EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
SimulatedGamepadParams params;
params.name = "Raw input test gamepad";
params.button_bounds = {std::nullopt};
auto token = SetupRawInputGamepad(consumer, std::move(params));
EXPECT_CALL(*consumer, OnGamepadRawInputChanged).Times(0);
service()->SimulateButtonInput(token, 0, 0.5,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
service()->SimulateButtonInput(token, 0, 0.8,
std::nullopt,
std::nullopt);
service()->SimulateInputFrame(token);
WaitForPoll();
CleanupRawInputGamepad(consumer, token);
}
}