• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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/store/in_memory_storage';
19import {Store} from 'common/store/store';
20import {TracePositionUpdate} from 'messaging/winscope_event';
21import {TreeNodeUtils} from 'test/unit/tree_node_utils';
22import {UserNotifierChecker} from 'test/unit/user_notifier_checker';
23import {PropertySource} from 'trace/tree_node/property_tree_node';
24import {
25  AbstractHierarchyViewerPresenter,
26  NotifyHierarchyViewCallbackType,
27} from 'viewers/common/abstract_hierarchy_viewer_presenter';
28import {TextFilter} from 'viewers/common/text_filter';
29import {UiHierarchyTreeNode} from 'viewers/common/ui_hierarchy_tree_node';
30import {UiTreeUtils} from 'viewers/common/ui_tree_utils';
31import {UserOptions} from 'viewers/common/user_options';
32import {RectSpec} from 'viewers/components/rects/rect_spec';
33import {Chip} from './chip';
34import {UiDataHierarchy} from './ui_data_hierarchy';
35
36export abstract class AbstractHierarchyViewerPresenterTest<
37  UiData extends UiDataHierarchy,
38> {
39  execute() {
40    describe('Common tests', () => {
41      let uiData: UiDataHierarchy;
42      let presenter: AbstractHierarchyViewerPresenter<UiData>;
43      let userNotifierChecker: UserNotifierChecker;
44      let storage: InMemoryStorage;
45
46      beforeAll(async () => {
47        jasmine.addCustomEqualityTester(TreeNodeUtils.treeNodeEqualityTester);
48        jasmine.addCustomEqualityTester(chipEqualityTester);
49        userNotifierChecker = new UserNotifierChecker();
50        await this.setUpTestEnvironment();
51      });
52
53      beforeEach(() => {
54        storage = new InMemoryStorage();
55        presenter = this.createPresenter((newData) => {
56          uiData = newData;
57        }, storage);
58      });
59
60      afterEach(() => {
61        userNotifierChecker.expectNone();
62        userNotifierChecker.reset();
63      });
64
65      it('has expected user options', async () => {
66        await presenter.onAppEvent(this.getPositionUpdate());
67        expect(uiData.hierarchyUserOptions).toEqual(this.expectedHierarchyOpts);
68        expect(uiData.propertiesUserOptions).toEqual(
69          this.expectedPropertiesOpts,
70        );
71        expect(uiData.rectsUserOptions).toEqual(this.expectedRectsOpts);
72      });
73
74      it('after highlighting a node, updates properties on position update', async () => {
75        await presenter.onAppEvent(this.getPositionUpdate());
76        const selectedTree = this.getSelectedTreeAfterPositionUpdate();
77        await presenter.onHighlightedNodeChange(selectedTree);
78        this.executePropertiesChecksAfterPositionUpdate(uiData);
79
80        const secondUpdate = this.getSecondPositionUpdate();
81        if (secondUpdate) {
82          await presenter.onAppEvent(secondUpdate);
83          assertDefined(this.executePropertiesChecksAfterSecondPositionUpdate)(
84            uiData,
85          );
86        }
87      });
88
89      it('after highlighting by id, updates properties tree on position update', async () => {
90        await presenter.onAppEvent(this.getPositionUpdate());
91        await presenter.onHighlightedIdChange(
92          this.getSelectedTreeAfterPositionUpdate().id,
93        );
94        this.executePropertiesChecksAfterPositionUpdate(uiData);
95
96        const secondUpdate = this.getSecondPositionUpdate();
97        if (secondUpdate) {
98          await presenter.onAppEvent(secondUpdate);
99          assertDefined(this.executePropertiesChecksAfterSecondPositionUpdate)(
100            uiData,
101          );
102        }
103      });
104
105      it('correctly keeps/discards calculated properties', async () => {
106        await presenter.onPropertiesUserOptionsChange({
107          showDefaults: {name: '', enabled: true},
108        });
109        await presenter.onAppEvent(this.getPositionUpdate());
110        await presenter.onHighlightedIdChange(
111          assertDefined(uiData.hierarchyTrees)[0].id,
112        );
113        const calculatedPropertyInRoot = uiData.propertiesTree?.findDfs(
114          (node) => node.source === PropertySource.CALCULATED,
115        );
116        expect(calculatedPropertyInRoot !== undefined).toEqual(
117          this.keepCalculatedPropertiesInRoot,
118        );
119
120        await presenter.onHighlightedIdChange(
121          this.getSelectedTreeAfterPositionUpdate().id,
122        );
123        const calculatedPropertyInChild = uiData.propertiesTree?.findDfs(
124          (node) => node.source === PropertySource.CALCULATED,
125        );
126        expect(calculatedPropertyInChild !== undefined).toEqual(
127          this.keepCalculatedPropertiesInChild,
128        );
129      });
130
131      if (this.shouldExecuteRectTests) {
132        it('sets properties tree and associated ui data from rect', async () => {
133          await presenter.onAppEvent(this.getPositionUpdate());
134          expect(uiData.rectSpec).toEqual(this.expectedInitialRectSpec);
135
136          const rect = assertDefined(uiData.rectsToDraw?.at(2));
137          await presenter.onHighlightedIdChange(rect.id);
138          expect(uiData.highlightedItem).toEqual(rect.id);
139          const propertiesTree = assertDefined(uiData.propertiesTree);
140          expect(propertiesTree.id).toEqual(rect.id);
141          expect(propertiesTree.getAllChildren().length).toBeGreaterThan(0);
142          assertDefined(this.executeSpecializedChecksForPropertiesFromRect)(
143            uiData,
144          );
145          expect(uiData.rectSpec).toEqual(this.expectedInitialRectSpec);
146
147          await presenter.onHighlightedIdChange(rect.id);
148          expect(uiData.highlightedItem).toEqual('');
149        });
150      }
151
152      if (this.shouldExecuteSimplifyNamesTest) {
153        it('simplifies names in hierarchy tree', async () => {
154          const longName = assertDefined(this.treeNodeLongName);
155          const shortName = assertDefined(this.treeNodeShortName);
156          const userOptions: UserOptions = {
157            simplifyNames: {
158              name: 'Simplify names',
159              enabled: false,
160            },
161          };
162
163          await presenter.onAppEvent(this.getPositionUpdate());
164          const longNameFilter = UiTreeUtils.makeNodeFilter(
165            new TextFilter(longName).getFilterPredicate(),
166          );
167          let nodeWithLongName = assertDefined(
168            assertDefined(uiData.hierarchyTrees)[0].findDfs(longNameFilter),
169          );
170          expect(nodeWithLongName.getDisplayName()).toEqual(shortName);
171          presenter.onPinnedItemChange(nodeWithLongName);
172          expect(uiData.pinnedItems).toEqual([nodeWithLongName]);
173
174          await presenter.onHierarchyUserOptionsChange(userOptions);
175          expect(uiData.hierarchyUserOptions).toEqual(userOptions);
176          nodeWithLongName = assertDefined(
177            assertDefined(uiData.hierarchyTrees)[0].findDfs(longNameFilter),
178          );
179          expect(longName).toContain(nodeWithLongName.getDisplayName());
180          expect(uiData.pinnedItems).toEqual([nodeWithLongName]);
181        });
182      }
183
184      function chipEqualityTester(
185        first: any,
186        second: any,
187      ): boolean | undefined {
188        if (first instanceof Chip || second instanceof Chip) {
189          return (
190            first.short === second.short &&
191            first.long === second.long &&
192            first.type === second.type
193          );
194        }
195        return undefined;
196      }
197    });
198
199    if (this.executeSpecializedTests) {
200      this.executeSpecializedTests();
201    }
202  }
203
204  abstract readonly shouldExecuteRectTests: boolean;
205  abstract readonly shouldExecuteSimplifyNamesTest: boolean;
206  abstract readonly keepCalculatedPropertiesInChild: boolean;
207  abstract readonly keepCalculatedPropertiesInRoot: boolean;
208  abstract readonly expectedHierarchyOpts: UserOptions;
209  abstract readonly expectedPropertiesOpts: UserOptions;
210
211  readonly expectedInitialRectSpec?: RectSpec;
212  readonly expectedRectsOpts?: UserOptions;
213  readonly treeNodeLongName?: string;
214  readonly treeNodeShortName?: string;
215
216  abstract setUpTestEnvironment(): Promise<void>;
217  abstract createPresenter(
218    callback: NotifyHierarchyViewCallbackType<UiData>,
219    storage: Store,
220  ): AbstractHierarchyViewerPresenter<UiData>;
221  abstract createPresenterWithEmptyTrace(
222    callback: NotifyHierarchyViewCallbackType<UiData>,
223  ): AbstractHierarchyViewerPresenter<UiData>;
224  abstract getPositionUpdate(): TracePositionUpdate;
225  abstract getSecondPositionUpdate(): TracePositionUpdate | undefined;
226  abstract getSelectedTree(): UiHierarchyTreeNode;
227  abstract getSelectedTreeAfterPositionUpdate(): UiHierarchyTreeNode;
228  abstract executePropertiesChecksAfterPositionUpdate(
229    uiData: UiDataHierarchy,
230  ): void;
231
232  executeSpecializedChecksForPropertiesFromRect?(uiData: UiDataHierarchy): void;
233  executePropertiesChecksAfterSecondPositionUpdate?(
234    uiData: UiDataHierarchy,
235  ): void;
236  executeSpecializedTests?(): void;
237}
238