Skip to content

Commit ba384ae

Browse files
committed
TST: migrate from nosetest to pytest
1 parent a99296e commit ba384ae

File tree

11 files changed

+283
-383
lines changed

11 files changed

+283
-383
lines changed

.circleci/config.yml

Lines changed: 6 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,17 @@ commands:
77
- run:
88
name: "Set environment variables."
99
command: |
10-
echo 'export GOLD_STANDARD=HEAD' >> $BASH_ENV
1110
echo 'export ROCKSTAR_DIR=$HOME/rockstar-galaxies' >> $BASH_ENV
1211
echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ROCKSTAR_DIR' >> $BASH_ENV
1312
echo 'export YT_DATA=$HOME/yt_test' >> $BASH_ENV
14-
echo 'export TEST_DIR=$HOME/test_results' >> $BASH_ENV
15-
echo 'export TEST_NAME=astro_analysis' >> $BASH_ENV
16-
echo 'export TEST_FLAGS="--nologcapture -v --with-answer-testing --local --local-dir $TEST_DIR --answer-name=$TEST_NAME --answer-big-data"' >> $BASH_ENV
1713
1814
install-with-yt-dev:
1915
description: "Install dependencies with yt from source."
2016
steps:
2117
- run:
2218
name: "Install dependencies with yt from source."
2319
command: |
20+
set -x
2421
source $BASH_ENV
2522
sudo apt update
2623
sudo apt upgrade
@@ -75,30 +72,7 @@ commands:
7572
girder-cli --api-url https://girder.hub.yt/api/v1 download 577c09480d7c6b0001ad5be2 $YT_DATA/enzo_tiny_cosmology
7673
fi
7774
78-
build-and-test-nose:
79-
description: "Build yt_astro_analysis and run tests (nose)."
80-
steps:
81-
- run:
82-
name: "Build yt_astro_analysis and run tests."
83-
command: |
84-
# tag the tip so we can get back there
85-
git tag tip
86-
source $BASH_ENV
87-
source $HOME/venv/bin/activate
88-
# generate answers if not cached
89-
if [ ! -f ${TEST_DIR}/${TEST_NAME}/${TEST_NAME} ]; then
90-
git checkout $GOLD_STANDARD
91-
python -m pip install -e .
92-
nosetests $TEST_FLAGS --answer-store
93-
fi
94-
# return to tip and run comparison tests
95-
git checkout tip
96-
python -m pip install -e .
97-
coverage run `which nosetests` $TEST_FLAGS
98-
# code coverage report
99-
codecov
100-
101-
build-and-test-pytest:
75+
build-and-test:
10276
description: "Build yt_astro_analysis and run tests (pytest)."
10377
steps:
10478
- run:
@@ -107,7 +81,7 @@ commands:
10781
source $BASH_ENV
10882
source $HOME/venv/bin/activate
10983
python -m pip install -e .
110-
pytest
84+
pytest --color=yes -ra -s
11185
11286
build-docs:
11387
description: "Test the docs build."
@@ -142,62 +116,6 @@ jobs:
142116

143117
working_directory: ~/yt_astro_analysis
144118

145-
steps:
146-
- checkout
147-
- set-env
148-
149-
- restore_cache:
150-
name: "Restore dependencies cache."
151-
key: python-<< parameters.tag >>-dependencies-bonxie
152-
153-
- install-with-yt-dev
154-
155-
- save_cache:
156-
name: "Save dependencies cache"
157-
key: python-<< parameters.tag >>-dependencies-bonxie
158-
paths:
159-
- ~/.cache/pip
160-
- ~/venv
161-
- ~/yt-git
162-
- ~/rockstar-galaxies
163-
164-
- restore_cache:
165-
name: "Restore test data cache."
166-
key: test-data-bonxie
167-
168-
- download-test-data
169-
170-
- save_cache:
171-
name: "Save test data cache."
172-
key: test-data-bonxie
173-
paths:
174-
- ~/yt_test
175-
176-
- restore_cache:
177-
name: "Restore test answers."
178-
key: python-<< parameters.tag >>-test-answers-bonxie
179-
180-
- build-and-test-nose
181-
182-
- save_cache:
183-
name: "Save test answers cache."
184-
key: python-<< parameters.tag >>-test-answers-bonxie
185-
paths:
186-
- ~/test_results
187-
188-
run-tests-pytest:
189-
# Run a subset of the test suite (yield-based tests are not supported by pytest)
190-
# this is necessary to test in Python 3.10+ because it's not compatible with nose
191-
parameters:
192-
tag:
193-
type: string
194-
default: latest
195-
executor:
196-
name: python
197-
tag: << parameters.tag >>
198-
199-
working_directory: ~/yt_astro_analysis
200-
201119
steps:
202120
- checkout
203121
- set-env
@@ -233,7 +151,7 @@ jobs:
233151
name: "Restore test answers."
234152
key: python-<< parameters.tag >>-test-answers-bonxie-pytest
235153

