Commit b2f9ade9 authored by Matt Reynolds's avatar Matt Reynolds Committed by Commit Bot

Improve gamepad-polling-access layout test

Under normal circumstances, gamepad state can only change in
response to certain triggers. By using the gamepadController test
API, the gamepad state may be changed in response to many more
triggers, including gamepad events themselves, which may cause
some events to not be dispatched. This CL modifies the layout test
to only change gamepad state outside of gamepad event listeners.

This CL also removes a hack where the string representation of the
gamepad state was being compared instead of the underlying buffers.
This hack was intended as a workaround for a bug caused by incorrect
buffer reuse behavior. The bug still exists but the test is modified
to test the reuse behavior as intended.

BUG=855760

Change-Id: I75144ae68efa355992d03692e2fd52b2f832281f
Reviewed-on: https://chromium-review.googlesource.com/1173329
Commit-Queue: Matt Reynolds <mattreynolds@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#583428}
parent a67d0227
...@@ -4,33 +4,22 @@ ...@@ -4,33 +4,22 @@
<script src="../resources/testharnessreport.js"></script> <script src="../resources/testharnessreport.js"></script>
<script src="resources/gamepad-helpers.js"></script> <script src="resources/gamepad-helpers.js"></script>
<script> <script>
// We will convert all |buttons| and |axes| to string to avoid reused buffer
// problem. https://crbug.com/855760 // TODO(crbug.com/146285): Allow more than 4 connected gamepads.
function buttonsToStr(buttons) { var MAX_GAMEPADS = 4;
return buttons.map(button => "[" + button.pressed + "," + button.touched + "," + button.value + "]")
.join(); function disconnectGamepads() {
} // Simulate disconnecting all gamepads.
function axesToStr(axes) { for (let i = 0; i < MAX_GAMEPADS; ++i) {
return axes.toString(); gamepadController.disconnect(i);
}
} }
promise_test(async () => { function connectOneGamepad() {
// Start all disconnected // Simulate a connected gamepad at index 0 with:
gamepadController.disconnect(0); // * id 'MockStick 3000'
gamepadController.disconnect(1); // * two buttons (values 1.0/pressed, 0.0/unpressed)
gamepadController.disconnect(2); // * two axes (values 0.5, -1.0)
gamepadController.disconnect(3);
assert_equals(navigator.getGamepads().length, 4, 'navigator.getGamepads().length');
assert_equals(navigator.getGamepads().item(0), null, 'navigator.getGamepads().item(0)');
assert_equals(navigator.getGamepads().item(1), null, 'navigator.getGamepads().item(1)');
assert_equals(navigator.getGamepads().item(2), null, 'navigator.getGamepads().item(2)');
assert_equals(navigator.getGamepads().item(3), null, 'navigator.getGamepads().item(3)');
assert_equals(navigator.getGamepads()[0], null, 'navigator.getGamepads()[0]');
assert_equals(navigator.getGamepads()[1], null, 'navigator.getGamepads()[1]');
assert_equals(navigator.getGamepads()[2], null, 'navigator.getGamepads()[2]');
assert_equals(navigator.getGamepads()[3], null, 'navigator.getGamepads()[3]');
// connect again, and check standard access
gamepadController.connect(0); gamepadController.connect(0);
gamepadController.setId(0, "MockStick 3000"); gamepadController.setId(0, "MockStick 3000");
gamepadController.setButtonCount(0, 2); gamepadController.setButtonCount(0, 2);
...@@ -40,51 +29,172 @@ promise_test(async () => { ...@@ -40,51 +29,172 @@ promise_test(async () => {
gamepadController.setAxisData(0, 0, .5); gamepadController.setAxisData(0, 0, .5);
gamepadController.setAxisData(0, 1, -1.0); gamepadController.setAxisData(0, 1, -1.0);
gamepadController.dispatchConnected(0); gamepadController.dispatchConnected(0);
}
await ongamepadconnected(); function testGamepadStateAllDisconnected() {
assert_equals(navigator.getGamepads()[0].id, 'MockStick 3000', 'navigator.getGamepads()[0].id'); // To pass this test, the getGamepads array should have only null elements.
assert_equals(navigator.getGamepads()[0].buttons.length, 2, 'navigator.getGamepads()[0].buttons.length'); let pads = navigator.getGamepads();
assert_equals(navigator.getGamepads()[0].buttons[0].value, 1.0, 'navigator.getGamepads()[0].buttons[0].value'); // According to the spec, the length of the array returned by getGamepads
assert_true(navigator.getGamepads()[0].buttons[0].pressed, 'navigator.getGamepads()[0].buttons[0].pressed'); // must be one greater than the maximum index of Gamepad objects in the
assert_equals(navigator.getGamepads()[0].buttons[1].value, 0.0, 'navigator.getGamepads()[0].buttons[1].value'); // array. It does not specify the size when there are no objects in the
assert_false(navigator.getGamepads()[0].buttons[1].pressed, 'navigator.getGamepads()[0].buttons[1].pressed'); // array. The current behavior in Chrome is to return an array with length
assert_equals(navigator.getGamepads()[0].axes.length, 2, 'navigator.getGamepads()[0].axes.length'); // equal to the maximum number of connected gamepads, and to fill all unused
assert_equals(navigator.getGamepads()[0].axes[0], 0.5, 'navigator.getGamepads()[0].axes[0]'); // slots with null.
assert_equals(navigator.getGamepads()[0].axes[1], -1.0, 'navigator.getGamepads()[0].axes[1]'); assert_equals(pads.length, MAX_GAMEPADS, 'pads.length');
for (let i = 0; i < pads.length; ++i) {
// Check that accessing the |axes| and |buttons| attributes fetches the // TODO(crbug.com/865642): Remove deprecated Gamepad.item method.
// same objects until their values change. assert_equals(pads.item(i), null);
assert_equals(navigator.getGamepads()[0].axes, navigator.getGamepads()[0].axes, 'navigator.getGamepads()[0].axes'); assert_equals(pads[i], null);
assert_equals(navigator.getGamepads()[0].buttons, navigator.getGamepads()[0].buttons, 'navigator.getGamepads()[0].buttons'); }
let oldAxesStr = axesToStr(navigator.getGamepads()[0].axes); }
let oldButtonsStr = buttonsToStr(navigator.getGamepads()[0].buttons);
function testGamepadStateOneConnected() {
// Updates with the same values are skipped. // To pass this test, Gamepad 0 should have the values set in
gamepadController.setAxisCount(0, 2); // setUpOneGamepad.
gamepadController.setButtonCount(0, 2); let gamepad = navigator.getGamepads()[0];
assert_equals(axesToStr(navigator.getGamepads()[0].axes), oldAxesStr, 'navigator.getGamepads()[0].axes'); assert_equals(gamepad.id, 'MockStick 3000');
assert_equals(buttonsToStr(navigator.getGamepads()[0].buttons), oldButtonsStr, 'navigator.getGamepads()[0].buttons'); assert_equals(gamepad.buttons.length, 2);
gamepadController.setAxisData(0, 0, .5); assert_equals(gamepad.buttons[0].value, 1.0);
gamepadController.setButtonData(0, 1, 0); assert_true(gamepad.buttons[0].pressed);
assert_equals(axesToStr(navigator.getGamepads()[0].axes), oldAxesStr, 'navigator.getGamepads()[0].axes'); assert_equals(gamepad.buttons[1].value, 0.0);
assert_equals(buttonsToStr(navigator.getGamepads()[0].buttons), oldButtonsStr, 'navigator.getGamepads()[0].buttons'); assert_false(gamepad.buttons[1].pressed);
assert_equals(gamepad.axes.length, 2);
assert_equals(gamepad.axes[0], 0.5);
assert_equals(gamepad.axes[1], -1.0);
}
function testNoChangeReturnsSameObjects() {
// Check that accessing gamepad state fetches the same objects until their
// values change.
assert_equals(navigator.getGamepads(),
navigator.getGamepads(), 'gamepad arrays differ');
assert_equals(navigator.getGamepads()[0],
navigator.getGamepads()[0], 'gamepad objects differ');
assert_equals(navigator.getGamepads()[0].axes,
navigator.getGamepads()[0].axes, 'axes arrays differ');
assert_equals(navigator.getGamepads()[0].buttons,
navigator.getGamepads()[0].buttons, 'gamepad buttons differ');
}
function testSameValueUpdateReturnsSameObjects() {
// Test that updates with the same value do not cause a new gamepad object
// to be returned.
let gamepadBefore = navigator.getGamepads()[0];
gamepadController.setAxisCount(0, gamepadBefore.axes.length);
gamepadController.setButtonCount(0, gamepadBefore.buttons.length);
let gamepadAfter = navigator.getGamepads()[0];
assert_equals(
gamepadBefore, gamepadAfter,
"expected same gamepad after same-value setAxisCount/setButtonCount");
assert_equals(
gamepadBefore.timestamp, gamepadAfter.timestamp,
"expected same timestamp after same-value setAxisCount/setButtonCount");
assert_equals(
gamepadBefore.axes, gamepadAfter.axes,
"expected same axes after same-value setAxisCount/setButtonCount");
assert_equals(
gamepadBefore.buttons, gamepadAfter.buttons,
"expected same buttons after same-value setAxisCount/setButtonCount");
gamepadBefore = navigator.getGamepads()[0];
gamepadController.setAxisData(0, 0, gamepadBefore.axes[0]);
gamepadController.setButtonData(0, 1, gamepadBefore.buttons[1].value);
gamepadAfter = navigator.getGamepads()[0];
assert_equals(
gamepadBefore, gamepadAfter,
"expected same gamepad after same-value setAxisData/setButtonData");
assert_equals(
gamepadBefore.timestamp, gamepadAfter.timestamp,
"expected same timestamp after same-value setAxisData/setButtonData");
assert_equals(
gamepadBefore.axes, gamepadAfter.axes,
"expected same axes after same-value setAxisData/setButtonData");
assert_equals(
gamepadBefore.buttons, gamepadAfter.buttons,
"expected same buttons after same-value setAxisData/setButtonData");
}
function testDifferentValueUpdateReturnsNewObjects() {
// Test that changing an axis value causes a new gamepad object to be
// returned.
// TODO(crbug.com/855760): Check that the buttons array is preserved.
gamepadBefore = navigator.getGamepads()[0];
gamepadController.setAxisData(0, 0, gamepadBefore.axes[0] + 0.1);
gamepadAfter = navigator.getGamepads()[0];
assert_not_equals(gamepadBefore, gamepadAfter,
"expected new gamepad after new-value setAxisData");
assert_not_equals(gamepadBefore.timestamp,
gamepadAfter.timestamp,
"expected new timestamp after new-value setAxisData");
assert_not_equals(gamepadBefore.axes, gamepadAfter.axes,
"expected new axes after new-value setAxisData");
// Test that changing a button value causes a new gamepad object to be
// returned.
// TODO(crbug.com/855760): Check that the axes array is preserved.
gamepadBefore = navigator.getGamepads()[0];
gamepadController.setButtonData(0, 0, gamepad.buttons[0].value + 0.1);
gamepadAfter = navigator.getGamepads()[0];
assert_not_equals(gamepadBefore, gamepadAfter,
"expected new gamepad after new-value setButtonData");
assert_not_equals(gamepadBefore.timestamp,
gamepadAfter.timestamp,
"expected new timestamp after new-value setButtonData");
assert_not_equals(gamepadBefore.buttons, gamepadAfter.buttons,
"expected new buttons after new-value setButtonData");
// Test that changing a button value and an axis value causes a new gamepad
// object to be returned.
gamepadBefore = navigator.getGamepads()[0];
gamepadController.setAxisData(0, 0, gamepad.axes[0] - 0.1);
gamepadController.setButtonData(0, 0, gamepad.buttons[0].value - 0.1);
gamepadAfter = navigator.getGamepads()[0];
assert_not_equals(
gamepadBefore, gamepadAfter,
"expected new gamepad after new-value setAxisData/setButtonData");
assert_not_equals(
gamepadBefore.timestamp, gamepadAfter.timestamp,
"expected new timestamp after new-value setAxisData/setButtonData");
assert_not_equals(
gamepadBefore.axes, gamepadAfter.axes,
"expected new axes after new-value setAxisData/setButtonData");
assert_not_equals(
gamepadBefore.buttons, gamepadAfter.buttons,
"expected new buttons after new-value setAxisData/setButtonData");
}
promise_test(async () => {
// First disconnect all gamepads.
disconnectGamepads();
testGamepadStateAllDisconnected();
// Updates with different values are not skipped. // Simulate a gamepad connection and verify the state changes as expected.
gamepadController.setAxisData(0, 0, .6); let connectPromise = ongamepadconnected();
assert_not_equals(axesToStr(navigator.getGamepads()[0].axes), oldAxesStr, 'navigator.getGamepads()[0].axes'); connectOneGamepad();
assert_equals(buttonsToStr(navigator.getGamepads()[0].buttons), oldButtonsStr, 'navigator.getGamepads()[0].buttons'); await connectPromise;
oldAxesStr = axesToStr(navigator.getGamepads()[0].axes); testGamepadStateOneConnected();
gamepadController.setButtonData(0, 0, .2); // Chrome has different internal behavior depending on whether a gamepad
assert_equals(axesToStr(navigator.getGamepads()[0].axes), oldAxesStr, 'navigator.getGamepads()[0].axes'); // event listener is registered. Exercise both paths to verify that the
assert_not_equals(buttonsToStr(navigator.getGamepads()[0].buttons), oldButtonsStr, 'navigator.getGamepads()[0].buttons'); // state changes are handled correctly in either case.
oldButtonsStr = buttonsToStr(navigator.getGamepads()[0].buttons); let disconnectListener = (e) => {};
gamepadController.setAxisData(0, 0, .9); window.addEventListener('gamepaddisconnected', disconnectListener);
gamepadController.setButtonData(0, 0, .3); testNoChangeReturnsSameObjects();
assert_not_equals(axesToStr(navigator.getGamepads()[0].axes), oldAxesStr, 'navigator.getGamepads()[0].axes'); testSameValueUpdateReturnsSameObjects();
assert_not_equals(buttonsToStr(navigator.getGamepads()[0].buttons), oldButtonsStr, 'navigator.getGamepads()[0].buttons'); // TODO(crbug.com/855760): With a listener registered, Chrome will only
// create a new object if there was a gamepad disconnection. Otherwise,
// the previous gamepad object is overwritten with the new data. Re-enable
// this test once Chrome correctly creates a new gamepad object.
//testDifferentValueUpdateReturnsNewObjects();
window.removeEventListener('gamepaddisconnected', disconnectListener);
testNoChangeReturnsSameObjects();
testSameValueUpdateReturnsSameObjects();
// TODO(crbug.com/855760): With no listener registered, Chrome always reuses
// the gamepad object. Re-enable this test once Chrome correctly creates a
// new gamepad object.
//testDifferentValueUpdateReturnsNewObjects();
}, "Typical polling access to gamepads contents."); }, "Typical polling access to gamepads contents.");
</script> </script>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment