diff options
Diffstat (limited to 'testing/web-platform/tests/generic-sensor/generic-sensor-tests.js')
-rw-r--r-- | testing/web-platform/tests/generic-sensor/generic-sensor-tests.js | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/testing/web-platform/tests/generic-sensor/generic-sensor-tests.js b/testing/web-platform/tests/generic-sensor/generic-sensor-tests.js new file mode 100644 index 0000000000..2f910806df --- /dev/null +++ b/testing/web-platform/tests/generic-sensor/generic-sensor-tests.js @@ -0,0 +1,559 @@ +'use strict'; + +// Run a set of tests for a given |sensorName|. +// |readingData| is an object with 3 keys, all of which are arrays of arrays: +// 1. "readings". Each value corresponds to one raw reading that will be +// processed by a sensor. +// 2. "expectedReadings". Each value corresponds to the processed value a +// sensor will make available to users (i.e. a capped or rounded value). +// Its length must match |readings|'. +// 3. "expectedRemappedReadings" (optional). Similar to |expectedReadings|, but +// used only by spatial sensors, whose reference frame can change the values +// returned by a sensor. +// Its length should match |readings|'. +// |verificationFunction| is called to verify that a given reading matches a +// value in |expectedReadings|. +// |featurePolicies| represents |sensorName|'s associated sensor feature name. + +function runGenericSensorTests(sensorName, + readingData, + verificationFunction, + featurePolicies) { + const sensorType = self[sensorName]; + + function validateReadingFormat(data) { + return Array.isArray(data) && data.every(element => Array.isArray(element)); + } + + const { readings, expectedReadings, expectedRemappedReadings } = readingData; + if (!validateReadingFormat(readings)) { + throw new TypeError('readingData.readings must be an array of arrays.'); + } + if (!validateReadingFormat(expectedReadings)) { + throw new TypeError('readingData.expectedReadings must be an array of ' + + 'arrays.'); + } + if (readings.length < expectedReadings.length) { + throw new TypeError('readingData.readings\' length must be bigger than ' + + 'or equal to readingData.expectedReadings\' length.'); + } + if (expectedRemappedReadings && + !validateReadingFormat(expectedRemappedReadings)) { + throw new TypeError('readingData.expectedRemappedReadings must be an ' + + 'array of arrays.'); + } + if (expectedRemappedReadings && + expectedReadings.length != expectedRemappedReadings.length) { + throw new TypeError('readingData.expectedReadings and ' + + 'readingData.expectedRemappedReadings must have the same ' + + 'length.'); + } + + sensor_test(async (t, sensorProvider) => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + sensorProvider.setGetSensorShouldFail(sensorName, true); + const sensor = new sensorType; + const sensorWatcher = new EventWatcher(t, sensor, ["reading", "error"]); + sensor.start(); + + const event = await sensorWatcher.wait_for("error"); + + assert_false(sensor.activated); + assert_equals(event.error.name, 'NotReadableError'); + }, `${sensorName}: Test that onerror is sent when sensor is not supported.`); + + sensor_test(async (t, sensorProvider) => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + sensorProvider.setPermissionsDenied(sensorName, true); + const sensor = new sensorType; + const sensorWatcher = new EventWatcher(t, sensor, ["reading", "error"]); + sensor.start(); + + const event = await sensorWatcher.wait_for("error"); + + assert_false(sensor.activated); + assert_equals(event.error.name, 'NotAllowedError'); + }, `${sensorName}: Test that onerror is sent when permissions are not\ + granted.`); + + sensor_test(async (t, sensorProvider) => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + const sensor = new sensorType({frequency: 560}); + const sensorWatcher = new EventWatcher(t, sensor, ["reading", "error"]); + sensor.start(); + + const mockSensor = await sensorProvider.getCreatedSensor(sensorName); + mockSensor.setStartShouldFail(true); + + const event = await sensorWatcher.wait_for("error"); + + assert_false(sensor.activated); + assert_equals(event.error.name, 'NotReadableError'); + }, `${sensorName}: Test that onerror is send when start() call has failed.`); + + sensor_test(async (t, sensorProvider) => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + const sensor = new sensorType({frequency: 560}); + const sensorWatcher = new EventWatcher(t, sensor, ["activate", "error"]); + sensor.start(); + + const mockSensor = await sensorProvider.getCreatedSensor(sensorName); + + await sensorWatcher.wait_for("activate"); + + assert_less_than_equal(mockSensor.getSamplingFrequency(), 60); + sensor.stop(); + assert_false(sensor.activated); + }, `${sensorName}: Test that frequency is capped to allowed maximum.`); + + sensor_test(async (t, sensorProvider) => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + const maxSupportedFrequency = 5; + sensorProvider.setMaximumSupportedFrequency(maxSupportedFrequency); + const sensor = new sensorType({frequency: 50}); + const sensorWatcher = new EventWatcher(t, sensor, ["activate", "error"]); + sensor.start(); + + const mockSensor = await sensorProvider.getCreatedSensor(sensorName); + + await sensorWatcher.wait_for("activate"); + + assert_equals(mockSensor.getSamplingFrequency(), maxSupportedFrequency); + sensor.stop(); + assert_false(sensor.activated); + }, `${sensorName}: Test that frequency is capped to the maximum supported\ + frequency.`); + + sensor_test(async (t, sensorProvider) => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + const minSupportedFrequency = 2; + sensorProvider.setMinimumSupportedFrequency(minSupportedFrequency); + const sensor = new sensorType({frequency: -1}); + const sensorWatcher = new EventWatcher(t, sensor, ["activate", "error"]); + sensor.start(); + + const mockSensor = await sensorProvider.getCreatedSensor(sensorName); + + await sensorWatcher.wait_for("activate"); + + assert_equals(mockSensor.getSamplingFrequency(), minSupportedFrequency); + sensor.stop(); + assert_false(sensor.activated); + }, `${sensorName}: Test that frequency is limited to the minimum supported\ + frequency.`); + + promise_test(async t => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + const iframe = document.createElement('iframe'); + iframe.allow = featurePolicies.join(' \'none\'; ') + ' \'none\';'; + iframe.srcdoc = '<script>' + + ' window.onmessage = message => {' + + ' if (message.data === "LOADED") {' + + ' try {' + + ' new ' + sensorName + '();' + + ' parent.postMessage("FAIL", "*");' + + ' } catch (e) {' + + ' parent.postMessage("PASS", "*");' + + ' }' + + ' }' + + ' };' + + '<\/script>'; + const iframeWatcher = new EventWatcher(t, iframe, "load"); + document.body.appendChild(iframe); + await iframeWatcher.wait_for("load"); + iframe.contentWindow.postMessage('LOADED', '*'); + + const windowWatcher = new EventWatcher(t, window, "message"); + const message = await windowWatcher.wait_for("message"); + assert_equals(message.data, 'PASS'); + }, `${sensorName}: Test that sensor cannot be constructed within iframe\ + disallowed to use feature policy.`); + + promise_test(async t => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + const iframe = document.createElement('iframe'); + iframe.allow = featurePolicies.join(';') + ';'; + iframe.srcdoc = '<script>' + + ' window.onmessage = message => {' + + ' if (message.data === "LOADED") {' + + ' try {' + + ' new ' + sensorName + '();' + + ' parent.postMessage("PASS", "*");' + + ' } catch (e) {' + + ' parent.postMessage("FAIL", "*");' + + ' }' + + ' }' + + ' };' + + '<\/script>'; + const iframeWatcher = new EventWatcher(t, iframe, "load"); + document.body.appendChild(iframe); + await iframeWatcher.wait_for("load"); + iframe.contentWindow.postMessage('LOADED', '*'); + + const windowWatcher = new EventWatcher(t, window, "message"); + const message = await windowWatcher.wait_for("message"); + assert_equals(message.data, 'PASS'); + }, `${sensorName}: Test that sensor can be constructed within an iframe\ + allowed to use feature policy.`); + + sensor_test(async (t, sensorProvider) => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + const sensor = new sensorType(); + const sensorWatcher = new EventWatcher(t, sensor, ["reading", "error"]); + sensor.start(); + assert_false(sensor.hasReading); + + const mockSensor = await sensorProvider.getCreatedSensor(sensorName); + mockSensor.setSensorReading(readings); + + await sensorWatcher.wait_for("reading"); + const expected = new RingBuffer(expectedReadings).next().value; + assert_true(verificationFunction(expected, sensor)); + assert_true(sensor.hasReading); + + sensor.stop(); + assert_true(verificationFunction(expected, sensor, /*isNull=*/true)); + assert_false(sensor.hasReading); + }, `${sensorName}: Test that 'onreading' is called and sensor reading is\ + valid.`); + + sensor_test(async (t, sensorProvider) => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + const sensor1 = new sensorType(); + const sensorWatcher1 = new EventWatcher(t, sensor1, ["reading", "error"]); + sensor1.start(); + + const sensor2 = new sensorType(); + const sensorWatcher2 = new EventWatcher(t, sensor2, ["reading", "error"]); + sensor2.start(); + + const mockSensor = await sensorProvider.getCreatedSensor(sensorName); + mockSensor.setSensorReading(readings); + + await Promise.all([sensorWatcher1.wait_for("reading"), + sensorWatcher2.wait_for("reading")]); + const expected = new RingBuffer(expectedReadings).next().value; + // Reading values are correct for both sensors. + assert_true(verificationFunction(expected, sensor1)); + assert_true(verificationFunction(expected, sensor2)); + + // After first sensor stops its reading values are null, + // reading values for the second sensor sensor remain. + sensor1.stop(); + assert_true(verificationFunction(expected, sensor1, /*isNull=*/true)); + assert_true(verificationFunction(expected, sensor2)); + + sensor2.stop(); + assert_true(verificationFunction(expected, sensor2, /*isNull=*/true)); + }, `${sensorName}: sensor reading is correct.`); + + // Tests that readings maps to expectedReadings correctly. Due to threshold + // check and rounding some values might be discarded or changed. + sensor_test(async (t, sensorProvider) => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + const sensor = new sensorType(); + const sensorWatcher = new EventWatcher(t, sensor, ["reading", "error"]); + sensor.start(); + + const mockSensor = await sensorProvider.getCreatedSensor(sensorName); + await mockSensor.setSensorReading(readings); + + for (let expectedReading of expectedReadings) { + await sensorWatcher.wait_for("reading"); + assert_true(sensor.hasReading, "hasReading"); + assert_true(verificationFunction(expectedReading, sensor), + "verification"); + } + + sensor.stop(); + }, `${sensorName}: Test that readings are all mapped to expectedReadings\ + correctly.`); + + sensor_test(async (t, sensorProvider) => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + const sensor = new sensorType(); + const sensorWatcher = new EventWatcher(t, sensor, ["reading", "error"]); + sensor.start(); + + const mockSensor = await sensorProvider.getCreatedSensor(sensorName); + mockSensor.setSensorReading(readings); + + await sensorWatcher.wait_for("reading"); + const cachedTimeStamp1 = sensor.timestamp; + + await sensorWatcher.wait_for("reading"); + const cachedTimeStamp2 = sensor.timestamp; + + assert_greater_than(cachedTimeStamp2, cachedTimeStamp1); + sensor.stop(); + }, `${sensorName}: sensor timestamp is updated when time passes.`); + + sensor_test(async t => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + const sensor = new sensorType(); + const sensorWatcher = new EventWatcher(t, sensor, ["activate", "error"]); + assert_false(sensor.activated); + sensor.start(); + assert_false(sensor.activated); + + await sensorWatcher.wait_for("activate"); + assert_true(sensor.activated); + + sensor.stop(); + assert_false(sensor.activated); + }, `${sensorName}: Test that sensor can be successfully created and its\ + states are correct.`); + + sensor_test(async t => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + const sensor = new sensorType(); + const sensorWatcher = new EventWatcher(t, sensor, ["activate", "error"]); + sensor.start(); + sensor.start(); + + await sensorWatcher.wait_for("activate"); + assert_true(sensor.activated); + sensor.stop(); + }, `${sensorName}: no exception is thrown when calling start() on already\ + started sensor.`); + + sensor_test(async t => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + const sensor = new sensorType(); + const sensorWatcher = new EventWatcher(t, sensor, ["activate", "error"]); + sensor.start(); + + await sensorWatcher.wait_for("activate"); + sensor.stop(); + sensor.stop(); + assert_false(sensor.activated); + }, `${sensorName}: no exception is thrown when calling stop() on already\ + stopped sensor.`); + + sensor_test(async (t, sensorProvider) => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + const sensor = new sensorType(); + const sensorWatcher = new EventWatcher(t, sensor, ["reading", "error"]); + sensor.start(); + + const mockSensor = await sensorProvider.getCreatedSensor(sensorName); + mockSensor.setSensorReading(readings); + + const expectedBuffer = new RingBuffer(expectedReadings); + await sensorWatcher.wait_for("reading"); + const expected1 = expectedBuffer.next().value; + assert_true(sensor.hasReading); + assert_true(verificationFunction(expected1, sensor)); + const timestamp = sensor.timestamp; + sensor.stop(); + assert_false(sensor.hasReading); + + sensor.start(); + await sensorWatcher.wait_for("reading"); + assert_true(sensor.hasReading); + // |readingData| may have a single reading/expectation value, and this + // is the second reading we are getting. For that case, make sure we + // also wrap around as if we had the same RingBuffer used in + // generic_sensor_mocks.js. + const expected2 = expectedBuffer.next().value; + assert_true(verificationFunction(expected2, sensor)); + // Make sure that 'timestamp' is already initialized. + assert_greater_than(timestamp, 0); + // Check that the reading is updated. + assert_greater_than(sensor.timestamp, timestamp); + sensor.stop(); + }, `${sensorName}: Test that fresh reading is fetched on start().`); + +// TBD file a WPT issue: visibilityChangeWatcher times out. +// sensor_test(async (t, sensorProvider) => { +// assert_implements(sensorName in self, `${sensorName} is not supported.`); +// const sensor = new sensorType(); +// const sensorWatcher = new EventWatcher(t, sensor, ["reading", "error"]); +// const visibilityChangeWatcher = new EventWatcher(t, document, +// "visibilitychange"); +// sensor.start(); + +// const mockSensor = await sensorProvider.getCreatedSensor(sensorName); +// mockSensor.setSensorReading(readings); + +// await sensorWatcher.wait_for("reading"); +// const expected = new RingBuffer(expectedReadings).next().value; +// assert_true(verificationFunction(expected, sensor)); +// const cachedTimestamp1 = sensor.timestamp; + +// const win = window.open('', '_blank'); +// await visibilityChangeWatcher.wait_for("visibilitychange"); +// const cachedTimestamp2 = sensor.timestamp; + +// win.close(); +// sensor.stop(); +// assert_equals(cachedTimestamp1, cachedTimestamp2); +// }, `${sensorName}: sensor readings can not be fired on the background tab.`); + + sensor_test(async (t, sensorProvider) => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + + const fastSensor = new sensorType({ frequency: 60 }); + t.add_cleanup(() => { fastSensor.stop(); }); + let eventWatcher = new EventWatcher(t, fastSensor, "activate"); + fastSensor.start(); + + // Wait for |fastSensor| to be activated so that the call to + // getSamplingFrequency() below works. + await eventWatcher.wait_for("activate"); + + const mockSensor = await sensorProvider.getCreatedSensor(sensorName); + mockSensor.setSensorReading(readings); + + // We need |fastSensorFrequency| because 60Hz might be higher than a sensor + // type's maximum allowed frequency. + const fastSensorFrequency = mockSensor.getSamplingFrequency(); + const slowSensorFrequency = fastSensorFrequency * 0.25; + + const slowSensor = new sensorType({ frequency: slowSensorFrequency }); + t.add_cleanup(() => { slowSensor.stop(); }); + eventWatcher = new EventWatcher(t, slowSensor, "activate"); + slowSensor.start(); + + // Wait for |slowSensor| to be activated before we check if the mock + // platform sensor's sampling frequency has changed. + await eventWatcher.wait_for("activate"); + assert_equals(mockSensor.getSamplingFrequency(), fastSensorFrequency); + + // Now stop |fastSensor| and verify that the sampling frequency has dropped + // to the one |slowSensor| had requested. + fastSensor.stop(); + return t.step_wait(() => { + return mockSensor.getSamplingFrequency() === slowSensorFrequency; + }, "Sampling frequency has dropped to slowSensor's requested frequency"); + }, `${sensorName}: frequency hint works.`); + + sensor_test(async (t, sensorProvider) => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + + const sensor1 = new sensorType(); + const sensor2 = new sensorType(); + + return new Promise((resolve, reject) => { + sensor1.addEventListener('reading', () => { + sensor2.addEventListener('activate', () => { + try { + assert_true(sensor1.activated); + assert_true(sensor1.hasReading); + assert_false(verificationFunction(null, sensor1, /*isNull=*/true)); + assert_not_equals(sensor1.timestamp, null); + + assert_true(sensor2.activated); + assert_false(verificationFunction(null, sensor2, /*isNull=*/true)); + assert_not_equals(sensor2.timestamp, null); + } catch (e) { + reject(e); + } + }, { once: true }); + sensor2.addEventListener('reading', () => { + try { + assert_true(sensor2.activated); + assert_true(sensor2.hasReading); + assert_sensor_equals(sensor1, sensor2); + resolve(); + } catch (e) { + reject(e); + } + }, { once: true }); + sensor2.start(); + }, { once: true }); + sensor1.start(); + }); + }, `${sensorName}: Readings delivered by shared platform sensor are\ + immediately accessible to all sensors.`); + +// Re-enable after https://github.com/w3c/sensors/issues/361 is fixed. +// test(() => { +// assert_throws_dom("NotSupportedError", +// () => { new sensorType({invalid: 1}) }); +// assert_throws_dom("NotSupportedError", +// () => { new sensorType({frequency: 60, invalid: 1}) }); +// if (!expectedRemappedReadings) { +// assert_throws_dom("NotSupportedError", +// () => { new sensorType({referenceFrame: "screen"}) }); +// } +// }, `${sensorName}: throw 'NotSupportedError' for an unsupported sensor\ +// option.`); + + test(() => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + const invalidFreqs = [ + "invalid", + NaN, + Infinity, + -Infinity, + {} + ]; + invalidFreqs.map(freq => { + assert_throws_js(TypeError, + () => { new sensorType({frequency: freq}) }, + `when freq is ${freq}`); + }); + }, `${sensorName}: throw 'TypeError' if frequency is invalid.`); + + if (!expectedRemappedReadings) { + // The sensorType does not represent a spatial sensor. + return; + } + + sensor_test(async (t, sensorProvider) => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + const sensor1 = new sensorType({frequency: 60}); + const sensor2 = new sensorType({frequency: 60, referenceFrame: "screen"}); + const sensorWatcher1 = new EventWatcher(t, sensor1, ["reading", "error"]); + const sensorWatcher2 = new EventWatcher(t, sensor1, ["reading", "error"]); + + sensor1.start(); + sensor2.start(); + + const mockSensor = await sensorProvider.getCreatedSensor(sensorName); + mockSensor.setSensorReading(readings); + + await Promise.all([sensorWatcher1.wait_for("reading"), + sensorWatcher2.wait_for("reading")]); + + const expected = new RingBuffer(expectedReadings).next().value; + const expectedRemapped = + new RingBuffer(expectedRemappedReadings).next().value; + assert_true(verificationFunction(expected, sensor1)); + assert_true(verificationFunction(expectedRemapped, sensor2)); + + sensor1.stop(); + assert_true(verificationFunction(expected, sensor1, /*isNull=*/true)); + assert_true(verificationFunction(expectedRemapped, sensor2)); + + sensor2.stop(); + assert_true(verificationFunction(expectedRemapped, sensor2, + /*isNull=*/true)); + }, `${sensorName}: sensor reading is correct when options.referenceFrame\ + is 'screen'.`); + + test(() => { + assert_implements(sensorName in self, `${sensorName} is not supported.`); + const invalidRefFrames = [ + "invalid", + null, + 123, + {}, + "", + true + ]; + invalidRefFrames.map(refFrame => { + assert_throws_js(TypeError, + () => { new sensorType({referenceFrame: refFrame}) }, + `when refFrame is ${refFrame}`); + }); + }, `${sensorName}: throw 'TypeError' if referenceFrame is not one of\ + enumeration values.`); +} + +function runGenericSensorInsecureContext(sensorName) { + test(() => { + assert_false(sensorName in window, `${sensorName} must not be exposed`); + }, `${sensorName} is not exposed in an insecure context.`); +} |