236-
- build-and-test-pytest
154+
- build-and-test
237155

238156
- save_cache:
239157
name: "Save test answers cache."
@@ -282,11 +200,7 @@ workflows:
282200
name: "Python 3.9 tests"
283201
tag: "3.9"
284202

285-
- run-tests-pytest:
286-
name: "Python 3.10 tests"
287-
tag: "3.10"
288-
289-
- run-tests-pytest:
203+
- run-tests:
290204
name: "Python 3.11 tests"
291205
tag: "3.11"
292206

@@ -307,7 +221,7 @@ workflows:
307221
name: "Python 3.9 tests"
308222
tag: "3.9"
309223

310-
- run-tests-pytest:
224+
- run-tests:
311225
name: "Python 3.11 tests"
312226
tag: "3.11"
313227

nose.cfg

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

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ select = [
9797
combine-as-imports = true
9898
known-third-party = [
9999
"IPython",
100-
"nose",
101100
"numpy",
102101
"sympy",
103102
"matplotlib",

requirements/tests.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,5 @@ astropy
33
scipy
44

55
# test dependencies
6-
nose
7-
nose-timer
86
pytest
97
girder-client
10-
codecov

yt_astro_analysis/cosmological_observation/light_cone/tests/test_light_cone.py

Lines changed: 61 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -14,96 +14,79 @@
1414
# -----------------------------------------------------------------------------
1515

1616
import os
17-
import shutil
18-
import tempfile
1917

18+
import h5py
2019
import numpy as np
20+
import numpy.testing as npt
21+
import pytest
22+
import unyt as un
2123

22-
from yt.testing import assert_equal
23-
from yt.units.yt_array import YTQuantity
24-
from yt.utilities.answer_testing.framework import AnswerTestingTest
25-
from yt.utilities.on_demand_imports import _h5py as h5py
24+
import yt # noqa
25+
from yt.testing import requires_file
2626
from yt_astro_analysis.cosmological_observation.api import LightCone
27-
from yt_astro_analysis.utilities.testing import requires_sim
2827

2928
ETC = "enzo_tiny_cosmology/32Mpc_32.enzo"
3029
_funits = {
31-
"density": YTQuantity(1, "g/cm**3"),
32-
"temperature": YTQuantity(1, "K"),
33-
"length": YTQuantity(1, "cm"),
30+
"density": un.unyt_quantity(1, "g/cm**3"),
31+
"temperature": un.unyt_quantity(1, "K"),
32+
"length": un.unyt_quantity(1, "cm"),
3433
}
3534

3635

37-
class LightConeProjectionTest(AnswerTestingTest):
38-
_type_name = "LightConeProjection"
39-
_attrs = ()
40-
41-
def __init__(self, parameter_file, simulation_type, field, weight_field=None):
42-
self.parameter_file = parameter_file
43-
self.simulation_type = simulation_type
44-
self.ds = os.path.basename(self.parameter_file)
45-
self.field = field
46-
self.weight_field = weight_field
47-
48-
@property
49-
def storage_name(self):
50-
return "_".join(
51-
(os.path.basename(self.parameter_file), self.field, str(self.weight_field))
52-
)
53-
54-
def run(self):
55-
# Set up in a temp dir
56-
tmpdir = tempfile.mkdtemp()
57-
curdir = os.getcwd()
58-
os.chdir(tmpdir)
59-
60-
lc = LightCone(
61-
self.parameter_file,
62-
self.simulation_type,
63-
0.0,
64-
0.1,
65-
observer_redshift=0.0,
66-
time_data=False,
67-
)
68-
lc.calculate_light_cone_solution(seed=123456789, filename="LC/solution.txt")
69-
lc.project_light_cone(
70-
(600.0, "arcmin"),
71-
(60.0, "arcsec"),
72-
self.field,
73-
weight_field=self.weight_field,
74-
save_stack=True,
75-
)
76-
77-
dname = f"{self.field}_{self.weight_field}"
78-
fh = h5py.File("LC/LightCone.h5", mode="r")
36+
@requires_file(ETC)
37+
@pytest.mark.parametrize(
38+
"field, weight_field, expected",
39+
[
40+
(
41+
"density",
42+
None,
43+
[6.0000463633868075e-05, 1.1336502301470154e-05, 0.08970763360935877],
44+
),
45+
(
46+
"temperature",
47+
"density",
48+
[37.79481498628398, 0.018410545597485613, 543702.4613479003],
49+
),
50+
],
51+
)
52+
def test_light_cone_projection(tmp_path, field, weight_field, expected):
53+
parameter_file = ETC
54+
simulation_type = "Enzo"
55+
field = field
56+
weight_field = weight_field
57+
58+
os.chdir(tmp_path)
59+
lc = LightCone(
60+
parameter_file,
61+
simulation_type,
62+
near_redshift=0.0,
63+
far_redshift=0.1,
64+
observer_redshift=0.0,
65+
time_data=False,
66+
)
67+
lc.calculate_light_cone_solution(seed=123456789, filename="LC/solution.txt")
68+
lc.project_light_cone(
69+
(600.0, "arcmin"),
70+
(60.0, "arcsec"),
71+
field,
72+
weight_field=weight_field,
73+
save_stack=True,
74+
)
75+
76+
dname = f"{field}_{weight_field}"
77+
with h5py.File("LC/LightCone.h5", mode="r") as fh:
7978
data = fh[dname][()]
8079
units = fh[dname].attrs["units"]
81-
if self.weight_field is None:
82-
punits = _funits[self.field] * _funits["length"]
80+
if weight_field is None:
81+
punits = _funits[field] * _funits["length"]
8382
else:
84-
punits = (
85-
_funits[self.field] * _funits[self.weight_field] * _funits["length"]
86-
)
87-
wunits = fh["weight_field_%s" % self.weight_field].attrs["units"]
88-
pwunits = _funits[self.weight_field] * _funits["length"]
83+
punits = _funits[field] * _funits[weight_field] * _funits["length"]
84+
wunits = fh[f"weight_field_{weight_field}"].attrs["units"]
85+
pwunits = _funits[weight_field] * _funits["length"]
8986
assert wunits == str(pwunits.units)
90-
assert units == str(punits.units)
91-
fh.close()
92-
93-
# clean up
94-
os.chdir(curdir)
95-
shutil.rmtree(tmpdir)
96-
97-
mean = data.mean()
98-
mi = data[data.nonzero()].min()
99-
ma = data.max()
100-
return np.array([mean, mi, ma])
101-
102-
def compare(self, new_result, old_result):
103-
assert_equal(new_result, old_result, verbose=True)
104-
87+
assert units == str(punits.units)
10588

106-
@requires_sim(ETC, "Enzo")
107-
def test_light_cone_projection():
108-
yield LightConeProjectionTest(ETC, "Enzo", "density")
109-
yield LightConeProjectionTest(ETC, "Enzo", "temperature", weight_field="density")
89+
mean = np.nanmean(data)
90+
mi = np.nanmin(data[data.nonzero()])
91+
ma = np.nanmax(data)
92+
npt.assert_equal([mean, mi, ma], expected, verbose=True)

yt_astro_analysis/halo_analysis/halo_catalog/analysis_operators.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ def add_quantity(name, function):
6363
quantity_registry[name] = AnalysisQuantity(function)
6464

6565

66+
def _remove_quantity(name):
67+
# this is useful to avoid test pollution when using add_quantity in tests
68+
# but it's not meant as public API
69+
quantity_registry.pop(name)
70+
71+
6672
class AnalysisQuantity(AnalysisCallback):
6773
r"""
6874
An AnalysisQuantity is a function that takes minimally a target object,

0 commit comments

Comments
 (0)