2323
2424from __future__ import annotations
2525
26- import json
2726import os
2827import socket
2928import sys
@@ -143,26 +142,16 @@ def _worker(): flag["ok"] = True
143142 t .start (); t .join (timeout = 2 )
144143 _record ("Layer 1" , "Daemon thread" , flag ["ok" ])
145144
146- # HTTP server round-trip (use a different port to avoid conflicts )
145+ # HTTP server class instantiable (daemon thread round-trip removed — unsafe in UEFN Slate tick )
147146 try :
148147 class _H (BaseHTTPRequestHandler ):
149- def do_GET (self ):
150- self .send_response (200 )
151- self .send_header ("Content-Type" , "application/json" )
152- self .end_headers ()
153- self .wfile .write (b'{"ok":true}' )
148+ def do_GET (self ): pass
154149 def log_message (self , * a ): pass
155-
156- srv = HTTPServer (("127.0.0.1" , 18765 ), _H )
157- th = threading .Thread (target = srv .serve_forever , daemon = True )
158- th .start ()
159- import urllib .request
160- resp = urllib .request .urlopen ("http://127.0.0.1:18765/" , timeout = 3 )
161- body = json .loads (resp .read ())
162- srv .shutdown ()
163- _record ("Layer 1" , "HTTP server round-trip" , body .get ("ok" ) is True )
150+ srv = HTTPServer (("127.0.0.1" , 0 ), _H ) # port 0 = OS assigns, never serve_forever
151+ srv .server_close ()
152+ _record ("Layer 1" , "HTTPServer instantiable" , True )
164153 except Exception as e :
165- _record ("Layer 1" , "HTTP server round-trip " , False , str (e ))
154+ _record ("Layer 1" , "HTTPServer instantiable " , False , str (e ))
166155
167156 # File write
168157 try :
@@ -293,7 +282,7 @@ def _layer_toolbelt() -> None:
293282 "config_list" ,
294283 "config_get" ,
295284 "mcp_status" ,
296- " mcp_restart" ,
285+ # mcp_restart intentionally excluded — registers Slate tick callbacks mid-test → crash
297286 "plugin_export_manifest" ,
298287 "plugin_validate_all" ,
299288 "plugin_list_custom" ,
@@ -381,12 +370,15 @@ def _layer_dashboard() -> None:
381370 "run: <UE>/Engine/Binaries/ThirdParty/Python3/Win64/python.exe -m pip install PySide6" )
382371 return
383372
373+ # QApplication instantiation skipped — creating one mid-tick and letting it
374+ # GC immediately causes EXCEPTION_ACCESS_VIOLATION in UEFN's Slate loop.
384375 try :
385376 from PySide6 .QtWidgets import QApplication
386- app = QApplication .instance () or QApplication (sys .argv )
387- _record ("Layer 5" , "QApplication" , app is not None )
377+ app = QApplication .instance ()
378+ _record ("Layer 5" , "QApplication (existing)" , app is not None ,
379+ "None = dashboard not yet launched (normal)" if app is None else "running" )
388380 except Exception as e :
389- _record ("Layer 5" , "QApplication" , False , str (e ))
381+ _record ("Layer 5" , "QApplication check " , False , str (e ))
390382
391383 try :
392384 from UEFN_Toolbelt .dashboard_pyside6 import ToolbeltDashboard
@@ -400,9 +392,9 @@ def _layer_dashboard() -> None:
400392def _layer_verse_book () -> None :
401393 _header ("Layer 6 — Verse Book (Spec Reference)" )
402394
403- # Locate verse-book relative to this file (tests/ → project root → verse-book)
395+ # smoke_test.py lives at Content/Python/UEFN_Toolbelt/ — go 3 levels up to project root
404396 here = os .path .dirname (os .path .abspath (__file__ ))
405- book_root = os .path .normpath (os .path .join (here , ".." , "verse-book" ))
397+ book_root = os .path .normpath (os .path .join (here , ".." , ".." , ".." , " verse-book" ))
406398 docs_path = os .path .join (book_root , "docs" )
407399
408400 # Clone present
0 commit comments