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