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 @@
// Default sensor frequency in default configurations.
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() {
// Class that mocks Sensor interface defined in sensor.mojom
class MockSensor {
......@@ -128,7 +154,7 @@ function sensorMocks() {
// Sets fake data that is used to deliver sensor reading updates.
async setSensorReading(readingData) {
this.readingData_ = readingData;
this.readingData_ = new RingBuffer(readingData);
return this;
}
......@@ -172,7 +198,13 @@ function sensorMocks() {
const timeout = (1 / maxFrequencyUsed) * 1000;
this.sensorReadingTimerId_ = window.setInterval(() => {
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
// increasing timestamp in seconds.
this.buffer_[1] = window.performance.now() * 0.001;
......@@ -447,7 +479,7 @@ function sensor_test(func, name, properties) {
async function setMockSensorDataForType(sensorProvider, sensorType, mockDataArray) {
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
......
......@@ -13,20 +13,27 @@
if (!window.testRunner)
debug('This test cannot be run without the TestRunner');
const kDefaultReading = [1.12345, 2.12345, 3.12345];
const kMappedReading = [-2.12345, 1.12345, 3.12345];
const kReadings = {
readings: [
[1.12345, 2.12345, 3.12345]
],
expectedReadings: [
[1.12345, 2.12345, 3.12345]
],
expectedRemappedReadings: [
[-2.12345, 1.12345, 3.12345]
]
}
runGenericSensorTests(
Accelerometer,
kDefaultReading,
verifyXyzSensorReading.bind(null, kDefaultReading),
verifyXyzSensorReading.bind(null, kMappedReading),
kReadings,
verifyXyzSensorReading,
['accelerometer']);
runGenericSensorTests(
LinearAccelerationSensor,
kDefaultReading,
verifyXyzSensorReading.bind(null, kDefaultReading),
verifyXyzSensorReading.bind(null, kMappedReading),
kReadings,
verifyXyzSensorReading,
['accelerometer']);
</script>
......@@ -13,12 +13,18 @@
if (!window.testRunner)
debug('This test cannot be run without the TestRunner');
const kDefaultReading = [3.1415];
const kReadings = {
readings: [
[3.1415]
],
expectedReadings: [
[3.1415]
]
};
runGenericSensorTests(
AmbientLightSensor,
kDefaultReading,
verifyAlsSensorReading.bind(null, kDefaultReading),
null,
kReadings,
verifyAlsSensorReading,
['ambient-light-sensor']);
</script>
......@@ -13,13 +13,21 @@
if (!window.testRunner)
debug('This test cannot be run without the TestRunner');
const kDefaultReading = [1.12345, 2.12345, 3.12345];
const kMappedReading = [-2.12345, 1.12345, 3.12345];
const kReadings = {
readings: [
[1.12345, 2.12345, 3.12345]
],
expectedReadings: [
[1.12345, 2.12345, 3.12345]
],
expectedRemappedReadings: [
[-2.12345, 1.12345, 3.12345]
]
}
runGenericSensorTests(
Gyroscope,
kDefaultReading,
verifyXyzSensorReading.bind(null, kDefaultReading),
verifyXyzSensorReading.bind(null, kMappedReading),
kReadings,
verifyXyzSensorReading,
['gyroscope']);
</script>
......@@ -13,13 +13,21 @@
if (!window.testRunner)
debug('This test cannot be run without the TestRunner');
const kDefaultReading = [-19.2, 12.1, -44.3];
const kMappedReading = [-12.1, -19.2, -44.3];
const kReadings = {
readings: [
[-19.2, 12.1, -44.3]
],
expectedReadings: [
[-19.2, 12.1, -44.3]
],
expectedRemappedReadings: [
[-12.1, -19.2, -44.3]
]
}
runGenericSensorTests(
Magnetometer,
kDefaultReading,
verifyXyzSensorReading.bind(null, kDefaultReading),
verifyXyzSensorReading.bind(null, kMappedReading),
kReadings,
verifyXyzSensorReading,
['magnetometer']);
</script>
......@@ -13,16 +13,14 @@
if (!window.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,
0, -1, 0, 0,
0, 0, -1, 0,
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) {
let sensorObject = new sensorType();
......@@ -74,11 +72,20 @@ async function checkPopulateMatrix(sensorProvider, sensorType) {
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(
AbsoluteOrientationSensor,
kDefaultReading,
verifyQuatSensorReading.bind(null, kDefaultReading),
verifyQuatSensorReading.bind(null, kMappedReading),
kReadings,
verifyQuatSensorReading,
['accelerometer', 'gyroscope', 'magnetometer']);
sensor_test(sensorProvider => {
......@@ -89,9 +96,8 @@ sensor_test(sensorProvider => {
runGenericSensorTests(
RelativeOrientationSensor,
kDefaultReading,
verifyQuatSensorReading.bind(null, kDefaultReading),
verifyQuatSensorReading.bind(null, kMappedReading),
kReadings,
verifyQuatSensorReading,
['accelerometer', 'gyroscope']);
sensor_test(sensorProvider => {
......
'use strict';
// Run a set of tests for a given |sensorType|. |readingData| is
// set for providing the mock values for sensor. |verifyReading|
// is called so that the value read in JavaScript are the values expected.
// |verifyRemappedReading| is called for verifying the reading is mapped
// to the screen coordinates for a spatial sensor. |featurePolicies| represents
// the |sensorType|’s associated sensor feature name.
// Run a set of tests for a given |sensorType|.
// |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 |sensorType|’s associated sensor feature name.
function runGenericSensorTests(sensorType,
readingData,
verifyReading,
verifyRemappedReading,
verificationFunction,
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.
class CallbackWrapper {
constructor(callback, rejectFunc) {
......@@ -234,13 +269,15 @@ function runGenericSensorTests(sensorType,
assert_false(sensorObject.hasReading);
let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name);
await mockSensor.setSensorReading(readingData);
await mockSensor.setSensorReading(readings);
await new Promise((resolve, reject) => {
let wrapper = new CallbackWrapper(() => {
assert_true(verifyReading(sensorObject));
const expected = new RingBuffer(expectedReadings).next().value;
assert_true(verificationFunction(expected, sensorObject))
assert_true(sensorObject.hasReading);
sensorObject.stop();
assert_true(verifyReading(sensorObject, true /*should be null*/));
assert_true(verificationFunction(expected, sensorObject,
/*isNull=*/true))
assert_false(sensorObject.hasReading);
resolve(mockSensor);
}, reject);
......@@ -264,10 +301,11 @@ function runGenericSensorTests(sensorType,
sensorObject.start();
let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name);
await mockSensor.setSensorReading(readingData);
await mockSensor.setSensorReading(readings);
await new Promise((resolve, reject) => {
let wrapper = new CallbackWrapper(() => {
assert_true(verifyReading(sensorObject));
const expected = new RingBuffer(expectedReadings).next().value;
assert_true(verificationFunction(expected, sensorObject));
resolve(mockSensor);
}, reject);
......@@ -296,11 +334,11 @@ function runGenericSensorTests(sensorType,
iframe.allow = "focus-without-user-activation";
let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name);
await mockSensor.setSensorReading(readingData);
await mockSensor.setSensorReading(readings);
await new Promise((resolve, reject) => {
let wrapper = new CallbackWrapper(() => {
assert_true(verifyReading(sensorObject));
const expected = new RingBuffer(expectedReadings).next().value;
assert_true(verificationFunction(expected, sensorObject));
resolve(mockSensor);
}, reject);
......@@ -328,21 +366,26 @@ function runGenericSensorTests(sensorType,
sensor2.start();
let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name);
await mockSensor.setSensorReading(readingData);
await mockSensor.setSensorReading(readings);
await new Promise((resolve, reject) => {
let wrapper = new CallbackWrapper(() => {
const expected = new RingBuffer(expectedReadings).next().value;
// Reading values are correct for both sensors.
assert_true(verifyReading(sensor1));
assert_true(verifyReading(sensor2));
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(verifyReading(sensor1, true /*should be null*/));
assert_true(verifyReading(sensor2));
assert_true(verificationFunction(expected, sensor1,
/*isNull=*/true));
assert_true(verificationFunction(expected, sensor2));
sensor2.stop();
assert_true(verifyReading(sensor2, true /*should be null*/));
assert_true(verificationFunction(expected, sensor2,
/*isNull=*/true));
resolve(mockSensor);
}, reject);
......@@ -361,7 +404,8 @@ function runGenericSensorTests(sensorType,
let slowSensor; // To be initialized later.
let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name);
await mockSensor.setSensorReading(readingData);
await mockSensor.setSensorReading(readings);
await new Promise((resolve, reject) => {
let fastSensorNotifiedCounter = 0;
let slowSensorNotifiedCounter = 0;
......@@ -471,11 +515,14 @@ function runGenericSensorTests(sensorType,
sensorObject.start();
let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name);
await mockSensor.setSensorReading(readingData);
await mockSensor.setSensorReading(readings);
await new Promise((resolve, reject) => {
const expectedBuffer = new RingBuffer(expectedReadings);
let wrapper1 = new CallbackWrapper(() => {
assert_true(sensorObject.hasReading);
assert_true(verifyReading(sensorObject));
const expected = expectedBuffer.next().value;
assert_true(verificationFunction(expected, sensorObject));
timestamp = sensorObject.timestamp;
sensorObject.stop();
......@@ -486,7 +533,12 @@ function runGenericSensorTests(sensorType,
let wrapper2 = new CallbackWrapper(() => {
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.
assert_greater_than(timestamp, 0);
// Check that the reading is updated.
......@@ -502,7 +554,7 @@ function runGenericSensorTests(sensorType,
}, `${sensorType.name}: Test that fresh reading is fetched on start().`);
sensor_test(async sensorProvider => {
if (!verifyRemappedReading) {
if (!expectedRemappedReadings) {
// The sensorType does not represent a spatial sensor.
return;
}
......@@ -514,18 +566,24 @@ function runGenericSensorTests(sensorType,
sensor2.start();
let mockSensor = await sensorProvider.getCreatedSensor(sensorType.name);
await mockSensor.setSensorReading(readingData);
await mockSensor.setSensorReading(readings);
await new Promise((resolve, reject) => {
let wrapper = new CallbackWrapper(() => {
assert_true(verifyReading(sensor1));
assert_true(verifyRemappedReading(sensor2));
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(verifyReading(sensor1, true /*should be null*/));
assert_true(verifyRemappedReading(sensor2));
assert_true(verificationFunction(expected, sensor1,
/*isNull=*/true));
assert_true(verificationFunction(expectedRemapped, sensor2));
sensor2.stop();
assert_true(verifyRemappedReading(sensor2, true /*should be null*/));
assert_true(verificationFunction(expectedRemapped, sensor2,
/*isNull=*/true));
resolve(mockSensor);
}, 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