xref: /aosp_15_r20/development/tools/winscope/src/viewers/viewer_transitions/presenter_test.ts (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
1/*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANYf KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import {assertDefined} from 'common/assert_utils';
18import {InMemoryStorage} from 'common/in_memory_storage';
19import {TracePositionUpdate} from 'messaging/winscope_event';
20import {ParserBuilder} from 'test/unit/parser_builder';
21import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
22import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
23import {TracesBuilder} from 'test/unit/traces_builder';
24import {TraceBuilder} from 'test/unit/trace_builder';
25import {UnitTestUtils} from 'test/unit/utils';
26import {Trace} from 'trace/trace';
27import {Traces} from 'trace/traces';
28import {TraceType} from 'trace/trace_type';
29import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
30import {NotifyLogViewCallbackType} from 'viewers/common/abstract_log_viewer_presenter';
31import {AbstractLogViewerPresenterTest} from 'viewers/common/abstract_log_viewer_presenter_test';
32import {LogSelectFilter} from 'viewers/common/log_filters';
33import {LogHeader, UiDataLog} from 'viewers/common/ui_data_log';
34import {Presenter} from './presenter';
35import {UiData} from './ui_data';
36
37class PresenterTransitionsTest extends AbstractLogViewerPresenterTest<UiData> {
38  override readonly expectedHeaders = [
39    {
40      header: new LogHeader({
41        name: 'Id',
42        cssClass: 'transition-id right-align',
43      }),
44    },
45    {
46      header: new LogHeader(
47        {name: 'Type', cssClass: 'transition-type'},
48        new LogSelectFilter(Array.from({length: 2}, () => '')),
49      ),
50      options: ['OPEN', 'TO_FRONT'],
51    },
52    {header: new LogHeader({name: 'Send Time', cssClass: 'send-time time'})},
53    {
54      header: new LogHeader({
55        name: 'Dispatch Time',
56        cssClass: 'dispatch-time time',
57      }),
58    },
59    {
60      header: new LogHeader({
61        name: 'Duration',
62        cssClass: 'duration right-align',
63      }),
64    },
65    {
66      header: new LogHeader(
67        {name: 'Handler', cssClass: 'handler'},
68        new LogSelectFilter(Array.from({length: 3}, () => '')),
69      ),
70      options: [
71        'N/A',
72        'com.android.wm.shell.recents.RecentsTransitionHandler',
73        'com.android.wm.shell.transition.DefaultMixedHandler',
74      ],
75    },
76    {
77      header: new LogHeader(
78        {name: 'Participants', cssClass: 'participants'},
79        new LogSelectFilter(
80          Array.from({length: 12}, () => ''),
81          true,
82          '250',
83          '100%',
84        ),
85      ),
86      options: [
87        '47',
88        '67',
89        '398',
90        '471',
91        '472',
92        '489',
93        '0xc3df4d',
94        '0x5ba3da0',
95        '0x97b5518',
96        '0xa884527',
97        '0xb887160',
98        '0xc5f6ee4',
99      ],
100    },
101    {
102      header: new LogHeader(
103        {name: 'Flags', cssClass: 'flags'},
104        new LogSelectFilter(
105          Array.from({length: 2}, () => ''),
106          true,
107          '250',
108          '100%',
109        ),
110      ),
111      options: ['TRANSIT_FLAG_IS_RECENTS', '0'],
112    },
113    {
114      header: new LogHeader(
115        {name: 'Status', cssClass: 'status right-align'},
116        new LogSelectFilter(Array.from({length: 3}, () => '')),
117      ),
118      options: ['MERGED', 'N/A', 'PLAYED'],
119    },
120  ];
121  private trace: Trace<PropertyTreeNode> | undefined;
122  private positionUpdate: TracePositionUpdate | undefined;
123
124  override async setUpTestEnvironment(): Promise<void> {
125    const parser = await UnitTestUtils.getPerfettoParser(
126      TraceType.TRANSITION,
127      'traces/perfetto/shell_transitions_trace.perfetto-trace',
128    );
129
130    this.trace = new TraceBuilder<PropertyTreeNode>()
131      .setType(TraceType.TRANSITION)
132      .setParser(parser)
133      .build();
134
135    this.positionUpdate = TracePositionUpdate.fromTraceEntry(
136      this.trace.getEntry(0),
137    );
138  }
139
140  override async createPresenterWithEmptyTrace(
141    callback: NotifyLogViewCallbackType<UiData>,
142  ): Promise<Presenter> {
143    const traces = new TracesBuilder()
144      .setEntries(TraceType.TRANSITION, [])
145      .build();
146    const trace = assertDefined(traces.getTrace(TraceType.TRANSITION));
147    return new Presenter(trace, traces, new InMemoryStorage(), callback);
148  }
149
150  override async createPresenter(
151    callback: NotifyLogViewCallbackType<UiData>,
152    trace = this.trace,
153    positionUpdate = assertDefined(this.getPositionUpdate()),
154  ): Promise<Presenter> {
155    const transitionTrace = assertDefined(trace);
156    const traces = new Traces();
157    traces.addTrace(transitionTrace);
158
159    const presenter = new Presenter(
160      transitionTrace,
161      traces,
162      new InMemoryStorage(),
163      callback,
164    );
165    await presenter.onAppEvent(positionUpdate); // trigger initialization
166    return presenter;
167  }
168
169  override getPositionUpdate(): TracePositionUpdate {
170    return assertDefined(this.positionUpdate);
171  }
172
173  override executePropertiesChecksAfterPositionUpdate(uiData: UiDataLog) {
174    expect(uiData.entries.length).toEqual(4);
175
176    const selectedTransition = assertDefined(uiData.propertiesTree);
177    const wmData = assertDefined(selectedTransition.getChildByName('wmData'));
178    expect(wmData.getChildByName('id')?.formattedValue()).toEqual('35');
179    expect(wmData.getChildByName('type')?.formattedValue()).toEqual('OPEN');
180    expect(wmData.getChildByName('createTimeNs')?.formattedValue()).toEqual(
181      '2023-11-21, 13:30:33.176',
182    );
183
184    const dispatchTime = uiData.entries[0].fields[3];
185    expect(dispatchTime?.propagateEntryTimestamp).toBeTrue();
186  }
187
188  override executeSpecializedTests() {
189    describe('Specialized tests', () => {
190      it('robust to corrupted transitions trace', async () => {
191        const timestamp10 = TimestampConverterUtils.makeRealTimestamp(10n);
192        const trace = new TraceBuilder<PropertyTreeNode>()
193          .setType(TraceType.TRANSITION)
194          .setParser(
195            new ParserBuilder<PropertyTreeNode>()
196              .setIsCorrupted(true)
197              .setEntries([
198                new PropertyTreeBuilder()
199                  .setRootId('TransitionsTraceEntry')
200                  .setName('transition0')
201                  .build(),
202              ])
203              .setTimestamps([timestamp10])
204              .build(),
205          )
206          .build();
207        const positionUpdate = TracePositionUpdate.fromTimestamp(timestamp10);
208        let uiData: UiData | undefined;
209        const presenter = await this.createPresenter(
210          (newData) => {
211            uiData = newData;
212          },
213          trace,
214          positionUpdate,
215        );
216        await presenter.onAppEvent(positionUpdate);
217        expect(uiData?.entries).toEqual([]);
218      });
219    });
220  }
221}
222
223describe('PresenterTransitions', () => {
224  new PresenterTransitionsTest().execute();
225});
226