blob: 74d0e5b4336d58b195093f1488f36daf4d8cbd09 [file] [log] [blame]
// Copyright (c) 2012 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 "device/gamepad/gamepad_provider.h"
#include <memory>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
#include "build/build_config.h"
#include "device/gamepad/gamepad_data_fetcher.h"
#include "device/gamepad/gamepad_test_helpers.h"
#include "device/gamepad/public/cpp/gamepad_switches.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace device {
namespace {
// Helper class to generate and record user gesture callbacks.
class UserGestureListener {
public:
UserGestureListener() : has_user_gesture_(false) {}
base::OnceClosure GetClosure() {
return base::BindOnce(&UserGestureListener::GotUserGesture,
weak_factory_.GetWeakPtr());
}
bool has_user_gesture() const { return has_user_gesture_; }
private:
void GotUserGesture() { has_user_gesture_ = true; }
bool has_user_gesture_;
base::WeakPtrFactory<UserGestureListener> weak_factory_{this};
};
class TestChangeClient : public GamepadChangeClient {
public:
TestChangeClient() = default;
TestChangeClient(const TestChangeClient&) = delete;
TestChangeClient& operator=(const TestChangeClient&) = delete;
~TestChangeClient() = default;
void OnGamepadConnectionChange(bool connected,
uint32_t index,
const Gamepad& pad) override {}
void OnGamepadChange(mojom::GamepadChangesPtr changes) override {
all_changes_.push_back(std::move(changes));
EXPECT_GT(num_changes_left_, 0);
if (--num_changes_left_ == 0)
run_loop_.Quit();
}
void RunUntilChangeEvents(int num_changes) {
// If we are explicitly not expecting any changes wait 20 milliseconds
// to ensure no changes come in.
if (num_changes == 0) {
num_changes_left_ = 0;
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
base::RunLoop().RunUntilIdle();
return;
}
num_changes_left_ = num_changes;
run_loop_.Run();
}
const std::vector<mojom::GamepadChangesPtr>& all_changes() const {
return all_changes_;
}
private:
int num_changes_left_ = 0;
base::RunLoop run_loop_;
std::vector<mojom::GamepadChangesPtr> all_changes_;
};
// Main test fixture
class GamepadProviderTest : public testing::Test, public GamepadTestHelper {
public:
GamepadProviderTest(const GamepadProviderTest&) = delete;
GamepadProviderTest& operator=(const GamepadProviderTest&) = delete;
GamepadProvider* CreateProvider(const Gamepads& test_data) {
auto fetcher = std::make_unique<MockGamepadDataFetcher>(test_data);
mock_data_fetcher_ = fetcher.get();
provider_ =
std::make_unique<GamepadProvider>(&change_client_, std::move(fetcher),
/*polling_thread=*/nullptr);
return provider_.get();
}
// Sleep until the shared memory buffer's seqlock advances the buffer version,
// indicating that the gamepad provider has written to it after polling the
// gamepad fetchers. The buffer will report an odd value for the version if
// the buffer is not in a consistent state, so we also require that the value
// is even before continuing.
void WaitForData(const GamepadHardwareBuffer* buffer) {
const base::subtle::Atomic32 initial_version = buffer->seqlock.ReadBegin();
base::subtle::Atomic32 current_version;
do {
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
current_version = buffer->seqlock.ReadBegin();
} while (current_version % 2 || current_version == initial_version);
}
// The provider polls the data on the background thread and then issues
// the callback on the client thread. Waiting for it to poll twice ensures
// that it was able to issue callbacks for the first poll.
void WaitForDataAndCallbacksIssued(const GamepadHardwareBuffer* buffer) {
WaitForData(buffer);
WaitForData(buffer);
}
void ReadGamepadHardwareBuffer(const GamepadHardwareBuffer* buffer,
Gamepads* output) {
memset(output, 0, sizeof(Gamepads));
base::subtle::Atomic32 version;
do {
version = buffer->seqlock.ReadBegin();
memcpy(output, &buffer->data, sizeof(Gamepads));
} while (buffer->seqlock.ReadRetry(version));
}
protected:
GamepadProviderTest() = default;
std::unique_ptr<GamepadProvider> provider_;
// Pointer owned by the provider.
MockGamepadDataFetcher* mock_data_fetcher_;
TestChangeClient change_client_;
};
TEST_F(GamepadProviderTest, PollingAccess) {
Gamepads test_data;
memset(&test_data, 0, sizeof(Gamepads));
test_data.items[0].connected = true;
test_data.items[0].timestamp = 0;
test_data.items[0].buttons_length = 1;
test_data.items[0].axes_length = 2;
test_data.items[0].buttons[0].value = 1.0f;
test_data.items[0].buttons[0].pressed = true;
test_data.items[0].axes[0] = -1.0f;
test_data.items[0].axes[1] = 0.5f;
GamepadProvider* provider = CreateProvider(test_data);
provider->SetSanitizationEnabled(false);
provider->Resume();
base::RunLoop().RunUntilIdle();
// Renderer-side, pull data out of poll buffer.
base::ReadOnlySharedMemoryRegion region =
provider->DuplicateSharedMemoryRegion();
base::ReadOnlySharedMemoryMapping mapping = region.Map();
EXPECT_TRUE(mapping.IsValid());
const GamepadHardwareBuffer* buffer =
static_cast<const GamepadHardwareBuffer*>(mapping.memory());
// Wait until the shared memory buffer has been written at least once.
WaitForData(buffer);
Gamepads output;
ReadGamepadHardwareBuffer(buffer, &output);
ASSERT_EQ(1u, output.items[0].buttons_length);
EXPECT_EQ(1.0f, output.items[0].buttons[0].value);
EXPECT_EQ(true, output.items[0].buttons[0].pressed);
ASSERT_EQ(2u, output.items[0].axes_length);
EXPECT_EQ(-1.0f, output.items[0].axes[0]);
EXPECT_EQ(0.5f, output.items[0].axes[1]);
}
TEST_F(GamepadProviderTest, ConnectDisconnectMultiple) {
Gamepads test_data;
test_data.items[0].connected = true;
test_data.items[0].timestamp = 0;
test_data.items[0].axes_length = 2;
test_data.items[0].axes[0] = -1.0f;
test_data.items[0].axes[1] = 0.5f;
test_data.items[1].connected = true;
test_data.items[1].timestamp = 0;
test_data.items[1].axes_length = 2;
test_data.items[1].axes[0] = 1.0f;
test_data.items[1].axes[1] = -0.5f;
Gamepads test_data_onedisconnected;
test_data_onedisconnected.items[1].connected = true;
test_data_onedisconnected.items[1].timestamp = 0;
test_data_onedisconnected.items[1].axes_length = 2;
test_data_onedisconnected.items[1].axes[0] = 1.0f;
test_data_onedisconnected.items[1].axes[1] = -0.5f;
GamepadProvider* provider = CreateProvider(test_data);
provider->SetSanitizationEnabled(false);
provider->Resume();
base::RunLoop().RunUntilIdle();
// Renderer-side, pull data out of poll buffer.
base::ReadOnlySharedMemoryRegion region =
provider->DuplicateSharedMemoryRegion();
base::ReadOnlySharedMemoryMapping mapping = region.Map();
EXPECT_TRUE(mapping.IsValid());
const GamepadHardwareBuffer* buffer =
static_cast<const GamepadHardwareBuffer*>(mapping.memory());
// Wait until the shared memory buffer has been written at least once.
WaitForData(buffer);
Gamepads output;
ReadGamepadHardwareBuffer(buffer, &output);
ASSERT_EQ(2u, output.items[0].axes_length);
EXPECT_EQ(-1.0f, output.items[0].axes[0]);
EXPECT_EQ(0.5f, output.items[0].axes[1]);
ASSERT_EQ(2u, output.items[1].axes_length);
EXPECT_EQ(1.0f, output.items[1].axes[0]);
EXPECT_EQ(-0.5f, output.items[1].axes[1]);
mock_data_fetcher_->SetTestData(test_data_onedisconnected);
WaitForDataAndCallbacksIssued(buffer);
ReadGamepadHardwareBuffer(buffer, &output);
EXPECT_EQ(0u, output.items[0].axes_length);
ASSERT_EQ(2u, output.items[1].axes_length);
EXPECT_EQ(1.0f, output.items[1].axes[0]);
EXPECT_EQ(-0.5f, output.items[1].axes[1]);
}
// Tests that waiting for a user gesture works properly.
TEST_F(GamepadProviderTest, UserGesture) {
Gamepads no_button_data;
no_button_data.items[0].connected = true;
no_button_data.items[0].timestamp = 0;
no_button_data.items[0].buttons_length = 1;
no_button_data.items[0].axes_length = 2;
no_button_data.items[0].buttons[0].value = 0.0f;
no_button_data.items[0].buttons[0].pressed = false;
no_button_data.items[0].axes[0] = 0.0f;
no_button_data.items[0].axes[1] = 0.4f;
Gamepads button_down_data = no_button_data;
button_down_data.items[0].buttons[0].value = 1.0f;
button_down_data.items[0].buttons[0].pressed = true;
UserGestureListener listener;
GamepadProvider* provider = CreateProvider(no_button_data);
provider->SetSanitizationEnabled(false);
provider->Resume();
provider->RegisterForUserGesture(listener.GetClosure());
base::RunLoop().RunUntilIdle();
// Renderer-side, pull data out of poll buffer.
base::ReadOnlySharedMemoryRegion region =
provider->DuplicateSharedMemoryRegion();
base::ReadOnlySharedMemoryMapping mapping = region.Map();
EXPECT_TRUE(mapping.IsValid());
const GamepadHardwareBuffer* buffer =
static_cast<const GamepadHardwareBuffer*>(mapping.memory());
// Wait until the shared memory buffer has been written at least once.
WaitForData(buffer);
// It should not have issued our callback.
EXPECT_FALSE(listener.has_user_gesture());
// Set a button down.
mock_data_fetcher_->SetTestData(button_down_data);
// The user gesture listener callback is not called until after the buffer has
// been updated. Wait for the second update to ensure callbacks have fired.
WaitForDataAndCallbacksIssued(buffer);
// It should have issued our callback.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(listener.has_user_gesture());
}
// Tests that waiting for a user gesture works properly.
TEST_F(GamepadProviderTest, Sanitization) {
Gamepads active_data;
active_data.items[0].connected = true;
active_data.items[0].timestamp = 0;
active_data.items[0].buttons_length = 1;
active_data.items[0].axes_length = 1;
active_data.items[0].buttons[0].value = 1.0f;
active_data.items[0].buttons[0].pressed = true;
active_data.items[0].axes[0] = -1.0f;
Gamepads zero_data;
zero_data.items[0].connected = true;
zero_data.items[0].timestamp = 0;
zero_data.items[0].buttons_length = 1;
zero_data.items[0].axes_length = 1;
zero_data.items[0].buttons[0].value = 0.0f;
zero_data.items[0].buttons[0].pressed = false;
zero_data.items[0].axes[0] = 0.0f;
UserGestureListener listener;
GamepadProvider* provider = CreateProvider(active_data);
provider->SetSanitizationEnabled(true);
provider->Resume();
base::RunLoop().RunUntilIdle();
// Renderer-side, pull data out of poll buffer.
base::ReadOnlySharedMemoryRegion region =
provider->DuplicateSharedMemoryRegion();
base::ReadOnlySharedMemoryMapping mapping = region.Map();
ASSERT_TRUE(mapping.IsValid());
const GamepadHardwareBuffer* buffer =
static_cast<const GamepadHardwareBuffer*>(mapping.memory());
// Wait until the shared memory buffer has been written at least once.
WaitForData(buffer);
Gamepads output;
ReadGamepadHardwareBuffer(buffer, &output);
// Initial data should all be zeroed out due to sanitization, even though the
// gamepad reported input
ASSERT_EQ(1u, output.items[0].buttons_length);
EXPECT_EQ(0.0f, output.items[0].buttons[0].value);
EXPECT_FALSE(output.items[0].buttons[0].pressed);
ASSERT_EQ(1u, output.items[0].axes_length);
EXPECT_EQ(0.0f, output.items[0].axes[0]);
// Zero out the inputs
mock_data_fetcher_->SetTestData(zero_data);
WaitForDataAndCallbacksIssued(buffer);
// Read updated data from shared memory
ReadGamepadHardwareBuffer(buffer, &output);
// Should still read zero, which is now an accurate reflection of the data
ASSERT_EQ(1u, output.items[0].buttons_length);
EXPECT_EQ(0.0f, output.items[0].buttons[0].value);
EXPECT_FALSE(output.items[0].buttons[0].pressed);
ASSERT_EQ(1u, output.items[0].axes_length);
EXPECT_EQ(0.0f, output.items[0].axes[0]);
// Re-set the active inputs
mock_data_fetcher_->SetTestData(active_data);
WaitForDataAndCallbacksIssued(buffer);
// Read updated data from shared memory
ReadGamepadHardwareBuffer(buffer, &output);
// Should now accurately reflect the reported data.
ASSERT_EQ(1u, output.items[0].buttons_length);
EXPECT_EQ(1.0f, output.items[0].buttons[0].value);
EXPECT_TRUE(output.items[0].buttons[0].pressed);
ASSERT_EQ(1u, output.items[0].axes_length);
EXPECT_EQ(-1.0f, output.items[0].axes[0]);
}
TEST_F(GamepadProviderTest, SendEvents) {
// This is a test for the logic that is currently behind this flag.
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableGamepadButtonAxisEvents);
Gamepads test_data;
test_data.items[0].connected = true;
test_data.items[0].timestamp = 0;
test_data.items[0].axes_length = 2;
test_data.items[0].axes[0] = -1.0f;
test_data.items[0].axes[1] = 0.5f;
test_data.items[0].buttons[0].value = 0.0f;
test_data.items[0].buttons[0].pressed = false;
test_data.items[0].buttons[1].value = 0.7f;
test_data.items[0].buttons[1].pressed = true;
test_data.items[0].buttons[2].value = 0.0f;
test_data.items[0].buttons[2].pressed = false;
test_data.items[0].buttons[3].value = 0.0f;
test_data.items[0].buttons[3].pressed = false;
test_data.items[0].buttons_length = 4;
test_data.items[0].axes_length = 2;
test_data.items[1].connected = true;
test_data.items[1].timestamp = 0;
test_data.items[1].axes_length = 2;
test_data.items[1].axes[0] = 1.0f;
test_data.items[1].axes[1] = -0.5f;
test_data.items[1].buttons[0].value = 0.0f;
test_data.items[1].buttons[0].pressed = false;
test_data.items[1].buttons[1].value = 1.0f;
test_data.items[1].buttons[1].pressed = true;
test_data.items[1].buttons[2].value = 1.0f;
test_data.items[1].buttons[2].pressed = true;
test_data.items[1].buttons_length = 3;
test_data.items[1].axes_length = 2;
Gamepads test_data_changed = test_data;
test_data_changed.items[0].axes[1] = -0.5f;
test_data_changed.items[0].buttons[0].value = 0.4f;
test_data_changed.items[0].buttons[1].value = 0.2f;
test_data_changed.items[0].buttons[1].pressed = false;
test_data_changed.items[0].buttons[3].value = 0.2f;
test_data_changed.items[1].axes[0] = 0.5f;
test_data_changed.items[1].buttons[0].value = 1.0f;
test_data_changed.items[1].buttons[0].pressed = true;
test_data_changed.items[1].buttons[2].value = 0.9f;
GamepadProvider* provider = CreateProvider(test_data);
provider->SetSanitizationEnabled(false);
provider->Resume();
base::RunLoop().RunUntilIdle();
// Renderer-side, pull data out of poll buffer.
base::ReadOnlySharedMemoryRegion region =
provider->DuplicateSharedMemoryRegion();
base::ReadOnlySharedMemoryMapping mapping = region.Map();
EXPECT_TRUE(mapping.IsValid());
const GamepadHardwareBuffer* buffer =
static_cast<const GamepadHardwareBuffer*>(mapping.memory());
// Wait until the shared memory buffer has been written at least once.
WaitForData(buffer);
mock_data_fetcher_->SetTestData(test_data_changed);
// Wait for changes to take place and events to fire.
WaitForDataAndCallbacksIssued(buffer);
change_client_.RunUntilChangeEvents(2);
const auto& changes = change_client_.all_changes();
// Ensure the |button_changes| and |axis_changes| objects have all the
// expected values.
ASSERT_EQ(2u, changes.size());
ASSERT_EQ(1u, changes[1]->axis_changes.size());
ASSERT_EQ(1u, changes[0]->axis_changes.size());
ASSERT_EQ(2u, changes[1]->button_changes.size());
ASSERT_EQ(3u, changes[0]->button_changes.size());
EXPECT_EQ(1u, changes[0]->axis_changes[0]->axis_index);
EXPECT_EQ(-0.5f, changes[0]->axis_changes[0]->axis_snapshot);
EXPECT_EQ(0u, changes[0]->button_changes[0]->button_index);
EXPECT_FALSE(changes[0]->button_changes[0]->button_up);
EXPECT_FALSE(changes[0]->button_changes[0]->button_down);
EXPECT_TRUE(changes[0]->button_changes[0]->value_changed);
EXPECT_EQ(changes[0]->button_changes[0]->button_snapshot,
test_data_changed.items[0].buttons[0]);
EXPECT_EQ(1u, changes[0]->button_changes[1]->button_index);
EXPECT_TRUE(changes[0]->button_changes[1]->button_up);
EXPECT_FALSE(changes[0]->button_changes[1]->button_down);
EXPECT_TRUE(changes[0]->button_changes[1]->value_changed);
EXPECT_EQ(changes[0]->button_changes[1]->button_snapshot,
test_data_changed.items[0].buttons[1]);
EXPECT_EQ(3u, changes[0]->button_changes[2]->button_index);
EXPECT_FALSE(changes[0]->button_changes[2]->button_up);
EXPECT_FALSE(changes[0]->button_changes[2]->button_down);
EXPECT_TRUE(changes[0]->button_changes[2]->value_changed);
EXPECT_EQ(changes[0]->button_changes[2]->button_snapshot,
test_data_changed.items[0].buttons[3]);
EXPECT_EQ(0u, changes[1]->axis_changes[0]->axis_index);
EXPECT_EQ(0.5f, changes[1]->axis_changes[0]->axis_snapshot);
EXPECT_EQ(0u, changes[1]->button_changes[0]->button_index);
EXPECT_FALSE(changes[1]->button_changes[0]->button_up);
EXPECT_TRUE(changes[1]->button_changes[0]->button_down);
EXPECT_TRUE(changes[1]->button_changes[0]->value_changed);
EXPECT_EQ(changes[1]->button_changes[0]->button_snapshot,
test_data_changed.items[1].buttons[0]);
EXPECT_EQ(2u, changes[1]->button_changes[1]->button_index);
EXPECT_FALSE(changes[1]->button_changes[1]->button_up);
EXPECT_FALSE(changes[1]->button_changes[1]->button_down);
EXPECT_TRUE(changes[1]->button_changes[1]->value_changed);
EXPECT_EQ(changes[1]->button_changes[1]->button_snapshot,
test_data_changed.items[1].buttons[2]);
}
TEST_F(GamepadProviderTest, DontSendEventsBeforeUserGesture) {
// This is a test for the logic that is currently behind this flag.
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableGamepadButtonAxisEvents);
Gamepads test_data;
test_data.items[0].connected = true;
test_data.items[0].timestamp = 0;
test_data.items[0].axes_length = 2;
test_data.items[0].axes[0] = 0.0f;
test_data.items[0].buttons[0].value = 0.0f;
test_data.items[0].buttons[0].pressed = false;
test_data.items[0].buttons_length = 1;
test_data.items[0].axes_length = 1;
Gamepads test_data_changed = test_data;
test_data_changed.items[0].axes[1] = 0.4f;
test_data_changed.items[0].buttons[0].value = 0.3f;
GamepadProvider* provider = CreateProvider(test_data);
provider->SetSanitizationEnabled(false);
provider->Resume();
base::RunLoop().RunUntilIdle();
// Renderer-side, pull data out of poll buffer.
base::ReadOnlySharedMemoryRegion region =
provider->DuplicateSharedMemoryRegion();
base::ReadOnlySharedMemoryMapping mapping = region.Map();
EXPECT_TRUE(mapping.IsValid());
const GamepadHardwareBuffer* buffer =
static_cast<const GamepadHardwareBuffer*>(mapping.memory());
// Wait until the shared memory buffer has been written at least once.
WaitForData(buffer);
mock_data_fetcher_->SetTestData(test_data_changed);
// Wait for changes to take place and allow potential events to fire.
WaitForDataAndCallbacksIssued(buffer);
change_client_.RunUntilChangeEvents(0);
EXPECT_TRUE(change_client_.all_changes().empty());
}
TEST_F(GamepadProviderTest, DontSendEventsWhenDisconnected) {
// This is a test for the logic that is currently behind this flag.
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableGamepadButtonAxisEvents);
Gamepads test_data;
test_data.items[0].connected = false;
test_data.items[0].timestamp = 0;
test_data.items[0].axes[0] = 0.0f;
test_data.items[0].buttons[0].value = 1.0f;
test_data.items[0].buttons[0].pressed = true;
test_data.items[0].buttons_length = 1;
test_data.items[0].axes_length = 1;
test_data.items[1].connected = true;
test_data.items[1].timestamp = 0;
test_data.items[1].axes[0] = 1.0f;
test_data.items[1].buttons[0].value = 0.0f;
test_data.items[1].buttons[0].pressed = false;
test_data.items[1].buttons_length = 1;
test_data.items[1].axes_length = 1;
Gamepads test_data_changed = test_data;
test_data_changed.items[0].axes[0] = 1.0f;
test_data_changed.items[0].buttons[0].value = 0.0f;
test_data_changed.items[0].buttons[0].pressed = false;
test_data_changed.items[1].connected = false;
test_data_changed.items[1].axes[0] = 0.0f;
test_data_changed.items[1].buttons[0].value = 1.0f;
test_data_changed.items[1].buttons[0].pressed = true;
GamepadProvider* provider = CreateProvider(test_data);
provider->SetSanitizationEnabled(false);
provider->Resume();
base::RunLoop().RunUntilIdle();
// Renderer-side, pull data out of poll buffer.
base::ReadOnlySharedMemoryRegion region =
provider->DuplicateSharedMemoryRegion();
base::ReadOnlySharedMemoryMapping mapping = region.Map();
EXPECT_TRUE(mapping.IsValid());
const GamepadHardwareBuffer* buffer =
static_cast<const GamepadHardwareBuffer*>(mapping.memory());
// Wait until the shared memory buffer has been written at least once.
WaitForData(buffer);
mock_data_fetcher_->SetTestData(test_data_changed);
// Wait for changes to take place and allow potential events to fire.
WaitForDataAndCallbacksIssued(buffer);
change_client_.RunUntilChangeEvents(0);
EXPECT_TRUE(change_client_.all_changes().empty());
}
TEST_F(GamepadProviderTest, DontSendEventsOnConnection) {
// This is a test for the logic that is currently behind this flag.
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableGamepadButtonAxisEvents);
Gamepads test_data;
test_data.items[0].connected = true;
test_data.items[0].timestamp = 0;
test_data.items[0].axes[0] = 0.0f;
test_data.items[0].buttons[0].value = 1.0f;
test_data.items[0].buttons[0].pressed = true;
test_data.items[0].buttons_length = 1;
test_data.items[0].axes_length = 1;
test_data.items[1].connected = false;
test_data.items[1].timestamp = 0;
test_data.items[1].axes[0] = 0.0f;
test_data.items[1].buttons[0].value = 1.0f;
test_data.items[1].buttons[0].pressed = true;
test_data.items[1].buttons_length = 1;
test_data.items[1].axes_length = 1;
Gamepads test_data_changed = test_data;
test_data_changed.items[1].connected = true;
test_data_changed.items[1].axes[0] = 1.0f;
test_data_changed.items[1].buttons[0].value = 0.0f;
test_data_changed.items[1].buttons[0].pressed = false;
GamepadProvider* provider = CreateProvider(test_data);
provider->SetSanitizationEnabled(false);
provider->Resume();
base::RunLoop().RunUntilIdle();
// Renderer-side, pull data out of poll buffer.
base::ReadOnlySharedMemoryRegion region =
provider->DuplicateSharedMemoryRegion();
base::ReadOnlySharedMemoryMapping mapping = region.Map();
EXPECT_TRUE(mapping.IsValid());
const GamepadHardwareBuffer* buffer =
static_cast<const GamepadHardwareBuffer*>(mapping.memory());
// Wait until the shared memory buffer has been written at least once.
WaitForData(buffer);
mock_data_fetcher_->SetTestData(test_data_changed);
// Wait for changes to take place and allow potential events to fire.
WaitForDataAndCallbacksIssued(buffer);
change_client_.RunUntilChangeEvents(0);
EXPECT_TRUE(change_client_.all_changes().empty());
}
} // namespace
} // namespace device