Commit a14c9c6a authored by Raphael Kubo da Costa's avatar Raphael Kubo da Costa Committed by Commit Bot

sensors: Simplify Blink-specific sensor tests, use a ring buffer for readings

The two changes are independent, but it is easier to land them together.

The idea behind them is to make it easier to then land some changes to
sensor/ambient-light-sensor.html that will cause it to round the values made
available to users of the Ambient Light Sensor API.

sensor-helpers.js now has a small RingBuffer class that is used to wrap the
readings passed via MockSensor.setSensorReading() so that we iterate over
the array of readings (and wrap around) and every time a reading event is
fired a potentially different value is emitted. This is useful for ALS
because we will no longer be able to use the same value in succession and
expect two reading events to be fired (only one will).

In order to make it easier to implement those changes in sensor-helpers.js,
runGenericSensorTests()'s signature has been simplified and all raw readings
and expected values have a standardized format, an array of arrays, where
each item corresponds to one reading. This also allows us to stop using
Function.prototype.bind() for the reading verification function and be more
explicit in telling which expected value we want in a specific reading
callback.

Bug: 642731, 606766
Change-Id: Ib9103b86116e99eff2f017806393489a8c1e99f8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1760954
Commit-Queue: Reilly Grant <reillyg@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Auto-Submit: Raphael Kubo da Costa <raphael.kubo.da.costa@intel.com>
Cr-Commit-Position: refs/heads/master@{#689658}
parent 254747dc
...@@ -3,6 +3,32 @@ ...@@ -3,6 +3,32 @@
// Default sensor frequency in default configurations. // Default sensor frequency in default configurations.
const DEFAULT_FREQUENCY = 5; const DEFAULT_FREQUENCY = 5;
// A "sliding window" that iterates over |data| and returns one item at a
// time, advancing and wrapping around as needed. |data| must be an array of
// arrays.
class RingBuffer {
constructor(data) {
this.bufferPosition_ = 0;
// Validate |data|'s format and deep-copy every element.
this.data_ = Array.from(data, element => {
if (!Array.isArray(element)) {
throw new TypeError('Every |data| element must be an array.');
}
return Array.from(element);
})
}
next() {
const value = this.data_[this.bufferPosition_];
this.bufferPosition_ = (this.bufferPosition_ + 1) % this.data_.length;
return { done: false, value: value };
}
[Symbol.iterator]() {
return this;
}
}
function sensorMocks() { function sensorMocks() {
// Class that mocks Sensor interface defined in sensor.mojom // Class that mocks Sensor interface defined in sensor.mojom
class MockSensor { class MockSensor {
...@@ -128,7 +154,7 @@ function sensorMocks() { ...@@ -128,7 +154,7 @@ function sensorMocks() {
// Sets fake data that is used to deliver sensor reading updates. // Sets fake data that is used to deliver sensor reading updates.
async setSensorReading(readingData) { async setSensorReading(readingData) {
this.readingData_ = readingData; this.readingData_ = new RingBuffer(readingData);
return this; return this;
} }
...@@ -172,7 +198,13 @@ function sensorMocks() { ...@@ -172,7 +198,13 @@ function sensorMocks() {
const timeout = (1 / maxFrequencyUsed) * 1000; const timeout = (1 / maxFrequencyUsed) * 1000;
this.sensorReadingTimerId_ = window.setInterval(() => { this.sensorReadingTimerId_ = window.setInterval(() => {
if (this.readingData_) { if (this.readingData_) {
this.buffer_.set(this.readingData_, 2); // |buffer_| is a TypedArray, so we need to make sure we pass an
// array to set().
const reading = this.readingData_.next().value;
assert_true(Array.isArray(reading), "The readings passed to " +
"setSensorReading() must arrays.");
this.buffer_.set(reading, 2);
// For all tests sensor reading should have monotonically // For all tests sensor reading should have monotonically
// increasing timestamp in seconds. // increasing timestamp in seconds.
this.buffer_[1] = window.performance.now() * 0.001; this.buffer_[1] = window.performance.now() * 0.001;
...@@ -447,7 +479,7 @@ function sensor_test(func, name, properties) { ...@@ -447,7 +479,7 @@ function sensor_test(func, name, properties) {
async function setMockSensorDataForType(sensorProvider, sensorType, mockDataArray) { async function setMockSensorDataForType(sensorProvider, sensorType, mockDataArray) {
const createdSensor = await sensorProvider.getCreatedSensor(sensorType); const createdSensor = await sensorProvider.getCreatedSensor(sensorType);
return createdSensor.setSensorReading(mockDataArray); return createdSensor.setSensorReading([mockDataArray]);
} }
// Returns a promise that will be resolved when an event equal to the given // Returns a promise that will be resolved when an event equal to the given
......
...@@ -13,20 +13,27 @@ ...@@ -13,20 +13,27 @@
if (!window.testRunner) if (!window.testRunner)
debug('This test cannot be run without the TestRunner'); debug('This test cannot be run without the TestRunner');
const kDefaultReading = [1.12345, 2.12345, 3.12345]; const kReadings = {
const kMappedReading = [-2.12345, 1.12345, 3.12345]; readings: [
[1.12345, 2.12345, 3.12345]
],
expectedReadings: [
[1.12345, 2.12345, 3.12345]
],
expectedRemappedReadings: [
[-2.12345, 1.12345, 3.12345]
]
}
runGenericSensorTests( runGenericSensorTests(
Accelerometer, Accelerometer,
kDefaultReading, kReadings,
verifyXyzSensorReading.bind(null, kDefaultReading), verifyXyzSensorReading,
verifyXyzSensorReading.bind(null, kMappedReading),
['accelerometer']); ['accelerometer']);
runGenericSensorTests( runGenericSensorTests(
LinearAccelerationSensor, LinearAccelerationSensor,
kDefaultReading, kReadings,
verifyXyzSensorReading.bind(null, kDefaultReading), verifyXyzSensorReading,
verifyXyzSensorReading.bind(null, kMappedReading),
['accelerometer']); ['accelerometer']);
</script> </script>
...@@ -13,12 +13,18 @@ ...@@ -13,12 +13,18 @@
if (!window.testRunner) if (!window.testRunner)
debug('This test cannot be run without the TestRunner'); debug('This test cannot be run without the TestRunner');
const kDefaultReading = [3.1415]; const kReadings = {
readings: [
[3.1415]
],
expectedReadings: [
[3.1415]
]
};
runGenericSensorTests( runGenericSensorTests(
AmbientLightSensor, AmbientLightSensor,
kDefaultReading, kReadings,
verifyAlsSensorReading.bind(null, kDefaultReading), verifyAlsSensorReading,
null,
['ambient-light-sensor']); ['ambient-light-sensor']);
</script> </script>
...@@ -13,13 +13,21 @@ ...@@ -13,13 +13,21 @@
if (!window.testRunner) if (!window.testRunner)
debug('This test cannot be run without the TestRunner'); debug('This test cannot be run without the TestRunner');
const kDefaultReading = [1.12345, 2.12345, 3.12345]; const kReadings = {
const kMappedReading = [-2.12345, 1.12345, 3.12345]; readings: [
[1.12345, 2.12345, 3.12345]
],
expectedReadings: [
[1.12345, 2.12345, 3.12345]
],
expectedRemappedReadings: [
[-2.12345, 1.12345, 3.12345]
]
}
runGenericSensorTests( runGenericSensorTests(
Gyroscope, Gyroscope,
kDefaultReading, kReadings,
verifyXyzSensorReading.bind(null, kDefaultReading), verifyXyzSensorReading,
verifyXyzSensorReading.bind(null, kMappedReading),
['gyroscope']); ['gyroscope']);
</script> </script>
...@@ -13,13 +13,21 @@ ...@@ -13,13 +13,21 @@
if (!window.testRunner) if (!window.testRunner)
debug('This test cannot be run without the TestRunner'); debug('This test cannot be run without the TestRunner');
const kDefaultReading = [-19.2, 12.1, -44.3]; const kReadings = {
const kMappedReading = [-12.1, -19.2, -44.3]; readings: [
[-19.2, 12.1, -44.3]
],
expectedReadings: [
[-19.2, 12.1, -44.3]
],
expectedRemappedReadings: [
[-12.1, -19.2, -44.3]
]
}
runGenericSensorTests( runGenericSensorTests(
Magnetometer, Magnetometer,
kDefaultReading, kReadings,
verifyXyzSensorReading.bind(null, kDefaultReading), verifyXyzSensorReading,
verifyXyzSensorReading.bind(null, kMappedReading),
['magnetometer']); ['magnetometer']);
</script> </script>
...@@ -13,16 +13,14 @@ ...@@ -13,16 +13,14 @@
if (!window.testRunner) if (!window.testRunner)
debug('This test cannot be run without the TestRunner'); debug('This test cannot be run without the TestRunner');
const kDefaultReading = [1, 0, 0, 0]; // 180 degrees around X axis. const kDefaultReading = [
[ 1, 0, 0, 0 ] // 180 degrees around X axis.
];
const kRotationMatrix = [1, 0, 0, 0, const kRotationMatrix = [1, 0, 0, 0,
0, -1, 0, 0, 0, -1, 0, 0,
0, 0, -1, 0, 0, 0, -1, 0,
0, 0, 0, 1]; 0, 0, 0, 1];
// For 'orientation.angle == 270', which is set for tests at
// at SensorProxy::GetScreenOrientationAngle().
const kMappedReading = [-0.707107, 0.707107, 0, 0];
async function checkPopulateMatrix(sensorProvider, sensorType) { async function checkPopulateMatrix(sensorProvider, sensorType) {
let sensorObject = new sensorType(); let sensorObject = new sensorType();
...@@ -74,11 +72,20 @@ async function checkPopulateMatrix(sensorProvider, sensorType) { ...@@ -74,11 +72,20 @@ async function checkPopulateMatrix(sensorProvider, sensorType) {
return mockSensor.removeConfigurationCalled(); return mockSensor.removeConfigurationCalled();
} }
const kReadings = {
readings: kDefaultReading,
expectedReadings: kDefaultReading,
expectedRemappedReadings: [
// For 'orientation.angle == 270', which is set for tests at
// at SensorProxy::GetScreenOrientationAngle().
[-0.707107, 0.707107, 0, 0]
]
}
runGenericSensorTests( runGenericSensorTests(
AbsoluteOrientationSensor, AbsoluteOrientationSensor,
kDefaultReading, kReadings,
verifyQuatSensorReading.bind(null, kDefaultReading), verifyQuatSensorReading,
verifyQuatSensorReading.bind(null, kMappedReading),
['accelerometer', 'gyroscope', 'magnetometer']); ['accelerometer', 'gyroscope', 'magnetometer']);
sensor_test(sensorProvider => { sensor_test(sensorProvider => {
...@@ -89,9 +96,8 @@ sensor_test(sensorProvider => { ...@@ -89,9 +96,8 @@ sensor_test(sensorProvider => {
runGenericSensorTests( runGenericSensorTests(
RelativeOrientationSensor, RelativeOrientationSensor,
kDefaultReading, kReadings,
verifyQuatSensorReading.bind(null, kDefaultReading), verifyQuatSensorReading,
verifyQuatSensorReading.bind(null, kMappedReading),
['accelerometer', 'gyroscope']); ['accelerometer', 'gyroscope']);
sensor_test(sensorProvider => { sensor_test(sensorProvider => {
......
'use strict'; 'use strict';
// Run a set of tests for a given |sensorType|. |readingData| is // Run a set of tests for a given |sensorType|.
// set for providing the mock values for sensor. |verifyReading| // |readingData| is an object with 3 keys, all of which are arrays of arrays:
// is called so that the value read in JavaScript are the values expected. // 1. "readings". Each value corresponds to one raw reading that will be
// |verifyRemappedReading| is called for verifying the reading is mapped // processed by a sensor.
// to the screen coordinates for a spatial sensor. |featurePolicies| represents // 2. "expectedReadings". Each value corresponds to the processed value a
// the |sensorType|’s associated sensor feature name. // 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 |sensorType|’s associated sensor feature name.
function runGenericSensorTests(sensorType, function runGenericSensorTests(sensorType,
readingData, readingData,
verifyReading, verificationFunction,
verifyRemappedReading,
featurePolicies) { featurePolicies) {
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.');
}
// Wraps callback and calls rejectFunc if callback throws an error. // Wraps callback and calls rejectFunc if callback throws an error.
class CallbackWrapper { class CallbackWrapper {
constructor(callback, rejectFunc) { constructor(callback, rejectFunc) {
...@@ -234,13 +269,15 @@ function runGenericSensorTests(sensorType, ...@@ -234,13 +269,15 @@ function runGenericSensorTests(sensorType,
assert_false(sensorObject.hasReading); assert_false(sensorObject.hasReading);
let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name); let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name);
await mockSensor.setSensorReading(readingData); await mockSensor.setSensorReading(readings);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
let wrapper = new CallbackWrapper(() => { let wrapper = new CallbackWrapper(() => {
assert_true(verifyReading(sensorObject)); const expected = new RingBuffer(expectedReadings).next().value;
assert_true(verificationFunction(expected, sensorObject))
assert_true(sensorObject.hasReading); assert_true(sensorObject.hasReading);
sensorObject.stop(); sensorObject.stop();
assert_true(verifyReading(sensorObject, true /*should be null*/)); assert_true(verificationFunction(expected, sensorObject,
/*isNull=*/true))
assert_false(sensorObject.hasReading); assert_false(sensorObject.hasReading);
resolve(mockSensor); resolve(mockSensor);
}, reject); }, reject);
...@@ -264,10 +301,11 @@ function runGenericSensorTests(sensorType, ...@@ -264,10 +301,11 @@ function runGenericSensorTests(sensorType,
sensorObject.start(); sensorObject.start();
let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name); let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name);
await mockSensor.setSensorReading(readingData); await mockSensor.setSensorReading(readings);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
let wrapper = new CallbackWrapper(() => { let wrapper = new CallbackWrapper(() => {
assert_true(verifyReading(sensorObject)); const expected = new RingBuffer(expectedReadings).next().value;
assert_true(verificationFunction(expected, sensorObject));
resolve(mockSensor); resolve(mockSensor);
}, reject); }, reject);
...@@ -296,11 +334,11 @@ function runGenericSensorTests(sensorType, ...@@ -296,11 +334,11 @@ function runGenericSensorTests(sensorType,
iframe.allow = "focus-without-user-activation"; iframe.allow = "focus-without-user-activation";
let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name); let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name);
await mockSensor.setSensorReading(readingData); await mockSensor.setSensorReading(readings);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
let wrapper = new CallbackWrapper(() => { let wrapper = new CallbackWrapper(() => {
assert_true(verifyReading(sensorObject)); const expected = new RingBuffer(expectedReadings).next().value;
assert_true(verificationFunction(expected, sensorObject));
resolve(mockSensor); resolve(mockSensor);
}, reject); }, reject);
...@@ -328,21 +366,26 @@ function runGenericSensorTests(sensorType, ...@@ -328,21 +366,26 @@ function runGenericSensorTests(sensorType,
sensor2.start(); sensor2.start();
let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name); let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name);
await mockSensor.setSensorReading(readingData); await mockSensor.setSensorReading(readings);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
let wrapper = new CallbackWrapper(() => { let wrapper = new CallbackWrapper(() => {
const expected = new RingBuffer(expectedReadings).next().value;
// Reading values are correct for both sensors. // Reading values are correct for both sensors.
assert_true(verifyReading(sensor1)); assert_true(verificationFunction(expected, sensor1));
assert_true(verifyReading(sensor2)); assert_true(verificationFunction(expected, sensor2));
// After first sensor stops its reading values are null, // After first sensor stops its reading values are null,
// reading values for the second sensor sensor remain. // reading values for the second sensor sensor remain.
sensor1.stop(); sensor1.stop();
assert_true(verifyReading(sensor1, true /*should be null*/));
assert_true(verifyReading(sensor2)); assert_true(verificationFunction(expected, sensor1,
/*isNull=*/true));
assert_true(verificationFunction(expected, sensor2));
sensor2.stop(); sensor2.stop();
assert_true(verifyReading(sensor2, true /*should be null*/)); assert_true(verificationFunction(expected, sensor2,
/*isNull=*/true));
resolve(mockSensor); resolve(mockSensor);
}, reject); }, reject);
...@@ -361,7 +404,8 @@ function runGenericSensorTests(sensorType, ...@@ -361,7 +404,8 @@ function runGenericSensorTests(sensorType,
let slowSensor; // To be initialized later. let slowSensor; // To be initialized later.
let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name); let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name);
await mockSensor.setSensorReading(readingData); await mockSensor.setSensorReading(readings);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
let fastSensorNotifiedCounter = 0; let fastSensorNotifiedCounter = 0;
let slowSensorNotifiedCounter = 0; let slowSensorNotifiedCounter = 0;
...@@ -471,11 +515,14 @@ function runGenericSensorTests(sensorType, ...@@ -471,11 +515,14 @@ function runGenericSensorTests(sensorType,
sensorObject.start(); sensorObject.start();
let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name); let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name);
await mockSensor.setSensorReading(readingData); await mockSensor.setSensorReading(readings);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
const expectedBuffer = new RingBuffer(expectedReadings);
let wrapper1 = new CallbackWrapper(() => { let wrapper1 = new CallbackWrapper(() => {
assert_true(sensorObject.hasReading); assert_true(sensorObject.hasReading);
assert_true(verifyReading(sensorObject)); const expected = expectedBuffer.next().value;
assert_true(verificationFunction(expected, sensorObject));
timestamp = sensorObject.timestamp; timestamp = sensorObject.timestamp;
sensorObject.stop(); sensorObject.stop();
...@@ -486,7 +533,12 @@ function runGenericSensorTests(sensorType, ...@@ -486,7 +533,12 @@ function runGenericSensorTests(sensorType,
let wrapper2 = new CallbackWrapper(() => { let wrapper2 = new CallbackWrapper(() => {
assert_true(sensorObject.hasReading); assert_true(sensorObject.hasReading);
assert_true(verifyReading(sensorObject)); // |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
// sensor-helpers.js.
const expected = expectedBuffer.next().value;
assert_true(verificationFunction(expected, sensorObject));
// Make sure that 'timestamp' is already initialized. // Make sure that 'timestamp' is already initialized.
assert_greater_than(timestamp, 0); assert_greater_than(timestamp, 0);
// Check that the reading is updated. // Check that the reading is updated.
...@@ -502,7 +554,7 @@ function runGenericSensorTests(sensorType, ...@@ -502,7 +554,7 @@ function runGenericSensorTests(sensorType,
}, `${sensorType.name}: Test that fresh reading is fetched on start().`); }, `${sensorType.name}: Test that fresh reading is fetched on start().`);
sensor_test(async sensorProvider => { sensor_test(async sensorProvider => {
if (!verifyRemappedReading) { if (!expectedRemappedReadings) {
// The sensorType does not represent a spatial sensor. // The sensorType does not represent a spatial sensor.
return; return;
} }
...@@ -514,18 +566,24 @@ function runGenericSensorTests(sensorType, ...@@ -514,18 +566,24 @@ function runGenericSensorTests(sensorType,
sensor2.start(); sensor2.start();
let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name); let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name);
await mockSensor.setSensorReading(readingData); await mockSensor.setSensorReading(readings);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
let wrapper = new CallbackWrapper(() => { let wrapper = new CallbackWrapper(() => {
assert_true(verifyReading(sensor1)); const expected = new RingBuffer(expectedReadings).next().value;
assert_true(verifyRemappedReading(sensor2)); const expectedRemapped =
new RingBuffer(expectedRemappedReadings).next().value;
assert_true(verificationFunction(expected, sensor1));
assert_true(verificationFunction(expectedRemapped, sensor2));
sensor1.stop(); sensor1.stop();
assert_true(verifyReading(sensor1, true /*should be null*/)); assert_true(verificationFunction(expected, sensor1,
assert_true(verifyRemappedReading(sensor2)); /*isNull=*/true));
assert_true(verificationFunction(expectedRemapped, sensor2));
sensor2.stop(); sensor2.stop();
assert_true(verifyRemappedReading(sensor2, true /*should be null*/)); assert_true(verificationFunction(expectedRemapped, sensor2,
/*isNull=*/true));
resolve(mockSensor); resolve(mockSensor);
}, reject); }, reject);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment