|
| 1 | +#!/usr/bin/env python |
| 2 | +# |
| 3 | +# This builds a html page of all images from the image comparison tests |
| 4 | +# and opens that page in the browser. |
| 5 | +# |
| 6 | +# $ python tools/visualize_tests.py |
| 7 | +# |
| 8 | + |
| 9 | +import argparse |
| 10 | +import os |
| 11 | +from collections import defaultdict |
| 12 | + |
| 13 | + |
| 14 | +html_template = """<html><head><style media="screen" type="text/css"> |
| 15 | +img{{ |
| 16 | + width:100%; |
| 17 | + max-width:800px; |
| 18 | +}} |
| 19 | +</style> |
| 20 | +</head><body> |
| 21 | +{failed} |
| 22 | +{body} |
| 23 | +</body></html> |
| 24 | +""" |
| 25 | + |
| 26 | +subdir_template = """<h2>{subdir}</h2><table> |
| 27 | +<thead><td>name</td><td>actual</td><td>expected</td><td>diff</td></thead> |
| 28 | +{rows} |
| 29 | +</table> |
| 30 | +""" |
| 31 | + |
| 32 | +failed_template = """<h2>Only Failed</h2><table> |
| 33 | +<thead><td>name</td><td>actual</td><td>expected</td><td>diff</td></thead> |
| 34 | +{rows} |
| 35 | +</table> |
| 36 | +""" |
| 37 | + |
| 38 | +row_template = ('<tr>' |
| 39 | + '<td>{0}{1}</td>' |
| 40 | + '<td>{2}</td>' |
| 41 | + '<td><a href="{3}"><img src="{3}"></a></td>' |
| 42 | + '<td>{4}</td>' |
| 43 | + '</tr>') |
| 44 | + |
| 45 | +linked_image_template = '<a href="{0}"><img src="{0}"></a>' |
| 46 | + |
| 47 | + |
| 48 | +def run(show_browser=True): |
| 49 | + """ |
| 50 | + Build a website for visual comparison |
| 51 | + """ |
| 52 | + image_dir = "result_images" |
| 53 | + _subdirs = (name |
| 54 | + for name in os.listdir(image_dir) |
| 55 | + if os.path.isdir(os.path.join(image_dir, name))) |
| 56 | + |
| 57 | + failed_rows = [] |
| 58 | + body_sections = [] |
| 59 | + for subdir in sorted(_subdirs): |
| 60 | + if subdir == "test_compare_images": |
| 61 | + # These are the images which test the image comparison functions. |
| 62 | + continue |
| 63 | + |
| 64 | + pictures = defaultdict(dict) |
| 65 | + for file in os.listdir(os.path.join(image_dir, subdir)): |
| 66 | + if os.path.isdir(os.path.join(image_dir, subdir, file)): |
| 67 | + continue |
| 68 | + fn, fext = os.path.splitext(file) |
| 69 | + if fext != ".png": |
| 70 | + continue |
| 71 | + # Always use / for URLs. |
| 72 | + if "-failed-diff" in fn: |
| 73 | + pictures[fn[:-12]]["f"] = "/".join((subdir, file)) |
| 74 | + elif "-expected" in fn: |
| 75 | + pictures[fn[:-9]]["e"] = "/".join((subdir, file)) |
| 76 | + else: |
| 77 | + pictures[fn]["c"] = "/".join((subdir, file)) |
| 78 | + |
| 79 | + subdir_rows = [] |
| 80 | + for name, test in sorted(pictures.items()): |
| 81 | + expected_image = test.get('e', '') |
| 82 | + actual_image = test.get('c', '') |
| 83 | + |
| 84 | + if 'f' in test: |
| 85 | + # A real failure in the image generation, resulting in |
| 86 | + # different images. |
| 87 | + status = " (failed)" |
| 88 | + failed = '<a href="{0}">diff</a>'.format(test['f']) |
| 89 | + current = linked_image_template.format(actual_image) |
| 90 | + failed_rows.append(row_template.format(name, "", current, |
| 91 | + expected_image, failed)) |
| 92 | + elif 'c' not in test: |
| 93 | + # A failure in the test, resulting in no current image |
| 94 | + status = " (failed)" |
| 95 | + failed = '--' |
| 96 | + current = '(Failure in test, no image produced)' |
| 97 | + failed_rows.append(row_template.format(name, "", current, |
| 98 | + expected_image, failed)) |
| 99 | + else: |
| 100 | + status = " (passed)" |
| 101 | + failed = '--' |
| 102 | + current = linked_image_template.format(actual_image) |
| 103 | + |
| 104 | + subdir_rows.append(row_template.format(name, status, current, |
| 105 | + expected_image, failed)) |
| 106 | + |
| 107 | + body_sections.append( |
| 108 | + subdir_template.format(subdir=subdir, rows='\n'.join(subdir_rows))) |
| 109 | + |
| 110 | + if failed_rows: |
| 111 | + failed = failed_template.format(rows='\n'.join(failed_rows)) |
| 112 | + else: |
| 113 | + failed = '' |
| 114 | + body = ''.join(body_sections) |
| 115 | + html = html_template.format(failed=failed, body=body) |
| 116 | + index = os.path.join(image_dir, "index.html") |
| 117 | + with open(index, "w") as f: |
| 118 | + f.write(html) |
| 119 | + |
| 120 | + show_message = not show_browser |
| 121 | + if show_browser: |
| 122 | + try: |
| 123 | + import webbrowser |
| 124 | + webbrowser.open(index) |
| 125 | + except: |
| 126 | + show_message = True |
| 127 | + |
| 128 | + if show_message: |
| 129 | + print("Open {} in a browser for a visual comparison.".format(index)) |
| 130 | + |
| 131 | + |
| 132 | +if __name__ == '__main__': |
| 133 | + parser = argparse.ArgumentParser() |
| 134 | + parser.add_argument('--no-browser', action='store_true', |
| 135 | + help="Don't show browser after creating index page.") |
| 136 | + args = parser.parse_args() |
| 137 | + run(show_browser=not args.no_browser) |
0 commit comments