Skip to content

Commit 0697ce7

Browse files
tests added
1 parent 1f93c18 commit 0697ce7

3 files changed

Lines changed: 178 additions & 45 deletions

File tree

Utilities/LivePlotter_v10.py

Lines changed: 45 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -31,55 +31,55 @@ def select_file():
3131
return tempdir
3232

3333

34+
def live_plot_from_csv(selected_file):
35+
# Set up the plot
36+
plt.style.use('fivethirtyeight')
37+
fig, axs = plt.subplots(3, 1, figsize=(9, 12))
38+
39+
def animate(i):
40+
# Reload data from CSV (in case it has changed)
41+
data = pd.read_csv(selected_file)
42+
x = data['Time (s)']
43+
y1 = data['Temperature (K)']
44+
y2 = data['Voltage (V)']
45+
46+
# Clear previous plots
47+
for ax in axs:
48+
ax.clear()
49+
50+
# Update subplots
51+
axs[0].plot(x, y1, label='T', color='b', linewidth=0.8)
52+
axs[0].scatter(x, y1, color='b')
53+
axs[0].set_title('V vs time', fontsize=13)
54+
axs[0].set_xlabel('Time (s)', fontsize=13)
55+
axs[0].set_ylabel('Temperature (K)', fontsize=13)
56+
axs[0].legend(loc='upper left')
57+
58+
axs[1].plot(x, y2, label='V', color='g', linewidth=0.8)
59+
axs[1].scatter(x, y2, color='g')
60+
axs[1].set_title('V vs time', fontsize=13)
61+
axs[1].set_xlabel('Time (s)', fontsize=13)
62+
axs[1].set_ylabel('Voltage (V)', fontsize=13)
63+
axs[1].legend(loc='upper left')
64+
65+
axs[2].plot(y1, y2, label='V vs T', color='r', linewidth=0.8)
66+
axs[2].scatter(y1, y2, color='r')
67+
axs[2].set_title('V vs T', fontsize=13)
68+
axs[2].set_xlabel('Temperature (K)', fontsize=13)
69+
axs[2].set_ylabel('Voltage (V)', fontsize=13)
70+
axs[2].legend(loc='upper left')
71+
72+
ani = FuncAnimation(plt.gcf(), animate, interval=1000, cache_frame_data=False)
73+
74+
plt.tight_layout()
75+
plt.show()
76+
77+
3478
# Call the function to select the file
3579
selected_file = str(select_file())
3680

3781
# Print the selected file path
3882
print(f"Selected file: {selected_file}")
3983

4084
# Load data from CSV file
41-
42-
43-
# Set up the plot
44-
plt.style.use('fivethirtyeight')
45-
fig, axs = plt.subplots(3, 1, figsize=(9, 12))
46-
47-
48-
def animate(i):
49-
# Reload data from CSV (in case it has changed)
50-
data = pd.read_csv(selected_file)
51-
x = data['Time (s)']
52-
y1 = data['Temperature (K)']
53-
y2 = data['Voltage (V)']
54-
55-
# Clear previous plots
56-
for ax in axs:
57-
ax.clear()
58-
59-
# Update subplots
60-
axs[0].plot(x, y1, label='T', color='b', linewidth=0.8)
61-
axs[0].scatter(x, y1, color='b')
62-
axs[0].set_title('V vs time', fontsize=13)
63-
axs[0].set_xlabel('Time (s)', fontsize=13)
64-
axs[0].set_ylabel('Temperature (K)', fontsize=13)
65-
axs[0].legend(loc='upper left')
66-
67-
axs[1].plot(x, y2, label='V', color='g', linewidth=0.8)
68-
axs[1].scatter(x, y2, color='g')
69-
axs[1].set_title('V vs time', fontsize=13)
70-
axs[1].set_xlabel('Time (s)', fontsize=13)
71-
axs[1].set_ylabel('Voltage (V)', fontsize=13)
72-
axs[1].legend(loc='upper left')
73-
74-
axs[2].plot(y1, y2, label='V vs T', color='r', linewidth=0.8)
75-
axs[2].scatter(y1, y2, color='r')
76-
axs[2].set_title('V vs T', fontsize=13)
77-
axs[2].set_xlabel('Temperature (K)', fontsize=13)
78-
axs[2].set_ylabel('Voltage (V)', fontsize=13)
79-
axs[2].legend(loc='upper left')
80-
81-
82-
ani = FuncAnimation(plt.gcf(), animate, interval=1000, cache_frame_data=False)
83-
84-
plt.tight_layout()
85-
plt.show()
85+
live_plot_from_csv(selected_file)

