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 * as fs from 'fs'; 16import * as path from 'path'; 17import * as puppeteer 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 var global: {__BROWSER__: puppeteer.Browser;}; 29const browser = assertExists(global.__BROWSER__); 30const expectedScreenshotPath = path.join('test', 'data', 'ui-screenshots'); 31 32async function getPage(): Promise<puppeteer.Page> { 33 const pages = (await browser.pages()); 34 expect(pages.length).toBe(1); 35 return pages[pages.length - 1]; 36} 37 38// Executed once at the beginning of the test. Navigates to the UI. 39beforeAll(async () => { 40 await failIfTraceProcessorHttpdIsActive(); 41 jest.setTimeout(60000); 42 const page = await getPage(); 43 await page.setViewport({width: 1920, height: 1080}); 44}); 45 46// After each test (regardless of nesting) capture a screenshot named after the 47// test('') name and compare the screenshot with the expected one in 48// /test/data/ui-screenshots. 49afterEach(async () => { 50 let testName = expect.getState().currentTestName; 51 testName = testName.replace(/[^a-z0-9-]/gmi, '_').toLowerCase(); 52 const page = await getPage(); 53 54 // cwd() is set to //out/ui when running tests, just create a subdir in there. 55 // The CI picks up this directory and uploads to GCS after every failed run. 56 const tmpDir = path.resolve('./ui-test-artifacts'); 57 if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir); 58 const screenshotName = `ui-${testName}.png`; 59 const actualFilename = path.join(tmpDir, screenshotName); 60 const expectedFilename = path.join(expectedScreenshotPath, screenshotName); 61 await page.screenshot({path: actualFilename}); 62 const rebaseline = process.env['PERFETTO_UI_TESTS_REBASELINE'] === '1'; 63 if (rebaseline) { 64 console.log('Saving reference screenshot into', expectedFilename); 65 fs.copyFileSync(actualFilename, expectedFilename); 66 } else { 67 await compareScreenshots(actualFilename, expectedFilename); 68 } 69}); 70 71describe('android_trace_30s', () => { 72 let page: puppeteer.Page; 73 74 beforeAll(async () => { 75 page = await getPage(); 76 await page.goto('http://localhost:10000/?testing=1'); 77 await waitForPerfettoIdle(page); 78 }); 79 80 test('load', async () => { 81 const file = await page.waitForSelector('input.trace_file'); 82 const tracePath = getTestTracePath('example_android_trace_30s.pb'); 83 assertExists(file).uploadFile(tracePath); 84 await waitForPerfettoIdle(page); 85 }); 86 87 test('expand_camera', async () => { 88 await page.click('.main-canvas'); 89 await page.click('h1[title="com.google.android.GoogleCamera 5506"]'); 90 await page.evaluate(() => { 91 document.querySelector('.scrolling-panel-container')!.scrollTo(0, 400); 92 }); 93 await waitForPerfettoIdle(page); 94 }); 95 96 // TODO(198431341): Test is flaky. We should de-flake and re-enable. 97 // test('search', async () => { 98 // const page = await getPage(); 99 // const searchInput = '.omnibox input'; 100 // await page.focus(searchInput); 101 // await page.keyboard.type('TrimMaps'); 102 // await waitForPerfettoIdle(page); 103 // for (let i = 0; i < 10; i++) { 104 // await page.keyboard.type('\n'); 105 // } 106 // await waitForPerfettoIdle(page); 107 //}); 108}); 109 110describe('chrome_rendering_desktop', () => { 111 let page: puppeteer.Page; 112 113 beforeAll(async () => { 114 page = await getPage(); 115 await page.goto('http://localhost:10000/?testing=1'); 116 await waitForPerfettoIdle(page); 117 }); 118 119 test('load', async () => { 120 const page = await getPage(); 121 const file = await page.waitForSelector('input.trace_file'); 122 const tracePath = getTestTracePath('chrome_rendering_desktop.pftrace'); 123 assertExists(file).uploadFile(tracePath); 124 await waitForPerfettoIdle(page); 125 }); 126 127 test('expand_browser_proc', async () => { 128 const page = await getPage(); 129 await page.click('.main-canvas'); 130 await page.click('h1[title="Browser 12685"]'); 131 await waitForPerfettoIdle(page); 132 }); 133 134 test('select_slice_with_flows', async () => { 135 const page = await getPage(); 136 const searchInput = '.omnibox input'; 137 await page.focus(searchInput); 138 await page.keyboard.type('GenerateRenderPass'); 139 await waitForPerfettoIdle(page); 140 for (let i = 0; i < 3; i++) { 141 await page.keyboard.type('\n'); 142 } 143 await waitForPerfettoIdle(page); 144 await page.focus('canvas'); 145 await page.keyboard.type('f'); // Zoom to selection 146 await waitForPerfettoIdle(page); 147 }); 148}); 149 150describe('routing', () => { 151 describe('open_two_traces_then_go_back', () => { 152 let page: puppeteer.Page; 153 154 beforeAll(async () => { 155 page = await getPage(); 156 await page.goto('http://localhost:10000/?testing=1'); 157 await waitForPerfettoIdle(page); 158 }); 159 160 test('open_first_trace_from_url', async () => { 161 await page.goto( 162 'http://localhost:10000/?testing=1/#!/?url=http://localhost:10000/test/data/chrome_memory_snapshot.pftrace'); 163 await waitForPerfettoIdle(page); 164 }); 165 166 test('open_second_trace_from_url', async () => { 167 await page.goto( 168 'http://localhost:10000/?testing=1#!/?url=http://localhost:10000/test/data/chrome_scroll_without_vsync.pftrace'); 169 await waitForPerfettoIdle(page); 170 }); 171 172 test('access_subpage_then_go_back', async () => { 173 await waitForPerfettoIdle(page); 174 await page.goto( 175 'http://localhost:10000/?testing=1/#!/metrics?local_cache_key=76c25a80-25dd-1eb7-2246-d7b3c7a10f91'); 176 await page.goBack(); 177 await waitForPerfettoIdle(page); 178 }); 179 }); 180 181 describe('start_from_no_trace', () => { 182 let page: puppeteer.Page; 183 184 beforeAll(async () => { 185 page = await getPage(); 186 await page.goto('about:blank'); 187 }); 188 189 test('go_to_page_with_no_trace', async () => { 190 await page.goto('http://localhost:10000/?testing=1#!/info'); 191 await waitForPerfettoIdle(page); 192 }); 193 194 test('open_trace ', async () => { 195 await page.goto( 196 'http://localhost:10000/?testing=1#!/viewer?local_cache_key=76c25a80-25dd-1eb7-2246-d7b3c7a10f91'); 197 await waitForPerfettoIdle(page); 198 }); 199 200 test('refresh', async () => { 201 await page.reload(); 202 await waitForPerfettoIdle(page); 203 }); 204 205 test('open_second_trace', async () => { 206 await page.goto( 207 'http://localhost:10000/?testing=1#!/viewer?local_cache_key=00000000-0000-0000-e13c-bd7db4ff646f'); 208 await waitForPerfettoIdle(page); 209 210 // click on the 'Continue' button in the interstitial 211 await page.click('[id="trace_id_open"]'); 212 await waitForPerfettoIdle(page); 213 }); 214 215 test('go_back_to_first_trace', async () => { 216 await page.goBack(); 217 await waitForPerfettoIdle(page); 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('open_invalid_trace', async () => { 224 await page.goto( 225 'http://localhost:10000/?testing=1#!/viewer?local_cache_key=invalid'); 226 await waitForPerfettoIdle(page); 227 }); 228 }); 229 230 describe('navigate', () => { 231 let page: puppeteer.Page; 232 233 beforeAll(async () => { 234 page = await getPage(); 235 await page.goto('http://localhost:10000/?testing=1'); 236 await waitForPerfettoIdle(page); 237 }); 238 239 test('open_trace_from_url', async () => { 240 await page.goto( 241 'http://localhost:10000/?testing=1/#!/?url=http://localhost:10000/test/data/chrome_memory_snapshot.pftrace'); 242 await waitForPerfettoIdle(page); 243 }); 244 245 test('navigate_back_and_forward', async () => { 246 await page.click('[id="info_and_stats"]'); 247 await waitForPerfettoIdle(page); 248 await page.click('[id="metrics"]'); 249 await waitForPerfettoIdle(page); 250 await page.goBack(); 251 await waitForPerfettoIdle(page); 252 await page.goBack(); 253 await waitForPerfettoIdle(page); 254 await page.goForward(); 255 await waitForPerfettoIdle(page); 256 await page.goForward(); 257 await waitForPerfettoIdle(page); 258 }); 259 }); 260 261 test('open_trace_and_go_back_to_landing_page', async () => { 262 const page = await getPage(); 263 await page.goto('http://localhost:10000/?testing=1'); 264 await page.goto( 265 'http://localhost:10000/?testing=1#!/viewer?local_cache_key=76c25a80-25dd-1eb7-2246-d7b3c7a10f91'); 266 await waitForPerfettoIdle(page); 267 await page.goBack(); 268 await waitForPerfettoIdle(page); 269 }); 270 271 test('open_invalid_trace_from_blank_page', async () => { 272 const page = await getPage(); 273 await page.goto('about:blank'); 274 await page.goto( 275 'http://localhost:10000/?testing=1#!/viewer?local_cache_key=invalid'); 276 await waitForPerfettoIdle(page); 277 }); 278}); 279