summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/orientation-event/resources/orientation-event-helpers.js
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/orientation-event/resources/orientation-event-helpers.js')
-rw-r--r--testing/web-platform/tests/orientation-event/resources/orientation-event-helpers.js274
1 files changed, 274 insertions, 0 deletions
diff --git a/testing/web-platform/tests/orientation-event/resources/orientation-event-helpers.js b/testing/web-platform/tests/orientation-event/resources/orientation-event-helpers.js
new file mode 100644
index 0000000000..01e91c62ae
--- /dev/null
+++ b/testing/web-platform/tests/orientation-event/resources/orientation-event-helpers.js
@@ -0,0 +1,274 @@
+'use strict';
+
+// @class SensorTestHelper
+//
+// SensorTestHelper is a helper utilities for orientation event tests.
+//
+// Usage example with device orientation:
+// const helper = new SensorTestHelper(t, 'deviceorientation');
+// await helper.grantSensorsPermissions();
+// await helper.initializeSensors();
+// const generatedData = generateOrientationData(1, 2, 3, false);
+// await helper.setData(generatedData);
+// await waitForEvent(getExpectedOrientationEvent(generatedData));
+class SensorTestHelper {
+ #eventName;
+ #sensorsEnabledByDefault;
+ #enabledSensors;
+ #disabledSensors;
+ #testObject;
+
+ // @param {object} t - A testharness.js subtest instance.
+ // @param {string} eventName - A name of event. Accepted values are
+ // devicemotion, deviceorientation or
+ // deviceorientationabsolute.
+ constructor(t, eventName) {
+ this.#eventName = eventName;
+ this.#testObject = t;
+ this.#testObject.add_cleanup(() => this.reset());
+
+ switch (this.#eventName) {
+ case 'devicemotion':
+ this.#sensorsEnabledByDefault =
+ new Set(['accelerometer', 'gyroscope', 'linear-acceleration']);
+ break;
+ case 'deviceorientation':
+ this.#sensorsEnabledByDefault = new Set(['relative-orientation']);
+ break;
+ case 'deviceorientationabsolute':
+ this.#sensorsEnabledByDefault = new Set(['absolute-orientation']);
+ break;
+ default:
+ throw new Error(`Invalid event name ${this.#eventName}`);
+ }
+ }
+
+ // Creates virtual sensors that will be used in tests.
+ //
+ // This function must be called before event listeners are added or calls
+ // to setData() or waitForEvent() are made.
+ //
+ // The |options| parameter is an object that accepts the following entries:
+ // - enabledSensors: A list of virtual sensor names that will be created
+ // instead of the default ones for a given event type.
+ // - disabledSensors: A list of virtual sensor names that will be created
+ // in a disabled state, so that creating a sensor of
+ // a given type is guaranteed to fail.
+ // An Error is thrown if the same name is passed to both options.
+ //
+ // A default list of virtual sensors based on the |eventName| parameter passed
+ // to the constructor is used if |options| is not specified.
+ //
+ // Usage examples
+ // Use default sensors for the given event type:
+ // await helper.initializeSensors()
+ // Enable specific sensors:
+ // await helper.initializeSensors({
+ // enabledSensors: ['accelerometer', 'gyroscope']
+ // })
+ // Disable some sensors, make some report as not available:
+ // await helper.initializeSensors({
+ // disabledSensors: ['gyroscope']
+ // })
+ // Enable some sensors, make some report as not available:
+ // await helper.initializeSensors({
+ // enabledSensors: ['accelerometer'],
+ // disabledSensors: ['gyroscope']
+ // })
+ async initializeSensors(options = {}) {
+ this.#disabledSensors = new Set(options.disabledSensors || []);
+ // Check that a sensor name is not in both |options.enabledSensors| and
+ // |options.disabledSensors|.
+ for (const sensor of (options.enabledSensors || [])) {
+ if (this.#disabledSensors.has(sensor)) {
+ throw new Error(`${sensor} can be defined only as enabledSensors or disabledSensors`);
+ }
+ }
+
+ this.#enabledSensors = new Set(options.enabledSensors || this.#sensorsEnabledByDefault);
+ // Remove sensors from enabledSensors that are in disabledSensors
+ for (const sensor of this.#disabledSensors) {
+ this.#enabledSensors.delete(sensor);
+ }
+
+ const createVirtualSensorPromises = [];
+ for (const sensor of this.#enabledSensors) {
+ createVirtualSensorPromises.push(
+ test_driver.create_virtual_sensor(sensor));
+ }
+ for (const sensor of this.#disabledSensors) {
+ createVirtualSensorPromises.push(
+ test_driver.create_virtual_sensor(sensor, {connected: false}));
+ }
+ await Promise.all(createVirtualSensorPromises);
+ }
+
+ // Updates virtual sensor with given data.
+ // @param {object} data - Generated data by generateMotionData or
+ // generateOrientationData which is passed to
+ // test_driver.update_virtual_sensor().
+ async setData(data) {
+ // WebDriver expects numbers for all values in the readings it receives. We
+ // convert null to zero here, but any other numeric value would work, as it
+ // is the presence of one or more sensors in initializeSensors()'
+ // options.disabledSensors that cause null to be reported in one or more
+ // event attributes.
+ const nullToZero = x => (x === null ? 0 : x);
+ if (this.#eventName === 'devicemotion') {
+ const degToRad = Math.PI / 180;
+ await Promise.all([
+ test_driver.update_virtual_sensor('accelerometer', {
+ 'x': nullToZero(data.accelerationIncludingGravityX),
+ 'y': nullToZero(data.accelerationIncludingGravityY),
+ 'z': nullToZero(data.accelerationIncludingGravityZ),
+ }),
+ test_driver.update_virtual_sensor('linear-acceleration', {
+ 'x': nullToZero(data.accelerationX),
+ 'y': nullToZero(data.accelerationY),
+ 'z': nullToZero(data.accelerationZ),
+ }),
+ test_driver.update_virtual_sensor('gyroscope', {
+ 'x': nullToZero(data.rotationRateAlpha) * degToRad,
+ 'y': nullToZero(data.rotationRateBeta) * degToRad,
+ 'z': nullToZero(data.rotationRateGamma) * degToRad,
+ }),
+ ]);
+ } else {
+ const sensorType =
+ data.absolute ? 'absolute-orientation' : 'relative-orientation';
+ await test_driver.update_virtual_sensor(sensorType, {
+ alpha: nullToZero(data.alpha),
+ beta: nullToZero(data.beta),
+ gamma: nullToZero(data.gamma),
+ });
+ }
+ }
+
+ // Grants permissions to sensors. Depending on |eventName|, requests
+ // permission to use either the DeviceMotionEvent or the
+ // DeviceOrientationEvent API.
+ async grantSensorsPermissions() {
+ // Required by all event types.
+ await test_driver.set_permission({name: 'accelerometer'}, 'granted');
+ await test_driver.set_permission({name: 'gyroscope'}, 'granted');
+ if (this.#eventName == 'deviceorientationabsolute') {
+ await test_driver.set_permission({name: 'magnetometer'}, 'granted');
+ }
+
+ const interfaceName = this.#eventName == 'devicemotion' ?
+ DeviceMotionEvent :
+ DeviceOrientationEvent;
+ await test_driver.bless('enable user activation', async () => {
+ const permission = await interfaceName.requestPermission();
+ assert_equals(permission, 'granted');
+ });
+ }
+
+ // Resets SensorTestHelper to default state. Removes all created virtual
+ // sensors.
+ async reset() {
+ const createdVirtualSensors =
+ new Set([...this.#enabledSensors, ...this.#disabledSensors]);
+
+ const sensorRemovalPromises = [];
+ for (const sensor of createdVirtualSensors) {
+ sensorRemovalPromises.push(test_driver.remove_virtual_sensor(sensor));
+ }
+ await Promise.all(sensorRemovalPromises);
+ }
+}
+
+function generateMotionData(
+ accelerationX, accelerationY, accelerationZ, accelerationIncludingGravityX,
+ accelerationIncludingGravityY, accelerationIncludingGravityZ,
+ rotationRateAlpha, rotationRateBeta, rotationRateGamma, interval = 16) {
+ const motionData = {
+ accelerationX: accelerationX,
+ accelerationY: accelerationY,
+ accelerationZ: accelerationZ,
+ accelerationIncludingGravityX: accelerationIncludingGravityX,
+ accelerationIncludingGravityY: accelerationIncludingGravityY,
+ accelerationIncludingGravityZ: accelerationIncludingGravityZ,
+ rotationRateAlpha: rotationRateAlpha,
+ rotationRateBeta: rotationRateBeta,
+ rotationRateGamma: rotationRateGamma,
+ interval: interval
+ };
+ return motionData;
+}
+
+function generateOrientationData(alpha, beta, gamma, absolute) {
+ const orientationData =
+ {alpha: alpha, beta: beta, gamma: gamma, absolute: absolute};
+ return orientationData;
+}
+
+function assertEventEquals(actualEvent, expectedEvent) {
+ // If two doubles differ by less than this amount, we can consider them
+ // to be effectively equal.
+ const EPSILON = 1e-8;
+
+ for (let key1 of Object.keys(Object.getPrototypeOf(expectedEvent))) {
+ if (typeof expectedEvent[key1] === 'object' &&
+ expectedEvent[key1] !== null) {
+ assertEventEquals(actualEvent[key1], expectedEvent[key1]);
+ } else if (typeof expectedEvent[key1] === 'number') {
+ assert_approx_equals(
+ actualEvent[key1], expectedEvent[key1], EPSILON, key1);
+ } else {
+ assert_equals(actualEvent[key1], expectedEvent[key1], key1);
+ }
+ }
+}
+
+function getExpectedOrientationEvent(expectedOrientationData) {
+ return new DeviceOrientationEvent('deviceorientation', {
+ alpha: expectedOrientationData.alpha,
+ beta: expectedOrientationData.beta,
+ gamma: expectedOrientationData.gamma,
+ absolute: expectedOrientationData.absolute,
+ });
+}
+
+function getExpectedAbsoluteOrientationEvent(expectedOrientationData) {
+ return new DeviceOrientationEvent('deviceorientationabsolute', {
+ alpha: expectedOrientationData.alpha,
+ beta: expectedOrientationData.beta,
+ gamma: expectedOrientationData.gamma,
+ absolute: expectedOrientationData.absolute,
+ });
+}
+
+function getExpectedMotionEvent(expectedMotionData) {
+ return new DeviceMotionEvent('devicemotion', {
+ acceleration: {
+ x: expectedMotionData.accelerationX,
+ y: expectedMotionData.accelerationY,
+ z: expectedMotionData.accelerationZ,
+ },
+ accelerationIncludingGravity: {
+ x: expectedMotionData.accelerationIncludingGravityX,
+ y: expectedMotionData.accelerationIncludingGravityY,
+ z: expectedMotionData.accelerationIncludingGravityZ,
+ },
+ rotationRate: {
+ alpha: expectedMotionData.rotationRateAlpha,
+ beta: expectedMotionData.rotationRateBeta,
+ gamma: expectedMotionData.rotationRateGamma,
+ },
+ interval: expectedMotionData.interval,
+ });
+}
+
+function waitForEvent(expected_event) {
+ return new Promise((resolve, reject) => {
+ window.addEventListener(expected_event.type, (event) => {
+ try {
+ assertEventEquals(event, expected_event);
+ resolve();
+ } catch (e) {
+ reject(e);
+ }
+ }, {once: true});
+ });
+}