Skip to content

Commit e06b0c0

Browse files
updated tests and joss_tests.yml
1 parent 4c446a6 commit e06b0c0

7 files changed

Lines changed: 1116 additions & 1097 deletions

.github/workflows/joss_tests.yml

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,30 @@ jobs:
1515
# PHASE 1: SETUP
1616
# ------------------------------------------------------------------------
1717
- name: Checkout repository
18-
uses: actions/checkout@v3
18+
uses: actions/checkout@v4
1919

2020
- name: Set up Python 3.10
21-
uses: actions/setup-python@v4
21+
uses: actions/setup-python@v5
2222
with:
2323
python-version: "3.10"
2424

25+
- name: Cache pip dependencies
26+
id: cache-pip
27+
uses: actions/cache@v4
28+
with:
29+
path: ~/.cache/pip
30+
# The key invalidates the cache if requirements change
31+
key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }}
32+
restore-keys: |
33+
${{ runner.os }}-pip-
34+
2535
- name: Install Dependencies
36+
# Use a dedicated step for installing dependencies.
37+
# The 'if' condition prevents re-installing if the cache was restored.
38+
if: steps.cache-pip.outputs.cache-hit != 'true'
2639
run: |
2740
python -m pip install --upgrade pip
28-
# Install PICA in editable mode
2941
pip install -e .
30-
# Install Testing, Linting, and Coverage tools
3142
pip install pytest flake8 pytest-cov
3243
3344
# ------------------------------------------------------------------------
@@ -56,14 +67,14 @@ jobs:
5667
# PHASE 4: CODECOV UPLOADS
5768
# ------------------------------------------------------------------------
5869
- name: Upload Coverage Report (XML)
59-
uses: codecov/codecov-action@v3
70+
uses: codecov/codecov-action@v4
6071
with:
6172
token: ${{ secrets.CODECOV_TOKEN }}
6273
files: ./coverage.xml
63-
fail_ci_if_error: false
74+
fail_ci_if_error: true # It's good practice to fail if coverage upload fails.
6475

6576
- name: Upload Test Results to Codecov (JUnit)
66-
if: ${{ !cancelled() }}
77+
if: always() # This ensures test results are uploaded even if previous steps fail.
6778
uses: codecov/test-results-action@v1
6879
with:
6980
token: ${{ secrets.CODECOV_TOKEN }}

tests/test_deep_simulation.py

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,11 @@ def side_effect(*args, **kwargs):
110110

111111
def test_01_k2400_iv_backend(self):
112112
# GLOBAL PATCH for sleep is critical here
113+
mock_sleep = patch('time.sleep', side_effect=self.get_circuit_breaker(5))
114+
mock_sleep.start()
115+
self.addCleanup(mock_sleep.stop)
116+
113117
with patch('pymeasure.instruments.keithley.Keithley2400') as MockInst:
114-
mock_sleep = patch('time.sleep', side_effect=self.get_circuit_breaker(5))
115-
mock_sleep.start()
116-
self.addCleanup(mock_sleep.stop)
117118

118119
spy = MockInst.return_value
119120
with patch('builtins.input', side_effect=['100', '10', 'test_file']), \
@@ -123,12 +124,12 @@ def test_01_k2400_iv_backend(self):
123124
spy.enable_source.assert_called()
124125

125126
def test_02_lakeshore_backend(self):
127+
mock_sleep = patch('time.sleep', side_effect=self.get_circuit_breaker(15))
128+
mock_sleep.start()
129+
self.addCleanup(mock_sleep.stop)
126130
with patch('pyvisa.ResourceManager') as MockRM, \
127131
patch('tkinter.Tk'), \
128132
patch('tkinter.filedialog.asksaveasfilename', return_value="test.csv"):
129-
mock_sleep = patch('time.sleep', side_effect=self.get_circuit_breaker(15))
130-
mock_sleep.start()
131-
self.addCleanup(mock_sleep.stop)
132133

133134
spy = MockRM.return_value.open_resource.return_value
134135
spy.query.side_effect = [
@@ -146,10 +147,10 @@ def test_02_lakeshore_backend(self):
146147
"Lakeshore_350_340.Backends.T_Control_L350_Simple_Backend_v10")
147148

