chrome-devtools-frontend
Version:
Chrome DevTools UI
171 lines (155 loc) • 7.83 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 type * as Protocol from '../../generated/protocol.js';
import * as Trace from '../../models/trace/trace.js';
import {describeWithEnvironment} from '../../testing/EnvironmentHelpers.js';
import {makeInstantEvent} from '../../testing/TraceHelpers.js';
import * as Timeline from './timeline.js';
async function loadWebDevTraceAsFile(): Promise<File> {
const file = new URL('./fixtures/traces/web-dev.json.gz', import.meta.url);
const response = await fetch(file);
const asBlob = await response.blob();
const asFile = new File([asBlob], 'web-dev.json.gz', {
type: 'application/gzip',
});
return asFile;
}
async function loadBasicCpuProfileAsFile(): Promise<File> {
const file = new URL('./fixtures/traces/node-fibonacci-website.cpuprofile.gz', import.meta.url);
const response = await fetch(file);
const asBlob = await response.blob();
const asFile = new File([asBlob], 'node-fibonacci-website.cpuprofile.gz', {
type: 'application/gzip',
});
return asFile;
}
describeWithEnvironment('TimelineLoader', () => {
const loadingStartedSpy = sinon.spy();
const loadingProgressSpy = sinon.spy();
const processingStartedSpy = sinon.spy();
const loadingCompleteSpy = sinon.spy();
const recordingProgressSpy = sinon.spy();
const loadingCompleteForTestSpy = sinon.spy();
const client: Timeline.TimelineController.Client = {
async loadingStarted() {
loadingStartedSpy();
},
async loadingProgress(progress?: number) {
loadingProgressSpy(progress);
},
async processingStarted() {
processingStartedSpy();
},
async loadingComplete(
collectedEvents: Trace.Types.Events.Event[],
exclusiveFilter: Trace.Extras.TraceFilter.TraceFilter|null,
metadata: Trace.Types.File.MetaData,
) {
loadingCompleteSpy(collectedEvents, exclusiveFilter, metadata);
},
recordingProgress: function(usage: number): void {
recordingProgressSpy(usage);
},
loadingCompleteForTest: function(): void {
loadingCompleteForTestSpy();
},
};
beforeEach(() => {
loadingStartedSpy.resetHistory();
loadingProgressSpy.resetHistory();
processingStartedSpy.resetHistory();
loadingCompleteSpy.resetHistory();
recordingProgressSpy.resetHistory();
loadingCompleteForTestSpy.resetHistory();
});
it('can load a saved trace file', async () => {
const file = await loadWebDevTraceAsFile();
const loader = await Timeline.TimelineLoader.TimelineLoader.loadFromFile(file, client);
await loader.traceFinalizedForTest();
sinon.assert.calledOnce(loadingStartedSpy);
// Exact number is deterministic so we can assert, but the fact it was 29
// calls doesn't really matter. We just want to check it got called "a
// bunch of times".
sinon.assert.callCount(loadingProgressSpy, 29);
sinon.assert.calledOnce(processingStartedSpy);
sinon.assert.calledOnce(loadingCompleteSpy);
// Get the arguments of the first (and only) call to the loadingComplete
// function. TS doesn't know what the types are (they are [any, any] by
// default), so we tell it that they align with the types of the
// loadingComplete parameters.
const [collectedEvents, exclusiveFilter, metadata] =
loadingCompleteSpy.args[0] as Parameters<Timeline.TimelineController.Client['loadingComplete']>;
assert.isNull(exclusiveFilter); // We are not filtering out any events for this trace.
// Ensure that we loaded something that looks about right!
assert.lengthOf(collectedEvents, 8252);
assert.notStrictEqual(metadata?.dataOrigin, Trace.Types.File.DataOrigin.CPU_PROFILE);
});
it('can load a saved CPUProfile file', async () => {
const file = await loadBasicCpuProfileAsFile();
const loader = await Timeline.TimelineLoader.TimelineLoader.loadFromFile(file, client);
await loader.traceFinalizedForTest();
sinon.assert.calledOnce(loadingStartedSpy);
// For the CPU Profile we are testing, loadingProgress will be called three times, because the
// file is not that big.
sinon.assert.callCount(loadingProgressSpy, 3);
sinon.assert.calledOnce(processingStartedSpy);
sinon.assert.calledOnce(loadingCompleteSpy);
// Get the arguments of the first (and only) call to the loadingComplete
// function. TS doesn't know what the types are (they are [any, any] by
// default), so we tell it that they align with the types of the
// loadingComplete parameters.
const [collectedEvents, /* exclusiveFilter */, metadata] =
loadingCompleteSpy.args[0] as Parameters<Timeline.TimelineController.Client['loadingComplete']>;
// We create fake trace event for CPU profile, includes one for
// TracingStartedInPage, one for metadata, one for root, and one for CPU
// profile
assert.lengthOf(collectedEvents, 4);
assert.strictEqual(metadata?.dataOrigin, Trace.Types.File.DataOrigin.CPU_PROFILE);
});
it('can load recorded trace events correctly', async () => {
const testTraceEvents: Trace.Types.Events.Event[] = [
makeInstantEvent('test-event-1', 1),
makeInstantEvent('test-event-2', 2),
];
const loader = Timeline.TimelineLoader.TimelineLoader.loadFromEvents(testTraceEvents, client);
await loader.traceFinalizedForTest();
sinon.assert.calledOnce(loadingStartedSpy);
// For the trace events we are testing, loadingProgress will be called only once, because the
// fake trace events array is very short.
sinon.assert.calledOnce(loadingProgressSpy);
sinon.assert.calledOnce(processingStartedSpy);
sinon.assert.calledOnce(loadingCompleteSpy);
// Get the arguments of the first (and only) call to the loadingComplete
// function. TS doesn't know what the types are (they are [any, any] by
// default), so we tell it that they align with the types of the
// loadingComplete parameters.
const [collectedEvents, exclusiveFilter, metadata] =
loadingCompleteSpy.args[0] as Parameters<Timeline.TimelineController.Client['loadingComplete']>;
assert.isNull(exclusiveFilter);
// Ensure that we loaded something that looks about right!
assert.lengthOf(collectedEvents, testTraceEvents.length);
assert.notStrictEqual(metadata?.dataOrigin, Trace.Types.File.DataOrigin.CPU_PROFILE);
});
it('can load recorded CPUProfile correctly', async () => {
const testProfile: Protocol.Profiler.Profile = {nodes: [], startTime: 0, endTime: 0};
const loader = Timeline.TimelineLoader.TimelineLoader.loadFromCpuProfile(testProfile, client);
await loader.traceFinalizedForTest();
sinon.assert.calledOnce(loadingStartedSpy);
// For the CPU Profile we are testing, loadingProgress will be called only once, because the
// fake Profile is basically empty.
sinon.assert.callCount(loadingProgressSpy, 1);
sinon.assert.calledOnce(processingStartedSpy);
sinon.assert.calledOnce(loadingCompleteSpy);
// Get the arguments of the first (and only) call to the loadingComplete
// function. TS doesn't know what the types are (they are [any, any] by
// default), so we tell it that they align with the types of the
// loadingComplete parameters.
const [collectedEvents, /* exclusiveFilter */, metadata] =
loadingCompleteSpy.args[0] as Parameters<Timeline.TimelineController.Client['loadingComplete']>;
// We create fake trace event for CPU profile, includes one for TracingStartedInPage,
// one for metadata, one for root, and one for CPU profile
assert.lengthOf(collectedEvents, 4);
assert.strictEqual(metadata?.dataOrigin, Trace.Types.File.DataOrigin.CPU_PROFILE);
});
});