| '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 |
| // https://github.com/web-platform-tests/wpt/pull/34289 is merged. |
| 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; |
| } |