Merge pull request #889 from trezor/tsusanka/ui-diff
Create UI report what differs from masterpull/891/head
commit
b22026f652
@ -1,24 +0,0 @@
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
import zipfile
|
||||
|
||||
RECORDS_WEBSITE = "https://firmware.corp.sldev.cz/ui_tests/"
|
||||
|
||||
|
||||
def fetch_recorded(recorded_hash, recorded_path):
|
||||
zip_src = RECORDS_WEBSITE + recorded_hash + ".zip"
|
||||
zip_dest = recorded_path / "recorded.zip"
|
||||
|
||||
try:
|
||||
urllib.request.urlretrieve(zip_src, zip_dest)
|
||||
except urllib.error.HTTPError:
|
||||
raise RuntimeError("No such recorded collection was found on '%s'." % zip_src)
|
||||
except urllib.error.URLError:
|
||||
raise RuntimeError(
|
||||
"Server firmware.corp.sldev.cz could not be found. Are you on VPN?"
|
||||
)
|
||||
|
||||
with zipfile.ZipFile(zip_dest, "r") as z:
|
||||
z.extractall(recorded_path)
|
||||
|
||||
zip_dest.unlink()
|
@ -0,0 +1,42 @@
|
||||
import json
|
||||
import pathlib
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
import zipfile
|
||||
from typing import Dict
|
||||
|
||||
import requests
|
||||
|
||||
RECORDS_WEBSITE = "https://firmware.corp.sldev.cz/ui_tests/"
|
||||
FIXTURES_MASTER = "https://raw.githubusercontent.com/trezor/trezor-firmware/master/tests/ui_tests/fixtures.json"
|
||||
FIXTURES_CURRENT = pathlib.Path(__file__).parent / "../fixtures.json"
|
||||
|
||||
|
||||
def fetch_recorded(hash, path):
|
||||
zip_src = RECORDS_WEBSITE + hash + ".zip"
|
||||
zip_dest = path / "recorded.zip"
|
||||
|
||||
try:
|
||||
urllib.request.urlretrieve(zip_src, zip_dest)
|
||||
except urllib.error.HTTPError:
|
||||
raise RuntimeError("No such recorded collection was found on '%s'." % zip_src)
|
||||
except urllib.error.URLError:
|
||||
raise RuntimeError(
|
||||
"Server firmware.corp.sldev.cz could not be found. Are you on VPN?"
|
||||
)
|
||||
|
||||
with zipfile.ZipFile(zip_dest, "r") as z:
|
||||
z.extractall(path)
|
||||
|
||||
zip_dest.unlink()
|
||||
|
||||
|
||||
def fetch_fixtures_master() -> Dict[str, str]:
|
||||
r = requests.get(FIXTURES_MASTER)
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
|
||||
|
||||
def fetch_fixtures_current() -> Dict[str, str]:
|
||||
with open(FIXTURES_CURRENT) as f:
|
||||
return json.loads(f.read())
|
@ -0,0 +1,49 @@
|
||||
import base64
|
||||
import filecmp
|
||||
from itertools import zip_longest
|
||||
|
||||
from dominate.tags import a, i, img, table, td, th, tr
|
||||
|
||||
|
||||
def report_links(tests, reports_path):
|
||||
if not tests:
|
||||
i("None!")
|
||||
return
|
||||
with table(border=1):
|
||||
with tr():
|
||||
th("Link to report")
|
||||
for test in sorted(tests):
|
||||
with tr():
|
||||
path = test.relative_to(reports_path)
|
||||
td(a(test.name, href=path))
|
||||
|
||||
|
||||
def write(fixture_test_path, doc, filename):
|
||||
(fixture_test_path / filename).write_text(doc.render())
|
||||
return fixture_test_path / filename
|
||||
|
||||
|
||||
def image(src):
|
||||
with td():
|
||||
if src:
|
||||
# open image file
|
||||
image = src.read_bytes()
|
||||
# encode image as base64
|
||||
image = base64.b64encode(image)
|
||||
# convert output to str
|
||||
image = image.decode()
|
||||
# img(src=src.relative_to(fixture_test_path))
|
||||
img(src="data:image/png;base64, " + image)
|
||||
else:
|
||||
i("missing")
|
||||
|
||||
|
||||
def diff_table(left_screens, right_screens):
|
||||
for left, right in zip_longest(left_screens, right_screens):
|
||||
if left and right and filecmp.cmp(right, left):
|
||||
background = "white"
|
||||
else:
|
||||
background = "red"
|
||||
with tr(bgcolor=background):
|
||||
image(left)
|
||||
image(right)
|
@ -0,0 +1,184 @@
|
||||
import shutil
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import dominate
|
||||
from dominate.tags import br, h1, h2, hr, i, p, table, td, th, tr
|
||||
|
||||
# These are imported directly because this script is run directly, isort gets confused by that.
|
||||
import download # isort:skip
|
||||
import html # isort:skip
|
||||
|
||||
REPORTS_PATH = Path(__file__).parent.resolve() / "reports" / "master_diff"
|
||||
RECORDED_SCREENS_PATH = Path(__file__).parent.parent.resolve() / "screens"
|
||||
|
||||
|
||||
def get_diff():
|
||||
master = download.fetch_fixtures_master()
|
||||
current = download.fetch_fixtures_current()
|
||||
|
||||
# removed items
|
||||
removed = {test: master[test] for test in (master.keys() - current.keys())}
|
||||
# added items
|
||||
added = {test: current[test] for test in (current.keys() - master.keys())}
|
||||
# items in both branches
|
||||
same = master.items() - removed.items() - added.items()
|
||||
# create the diff
|
||||
diff = dict()
|
||||
for master_test, master_hash in same:
|
||||
if current.get(master_test) == master_hash:
|
||||
continue
|
||||
diff[master_test] = master[master_test], current[master_test]
|
||||
|
||||
return removed, added, diff
|
||||
|
||||
|
||||
def removed(screens_path, test_name):
|
||||
doc = dominate.document(title=test_name)
|
||||
screens = sorted(screens_path.iterdir())
|
||||
|
||||
with doc:
|
||||
h1(test_name)
|
||||
p(
|
||||
"This UI test has been removed from fixtures.json.",
|
||||
style="color: red; font-weight: bold;",
|
||||
)
|
||||
hr()
|
||||
|
||||
with table(border=1):
|
||||
with tr():
|
||||
th("Removed files")
|
||||
|
||||
for screen in screens:
|
||||
with tr():
|
||||
html.image(screen)
|
||||
|
||||
return html.write(REPORTS_PATH / "removed", doc, test_name + ".html")
|
||||
|
||||
|
||||
def added(screens_path, test_name):
|
||||
doc = dominate.document(title=test_name)
|
||||
screens = sorted(screens_path.iterdir())
|
||||
|
||||
with doc:
|
||||
h1(test_name)
|
||||
p(
|
||||
"This UI test has been added to fixtures.json.",
|
||||
style="color: green; font-weight: bold;",
|
||||
)
|
||||
hr()
|
||||
|
||||
with table(border=1):
|
||||
with tr():
|
||||
th("Added files")
|
||||
|
||||
for screen in screens:
|
||||
with tr():
|
||||
html.image(screen)
|
||||
|
||||
return html.write(REPORTS_PATH / "added", doc, test_name + ".html")
|
||||
|
||||
|
||||
def diff(
|
||||
master_screens_path, current_screens_path, test_name, master_hash, current_hash
|
||||
):
|
||||
doc = dominate.document(title=test_name)
|
||||
master_screens = sorted(master_screens_path.iterdir())
|
||||
current_screens = sorted(current_screens_path.iterdir())
|
||||
|
||||
with doc:
|
||||
h1(test_name)
|
||||
p("This UI test differs from master.", style="color: grey; font-weight: bold;")
|
||||
with table():
|
||||
with tr():
|
||||
td("Master:")
|
||||
td(master_hash, style="color: red;")
|
||||
with tr():
|
||||
td("Current:")
|
||||
td(current_hash, style="color: green;")
|
||||
hr()
|
||||
|
||||
with table(border=1, width=600):
|
||||
with tr():
|
||||
th("Master")
|
||||
th("Current branch")
|
||||
|
||||
html.diff_table(master_screens, current_screens)
|
||||
|
||||
return html.write(REPORTS_PATH / "diff", doc, test_name + ".html")
|
||||
|
||||
|
||||
def index():
|
||||
removed = list((REPORTS_PATH / "removed").iterdir())
|
||||
added = list((REPORTS_PATH / "added").iterdir())
|
||||
diff = list((REPORTS_PATH / "diff").iterdir())
|
||||
|
||||
title = "UI changes from master"
|
||||
doc = dominate.document(title=title)
|
||||
|
||||
with doc:
|
||||
h1("UI changes from master")
|
||||
hr()
|
||||
|
||||
h2("Removed:", style="color: red;")
|
||||
i("UI fixtures that have been removed:")
|
||||
html.report_links(removed, REPORTS_PATH)
|
||||
br()
|
||||
hr()
|
||||
|
||||
h2("Added:", style="color: green;")
|
||||
i("UI fixtures that have been added:")
|
||||
html.report_links(added, REPORTS_PATH)
|
||||
br()
|
||||
hr()
|
||||
|
||||
h2("Differs:", style="color: grey;")
|
||||
i("UI fixtures that have been modified:")
|
||||
html.report_links(diff, REPORTS_PATH)
|
||||
|
||||
return html.write(REPORTS_PATH, doc, "index.html")
|
||||
|
||||
|
||||
def create_dirs():
|
||||
# delete the reports dir to clear previous entries and create folders
|
||||
shutil.rmtree(REPORTS_PATH, ignore_errors=True)
|
||||
REPORTS_PATH.mkdir()
|
||||
(REPORTS_PATH / "removed").mkdir()
|
||||
(REPORTS_PATH / "added").mkdir()
|
||||
(REPORTS_PATH / "diff").mkdir()
|
||||
|
||||
|
||||
def create_reports():
|
||||
removed_tests, added_tests, diff_tests = get_diff()
|
||||
|
||||
with tempfile.TemporaryDirectory(prefix="trezor-records-") as temp_dir:
|
||||
temp_dir = Path(temp_dir)
|
||||
|
||||
for test_name, test_hash in removed_tests.items():
|
||||
download.fetch_recorded(test_hash, temp_dir)
|
||||
removed(temp_dir, test_name)
|
||||
|
||||
for test_name, test_hash in added_tests.items():
|
||||
path = RECORDED_SCREENS_PATH / test_name / "actual"
|
||||
if not path.exists():
|
||||
raise RuntimeError("Folder does not exist, has it been recorded?", path)
|
||||
added(path, test_name)
|
||||
|
||||
for test_name, (master_hash, current_hash) in diff_tests.items():
|
||||
master_screens = temp_dir
|
||||
download.fetch_recorded(master_hash, master_screens)
|
||||
|
||||
current_screens = RECORDED_SCREENS_PATH / test_name / "actual"
|
||||
if not current_screens.exists():
|
||||
raise RuntimeError(
|
||||
"Folder does not exist, has it been recorded?", current_screens
|
||||
)
|
||||
diff(
|
||||
master_screens, current_screens, test_name, master_hash, current_hash,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
create_dirs()
|
||||
create_reports()
|
||||
index()
|
Loading…
Reference in new issue