Skip to content

Commit cd92052

Browse files
targoscodebytere
authored andcommittedJun 7, 2020
test: add hr-time Web platform tests
Refs: #32790 PR-URL: #33287 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
1 parent 68551d2 commit cd92052

19 files changed

+537
-0
lines changed
 

‎test/fixtures/wpt/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Last update:
1717
- interfaces: https://github.com/web-platform-tests/wpt/tree/712c9f275e/interfaces
1818
- html/webappapis/microtask-queuing: https://github.com/web-platform-tests/wpt/tree/0c3bed38df/html/webappapis/microtask-queuing
1919
- html/webappapis/timers: https://github.com/web-platform-tests/wpt/tree/ddfe9c089b/html/webappapis/timers
20+
- hr-time: https://github.com/web-platform-tests/wpt/tree/a5d1774ecf/hr-time
2021

2122
[Web Platform Tests]: https://github.com/web-platform-tests/wpt
2223
[`git node wpt`]: https://github.com/nodejs/node-core-utils/blob/master/docs/git-node.md#git-node-wpt

‎test/fixtures/wpt/hr-time/META.yml

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
spec: https://w3c.github.io/hr-time/
2+
suggested_reviewers:
3+
- plehegar
4+
- igrigorik
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
test(function() {
2+
assert_true((self.performance !== undefined), "self.performance exists");
3+
assert_equals(typeof self.performance, "object", "self.performance is an object");
4+
assert_equals((typeof self.performance.now), "function", "self.performance.now() is a function");
5+
assert_equals(typeof self.performance.now(), "number", "self.performance.now() returns a number");
6+
}, "self.performance.now() is a function that returns a number");
7+
8+
test(function() {
9+
assert_true(self.performance.now() > 0);
10+
}, "self.performance.now() returns a positive number");
11+
12+
test(function() {
13+
var now1 = self.performance.now();
14+
var now2 = self.performance.now();
15+
assert_true((now2-now1) >= 0);
16+
}, "self.performance.now() difference is not negative");
17+
18+
async_test(function() {
19+
// Check whether the performance.now() method is close to Date() within 30ms (due to inaccuracies)
20+
var initial_hrt = self.performance.now();
21+
var initial_date = Date.now();
22+
this.step_timeout(function() {
23+
var final_hrt = self.performance.now();
24+
var final_date = Date.now();
25+
assert_approx_equals(final_hrt - initial_hrt, final_date - initial_date, 30, 'High resolution time value increased by approximately the same amount as time from date object');
26+
this.done();
27+
}, 2000);
28+
}, 'High resolution time has approximately the right relative magnitude');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// META: global=window,worker
2+
// META: script=/resources/WebIDLParser.js
3+
// META: script=/resources/idlharness.js
4+
// META: timeout=long
5+
6+
'use strict';
7+
8+
// https://w3c.github.io/hr-time/
9+
10+
idl_test(
11+
['hr-time'],
12+
['html', 'dom'],
13+
async idl_array => {
14+
if (self.GLOBAL.isWorker()) {
15+
idl_array.add_objects({ WorkerGlobalScope: ['self'] });
16+
} else {
17+
idl_array.add_objects({ Window: ['self'] });
18+
}
19+
idl_array.add_objects({
20+
Performance: ['performance'],
21+
});
22+
}
23+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// The time values returned when calling the now method MUST be monotonically increasing and not subject to system clock adjustments or system clock skew.
2+
test(function() {
3+
assert_true(self.performance.now() > 0, "self.performance.now() returns positive numbers");
4+
}, "self.performance.now() returns a positive number");
5+
6+
// The difference between any two chronologically recorded time values returned from the now method MUST never be negative.
7+
test(function() {
8+
var now1 = self.performance.now();
9+
var now2 = self.performance.now();
10+
assert_true((now2-now1) >= 0, "self.performance.now() difference is not negative");
11+
},
12+
"self.performance.now() difference is not negative"
13+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<script src="/resources/testharness.js"></script>
5+
<script src="/resources/testharnessreport.js"></script>
6+
</head>
7+
<body>
8+
<script>
9+
10+
test(() => {
11+
// Check Performance attributes.
12+
assert_equals(typeof(performance.toJSON), 'function');
13+
const json = performance.toJSON();
14+
assert_equals(typeof(json), 'object');
15+
assert_equals(json.timeOrigin, performance.timeOrigin,
16+
'performance.toJSON().timeOrigin should match performance.timeOrigin');
17+
18+
// Check PerformanceTiming toJSON.
19+
const jsonTiming = json.timing;
20+
const timing = performance.timing;
21+
assert_equals(typeof(timing.toJSON), 'function');
22+
const timingJSON = timing.toJSON();
23+
assert_equals(typeof(timingJSON), 'object');
24+
// Check PerformanceTiming attributes, from both:
25+
// 1) |jsonTiming| from Performance.
26+
// 2) |timingJSON| from PerformanceTiming.
27+
const performanceTimingKeys = [
28+
'navigationStart',
29+
'unloadEventStart',
30+
'unloadEventEnd',
31+
'redirectStart',
32+
'redirectEnd',
33+
'fetchStart',
34+
'domainLookupStart',
35+
'domainLookupEnd',
36+
'connectStart',
37+
'connectEnd',
38+
'secureConnectionStart',
39+
'requestStart',
40+
'responseStart',
41+
'responseEnd',
42+
'domLoading',
43+
'domInteractive',
44+
'domContentLoadedEventStart',
45+
'domContentLoadedEventEnd',
46+
'domComplete',
47+
'loadEventStart',
48+
'loadEventEnd'
49+
];
50+
for (const key of performanceTimingKeys) {
51+
assert_equals(jsonTiming[key], timing[key],
52+
`performance.toJSON().timing.${key} should match performance.timing.${key}`);
53+
assert_equals(timingJSON[key], timing[key],
54+
`performance.timing.toJSON().${key} should match performance.timing.${key}`);
55+
}
56+
57+
// Check PerformanceNavigation toJSON.
58+
const jsonNavigation = json.navigation;
59+
const navigation = performance.navigation;
60+
assert_equals(typeof(navigation.toJSON), 'function');
61+
const navigationJSON = navigation.toJSON();
62+
assert_equals(typeof(navigationJSON), 'object');
63+
// Check PerformanceNavigation attributes, from both:
64+
// 1) |jsonNavigation| from Performance.
65+
// 2) |navigationJSON| from PerformanceNavigation.
66+
let performanceNavigationKeys = ['type', 'redirectCount'];
67+
for (const key of performanceNavigationKeys) {
68+
assert_equals(jsonNavigation[key], navigation[key],
69+
`performance.toJSON().navigation.${key} should match performance.navigation.${key}`);
70+
assert_equals(navigationJSON[key], navigation[key],
71+
`performance.navigation.toJSON().${key} should match performance.navigation.${key}`);
72+
}
73+
}, 'Test performance.toJSON()');
74+
</script>
75+
</body>
76+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<!DOCTYPE HTML>
2+
<html>
3+
<head>
4+
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
5+
<title>window.performance.now frame</title>
6+
<link rel="author" title="Google" href="http://www.google.com/" />
7+
</head>
8+
<body></body>
9+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Helper page for ../unload-manual.html</title>
5+
</head>
6+
<body>
7+
<script src="./unload.js"></script>
8+
<script>
9+
setupListeners("a", "./unload-b.html");
10+
</script>
11+
<button id="proceed">Click me!</button>
12+
</body>
13+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Helper page for ../unload-manual.html</title>
5+
</head>
6+
<body>
7+
<script src="./unload.js"></script>
8+
<script>
9+
setupListeners("b", "./unload-c.html");
10+
</script>
11+
<button id="proceed">Click me again!</button>
12+
</body>
13+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Helper page for ../unload-manual.html</title>
5+
</head>
6+
<body>
7+
<script src="./unload.js"></script>
8+
<script>
9+
setupListeners("c", null);
10+
</script>
11+
<button id="proceed">Click me, one last time!</button>
12+
</body>
13+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
const syncDelay = ms => {
2+
const start = performance.now();
3+
let elapsedTime;
4+
do {
5+
elapsedTime = performance.now() - start;
6+
} while (elapsedTime < ms);
7+
};
8+
9+
const markTime = (docName, lifecycleEventName) => {
10+
// Calculating these values before the below `mark` invocation ensures that delays in
11+
// reaching across to the other window object doesn't interfere with the correctness
12+
// of the test.
13+
const dateNow = Date.now();
14+
const performanceNow = performance.now();
15+
16+
window.opener.mark({
17+
docName,
18+
lifecycleEventName,
19+
performanceNow: performanceNow,
20+
dateNow: dateNow
21+
});
22+
};
23+
24+
const setupUnloadPrompt = (docName, msg) => {
25+
window.addEventListener("beforeunload", ev => {
26+
markTime(docName, "beforeunload");
27+
return ev.returnValue = msg || "Click OK to continue test."
28+
});
29+
};
30+
31+
const setupListeners = (docName, nextDocument) => {
32+
window.addEventListener("load", () => {
33+
markTime(docName, "load");
34+
document.getElementById("proceed").addEventListener("click", ev => {
35+
ev.preventDefault();
36+
if (nextDocument) {
37+
document.location = nextDocument;
38+
} else {
39+
window.close();
40+
}
41+
})
42+
});
43+
44+
setupUnloadPrompt(docName);
45+
46+
window.addEventListener("unload", () => {
47+
markTime(docName, "unload");
48+
if (docName !== "c") { syncDelay(1000); }
49+
});
50+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" >
5+
<title>window.performance.now across frames</title>
6+
<link rel="author" title="Google" href="http://www.google.com/">
7+
<link rel="help" href="http://www.w3.org/TR/hr-time/#sec-extenstions-performance-interface">
8+
9+
<script src="/resources/testharness.js"></script>
10+
<script src="/resources/testharnessreport.js"></script>
11+
12+
<script type="text/javascript">
13+
setup({explicit_done: true});
14+
15+
var setup_frame = async_test("Setup the frame");
16+
17+
function start_test() {
18+
setup_frame.step_timeout(function () {
19+
var iframe = document.createElement('iframe');
20+
iframe.id = 'frameContext';
21+
iframe.onload = finish_test;
22+
iframe.src = "resources/now_frame.html";
23+
document.body.appendChild(iframe);
24+
setup_frame.done();
25+
}, 1000);
26+
}
27+
28+
function finish_test() {
29+
var childWindow = document.getElementById('frameContext').contentWindow;
30+
31+
// Verify a positive number is returned for both the frame and parent.
32+
test(function() { assert_true(window.performance.now() > 0); }, 'parent performance.now() > 0');
33+
test(function() { assert_true(childWindow.performance.now() > 0); }, 'child performance.now() > 0');
34+
35+
// Verify that the test properly created the child at least a second after the parent.
36+
test(function () { assert_true(childWindow.performance.timing.navigationStart > (window.performance.timing.navigationStart + 1000)); },
37+
'Child created at least 1 second after parent');
38+
39+
test(function () {
40+
var parentNow = window.performance.now();
41+
var childNow = childWindow.performance.now();
42+
var childParentSkew = Math.abs(childNow - parentNow);
43+
assert_true(childParentSkew > 1000, 'Child and parent\'s now()s have different bases (skewed more than 1 second)');
44+
45+
var childLoadTime = childWindow.performance.timing.loadEventStart - childWindow.performance.timing.navigationStart;
46+
assert_true(1000 > (childNow - childLoadTime), 'Child\'s now() is based on its document\'s navigationStart');
47+
}, 'Child and parent time bases are correct');
48+
49+
done();
50+
}
51+
</script>
52+
53+
</head>
54+
<body onload="start_test()">
55+
<h1>Description</h1>
56+
<p>This test validates the values of the window.performance.now() are based on the current document's navigationStart.</p>
57+
<div id="log"></div>
58+
</body>
59+
</html>
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<script src="/resources/testharness.js"></script>
5+
<script src="/resources/testharnessreport.js"></script>
6+
</head>
7+
<body>
8+
<script>
9+
const windowOrigin = performance.timeOrigin;
10+
11+
test(() => {
12+
// Use a 30ms cushion when comparing with Date() to account for inaccuracy.
13+
const startTime = Date.now();
14+
assert_greater_than_equal(startTime + 30, windowOrigin, 'Date.now() should be at least as large as the window timeOrigin.');
15+
const startNow = performance.now();
16+
assert_less_than_equal(startTime, windowOrigin + startNow + 30, 'Date.now() should be close to window timeOrigin.');
17+
}, 'Window timeOrigin is close to Date.now() when there is no system clock adjustment.');
18+
19+
const workerScript = 'postMessage({timeOrigin: performance.timeOrigin})';
20+
const blob = new Blob([workerScript]);
21+
22+
async_test(function(t) {
23+
const beforeWorkerCreation = performance.now();
24+
const worker = new Worker(URL.createObjectURL(blob));
25+
worker.addEventListener('message', t.step_func_done(function(event) {
26+
const workerOrigin = event.data.timeOrigin;
27+
assert_greater_than_equal(workerOrigin, windowOrigin + beforeWorkerCreation, 'Worker timeOrigin should be greater than the window timeOrigin.');
28+
const afterWorkerCreation = performance.now();
29+
assert_less_than_equal(workerOrigin - windowOrigin, afterWorkerCreation, 'Window and worker timeOrigins should be close.');
30+
}));
31+
}, 'Window and worker timeOrigins are close when created one after another.');
32+
33+
async_test(function(t) {
34+
this.step_timeout(function() {
35+
const workerCreation = performance.now();
36+
const worker = new Worker(URL.createObjectURL(blob));
37+
worker.addEventListener('message', t.step_func_done(function(event) {
38+
const workerOrigin = event.data.timeOrigin;
39+
assert_greater_than_equal(workerOrigin - windowOrigin, 200, 'We waited 200ms to spawn the second worker, so its timeOrigin should be greater than that of the window.');
40+
}));
41+
}, 200);
42+
}, 'Window and worker timeOrigins differ when worker is created after a delay.');
43+
</script>
44+
</body>
45+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
<title>window.performance.now should not enable timing attacks</title>
6+
<link rel="author" title="W3C" href="http://www.w3.org/" />
7+
<link rel="help" href="http://w3c.github.io/hr-time/#privacy-security"/>
8+
<script src="/resources/testharness.js"></script>
9+
<script src="/resources/testharnessreport.js"></script>
10+
<script>
11+
test(function() {
12+
function check_resolutions(times, length) {
13+
var end = length - 2;
14+
15+
// we compare each value with the following ones
16+
for (var i = 0; i < end; i++) {
17+
var h1 = times[i];
18+
for (var j = i+1; j < end; j++) {
19+
var h2 = times[j];
20+
var diff = h2 - h1;
21+
assert_true((diff === 0) || ((diff * 1000) >= 5),
22+
"Differences smaller than 5 microseconds: " + diff);
23+
}
24+
}
25+
return true;
26+
}
27+
28+
var times = new Array(10);
29+
var index = 0;
30+
var hrt1, hrt2, hrt;
31+
32+
// rapid firing of performance.now
33+
hrt1 = performance.now();
34+
hrt2 = performance.now();
35+
times[index++] = hrt1;
36+
times[index++] = hrt2;
37+
38+
// ensure that we get performance.now() to return a different value
39+
do {
40+
hrt = performance.now();
41+
times[index++] = hrt;
42+
} while ((hrt - hrt1) === 0);
43+
44+
assert_true(check_resolutions(times, index), 'Difference should be at least 5 microseconds.');
45+
}, 'The recommended minimum resolution of the Performance interface has been set to 5 microseconds');
46+
</script>
47+
</head>
48+
<body>
49+
<h1>Description</h1>
50+
<p>The recommended minimum resolution of the Performance interface should be set to 5 microseconds.</p>
51+
52+
<div id="log"></div>
53+
54+
</body>
55+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>time origin value manual test</title>
5+
<link rel="help" href="https://w3c.github.io/hr-time/#time-origin-1">
6+
<link rel="prefetch" href="./resources/unload-a.html">
7+
<link rel="prefetch" href="./resources/unload-b.html">
8+
<link rel="prefetch" href="./resources/unload-c.html">
9+
</head>
10+
<body>
11+
<script src="/resources/testharness.js"></script>
12+
<script src="/resources/testharnessreport.js"></script>
13+
<script>
14+
setup({ explicit_timeout: true });
15+
16+
const ACCEPTABLE_VARIANCE = 400; // ms
17+
18+
const isRoughlyEqual = (a, b) => Math.abs(a - b) < ACCEPTABLE_VARIANCE;
19+
20+
const timings = { a: {}, b: {}, c: {} };
21+
const t = async_test("hr-time time origin");
22+
23+
window.mark = msg => {
24+
timings[msg.docName][msg.lifecycleEventName] = {
25+
performanceNow: msg.performanceNow,
26+
dateNow: msg.dateNow
27+
};
28+
29+
if (msg.docName === "c" && msg.lifecycleEventName === "unload") {
30+
setTimeout(makeAssertions, 0);
31+
}
32+
};
33+
34+
function makeAssertions () {
35+
t.step(() => {
36+
const loadTimeBetweenAandB = timings.b.load.dateNow - timings.a.unload.dateNow;
37+
const loadTimeBetweenBandC = timings.c.load.dateNow - timings.b.unload.dateNow;
38+
39+
assert_true(
40+
isRoughlyEqual(loadTimeBetweenAandB, timings.b.load.performanceNow),
41+
"Document in reused window's time origin should be time of close of pop-up box."
42+
);
43+
assert_true(
44+
isRoughlyEqual(loadTimeBetweenBandC, timings.c.load.performanceNow),
45+
"Document in reused window's time origin should be time of close of pop-up box."
46+
);
47+
assert_true(
48+
!isRoughlyEqual(timings.a.unload.performanceNow, 0),
49+
"Time origin during unload event should match that of rest of document."
50+
);
51+
assert_true(
52+
!isRoughlyEqual(timings.b.unload.performanceNow, 0),
53+
"Time origin during unload event should match that of rest of document."
54+
);
55+
assert_true(
56+
!isRoughlyEqual(timings.c.unload.performanceNow, 0),
57+
"Time origin during unload event should match that of rest of document."
58+
);
59+
});
60+
t.done();
61+
}
62+
</script>
63+
64+
<h2>Description</h2>
65+
<p>This test validates the behavior of <code>performance.now()</code> with respect to its time origin.</p>
66+
<div id="log">
67+
<h2>Manual Test Steps</h2>
68+
<ol>
69+
<li><a href="resources/unload-a.html" target="_blank">Click here</a>
70+
</ol>
71+
</div>
72+
</body>
73+
<html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"use strict"
2+
// https://w3c.github.io/hr-time/#time-origin
3+
4+
async_test(function(test) {
5+
// Cache global time before starting worker
6+
const globalTimeOrigin = performance.timeOrigin;
7+
const globalNowBeforeWorkerStart = performance.now();
8+
9+
// Start worker and retrieve time
10+
const workerScript = "postMessage({timeOrigin: performance.timeOrigin, now: performance.now()})";
11+
const blob = new Blob([workerScript]);
12+
let worker = new Worker(URL.createObjectURL(blob));
13+
14+
worker.addEventListener("message", test.step_func_done(function(event) {
15+
const workerTimeOrigin = event.data.timeOrigin;
16+
const workerNow = event.data.now;
17+
18+
assert_not_equals(workerTimeOrigin, 0, "worker timeOrigin must not be 0");
19+
assert_not_equals(performance.timeOrigin, 0, "Document timeOrigin must not be 0");
20+
21+
assert_equals(globalTimeOrigin, performance.timeOrigin, "timeOrigin should not be changed in same document mode");
22+
assert_less_than(globalTimeOrigin, workerTimeOrigin, "Document timeOrigin must be earlier than worker timeOrigin");
23+
24+
// Document and worker's now() start from their respective timeOrigins.
25+
const timeDiff = workerTimeOrigin - globalTimeOrigin; // convert worker's time to Document time.
26+
assert_less_than(globalTimeOrigin + globalNowBeforeWorkerStart, globalTimeOrigin + timeDiff + workerNow, "Document old now is earlier than worker now.");
27+
28+
// Comparing timing between Document and worker threads could be delicate as it relies on the thread implementation and could be subject to race conditions.
29+
}));
30+
}, 'timeOrigin and now() should be correctly ordered between window and worker');

‎test/fixtures/wpt/versions.json

+4
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,9 @@
2626
"html/webappapis/timers": {
2727
"commit": "ddfe9c089bab565a9d3aa37bdef63d8012c1a94c",
2828
"path": "html/webappapis/timers"
29+
},
30+
"hr-time": {
31+
"commit": "a5d1774ecf41751d1c9357c27c709ee33bf3e279",
32+
"path": "hr-time"
2933
}
3034
}

‎test/wpt/status/hr-time.json

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

‎test/wpt/test-hr-time.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'use strict';
2+
3+
// Flags: --expose-internals
4+
5+
require('../common');
6+
const { WPTRunner } = require('../common/wpt');
7+
const { performance, PerformanceObserver } = require('perf_hooks');
8+
9+
const runner = new WPTRunner('hr-time');
10+
11+
runner.copyGlobalsFromObject(global, [
12+
'setInterval',
13+
'clearInterval',
14+
'setTimeout',
15+
'clearTimeout'
16+
]);
17+
18+
runner.defineGlobal('performance', {
19+
get() {
20+
return performance;
21+
}
22+
});
23+
runner.defineGlobal('PerformanceObserver', {
24+
value: PerformanceObserver
25+
});
26+
27+
runner.runJsTests();

0 commit comments

Comments
 (0)
Please sign in to comment.