UNPKG

chrome-devtools-frontend

Version:
263 lines (229 loc) • 13.4 kB
// Copyright 2023 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import {describeWithEnvironment} from '../../../testing/EnvironmentHelpers.js'; import {TraceLoader} from '../../../testing/TraceLoader.js'; import * as Trace from '../trace.js'; describeWithEnvironment('InitiatorsHandler', () => { beforeEach(() => { Trace.Handlers.ModelHandlers.Initiators.reset(); }); it('for an UpdateLayoutTree event it sets the initiator to the previous ScheduledStyleRecalculation event', async function() { const traceEvents = await TraceLoader.rawEvents(this, 'web-dev-with-commit.json.gz'); for (const event of traceEvents) { Trace.Handlers.ModelHandlers.Initiators.handleEvent(event); } await Trace.Handlers.ModelHandlers.Initiators.finalize(); const data = Trace.Handlers.ModelHandlers.Initiators.data(); const updateLayoutTreeEvent = traceEvents.find(event => { return Trace.Types.Events.isUpdateLayoutTree(event) && event.ts === 122411039965; }); if (!updateLayoutTreeEvent || !Trace.Types.Events.isUpdateLayoutTree(updateLayoutTreeEvent)) { throw new Error('Could not find layout tree event.'); } const initiator = data.eventToInitiator.get(updateLayoutTreeEvent); if (!initiator) { throw new Error('Did not find expected initiator for updateLayoutTreeEvent'); } assert.isTrue(Trace.Types.Events.isScheduleStyleRecalculation(initiator)); assert.strictEqual(updateLayoutTreeEvent.args.beginData?.frame, '25D2F12F1818C70B5BD4325CC9ACD8FF'); assert.strictEqual(updateLayoutTreeEvent.args.beginData?.frame, initiator.args?.data?.frame); }); it('for a Layout event it sets the initiator to the last InvalidateLayout event on that frame', async function() { const traceEvents = await TraceLoader.rawEvents(this, 'web-dev-with-commit.json.gz'); for (const event of traceEvents) { Trace.Handlers.ModelHandlers.Initiators.handleEvent(event); } await Trace.Handlers.ModelHandlers.Initiators.finalize(); const data = Trace.Handlers.ModelHandlers.Initiators.data(); const layoutEvent = traceEvents.find(event => { return Trace.Types.Events.isLayout(event) && event.ts === 122411039994; }); if (!layoutEvent || !Trace.Types.Events.isLayout(layoutEvent)) { throw new Error('Could not find layout event.'); } const initiator = data.eventToInitiator.get(layoutEvent); if (!initiator) { throw new Error('Did not find expected initiator for LayoutEvent'); } assert.isTrue(Trace.Types.Events.isInvalidateLayout(initiator)); assert.strictEqual(initiator.ts, 122411036517); }); it('for a Layout event it sets the initiator to the last ScheduledStyleRecalculation if it occurred before the InvalidateLayout event', async function() { const traceEvents = await TraceLoader.rawEvents(this, 'web-dev-with-commit.json.gz'); for (const event of traceEvents) { Trace.Handlers.ModelHandlers.Initiators.handleEvent(event); } await Trace.Handlers.ModelHandlers.Initiators.finalize(); const data = Trace.Handlers.ModelHandlers.Initiators.data(); const layoutEvent = traceEvents.find(event => { return Trace.Types.Events.isLayout(event) && event.ts === 122411054960; }); if (!layoutEvent || !Trace.Types.Events.isLayout(layoutEvent)) { throw new Error('Could not find layout event.'); } const initiator = data.eventToInitiator.get(layoutEvent); if (!initiator) { throw new Error('Did not find expected initiator for LayoutEvent'); } assert.isTrue(Trace.Types.Events.isScheduleStyleRecalculation(initiator)); assert.strictEqual(initiator.ts, 122411054482); }); it('sets an initiator relationship between a requestAnimationFrame and the scheduled FunctionCall', async function() { const {parsedTrace} = await TraceLoader.traceEngine(this, 'async-js-calls.json.gz'); const requestAnimationFrameCall = parsedTrace.Renderer.allTraceEntries.find( e => Trace.Types.Events.isProfileCall(e) && e.callFrame.functionName === 'requestAnimationFrame'); if (!requestAnimationFrameCall) { throw new Error('Could not find requestAnimationFrame call'); } const functionCallEvent = parsedTrace.Renderer.allTraceEntries.find( e => Trace.Types.Events.isFunctionCall(e) && e.ts > requestAnimationFrameCall.ts); if (!functionCallEvent) { throw new Error('Could not find FunctionCall event'); } assert.strictEqual(parsedTrace.Initiators.eventToInitiator.get(functionCallEvent), requestAnimationFrameCall); assert.deepEqual(parsedTrace.Initiators.initiatorToEvents.get(requestAnimationFrameCall), [functionCallEvent]); }); it('sets an initiator relationship between a setTimeout and the scheduled FunctionCall', async function() { const {parsedTrace} = await TraceLoader.traceEngine(this, 'async-js-calls.json.gz'); const setTimeoutCall = parsedTrace.Renderer.allTraceEntries .filter(e => Trace.Types.Events.isProfileCall(e) && e.callFrame.functionName === 'setTimeout') .at(-1); if (!setTimeoutCall) { throw new Error('Could not find setTimeout call'); } const functionCallEvent = parsedTrace.Renderer.allTraceEntries.find( e => Trace.Types.Events.isFunctionCall(e) && e.ts > setTimeoutCall.ts); if (!functionCallEvent) { throw new Error('Could not find FunctionCall event'); } assert.strictEqual(parsedTrace.Initiators.eventToInitiator.get(functionCallEvent), setTimeoutCall); assert.deepEqual(parsedTrace.Initiators.initiatorToEvents.get(setTimeoutCall), [functionCallEvent]); }); it('sets an initiator relationship between a requestIdleCallback and the scheduled FunctionCall', async function() { const {parsedTrace} = await TraceLoader.traceEngine(this, 'async-js-calls.json.gz'); const requestIdleCallback = parsedTrace.Renderer.allTraceEntries.find( e => Trace.Types.Events.isProfileCall(e) && e.callFrame.functionName === 'requestIdleCallback'); if (!requestIdleCallback) { throw new Error('Could not find requestIdleCallback call'); } const functionCallEvent = parsedTrace.Renderer.allTraceEntries.find( e => Trace.Types.Events.isFunctionCall(e) && e.ts > requestIdleCallback.ts); if (!functionCallEvent) { throw new Error('Could not find FunctionCall event'); } assert.strictEqual(parsedTrace.Initiators.eventToInitiator.get(functionCallEvent), requestIdleCallback); assert.deepEqual(parsedTrace.Initiators.initiatorToEvents.get(requestIdleCallback), [functionCallEvent]); }); it('sets an initiator relationship between a console.createTask and the scheduled task.run', async function() { const {parsedTrace} = await TraceLoader.traceEngine(this, 'async-js-calls.json.gz'); const schedulerFuntion = parsedTrace.Renderer.allTraceEntries.find( e => Trace.Types.Events.isProfileCall(e) && e.callFrame.functionName === 'startExample'); if (!schedulerFuntion) { throw new Error('Could not find scheduler function call'); } const consoleRunTask = parsedTrace.Renderer.allTraceEntries.find( e => Trace.Types.Events.isConsoleRunTask(e) && e.ts > schedulerFuntion.ts); assert.exists(consoleRunTask); assert.strictEqual(parsedTrace.Initiators.eventToInitiator.get(consoleRunTask), schedulerFuntion); assert.deepEqual(parsedTrace.Initiators.initiatorToEvents.get(schedulerFuntion), [consoleRunTask]); }); it('for a WebSocketSendHandshakeRequest the initiator is the WebSocketCreate event', async function() { const traceEvents = await TraceLoader.rawEvents(this, 'web-sockets.json.gz'); for (const event of traceEvents) { Trace.Handlers.ModelHandlers.Initiators.handleEvent(event); } await Trace.Handlers.ModelHandlers.Initiators.finalize(); const data = Trace.Handlers.ModelHandlers.Initiators.data(); const webSocketCreateEvent = traceEvents.find(Trace.Types.Events.isWebSocketCreate); if (!webSocketCreateEvent) { throw new Error('Could not fnd WebSocketCreateEvent'); } const webSocketSendHandshakeRequestEvent = traceEvents.find(Trace.Types.Events.isWebSocketSendHandshakeRequest); if (!webSocketSendHandshakeRequestEvent) { throw new Error('Could not find WebSocketSendHandshakeRequest'); } assert.strictEqual(data.eventToInitiator.get(webSocketSendHandshakeRequestEvent), webSocketCreateEvent); }); it('for a WebSocketReceiveHandshakeResponse the initiator is the WebSocketCreate event', async function() { const traceEvents = await TraceLoader.rawEvents(this, 'web-sockets.json.gz'); for (const event of traceEvents) { Trace.Handlers.ModelHandlers.Initiators.handleEvent(event); } await Trace.Handlers.ModelHandlers.Initiators.finalize(); const data = Trace.Handlers.ModelHandlers.Initiators.data(); const webSocketCreateEvent = traceEvents.find(Trace.Types.Events.isWebSocketCreate); if (!webSocketCreateEvent) { throw new Error('Could not fnd WebSocketCreateEvent'); } const webSocketReceieveHandshakeResponseEvent = traceEvents.find(Trace.Types.Events.isWebSocketReceiveHandshakeResponse); if (!webSocketReceieveHandshakeResponseEvent) { throw new Error('Could not find WebSocketReceiveHandshakeResponse event'); } const webSocketSendHandshakeRequestEvent = traceEvents.find(Trace.Types.Events.isWebSocketSendHandshakeRequest); if (!webSocketSendHandshakeRequestEvent) { throw new Error('Could not find WebSocketSendHandshakeRequest'); } assert.strictEqual(data.eventToInitiator.get(webSocketReceieveHandshakeResponseEvent), webSocketCreateEvent); assert.deepEqual( data.initiatorToEvents.get(webSocketCreateEvent), [webSocketSendHandshakeRequestEvent, webSocketReceieveHandshakeResponseEvent]); }); it('for a PostMessage Handler event the initiator is the PostMessage Dispatch event', async function() { const traceEvents = await TraceLoader.rawEvents(this, 'postmessage-initiators.json.gz'); for (const event of traceEvents) { Trace.Handlers.ModelHandlers.Flows.handleEvent(event); Trace.Handlers.ModelHandlers.Initiators.handleEvent(event); } await Trace.Handlers.ModelHandlers.Flows.finalize(); await Trace.Handlers.ModelHandlers.Initiators.finalize(); const data = Trace.Handlers.ModelHandlers.Initiators.data(); const schedulePostMessageEvent = traceEvents.find(Trace.Types.Events.isSchedulePostMessage); if (!schedulePostMessageEvent) { throw new Error('Could not find schedulePostMessageEvent event'); } const handlePostMessageEvent = traceEvents.find(Trace.Types.Events.isHandlePostMessage); if (!handlePostMessageEvent) { throw new Error('Could not find handlePostMessageEvent event'); } assert.strictEqual(data.eventToInitiator.get(handlePostMessageEvent), schedulePostMessageEvent); assert.deepEqual(data.initiatorToEvents.get(schedulePostMessageEvent), [handlePostMessageEvent]); }); it('pairs the postTask-scheduled tasks with their scheduling initiators', async function() { const traceEvents = await TraceLoader.rawEvents(this, 'scheduler-post-task.json.gz'); for (const event of traceEvents) { Trace.Handlers.ModelHandlers.Initiators.handleEvent(event); } await Trace.Handlers.ModelHandlers.Initiators.finalize(); const data = Trace.Handlers.ModelHandlers.Initiators.data(); const scheduleEvents = traceEvents.filter(Trace.Types.Events.isSchedulePostTaskCallback); assert.isNotEmpty(scheduleEvents, 'Could not find SchedulePostTaskCallback events'); const runEvents = traceEvents.filter(Trace.Types.Events.isRunPostTaskCallback); assert.isNotEmpty(runEvents, 'Could not find RunPostTaskCallback events'); const cancelEvents = traceEvents.filter(Trace.Types.Events.isAbortPostTaskCallback); assert.isNotEmpty(cancelEvents, 'Could not find AbortPostTaskCallback events'); assert.containsAllKeys(data.initiatorToEvents, scheduleEvents, 'Not all schedule events in initiators'); // All end events have a SchedulePostTaskCallback initiator. for (const endEvent of [...runEvents, ...cancelEvents]) { const initiator = data.eventToInitiator.get(endEvent); assert.exists(initiator); assert(Trace.Types.Events.isSchedulePostTaskCallback(initiator)); assert.strictEqual(endEvent.args.data.taskId, initiator.args.data.taskId); assert(data.initiatorToEvents.get(initiator)?.includes(endEvent)); } // There is one task that cancels itself while running, so it has both run and cancel events. const doubleEvents = scheduleEvents.some(scheduleEvent => { const endEvents = data.initiatorToEvents.get(scheduleEvent); if (!endEvents || endEvents.length < 2) { return false; } return endEvents.some(Trace.Types.Events.isRunPostTaskCallback) && endEvents.some(Trace.Types.Events.isAbortPostTaskCallback); }); assert(doubleEvents, 'initiator with both run and cancel initiated events not found'); }); });