diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 8e6f556..833d0b4 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -14,3 +14,4 @@ A short description of the changes in this PR. * [ ] `docker/service_version.txt` updated if publishing a release. * [ ] Tests added/updated and passing. * [ ] Documentation updated (if needed). +* [ ] Jira ticket updated with expected fixversion hoss-X.Y.Z diff --git a/CHANGELOG.md b/CHANGELOG.md index 8501142..03075cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## [unreleased] - 2025-10-20 + +### Changed + +- Updated tests to be less dependent on architecture when comparing floats. +- Fixed test that modified a source file fixture. +- Changed infrastructure so that local and Docker runs of the tests produce output in same locations. +- GitHub once again captures the artifacts from the tests and coverage. + + ## [v1.1.12] - 2025-10-15 ### Added diff --git a/README.md b/README.md index 84f0326..176450f 100644 --- a/README.md +++ b/README.md @@ -255,8 +255,8 @@ checking the repository for some coding standard best practices. These include: To enable these checks: ```bash -# Install pre-commit Python package as part of test requirements: -pip install -r tests/pip_test_requirements.txt +# Install pre-commit Python package: +pip install pre-commit # Install the git hook scripts: pre-commit install diff --git a/bin/run-test b/bin/run-test index 0c10269..b29b1f0 100755 --- a/bin/run-test +++ b/bin/run-test @@ -24,6 +24,6 @@ mkdir -p coverage # Run the tests in a Docker container with mounted volumes for XML report # output and test coverage reporting docker run --platform linux/amd64 --rm \ - -v $(pwd)/test-reports:/home/tests/reports \ - -v $(pwd)/coverage:/home/tests/coverage \ + -v $(pwd)/test-reports:/home/test-reports \ + -v $(pwd)/coverage:/home/coverage \ ghcr.io/nasa/harmony-opendap-subsetter-test "$@" diff --git a/tests/pip_test_requirements.txt b/tests/pip_test_requirements.txt index 1ad2d6b..1b5d68f 100644 --- a/tests/pip_test_requirements.txt +++ b/tests/pip_test_requirements.txt @@ -1,7 +1,5 @@ -coverage~=7.9.2 -pre-commit~=4.2.0 -pycodestyle~=2.14.0 -pylint ~= 3.3.7 -pytest~=8.3.3 -pytest-mock==3.14.0 -unittest-xml-reporting~=3.2.0 +pycodestyle ~= 2.14.0 +pylint ~= 3.3.9 +pytest ~= 8.3.3 +pytest-mock == 3.14.0 +pytest-cov == 7.0.0 diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 3a6c825..abc3f70 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash #################################### # # A script invoked by the test Dockerfile to run the Python `unittest` suite @@ -16,10 +16,10 @@ STATUS=0 export HDF5_DISABLE_VERSION_CHECK=1 -# Run all tests using pytest (will also run unittest tests), producing JUnit -# compatible output -echo "\nRunning tests..." -coverage run -m pytest tests/ --junitxml=tests/reports/pytest-results.xml -v +echo -e "\nRunning tests..." + +pytest ./tests -s --cov=hoss --junitxml=test-reports/pytest-results.xml --cov-report=html:coverage --cov-report term + RESULT=$? if [ "$RESULT" -ne "0" ]; then @@ -27,10 +27,7 @@ if [ "$RESULT" -ne "0" ]; then echo "ERROR: tests generated errors" fi -echo "\n" -echo "Test Coverage Estimates" -coverage report --omit="tests/*" -coverage html --omit="tests/*" -d /home/tests/coverage +echo -e "\n" # Run pylint # Ignored errors/warnings: diff --git a/tests/unit/test_coordinate_utilities.py b/tests/unit/test_coordinate_utilities.py index f651d74..df62051 100644 --- a/tests/unit/test_coordinate_utilities.py +++ b/tests/unit/test_coordinate_utilities.py @@ -1165,7 +1165,7 @@ def test_get_dimension_order_and_dim_values(self): self.lat_arr, self.lon_arr, row_indices, crs, is_row=True ) self.assertEqual(y_x_order, True) - self.assertListEqual(dim_values, expected_dim_values) + np.testing.assert_allclose(dim_values, expected_dim_values, rtol=1e-7) with self.subTest('Get y_x order when projected_dim is changing across column'): col_indices = [[0, 0], [0, 9]] expected_dim_values = [-17299990.048985746, 17213152.396759935] @@ -1173,7 +1173,7 @@ def test_get_dimension_order_and_dim_values(self): self.lat_arr, self.lon_arr, col_indices, crs, is_row=False ) self.assertEqual(y_x_order, True) - self.assertListEqual(dim_values, expected_dim_values) + np.testing.assert_allclose(dim_values, expected_dim_values, rtol=1e-7) with self.subTest('Get x_y order when projected_dim is changing across row'): row_indices = [[0, 0], [4, 0]] expected_dim_values = [-17299990.048985746, 17213152.396759935] @@ -1185,7 +1185,7 @@ def test_get_dimension_order_and_dim_values(self): is_row=True, ) self.assertEqual(y_x_order, False) - self.assertListEqual(dim_values, expected_dim_values) + np.testing.assert_allclose(dim_values, expected_dim_values, rtol=1e-7) with self.subTest('Get x_y order when projected_dim is changing across column'): col_indices = [[0, 0], [0, 9]] expected_dim_values = [7341677.255608977, -7341677.255608977] @@ -1197,7 +1197,7 @@ def test_get_dimension_order_and_dim_values(self): is_row=False, ) self.assertEqual(y_x_order, False) - self.assertListEqual(dim_values, expected_dim_values) + np.testing.assert_allclose(dim_values, expected_dim_values, rtol=1e-7) with self.subTest('Get y_x order when projected_dims are not varying'): lat_arr = np.array( [ @@ -1235,7 +1235,7 @@ def test_get_dimension_order_and_dim_values(self): self.lat_arr_3d, self.lon_arr_3d, row_indices, crs, is_row=True ) self.assertEqual(y_x_order, True) - self.assertListEqual(dim_values, expected_dim_values) + np.testing.assert_allclose(dim_values, expected_dim_values, rtol=1e-7) def test_create_dimension_arrays_from_coordinates( self, diff --git a/tests/unit/test_dimension_utilities.py b/tests/unit/test_dimension_utilities.py index 13e71b1..9a85f10 100644 --- a/tests/unit/test_dimension_utilities.py +++ b/tests/unit/test_dimension_utilities.py @@ -1,5 +1,6 @@ from logging import getLogger from os.path import exists +from pathlib import Path from shutil import copy, rmtree from tempfile import mkdtemp from unittest import TestCase @@ -677,8 +678,12 @@ def test_write_bounds(self): and in a nested group. """ + + test_filename = Path(self.temp_dir) / 'ATL16_prefetch_group.nc4' + copy('tests/data/ATL16_prefetch_group.nc4', test_filename) + varinfo_prefetch = VarInfoFromDmr('tests/data/ATL16_prefetch_group.dmr') - prefetch_dataset = Dataset('tests/data/ATL16_prefetch_group.nc4', 'r+') + prefetch_dataset = Dataset(test_filename, 'r+') # Expected variable contents in file. expected_bounds_data = self.bounds_array diff --git a/tests/unit/test_projection_utilities.py b/tests/unit/test_projection_utilities.py index 54652c1..2695b9d 100644 --- a/tests/unit/test_projection_utilities.py +++ b/tests/unit/test_projection_utilities.py @@ -44,6 +44,8 @@ is_projection_x_dimension, is_projection_y_dimension, ) +from tests import utilities +from tests.utilities import assert_float_dict_almost_equal class TestProjectionUtilities(TestCase): @@ -359,7 +361,8 @@ def test_get_projected_x_y_extents(self): } with self.subTest('Bounding box input'): - self.assertDictEqual( + + assert_float_dict_almost_equal( get_projected_x_y_extents( x_values, y_values, crs, bounding_box=bounding_box ), @@ -367,7 +370,7 @@ def test_get_projected_x_y_extents(self): ) with self.subTest('Shape file input'): - self.assertDictEqual( + assert_float_dict_almost_equal( get_projected_x_y_extents( x_values, y_values, crs, shape_file=polygon_path ), @@ -403,7 +406,7 @@ def test_get_projected_x_y_extents_whole_earth(self): 'y_max': 12702440.710450241, } with self.subTest('Whole Earth LAEA - Bounding box input'): - self.assertDictEqual( + assert_float_dict_almost_equal( get_projected_x_y_extents( x_values, y_values, crs, bounding_box=whole_earth_bbox ), @@ -411,7 +414,7 @@ def test_get_projected_x_y_extents_whole_earth(self): ) with self.subTest('Whole Earth LAEA - Shape file input'): - self.assertDictEqual( + assert_float_dict_almost_equal( get_projected_x_y_extents( x_values, y_values, crs, shape_file=polygon_path ), @@ -1160,7 +1163,7 @@ def test_get_x_y_extents_from_geographic_points(self): 'y_max': 1670250.0136418417, } - self.assertDictEqual( + assert_float_dict_almost_equal( get_x_y_extents_from_geographic_points(points, crs), expected_x_y_extents ) diff --git a/tests/utilities.py b/tests/utilities.py index ffe1acc..12802ed 100644 --- a/tests/utilities.py +++ b/tests/utilities.py @@ -5,6 +5,7 @@ from typing import List from unittest.mock import MagicMock +import numpy as np from harmony_service_lib.util import bbox_to_geometry from pystac import Asset, Catalog, Item @@ -93,3 +94,22 @@ def create_stac(granules: List[Granule]) -> Catalog: catalog.add_item(item) return catalog + + +def assert_float_dict_almost_equal(dict1, dict2, decimal=8): + """Assert two dicts are almost equal. + + Two dictionaries that have only floating point values are compared for + almost equivalence. + + """ + assert ( + dict1.keys() == dict2.keys() + ), f"Keys mismatch: {dict1.keys()} != {dict2.keys()}" + for key in dict1: + np.testing.assert_almost_equal( + dict1[key], + dict2[key], + decimal=decimal, + err_msg=f'Mismatch in {key}: {dict1[key]} != {dict2[key]}', + )