From 43a97878ce14b72f0981164f87f2e35e14151312 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:22:09 +0200 Subject: Adding upstream version 110.0.1. Signed-off-by: Daniel Baumann --- testing/web-platform/tests/serial/META.yml | 1 + testing/web-platform/tests/serial/README.md | 20 ++ .../tests/serial/idlharness.https.any.js | 22 ++ .../tests/serial/resources/automation.js | 66 +++++ .../web-platform/tests/serial/resources/common.js | 33 +++ .../web-platform/tests/serial/resources/manual.js | 38 +++ .../serial-allowed-by-permissions-policy-worker.js | 14 + ...serial-disabled-by-permissions-policy-worker.js | 17 ++ ...olicy-attribute-redirect-on-load.https.sub.html | 44 ++++ ...-by-permissions-policy-attribute.https.sub.html | 46 ++++ ...al-allowed-by-permissions-policy.https.sub.html | 49 ++++ ...ed-by-permissions-policy.https.sub.html.headers | 1 + ...erial-default-permissions-policy.https.sub.html | 27 ++ ...l-disabled-by-permissions-policy.https.sub.html | 48 ++++ ...ed-by-permissions-policy.https.sub.html.headers | 1 + .../tests/serial/serialPort_close.https.any.js | 26 ++ .../serial/serialPort_disconnect-manual.https.html | 83 ++++++ .../tests/serial/serialPort_events.https.any.js | 197 ++++++++++++++ .../tests/serial/serialPort_forget.https.any.js | 43 +++ .../tests/serial/serialPort_getInfo.https.any.js | 24 ++ .../serial/serialPort_getSignals.https.any.js | 63 +++++ .../serial/serialPort_loopback-manual.https.html | 97 +++++++ ...erialPort_loopback_BreakError-manual.https.html | 55 ++++ ...t_loopback_BufferOverrunError-manual.https.html | 65 +++++ ...rialPort_loopback_flowControl-manual.https.html | 59 +++++ .../serial/serialPort_ondisconnect.https.any.js | 40 +++ .../tests/serial/serialPort_open.https.any.js | 95 +++++++ .../serial/serialPort_readable-manual.https.html | 107 ++++++++ .../serial/serialPort_readable_byob.https.any.js | 65 +++++ .../serial/serialPort_readable_cancel.https.any.js | 69 +++++ .../serial/serialPort_readable_chain.https.any.js | 30 +++ .../serialPort_readable_closeLocked.https.any.js | 17 ++ .../serialPort_readable_disconnect.https.any.js | 25 ++ .../serialPort_readable_largeRead.https.any.js | 23 ++ .../serial/serialPort_readable_open.https.any.js | 21 ++ .../serialPort_readable_parityError.https.any.js | 52 ++++ .../serialPort_readable_pipeThrough.https.any.js | 26 ++ .../serialPort_readable_smallRead.https.any.js | 23 ++ .../serial/serialPort_setSignals.https.any.js | 59 +++++ .../tests/serial/serialPort_writable.https.any.js | 289 +++++++++++++++++++++ .../serialPort_writable_detachBuffer.https.any.js | 48 ++++ .../tests/serial/serial_getPorts.https.any.js | 25 ++ .../tests/serial/serial_onconnect.https.any.js | 29 +++ .../tests/serial/serial_ondisconnect.https.any.js | 37 +++ .../serial/serial_requestPort.https.window.js | 66 +++++ 45 files changed, 2285 insertions(+) create mode 100644 testing/web-platform/tests/serial/META.yml create mode 100644 testing/web-platform/tests/serial/README.md create mode 100644 testing/web-platform/tests/serial/idlharness.https.any.js create mode 100644 testing/web-platform/tests/serial/resources/automation.js create mode 100644 testing/web-platform/tests/serial/resources/common.js create mode 100644 testing/web-platform/tests/serial/resources/manual.js create mode 100644 testing/web-platform/tests/serial/resources/serial-allowed-by-permissions-policy-worker.js create mode 100644 testing/web-platform/tests/serial/resources/serial-disabled-by-permissions-policy-worker.js create mode 100644 testing/web-platform/tests/serial/serial-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html create mode 100644 testing/web-platform/tests/serial/serial-allowed-by-permissions-policy-attribute.https.sub.html create mode 100644 testing/web-platform/tests/serial/serial-allowed-by-permissions-policy.https.sub.html create mode 100644 testing/web-platform/tests/serial/serial-allowed-by-permissions-policy.https.sub.html.headers create mode 100644 testing/web-platform/tests/serial/serial-default-permissions-policy.https.sub.html create mode 100644 testing/web-platform/tests/serial/serial-disabled-by-permissions-policy.https.sub.html create mode 100644 testing/web-platform/tests/serial/serial-disabled-by-permissions-policy.https.sub.html.headers create mode 100644 testing/web-platform/tests/serial/serialPort_close.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_disconnect-manual.https.html create mode 100644 testing/web-platform/tests/serial/serialPort_events.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_forget.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_getInfo.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_getSignals.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_loopback-manual.https.html create mode 100644 testing/web-platform/tests/serial/serialPort_loopback_BreakError-manual.https.html create mode 100644 testing/web-platform/tests/serial/serialPort_loopback_BufferOverrunError-manual.https.html create mode 100644 testing/web-platform/tests/serial/serialPort_loopback_flowControl-manual.https.html create mode 100644 testing/web-platform/tests/serial/serialPort_ondisconnect.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_open.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_readable-manual.https.html create mode 100644 testing/web-platform/tests/serial/serialPort_readable_byob.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_readable_cancel.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_readable_chain.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_readable_closeLocked.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_readable_disconnect.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_readable_largeRead.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_readable_open.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_readable_parityError.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_readable_pipeThrough.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_readable_smallRead.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_setSignals.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_writable.https.any.js create mode 100644 testing/web-platform/tests/serial/serialPort_writable_detachBuffer.https.any.js create mode 100644 testing/web-platform/tests/serial/serial_getPorts.https.any.js create mode 100644 testing/web-platform/tests/serial/serial_onconnect.https.any.js create mode 100644 testing/web-platform/tests/serial/serial_ondisconnect.https.any.js create mode 100644 testing/web-platform/tests/serial/serial_requestPort.https.window.js (limited to 'testing/web-platform/tests/serial') diff --git a/testing/web-platform/tests/serial/META.yml b/testing/web-platform/tests/serial/META.yml new file mode 100644 index 0000000000..64938c9298 --- /dev/null +++ b/testing/web-platform/tests/serial/META.yml @@ -0,0 +1 @@ +spec: https://wicg.github.io/serial/ diff --git a/testing/web-platform/tests/serial/README.md b/testing/web-platform/tests/serial/README.md new file mode 100644 index 0000000000..7040cf0a9d --- /dev/null +++ b/testing/web-platform/tests/serial/README.md @@ -0,0 +1,20 @@ +# Web Serial Testing + +Automated testing for the [Web Serial API] relies on a test-only interface which +must be provided by browsers under test. This is similar to [WebUSB] however +there is no separate specification of the API other than the tests themselves +and the Chromium implementation. + +Tests in this suite include `resources/automation.js` to detect and load the +test API as needed. + +The Chromium implementation is provided by +`../resources/chromium/fake-serial.js` using [MojoJS]. + +Tests with the "-manual" suffix do not use the test-only interface and expect a +real hardware device to be connected. The specific characteristics of the device +are described in each test. + +[MojoJS]: https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/testing/web_platform_tests.md#mojojs +[WebUSB]: ../webusb +[Web Serial API]: https://wicg.github.io/serial diff --git a/testing/web-platform/tests/serial/idlharness.https.any.js b/testing/web-platform/tests/serial/idlharness.https.any.js new file mode 100644 index 0000000000..b240da933a --- /dev/null +++ b/testing/web-platform/tests/serial/idlharness.https.any.js @@ -0,0 +1,22 @@ +// META: script=/resources/WebIDLParser.js +// META: script=/resources/idlharness.js + +'use strict'; + +idl_test( + ['serial'], + ['html', 'dom'], + idl_array => { + idl_array.add_objects({ + Serial: ['navigator.serial'], + // TODO: SerialPort + // TODO: SerialPortInfo + }); + + if (self.GLOBAL.isWorker()) { + idl_array.add_objects({ WorkerNavigator: ['navigator'] }); + } else { + idl_array.add_objects({ Navigator: ['navigator'] }); + } + } +); diff --git a/testing/web-platform/tests/serial/resources/automation.js b/testing/web-platform/tests/serial/resources/automation.js new file mode 100644 index 0000000000..e88fdb1a9d --- /dev/null +++ b/testing/web-platform/tests/serial/resources/automation.js @@ -0,0 +1,66 @@ +'use strict'; + +// These tests rely on the User Agent providing an implementation of the +// FakeSerialService interface which replaces the platform-specific +// implementation of the Web Serial API with one that can be automated from +// Javascript for testing purposes. +// +// In Chromium-based browsers this implementation is provided by a polyfill +// in order to reduce the amount of test-only code shipped to users. To enable +// these tests the browser must be run with these options: +// +// --enable-blink-features=MojoJS,MojoJSTest + +let fakeSerialService = undefined; + +// Returns a SerialPort instance and associated FakeSerialPort instance. +async function getFakeSerialPort(fake) { + let token = fake.addPort(); + let fakePort = fake.getFakePort(token); + + let ports = await navigator.serial.getPorts(); + assert_equals(ports.length, 1); + + let port = ports[0]; + assert_true(port instanceof SerialPort); + + return { port, fakePort }; +} + +function serial_test(func, name, properties) { + promise_test(async (test) => { + assert_implements(navigator.serial, 'missing navigator.serial'); + if (fakeSerialService === undefined) { + // Try loading a polyfill for the fake serial service. + if (isChromiumBased) { + const fakes = await import('/resources/chromium/fake-serial.js'); + fakeSerialService = fakes.fakeSerialService; + } + } + assert_implements(fakeSerialService, 'missing fakeSerialService after initialization'); + + fakeSerialService.start(); + try { + await func(test, fakeSerialService); + } finally { + fakeSerialService.stop(); + fakeSerialService.reset(); + } + }, name, properties); +} + +function trustedClick() { + return new Promise(resolve => { + let button = document.createElement('button'); + button.textContent = 'click to continue test'; + button.style.display = 'block'; + button.style.fontSize = '20px'; + button.style.padding = '10px'; + button.onclick = () => { + document.body.removeChild(button); + resolve(); + }; + document.body.appendChild(button); + test_driver.click(button); + }); +} diff --git a/testing/web-platform/tests/serial/resources/common.js b/testing/web-platform/tests/serial/resources/common.js new file mode 100644 index 0000000000..5177f83a86 --- /dev/null +++ b/testing/web-platform/tests/serial/resources/common.js @@ -0,0 +1,33 @@ +// Compare two Uint8Arrays. +function compareArrays(actual, expected) { + assert_true(actual instanceof Uint8Array, 'actual is Uint8Array'); + assert_true(expected instanceof Uint8Array, 'expected is Uint8Array'); + assert_equals(actual.byteLength, expected.byteLength, 'lengths equal'); + for (let i = 0; i < expected.byteLength; ++i) + assert_equals(actual[i], expected[i], `Mismatch at position ${i}.`); +} + +// Reads from |reader| until at least |targetLength| is read or the stream is +// closed. The data is returned as a combined Uint8Array. +async function readWithLength(reader, targetLength) { + const chunks = []; + let actualLength = 0; + + while (true) { + let {value, done} = await reader.read(); + chunks.push(value); + actualLength += value.byteLength; + + if (actualLength >= targetLength || done) { + // It would be better to allocate |buffer| up front with the number of + // of bytes expected but this is the best that can be done without a + // BYOB reader to control the amount of data read. + const buffer = new Uint8Array(actualLength); + chunks.reduce((offset, chunk) => { + buffer.set(chunk, offset); + return offset + chunk.byteLength; + }, 0); + return buffer; + } + } +} diff --git a/testing/web-platform/tests/serial/resources/manual.js b/testing/web-platform/tests/serial/resources/manual.js new file mode 100644 index 0000000000..ac19136b35 --- /dev/null +++ b/testing/web-platform/tests/serial/resources/manual.js @@ -0,0 +1,38 @@ +let manualTestPort = null; + +navigator.serial.addEventListener('disconnect', (e) => { + if (e.target === manualTestPort) { + manualTestPort = null; + } +}) + +async function getPortForManualTest() { + if (manualTestPort) { + return manualTestPort; + } + + const button = document.createElement('button'); + button.textContent = 'Click to select a device'; + button.style.display = 'block'; + button.style.fontSize = '20px'; + button.style.padding = '10px'; + + await new Promise((resolve) => { + button.onclick = () => { + document.body.removeChild(button); + resolve(); + }; + document.body.appendChild(button); + }); + + manualTestPort = await navigator.serial.requestPort({filters: []}); + assert_true(manualTestPort instanceof SerialPort); + + return manualTestPort; +} + +function manual_serial_test(func, name, properties) { + promise_test(async (test) => { + await func(test, await getPortForManualTest()); + }, name, properties); +} diff --git a/testing/web-platform/tests/serial/resources/serial-allowed-by-permissions-policy-worker.js b/testing/web-platform/tests/serial/resources/serial-allowed-by-permissions-policy-worker.js new file mode 100644 index 0000000000..cef0aacdfc --- /dev/null +++ b/testing/web-platform/tests/serial/resources/serial-allowed-by-permissions-policy-worker.js @@ -0,0 +1,14 @@ +'use strict'; + +importScripts('/resources/testharness.js'); + +let workerType; + +if (typeof postMessage === 'function') { + workerType = 'dedicated'; +} + +promise_test(() => navigator.serial.getPorts(), + `Inherited header permissions policy allows ${workerType} workers.`); + +done(); diff --git a/testing/web-platform/tests/serial/resources/serial-disabled-by-permissions-policy-worker.js b/testing/web-platform/tests/serial/resources/serial-disabled-by-permissions-policy-worker.js new file mode 100644 index 0000000000..afac442974 --- /dev/null +++ b/testing/web-platform/tests/serial/resources/serial-disabled-by-permissions-policy-worker.js @@ -0,0 +1,17 @@ +'use strict'; + +importScripts('/resources/testharness.js'); + +const header = 'Permissions-Policy header serial=()'; +let workerType; + +if (typeof postMessage === 'function') { + workerType = 'dedicated'; +} + +promise_test(() => navigator.serial.getPorts().then( + () => assert_unreached('expected promise to reject with SecurityError'), + error => assert_equals(error.name, 'SecurityError')), + `Inherited ${header} disallows ${workerType} workers.`); + +done(); diff --git a/testing/web-platform/tests/serial/serial-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html b/testing/web-platform/tests/serial/serial-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html new file mode 100644 index 0000000000..ac278ff0cf --- /dev/null +++ b/testing/web-platform/tests/serial/serial-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html @@ -0,0 +1,44 @@ + + + + + + + diff --git a/testing/web-platform/tests/serial/serial-allowed-by-permissions-policy-attribute.https.sub.html b/testing/web-platform/tests/serial/serial-allowed-by-permissions-policy-attribute.https.sub.html new file mode 100644 index 0000000000..02560b180f --- /dev/null +++ b/testing/web-platform/tests/serial/serial-allowed-by-permissions-policy-attribute.https.sub.html @@ -0,0 +1,46 @@ + + + + + + + diff --git a/testing/web-platform/tests/serial/serial-allowed-by-permissions-policy.https.sub.html b/testing/web-platform/tests/serial/serial-allowed-by-permissions-policy.https.sub.html new file mode 100644 index 0000000000..1be8eb979f --- /dev/null +++ b/testing/web-platform/tests/serial/serial-allowed-by-permissions-policy.https.sub.html @@ -0,0 +1,49 @@ + + + + + + + + diff --git a/testing/web-platform/tests/serial/serial-allowed-by-permissions-policy.https.sub.html.headers b/testing/web-platform/tests/serial/serial-allowed-by-permissions-policy.https.sub.html.headers new file mode 100644 index 0000000000..27deb82758 --- /dev/null +++ b/testing/web-platform/tests/serial/serial-allowed-by-permissions-policy.https.sub.html.headers @@ -0,0 +1 @@ +Permissions-Policy: serial=* diff --git a/testing/web-platform/tests/serial/serial-default-permissions-policy.https.sub.html b/testing/web-platform/tests/serial/serial-default-permissions-policy.https.sub.html new file mode 100644 index 0000000000..e3908d9215 --- /dev/null +++ b/testing/web-platform/tests/serial/serial-default-permissions-policy.https.sub.html @@ -0,0 +1,27 @@ + + + + + + + diff --git a/testing/web-platform/tests/serial/serial-disabled-by-permissions-policy.https.sub.html b/testing/web-platform/tests/serial/serial-disabled-by-permissions-policy.https.sub.html new file mode 100644 index 0000000000..53646c526c --- /dev/null +++ b/testing/web-platform/tests/serial/serial-disabled-by-permissions-policy.https.sub.html @@ -0,0 +1,48 @@ + + + + + + + diff --git a/testing/web-platform/tests/serial/serial-disabled-by-permissions-policy.https.sub.html.headers b/testing/web-platform/tests/serial/serial-disabled-by-permissions-policy.https.sub.html.headers new file mode 100644 index 0000000000..690b696751 --- /dev/null +++ b/testing/web-platform/tests/serial/serial-disabled-by-permissions-policy.https.sub.html.headers @@ -0,0 +1 @@ +Permissions-Policy: serial=() diff --git a/testing/web-platform/tests/serial/serialPort_close.https.any.js b/testing/web-platform/tests/serial/serialPort_close.https.any.js new file mode 100644 index 0000000000..ec949a9917 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_close.https.any.js @@ -0,0 +1,26 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + await promise_rejects_dom(t, 'InvalidStateError', port.close()); +}, 'A SerialPort cannot be closed if it was never opened.'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + await port.open({baudRate: 9600}); + await port.close(); + await promise_rejects_dom(t, 'InvalidStateError', port.close()); +}, 'A SerialPort cannot be closed if it is already closed.'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + await port.open({baudRate: 9600}); + const closePromise = port.close(); + await promise_rejects_dom(t, 'InvalidStateError', port.close()); + await closePromise; +}, 'A SerialPort cannot be closed if it is being closed.'); diff --git a/testing/web-platform/tests/serial/serialPort_disconnect-manual.https.html b/testing/web-platform/tests/serial/serialPort_disconnect-manual.https.html new file mode 100644 index 0000000000..5386201e6c --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_disconnect-manual.https.html @@ -0,0 +1,83 @@ + + + + + + + + + + + +

