1// Copyright (C) 2024 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import {test, Page, Locator} from '@playwright/test'; 16import {PerfettoTestHelper} from './perfetto_ui_test_helper'; 17 18test.describe.configure({mode: 'parallel'}); 19 20let pth: PerfettoTestHelper; 21let page: Page; 22 23async function clickTableHeader(headerName: string | RegExp) { 24 await page 25 .locator('.pf-content tr.header a.pf-anchor', {hasText: headerName}) 26 .click(); 27} 28 29function getTableCells(text?: string | RegExp): Locator { 30 return page.locator( 31 '.pf-details-shell .generic-table tr:not(.header) a.pf-anchor', 32 { 33 hasText: text, 34 }, 35 ); 36} 37 38async function clickFirstTableCell(text: string | RegExp) { 39 await getTableCells(text).nth(0).click(); 40} 41 42test.beforeEach(async ({browser}, _testInfo) => { 43 page = await browser.newPage(); 44 pth = new PerfettoTestHelper(page); 45}); 46 47test('slices with same name', async () => { 48 await pth.openTraceFile('chrome_scroll_without_vsync.pftrace'); 49 50 const sliceName = 'LatencyInfo.Flow'; 51 await pth.searchSlice(sliceName); 52 await page 53 .locator('.pf-details-shell a.pf-anchor', {hasText: sliceName}) 54 .click(); 55 await pth.clickMenuItem('Slices with the same name'); 56 await clickTableHeader(/^id/); 57 await pth.clickMenuItem('Sort: lowest first'); 58 await pth.waitForIdleAndScreenshot(`slices-with-same-name.png`); 59}); 60 61test('Table interactions', async () => { 62 await pth.openTraceFile('chrome_scroll_without_vsync.pftrace'); 63 64 // Show the slice table via command. 65 await pth.runCommand('perfetto.ShowTable.slice'); 66 // Sort the table by id for consistent ordering. 67 await clickTableHeader(/^id/); 68 await pth.clickMenuItem('Sort: lowest first'); 69 await pth.waitForIdleAndScreenshot(`slices-table.png`); 70 71 // Hide `category` column. 72 await clickTableHeader('category'); 73 await pth.clickMenuItem('Hide'); 74 await pth.waitForIdleAndScreenshot(`slices-table-hide-column.png`); 75 76 // Sort the table by dur in descending order. Note that we must explicitly exclude 77 // the "thread_dur" column, as it also contains "dur" in its name. 78 clickTableHeader(/^dur/); 79 await pth.clickMenuItem('Sort: highest first'); 80 await pth.waitForIdleAndScreenshot(`slices-table-sorted.png`); 81 82 // Filter out all "EventLatency" slices. 83 clickFirstTableCell('EventLatency'); 84 await pth.clickMenuItem('Add filter'); 85 await pth.clickMenuItem('not equals'); 86 await pth.waitForIdleAndScreenshot(`slices-table-filter1.png`); 87 88 // Filter to thread-only slices by clicking on the second NULL value. 89 await getTableCells('NULL').nth(1).click(); 90 await pth.clickMenuItem('Add filter'); 91 await pth.clickMenuItem('is not null'); 92 await pth.waitForIdleAndScreenshot(`slices-table-filter2.png`); 93 94 // Filter to LatencyInfo.Flow events. 95 clickFirstTableCell('LatencyInfo.Flow'); 96 await pth.clickMenuItem('Add filter'); 97 await pth.clickMenuItem(/^equals/); 98 await pth.waitForIdleAndScreenshot(`slices-table-filter3.png`); 99 100 // Add argument. 101 await clickTableHeader(/^name/); 102 await pth.clickMenuItem('Add column'); 103 await pth.clickMenuItem('arg_set_id'); 104 await pth.clickMenuItem('chrome_latency_info.trace_id'); 105 await pth.waitForIdleAndScreenshot(`slices-table-add-argument.png`); 106 107 // Sort by argument. 108 await clickTableHeader('chrome_latency_info.trace_id'); 109 await pth.clickMenuItem('Sort: highest first'); 110 await pth.waitForIdleAndScreenshot(`slices-table-sort-by-argument.png`); 111 112 // Sort by argument. 113 await clickFirstTableCell('3390'); 114 await pth.clickMenuItem('Add filter'); 115 await pth.clickMenuItem('not equals'); 116 await pth.waitForIdleAndScreenshot(`slices-table-filter-by-argument.png`); 117}); 118 119// 120// These tests check that the "go to" functionality for ID columns of the key tables 121// (slice, sched, thread_state, process and thread) —- for example, clicking on a 122// slice id column should update the selection and focus the relevant slice. 123// 124 125test('Go to slice', async () => { 126 await pth.openTraceFile('chrome_scroll_without_vsync.pftrace'); 127 128 // Show the slice table via command. 129 await pth.runCommand('perfetto.ShowTable.slice'); 130 // Sort the table by id for consistent ordering. 131 await clickTableHeader(/^id/); 132 await pth.clickMenuItem('Sort: lowest first'); 133 await pth.waitForIdleAndScreenshot(`open-table.png`); 134 135 // Sort the table by dur in descending order. Note that we must explicitly exclude 136 // the "thread_dur" column, as it also contains "dur" in its name. 137 await clickTableHeader(/^dur/); 138 await pth.clickMenuItem('Sort: highest first'); 139 await pth.waitForIdleAndScreenshot(`sorted.png`); 140 141 // Go to the first slice. 142 await getTableCells().nth(0).click(); 143 await pth.waitForIdleAndScreenshot(`go-to.png`); 144 145 // Go to current selection tab. 146 await pth.switchToTab('Current Selection'); 147 await pth.waitForIdleAndScreenshot(`current-selection.png`); 148}); 149 150test('Go to thread_state', async () => { 151 // Open Android trace with kernel scheduling data. 152 await pth.openTraceFile('api34_startup_cold.perfetto-trace'); 153 154 // Show the slice table via command. 155 await pth.runCommand('perfetto.ShowTable.thread_state'); 156 // Sort the table by id for consistent ordering. 157 await clickTableHeader(/^id/); 158 await pth.clickMenuItem('Sort: lowest first'); 159 await pth.waitForIdleAndScreenshot(`open-table.png`); 160 161 // Sort the table by dur in descending order. Note that we must explicitly exclude 162 // the "thread_dur" column, as it also contains "dur" in its name. 163 await clickTableHeader(/^dur/); 164 await pth.clickMenuItem('Sort: highest first'); 165 await pth.waitForIdleAndScreenshot(`sorted.png`); 166 167 // Filter out sleeps. 168 await clickFirstTableCell(/^S/); 169 await pth.clickMenuItem('Add filter'); 170 await pth.clickMenuItem('not equals'); 171 await pth.waitForIdleAndScreenshot(`filtered.png`); 172 173 // Go to the first thread_state. 174 await getTableCells().nth(0).click(); 175 await pth.waitForIdleAndScreenshot(`go-to.png`); 176 177 // Go to current selection tab. 178 await pth.switchToTab('Current Selection'); 179 await pth.waitForIdleAndScreenshot(`current-selection.png`); 180}); 181 182test('Go to sched', async () => { 183 // Open Android trace with kernel scheduling data. 184 await pth.openTraceFile('api34_startup_cold.perfetto-trace'); 185 186 // Show the slice table via command. 187 await pth.runCommand('perfetto.ShowTable.sched'); 188 // Sort the table by id for consistent ordering. 189 await clickTableHeader(/^id/); 190 await pth.clickMenuItem('Sort: lowest first'); 191 await pth.waitForIdleAndScreenshot(`open-table.png`); 192 193 // Sort the table by dur in descending order. Note that we must explicitly exclude 194 // the "thread_dur" column, as it also contains "dur" in its name. 195 await clickTableHeader(/^dur/); 196 await pth.clickMenuItem('Sort: highest first'); 197 await pth.waitForIdleAndScreenshot(`sorted.png`); 198 199 // Filter out idle. 200 await clickFirstTableCell('swapper'); 201 await pth.clickMenuItem('Add filter'); 202 await getTableCells('120').nth(0).click(); 203 await pth.clickMenuItem('Add filter'); 204 await pth.clickMenuItem('not equals'); 205 await pth.waitForIdleAndScreenshot(`filtered.png`); 206 207 // Go to the first slice. 208 await getTableCells().nth(0).click(); 209 await pth.waitForIdleAndScreenshot(`go-to.png`); 210 211 // Go to current selection tab. 212 await pth.switchToTab('Current Selection'); 213 await pth.waitForIdleAndScreenshot(`current-selection.png`); 214}); 215 216// 217// For process and thread tables, we open a new tab instead of updating selection. 218// 219 220test('Go to process', async () => { 221 await pth.openTraceFile('chrome_scroll_without_vsync.pftrace'); 222 223 // Show the slice table via command. 224 await pth.runCommand('perfetto.ShowTable.process'); 225 // Sort the table by id for consistent ordering. 226 await clickTableHeader(/^upid/); 227 await pth.clickMenuItem('Sort: lowest first'); 228 await pth.waitForIdleAndScreenshot(`open-table.png`); 229 230 // Sort the table by dur in descending order. Note that we must explicitly exclude 231 // the "thread_dur" column, as it also contains "dur" in its name. 232 await clickTableHeader(/^name/); 233 await pth.clickMenuItem('Sort: highest first'); 234 await pth.waitForIdleAndScreenshot(`sorted.png`); 235 236 // Go to the first process. 237 await getTableCells().nth(0).click(); 238 await pth.clickMenuItem('Show process details'); 239 await pth.waitForIdleAndScreenshot(`go-to.png`); 240}); 241 242test('Go to thread', async () => { 243 await pth.openTraceFile('chrome_scroll_without_vsync.pftrace'); 244 245 // Show the slice table via command. 246 await pth.runCommand('perfetto.ShowTable.thread'); 247 // Sort the table by id for consistent ordering. 248 await clickTableHeader(/^utid/); 249 await pth.clickMenuItem('Sort: lowest first'); 250 await pth.waitForIdleAndScreenshot(`open-table.png`); 251 252 // Sort the table by dur in descending order. Note that we must explicitly exclude 253 // the "thread_dur" column, as it also contains "dur" in its name. 254 await clickTableHeader(/^name/); 255 await pth.clickMenuItem('Sort: highest first'); 256 await pth.waitForIdleAndScreenshot(`sorted.png`); 257 258 // Go to the first thread. 259 await getTableCells().nth(0).click(); 260 await pth.clickMenuItem('Show thread details'); 261 await pth.waitForIdleAndScreenshot(`go-to.png`); 262}); 263