148149
def test_03_k6517b_pyro_backend(self):
150+
mock_sleep = patch('time.sleep', side_effect=self.get_circuit_breaker(10))
151+
mock_sleep.start()
152+
self.addCleanup(mock_sleep.stop)
149153
with patch('pymeasure.instruments.keithley.Keithley6517B') as MockInst:
150-
mock_sleep = patch('time.sleep', side_effect=self.get_circuit_breaker(10))
151-
mock_sleep.start()
152-
self.addCleanup(mock_sleep.stop)
153154

154155
spy = MockInst.return_value
155156
spy.current = 1.23e-9
@@ -171,10 +172,10 @@ def test_04_lcr_keysight_backend(self):
171172
"LCR_Keysight_E4980A.Backends.CV_KE4980A_Simple_Backend_v10")
172173

173174
def test_05_delta_simple(self):
175+
mock_sleep = patch('time.sleep', side_effect=self.get_circuit_breaker(10))
176+
mock_sleep.start()
177+
self.addCleanup(mock_sleep.stop)
174178
with patch('pyvisa.ResourceManager') as MockRM:
175-
mock_sleep = patch('time.sleep', side_effect=self.get_circuit_breaker(10))
176-
mock_sleep.start()
177-
self.addCleanup(mock_sleep.stop)
178179

179180
MockRM.return_value.open_resource.return_value
180181
inputs = ['0', '1e-5', '1e-6', 'test_file', 'y', 'y']
@@ -201,11 +202,11 @@ def test_06_delta_sensing(self):
201202
print(" [SKIP] Module not found, skipping.")
202203

203204
def test_07_lockin_backend(self):
205+
mock_sleep = patch('time.sleep', side_effect=self.get_circuit_breaker(5))
206+
mock_sleep.start()
207+
self.addCleanup(mock_sleep.stop)
204208
with patch('pyvisa.ResourceManager') as MockRM:
205-
mock_sleep = patch('time.sleep', side_effect=self.get_circuit_breaker(5))
206209
spy = MockRM.return_value.open_resource.return_value
207-
mock_sleep.start()
208-
self.addCleanup(mock_sleep.stop)
209210

