Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pydm
pandas
scipy
pytest
pytest-qt
qtawesome
Expand Down
49 changes: 18 additions & 31 deletions trace/examples/FormulaExample.trc
Original file line number Diff line number Diff line change
@@ -1,52 +1,37 @@
{
"archiver_url": "http://lcls-archapp.slac.stanford.edu",
"plot": {
"refreshInterval": 0.0,
"refreshInterval": 1.0,
"title": "",
"xGrid": false,
"yGrid": false,
"opacity": 128,
"backgroundColor": "#ffffff",
"legend": false,
"crosshair": false,
"mouseMode": 1
},
"time_axis": {
"name": "Main Time Axis",
"start": "2024-09-04 11:03:44",
"end": "2024-09-04 12:03:44",
"start": "-1m",
"end": "now",
"location": "bottom"
},
"y-axes": [
{
"name": "Axis 1",
"orientation": "left",
"minRange": -1.3843656974520628e-09,
"maxRange": 4.752694382245206e-08,
"label": "Axis 1",
"minRange": 1.1067500225324897e-08,
"maxRange": 2.6286015399675106e-08,
"autoRange": true,
"logMode": false
},
{
"name": "Axis 2",
"name": "torr",
"orientation": "left",
"minRange": 9.047133561624635e-10,
"maxRange": 1.3621653831337537e-08,
"autoRange": true,
"logMode": false
},
{
"name": "Axis 3",
"orientation": "left",
"minRange": 1.3015564414604301e-08,
"maxRange": 4.65547480853957e-08,
"autoRange": true,
"logMode": false
},
{
"name": "Axis 4",
"orientation": "left",
"minRange": -1.04,
"maxRange": 1.04,
"label": "torr",
"minRange": 1.0577346003248955e-09,
"maxRange": 1.6276249774675104e-08,
"autoRange": true,
"logMode": false
}
Expand All @@ -55,25 +40,27 @@
{
"useArchiveData": true,
"liveData": true,
"showExtensionLine": false,
"channel": "KLYS:LI22:31:KVAC",
"name": "KLYS:LI22:31:KVAC",
"color": "#008cf9",
"lineStyle": 1,
"lineWidth": 1,
"symbolSize": 10,
"yAxisName": "Axis 1",
"yAxisName": "torr",
"thresholdColor": "white"
},
{
"useArchiveData": true,
"liveData": true,
"showExtensionLine": false,
"channel": "KLYS:LI22:41:KVAC",
"name": "KLYS:LI22:41:KVAC",
"color": "#006e00",
"lineStyle": 1,
"lineWidth": 1,
"symbolSize": 10,
"yAxisName": "Axis 1",
"yAxisName": "torr",
"thresholdColor": "white"
}
],
Expand All @@ -82,12 +69,12 @@
"useArchiveData": true,
"liveData": true,
"plot_style": "Line",
"formula": "f://{A}+{B}",
"formula": "f://{x1}+{x2}",
"curveDict": {
"A": "KLYS:LI22:31:KVAC",
"B": "KLYS:LI22:41:KVAC"
"x1": "KLYS:LI22:31:KVAC",
"x2": "KLYS:LI22:41:KVAC"
},
"name": "f://{A}+{B}",
"name": "f://{x1}+{x2}",
"color": "#b80058",
"lineStyle": 1,
"lineWidth": 1,
Expand Down
10 changes: 8 additions & 2 deletions trace/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from unittest import mock

import pytest
from qtpy.QtWidgets import QMenu

from pydm.application import PyDMApplication

Expand Down Expand Up @@ -73,10 +74,15 @@ def qtrace(qtbot, qapp):
------
An instance of TraceDisplay.
"""
trace = TraceDisplay()
# TraceDisplay.__init__ returns early when app.main_window is None (use_main_window=False).
# Patch it with a MagicMock so build_ui() and configure_app() run normally.
# construct_trace_menu is also patched because QMenu rejects a MagicMock parent.
with mock.patch.object(qapp, "main_window", mock.MagicMock()):
with mock.patch.object(TraceDisplay, "construct_trace_menu", return_value=QMenu()):
trace = TraceDisplay()

# updateXAxis would be called on application render; necessary for testing X-Axis
trace.main_plot.updateXAxis(True)
trace.plot.updateXAxis(True)
yield trace

trace.close()
Expand Down
175 changes: 175 additions & 0 deletions trace/tests/test_file_io/test_file_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
from os import environ
from unittest.mock import Mock, patch

import pytest

from file_io import TraceFileHandler

DUMMY_ARCHIVER_URL = "http://dummy.archiver.url/retrieval"


@pytest.fixture
def file_handler(qapp):
"""Fixture for an instance of TraceFileHandler with a mocked plot.

Yields
------
An instance of TraceFileHandler.
"""
mock_plot = Mock()
with patch.dict(environ, {"PYDM_ARCHIVER_URL": DUMMY_ARCHIVER_URL}):
with patch("file_io.file_handler.QMessageBox.warning", return_value=Mock()):
yield TraceFileHandler(mock_plot)


def test_open_file_curves_signal_includes_formulas(file_handler, get_test_file, qtbot):
"""Test that opening a .trc file with formulas emits curves_signal with both
regular curves and formula curves combined into a single list.

Parameters
----------
file_handler : fixture
Instance of TraceFileHandler for testing
get_test_file : fixture
A fixture used to get test files from the test_data directory
qtbot : fixture
pytest-qt fixture for Qt testing

Expectations
------------
curves_signal is emitted once with a list whose length equals the number
of regular curves plus the number of formulas in the file.
"""
test_file = get_test_file("test_file.trc")

with patch.dict(environ, {"PYDM_ARCHIVER_URL": DUMMY_ARCHIVER_URL}):
with qtbot.wait_signal(file_handler.curves_signal) as blocker:
file_handler.open_file(test_file)

# test_file.trc has 2 regular curves + 1 formula
received_curves = blocker.args[0]
assert len(received_curves) == 3


def test_open_file_formula_entries_have_formula_key(file_handler, get_test_file, qtbot):
"""Test that formula entries in the emitted curves_signal have the 'formula' key.

Parameters
----------
file_handler : fixture
Instance of TraceFileHandler for testing
get_test_file : fixture
A fixture used to get test files from the test_data directory
qtbot : fixture
pytest-qt fixture for Qt testing

Expectations
------------
Exactly one entry in curves_signal has a 'formula' key, and its value
matches the formula string from the test file.
"""
test_file = get_test_file("test_file.trc")

with patch.dict(environ, {"PYDM_ARCHIVER_URL": DUMMY_ARCHIVER_URL}):
with qtbot.wait_signal(file_handler.curves_signal) as blocker:
file_handler.open_file(test_file)

received_curves = blocker.args[0]
formula_entries = [c for c in received_curves if "formula" in c]

assert len(formula_entries) == 1
assert formula_entries[0]["formula"] == "f://{x0}+{x1}"


def test_open_file_regular_curve_entries_have_channel_key(file_handler, get_test_file, qtbot):
"""Test that regular curve entries in the emitted curves_signal have the 'channel' key.

Parameters
----------
file_handler : fixture
Instance of TraceFileHandler for testing
get_test_file : fixture
A fixture used to get test files from the test_data directory
qtbot : fixture
pytest-qt fixture for Qt testing

Expectations
------------
Exactly two entries in curves_signal have a 'channel' key, corresponding
to the regular PV curves in the test file.
"""
test_file = get_test_file("test_file.trc")

with patch.dict(environ, {"PYDM_ARCHIVER_URL": DUMMY_ARCHIVER_URL}):
with qtbot.wait_signal(file_handler.curves_signal) as blocker:
file_handler.open_file(test_file)

received_curves = blocker.args[0]
channel_entries = [c for c in received_curves if "channel" in c]

assert len(channel_entries) == 2


def test_open_file_formulas_appended_after_curves(file_handler, get_test_file, qtbot):
"""Test that formula entries appear after regular curve entries in curves_signal,
reflecting the file_data["curves"] + file_data["formula"] concatenation order.

Parameters
----------
file_handler : fixture
Instance of TraceFileHandler for testing
get_test_file : fixture
A fixture used to get test files from the test_data directory
qtbot : fixture
pytest-qt fixture for Qt testing

Expectations
------------
The first entries in curves_signal have 'channel' keys (regular curves),
and the last entries have 'formula' keys (formula curves).
"""
test_file = get_test_file("test_file.trc")

with patch.dict(environ, {"PYDM_ARCHIVER_URL": DUMMY_ARCHIVER_URL}):
with qtbot.wait_signal(file_handler.curves_signal) as blocker:
file_handler.open_file(test_file)

received_curves = blocker.args[0]

assert "channel" in received_curves[0]
assert "channel" in received_curves[1]
assert "formula" in received_curves[2]


def test_open_file_formula_properties_match_trc_file(file_handler, get_test_file, qtbot):
"""Test that the formula entry in curves_signal contains all properties
from the .trc file, including name, color, yAxisName, and curveDict.

Parameters
----------
file_handler : fixture
Instance of TraceFileHandler for testing
get_test_file : fixture
A fixture used to get test files from the test_data directory
qtbot : fixture
pytest-qt fixture for Qt testing

Expectations
------------
The formula entry in curves_signal has the correct formula string, name,
color, yAxisName, lineWidth, and curveDict matching the test file.
"""
test_file = get_test_file("test_file.trc")

with patch.dict(environ, {"PYDM_ARCHIVER_URL": DUMMY_ARCHIVER_URL}):
with qtbot.wait_signal(file_handler.curves_signal) as blocker:
file_handler.open_file(test_file)

received_curves = blocker.args[0]
formula_entry = next(c for c in received_curves if "formula" in c)

assert formula_entry["name"] == "formula0"
assert formula_entry["formula"] == "f://{x0}+{x1}"
assert formula_entry["yAxisName"] == "Main Range Axis"
assert formula_entry["color"] == "#00ff00"
assert formula_entry["curveDict"] == {"x0": "KLYS:LI22:31:KVAC", "x1": "KLYS:LI22:41:KVAC"}
Loading
Loading