mirror of
https://github.com/flectar/mail.git
synced 2026-11-11 17:57:54 +00:00
121 lines
6.5 KiB
Python
121 lines
6.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Render synthetic folder trees and count constructed sidebar delegates.
|
|
|
|
Requires slint-viewer 1.18+. Use --baseline-ref with a pre-virtualization
|
|
commit to compare the original ScrollView. Timings include interpretation, layout, and one software frame;
|
|
they are diagnostic, not a compiled application or hardware benchmark.
|
|
"""
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
import re
|
|
import statistics
|
|
import subprocess
|
|
import tempfile
|
|
import time
|
|
|
|
REPO = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
def mailbox(account_id, folder=None):
|
|
return {
|
|
"account_id": account_id, "folder_id": folder if folder is not None else -1,
|
|
"parent_folder_id": -1, "depth": 0, "has_children": False, "expanded": True,
|
|
"is_standard": False, "label_has_emoji": False,
|
|
"label": f"Folder {folder}" if folder is not None else f"Account {account_id}",
|
|
"scope": f"Folder:{folder}" if folder is not None else f"Account:{account_id}",
|
|
"context": f"Account:{account_id}", "detail": "", "avatar": "A",
|
|
"has_avatar": False, "is_account": folder is None, "count": "",
|
|
"color": "#000000", "has_custom_color": False,
|
|
}
|
|
|
|
|
|
def fixture(account_count, folder_count, flat):
|
|
accounts = [mailbox(i) for i in range(account_count)]
|
|
folders = [mailbox(i, i * folder_count + f) for i in range(account_count) for f in range(folder_count)]
|
|
if not flat:
|
|
return {"account-mailboxes": accounts, "mailboxes": accounts + folders}
|
|
label = dict(id=-1, name="", name_has_emoji=False, color="#000000", applied=False, is_auto=False)
|
|
|
|
def row(kind, key, data=None, opened=False):
|
|
return dict(kind=kind, key=key, mailbox=data or mailbox(-1), label=label, open=opened)
|
|
|
|
rows = [row("unified-heading", "heading"), row("unified-section", "unified", opened=True),
|
|
row("unified-inbox", "inbox"), row("categories-section", "categories"),
|
|
row("accounts-heading", "accounts")]
|
|
for account in accounts:
|
|
aid = account["account_id"]
|
|
rows.append(row("account", f"account:{aid}", account, True))
|
|
rows.extend(row("folder", f"folder:{f['folder_id']}", f) for f in folders if f["account_id"] == aid)
|
|
rows.append(row("new-folder", f"new:{aid}", account))
|
|
return {"rows": rows}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--baseline-ref")
|
|
parser.add_argument("--rounds", type=int, default=3)
|
|
parser.add_argument("--output-dir", type=Path, default=Path("/tmp/flectar-sidebar-benchmark"))
|
|
args = parser.parse_args()
|
|
args.output_dir.mkdir(parents=True, exist_ok=True)
|
|
path = "ui/components/sidebar.slint"
|
|
source = subprocess.check_output(["git", "show", f"{args.baseline_ref}:{path}"], cwd=REPO, text=True) if args.baseline_ref else (REPO / path).read_text()
|
|
flat = "[SidebarRow]" in source
|
|
for component in ("SidebarItem", "SidebarSectionHeader"):
|
|
source = source.replace(f"export component {component} inherits Rectangle {{",
|
|
f'export component {component} inherits Rectangle {{\n init => {{ debug("sidebar-delegate-created"); }}')
|
|
source = re.sub(r'"((?:\.\./|\./)[^"\n]+)"', lambda m: json.dumps(str((REPO / "ui/components" / m[1]).resolve())), source)
|
|
with tempfile.TemporaryDirectory(prefix="sidebar-bench-") as directory:
|
|
directory = Path(directory)
|
|
if not args.baseline_ref and (REPO / "ui/components/sidebar-controls.slint").exists():
|
|
controls = (REPO / "ui/components/sidebar-controls.slint").read_text()
|
|
for component in ("SidebarItem", "SidebarSectionHeader"):
|
|
controls = controls.replace(f"export component {component} inherits SidebarRowSurface {{",
|
|
f'export component {component} inherits SidebarRowSurface {{\n init => {{ debug("sidebar-delegate-created"); }}')
|
|
controls = re.sub(r'"((?:\.\./|\./)[^"\n]+)"', lambda m: json.dumps(str((REPO / "ui/components" / m[1]).resolve())), controls)
|
|
(directory / "sidebar-controls.slint").write_text(controls)
|
|
(directory / "sidebar.slint").write_text(source)
|
|
model_type = "SidebarRow" if flat else "MailboxRow"
|
|
account_property = "" if flat else "in property <[MailboxRow]> account-mailboxes;"
|
|
binding = ("rows: root.rows;" if flat else
|
|
"mailboxes: root.rows; account-mailboxes: root.account-mailboxes;")
|
|
(directory / "main.slint").write_text(f'''import {{ {model_type} }} from "{REPO / 'ui/models.slint'}";
|
|
import {{ MailSidebar }} from "sidebar.slint";
|
|
export component Harness inherits Window {{
|
|
width: 240px; height: 800px;
|
|
in property <[{model_type}]> rows;
|
|
{account_property}
|
|
MailSidebar {{
|
|
{binding}
|
|
background-color: #f4f4f4; body-color: #222; title-color: #222;
|
|
muted-color: #777; accent-color: #06e;
|
|
selection-background: #dfecff; hover-background: #ddd;
|
|
}}
|
|
}}
|
|
''')
|
|
for account_count, folder_count in [(1, 750), (5, 150), (5, 1500)]:
|
|
data = fixture(account_count, folder_count, flat)
|
|
if not flat:
|
|
data["rows"] = data.pop("mailboxes")
|
|
(directory / "data.json").write_text(json.dumps(data))
|
|
times, counts = [], []
|
|
output = args.output_dir / f"{'flat' if flat else 'baseline'}-{account_count}x{folder_count}.png"
|
|
for _ in range(args.rounds):
|
|
started = time.perf_counter()
|
|
result = subprocess.run(["slint-viewer", "-I", str(REPO / "ui/components"), "--style", "fluent", "--load-data", str(directory / "data.json"),
|
|
"--screenshot", str(output), str(directory / "main.slint")], capture_output=True, text=True)
|
|
if result.returncode:
|
|
raise RuntimeError(result.stdout + result.stderr)
|
|
times.append(time.perf_counter() - started)
|
|
counts.append((result.stdout + result.stderr).count("sidebar-delegate-created"))
|
|
if "Warning:" in result.stderr:
|
|
raise RuntimeError(result.stderr)
|
|
assert min(counts) > 0, "instrumentation must observe instantiated delegates"
|
|
if flat:
|
|
assert max(counts) < 100, f"virtualization regression: {counts} delegates"
|
|
print(f"accounts={account_count} folders_each={folder_count} delegates={counts} median_seconds={statistics.median(times):.4f} screenshot={output}", flush=True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|