• 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.d
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 {TraceBuilder} from 'test/unit/trace_builder';
22import {TreeNodeUtils} from 'test/unit/tree_node_utils';
23import {UserNotifierChecker} from 'test/unit/user_notifier_checker';
24import {UnitTestUtils} from 'test/unit/utils';
25import {Traces} from 'trace/traces';
26import {ImeTraceType, TraceType} from 'trace/trace_type';
27import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
28import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
29import {ImeUiData} from 'viewers/common/ime_ui_data';
30import {PresenterInputMethodClients} from 'viewers/viewer_input_method_clients/presenter_input_method_clients';
31import {PresenterInputMethodManagerService} from 'viewers/viewer_input_method_manager_service/presenter_input_method_manager_service';
32import {PresenterInputMethodService} from 'viewers/viewer_input_method_service/presenter_input_method_service';
33import {NotifyHierarchyViewCallbackType} from './abstract_hierarchy_viewer_presenter';
34import {AbstractHierarchyViewerPresenterTest} from './abstract_hierarchy_viewer_presenter_test';
35import {AbstractPresenterInputMethod} from './abstract_presenter_input_method';
36import {VISIBLE_CHIP} from './chip';
37import {UiDataHierarchy} from './ui_data_hierarchy';
38import {UiHierarchyTreeNode} from './ui_hierarchy_tree_node';
39import {UiPropertyTreeNode} from './ui_property_tree_node';
40import {ViewerEvents} from './viewer_events';
41
42export abstract class AbstractPresenterInputMethodTest extends AbstractHierarchyViewerPresenterTest<ImeUiData> {
43  private traces: Traces | undefined;
44  private positionUpdate: TracePositionUpdate | undefined;
45  private secondPositionUpdate: TracePositionUpdate | undefined;
46  private selectedTree: UiHierarchyTreeNode | undefined;
47  private entries: Map<TraceType, HierarchyTreeNode> | undefined;
48
49  override readonly shouldExecuteRectTests = false;
50  override readonly shouldExecuteSimplifyNamesTest = false;
51  override readonly keepCalculatedPropertiesInChild = false;
52  override readonly keepCalculatedPropertiesInRoot = false;
53  override readonly expectedHierarchyOpts = {
54    showOnlyVisible: {
55      name: 'Show only',
56      chip: VISIBLE_CHIP,
57      enabled: false,
58    },
59    simplifyNames: {
60      name: 'Simplify names',
61      enabled: true,
62    },
63    flat: {
64      name: 'Flat',
65      enabled: false,
66    },
67  };
68  override readonly expectedPropertiesOpts = {
69    showDefaults: {
70      name: 'Show defaults',
71      enabled: false,
72      tooltip: `If checked, shows the value of all properties.
73Otherwise, hides all properties whose value is
74the default for its data type.`,
75    },
76  };
77
78  override async setUpTestEnvironment(): Promise<void> {
79    let secondEntries: Map<TraceType, HierarchyTreeNode>;
80    [this.entries, secondEntries] = await UnitTestUtils.getImeTraceEntries();
81    this.traces = new Traces();
82    const traceEntries = [assertDefined(this.entries.get(this.imeTraceType))];
83    const secondEntry = secondEntries.get(this.imeTraceType);
84    if (secondEntry) {
85      traceEntries.push(secondEntry);
86    }
87
88    const trace = new TraceBuilder<HierarchyTreeNode>()
89      .setType(this.imeTraceType)
90      .setEntries(traceEntries)
91      .setFrame(0, 0)
92      .build();
93    this.traces.addTrace(trace);
94
95    const sfEntry = this.entries.get(TraceType.SURFACE_FLINGER);
96    if (sfEntry) {
97      this.traces.addTrace(
98        new TraceBuilder<HierarchyTreeNode>()
99          .setType(TraceType.SURFACE_FLINGER)
100          .setEntries([sfEntry])
101          .setFrame(0, 0)
102          .build(),
103      );
104    }
105
106    const wmEntry = this.entries.get(TraceType.WINDOW_MANAGER);
107    if (wmEntry) {
108      this.traces.addTrace(
109        new TraceBuilder<HierarchyTreeNode>()
110          .setType(TraceType.WINDOW_MANAGER)
111          .setEntries([wmEntry])
112          .setFrame(0, 0)
113          .build(),
114      );
115    }
116
117    const entry = trace.getEntry(0);
118    this.positionUpdate = TracePositionUpdate.fromTraceEntry(entry);
119    this.secondPositionUpdate = secondEntry
120      ? TracePositionUpdate.fromTraceEntry(trace.getEntry(1))
121      : undefined;
122
123    this.selectedTree = UiHierarchyTreeNode.from(this.getSelectedNode());
124  }
125
126  override createPresenterWithEmptyTrace(
127    callback: NotifyHierarchyViewCallbackType<ImeUiData>,
128  ): AbstractPresenterInputMethod {
129    const trace = UnitTestUtils.makeEmptyTrace(this.imeTraceType);
130    const traces = new Traces();
131    traces.addTrace(trace);
132    return new this.PresenterInputMethod(
133      trace,
134      traces,
135      new InMemoryStorage(),
136      callback,
137    );
138  }
139
140  override createPresenter(
141    callback: NotifyHierarchyViewCallbackType<ImeUiData>,
142    storage: Store,
143  ): AbstractPresenterInputMethod {
144    const traces = assertDefined(this.traces);
145    const trace = assertDefined(traces.getTrace(this.imeTraceType));
146    return new this.PresenterInputMethod(trace, traces, storage, callback);
147  }
148
149  override getPositionUpdate(): TracePositionUpdate {
150    return assertDefined(this.positionUpdate);
151  }
152
153  override getSecondPositionUpdate(): TracePositionUpdate | undefined {
154    return this.secondPositionUpdate;
155  }
156
157  override getSelectedTree(): UiHierarchyTreeNode {
158    return assertDefined(this.selectedTree);
159  }
160
161  override getSelectedTreeAfterPositionUpdate(): UiHierarchyTreeNode {
162    return assertDefined(this.selectedTree);
163  }
164
165  override executePropertiesChecksAfterPositionUpdate(uiData: UiDataHierarchy) {
166    const trees = assertDefined(uiData.hierarchyTrees);
167    expect(trees.length).toEqual(this.numberOfNestedChildren);
168  }
169
170  override executePropertiesChecksAfterSecondPositionUpdate(
171    uiData: UiDataHierarchy,
172  ) {
173    const trees = assertDefined(uiData.hierarchyTrees);
174    expect(trees.length).toEqual(1);
175  }
176
177  override executeSpecializedTests() {
178    describe('AbstractPresenterInputMethod', () => {
179      let presenter: AbstractPresenterInputMethod;
180      let uiData: ImeUiData;
181      let traces: Traces;
182      let entries: Map<TraceType, HierarchyTreeNode>;
183      let Presenter:
184        | typeof PresenterInputMethodClients
185        | typeof PresenterInputMethodService
186        | typeof PresenterInputMethodManagerService;
187      let imeTraceType: ImeTraceType;
188      let userNotifierChecker: UserNotifierChecker;
189
190      beforeAll(async () => {
191        jasmine.addCustomEqualityTester(TreeNodeUtils.treeNodeEqualityTester);
192        userNotifierChecker = new UserNotifierChecker();
193        Presenter = this.PresenterInputMethod;
194        imeTraceType = this.imeTraceType;
195        await this.setUpTestEnvironment();
196        entries = assertDefined(this.entries);
197        await loadTraces();
198      });
199
200      afterEach(() => {
201        userNotifierChecker.expectNone();
202        userNotifierChecker.reset();
203      });
204
205      it('adds event listeners', async () => {
206        setUpPresenter([imeTraceType]);
207        const element = document.createElement('div');
208        presenter.addEventListeners(element);
209
210        const spy: jasmine.Spy = spyOn(
211          presenter,
212          'onAdditionalPropertySelected',
213        );
214        const selectedItem = {
215          name: '',
216          treeNode: TreeNodeUtils.makePropertyNode('', '', null),
217        };
218        element.dispatchEvent(
219          new CustomEvent(ViewerEvents.AdditionalPropertySelected, {
220            detail: {selectedItem},
221          }),
222        );
223        expect(spy).toHaveBeenCalledWith(selectedItem);
224      });
225
226      it('is robust to traces without SF', async () => {
227        setUpPresenter([imeTraceType, TraceType.WINDOW_MANAGER]);
228        await presenter.onAppEvent(this.getPositionUpdate());
229        expect(uiData.hierarchyUserOptions).toBeTruthy();
230        expect(uiData.propertiesUserOptions).toBeTruthy();
231        expect(uiData.hierarchyTrees).toBeDefined();
232      });
233
234      it('is robust to traces without WM', async () => {
235        setUpPresenter([imeTraceType, TraceType.SURFACE_FLINGER]);
236        await presenter.onAppEvent(this.getPositionUpdate());
237        expect(uiData.hierarchyUserOptions).toBeTruthy();
238        expect(uiData.propertiesUserOptions).toBeTruthy();
239        expect(uiData.hierarchyTrees).toBeDefined();
240      });
241
242      it('is robust to traces without WM and SF', async () => {
243        setUpPresenter([imeTraceType]);
244        await presenter.onAppEvent(this.getPositionUpdate());
245        expect(uiData.hierarchyUserOptions).toBeTruthy();
246        expect(uiData.propertiesUserOptions).toBeTruthy();
247        expect(uiData.hierarchyTrees).toBeDefined();
248      });
249
250      it('can set new additional properties tree and associated ui data from hierarchy tree node', async () => {
251        setUpPresenter([imeTraceType, TraceType.WINDOW_MANAGER]);
252        expect(uiData.propertiesTree).toBeUndefined();
253        await presenter.onAppEvent(this.getPositionUpdate());
254        await presenter.onAdditionalPropertySelected({
255          name: 'Test Tree',
256          treeNode: this.getSelectedTree(),
257        });
258        expect(assertDefined(uiData.propertiesTree).getDisplayName()).toEqual(
259          'Test Tree',
260        );
261        expect(uiData.highlightedItem).toEqual(this.getSelectedTree().id);
262      });
263
264      it('can set new properties tree and associated ui data from id', async () => {
265        setUpPresenter([imeTraceType, TraceType.WINDOW_MANAGER]);
266        expect(uiData.propertiesTree).toBeUndefined();
267        await presenter.onAppEvent(this.getPositionUpdate());
268
269        const selectedTree = this.getSelectedTree();
270        await presenter.onHighlightedIdChange(selectedTree.id);
271        const propertiesTree = assertDefined(uiData.propertiesTree);
272        expect(propertiesTree.getDisplayName()).toEqual(selectedTree.name);
273        expect(uiData.highlightedItem).toEqual(this.getSelectedTree().id);
274
275        await presenter.onHighlightedIdChange(selectedTree.id);
276        expect(uiData.propertiesTree).toEqual(propertiesTree);
277        expect(uiData.highlightedItem).toEqual('');
278      });
279
280      if (this.getPropertiesTree) {
281        it('can set new additional properties tree and associated ui data from property tree node', async () => {
282          const selectedPropertyTree = assertDefined(this.getPropertiesTree)();
283          if (!selectedPropertyTree) {
284            return;
285          }
286          setUpPresenter([imeTraceType]);
287          expect(uiData.propertiesTree).toBeUndefined();
288          await presenter.onAppEvent(this.getPositionUpdate());
289          await presenter.onAdditionalPropertySelected({
290            name: 'Additional Properties Tree',
291            treeNode: selectedPropertyTree,
292          });
293          const propertiesTree = assertDefined(uiData.propertiesTree);
294          expect(propertiesTree.getDisplayName()).toEqual(
295            'Additional Properties Tree',
296          );
297          expect(propertiesTree).toEqual(
298            UiPropertyTreeNode.from(selectedPropertyTree),
299          );
300          expect(uiData.highlightedItem).toEqual(selectedPropertyTree.id);
301
302          // clears additional property tree selection
303          const selectedTree = this.getSelectedTree();
304          await presenter.onHighlightedIdChange(selectedTree.id);
305          expect(uiData.propertiesTree?.getDisplayName()).toEqual(
306            selectedTree.name,
307          );
308        });
309      }
310
311      function setUpPresenter(traceTypes: TraceType[]) {
312        traceTypes.forEach((traceType) => {
313          const trace = new TraceBuilder<HierarchyTreeNode>()
314            .setType(traceType)
315            .setEntries([assertDefined(entries.get(traceType))])
316            .setFrame(0, 0)
317            .build();
318
319          assertDefined(traces).addTrace(trace);
320        });
321        presenter = createPresenter(traces);
322      }
323
324      function createPresenter(traces: Traces): AbstractPresenterInputMethod {
325        const callback = (newData: ImeUiData) => {
326          uiData = newData;
327        };
328        const trace = assertDefined(traces.getTrace(imeTraceType));
329        return new Presenter(
330          trace,
331          traces,
332          new InMemoryStorage(),
333          callback as NotifyHierarchyViewCallbackType<ImeUiData>,
334        );
335      }
336
337      async function loadTraces() {
338        traces = new Traces();
339        entries = (await UnitTestUtils.getImeTraceEntries())[0];
340      }
341    });
342  }
343
344  protected getPropertiesTree?(): PropertyTreeNode;
345  protected abstract getSelectedNode(): HierarchyTreeNode;
346
347  protected abstract readonly numberOfNestedChildren: number;
348  protected abstract readonly PresenterInputMethod:
349    | typeof PresenterInputMethodClients
350    | typeof PresenterInputMethodService
351    | typeof PresenterInputMethodManagerService;
352  protected abstract readonly imeTraceType: ImeTraceType;
353}
354