Skip to content

Commit 318d555

Browse files
scope increased for the test
1 parent f4043d9 commit 318d555

1 file changed

Lines changed: 195 additions & 35 deletions

File tree

tests/test_deep_simulation.py

Lines changed: 195 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
import sys
33
import os
44
import importlib
5-
from unittest.mock import MagicMock, patch, mock_open, call
5+
from unittest.mock import MagicMock, patch, mock_open, call, ANY
66

77
# -------------------------------------------------------------------------
8-
# 1. GLOBAL MOCKS
8+
# 1. GLOBAL HEADLESS MOCKS
9+
# We mock the entire physical world so tests run on GitHub servers.
910
# -------------------------------------------------------------------------
1011
sys.modules['tkinter'] = MagicMock()
1112
sys.modules['tkinter.ttk'] = MagicMock()
@@ -22,80 +23,239 @@
2223
class TestDeepSimulation(unittest.TestCase):
2324

2425
def setUp(self):
26+
# Add project root to path so we can import your scripts
2527
self.root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
2628
if self.root_dir not in sys.path:
2729
sys.path.insert(0, self.root_dir)
2830

29-
# --- FIX FOR UNPACKING ERROR ---
31+
# --- CRITICAL FIX FOR "not enough values to unpack" ---
32+
# Many scripts call: fig, ax = plt.subplots()
33+
# We tell the mock to return exactly two items.
3034
mock_fig = MagicMock()
3135
mock_ax = MagicMock()
32-
# Force plt.subplots() to return a tuple of (figure, axis)
3336
sys.modules['matplotlib.pyplot'].subplots.return_value = (mock_fig, mock_ax)
3437

3538
def run_module_safely(self, module_name):
39+
"""Helper: Import module, run main() if exists, handle 'Force Exit'."""
3640
if module_name in sys.modules:
3741
del sys.modules[module_name]
42+
3843
try:
3944
mod = importlib.import_module(module_name)
4045
if hasattr(mod, 'main'):
4146
mod.main()
4247
except Exception as e:
43-
if "Force Test Exit" in str(e):
44-
pass
48+
if "Force Test Exit" in str(e) or isinstance(e, KeyboardInterrupt):
49+
pass # Expected circuit breaker
4550
else:
4651
print(f" [Info] Script '{module_name}' stopped with: {e}")
4752

48-
def test_keithley2400_iv_protocol(self):
49-
print("\n[SIMULATION] Keithley 2400 I-V Protocol...")
50-
with patch('pymeasure.instruments.keithley.Keithley2400') as MockK2400:
51-
spy_inst = MockK2400.return_value
52-
spy_inst.voltage = 1.23
53-
with patch('builtins.input', side_effect=['100', '10', 'test_output']), \
53+
# =========================================================================
54+
# 1. KEITHLEY 2400 (I-V Sweep)
55+
# =========================================================================
56+
def test_k2400_iv_backend(self):
57+
print("\n[SIMULATION] Keithley 2400 I-V Sweep...")
58+
with patch('pymeasure.instruments.keithley.Keithley2400') as MockInst:
59+
spy = MockInst.return_value
60+
spy.voltage = 1.23
61+
62+
with patch('builtins.input', side_effect=['100', '10', 'test']), \
5463
patch('pandas.DataFrame.to_csv'):
64+
5565
self.run_module_safely("Keithley_2400.Backends.IV_K2400_Loop_Backend_v10")
56-
spy_inst.enable_source.assert_called()
57-
print(" -> Verified: Source Output Enabled")
66+
67+
spy.enable_source.assert_called()
68+
spy.shutdown.assert_called()
69+
print(" -> Verified: Output Enabled -> Measured -> Shutdown")
5870

59-
def test_lakeshore_visa_communication(self):
60-
print("\n[SIMULATION] Lakeshore 350 SCPI Commands...")
71+
# =========================================================================
72+
# 2. LAKESHORE 350 (Temp Control)
73+
# =========================================================================
74+
def test_lakeshore_backend(self):
75+
print("\n[SIMULATION] Lakeshore 350 Control...")
6176
with patch('pyvisa.ResourceManager') as MockRM:
62-
spy_instr = MockRM.return_value.open_resource.return_value
63-
spy_instr.query.side_effect = ["LSCI,MODEL350,0,0"] + ["10.0"] * 20 # Temp readings
77+
spy = MockRM.return_value.open_resource.return_value
78+
spy.query.side_effect = ["LSCI,MODEL350,0,0"] + ["10.0", "10.1", "10.2", "300.0"] * 10
6479

65-
# Inputs: Start=10, End=300, Rate=10, Cutoff=350
80+
# Valid Inputs: Start=10, End=300, Rate=10, Cutoff=350
6681
fake_inputs = ['10', '300', '10', '350']
67-
sys.modules['tkinter'].filedialog.asksaveasfilename.return_value = "dummy.csv"
6882

