1+ """
2+ tests/test_gui_layouts.py
3+ Tests GUI instantiation by deeply patching the Matplotlib Figure class.
4+ """
5+ import pytest
6+ import os
7+ import sys
8+ import inspect
9+ from unittest .mock import MagicMock , patch
10+
11+ project_root = os .path .abspath (os .path .join (os .path .dirname (__file__ ), '..' ))
12+ if project_root not in sys .path :
13+ sys .path .insert (0 , project_root )
14+
15+ GUI_TARGETS = [
16+ "Keithley_6517B.High_Resistance.IV_K6517B_GUI_v11" ,
17+ "Keithley_6517B.High_Resistance.RT_K6517B_L350_T_Control_GUI_v13" ,
18+ "Keithley_2400.IV_K2400_GUI_v5" ,
19+ "Lakeshore_350_340.T_Sensing_L350_GUI_v4"
20+ ]
21+
22+ # --- SAFE CLASSES ---
23+ class SafeAxis (MagicMock ):
24+ """An Axis that always returns a list for plot()"""
25+ def plot (self , * args , ** kwargs ):
26+ return [MagicMock ()]
27+
28+ class SafeFigure (MagicMock ):
29+ """A Figure that creates SafeAxes"""
30+ def add_subplot (self , * args , ** kwargs ):
31+ return SafeAxis ()
32+
33+ def subplots (self , nrows = 1 , ncols = 1 , ** kwargs ):
34+ count = nrows * ncols
35+ if count > 1 :
36+ return [SafeAxis () for _ in range (count )]
37+ return SafeAxis ()
38+
39+ def tight_layout (self , * args , ** kwargs ):
40+ pass
41+
42+ @pytest .fixture
43+ def safe_gui_environment ():
44+ # We need to patch where the code IMPORTS it from.
45+ # Most GUI files likely do: from matplotlib.figure import Figure
46+
47+ with patch ('matplotlib.figure.Figure' , side_effect = SafeFigure ) as MockFigClass , \
48+ patch ('matplotlib.pyplot.figure' , return_value = SafeFigure ()), \
49+ patch ('matplotlib.backends.backend_tkagg.FigureCanvasTkAgg' ) as MockCanvas :
50+
51+ # Setup Canvas
52+ mock_canvas_instance = MockCanvas .return_value
53+ mock_canvas_instance .draw = MagicMock ()
54+ mock_canvas_instance .get_tk_widget .return_value = MagicMock ()
55+
56+ # Also mock other heavy libs just in case
57+ with patch .dict ('sys.modules' , {
58+ 'tkinter' : MagicMock (),
59+ 'tkinter.ttk' : MagicMock (),
60+ 'tkinter.filedialog' : MagicMock (),
61+ 'tkinter.messagebox' : MagicMock (),
62+ 'tkinter.simpledialog' : MagicMock (),
63+ 'pyvisa' : MagicMock (),
64+ 'pymeasure' : MagicMock (),
65+ 'PIL' : MagicMock (),
66+ 'PIL.Image' : MagicMock (),
67+ 'PIL.ImageTk' : MagicMock ()
68+ }):
69+ yield
70+
71+ @pytest .mark .parametrize ("module_name" , GUI_TARGETS )
72+ def test_instantiate_gui_layout (module_name , safe_gui_environment ):
73+ import importlib
74+ try :
75+ # Reload to ensure patches take effect on import
76+ if module_name in sys .modules :
77+ del sys .modules [module_name ]
78+ module = importlib .import_module (module_name )
79+ except ImportError :
80+ pytest .skip (f"Could not import { module_name } " )
81+
82+ gui_class = None
83+ for name , obj in inspect .getmembers (module , inspect .isclass ):
84+ if obj .__module__ == module .__name__ :
85+ if "GUI" in name or "App" in name or "Window" in name :
86+ gui_class = obj
87+ break
88+
89+ if gui_class :
90+ try :
91+ app = gui_class (MagicMock ())
92+ assert app is not None
93+ print (f"\n [Layout] Successfully built layout for { module_name } " )
94+ except Exception as e :
95+ pytest .fail (f"Layout crash in { module_name } : { e } " )
96+ else :
97+ print (f"\n [Skip] No GUI class found in { module_name } " )
0 commit comments