artifacts
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
"""Wrap the body of the if (app.active_track or app.active_tickets) and app.node_editor_ctx:
|
||||
block in render_task_dag_panel with a try/except. To do this without manually
|
||||
reindenting ~195 lines, we use a Python script that finds the if line, dedents
|
||||
the body, inserts try/except, and re-indents.
|
||||
"""
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
GUI_2 = Path(r"C:\projects\manual_slop_tier2\src\gui_2.py")
|
||||
text = GUI_2.read_text(encoding="utf-8")
|
||||
lines = text.splitlines(keepends=True)
|
||||
|
||||
# Find the function start
|
||||
fn_start = None
|
||||
for i, line in enumerate(lines):
|
||||
if "def render_task_dag_panel" in line:
|
||||
fn_start = i
|
||||
break
|
||||
|
||||
if fn_start is None:
|
||||
raise RuntimeError("render_task_dag_panel not found")
|
||||
|
||||
# Find the if (app.active_track or app.active_tickets) and app.node_editor_ctx: line
|
||||
if_start = None
|
||||
for i in range(fn_start, len(lines)):
|
||||
line = lines[i]
|
||||
stripped = line.lstrip()
|
||||
if stripped.startswith("if (app.active_track or app.active_tickets)"):
|
||||
if_start = i
|
||||
break
|
||||
|
||||
if if_start is None:
|
||||
raise RuntimeError("if not found")
|
||||
|
||||
# Find the end of the if block (matching 0-indent next line, or next def)
|
||||
if_end = None
|
||||
for i in range(if_start + 1, len(lines)):
|
||||
line = lines[i]
|
||||
if line.strip() == "" or line.startswith(" ") or line.startswith("\t"):
|
||||
continue
|
||||
# Found a non-indented line that's not blank
|
||||
if_end = i
|
||||
break
|
||||
|
||||
if if_end is None:
|
||||
if_end = len(lines)
|
||||
|
||||
# Find body bounds (indented lines)
|
||||
body_start = if_start + 1
|
||||
while body_start < if_end and lines[body_start].strip() == "":
|
||||
body_start += 1
|
||||
|
||||
# Find actual body end (last indented line before 0-indent)
|
||||
body_end = body_end = if_end
|
||||
for i in range(if_end - 1, if_start, -1):
|
||||
if lines[i].strip() != "":
|
||||
body_end = i + 1
|
||||
break
|
||||
|
||||
# Get the indentation of the if line
|
||||
if_indent = len(lines[if_start]) - len(lines[if_start].lstrip())
|
||||
# Body indent should be if_indent + 2 (next level)
|
||||
body_indent = if_indent + 2
|
||||
|
||||
# Extract body lines (preserve content)
|
||||
body_lines = lines[body_start:body_end]
|
||||
|
||||
# Detent: strip body_indent spaces from each line
|
||||
detented_body = []
|
||||
for ln in body_lines:
|
||||
if ln.strip() == "":
|
||||
detented_body.append("\n")
|
||||
elif ln.startswith(" " * body_indent):
|
||||
detented_body.append(" " + ln[body_indent:])
|
||||
else:
|
||||
detented_body.append(ln)
|
||||
|
||||
# Re-indent: add body_indent spaces to each line
|
||||
reindented_body = []
|
||||
for ln in detented_body:
|
||||
if ln.strip() == "":
|
||||
reindented_body.append("\n")
|
||||
else:
|
||||
reindented_body.append(" " * (body_indent - 1) + ln)
|
||||
|
||||
# Build the new content
|
||||
new_lines = []
|
||||
new_lines.append(lines[if_start]) # the if line
|
||||
new_lines.append(" " * (if_indent + 1) + "try:\n")
|
||||
for ln in reindented_body:
|
||||
new_lines.append(ln)
|
||||
new_lines.append(" " * (if_indent + 1) + "except Exception as dag_err:\n")
|
||||
new_lines.append(" " * (if_indent + 2) + "# imgui-node-editor can raise ImGui assertions (e.g. Missing PopID) that\n")
|
||||
new_lines.append(" " * (if_indent + 2) + "# would otherwise kill the ImGui main loop and break every subsequent\n")
|
||||
new_lines.append(" " * (if_indent + 2) + "# test in the same xdist worker that shares the live_gui subprocess.\n")
|
||||
new_lines.append(" " * (if_indent + 2) + "if not hasattr(app, '_last_request_errors'): app._last_request_errors = []\n")
|
||||
new_lines.append(" " * (if_indent + 2) + "app._last_request_errors.append(('render_task_dag_panel.dag_error', dag_err))\n")
|
||||
|
||||
# Lines after the if body (0-indent)
|
||||
for i in range(body_end, if_end):
|
||||
new_lines.append(lines[i])
|
||||
|
||||
# Replace
|
||||
new_text = "".join(lines[:if_start]) + "".join(new_lines) + "".join(lines[if_end:])
|
||||
|
||||
# Write back
|
||||
GUI_2.write_text(new_text, encoding="utf-8")
|
||||
print(f"OK: modified if block at line {if_start+1}, body lines {body_end - body_start}")
|
||||
@@ -0,0 +1,138 @@
|
||||
"""Verify runtime dock state by monkey-patching and registering a custom callback."""
|
||||
import sys, time, subprocess, shutil, urllib.request, json
|
||||
from pathlib import Path
|
||||
|
||||
PROJ = Path("C:/projects/manual_slop_tier2")
|
||||
WORK = PROJ / "tests" / "artifacts" / "diag6_runtime_state"
|
||||
LOG = PROJ / "tests" / "artifacts" / "diag6_runtime_state.log"
|
||||
RUNTIME_INI = PROJ / "tests" / "artifacts" / "diag6_runtime_ini.txt"
|
||||
|
||||
# Clean
|
||||
if WORK.exists():
|
||||
shutil.rmtree(WORK)
|
||||
WORK.mkdir(parents=True, exist_ok=True)
|
||||
if LOG.exists():
|
||||
LOG.unlink()
|
||||
if RUNTIME_INI.exists():
|
||||
RUNTIME_INI.unlink()
|
||||
|
||||
# Wrapper that registers a custom callback BEFORE the app launches
|
||||
WRAPPER = PROJ / "tests" / "artifacts" / "diag6_wrapper.py"
|
||||
WRAPPER.write_text('''
|
||||
import sys, json
|
||||
sys.path.insert(0, "C:/projects/manual_slop_tier2")
|
||||
sys.argv = ["sloppy.py", "--enable-test-hooks"]
|
||||
|
||||
# Import imgui and patch save_ini_settings_to_disk to also write to a known file
|
||||
from imgui_bundle import imgui
|
||||
_orig_save_disk = imgui.save_ini_settings_to_disk
|
||||
def _hooked_save_disk(path):
|
||||
res = _orig_save_disk(path)
|
||||
try:
|
||||
mem = imgui.save_ini_settings_to_memory()
|
||||
with open("C:/projects/manual_slop_tier2/tests/artifacts/diag6_runtime_ini.txt", "w", encoding="utf-8") as f:
|
||||
f.write(str(mem) if mem else "")
|
||||
except Exception as e:
|
||||
with open("C:/projects/manual_slop_tier2/tests/artifacts/diag6_runtime_ini.txt", "w", encoding="utf-8") as f:
|
||||
f.write(f"ERR: {e}")
|
||||
return res
|
||||
imgui.save_ini_settings_to_disk = _hooked_save_disk
|
||||
|
||||
# Also patch save_ini_settings_to_memory to capture runtime state
|
||||
def _hooked_save_mem():
|
||||
res = imgui.save_ini_settings_to_memory()
|
||||
try:
|
||||
with open("C:/projects/manual_slop_tier2/tests/artifacts/diag6_runtime_ini.txt", "w", encoding="utf-8") as f:
|
||||
f.write(str(res) if res else "")
|
||||
except Exception:
|
||||
pass
|
||||
return res
|
||||
|
||||
# Now import app
|
||||
from src.gui_2 import App as _App
|
||||
_orig_init = _App.__init__
|
||||
def _patched_init(self, *a, **kw):
|
||||
_orig_init(self, *a, **kw)
|
||||
# Register a custom callback that saves the runtime INI to a known location
|
||||
self.controller._predefined_callbacks["_diag_save_runtime_ini"] = lambda: _hooked_save_mem()
|
||||
_App.__init__ = _patched_init
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from src.paths import initialize_paths
|
||||
initialize_paths(None)
|
||||
from src.gui_2 import main as _sloppy_main
|
||||
_sloppy_main()
|
||||
''')
|
||||
|
||||
# Spawn
|
||||
print("Launching with patched callback...", flush=True)
|
||||
proc = subprocess.Popen(
|
||||
["uv", "run", "python", "-u", str(WRAPPER.absolute())],
|
||||
stdout=open(LOG, "w"),
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=str(WORK),
|
||||
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if sys.platform == "win32" else 0,
|
||||
)
|
||||
|
||||
ready = False
|
||||
for i in range(30):
|
||||
time.sleep(0.5)
|
||||
try:
|
||||
urllib.request.urlopen("http://127.0.0.1:8999/status", timeout=1)
|
||||
ready = True
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not ready:
|
||||
subprocess.run(["taskkill", "/F", "/T", "/PID", str(proc.pid)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False)
|
||||
print("NO_HOOK", flush=True)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Hook ready. Waiting 15s for first frame + dock layout to settle...", flush=True)
|
||||
time.sleep(15)
|
||||
|
||||
# Trigger the custom callback to save runtime INI
|
||||
print("Triggering _diag_save_runtime_ini callback...", flush=True)
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
"http://127.0.0.1:8999/api/gui",
|
||||
data=json.dumps({"action": "custom_callback", "callback": "_diag_save_runtime_ini", "args": []}).encode(),
|
||||
headers={"Content-Type": "application/json"},
|
||||
method="POST",
|
||||
)
|
||||
r = urllib.request.urlopen(req, timeout=5)
|
||||
print(f" POST response: {r.read().decode()}", flush=True)
|
||||
except Exception as e:
|
||||
print(f" POST failed: {e}", flush=True)
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
# Read runtime INI
|
||||
if RUNTIME_INI.exists():
|
||||
runtime_text = RUNTIME_INI.read_text(encoding="utf-8", errors="replace")
|
||||
print(f"\n=== RUNTIME INI ({len(runtime_text)} bytes) ===", flush=True)
|
||||
for line in runtime_text.splitlines()[:60]:
|
||||
print(f" {line}", flush=True)
|
||||
print("=== END ===", flush=True)
|
||||
else:
|
||||
print(f"\nRUNTIME_INI not written to {RUNTIME_INI}", flush=True)
|
||||
|
||||
# Compare with bundled
|
||||
bundled = (PROJ / "layouts" / "default.ini").read_text(encoding="utf-8")
|
||||
if RUNTIME_INI.exists():
|
||||
runtime_text = RUNTIME_INI.read_text(encoding="utf-8", errors="replace")
|
||||
if runtime_text == bundled:
|
||||
print("\n=> RUNTIME INI == BUNDLED INI (HelloImGui honored the layout)", flush=True)
|
||||
else:
|
||||
print("\n=> RUNTIME INI != BUNDLED INI (HelloImGui rebuilt the dock)", flush=True)
|
||||
# Show diff
|
||||
bundled_lines = bundled.splitlines()
|
||||
runtime_lines = runtime_text.splitlines()
|
||||
print(f" bundled: {len(bundled_lines)} lines", flush=True)
|
||||
print(f" runtime: {len(runtime_lines)} lines", flush=True)
|
||||
|
||||
# Kill
|
||||
subprocess.run(["taskkill", "/F", "/T", "/PID", str(proc.pid)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False)
|
||||
time.sleep(1)
|
||||
@@ -0,0 +1,102 @@
|
||||
"""Regression test: verify the auto-dock layout actually takes effect at runtime.
|
||||
|
||||
Launches sloppy.py, waits for first frame + a few seconds, then triggers
|
||||
imgui.save_ini_settings_to_disk via custom callback. If HelloImGui honored
|
||||
the bundled INI's dock structure, the saved INI matches the bundled one.
|
||||
If HelloImGui rebuilt the dock (because runtime DockSpace ID didn't match
|
||||
or windows weren't yet created when INI was read), the saved INI differs.
|
||||
"""
|
||||
import os, sys, time, subprocess, shutil, json, urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
PROJ = Path("C:/projects/manual_slop_tier2")
|
||||
WORK = PROJ / "tests" / "artifacts" / "diag5_verify_autodock"
|
||||
LOG = PROJ / "tests" / "artifacts" / "diag5_verify_autodock.log"
|
||||
INI_INSTALLED = WORK / "manualslop_layout.ini"
|
||||
INI_AFTER_SAVE = WORK / "manualslop_layout.ini" # same path, post-save
|
||||
|
||||
# Clean workspace
|
||||
if WORK.exists():
|
||||
shutil.rmtree(WORK)
|
||||
WORK.mkdir(parents=True, exist_ok=True)
|
||||
if LOG.exists():
|
||||
LOG.unlink()
|
||||
|
||||
# Bundle the layout INI for comparison (this is what gets installed)
|
||||
BUNDLED_INI = (PROJ / "layouts" / "default.ini").read_text(encoding="utf-8")
|
||||
|
||||
# Spawn sloppy.py with --enable-test-hooks so we can call API
|
||||
print("Launching sloppy.py...", flush=True)
|
||||
proc = subprocess.Popen(
|
||||
["uv", "run", "python", "-u", str(PROJ / "sloppy.py"), "--enable-test-hooks"],
|
||||
stdout=open(LOG, "w"),
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=str(WORK),
|
||||
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if sys.platform == "win32" else 0,
|
||||
)
|
||||
|
||||
ready = False
|
||||
for i in range(30):
|
||||
time.sleep(0.5)
|
||||
try:
|
||||
urllib.request.urlopen("http://127.0.0.1:8999/status", timeout=1)
|
||||
ready = True
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not ready:
|
||||
subprocess.run(["taskkill", "/F", "/T", "/PID", str(proc.pid)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False)
|
||||
print("NO_HOOK: hook server never started", flush=True)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Hook ready, waiting 5s for first frame + dock layout to settle...", flush=True)
|
||||
time.sleep(5)
|
||||
|
||||
# Check that the install wrote the INI
|
||||
assert INI_INSTALLED.exists(), f"pre-run install did not write INI to {INI_INSTALLED}"
|
||||
installed_size = INI_INSTALLED.stat().st_size
|
||||
print(f"Installed INI: {installed_size} bytes at {INI_INSTALLED}", flush=True)
|
||||
|
||||
# Now trigger save_ini_settings_to_disk via a custom callback
|
||||
print("Triggering save_ini_settings_to_disk via custom callback...", flush=True)
|
||||
|
||||
# Push a callback that calls imgui.save_ini_settings_to_disk
|
||||
payload = {
|
||||
"callback": "_trigger_save_ini",
|
||||
"args": ["manualslop_layout.ini"]
|
||||
}
|
||||
|
||||
# Try direct POST to /api/gui
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
"http://127.0.0.1:8999/api/gui",
|
||||
data=json.dumps(payload).encode(),
|
||||
headers={"Content-Type": "application/json"},
|
||||
method="POST",
|
||||
)
|
||||
r = urllib.request.urlopen(req, timeout=5)
|
||||
print(f" POST /api/gui response: {r.read().decode()}", flush=True)
|
||||
except Exception as e:
|
||||
print(f" POST /api/gui failed: {e}", flush=True)
|
||||
|
||||
# Wait for save to complete
|
||||
time.sleep(2)
|
||||
|
||||
# Check the INI after save
|
||||
if not INI_AFTER_SAVE.exists():
|
||||
print(f"NO_INI: file not found at {INI_AFTER_SAVE}", flush=True)
|
||||
else:
|
||||
after_text = INI_AFTER_SAVE.read_text(encoding="utf-8")
|
||||
after_size = INI_AFTER_SAVE.stat().st_size
|
||||
print(f"Post-save INI: {after_size} bytes", flush=True)
|
||||
print(f"Same as bundled? {after_text == BUNDLED_INI}", flush=True)
|
||||
|
||||
# Show first 20 lines of post-save INI
|
||||
print("\n--- POST-SAVE INI (first 20 lines) ---", flush=True)
|
||||
for line in after_text.splitlines()[:20]:
|
||||
print(f" {line}", flush=True)
|
||||
print("--- END ---", flush=True)
|
||||
|
||||
# Kill
|
||||
subprocess.run(["taskkill", "/F", "/T", "/PID", str(proc.pid)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False)
|
||||
@@ -0,0 +1,69 @@
|
||||
"""Check what windows are visible in the runtime via the live_gui fixture."""
|
||||
import subprocess, time, urllib.request, json, sys
|
||||
from pathlib import Path
|
||||
|
||||
PROJ = Path("C:/projects/manual_slop_tier2")
|
||||
WORK = PROJ / "tests" / "artifacts" / "diag7_visible"
|
||||
LOG = PROJ / "tests" / "artifacts" / "diag7_visible.log"
|
||||
|
||||
if WORK.exists():
|
||||
import shutil
|
||||
shutil.rmtree(WORK)
|
||||
WORK.mkdir(parents=True, exist_ok=True)
|
||||
if LOG.exists():
|
||||
LOG.unlink()
|
||||
|
||||
# Spawn
|
||||
print("Launching...", flush=True)
|
||||
proc = subprocess.Popen(
|
||||
["uv", "run", "python", "-u", str(PROJ / "sloppy.py"), "--enable-test-hooks"],
|
||||
stdout=open(LOG, "w"),
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=str(WORK),
|
||||
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if sys.platform == "win32" else 0,
|
||||
)
|
||||
|
||||
ready = False
|
||||
for i in range(30):
|
||||
time.sleep(0.5)
|
||||
try:
|
||||
urllib.request.urlopen("http://127.0.0.1:8999/status", timeout=1)
|
||||
ready = True
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not ready:
|
||||
subprocess.run(["taskkill", "/F", "/T", "/PID", str(proc.pid)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False)
|
||||
print("NO_HOOK", flush=True)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Ready, waiting 10s...", flush=True)
|
||||
time.sleep(10)
|
||||
|
||||
# Query /api/gui/state for show_windows
|
||||
try:
|
||||
r = urllib.request.urlopen("http://127.0.0.1:8999/api/gui/state", timeout=2)
|
||||
state = json.loads(r.read().decode())
|
||||
print(f"\nshow_windows state:", flush=True)
|
||||
if "show_windows" in state:
|
||||
for k, v in sorted(state["show_windows"].items()):
|
||||
print(f" {k}: {v}", flush=True)
|
||||
except Exception as e:
|
||||
print(f"ERROR: {e}", flush=True)
|
||||
|
||||
# Trigger save_ini to memory to capture runtime state
|
||||
import json
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
"http://127.0.0.1:8999/api/gui",
|
||||
data=json.dumps({"action": "custom_callback", "callback": "noop", "args": []}).encode(),
|
||||
headers={"Content-Type": "application/json"},
|
||||
method="POST",
|
||||
)
|
||||
urllib.request.urlopen(req, timeout=2)
|
||||
except: pass
|
||||
|
||||
# Kill
|
||||
subprocess.run(["taskkill", "/F", "/T", "/PID", str(proc.pid)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False)
|
||||
time.sleep(1)
|
||||
@@ -0,0 +1,23 @@
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
result = subprocess.run(
|
||||
["uv", "run", "python", "scripts/audit_exception_handling.py", "--src", "src", "--json"],
|
||||
capture_output=True, text=True, cwd=r"C:\projects\manual_slop_tier2",
|
||||
)
|
||||
if result.returncode != 0:
|
||||
print("AUDIT FAILED:", result.stderr[:1000])
|
||||
sys.exit(1)
|
||||
|
||||
data = json.loads(result.stdout)
|
||||
gui = [f for f in data.get("files", []) if "gui_2" in f.get("filename", "")][0]
|
||||
broad = [f for f in gui.get("findings", []) if f.get("category") == "INTERNAL_BROAD_CATCH"]
|
||||
silent = [f for f in gui.get("findings", []) if f.get("category") == "INTERNAL_SILENT_SWALLOW"]
|
||||
print("BROAD CATCHES:")
|
||||
for f in broad:
|
||||
print(f" L{f['line']} {f['context']}")
|
||||
print("SILENT SWALLOWS:")
|
||||
for f in silent:
|
||||
print(f" L{f['line']} {f['context']}")
|
||||
print(f"TOTAL: {len(broad)} broad, {len(silent)} silent")
|
||||
@@ -0,0 +1,27 @@
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
result = subprocess.run(
|
||||
["uv", "run", "python", "scripts/audit_exception_handling.py", "--src", "src", "--json"],
|
||||
capture_output=True, text=True, cwd=r"C:\projects\manual_slop_tier2",
|
||||
)
|
||||
data = json.loads(result.stdout)
|
||||
|
||||
# Check all categories for the helpers we care about
|
||||
gui = [f for f in data.get("files", []) if "gui_2" in f.get("filename", "")][0]
|
||||
all_findings = gui.get("findings", [])
|
||||
# Group by context
|
||||
by_context = {}
|
||||
for f in all_findings:
|
||||
ctx = f.get("context", "")
|
||||
by_context.setdefault(ctx, []).append(f)
|
||||
|
||||
# Check the existing helper
|
||||
for ctx in ["_tier_stream_scroll_sync_result", "_tier_stream_scroll_sync_result.dispatcher", "_install_default_layout_if_empty", "_install_default_layout_if_empty_result", "render_tier_stream_panel"]:
|
||||
print(f"=== {ctx} ===")
|
||||
findings = by_context.get(ctx, [])
|
||||
for f in findings:
|
||||
print(f" L{f['line']} [{f['category']}] {f['kind']}: {f.get('context_extra', '')}")
|
||||
if not findings:
|
||||
print(" (no findings)")
|
||||
Reference in New Issue
Block a user