summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/generic-sensor/resources/generic-sensor-helpers.js
blob: 146f4292ade4bf8fd965991c237a766b16cad84b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
'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;
}