Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
8faeec3
Update UnitTesting\estUtils.py to use PySide signals and slots
krzywon Feb 9, 2026
f6ab313
Move unit test data into the sas.qtgui.UnitTesting
krzywon Feb 9, 2026
6a74590
Update file loading in Calculators\UnitTesting to use the new file lo…
krzywon Feb 9, 2026
16af3a8
assert function.called -> function.assert_called()
krzywon Feb 9, 2026
f12cf40
Update window sizes to match new values and use PySide SizePolicy imp…
krzywon Feb 9, 2026
e22cae0
Enable all failing tests
krzywon Feb 9, 2026
b678e7e
QtWidgets.qApp.processEvents -> QtWidgets.QApplication.processEvents
krzywon Feb 9, 2026
b28d16a
Update data operations panel tests to match latest functionality
krzywon Feb 9, 2026
63818a6
Update density and sld calculator tests to match latest periodictable…
krzywon Feb 9, 2026
d23ad18
Fix all of the GSC tests
krzywon Feb 9, 2026
90c1681
Reenable skipped plotting tests
krzywon Feb 9, 2026
ecac8be
Plotting unit tests: QtWidgets.qApp -> QtWidgets.QApplication
krzywon Feb 9, 2026
c63f51f
Plotting unit tests: assert <function>.called -> <function>.assert_ca…
krzywon Feb 10, 2026
00b45a0
Get colormap tests beyond the fixtures point
krzywon Feb 10, 2026
152d88b
Fix call to return values in linear fit test (not passing yet)
krzywon Feb 10, 2026
e7a569f
Use proper data set in plotter 2d tests
krzywon Feb 10, 2026
020f962
Update plotter tests to match api change (codeEditor -> textEdit)
krzywon Feb 10, 2026
40c6a9e
Update invariant q range slider test to match API changes
krzywon Feb 10, 2026
d02de6e
Update inversion q range slider tests to match actual functionality
krzywon Feb 10, 2026
5972c1b
Add safety check to inversion logic to ensure dy exists before checki…
krzywon Feb 10, 2026
e5db6df
Match inversion q-range slider tests to match latest inversion (tabbe…
krzywon Feb 10, 2026
d8d5a81
Fix get_bins output to match method api
krzywon Feb 10, 2026
9bfcc2f
Explicitly set the min/max q values for the plotter 2D test data set
krzywon Feb 10, 2026
2e58b27
Change the test number of plot context menu actions to match the exis…
krzywon Feb 10, 2026
d2470b4
Fix the plotter data passed to the plotter tests
krzywon Feb 10, 2026
35b4c24
Set the dummy manager class in the slicer tests to a QWidget
krzywon Feb 10, 2026
e3e5832
Ensure slicer parameter tests dummy manager active_plots is set when …
krzywon Feb 10, 2026
257098b
Update color map tests to match latest mpl API
krzywon Feb 10, 2026
1150e9b
Remove unneeded assert from the add text plotter tests
krzywon Feb 10, 2026
a1c8ff2
Update linear fit tests to match latest api changes
krzywon Feb 10, 2026
e415c1f
Fix last few issues in plotter2d tests
krzywon Feb 10, 2026
1e8639f
Fix last few issues in plotter tests
krzywon Feb 10, 2026
3f52078
Turn off Invariant QRangeSlider tests and revert all work to original…
krzywon Feb 10, 2026
1a03957
Fix the last slicer parameter tests
krzywon Feb 10, 2026
ccdb71d
Remove xfail decorator from MainWindow\UnitTesting tests
krzywon Feb 10, 2026
972f54a
Fix failing welcome panel test
krzywon Feb 10, 2026
53e1a2d
qApp -> QApplication in MainWindow\UnitTesting
krzywon Feb 10, 2026
022d075
Clean up GuiUtils imports in DataExplorerTest.py
krzywon Feb 10, 2026
094b47c
Look in sas.qtgui.UnitTesting for gui test data files
krzywon Feb 10, 2026
2939a37
Cleaning up DataExplorerTest.py - down to 4 failures and 1 error
krzywon Feb 10, 2026
aa614fe
Finishing up the data explorer tests - still 1 xfail and 1 skipped, b…
krzywon Feb 11, 2026
6db736f
Fix inversion perspective currentTabDataId call to match latest funct…
krzywon Feb 11, 2026
a4a1292
Get all main window unit tests working
krzywon Feb 11, 2026
ce6232b
Get the gui manager tests running (with 1 xfail)
krzywon Feb 11, 2026
ea1f808
Get the gui manager tests running (with 1 xfail)
krzywon Feb 11, 2026
e0064ba
Get corfunc tests working
krzywon Feb 11, 2026
85a28f1
Skip the failing multi constraint test because the method being teste…
krzywon Feb 11, 2026
a40a7c3
Fix fitting complex constraint tests
krzywon Feb 11, 2026
2159b33
Disable fitting constraint widget tests because of fatal exceptions
krzywon Feb 11, 2026
2d93cb5
Fix fit page tests
krzywon Feb 11, 2026
a09d229
Fix fitting options tests with 1 xfail
krzywon Feb 11, 2026
ddcd868
Fix fitting perspective tests with 2 xfail
krzywon Feb 11, 2026
6eb5353
Get fitting widget tests into a passing state
krzywon Feb 11, 2026
40ba551
Get invariant detail tests working
krzywon Feb 11, 2026
a583554
Fix size distribution tests
krzywon Feb 11, 2026
04d47ad
Disable particle editor tests in pyproject.toml
krzywon Feb 11, 2026
ba866f9
Disable inversion tests in pyproject.toml (too many issues!)
krzywon Feb 11, 2026
7dfc994
Update inversion tests (but not going any further than this!)
krzywon Feb 11, 2026
ce15546
Finalize invariant unit test fixes
krzywon Feb 11, 2026
599f5d1
Fix file converter tests
krzywon Feb 11, 2026
f59e641
Fix grid panel tests
krzywon Feb 11, 2026
738ca58
Fix tabbed model editor tests
krzywon Feb 11, 2026
20b95d9
Xfail logger test
krzywon Feb 11, 2026
cb743da
Fix plugin definition tests
krzywon Feb 11, 2026
b18cedd
Fix gui utils tests
krzywon Feb 11, 2026
bc2bd93
Move add multie editro tests into quarantine folder and add folder to…
krzywon Feb 11, 2026
e6c46d6
Reenable the gui tests in the CI
krzywon Feb 11, 2026
a331ff1
Fix preference panel tests
krzywon Feb 11, 2026
ec5d39c
Move deactivated tests to folders called Quarantine and add those dir…
krzywon Feb 12, 2026
837229d
Try running the GUI tests on the windows runner instead of linux (test)
krzywon Feb 12, 2026
0f190bc
Remove linux bash commands from gui test runner
krzywon Feb 12, 2026
35c3d8f
Revert back to linux runner for GUI tests
krzywon Feb 12, 2026
a460ef2
Run gui tests directly against the installed sasview wheels
krzywon Feb 12, 2026
a1fca64
Build the UI files just before the UI tests to ensure they exist befo…
krzywon Feb 12, 2026
009a892
Remove qapp definition that threw an error during CI GUI tests
krzywon Feb 12, 2026
623179c
Give the suggestionss outlined in https://pytest-qt.readthedocs.io/en…
krzywon Feb 17, 2026
f6a697d
Move the test running dependency installation into the GUI test secti…
krzywon Feb 17, 2026
f890af7
Set the Qt platform to offscreen for the GUI tests to fix the segfaul…
krzywon Feb 24, 2026
5823c74
Quarantine GuiManagerTest.py due to the segfault when running the tests
krzywon Feb 24, 2026
ce4d44a
Quarantine MainWindowTest.py due to the segfault when running the tests
krzywon Feb 24, 2026
9626cd0
Quarantine QRangeSliderTests.py due to the segfault when running the …
krzywon Feb 24, 2026
fdc5ea5
Fixes for broken unit tests
krzywon Feb 24, 2026
2881a39
Fixes for broken unit tests part 2
krzywon Feb 24, 2026
21dc2be
Merge branch 'main' into gui-tests-activation
krzywon Feb 24, 2026
30ba7f7
Fixes for broken invariant tests
krzywon Feb 24, 2026
924d531
small fixes like remove .show(), use relative path to test data file …
backmari Feb 24, 2026
0e57411
Create a standard base_path importable for all gui tests to import data
krzywon Mar 4, 2026
6ea7559
Fixes issues noted in review
krzywon Mar 4, 2026
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
28 changes: 18 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ env:
INSTALLER_OLD_RUNID: 3204825457
# The test-installer job can run with the pyinstaller debug bundle
INSTALLER_USE_DEBUG: false
# https://pytest-qt.readthedocs.io/en/latest/troubleshooting.html - Required for PySide pytest runner
DISPLAY: ':99.0'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@krzywon just noticed this now (sorry) - setting DISPLAY like this here but allowing xvfb-run to pick its own display number with -a is fragile. -a is for automatically determining which display to use, starting at :99. It also should be unnecessary as xvfb-run sets DISPLAY on its own and sets it to the value of the display that it has created. It should be safe(r) to remove this when next touching the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll open an issue to remove it. This was something I tried after reading the commented link, but wasn't sure it was doing anything, and based on what you're suggesting, it likely isn't.


jobs:

Expand Down Expand Up @@ -318,16 +320,22 @@ jobs:
cd $RUNNER_TEMP
python -m pytest -v -s test

# - name: Test GUI (Linux)
# if: ${{ matrix.os == 'ubuntu-latest' }}
# env:
# PYOPENCL_COMPILER_OUTPUT: 1
# run: |
# # Suppress SIGSEGV from the tests until they can be fixed
# retval=0
# xvfb-run -a --server-args="-screen 0 1600x900x24" python -m pytest -rsx -v src/sas/qtgui/ || retval=$?
# if [ $retval -eq 139 ]; then echo "WARNING: Python interpreter exited with Segmentation Fault. This normally indicates that Qt objects were not correctly deleted. This error is currently suppressed in SasView's test suite."; retval=0; fi
# exit $retval
- name: Test GUI (Linux)
if: ${{ matrix.os == 'ubuntu-latest' }}
env:
PYOPENCL_COMPILER_OUTPUT: 1
QT_QPA_PLATFORM: offscreen
run: |
# Ensure all test running dependencies are installed (https://pytest-qt.readthedocs.io/en/latest/troubleshooting.html)
sudo apt install -y libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 x11-utils
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1920x1200x24 -ac +extension GLX
# Run the UI generation routine (with force recreate enabled)
python src/sas/qtgui/convertUI.py -f
# Suppress SIGSEGV from the tests until they can be fixed
retval=0
xvfb-run -a --server-args="-screen 0 1600x900x24" python -m pytest -rsx -v src/sas/qtgui/ || retval=$?
if [ $retval -eq 139 ]; then echo "WARNING: Python interpreter exited with Segmentation Fault. This normally indicates that Qt objects were not correctly deleted. This error is currently suppressed in SasView's test suite."; retval=0; fi
exit $retval

installer-matrix:
needs: [matrix, build-sasview]
Expand Down
6 changes: 6 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,12 @@ pythonpath = "src"
norecursedirs = [
"test/sasrealspace",
"test/calculatorview",
"src/sas/qtgui/MainWindow/UnitTesting/Quarantine",
"src/sas/qtgui/Perspectives/Fitting/UnitTesting/Quarantine",
"src/sas/qtgui/Perspectives/Inversion",
"src/sas/qtgui/Perspectives/ParticleEditor",
"src/sas/qtgui/Plotting/UnitTesting/Quarantine",
"src/sas/qtgui/Utilities/UnitTesting/Quarantine"
]

[tool.ruff]
Expand Down
36 changes: 14 additions & 22 deletions src/sas/qtgui/Calculators/UnitTesting/DataOperationUtilityTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def testDefaults(self, widget, mocker):
"| (append)]"
# size
assert widget.size().height() == 425
assert widget.size().width() == 951
assert widget.size().width() == 1168

# content of line edits
assert widget.txtNumber.text() == '1.0'
Expand All @@ -67,12 +67,9 @@ def testDefaults(self, widget, mocker):
['+', '-', '*', '/', '|']

# Tooltips
assert str(widget.cmdCompute.toolTip()) == "Generate the Data " \
"and send to Data " \
"Explorer."
assert str(widget.cmdCompute.toolTip()) == "Generate the Data and show the preview."
assert str(widget.cmdClose.toolTip()) == "Close this panel."
assert str(widget.cmdHelp.toolTip()) == \
"Get help on Data Operations."
assert str(widget.cmdHelp.toolTip()) == "Get help on Data Operations."
assert widget.txtNumber.toolTip() == "If no Data2 loaded, " \
"enter a number to be " \
"applied to Data1 using " \
Expand Down Expand Up @@ -101,15 +98,13 @@ def testDefaults(self, widget, mocker):
assert not widget.data1OK

mocker.patch.object(widget, 'newPlot')
assert widget.newPlot.called_once()
assert widget.newPlot.called_once()
assert widget.newPlot.called_once()
widget.newPlot.assert_not_called()

def testHelp(self, widget, mocker):
""" Assure help file is shown """
mocker.patch.object(widget.manager, 'showHelp', create=True)
widget.onHelp()
assert widget.manager.showHelp.called_once()
widget.manager.showHelp.assert_called_once()
args = widget.manager.showHelp.call_args
assert 'data_operator_help.html' in args[0][0]

Expand All @@ -125,7 +120,6 @@ def testOnClose(self, widget):
closeButton = widget.cmdClose
QTest.mouseClick(closeButton, Qt.LeftButton)

@pytest.mark.xfail(reason="2022-09 already broken")
def testOnCompute(self, widget, mocker):
""" Test onCompute function """

Expand All @@ -136,6 +130,7 @@ def testOnCompute(self, widget, mocker):

# mock update of plot
mocker.patch.object(widget, 'updatePlot')
mocker.patch.object(widget, 'onPrepareOutputData')

# enable onCompute to run (check on data type)
mocker.patch.object(widget, 'onCheckChosenData', return_value=True)
Expand All @@ -148,11 +143,9 @@ def testOnCompute(self, widget, mocker):
assert widget.output.x.tolist() == \
widget.data1.x.tolist()
assert widget.output.y.tolist() == [12.0, 13.0, 14.0]
assert widget.updatePlot.called_once()

mocker.patch.object(widget, 'onPrepareOutputData')

assert widget.onPrepareOutputData.called_once()
widget.updatePlot.assert_called_once()
widget.onPrepareOutputData.assert_not_called()

def testOnSelectData1(self, widget, mocker):
""" Test ComboBox for Data1 """
Expand All @@ -168,7 +161,7 @@ def testOnSelectData1(self, widget, mocker):

widget.cbData1.addItems(['Select Data', 'datafile1'])
widget.cbData1.setCurrentIndex(widget.cbData1.count()-1)
assert widget.updatePlot.called_once()
widget.updatePlot.assert_called_once()
# Compute button disabled if data2OK == False
assert widget.cmdCompute.isEnabled() == widget.data2OK

Expand Down Expand Up @@ -199,20 +192,20 @@ def testOnSelectData2(self, widget, mocker):
assert widget.cmdCompute.isEnabled() == widget.data1OK
assert isinstance(widget.data2, float)
# call updatePlot
assert widget.updatePlot.called_once()
widget.updatePlot.assert_called()

# Case 4: when Data2 is a file
mocker.patch.object(widget, 'filenames', return_value={'datafile2': 'details'})
widget.cbData2.addItems(['Select Data', 'Number', 'datafile2'])
widget.cbData2.setCurrentIndex(widget.cbData2.count() - 1)
assert widget.updatePlot.called_once()
widget.updatePlot.assert_called()
# editing of txtNumber is disabled when Data2 is a file
assert not widget.txtNumber.isEnabled()
# Compute button enabled only if data1OK True
assert widget.cmdCompute.isEnabled() == \
widget.data1OK
# call updatePlot
assert widget.updatePlot.called_once()
widget.updatePlot.assert_called()

def testUpdateCombobox(self, widget):
""" Test change of contents of comboboxes for Data1 and Data2 """
Expand Down Expand Up @@ -364,7 +357,6 @@ def testFindId(self, widget):
id_out = widget._findId('datafile2')
assert id_out == 'datafile2'

@pytest.mark.xfail(reason="2022-09 already broken")
def testExtractData(self, widget):
"""
Test function to extract data to be computed from input filenames
Expand All @@ -385,10 +377,10 @@ def testExtractData(self, widget):
widget.filenames = {'datafile2': DataState(data2),
'datafile1': DataState(data1)}
output1D = widget._extractData('datafile1')
assert isinstance(output1D, Data1D)
assert isinstance(output1D.data, Data1D)

output2D = widget._extractData('datafile2')
assert isinstance(output2D, Data2D)
assert isinstance(output2D.data, Data2D)

# TODO
def testOnPrepareOutputData(self, widget):
Expand Down
18 changes: 8 additions & 10 deletions src/sas/qtgui/Calculators/UnitTesting/DensityCalculatorTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
class ToMolarMassTest:
""" Test the auxiliary conversion method"""
def testGoodEasy(self):
assert toMolarMass("H2") == "2.01588"
assert toMolarMass("H2") == "2.016"

def testGoodComplex(self):
assert toMolarMass("H24O12C4C6N2Pu") == "608.304"

def testGoodComplex2(self):
assert toMolarMass("(H2O)0.5(D2O)0.5") == "19.0214"
assert toMolarMass("(H2O)0.5(D2O)0.5") == "19.0211"

def testBadInputInt(self):
assert toMolarMass(1) == ""
Expand Down Expand Up @@ -47,7 +47,9 @@ def testDefaults(self, widget):
assert widget.ui.editMolecularFormula.styleSheet() == ''
assert widget.model.columnCount() == 1
assert widget.model.rowCount() == 4
assert widget.sizePolicy().Policy() == QtWidgets.QSizePolicy.Fixed
sp = widget.sizePolicy()
assert sp.horizontalPolicy() == QtWidgets.QSizePolicy.Policy.Fixed
assert sp.verticalPolicy() == QtWidgets.QSizePolicy.Policy.Fixed


def testModelMolecularFormula(self, widget, qtbot):
Expand Down Expand Up @@ -89,8 +91,6 @@ def testSimpleEntry(self, widget, qtbot):
''' Default compound calculations '''
qtbot.addWidget(widget)

widget.show()

qtbot.keyClicks(widget.ui.editMolarVolume, "1.0")

# Send tab x3
Expand All @@ -113,14 +113,12 @@ def testSimpleEntry(self, widget, qtbot):
QTest.qWait(100)

# Assure the molar volume field got updated
assert widget.ui.editMolarVolume.text() == '1.126'
assert widget.ui.editMolarVolume.text() == '1.1259'

def testComplexEntryAndReset(self, widget, qtbot):
''' User entered compound calculations and subsequent reset'''
qtbot.addWidget(widget)

widget.show()

widget.ui.editMolecularFormula.clear()
qtbot.keyClicks(widget.ui.editMolecularFormula, "KMnO4")
qtbot.keyClicks(widget.ui.editMolarVolume, "2.0")
Expand All @@ -136,7 +134,7 @@ def testComplexEntryAndReset(self, widget, qtbot):
qtbot.keyEvent(QTest.Press, widget, key, QtCore.Qt.NoModifier)

# Assure the mass density field is set
assert widget.ui.editMassDensity.text() == '79.017'
assert widget.ui.editMassDensity.text() == '79.016'

# Reset the widget
qtbot.mouseClick(widget.ui.buttonBox.button(QtWidgets.QDialogButtonBox.Reset), QtCore.Qt.LeftButton)
Expand All @@ -153,6 +151,6 @@ def testHelp(self, widget, mocker):
widget.manager = QtWidgets.QWidget()
mocker.patch.object(widget.manager, 'showHelp', create=True)
widget.displayHelp()
assert widget.manager.showHelp.called_once()
widget.manager.showHelp.assert_called_once()
args = widget.manager.showHelp.call_args
assert 'density_calculator_help.html' in args[0][0]
Loading