• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2022 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 ANY 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 {Rect} from 'common/rect';
20import {TracePositionUpdate} from 'messaging/winscope_event';
21import {TraceBuilder} from 'test/unit/trace_builder';
22import {UnitTestUtils} from 'test/unit/utils';
23import {CustomQueryType} from 'trace/custom_query';
24import {Trace} from 'trace/trace';
25import {Traces} from 'trace/traces';
26import {TraceType} from 'trace/trace_type';
27import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
28import {NotifyHierarchyViewCallbackType} from 'viewers/common/abstract_hierarchy_viewer_presenter';
29import {AbstractHierarchyViewerPresenterTest} from 'viewers/common/abstract_hierarchy_viewer_presenter_test';
30import {DiffType} from 'viewers/common/diff_type';
31import {UiDataHierarchy} from 'viewers/common/ui_data_hierarchy';
32import {UiHierarchyTreeNode} from 'viewers/common/ui_hierarchy_tree_node';
33import {UiTreeUtils} from 'viewers/common/ui_tree_utils';
34import {Presenter} from './presenter';
35import {UiData} from './ui_data';
36
37class PresenterSurfaceFlingerTest extends AbstractHierarchyViewerPresenterTest {
38  private traceSf: Trace<HierarchyTreeNode> | undefined;
39  private positionUpdate: TracePositionUpdate | undefined;
40  private secondPositionUpdate: TracePositionUpdate | undefined;
41  private positionUpdateMultiDisplayEntry: TracePositionUpdate | undefined;
42  private selectedTree: UiHierarchyTreeNode | undefined;
43  private selectedTreeAfterPositionUpdate: UiHierarchyTreeNode | undefined;
44
45  override readonly shouldExecuteFlatTreeTest = true;
46  override readonly shouldExecuteRectTests = true;
47  override readonly shouldExecuteShowDiffTests = true;
48  override readonly shouldExecuteSimplifyNamesTest = true;
49
50  override readonly numberOfDefaultProperties = 34;
51  override readonly numberOfNonDefaultProperties = 22;
52  override readonly expectedFirstRect = new Rect(0, 0, 1080, 2400);
53  override readonly propertiesFilterString = 'bound';
54  override readonly expectedTotalRects = 7;
55  override readonly expectedVisibleRects = 6;
56  override readonly treeNodeLongName =
57    'ActivityRecord{64953af u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity#96';
58  override readonly treeNodeShortName =
59    'ActivityRecord{64953af u0 com.google.(...).NexusLauncherActivity#96';
60  override readonly numberOfFilteredProperties = 3;
61  override readonly hierarchyFilterString = 'Wallpaper';
62  override readonly expectedHierarchyChildrenAfterStringFilter = 4;
63  override readonly propertyWithDiff = 'bounds';
64  override readonly expectedPropertyDiffType = DiffType.ADDED;
65
66  private readonly numberOfFlattenedChildren = 94;
67  private readonly numberOfVisibleChildren = 6;
68  private readonly numberOfNestedChildren = 3;
69
70  override async setUpTestEnvironment(): Promise<void> {
71    this.traceSf = new TraceBuilder<HierarchyTreeNode>()
72      .setType(TraceType.SURFACE_FLINGER)
73      .setEntries([
74        await UnitTestUtils.getLayerTraceEntry(0),
75        await UnitTestUtils.getMultiDisplayLayerTraceEntry(),
76        await UnitTestUtils.getLayerTraceEntry(1),
77      ])
78      .build();
79
80    const firstEntry = this.traceSf.getEntry(0);
81    this.positionUpdate = TracePositionUpdate.fromTraceEntry(firstEntry);
82    this.positionUpdateMultiDisplayEntry = TracePositionUpdate.fromTraceEntry(
83      this.traceSf.getEntry(1),
84    );
85    this.secondPositionUpdate = TracePositionUpdate.fromTraceEntry(
86      this.traceSf.getEntry(2),
87    );
88
89    const firstEntryDataTree = await firstEntry.getValue();
90    const layer = assertDefined(
91      firstEntryDataTree.findDfs(
92        UiTreeUtils.makeIdMatchFilter('53 Dim layer#53'),
93      ),
94    );
95    const selectedTreeParent = UiHierarchyTreeNode.from(
96      assertDefined(layer.getZParent()),
97    );
98    this.selectedTree = assertDefined(
99      selectedTreeParent.getChildByName('Dim layer#53'),
100    );
101    this.selectedTreeAfterPositionUpdate = UiHierarchyTreeNode.from(
102      assertDefined(
103        firstEntryDataTree.findDfs(
104          UiTreeUtils.makeIdMatchFilter('79 Wallpaper BBQ wrapper#79'),
105        ),
106      ),
107    );
108    const rect = assertDefined(
109      this.selectedTreeAfterPositionUpdate.getRects()?.at(0),
110    );
111    Object.assign(rect, {isVisible: false});
112  }
113
114  override createPresenterWithEmptyTrace(
115    callback: NotifyHierarchyViewCallbackType,
116  ): Presenter {
117    const trace = new TraceBuilder<HierarchyTreeNode>()
118      .setType(TraceType.SURFACE_FLINGER)
119      .setEntries([])
120      .build();
121    const traces = new Traces();
122    traces.addTrace(trace);
123    return new Presenter(trace, traces, new InMemoryStorage(), callback);
124  }
125
126  override createPresenter(
127    callback: NotifyHierarchyViewCallbackType,
128  ): Presenter {
129    const traces = new Traces();
130    const trace = assertDefined(this.traceSf);
131    traces.addTrace(trace);
132    return new Presenter(trace, traces, new InMemoryStorage(), callback);
133  }
134
135  override getPositionUpdate(): TracePositionUpdate {
136    return assertDefined(this.positionUpdate);
137  }
138
139  override getSecondPositionUpdate(): TracePositionUpdate {
140    return assertDefined(this.secondPositionUpdate);
141  }
142
143  override getShowDiffPositionUpdate(): TracePositionUpdate {
144    return assertDefined(this.positionUpdate);
145  }
146
147  override getExpectedChildrenBeforeVisibilityFilter(): number {
148    return this.numberOfFlattenedChildren;
149  }
150
151  override getExpectedChildrenAfterVisibilityFilter(): number {
152    return this.numberOfVisibleChildren;
153  }
154
155  override getExpectedChildrenBeforeFlatFilter(): number {
156    return this.numberOfNestedChildren;
157  }
158
159  override getExpectedChildrenAfterFlatFilter(): number {
160    return this.numberOfFlattenedChildren;
161  }
162
163  override getExpectedHierarchyChildrenBeforeStringFilter(): number {
164    return this.numberOfFlattenedChildren;
165  }
166
167  override executeSpecializedChecksForPropertiesFromNode(
168    uiData: UiDataHierarchy,
169  ) {
170    expect(assertDefined((uiData as UiData).curatedProperties).flags).toEqual(
171      'HIDDEN (0x1)',
172    );
173  }
174
175  override getSelectedTree(): UiHierarchyTreeNode {
176    return assertDefined(this.selectedTree);
177  }
178
179  override getSelectedTreeAfterPositionUpdate(): UiHierarchyTreeNode {
180    return assertDefined(this.selectedTreeAfterPositionUpdate);
181  }
182
183  override executeChecksForPropertiesTreeAfterPositionUpdate(
184    uiData: UiDataHierarchy,
185  ) {
186    expect(
187      assertDefined(
188        uiData.propertiesTree
189          ?.getChildByName('metadata')
190          ?.getChildByName('2')
191          ?.getChildByName('byteOffset'),
192      ).formattedValue(),
193    ).toEqual('2919');
194  }
195
196  override executeChecksForPropertiesTreeAfterSecondPositionUpdate(
197    uiData: UiDataHierarchy,
198  ) {
199    expect(
200      assertDefined(
201        uiData.propertiesTree
202          ?.getChildByName('metadata')
203          ?.getChildByName('2')
204          ?.getChildByName('byteOffset'),
205      ).formattedValue(),
206    ).toEqual('44517');
207  }
208
209  override executeSpecializedChecksForPropertiesFromRect(
210    uiData: UiDataHierarchy,
211  ) {
212    const curatedProperties = assertDefined(
213      (uiData as UiData).curatedProperties,
214    );
215    expect(curatedProperties.flags).toEqual('ENABLE_BACKPRESSURE (0x100)');
216    expect(curatedProperties.summary).toEqual([
217      {
218        key: 'Covered by',
219        layerValues: [
220          {
221            layerId: '65',
222            nodeId: '65 ScreenDecorOverlayBottom#65',
223            name: 'ScreenDecorOverlayBottom#65',
224          },
225          {
226            layerId: '62',
227            nodeId: '62 ScreenDecorOverlay#62',
228            name: 'ScreenDecorOverlay#62',
229          },
230          {
231            layerId: '85',
232            nodeId: '85 NavigationBar0#85',
233            name: 'NavigationBar0#85',
234          },
235          {
236            layerId: '89',
237            nodeId: '89 StatusBar#89',
238            name: 'StatusBar#89',
239          },
240        ],
241      },
242    ]);
243  }
244
245  override executeSpecializedTests() {
246    describe('Specialized tests', () => {
247      let presenter: Presenter;
248      let uiData: UiData;
249
250      beforeAll(async () => {
251        await this.setUpTestEnvironment();
252      });
253
254      beforeEach(() => {
255        const notifyViewCallback = (newData: UiData) => {
256          uiData = newData;
257        };
258        presenter = this.createPresenter(
259          notifyViewCallback as NotifyHierarchyViewCallbackType,
260        );
261      });
262
263      it('handles displays with no visible layers', async () => {
264        await presenter?.onAppEvent(
265          assertDefined(this.positionUpdateMultiDisplayEntry),
266        );
267        expect(uiData?.displays?.length).toEqual(5);
268        // we want the displays to be sorted by name
269        expect(uiData?.displays).toEqual([
270          {
271            displayId: '11529215046312967684',
272            groupId: 5,
273            name: 'ClusterOsDouble-VD',
274          },
275          {displayId: '4619827259835644672', groupId: 0, name: 'EMU_display_0'},
276          {displayId: '4619827551948147201', groupId: 2, name: 'EMU_display_1'},
277          {displayId: '4619827124781842690', groupId: 3, name: 'EMU_display_2'},
278          {displayId: '4619827540095559171', groupId: 4, name: 'EMU_display_3'},
279        ]);
280      });
281
282      it('updates view capture package names', async () => {
283        const traceVc = new TraceBuilder<HierarchyTreeNode>()
284          .setType(TraceType.VIEW_CAPTURE)
285          .setEntries([await UnitTestUtils.getViewCaptureEntry()])
286          .setParserCustomQueryResult(CustomQueryType.VIEW_CAPTURE_METADATA, {
287            packageName: 'com.google.android.apps.nexuslauncher',
288            windowName: 'not_used',
289          })
290          .build();
291        const traces = new Traces();
292
293        const traceSf = assertDefined(this.traceSf);
294        traces.addTrace(traceSf);
295        traces.addTrace(traceVc);
296        const notifyViewCallback = (newData: UiData) => {
297          uiData = newData;
298        };
299        const presenter = new Presenter(
300          traceSf,
301          traces,
302          new InMemoryStorage(),
303          notifyViewCallback as NotifyHierarchyViewCallbackType,
304        );
305
306        const firstEntry = traceSf.getEntry(0);
307        const positionUpdate = TracePositionUpdate.fromTraceEntry(firstEntry);
308
309        await presenter.onAppEvent(positionUpdate);
310        expect(
311          uiData.rectsToDraw.filter((rect) => rect.hasContent).length,
312        ).toEqual(1);
313      });
314    });
315  }
316}
317
318describe('PresenterSurfaceFlinger', () => {
319  new PresenterSurfaceFlingerTest().execute();
320});
321