210211
spy.query.side_effect = [
211212
"SRS,SR830,s/n12345,ver1.07", # *IDN?
@@ -218,11 +219,12 @@ def test_07_lockin_backend(self):
218219
def test_08_combined_2400_2182(self):
219220
# THIS WAS THE TEST CAUSING THE HANG
220221
# We suspect input mismatch or resource opening hang.
222+
mock_sleep = patch('time.sleep', side_effect=self.get_circuit_breaker(10))
223+
mock_sleep.start()
224+
self.addCleanup(mock_sleep.stop)
221225
with patch('pyvisa.ResourceManager') as MockRM:
222-
mock_sleep = patch('time.sleep', side_effect=self.get_circuit_breaker(10))
223226
mock_pymeasure = patch('pymeasure.instruments.keithley.Keithley2400')
224227
mock_pymeasure.start()
225-
self.addCleanup(mock_sleep.stop)
226228

227229
rm = MockRM.return_value
228230
k2182_spy = MagicMock()
@@ -232,18 +234,17 @@ def test_08_combined_2400_2182(self):
232234
# Add extra inputs just in case the script asks for more than
233235
# expected
234236
inputs = ['10', '1', 'test_file', 'y', 'y', 'y', 'y']
235-
mock_sleep.start()
236237
with patch('builtins.input', side_effect=inputs), \
237238
patch('pandas.DataFrame.to_csv'):
238239
self.run_module_safely(
239240
"Keithley_2400_Keithley_2182.Backends.IV_K2400_K2182_Backend_v1")
240241
mock_pymeasure.stop()
241242

242243
def test_09_poling(self):
244+
mock_sleep = patch('time.sleep', side_effect=self.get_circuit_breaker(5))
245+
mock_sleep.start()
246+
self.addCleanup(mock_sleep.stop)
243247
with patch('pymeasure.instruments.keithley.Keithley6517B'):
244-
mock_sleep = patch('time.sleep', side_effect=self.get_circuit_breaker(5))
245-
mock_sleep.start()
246-
self.addCleanup(mock_sleep.stop)
247248

248249
inputs = ['100', '10', 'y']
249250
with patch('builtins.input', side_effect=inputs):

tests/test_gui_iv_k2400.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ def tearDown(self):
2323
self.root.destroy()
2424

2525
@patch('Keithley_2400.IV_K2400_GUI_v5.Keithley2400_IV_Backend')
26-
@patch('matplotlib.pyplot.subplots')
27-
def test_start_measurement_logic(self, mock_subplots, MockBackend):
26+
@patch('matplotlib.figure.Figure.subplots')
27+
def test_start_measurement_logic(self, mock_fig_subplots, MockBackend):
2828
"""
2929
Tests the core logic of the 'Start' button click.
3030
Verifies that parameters are read from the UI and passed to the backend correctly.
@@ -33,7 +33,7 @@ def test_start_measurement_logic(self, mock_subplots, MockBackend):
3333
# Configure the mock for subplots to return two mock axes
3434
mock_ax_vi = MagicMock()
3535
mock_ax_ri = MagicMock()
36-
mock_subplots.return_value = (MagicMock(), (mock_ax_vi, mock_ax_ri))
36+
mock_fig_subplots.return_value = (mock_ax_vi, mock_ax_ri)
3737

3838
# Instantiate the GUI. This also creates all the tk widgets.
3939
app = MeasurementAppGUI(self.root)

tests/test_iv_k2400_loop_backend.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
class TestIVK2400LoopBackend(unittest.TestCase):
1010

1111
@patch('builtins.input', side_effect=['10', '2', 'test_output'])
12-
@patch('Keithley_2400.Backends.IV_K2400_Loop_Backend_v10.Keithley2400')
12+
@patch('pymeasure.instruments.keithley.Keithley2400')
1313
@patch('matplotlib.pyplot.show')
1414
@patch('pandas.DataFrame.to_csv')
1515
def test_main_full_run(self, mock_to_csv, mock_plt_show, mock_keithley_class, mock_input):
@@ -47,7 +47,7 @@ def ramp_side_effect(current):
4747

4848
# --- ASSERTIONS ---
4949
# 1. Check instrument initialization
50-
mock_keithley_class.assert_called_with("GPIB::4")
50+
mock_keithley_class.assert_called_with("GPIB::4", adapter_args={"py_library": "@sim"})
5151
mock_keithley_instance.disable_buffer.assert_called_once()
5252

5353
# 2. Check instrument configuration

tests/test_iv_k6517b_simple_backend.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
class TestIVK6517BSimpleBackend(unittest.TestCase):
99
@patch('time.sleep', MagicMock())
1010
@patch('builtins.input', side_effect=['-10', '10', '5', '0.1', 'test_iv_simple.csv'])
11-
@patch('Keithley_6517B.High_Resistance.Backends.IV_K6517B_Simple_Backend_v10.Keithley6517B')
11+
@patch('pymeasure.instruments.keithley.Keithley6517B')
1212
@patch('builtins.open', new_callable=mock_open)
1313
@patch('matplotlib.pyplot.show')
1414
def test_full_run(self, mock_show, mock_file, mock_keithley_class, mock_input):
@@ -33,7 +33,7 @@ def test_full_run(self, mock_show, mock_file, mock_keithley_class, mock_input):
3333

3434
# --- Assertions ---
3535
# 1. Was the instrument initialized correctly?
36-
mock_keithley_class.assert_called_once_with("GPIB1::27::INSTR")
36+
mock_keithley_class.assert_called_once_with("GPIB1::27::INSTR", adapter_args={"py_library": "@sim"})
3737

3838
# 2. Was the zero-check and correction sequence performed?
3939
mock_instrument.reset.assert_called_once()

tests/test_t_control_l350_backend.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def test_main_runs_and_completes(self, mock_time, mock_open_file,
116116

117117
# --- ASSERTIONS ---
118118
# Check initialization
119-
mock_ls_class.assert_called_once()
119+
mock_ls_class.assert_called_once_with("GPIB0::12::INSTR", adapter_args={"py_library": "@sim"})
120120
mock_controller.reset_and_clear.assert_called_once()
121121
mock_controller.setup_heater.assert_called_once()
122122

0 commit comments

Comments
 (0)