Skip to content

Commit 32cf5af

Browse files
test updated , more logs
1 parent d052fcc commit 32cf5af

1 file changed

Lines changed: 102 additions & 85 deletions

File tree

tests/test_deep_simulation.py

Lines changed: 102 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,28 @@
22
import sys
33
import os
44
import importlib
5+
import signal
56
from unittest.mock import MagicMock, patch, mock_open
67

78
# -------------------------------------------------------------------------
8-
# 1. GLOBAL MOCKS (The "Matrix")
9-
# We mock the entire physical world to ensure tests run on GitHub.
9+
# 1. GLOBAL MOCKS
1010
# -------------------------------------------------------------------------
11+
# Mock GUI elements
1112
sys.modules['tkinter'] = MagicMock()
1213
sys.modules['tkinter.ttk'] = MagicMock()
1314
sys.modules['tkinter.messagebox'] = MagicMock()
1415
sys.modules['tkinter.filedialog'] = MagicMock()
1516

16-
# Matplotlib Mocks
17+
# Mock Multiprocessing to prevent Queue.get() hangs
18+
mock_mp = MagicMock()
19+
sys.modules['multiprocessing'] = mock_mp
20+
sys.modules['multiprocessing.queues'] = MagicMock()
21+
22+
# Mock Matplotlib
1723
mock_plt = MagicMock()
1824
mock_fig = MagicMock()
1925
mock_ax = MagicMock()
20-
mock_plt.subplots.return_value = (mock_fig, mock_ax)
21-
mock_plt.subplots.side_effect = None
22-
26+
mock_plt.subplots.return_value = (mock_fig, mock_ax)
2327
sys.modules['matplotlib'] = MagicMock()
2428
sys.modules['matplotlib.pyplot'] = mock_plt
2529
sys.modules['matplotlib.figure'] = MagicMock()
@@ -29,179 +33,192 @@
2933
class TestDeepSimulation(unittest.TestCase):
3034

3135
def setUp(self):
32-
# Add project root to path
3336
self.root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
3437
if self.root_dir not in sys.path:
3538
sys.path.insert(0, self.root_dir)
39+
print(f"\n[TEST START] {self._testMethodName}", flush=True)
3640

37-
def get_circuit_breaker(self, limit=10):
38-
"""Returns a side_effect for time.sleep that raises exception after limit."""
39-
return [None] * limit + [Exception("Force Test Exit")]
41+
def tearDown(self):
42+
print(f"[TEST END] {self._testMethodName}\n", flush=True)
43+
44+
# -------------------------------------------------------------------------
45+
# HELPER: The "Watchdog" Timer
46+
# -------------------------------------------------------------------------
47+
def _timeout_handler(self, signum, frame):
48+
raise TimeoutError(f"Test {self._testMethodName} took longer than 30s! Infinite Loop suspected.")
4049

4150
def run_module_safely(self, module_name):
42-
"""Helper: Import module, run main() if exists, handle 'Force Exit'."""
51+
"""Imports and runs a module with a strict 30-second timeout."""
52+
# Set an alarm for 30 seconds (Works on Linux/GitHub Actions)
53+
signal.signal(signal.SIGALRM, self._timeout_handler)
54+
signal.alarm(30)
55+
4356
if module_name in sys.modules:
4457
del sys.modules[module_name]
4558

4659
try:
60+
print(f" -> Importing {module_name}...", flush=True)
4761
mod = importlib.import_module(module_name)
4862
if hasattr(mod, 'main'):
63+
print(f" -> Running {module_name}.main()...", flush=True)
4964
mod.main()
65+
else:
66+
print(f" -> Module loaded (no main function).", flush=True)
5067
except Exception as e:
51-
# Ignore expected exit signals from our circuit breakers
52-
if "Force Test Exit" in str(e) or isinstance(e, KeyboardInterrupt) or isinstance(e, SystemExit):
53-
pass
68+
if "Force Test Exit" in str(e) or isinstance(e, SystemExit):
69+
print(" -> [SUCCESS] Script exited cleanly via Circuit Breaker.", flush=True)
70+
elif isinstance(e, TimeoutError):
71+
print(f" -> [FAIL] CRITICAL TIMEOUT: {e}", flush=True)
72+
raise e # Re-raise to fail the test
5473
else:
55-
print(f" [Info] Script '{module_name}' stopped with: {e}")
74+
print(f" -> [INFO] Script stopped with: {e}", flush=True)
75+
finally:
76+
signal.alarm(0) # Disable the alarm
77+
78+
def get_circuit_breaker(self, limit=10):
79+
"""A mock sleep that counts down and raises an error to break infinite loops."""
80+
def side_effect(*args, **kwargs):
81+
side_effect.counter += 1
82+
# Print a heartbeat so we know the loop is actually running
83+
if side_effect.counter % 2 == 0:
84+
print(f" [Clock] Tick {side_effect.counter}/{limit}...", flush=True)
85+
86+
if side_effect.counter >= limit:
87+
print(" [Clock] Limit reached! Forcing exit.", flush=True)
88+
raise Exception("Force Test Exit")
89+
90+
side_effect.counter = 0
91+
return side_effect
5692

