blob: eaccea4f89b1ff69af70bde51939627b4771cfeb [file]
// Copyright 2019 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 'dart:convert';
import 'package:devtools_app/src/profiler/cpu_profile_model.dart';
import 'package:devtools_app/src/timeline/timeline_model.dart';
import 'package:devtools_app/src/trace_event.dart';
import 'package:devtools_app/src/utils.dart';
import 'package:devtools_testing/support/cpu_profile_test_data.dart';
import 'package:devtools_testing/support/test_utils.dart';
import 'package:devtools_testing/support/timeline_test_data.dart';
import 'package:test/test.dart';
void main() {
group('TimelineData', () {
TimelineData timelineData;
setUp(() {
timelineData = TimelineData(
displayRefreshRate: 60.0,
timelineEvents: [
goldenAsyncTimelineEvent,
goldenUiTimelineEvent,
goldenRasterTimelineEvent,
unknownEvent,
],
);
});
test('init', () async {
expect(timelineData.traceEvents, isEmpty);
expect(timelineData.frames, isEmpty);
expect(timelineData.selectedFrame, isNull);
expect(timelineData.selectedFrameId, isNull);
expect(timelineData.selectedEvent, isNull);
expect(timelineData.displayRefreshRate, 60.0);
expect(timelineData.cpuProfileData, isNull);
});
test('to json', () {
expect(
timelineData.json,
equals({
TimelineData.traceEventsKey: [],
TimelineData.cpuProfileKey: {},
TimelineData.selectedFrameIdKey: null,
TimelineData.selectedEventKey: {},
TimelineData.displayRefreshRateKey: 60,
}));
timelineData = TimelineData(displayRefreshRate: 60)
..traceEvents.add({'name': 'FakeTraceEvent'})
..cpuProfileData = CpuProfileData.parse(goldenCpuProfileDataJson)
..selectedEvent = vsyncEvent;
expect(
timelineData.json,
equals({
TimelineData.traceEventsKey: [
{'name': 'FakeTraceEvent'}
],
TimelineData.cpuProfileKey: goldenCpuProfileDataJson,
TimelineData.selectedFrameIdKey: null,
TimelineData.selectedEventKey: vsyncEvent.json,
TimelineData.displayRefreshRateKey: 60,
}),
);
});
test('clear', () async {
timelineData = TimelineData(
displayRefreshRate: 120,
timelineEvents: [
goldenAsyncTimelineEvent,
goldenUiTimelineEvent,
goldenRasterTimelineEvent,
unknownEvent,
],
)
..traceEvents.add({'test': 'trace event'})
..frames.add(testFrame0)
..selectedEvent = vsyncEvent
..selectedFrame = testFrame0
..cpuProfileData = CpuProfileData.parse(jsonDecode(jsonEncode({})));
expect(timelineData.traceEvents, isNotEmpty);
expect(timelineData.frames, isNotEmpty);
expect(timelineData.selectedFrame, isNotNull);
expect(timelineData.selectedFrameId, 'id_0');
expect(timelineData.selectedEvent, isNotNull);
expect(timelineData.displayRefreshRate, equals(120));
expect(timelineData.cpuProfileData, isNotNull);
expect(timelineData.timelineEvents, isNotEmpty);
timelineData.clear();
expect(timelineData.traceEvents, isEmpty);
expect(timelineData.frames, isEmpty);
expect(timelineData.selectedFrame, isNull);
expect(timelineData.selectedFrameId, isNull);
expect(timelineData.selectedEvent, isNull);
expect(timelineData.cpuProfileData, isNull);
expect(timelineData.timelineEvents, isEmpty);
});
test('initializeEventBuckets', () {
expect(timelineData.eventGroups, isEmpty);
timelineData.initializeEventGroups();
expect(
timelineData.eventGroups[TimelineData.uiKey].rows[0].events.length,
equals(1),
);
expect(
timelineData.eventGroups[TimelineData.rasterKey].rows[0].events.length,
equals(1),
);
expect(
timelineData.eventGroups[TimelineData.unknownKey].rows[0].events.length,
equals(1),
);
expect(timelineData.eventGroups['A'].rows[0].events.length, equals(1));
});
test('event bucket compare', () {
expect(TimelineData.eventGroupComparator('UI', 'Raster'), equals(-1));
expect(TimelineData.eventGroupComparator('Raster', 'UI'), equals(1));
expect(TimelineData.eventGroupComparator('UI', 'UI'), equals(0));
expect(TimelineData.eventGroupComparator('UI', 'Async'), equals(1));
expect(TimelineData.eventGroupComparator('A', 'B'), equals(-1));
expect(TimelineData.eventGroupComparator('Z', 'Unknown'), equals(-1));
});
});
group('OfflineTimelineData', () {
test('init from parse', () {
OfflineTimelineData offlineData = OfflineTimelineData.parse({});
expect(offlineData.traceEvents, isEmpty);
expect(offlineData.frames, isEmpty);
expect(offlineData.selectedFrame, isNull);
expect(offlineData.selectedFrameId, isNull);
expect(offlineData.selectedEvent, isNull);
expect(offlineData.displayRefreshRate, equals(60.0));
expect(offlineData.cpuProfileData, isNull);
offlineData = OfflineTimelineData.parse(offlineTimelineDataJson);
expect(
offlineData.traceEvents,
equals(goldenTraceEventsJson),
);
expect(offlineData.frames, isEmpty);
expect(offlineData.selectedFrame, isNull);
expect(offlineData.selectedFrameId, equals('PipelineItem-1'));
expect(offlineData.selectedEvent, isA<OfflineTimelineEvent>());
final expectedFirstTraceJson =
Map<String, dynamic>.from(vsyncEvent.beginTraceEventJson);
expectedFirstTraceJson[TraceEvent.argsKey]
.addAll({TraceEvent.typeKey: TimelineEventType.ui});
expectedFirstTraceJson.addAll(
{TraceEvent.durationKey: vsyncEvent.time.duration.inMicroseconds});
expect(
offlineData.selectedEvent.json,
equals({TimelineEvent.firstTraceKey: expectedFirstTraceJson}),
);
expect(offlineData.displayRefreshRate, equals(120));
expect(offlineData.cpuProfileData.json, equals(goldenCpuProfileDataJson));
});
test('shallowClone', () {
final offlineData = OfflineTimelineData.parse(offlineTimelineDataJson);
final clone = offlineData.shallowClone();
expect(offlineData.traceEvents, equals(clone.traceEvents));
expect(offlineData.frames, equals(clone.frames));
expect(offlineData.selectedFrame, equals(clone.selectedFrame));
expect(offlineData.selectedFrameId, equals(clone.selectedFrameId));
expect(offlineData.selectedEvent, equals(clone.selectedEvent));
expect(offlineData.displayRefreshRate, equals(clone.displayRefreshRate));
expect(offlineData.cpuProfileData, equals(clone.cpuProfileData));
expect(identical(offlineData, clone), isFalse);
});
});
group('SyncTimelineEvent', () {
test('maybeRemoveDuplicate', () {
final goldenCopy = goldenUiTimelineEvent.deepCopy();
// Event with no duplicates should be unchanged.
goldenCopy.maybeRemoveDuplicate();
expect(goldenCopy.toString(), equals(goldenUiString));
// Add a duplicate event in [goldenCopy]'s event tree.
final duplicateEvent = goldenCopy.deepCopy();
duplicateEvent.parent = goldenCopy;
duplicateEvent.children
..clear()
..addAll(goldenCopy.children);
goldenCopy.children
..clear()
..add(duplicateEvent);
expect(goldenCopy.toString(), isNot(equals(goldenUiString)));
goldenCopy.maybeRemoveDuplicate();
expect(goldenCopy.toString(), equals(goldenUiString));
});
test('removeChild', () {
final goldenCopy = goldenUiTimelineEvent.deepCopy();
// VSYNC
// Animator::BeginFrame
// Framework Workload
// Engine::BeginFrame <-- [goldenEvent], [copyEvent]
// Frame <-- event we will remove
final TimelineEvent engineBeginFrameEvent =
goldenCopy.children.first.children.first.children.first;
expect(engineBeginFrameEvent.name, equals('Engine::BeginFrame'));
// Ensure [engineBeginFrameEvent]'s only child is the Frame event.
expect(engineBeginFrameEvent.children.length, equals(1));
final frameEvent = engineBeginFrameEvent.children.first;
expect(frameEvent.children.length, equals(7));
// Remove the Frame event from [engineBeginFrameEvent]'s chiengineBeginFrameEventldren.
engineBeginFrameEvent.removeChild(frameEvent);
// Now [frameEvent]'s children are [engineBeginFrameEvent]'s children.
expect(engineBeginFrameEvent.children.length, equals(7));
expect(
collectionEquals(engineBeginFrameEvent.children, frameEvent.children),
isTrue,
);
});
test('addChild', () {
final TimelineEvent engineBeginFrame =
testSyncTimelineEvent(engineBeginFrameTrace);
expect(engineBeginFrame.children.isEmpty, isTrue);
// Add child [animate] to a leaf [engineBeginFrame].
final TimelineEvent animate = testSyncTimelineEvent(animateTrace)
..time.end = const Duration(microseconds: 118039650871);
engineBeginFrame.addChild(animate);
expect(engineBeginFrame.children.length, equals(1));
expect(engineBeginFrame.children.first.name, equals(animateEvent.name));
// Add child [layout] where child is sibling of existing children
// [animate].
final TimelineEvent layout = testSyncTimelineEvent(layoutTrace)
..time.end = const Duration(microseconds: 118039651087);
engineBeginFrame.addChild(layout);
expect(engineBeginFrame.children.length, equals(2));
expect(engineBeginFrame.children.last.name, equals(layoutEvent.name));
// Add child [build] where existing child [layout] is parent of child.
final TimelineEvent build = testSyncTimelineEvent(buildTrace)
..time.end = const Duration(microseconds: 118039651017);
engineBeginFrame.addChild(build);
expect(engineBeginFrame.children.length, equals(2));
expect(layout.children.length, equals(1));
expect(layout.children.first.name, equals(buildEvent.name));
// Add child [frame] child is parent of existing children [animate] and
// [layout].
final TimelineEvent frame = testSyncTimelineEvent(frameTrace)
..time.end = const Duration(microseconds: 118039652334);
engineBeginFrame.addChild(frame);
expect(engineBeginFrame.children.length, equals(1));
expect(engineBeginFrame.children.first.name, equals(frameEvent.name));
expect(frame.children.length, equals(2));
expect(frame.children.first.name, equals(animateEvent.name));
expect(frame.children.last.name, equals(layoutEvent.name));
});
});
group('AsyncTimelineEvent', () {
test('isWellFormedDeep', () {
expect(goldenAsyncTimelineEvent.isWellFormedDeep, isTrue);
final copy = goldenAsyncTimelineEvent.deepCopy();
copy.children.last.children.last
.addChild(AsyncTimelineEvent(asyncStartDTrace));
expect(copy.isWellFormedDeep, isFalse);
});
test('maxEndMicros', () {
expect(goldenAsyncTimelineEvent.maxEndMicros, equals(193938740983));
});
test('displayDepth', () {
expect(goldenAsyncTimelineEvent.displayDepth, equals(6));
expect(asyncEventB.displayDepth, equals(3));
expect(asyncEventC.displayDepth, equals(2));
expect(asyncEventD.displayDepth, equals(1));
expect(asyncEventWithDeepOverlap.displayDepth, equals(5));
});
test('couldBeParentOf', () {
expect(asyncEventA.couldBeParentOf(asyncEventB1), isFalse);
expect(asyncEventB.couldBeParentOf(asyncEventB1), isTrue);
expect(asyncEventB.couldBeParentOf(asyncEventC1), isFalse);
expect(asyncEventC.couldBeParentOf(asyncEventC1), isTrue);
expect(asyncParentId1.couldBeParentOf(asyncChildId1), isTrue);
expect(asyncParentId1.couldBeParentOf(asyncChildId2), isFalse);
});
test('addEndEvent', () {
final event = AsyncTimelineEvent(asyncStartATrace);
expect(event.endTraceEventJson, isNull);
expect(event.time.end, isNull);
event.addEndEvent(asyncEndATrace);
expect(event.endTraceEventJson, equals(asyncEndATrace.event.json));
expect(
event.time.end.inMicroseconds,
asyncEndATrace.event.timestampMicros,
);
});
});
}