diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/js-self-profiling | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/js-self-profiling')
28 files changed, 717 insertions, 0 deletions
diff --git a/testing/web-platform/tests/js-self-profiling/META.yml b/testing/web-platform/tests/js-self-profiling/META.yml new file mode 100644 index 0000000000..84e5177d5e --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/META.yml @@ -0,0 +1 @@ +spec: https://wicg.github.io/js-self-profiling/ diff --git a/testing/web-platform/tests/js-self-profiling/__dir__.headers b/testing/web-platform/tests/js-self-profiling/__dir__.headers new file mode 100644 index 0000000000..93537b44de --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/__dir__.headers @@ -0,0 +1 @@ +Document-Policy: js-profiling diff --git a/testing/web-platform/tests/js-self-profiling/class-getter-names.https.html b/testing/web-platform/tests/js-self-profiling/class-getter-names.https.html new file mode 100644 index 0000000000..46e2ed2748 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/class-getter-names.https.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html> + +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + + <script src="resources/profile-utils.js"></script> +</head> + +<body> + <script> + // Getter methods should use `get ${label}` as the function/frame name. Source: + // https://www.ecma-international.org/ecma-262/#sec-method-definitions-runtime-semantics-propertydefinitionevaluation + promise_test(t => ProfileUtils.testFunction(sample => { + class SomeClass { + get someValue() { + sample(); + } + } + let instance = new SomeClass(); + instance.someValue; + }, { + name: 'get someValue', + } + ), 'class getter names are logged correctly'); + </script> +</body> + +</html> diff --git a/testing/web-platform/tests/js-self-profiling/class-names.https.html b/testing/web-platform/tests/js-self-profiling/class-names.https.html new file mode 100644 index 0000000000..9d201b03a1 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/class-names.https.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html> + +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + + <script src="resources/profile-utils.js"></script> +</head> + +<body> + <script> + // Methods should use their label as the function/frame name. Source: + // https://www.ecma-international.org/ecma-262/#sec-method-definitions-runtime-semantics-propertydefinitionevaluation + promise_test(async t => { + class SomeClass { + method(sample) { + sample(); + } + } + let instance = new SomeClass(); + + await ProfileUtils.testFunction(instance.method.bind(instance), { + name: 'method', + }); + }, 'class method names are logged correctly'); + </script> +</body> + +</html> diff --git a/testing/web-platform/tests/js-self-profiling/class-setter-names.https.html b/testing/web-platform/tests/js-self-profiling/class-setter-names.https.html new file mode 100644 index 0000000000..f03a372460 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/class-setter-names.https.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html> + +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + + <script src="resources/profile-utils.js"></script> +</head> + +<body> + <script> + // Setter methods should use `set ${label}` as the function/frame name. Source: + // https://www.ecma-international.org/ecma-262/#sec-method-definitions-runtime-semantics-propertydefinitionevaluation + promise_test(t => ProfileUtils.testFunction(sample => { + class SomeClass { + set someValue(_) { + sample(); + } + } + let instance = new SomeClass(); + instance.someValue = 5; + }, { + name: 'set someValue', + } + ), 'class setter names are logged correctly'); + </script> +</body> + +</html> diff --git a/testing/web-platform/tests/js-self-profiling/concurrent-profilers.https.html b/testing/web-platform/tests/js-self-profiling/concurrent-profilers.https.html new file mode 100644 index 0000000000..26f37ec8bd --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/concurrent-profilers.https.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> + <script> + promise_test(async t => { + const profiler_a = new Profiler({ + sampleInterval: 10, + maxBufferSize: Number.MAX_SAFE_INTEGER, + }); + + const profiler_b = new Profiler({ + sampleInterval: 10, + maxBufferSize: Number.MAX_SAFE_INTEGER, + }); + + const trace_b = await profiler_b.stop(); + const trace_a = await profiler_a.stop(); + }, 'concurrent profilers should be supported'); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/js-self-profiling/cross-origin-script-cors.sub.html b/testing/web-platform/tests/js-self-profiling/cross-origin-script-cors.sub.html new file mode 100644 index 0000000000..f2b2c8d14e --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/cross-origin-script-cors.sub.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + + <script src="resources/profile-utils.js"></script> + + <script crossorigin src="https://{{hosts[alt][]}}:{{ports[https][0]}}/js-self-profiling/resources/profiling-script.js"></script> +</head> +<body> + <script> + promise_test(async t => { + const trace = await ProfilingScript.profileBuiltinsInNewTask(); + + // Ensure that signal from the external script was gathered. + assert_equals(trace.resources.length, 1); + assert_equals(trace.resources[0], 'https://{{hosts[alt][]}}:{{ports[https][0]}}/js-self-profiling/resources/profiling-script.js'); + assert_greater_than(trace.frames.length, 0); + assert_greater_than(trace.stacks.length, 0); + assert_greater_than(trace.samples.length, 0); + }, 'cors cross-origin script execution is not observable'); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/js-self-profiling/cross-origin-script-no-cors.sub.html b/testing/web-platform/tests/js-self-profiling/cross-origin-script-no-cors.sub.html new file mode 100644 index 0000000000..ea6fd3384e --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/cross-origin-script-no-cors.sub.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + + <script src="resources/profile-utils.js"></script> + + <script src="https://{{hosts[alt][]}}:{{ports[https][0]}}/js-self-profiling/resources/profiling-script.js"></script> +</head> +<body> + <script> + promise_test(async t => { + const trace = await ProfilingScript.profileBuiltinsInNewTask(); + + // Ensure that no signal from the external script was gathered. + assert_equals(trace.resources.length, 0); + assert_equals(trace.frames.length, 0); + assert_equals(trace.stacks.length, 0); + assert_greater_than(trace.samples.length, 0); + }, 'no-cors cross-origin script execution is not observable'); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/js-self-profiling/external-script.https.html b/testing/web-platform/tests/js-self-profiling/external-script.https.html new file mode 100644 index 0000000000..9f9438ba6d --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/external-script.https.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + + <script src="resources/profile-utils.js"></script> + + <script id="external-script" src="resources/external-script.js"></script> +</head> +<body> + <script> + promise_test(async t => { + const trace = await ProfileUtils.profileFunction(function trampoline(sample) { + externalScriptFunction(sample); + }); + + const scriptUrl = document.querySelector('#external-script').src; + assert_true(ProfileUtils.containsResource(trace, scriptUrl), + 'external resource is included'); + + const expectedTrampolineFrame = { + name: 'trampoline', + resourceId: trace.resources.indexOf( + location.href, + ), + }; + const expectedExternalFrame = { + name: 'externalScriptFunction', + resourceId: trace.resources.indexOf(scriptUrl), + line: EXTERNAL_SCRIPT_FUNCTION_LINE, + column: EXTERNAL_SCRIPT_FUNCTION_COLUMN, + }; + + assert_true(ProfileUtils.containsFrame(trace, expectedTrampolineFrame), + 'trampoline function included'); + + assert_true(ProfileUtils.containsFrame(trace, expectedExternalFrame), + 'external script function included'); + + assert_true(ProfileUtils.containsSubstack(trace, [ + externalScriptFunction, + expectedTrampolineFrame, + ]), + 'stack exists with external script function'); + + }, 'external script function details'); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/js-self-profiling/function-anonymous-names.https.html b/testing/web-platform/tests/js-self-profiling/function-anonymous-names.https.html new file mode 100644 index 0000000000..fd7fbecc50 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/function-anonymous-names.https.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<html> + +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + + <script src="resources/profile-utils.js"></script> +</head> + +<body> + <script> + promise_test(async t => { + const f = function (sample) { + sample(); + }; + await ProfileUtils.testFunction(f, { + name: '', + }); + }, 'anonymous function expression names are logged correctly'); + </script> +</body> + +</html> diff --git a/testing/web-platform/tests/js-self-profiling/function-declaration-names.https.html b/testing/web-platform/tests/js-self-profiling/function-declaration-names.https.html new file mode 100644 index 0000000000..9cb02cbfc0 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/function-declaration-names.https.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<html> + +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + + <script src="resources/profile-utils.js"></script> +</head> + +<body> + <script> + promise_test(async t => { + function namedFunctionDeclaration(sample) { + sample(); + }; + await ProfileUtils.testFunction(namedFunctionDeclaration, { + name: 'namedFunctionDeclaration', + }); + }, 'function declaration names are logged correctly'); + </script> +</body> + +</html> diff --git a/testing/web-platform/tests/js-self-profiling/function-expression-names.https.html b/testing/web-platform/tests/js-self-profiling/function-expression-names.https.html new file mode 100644 index 0000000000..402797402e --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/function-expression-names.https.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<html> + +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + + <script src="resources/profile-utils.js"></script> +</head> + +<body> + <script> + promise_test(async t => { + const f = function namedFunctionExpression(sample) { + sample(); + }; + await ProfileUtils.testFunction(f, { + name: 'namedFunctionExpression', + }); + }, 'function expression names are logged correctly'); + </script> +</body> + +</html> diff --git a/testing/web-platform/tests/js-self-profiling/idlharness.https.html b/testing/web-platform/tests/js-self-profiling/idlharness.https.html new file mode 100644 index 0000000000..3807a6e4fa --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/idlharness.https.html @@ -0,0 +1,25 @@ +<!doctype html> +<meta charset=utf-8> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/WebIDLParser.js"></script> +<script src="/resources/idlharness.js"></script> +<body> + <script> + 'use strict'; + + idl_test( + ['js-self-profiling'], + ['hr-time', 'dom'], + async idl_array => { + idl_array.add_objects({ + Profiler: ['profiler'], + }); + + self.profiler = new Profiler({ + sampleInterval: 1, + maxBufferSize: 1, + }); + } + ); + </script> diff --git a/testing/web-platform/tests/js-self-profiling/iframe-context-filtration.https.html b/testing/web-platform/tests/js-self-profiling/iframe-context-filtration.https.html new file mode 100644 index 0000000000..4d26264211 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/iframe-context-filtration.https.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + + <script src="resources/profile-utils.js"></script> +</head> +<body> + <iframe src="resources/child-frame.html"></iframe> + + <script> + promise_test(_ => new Promise(res => window.addEventListener('load', res)), + 'wait for load event'); + + promise_test(async t => { + const profiler = new Profiler({ + sampleInterval: 10, + maxBufferSize: Number.MAX_SAFE_INTEGER, + }); + + const iframe = frames[0]; + await ProfileUtils.forceSampleFrame(iframe); + + const trace = await profiler.stop(); + + assert_false(ProfileUtils.containsFrame(trace, { name: 'sampleFromMessage' }), + 'function from child context not included in trace'); + + const childUrl = iframe.src; + assert_false(ProfileUtils.containsResource(trace, childUrl), + 'child resources are not included'); + }, 'functions from child frame are not included in profile created by parent frame'); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/js-self-profiling/inline-script.html b/testing/web-platform/tests/js-self-profiling/inline-script.html new file mode 100644 index 0000000000..56a7b55c6b --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/inline-script.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + + <script src="resources/profile-utils.js"></script> +</head> +<body> + <script> + // Note: moving these function definitions will change the expected + // outcomes below. + function nestedInlineScriptFunction(sample) { + sample(); + } + + function inlineScriptFunction(sample) { + nestedInlineScriptFunction(sample); + } + </script> + + <script> + promise_test(async t => { + const trace = await ProfileUtils.profileFunction(inlineScriptFunction); + + assert_true(ProfileUtils.containsResource(trace, location.href), + 'inline script resource is included'); + + assert_true(ProfileUtils.containsSubstack(trace, [ + { + name: 'nestedInlineScriptFunction', + resourceId: trace.resources.indexOf(location.href), + line: 13, + column: 40, + }, + { + name: 'inlineScriptFunction', + resourceId: trace.resources.indexOf(location.href), + line: 17, + column: 34, + }, + ])); + }, 'inline script function details'); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/js-self-profiling/max-buffer-size.window.js b/testing/web-platform/tests/js-self-profiling/max-buffer-size.window.js new file mode 100644 index 0000000000..659d4cd14d --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/max-buffer-size.window.js @@ -0,0 +1,49 @@ +// META: script=resources/profile-utils.js + +promise_test(async t => { + assert_throws_js(TypeError, () => { + new Profiler({ sampleInterval: 10 }); + }); +}, 'max buffer size must be defined'); + +promise_test(async t => { + const profiler = new Profiler({ + sampleInterval: 10, + maxBufferSize: 2, + }); + + // Force 3 samples with a max buffer size of 2. + for (let i = 0; i < 3; i++) { + ProfileUtils.forceSample(); + } + + const trace = await profiler.stop(); + assert_equals(trace.samples.length, 2); +}, 'max buffer size is not exceeded'); + +promise_test(async t => { + const largeBufferProfiler = new Profiler({ sampleInterval: 10, maxBufferSize: Number.MAX_SAFE_INTEGER }); + const smallBufferProfiler = new Profiler({ sampleInterval: 10, maxBufferSize: 1 }); + + const watcher = new EventWatcher(t, smallBufferProfiler, ['samplebufferfull']); + + largeBufferProfiler.addEventListener('samplebufferfull', () => { + assert_unreached('samplebufferfull invoked on wrong profiler'); + largeBufferProfiler.stop(); + smallBufferProfiler.stop(); + }); + + smallBufferProfiler.addEventListener('samplebufferfull', () => { + largeBufferProfiler.stop(); + smallBufferProfiler.stop(); + }); + + // Force two samples to be taken, which should exceed + // |smallBufferProfiler|'s buffer size. + for (let i = 0; i < 2; i++) { + ProfileUtils.forceSample(); + } + + // Ensure that |smallBufferProfiler|'s buffer size is exceeded. + await watcher.wait_for('samplebufferfull'); +}, 'ensure samplebufferfull is fired on full profiler'); diff --git a/testing/web-platform/tests/js-self-profiling/resources/__dir__.headers b/testing/web-platform/tests/js-self-profiling/resources/__dir__.headers new file mode 100644 index 0000000000..93537b44de --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/resources/__dir__.headers @@ -0,0 +1 @@ +Document-Policy: js-profiling diff --git a/testing/web-platform/tests/js-self-profiling/resources/child-frame.html b/testing/web-platform/tests/js-self-profiling/resources/child-frame.html new file mode 100644 index 0000000000..7f8125eae7 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/resources/child-frame.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<html> +<head> +</head> +<body> + <script src="profile-utils.js"></script> +</body> +</html> diff --git a/testing/web-platform/tests/js-self-profiling/resources/external-script.js b/testing/web-platform/tests/js-self-profiling/resources/external-script.js new file mode 100644 index 0000000000..6f6bd42b25 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/resources/external-script.js @@ -0,0 +1,9 @@ +// NOTE: Modifying the location of functions in this file will cause +// `external-script.html` to fail! Please update the following constants +// accordingly. +const EXTERNAL_SCRIPT_FUNCTION_LINE = 7; +const EXTERNAL_SCRIPT_FUNCTION_COLUMN = 32; + +function externalScriptFunction(sample) { + sample(); +} diff --git a/testing/web-platform/tests/js-self-profiling/resources/external-script.js.headers b/testing/web-platform/tests/js-self-profiling/resources/external-script.js.headers new file mode 100644 index 0000000000..cb762eff80 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/resources/external-script.js.headers @@ -0,0 +1 @@ +Access-Control-Allow-Origin: * diff --git a/testing/web-platform/tests/js-self-profiling/resources/profile-utils.js b/testing/web-platform/tests/js-self-profiling/resources/profile-utils.js new file mode 100644 index 0000000000..2c32f34608 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/resources/profile-utils.js @@ -0,0 +1,149 @@ +(function(global) { + const TEST_SAMPLE_INTERVAL = 10; + const ENSURE_SAMPLE_SPIN_WAIT_MS = 500; + + function forceSample() { + // Spin for |TEST_SAMPLE_INTERVAL + 500|ms to ensure that a sample occurs + // before this function returns. As periodic sampling is enforced by a + // SHOULD clause, it is indeed testable. + // + // More reliable sampling will be handled in a future testdriver RFC + // (https://github.com/web-platform-tests/rfcs/pull/81). + for (const deadline = performance.now() + TEST_SAMPLE_INTERVAL + + ENSURE_SAMPLE_SPIN_WAIT_MS; + performance.now() < deadline;) + ; + } + + // Creates a new profile that captures the execution of when the given + // function calls the `sample` function passed to it. + async function profileFunction(func) { + const profiler = new Profiler({ + sampleInterval: TEST_SAMPLE_INTERVAL, + maxBufferSize: Number.MAX_SAFE_INTEGER, + }); + + func(() => forceSample()); + + const trace = await profiler.stop(); + + // Sanity check ensuring that we captured a sample. + assert_greater_than(trace.resources.length, 0); + assert_greater_than(trace.frames.length, 0); + assert_greater_than(trace.stacks.length, 0); + assert_greater_than(trace.samples.length, 0); + + return trace; + } + + async function testFunction(func, frame) { + const trace = await profileFunction(func); + assert_true(containsFrame(trace, frame), 'trace contains frame'); + } + + function substackMatches(trace, stackId, expectedStack) { + if (expectedStack.length === 0) { + return true; + } + if (stackId === undefined) { + return false; + } + + const stackElem = trace.stacks[stackId]; + const expectedFrame = expectedStack[0]; + + if (!frameMatches(trace.frames[stackElem.frameId], expectedFrame)) { + return false; + } + return substackMatches(trace, stackElem.parentId, expectedStack.slice(1)); + } + + // Returns true if the trace contains a frame matching the given specification. + // We define a "match" as follows: a frame A matches an expectation E if (and + // only if) for each field of E, A has the same value. + function containsFrame(trace, expectedFrame) { + return trace.frames.find(frame => { + return frameMatches(frame, expectedFrame); + }) !== undefined; + } + + // Returns true if a trace contains a substack in one of its samples, ordered + // leaf to root. + function containsSubstack(trace, expectedStack) { + return trace.samples.find(sample => { + let stackId = sample.stackId; + while (stackId !== undefined) { + if (substackMatches(trace, stackId, expectedStack)) { + return true; + } + stackId = trace.stacks[stackId].parentId; + } + return false; + }) !== undefined; + } + + function containsResource(trace, expectedResource) { + return trace.resources.includes(expectedResource); + } + + // Returns true if a trace contains a sample matching the given specification. + // We define a "match" as follows: a sample A matches an expectation E if (and + // only if) for each field of E, A has the same value. + function containsSample(trace, expectedSample) { + return trace.samples.find(sample => { + return sampleMatches(sample, expectedSample); + }) !== undefined; + } + + // Compares each set field of `expected` against the given frame `actual`. + function sampleMatches(actual, expected) { + return (expected.timestamp === undefined || + expected.timestamp === actual.timestamp) && + (expected.stackId === undefined || + expected.stackId === actual.stackId) && + (expected.marker === undefined || expected.marker === actual.marker); + } + + // Compares each set field of `expected` against the given frame `actual`. + function frameMatches(actual, expected) { + return (expected.name === undefined || expected.name === actual.name) && + (expected.resourceId === undefined || expected.resourceId === actual.resourceId) && + (expected.line === undefined || expected.line === actual.line) && + (expected.column === undefined || expected.column === actual.column); + } + + function forceSampleFrame(frame) { + const channel = new MessageChannel(); + const replyPromise = new Promise(res => { + channel.port1.onmessage = res; + }); + frame.postMessage('', '*', [channel.port2]); + return replyPromise; + } + + window.addEventListener('message', message => { + // Force sample in response to messages received. + (function sampleFromMessage() { + ProfileUtils.forceSample(); + message.ports[0].postMessage(''); + })(); + }); + + global.ProfileUtils = { + // Capturing + profileFunction, + forceSample, + + // Containment checks + containsFrame, + containsSubstack, + containsResource, + containsSample, + + // Cross-frame sampling + forceSampleFrame, + + // Assertions + testFunction, + }; +})(this); diff --git a/testing/web-platform/tests/js-self-profiling/resources/profiling-script.js b/testing/web-platform/tests/js-self-profiling/resources/profiling-script.js new file mode 100644 index 0000000000..9f09e05b34 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/resources/profiling-script.js @@ -0,0 +1,26 @@ +(function(global) { + let counter = 0; + + // Spins up a new profiler and performs some work in a new top-level task, + // calling some builtins. Returns a promise for the resulting trace. + const profileBuiltinsInNewTask = () => { + // Run profiling logic in a new task to eliminate the caller stack. + return new Promise(resolve => { + setTimeout(async () => { + const profiler = new Profiler({ sampleInterval: 10, maxBufferSize: 10000 }); + for (const deadline = performance.now() + 500; performance.now() < deadline;) { + // Run a range of builtins to ensure they get included in the trace. + // Store this computation in a variable to prevent getting optimized out. + counter += Math.random(); + counter += performance.now(); + } + const trace = await profiler.stop(); + resolve(trace); + }); + }); + } + + global.ProfilingScript = { + profileBuiltinsInNewTask, + } +})(window); diff --git a/testing/web-platform/tests/js-self-profiling/resources/profiling-script.js.headers b/testing/web-platform/tests/js-self-profiling/resources/profiling-script.js.headers new file mode 100644 index 0000000000..cb762eff80 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/resources/profiling-script.js.headers @@ -0,0 +1 @@ +Access-Control-Allow-Origin: * diff --git a/testing/web-platform/tests/js-self-profiling/tentative/README.md b/testing/web-platform/tests/js-self-profiling/tentative/README.md new file mode 100644 index 0000000000..a6b8d008f2 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/tentative/README.md @@ -0,0 +1,3 @@ +Tests in this directory are for the proposed js-self-profiling API marker extension. This is not yet standardised and browsers should not be expected to pass these tests. + +See the explainer at https://github.com/WICG/js-self-profiling/blob/main/markers.md for more information about the API. diff --git a/testing/web-platform/tests/js-self-profiling/tentative/marker-vm-state.https.html b/testing/web-platform/tests/js-self-profiling/tentative/marker-vm-state.https.html new file mode 100644 index 0000000000..07dbe7f0ff --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/tentative/marker-vm-state.https.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html> + +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + + <script src="../resources/profile-utils.js"></script> +</head> + +<body> + + <script> + // Note: moving these function definitions will change the expected + // outcomes below. + function scriptFunction(sample) { + sample(); + } + </script> + + <script> + promise_test(async t => { + // *.headers file should ensure we sesrve COOP and COEP headers. + assert_true(self.crossOriginIsolated, + "Cross origin isolation is required to surface markers"); + const trace = await ProfileUtils.profileFunction(scriptFunction); + + assert_true(ProfileUtils.containsSample(trace, { + stackId: 7, + marker: 'script' + })); + }, 'markers logged correctly'); + + </script> +</body> + +</html> diff --git a/testing/web-platform/tests/js-self-profiling/tentative/marker-vm-state.https.html.headers b/testing/web-platform/tests/js-self-profiling/tentative/marker-vm-state.https.html.headers new file mode 100644 index 0000000000..228047a15a --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/tentative/marker-vm-state.https.html.headers @@ -0,0 +1,3 @@ +Document-Policy: js-profiling +Cross-Origin-Embedder-Policy: require-corp +Cross-Origin-Opener-Policy: same-origin diff --git a/testing/web-platform/tests/js-self-profiling/time-domain.window.js b/testing/web-platform/tests/js-self-profiling/time-domain.window.js new file mode 100644 index 0000000000..5791a3de75 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/time-domain.window.js @@ -0,0 +1,19 @@ +// META: script=resources/profile-utils.js + +promise_test(async () => { + const start = performance.now(); + + const profiler = new Profiler({ + sampleInterval: 10, + maxBufferSize: Number.MAX_SAFE_INTEGER, + }); + ProfileUtils.forceSample(); + const trace = await profiler.stop(); + + const end = performance.now(); + + assert_greater_than(trace.samples.length, 0); + for (const sample of trace.samples) { + assert_between_inclusive(sample.timestamp, start, end); + } +}, 'sample timestamps use the current high-resolution time'); diff --git a/testing/web-platform/tests/js-self-profiling/without-document-policy/disabled.https.html b/testing/web-platform/tests/js-self-profiling/without-document-policy/disabled.https.html new file mode 100644 index 0000000000..bff9851263 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/without-document-policy/disabled.https.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> + <script> + test(t => { + assert_throws_dom('NotAllowedError', () => { + new Profiler({ sampleInterval: 10, maxBufferSize: Number.MAX_SAFE_INTEGER }); + }); + }, 'profiling should throw without passing document policy'); + </script> +</body> +</html> |