5793
# =========================================================================
58-
# SECTION 1: MAIN MEASUREMENT MODULES
94+
# TESTS
5995
# =========================================================================
6096

6197
def test_01_k2400_iv_backend(self):
62-
print("\n[SIMULATION] 1. Keithley 2400 I-V Sweep...")
63-
with patch('pymeasure.instruments.keithley.Keithley2400') as MockInst:
98+
# GLOBAL PATCH for sleep is critical here
99+
with patch('pymeasure.instruments.keithley.Keithley2400') as MockInst, \
100+
patch('time.sleep', side_effect=self.get_circuit_breaker(5)):
101+
64102
spy = MockInst.return_value
65-
# FIXED: Patch time.sleep globally instead of specific path to ensure capture
66-
breaker = self.get_circuit_breaker(5)
67-
with patch('builtins.input', side_effect=['100', '10', 'test']), \
68-
patch('pandas.DataFrame.to_csv'), \
69-
patch('time.sleep', side_effect=breaker):
103+
with patch('builtins.input', side_effect=['100', '10', 'test_file']), \
104+
patch('pandas.DataFrame.to_csv'):
70105
self.run_module_safely("Keithley_2400.Backends.IV_K2400_Loop_Backend_v10")
71106
spy.enable_source.assert_called()
72107

73108
def test_02_lakeshore_backend(self):
74-
print("\n[SIMULATION] 2. Lakeshore 350 Control...")
75-
with patch('pyvisa.ResourceManager') as MockRM:
109+
with patch('pyvisa.ResourceManager') as MockRM, \
110+
patch('time.sleep', side_effect=self.get_circuit_breaker(15)):
111+
76112
spy = MockRM.return_value.open_resource.return_value
77-
spy.query.side_effect = ["LSCI,MODEL350,0,0"] + ["10.0", "10.1", "10.2", "300.0"] * 50
78-
breaker = self.get_circuit_breaker(15)
113+
spy.query.side_effect = ["LSCI,MODEL350,0,0"] + ["10.0", "300.0"] * 20
114+
79115
with patch('builtins.input', side_effect=['10', '300', '10', '350']), \
80116
patch('builtins.open', mock_open()), \
81-
patch('time.sleep', side_effect=breaker), \
82117
patch('tkinter.filedialog.asksaveasfilename', return_value="test.csv"), \
83118
patch('matplotlib.pyplot.show'):
84119
self.run_module_safely("Lakeshore_350_340.Backends.T_Control_L350_Simple_Backend_v10")
85120

86121
def test_03_k6517b_pyro_backend(self):
87-
print("\n[SIMULATION] 3. Keithley 6517B Pyroelectric...")
88-
with patch('pymeasure.instruments.keithley.Keithley6517B') as MockInst:
122+
with patch('pymeasure.instruments.keithley.Keithley6517B') as MockInst, \
123+
patch('time.sleep', side_effect=self.get_circuit_breaker(5)):
89124
spy = MockInst.return_value
90125
spy.current = 1.23e-9
91-
breaker = self.get_circuit_breaker(5)
92-
with patch('pandas.DataFrame.to_csv'), patch('time.sleep', side_effect=breaker):
126+
with patch('pandas.DataFrame.to_csv'):
93127
self.run_module_safely("Keithley_6517B.Pyroelectricity.Backends.Current_K6517B_Simple_Backend_v10")
94128

