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/in_memory_storage'; 19import {TracePositionUpdate} from 'messaging/winscope_event'; 20import {TraceBuilder} from 'test/unit/trace_builder'; 21import {TreeNodeUtils} from 'test/unit/tree_node_utils'; 22import {UnitTestUtils} from 'test/unit/utils'; 23import {Traces} from 'trace/traces'; 24import {ImeTraceType, TraceType} from 'trace/trace_type'; 25import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node'; 26import {PropertyTreeNode} from 'trace/tree_node/property_tree_node'; 27import {ImeUiData} from 'viewers/common/ime_ui_data'; 28import {PresenterInputMethodClients} from 'viewers/viewer_input_method_clients/presenter_input_method_clients'; 29import {PresenterInputMethodManagerService} from 'viewers/viewer_input_method_manager_service/presenter_input_method_manager_service'; 30import {PresenterInputMethodService} from 'viewers/viewer_input_method_service/presenter_input_method_service'; 31import {NotifyHierarchyViewCallbackType} from './abstract_hierarchy_viewer_presenter'; 32import {AbstractHierarchyViewerPresenterTest} from './abstract_hierarchy_viewer_presenter_test'; 33import {AbstractPresenterInputMethod} from './abstract_presenter_input_method'; 34import {UiDataHierarchy} from './ui_data_hierarchy'; 35import {UiHierarchyTreeNode} from './ui_hierarchy_tree_node'; 36import {UiPropertyTreeNode} from './ui_property_tree_node'; 37 38export abstract class AbstractPresenterInputMethodTest extends AbstractHierarchyViewerPresenterTest { 39 private traces: Traces | undefined; 40 private positionUpdate: TracePositionUpdate | undefined; 41 private secondPositionUpdate: TracePositionUpdate | undefined; 42 private selectedTree: UiHierarchyTreeNode | undefined; 43 private entries: Map<TraceType, HierarchyTreeNode> | undefined; 44 45 override readonly shouldExecuteFlatTreeTest = true; 46 override readonly shouldExecuteRectTests = false; 47 override readonly shouldExecuteShowDiffTests = false; 48 override readonly shouldExecuteSimplifyNamesTest = false; 49 50 override readonly hierarchyFilterString = 'Reject all'; 51 override readonly expectedHierarchyChildrenAfterStringFilter = 0; 52 53 override async setUpTestEnvironment(): Promise<void> { 54 let secondEntries: Map<TraceType, HierarchyTreeNode>; 55 [this.entries, secondEntries] = await UnitTestUtils.getImeTraceEntries(); 56 this.traces = new Traces(); 57 const traceEntries = [assertDefined(this.entries.get(this.imeTraceType))]; 58 const secondEntry = secondEntries.get(this.imeTraceType); 59 if (secondEntry) { 60 traceEntries.push(secondEntry); 61 } 62 63 const trace = new TraceBuilder<HierarchyTreeNode>() 64 .setType(this.imeTraceType) 65 .setEntries(traceEntries) 66 .setFrame(0, 0) 67 .build(); 68 this.traces.addTrace(trace); 69 70 const sfEntry = this.entries.get(TraceType.SURFACE_FLINGER); 71 if (sfEntry) { 72 this.traces.addTrace( 73 new TraceBuilder<HierarchyTreeNode>() 74 .setType(TraceType.SURFACE_FLINGER) 75 .setEntries([sfEntry]) 76 .setFrame(0, 0) 77 .build(), 78 ); 79 } 80 81 const wmEntry = this.entries.get(TraceType.WINDOW_MANAGER); 82 if (wmEntry) { 83 this.traces.addTrace( 84 new TraceBuilder<HierarchyTreeNode>() 85 .setType(TraceType.WINDOW_MANAGER) 86 .setEntries([wmEntry]) 87 .setFrame(0, 0) 88 .build(), 89 ); 90 } 91 92 const entry = trace.getEntry(0); 93 this.positionUpdate = TracePositionUpdate.fromTraceEntry(entry); 94 this.secondPositionUpdate = secondEntry 95 ? TracePositionUpdate.fromTraceEntry(trace.getEntry(1)) 96 : undefined; 97 98 this.selectedTree = UiHierarchyTreeNode.from(this.getSelectedNode()); 99 } 100 101 override createPresenterWithEmptyTrace( 102 callback: NotifyHierarchyViewCallbackType, 103 ): AbstractPresenterInputMethod { 104 const trace = new TraceBuilder<HierarchyTreeNode>() 105 .setType(this.imeTraceType) 106 .setEntries([]) 107 .build(); 108 const traces = new Traces(); 109 traces.addTrace(trace); 110 return new this.PresenterInputMethod( 111 trace, 112 traces, 113 new InMemoryStorage(), 114 callback, 115 ); 116 } 117 118 override createPresenter( 119 callback: NotifyHierarchyViewCallbackType, 120 ): AbstractPresenterInputMethod { 121 const traces = assertDefined(this.traces); 122 const trace = assertDefined(traces.getTrace(this.imeTraceType)); 123 return new this.PresenterInputMethod( 124 trace, 125 traces, 126 new InMemoryStorage(), 127 callback, 128 ); 129 } 130 131 override getPositionUpdate(): TracePositionUpdate { 132 return assertDefined(this.positionUpdate); 133 } 134 135 override getSecondPositionUpdate(): TracePositionUpdate | undefined { 136 return this.secondPositionUpdate; 137 } 138 139 override getShowDiffPositionUpdate(): TracePositionUpdate { 140 return assertDefined(this.positionUpdate); 141 } 142 143 override getExpectedChildrenBeforeVisibilityFilter(): number { 144 return this.numberOfFlattenedChildren; 145 } 146 147 override getExpectedChildrenAfterVisibilityFilter(): number { 148 return this.numberOfVisibleChildren; 149 } 150 151 override getExpectedChildrenBeforeFlatFilter(): number { 152 return this.numberOfNestedChildren; 153 } 154 155 override getExpectedChildrenAfterFlatFilter(): number { 156 return this.numberOfFlattenedChildren; 157 } 158 159 override getExpectedHierarchyChildrenBeforeStringFilter(): number { 160 return this.numberOfFlattenedChildren; 161 } 162 163 override getSelectedTree(): UiHierarchyTreeNode { 164 return assertDefined(this.selectedTree); 165 } 166 167 override getSelectedTreeAfterPositionUpdate(): UiHierarchyTreeNode { 168 return assertDefined(this.selectedTree); 169 } 170 171 override executeChecksForPropertiesTreeAfterPositionUpdate( 172 uiData: UiDataHierarchy, 173 ) { 174 const trees = assertDefined(uiData.hierarchyTrees); 175 expect(trees.length).toEqual(this.numberOfNestedChildren); 176 } 177 178 override executeChecksForPropertiesTreeAfterSecondPositionUpdate( 179 uiData: UiDataHierarchy, 180 ) { 181 const trees = assertDefined(uiData.hierarchyTrees); 182 expect(trees.length).toEqual(1); 183 } 184 185 override executeSpecializedTests() { 186 describe('AbstractPresenterInputMethod', () => { 187 let presenter: AbstractPresenterInputMethod; 188 let uiData: ImeUiData; 189 let traces: Traces; 190 let entries: Map<TraceType, HierarchyTreeNode>; 191 let Presenter: 192 | typeof PresenterInputMethodClients 193 | typeof PresenterInputMethodService 194 | typeof PresenterInputMethodManagerService; 195 let imeTraceType: ImeTraceType; 196 197 beforeAll(async () => { 198 jasmine.addCustomEqualityTester(TreeNodeUtils.treeNodeEqualityTester); 199 Presenter = this.PresenterInputMethod; 200 imeTraceType = this.imeTraceType; 201 await this.setUpTestEnvironment(); 202 entries = assertDefined(this.entries); 203 await loadTraces(); 204 }); 205 206 it('is robust to traces without SF', async () => { 207 setUpPresenter([imeTraceType, TraceType.WINDOW_MANAGER]); 208 await presenter.onAppEvent(this.getPositionUpdate()); 209 expect(uiData.hierarchyUserOptions).toBeTruthy(); 210 expect(uiData.propertiesUserOptions).toBeTruthy(); 211 expect(uiData.hierarchyTrees).toBeDefined(); 212 }); 213 214 it('is robust to traces without WM', async () => { 215 setUpPresenter([imeTraceType, TraceType.SURFACE_FLINGER]); 216 await presenter.onAppEvent(this.getPositionUpdate()); 217 expect(uiData.hierarchyUserOptions).toBeTruthy(); 218 expect(uiData.propertiesUserOptions).toBeTruthy(); 219 expect(uiData.hierarchyTrees).toBeDefined(); 220 }); 221 222 it('is robust to traces without WM and SF', async () => { 223 setUpPresenter([imeTraceType]); 224 await presenter.onAppEvent(this.getPositionUpdate()); 225 expect(uiData.hierarchyUserOptions).toBeTruthy(); 226 expect(uiData.propertiesUserOptions).toBeTruthy(); 227 expect(uiData.hierarchyTrees).toBeDefined(); 228 }); 229 230 it('can set new additional properties tree and associated ui data from hierarchy tree node', async () => { 231 setUpPresenter([imeTraceType, TraceType.WINDOW_MANAGER]); 232 expect(uiData.propertiesTree).toBeUndefined(); 233 await presenter.onAppEvent(this.getPositionUpdate()); 234 await presenter.onAdditionalPropertySelected({ 235 name: 'Test Tree', 236 treeNode: this.getSelectedTree(), 237 }); 238 const propertiesTree = assertDefined(uiData.propertiesTree); 239 expect(propertiesTree.getDisplayName()).toEqual('Test Tree'); 240 expect(uiData.highlightedItem).toEqual(this.getSelectedTree().id); 241 }); 242 243 if (this.getPropertiesTree) { 244 it('can set new additional properties tree and associated ui data from property tree node', async () => { 245 const selectedPropertyTree = assertDefined(this.getPropertiesTree)(); 246 if (!selectedPropertyTree) { 247 return; 248 } 249 setUpPresenter([imeTraceType]); 250 expect(uiData.propertiesTree).toBeUndefined(); 251 await presenter.onAppEvent(this.getPositionUpdate()); 252 await presenter.onAdditionalPropertySelected({ 253 name: 'Additional Properties Tree', 254 treeNode: selectedPropertyTree, 255 }); 256 const propertiesTree = assertDefined(uiData.propertiesTree); 257 expect(propertiesTree.getDisplayName()).toEqual( 258 'Additional Properties Tree', 259 ); 260 expect(propertiesTree).toEqual( 261 UiPropertyTreeNode.from(selectedPropertyTree), 262 ); 263 expect(uiData.highlightedItem).toEqual(selectedPropertyTree.id); 264 }); 265 } 266 267 function setUpPresenter(traceTypes: TraceType[]) { 268 traceTypes.forEach((traceType) => { 269 const trace = new TraceBuilder<HierarchyTreeNode>() 270 .setType(traceType) 271 .setEntries([assertDefined(entries.get(traceType))]) 272 .setFrame(0, 0) 273 .build(); 274 275 assertDefined(traces).addTrace(trace); 276 }); 277 presenter = createPresenter(traces); 278 } 279 280 function createPresenter(traces: Traces): AbstractPresenterInputMethod { 281 const callback = (newData: ImeUiData) => { 282 uiData = newData; 283 }; 284 const trace = assertDefined(traces.getTrace(imeTraceType)); 285 return new Presenter( 286 trace, 287 traces, 288 new InMemoryStorage(), 289 callback as NotifyHierarchyViewCallbackType, 290 ); 291 } 292 293 async function loadTraces() { 294 traces = new Traces(); 295 entries = (await UnitTestUtils.getImeTraceEntries())[0]; 296 } 297 }); 298 } 299 300 protected getPropertiesTree?(): PropertyTreeNode; 301 protected abstract getSelectedNode(): HierarchyTreeNode; 302 303 protected abstract readonly numberOfFlattenedChildren: number; 304 protected abstract readonly numberOfVisibleChildren: number; 305 protected abstract readonly numberOfNestedChildren: number; 306 protected abstract readonly PresenterInputMethod: 307 | typeof PresenterInputMethodClients 308 | typeof PresenterInputMethodService 309 | typeof PresenterInputMethodManagerService; 310 protected abstract readonly imeTraceType: ImeTraceType; 311} 312