Settings sat 7px further in than the Dashboard/Giám sát rows above it — its
QSS gave it a 6px side margin where those rows start at the rail edge. At the
collapsed 54px width that put its icon near the middle of the rail, which is
what "thu gọn menu lại ra giữa" was describing. Icon and label now start on
the same x as the rows, open and collapsed, in both themes.
The help panel's transcript is rendered HTML, so switching language re-labelled
the chrome but left the greeting — and the "AI Assistant" speaker label — in
whatever language the panel was built in. retranslate() now rewrites the
greeting (matched by identity, so a real reply is never touched) and re-renders.
The sparkle is #FDBE59, sampled from the audit page's own render. Its CSS says
.spark{color:#0F9B8A}, but the glyph is the ✨ emoji and a colour emoji ignores
CSS colour, so the page has always drawn a gold star.
Behind all three: MainWindow does not style itself — run() calls
app.setStyleSheet — so 12 of 13 checkers were measuring a window with no
padding, margins or borders. Every QSS-driven layout bug was invisible to them,
and an unstyled window reported an icon drift that does not exist. Added
_apply_theme() and wired it through.
Two checker repairs that followed:
· check_no_hscroll flagged the 9pt dialogs on sizeHintForColumn(0), which
returns 182px at 9pt, 11pt and 14pt alike. Nothing was clipped. It now
compares the painted text against the width actually on screen, and fails
on a squeezed list (24 combos) where the old test passed.
· the checkers print Vietnamese and died mid-report on a cp932 console.
New: check_rail_align (icons hold one line, both themes, both states) and
check_help_i18n (transcript follows the language). Both verified to fail
without their fix.
15/15 checkers pass. check_nav and check_design_parity segfault in Qt teardown
roughly one run in three — pre-existing, after the verdict prints, and it
happens with or without the theme change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
133 lines
4.9 KiB
Python
133 lines
4.9 KiB
Python
"""Round 5: do the checks actually bite?
|
||
|
||
Rounds 1–4 all report green. That is only worth something if the checks would
|
||
have turned red had the work not been done. So this round breaks the app on
|
||
purpose, one feature at a time, and fails if the corresponding check still
|
||
passes — a check that cannot fail is not evidence.
|
||
|
||
Each mutation is applied by monkey-patching the module BEFORE the checker
|
||
builds its own window, then undone.
|
||
|
||
Run: python tools/check_probes_bite.py
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import io
|
||
import os
|
||
import runpy
|
||
import subprocess
|
||
import sys
|
||
|
||
|
||
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
||
from pathlib import Path
|
||
|
||
REPO = Path(__file__).resolve().parent.parent
|
||
sys.path.insert(0, str(REPO.parent))
|
||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||
|
||
# (name, file, find, replace, checker that must FAIL because of it)
|
||
MUTATIONS = [
|
||
("bo cham tro ly 26px -> 64px",
|
||
"ui/help_agent_widget.py", "_DOT = 26", "_DOT = 64",
|
||
"check_layout_geometry.py"),
|
||
("tra lane Running ve khong vien",
|
||
"ui/schedule_task_tab.py",
|
||
'if status == "running" and counts[status]:',
|
||
'if False:',
|
||
"check_design_parity.py"),
|
||
("bo cot muc luc cua Cai dat",
|
||
"ui/settings_dialog.py",
|
||
"self.section_list, self.section_stack = section_panels(pages)",
|
||
"self.section_list, self.section_stack = section_panels(pages[:1])",
|
||
"check_dialogs.py"),
|
||
("noi lai dai tab flow Co4E",
|
||
"ui/co4e_tab.py",
|
||
"self.flow_scroll.setVisible(False)",
|
||
"self.flow_scroll.setVisible(True)",
|
||
"check_co4e.py"),
|
||
("bo dong 'Tat ca project...' khoi GAN DAY",
|
||
"app.py",
|
||
'more.setData(0, Qt.UserRole, {"all": True})',
|
||
'more.setData(0, Qt.UserRole, {})',
|
||
"check_design_parity.py"),
|
||
("tra thanh menu ve accordion (bo nhom day)",
|
||
"app.py",
|
||
'rows.append((self.nav_bottom, self._ROW_DASHBOARD, None,',
|
||
'rows.append((self.nav, self._ROW_DASHBOARD, None,',
|
||
"check_layout_geometry.py"),
|
||
]
|
||
|
||
|
||
def run_checker(script: str) -> int:
|
||
"""Run a checker in a fresh process; return its exit code."""
|
||
proc = subprocess.run(
|
||
[sys.executable, str(REPO / "tools" / script)],
|
||
cwd=REPO, capture_output=True, text=True, encoding="utf-8",
|
||
errors="replace", env={**os.environ, "QT_QPA_PLATFORM": "offscreen",
|
||
"PYTHONIOENCODING": "utf-8"})
|
||
return proc.returncode
|
||
|
||
|
||
def tree_state() -> str:
|
||
return subprocess.run(["git", "status", "--short"], cwd=REPO,
|
||
capture_output=True, text=True).stdout.strip()
|
||
|
||
|
||
def main() -> int:
|
||
fails: list[str] = []
|
||
# Compare the tree BEFORE and AFTER, not against a clean tree: work in
|
||
# progress is legitimately uncommitted, and demanding a clean tree made this
|
||
# round fail for a reason that has nothing to do with the mutations.
|
||
before = tree_state()
|
||
print(f"{'hong gi':44} {'phep do':26} ket qua")
|
||
print("-" * 88)
|
||
for name, rel, find, repl, checker in MUTATIONS:
|
||
path = REPO / rel
|
||
# newline="" both ways: the default translates on read AND write, so a
|
||
# LF file came back as CRLF and every mutated file was left "modified"
|
||
# even after being restored.
|
||
with io.open(path, "r", encoding="utf-8", newline="") as fh:
|
||
original = fh.read()
|
||
if find not in original:
|
||
fails.append(f"{name}: khong tim thay doan can sua trong {rel}")
|
||
print(f"{name:44} {checker:26} *** KHONG AP DUNG DUOC ***")
|
||
continue
|
||
|
||
def write(text: str) -> None:
|
||
with io.open(path, "w", encoding="utf-8", newline="") as fh:
|
||
fh.write(text)
|
||
|
||
write(original.replace(find, repl, 1))
|
||
try:
|
||
code = run_checker(checker)
|
||
finally:
|
||
write(original) # always restore
|
||
bit = code != 0
|
||
print(f"{name:44} {checker:26} {'BAT DUOC' if bit else '*** KHONG BAT ***'}")
|
||
if not bit:
|
||
fails.append(f"{name}: {checker} van bao xanh du da lam hong")
|
||
|
||
# Everything must be back exactly as it was before this run.
|
||
after = tree_state()
|
||
same = after == before
|
||
print()
|
||
print("cay lam viec sau khi thu giong het truoc:", "co" if same else "*** KHAC ***")
|
||
if not same:
|
||
print(" truoc:", before.replace("\n", " | ") or "(sach)")
|
||
print(" sau :", after.replace("\n", " | ") or "(sach)")
|
||
fails.append("file chua duoc khoi phuc sau khi thu")
|
||
|
||
print()
|
||
if fails:
|
||
print("*** VONG 5 THAT BAI ***")
|
||
for f in fails:
|
||
print(" " + f)
|
||
return 1
|
||
print(f"KET QUA VONG 5: ca {len(MUTATIONS)} phep do deu bat duoc loi khi co tinh lam hong")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|