Skip to content

Commit 3ebedcd

Browse files
authored
Replace figure infra (#216)
1 parent a316da9 commit 3ebedcd

19 files changed

Lines changed: 313 additions & 154 deletions

.circleci/codecov_upload.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/bin/bash -e
2+
curl https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --no-default-keyring --keyring trustedkeys.gpg --import
3+
curl -Os https://uploader.codecov.io/latest/linux/codecov
4+
curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM
5+
curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM.sig
6+
gpgv codecov.SHA256SUM.sig codecov.SHA256SUM
7+
shasum -a 256 -c codecov.SHA256SUM
8+
chmod +x codecov
9+
./codecov "$@"

.circleci/config.yml

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
version: 2.1
2+
3+
no-backports: &no-backports
4+
name: Skip any branches called cherry-pick
5+
command: |
6+
if [[ "${CIRCLE_BRANCH}" == *"cherry-pick"* || "${CIRCLE_BRANCH}" == *"backport"* ]]; then
7+
circleci step halt
8+
fi
9+
10+
skip-check: &skip-check
11+
name: Check for [ci skip]
12+
command: bash .circleci/early_exit.sh
13+
14+
merge-check: &merge-check
15+
name: Check if we need to merge upstream main
16+
command: |
17+
if [[ -n "${CIRCLE_PR_NUMBER}" ]]; then
18+
git fetch origin --tags
19+
git fetch origin +refs/pull/$CIRCLE_PR_NUMBER/merge:pr/$CIRCLE_PR_NUMBER/merge
20+
git checkout -qf pr/$CIRCLE_PR_NUMBER/merge
21+
fi
22+
23+
apt-run: &apt-install
24+
name: Install apt packages
25+
command: |
26+
sudo apt update
27+
sudo apt install -y xvfb libglfw3-dev libgles2-mesa-dev libegl1-mesa-dev mesa-utils
28+
29+
jobs:
30+
figure:
31+
parameters:
32+
jobname:
33+
type: string
34+
docker:
35+
- image: cimg/python:3.13
36+
environment:
37+
TOXENV=<< parameters.jobname >>
38+
steps:
39+
- run: *no-backports
40+
- checkout
41+
- run: *skip-check
42+
- run: *merge-check
43+
- run: *apt-install
44+
- run: pip install --user -U tox tox-pypi-filter
45+
- run:
46+
name: Running X virtual framebuffer
47+
command: Xvfb :99 -screen 0 1280x1024x24
48+
background: true
49+
- run: xvfb-run -a tox -v
50+
- run:
51+
name: Running codecov
52+
command: bash -e .circleci/codecov_upload.sh -f ".tmp/${TOXENV}/coverage.xml"
53+
- store_artifacts:
54+
path: .tmp/<< parameters.jobname >>/figure_test_images
55+
- run:
56+
name: "Image comparison page is available at: "
57+
command: echo "${CIRCLE_BUILD_URL}/artifacts/${CIRCLE_NODE_INDEX}/.tmp/${TOXENV}/figure_test_images/fig_comparison.html"
58+
59+
deploy-reference-images:
60+
parameters:
61+
jobname:
62+
type: string
63+
docker:
64+
- image: cimg/python:3.13
65+
environment:
66+
TOXENV: << parameters.jobname >>
67+
GIT_SSH_COMMAND: ssh -i ~/.ssh/id_rsa_7b8fc81c13a3b446ec9aa50d3f626978
68+
steps:
69+
- checkout
70+
- run: *skip-check
71+
- run: *merge-check
72+
- run: *apt-install
73+
# Clear out all the ssh keys so that it always uses the write deploy key
74+
- run: ssh-add -D
75+
# Add private key for deploying to the figure tests repo
76+
- add_ssh_keys:
77+
fingerprints: "7b:8f:c8:1c:13:a3:b4:46:ec:9a:a5:0d:3f:62:69:78"
78+
- run: ssh-keyscan github.com >> ~/.ssh/known_hosts
79+
- run: git config --global user.email "sunpy@circleci" && git config --global user.name "SunPy Circle CI"
80+
- run: git clone git@github.com:sunpy/sunpy-figure-tests.git --depth 1 -b sunkit-pyvista-${CIRCLE_BRANCH} ~/sunpy-figure-tests/
81+
# Generate Reference images
82+
- run: pip install --user -U tox tox-pypi-filter
83+
- run: rm -rf /home/circleci/sunpy-figure-tests/figures/$TOXENV/*
84+
- run: tox -v -- --mpl-generate-path=/home/circleci/sunpy-figure-tests/figures/$TOXENV | tee toxlog
85+
- run: |
86+
hashlib=$(grep "^figure_hashes.*\.json$" toxlog)
87+
cp ./sunpy/tests/$hashlib /home/circleci/sunpy-figure-tests/figures/$TOXENV/
88+
- run: |
89+
cd ~/sunpy-figure-tests/
90+
git pull
91+
git status
92+
git add .
93+
git commit -m "Update reference figures from ${CIRCLE_BRANCH}" || echo "No changes to reference images to deploy"
94+
git push
95+
96+
workflows:
97+
98+
figure-tests:
99+
jobs:
100+
- figure:
101+
name: << matrix.jobname >>
102+
matrix:
103+
parameters:
104+
jobname:
105+
- "py313-figure"
106+
107+
- deploy-reference-images:
108+
name: baseline-<< matrix.jobname >>
109+
matrix:
110+
parameters:
111+
jobname:
112+
- "py313-figure"
113+
requires:
114+
- << matrix.jobname >>
115+
filters:
116+
branches:
117+
only:
118+
- main

.circleci/early_exit.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/bin/bash
2+
3+
commitmessage=$(git log --pretty=%B -n 1)
4+
if [[ $commitmessage = *"[ci skip]"* ]] || [[ $commitmessage = *"[skip ci]"* ]]; then
5+
echo "Skipping build because [ci skip] found in commit message"
6+
circleci step halt
7+
fi

.github/workflows/ci.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,8 @@ jobs:
7171
- libglfw3-dev
7272
- libgles2-mesa-dev
7373
envs: |
74-
- linux: py314
75-
- windows: py312
76-
- macos: py312
74+
- windows: py313
75+
- macos: py313
7776
- linux: py312-oldestdeps
7877
- linux: py314-devdeps
7978
secrets:

conftest.py

Lines changed: 0 additions & 95 deletions
This file was deleted.

pyproject.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ all = ["sunkit_pyvista"]
3838
tests = [
3939
"pytest",
4040
"pytest-astropy",
41+
"pytest-mpl",
4142
"scikit-image",
4243
"sunkit-magex>=1.1.0",
4344
"sunkit_pyvista[all]",
@@ -74,6 +75,19 @@ exclude = ["sunkit_pyvista._dev*"]
7475
version_file = "sunkit_pyvista/_version.py"
7576

7677
[tool.gilesbot]
78+
[tool.gilesbot.circleci_artifacts]
79+
enabled = true
80+
81+
[tool.gilesbot.circleci_artifacts.figure_report]
82+
url = ".tmp/py312-figure/figure_test_images/fig_comparison.html"
83+
message = "Click details to see the figure test comparisons, for py312-figure."
84+
report_on_fail = true
85+
86+
[tool.gilesbot.circleci_artifacts.figure_report_devdeps]
87+
url = ".tmp/py312-figure-devdeps/figure_test_images/fig_comparison.html"
88+
message = "Click details to see the figure test comparisons for py312-figure-devdeps."
89+
report_on_fail = true
90+
7791
[tool.gilesbot.pull_requests]
7892
enabled = true
7993

pytest.ini

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ addopts =
2424
--doctest-ignore-import-errors
2525
-p no:unraisableexception
2626
-p no:threadexception
27+
-m "not mpl_image_compare"
28+
mpl-results-path = figure_test_images
29+
mpl-use-full-test-name = true
2730
markers =
31+
mpl_image_compare: marks this test function as using hash-based Matplotlib figure verification. This mark is not meant to be directly applied, but is instead automatically applied when a test function uses the @sunkit_pyvista.tests.helpers.figure_test decorator.
2832
display_server: marks this test function as needing a display server.
2933
filterwarnings =
3034
# Turn all warnings into errors so they do not pass silently.

sunkit_pyvista/plotter.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,32 @@
2020

2121
from sunkit_pyvista.utils import get_limb_coordinates
2222

23-
__all__ = ["SunpyPlotter", "CartesianPlotter"]
23+
__all__ = ["SaveMixIn", "SunpyPlotter", "CartesianPlotter"]
2424

2525

26-
class SunpyPlotter(pv.Plotter):
26+
class SaveMixIn:
27+
"""
28+
A mixin class to add savefig used for pytest-mpl figure comparison testing.
29+
"""
30+
31+
def savefig(self, filename, **kwargs):
32+
"""
33+
Save a screenshot of the current figure.
34+
35+
This is a wrapper around `pyvista.Plotter.screenshot` and was written
36+
so we use pytest-mpl's figure comparison testing.
37+
38+
Parameters
39+
----------
40+
filename : str
41+
The name of the file to save the screenshot to.
42+
kwargs : dict
43+
Keyword arguments are ignored.
44+
"""
45+
self.screenshot(filename)
46+
47+
48+
class SunpyPlotter(SaveMixIn, pv.Plotter):
2749
"""
2850
A plotter for 3D data.
2951
@@ -614,7 +636,7 @@ def plot_limb(self, m, *, radius=0.02, **kwargs):
614636
self._add_mesh_to_dict(block_name="limbs", mesh=limb_block)
615637

616638

617-
class CartesianPlotter(pv.Plotter):
639+
class CartesianPlotter(SaveMixIn, pv.Plotter):
618640
"""
619641
A plotter for 3D data in a Cartesian box.
620642

sunkit_pyvista/tests/conftest.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
1+
import logging
2+
13
import pytest
4+
import pyvista
5+
import pyvista as pv
26

37
import sunpy.data.test as test
48
import sunpy.map as smap
59

610
from sunkit_pyvista import CartesianPlotter, SunpyPlotter
711

12+
pv.OFF_SCREEN = True
13+
14+
try:
15+
pyvista.start_xvfb()
16+
except Exception as e: # NOQA: BLE001
17+
msg = f"Could not start xvfb server:\n{e}"
18+
logging.info(msg)
19+
820

921
@pytest.fixture()
1022
def aia171_test_map():
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"sunkit_pyvista.tests.test_magex.test_field_lines_figure": "b988779ed9e44ab423cfa14406b8cc1bf97b66baa0518e1d6ab7046ecf01a20d",
3+
"sunkit_pyvista.tests.test_magex.test_field_lines_and_color_func": "f9ef4edf7dbc0fa549426aa2546dcbacb3787431ee7d90bd74991c0f9b92e4c0",
4+
"sunkit_pyvista.tests.test_magex.test_current_sheet_figure": "45a9baad2b4dc2388795519561ad50d7aca64a4043a1e9b46b3f5fb673d5c018",
5+
"sunkit_pyvista.tests.test_plotting.test_plot_map_with_functionality": "c4e366bb35e1563d26b3447f5312e5c5bad46d63160d0412306f86b9d1ceef85",
6+
"sunkit_pyvista.tests.test_plotting.test_cartesian_plotter": "c2cd087ffeab32e0977795bf4295c03eb16dd3a4d92726718d56fbf9e40c9209"
7+
}

0 commit comments

Comments
 (0)