1cb93a386Sopenharmony_ci#!/usr/bin/env python 2cb93a386Sopenharmony_ci# Copyright 2019 Google LLC. 3cb93a386Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be 4cb93a386Sopenharmony_ci# found in the LICENSE file. 5cb93a386Sopenharmony_ci 6cb93a386Sopenharmony_ci''' 7cb93a386Sopenharmony_ciThis tool compares the PDF output of Skia's DM tool of two commits. 8cb93a386Sopenharmony_ci 9cb93a386Sopenharmony_ciIt relies on pdfium_test being in the PATH. To build: 10cb93a386Sopenharmony_ci 11cb93a386Sopenharmony_cimkdir -p ~/src/pdfium 12cb93a386Sopenharmony_cicd ~/src/pdfium 13cb93a386Sopenharmony_cigclient config --unmanaged https://pdfium.googlesource.com/pdfium.git 14cb93a386Sopenharmony_cigclient sync 15cb93a386Sopenharmony_cicd pdfium 16cb93a386Sopenharmony_cign gen out/default --args='pdf_enable_xfa=false pdf_enable_v8=false pdf_is_standalone=true' 17cb93a386Sopenharmony_cininja -C out/default pdfium_test 18cb93a386Sopenharmony_cicp out/default/pdfium_test ~/bin/ 19cb93a386Sopenharmony_ci''' 20cb93a386Sopenharmony_ci 21cb93a386Sopenharmony_ciimport os 22cb93a386Sopenharmony_ciimport re 23cb93a386Sopenharmony_ciimport shutil 24cb93a386Sopenharmony_ciimport subprocess 25cb93a386Sopenharmony_ciimport sys 26cb93a386Sopenharmony_ciimport tempfile 27cb93a386Sopenharmony_ciimport threading 28cb93a386Sopenharmony_ci 29cb93a386Sopenharmony_ciEXTRA_GN_ARGS = os.environ.get('PDF_COMPARISON_GN_ARGS', '') 30cb93a386Sopenharmony_ci 31cb93a386Sopenharmony_ciREFERENCE_BACKEND = 'gl' if 'PDF_COMPARISON_NOGPU' not in os.environ else '8888' 32cb93a386Sopenharmony_ci 33cb93a386Sopenharmony_ciDPI = float(os.environ.get('PDF_COMPARISON_DPI', 72)) 34cb93a386Sopenharmony_ci 35cb93a386Sopenharmony_ciPDF_CONFIG = 'pdf' if 'PDF_COMPARISON_300DPI' not in os.environ else 'pdf300' 36cb93a386Sopenharmony_ci 37cb93a386Sopenharmony_ciBAD_TESTS = [ 38cb93a386Sopenharmony_ci 'image-cacherator-from-picture', 39cb93a386Sopenharmony_ci 'image-cacherator-from-raster', 40cb93a386Sopenharmony_ci 'mixershader', 41cb93a386Sopenharmony_ci 'shadermaskfilter_image', 42cb93a386Sopenharmony_ci 'tilemode_decal', 43cb93a386Sopenharmony_ci] 44cb93a386Sopenharmony_ci 45cb93a386Sopenharmony_ciNINJA = 'ninja' 46cb93a386Sopenharmony_ci 47cb93a386Sopenharmony_ciPDFIUM_TEST = 'pdfium_test' 48cb93a386Sopenharmony_ci 49cb93a386Sopenharmony_ciNUM_THREADS = int(os.environ.get('PDF_COMPARISON_THREADS', 40)) 50cb93a386Sopenharmony_ci 51cb93a386Sopenharmony_ciSOURCES = ['gm'] 52cb93a386Sopenharmony_ci 53cb93a386Sopenharmony_cidef test_exe(cmd): 54cb93a386Sopenharmony_ci with open(os.devnull, 'w') as o: 55cb93a386Sopenharmony_ci try: 56cb93a386Sopenharmony_ci subprocess.call([cmd], stdout=o, stderr=o) 57cb93a386Sopenharmony_ci except OSError: 58cb93a386Sopenharmony_ci return False 59cb93a386Sopenharmony_ci return True 60cb93a386Sopenharmony_ci 61cb93a386Sopenharmony_cidef print_cmd(cmd, o): 62cb93a386Sopenharmony_ci m = re.compile('[^A-Za-z0-9_./-]') 63cb93a386Sopenharmony_ci o.write('+ ') 64cb93a386Sopenharmony_ci for c in cmd: 65cb93a386Sopenharmony_ci if m.search(c) is not None: 66cb93a386Sopenharmony_ci o.write(repr(c) + ' ') 67cb93a386Sopenharmony_ci else: 68cb93a386Sopenharmony_ci o.write(c + ' ') 69cb93a386Sopenharmony_ci o.write('\n') 70cb93a386Sopenharmony_ci o.flush() 71cb93a386Sopenharmony_ci 72cb93a386Sopenharmony_cidef check_call(cmd, **kwargs): 73cb93a386Sopenharmony_ci print_cmd(cmd, sys.stdout) 74cb93a386Sopenharmony_ci return subprocess.check_call(cmd, **kwargs) 75cb93a386Sopenharmony_ci 76cb93a386Sopenharmony_cidef check_output(cmd, **kwargs): 77cb93a386Sopenharmony_ci print_cmd(cmd, sys.stdout) 78cb93a386Sopenharmony_ci return subprocess.check_output(cmd, **kwargs) 79cb93a386Sopenharmony_ci 80cb93a386Sopenharmony_cidef remove(*paths): 81cb93a386Sopenharmony_ci for path in paths: 82cb93a386Sopenharmony_ci os.remove(path) 83cb93a386Sopenharmony_ci 84cb93a386Sopenharmony_cidef timeout(deadline, cmd): 85cb93a386Sopenharmony_ci #print_cmd(cmd, sys.stdout) 86cb93a386Sopenharmony_ci with open(os.devnull, 'w') as o: 87cb93a386Sopenharmony_ci proc = subprocess.Popen(cmd, stdout=o, stderr=subprocess.STDOUT) 88cb93a386Sopenharmony_ci timer = threading.Timer(deadline, proc.terminate) 89cb93a386Sopenharmony_ci timer.start() 90cb93a386Sopenharmony_ci proc.wait() 91cb93a386Sopenharmony_ci timer.cancel() 92cb93a386Sopenharmony_ci return proc.returncode 93cb93a386Sopenharmony_ci 94cb93a386Sopenharmony_cidef is_same(path1, path2): 95cb93a386Sopenharmony_ci if not os.path.isfile(path1) or not os.path.isfile(path2): 96cb93a386Sopenharmony_ci return os.path.isfile(path1) == os.path.isfile(path2) 97cb93a386Sopenharmony_ci with open(path1, 'rb') as f1: 98cb93a386Sopenharmony_ci with open(path2, 'rb') as f2: 99cb93a386Sopenharmony_ci while True: 100cb93a386Sopenharmony_ci c1, c2 = f1.read(4096), f2.read(4096) 101cb93a386Sopenharmony_ci if c1 != c2: 102cb93a386Sopenharmony_ci return False 103cb93a386Sopenharmony_ci if not c1: 104cb93a386Sopenharmony_ci return True 105cb93a386Sopenharmony_ci 106cb93a386Sopenharmony_ci 107cb93a386Sopenharmony_cidef getfilesoftype(directory, ending): 108cb93a386Sopenharmony_ci for dirpath, _, filenames in os.walk(directory): 109cb93a386Sopenharmony_ci rp = os.path.normpath(os.path.relpath(dirpath, directory)) 110cb93a386Sopenharmony_ci for f in filenames: 111cb93a386Sopenharmony_ci if f.endswith(ending): 112cb93a386Sopenharmony_ci yield os.path.join(rp, f) 113cb93a386Sopenharmony_ci 114cb93a386Sopenharmony_cidef get_common_paths(dirs, ext): 115cb93a386Sopenharmony_ci return sorted(list( 116cb93a386Sopenharmony_ci set.intersection(*(set(getfilesoftype(d, ext)) for d in dirs)))) 117cb93a386Sopenharmony_ci 118cb93a386Sopenharmony_cidef printable_path(d): 119cb93a386Sopenharmony_ci if 'TMPDIR' in os.environ: 120cb93a386Sopenharmony_ci return d.replace(os.path.normpath(os.environ['TMPDIR']) + '/', '$TMPDIR/') 121cb93a386Sopenharmony_ci return d 122cb93a386Sopenharmony_ci 123cb93a386Sopenharmony_cidef spawn(cmd): 124cb93a386Sopenharmony_ci with open(os.devnull, 'w') as o: 125cb93a386Sopenharmony_ci subprocess.Popen(cmd, stdout=o, stderr=o) 126cb93a386Sopenharmony_ci 127cb93a386Sopenharmony_cidef sysopen(arg): 128cb93a386Sopenharmony_ci plat = sys.platform 129cb93a386Sopenharmony_ci if plat.startswith('darwin'): 130cb93a386Sopenharmony_ci spawn(["open", arg]) 131cb93a386Sopenharmony_ci elif plat.startswith('win'): 132cb93a386Sopenharmony_ci # pylint: disable=no-member 133cb93a386Sopenharmony_ci os.startfile(arg) 134cb93a386Sopenharmony_ci else: 135cb93a386Sopenharmony_ci spawn(["xdg-open", arg]) 136cb93a386Sopenharmony_ci 137cb93a386Sopenharmony_ciHTML_HEAD = ''' 138cb93a386Sopenharmony_ci<!DOCTYPE html> 139cb93a386Sopenharmony_ci<html lang="en"> 140cb93a386Sopenharmony_ci<head> 141cb93a386Sopenharmony_ci<meta charset="utf-8"> 142cb93a386Sopenharmony_ci<title>DIFF</title> 143cb93a386Sopenharmony_ci<style> 144cb93a386Sopenharmony_cibody{ 145cb93a386Sopenharmony_cibackground-size:16px 16px; 146cb93a386Sopenharmony_cibackground-color:rgb(230,230,230); 147cb93a386Sopenharmony_cibackground-image: 148cb93a386Sopenharmony_cilinear-gradient(45deg,rgba(255,255,255,.2) 25%,transparent 25%,transparent 50%, 149cb93a386Sopenharmony_cirgba(255,255,255,.2) 50%,rgba(255,255,255,.2) 75%,transparent 75%,transparent)} 150cb93a386Sopenharmony_cidiv.r{position:relative;left:0;top:0} 151cb93a386Sopenharmony_citable{table-layout:fixed;width:100%} 152cb93a386Sopenharmony_ciimg.s{max-width:100%;max-height:320;left:0;top:0} 153cb93a386Sopenharmony_ciimg.b{position:absolute;mix-blend-mode:difference} 154cb93a386Sopenharmony_ci</style> 155cb93a386Sopenharmony_ci<script> 156cb93a386Sopenharmony_cifunction r(c,e,n,g){ 157cb93a386Sopenharmony_cit=document.getElementById("t"); 158cb93a386Sopenharmony_cifunction ce(t){return document.createElement(t);} 159cb93a386Sopenharmony_cifunction ct(n){return document.createTextNode(n);} 160cb93a386Sopenharmony_cifunction ac(u,v){u.appendChild(v);} 161cb93a386Sopenharmony_cifunction cn(u,v){u.className=v;} 162cb93a386Sopenharmony_cifunction it(s){ td=ce("td"); a=ce("a"); a.href=s; img=ce("img"); img.src=s; 163cb93a386Sopenharmony_ci cn(img,"s"); ac(a,img); ac(td,a); return td; } 164cb93a386Sopenharmony_citr=ce("tr"); td=ce("td"); td.colSpan="4"; ac(td, ct(n)); ac(tr,td); 165cb93a386Sopenharmony_ciac(t,tr); tr=ce("tr"); td=ce("td"); dv=ce("div"); cn(dv,"r"); 166cb93a386Sopenharmony_ciimg=ce("img"); img.src=c; cn(img,"s"); ac(dv,img); img=ce("img"); 167cb93a386Sopenharmony_ciimg.src=e; cn(img,"s b"); ac(dv,img); ac(td,dv); ac(tr,td); 168cb93a386Sopenharmony_ciac(tr,it(c)); ac(tr,it(e)); ac(tr,it(g)); ac(t,tr); } 169cb93a386Sopenharmony_cidocument.addEventListener('DOMContentLoaded',function(){ 170cb93a386Sopenharmony_ci''' 171cb93a386Sopenharmony_ci 172cb93a386Sopenharmony_ciHTML_TAIL = ''']; 173cb93a386Sopenharmony_cifor(i=0;i<z.length;i++){ 174cb93a386Sopenharmony_cir(c+z[i][0],e+z[i][0],z[i][2],c+z[i][1]);}},false); 175cb93a386Sopenharmony_ci</script></head><body><table id="t"> 176cb93a386Sopenharmony_ci<tr><th>BEFORE-AFTER DIFF</th> 177cb93a386Sopenharmony_ci<th>BEFORE</th><th>AFTER</th> 178cb93a386Sopenharmony_ci<th>REFERENCE</th></tr> 179cb93a386Sopenharmony_ci</table></body></html>''' 180cb93a386Sopenharmony_ci 181cb93a386Sopenharmony_cidef shard(fn, arglist): 182cb93a386Sopenharmony_ci jobs = [[arg for j, arg in enumerate(arglist) if j % NUM_THREADS == i] 183cb93a386Sopenharmony_ci for i in range(NUM_THREADS)] 184cb93a386Sopenharmony_ci results = [] 185cb93a386Sopenharmony_ci def do_shard(*args): 186cb93a386Sopenharmony_ci for arg in args: 187cb93a386Sopenharmony_ci results.append(fn(arg)) 188cb93a386Sopenharmony_ci thread_list = [] 189cb93a386Sopenharmony_ci for job in jobs: 190cb93a386Sopenharmony_ci t = threading.Thread(target=do_shard, args=job) 191cb93a386Sopenharmony_ci t.start() 192cb93a386Sopenharmony_ci thread_list += [t] 193cb93a386Sopenharmony_ci for t in thread_list: 194cb93a386Sopenharmony_ci t.join() 195cb93a386Sopenharmony_ci return results 196cb93a386Sopenharmony_ci 197cb93a386Sopenharmony_cidef shardsum(fn, arglist): 198cb93a386Sopenharmony_ci 'return the number of True results returned by fn(arg) for arg in arglist.' 199cb93a386Sopenharmony_ci return sum(1 for result in shard(fn, arglist) if result) 200cb93a386Sopenharmony_ci 201cb93a386Sopenharmony_cidef checkout_worktree(checkoutable): 202cb93a386Sopenharmony_ci directory = os.path.join(tempfile.gettempdir(), 'skpdf_control_tree') 203cb93a386Sopenharmony_ci commit = check_output(['git', 'rev-parse', checkoutable]).strip() 204cb93a386Sopenharmony_ci if os.path.isdir(directory): 205cb93a386Sopenharmony_ci try: 206cb93a386Sopenharmony_ci check_call(['git', 'checkout', commit], cwd=directory) 207cb93a386Sopenharmony_ci return directory 208cb93a386Sopenharmony_ci except subprocess.CalledProcessError: 209cb93a386Sopenharmony_ci shutil.rmtree(directory) 210cb93a386Sopenharmony_ci check_call(['git', 'worktree', 'add', '-f', directory, commit]) 211cb93a386Sopenharmony_ci return directory 212cb93a386Sopenharmony_ci 213cb93a386Sopenharmony_cidef build_skia(directory, executable): 214cb93a386Sopenharmony_ci args = ('--args=is_debug=false' 215cb93a386Sopenharmony_ci ' extra_cflags=["-DSK_PDF_LESS_COMPRESSION",' 216cb93a386Sopenharmony_ci ' "-DSK_PDF_BASE85_BINARY"] ') 217cb93a386Sopenharmony_ci if test_exe('ccache'): 218cb93a386Sopenharmony_ci args += ' cc_wrapper="ccache"' 219cb93a386Sopenharmony_ci args += EXTRA_GN_ARGS 220cb93a386Sopenharmony_ci build_dir = directory + '/out/pdftest' 221cb93a386Sopenharmony_ci check_call([sys.executable, 'bin/sync'], cwd=directory) 222cb93a386Sopenharmony_ci check_call([directory + '/bin/gn', 'gen', 'out/pdftest', args], 223cb93a386Sopenharmony_ci cwd=directory) 224cb93a386Sopenharmony_ci check_call([NINJA, executable], cwd=build_dir) 225cb93a386Sopenharmony_ci return os.path.join(build_dir, executable) 226cb93a386Sopenharmony_ci 227cb93a386Sopenharmony_cidef build_and_run_dm(directory, data_dir): 228cb93a386Sopenharmony_ci dm = build_skia(directory, 'dm') 229cb93a386Sopenharmony_ci for source in SOURCES: 230cb93a386Sopenharmony_ci os.makedirs(os.path.join(data_dir, PDF_CONFIG, source)) 231cb93a386Sopenharmony_ci dm_args = [dm, '--src'] + SOURCES + ['--config', PDF_CONFIG, '-w', data_dir] 232cb93a386Sopenharmony_ci if BAD_TESTS: 233cb93a386Sopenharmony_ci dm_args += ['-m'] + ['~^%s$' % x for x in BAD_TESTS] 234cb93a386Sopenharmony_ci check_call(dm_args, cwd=directory) 235cb93a386Sopenharmony_ci return dm 236cb93a386Sopenharmony_ci 237cb93a386Sopenharmony_cidef rasterize(path): 238cb93a386Sopenharmony_ci ret = timeout(30, [PDFIUM_TEST, '--png', '--scale=%g' % (DPI / 72.0), path]) 239cb93a386Sopenharmony_ci if ret != 0: 240cb93a386Sopenharmony_ci sys.stdout.write( 241cb93a386Sopenharmony_ci '\nTIMEOUT OR ERROR [%d] "%s"\n' % (ret, printable_path(path))) 242cb93a386Sopenharmony_ci return 243cb93a386Sopenharmony_ci assert os.path.isfile(path + '.0.png') 244cb93a386Sopenharmony_ci 245cb93a386Sopenharmony_cidef main(control_commitish): 246cb93a386Sopenharmony_ci assert os.pardir == '..' and '/' in [os.sep, os.altsep] 247cb93a386Sopenharmony_ci assert test_exe(NINJA) 248cb93a386Sopenharmony_ci assert test_exe(PDFIUM_TEST) 249cb93a386Sopenharmony_ci os.chdir(os.path.dirname(__file__) + '/../..') 250cb93a386Sopenharmony_ci control_worktree = checkout_worktree(control_commitish) 251cb93a386Sopenharmony_ci tmpdir = tempfile.mkdtemp(prefix='skpdf_') 252cb93a386Sopenharmony_ci exp = tmpdir + '/experim' 253cb93a386Sopenharmony_ci con = tmpdir + '/control' 254cb93a386Sopenharmony_ci build_and_run_dm(os.curdir, exp) 255cb93a386Sopenharmony_ci dm = build_and_run_dm(control_worktree, con) 256cb93a386Sopenharmony_ci image_diff_metric = build_skia(control_worktree, 'image_diff_metric') 257cb93a386Sopenharmony_ci 258cb93a386Sopenharmony_ci out = sys.stdout 259cb93a386Sopenharmony_ci common_paths = get_common_paths([con, exp], '.pdf') 260cb93a386Sopenharmony_ci out.write('\nNumber of PDFs: %d\n\n' % len(common_paths)) 261cb93a386Sopenharmony_ci def compare_identical(path): 262cb93a386Sopenharmony_ci cpath, epath = (os.path.join(x, path) for x in (con, exp)) 263cb93a386Sopenharmony_ci if is_same(cpath, epath): 264cb93a386Sopenharmony_ci remove(cpath, epath) 265cb93a386Sopenharmony_ci return True 266cb93a386Sopenharmony_ci return False 267cb93a386Sopenharmony_ci identical_count = shardsum(compare_identical, common_paths) 268cb93a386Sopenharmony_ci out.write('Number of identical PDFs: %d\n\n' % identical_count) 269cb93a386Sopenharmony_ci 270cb93a386Sopenharmony_ci differing_paths = get_common_paths([con, exp], '.pdf') 271cb93a386Sopenharmony_ci if not differing_paths: 272cb93a386Sopenharmony_ci out.write('All PDFs are the same!\n') 273cb93a386Sopenharmony_ci sys.exit(0) 274cb93a386Sopenharmony_ci out.write('Number of differing PDFs: %d\n' % len(differing_paths)) 275cb93a386Sopenharmony_ci for p in differing_paths: 276cb93a386Sopenharmony_ci out.write(' %s\n' % printable_path(tmpdir + '/*/' + p)) 277cb93a386Sopenharmony_ci out.write('\n') 278cb93a386Sopenharmony_ci shard(rasterize, 279cb93a386Sopenharmony_ci [os.path.join(x, p) for p in differing_paths for x in [con, exp]]) 280cb93a386Sopenharmony_ci 281cb93a386Sopenharmony_ci common_pngs = get_common_paths([con, exp], '.pdf.0.png') 282cb93a386Sopenharmony_ci identical_count = shardsum(compare_identical, common_pngs) 283cb93a386Sopenharmony_ci out.write('Number of PDFs that rasterize the same: %d\n\n' 284cb93a386Sopenharmony_ci % identical_count) 285cb93a386Sopenharmony_ci 286cb93a386Sopenharmony_ci differing_pngs = get_common_paths([con, exp], '.pdf.0.png') 287cb93a386Sopenharmony_ci if not differing_pngs: 288cb93a386Sopenharmony_ci out.write('All PDFs rasterize the same!\n') 289cb93a386Sopenharmony_ci sys.exit(0) 290cb93a386Sopenharmony_ci out.write('Number of PDFs that rasterize differently: %d\n' 291cb93a386Sopenharmony_ci % len(differing_pngs)) 292cb93a386Sopenharmony_ci for p in differing_pngs: 293cb93a386Sopenharmony_ci out.write(' %s\n' % printable_path(tmpdir + '/*/' + p)) 294cb93a386Sopenharmony_ci out.write('\n') 295cb93a386Sopenharmony_ci 296cb93a386Sopenharmony_ci scores = dict() 297cb93a386Sopenharmony_ci def compare_differing_pngs(path): 298cb93a386Sopenharmony_ci cpath, epath = (os.path.join(x, path) for x in (con, exp)) 299cb93a386Sopenharmony_ci s = float(subprocess.check_output([image_diff_metric, cpath, epath])) 300cb93a386Sopenharmony_ci indicator = '.' if s < 0.001 else ':' if s < 0.01 else '!' 301cb93a386Sopenharmony_ci sys.stdout.write(indicator) 302cb93a386Sopenharmony_ci sys.stdout.flush() 303cb93a386Sopenharmony_ci scores[path] = s 304cb93a386Sopenharmony_ci shard(compare_differing_pngs, differing_pngs) 305cb93a386Sopenharmony_ci paths = sorted(scores.iterkeys(), key=lambda p: -scores[p]) 306cb93a386Sopenharmony_ci out.write('\n\n') 307cb93a386Sopenharmony_ci for p in paths: 308cb93a386Sopenharmony_ci pdfpath = printable_path(tmpdir + '/*/' + p.replace('.0.png', '')) 309cb93a386Sopenharmony_ci out.write(' %6.4f %s\n' % (scores[p], pdfpath)) 310cb93a386Sopenharmony_ci out.write('\n') 311cb93a386Sopenharmony_ci 312cb93a386Sopenharmony_ci errors = [] 313cb93a386Sopenharmony_ci rc = re.compile('^' + PDF_CONFIG + r'/([^/]*)/([^/]*)\.pdf\.0\.png$') 314cb93a386Sopenharmony_ci for p in paths: 315cb93a386Sopenharmony_ci m = rc.match(p) 316cb93a386Sopenharmony_ci assert(m) 317cb93a386Sopenharmony_ci source, name = m.groups() 318cb93a386Sopenharmony_ci errors.append((source, name, scores[p])) 319cb93a386Sopenharmony_ci 320cb93a386Sopenharmony_ci for source in SOURCES: 321cb93a386Sopenharmony_ci os.makedirs(os.path.join(con, REFERENCE_BACKEND, source)) 322cb93a386Sopenharmony_ci dm_args = [dm, '--src'] + SOURCES + [ 323cb93a386Sopenharmony_ci '--config', REFERENCE_BACKEND, '-w', con, '-m'] + [ 324cb93a386Sopenharmony_ci '^%s$' % name for _, name, _ in errors] 325cb93a386Sopenharmony_ci check_call(dm_args, cwd=control_worktree) 326cb93a386Sopenharmony_ci 327cb93a386Sopenharmony_ci report = tmpdir + '/report.html' 328cb93a386Sopenharmony_ci with open(report, 'w') as o: 329cb93a386Sopenharmony_ci o.write(HTML_HEAD) 330cb93a386Sopenharmony_ci o.write('c="%s/";\n' % os.path.relpath(con, tmpdir)) 331cb93a386Sopenharmony_ci o.write('e="%s/";\n' % os.path.relpath(exp, tmpdir)) 332cb93a386Sopenharmony_ci o.write('z=[\n') 333cb93a386Sopenharmony_ci for source, name, score in errors: 334cb93a386Sopenharmony_ci gt = REFERENCE_BACKEND + '/' + source + '/' + name + '.png' 335cb93a386Sopenharmony_ci p = '%s/%s/%s.pdf.0.png' % (PDF_CONFIG, source, name) 336cb93a386Sopenharmony_ci desc = '%s | %s | %g' % (source, name, score) 337cb93a386Sopenharmony_ci o.write('["%s","%s","%s"],\n' % (p, gt, desc)) 338cb93a386Sopenharmony_ci o.write(HTML_TAIL) 339cb93a386Sopenharmony_ci out.write(printable_path(report) + '\n') 340cb93a386Sopenharmony_ci sysopen(report) 341cb93a386Sopenharmony_ci 342cb93a386Sopenharmony_ciif __name__ == '__main__': 343cb93a386Sopenharmony_ci if len(sys.argv) != 2: 344cb93a386Sopenharmony_ci USAGE = ('\nusage:\n {0} COMMIT_OR_BRANCH_TO_COMPARE_TO\n\n' 345cb93a386Sopenharmony_ci 'e.g.:\n {0} HEAD\nor\n {0} HEAD~1\n\n') 346cb93a386Sopenharmony_ci sys.stderr.write(USAGE.format(sys.argv[0])) 347cb93a386Sopenharmony_ci sys.exit(1) 348cb93a386Sopenharmony_ci main(sys.argv[1]) 349