fix(gui_2): use str sentinel not bytes in _capture_workspace_profile
This commit is contained in:
+133
@@ -0,0 +1,133 @@
|
|||||||
|
"""Manually start sloppy.py, then run the test against the same GUI process."""
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import socket
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Start sloppy.py
|
||||||
|
project_root = Path("C:/projects/manual_slop").absolute()
|
||||||
|
gui_script = project_root / "sloppy.py"
|
||||||
|
test_workspace = project_root / "tests" / "artifacts" / "live_gui_workspace"
|
||||||
|
|
||||||
|
# Clean up old workspace
|
||||||
|
if test_workspace.exists():
|
||||||
|
import shutil
|
||||||
|
for _ in range(5):
|
||||||
|
try:
|
||||||
|
shutil.rmtree(test_workspace)
|
||||||
|
break
|
||||||
|
except PermissionError:
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
test_workspace.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Create minimal files
|
||||||
|
(test_workspace / "manual_slop.toml").write_text("[project]\nname = 'TestProject'\n\n[conductor]\ndir = 'conductor'\n", encoding="utf-8")
|
||||||
|
(test_workspace / "conductor" / "tracks").mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
config_content = {
|
||||||
|
'ai': {'provider': 'gemini', 'model': 'gemini-2.5-flash-lite'},
|
||||||
|
'projects': {
|
||||||
|
'paths': [str((test_workspace / 'manual_slop.toml').absolute())],
|
||||||
|
'active': str((test_workspace / 'manual_slop.toml').absolute())
|
||||||
|
},
|
||||||
|
'paths': {
|
||||||
|
'logs_dir': str((test_workspace / "logs").absolute()),
|
||||||
|
'scripts_dir': str((test_workspace / "scripts" / "generated").absolute())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
import tomli_w
|
||||||
|
with open(test_workspace / 'config.toml', 'wb') as f:
|
||||||
|
tomli_w.dump(config_content, f)
|
||||||
|
|
||||||
|
# Start sloppy.py
|
||||||
|
os.makedirs("logs", exist_ok=True)
|
||||||
|
log_file = open("logs/sloppy_py_test_2.log", "w", encoding="utf-8")
|
||||||
|
env = os.environ.copy()
|
||||||
|
env["PYTHONPATH"] = str(project_root.absolute())
|
||||||
|
env["SLOP_CONFIG"] = str((test_workspace / "config.toml").absolute())
|
||||||
|
env["SLOP_GLOBAL_PRESETS"] = str((test_workspace / "presets.toml").absolute())
|
||||||
|
env["SLOP_GLOBAL_TOOL_PRESETS"] = str((test_workspace / "tool_presets.toml").absolute())
|
||||||
|
|
||||||
|
print("Starting sloppy.py...")
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
["uv", "run", "python", "-u", str(gui_script), "--enable-test-hooks"],
|
||||||
|
stdout=log_file,
|
||||||
|
stderr=log_file,
|
||||||
|
text=True,
|
||||||
|
cwd=str(test_workspace.absolute()),
|
||||||
|
env=env,
|
||||||
|
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if os.name == 'nt' else 0
|
||||||
|
)
|
||||||
|
print(f"Started PID: {proc.pid}")
|
||||||
|
|
||||||
|
# Wait for hook server
|
||||||
|
import requests
|
||||||
|
for i in range(30):
|
||||||
|
try:
|
||||||
|
resp = requests.get("http://127.0.0.1:8999/status", timeout=0.5)
|
||||||
|
if resp.status_code == 200:
|
||||||
|
print(f"Hook server ready after {i*0.5}s")
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
time.sleep(0.5)
|
||||||
|
else:
|
||||||
|
print("Hook server didn't start!")
|
||||||
|
proc.kill()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Wait extra for imgui to fully initialize
|
||||||
|
print("Waiting 3s for imgui to stabilize...")
|
||||||
|
time.sleep(3.0)
|
||||||
|
|
||||||
|
# Now run the actual test flow
|
||||||
|
from src.api_hook_client import ApiHookClient
|
||||||
|
client = ApiHookClient()
|
||||||
|
|
||||||
|
print("\n[1] set_value show_windows {Diagnostics: True}")
|
||||||
|
client.set_value('show_windows', {'Diagnostics': True})
|
||||||
|
time.sleep(1.0)
|
||||||
|
|
||||||
|
print("\n[2] push_event save_workspace_profile")
|
||||||
|
client.push_event('custom_callback', {'callback': 'save_workspace_profile', 'args': ['Tier3Profile', 'project']})
|
||||||
|
time.sleep(1.0)
|
||||||
|
|
||||||
|
print("\n[3] set_value show_windows {Diagnostics: False}")
|
||||||
|
client.set_value('show_windows', {'Diagnostics': False})
|
||||||
|
|
||||||
|
print("\n[4] set_value ui_auto_switch_layout")
|
||||||
|
client.set_value('ui_auto_switch_layout', True)
|
||||||
|
|
||||||
|
print("\n[5] set_value ui_tier_layout_bindings")
|
||||||
|
client.set_value('ui_tier_layout_bindings', {'Tier 1': '', 'Tier 2': '', 'Tier 3': 'Tier3Profile', 'Tier 4': ''})
|
||||||
|
|
||||||
|
def trigger_tier(tier):
|
||||||
|
client.push_event("mma_state_update", {"status": "running", "active_tier": tier})
|
||||||
|
|
||||||
|
print("\n[6] trigger Tier 2")
|
||||||
|
trigger_tier('Tier 2 (Tech Lead)')
|
||||||
|
time.sleep(1.0)
|
||||||
|
val = client.get_value('show_windows')
|
||||||
|
print(f"[after Tier 2] show_windows: {val!r}")
|
||||||
|
assert val is not None, "show_windows is None"
|
||||||
|
assert val.get('Diagnostics', False) == False, f"Expected False, got {val}"
|
||||||
|
|
||||||
|
print("\n[7] trigger Tier 3")
|
||||||
|
trigger_tier('Tier 3 (Worker): task-1')
|
||||||
|
time.sleep(1.0)
|
||||||
|
val = client.get_value('show_windows')
|
||||||
|
print(f"[after Tier 3] show_windows: {val!r}")
|
||||||
|
assert val.get('Diagnostics', False) == True, f"Expected True, got {val}"
|
||||||
|
|
||||||
|
print("\nALL ASSERTIONS PASSED!")
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
print("Killing sloppy.py...")
|
||||||
|
proc.kill()
|
||||||
|
try:
|
||||||
|
proc.wait(timeout=5)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
log_file.close()
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
"""Minimal reproducer for the auto_switch_sim GUI crash."""
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
sys.path.insert(0, 'C:/projects/manual_slop')
|
||||||
|
sys.path.insert(0, 'C:/projects/manual_slop/src')
|
||||||
|
|
||||||
|
from src.api_hook_client import ApiHookClient
|
||||||
|
client = ApiHookClient()
|
||||||
|
if not client.wait_for_server(timeout=15):
|
||||||
|
print('FAIL: server not up')
|
||||||
|
sys.exit(1)
|
||||||
|
print('OK: server up')
|
||||||
|
|
||||||
|
print('Step 1: click btn_reset')
|
||||||
|
client.click('btn_reset')
|
||||||
|
time.sleep(1.0)
|
||||||
|
print('Step 1 done, status=', client.get_value('ai_status'))
|
||||||
|
|
||||||
|
print('Step 2: set_value current_provider gemini_cli')
|
||||||
|
client.set_value('current_provider', 'gemini_cli')
|
||||||
|
time.sleep(1.0)
|
||||||
|
print('Step 2 done')
|
||||||
|
|
||||||
|
print('Step 3: set_value gcli_path')
|
||||||
|
mock_path = os.path.abspath('tests/mock_concurrent_mma.py')
|
||||||
|
client.set_value('gcli_path', '"' + sys.executable + '" "' + mock_path + '"')
|
||||||
|
time.sleep(1.0)
|
||||||
|
print('Step 3 done')
|
||||||
+5
-5
@@ -69,6 +69,11 @@ scale = 1.0199999809265137
|
|||||||
transparency = 1.0
|
transparency = 1.0
|
||||||
child_transparency = 1.0
|
child_transparency = 1.0
|
||||||
|
|
||||||
|
[theme.tone_mapping.solarized_light]
|
||||||
|
brightness = 0.6899999976158142
|
||||||
|
contrast = 0.8600000143051147
|
||||||
|
gamma = 0.7699999809265137
|
||||||
|
|
||||||
[theme.tone_mapping.moss]
|
[theme.tone_mapping.moss]
|
||||||
brightness = 1.059999942779541
|
brightness = 1.059999942779541
|
||||||
contrast = 0.5799999833106995
|
contrast = 0.5799999833106995
|
||||||
@@ -84,11 +89,6 @@ brightness = 0.7699999809265137
|
|||||||
contrast = 0.7200000286102295
|
contrast = 0.7200000286102295
|
||||||
gamma = 0.6899999976158142
|
gamma = 0.6899999976158142
|
||||||
|
|
||||||
[theme.tone_mapping.solarized_light]
|
|
||||||
brightness = 0.6899999976158142
|
|
||||||
contrast = 0.8600000143051147
|
|
||||||
gamma = 0.7699999809265137
|
|
||||||
|
|
||||||
[mma]
|
[mma]
|
||||||
max_workers = 4
|
max_workers = 4
|
||||||
|
|
||||||
|
|||||||
@@ -9,5 +9,5 @@ active = "main"
|
|||||||
|
|
||||||
[discussions.main]
|
[discussions.main]
|
||||||
git_commit = ""
|
git_commit = ""
|
||||||
last_updated = "2026-06-04T20:08:39"
|
last_updated = "2026-06-05T18:41:59"
|
||||||
history = []
|
history = []
|
||||||
|
|||||||
+9
-9
@@ -598,15 +598,15 @@ class App:
|
|||||||
finally:
|
finally:
|
||||||
self._is_applying_snapshot = False
|
self._is_applying_snapshot = False
|
||||||
|
|
||||||
def _capture_workspace_profile(self, name: str) -> models.WorkspaceProfile:
|
def _capture_workspace_profile(self, name: str) -> models.WorkspaceProfile:
|
||||||
if not getattr(self, "_ini_capture_ready", False):
|
if not getattr(self, "_ini_capture_ready", False):
|
||||||
self._ini_capture_ready = True
|
self._ini_capture_ready = True
|
||||||
ini = b""
|
ini = ""
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
ini = imgui.save_ini_settings_to_memory()
|
ini = str(imgui.save_ini_settings_to_memory() or "")
|
||||||
except Exception:
|
except Exception:
|
||||||
ini = b""
|
ini = ""
|
||||||
panel_states = {
|
panel_states = {
|
||||||
"ui_separate_context_preview": getattr(self, "ui_separate_context_preview", False),
|
"ui_separate_context_preview": getattr(self, "ui_separate_context_preview", False),
|
||||||
"ui_separate_message_panel": getattr(self, "ui_separate_message_panel", False),
|
"ui_separate_message_panel": getattr(self, "ui_separate_message_panel", False),
|
||||||
|
|||||||
Reference in New Issue
Block a user