• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2022 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
15// Helper function to create DOM elements faster: takes a Mithril-style
16// "selector" of the form "tag.class1.class2" and a list of child objects that
17// can be either strings or DOM elements.
18function m(selector, ...children) {
19  const parts = selector.split('.');
20  if (parts.length === 0) {
21    throw new Error(
22      'Selector passed to element should be of a form tag.class1.class2',
23    );
24  }
25
26  const result = document.createElement(parts[0]);
27  for (let i = 1; i < parts.length; i++) {
28    result.classList.add(parts[i]);
29  }
30  for (const child of children) {
31    if (typeof child === 'string') {
32      const childNode = document.createTextNode(child);
33      result.appendChild(childNode);
34    } else {
35      result.appendChild(child);
36    }
37  }
38  return result;
39}
40
41function getCiRun() {
42  const url = new URL(window.location.href);
43  const parts = url.pathname.split('/');
44
45  // Example report URL:
46  // https://storage.googleapis.com/perfetto-ci-artifacts/20220711123401--cls-2149676-1--ui-clang-x86_64-release/ui-test-artifacts/index.html
47  // Parts would contain ['', 'perfetto-ci-artifacts',
48  // '20220711123401--cls-2149676-1--ui-clang-x86_64-release', ...] in this
49  // case, which means that we need to check length of the array and get third
50  // element out of it.
51  if (parts.length >= 3) {
52    return parts[2];
53  }
54  return null;
55}
56
57function imageLinkElement(path) {
58  const img = m('img');
59  img.src = path;
60  const link = m('a');
61  link.appendChild(img);
62  link.href = path;
63  link.target = '_blank';
64  return link;
65}
66
67function processLines(lines) {
68  const container = document.querySelector('.container');
69  container.innerHTML = '';
70  const children = [];
71
72  // report.txt is a text file with a pair of file names on each line, separated
73  // by semicolon. E.g. "screenshot.png;screenshot-diff.png"
74  for (const line of lines) {
75    // Skip empty lines (happens when the file is completely empty).
76    if (line.length === 0) {
77      continue;
78    }
79
80    const parts = line.split(';');
81    if (parts.length !== 2) {
82      console.warn(
83        `Malformed line (expected two files separated via semicolon) ${line}!`,
84      );
85      continue;
86    }
87
88    const [output, diff] = parts;
89    children.push(
90      m(
91        'div.row',
92        m('div.cell', output, m('div.image-wrapper', imageLinkElement(output))),
93        m('div.cell', diff, m('div.image-wrapper', imageLinkElement(diff))),
94      ),
95    );
96  }
97
98  if (children.length === 0) {
99    container.appendChild(m('div', 'All good!'));
100    return;
101  }
102
103  const run = getCiRun();
104
105  if (run !== null) {
106    const cmd = `tools/download_changed_screenshots.py ${run}`;
107    const button = m('button', 'Copy');
108    button.addEventListener('click', async () => {
109      await navigator.clipboard.writeText(cmd);
110      button.innerText = 'Copied!';
111    });
112
113    container.appendChild(
114      m(
115        'div.message',
116        'Use following command from Perfetto checkout directory to apply the ' +
117          'changes: ',
118        m('span.cmd', cmd),
119        button,
120      ),
121    );
122  }
123
124  for (const child of children) {
125    container.appendChild(child);
126  }
127}
128
129async function loadDiffs() {
130  try {
131    const report = await fetch('report.txt');
132    const response = await report.text();
133    processLines(response.split('\n'));
134  } catch (e) {
135    // report.txt is not available when all tests have succeeded, treat fetching
136    // error as absence of failures
137    processLines([]);
138  }
139}
140
141document.addEventListener('DOMContentLoaded', () => {
142  loadDiffs();
143});
144