95129
def test_04_lcr_keysight_backend(self):
96-
print("\n[SIMULATION] 4. Keysight E4980A LCR...")
97130
with patch('pymeasure.instruments.agilent.AgilentE4980'), \
98-
patch('pyvisa.ResourceManager') as MockRM:
131+
patch('pyvisa.ResourceManager') as MockRM, \
132+
patch('time.sleep', side_effect=self.get_circuit_breaker(5)):
99133
visa_spy = MockRM.return_value.open_resource.return_value
100134
visa_spy.query.return_value = "0.5"
101-
breaker = self.get_circuit_breaker(5)
102-
with patch('pandas.DataFrame.to_csv'), patch('time.sleep', side_effect=breaker):
135+
with patch('pandas.DataFrame.to_csv'):
103136
self.run_module_safely("LCR_Keysight_E4980A.Backends.CV_KE4980A_Simple_Backend_v10")
104137

105-
# =========================================================================
106-
# SECTION 2: COMPLEX & COMBINED MODULES
107-
# =========================================================================
108-
109138
def test_05_delta_simple(self):
110-
print("\n[SIMULATION] 5. Delta Mode (Simple)...")
111-
with patch('pyvisa.ResourceManager') as MockRM:
139+
with patch('pyvisa.ResourceManager') as MockRM, \
140+
patch('time.sleep', side_effect=self.get_circuit_breaker(10)):
112141
k6221 = MockRM.return_value.open_resource.return_value
113-
breaker = self.get_circuit_breaker(10)
114142
inputs = ['0', '1e-5', '1e-6', 'test_file', 'y', 'y']
115143
with patch('builtins.input', side_effect=inputs), \
116-
patch('pandas.DataFrame.to_csv'), \
117-
patch('time.sleep', side_effect=breaker):
144+
patch('pandas.DataFrame.to_csv'):
118145
self.run_module_safely("Delta_mode_Keithley_6221_2182.Backends.Delta_K6221_K2182_Simple_v7")
119146

120147
def test_06_delta_sensing(self):
121-
print("\n[SIMULATION] 6. Delta Mode (T-Sensing)...")
122-
with patch('pyvisa.ResourceManager') as MockRM:
148+
with patch('pyvisa.ResourceManager') as MockRM, \
149+
patch('time.sleep', side_effect=self.get_circuit_breaker(10)):
123150
inst = MockRM.return_value.open_resource.return_value
124-
inst.query.return_value = "+1.23E-5"
125-
breaker = self.get_circuit_breaker(10)
151+
inst.query.return_value = "+1.23E-5"
126152
inputs = ['10', '300', '10', 'test_file', 'y']
127153
with patch('builtins.input', side_effect=inputs), \
128-
patch('pandas.DataFrame.to_csv'), \
129-
patch('time.sleep', side_effect=breaker):
154+
patch('pandas.DataFrame.to_csv'):
130155
try:
131156
self.run_module_safely("Delta_mode_Keithley_6221_2182.Backends.Delta_K6221_K2182_L350_T_Sensing_Backend_v1")
132157
except ModuleNotFoundError:
133-
print(" [Skip] Delta Sensing script not found")
158+
print(" [SKIP] Module not found, skipping.")
134159

135160
def test_07_lockin_backend(self):
136-
print("\n[SIMULATION] 7. Lock-in Amplifier SR830...")
137-
with patch('pyvisa.ResourceManager') as MockRM:
161+
with patch('pyvisa.ResourceManager') as MockRM, \
162+
patch('time.sleep', side_effect=self.get_circuit_breaker(5)):
138163
spy = MockRM.return_value.open_resource.return_value
139164
spy.query.return_value = "1.23,4.56"
140-
breaker = self.get_circuit_breaker(5)
141-
with patch('time.sleep', side_effect=breaker):
142-
self.run_module_safely("Lock_in_amplifier.BasicTest_S830_Backend_v1")
165+
self.run_module_safely("Lock_in_amplifier.BasicTest_S830_Backend_v1")
143166

