blob: 43f5df3a21baeaad1912b6620984d9aa4fe1ce40 [file] [log] [blame]
<!DOCTYPE html>
<body>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<script src="resources/gamepad-helpers.js"></script>
<script>
function changeOneGamepadId(gamepadIdString) {
// Change the gamepad ID string to a new value.
gamepadController.setId(0, gamepadIdString);
gamepadController.dispatchConnected(0);
}
function changeTwoGamepadIds(gamepadIdString) {
// Change the gamepad ID strings for two gamepads. Dispatch after modifying
// both IDs to simulate both changing as a result of the same gamepad poll
// event.
gamepadController.setId(0, gamepadIdString);
gamepadController.setId(1, gamepadIdString);
gamepadController.dispatchConnected(0);
gamepadController.dispatchConnected(1);
}
promise_test(async (t) => {
disconnectGamepads();
testGamepadStateAllDisconnected();
let connectListener1Called = false;
let connectListener2Called = false;
let disconnectListenerCalled = false;
// Typically it is not possible for the gamepad connection state to change
// while an event listener is being run, but the test API allows us to
// disconnect a gamepad in javascript. Make sure that the gamepad state is
// not changed while listeners are still running, and that disconnect
// listeners are still called if the disconnect occurs within an event
// listener.
let connectListener1 = (e) => {
assert_equals(
e.gamepad.connected, true,
"expected gamepad connected in first connect listener");
disconnectGamepads();
assert_equals(
e.gamepad.connected, true,
"expected gamepad still connected in first listener");
connectListener1Called = true;
};
let connectListener2 = (e) => {
assert_equals(
e.gamepad.connected, true,
"expected gamepad connected in second connect listener");
disconnectGamepads();
assert_equals(
e.gamepad.connected, true,
"expected gamepad still connected in second listener");
connectListener2Called = true;
};
let disconnectListener = (e) => {
assert_equals(
e.gamepad.connected, false,
"expected gamepad disconnected in disconnect listener");
// Ensure all gamepadconnected listeners have completed.
assert_equals(
connectListener1Called, true,
"expected first connect listener called before disconnect");
assert_equals(
connectListener2Called, true,
"expected second connect listener called before disconnect");
disconnectListenerCalled = true;
};
window.addEventListener('gamepadconnected', connectListener1);
window.addEventListener('gamepadconnected', connectListener2);
window.addEventListener('gamepaddisconnected', disconnectListener);
// Simulate a connection, and expect a disconnection to occur during the
// gamepadconnected event listeners.
let connectPromise = ongamepadconnected();
let disconnectPromise = ongamepaddisconnected();
connectGamepads(1);
await Promise.all([connectPromise, disconnectPromise]);
assert_equals(connectListener1Called, true,
"expected first gamepadconnected listener to be called");
assert_equals(connectListener2Called, true,
"expected second gamepadconnected listener to be called");
assert_equals(disconnectListenerCalled, true,
"expected gamepaddisconnected listener to be called");
testGamepadStateAllDisconnected();
// Clean up.
window.removeEventListener('gamepadconnected', connectListener1);
window.removeEventListener('gamepadconnected', connectListener2);
window.removeEventListener('gamepaddisconnected', disconnectListener);
}, "Gamepad disconnection inside event listener.");
promise_test(async (t) => {
disconnectGamepads();
testGamepadStateAllDisconnected();
let connectListener1Called = false;
let connectListener2Called = false;
let disconnectListener1Called = false;
let disconnectListener2Called = false;
// Configure two event listeners such that the first listener to be executed
// will remove the other listener.
let connectListener1 = (e) => {
window.removeEventListener('gamepadconnected', connectListener2);
connectListener1Called = true;
};
let connectListener2 = (e) => {
window.removeEventListener('gamepadconnected', connectListener1);
connectListener2Called = true;
};
let disconnectListener1 = (e) => {
window.removeEventListener('gamepaddisconnected', disconnectListener2);
disconnectListener1Called = true;
};
let disconnectListener2 = (e) => {
window.removeEventListener('gamepaddisconnected', disconnectListener1);
disconnectListener2Called = true;
};
window.addEventListener('gamepadconnected', connectListener1);
window.addEventListener('gamepadconnected', connectListener2);
window.addEventListener('gamepaddisconnected', disconnectListener1);
window.addEventListener('gamepaddisconnected', disconnectListener2);
// Simulate a connection.
let connectPromise = ongamepadconnected();
connectGamepads(1);
await connectPromise;
let numConnectListenersCalled = Number(connectListener1Called) +
Number(connectListener2Called);
assert_equals(numConnectListenersCalled, 1,
"expected exactly one connect listener to be called");
// Simulate a disconnection.
let disconnectPromise = ongamepaddisconnected();
disconnectGamepads();
await disconnectPromise;
let numDisconnectListenersCalled = Number(disconnectListener1Called) +
Number(disconnectListener2Called);
assert_equals(numDisconnectListenersCalled, 1,
"expected exactly one disconnect listener to be called");
testGamepadStateAllDisconnected();
// Clean up.
window.removeEventListener('gamepadconnected', connectListener1);
window.removeEventListener('gamepadconnected', connectListener2);
window.removeEventListener('gamepaddisconnected', disconnectListener1);
window.removeEventListener('gamepaddisconnected', disconnectListener2);
}, "Remove event listener from inside event listener.");
promise_test(async (t) => {
disconnectGamepads();
testGamepadStateAllDisconnected();
// In normal circumstances, a gamepad is only connected or disconnected when
// a signal is received from the host API. However, if the page is inactive
// when this signal is received, the renderer checks for connection changes
// and fires the appropriate events once the page is active.
//
// If both a disconnection and a connection occur while the page is
// inactive, the renderer compares the previous device info with the current
// device info to determine whether it is the same device. If it differs,
// gamepaddisconnected and gamepadconnected are fired.
//
// This test simulates this case by changing the gamepad ID string.
// Connect a gamepad.
let connectPromise1 = ongamepadconnected();
connectGamepads(1);
await connectPromise1;
// Register connect and disconnect listeners, and check that they are called
// in the correct order (disconnect first).
let connectListenerCalled = false;
let disconnectListenerCalled = false;
let disconnectListener = (e) => {
assert_equals(connectListenerCalled, false,
"disconnect listener should be called first");
assert_equals(disconnectListenerCalled, false,
"expected disconnect listener to be called only once");
disconnectListenerCalled = true;
};
let connectListener = (e) => {
assert_equals(disconnectListenerCalled, true,
"connect listener should be called second");
assert_equals(connectListenerCalled, false,
"expected connect listener to be called only once");
connectListenerCalled = true;
};
window.addEventListener('gamepadconnected', connectListener, {once: true});
window.addEventListener('gamepaddisconnected', disconnectListener,
{once: true});
// Simulate a change to the gamepad ID. This should be detected as a
// different gamepad and fire connection change events.
let disconnectPromise1 = ongamepaddisconnected();
let connectPromise2 = ongamepadconnected();
changeOneGamepadId("MockStick 3001");
await Promise.all([disconnectPromise1, connectPromise2]);
assert_equals(disconnectListenerCalled, true,
"expected disconnect listener to be called");
assert_equals(connectListenerCalled, true,
"expected connect listener to be called");
// Simulate a disconnection.
let disconnectPromise2 = ongamepaddisconnected();
disconnectGamepads();
await disconnectPromise2;
testGamepadStateAllDisconnected();
}, "Check disconnect listener called first during gamepad ID change.");
promise_test(async (t) => {
disconnectGamepads();
testGamepadStateAllDisconnected();
// Connect a gamepad.
let connectPromise = ongamepadconnected();
connectGamepads(1);
await connectPromise;
// Change the gamepad ID. When the ID is changed there are no
// gamepadconnected listeners, but one is added in the gamepaddisconnected
// listener. Make sure the newly-added gamepadconnected listener is called.
let disconnectThenConnectPromise = new Promise(resolve => {
window.addEventListener('gamepaddisconnected', (e) => {
window.addEventListener('gamepadconnected', (e) => {
resolve();
}, {once: true});
}, {once: true});
});
changeOneGamepadId("MockStick 3001");
await disconnectThenConnectPromise;
// Disconnect the gamepad.
let disconnectPromise = ongamepaddisconnected();
disconnectGamepads();
await disconnectPromise;
testGamepadStateAllDisconnected();
}, "Add connection event listener while handling a gamepad ID change.");
promise_test(async (t) => {
disconnectGamepads();
testGamepadStateAllDisconnected();
let eventWatcher = new EventWatcher(t, window, ['gamepadconnected',
'gamepaddisconnected']);
let eventPromise = eventWatcher.wait_for([
// Connect gamepads.
'gamepadconnected',
'gamepadconnected',
// Change one gamepad ID.
'gamepaddisconnected',
'gamepadconnected',
// Change the other gamepad ID.
'gamepaddisconnected',
'gamepadconnected',
// Disconnect gamepads.
'gamepaddisconnected',
'gamepaddisconnected'
]);
// Connect two gamepads.
let connectPromise1 = onGamepadEventWithIndex('gamepadconnected', 0);
let connectPromise2 = onGamepadEventWithIndex('gamepadconnected', 1);
connectGamepads(2);
await Promise.all([connectPromise1, connectPromise2]);
// Simulate a gamepad ID change for both gamepads.
let disconnectPromise1 = onGamepadEventWithIndex('gamepaddisconnected', 0);
let disconnectPromise2 = onGamepadEventWithIndex('gamepaddisconnected', 1);
let connectPromise3 = onGamepadEventWithIndex('gamepadconnected', 0);
let connectPromise4 = onGamepadEventWithIndex('gamepadconnected', 1);
changeTwoGamepadIds("MockStick 3001");
await Promise.all([connectPromise3, connectPromise4,
disconnectPromise1, disconnectPromise2]);
// Disconnect both gamepads.
let disconnectPromise3 = onGamepadEventWithIndex('gamepaddisconnected', 0);
let disconnectPromise4 = onGamepadEventWithIndex('gamepaddisconnected', 1);
disconnectGamepads();
await Promise.all([disconnectPromise3, disconnectPromise4]);
// Verify that gamepadconnected and gamepaddisconnected events were received
// in the expected order.
await eventPromise;
testGamepadStateAllDisconnected();
}, "Check connection event order during two gamepad ID changes.");
promise_test(async (t) => {
disconnectGamepads();
testGamepadStateAllDisconnected();
// Connect two gamepads.
let connectPromise1 = onGamepadEventWithIndex('gamepadconnected', 0);
let connectPromise2 = onGamepadEventWithIndex('gamepadconnected', 1);
connectGamepads(2);
await Promise.all([connectPromise1, connectPromise2]);
// Change the gamepad IDs. When the ID is changed there are gamepadconnected
// and gamepaddisconnected listeners, but both are removed by the
// gamepaddisconnected listener. This leaves zero listeners for the
// remainder of events. Make sure the removed listeners are not called.
let listenersRemoved = false;
let disconnectListener = (e) => {
assert_equals(listenersRemoved, false,
"disconnect listener called unexpectedly");
window.removeEventListener('gamepaddisconnected', disconnectListener);
window.removeEventListener('gamepadconnected', connectListener);
listenersRemoved = true;
}
let connectListener = (e) => {
assert_equals(listenersRemoved, false,
"connect listener called unexpectedly");
};
window.addEventListener('gamepadconnected', connectListener);
window.addEventListener('gamepaddisconnected', disconnectListener);
// Simulate changing both gamepad IDs. Set a zero-length timeout instead of
// waiting for connection events to avoid registering more listeners.
changeTwoGamepadIds("MockStick 3001");
await new Promise(resolve => setInterval(resolve, 0));
// Disconnect both gamepads.
let disconnectPromise1 = onGamepadEventWithIndex('gamepaddisconnected', 0);
let disconnectPromise2 = onGamepadEventWithIndex('gamepaddisconnected', 1);
disconnectGamepads();
await Promise.all([disconnectPromise1, disconnectPromise2]);
testGamepadStateAllDisconnected();
}, "Remove all connection event listeners while handling a gamepad ID change.");
</script>
</body>