• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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-]/gim, '_').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('.pf-overlay');
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('.pf-overlay');
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      );
172      await waitForPerfettoIdle(page);
173    });
174
175    test('open_second_trace_from_url', async () => {
176      await page.goto(
177        'http://localhost:10000/?testing=1#!/?url=http://localhost:10000/test/data/chrome_scroll_without_vsync.pftrace',
178      );
179      await waitForPerfettoIdle(page);
180    });
181
182    test('access_subpage_then_go_back', async () => {
183      await waitForPerfettoIdle(page);
184      await page.goto(
185        'http://localhost:10000/?testing=1/#!/metrics?local_cache_key=76c25a80-25dd-1eb7-2246-d7b3c7a10f91',
186      );
187      await page.goBack();
188      await waitForPerfettoIdle(page);
189    });
190  });
191
192  describe('start_from_no_trace', () => {
193    let page: Page;
194
195    beforeAll(async () => {
196      page = await getPage();
197      await page.goto('about:blank');
198    });
199
200    test('go_to_page_with_no_trace', async () => {
201      await page.goto('http://localhost:10000/?testing=1#!/info');
202      await waitForPerfettoIdle(page);
203    });
204
205    test('open_trace ', async () => {
206      await page.goto(
207        'http://localhost:10000/?testing=1#!/viewer?local_cache_key=76c25a80-25dd-1eb7-2246-d7b3c7a10f91',
208      );
209      await waitForPerfettoIdle(page);
210    });
211
212    test('refresh', async () => {
213      await page.reload();
214      await waitForPerfettoIdle(page);
215    });
216
217    test('open_second_trace', async () => {
218      await page.goto(
219        'http://localhost:10000/?testing=1#!/viewer?local_cache_key=00000000-0000-0000-e13c-bd7db4ff646f',
220      );
221      await waitForPerfettoIdle(page);
222
223      // click on the 'Continue' button in the interstitial
224      await page.click('[id="trace_id_open"]');
225      await waitForPerfettoIdle(page);
226    });
227
228    test('go_back_to_first_trace', async () => {
229      await page.goBack();
230      await waitForPerfettoIdle(page);
231      // click on the 'Continue' button in the interstitial
232      await page.click('[id="trace_id_open"]');
233      await waitForPerfettoIdle(page);
234    });
235
236    test('open_invalid_trace', async () => {
237      await page.goto(
238        'http://localhost:10000/?testing=1#!/viewer?local_cache_key=invalid',
239      );
240      await waitForPerfettoIdle(page);
241    });
242  });
243
244  describe('navigate', () => {
245    let page: Page;
246
247    beforeAll(async () => {
248      page = await getPage();
249      await page.goto('http://localhost:10000/?testing=1');
250      await waitForPerfettoIdle(page);
251    });
252
253    test('open_trace_from_url', async () => {
254      await page.goto(
255        'http://localhost:10000/?testing=1/#!/?url=http://localhost:10000/test/data/chrome_memory_snapshot.pftrace',
256      );
257      await waitForPerfettoIdle(page);
258    });
259
260    test('navigate_back_and_forward', async () => {
261      await page.click('[id="info_and_stats"]');
262      await waitForPerfettoIdle(page);
263      await page.click('[id="metrics"]');
264      await waitForPerfettoIdle(page);
265      await page.goBack();
266      await waitForPerfettoIdle(page);
267      await page.goBack();
268      await waitForPerfettoIdle(page);
269      await page.goForward();
270      await waitForPerfettoIdle(page);
271      await page.goForward();
272      await waitForPerfettoIdle(page);
273    });
274  });
275
276  test('open_trace_and_go_back_to_landing_page', async () => {
277    const page = await getPage();
278    await page.goto('http://localhost:10000/?testing=1');
279    await page.goto(
280      'http://localhost:10000/?testing=1#!/viewer?local_cache_key=76c25a80-25dd-1eb7-2246-d7b3c7a10f91',
281    );
282    await waitForPerfettoIdle(page);
283    await page.goBack();
284    await waitForPerfettoIdle(page);
285  });
286
287  test('open_invalid_trace_from_blank_page', async () => {
288    const page = await getPage();
289    await page.goto('about:blank');
290    await page.goto(
291      'http://localhost:10000/?testing=1#!/viewer?local_cache_key=invalid',
292    );
293    await waitForPerfettoIdle(page);
294  });
295});
296
297// Regression test for b/235335853.
298describe('modal_dialog', () => {
299  let page: Page;
300
301  beforeAll(async () => {
302    page = await getPage();
303    await page.goto('http://localhost:10000/?testing=1');
304    await waitForPerfettoIdle(page);
305  });
306
307  test('show_dialog_1', async () => {
308    await page.click('#keyboard_shortcuts');
309    await waitForPerfettoIdle(page);
310  });
311
312  test('dismiss_1', async () => {
313    await page.keyboard.press('Escape');
314    await waitForPerfettoIdle(page);
315  });
316
317  test('switch_page_no_dialog', async () => {
318    await page.click('#record_new_trace');
319    await waitForPerfettoIdle(page);
320  });
321
322  test('show_dialog_2', async () => {
323    await page.click('#keyboard_shortcuts');
324    await waitForPerfettoIdle(page);
325  });
326
327  test('dismiss_2', async () => {
328    await page.keyboard.press('Escape');
329    await waitForPerfettoIdle(page);
330  });
331});
332
333describe('features', () => {
334  let page: Page;
335
336  beforeAll(async () => {
337    page = await getPage();
338    await page.goto('http://localhost:10000/?testing=1');
339    await waitForPerfettoIdle(page);
340  });
341
342  // Test that we show a (debuggable) chip next to tracks for debuggable apps.
343  // Regression test for aosp/3106008 .
344  test('track_debuggable_chip', async () => {
345    const page = await getPage();
346    await page.goto(
347      'http://localhost:10000/?testing=1/#!/?url=http://localhost:10000/test/data/api32_startup_warm.perfetto-trace',
348    );
349    await waitForPerfettoIdle(page);
350    await page.hover(
351      'h1[title="androidx.benchmark.integration.macrobenchmark.test 7527"]',
352    );
353    await waitForPerfettoIdle(page);
354  });
355});
356