gecko-dev/testing/web-platform/tests/web-animations/testcommon.js
Boris Chiou 58f7b4eb26 Bug 1622369 - Await the async function to make sure any async steps inside the function will be blocked. r=birtles
It's easy to get time out on mac if we don't await the async function.
The async functions return an implicit Promise. If the caller doesn't await
it, any async steps inside the function will not be blocked.

Differential Revision: https://phabricator.services.mozilla.com/D68448

--HG--
extra : moz-landing-system : lando
2020-03-27 19:00:02 +00:00

277 lines
8.2 KiB
JavaScript

/*
Distributed under both the W3C Test Suite License [1] and the W3C
3-clause BSD License [2]. To contribute to a W3C Test Suite, see the
policies and contribution forms [3].
[1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license
[2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license
[3] http://www.w3.org/2004/10/27-testcases
*/
'use strict';
const MS_PER_SEC = 1000;
// The recommended minimum precision to use for time values[1].
//
// [1] https://drafts.csswg.org/web-animations/#precision-of-time-values
const TIME_PRECISION = 0.0005; // ms
// Allow implementations to substitute an alternative method for comparing
// times based on their precision requirements.
if (!window.assert_times_equal) {
window.assert_times_equal = (actual, expected, description) => {
assert_approx_equals(actual, expected, TIME_PRECISION * 2, description);
};
}
// Allow implementations to substitute an alternative method for comparing
// a time value based on its precision requirements with a fixed value.
if (!window.assert_time_equals_literal) {
window.assert_time_equals_literal = (actual, expected, description) => {
if (Math.abs(expected) === Infinity) {
assert_equals(actual, expected, description);
} else {
assert_approx_equals(actual, expected, TIME_PRECISION, description);
}
}
}
// creates div element, appends it to the document body and
// removes the created element during test cleanup
function createDiv(test, doc) {
return createElement(test, 'div', doc);
}
// creates element of given tagName, appends it to the document body and
// removes the created element during test cleanup
// if tagName is null or undefined, returns div element
function createElement(test, tagName, doc) {
if (!doc) {
doc = document;
}
const element = doc.createElement(tagName || 'div');
doc.body.appendChild(element);
test.add_cleanup(() => {
element.remove();
});
return element;
}
// Creates a style element with the specified rules, appends it to the document
// head and removes the created element during test cleanup.
// |rules| is an object. For example:
// { '@keyframes anim': '' ,
// '.className': 'animation: anim 100s;' };
// or
// { '.className1::before': 'content: ""; width: 0px; transition: all 10s;',
// '.className2::before': 'width: 100px;' };
// The object property name could be a keyframes name, or a selector.
// The object property value is declarations which are property:value pairs
// split by a space.
function createStyle(test, rules, doc) {
if (!doc) {
doc = document;
}
const extraStyle = doc.createElement('style');
doc.head.appendChild(extraStyle);
if (rules) {
const sheet = extraStyle.sheet;
for (const selector in rules) {
sheet.insertRule(`${selector}{${rules[selector]}}`,
sheet.cssRules.length);
}
}
test.add_cleanup(() => {
extraStyle.remove();
});
}
// Cubic bezier with control points (0, 0), (x1, y1), (x2, y2), and (1, 1).
function cubicBezier(x1, y1, x2, y2) {
const xForT = t => {
const omt = 1-t;
return 3 * omt * omt * t * x1 + 3 * omt * t * t * x2 + t * t * t;
};
const yForT = t => {
const omt = 1-t;
return 3 * omt * omt * t * y1 + 3 * omt * t * t * y2 + t * t * t;
};
const tForX = x => {
// Binary subdivision.
let mint = 0, maxt = 1;
for (let i = 0; i < 30; ++i) {
const guesst = (mint + maxt) / 2;
const guessx = xForT(guesst);
if (x < guessx) {
maxt = guesst;
} else {
mint = guesst;
}
}
return (mint + maxt) / 2;
};
return x => {
if (x == 0) {
return 0;
}
if (x == 1) {
return 1;
}
return yForT(tForX(x));
};
}
function stepEnd(nsteps) {
return x => Math.floor(x * nsteps) / nsteps;
}
function stepStart(nsteps) {
return x => {
const result = Math.floor(x * nsteps + 1.0) / nsteps;
return (result > 1.0) ? 1.0 : result;
};
}
function waitForAnimationFrames(frameCount) {
return new Promise(resolve => {
function handleFrame() {
if (--frameCount <= 0) {
resolve();
} else {
window.requestAnimationFrame(handleFrame); // wait another frame
}
}
window.requestAnimationFrame(handleFrame);
});
}
// Continually calls requestAnimationFrame until |minDelay| has elapsed
// as recorded using document.timeline.currentTime (i.e. frame time not
// wall-clock time).
function waitForAnimationFramesWithDelay(minDelay) {
const startTime = document.timeline.currentTime;
return new Promise(resolve => {
(function handleFrame() {
if (document.timeline.currentTime - startTime >= minDelay) {
resolve();
} else {
window.requestAnimationFrame(handleFrame);
}
}());
});
}
// Waits for a requestAnimationFrame callback in the next refresh driver tick.
function waitForNextFrame() {
const timeAtStart = document.timeline.currentTime;
return new Promise(resolve => {
(function handleFrame() {
if (timeAtStart === document.timeline.currentTime) {
window.requestAnimationFrame(handleFrame);
} else {
resolve();
}
}());
});
}
async function insertFrameAndAwaitLoad(test, iframe, doc) {
const eventWatcher = new EventWatcher(test, iframe, ['load']);
const event_promise = eventWatcher.wait_for('load');
doc.body.appendChild(iframe);
test.add_cleanup(() => { doc.body.removeChild(iframe); });
await event_promise;
}
// Returns 'matrix()' or 'matrix3d()' function string generated from an array.
function createMatrixFromArray(array) {
return (array.length == 16 ? 'matrix3d' : 'matrix') + `(${array.join()})`;
}
// Returns 'matrix3d()' function string equivalent to
// 'rotate3d(x, y, z, radian)'.
function rotate3dToMatrix3d(x, y, z, radian) {
return createMatrixFromArray(rotate3dToMatrix(x, y, z, radian));
}
// Returns an array of the 4x4 matrix equivalent to 'rotate3d(x, y, z, radian)'.
// https://drafts.csswg.org/css-transforms-2/#Rotate3dDefined
function rotate3dToMatrix(x, y, z, radian) {
const sc = Math.sin(radian / 2) * Math.cos(radian / 2);
const sq = Math.sin(radian / 2) * Math.sin(radian / 2);
// Normalize the vector.
const length = Math.sqrt(x*x + y*y + z*z);
x /= length;
y /= length;
z /= length;
return [
1 - 2 * (y*y + z*z) * sq,
2 * (x * y * sq + z * sc),
2 * (x * z * sq - y * sc),
0,
2 * (x * y * sq - z * sc),
1 - 2 * (x*x + z*z) * sq,
2 * (y * z * sq + x * sc),
0,
2 * (x * z * sq + y * sc),
2 * (y * z * sq - x * sc),
1 - 2 * (x*x + y*y) * sq,
0,
0,
0,
0,
1
];
}
// Compare matrix string like 'matrix(1, 0, 0, 1, 100, 0)' with tolerances.
function assert_matrix_equals(actual, expected, description) {
const matrixRegExp = /^matrix(?:3d)*\((.+)\)/;
assert_regexp_match(actual, matrixRegExp,
'Actual value is not a matrix')
assert_regexp_match(expected, matrixRegExp,
'Expected value is not a matrix');
const actualMatrixArray =
actual.match(matrixRegExp)[1].split(',').map(Number);
const expectedMatrixArray =
expected.match(matrixRegExp)[1].split(',').map(Number);
assert_equals(actualMatrixArray.length, expectedMatrixArray.length,
`dimension of the matrix: ${description}`);
for (let i = 0; i < actualMatrixArray.length; i++) {
assert_approx_equals(actualMatrixArray[i], expectedMatrixArray[i], 0.0001,
`expected ${expected} but got ${actual}: ${description}`);
}
}
// Compare rotate3d vector like '0 1 0 45deg' with tolerances.
function assert_rotate3d_equals(actual, expected, description) {
const rotationRegExp =/^((([+-]?\d+(\.+\d+)?\s){3})?\d+(\.+\d+)?)deg/;
assert_regexp_match(actual, rotationRegExp,
'Actual value is not a rotate3d vector')
assert_regexp_match(expected, rotationRegExp,
'Expected value is not a rotate3d vector');
const actualRotationVector =
actual.match(rotationRegExp)[1].split(' ').map(Number);
const expectedRotationVector =
expected.match(rotationRegExp)[1].split(' ').map(Number);
assert_equals(actualRotationVector.length, expectedRotationVector.length,
`dimension of the matrix: ${description}`);
for (let i = 0; i < actualRotationVector.length; i++) {
assert_approx_equals(actualRotationVector[i], expectedRotationVector[i], 0.0001,
`expected ${expected} but got ${actual}: ${description}`);
}
}