Transition refactor.
Final presenter/parser integration.
Cleaned up and fixed transitions in timeline (showing only play stage of transitions).
Bug: b/311643292
Test: npm run test:unit:ci
Change-Id: I1f1be39adcd8b1bf71ec4ad5829121e2e5bec636
diff --git a/tools/winscope/src/app/components/timeline/expanded-timeline/expanded_timeline_component_test.ts b/tools/winscope/src/app/components/timeline/expanded-timeline/expanded_timeline_component_test.ts
index 3bc3469..c81c89f 100644
--- a/tools/winscope/src/app/components/timeline/expanded-timeline/expanded_timeline_component_test.ts
+++ b/tools/winscope/src/app/components/timeline/expanded-timeline/expanded_timeline_component_test.ts
@@ -28,7 +28,7 @@
import {TimelineData} from 'app/timeline_data';
import {assertDefined} from 'common/assert_utils';
import {RealTimestamp} from 'common/time';
-import {Transition} from 'flickerlib/common';
+import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
import {TracesBuilder} from 'test/unit/traces_builder';
import {TracePosition} from 'trace/trace_position';
import {TraceType} from 'trace/trace_type';
@@ -78,14 +78,38 @@
.setEntries(TraceType.TRANSACTIONS, [{}])
.setTimestamps(TraceType.TRANSACTIONS, [new RealTimestamp(12n)])
.setEntries(TraceType.TRANSITION, [
- {
- createTime: {unixNanos: 10n},
- finishTime: {unixNanos: 30n},
- } as Transition,
- {
- createTime: {unixNanos: 60n},
- finishTime: {unixNanos: 110n},
- } as Transition,
+ new PropertyTreeBuilder()
+ .setIsRoot(true)
+ .setRootId('TransitionsTraceEntry')
+ .setName('transition')
+ .setChildren([
+ {
+ name: 'wmData',
+ children: [{name: 'finishTimeNs', value: 30n}],
+ },
+ {
+ name: 'shellData',
+ children: [{name: 'dispatchTimeNs', value: 10n}],
+ },
+ {name: 'aborted', value: false},
+ ])
+ .build(),
+ new PropertyTreeBuilder()
+ .setIsRoot(true)
+ .setRootId('TransitionsTraceEntry')
+ .setName('transition')
+ .setChildren([
+ {
+ name: 'wmData',
+ children: [{name: 'finishTimeNs', value: 110n}],
+ },
+ {
+ name: 'shellData',
+ children: [{name: 'dispatchTimeNs', value: 60n}],
+ },
+ {name: 'aborted', value: false},
+ ])
+ .build(),
])
.setTimestamps(TraceType.TRANSITION, [new RealTimestamp(10n), new RealTimestamp(60n)])
.setTimestamps(TraceType.PROTO_LOG, [])
@@ -116,7 +140,7 @@
// initially only first entry of SF is set
singleTimelines.forEach((timeline) => {
- if (timeline.trace.type === TraceType.SURFACE_FLINGER) {
+ if (assertDefined(timeline.trace).type === TraceType.SURFACE_FLINGER) {
const entry = assertDefined(timeline.selectedEntry);
expect(entry.getFullTrace().type).toBe(TraceType.SURFACE_FLINGER);
} else {
@@ -130,7 +154,9 @@
it('passes selectedEntry of correct type into each timeline on position change', () => {
// 3 out of the 5 traces have timestamps before or at 11n
- component.timelineData.setPosition(TracePosition.fromTimestamp(new RealTimestamp(11n)));
+ assertDefined(component.timelineData).setPosition(
+ TracePosition.fromTimestamp(new RealTimestamp(11n))
+ );
fixture.detectChanges();
const singleTimelines = assertDefined(component.singleTimelines);
@@ -139,24 +165,28 @@
singleTimelines.forEach((timeline) => {
// protolog and transactions traces have no timestamps before current position
if (
- timeline.trace.type === TraceType.PROTO_LOG ||
- timeline.trace.type === TraceType.TRANSACTIONS
+ assertDefined(timeline.trace).type === TraceType.PROTO_LOG ||
+ assertDefined(timeline.trace).type === TraceType.TRANSACTIONS
) {
expect(timeline.selectedEntry).toBeUndefined();
} else {
const selectedEntry = assertDefined(timeline.selectedEntry);
- expect(selectedEntry.getFullTrace().type).toEqual(timeline.trace.type);
+ expect(selectedEntry.getFullTrace().type).toEqual(assertDefined(timeline.trace).type);
}
});
const transitionTimeline = assertDefined(component.transitionTimelines).first;
const selectedEntry = assertDefined(transitionTimeline.selectedEntry);
- expect(selectedEntry.getFullTrace().type).toEqual(transitionTimeline.trace.type);
+ expect(selectedEntry.getFullTrace().type).toEqual(assertDefined(transitionTimeline.trace).type);
});
it('getAllLoadedTraces causes timelines to render in correct order', () => {
// traces in timelineData are in order of being set in Traces API
- expect(component.timelineData.getTraces().mapTrace((trace) => trace.type)).toEqual([
+ expect(
+ assertDefined(component.timelineData)
+ .getTraces()
+ .mapTrace((trace) => trace.type)
+ ).toEqual([
TraceType.SURFACE_FLINGER,
TraceType.WINDOW_MANAGER,
TraceType.TRANSACTIONS,
diff --git a/tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_component.ts b/tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_component.ts
index 0eb262b..472baaa 100644
--- a/tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_component.ts
+++ b/tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_component.ts
@@ -15,13 +15,15 @@
*/
import {Component, ElementRef, EventEmitter, Input, Output, ViewChild} from '@angular/core';
+import {assertDefined} from 'common/assert_utils';
import {Point} from 'common/geometry_types';
import {Rect} from 'common/rect';
-import {ElapsedTimestamp, RealTimestamp, TimeRange, Timestamp, TimestampType} from 'common/time';
-import {Transition} from 'flickerlib/common';
+import {TimeRange, Timestamp} from 'common/time';
import {Trace, TraceEntry} from 'trace/trace';
import {TracePosition} from 'trace/trace_position';
import {TraceType} from 'trace/trace_type';
+import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
+import {TimelineUtils} from '../timeline_utils';
import {AbstractTimelineRowComponent} from './abstract_timeline_row_component';
@Component({
@@ -29,7 +31,7 @@
template: `
<div
class="transition-timeline"
- matTooltip="Some or all transitions will not be rendered in timeline due to unknown creation time"
+ matTooltip="Some or all transitions will not be rendered in timeline due to unknown dispatch time"
[matTooltipDisabled]="shouldNotRenderEntries.length === 0"
#wrapper>
<canvas #canvas></canvas>
@@ -43,19 +45,18 @@
`,
],
})
-export class TransitionTimelineComponent extends AbstractTimelineRowComponent<Transition> {
+export class TransitionTimelineComponent extends AbstractTimelineRowComponent<PropertyTreeNode> {
@Input() color = '#AF5CF7';
- @Input() trace!: Trace<Transition>;
- @Input() selectedEntry: TraceEntry<Transition> | undefined = undefined;
- @Input() selectionRange!: TimeRange;
+ @Input() trace: Trace<PropertyTreeNode> | undefined;
+ @Input() selectedEntry: TraceEntry<PropertyTreeNode> | undefined;
+ @Input() selectionRange: TimeRange | undefined;
@Output() readonly onTracePositionUpdate = new EventEmitter<TracePosition>();
- @ViewChild('canvas', {static: false}) override canvasRef!: ElementRef;
- @ViewChild('wrapper', {static: false}) override wrapperRef!: ElementRef;
+ @ViewChild('canvas', {static: false}) override canvasRef: ElementRef | undefined;
+ @ViewChild('wrapper', {static: false}) override wrapperRef: ElementRef | undefined;
- hoveringEntry?: TraceEntry<Transition>;
-
+ hoveringEntry?: TraceEntry<PropertyTreeNode>;
rowsToUse = new Map<number, number>();
maxRowsRequires = 0;
shouldNotRenderEntries: number[] = [];
@@ -70,20 +71,30 @@
private async computeRowsForEntries(): Promise<void> {
const rowAvailableFrom: Array<bigint | undefined> = [];
await Promise.all(
- (this.trace as Trace<Transition>).mapEntry(async (entry) => {
+ (this.trace as Trace<PropertyTreeNode>).mapEntry(async (entry) => {
const transition = await entry.getValue();
const index = entry.getIndex();
- if (transition.createTime.isMin) {
+
+ const timeRange = TimelineUtils.getTimeRangeForTransition(
+ transition,
+ entry.getTimestamp().getType(),
+ assertDefined(this.selectionRange)
+ );
+
+ if (!timeRange) {
this.shouldNotRenderEntries.push(index);
}
+
let rowToUse = 0;
while (
- (rowAvailableFrom[rowToUse] ?? 0n) > BigInt(transition.createTime.unixNanos.toString())
+ (rowAvailableFrom[rowToUse] ?? 0n) >
+ (timeRange?.from.getValueNs() ?? assertDefined(this.selectionRange).from.getValueNs())
) {
rowToUse++;
}
- rowAvailableFrom[rowToUse] = BigInt(transition.finishTime.unixNanos.toString());
+ rowAvailableFrom[rowToUse] =
+ timeRange?.to.getValueNs() ?? assertDefined(this.selectionRange).to.getValueNs();
if (rowToUse + 1 > this.maxRowsRequires) {
this.maxRowsRequires = rowToUse + 1;
@@ -93,7 +104,7 @@
);
}
- private getRowToUseFor(entry: TraceEntry<Transition>): number {
+ private getRowToUseFor(entry: TraceEntry<PropertyTreeNode>): number {
const rowToUse = this.rowsToUse.get(entry.getIndex());
if (rowToUse === undefined) {
console.error('Failed to find', entry, 'in', this.rowsToUse);
@@ -124,33 +135,49 @@
this.hoveringEntry = currentHoverEntry;
- if (!this.hoveringEntry || this.shouldNotRenderEntry(this.hoveringEntry)) {
+ if (!this.hoveringEntry) {
return;
}
- const hoveringSegment = await this.getSegmentForTransition(this.hoveringEntry);
+ const transition = await this.hoveringEntry.getValue();
+ const timeRange = TimelineUtils.getTimeRangeForTransition(
+ transition,
+ this.hoveringEntry.getTimestamp().getType(),
+ assertDefined(this.selectionRange)
+ );
+
+ if (!timeRange) {
+ return;
+ }
+
const rowToUse = this.getRowToUseFor(this.hoveringEntry);
- const rect = this.getSegmentRect(hoveringSegment.from, hoveringSegment.to, rowToUse);
+ const rect = this.getSegmentRect(timeRange.from, timeRange.to, rowToUse);
this.canvasDrawer.drawRectBorder(rect);
}
protected override async getEntryAt(
mousePoint: Point
- ): Promise<TraceEntry<Transition> | undefined> {
- if (this.trace.type !== TraceType.TRANSITION) {
+ ): Promise<TraceEntry<PropertyTreeNode> | undefined> {
+ if (assertDefined(this.trace).type !== TraceType.TRANSITION) {
return undefined;
}
- const transitionEntries: Array<Promise<TraceEntry<Transition> | undefined>> = [];
- this.trace.forEachEntry((entry) => {
+ const transitionEntries: Array<Promise<TraceEntry<PropertyTreeNode> | undefined>> = [];
+ assertDefined(this.trace).forEachEntry((entry) => {
transitionEntries.push(
(async () => {
- if (this.shouldNotRenderEntry(entry)) {
+ const transition = await entry.getValue();
+ const timeRange = TimelineUtils.getTimeRangeForTransition(
+ transition,
+ entry.getTimestamp().getType(),
+ assertDefined(this.selectionRange)
+ );
+
+ if (!timeRange) {
return undefined;
}
- const transitionSegment = await this.getSegmentForTransition(entry);
const rowToUse = this.getRowToUseFor(entry);
- const rect = this.getSegmentRect(transitionSegment.from, transitionSegment.to, rowToUse);
+ const rect = this.getSegmentRect(timeRange.from, timeRange.to, rowToUse);
if (rect.containsPoint(mousePoint)) {
return entry;
}
@@ -178,16 +205,16 @@
}
private getXPosOf(entry: Timestamp): number {
- const start = this.selectionRange.from.getValueNs();
- const end = this.selectionRange.to.getValueNs();
+ const start = assertDefined(this.selectionRange).from.getValueNs();
+ const end = assertDefined(this.selectionRange).to.getValueNs();
return Number((BigInt(this.availableWidth) * (entry.getValueNs() - start)) / (end - start));
}
private getSegmentRect(start: Timestamp, end: Timestamp, rowToUse: number): Rect {
const xPosStart = this.getXPosOf(start);
- const selectionStart = this.selectionRange.from.getValueNs();
- const selectionEnd = this.selectionRange.to.getValueNs();
+ const selectionStart = assertDefined(this.selectionRange).from.getValueNs();
+ const selectionEnd = assertDefined(this.selectionRange).to.getValueNs();
const width = Number(
(BigInt(this.availableWidth) * (end.getValueNs() - start.getValueNs())) /
@@ -212,37 +239,24 @@
override async drawTimeline() {
await Promise.all(
- (this.trace as Trace<Transition>).mapEntry(async (entry) => {
- if (this.shouldNotRenderEntry(entry)) {
+ (this.trace as Trace<PropertyTreeNode>).mapEntry(async (entry) => {
+ const transition = await entry.getValue();
+ const timeRange = TimelineUtils.getTimeRangeForTransition(
+ transition,
+ entry.getTimestamp().getType(),
+ assertDefined(this.selectionRange)
+ );
+ if (!timeRange) {
return;
}
- const transitionSegment = await this.getSegmentForTransition(entry);
const rowToUse = this.getRowToUseFor(entry);
- const aborted = (await entry.getValue()).aborted;
- this.drawSegment(transitionSegment.from, transitionSegment.to, rowToUse, aborted);
+ const aborted = assertDefined(transition.getChildByName('aborted')).getValue();
+ this.drawSegment(timeRange.from, timeRange.to, rowToUse, aborted);
})
);
this.drawSelectedTransitionEntry();
}
- private async getSegmentForTransition(entry: TraceEntry<Transition>): Promise<TimeRange> {
- const transition = await entry.getValue();
-
- let createTime: Timestamp;
- let finishTime: Timestamp;
- if (entry.getTimestamp().getType() === TimestampType.REAL) {
- createTime = new RealTimestamp(BigInt(transition.createTime.unixNanos.toString()));
- finishTime = new RealTimestamp(BigInt(transition.finishTime.unixNanos.toString()));
- } else if (entry.getTimestamp().getType() === TimestampType.ELAPSED) {
- createTime = new ElapsedTimestamp(BigInt(transition.createTime.elapsedNanos.toString()));
- finishTime = new ElapsedTimestamp(BigInt(transition.finishTime.elapsedNanos.toString()));
- } else {
- throw new Error('Unspported timestamp type');
- }
-
- return {from: createTime, to: finishTime};
- }
-
private drawSegment(start: Timestamp, end: Timestamp, rowToUse: number, aborted: boolean) {
const rect = this.getSegmentRect(start, end, rowToUse);
const alpha = aborted ? 0.25 : 1.0;
@@ -250,21 +264,23 @@
}
private async drawSelectedTransitionEntry() {
- if (this.selectedEntry === undefined || this.shouldNotRenderEntry(this.selectedEntry)) {
+ if (this.selectedEntry === undefined) {
+ return;
+ }
+ const transition = await this.selectedEntry.getValue();
+ const timeRange = TimelineUtils.getTimeRangeForTransition(
+ transition,
+ this.selectedEntry.getTimestamp().getType(),
+ assertDefined(this.selectionRange)
+ );
+ if (!timeRange) {
return;
}
- const transitionSegment = await this.getSegmentForTransition(this.selectedEntry);
-
- const transition = await this.selectedEntry.getValue();
const rowIndex = this.getRowToUseFor(this.selectedEntry);
- const rect = this.getSegmentRect(transitionSegment.from, transitionSegment.to, rowIndex);
- const alpha = transition.aborted ? 0.25 : 1.0;
+ const rect = this.getSegmentRect(timeRange.from, timeRange.to, rowIndex);
+ const alpha = transition.getChildByName('aborted') ? 0.25 : 1.0;
this.canvasDrawer.drawRect(rect, this.color, alpha);
this.canvasDrawer.drawRectBorder(rect);
}
-
- private shouldNotRenderEntry(entry: TraceEntry<Transition>): boolean {
- return this.shouldNotRenderEntries.includes(entry.getIndex());
- }
}
diff --git a/tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_component_test.ts b/tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_component_test.ts
index 2c2b149..bacad72 100644
--- a/tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_component_test.ts
+++ b/tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_component_test.ts
@@ -27,10 +27,11 @@
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {Rect} from 'common/rect';
import {RealTimestamp} from 'common/time';
-import {Transition} from 'flickerlib/common';
+import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
import {TraceBuilder} from 'test/unit/trace_builder';
import {waitToBeCalled} from 'test/utils';
import {TraceType} from 'trace/trace_type';
+import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
import {TransitionTimelineComponent} from './transition_timeline_component';
describe('TransitionTimelineComponent', () => {
@@ -66,17 +67,42 @@
});
it('can draw non-overlapping transitions', async () => {
- component.trace = new TraceBuilder()
+ component.trace = new TraceBuilder<PropertyTreeNode>()
.setType(TraceType.TRANSITION)
.setEntries([
- {
- createTime: {unixNanos: 10n},
- finishTime: {unixNanos: 30n},
- } as Transition,
- {
- createTime: {unixNanos: 60n},
- finishTime: {unixNanos: 110n},
- } as Transition,
+ new PropertyTreeBuilder()
+ .setIsRoot(true)
+ .setRootId('TransitionsTraceEntry')
+ .setName('transition')
+ .setChildren([
+ {
+ name: 'wmData',
+ children: [{name: 'finishTimeNs', value: 30n}],
+ },
+ {
+ name: 'shellData',
+ children: [{name: 'dispatchTimeNs', value: 10n}],
+ },
+ {name: 'aborted', value: false},
+ ])
+ .build(),
+
+ new PropertyTreeBuilder()
+ .setIsRoot(true)
+ .setRootId('TransitionsTraceEntry')
+ .setName('transition')
+ .setChildren([
+ {
+ name: 'wmData',
+ children: [{name: 'finishTimeNs', value: 110n}],
+ },
+ {
+ name: 'shellData',
+ children: [{name: 'dispatchTimeNs', value: 60n}],
+ },
+ {name: 'aborted', value: false},
+ ])
+ .build(),
])
.setTimestamps([new RealTimestamp(10n), new RealTimestamp(60n)])
.build();
@@ -107,17 +133,41 @@
});
it('can draw transitions zoomed in', async () => {
- component.trace = new TraceBuilder()
+ component.trace = new TraceBuilder<PropertyTreeNode>()
.setType(TraceType.TRANSITION)
.setEntries([
- {
- createTime: {unixNanos: 0n},
- finishTime: {unixNanos: 20n},
- } as Transition,
- {
- createTime: {unixNanos: 60n},
- finishTime: {unixNanos: 160n},
- } as Transition,
+ new PropertyTreeBuilder()
+ .setIsRoot(true)
+ .setRootId('TransitionsTraceEntry')
+ .setName('transition')
+ .setChildren([
+ {
+ name: 'wmData',
+ children: [{name: 'finishTimeNs', value: 20n}],
+ },
+ {
+ name: 'shellData',
+ children: [{name: 'dispatchTimeNs', value: 0n}],
+ },
+ {name: 'aborted', value: false},
+ ])
+ .build(),
+ new PropertyTreeBuilder()
+ .setIsRoot(true)
+ .setRootId('TransitionsTraceEntry')
+ .setName('transition')
+ .setChildren([
+ {
+ name: 'wmData',
+ children: [{name: 'finishTimeNs', value: 160n}],
+ },
+ {
+ name: 'shellData',
+ children: [{name: 'dispatchTimeNs', value: 60n}],
+ },
+ {name: 'aborted', value: false},
+ ])
+ .build(),
])
.setTimestamps([new RealTimestamp(10n), new RealTimestamp(60n)])
.build();
@@ -136,7 +186,7 @@
expect(drawRectSpy).toHaveBeenCalledTimes(2);
expect(drawRectSpy).toHaveBeenCalledWith(
- new Rect(-Math.floor(width / 10), padding, Math.floor(width / 5), oneRowHeight),
+ new Rect(0, padding, Math.floor(width / 10), oneRowHeight),
component.color,
1
);
@@ -148,12 +198,23 @@
});
it('can draw selected entry', async () => {
- const transition: Transition = {
- createTime: {unixNanos: 35n},
- finishTime: {unixNanos: 85n},
- aborted: true,
- };
- component.trace = new TraceBuilder()
+ const transition = new PropertyTreeBuilder()
+ .setIsRoot(true)
+ .setRootId('TransitionsTraceEntry')
+ .setName('transition')
+ .setChildren([
+ {
+ name: 'wmData',
+ children: [{name: 'finishTimeNs', value: 85n}],
+ },
+ {
+ name: 'shellData',
+ children: [{name: 'dispatchTimeNs', value: 35n}],
+ },
+ {name: 'aborted', value: false},
+ ])
+ .build();
+ component.trace = new TraceBuilder<PropertyTreeNode>()
.setType(TraceType.TRANSITION)
.setEntries([transition])
.setTimestamps([new RealTimestamp(35n)])
@@ -163,19 +224,16 @@
const drawRectSpy = spyOn(component.canvasDrawer, 'drawRect');
const drawRectBorderSpy = spyOn(component.canvasDrawer, 'drawRectBorder');
-
const waitPromises = [waitToBeCalled(drawRectSpy, 1), waitToBeCalled(drawRectBorderSpy, 1)];
fixture.detectChanges();
await fixture.whenRenderingDone();
-
await Promise.all(waitPromises);
const padding = 5;
const oneRowTotalHeight = 30;
const oneRowHeight = oneRowTotalHeight - padding;
const width = component.canvasDrawer.getScaledCanvasWidth();
-
const expectedRect = new Rect(
Math.floor((width * 1) / 4),
padding,
@@ -183,19 +241,29 @@
oneRowHeight
);
expect(drawRectSpy).toHaveBeenCalledTimes(2); // once drawn as a normal entry another time with rect border
- expect(drawRectSpy).toHaveBeenCalledWith(expectedRect, component.color, 0.25);
-
+ expect(drawRectSpy).toHaveBeenCalledWith(expectedRect, component.color, 1);
expect(drawRectBorderSpy).toHaveBeenCalledTimes(1);
expect(drawRectBorderSpy).toHaveBeenCalledWith(expectedRect);
});
it('can draw hovering entry', async () => {
- const transition: Transition = {
- createTime: {unixNanos: 35n},
- finishTime: {unixNanos: 85n},
- aborted: true,
- };
- component.trace = new TraceBuilder()
+ const transition = new PropertyTreeBuilder()
+ .setIsRoot(true)
+ .setRootId('TransitionsTraceEntry')
+ .setName('transition')
+ .setChildren([
+ {
+ name: 'wmData',
+ children: [{name: 'finishTimeNs', value: 85n}],
+ },
+ {
+ name: 'shellData',
+ children: [{name: 'dispatchTimeNs', value: 35n}],
+ },
+ {name: 'aborted', value: false},
+ ])
+ .build();
+ component.trace = new TraceBuilder<PropertyTreeNode>()
.setType(TraceType.TRANSITION)
.setEntries([transition])
.setTimestamps([new RealTimestamp(35n)])
@@ -212,17 +280,14 @@
const oneRowTotalHeight = 30;
const oneRowHeight = oneRowTotalHeight - padding;
const width = component.canvasDrawer.getScaledCanvasWidth();
-
component.handleMouseMove({
offsetX: Math.floor(width / 2),
offsetY: oneRowTotalHeight / 2,
preventDefault: () => {},
stopPropagation: () => {},
} as MouseEvent);
-
await waitToBeCalled(drawRectSpy, 1);
await waitToBeCalled(drawRectBorderSpy, 1);
-
const expectedRect = new Rect(
Math.floor((width * 1) / 4),
padding,
@@ -230,24 +295,47 @@
oneRowHeight
);
expect(drawRectSpy).toHaveBeenCalledTimes(1);
- expect(drawRectSpy).toHaveBeenCalledWith(expectedRect, component.color, 0.25);
-
+ expect(drawRectSpy).toHaveBeenCalledWith(expectedRect, component.color, 1);
expect(drawRectBorderSpy).toHaveBeenCalledTimes(1);
expect(drawRectBorderSpy).toHaveBeenCalledWith(expectedRect);
});
it('can draw overlapping transitions (default)', async () => {
- component.trace = new TraceBuilder()
+ component.trace = new TraceBuilder<PropertyTreeNode>()
.setType(TraceType.TRANSITION)
.setEntries([
- {
- createTime: {unixNanos: 10n},
- finishTime: {unixNanos: 85n},
- } as Transition,
- {
- createTime: {unixNanos: 60n},
- finishTime: {unixNanos: 110n},
- } as Transition,
+ new PropertyTreeBuilder()
+ .setIsRoot(true)
+ .setRootId('TransitionsTraceEntry')
+ .setName('transition')
+ .setChildren([
+ {
+ name: 'wmData',
+ children: [{name: 'finishTimeNs', value: 85n}],
+ },
+ {
+ name: 'shellData',
+ children: [{name: 'dispatchTimeNs', value: 10n}],
+ },
+ {name: 'aborted', value: false},
+ ])
+ .build(),
+ new PropertyTreeBuilder()
+ .setIsRoot(true)
+ .setRootId('TransitionsTraceEntry')
+ .setName('transition')
+ .setChildren([
+ {
+ name: 'wmData',
+ children: [{name: 'finishTimeNs', value: 110n}],
+ },
+ {
+ name: 'shellData',
+ children: [{name: 'dispatchTimeNs', value: 60n}],
+ },
+ {name: 'aborted', value: false},
+ ])
+ .build(),
])
.setTimestamps([new RealTimestamp(10n), new RealTimestamp(60n)])
.build();
@@ -284,17 +372,41 @@
});
it('can draw overlapping transitions (contained)', async () => {
- component.trace = new TraceBuilder()
+ component.trace = new TraceBuilder<PropertyTreeNode>()
.setType(TraceType.TRANSITION)
.setEntries([
- {
- createTime: {unixNanos: 10n},
- finishTime: {unixNanos: 85n},
- } as Transition,
- {
- createTime: {unixNanos: 35n},
- finishTime: {unixNanos: 60n},
- } as Transition,
+ new PropertyTreeBuilder()
+ .setIsRoot(true)
+ .setRootId('TransitionsTraceEntry')
+ .setName('transition')
+ .setChildren([
+ {
+ name: 'wmData',
+ children: [{name: 'finishTimeNs', value: 85n}],
+ },
+ {
+ name: 'shellData',
+ children: [{name: 'dispatchTimeNs', value: 10n}],
+ },
+ {name: 'aborted', value: false},
+ ])
+ .build(),
+ new PropertyTreeBuilder()
+ .setIsRoot(true)
+ .setRootId('TransitionsTraceEntry')
+ .setName('transition')
+ .setChildren([
+ {
+ name: 'wmData',
+ children: [{name: 'finishTimeNs', value: 60n}],
+ },
+ {
+ name: 'shellData',
+ children: [{name: 'dispatchTimeNs', value: 35n}],
+ },
+ {name: 'aborted', value: false},
+ ])
+ .build(),
])
.setTimestamps([new RealTimestamp(10n), new RealTimestamp(35n)])
.build();
@@ -331,14 +443,28 @@
});
it('can draw aborted transitions', async () => {
- component.trace = new TraceBuilder()
+ component.trace = new TraceBuilder<PropertyTreeNode>()
.setType(TraceType.TRANSITION)
.setEntries([
- {
- createTime: {unixNanos: 35n},
- finishTime: {unixNanos: 85n},
- aborted: true,
- } as Transition,
+ new PropertyTreeBuilder()
+ .setIsRoot(true)
+ .setRootId('TransitionsTraceEntry')
+ .setName('transition')
+ .setChildren([
+ {
+ name: 'wmData',
+ value: null,
+ },
+ {
+ name: 'shellData',
+ children: [
+ {name: 'dispatchTimeNs', value: 35n},
+ {name: 'abortTimeNs', value: 85n},
+ ],
+ },
+ {name: 'aborted', value: true},
+ ])
+ .build(),
])
.setTimestamps([new RealTimestamp(35n)])
.build();
@@ -363,18 +489,28 @@
);
});
- it('does not render transition with min creation time', async () => {
- component.trace = new TraceBuilder()
+ it('does not render transition with create time but no dispatch time', async () => {
+ component.trace = new TraceBuilder<PropertyTreeNode>()
.setType(TraceType.TRANSITION)
.setEntries([
- {
- createTime: {unixNanos: 10n, isMin: true},
- finishTime: {unixNanos: 30n},
- } as Transition,
+ new PropertyTreeBuilder()
+ .setIsRoot(true)
+ .setRootId('TransitionsTraceEntry')
+ .setName('transition')
+ .setChildren([
+ {
+ name: 'wmData',
+ children: [
+ {name: 'createTimeNs', value: 10n},
+ {name: 'finishTimeNs', value: 85n},
+ ],
+ },
+ {name: 'aborted', value: false},
+ ])
+ .build(),
])
.setTimestamps([new RealTimestamp(10n)])
.build();
- component.shouldNotRenderEntries.push(0);
component.selectionRange = {from: new RealTimestamp(10n), to: new RealTimestamp(110n)};
const drawRectSpy = spyOn(component.canvasDrawer, 'drawRect');
diff --git a/tools/winscope/src/app/components/timeline/mini-timeline/drawer/mini_timeline_drawer_input.ts b/tools/winscope/src/app/components/timeline/mini-timeline/drawer/mini_timeline_drawer_input.ts
index 7ce94f2..49b304b 100644
--- a/tools/winscope/src/app/components/timeline/mini-timeline/drawer/mini_timeline_drawer_input.ts
+++ b/tools/winscope/src/app/components/timeline/mini-timeline/drawer/mini_timeline_drawer_input.ts
@@ -1,11 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {Transformer} from 'app/components/timeline/mini-timeline/transformer';
+import {Segment} from 'app/components/timeline/segment';
+import {TimelineUtils} from 'app/components/timeline/timeline_utils';
import {TimelineData} from 'app/timeline_data';
-import {ElapsedTimestamp, RealTimestamp, TimeRange, Timestamp, TimestampType} from 'common/time';
-import {Transition} from 'flickerlib/common';
+import {assertDefined} from 'common/assert_utils';
+import {TimeRange, Timestamp} from 'common/time';
import {Trace, TraceEntry} from 'trace/trace';
import {Traces} from 'trace/traces';
import {TraceType} from 'trace/trace_type';
-import {Segment} from '../../segment';
-import {Transformer} from '../transformer';
+import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
import {MiniCanvasDrawerData, TimelineEntries} from './mini_canvas_drawer_data';
export class MiniTimelineDrawerInput {
@@ -47,11 +65,13 @@
await Promise.all(
this.traces.mapTrace(async (trace, type) => {
- const activeEntry = this.timelineData.findCurrentEntryFor(trace.type);
+ const activeEntry = this.timelineData.findCurrentEntryFor(
+ trace.type
+ ) as TraceEntry<PropertyTreeNode>;
if (type === TraceType.TRANSITION) {
// Transition trace is a special case, with entries with time ranges
- const transitionTrace = this.traces.getTrace(type)!;
+ const transitionTrace = assertDefined(this.traces.getTrace(type));
transformedTraceSegments.set(trace.type, {
points: [],
activePoint: undefined,
@@ -78,7 +98,7 @@
private async transformTransitionTraceTimestamps(
transformer: Transformer,
- trace: Trace<Transition>
+ trace: Trace<PropertyTreeNode>
): Promise<Segment[]> {
const promises: Array<Promise<Segment | undefined>> = [];
trace.forEachEntry((entry) => {
@@ -90,27 +110,21 @@
private async transformTransitionEntry(
transformer: Transformer,
- entry: TraceEntry<Transition>
+ entry: TraceEntry<PropertyTreeNode>
): Promise<Segment | undefined> {
- const transition = await entry.getValue();
- let createTime: Timestamp;
- let finishTime: Timestamp;
+ const transition: PropertyTreeNode = await entry.getValue();
- if (transition.createTime.isMin || transition.finishTime.isMax) {
+ const timeRange = TimelineUtils.getTimeRangeForTransition(
+ transition,
+ entry.getTimestamp().getType(),
+ this.selection
+ );
+
+ if (!timeRange) {
return undefined;
}
- if (entry.getTimestamp().getType() === TimestampType.REAL) {
- createTime = new RealTimestamp(BigInt(transition.createTime.unixNanos.toString()));
- finishTime = new RealTimestamp(BigInt(transition.finishTime.unixNanos.toString()));
- } else if (entry.getTimestamp().getType() === TimestampType.ELAPSED) {
- createTime = new ElapsedTimestamp(BigInt(transition.createTime.elapsedNanos.toString()));
- finishTime = new ElapsedTimestamp(BigInt(transition.finishTime.elapsedNanos.toString()));
- } else {
- throw new Error('Unspported timestamp type');
- }
-
- return {from: transformer.transform(createTime), to: transformer.transform(finishTime)};
+ return {from: transformer.transform(timeRange.from), to: transformer.transform(timeRange.to)};
}
private transformTraceTimestamps(transformer: Transformer, trace: Trace<{}>): number[] {
diff --git a/tools/winscope/src/parsers/perfetto/parser_transitions.ts b/tools/winscope/src/parsers/perfetto/parser_transitions.ts
index 4e8cb74..fb1876c 100644
--- a/tools/winscope/src/parsers/perfetto/parser_transitions.ts
+++ b/tools/winscope/src/parsers/perfetto/parser_transitions.ts
@@ -15,15 +15,6 @@
*/
import {assertDefined} from 'common/assert_utils';
import {TimestampType} from 'common/time';
-import {
- CrossPlatform,
- ShellTransitionData,
- Timestamp,
- Transition,
- TransitionChange,
- TransitionType,
- WmTransitionData,
-} from 'flickerlib/common';
import {ParserTransitionsUtils} from 'parsers/transitions/parser_transitions_utils';
import {perfetto} from 'protos/transitions/latest/static';
import {TraceFile} from 'trace/trace_file';
@@ -33,7 +24,7 @@
import {AbstractParser} from './abstract_parser';
import {FakeProtoBuilder} from './fake_proto_builder';
-export class ParserTransitions extends AbstractParser<Transition> {
+export class ParserTransitions extends AbstractParser<PropertyTreeNode> {
private handlerIdToName: {[id: number]: string} | undefined = undefined;
constructor(traceFile: TraceFile, traceProcessor: WasmEngineProxy) {
@@ -44,7 +35,7 @@
return TraceType.TRANSITION;
}
- override async getEntry(index: number, timestampType: TimestampType): Promise<Transition> {
+ override async getEntry(index: number, timestampType: TimestampType): Promise<PropertyTreeNode> {
const transitionProto = await this.queryTransition(index);
if (this.handlerIdToName === undefined) {
@@ -53,35 +44,7 @@
handlers.forEach((it) => (assertDefined(this.handlerIdToName)[it.id] = it.name));
}
- return new Transition(
- Number(transitionProto.id),
- new WmTransitionData(
- this.toTimestamp(transitionProto.createTimeNs?.toString()),
- this.toTimestamp(transitionProto.sendTimeNs?.toString()),
- this.toTimestamp(transitionProto.wmAbortTimeNs?.toString()),
- this.toTimestamp(transitionProto.finishTimeNs?.toString()),
- this.toTimestamp(transitionProto.startingWindowRemoveTimeNs?.toString()),
- transitionProto.startTransactionId.toString(),
- transitionProto.finishTransactionId.toString(),
- TransitionType.Companion.fromInt(Number(transitionProto.type)),
- transitionProto.targets.map(
- (it: perfetto.protos.ShellTransition.ITarget) =>
- new TransitionChange(
- TransitionType.Companion.fromInt(Number(it.mode)),
- Number(it.layerId),
- Number(it.windowId)
- )
- )
- ),
- new ShellTransitionData(
- this.toTimestamp(transitionProto.dispatchTimeNs?.toString()),
- this.toTimestamp(transitionProto.mergeRequestTimeNs?.toString()),
- this.toTimestamp(transitionProto.mergeTimeNs?.toString()),
- this.toTimestamp(transitionProto.shellAbortTimeNs?.toString()),
- this.handlerIdToName[Number(transitionProto.handler)],
- transitionProto.mergeTarget ? Number(transitionProto.mergeTarget) : null
- )
- );
+ return this.makePropertiesTree(timestampType, transitionProto);
}
protected override getTableName(): string {
@@ -125,17 +88,6 @@
return ParserTransitionsUtils.makeTransitionPropertiesTree(shellEntryTree, wmEntryTree);
}
- private toTimestamp(n: string | undefined): Timestamp | null {
- if (n === undefined) {
- return null;
- }
-
- const realToElapsedTimeOffsetNs = assertDefined(this.realToElapsedTimeOffsetNs);
- const unixNs = BigInt(n) + realToElapsedTimeOffsetNs;
-
- return CrossPlatform.timestamp.fromString(n.toString(), null, unixNs.toString());
- }
-
private async queryTransition(index: number): Promise<perfetto.protos.ShellTransition> {
const protoBuilder = new FakeProtoBuilder();
diff --git a/tools/winscope/src/parsers/perfetto/parser_transitions_test.ts b/tools/winscope/src/parsers/perfetto/parser_transitions_test.ts
index 84b4324..2096f03 100644
--- a/tools/winscope/src/parsers/perfetto/parser_transitions_test.ts
+++ b/tools/winscope/src/parsers/perfetto/parser_transitions_test.ts
@@ -15,14 +15,14 @@
*/
import {assertDefined} from 'common/assert_utils';
import {ElapsedTimestamp, RealTimestamp, TimestampType} from 'common/time';
-import {Transition, TransitionType} from 'flickerlib/common';
import {UnitTestUtils} from 'test/unit/utils';
import {Parser} from 'trace/parser';
import {TraceType} from 'trace/trace_type';
+import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
describe('Perfetto ParserTransitions', () => {
describe('valid trace', () => {
- let parser: Parser<Transition>;
+ let parser: Parser<PropertyTreeNode>;
beforeAll(async () => {
parser = await UnitTestUtils.getPerfettoParser(
@@ -57,30 +57,51 @@
it('decodes transition properties', async () => {
const entry = await parser.getEntry(0, TimestampType.REAL);
+ const wmDataNode = assertDefined(entry.getChildByName('wmData'));
+ const shellDataNode = assertDefined(entry.getChildByName('shellData'));
- expect(entry.id).toEqual(32);
- expect(entry.createTime.elapsedNanos.toString()).toEqual('479583450794');
- expect(entry.sendTime.elapsedNanos.toString()).toEqual('479596405791');
- expect(entry.abortTime).toEqual(null);
- expect(entry.finishTime.elapsedNanos.toString()).toEqual('480124777862');
- expect(entry.startingWindowRemoveTime.elapsedNanos.toString()).toEqual('479719652658');
- expect(entry.dispatchTime.elapsedNanos.toString()).toEqual('479602824452');
- expect(entry.mergeRequestTime).toEqual(null);
- expect(entry.mergeTime).toEqual(null);
- expect(entry.shellAbortTime).toEqual(null);
- expect(entry.startTransactionId.toString()).toEqual('5811090758076');
- expect(entry.finishTransactionId.toString()).toEqual('5811090758077');
- expect(entry.type).toEqual(TransitionType.OPEN);
- expect(entry.mergeTarget).toEqual(null);
- expect(entry.handler).toEqual('com.android.wm.shell.transition.DefaultMixedHandler');
- expect(entry.merged).toEqual(false);
- expect(entry.played).toEqual(true);
- expect(entry.aborted).toEqual(false);
- expect(entry.changes.length).toEqual(2);
- expect(entry.changes[0].layerId).toEqual(398);
- expect(entry.changes[1].layerId).toEqual(47);
- expect(entry.changes[0].transitMode).toEqual(TransitionType.TO_FRONT);
- expect(entry.changes[1].transitMode).toEqual(TransitionType.TO_BACK);
+ expect(entry.getChildByName('id')?.getValue()).toEqual(32n);
+ expect(wmDataNode.getChildByName('createTimeNs')?.formattedValue()).toEqual(
+ '2023-11-21T13:38:23.083364560'
+ );
+ expect(wmDataNode.getChildByName('sendTimeNs')?.formattedValue()).toEqual(
+ '2023-11-21T13:38:23.096319557'
+ );
+ expect(wmDataNode.getChildByName('finishTimeNs')?.formattedValue()).toEqual(
+ '2023-11-21T13:38:23.624691628'
+ );
+ expect(entry.getChildByName('merged')?.getValue()).toBeFalse();
+ expect(entry.getChildByName('played')?.getValue()).toBeTrue();
+ expect(entry.getChildByName('aborted')?.getValue()).toBeFalse();
+
+ expect(
+ assertDefined(wmDataNode.getChildByName('startingWindowRemoveTimeNs')).formattedValue()
+ ).toEqual('2023-11-21T13:38:23.219566424');
+ expect(
+ assertDefined(wmDataNode.getChildByName('startTransactionId')).formattedValue()
+ ).toEqual('5811090758076');
+ expect(
+ assertDefined(wmDataNode.getChildByName('finishTransactionId')).formattedValue()
+ ).toEqual('5811090758077');
+ expect(assertDefined(wmDataNode.getChildByName('type')).formattedValue()).toEqual('OPEN');
+
+ const targets = assertDefined(wmDataNode.getChildByName('targets')).getAllChildren();
+ expect(targets.length).toEqual(2);
+ expect(assertDefined(targets[0].getChildByName('layerId')).formattedValue()).toEqual('398');
+ expect(assertDefined(targets[1].getChildByName('layerId')).formattedValue()).toEqual('47');
+ expect(assertDefined(targets[0].getChildByName('mode')).formattedValue()).toEqual('TO_FRONT');
+ expect(assertDefined(targets[1].getChildByName('mode')).formattedValue()).toEqual('TO_BACK');
+
+ expect(
+ assertDefined(shellDataNode.getChildByName('dispatchTimeNs')).formattedValue()
+ ).toEqual('2023-11-21T13:38:23.102738218');
+ expect(shellDataNode.getChildByName('mergeRequestTime')).toBeUndefined();
+ expect(shellDataNode.getChildByName('mergeTime')).toBeUndefined();
+ expect(shellDataNode.getChildByName('abortTimeNs')).toBeUndefined();
+ expect(shellDataNode.getChildByName('mergeTarget')).toBeUndefined();
+ expect(assertDefined(shellDataNode.getChildByName('handler')).formattedValue()).toEqual(
+ 'com.android.wm.shell.transition.DefaultMixedHandler'
+ );
});
});
});
diff --git a/tools/winscope/src/parsers/transitions/parser_transitions_shell.ts b/tools/winscope/src/parsers/transitions/parser_transitions_shell.ts
index 4dddd63..5babf30 100644
--- a/tools/winscope/src/parsers/transitions/parser_transitions_shell.ts
+++ b/tools/winscope/src/parsers/transitions/parser_transitions_shell.ts
@@ -16,7 +16,6 @@
import {assertDefined} from 'common/assert_utils';
import {ElapsedTimestamp, RealTimestamp, Timestamp, TimestampType} from 'common/time';
-import {CrossPlatform, ShellTransitionData, Transition, WmTransitionData} from 'flickerlib/common';
import {AbstractParser} from 'parsers/abstract_parser';
import root from 'protos/transitions/udc/json';
import {com} from 'protos/transitions/udc/static';
@@ -25,13 +24,14 @@
import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
import {ParserTransitionsUtils} from './parser_transitions_utils';
-export class ParserTransitionsShell extends AbstractParser {
+export class ParserTransitionsShell extends AbstractParser<PropertyTreeNode> {
private static readonly WmShellTransitionsTraceProto = root.lookupType(
'com.android.wm.shell.WmShellTransitionTraceProto'
);
private realToElapsedTimeOffsetNs: undefined | bigint;
private handlerMapping: undefined | {[key: number]: string};
+ protected override shouldAddDefaultsToProto = false;
constructor(trace: TraceFile) {
super(trace);
@@ -61,98 +61,37 @@
index: number,
timestampType: TimestampType,
entryProto: com.android.wm.shell.ITransition
- ): Transition {
- return this.parseShellTransitionEntry(entryProto);
+ ): PropertyTreeNode {
+ return this.makePropertiesTree(timestampType, entryProto);
}
- override getTimestamp(type: TimestampType, decodedEntry: Transition): undefined | Timestamp {
- decodedEntry = this.parseShellTransitionEntry(decodedEntry);
-
+ override getTimestamp(
+ type: TimestampType,
+ entry: com.android.wm.shell.ITransition
+ ): undefined | Timestamp {
+ // for consistency with all transitions, elapsed nanos are defined as shell dispatch time else 0n
+ const decodedEntry = this.processDecodedEntry(0, type, entry);
+ const dispatchTimeLong = decodedEntry
+ .getChildByName('shellData')
+ ?.getChildByName('dispatchTimeNs')
+ ?.getValue();
+ const timestampNs = dispatchTimeLong ? BigInt(dispatchTimeLong.toString()) : 0n;
if (type === TimestampType.ELAPSED) {
- return new ElapsedTimestamp(BigInt(decodedEntry.timestamp.elapsedNanos.toString()));
+ return new ElapsedTimestamp(timestampNs);
}
-
if (type === TimestampType.REAL) {
- return new RealTimestamp(BigInt(decodedEntry.timestamp.unixNanos.toString()));
+ return new RealTimestamp(timestampNs + assertDefined(this.realToElapsedTimeOffsetNs));
}
-
- throw new Error('Timestamp type unsupported');
+ throw Error('Timestamp type unsupported');
}
protected getMagicNumber(): number[] | undefined {
return [0x09, 0x57, 0x4d, 0x53, 0x54, 0x52, 0x41, 0x43, 0x45]; // .WMSTRACE
}
- private parseShellTransitionEntry(entry: com.android.wm.shell.ITransition): Transition {
- this.validateShellTransitionEntry(entry);
-
- if (this.realToElapsedTimeOffsetNs === undefined) {
- throw new Error('missing realToElapsedTimeOffsetNs');
- }
-
- let dispatchTime = null;
- if (entry.dispatchTimeNs && BigInt(entry.dispatchTimeNs.toString()) !== 0n) {
- const unixNs = BigInt(entry.dispatchTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
- dispatchTime = CrossPlatform.timestamp.fromString(
- entry.dispatchTimeNs.toString(),
- null,
- unixNs.toString()
- );
- }
-
- let mergeRequestTime = null;
- if (entry.mergeRequestTimeNs && BigInt(entry.mergeRequestTimeNs.toString()) !== 0n) {
- const unixNs = BigInt(entry.mergeRequestTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
- mergeRequestTime = CrossPlatform.timestamp.fromString(
- entry.mergeRequestTimeNs.toString(),
- null,
- unixNs.toString()
- );
- }
-
- let mergeTime = null;
- if (entry.mergeTimeNs && BigInt(entry.mergeTimeNs.toString()) !== 0n) {
- const unixNs = BigInt(entry.mergeTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
- mergeTime = CrossPlatform.timestamp.fromString(
- entry.mergeTimeNs.toString(),
- null,
- unixNs.toString()
- );
- }
-
- let abortTime = null;
- if (entry.abortTimeNs && BigInt(entry.abortTimeNs.toString()) !== 0n) {
- const unixNs = BigInt(entry.abortTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
- abortTime = CrossPlatform.timestamp.fromString(
- entry.abortTimeNs.toString(),
- null,
- unixNs.toString()
- );
- }
-
- const mergeTarget = entry.mergeTarget ? entry.mergeTarget : null;
-
- if (this.handlerMapping === undefined) {
- throw new Error('Missing handler mapping!');
- }
-
- return new Transition(
- entry.id,
- new WmTransitionData(),
- new ShellTransitionData(
- dispatchTime,
- mergeRequestTime,
- mergeTime,
- abortTime,
- this.handlerMapping[assertDefined(entry.handler)],
- mergeTarget
- )
- );
- }
-
private validateShellTransitionEntry(entry: com.android.wm.shell.ITransition) {
if (entry.id === 0) {
- throw new Error('Entry need a non null id');
+ throw new Error('Proto needs a non-null id');
}
if (
!entry.dispatchTimeNs &&
@@ -162,6 +101,12 @@
) {
throw new Error('Requires at least one non-null timestamp');
}
+ if (this.realToElapsedTimeOffsetNs === undefined) {
+ throw new Error('missing realToElapsedTimeOffsetNs');
+ }
+ if (this.handlerMapping === undefined) {
+ throw new Error('Missing handler mapping');
+ }
}
private makePropertiesTree(
diff --git a/tools/winscope/src/parsers/transitions/parser_transitions_shell_test.ts b/tools/winscope/src/parsers/transitions/parser_transitions_shell_test.ts
index 0e85f2e..02e1140 100644
--- a/tools/winscope/src/parsers/transitions/parser_transitions_shell_test.ts
+++ b/tools/winscope/src/parsers/transitions/parser_transitions_shell_test.ts
@@ -14,18 +14,20 @@
* limitations under the License.
*/
+import {assertDefined} from 'common/assert_utils';
import {ElapsedTimestamp, RealTimestamp, TimestampType} from 'common/time';
import {UnitTestUtils} from 'test/unit/utils';
import {Parser} from 'trace/parser';
import {TraceType} from 'trace/trace_type';
+import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
describe('ShellFileParserTransitions', () => {
- let parser: Parser<object>;
+ let parser: Parser<PropertyTreeNode>;
beforeAll(async () => {
- parser = await UnitTestUtils.getParser(
+ parser = (await UnitTestUtils.getParser(
'traces/elapsed_and_real_timestamp/shell_transition_trace.pb'
- );
+ )) as Parser<PropertyTreeNode>;
});
it('has expected trace type', () => {
@@ -33,29 +35,28 @@
});
it('provides elapsed timestamps', () => {
- const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
-
- expect(timestamps.length).toEqual(6);
-
+ const timestamps = assertDefined(parser.getTimestamps(TimestampType.ELAPSED));
const expected = [
new ElapsedTimestamp(57649649922341n),
- new ElapsedTimestamp(57649829445249n),
- new ElapsedTimestamp(57649829526223n),
+ new ElapsedTimestamp(0n),
+ new ElapsedTimestamp(0n),
+ new ElapsedTimestamp(57651299086892n),
+ new ElapsedTimestamp(0n),
+ new ElapsedTimestamp(0n),
];
- expect(timestamps.slice(0, 3)).toEqual(expected);
+ expect(timestamps).toEqual(expected);
});
it('provides real timestamps', () => {
+ const timestamps = assertDefined(parser.getTimestamps(TimestampType.REAL));
const expected = [
new RealTimestamp(1683188477607285317n),
- new RealTimestamp(1683188477786808225n),
- new RealTimestamp(1683188477786889199n),
+ new RealTimestamp(1683130827957362976n),
+ new RealTimestamp(1683130827957362976n),
+ new RealTimestamp(1683188479256449868n),
+ new RealTimestamp(1683130827957362976n),
+ new RealTimestamp(1683130827957362976n),
];
-
- const timestamps = parser.getTimestamps(TimestampType.REAL)!;
-
- expect(timestamps.length).toEqual(6);
-
- expect(timestamps.slice(0, 3)).toEqual(expected);
+ expect(timestamps).toEqual(expected);
});
});
diff --git a/tools/winscope/src/parsers/transitions/parser_transitions_wm.ts b/tools/winscope/src/parsers/transitions/parser_transitions_wm.ts
index 4bf330b..670b277 100644
--- a/tools/winscope/src/parsers/transitions/parser_transitions_wm.ts
+++ b/tools/winscope/src/parsers/transitions/parser_transitions_wm.ts
@@ -14,15 +14,8 @@
* limitations under the License.
*/
+import {assertDefined} from 'common/assert_utils';
import {ElapsedTimestamp, RealTimestamp, Timestamp, TimestampType} from 'common/time';
-import {
- CrossPlatform,
- ShellTransitionData,
- Transition,
- TransitionChange,
- TransitionType,
- WmTransitionData,
-} from 'flickerlib/common';
import {AbstractParser} from 'parsers/abstract_parser';
import root from 'protos/transitions/udc/json';
import {com} from 'protos/transitions/udc/static';
@@ -31,12 +24,13 @@
import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
import {ParserTransitionsUtils} from './parser_transitions_utils';
-export class ParserTransitionsWm extends AbstractParser {
+export class ParserTransitionsWm extends AbstractParser<PropertyTreeNode> {
private static readonly TransitionTraceProto = root.lookupType(
'com.android.server.wm.shell.TransitionTraceProto'
);
private realToElapsedTimeOffsetNs: undefined | bigint;
+ protected override shouldAddDefaultsToProto = false;
constructor(trace: TraceFile) {
super(trace);
@@ -50,8 +44,8 @@
index: number,
timestampType: TimestampType,
entryProto: com.android.server.wm.shell.ITransition
- ): Transition {
- return this.parseWmTransitionEntry(entryProto);
+ ): PropertyTreeNode {
+ return this.makePropertiesTree(timestampType, entryProto);
}
override decodeTrace(buffer: Uint8Array): com.android.server.wm.shell.ITransition[] {
@@ -69,126 +63,20 @@
return [0x09, 0x54, 0x52, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45]; // .TRNTRACE
}
- override getTimestamp(type: TimestampType, decodedEntry: Transition): undefined | Timestamp {
- decodedEntry = this.parseWmTransitionEntry(decodedEntry);
-
+ override getTimestamp(
+ type: TimestampType,
+ entry: com.android.server.wm.shell.ITransition
+ ): undefined | Timestamp {
+ // for consistency with all transitions, elapsed nanos are defined as shell dispatch time else 0n
if (type === TimestampType.ELAPSED) {
- return new ElapsedTimestamp(BigInt(decodedEntry.timestamp.elapsedNanos.toString()));
+ return new ElapsedTimestamp(0n);
}
-
if (type === TimestampType.REAL) {
- return new RealTimestamp(BigInt(decodedEntry.timestamp.unixNanos.toString()));
+ return new RealTimestamp(assertDefined(this.realToElapsedTimeOffsetNs));
}
-
throw new Error('Timestamp type unsupported');
}
- private parseWmTransitionEntry(entry: com.android.server.wm.shell.ITransition): Transition {
- this.validateWmTransitionEntry(entry);
- let changes: TransitionChange[] | null;
- if (!entry.targets || entry.targets.length === 0) {
- changes = null;
- } else {
- changes = entry.targets.map(
- (target) =>
- new TransitionChange(
- TransitionType.Companion.fromInt(target.mode),
- target.layerId,
- target.windowId
- )
- );
- }
-
- if (this.realToElapsedTimeOffsetNs === undefined) {
- throw new Error('missing realToElapsedTimeOffsetNs');
- }
-
- let createTime = null;
- if (entry.createTimeNs && BigInt(entry.createTimeNs.toString()) !== 0n) {
- const unixNs = BigInt(entry.createTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
- createTime = CrossPlatform.timestamp.fromString(
- entry.createTimeNs.toString(),
- null,
- unixNs.toString()
- );
- }
-
- let sendTime = null;
- if (entry.sendTimeNs && BigInt(entry.sendTimeNs.toString()) !== 0n) {
- const unixNs = BigInt(entry.sendTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
- sendTime = CrossPlatform.timestamp.fromString(
- entry.sendTimeNs.toString(),
- null,
- unixNs.toString()
- );
- }
-
- let abortTime = null;
- if (entry.abortTimeNs && BigInt(entry.abortTimeNs.toString()) !== 0n) {
- const unixNs = BigInt(entry.abortTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
- abortTime = CrossPlatform.timestamp.fromString(
- entry.abortTimeNs.toString(),
- null,
- unixNs.toString()
- );
- }
-
- let finishTime = null;
- if (entry.finishTimeNs && BigInt(entry.finishTimeNs.toString()) !== 0n) {
- const unixNs = BigInt(entry.finishTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
- finishTime = CrossPlatform.timestamp.fromString(
- entry.finishTimeNs.toString(),
- null,
- unixNs.toString()
- );
- }
-
- const startingWindowRemoveTime = null;
- if (
- entry.startingWindowRemoveTimeNs &&
- BigInt(entry.startingWindowRemoveTimeNs.toString()) !== 0n
- ) {
- const unixNs =
- BigInt(entry.startingWindowRemoveTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
- finishTime = CrossPlatform.timestamp.fromString(
- entry.startingWindowRemoveTimeNs.toString(),
- null,
- unixNs.toString()
- );
- }
-
- let startTransactionId = null;
- if (entry.startTransactionId && BigInt(entry.startTransactionId.toString()) !== 0n) {
- startTransactionId = BigInt(entry.startTransactionId.toString());
- }
-
- let finishTransactionId = null;
- if (entry.finishTransactionId && BigInt(entry.finishTransactionId.toString()) !== 0n) {
- finishTransactionId = BigInt(entry.finishTransactionId.toString());
- }
-
- let type = null;
- if (entry.type !== 0) {
- type = TransitionType.Companion.fromInt(entry.type);
- }
-
- return new Transition(
- entry.id,
- new WmTransitionData(
- createTime,
- sendTime,
- abortTime,
- finishTime,
- startingWindowRemoveTime,
- startTransactionId?.toString(),
- finishTransactionId?.toString(),
- type,
- changes
- ),
- new ShellTransitionData()
- );
- }
-
private validateWmTransitionEntry(entry: com.android.server.wm.shell.ITransition) {
if (entry.id === 0) {
throw new Error('Entry need a non null id');
@@ -196,6 +84,9 @@
if (!entry.createTimeNs && !entry.sendTimeNs && !entry.abortTimeNs && !entry.finishTimeNs) {
throw new Error('Requires at least one non-null timestamp');
}
+ if (this.realToElapsedTimeOffsetNs === undefined) {
+ throw new Error('missing realToElapsedTimeOffsetNs');
+ }
}
private makePropertiesTree(
diff --git a/tools/winscope/src/parsers/transitions/parser_transitions_wm_test.ts b/tools/winscope/src/parsers/transitions/parser_transitions_wm_test.ts
index 06ba000..374b980 100644
--- a/tools/winscope/src/parsers/transitions/parser_transitions_wm_test.ts
+++ b/tools/winscope/src/parsers/transitions/parser_transitions_wm_test.ts
@@ -14,18 +14,20 @@
* limitations under the License.
*/
+import {assertDefined} from 'common/assert_utils';
import {ElapsedTimestamp, RealTimestamp, TimestampType} from 'common/time';
import {UnitTestUtils} from 'test/unit/utils';
import {Parser} from 'trace/parser';
import {TraceType} from 'trace/trace_type';
+import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
describe('WmFileParserTransitions', () => {
- let parser: Parser<object>;
+ let parser: Parser<PropertyTreeNode>;
beforeAll(async () => {
- parser = await UnitTestUtils.getParser(
+ parser = (await UnitTestUtils.getParser(
'traces/elapsed_and_real_timestamp/wm_transition_trace.pb'
- );
+ )) as Parser<PropertyTreeNode>;
});
it('has expected trace type', () => {
@@ -33,29 +35,16 @@
});
it('provides elapsed timestamps', () => {
- const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
-
+ const timestamps = assertDefined(parser.getTimestamps(TimestampType.ELAPSED));
expect(timestamps.length).toEqual(8);
-
- const expected = [
- new ElapsedTimestamp(57649586217344n),
- new ElapsedTimestamp(57649691956439n),
- new ElapsedTimestamp(57650183020323n),
- ];
- expect(timestamps.slice(0, 3)).toEqual(expected);
+ const expected = new ElapsedTimestamp(0n);
+ timestamps.forEach((timestamp) => expect(timestamp).toEqual(expected));
});
it('provides real timestamps', () => {
- const expected = [
- new RealTimestamp(1683188477542869667n),
- new RealTimestamp(1683188477648608762n),
- new RealTimestamp(1683188478139672646n),
- ];
-
- const timestamps = parser.getTimestamps(TimestampType.REAL)!;
-
+ const timestamps = assertDefined(parser.getTimestamps(TimestampType.REAL));
expect(timestamps.length).toEqual(8);
-
- expect(timestamps.slice(0, 3)).toEqual(expected);
+ const expected = new RealTimestamp(1683130827956652323n);
+ timestamps.forEach((timestamp) => expect(timestamp).toEqual(expected));
});
});
diff --git a/tools/winscope/src/parsers/transitions/traces_parser_transitions.ts b/tools/winscope/src/parsers/transitions/traces_parser_transitions.ts
index fcc02d2..7d9085b 100644
--- a/tools/winscope/src/parsers/transitions/traces_parser_transitions.ts
+++ b/tools/winscope/src/parsers/transitions/traces_parser_transitions.ts
@@ -16,7 +16,6 @@
import {assertDefined} from 'common/assert_utils';
import {Timestamp, TimestampType} from 'common/time';
-import {Transition, TransitionsTrace} from 'flickerlib/common';
import {AbstractTracesParser} from 'parsers/abstract_traces_parser';
import {Trace} from 'trace/trace';
import {Traces} from 'trace/traces';
@@ -24,11 +23,11 @@
import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
import {ParserTransitionsUtils} from './parser_transitions_utils';
-export class TracesParserTransitions extends AbstractTracesParser<Transition> {
- private readonly wmTransitionTrace: Trace<object> | undefined;
- private readonly shellTransitionTrace: Trace<object> | undefined;
+export class TracesParserTransitions extends AbstractTracesParser<PropertyTreeNode> {
+ private readonly wmTransitionTrace: Trace<PropertyTreeNode> | undefined;
+ private readonly shellTransitionTrace: Trace<PropertyTreeNode> | undefined;
private readonly descriptors: string[];
- private decodedEntries: Transition[] | undefined;
+ private decodedEntries: PropertyTreeNode[] | undefined;
constructor(traces: Traces) {
super();
@@ -54,19 +53,17 @@
throw new Error('Missing Shell Transition trace');
}
- const wmTransitionEntries: Transition[] = await Promise.all(
+ const wmTransitionEntries: PropertyTreeNode[] = await Promise.all(
this.wmTransitionTrace.mapEntry((entry) => entry.getValue())
);
- const shellTransitionEntries: Transition[] = await Promise.all(
+ const shellTransitionEntries: PropertyTreeNode[] = await Promise.all(
this.shellTransitionTrace.mapEntry((entry) => entry.getValue())
);
- const transitionsTrace = new TransitionsTrace(
- wmTransitionEntries.concat(shellTransitionEntries)
- );
+ const allEntries = wmTransitionEntries.concat(shellTransitionEntries);
- this.decodedEntries = transitionsTrace.asCompressed().entries as Transition[];
+ this.decodedEntries = this.compressEntries(allEntries);
await this.parseTimestamps();
}
@@ -75,7 +72,7 @@
return assertDefined(this.decodedEntries).length;
}
- override getEntry(index: number, timestampType: TimestampType): Promise<Transition> {
+ override getEntry(index: number, timestampType: TimestampType): Promise<PropertyTreeNode> {
const entry = assertDefined(this.decodedEntries)[index];
return Promise.resolve(entry);
}
@@ -88,13 +85,20 @@
return TraceType.TRANSITION;
}
- override getTimestamp(type: TimestampType, transition: Transition): undefined | Timestamp {
- if (type === TimestampType.ELAPSED) {
- return new Timestamp(type, BigInt(transition.timestamp.elapsedNanos.toString()));
- } else if (type === TimestampType.REAL) {
- return new Timestamp(type, BigInt(transition.timestamp.unixNanos.toString()));
- }
- return undefined;
+ override getTimestamp(
+ type: TimestampType,
+ decodedEntry: PropertyTreeNode
+ ): undefined | Timestamp {
+ // for consistency with all transitions, elapsed nanos are defined as shell dispatch time else 0n
+ const realToElapsedTimeOffsetNs = decodedEntry
+ .getChildByName('realToElapsedTimeOffsetNs')
+ ?.getValue();
+ const dispatchTimeLong = decodedEntry
+ .getChildByName('shellData')
+ ?.getChildByName('dispatchTimeNs')
+ ?.getValue();
+ const timestampNs = dispatchTimeLong ? BigInt(dispatchTimeLong.toString()) : 0n;
+ return Timestamp.from(type, timestampNs, realToElapsedTimeOffsetNs);
}
private compressEntries(allTransitions: PropertyTreeNode[]): PropertyTreeNode[] {
@@ -122,9 +126,26 @@
}
private compareByTimestamp(a: PropertyTreeNode, b: PropertyTreeNode): number {
- const aTimestamp = assertDefined(a.getChildByName('timestampNs')).getValue();
- const bTimestamp = assertDefined(b.getChildByName('timestampNs')).getValue();
- return aTimestamp < bTimestamp ? -1 : 1;
+ const aTimestamp = BigInt(
+ assertDefined(a.getChildByName('shellData'))
+ .getChildByName('dispatchTimeNs')
+ ?.getValue()
+ .toString() ?? 0n
+ );
+ const bTimestamp = BigInt(
+ assertDefined(b.getChildByName('shellData'))
+ .getChildByName('dispatchTimeNs')
+ ?.getValue()
+ .toString() ?? 0n
+ );
+ if (aTimestamp !== bTimestamp) {
+ return aTimestamp < bTimestamp ? -1 : 1;
+ }
+ // if dispatchTimeNs not present for both, fallback to id
+ return assertDefined(a.getChildByName('id')).getValue() <
+ assertDefined(b.getChildByName('id')).getValue()
+ ? -1
+ : 1;
}
private mergePartialTransitions(
diff --git a/tools/winscope/src/parsers/transitions/traces_parser_transitions_test.ts b/tools/winscope/src/parsers/transitions/traces_parser_transitions_test.ts
index 7f9dbe0..4620d9f 100644
--- a/tools/winscope/src/parsers/transitions/traces_parser_transitions_test.ts
+++ b/tools/winscope/src/parsers/transitions/traces_parser_transitions_test.ts
@@ -14,20 +14,21 @@
* limitations under the License.
*/
+import {assertDefined} from 'common/assert_utils';
import {Timestamp, TimestampType} from 'common/time';
-import {Transition} from 'flickerlib/common';
import {UnitTestUtils} from 'test/unit/utils';
import {Parser} from 'trace/parser';
import {TraceType} from 'trace/trace_type';
+import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
describe('ParserTransitions', () => {
- let parser: Parser<Transition>;
+ let parser: Parser<PropertyTreeNode>;
beforeAll(async () => {
- parser = await UnitTestUtils.getTracesParser([
+ parser = (await UnitTestUtils.getTracesParser([
'traces/elapsed_and_real_timestamp/wm_transition_trace.pb',
'traces/elapsed_and_real_timestamp/shell_transition_trace.pb',
- ]);
+ ])) as Parser<PropertyTreeNode>;
});
it('has expected trace type', () => {
@@ -35,29 +36,24 @@
});
it('provides elapsed timestamps', () => {
- const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
-
- expect(timestamps.length).toEqual(4);
-
+ const timestamps = assertDefined(parser.getTimestamps(TimestampType.ELAPSED));
const expected = [
- new Timestamp(TimestampType.ELAPSED, 57649586217344n),
- new Timestamp(TimestampType.ELAPSED, 57649691956439n),
- new Timestamp(TimestampType.ELAPSED, 57651263812071n),
+ new Timestamp(TimestampType.ELAPSED, 0n),
+ new Timestamp(TimestampType.ELAPSED, 0n),
+ new Timestamp(TimestampType.ELAPSED, 57649649922341n),
+ new Timestamp(TimestampType.ELAPSED, 57651299086892n),
];
- expect(timestamps.slice(0, 3)).toEqual(expected);
+ expect(timestamps).toEqual(expected);
});
it('provides real timestamps', () => {
+ const timestamps = assertDefined(parser.getTimestamps(TimestampType.REAL));
const expected = [
- new Timestamp(TimestampType.REAL, 1683188477542869667n),
- new Timestamp(TimestampType.REAL, 1683188477648608762n),
- new Timestamp(TimestampType.REAL, 1683188479220464394n),
+ new Timestamp(TimestampType.REAL, 1683130827956652323n),
+ new Timestamp(TimestampType.REAL, 1683130827956652323n),
+ new Timestamp(TimestampType.REAL, 1683188477606574664n),
+ new Timestamp(TimestampType.REAL, 1683188479255739215n),
];
-
- const timestamps = parser.getTimestamps(TimestampType.REAL)!;
-
- expect(timestamps.length).toEqual(4);
-
- expect(timestamps.slice(0, 3)).toEqual(expected);
+ expect(timestamps).toEqual(expected);
});
});
diff --git a/tools/winscope/src/parsers/view_capture/parser_view_capture_window.ts b/tools/winscope/src/parsers/view_capture/parser_view_capture_window.ts
index 27e5439..818dcca 100644
--- a/tools/winscope/src/parsers/view_capture/parser_view_capture_window.ts
+++ b/tools/winscope/src/parsers/view_capture/parser_view_capture_window.ts
@@ -78,7 +78,7 @@
private readonly descriptors: string[],
private readonly frameData: com.android.app.viewcapture.data.IFrameData[],
private readonly traceType: TraceType,
- private readonly realToElapsedTimeOffsetNanos: bigint,
+ private readonly realToElapsedTimeOffsetNs: bigint,
private readonly packageName: string,
private readonly classNames: string[]
) {
@@ -139,7 +139,7 @@
const timestamp = Timestamp.from(
type,
BigInt(assertDefined(entry.timestamp).toString()),
- this.realToElapsedTimeOffsetNanos
+ this.realToElapsedTimeOffsetNs
);
if (timestamp === undefined) {
areTimestampsValid = false;
diff --git a/tools/winscope/src/trace/trace_type.ts b/tools/winscope/src/trace/trace_type.ts
index 2094f14..e033d38 100644
--- a/tools/winscope/src/trace/trace_type.ts
+++ b/tools/winscope/src/trace/trace_type.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import {Cuj, Event, Transition} from 'flickerlib/common';
+import {Cuj, Event} from 'flickerlib/common';
import {LogMessage} from './protolog';
import {ScreenRecordingTraceEntry} from './screen_recording';
import {HierarchyTreeNode} from './tree_node/hierarchy_tree_node';
@@ -62,9 +62,9 @@
[TraceType.INPUT_METHOD_MANAGER_SERVICE]: HierarchyTreeNode;
[TraceType.INPUT_METHOD_SERVICE]: HierarchyTreeNode;
[TraceType.EVENT_LOG]: Event;
- [TraceType.WM_TRANSITION]: object;
- [TraceType.SHELL_TRANSITION]: object;
- [TraceType.TRANSITION]: Transition;
+ [TraceType.WM_TRANSITION]: PropertyTreeNode;
+ [TraceType.SHELL_TRANSITION]: PropertyTreeNode;
+ [TraceType.TRANSITION]: PropertyTreeNode;
[TraceType.CUJS]: Cuj;
[TraceType.TAG]: object;
[TraceType.ERROR]: object;
diff --git a/tools/winscope/src/viewers/viewer_transitions/presenter.ts b/tools/winscope/src/viewers/viewer_transitions/presenter.ts
index 9c58541..9c070bf 100644
--- a/tools/winscope/src/viewers/viewer_transitions/presenter.ts
+++ b/tools/winscope/src/viewers/viewer_transitions/presenter.ts
@@ -15,21 +15,25 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {TimeUtils} from 'common/time_utils';
-import {Transition} from 'flickerlib/common';
import {WinscopeEvent, WinscopeEventType} from 'messaging/winscope_event';
import {CustomQueryType} from 'trace/custom_query';
import {Trace} from 'trace/trace';
import {Traces} from 'trace/traces';
import {TraceEntryFinder} from 'trace/trace_entry_finder';
import {TraceType} from 'trace/trace_type';
+import {Transition} from 'trace/transition';
import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
-import {PropertiesTreeNodeLegacy} from 'viewers/common/ui_tree_utils_legacy';
+import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
+import {Filter} from 'viewers/common/operations/filter';
+import {UiPropertyTreeNode} from 'viewers/common/ui_property_tree_node';
+import {UiTreeFormatter} from 'viewers/common/ui_tree_formatter';
+import {UiTreeUtils} from 'viewers/common/ui_tree_utils';
+import {UpdateTransitionChangesNames} from './operations/update_transition_changes_names';
import {UiData} from './ui_data';
export class Presenter {
private isInitialized = false;
- private transitionTrace: Trace<object>;
+ private transitionTrace: Trace<PropertyTreeNode>;
private surfaceFlingerTrace: Trace<HierarchyTreeNode> | undefined;
private windowManagerTrace: Trace<HierarchyTreeNode> | undefined;
private layerIdToName = new Map<number, string>();
@@ -54,20 +58,17 @@
const entry = TraceEntryFinder.findCorrespondingEntry(this.transitionTrace, event.position);
- this.uiData.selectedTransition = await entry?.getValue();
- if (this.uiData.selectedTransition !== undefined) {
- await this.onTransitionSelected(this.uiData.selectedTransition);
+ const transition = await entry?.getValue();
+ if (transition !== undefined) {
+ this.onTransitionSelected(transition);
}
this.notifyUiDataCallback(this.uiData);
});
}
- async onTransitionSelected(transition: Transition): Promise<void> {
- this.uiData.selectedTransition = transition;
- this.uiData.selectedTransitionPropertiesTree = await this.makeSelectedTransitionPropertiesTree(
- transition
- );
+ onTransitionSelected(transition: PropertyTreeNode): void {
+ this.uiData.selectedTransition = this.makeUiPropertiesTree(transition);
this.notifyUiDataCallback(this.uiData);
}
@@ -98,141 +99,45 @@
}
private async computeUiData(): Promise<UiData> {
- const entryPromises = this.transitionTrace.mapEntry((entry, originalIndex) => {
+ const entryPromises = this.transitionTrace.mapEntry((entry) => {
return entry.getValue();
});
- const transitions = await Promise.all(entryPromises);
-
+ const entries = await Promise.all(entryPromises);
+ const transitions = this.makeTransitions(entries);
const selectedTransition = this.uiData?.selectedTransition ?? undefined;
- const selectedTransitionPropertiesTree =
- this.uiData?.selectedTransitionPropertiesTree ?? undefined;
- const timestampType = this.transitionTrace.getTimestampType();
- if (timestampType === undefined) {
- throw new Error('Missing timestamp type in trace!');
- }
- return new UiData(
- transitions,
- selectedTransition,
- timestampType,
- selectedTransitionPropertiesTree
- );
+ return new UiData(transitions, selectedTransition);
}
- private async makeSelectedTransitionPropertiesTree(
- transition: Transition
- ): Promise<PropertiesTreeNodeLegacy> {
- const changes: PropertiesTreeNodeLegacy[] = [];
+ private makeTransitions(entries: PropertyTreeNode[]): Transition[] {
+ return entries.map((transitionNode) => {
+ const wmDataNode = assertDefined(transitionNode.getChildByName('wmData'));
- for (const change of transition.changes) {
- const layerName = this.layerIdToName.get(change.layerId);
- const windowTitle = this.windowTokenToTitle.get(change.windowId.toString(16));
+ const transition: Transition = {
+ id: assertDefined(transitionNode.getChildByName('id')).getValue(),
+ type: assertDefined(wmDataNode.getChildByName('type')).formattedValue(),
+ sendTime: wmDataNode.getChildByName('sendTimeNs')?.formattedValue(),
+ finishTime: wmDataNode.getChildByName('finishTimeNs')?.formattedValue(),
+ duration: transitionNode.getChildByName('duration')?.formattedValue(),
+ merged: assertDefined(transitionNode.getChildByName('merged')).getValue(),
+ aborted: assertDefined(transitionNode.getChildByName('aborted')).getValue(),
+ played: assertDefined(transitionNode.getChildByName('played')).getValue(),
+ realToElapsedTimeOffsetNs: assertDefined(
+ transitionNode.getChildByName('realToElapsedTimeOffsetNs')
+ ).getValue(),
+ propertiesTree: transitionNode,
+ };
+ return transition;
+ });
+ }
- const layerIdValue = layerName ? `${change.layerId} (${layerName})` : change.layerId;
- const windowIdValue = windowTitle
- ? `0x${change.windowId.toString(16)} (${windowTitle})`
- : `0x${change.windowId.toString(16)}`;
+ private makeUiPropertiesTree(transitionNode: PropertyTreeNode): UiPropertyTreeNode {
+ const tree = UiPropertyTreeNode.from(transitionNode);
- changes.push({
- propertyKey: 'change',
- children: [
- {propertyKey: 'transitMode', propertyValue: change.transitMode},
- {propertyKey: 'layerId', propertyValue: layerIdValue},
- {propertyKey: 'windowId', propertyValue: windowIdValue},
- ],
- });
- }
-
- const properties: PropertiesTreeNodeLegacy[] = [
- {propertyKey: 'id', propertyValue: transition.id},
- {propertyKey: 'type', propertyValue: transition.type},
- {propertyKey: 'aborted', propertyValue: `${transition.aborted}`},
- ];
-
- if (transition.handler) {
- properties.push({propertyKey: 'handler', propertyValue: transition.handler});
- }
-
- const timestampType = this.transitionTrace.getTimestampType();
-
- if (!transition.createTime.isMin) {
- properties.push({
- propertyKey: 'createTime',
- propertyValue: TimeUtils.formattedKotlinTimestamp(transition.createTime, timestampType),
- });
- }
-
- if (!transition.sendTime.isMin) {
- properties.push({
- propertyKey: 'sendTime',
- propertyValue: TimeUtils.formattedKotlinTimestamp(transition.sendTime, timestampType),
- });
- }
-
- if (!transition.dispatchTime.isMin) {
- properties.push({
- propertyKey: 'dispatchTime',
- propertyValue: TimeUtils.formattedKotlinTimestamp(transition.dispatchTime, timestampType),
- });
- }
-
- if (!transition.finishTime.isMax) {
- properties.push({
- propertyKey: 'finishTime',
- propertyValue: TimeUtils.formattedKotlinTimestamp(transition.finishTime, timestampType),
- });
- }
-
- if (transition.mergeRequestTime) {
- properties.push({
- propertyKey: 'mergeRequestTime',
- propertyValue: TimeUtils.formattedKotlinTimestamp(
- transition.mergeRequestTime,
- timestampType
- ),
- });
- }
-
- if (transition.shellAbortTime) {
- properties.push({
- propertyKey: 'shellAbortTime',
- propertyValue: TimeUtils.formattedKotlinTimestamp(transition.shellAbortTime, timestampType),
- });
- }
-
- if (transition.mergeTime) {
- properties.push({
- propertyKey: 'mergeTime',
- propertyValue: TimeUtils.formattedKotlinTimestamp(transition.mergeTime, timestampType),
- });
- }
-
- if (transition.mergeTarget) {
- properties.push({
- propertyKey: 'mergeTarget',
- propertyValue: transition.mergeTarget,
- });
- }
-
- if (transition.startTransactionId !== -1) {
- properties.push({
- propertyKey: 'startTransactionId',
- propertyValue: transition.startTransactionId,
- });
- }
- if (transition.finishTransactionId !== -1) {
- properties.push({
- propertyKey: 'finishTransactionId',
- propertyValue: transition.finishTransactionId,
- });
- }
- if (changes.length > 0) {
- properties.push({propertyKey: 'changes', children: changes});
- }
-
- return {
- children: properties,
- propertyKey: 'Selected Transition',
- };
+ return new UiTreeFormatter<UiPropertyTreeNode>()
+ .setUiTree(tree)
+ .addOperation(new Filter([UiTreeUtils.isNotCalculated], false))
+ .addOperation(new UpdateTransitionChangesNames(this.layerIdToName, this.windowTokenToTitle))
+ .format();
}
}
diff --git a/tools/winscope/src/viewers/viewer_transitions/ui_data.ts b/tools/winscope/src/viewers/viewer_transitions/ui_data.ts
index 131be8e..cf95247 100644
--- a/tools/winscope/src/viewers/viewer_transitions/ui_data.ts
+++ b/tools/winscope/src/viewers/viewer_transitions/ui_data.ts
@@ -14,17 +14,11 @@
* limitations under the License.
*/
-import {TimestampType} from 'common/time';
-import {Transition} from 'flickerlib/common';
-import {PropertiesTreeNodeLegacy} from 'viewers/common/ui_tree_utils_legacy';
+import {Transition} from 'trace/transition';
+import {UiPropertyTreeNode} from 'viewers/common/ui_property_tree_node';
export class UiData {
- constructor(
- public entries: Transition[],
- public selectedTransition: Transition,
- public timestampType: TimestampType,
- public selectedTransitionPropertiesTree?: PropertiesTreeNodeLegacy
- ) {}
+ constructor(public entries: Transition[], public selectedTransition?: UiPropertyTreeNode) {}
- static EMPTY = new UiData([], undefined, TimestampType.REAL, undefined);
+ static EMPTY = new UiData([], undefined);
}
diff --git a/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component.ts b/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component.ts
index a3efd1c..8e8a066 100644
--- a/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component.ts
+++ b/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component.ts
@@ -15,10 +15,8 @@
*/
import {Component, ElementRef, Inject, Input} from '@angular/core';
-import {ElapsedTimestamp, TimestampType} from 'common/time';
-import {TimeUtils} from 'common/time_utils';
-import {Transition} from 'flickerlib/common';
-import {Terminal} from 'viewers/common/ui_tree_utils_legacy';
+import {Transition} from 'trace/transition';
+import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
import {Events} from './events';
import {UiData} from './ui_data';
@@ -28,11 +26,11 @@
<div class="card-grid container">
<div class="entries">
<div class="table-header table-row">
- <div class="id">Id</div>
- <div class="type">Type</div>
- <div class="send-time">Send Time</div>
- <div class="duration">Duration</div>
- <div class="status">Status</div>
+ <div class="id mat-body-2">Id</div>
+ <div class="type mat-body-2">Type</div>
+ <div class="send-time mat-body-2">Send Time</div>
+ <div class="duration mat-body-2">Duration</div>
+ <div class="status mat-body-2">Status</div>
</div>
<cdk-virtual-scroll-viewport itemSize="53" class="scroll">
<div
@@ -47,34 +45,22 @@
<span class="mat-body-1">{{ transition.type }}</span>
</div>
<div class="send-time">
- <span *ngIf="!transition.sendTime.isMin" class="mat-body-1">{{
- formattedTime(transition.sendTime, uiData.timestampType)
- }}</span>
- <span *ngIf="transition.sendTime.isMin"> n/a </span>
+ <span *ngIf="transition.sendTime" class="mat-body-1">{{ transition.sendTime }}</span>
+ <span *ngIf="!transition.sendTime" class="mat-body-1"> n/a </span>
</div>
<div class="duration">
- <span
- *ngIf="!transition.sendTime.isMin && !transition.finishTime.isMax"
- class="mat-body-1"
- >{{
- formattedTimeDiff(
- transition.sendTime,
- transition.finishTime,
- uiData.timestampType
- )
- }}</span
- >
- <span *ngIf="transition.sendTime.isMin || transition.finishTime.isMax">n/a</span>
+ <span *ngIf="transition.duration" class="mat-body-1">{{ transition.duration }}</span>
+ <span *ngIf="!transition.duration" class="mat-body-1"> n/a </span>
</div>
<div class="status">
<div *ngIf="transition.merged">
- <span>MERGED</span>
+ <span class="mat-body-1">MERGED</span>
<mat-icon aria-hidden="false" fontIcon="merge" matTooltip="merged" icon-gray>
</mat-icon>
</div>
<div *ngIf="transition.aborted && !transition.merged">
- <span>ABORTED</span>
+ <span class="mat-body-1">ABORTED</span>
<mat-icon
aria-hidden="false"
fontIcon="close"
@@ -84,7 +70,7 @@
</div>
<div *ngIf="transition.played && !transition.aborted && !transition.merged">
- <span>PLAYED</span>
+ <span class="mat-body-1">PLAYED</span>
<mat-icon
aria-hidden="false"
fontIcon="check"
@@ -101,12 +87,8 @@
<div class="container-properties">
<h3 class="properties-title mat-title">Selected Transition</h3>
- <tree-view-legacy
- [item]="uiData.selectedTransitionPropertiesTree"
- [showNode]="showNode"
- [isLeaf]="isLeaf">
- </tree-view-legacy>
- <div *ngIf="!uiData.selectedTransitionPropertiesTree">No selected transition.</div>
+ <tree-view [node]="uiData.selectedTransition"></tree-view>
+ <div *ngIf="!uiData.selectedTransition" class="mat-body-1">No selected transition.</div>
</div>
</div>
`,
@@ -214,210 +196,39 @@
],
})
export class ViewerTransitionsComponent {
- transitionHeight = '20px';
- transitionDividerWidth = '3px';
-
- constructor(@Inject(ElementRef) elementRef: ElementRef) {
- this.elementRef = elementRef;
- }
+ constructor(@Inject(ElementRef) private elementRef: ElementRef) {}
@Input()
set inputData(data: UiData) {
this.uiData = data;
}
- getMinOfRanges(): bigint {
- if (this.uiData.entries.length === 0) {
- return 0n;
- }
- const minOfRange = bigIntMin(
- ...this.uiData.entries
- .filter((it) => !it.createTime.isMin)
- .map((it) => BigInt(it.createTime.elapsedNanos.toString()))
- );
- return minOfRange;
- }
-
- getMaxOfRanges(): bigint {
- if (this.uiData.entries.length === 0) {
- return 0n;
- }
- const maxOfRange = bigIntMax(
- ...this.uiData.entries
- .filter((it) => !it.finishTime.isMax)
- .map((it) => BigInt(it.finishTime.elapsedNanos.toString()))
- );
- return maxOfRange;
- }
-
- formattedTime(time: any, timestampType: TimestampType): string {
- return TimeUtils.formattedKotlinTimestamp(time, timestampType);
- }
-
- formattedTimeDiff(time1: any, time2: any, timestampType: TimestampType): string {
- const timeDiff = new ElapsedTimestamp(
- BigInt(time2.elapsedNanos.toString()) - BigInt(time1.elapsedNanos.toString())
- );
- return TimeUtils.format(timeDiff);
- }
-
- widthOf(transition: Transition) {
- const fullRange = this.getMaxOfRanges() - this.getMinOfRanges();
-
- let finish = BigInt(transition.finishTime.elapsedNanos.toString());
- if (transition.finishTime.elapsedNanos.isMax) {
- finish = this.getMaxOfRanges();
- }
-
- let start = BigInt(transition.createTime.elapsedNanos.toString());
- if (transition.createTime.elapsedNanos.isMin) {
- start = this.getMinOfRanges();
- }
-
- const minWidthPercent = 0.5;
- return `${Math.max(minWidthPercent, Number((finish - start) * 100n) / Number(fullRange))}%`;
- }
-
- startOf(transition: Transition) {
- const fullRange = this.getMaxOfRanges() - this.getMinOfRanges();
- return `${
- Number(
- (BigInt(transition.createTime.elapsedNanos.toString()) - this.getMinOfRanges()) * 100n
- ) / Number(fullRange)
- }%`;
- }
-
- sendOf(transition: Transition) {
- const fullRange = this.getMaxOfRanges() - this.getMinOfRanges();
- return `${
- Number((BigInt(transition.sendTime.elapsedNanos.toString()) - this.getMinOfRanges()) * 100n) /
- Number(fullRange)
- }%`;
- }
-
onTransitionClicked(transition: Transition): void {
- this.emitEvent(Events.TransitionSelected, transition);
- }
-
- transitionRectStyle(transition: Transition): string {
- if (this.uiData.selectedTransition === transition) {
- return 'fill:rgb(0, 0, 230)';
- } else if (transition.aborted) {
- return 'fill:rgb(255, 0, 0)';
- } else {
- return 'fill:rgb(78, 205, 230)';
- }
- }
-
- transitionDividerRectStyle(transition: Transition): string {
- return 'fill:rgb(255, 0, 0)';
- }
-
- showNode(item: any) {
- return (
- !(item instanceof Terminal) &&
- !(item.name instanceof Terminal) &&
- !(item.propertyKey instanceof Terminal)
- );
- }
-
- isLeaf(item: any) {
- return (
- !item.children ||
- item.children.length === 0 ||
- item.children.filter((c: any) => !(c instanceof Terminal)).length === 0
- );
+ this.emitEvent(Events.TransitionSelected, transition.propertiesTree);
}
isCurrentTransition(transition: Transition): boolean {
- return this.uiData.selectedTransition === transition;
+ return (
+ transition.id ===
+ this.uiData.selectedTransition
+ ?.getChildByName('wmData')
+ ?.getChildByName('id')
+ ?.getValue() ||
+ transition.id ===
+ this.uiData.selectedTransition
+ ?.getChildByName('shellData')
+ ?.getChildByName('id')
+ ?.getValue()
+ );
}
- assignRowsToTransitions(): Map<Transition, number> {
- const fullRange = this.getMaxOfRanges() - this.getMinOfRanges();
- const assignedRows = new Map<Transition, number>();
-
- const sortedTransitions = [...this.uiData.entries].sort((t1, t2) => {
- const diff =
- BigInt(t1.createTime.elapsedNanos.toString()) -
- BigInt(t2.createTime.elapsedNanos.toString());
- if (diff < 0) {
- return -1;
- }
- if (diff > 0) {
- return 1;
- }
- return 0;
- });
-
- const rowFirstAvailableTime = new Map<number, bigint>();
- let rowsUsed = 1;
- rowFirstAvailableTime.set(0, 0n);
-
- for (const transition of sortedTransitions) {
- const start = BigInt(transition.createTime.elapsedNanos.toString());
- const end = BigInt(transition.finishTime.elapsedNanos.toString());
-
- let rowIndexWithSpace = undefined;
- for (let rowIndex = 0; rowIndex < rowsUsed; rowIndex++) {
- if (start > rowFirstAvailableTime.get(rowIndex)!) {
- // current row has space
- rowIndexWithSpace = rowIndex;
- break;
- }
- }
-
- if (rowIndexWithSpace === undefined) {
- rowIndexWithSpace = rowsUsed;
- rowsUsed++;
- }
-
- assignedRows.set(transition, rowIndexWithSpace);
-
- const minimumPaddingBetweenEntries = fullRange / 100n;
-
- rowFirstAvailableTime.set(rowIndexWithSpace, end + minimumPaddingBetweenEntries);
- }
-
- return assignedRows;
- }
-
- timelineRows(): number[] {
- return [...new Set(this.assignRowsToTransitions().values())];
- }
-
- transitionsOnRow(row: number): Transition[] {
- const transitions = [];
- const assignedRows = this.assignRowsToTransitions();
-
- for (const transition of assignedRows.keys()) {
- if (row === assignedRows.get(transition)) {
- transitions.push(transition);
- }
- }
-
- return transitions;
- }
-
- rowsRequiredForTransitions(): number {
- return Math.max(...this.assignRowsToTransitions().values());
- }
-
- emitEvent(event: string, data: any) {
+ emitEvent(event: string, propertiesTree: PropertyTreeNode) {
const customEvent = new CustomEvent(event, {
bubbles: true,
- detail: data,
+ detail: propertiesTree,
});
this.elementRef.nativeElement.dispatchEvent(customEvent);
}
uiData: UiData = UiData.EMPTY;
- private elementRef: ElementRef;
-}
-
-function bigIntMax(...args: Array<bigint>) {
- return args.reduce((m, e) => (e > m ? e : m));
-}
-function bigIntMin(...args: Array<bigint>) {
- return args.reduce((m, e) => (e < m ? e : m));
}
diff --git a/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component_test.ts b/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component_test.ts
index 143a0bf..3c4f110 100644
--- a/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component_test.ts
+++ b/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component_test.ts
@@ -20,24 +20,19 @@
import {MatDividerModule} from '@angular/material/divider';
import {assertDefined} from 'common/assert_utils';
import {TimestampType} from 'common/time';
-import {
- CrossPlatform,
- ShellTransitionData,
- Transition,
- TransitionChange,
- TransitionType,
- WmTransitionData,
-} from 'flickerlib/common';
import {TracePositionUpdate} from 'messaging/winscope_event';
+import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
import {UnitTestUtils} from 'test/unit/utils';
+import {Parser} from 'trace/parser';
import {Trace} from 'trace/trace';
import {Traces} from 'trace/traces';
import {TracePosition} from 'trace/trace_position';
import {TraceType} from 'trace/trace_type';
-import {TreeComponentLegacy} from 'viewers/components/legacy/tree_component';
-import {TreeNodeComponentLegacy} from 'viewers/components/legacy/tree_node_component';
-import {TreeNodeDataViewComponentLegacy} from 'viewers/components/legacy/tree_node_data_view_component';
-import {TreeNodePropertiesDataViewComponentLegacy} from 'viewers/components/legacy/tree_node_properties_data_view_component';
+import {Transition} from 'trace/transition';
+import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
+import {TreeComponent} from 'viewers/components/tree_component';
+import {TreeNodeComponent} from 'viewers/components/tree_node_component';
+import {TreeNodePropertiesDataViewComponent} from 'viewers/components/tree_node_properties_data_view_component';
import {Events} from './events';
import {Presenter} from './presenter';
import {UiData} from './ui_data';
@@ -54,10 +49,9 @@
imports: [MatDividerModule, ScrollingModule],
declarations: [
ViewerTransitionsComponent,
- TreeComponentLegacy,
- TreeNodeComponentLegacy,
- TreeNodeDataViewComponentLegacy,
- TreeNodePropertiesDataViewComponentLegacy,
+ TreeComponent,
+ TreeNodeComponent,
+ TreeNodePropertiesDataViewComponent,
],
schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA],
}).compileComponents();
@@ -77,10 +71,9 @@
it('renders entries', () => {
expect(htmlElement.querySelector('.scroll')).toBeTruthy();
- const entry = htmlElement.querySelector('.scroll .entry');
- expect(entry).toBeTruthy();
- expect(entry!.innerHTML).toContain('TO_FRONT');
- expect(entry!.innerHTML).toContain('10ns');
+ const entry = assertDefined(htmlElement.querySelector('.scroll .entry'));
+ expect(entry.innerHTML).toContain('TO_FRONT');
+ expect(entry.innerHTML).toContain('10ns');
});
it('shows message when no transition is selected', () => {
@@ -103,29 +96,29 @@
expect(emitEventSpy).not.toHaveBeenCalled();
const id0 = assertDefined(entry1.querySelector('.id')).textContent;
- expect(id0).toBe('0');
+ expect(id0).toEqual('0');
entry1.click();
fixture.detectChanges();
expect(emitEventSpy).toHaveBeenCalled();
expect(emitEventSpy).toHaveBeenCalledWith(Events.TransitionSelected, jasmine.any(Object));
- expect(emitEventSpy.calls.mostRecent().args[1].id).toBe(0);
+ expect(emitEventSpy.calls.mostRecent().args[1].getChildByName('id')?.getValue()).toEqual(0);
const id1 = assertDefined(entry2.querySelector('.id')).textContent;
- expect(id1).toBe('1');
+ expect(id1).toEqual('1');
entry2.click();
fixture.detectChanges();
expect(emitEventSpy).toHaveBeenCalled();
expect(emitEventSpy).toHaveBeenCalledWith(Events.TransitionSelected, jasmine.any(Object));
- expect(emitEventSpy.calls.mostRecent().args[1].id).toBe(1);
+ expect(emitEventSpy.calls.mostRecent().args[1].getChildByName('id')?.getValue()).toEqual(1);
});
it('updates tree view on TracePositionUpdate event', async () => {
- const parser = await UnitTestUtils.getTracesParser([
+ const parser = (await UnitTestUtils.getTracesParser([
'traces/elapsed_and_real_timestamp/wm_transition_trace.pb',
'traces/elapsed_and_real_timestamp/shell_transition_trace.pb',
- ]);
+ ])) as Parser<PropertyTreeNode>;
const trace = Trace.fromParser(parser, TimestampType.REAL);
const traces = new Traces();
traces.setTrace(TraceType.TRANSITION, trace);
@@ -141,13 +134,17 @@
const selectedTransitionEntry = assertDefined(
traces.getTrace(TraceType.TRANSITION)?.getEntry(2)
);
- const selectedTransition = (await selectedTransitionEntry.getValue()) as Transition;
+ const selectedTransition = await selectedTransitionEntry.getValue();
+ const selectedTransitionId = assertDefined(selectedTransition.getChildByName('id')).getValue();
await presenter.onAppEvent(
new TracePositionUpdate(TracePosition.fromTraceEntry(selectedTransitionEntry))
);
- expect(component.uiData.selectedTransition.id).toBe(selectedTransition.id);
- expect(component.uiData.selectedTransitionPropertiesTree).toBeTruthy();
+ expect(
+ assertDefined(
+ component.uiData.selectedTransition?.getChildByName('wmData')?.getChildByName('id')
+ ).getValue()
+ ).toEqual(selectedTransitionId);
fixture.detectChanges();
@@ -155,7 +152,7 @@
fixture.nativeElement.querySelector('.container-properties')
) as any as HTMLElement;
const textContentWithoutWhitespaces = treeView.textContent?.replace(/(\s|\t|\n)*/g, '');
- expect(textContentWithoutWhitespaces).toContain(`id:${selectedTransition.id}`);
+ expect(textContentWithoutWhitespaces).toContain(`id:${selectedTransitionId}`);
});
});
@@ -163,54 +160,37 @@
let mockTransitionIdCounter = 0;
const transitions = [
- createMockTransition(10, 20, 30, mockTransitionIdCounter++),
- createMockTransition(40, 42, 50, mockTransitionIdCounter++),
- createMockTransition(45, 46, 49, mockTransitionIdCounter++),
- createMockTransition(55, 58, 70, mockTransitionIdCounter++),
+ createMockTransition(20, 30, mockTransitionIdCounter++),
+ createMockTransition(42, 50, mockTransitionIdCounter++),
+ createMockTransition(46, 49, mockTransitionIdCounter++),
+ createMockTransition(58, 70, mockTransitionIdCounter++),
];
- const selectedTransition = undefined;
- const selectedTransitionPropertiesTree = undefined;
- const timestampType = TimestampType.REAL;
-
- return new UiData(
- transitions,
- selectedTransition,
- timestampType,
- selectedTransitionPropertiesTree
- );
+ return new UiData(transitions, undefined);
}
function createMockTransition(
- createTimeNanos: number,
sendTimeNanos: number,
finishTimeNanos: number,
id: number
): Transition {
- const createTime = CrossPlatform.timestamp.fromString(createTimeNanos.toString(), null, null);
- const sendTime = CrossPlatform.timestamp.fromString(sendTimeNanos.toString(), null, null);
- const abortTime = null;
- const finishTime = CrossPlatform.timestamp.fromString(finishTimeNanos.toString(), null, null);
- const startingWindowRemoveTime = null;
+ const transitionTree = new PropertyTreeBuilder()
+ .setIsRoot(true)
+ .setRootId('TransitionTraceEntry')
+ .setName('transition')
+ .setChildren([{name: 'id', value: id}])
+ .build();
- const startTransactionId = '-1';
- const finishTransactionId = '-1';
- const type = TransitionType.TO_FRONT;
- const changes: TransitionChange[] = [];
-
- return new Transition(
+ return {
id,
- new WmTransitionData(
- createTime,
- sendTime,
- abortTime,
- finishTime,
- startingWindowRemoveTime,
- startTransactionId,
- finishTransactionId,
- type,
- changes
- ),
- new ShellTransitionData()
- );
+ type: 'TO_FRONT',
+ sendTime: sendTimeNanos.toString() + 'ns',
+ finishTime: finishTimeNanos.toString() + 'ns',
+ duration: (finishTimeNanos - sendTimeNanos).toString() + 'ns',
+ merged: false,
+ aborted: false,
+ played: false,
+ realToElapsedTimeOffsetNs: undefined,
+ propertiesTree: transitionTree,
+ };
}