+ These tests require a serial device to be connected and disconnected. +

+ + + diff --git a/testing/web-platform/tests/serial/serialPort_events.https.any.js b/testing/web-platform/tests/serial/serialPort_events.https.any.js new file mode 100644 index 0000000000..fa3db6a3b3 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_events.https.any.js @@ -0,0 +1,197 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + const targets = [navigator.serial, port]; + const expectedTargets = [navigator.serial]; + + const actualTargets = []; + function eventHandler(evt) { + actualTargets.push(evt.currentTarget); + + if (evt.currentTarget == navigator.serial) { + evt.stopPropagation(); + } + } + + targets.forEach((target) => { + target.addEventListener('foo', eventHandler, {capture: true}); + // stopPropagation() during capturing prevents bubbling. + target.addEventListener('foo', eventHandler); + + t.add_cleanup(() => { + target.removeEventListener('foo', eventHandler, {capture: true}); + target.removeEventListener('foo', eventHandler); + }); + }); + + port.dispatchEvent(new CustomEvent('foo', {bubbles: true})); + + assert_array_equals(actualTargets, expectedTargets, 'actualTargets'); +}, 'stopPropagation() during capturing'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + const targets = [navigator.serial, port]; + const expectedTargets = [navigator.serial]; + + const actualTargets = []; + function eventHandler(evt) { + actualTargets.push(evt.currentTarget); + + if (evt.currentTarget == navigator.serial) { + evt.cancelBubble = true; + } + } + + targets.forEach((target) => { + target.addEventListener('foo', eventHandler, {capture: true}); + // Setting cancelBubble during capturing prevents bubbling. + target.addEventListener('foo', eventHandler); + + t.add_cleanup(() => { + target.removeEventListener('foo', eventHandler, {capture: true}); + target.removeEventListener('foo', eventHandler); + }); + }); + + port.dispatchEvent(new CustomEvent('foo', {bubbles: true})); + + assert_array_equals(actualTargets, expectedTargets, 'actualTargets'); +}, 'Set cancelBubble during capturing'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + const targets = [navigator.serial, port]; + const expectedTargets = [port]; + + const actualTargets = []; + function eventHandler(evt) { + actualTargets.push(evt.currentTarget); + + if (evt.currentTarget == port) { + evt.stopPropagation(); + } + } + + targets.forEach((target) => { + target.addEventListener('foo', eventHandler); + + t.add_cleanup(() => { + target.removeEventListener('foo', eventHandler); + }); + }); + + port.dispatchEvent(new CustomEvent('foo', {bubbles: true})); + + assert_array_equals(actualTargets, expectedTargets, 'actualTargets'); +}, 'stopPropagation() during bubbling'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + const targets = [navigator.serial, port]; + const expectedTargets = [port]; + + const actualTargets = []; + function eventHandler(evt) { + actualTargets.push(evt.currentTarget); + + if (evt.currentTarget == port) { + evt.cancelBubble = true; + } + } + + targets.forEach((target) => { + target.addEventListener('foo', eventHandler); + + t.add_cleanup(() => { + target.removeEventListener('foo', eventHandler); + }); + }); + + port.dispatchEvent(new CustomEvent('foo', {bubbles: true})); + + assert_array_equals(actualTargets, expectedTargets, 'actualTargets'); +}, 'Set cancelBubble during bubbling'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + const targets = [navigator.serial, port]; + const expectedTargets = [ + navigator.serial, + port, + navigator.serial, + port, + ]; + const expectedTypes = [ + 'foo', + 'bar', + 'bar', + 'foo', + ]; + + const actualTargets = []; + const actualTypes = []; + function eventHandler(evt) { + actualTargets.push(evt.currentTarget); + actualTypes.push(evt.type); + + if (evt.currentTarget == navigator.serial && evt.type == 'foo') { + port.dispatchEvent(new CustomEvent('bar', {bubbles: true})); + } + } + + targets.forEach((target) => { + target.addEventListener('foo', eventHandler, {capture: true}); + target.addEventListener('bar', eventHandler); + + t.add_cleanup(() => { + target.removeEventListener('foo', eventHandler, {capture: true}); + target.removeEventListener('bar', eventHandler); + }); + }); + + port.dispatchEvent(new CustomEvent('foo', {bubbles: true})); + + assert_array_equals(actualTargets, expectedTargets, 'actualTargets'); + assert_array_equals(actualTypes, expectedTypes, 'actualTypes'); +}, 'An event dispatched in an event handler is propagated before continuing'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + const targets = [navigator.serial, port]; + const expected = [ + 'capturing Serial', + 'capturing SerialPort', + 'bubbling SerialPort', + 'bubbling Serial', + ]; + + const actual = []; + targets.forEach((target) => { + const bubblingEventHandler = () => { + actual.push(`bubbling ${target.constructor.name}`); + }; + target.addEventListener('foo', bubblingEventHandler); + const capturingEventHandler = () => { + actual.push(`capturing ${target.constructor.name}`); + }; + target.addEventListener('foo', capturingEventHandler, {capture: true}); + + t.add_cleanup(() => { + target.removeEventListener('foo', bubblingEventHandler, {capture: true}); + target.removeEventListener('foo', capturingEventHandler); + }); + }); + + port.dispatchEvent(new CustomEvent('foo', {bubbles: true})); + assert_array_equals(actual, expected); +}, 'Capturing and bubbling events delivered to listeners in the expected order'); diff --git a/testing/web-platform/tests/serial/serialPort_forget.https.any.js b/testing/web-platform/tests/serial/serialPort_forget.https.any.js new file mode 100644 index 0000000000..8c0d96f695 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_forget.https.any.js @@ -0,0 +1,43 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + await port.forget(); + return promise_rejects_dom( + t, 'NetworkError', port.open({baudRate: 9600})); +}, 'open() rejects after forget()'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + await port.open({baudRate: 9600}); + await port.forget(); + return promise_rejects_dom(t, 'InvalidStateError', port.close()); +}, 'close() rejects after forget()'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + await port.open({baudRate: 9600}); + await port.forget(); + return promise_rejects_dom(t, 'InvalidStateError', port.setSignals()); +}, 'setSignals() rejects after forget()'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + await port.open({baudRate: 9600}); + await port.forget(); + return promise_rejects_dom(t, 'InvalidStateError', port.getSignals()); +}, 'getSignals() rejects after forget()'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + const portsBeforeForget = await navigator.serial.getPorts(); + assert_equals(portsBeforeForget.length, 1); + assert_equals(portsBeforeForget[0], port); + + await port.forget(); + + const portsAfterForget = await navigator.serial.getPorts(); + assert_equals(portsAfterForget.length, 0); +}, 'forget() removes the device from getPorts()'); \ No newline at end of file diff --git a/testing/web-platform/tests/serial/serialPort_getInfo.https.any.js b/testing/web-platform/tests/serial/serialPort_getInfo.https.any.js new file mode 100644 index 0000000000..2e0df3b8bd --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_getInfo.https.any.js @@ -0,0 +1,24 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + // Wait for getPorts() to resolve in order to ensure that the Mojo client + // interface has been configured. + let ports = await navigator.serial.getPorts(); + assert_equals(ports.length, 0); + + [{}, + {usbVendorId: 1}, + {usbProductId: 2}, + {usbVendorId: 1, usbProductId: 2}, + ].forEach((expectedInfo) => { + serial_test(async (t, fake) => { + let watcher = new EventWatcher(t, navigator.serial, ['connect']); + fake.addPort(expectedInfo); + let evt = await watcher.wait_for(['connect']); + let info = evt.target.getInfo(); + assert_object_equals(expectedInfo, info); + }, `getInfo() returns ${JSON.stringify(expectedInfo)}`); + }); +}, 'getInfo() meta test'); diff --git a/testing/web-platform/tests/serial/serialPort_getSignals.https.any.js b/testing/web-platform/tests/serial/serialPort_getSignals.https.any.js new file mode 100644 index 0000000000..a570e35987 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_getSignals.https.any.js @@ -0,0 +1,63 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + await promise_rejects_dom(t, 'InvalidStateError', port.getSignals()); +}, 'getSignals() rejects if the port is not open'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + await port.open({baudRate: 9600}); + + let expectedSignals = { + dataCarrierDetect: false, + clearToSend: false, + ringIndicator: false, + dataSetReady: false + }; + fakePort.simulateInputSignals(expectedSignals); + let signals = await port.getSignals(); + assert_object_equals(signals, expectedSignals); + + expectedSignals.dataCarrierDetect = true; + fakePort.simulateInputSignals(expectedSignals); + signals = await port.getSignals(); + assert_object_equals(signals, expectedSignals, 'DCD set'); + + expectedSignals.clearToSend = true; + fakePort.simulateInputSignals(expectedSignals); + signals = await port.getSignals(); + assert_object_equals(signals, expectedSignals, 'CTS set'); + + expectedSignals.ringIndicator = true; + fakePort.simulateInputSignals(expectedSignals); + signals = await port.getSignals(); + assert_object_equals(signals, expectedSignals, 'RI set'); + + expectedSignals.dataSetReady = true; + fakePort.simulateInputSignals(expectedSignals); + signals = await port.getSignals(); + assert_object_equals(signals, expectedSignals, 'DSR set'); +}, 'getSignals() returns the current state of input control signals'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + await port.open({baudRate: 9600}); + + fakePort.simulateInputSignalFailure(true); + await promise_rejects_dom(t, 'NetworkError', port.getSignals()); + + fakePort.simulateInputSignalFailure(false); + const expectedSignals = { + dataCarrierDetect: false, + clearToSend: false, + ringIndicator: false, + dataSetReady: false + }; + const signals = await port.getSignals(); + assert_object_equals(signals, expectedSignals); + + await port.close(); +}, 'getSignals() rejects on failure'); diff --git a/testing/web-platform/tests/serial/serialPort_loopback-manual.https.html b/testing/web-platform/tests/serial/serialPort_loopback-manual.https.html new file mode 100644 index 0000000000..bf876b6610 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_loopback-manual.https.html @@ -0,0 +1,97 @@ + + + + + + + + + + + +

+ These tests require a connected serial device configured to act as a + "loopback" device, with the transmit and receive pins wired together. +

+ + + diff --git a/testing/web-platform/tests/serial/serialPort_loopback_BreakError-manual.https.html b/testing/web-platform/tests/serial/serialPort_loopback_BreakError-manual.https.html new file mode 100644 index 0000000000..369fac6155 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_loopback_BreakError-manual.https.html @@ -0,0 +1,55 @@ + + + + + + + + + + + +

+ These tests require a connected serial device configured to act as a + "loopback" device, with the transmit and receive pins wired together. +

+ + + diff --git a/testing/web-platform/tests/serial/serialPort_loopback_BufferOverrunError-manual.https.html b/testing/web-platform/tests/serial/serialPort_loopback_BufferOverrunError-manual.https.html new file mode 100644 index 0000000000..06f8271ac9 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_loopback_BufferOverrunError-manual.https.html @@ -0,0 +1,65 @@ + + + + + + + + + + + +

+ These tests require a connected serial device configured to act as a + "loopback" device, with the transmit and receive pins wired together. +

+ + + diff --git a/testing/web-platform/tests/serial/serialPort_loopback_flowControl-manual.https.html b/testing/web-platform/tests/serial/serialPort_loopback_flowControl-manual.https.html new file mode 100644 index 0000000000..930dab74d6 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_loopback_flowControl-manual.https.html @@ -0,0 +1,59 @@ + + + + + + + + + + + +

+ These tests require a connected serial device configured to act as a + "loopback" device, with the TX and RX pins and RTS and CTS pins wired + together. +

+ + + diff --git a/testing/web-platform/tests/serial/serialPort_ondisconnect.https.any.js b/testing/web-platform/tests/serial/serialPort_ondisconnect.https.any.js new file mode 100644 index 0000000000..b20f69d265 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_ondisconnect.https.any.js @@ -0,0 +1,40 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + // Don't listen for 'disconnect' events on navigator.serial so we can listen + // for them on each SerialPort instance instead. + const eventWatcher = new EventWatcher(t, navigator.serial, ['connect']); + + // Wait for getPorts() to resolve in order to ensure that the Mojo client + // interface has been configured. + let ports = await navigator.serial.getPorts(); + assert_equals(ports.length, 0); + + // Add ports one at a time so that we can map tokens to ports. + const token1 = fake.addPort(); + const port1 = (await eventWatcher.wait_for(['connect'])).target; + const port1Watcher = new EventWatcher(t, port1, ['disconnect']); + + const token2 = fake.addPort(); + const port2 = (await eventWatcher.wait_for(['connect'])).target; + const port2Watcher = new EventWatcher(t, port2, ['disconnect']); + + fake.removePort(token2); + const event1 = await port2Watcher.wait_for(['disconnect']); + assert_true(event1 instanceof Event); + assert_equals(event1.target, port2); + + ports = await navigator.serial.getPorts(); + assert_equals(ports.length, 1); + assert_equals(ports[0], port1); + + fake.removePort(token1); + const event2 = await port1Watcher.wait_for(['disconnect']); + assert_true(event2 instanceof Event); + assert_equals(event2.target, port1); + + ports = await navigator.serial.getPorts(); + assert_equals(ports.length, 0); +}, 'A "disconnect" event is fired on ports when they are removed.'); diff --git a/testing/web-platform/tests/serial/serialPort_open.https.any.js b/testing/web-platform/tests/serial/serialPort_open.https.any.js new file mode 100644 index 0000000000..ca13877088 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_open.https.any.js @@ -0,0 +1,95 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + await port.open({baudRate: 9600}); + return promise_rejects_dom( + t, 'InvalidStateError', port.open({baudRate: 9600})); +}, 'A SerialPort cannot be opened if it is already open.'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + const firstRequest = port.open({baudRate: 9600}); + await promise_rejects_dom( + t, 'InvalidStateError', port.open({baudRate: 9600})); + await firstRequest; +}, 'Simultaneous calls to open() are disallowed.'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + await promise_rejects_js(t, TypeError, port.open({})); + + await Promise.all([-1, 0].map( + baudRate => { + return promise_rejects_js(t, TypeError, port.open({baudRate}))})); +}, 'Baud rate is required and must be greater than zero.'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + await Promise.all([-1, 0, 6, 9].map(dataBits => { + return promise_rejects_js( + t, TypeError, port.open({baudRate: 9600, dataBits})); + })); + + await[undefined, 7, 8].reduce(async (previousTest, dataBits) => { + await previousTest; + await port.open({baudRate: 9600, dataBits}); + await port.close(); + }, Promise.resolve()); +}, 'Data bits must be 7 or 8'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + await Promise.all([0, null, 'cats'].map(parity => { + return promise_rejects_js( + t, TypeError, port.open({baudRate: 9600, parity}), + `Should reject parity option "${parity}"`); + })); + + await[undefined, 'none', 'even', 'odd'].reduce( + async (previousTest, parity) => { + await previousTest; + await port.open({baudRate: 9600, parity}); + await port.close(); + }, + Promise.resolve()); +}, 'Parity must be "none", "even" or "odd"'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + await Promise.all([-1, 0, 3, 4].map(stopBits => { + return promise_rejects_js( + t, TypeError, port.open({baudRate: 9600, stopBits})); + })); + + await[undefined, 1, 2].reduce(async (previousTest, stopBits) => { + await previousTest; + await port.open({baudRate: 9600, stopBits}); + await port.close(); + }, Promise.resolve()); +}, 'Stop bits must be 1 or 2'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + await promise_rejects_js( + t, TypeError, port.open({baudRate: 9600, bufferSize: -1})); + await promise_rejects_js( + t, TypeError, port.open({baudRate: 9600, bufferSize: 0})); +}, 'Buffer size must be greater than zero.'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + const bufferSize = 1 * 1024 * 1024 * 1024 /* 1 GiB */; + return promise_rejects_js( + t, TypeError, port.open({baudRate: 9600, bufferSize})); +}, 'Unreasonably large buffer sizes are rejected.'); diff --git a/testing/web-platform/tests/serial/serialPort_readable-manual.https.html b/testing/web-platform/tests/serial/serialPort_readable-manual.https.html new file mode 100644 index 0000000000..4e49ef4061 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_readable-manual.https.html @@ -0,0 +1,107 @@ + + + + + + + + + + + +

+ These tests require a device configured with the following Arduino sketch: + +

+uint32_t seed = 0;
+uint32_t bytesToSend = 0;
+
+uint8_t nextByte() {
+  seed = (1103515245 * seed + 12345) % 0x8000000;
+  return (seed >> 16) & 0xFF;
+}
+
+void setup() {
+  Serial.begin(115200);
+}
+
+void loop() {
+  if (!Serial) {
+    return;
+  }
+
+  if (bytesToSend == 0) {
+    // Read the seed and number of bytes to send from the host as 32-bit
+    // little-endian values.
+    if (Serial.available() < 8) {
+      return;
+    }
+
+    uint8_t buf[8];
+    Serial.readBytes((char*)buf, sizeof buf);
+    seed = (uint32_t)buf[0] |
+            ((uint32_t)buf[1] << 8) |
+            ((uint32_t)buf[2] << 16) |
+            ((uint32_t)buf[3] << 24);
+    bytesToSend = (uint32_t)buf[4] |
+                  ((uint32_t)buf[5] << 8) |
+                  ((uint32_t)buf[6] << 16) |
+                  ((uint32_t)buf[7] << 24);
+  } else {
+    uint8_t buf[64];
+    uint32_t count = min(sizeof buf, bytesToSend);
+    for (uint32_t i = 0; i < count; ++i) {
+      buf[i] = nextByte();
+    }
+    bytesToSend -= count;
+    Serial.write((char*)buf, count);
+  }
+}
+      
+

+

+ +

+ + + diff --git a/testing/web-platform/tests/serial/serialPort_readable_byob.https.any.js b/testing/web-platform/tests/serial/serialPort_readable_byob.https.any.js new file mode 100644 index 0000000000..8672cf7124 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_readable_byob.https.any.js @@ -0,0 +1,65 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +async function readInto(reader, buffer) { + let offset = 0; + while (offset < buffer.byteLength) { + const {value: view, done} = + await reader.read(new Uint8Array(buffer, offset)); + buffer = view.buffer; + if (done) { + break; + } + offset += view.byteLength; + } + return buffer; +} + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + const bufferSize = 1024; + await port.open({baudRate: 9600, bufferSize}); + + const reader = port.readable.getReader({mode: 'byob'}); + assert_true(reader instanceof ReadableStreamBYOBReader); + + await fakePort.writable(); + const data = new Uint8Array(bufferSize); + for (let i = 0; i < data.byteLength; ++i) + data[i] = i & 0xff; + fakePort.write(data); + + let buffer = new ArrayBuffer(512); + buffer = await readInto(reader, buffer); + assert_equals(512, buffer.byteLength, 'original size retained'); + compareArrays(data.subarray(0, 512), new Uint8Array(buffer)); + + buffer = await readInto(reader, buffer); + assert_equals(512, buffer.byteLength, 'original size retained'); + compareArrays(data.subarray(512), new Uint8Array(buffer)); + reader.releaseLock(); + + await port.close(); +}, 'Can read specific subsets of the available data'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + await port.open({baudRate: 9600, bufferSize: 64}); + + const reader = port.readable.getReader({mode: 'byob'}); + assert_true(reader instanceof ReadableStreamBYOBReader); + + await fakePort.writable(); + const data = new Uint8Array(1024); + for (let i = 0; i < data.byteLength; ++i) + data[i] = i & 0xff; + fakePort.write(data); + + let buffer = new ArrayBuffer(1024); + buffer = await readInto(reader, buffer); + compareArrays(data, new Uint8Array(buffer)); + reader.releaseLock(); + + await port.close(); +}, 'Can read a large amount of data over multiple iterations'); diff --git a/testing/web-platform/tests/serial/serialPort_readable_cancel.https.any.js b/testing/web-platform/tests/serial/serialPort_readable_cancel.https.any.js new file mode 100644 index 0000000000..fd1b08f6e7 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_readable_cancel.https.any.js @@ -0,0 +1,69 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + await port.open({baudRate: 9600, bufferSize: 64}); + + const reader = port.readable.getReader(); + const readPromise = reader.read(); + await reader.cancel(); + const {value, done} = await readPromise; + assert_true(done); + assert_equals(undefined, value); + + await port.close(); +}, 'Can cancel while reading'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + await port.open({baudRate: 9600, bufferSize: 64}); + + const reader = port.readable.getReader(); + const closed = (async () => { + const {value, done} = await reader.read(); + assert_true(done); + assert_equals(undefined, value); + reader.releaseLock(); + await port.close(); + assert_equals(port.readable, null); + })(); + + await reader.cancel(); + await closed; +}, 'Can close while canceling'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + await port.open({baudRate: 9600, bufferSize: 64}); + + const reader = port.readable.getReader(); + + await fakePort.writable(); + const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); + await fakePort.write(data); + + await reader.cancel(); + await port.close(); +}, 'Cancel discards a small amount of data waiting to be read'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + // Select a buffer size smaller than the amount of data transferred. + await port.open({baudRate: 9600, bufferSize: 64}); + + const reader = port.readable.getReader(); + + await fakePort.writable(); + const data = new Uint8Array(1024); + // Writing will fail because there was more data to send than could fit in the + // buffer and none of it was read. + const writePromise = + promise_rejects_dom(t, 'InvalidStateError', fakePort.write(data)); + + await reader.cancel(); + await writePromise; + + await port.close(); +}, 'Cancel discards a large amount of data waiting to be read'); diff --git a/testing/web-platform/tests/serial/serialPort_readable_chain.https.any.js b/testing/web-platform/tests/serial/serialPort_readable_chain.https.any.js new file mode 100644 index 0000000000..552567cdfe --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_readable_chain.https.any.js @@ -0,0 +1,30 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + // Select a buffer size larger than the amount of data transferred. + await port.open({baudRate: 9600, bufferSize: 64}); + + const decoder = new TextDecoderStream(); + const streamClosed = port.readable.pipeTo(decoder.writable); + const readable = decoder.readable.pipeThrough(new TransformStream()) + .pipeThrough(new TransformStream()) + .pipeThrough(new TransformStream()) + .pipeThrough(new TransformStream()); + const reader = readable.getReader(); + + await fakePort.writable(); + fakePort.write(new TextEncoder().encode('Hello world!')); + + const {value, done} = await reader.read(); + assert_false(done); + assert_equals('Hello world!', value); + await reader.cancel('arbitrary reason'); + await streamClosed.catch(reason => { + assert_equals('arbitrary reason', reason); + }); + + await port.close(); +}, 'Stream closure is observable through a long chain of transforms'); diff --git a/testing/web-platform/tests/serial/serialPort_readable_closeLocked.https.any.js b/testing/web-platform/tests/serial/serialPort_readable_closeLocked.https.any.js new file mode 100644 index 0000000000..21196f758b --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_readable_closeLocked.https.any.js @@ -0,0 +1,17 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + await port.open({baudRate: 9600}); + assert_true(port.readable instanceof ReadableStream); + + const reader = port.readable.getReader(); + await promise_rejects_js(t, TypeError, port.close()); + + reader.releaseLock(); + await port.close(); + assert_equals(port.readable, null); +}, 'Port cannot be closed while readable is locked'); diff --git a/testing/web-platform/tests/serial/serialPort_readable_disconnect.https.any.js b/testing/web-platform/tests/serial/serialPort_readable_disconnect.https.any.js new file mode 100644 index 0000000000..3532745ae0 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_readable_disconnect.https.any.js @@ -0,0 +1,25 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + // Select a buffer size smaller than the amount of data transferred. + await port.open({baudRate: 9600, bufferSize: 64}); + + const reader = port.readable.getReader(); + + await fakePort.writable(); + const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); + fakePort.write(data); + fakePort.simulateDisconnectOnRead(); + + const {value, done} = await reader.read(); + assert_false(done); + compareArrays(data, value); + + await promise_rejects_dom(t, 'NetworkError', reader.read()); + assert_equals(port.readable, null); + + await port.close(); +}, 'Disconnect error closes readable and sets it to null'); diff --git a/testing/web-platform/tests/serial/serialPort_readable_largeRead.https.any.js b/testing/web-platform/tests/serial/serialPort_readable_largeRead.https.any.js new file mode 100644 index 0000000000..f8087514f7 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_readable_largeRead.https.any.js @@ -0,0 +1,23 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + // Select a buffer size smaller than the amount of data transferred. + await port.open({baudRate: 9600, bufferSize: 64}); + + const reader = port.readable.getReader(); + + await fakePort.writable(); + const data = new Uint8Array(1024); // Much larger than bufferSize above. + for (let i = 0; i < data.byteLength; ++i) + data[i] = i & 0xff; + fakePort.write(data); + + const value = await readWithLength(reader, data.byteLength); + compareArrays(data, value); + reader.releaseLock(); + + await port.close(); +}, 'Can read a large amount of data'); diff --git a/testing/web-platform/tests/serial/serialPort_readable_open.https.any.js b/testing/web-platform/tests/serial/serialPort_readable_open.https.any.js new file mode 100644 index 0000000000..1a12cc2da4 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_readable_open.https.any.js @@ -0,0 +1,21 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + assert_equals(port.readable, null); + + await port.open({baudRate: 9600}); + const readable = port.readable; + assert_true(readable instanceof ReadableStream); + + await port.close(); + assert_equals(port.readable, null); + + const reader = readable.getReader(); + const {value, done} = await reader.read(); + assert_true(done); + assert_equals(value, undefined); +}, 'SerialPort.readable is set by open() and closes on port close'); diff --git a/testing/web-platform/tests/serial/serialPort_readable_parityError.https.any.js b/testing/web-platform/tests/serial/serialPort_readable_parityError.https.any.js new file mode 100644 index 0000000000..7afd494dc5 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_readable_parityError.https.any.js @@ -0,0 +1,52 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +// ParityError is not (as of 2020/03/23) a valid DOMException, so cannot use +// promise_rejects_dom for it. +async function promise_rejects_with_parity_error(t, promise) { + return promise + .then(() => { + assert_false('Should have rejected'); + }) + .catch(e => { + assert_equals(e.constructor, DOMException); + assert_equals(e.name, 'ParityError'); + }); +} + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + // Select a buffer size smaller than the amount of data transferred. + await port.open({baudRate: 9600, bufferSize: 64}); + + let readable = port.readable; + let reader = readable.getReader(); + + await fakePort.writable(); + const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); + fakePort.write(data); + fakePort.simulateParityError(); + + let {value, done} = await reader.read(); + assert_false(done); + compareArrays(data, value); + + await promise_rejects_with_parity_error(t, reader.read()); + assert_not_equals(port.readable, readable); + + readable = port.readable; + assert_true(readable instanceof ReadableStream); + reader = port.readable.getReader(); + + await fakePort.writable(); + fakePort.write(data); + + ({value, done} = await reader.read()); + assert_false(done); + compareArrays(data, value); + reader.releaseLock(); + + await port.close(); + assert_equals(port.readable, null); +}, 'Parity error closes readable and replaces it with a new stream'); diff --git a/testing/web-platform/tests/serial/serialPort_readable_pipeThrough.https.any.js b/testing/web-platform/tests/serial/serialPort_readable_pipeThrough.https.any.js new file mode 100644 index 0000000000..13c6d48023 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_readable_pipeThrough.https.any.js @@ -0,0 +1,26 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + // Select a buffer size larger than the amount of data transferred. + await port.open({baudRate: 9600, bufferSize: 64}); + + const decoder = new TextDecoderStream(); + const streamClosed = port.readable.pipeTo(decoder.writable); + const reader = decoder.readable.getReader(); + + await fakePort.writable(); + fakePort.write(new TextEncoder().encode('Hello world!')); + + const {value, done} = await reader.read(); + assert_false(done); + assert_equals('Hello world!', value); + await reader.cancel(); + await streamClosed.catch(reason => { + assert_equals(undefined, reason); + }); + + await port.close(); +}, 'Can pipe readable through a transform stream.') diff --git a/testing/web-platform/tests/serial/serialPort_readable_smallRead.https.any.js b/testing/web-platform/tests/serial/serialPort_readable_smallRead.https.any.js new file mode 100644 index 0000000000..b926aa608f --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_readable_smallRead.https.any.js @@ -0,0 +1,23 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + // Select a buffer size larger than the amount of data transferred. + await port.open({baudRate: 9600, bufferSize: 64}); + + const reader = port.readable.getReader(); + assert_true(reader instanceof ReadableStreamDefaultReader); + + await fakePort.writable(); + const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); + fakePort.write(data); + + let {value, done} = await reader.read(); + assert_false(done); + compareArrays(data, value); + reader.releaseLock(); + + await port.close(); +}, 'Can read a small amount of data'); diff --git a/testing/web-platform/tests/serial/serialPort_setSignals.https.any.js b/testing/web-platform/tests/serial/serialPort_setSignals.https.any.js new file mode 100644 index 0000000000..27911ab7db --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_setSignals.https.any.js @@ -0,0 +1,59 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + await promise_rejects_dom(t, 'InvalidStateError', port.setSignals({})); +}, 'setSignals() rejects if the port is not open'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + await port.open({baudRate: 9600}); + + let expectedSignals = { + dataTerminalReady: true, + requestToSend: false, + break: false + }; + assert_object_equals(fakePort.outputSignals, expectedSignals, 'initial'); + + await promise_rejects_js(t, TypeError, port.setSignals()); + assert_object_equals(fakePort.outputSignals, expectedSignals, 'no-op'); + + await promise_rejects_js(t, TypeError, port.setSignals({})); + assert_object_equals(fakePort.outputSignals, expectedSignals, 'no-op'); + + await port.setSignals({dataTerminalReady: false}); + expectedSignals.dataTerminalReady = false; + assert_object_equals(fakePort.outputSignals, expectedSignals, 'clear DTR'); + + await port.setSignals({requestToSend: true}); + expectedSignals.requestToSend = true; + assert_object_equals(fakePort.outputSignals, expectedSignals, 'set RTS'); + + await port.setSignals({break: true}); + expectedSignals.break = true; + assert_object_equals(fakePort.outputSignals, expectedSignals, 'set BRK'); + + await port.setSignals( + {dataTerminalReady: true, requestToSend: false, break: false}); + expectedSignals.dataTerminalReady = true; + expectedSignals.requestToSend = false; + expectedSignals.break = false; + assert_object_equals(fakePort.outputSignals, expectedSignals, 'invert'); +}, 'setSignals() modifies the state of the port'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + await port.open({baudRate: 9600}); + + fakePort.simulateOutputSignalFailure(true); + await promise_rejects_dom(t, 'NetworkError', port.setSignals({break: true})); + + fakePort.simulateOutputSignalFailure(false); + await port.setSignals({break: true}); + assert_true(fakePort.outputSignals.break); + + await port.close(); +}, 'setSignals() rejects on failure'); diff --git a/testing/web-platform/tests/serial/serialPort_writable.https.any.js b/testing/web-platform/tests/serial/serialPort_writable.https.any.js new file mode 100644 index 0000000000..087c881500 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_writable.https.any.js @@ -0,0 +1,289 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + assert_equals(port.writable, null); + + await port.open({baudRate: 9600}); + const writable = port.writable; + assert_true(writable instanceof WritableStream); + + await port.close(); + assert_equals(port.writable, null); + + const writer = writable.getWriter(); + const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); + await promise_rejects_dom(t, 'InvalidStateError', writer.write(data)); +}, 'open() and close() set and unset SerialPort.writable'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + await port.open({baudRate: 9600}); + assert_true(port.writable instanceof WritableStream); + + const writer = port.writable.getWriter(); + await promise_rejects_js(t, TypeError, port.close()); + + writer.releaseLock(); + await port.close(); + assert_equals(port.writable, null); +}, 'Port cannot be closed while writable is locked'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + await port.open({baudRate: 9600}); + assert_true(port.writable instanceof WritableStream); + + const writer = port.writable.getWriter(); + const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); + let writePromise = writer.write(data); + writer.releaseLock(); + + await fakePort.readable(); + let {value, done} = await fakePort.read(); + await writePromise; + compareArrays(value, data); + + await port.close(); + assert_equals(port.writable, null); +}, 'Can write a small amount of data'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + // Select a buffer size smaller than the amount of data transferred. + await port.open({baudRate: 9600, bufferSize: 64}); + + const writer = port.writable.getWriter(); + const data = new Uint8Array(1024); // Much larger than bufferSize above. + for (let i = 0; i < data.byteLength; ++i) + data[i] = i & 0xff; + writer.write(data); + writer.releaseLock(); + + await fakePort.readable(); + const value = await fakePort.readWithLength(data.byteLength); + compareArrays(data, value); + + await port.close(); +}, 'Can write a large amount of data'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + await port.open({baudRate: 9600}); + + const writable = port.writable; + assert_true(writable instanceof WritableStream); + let writer = writable.getWriter(); + + await fakePort.readable(); + fakePort.simulateSystemErrorOnWrite(); + const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); + await promise_rejects_dom(t, 'UnknownError', writer.write(data)); + + assert_true(port.writable instanceof WritableStream); + assert_not_equals(port.writable, writable); + + writer = port.writable.getWriter(); + let writePromise = writer.write(data); + writer.releaseLock(); + await fakePort.readable(); + let {value, done} = await fakePort.read(); + await writePromise; + compareArrays(value, data); + + await port.close(); + assert_equals(port.writable, null); +}, 'System error closes writable and replaces it with a new stream'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + await port.open({baudRate: 9600}); + + assert_true(port.writable instanceof WritableStream); + const writer = port.writable.getWriter(); + + await fakePort.readable(); + fakePort.simulateDisconnectOnWrite(); + const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); + await promise_rejects_dom(t, 'NetworkError', writer.write(data)); + assert_equals(port.writable, null); + + await port.close(); +}, 'Disconnect error closes writable and sets it to null'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + await port.open({baudRate: 9600, bufferSize: 64}); + const originalWritable = port.writable; + assert_true(originalWritable instanceof WritableStream); + + let writer = originalWritable.getWriter(); + let data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); + // The buffer size is large enough to allow this write to complete without + // the data being read from the fake port. + await writer.write(data); + await writer.abort(); + + assert_true(port.writable instanceof WritableStream); + assert_true(port.writable !== originalWritable); + writer = port.writable.getWriter(); + data = new Uint8Array([9, 10, 11, 12, 13, 14, 15, 16]); + const writePromise = writer.write(data); + writer.releaseLock(); + + await fakePort.readable(); + const {value, done} = await fakePort.read(); + await writePromise; + compareArrays(value, data); + + await port.close(); + assert_equals(port.writable, null); +}, 'abort() discards the write buffer'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + // Select a buffer size smaller than the amount of data transferred. + await port.open({baudRate: 9600, bufferSize: 64}); + + const writer = port.writable.getWriter(); + // Wait for microtasks to execute in order to ensure that the WritableStream + // has been set up completely. + await Promise.resolve(); + + const data = new Uint8Array(1024); // Much larger than bufferSize above. + for (let i = 0; i < data.byteLength; ++i) + data[i] = i & 0xff; + const writePromise = + promise_rejects_exactly(t, 'Aborting.', writer.write(data)); + + await writer.abort('Aborting.'); + await writePromise; + await port.close(); + assert_equals(port.writable, null); +}, 'abort() does not wait for the write buffer to be cleared'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + // Select a buffer size smaller than the amount of data transferred. + await port.open({baudRate: 9600, bufferSize: 64}); + + const writer = port.writable.getWriter(); + const data = new Uint8Array(1024); // Much larger than bufferSize above. + for (let i = 0; i < data.byteLength; ++i) + data[i] = i & 0xff; + const closed = (async () => { + await promise_rejects_exactly(t, 'Aborting.', writer.write(data)); + writer.releaseLock(); + await port.close(); + assert_equals(port.writable, null); + })(); + + await writer.abort('Aborting.'); + await closed; +}, 'Can close while aborting'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + // Select a buffer size smaller than the amount of data transferred. + await port.open({baudRate: 9600, bufferSize: 64}); + + const writer = port.writable.getWriter(); + const data = new Uint8Array(1024); // Much larger than bufferSize above. + for (let i = 0; i < data.byteLength; ++i) + data[i] = i & 0xff; + writer.write(data); + + let readComplete = false; + let writePromise = writer.close().then(() => { + assert_true(readComplete); + }); + + await fakePort.readable(); + let readPromise = fakePort.readWithLength(data.byteLength).then(result => { + readComplete = true; + return result; + }); + const value = await readPromise; + compareArrays(data, value); + await writePromise; + + await port.close(); +}, 'close() waits for the write buffer to be cleared'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + // Select a buffer size smaller than the amount of data transferred. + await port.open({baudRate: 9600, bufferSize: 64}); + + const writer = port.writable.getWriter(); + // Wait for microtasks to execute in order to ensure that the WritableStream + // has been set up completely. + await Promise.resolve(); + + const data = new Uint8Array(1024); // Much larger than bufferSize above. + for (let i = 0; i < data.byteLength; ++i) + data[i] = i & 0xff; + const writePromise = + promise_rejects_exactly(t, 'Aborting.', writer.write(data)); + const closePromise = promise_rejects_exactly(t, 'Aborting.', writer.close()); + + await writer.abort('Aborting.'); + await writePromise; + await closePromise; + await port.close(); + assert_equals(port.writable, null); +}, 'Can abort while closing'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + await port.open({baudRate: 9600}); + assert_true(port.writable instanceof WritableStream); + + const encoder = new TextEncoderStream(); + const streamClosed = encoder.readable.pipeTo(port.writable); + const writer = encoder.writable.getWriter(); + const writePromise = writer.write('Hello world!'); + + await fakePort.readable(); + const {value, done} = await fakePort.read(); + await writePromise; + assert_equals('Hello world!', new TextDecoder().decode(value)); + await writer.close(); + await streamClosed; + + await port.close(); + assert_equals(port.writable, null); +}, 'Can pipe a stream to writable'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + + await port.open({baudRate: 9600}); + assert_true(port.writable instanceof WritableStream); + + const transform = new TransformStream(); + const readable = transform.readable.pipeThrough(new TextEncoderStream()) + .pipeThrough(new TransformStream()) + .pipeThrough(new TransformStream()) + .pipeThrough(new TransformStream()); + const streamClosed = readable.pipeTo(port.writable); + const writer = transform.writable.getWriter(); + const writePromise = writer.write('Hello world!'); + + await fakePort.readable(); + const {value, done} = await fakePort.read(); + await writePromise; + assert_equals('Hello world!', new TextDecoder().decode(value)); + await writer.close(); + await streamClosed; + + await port.close(); + assert_equals(port.writable, null); +}, 'Stream closure is observable through a long chain of transformers'); diff --git a/testing/web-platform/tests/serial/serialPort_writable_detachBuffer.https.any.js b/testing/web-platform/tests/serial/serialPort_writable_detachBuffer.https.any.js new file mode 100644 index 0000000000..828e877726 --- /dev/null +++ b/testing/web-platform/tests/serial/serialPort_writable_detachBuffer.https.any.js @@ -0,0 +1,48 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +function detachBuffer(buffer) { + const channel = new MessageChannel(); + channel.port1.postMessage('', [buffer]); +} + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + await port.open({baudRate: 9600, bufferSize: 64}); + + const writer = port.writable.getWriter(); + const data = new Uint8Array(64); + detachBuffer(data.buffer); + + // Writing a detached buffer is equivalent to writing an empty buffer so this + // should trivially succeed. + await writer.write(data); + writer.releaseLock(); + + await port.close(); +}, 'Writing a detached buffer is safe'); + +serial_test(async (t, fake) => { + const {port, fakePort} = await getFakeSerialPort(fake); + // Select a buffer size smaller than the amount of data transferred. + await port.open({baudRate: 9600, bufferSize: 64}); + + // Start writing a buffer much larger than bufferSize above so that it can't + // all be transfered in a single operation. + const writer = port.writable.getWriter(); + const data = new Uint8Array(1024); + const promise = writer.write(data); + writer.releaseLock(); + + // Read half of the written data and then detach the buffer. + await fakePort.readable(); + await fakePort.readWithLength(data.byteLength / 2); + detachBuffer(data.buffer); + + // When the buffer is detached its length becomes zero and so the write should + // succeed but it is undefined how much data was written before that happened. + await promise; + + await port.close(); +}, 'Detaching a buffer while writing is safe'); diff --git a/testing/web-platform/tests/serial/serial_getPorts.https.any.js b/testing/web-platform/tests/serial/serial_getPorts.https.any.js new file mode 100644 index 0000000000..03ab5f7e26 --- /dev/null +++ b/testing/web-platform/tests/serial/serial_getPorts.https.any.js @@ -0,0 +1,25 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + fake.addPort(); + fake.addPort(); + + let ports = await navigator.serial.getPorts(); + assert_equals(ports.length, 2); + assert_true(ports[0] instanceof SerialPort); + assert_true(ports[1] instanceof SerialPort); +}, 'getPorts() returns the set of configured fake ports'); + +serial_test(async (t, fake) => { + fake.addPort(); + + let portsFirst = await navigator.serial.getPorts(); + assert_equals(portsFirst.length, 1, 'first call returns one port'); + assert_true(portsFirst[0] instanceof SerialPort); + let portsSecond = await navigator.serial.getPorts(); + assert_equals(portsSecond.length, 1, 'second call returns one port'); + assert_true(portsSecond[0] instanceof SerialPort); + assert_true(portsFirst[0] === portsSecond[0]); +}, 'getPorts() returns the same port objects every time'); diff --git a/testing/web-platform/tests/serial/serial_onconnect.https.any.js b/testing/web-platform/tests/serial/serial_onconnect.https.any.js new file mode 100644 index 0000000000..e2ff9a0f0a --- /dev/null +++ b/testing/web-platform/tests/serial/serial_onconnect.https.any.js @@ -0,0 +1,29 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + const eventWatcher = + new EventWatcher(t, navigator.serial, ['connect', 'disconnect']); + + // Wait for getPorts() to resolve in order to ensure that the Mojo client + // interface has been configured. + let ports = await navigator.serial.getPorts(); + assert_equals(ports.length, 0); + + fake.addPort(); + const event1 = await eventWatcher.wait_for(['connect']); + assert_true(event1 instanceof Event); + assert_true(event1.target instanceof SerialPort); + + fake.addPort(); + const event2 = await eventWatcher.wait_for(['connect']); + assert_true(event2 instanceof Event); + assert_true(event2.target instanceof SerialPort); + assert_not_equals(event1.target, event2.target); + + ports = await navigator.serial.getPorts(); + assert_equals(ports.length, 2); + assert_in_array(event1.target, ports); + assert_in_array(event2.target, ports); +}, 'A "connect" event is fired when ports are added.'); diff --git a/testing/web-platform/tests/serial/serial_ondisconnect.https.any.js b/testing/web-platform/tests/serial/serial_ondisconnect.https.any.js new file mode 100644 index 0000000000..583a9ea52c --- /dev/null +++ b/testing/web-platform/tests/serial/serial_ondisconnect.https.any.js @@ -0,0 +1,37 @@ +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test(async (t, fake) => { + const eventWatcher = + new EventWatcher(t, navigator.serial, ['connect', 'disconnect']); + + // Wait for getPorts() to resolve in order to ensure that the Mojo client + // interface has been configured. + let ports = await navigator.serial.getPorts(); + assert_equals(ports.length, 0); + + // Add ports one at a time so that we can map tokens to ports. + const token1 = fake.addPort(); + const port1 = (await eventWatcher.wait_for(['connect'])).target; + + const token2 = fake.addPort(); + const port2 = (await eventWatcher.wait_for(['connect'])).target; + + fake.removePort(token2); + const event1 = await eventWatcher.wait_for(['disconnect']); + assert_true(event1 instanceof Event); + assert_equals(event1.target, port2); + + ports = await navigator.serial.getPorts(); + assert_equals(ports.length, 1); + assert_equals(ports[0], port1); + + fake.removePort(token1); + const event2 = await eventWatcher.wait_for(['disconnect']); + assert_true(event2 instanceof Event); + assert_equals(event2.target, port1); + + ports = await navigator.serial.getPorts(); + assert_equals(ports.length, 0); +}, 'A "disconnect" event is fired when ports are added.'); diff --git a/testing/web-platform/tests/serial/serial_requestPort.https.window.js b/testing/web-platform/tests/serial/serial_requestPort.https.window.js new file mode 100644 index 0000000000..4edcfa6a46 --- /dev/null +++ b/testing/web-platform/tests/serial/serial_requestPort.https.window.js @@ -0,0 +1,66 @@ +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/resources/test-only-api.js +// META: script=/serial/resources/common.js +// META: script=resources/automation.js + +serial_test((t, fake) => { + return promise_rejects_dom( + t, 'SecurityError', navigator.serial.requestPort()); +}, 'requestPort() rejects without a user gesture'); + +serial_test(async (t, fake) => { + await trustedClick(); + return promise_rejects_dom( + t, 'NotFoundError', navigator.serial.requestPort()); +}, 'requestPort() rejects if no port has been selected'); + +serial_test(async (t, fake) => { + let token = fake.addPort(); + fake.setSelectedPort(token); + + await trustedClick(); + let port = await navigator.serial.requestPort(); + assert_true(port instanceof SerialPort); +}, 'requestPort() returns the selected port'); + +serial_test(async (t, fake) => { + let token = fake.addPort(); + fake.setSelectedPort(token); + + await trustedClick(); + let firstPort = await navigator.serial.requestPort(); + assert_true(firstPort instanceof SerialPort); + let secondPort = await navigator.serial.requestPort(); + assert_true(secondPort instanceof SerialPort); + assert_true(firstPort === secondPort); +}, 'requestPort() returns the same port object every time'); + +serial_test(async (t, fake) => { + let token = fake.addPort(); + fake.setSelectedPort(token); + + await trustedClick(); + let port = await navigator.serial.requestPort({filters: []}); + assert_true(port instanceof SerialPort); +}, 'An empty list of filters is valid'); + +serial_test(async (t, fake) => { + let token = fake.addPort(); + fake.setSelectedPort(token); + + await trustedClick(); + return promise_rejects_js(t, TypeError, navigator.serial.requestPort({ + filters: [{}], + })); +}, 'An empty filter is not valid'); + +serial_test(async (t, fake) => { + let token = fake.addPort(); + fake.setSelectedPort(token); + + await trustedClick(); + return promise_rejects_js(t, TypeError, navigator.serial.requestPort({ + filters: [{usbProductId: 0x0001}], + })); +}, 'requestPort() requires a USB vendor ID if a product ID specified'); -- cgit v1.2.3