1// Copyright (C) 2021 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 fs from 'fs'; 16import path from 'path'; 17import {Browser, Page} from 'puppeteer'; 18 19import {assertExists} from '../base/logging'; 20 21import { 22 compareScreenshots, 23 failIfTraceProcessorHttpdIsActive, 24 getTestTracePath, 25 waitForPerfettoIdle, 26} from './perfetto_ui_test_helper'; 27 28declare let global: {__BROWSER__: Browser;}; 29const browser = assertExists(global.__BROWSER__); 30const expectedScreenshotPath = path.join('test', 'data', 'ui-screenshots'); 31const tmpDir = path.resolve('./ui-test-artifacts'); 32const reportPath = path.join(tmpDir, 'report.txt'); 33 34async function getPage(): Promise<Page> { 35 const pages = (await browser.pages()); 36 expect(pages.length).toBe(1); 37 return pages[pages.length - 1]; 38} 39 40// Executed once at the beginning of the test. Navigates to the UI. 41beforeAll(async () => { 42 await failIfTraceProcessorHttpdIsActive(); 43 jest.setTimeout(60000); 44 const page = await getPage(); 45 await page.setViewport({width: 1920, height: 1080}); 46 47 // Empty the file with collected screenshot diffs 48 fs.writeFileSync(reportPath, ''); 49}); 50 51// After each test (regardless of nesting) capture a screenshot named after the 52// test('') name and compare the screenshot with the expected one in 53// /test/data/ui-screenshots. 54afterEach(async () => { 55 let testName = expect.getState().currentTestName; 56 testName = testName.replace(/[^a-z0-9-]/gmi, '_').toLowerCase(); 57 const page = await getPage(); 58 59 const screenshotName = `ui-${testName}.png`; 60 const actualFilename = path.join(tmpDir, screenshotName); 61 const expectedFilename = path.join(expectedScreenshotPath, screenshotName); 62 await page.screenshot({path: actualFilename}); 63 const rebaseline = process.env['PERFETTO_UI_TESTS_REBASELINE'] === '1'; 64 if (rebaseline) { 65 console.log('Saving reference screenshot into', expectedFilename); 66 fs.copyFileSync(actualFilename, expectedFilename); 67 } else { 68 await compareScreenshots(reportPath, actualFilename, expectedFilename); 69 } 70}); 71 72describe('android_trace_30s', () => { 73 let page: Page; 74 75 beforeAll(async () => { 76 page = await getPage(); 77 await page.goto('http://localhost:10000/?testing=1'); 78 await waitForPerfettoIdle(page); 79 }); 80 81 test('load', async () => { 82 const file = await page.waitForSelector('input.trace_file'); 83 const tracePath = getTestTracePath('example_android_trace_30s.pb'); 84 assertExists(file).uploadFile(tracePath); 85 await waitForPerfettoIdle(page); 86 }); 87 88 test('expand_camera', async () => { 89 await page.click('.main-canvas'); 90 await page.click('h1[title="com.google.android.GoogleCamera 5506"]'); 91 await page.evaluate(() => { 92 document.querySelector('.scrolling-panel-container')!.scrollTo(0, 400); 93 }); 94 await waitForPerfettoIdle(page); 95 }); 96}); 97 98describe('chrome_rendering_desktop', () => { 99 let page: Page; 100 101 beforeAll(async () => { 102 page = await getPage(); 103 await page.goto('http://localhost:10000/?testing=1'); 104 await waitForPerfettoIdle(page); 105 }); 106 107 test('load', async () => { 108 const page = await getPage(); 109 const file = await page.waitForSelector('input.trace_file'); 110 const tracePath = getTestTracePath('chrome_rendering_desktop.pftrace'); 111 assertExists(file).uploadFile(tracePath); 112 await waitForPerfettoIdle(page); 113 }); 114 115 test('expand_browser_proc', async () => { 116 const page = await getPage(); 117 await page.click('.main-canvas'); 118 await page.click('h1[title="Browser 12685"]'); 119 await waitForPerfettoIdle(page); 120 }); 121 122 test('select_slice_with_flows', async () => { 123 const page = await getPage(); 124 const searchInput = '.omnibox input'; 125 await page.focus(searchInput); 126 await page.keyboard.type('GenerateRenderPass'); 127 await waitForPerfettoIdle(page); 128 for (let i = 0; i < 3; i++) { 129 await page.keyboard.type('\n'); 130 } 131 await waitForPerfettoIdle(page); 132 await page.focus('canvas'); 133 await page.keyboard.type('f'); // Zoom to selection 134 await waitForPerfettoIdle(page); 135 }); 136}); 137 138// Tests that chrome traces with missing process/thread names still open 139// correctly in the UI. 140describe('chrome_missing_track_names', () => { 141 let page: Page; 142 143 beforeAll(async () => { 144 page = await getPage(); 145 await page.goto('http://localhost:10000/?testing=1'); 146 await waitForPerfettoIdle(page); 147 }); 148 149 test('load', async () => { 150 const page = await getPage(); 151 const file = await page.waitForSelector('input.trace_file'); 152 const tracePath = getTestTracePath('chrome_missing_track_names.pb.gz'); 153 assertExists(file).uploadFile(tracePath); 154 await waitForPerfettoIdle(page); 155 }); 156}); 157 158describe('routing', () => { 159 describe('open_two_traces_then_go_back', () => { 160 let page: Page; 161 162 beforeAll(async () => { 163 page = await getPage(); 164 await page.goto('http://localhost:10000/?testing=1'); 165 await waitForPerfettoIdle(page); 166 }); 167 168 test('open_first_trace_from_url', async () => { 169 await page.goto( 170 'http://localhost:10000/?testing=1/#!/?url=http://localhost:10000/test/data/chrome_memory_snapshot.pftrace'); 171 await waitForPerfettoIdle(page); 172 }); 173 174 test('open_second_trace_from_url', async () => { 175 await page.goto( 176 'http://localhost:10000/?testing=1#!/?url=http://localhost:10000/test/data/chrome_scroll_without_vsync.pftrace'); 177 await waitForPerfettoIdle(page); 178 }); 179 180 test('access_subpage_then_go_back', async () => { 181 await waitForPerfettoIdle(page); 182 await page.goto( 183 'http://localhost:10000/?testing=1/#!/metrics?local_cache_key=76c25a80-25dd-1eb7-2246-d7b3c7a10f91'); 184 await page.goBack(); 185 await waitForPerfettoIdle(page); 186 }); 187 }); 188 189 describe('start_from_no_trace', () => { 190 let page: Page; 191 192 beforeAll(async () => { 193 page = await getPage(); 194 await page.goto('about:blank'); 195 }); 196 197 test('go_to_page_with_no_trace', async () => { 198 await page.goto('http://localhost:10000/?testing=1#!/info'); 199 await waitForPerfettoIdle(page); 200 }); 201 202 test('open_trace ', async () => { 203 await page.goto( 204 'http://localhost:10000/?testing=1#!/viewer?local_cache_key=76c25a80-25dd-1eb7-2246-d7b3c7a10f91'); 205 await waitForPerfettoIdle(page); 206 }); 207 208 test('refresh', async () => { 209 await page.reload(); 210 await waitForPerfettoIdle(page); 211 }); 212 213 test('open_second_trace', async () => { 214 await page.goto( 215 'http://localhost:10000/?testing=1#!/viewer?local_cache_key=00000000-0000-0000-e13c-bd7db4ff646f'); 216 await waitForPerfettoIdle(page); 217 218 // click on the 'Continue' button in the interstitial 219 await page.click('[id="trace_id_open"]'); 220 await waitForPerfettoIdle(page); 221 }); 222 223 test('go_back_to_first_trace', async () => { 224 await page.goBack(); 225 await waitForPerfettoIdle(page); 226 // click on the 'Continue' button in the interstitial 227 await page.click('[id="trace_id_open"]'); 228 await waitForPerfettoIdle(page); 229 }); 230 231 test('open_invalid_trace', async () => { 232 await page.goto( 233 'http://localhost:10000/?testing=1#!/viewer?local_cache_key=invalid'); 234 await waitForPerfettoIdle(page); 235 }); 236 }); 237 238 describe('navigate', () => { 239 let page: Page; 240 241 beforeAll(async () => { 242 page = await getPage(); 243 await page.goto('http://localhost:10000/?testing=1'); 244 await waitForPerfettoIdle(page); 245 }); 246 247 test('open_trace_from_url', async () => { 248 await page.goto( 249 'http://localhost:10000/?testing=1/#!/?url=http://localhost:10000/test/data/chrome_memory_snapshot.pftrace'); 250 await waitForPerfettoIdle(page); 251 }); 252 253 test('navigate_back_and_forward', async () => { 254 await page.click('[id="info_and_stats"]'); 255 await waitForPerfettoIdle(page); 256 await page.click('[id="metrics"]'); 257 await waitForPerfettoIdle(page); 258 await page.goBack(); 259 await waitForPerfettoIdle(page); 260 await page.goBack(); 261 await waitForPerfettoIdle(page); 262 await page.goForward(); 263 await waitForPerfettoIdle(page); 264 await page.goForward(); 265 await waitForPerfettoIdle(page); 266 }); 267 }); 268 269 test('open_trace_and_go_back_to_landing_page', async () => { 270 const page = await getPage(); 271 await page.goto('http://localhost:10000/?testing=1'); 272 await page.goto( 273 'http://localhost:10000/?testing=1#!/viewer?local_cache_key=76c25a80-25dd-1eb7-2246-d7b3c7a10f91'); 274 await waitForPerfettoIdle(page); 275 await page.goBack(); 276 await waitForPerfettoIdle(page); 277 }); 278 279 test('open_invalid_trace_from_blank_page', async () => { 280 const page = await getPage(); 281 await page.goto('about:blank'); 282 await page.goto( 283 'http://localhost:10000/?testing=1#!/viewer?local_cache_key=invalid'); 284 await waitForPerfettoIdle(page); 285 }); 286}); 287 288// Regression test for b/235335853. 289describe('modal_dialog', () => { 290 let page: Page; 291 292 beforeAll(async () => { 293 page = await getPage(); 294 await page.goto('http://localhost:10000/?testing=1'); 295 await waitForPerfettoIdle(page); 296 }); 297 298 test('show_dialog_1', async () => { 299 await page.click('#keyboard_shortcuts'); 300 await waitForPerfettoIdle(page); 301 }); 302 303 test('dismiss_1', async () => { 304 await page.keyboard.press('Escape'); 305 await waitForPerfettoIdle(page); 306 }); 307 308 test('switch_page_no_dialog', async () => { 309 await page.click('#record_new_trace'); 310 await waitForPerfettoIdle(page); 311 }); 312 313 test('show_dialog_2', async () => { 314 await page.click('#keyboard_shortcuts'); 315 await waitForPerfettoIdle(page); 316 }); 317 318 test('dismiss_2', async () => { 319 await page.keyboard.press('Escape'); 320 await waitForPerfettoIdle(page); 321 }); 322}); 323