• 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 * 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