Source code
Revision control
Copy as Markdown
Other Tools
'use strict';
// If two doubles differ by less than this amount, we can consider them
// to be effectively equal.
const kEpsilon = 1e-8;
class RingBuffer {
constructor(data) {
if (!Array.isArray(data)) {
throw new TypeError('`data` must be an array.');
}
this.bufferPosition_ = 0;
this.data_ = Array.from(data);
}
get data() {
return Array.from(this.data_);
}
next() {
const value = this.data_[this.bufferPosition_];
this.bufferPosition_ = (this.bufferPosition_ + 1) % this.data_.length;
return {done: false, value: value};
}
value() {
return this.data_[this.bufferPosition_];
}
[Symbol.iterator]() {
return this;
}
reset() {
this.bufferPosition_ = 0;
}
};
// Calls test_driver.update_virtual_sensor() until it results in a "reading"
// event. It waits |timeoutInMs| before considering that an event has not been
// delivered.
async function update_virtual_sensor_until_reading(
t, readings, readingPromise, testDriverName, timeoutInMs) {
while (true) {
await test_driver.update_virtual_sensor(
testDriverName, readings.next().value);
const value = await Promise.race([
new Promise(
resolve => {t.step_timeout(() => resolve('TIMEOUT'), timeoutInMs)}),
readingPromise,
]);
if (value !== 'TIMEOUT') {
break;
}
}
}
// This could be turned into a t.step_wait() call once
async function wait_for_virtual_sensor_state(testDriverName, predicate) {
const result =
await test_driver.get_virtual_sensor_information(testDriverName);
if (!predicate(result)) {
await wait_for_virtual_sensor_state(testDriverName, predicate);
}
}
function validate_sensor_data(sensorData) {
if (!('sensorName' in sensorData)) {
throw new TypeError('sensorData.sensorName is missing');
}
if (!('permissionName' in sensorData)) {
throw new TypeError('sensorData.permissionName is missing');
}
if (!('testDriverName' in sensorData)) {
throw new TypeError('sensorData.testDriverName is missing');
}
if (sensorData.featurePolicyNames !== undefined &&
!Array.isArray(sensorData.featurePolicyNames)) {
throw new TypeError('sensorData.featurePolicyNames must be an array');
}
}
function validate_reading_data(readingData) {
if (!Array.isArray(readingData.readings)) {
throw new TypeError('readingData.readings must be an array.');
}
if (!Array.isArray(readingData.expectedReadings)) {
throw new TypeError('readingData.expectedReadings must be an array.');
}
if (readingData.readings.length < readingData.expectedReadings.length) {
throw new TypeError(
'readingData.readings\' length must be bigger than ' +
'or equal to readingData.expectedReadings\' length.');
}
if (readingData.expectedRemappedReadings &&
!Array.isArray(readingData.expectedRemappedReadings)) {
throw new TypeError(
'readingData.expectedRemappedReadings must be an ' +
'array.');
}
if (readingData.expectedRemappedReadings &&
readingData.expectedReadings.length !=
readingData.expectedRemappedReadings.length) {
throw new TypeError(
'readingData.expectedReadings and ' +
'readingData.expectedRemappedReadings must have the same ' +
'length.');
}
}
function get_sensor_reading_properties(sensor) {
const className = sensor[Symbol.toStringTag];
if ([
'Accelerometer', 'GravitySensor', 'Gyroscope',
'LinearAccelerationSensor', 'Magnetometer', 'ProximitySensor'
].includes(className)) {
return ['x', 'y', 'z'];
} else if (className == 'AmbientLightSensor') {
return ['illuminance'];
} else if ([
'AbsoluteOrientationSensor', 'RelativeOrientationSensor'
].includes(className)) {
return ['quaternion'];
} else {
throw new TypeError(`Unexpected sensor '${className}'`);
}
}
// Checks that `sensor` and `expectedSensorLike` have the same properties
// (except for timestamp) and they have the same values.
//
// Options allows configuring some aspects of the comparison:
// - ignoreTimestamps (boolean): If true, `sensor` and `expectedSensorLike`'s
// "timestamp" attribute will not be compared. If `expectedSensorLike` does
// not have a "timestamp" attribute, the values will not be compared either.
// This is particularly useful when comparing sensor objects from different
// origins (and consequently different time origins).
function assert_sensor_reading_equals(
sensor, expectedSensorLike, options = {}) {
for (const prop of get_sensor_reading_properties(sensor)) {
assert_true(
prop in expectedSensorLike,
`expectedSensorLike must have a property called '${prop}'`);
if (Array.isArray(sensor[prop]))
assert_array_approx_equals(
sensor[prop], expectedSensorLike[prop], kEpsilon);
else
assert_approx_equals(sensor[prop], expectedSensorLike[prop], kEpsilon);
}
assert_not_equals(sensor.timestamp, null);
if ('timestamp' in expectedSensorLike && !options.ignoreTimestamps) {
assert_equals(
sensor.timestamp, expectedSensorLike.timestamp,
'Sensor timestamps must be equal');
}
}
function assert_sensor_reading_is_null(sensor) {
for (const prop of get_sensor_reading_properties(sensor)) {
assert_equals(sensor[prop], null);
}
assert_equals(sensor.timestamp, null);
}
function serialize_sensor_data(sensor) {
const sensorData = {};
for (const property of get_sensor_reading_properties(sensor)) {
sensorData[property] = sensor[property];
}
sensorData['timestamp'] = sensor.timestamp;
// Note that this is not serialized by postMessage().
sensorData[Symbol.toStringTag] = sensor[Symbol.toStringTag];
return sensorData;
}