| 'use strict'; |
| |
| // Run a set of tests for a given |sensorType|. |updateReading| is |
| // a called by the test to provide the mock values for sensor. |verifyReading| |
| // is called so that the value read in JavaScript are the values expected (the ones |
| // sent by |updateReading|). |
| function runGenericSensorTests(sensorType, |
| updateReading, |
| verifyReading, |
| verifyRemappedReading, |
| featurePolicies) { |
| sensor_test(sensor => { |
| sensor.mockSensorProvider.setGetSensorShouldFail(true); |
| let sensorObject = new sensorType; |
| sensorObject.start(); |
| return new Promise((resolve, reject) => { |
| let wrapper = new CallbackWrapper(event => { |
| assert_false(sensorObject.activated); |
| assert_equals(event.error.name, 'NotReadableError'); |
| sensorObject.onerror = null; |
| resolve(); |
| }, reject); |
| |
| sensorObject.onerror = wrapper.callback; |
| }); |
| }, `${sensorType.name}: Test that onerror is sent when sensor is not supported.`); |
| |
| sensor_test(sensor => { |
| sensor.mockSensorProvider.setPermissionsDenied(true); |
| let sensorObject = new sensorType; |
| sensorObject.start(); |
| return new Promise((resolve, reject) => { |
| let wrapper = new CallbackWrapper(event => { |
| assert_false(sensorObject.activated); |
| assert_equals(event.error.name, 'NotAllowedError'); |
| sensorObject.onerror = null; |
| resolve(); |
| }, reject); |
| |
| sensorObject.onerror = wrapper.callback; |
| }); |
| }, `${sensorType.name}: Test that onerror is sent when permissions are not granted.`); |
| |
| sensor_test(async sensor => { |
| let sensorObject = new sensorType({frequency: 560}); |
| sensorObject.start(); |
| |
| let mockSensor = await sensor.mockSensorProvider.getCreatedSensor(); |
| mockSensor.setStartShouldFail(true); |
| await mockSensor.addConfigurationCalled(); |
| await new Promise((resolve, reject) => { |
| let wrapper = new CallbackWrapper(event => { |
| assert_false(sensorObject.activated); |
| assert_equals(event.error.name, 'NotReadableError'); |
| sensorObject.onerror = null; |
| resolve(); |
| }, reject); |
| |
| sensorObject.onerror = wrapper.callback; |
| }); |
| }, `${sensorType.name}: Test that onerror is send when start() call has failed.`); |
| |
| sensor_test(async sensor => { |
| let sensorObject = new sensorType(); |
| sensorObject.start(); |
| |
| let mockSensor = await sensor.mockSensorProvider.getCreatedSensor(); |
| mockSensor.setStartShouldFail(true); |
| await mockSensor.addConfigurationCalled(); |
| return mockSensor.removeConfigurationCalled(); |
| }, `${sensorType.name}: Test that no pending configuration left after start() failure.`); |
| |
| sensor_test(async sensor => { |
| let sensorObject = new sensorType({frequency: 560}); |
| sensorObject.start(); |
| |
| let mockSensor = await sensor.mockSensorProvider.getCreatedSensor(); |
| await mockSensor.addConfigurationCalled(); |
| await new Promise((resolve, reject) => { |
| let wrapper = new CallbackWrapper(() => { |
| assert_less_than_equal(mockSensor.getSamplingFrequency(), 60); |
| sensorObject.stop(); |
| assert_false(sensorObject.activated); |
| resolve(mockSensor); |
| }, reject); |
| sensorObject.onactivate = wrapper.callback; |
| sensorObject.onerror = reject; |
| }); |
| return mockSensor.removeConfigurationCalled(); |
| }, `${sensorType.name}: Test that frequency is capped to allowed maximum.`); |
| |
| sensor_test(async sensor => { |
| let sensorObject = new sensorType(); |
| sensorObject.start(); |
| let mockSensor = await sensor.mockSensorProvider.getCreatedSensor(); |
| await mockSensor.addConfigurationCalled(); |
| await new Promise((resolve, reject) => { |
| sensorObject.onactivate = () => { |
| // Now sensor proxy is initialized. |
| let anotherSensor = new sensorType({frequency: 21}); |
| anotherSensor.start(); |
| anotherSensor.stop(); |
| resolve(mockSensor); |
| } |
| }); |
| await mockSensor.removeConfigurationCalled(); |
| sensorObject.stop(); |
| return mockSensor.removeConfigurationCalled(); |
| }, `${sensorType.name}: Test that configuration is removed for a stopped sensor.`); |
| |
| sensor_test(async sensor => { |
| const maxSupportedFrequency = 5; |
| sensor.mockSensorProvider.setMaximumSupportedFrequency(maxSupportedFrequency); |
| let sensorObject = new sensorType({frequency: 50}); |
| sensorObject.start(); |
| |
| let mockSensor = await sensor.mockSensorProvider.getCreatedSensor(); |
| await mockSensor.addConfigurationCalled(); |
| await new Promise((resolve, reject) => { |
| let wrapper = new CallbackWrapper(() => { |
| assert_equals(mockSensor.getSamplingFrequency(), maxSupportedFrequency); |
| sensorObject.stop(); |
| assert_false(sensorObject.activated); |
| resolve(mockSensor); |
| }, reject); |
| sensorObject.onactivate = wrapper.callback; |
| sensorObject.onerror = reject; |
| }); |
| return mockSensor.removeConfigurationCalled(); |
| }, `${sensorType.name}: Test that frequency is capped to the maximum supported from frequency.`); |
| |
| sensor_test(async sensor => { |
| const minSupportedFrequency = 2; |
| sensor.mockSensorProvider.setMinimumSupportedFrequency(minSupportedFrequency); |
| let sensorObject = new sensorType({frequency: -1}); |
| sensorObject.start(); |
| |
| let mockSensor = await sensor.mockSensorProvider.getCreatedSensor(); |
| await mockSensor.addConfigurationCalled(); |
| await new Promise((resolve, reject) => { |
| let wrapper = new CallbackWrapper(() => { |
| assert_equals(mockSensor.getSamplingFrequency(), minSupportedFrequency); |
| sensorObject.stop(); |
| assert_false(sensorObject.activated); |
| resolve(mockSensor); |
| }, reject); |
| sensorObject.onactivate = wrapper.callback; |
| sensorObject.onerror = reject; |
| }); |
| return mockSensor.removeConfigurationCalled(); |
| }, `${sensorType.name}: Test that frequency is limited to the minimum supported from frequency.`); |
| |
| sensor_test(async sensor => { |
| let sensorObject = new sensorType({frequency: 60}); |
| assert_false(sensorObject.activated); |
| sensorObject.start(); |
| assert_false(sensorObject.activated); |
| |
| let mockSensor = await sensor.mockSensorProvider.getCreatedSensor(); |
| await new Promise((resolve, reject) => { |
| let wrapper = new CallbackWrapper(() => { |
| assert_true(sensorObject.activated); |
| sensorObject.stop(); |
| assert_false(sensorObject.activated); |
| resolve(mockSensor); |
| }, reject); |
| sensorObject.onactivate = wrapper.callback; |
| sensorObject.onerror = reject; |
| }); |
| return mockSensor.removeConfigurationCalled(); |
| }, `${sensorType.name}: Test that sensor can be successfully created and its states are correct.`); |
| |
| sensor_test(async sensor => { |
| let sensorObject = new sensorType(); |
| sensorObject.start(); |
| |
| let mockSensor = await sensor.mockSensorProvider.getCreatedSensor(); |
| await new Promise((resolve, reject) => { |
| let wrapper = new CallbackWrapper(() => { |
| assert_true(sensorObject.activated); |
| sensorObject.stop(); |
| assert_false(sensorObject.activated); |
| resolve(mockSensor); |
| }, reject); |
| |
| sensorObject.onactivate = wrapper.callback; |
| sensorObject.onerror = reject; |
| }); |
| return mockSensor.removeConfigurationCalled(); |
| }, `${sensorType.name}: Test that sensor can be constructed with default configuration.`); |
| |
| sensor_test(async sensor => { |
| let sensorObject = new sensorType({frequency: 60}); |
| sensorObject.start(); |
| |
| let mockSensor = await sensor.mockSensorProvider.getCreatedSensor(); |
| await mockSensor.addConfigurationCalled(); |
| await new Promise((resolve, reject) => { |
| let wrapper = new CallbackWrapper(() => { |
| assert_true(sensorObject.activated); |
| sensorObject.stop(); |
| assert_false(sensorObject.activated); |
| resolve(mockSensor); |
| }, reject); |
| sensorObject.onactivate = wrapper.callback; |
| sensorObject.onerror = reject; |
| }); |
| return mockSensor.removeConfigurationCalled(); |
| }, `${sensorType.name}: Test that addConfiguration and removeConfiguration is called.`); |
| |
| async function checkOnReadingIsCalledAndReadingIsValid(sensor) { |
| let sensorObject = new sensorType({frequency: 60}); |
| sensorObject.start(); |
| assert_false(sensorObject.hasReading); |
| |
| let mockSensor = await sensor.mockSensorProvider.getCreatedSensor(); |
| await mockSensor.setUpdateSensorReadingFunction(updateReading); |
| await new Promise((resolve, reject) => { |
| let wrapper = new CallbackWrapper(() => { |
| assert_true(verifyReading(sensorObject)); |
| assert_true(sensorObject.hasReading); |
| sensorObject.stop(); |
| assert_true(verifyReading(sensorObject, true /*should be null*/)); |
| assert_false(sensorObject.hasReading); |
| resolve(mockSensor); |
| }, reject); |
| |
| sensorObject.onreading = wrapper.callback; |
| sensorObject.onerror = reject; |
| }); |
| return mockSensor.removeConfigurationCalled(); |
| } |
| |
| sensor_test(sensor => checkOnReadingIsCalledAndReadingIsValid(sensor), |
| `${sensorType.name}: Test that onreading is called and sensor reading is valid (onchange reporting).`); |
| |
| sensor_test(sensor => { |
| sensor.mockSensorProvider.setContinuousReportingMode(); |
| return checkOnReadingIsCalledAndReadingIsValid(sensor); |
| }, `${sensorType.name}: Test that onreading is called and sensor reading is valid (continuous reporting).`); |
| |
| sensor_test(async sensor => { |
| let sensorObject = new sensorType({frequency: 60}); |
| sensorObject.start(); |
| |
| let mockSensor = await sensor.mockSensorProvider.getCreatedSensor(); |
| await mockSensor.setUpdateSensorReadingFunction(updateReading); |
| await new Promise((resolve, reject) => { |
| let wrapper = new CallbackWrapper(() => { |
| assert_true(verifyReading(sensorObject)); |
| resolve(mockSensor); |
| }, reject); |
| |
| sensorObject.onreading = wrapper.callback; |
| sensorObject.onerror = reject; |
| }); |
| testRunner.setPageVisibility('hidden'); |
| await mockSensor.suspendCalled(); |
| |
| testRunner.setPageVisibility('visible'); |
| await mockSensor.resumeCalled(); |
| |
| sensorObject.stop(); |
| await mockSensor.removeConfigurationCalled(); |
| }, `${sensorType.name}: Test that sensor receives suspend / resume notifications when page\ |
| visibility changes.`); |
| |
| sensor_test(async sensor => { |
| let sensorObject = new sensorType({frequency: 60}); |
| sensorObject.start(); |
| |
| // Create a focused editbox inside a cross-origin iframe, sensor notification must suspend. |
| const iframeSrc = 'data:text/html;charset=utf-8,<html><body><input type="text" autofocus></body></html>'; |
| let iframe = document.createElement('iframe'); |
| iframe.src = encodeURI(iframeSrc); |
| |
| let mockSensor = await sensor.mockSensorProvider.getCreatedSensor(); |
| await mockSensor.setUpdateSensorReadingFunction(updateReading); |
| |
| await new Promise((resolve, reject) => { |
| let wrapper = new CallbackWrapper(() => { |
| assert_true(verifyReading(sensorObject)); |
| resolve(mockSensor); |
| }, reject); |
| |
| sensorObject.onreading = wrapper.callback; |
| sensorObject.onerror = reject; |
| }); |
| |
| document.body.appendChild(iframe); |
| await mockSensor.suspendCalled(); |
| |
| window.focus(); |
| await mockSensor.resumeCalled(); |
| |
| sensorObject.stop(); |
| document.body.removeChild(iframe); |
| return mockSensor.removeConfigurationCalled(); |
| }, `${sensorType.name}: Test that sensor receives suspend / resume notifications when\ |
| cross-origin subframe is focused`); |
| |
| sensor_test(async sensor => { |
| let sensor1 = new sensorType({frequency: 60}); |
| sensor1.start(); |
| |
| let sensor2 = new sensorType({frequency: 20}); |
| sensor2.start(); |
| |
| let mockSensor = await sensor.mockSensorProvider.getCreatedSensor(); |
| await mockSensor.setUpdateSensorReadingFunction(updateReading); |
| await new Promise((resolve, reject) => { |
| let wrapper = new CallbackWrapper(() => { |
| // Reading values are correct for both sensors. |
| assert_true(verifyReading(sensor1)); |
| assert_true(verifyReading(sensor2)); |
| |
| // After first sensor stops its reading values are null, |
| // reading values for the second sensor sensor remain. |
| sensor1.stop(); |
| assert_true(verifyReading(sensor1, true /*should be null*/)); |
| assert_true(verifyReading(sensor2)); |
| |
| sensor2.stop(); |
| assert_true(verifyReading(sensor2, true /*should be null*/)); |
| |
| resolve(mockSensor); |
| }, reject); |
| |
| sensor1.onreading = wrapper.callback; |
| sensor1.onerror = reject; |
| sensor2.onerror = reject; |
| }); |
| return mockSensor.removeConfigurationCalled(); |
| }, `${sensorType.name}: Test that sensor reading is correct.`); |
| |
| async function checkFrequencyHintWorks(sensor) { |
| let fastSensor = new sensorType({frequency: 60}); |
| fastSensor.start(); |
| |
| let slowSensor; // To be initialized later. |
| |
| let mockSensor = await sensor.mockSensorProvider.getCreatedSensor(); |
| await mockSensor.setUpdateSensorReadingFunction(updateReading); |
| await new Promise((resolve, reject) => { |
| let fastSensorNotifiedCounter = 0; |
| let slowSensorNotifiedCounter = 0; |
| |
| let slowSensorWrapper = new CallbackWrapper(() => { |
| // Skip the initial notification that always comes immediately. |
| if (slowSensorNotifiedCounter === 1) { |
| assert_true(fastSensorNotifiedCounter > 2, |
| "Fast sensor overtakes the slow one"); |
| fastSensor.stop(); |
| slowSensor.stop(); |
| resolve(mockSensor); |
| } |
| |
| slowSensorNotifiedCounter++; |
| }, reject); |
| |
| let fastSensorWrapper = new CallbackWrapper(() => { |
| if (fastSensorNotifiedCounter === 0) { |
| // For Magnetometer and ALS, the maximum frequency is less than 60Hz |
| // we make "slow" sensor 4 times slower than the actual applied |
| // frequency, so that the "fast" sensor will immediately overtake it |
| // despite the notification adjustments. |
| const slowFrequency = mockSensor.getSamplingFrequency() * 0.25; |
| slowSensor = new sensorType({frequency: slowFrequency}); |
| slowSensor.onreading = slowSensorWrapper.callback; |
| slowSensor.onerror = reject; |
| slowSensor.start(); |
| } |
| |
| fastSensorNotifiedCounter++; |
| }, reject); |
| |
| fastSensor.onreading = fastSensorWrapper.callback; |
| fastSensor.onerror = reject; |
| }); |
| return mockSensor.removeConfigurationCalled(); |
| } |
| |
| sensor_test(sensor => checkFrequencyHintWorks(sensor), |
| `${sensorType.name}: Test that frequency hint works (onchange reporting).`); |
| |
| sensor_test(sensor => { |
| sensor.mockSensorProvider.setContinuousReportingMode(); |
| return checkFrequencyHintWorks(sensor); |
| }, `${sensorType.name}: Test that frequency hint works (continuous reporting).`); |
| |
| promise_test(() => { |
| return new Promise((resolve,reject) => { |
| let iframe = document.createElement('iframe'); |
| iframe.allow = featurePolicies.join(' \'none\'; ') + ' \'none\';'; |
| iframe.srcdoc = '<script>' + |
| ' window.onmessage = message => {' + |
| ' if (message.data === "LOADED") {' + |
| ' try {' + |
| ' new ' + sensorType.name + '();' + |
| ' parent.postMessage("FAIL", "*");' + |
| ' } catch (e) {' + |
| ' parent.postMessage("PASS", "*");' + |
| ' }' + |
| ' }' + |
| ' };' + |
| '<\/script>'; |
| iframe.onload = () => iframe.contentWindow.postMessage('LOADED', '*'); |
| document.body.appendChild(iframe); |
| window.onmessage = message => { |
| if (message.data == 'PASS') { |
| resolve(); |
| } else if (message.data == 'FAIL') { |
| reject(); |
| } |
| } |
| }); |
| }, `${sensorType.name}: Test that sensor cannot be constructed within iframe disallowed to use feature policy.`); |
| |
| promise_test(() => { |
| return new Promise((resolve,reject) => { |
| let iframe = document.createElement('iframe'); |
| iframe.allow = featurePolicies.join(';') + ';'; |
| iframe.srcdoc = '<script>' + |
| ' window.onmessage = message => {' + |
| ' if (message.data === "LOADED") {' + |
| ' try {' + |
| ' new ' + sensorType.name + '();' + |
| ' parent.postMessage("PASS", "*");' + |
| ' } catch (e) {' + |
| ' parent.postMessage("FAIL", "*");' + |
| ' }' + |
| ' }' + |
| ' };' + |
| '<\/script>'; |
| iframe.onload = () => iframe.contentWindow.postMessage('LOADED', '*'); |
| document.body.appendChild(iframe); |
| window.onmessage = message => { |
| if (message.data == 'PASS') { |
| resolve(); |
| } else if (message.data == 'FAIL') { |
| reject(); |
| } |
| } |
| }); |
| }, `${sensorType.name}: Test that sensor can be constructed within an iframe allowed to use feature policy.`); |
| |
| sensor_test(async sensor => { |
| let sensorObject = new sensorType({frequency: 60}); |
| let timestamp = 0; |
| sensorObject.start(); |
| |
| let mockSensor = await sensor.mockSensorProvider.getCreatedSensor(); |
| await mockSensor.setUpdateSensorReadingFunction(updateReading); |
| await new Promise((resolve, reject) => { |
| let wrapper1 = new CallbackWrapper(() => { |
| assert_true(sensorObject.hasReading); |
| assert_true(verifyReading(sensorObject)); |
| timestamp = sensorObject.timestamp; |
| sensorObject.stop(); |
| |
| assert_false(sensorObject.hasReading); |
| sensorObject.onreading = wrapper2.callback; |
| sensorObject.start(); |
| }, reject); |
| |
| let wrapper2 = new CallbackWrapper(() => { |
| assert_true(sensorObject.hasReading); |
| assert_true(verifyReading(sensorObject)); |
| // Make sure that 'timestamp' is already initialized. |
| assert_greater_than(timestamp, 0); |
| // Check that the reading is updated. |
| assert_greater_than(sensorObject.timestamp, timestamp); |
| sensorObject.stop(); |
| resolve(mockSensor); |
| }, reject); |
| |
| sensorObject.onreading = wrapper1.callback; |
| sensorObject.onerror = reject; |
| }); |
| return mockSensor.removeConfigurationCalled(); |
| }, `${sensorType.name}: Test that fresh reading is fetched on start().`); |
| |
| sensor_test(async sensor => { |
| if (!verifyRemappedReading) { |
| // The sensorType does not represent a spatial sensor. |
| return; |
| } |
| |
| let sensor1 = new sensorType({frequency: 60}); |
| let sensor2 = new sensorType({frequency: 60, referenceFrame: "screen"}); |
| |
| sensor1.start(); |
| sensor2.start(); |
| |
| let mockSensor = await sensor.mockSensorProvider.getCreatedSensor(); |
| await mockSensor.setUpdateSensorReadingFunction(update_sensor_reading); |
| await new Promise((resolve, reject) => { |
| let wrapper = new CallbackWrapper(() => { |
| assert_true(verifyReading(sensor1)); |
| assert_true(verifyRemappedReading(sensor2)); |
| |
| sensor1.stop(); |
| assert_true(verifyReading(sensor1, true /*should be null*/)); |
| assert_true(verifyRemappedReading(sensor2)); |
| |
| sensor2.stop(); |
| assert_true(verifyRemappedReading(sensor2, true /*should be null*/)); |
| |
| resolve(mockSensor); |
| }, reject); |
| |
| sensor1.onreading = wrapper.callback; |
| sensor1.onerror = reject; |
| sensor2.onerror = reject; |
| }); |
| return mockSensor.removeConfigurationCalled(); |
| }, `${sensorType.name}: Test that reading is mapped to the screen coordinates`); |
| } |