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