From 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:47:29 +0200 Subject: Adding upstream version 115.8.0esr. Signed-off-by: Daniel Baumann --- .../js-self-profiling/resources/profile-utils.js | 149 +++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 testing/web-platform/tests/js-self-profiling/resources/profile-utils.js (limited to 'testing/web-platform/tests/js-self-profiling/resources/profile-utils.js') 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); -- cgit v1.2.3