69-
# Circuit Breaker
70-
mock_sleep = MagicMock(side_effect=[None]*5 + [Exception("Force Test Exit")])
83+
# Sleep Circuit Breaker
84+
breaker = MagicMock(side_effect=[None]*5 + [Exception("Force Test Exit")])
7185

7286
with patch('builtins.input', side_effect=fake_inputs), \
7387
patch('builtins.open', mock_open()), \
74-
patch('time.sleep', mock_sleep):
88+
patch('time.sleep', breaker), \
89+
patch('tkinter.filedialog.asksaveasfilename', return_value="test.csv"):
7590

7691
self.run_module_safely("Lakeshore_350_340.Backends.T_Control_L350_Simple_Backend_v10")
92+
93+
spy.query.assert_any_call('*IDN?')
94+
writes = [str(c) for c in spy.write.mock_calls]
95+
self.assertTrue(any("HTRSET" in c for c in writes), "Heater setup not sent")
96+
print(" -> Verified: Heater Configured (HTRSET)")
7797

78-
try:
79-
spy_instr.query.assert_any_call('*IDN?')
80-
print(" -> Verified: *IDN? Query Sent")
81-
except AssertionError:
82-
print(" [FAIL] IDN Query missed.")
98+
# =========================================================================
99+
# 3. KEITHLEY 6517B (Pyroelectric Current)
100+
# =========================================================================
101+
def test_k6517b_pyro_backend(self):
102+
print("\n[SIMULATION] Keithley 6517B Pyroelectric Current...")
103+
with patch('pymeasure.instruments.keithley.Keithley6517B') as MockInst:
104+
spy = MockInst.return_value
105+
spy.current = 1.23e-9
106+
107+
# Circuit breaker for 'while True'
108+
breaker = MagicMock(side_effect=[None]*3 + [KeyboardInterrupt])
83109

84-
write_calls = [str(c) for c in spy_instr.write.mock_calls]
85-
if any("HTRSET" in c for c in write_calls):
86-
print(" -> Verified: Heater Configured (HTRSET)")
110+
with patch('pandas.DataFrame.to_csv') as mock_save, \
111+
patch('time.sleep', breaker):
112+
113+
self.run_module_safely("Keithley_6517B.Pyroelectricity.Backends.Current_K6517B_Simple_Backend_v10")
114+
115+
spy.measure_current.assert_called()
116+
spy.shutdown.assert_called()
117+
print(" -> Verified: Measure Current -> Ctrl+C Caught -> Shutdown")
118+
119+
# =========================================================================
120+
# 4. KEYSIGHT E4980A (LCR Meter)
121+
# =========================================================================
122+
def test_lcr_keysight_backend(self):
123+
print("\n[SIMULATION] Keysight E4980A LCR Meter...")
124+
with patch('pymeasure.instruments.agilent.AgilentE4980') as MockLCR, \
125+
patch('pyvisa.ResourceManager') as MockRM:
126+
127+
lcr_spy = MockLCR.return_value
128+
visa_spy = MockRM.return_value.open_resource.return_value
129+
130+
# Mock Values: [Capacitance, Resistance]
131+
lcr_spy.values.return_value = [1.5e-9, 1000]
132+
visa_spy.query.return_value = "0.5" # Voltage Level
133+
134+
with patch('pandas.DataFrame.to_csv'), \
135+
patch('time.sleep'): # Mute sleep for speed
136+
137+
self.run_module_safely("LCR_Keysight_E4980A.Backends.CV_KE4980A_Simple_Backend_v10")
138+
139+
visa_spy.write.assert_any_call('*RST; *CLS')
140+
lcr_spy.shutdown.assert_called()
141+
print(" -> Verified: Reset -> Protocol Loop -> Shutdown")
142+
143+
# =========================================================================
144+
# 5. DELTA MODE (K6221 + K2182)
145+
# =========================================================================
146+
def test_delta_mode_backend(self):
147+
print("\n[SIMULATION] Delta Mode (K6221 + K2182)...")
148+
with patch('pyvisa.ResourceManager') as MockRM:
149+
k6221 = MagicMock()
150+
MockRM.return_value.open_resource.return_value = k6221
151+
152+
# Inputs: Start=0, Stop=10e-6, Step=1e-6, File=test
153+
fake_inputs = ['0', '0.00001', '0.000001', 'delta_test']
154+
155+
with patch('builtins.input', side_effect=fake_inputs), \
156+
patch('pandas.DataFrame.to_csv'), \
157+
patch('time.sleep'):
158+
159+
self.run_module_safely("Delta_mode_Keithley_6221_2182.Backends.Delta_K6221_K2182_Simple_v7")
160+
161+
self.assertTrue(k6221.write.called)
162+
print(" -> Verified: Commands sent to K6221/K2182")
163+
164+
# =========================================================================
165+
# 6. LOCK-IN AMPLIFIER (SR830)
166+
# =========================================================================
167+
def test_lockin_backend(self):
168+
print("\n[SIMULATION] SRS SR830 Lock-in...")
169+
with patch('pyvisa.ResourceManager') as MockRM:
170+
spy = MockRM.return_value.open_resource.return_value
171+
spy.query.return_value = "1.23,4.56" # Mock X,Y response
172+
173+
with patch('time.sleep'):
174+
self.run_module_safely("Lock_in_amplifier.BasicTest_S830_Backend_v1")
175+
176+
spy.query.assert_any_call('*IDN?')
177+
print(" -> Verified: Lock-in IDN Queried")
178+
179+
# =========================================================================
180+
# 7. COMBINED K2400 + K2182
181+
# =========================================================================
182+
def test_k2400_k2182_backend(self):
183+
print("\n[SIMULATION] Combined K2400 + K2182...")
184+
with patch('pyvisa.ResourceManager') as MockRM:
185+
rm = MockRM.return_value
186+
# We mocking opening 2 different resources
187+
k2400 = MagicMock()
188+
k2182 = MagicMock()
189+
rm.open_resource.side_effect = [k2400, k2182] # First call 2400, second 2182
190+
191+
fake_inputs = ['10', '1', 'test'] # Current, Step, File
192+
193+
with patch('builtins.input', side_effect=fake_inputs), \
194+
patch('pandas.DataFrame.to_csv'), \
195+
patch('time.sleep'):
196+
197+
self.run_module_safely("Keithley_2400_Keithley_2182.Backends.IV_K2400_K2182_Backend_v1")
198+
199+
print(" -> Verified: Multi-instrument script executed.")
87200

