From 3e767837e671e09ecbb8192c15eb8fd54e0aa252 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Sun, 4 Jun 2023 17:29:18 -0600 Subject: [PATCH 1/3] Add basic GUI tests --- .github/workflows/test.yml | 13 +- Makefile | 2 +- doc/contributing.md | 32 +++- test/conftest.py | 10 ++ test/data/option_d.py | 311 +++++++++++++++++++++++++++++++++++++ test/test_gui.py | 44 ++++++ viscm/cli.py | 99 ++++++++---- viscm/gui.py | 2 +- 8 files changed, 471 insertions(+), 42 deletions(-) create mode 100644 test/conftest.py create mode 100644 test/data/option_d.py create mode 100644 test/test_gui.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 131562e..9c7d542 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,10 +26,19 @@ jobs: with: python-version: "${{ matrix.python-version }}" - - name: "Install dependencies in pyproject.toml" + - name: "Install test and project dependencies" run: | + # Project dependencies from pyproject.toml + # NOTE: Also builds viscm. How do we avoid this? pip install . - pip install pytest pytest-cov ${{ matrix.pyqt-dependency }} + + # Test dependencies + pip install pytest pytest-cov pytest-qt pytest-xvfb ${{ matrix.pyqt-dependency }} + # pytest-qt CI dependencies: https://pytest-qt.readthedocs.io/en/latest/troubleshooting.html#github-actions + sudo apt update + sudo apt install -y \ + xvfb \ + libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 x11-utils - name: "Run tests" run: "make test" diff --git a/Makefile b/Makefile index b8cc123..80f7393 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: test test: python -m pytest --version - python -m pytest -v test/ + python -m pytest --xvfb-backend=xvfb -v test/ .PHONY: lint diff --git a/doc/contributing.md b/doc/contributing.md index 45cc0df..95fe327 100644 --- a/doc/contributing.md +++ b/doc/contributing.md @@ -2,28 +2,37 @@ Install development dependencies: -``` +```bash conda env create # or `mamba env create` ``` ## Development install -``` +```bash pip install -e . ``` ## Testing the build -``` +```bash rm -rf dist python -m build pip install dist/*.whl # or `dist/*.tar.gz` ``` -## Code formatting and linting +## Tests + +See `Makefile` for convenience commands. + + +### Code linting (and formatting) + +```bash +make lint +``` This codebase uses [black](https://black.readthedocs.io/en/stable/) and [ruff](https://github.com/charliermarsh/ruff) to automatically format and lint the code. @@ -37,3 +46,18 @@ and then another commit should immediately follow which updates `.git-blame-ignore-revs`. For example: [1fec42d](https://github.com/matplotlib/viscm/pull/64/commits/1fec42d0baf90e00d510efd76cb6006fa0c70dc4), [8aa7bb0](https://github.com/matplotlib/viscm/pull/64/commits/8aa7bb01440aeca6f8bbcefe0671c28f2ce284c6). + + +### Unit tests + +```bash +make test +``` + +Unit tests require `xvfb` (X Virtual Framebuffer) to test the GUI. If `xvfb` is not +installed, you'll receive `ERROR: xvfb backend xvfb requested but not installed`. +Install with: + +```bash +sudo apt install xvfb +``` diff --git a/test/conftest.py b/test/conftest.py new file mode 100644 index 0000000..0c1de49 --- /dev/null +++ b/test/conftest.py @@ -0,0 +1,10 @@ +from pathlib import Path + +import pytest + + +@pytest.fixture +def tests_data_dir() -> Path: + tests_dir = Path(__file__).parent.resolve() + tests_data_dir = tests_dir / "data" + return tests_data_dir diff --git a/test/data/option_d.py b/test/data/option_d.py new file mode 100644 index 0000000..f2868b9 --- /dev/null +++ b/test/data/option_d.py @@ -0,0 +1,311 @@ +"""Viridis / Option D colormap from mpl-colormaps + +https://github.com/BIDS/colormap/blob/bc549477db0c12b54a5928087552ad2cf274980f/option_d.py + +mpl-colormaps by Nathaniel Smith & Stefan van der Walt + +To the extent possible under law, the persons who associated CC0 with +mpl-colormaps have waived all copyright and related or neighboring rights +to mpl-colormaps. + +You should have received a copy of the CC0 legalcode along with this +work. If not, see . +""" +from matplotlib.colors import LinearSegmentedColormap + +# Used to reconstruct the colormap in viscm +parameters = { + "xp": [ + 22.674387857633945, + 11.221508276482126, + -14.356589454756971, + -47.18817758739222, + -34.59001004812521, + -6.0516291196352654, + ], + "yp": [ + -20.102530541012214, + -33.08246073298429, + -42.24476439790574, + -5.595549738219887, + 42.5065445026178, + 40.13395157135497, + ], + "min_JK": 18.8671875, + "max_JK": 92.5, +} + +cm_data = [ + [0.26700401, 0.00487433, 0.32941519], + [0.26851048, 0.00960483, 0.33542652], + [0.26994384, 0.01462494, 0.34137895], + [0.27130489, 0.01994186, 0.34726862], + [0.27259384, 0.02556309, 0.35309303], + [0.27380934, 0.03149748, 0.35885256], + [0.27495242, 0.03775181, 0.36454323], + [0.27602238, 0.04416723, 0.37016418], + [0.2770184, 0.05034437, 0.37571452], + [0.27794143, 0.05632444, 0.38119074], + [0.27879067, 0.06214536, 0.38659204], + [0.2795655, 0.06783587, 0.39191723], + [0.28026658, 0.07341724, 0.39716349], + [0.28089358, 0.07890703, 0.40232944], + [0.28144581, 0.0843197, 0.40741404], + [0.28192358, 0.08966622, 0.41241521], + [0.28232739, 0.09495545, 0.41733086], + [0.28265633, 0.10019576, 0.42216032], + [0.28291049, 0.10539345, 0.42690202], + [0.28309095, 0.11055307, 0.43155375], + [0.28319704, 0.11567966, 0.43611482], + [0.28322882, 0.12077701, 0.44058404], + [0.28318684, 0.12584799, 0.44496], + [0.283072, 0.13089477, 0.44924127], + [0.28288389, 0.13592005, 0.45342734], + [0.28262297, 0.14092556, 0.45751726], + [0.28229037, 0.14591233, 0.46150995], + [0.28188676, 0.15088147, 0.46540474], + [0.28141228, 0.15583425, 0.46920128], + [0.28086773, 0.16077132, 0.47289909], + [0.28025468, 0.16569272, 0.47649762], + [0.27957399, 0.17059884, 0.47999675], + [0.27882618, 0.1754902, 0.48339654], + [0.27801236, 0.18036684, 0.48669702], + [0.27713437, 0.18522836, 0.48989831], + [0.27619376, 0.19007447, 0.49300074], + [0.27519116, 0.1949054, 0.49600488], + [0.27412802, 0.19972086, 0.49891131], + [0.27300596, 0.20452049, 0.50172076], + [0.27182812, 0.20930306, 0.50443413], + [0.27059473, 0.21406899, 0.50705243], + [0.26930756, 0.21881782, 0.50957678], + [0.26796846, 0.22354911, 0.5120084], + [0.26657984, 0.2282621, 0.5143487], + [0.2651445, 0.23295593, 0.5165993], + [0.2636632, 0.23763078, 0.51876163], + [0.26213801, 0.24228619, 0.52083736], + [0.26057103, 0.2469217, 0.52282822], + [0.25896451, 0.25153685, 0.52473609], + [0.25732244, 0.2561304, 0.52656332], + [0.25564519, 0.26070284, 0.52831152], + [0.25393498, 0.26525384, 0.52998273], + [0.25219404, 0.26978306, 0.53157905], + [0.25042462, 0.27429024, 0.53310261], + [0.24862899, 0.27877509, 0.53455561], + [0.2468114, 0.28323662, 0.53594093], + [0.24497208, 0.28767547, 0.53726018], + [0.24311324, 0.29209154, 0.53851561], + [0.24123708, 0.29648471, 0.53970946], + [0.23934575, 0.30085494, 0.54084398], + [0.23744138, 0.30520222, 0.5419214], + [0.23552606, 0.30952657, 0.54294396], + [0.23360277, 0.31382773, 0.54391424], + [0.2316735, 0.3181058, 0.54483444], + [0.22973926, 0.32236127, 0.54570633], + [0.22780192, 0.32659432, 0.546532], + [0.2258633, 0.33080515, 0.54731353], + [0.22392515, 0.334994, 0.54805291], + [0.22198915, 0.33916114, 0.54875211], + [0.22005691, 0.34330688, 0.54941304], + [0.21812995, 0.34743154, 0.55003755], + [0.21620971, 0.35153548, 0.55062743], + [0.21429757, 0.35561907, 0.5511844], + [0.21239477, 0.35968273, 0.55171011], + [0.2105031, 0.36372671, 0.55220646], + [0.20862342, 0.36775151, 0.55267486], + [0.20675628, 0.37175775, 0.55311653], + [0.20490257, 0.37574589, 0.55353282], + [0.20306309, 0.37971644, 0.55392505], + [0.20123854, 0.38366989, 0.55429441], + [0.1994295, 0.38760678, 0.55464205], + [0.1976365, 0.39152762, 0.55496905], + [0.19585993, 0.39543297, 0.55527637], + [0.19410009, 0.39932336, 0.55556494], + [0.19235719, 0.40319934, 0.55583559], + [0.19063135, 0.40706148, 0.55608907], + [0.18892259, 0.41091033, 0.55632606], + [0.18723083, 0.41474645, 0.55654717], + [0.18555593, 0.4185704, 0.55675292], + [0.18389763, 0.42238275, 0.55694377], + [0.18225561, 0.42618405, 0.5571201], + [0.18062949, 0.42997486, 0.55728221], + [0.17901879, 0.43375572, 0.55743035], + [0.17742298, 0.4375272, 0.55756466], + [0.17584148, 0.44128981, 0.55768526], + [0.17427363, 0.4450441, 0.55779216], + [0.17271876, 0.4487906, 0.55788532], + [0.17117615, 0.4525298, 0.55796464], + [0.16964573, 0.45626209, 0.55803034], + [0.16812641, 0.45998802, 0.55808199], + [0.1666171, 0.46370813, 0.55811913], + [0.16511703, 0.4674229, 0.55814141], + [0.16362543, 0.47113278, 0.55814842], + [0.16214155, 0.47483821, 0.55813967], + [0.16066467, 0.47853961, 0.55811466], + [0.15919413, 0.4822374, 0.5580728], + [0.15772933, 0.48593197, 0.55801347], + [0.15626973, 0.4896237, 0.557936], + [0.15481488, 0.49331293, 0.55783967], + [0.15336445, 0.49700003, 0.55772371], + [0.1519182, 0.50068529, 0.55758733], + [0.15047605, 0.50436904, 0.55742968], + [0.14903918, 0.50805136, 0.5572505], + [0.14760731, 0.51173263, 0.55704861], + [0.14618026, 0.51541316, 0.55682271], + [0.14475863, 0.51909319, 0.55657181], + [0.14334327, 0.52277292, 0.55629491], + [0.14193527, 0.52645254, 0.55599097], + [0.14053599, 0.53013219, 0.55565893], + [0.13914708, 0.53381201, 0.55529773], + [0.13777048, 0.53749213, 0.55490625], + [0.1364085, 0.54117264, 0.55448339], + [0.13506561, 0.54485335, 0.55402906], + [0.13374299, 0.54853458, 0.55354108], + [0.13244401, 0.55221637, 0.55301828], + [0.13117249, 0.55589872, 0.55245948], + [0.1299327, 0.55958162, 0.55186354], + [0.12872938, 0.56326503, 0.55122927], + [0.12756771, 0.56694891, 0.55055551], + [0.12645338, 0.57063316, 0.5498411], + [0.12539383, 0.57431754, 0.54908564], + [0.12439474, 0.57800205, 0.5482874], + [0.12346281, 0.58168661, 0.54744498], + [0.12260562, 0.58537105, 0.54655722], + [0.12183122, 0.58905521, 0.54562298], + [0.12114807, 0.59273889, 0.54464114], + [0.12056501, 0.59642187, 0.54361058], + [0.12009154, 0.60010387, 0.54253043], + [0.11973756, 0.60378459, 0.54139999], + [0.11951163, 0.60746388, 0.54021751], + [0.11942341, 0.61114146, 0.53898192], + [0.11948255, 0.61481702, 0.53769219], + [0.11969858, 0.61849025, 0.53634733], + [0.12008079, 0.62216081, 0.53494633], + [0.12063824, 0.62582833, 0.53348834], + [0.12137972, 0.62949242, 0.53197275], + [0.12231244, 0.63315277, 0.53039808], + [0.12344358, 0.63680899, 0.52876343], + [0.12477953, 0.64046069, 0.52706792], + [0.12632581, 0.64410744, 0.52531069], + [0.12808703, 0.64774881, 0.52349092], + [0.13006688, 0.65138436, 0.52160791], + [0.13226797, 0.65501363, 0.51966086], + [0.13469183, 0.65863619, 0.5176488], + [0.13733921, 0.66225157, 0.51557101], + [0.14020991, 0.66585927, 0.5134268], + [0.14330291, 0.66945881, 0.51121549], + [0.1466164, 0.67304968, 0.50893644], + [0.15014782, 0.67663139, 0.5065889], + [0.15389405, 0.68020343, 0.50417217], + [0.15785146, 0.68376525, 0.50168574], + [0.16201598, 0.68731632, 0.49912906], + [0.1663832, 0.69085611, 0.49650163], + [0.1709484, 0.69438405, 0.49380294], + [0.17570671, 0.6978996, 0.49103252], + [0.18065314, 0.70140222, 0.48818938], + [0.18578266, 0.70489133, 0.48527326], + [0.19109018, 0.70836635, 0.48228395], + [0.19657063, 0.71182668, 0.47922108], + [0.20221902, 0.71527175, 0.47608431], + [0.20803045, 0.71870095, 0.4728733], + [0.21400015, 0.72211371, 0.46958774], + [0.22012381, 0.72550945, 0.46622638], + [0.2263969, 0.72888753, 0.46278934], + [0.23281498, 0.73224735, 0.45927675], + [0.2393739, 0.73558828, 0.45568838], + [0.24606968, 0.73890972, 0.45202405], + [0.25289851, 0.74221104, 0.44828355], + [0.25985676, 0.74549162, 0.44446673], + [0.26694127, 0.74875084, 0.44057284], + [0.27414922, 0.75198807, 0.4366009], + [0.28147681, 0.75520266, 0.43255207], + [0.28892102, 0.75839399, 0.42842626], + [0.29647899, 0.76156142, 0.42422341], + [0.30414796, 0.76470433, 0.41994346], + [0.31192534, 0.76782207, 0.41558638], + [0.3198086, 0.77091403, 0.41115215], + [0.3277958, 0.77397953, 0.40664011], + [0.33588539, 0.7770179, 0.40204917], + [0.34407411, 0.78002855, 0.39738103], + [0.35235985, 0.78301086, 0.39263579], + [0.36074053, 0.78596419, 0.38781353], + [0.3692142, 0.78888793, 0.38291438], + [0.37777892, 0.79178146, 0.3779385], + [0.38643282, 0.79464415, 0.37288606], + [0.39517408, 0.79747541, 0.36775726], + [0.40400101, 0.80027461, 0.36255223], + [0.4129135, 0.80304099, 0.35726893], + [0.42190813, 0.80577412, 0.35191009], + [0.43098317, 0.80847343, 0.34647607], + [0.44013691, 0.81113836, 0.3409673], + [0.44936763, 0.81376835, 0.33538426], + [0.45867362, 0.81636288, 0.32972749], + [0.46805314, 0.81892143, 0.32399761], + [0.47750446, 0.82144351, 0.31819529], + [0.4870258, 0.82392862, 0.31232133], + [0.49661536, 0.82637633, 0.30637661], + [0.5062713, 0.82878621, 0.30036211], + [0.51599182, 0.83115784, 0.29427888], + [0.52577622, 0.83349064, 0.2881265], + [0.5356211, 0.83578452, 0.28190832], + [0.5455244, 0.83803918, 0.27562602], + [0.55548397, 0.84025437, 0.26928147], + [0.5654976, 0.8424299, 0.26287683], + [0.57556297, 0.84456561, 0.25641457], + [0.58567772, 0.84666139, 0.24989748], + [0.59583934, 0.84871722, 0.24332878], + [0.60604528, 0.8507331, 0.23671214], + [0.61629283, 0.85270912, 0.23005179], + [0.62657923, 0.85464543, 0.22335258], + [0.63690157, 0.85654226, 0.21662012], + [0.64725685, 0.85839991, 0.20986086], + [0.65764197, 0.86021878, 0.20308229], + [0.66805369, 0.86199932, 0.19629307], + [0.67848868, 0.86374211, 0.18950326], + [0.68894351, 0.86544779, 0.18272455], + [0.69941463, 0.86711711, 0.17597055], + [0.70989842, 0.86875092, 0.16925712], + [0.72039115, 0.87035015, 0.16260273], + [0.73088902, 0.87191584, 0.15602894], + [0.74138803, 0.87344918, 0.14956101], + [0.75188414, 0.87495143, 0.14322828], + [0.76237342, 0.87642392, 0.13706449], + [0.77285183, 0.87786808, 0.13110864], + [0.78331535, 0.87928545, 0.12540538], + [0.79375994, 0.88067763, 0.12000532], + [0.80418159, 0.88204632, 0.11496505], + [0.81457634, 0.88339329, 0.11034678], + [0.82494028, 0.88472036, 0.10621724], + [0.83526959, 0.88602943, 0.1026459], + [0.84556056, 0.88732243, 0.09970219], + [0.8558096, 0.88860134, 0.09745186], + [0.86601325, 0.88986815, 0.09595277], + [0.87616824, 0.89112487, 0.09525046], + [0.88627146, 0.89237353, 0.09537439], + [0.89632002, 0.89361614, 0.09633538], + [0.90631121, 0.89485467, 0.09812496], + [0.91624212, 0.89609127, 0.1007168], + [0.92610579, 0.89732977, 0.10407067], + [0.93590444, 0.8985704, 0.10813094], + [0.94563626, 0.899815, 0.11283773], + [0.95529972, 0.90106534, 0.11812832], + [0.96489353, 0.90232311, 0.12394051], + [0.97441665, 0.90358991, 0.13021494], + [0.98386829, 0.90486726, 0.13689671], + [0.99324789, 0.90615657, 0.1439362], +] + +test_cm = LinearSegmentedColormap.from_list(__file__, cm_data) + + +if __name__ == "__main__": + import matplotlib.pyplot as plt + import numpy as np + + try: + from viscm import viscm + + viscm(test_cm) + except ImportError: + print("viscm not found, falling back on simple display") + plt.imshow(np.linspace(0, 100, 256)[None, :], aspect="auto", cmap=test_cm) + plt.show() diff --git a/test/test_gui.py b/test/test_gui.py new file mode 100644 index 0000000..6b06ee8 --- /dev/null +++ b/test/test_gui.py @@ -0,0 +1,44 @@ +from pathlib import Path + +import pytest + +from viscm.cli import _make_window + + +class TestGui: + def test_gui_view_opens(self, qtbot): + window = _make_window( + action="view", + cmap="viridis", + cmap_type="linear", + cmap_spline_method="CatmulClark", + cmap_uniform_space="CAM02-UCS", + save=None, + quit_immediately=False, + ) + window.show() + qtbot.addWidget(window) + + assert window.isVisible() + + @pytest.mark.xfail( + reason="Unknown. See https://github.com/matplotlib/viscm/issues/71", + ) + def test_gui_edit_pyfile_opens(self, tests_data_dir: Path, qtbot): + """Reproduce viridis from README instructions. + + https://github.com/matplotlib/viscm/pull/58 + """ + window = _make_window( + action="edit", + cmap=str(tests_data_dir / "option_d.py"), + cmap_type="linear", + cmap_spline_method="Bezier", + cmap_uniform_space="buggy-CAM02-UCS", + save=None, + quit_immediately=False, + ) + window.show() + qtbot.addWidget(window) + + assert window.isVisible() diff --git a/viscm/cli.py b/viscm/cli.py index 3b349a1..6625693 100644 --- a/viscm/cli.py +++ b/viscm/cli.py @@ -1,4 +1,6 @@ import sys +from pathlib import Path +from typing import Union import matplotlib.pyplot as plt @@ -71,39 +73,79 @@ def cli(): "--save", metavar="FILE", default=None, - help="Immediately save visualization to a file " "(view-mode only).", + help="Immediately save visualization to a file (view-mode only).", ) parser.add_argument( "--quit", default=False, action="store_true", - help="Quit immediately after starting " "(useful with --save).", + help="Quit immediately after starting (useful with --save).", ) args = parser.parse_args(argv) - cm = gui.Colormap(args.type, args.method, args.uniform_space) app = gui.QtWidgets.QApplication([]) - if args.colormap: - cm.load(args.colormap) + try: + mainwindow = _make_window( + action=args.action, + cmap=args.colormap, + cmap_type=args.type, + cmap_spline_method=args.method, + cmap_uniform_space=args.uniform_space, + save=Path(args.save) if args.save else None, + quit_immediately=args.quit, + ) + except Exception as e: + sys.exit(str(e)) + + mainwindow.show() + + # PyQt messes up signal handling by default. Python signal handlers (e.g., + # the default handler for SIGINT that raises KeyboardInterrupt) can only + # run when we enter the Python interpreter, which doesn't happen while + # idling in the Qt mainloop. (Unless we register a timer to poll + # explicitly.) So here we unregister Python's default signal handler and + # replace it with... the *operating system's* default signal handler, so + # instead of a KeyboardInterrupt our process just exits. + import signal + + signal.signal(signal.SIGINT, signal.SIG_DFL) + + app.exec_() + + +def _make_window( + *, + action: str, + cmap: Union[str, None], + cmap_type: str, + cmap_spline_method: str, + cmap_uniform_space: str, + save: Union[Path, None], + quit_immediately: bool, +): + # Hold a reference so it doesn't get GC'ed + fig = plt.figure() + figure_canvas = gui.FigureCanvas(fig) + + cm = gui.Colormap(cmap_type, cmap_spline_method, cmap_uniform_space) + if cmap: + cm.load(cmap) # Easter egg! I keep typing 'show' instead of 'view' so accept both - if args.action in ("view", "show"): + if action in ("view", "show"): if cm is None: - sys.exit("Please specify a colormap") - fig = plt.figure() - figure_canvas = gui.FigureCanvas(fig) + raise RuntimeError("Please specify a colormap") + v = gui.viscm(cm.cmap, name=cm.name, figure=fig, uniform_space=cm.uniform_space) - mainwindow = gui.ViewerWindow(figure_canvas, v, cm.name) - if args.save is not None: + window = gui.ViewerWindow(figure_canvas, v, cm.name) + if save is not None: v.figure.set_size_inches(20, 12) - v.figure.savefig(args.save) - elif args.action == "edit": + v.figure.savefig(str(save)) + elif action == "edit": if not cm.can_edit: sys.exit("Sorry, I don't know how to edit the specified colormap") - # Hold a reference so it doesn't get GC'ed - fig = plt.figure() - figure_canvas = gui.FigureCanvas(fig) + v = gui.viscm_editor( figure=fig, uniform_space=cm.uniform_space, @@ -111,11 +153,13 @@ def cli(): method=cm.method, **cm.params, ) - mainwindow = gui.EditorWindow(figure_canvas, v) + window = gui.EditorWindow(figure_canvas, v) else: - raise RuntimeError("can't happen") + raise RuntimeError( + "Action must be 'edit', 'view', or 'show'. This should never happen.", + ) - if args.quit: + if quit_immediately: sys.exit() figure_canvas.setSizePolicy( @@ -123,21 +167,8 @@ def cli(): ) figure_canvas.updateGeometry() - mainwindow.resize(800, 600) - mainwindow.show() - - # PyQt messes up signal handling by default. Python signal handlers (e.g., - # the default handler for SIGINT that raises KeyboardInterrupt) can only - # run when we enter the Python interpreter, which doesn't happen while - # idling in the Qt mainloop. (Unless we register a timer to poll - # explicitly.) So here we unregister Python's default signal handler and - # replace it with... the *operating system's* default signal handler, so - # instead of a KeyboardInterrupt our process just exits. - import signal - - signal.signal(signal.SIGINT, signal.SIG_DFL) - - app.exec_() + window.resize(800, 600) + return window if __name__ == "__main__": diff --git a/viscm/gui.py b/viscm/gui.py index b3a1a2e..927943e 100644 --- a/viscm/gui.py +++ b/viscm/gui.py @@ -959,7 +959,7 @@ def loadpyfile(path): "__file__": os.path.basename(path), } - # FIXME: Should be `args.colormap` should be `path`? + # FIXME: `args.colormap` should be `path`? with open(args.colormap) as f: # noqa: F821 code = compile( f.read(), From 52758b93f25fa1de68642ff6eac182ee430c4c83 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Sun, 4 Jun 2023 18:14:18 -0600 Subject: [PATCH 2/3] Fix failing viscm editor test The old behavior was an implicit conversion to `int`. This warning was being thrown in Python 3.9: ``` DeprecationWarning: an integer is required (got type float). Implicit conversion to integers using __int__ is deprecated, and may be removed in a future version of Python. ``` And in Python 3.10, this turned in to an error, as seen in : ``` TypeError: setValue(self, a0: int): argument 1 has unexpected type 'float' ``` The new version of this code simply makes the old behavior (conversion to int) explicit. I'm unsure whether this was the original intent. --- .github/workflows/test.yml | 4 ++++ test/test_gui.py | 5 ----- viscm/gui.py | 8 ++++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9c7d542..4a37ef9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,3 +42,7 @@ jobs: - name: "Run tests" run: "make test" + env: + # In Pythons >= 3.10, tests fail with `RuntimeError: Invalid DISPLAY + # variable`, unless this variable is set: + MPLBACKEND: "Agg" diff --git a/test/test_gui.py b/test/test_gui.py index 6b06ee8..7a364f2 100644 --- a/test/test_gui.py +++ b/test/test_gui.py @@ -1,7 +1,5 @@ from pathlib import Path -import pytest - from viscm.cli import _make_window @@ -21,9 +19,6 @@ def test_gui_view_opens(self, qtbot): assert window.isVisible() - @pytest.mark.xfail( - reason="Unknown. See https://github.com/matplotlib/viscm/issues/71", - ) def test_gui_edit_pyfile_opens(self, tests_data_dir: Path, qtbot): """Reproduce viridis from README instructions. diff --git a/viscm/gui.py b/viscm/gui.py index 927943e..0fba1fa 100644 --- a/viscm/gui.py +++ b/viscm/gui.py @@ -1154,7 +1154,7 @@ def __init__(self, figurecanvas, viscm_editor, parent=None): self.max_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal) self.max_slider.setMinimum(0) self.max_slider.setMaximum(100) - self.max_slider.setValue(viscm_editor.max_Jp) + self.max_slider.setValue(int(viscm_editor.max_Jp)) self.max_slider.setTickPosition(QtWidgets.QSlider.TicksBelow) self.max_slider.setTickInterval(10) self.max_slider.valueChanged.connect(self.updatejp) @@ -1164,7 +1164,7 @@ def __init__(self, figurecanvas, viscm_editor, parent=None): self.min_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal) self.min_slider.setMinimum(0) self.min_slider.setMaximum(100) - self.min_slider.setValue(viscm_editor.min_Jp) + self.min_slider.setValue(int(viscm_editor.min_Jp)) self.min_slider.setTickPosition(QtWidgets.QSlider.TicksBelow) self.min_slider.setTickInterval(10) self.min_slider.valueChanged.connect(self.updatejp) @@ -1277,8 +1277,8 @@ def update_smoothness_slider(self): def swapjp(self): jp1, jp2 = self.min_slider.value(), self.max_slider.value() - self.min_slider.setValue(jp2) - self.max_slider.setValue(jp1) + self.min_slider.setValue(int(jp2)) + self.max_slider.setValue(int(jp1)) self.updatejp() def updatejp(self): From b86bf1af644b353e18bd813a66ae15f81beec675 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Mon, 5 Jun 2023 21:00:59 -0600 Subject: [PATCH 3/3] Skip GUI tests if xvfb not installed --- Makefile | 2 +- doc/contributing.md | 3 +-- test/test_gui.py | 9 +++++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 80f7393..b8cc123 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: test test: python -m pytest --version - python -m pytest --xvfb-backend=xvfb -v test/ + python -m pytest -v test/ .PHONY: lint diff --git a/doc/contributing.md b/doc/contributing.md index 95fe327..ad445ba 100644 --- a/doc/contributing.md +++ b/doc/contributing.md @@ -55,8 +55,7 @@ make test ``` Unit tests require `xvfb` (X Virtual Framebuffer) to test the GUI. If `xvfb` is not -installed, you'll receive `ERROR: xvfb backend xvfb requested but not installed`. -Install with: +installed, GUI tests will be skipped. Install with: ```bash sudo apt install xvfb diff --git a/test/test_gui.py b/test/test_gui.py index 7a364f2..6f1ca1c 100644 --- a/test/test_gui.py +++ b/test/test_gui.py @@ -1,8 +1,17 @@ from pathlib import Path +import pytest +import pytest_xvfb + from viscm.cli import _make_window +xvfb_installed = pytest_xvfb.xvfb_instance is not None + +@pytest.mark.skipif( + not xvfb_installed, + reason="Xvfb must be installed for this test.", +) class TestGui: def test_gui_view_opens(self, qtbot): window = _make_window(