144167
def test_08_combined_2400_2182(self):
145-
print("\n[SIMULATION] 8. Combined K2400 + K2182...")
146-
with patch('pyvisa.ResourceManager') as MockRM:
168+
# THIS WAS THE TEST CAUSING THE HANG
169+
# We suspect input mismatch or resource opening hang.
170+
with patch('pyvisa.ResourceManager') as MockRM, \
171+
patch('time.sleep', side_effect=self.get_circuit_breaker(10)):
172+
147173
rm = MockRM.return_value
148-
rm.open_resource.side_effect = [MagicMock(), MagicMock(), MagicMock()]
149-
breaker = self.get_circuit_breaker(10)
150-
inputs = ['10', '1', 'test_file', 'y']
174+
# Ensure side_effect doesn't run out if script asks for many resources
175+
rm.open_resource.return_value = MagicMock()
176+
177+
# Add extra inputs just in case the script asks for more than expected
178+
inputs = ['10', '1', 'test_file', 'y', 'y', 'y', 'y']
179+
151180
with patch('builtins.input', side_effect=inputs), \
152-
patch('pandas.DataFrame.to_csv'), \
153-
patch('time.sleep', side_effect=breaker):
181+
patch('pandas.DataFrame.to_csv'):
154182
self.run_module_safely("Keithley_2400_Keithley_2182.Backends.IV_K2400_K2182_Backend_v1")
155183

156184
def test_09_poling(self):
157-
print("\n[SIMULATION] 9. Poling K6517B...")
158-
with patch('pyvisa.ResourceManager') as MockRM:
185+
with patch('pyvisa.ResourceManager') as MockRM, \
186+
patch('time.sleep', side_effect=self.get_circuit_breaker(5)):
159187
spy = MockRM.return_value.open_resource.return_value
160-
breaker = self.get_circuit_breaker(5)
161188
inputs = ['100', '10', 'y']
162-
with patch('builtins.input', side_effect=inputs), \
163-
patch('time.sleep', side_effect=breaker):
189+
with patch('builtins.input', side_effect=inputs):
164190
self.run_module_safely("Keithley_6517B.Pyroelectricity.Backends.Poling_K6517B_Backend_v10")
165191

166192
def test_10_high_resistance(self):
167-
print("\n[SIMULATION] 10. High Resistance K6517B...")
168-
with patch('pyvisa.ResourceManager') as MockRM:
193+
with patch('pyvisa.ResourceManager') as MockRM, \
194+
patch('time.sleep', side_effect=self.get_circuit_breaker(5)):
169195
spy = MockRM.return_value.open_resource.return_value
170196
spy.query.return_value = "+1.0E+12,0,0"
171-
breaker = self.get_circuit_breaker(5)
172197
inputs = ['10', '1', 'test_file', 'y']
173198
with patch('builtins.input', side_effect=inputs), \
174-
patch('pandas.DataFrame.to_csv'), \
175-
patch('time.sleep', side_effect=breaker):
199+
patch('pandas.DataFrame.to_csv'):
176200
try:
177201
self.run_module_safely("Keithley_6517B.High_Resistance.Backends.IV_K6517B_Simple_Backend_v10")
178202
except Exception:
179203
pass
180204

181-
# =========================================================================
182-
# SECTION 3: UTILITY MODULES
183-
# =========================================================================
184-
185205
def test_11_gpib_scanner(self):
186-
print("\n[SIMULATION] 11. GPIB Scanner...")
187206
with patch('pyvisa.ResourceManager') as MockRM:
188207
rm = MockRM.return_value
189208
rm.list_resources.return_value = ('GPIB0::24::INSTR',)
190209
try:
191210
import Utilities.GPIB_Instrument_Scanner_GUI_v4 as scanner
192211
if hasattr(scanner, 'GPIBScannerWindow'):
193-
print(" -> Verified: Import successful")
212+
print(" -> Verified: Import successful", flush=True)
194213
except ImportError:
195214
pass
196215

197216
def test_12_gpib_rescue(self):
198-
print("\n[SIMULATION] 12. GPIB Rescue...")
199-
with patch('pyvisa.ResourceManager') as MockRM:
217+
with patch('pyvisa.ResourceManager') as MockRM, \
218+
patch('time.sleep', side_effect=self.get_circuit_breaker(3)):
200219
rm = MockRM.return_value
201220
rm.list_resources.return_value = ('GPIB0::1::INSTR',)
202-
breaker = self.get_circuit_breaker(3)
203-
with patch('time.sleep', side_effect=breaker):
204-
self.run_module_safely("Utilities.GPIB_Interface_Rescue_Simple_Backened_v2_")
221+
self.run_module_safely("Utilities.GPIB_Interface_Rescue_Simple_Backened_v2_")
205222

206223
if __name__ == '__main__':
207224
unittest.main()

0 commit comments

Comments
 (0)