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