chrome-devtools-frontend
Version:
Chrome DevTools UI
174 lines (148 loc) • 8.32 kB
text/typescript
// 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 * as Trace from '../../models/trace/trace.js';
import {describeWithEnvironment} from '../../testing/EnvironmentHelpers.js';
import {TraceLoader} from '../../testing/TraceLoader.js';
import * as Timeline from './timeline.js';
describeWithEnvironment('Initiators', () => {
describe('initiator-initiated event relationships', () => {
let requestIdleCallbackCall: Trace.Types.Events.SyntheticProfileCall;
let functionCallByrequestIdleCallback: Trace.Types.Events.Event;
let setTimeoutCall: Trace.Types.Events.SyntheticProfileCall;
let functionCallBySetTimeout: Trace.Types.Events.Event;
let rAFCall: Trace.Types.Events.SyntheticProfileCall;
let functionCallByRAF: Trace.Types.Events.Event;
let parsedTrace: Trace.Handlers.Types.ParsedTrace;
beforeEach(async function() {
parsedTrace = (await TraceLoader.traceEngine(this, 'async-js-calls.json.gz')).parsedTrace;
setTimeoutCall =
parsedTrace.Renderer.allTraceEntries
.filter(e => Trace.Types.Events.isProfileCall(e) && e.callFrame.functionName === 'setTimeout')
.at(-1) as Trace.Types.Events.SyntheticProfileCall;
assert.exists(setTimeoutCall);
assert.isTrue(Trace.Types.Events.isProfileCall(setTimeoutCall));
functionCallBySetTimeout =
parsedTrace.Renderer.allTraceEntries.find(
e => Trace.Types.Events.isFunctionCall(e) && e.ts > setTimeoutCall.ts) as Trace.Types.Events.Event;
assert.exists(functionCallBySetTimeout);
rAFCall =
parsedTrace.Renderer.allTraceEntries
.filter(e => Trace.Types.Events.isProfileCall(e) && e.callFrame.functionName === 'requestAnimationFrame')
.at(-1) as Trace.Types.Events.SyntheticProfileCall;
assert.exists(rAFCall);
assert.isTrue(Trace.Types.Events.isProfileCall(rAFCall));
functionCallByRAF =
parsedTrace.Renderer.allTraceEntries.find(e => Trace.Types.Events.isFunctionCall(e) && e.ts > rAFCall.ts) as
Trace.Types.Events.Event;
assert.exists(functionCallByRAF);
requestIdleCallbackCall =
parsedTrace.Renderer.allTraceEntries
.filter(e => Trace.Types.Events.isProfileCall(e) && e.callFrame.functionName === 'requestIdleCallback')
.at(-1) as Trace.Types.Events.SyntheticProfileCall;
assert.exists(requestIdleCallbackCall);
assert.isTrue(Trace.Types.Events.isProfileCall(requestIdleCallbackCall));
functionCallByrequestIdleCallback = parsedTrace.Renderer.allTraceEntries.find(
e => Trace.Types.Events.isFunctionCall(e) &&
e.ts > requestIdleCallbackCall.ts) as Trace.Types.Events.Event;
assert.exists(functionCallByrequestIdleCallback);
});
it('returns the initiator data', async function() {
const initiatorData = Timeline.Initiators.initiatorsDataToDraw(parsedTrace, functionCallBySetTimeout, [], []);
assert.deepEqual(initiatorData[0], {
event: functionCallBySetTimeout,
initiator: setTimeoutCall,
});
});
it('can walk up the tree to find the first parent with an initiator', async function() {
// Find any of the bar() calls; they have a parent event
// (FunctionCall) that has an initiator.
const barCall = parsedTrace.Renderer.allTraceEntries.find(
e => Trace.Types.Events.isProfileCall(e) && e.callFrame.functionName === 'bar');
assert.exists(barCall);
// Find the initator data but starting at the fibonacci()
// call.
const initiatorsData = Timeline.Initiators.initiatorsDataToDraw(parsedTrace, barCall, [], []);
assert.deepEqual(initiatorsData[0], {
event: functionCallBySetTimeout,
initiator: setTimeoutCall,
});
});
it('will walk back through the initiators to find the entire chain', async function() {
// Find any of the baz() calls; they have a parent event
// (FunctionCall) that has an initiator.
const bazCall = parsedTrace.Renderer.allTraceEntries.find(
e => Trace.Types.Events.isProfileCall(e) && e.callFrame.functionName === 'baz');
assert.exists(bazCall);
// Find the initators data but starting at the baz()
// call. We expect to find 3 initiatorData objects here:
// 1. baz() ===> FunctionCall caused by requestIdleCallback
// 2. The requestIdleCallback from (1), caused by a prior setTimeout.
// 3. The setTimeout from (2), caused by a prior requestAnimationFrame.
const initiatorsData = Timeline.Initiators.initiatorsDataToDraw(parsedTrace, bazCall, [], []);
assert.deepEqual(initiatorsData, [
{
event: functionCallByrequestIdleCallback,
initiator: requestIdleCallbackCall,
},
{
event: functionCallBySetTimeout,
initiator: setTimeoutCall,
},
{
event: functionCallByRAF,
initiator: rAFCall,
},
]);
});
it('will walk forward to find the events initiated by the selected entry', async function() {
const initatorsData = Timeline.Initiators.initiatorsDataToDraw(parsedTrace, rAFCall, [], []);
assert.lengthOf(initatorsData, 1);
assert.strictEqual(initatorsData[0].event, functionCallByRAF);
assert.strictEqual(initatorsData[0].initiator, rAFCall);
});
it('will return the closest expandable ancestor as an initiator in a pair if the initiator itself is hidden',
async function() {
// Get the parent of rAF to add to the expandable events array.
// When we add rAF to hidden entries list, it will be the
// closest expandable parent and the initiator should point to it.
const rAFParent = parsedTrace.Renderer.entryToNode.get(rAFCall)?.parent;
assert.exists(rAFParent);
// Find the initatorData objects starting at the rAF
// call. We expect to find one initatorData here:
// rAF callback initiated by rAF -> Parent of rAF because rAF is hidden
const initiatorsData =
Timeline.Initiators.initiatorsDataToDraw(parsedTrace, rAFCall, [rAFCall], [rAFParent?.entry]);
assert.lengthOf(initiatorsData, 1);
assert.strictEqual(initiatorsData[0].event, functionCallByRAF);
assert.strictEqual(initiatorsData[0].initiator, rAFParent.entry);
// Ensure the expandable entry is marked as hidden
assert.isTrue(initiatorsData[0].isInitiatorHidden);
});
it('will return the closest expandable ancestor as an initiated event in a pair if the event itself is hidden',
async function() {
const functionCallByRAFParent = parsedTrace.Renderer.entryToNode.get(functionCallByRAF)?.parent;
assert.exists(functionCallByRAFParent);
const initiatorsData = Timeline.Initiators.initiatorsDataToDraw(
parsedTrace, rAFCall, [functionCallByRAF], [functionCallByRAFParent?.entry]);
assert.lengthOf(initiatorsData, 1);
assert.strictEqual(initiatorsData[0].event, functionCallByRAFParent?.entry);
assert.strictEqual(initiatorsData[0].initiator, rAFCall);
// Ensure the expandable entry is marked as hidden
assert.isTrue(initiatorsData[0].isEntryHidden);
});
});
describe('Network Requests', function() {
it('returns the initiator data for network requests', async function() {
const {parsedTrace} = await TraceLoader.traceEngine(this, 'network-requests-initiators.json.gz');
// Find the network request to test, it is initiated by `youtube.com`.
const event = parsedTrace.NetworkRequests.byTime.find(event => event.ts === 1491680762420);
assert.exists(event);
// Find the `youtube.com` network request.
const initiator = parsedTrace.NetworkRequests.byTime.find(event => event.ts === 1491680629144);
assert.exists(initiator);
const initiatorData = Timeline.Initiators.initiatorsDataToDrawForNetwork(parsedTrace, event);
assert.deepEqual(initiatorData, [{event, initiator}]);
});
});
});