blob: 26ff691959917b7b4921900f0603dcc23867c0ca [file] [log] [blame]
'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 and ' +
'readingData.expectedReadings must have the same ' +
'length.');
}
if (expectedRemappedReadings &&
!validateReadingFormat(expectedRemappedReadings)) {
throw new TypeError('readingData.expectedRemappedReadings must be an ' +
'array of arrays.');
}
if (expectedRemappedReadings &&
readings.length != expectedRemappedReadings.length) {
throw new TypeError('readingData.readings 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);
await 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);
await 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.`);
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);
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);
await 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);
// await 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);
await 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.`);
// 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);
await 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.`);
}