88-
def test_gpib_scanner_loop(self):
89-
print("\n[SIMULATION] Testing GPIB Scanner Loop...")
201+
# =========================================================================
202+
# 8. KEITHLEY 6517B (High Resistance I-V)
203+
# =========================================================================
204+
def test_k6517b_high_res(self):
205+
print("\n[SIMULATION] Keithley 6517B High Resistance...")
206+
with patch('pyvisa.ResourceManager') as MockRM:
207+
spy = MockRM.return_value.open_resource.return_value
208+
spy.query.return_value = "+1.23E-12" # Current reading
209+
210+
fake_inputs = ['10', '1', 'test'] # Voltage, Step, File
211+
212+
with patch('builtins.input', side_effect=fake_inputs), \
213+
patch('pandas.DataFrame.to_csv'), \
214+
patch('time.sleep'):
215+
216+
self.run_module_safely("Keithley_6517B.High_Resistance.Backends.IV_K6517B_Simple_Backend_v10")
217+
218+
# Check if voltage was applied
219+
writes = [str(c) for c in spy.write.mock_calls]
220+
self.assertTrue(any("SOUR:VOLT" in c for c in writes), "Voltage Source not set")
221+
print(" -> Verified: Voltage Source Commands Sent")
222+
223+
# =========================================================================
224+
# 9. POLING K6517B
225+
# =========================================================================
226+
def test_k6517b_poling(self):
227+
print("\n[SIMULATION] Keithley 6517B Poling...")
228+
with patch('pyvisa.ResourceManager') as MockRM:
229+
spy = MockRM.return_value.open_resource.return_value
230+
231+
# Inputs: Voltage=100, Time=10
232+
fake_inputs = ['100', '10']
233+
234+
with patch('builtins.input', side_effect=fake_inputs), \
235+
patch('time.sleep'):
236+
237+
self.run_module_safely("Keithley_6517B.Pyroelectricity.Backends.Poling_K6517B_Backend_v10")
238+
239+
# Check for output enable
240+
writes = [str(c) for c in spy.write.mock_calls]
241+
if any("OPER" in c or "ON" in c for c in writes):
242+
print(" -> Verified: Poling Voltage Enabled")
243+
244+
# =========================================================================
245+
# 10. GPIB SCANNER (Utility)
246+
# =========================================================================
247+
def test_gpib_scanner(self):
248+
print("\n[SIMULATION] GPIB Scanner Utility...")
90249
with patch('pyvisa.ResourceManager') as MockRM:
91250
rm = MockRM.return_value
92-
rm.list_resources.return_value = ('GPIB0::24::INSTR', 'GPIB0::12::INSTR')
251+
rm.list_resources.return_value = ('GPIB0::24::INSTR',)
252+
93253
try:
94254
import Utilities.GPIB_Instrument_Scanner_Frontend_v4 as scanner
95255
if hasattr(scanner, 'GPIBScannerWindow'):
96256
scanner.GPIBScannerWindow(MagicMock(), MagicMock())
97257
rm.list_resources.assert_called()
98-
print(" -> Verified: Scanner requested resource list")
258+
print(" -> Verified: Scanner listed resources")
99259
except ImportError:
100260
pass
101261

0 commit comments

Comments
 (0)