blob: 11dabda3d2bebb112be2060a5d8a208b5b8d0bff [file] [log] [blame]
/*
* Copyright (C) 2024 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 ANYf KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {assertDefined} from 'common/assert_utils';
import {InMemoryStorage} from 'common/store/in_memory_storage';
import {wait} from 'common/time/time_utils';
import {TracePositionUpdate} from 'messaging/winscope_event';
import {getPerfettoParser} from 'test/unit/fixture_utils';
import {ParserBuilder} from 'test/unit/parser_builder';
import {makeRealTimestamp} from 'test/unit/time_test_helpers';
import {TraceBuilder} from 'test/unit/trace_builder';
import {TracesBuilder} from 'test/unit/traces_builder';
import {Trace} from 'trace_api/trace';
import {TraceType} from 'trace_api/trace_type';
import {Traces} from 'trace_api/traces';
import {HierarchyTreeNode} from 'tree_node/hierarchy_tree_node';
import {NotifyLogViewCallbackType} from 'viewers/common/abstract_log_viewer_presenter';
import {AbstractLogViewerPresenterTest} from 'viewers/common/abstract_log_viewer_presenter_test';
import {LogSelectFilter} from 'viewers/common/log_filters';
import {LogHeader, UiDataLog} from 'viewers/common/ui_data_log';
import {Presenter} from './presenter';
import {UiData} from './ui_data';
class PresenterTransitionsTest extends AbstractLogViewerPresenterTest<UiData> {
override readonly expectedHeaders = [
{
header: new LogHeader({
name: 'Id',
cssClass: 'transition-id right-align',
}),
},
{
header: new LogHeader(
{name: 'Type', cssClass: 'transition-type'},
new LogSelectFilter(Array.from({length: 2}, () => '')),
),
options: ['OPEN', 'TO_FRONT'],
},
{header: new LogHeader({name: 'Send Time', cssClass: 'send-time time'})},
{
header: new LogHeader({
name: 'Dispatch Time',
cssClass: 'dispatch-time time',
}),
},
{
header: new LogHeader({
name: 'Duration',
cssClass: 'duration right-align',
}),
},
{
header: new LogHeader(
{name: 'Handler', cssClass: 'handler'},
new LogSelectFilter(Array.from({length: 3}, () => '')),
),
options: [
'N/A',
'com.android.wm.shell.recents.RecentsTransitionHandler',
'com.android.wm.shell.transition.DefaultMixedHandler',
],
},
{
header: new LogHeader(
{name: 'Participants', cssClass: 'participants'},
new LogSelectFilter(
Array.from({length: 12}, () => ''),
true,
'250',
'100%',
),
),
options: [
'47',
'67',
'398',
'471',
'472',
'489',
'0xc3df4d',
'0x5ba3da0',
'0x97b5518',
'0xa884527',
'0xb887160',
'0xc5f6ee4',
],
},
{
header: new LogHeader(
{name: 'Flags', cssClass: 'flags'},
new LogSelectFilter(
Array.from({length: 2}, () => ''),
true,
'250',
'100%',
),
),
options: ['TRANSIT_FLAG_IS_RECENTS', '0x0'],
},
{
header: new LogHeader(
{name: 'Status', cssClass: 'status right-align'},
new LogSelectFilter(Array.from({length: 3}, () => '')),
),
options: ['MERGED', 'N/A', 'PLAYED'],
},
];
private trace: Trace<HierarchyTreeNode> | undefined;
private positionUpdate: TracePositionUpdate | undefined;
override async setUpTestEnvironment(): Promise<void> {
const parser = await getPerfettoParser(
TraceType.TRANSITION,
'traces/perfetto/shell_transitions_trace.perfetto-trace',
);
this.trace = new TraceBuilder<HierarchyTreeNode>()
.setType(TraceType.TRANSITION)
.setParser(parser)
.build();
this.positionUpdate = TracePositionUpdate.fromTraceEntry(
this.trace.getEntry(0),
);
}
override async createPresenterWithEmptyTrace(
callback: NotifyLogViewCallbackType<UiData>,
): Promise<Presenter> {
const traces = new TracesBuilder()
.setEntries(TraceType.TRANSITION, [])
.build();
const trace = assertDefined(traces.getTrace(TraceType.TRANSITION));
return new Presenter(trace, traces, new InMemoryStorage(), callback);
}
override async createPresenter(
callback: NotifyLogViewCallbackType<UiData>,
trace = this.trace,
positionUpdate = assertDefined(this.getPositionUpdate()),
): Promise<Presenter> {
const transitionTrace = assertDefined(trace);
const traces = new Traces();
traces.addTrace(transitionTrace);
const presenter = new Presenter(
transitionTrace,
traces,
new InMemoryStorage(),
callback,
);
await presenter.onAppEvent(positionUpdate); // trigger initialization
return presenter;
}
override getPositionUpdate(): TracePositionUpdate {
return assertDefined(this.positionUpdate);
}
override executePropertiesChecksAfterPositionUpdate(uiData: UiDataLog) {
expect(uiData.entries.length).toBe(4);
const selectedTransition = assertDefined(uiData.propertiesTree);
expect(selectedTransition.getChildByName('id')?.formattedValue()).toBe(
'32',
);
expect(selectedTransition.getChildByName('type')?.formattedValue()).toBe(
'OPEN',
);
expect(
selectedTransition.getChildByName('createTimeNs')?.formattedValue(),
).toBe('2023-11-21, 13:30:25.429');
const dispatchTimeEntryTs = uiData.entries[0].fields[3];
expect(dispatchTimeEntryTs?.propagateEntryTimestamp).toBeTrue();
const sendTimeNotEntryTs = uiData.entries[0].fields[2];
expect(sendTimeNotEntryTs?.propagateEntryTimestamp).toBeFalse();
const dispatchTimeNotEntryTs = uiData.entries[3].fields[3];
expect(dispatchTimeNotEntryTs?.propagateEntryTimestamp).toBeFalse();
const sendTimeEntryTs = uiData.entries[3].fields[2];
expect(sendTimeEntryTs?.propagateEntryTimestamp).toBeTrue();
}
override executeSpecializedTests() {
describe('Specialized tests', () => {
it('robust to corrupted transitions trace', async () => {
const timestamp10 = makeRealTimestamp(10n);
const trace = new TraceBuilder<HierarchyTreeNode | undefined>()
.setType(TraceType.TRANSITION)
.setParser(
new ParserBuilder<HierarchyTreeNode | undefined>()
.setEntries([undefined])
.setTimestamps([timestamp10])
.build(),
)
.build();
const positionUpdate = TracePositionUpdate.fromTimestamp(timestamp10);
let uiData: UiData | undefined;
const presenter = await this.createPresenter(
(newData) => {
uiData = newData;
},
trace as Trace<HierarchyTreeNode>,
positionUpdate,
);
await presenter.onAppEvent(positionUpdate);
await wait(() => uiData !== undefined && !uiData.isFetchingData);
expect(uiData?.entries).toEqual([]);
});
});
}
}
describe('PresenterTransitions', () => {
new PresenterTransitionsTest().execute();
});