chrome-devtools-frontend
Version:
Chrome DevTools UI
249 lines (203 loc) • 12.1 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 {describeWithMockConnection} from '../../../testing/MockConnection.js';
import {TraceLoader} from '../../../testing/TraceLoader.js';
import * as Trace from '../trace.js';
async function processTrace(events: readonly Trace.Types.Events.Event[]): Promise<void> {
// The FramesHandler depends on a few other handlers, so we run all of them as part of these tests.
const handlersInOrder: Trace.Handlers.Types.HandlerName[] = [
'Meta',
'Samples',
'AuctionWorklets',
'Renderer',
'LayerTree',
'Frames',
];
for (const handlerName of handlersInOrder) {
const handler = Trace.Handlers.ModelHandlers[handlerName];
handler.reset();
}
for (const event of events) {
for (const handlerName of handlersInOrder) {
Trace.Handlers.ModelHandlers[handlerName].handleEvent(event);
}
}
for (const handlerName of handlersInOrder) {
const handler = Trace.Handlers.ModelHandlers[handlerName];
await handler.finalize({});
}
}
describeWithMockConnection('FramesHandler', () => {
it('can parse out a trace and return the frames', async function() {
const rawEvents = await TraceLoader.rawEvents(this, 'web-dev-with-commit.json.gz');
await processTrace(rawEvents);
const parsedFrames = Trace.Handlers.ModelHandlers.Frames.data().frames;
assert.lengthOf(parsedFrames, 18);
// Assert a couple of frames to check the data, including one that is partial and was dropped.
assert.strictEqual(parsedFrames[0].startTime, 122411104714);
assert.strictEqual(parsedFrames[0].duration, 37847);
assert.isFalse(parsedFrames[0].isPartial);
assert.isFalse(parsedFrames[0].isPartial);
assert.strictEqual(parsedFrames[2].startTime, 122411159244);
assert.strictEqual(parsedFrames[2].duration, 16683);
assert.isTrue(parsedFrames[2].isPartial);
assert.isTrue(parsedFrames[2].dropped);
});
it('assigns each frame an index', async function() {
const rawEvents = await TraceLoader.rawEvents(this, 'web-dev-with-commit.json.gz');
await processTrace(rawEvents);
const parsedFrames = Trace.Handlers.ModelHandlers.Frames.data().frames;
assert.lengthOf(parsedFrames, 18);
parsedFrames.forEach((frame, arrayIndex) => {
// Seems silly, but this means we know the frame's index without having
// to look it up in the trace data.
assert.strictEqual(frame.index, arrayIndex);
});
});
it('can create LayerPaintEvents from Paint and snapshot events', async function() {
// Advanced instrumentation trace file is large: allow the bots more time
// to process it.
this.timeout(20_000);
const rawEvents = await TraceLoader.rawEvents(this, 'web-dev-with-advanced-instrumentation.json.gz');
await processTrace(rawEvents);
const parsedFrames = Trace.Handlers.ModelHandlers.Frames.data().frames;
assert.lengthOf(parsedFrames, 25);
const frameWithPaints = parsedFrames.at(2);
if (!frameWithPaints) {
throw new Error('Could not find frame at index 2');
}
// Check we have the right one.
assert.strictEqual(frameWithPaints.seqId, 1127448);
assert.lengthOf(frameWithPaints.paints, 7);
});
it('can return frames within a given window', async function() {
const rawEvents = await TraceLoader.rawEvents(this, 'web-dev-with-commit.json.gz');
await processTrace(rawEvents);
const parsedFrames = Trace.Handlers.ModelHandlers.Frames.data().frames;
assert.lengthOf(parsedFrames, 18);
const startTime = Trace.Types.Timing.Micro(parsedFrames[0].startTime);
const endTime = Trace.Types.Timing.Micro(parsedFrames[3].endTime);
const framesWithinWindow = Trace.Handlers.ModelHandlers.Frames.framesWithinWindow(parsedFrames, startTime, endTime);
assert.deepEqual(framesWithinWindow, [
parsedFrames[0],
parsedFrames[1],
parsedFrames[2],
parsedFrames[3],
]);
});
});
describe('FramesHandler', () => {
it('visualizes zero frames when no BeginFrames are added', () => {
const beginFrameQueue = new Trace.Handlers.ModelHandlers.Frames.TimelineFrameBeginFrameQueue();
const framesToVisualize = beginFrameQueue.processPendingBeginFramesOnDrawFrame(100);
assert.isEmpty(framesToVisualize);
});
it('visualizes zero frames when no BeginFrame in queue matches DrawFrame', () => {
const beginFrameQueue = new Trace.Handlers.ModelHandlers.Frames.TimelineFrameBeginFrameQueue();
beginFrameQueue.addFrameIfNotExists(100, Trace.Types.Timing.Micro(1000000), false, false);
beginFrameQueue.addFrameIfNotExists(101, Trace.Types.Timing.Micro(1000016), false, false);
beginFrameQueue.addFrameIfNotExists(102, Trace.Types.Timing.Micro(1000032), false, false);
const framesToVisualize = beginFrameQueue.processPendingBeginFramesOnDrawFrame(103);
assert.isEmpty(framesToVisualize);
});
it('ignores BeginFrames without corresponding DrawFrames', () => {
const beginFrameQueue = new Trace.Handlers.ModelHandlers.Frames.TimelineFrameBeginFrameQueue();
beginFrameQueue.addFrameIfNotExists(100, Trace.Types.Timing.Micro(1000000), false, false);
beginFrameQueue.addFrameIfNotExists(101, Trace.Types.Timing.Micro(1000016), false, false);
beginFrameQueue.addFrameIfNotExists(102, Trace.Types.Timing.Micro(1000032), false, false);
beginFrameQueue.addFrameIfNotExists(103, Trace.Types.Timing.Micro(Trace.Types.Timing.Micro(1000048)), false, false);
const framesToVisualize = beginFrameQueue.processPendingBeginFramesOnDrawFrame(102);
// Visualized frame: 102 (non-dropped).
// The other frames that are neither drawn nor dropped (100, 101) are
// excluded from visualization.
assert.lengthOf(framesToVisualize, 1);
assert.isFalse(framesToVisualize[0].isDropped);
assert.strictEqual(framesToVisualize[0].seqId, 102);
assert.strictEqual(framesToVisualize[0].startTime, Trace.Types.Timing.Micro(1000032));
});
it('visualizes dropped BeginFrames before a presented frame', () => {
const beginFrameQueue = new Trace.Handlers.ModelHandlers.Frames.TimelineFrameBeginFrameQueue();
beginFrameQueue.addFrameIfNotExists(100, Trace.Types.Timing.Micro(1000000), false, false);
beginFrameQueue.addFrameIfNotExists(101, Trace.Types.Timing.Micro(1000016), true, false);
beginFrameQueue.addFrameIfNotExists(102, Trace.Types.Timing.Micro(1000032), false, false);
beginFrameQueue.addFrameIfNotExists(103, Trace.Types.Timing.Micro(1000048), true, false);
beginFrameQueue.addFrameIfNotExists(104, Trace.Types.Timing.Micro(1000064), false, false);
beginFrameQueue.addFrameIfNotExists(105, Trace.Types.Timing.Micro(1000080), false, false);
beginFrameQueue.addFrameIfNotExists(106, Trace.Types.Timing.Micro(1000096), false, false);
const framesToVisualize = beginFrameQueue.processPendingBeginFramesOnDrawFrame(105);
// Visualized frames: 101 (dropped), 103 (dropped) and 105 (non-dropped).
// The other frames that are neither drawn nor dropped (100, 102 and 104)
// are excluded from visualization.
assert.lengthOf(framesToVisualize, 3);
assert.isTrue(framesToVisualize[0].isDropped);
assert.strictEqual(framesToVisualize[0].seqId, 101);
assert.strictEqual(framesToVisualize[0].startTime, Trace.Types.Timing.Micro(1000016));
assert.isTrue(framesToVisualize[1].isDropped);
assert.strictEqual(framesToVisualize[1].seqId, 103);
assert.strictEqual(framesToVisualize[1].startTime, Trace.Types.Timing.Micro(1000048));
assert.isFalse(framesToVisualize[2].isDropped);
assert.strictEqual(framesToVisualize[2].seqId, 105);
assert.strictEqual(framesToVisualize[2].startTime, Trace.Types.Timing.Micro(1000080));
});
it('changes dropped status of specified frames via setDropped()', () => {
const beginFrameQueue = new Trace.Handlers.ModelHandlers.Frames.TimelineFrameBeginFrameQueue();
beginFrameQueue.addFrameIfNotExists(100, Trace.Types.Timing.Micro(1000000), false, false);
beginFrameQueue.setDropped(100, true);
beginFrameQueue.addFrameIfNotExists(101, Trace.Types.Timing.Micro(1000016), true, false);
beginFrameQueue.addFrameIfNotExists(102, Trace.Types.Timing.Micro(1000032), false, false);
beginFrameQueue.addFrameIfNotExists(103, Trace.Types.Timing.Micro(1000048), true, false);
beginFrameQueue.addFrameIfNotExists(104, Trace.Types.Timing.Micro(1000064), false, false);
beginFrameQueue.addFrameIfNotExists(105, Trace.Types.Timing.Micro(1000080), false, false);
beginFrameQueue.addFrameIfNotExists(106, Trace.Types.Timing.Micro(1000096), true, false);
beginFrameQueue.setDropped(101, false);
const framesToVisualize = beginFrameQueue.processPendingBeginFramesOnDrawFrame(105);
// Visualized frames: 100 (dropped), 103 (dropped) and 105 (non-dropped).
// The other frames that are neither drawn nor dropped (101, 102 and 104)
// are excluded from visualization.
assert.lengthOf(framesToVisualize, 3);
assert.isTrue(framesToVisualize[0].isDropped);
assert.strictEqual(framesToVisualize[0].seqId, 100);
assert.strictEqual(framesToVisualize[0].startTime, Trace.Types.Timing.Micro(1000000));
assert.isTrue(framesToVisualize[1].isDropped);
assert.strictEqual(framesToVisualize[1].seqId, 103);
assert.strictEqual(framesToVisualize[1].startTime, Trace.Types.Timing.Micro(1000048));
assert.isFalse(framesToVisualize[2].isDropped);
assert.strictEqual(framesToVisualize[2].seqId, 105);
assert.strictEqual(framesToVisualize[2].startTime, Trace.Types.Timing.Micro(1000080));
});
it('pops processed frames out of the queue', () => {
const beginFrameQueue = new Trace.Handlers.ModelHandlers.Frames.TimelineFrameBeginFrameQueue();
beginFrameQueue.addFrameIfNotExists(100, Trace.Types.Timing.Micro(1000000), true, false);
beginFrameQueue.addFrameIfNotExists(101, Trace.Types.Timing.Micro(1000016), false, false);
beginFrameQueue.addFrameIfNotExists(102, Trace.Types.Timing.Micro(1000032), false, false);
beginFrameQueue.addFrameIfNotExists(103, Trace.Types.Timing.Micro(1000048), true, false);
beginFrameQueue.addFrameIfNotExists(104, Trace.Types.Timing.Micro(1000064), false, false);
beginFrameQueue.addFrameIfNotExists(105, Trace.Types.Timing.Micro(1000080), true, false);
beginFrameQueue.addFrameIfNotExists(106, Trace.Types.Timing.Micro(1000096), true, false);
// Pop frame 100, 101 (not visualized) and 102 from queue.
let framesToVisualize = beginFrameQueue.processPendingBeginFramesOnDrawFrame(102);
assert.lengthOf(framesToVisualize, 2);
assert.isTrue(framesToVisualize[0].isDropped);
assert.strictEqual(framesToVisualize[0].seqId, 100);
assert.strictEqual(framesToVisualize[0].startTime, Trace.Types.Timing.Micro(1000000));
assert.isFalse(framesToVisualize[1].isDropped);
assert.strictEqual(framesToVisualize[1].seqId, 102);
assert.strictEqual(framesToVisualize[1].startTime, Trace.Types.Timing.Micro(1000032));
// Pop frame 103, 104 (not visualized) and 105 from queue
framesToVisualize = beginFrameQueue.processPendingBeginFramesOnDrawFrame(105);
assert.lengthOf(framesToVisualize, 2);
assert.isTrue(framesToVisualize[0].isDropped);
assert.strictEqual(framesToVisualize[0].seqId, 103);
assert.strictEqual(framesToVisualize[0].startTime, Trace.Types.Timing.Micro(1000048));
assert.isTrue(framesToVisualize[1].isDropped);
assert.strictEqual(framesToVisualize[1].seqId, 105);
assert.strictEqual(framesToVisualize[1].startTime, Trace.Types.Timing.Micro(1000080));
// Pop frame 106 from queue
framesToVisualize = beginFrameQueue.processPendingBeginFramesOnDrawFrame(106);
assert.lengthOf(framesToVisualize, 1);
assert.isTrue(framesToVisualize[0].isDropped);
assert.strictEqual(framesToVisualize[0].seqId, 106);
assert.strictEqual(framesToVisualize[0].startTime, Trace.Types.Timing.Micro(1000096));
});
});