tests/test_live_plotter.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import os
2+
import pandas as pd
3+
import matplotlib.pyplot as plt
4+
from unittest import mock
5+
from Utilities.LivePlotter_v10 import live_plot_from_csv
6+
import pytest
7+
8+
@pytest.fixture
9+
def dummy_csv_file(tmp_path):
10+
# Create a dummy CSV file for testing
11+
data = {
12+
'Time (s)': [0, 1, 2, 3, 4],
13+
'Temperature (K)': [100, 110, 105, 115, 120],
14+
'Voltage (V)': [1, 1.1, 1.05, 1.15, 1.2]
15+
}
16+
df = pd.DataFrame(data)
17+
csv_path = tmp_path / "test_data.csv"
18+
df.to_csv(csv_path, index=False)
19+
return csv_path
20+
21+
def test_live_plot_from_csv(dummy_csv_file):
22+
# Mock plt.show() to prevent it from blocking the test
23+
with mock.patch('matplotlib.pyplot.show') as mock_show:
24+
# Mock FuncAnimation to prevent actual animation from running
25+
with mock.patch('matplotlib.animation.FuncAnimation'):
26+
# Mock the plot and scatter methods of the axes
27+
with mock.patch('matplotlib.axes.Axes.plot') as mock_plot, \
28+
mock.patch('matplotlib.axes.Axes.scatter') as mock_scatter:
29+
live_plot_from_csv(dummy_csv_file)
30+
31+
# Assert that plt.show() was called
32+
mock_show.assert_called_once()
33+
34+
# Assert that plot and scatter were called multiple times
35+
assert mock_plot.call_count >= 3 # For the three subplots
36+
assert mock_scatter.call_count >= 3 # For the three subplots

tests/test_pica_launcher.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import unittest
2+
from unittest.mock import patch, MagicMock
3+
import sys
4+
import os
5+
6+
# -----------------------------------------------------------------------------
7+
# MOCK HELPER CLASSES for GUI Testing
8+
# These classes simulate the necessary parts of the GUI environment (like Tkinter)
9+
# -----------------------------------------------------------------------------
10+
class MockTkWidget:
11+
"""Mock object for Tkinter/PyQt widgets to simulate button interactions."""
12+
def __init__(self, command=None):
13+
self.command = command
14+
15+
def get(self):
16+
"""Not typically used for launcher buttons, but included for completeness."""
17+
return ''
18+
19+
def invoke(self):
20+
"""Simulates a button click (calls the attached command)."""
21+
if self.command:
22+
self.command()
23+
24+
class MockPicaLauncher:
25+
"""
26+
Simulates the PICA_v6.py launcher class structure.
27+
NOTE: Adapt the button names and method names below to match your real PICA_v6.py file.
28+
"""
29+
def __init__(self):
30+
# Attach MockTkWidget to simulate the 'command' binding for the button
31+
self.button_iv_sweep = MockTkWidget(command=self.open_iv_sweep)
32+
self.button_temp_control = MockTkWidget(command=self.open_temp_control)
33+
34+
# Internal state tracker to confirm methods are called
35+
self.open_gui_calls = {'iv_sweep': 0, 'temp_control': 0}
36+
37+
def open_iv_sweep(self):
38+
"""Simulates the handler for the IV sweep button."""
39+
self.open_gui_calls['iv_sweep'] += 1
40+
41+
def open_temp_control(self):
42+
"""Simulates the handler for the Temperature Control button."""
43+
self.open_gui_calls['temp_control'] += 1
44+
45+
46+
# -----------------------------------------------------------------------------
47+
# THE ACTUAL TESTS for the Launcher
48+
# -----------------------------------------------------------------------------
49+
50+
class TestPicaLauncher(unittest.TestCase):
51+
"""
52+
Tests the main PICA Launcher (PICA_v6.py) to ensure correct routing logic.
53+
"""
54+
55+
# We patch the actual method that is called by the button to ensure it fires.
56+
@patch('__main__.MockPicaLauncher.open_iv_sweep')
57+
def test_iv_sweep_button_click_calls_function(self, mock_open_iv_sweep):
58+
"""Tests that clicking the IV Sweep button calls its launch function once."""
59+
launcher = MockPicaLauncher()
60+
61+
# Simulate button click
62+
launcher.button_iv_sweep.invoke()
63+
64+
# Assert the mocked function was called
65+
mock_open_iv_sweep.assert_called_once()
66+
67+
# Assert the internal counter was incremented
68+
self.assertEqual(launcher.open_gui_calls['iv_sweep'], 1)
69+
70+
@patch('__main__.MockPicaLauncher.open_temp_control')
71+
def test_temp_control_button_click_calls_function(self, mock_open_temp_control):
72+
"""Tests that clicking the Temp Control button calls its launch function once."""
73+
launcher = MockPicaLauncher()
74+
75+
# Simulate button click
76+
launcher.button_temp_control.invoke()
77+
78+
# Assert the mocked function was called
79+
mock_open_temp_control.assert_called_once()
80+
81+
# Assert the internal counter was incremented
82+
self.assertEqual(launcher.open_gui_calls['temp_control'], 1)
83+
84+
def test_multiple_clicks(self):
85+
"""Tests that multiple clicks correctly register multiple calls."""
86+
launcher = MockPicaLauncher()
87+
88+
# Simulate multiple clicks
89+
for _ in range(3):
90+
launcher.button_iv_sweep.invoke()
91+
92+
self.assertEqual(launcher.open_gui_calls['iv_sweep'], 3)
93+
self.assertEqual(launcher.open_gui_calls['temp_control'], 0) # Ensure others are not called
94+
95+
96+
if __name__ == '__main__':
97+
unittest.main()

0 commit comments

Comments
 (0)