diff --git a/.github/workflows/run_lib_tests.yml b/.github/workflows/run_lib_tests.yml index 74ac0d8..a8faf53 100644 --- a/.github/workflows/run_lib_tests.yml +++ b/.github/workflows/run_lib_tests.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.10', '3.11'] + python-version: ['3.10', '3.11', '3.12'] steps: - name: Checkout harmony-browse-image-generator repository diff --git a/.github/workflows/run_service_tests.yml b/.github/workflows/run_service_tests.yml index 1568825..bdb1eeb 100644 --- a/.github/workflows/run_service_tests.yml +++ b/.github/workflows/run_service_tests.yml @@ -29,14 +29,8 @@ jobs: - name: Run test image run: ./bin/run-test - - name: Archive test results + - name: Archive test results and coverage uses: actions/upload-artifact@v4 with: - name: Test results - path: test-reports/ - - - name: Archive coverage report - uses: actions/upload-artifact@v4 - with: - name: Coverage report - path: coverage/* + name: reports + path: reports/**/* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 598b84a..63ece25 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,13 +10,10 @@ repos: - id: check-yaml - id: check-added-large-files - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.1 + rev: v0.8.2 hooks: - id: ruff - args: ["--fix", "--show-fixes", "--extend-select", "I"] - - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.10.0 - hooks: - - id: black-jupyter - args: ["--skip-string-normalization"] - language_version: python3.11 + args: ["--fix", "--show-fixes"] + types_or: [python, jupyter] + - id: ruff-format + types_or: [python, jupyter] diff --git a/CHANGELOG.md b/CHANGELOG.md index 1768fc9..53a76e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,19 @@ HyBIG follows semantic versioning. All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). +## [unreleased] - 2024-12-10 + +### Changed + +* Changed pre-commit configuration to remove `black-jupyter` dependency [#38](https://github.com/nasa/harmony-browse-image-generator/pull/38) +* Updates service image's python to 3.12 [#38](https://github.com/nasa/harmony-browse-image-generator/pull/38) +* Simplifies test scripts to run with pytest and pytest plugins [#38](https://github.com/nasa/harmony-browse-image-generator/pull/38) + +### Removed + +* Removes `test_code_format.py` in favor of `ruff` pre-commit configuration [#38](https://github.com/nasa/harmony-browse-image-generator/pull/38) + + ## [v2.0.2] - 2024-10-15 ### Fixed diff --git a/bin/run-test b/bin/run-test index b9df28d..aaf81c2 100755 --- a/bin/run-test +++ b/bin/run-test @@ -18,14 +18,14 @@ set -ex find . | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm -rf # Make the directory into which XML format test reports will be saved -mkdir -p test-reports +mkdir -p reports/test-reports # Make the directory into which coverage reports will be saved -mkdir -p coverage +mkdir -p reports/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)/reports/test-reports:/home/reports/test-reports \ + -v $(pwd)/reports/coverage:/home/reports/coverage \ ghcr.io/nasa/harmony-browse-image-generator-test "$@" diff --git a/docker/service.Dockerfile b/docker/service.Dockerfile index 660c591..5de1ecf 100644 --- a/docker/service.Dockerfile +++ b/docker/service.Dockerfile @@ -15,7 +15,7 @@ # and updates the entrypoint of the new service container # ############################################################################### -FROM python:3.11 +FROM python:3.12 WORKDIR "/home" diff --git a/docs/HyBIG-Example-Usage.ipynb b/docs/HyBIG-Example-Usage.ipynb index 1fc04d9..7514229 100644 --- a/docs/HyBIG-Example-Usage.ipynb +++ b/docs/HyBIG-Example-Usage.ipynb @@ -230,7 +230,7 @@ }, "outputs": [], "source": [ - "# if gdalinfo is installed you can see the palette associated with the PNG image.\n", + "# if gdalinfo is installed view the palette associated with the PNG image.\n", "!gdalinfo hybig-output/example1/VCF5KYR_1991001_001_2018224205008.png" ] }, @@ -318,7 +318,7 @@ "metadata": {}, "outputs": [], "source": [ - "# If gdal is installed this will show you the corner points associated with the output files.\n", + "# If gdal is installed, show the corner points associated with the output files.\n", "!gdalinfo hybig-output/example2/ASTGTMV003_N00E022_dem.jpg | grep -E \"Left|Right\"" ] }, @@ -690,7 +690,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.5" + "version": "3.12.6" }, "name": "HyBIG-Example-Usage.ipynb" }, diff --git a/harmony_service/adapter.py b/harmony_service/adapter.py index 89b66f4..3dce207 100644 --- a/harmony_service/adapter.py +++ b/harmony_service/adapter.py @@ -7,10 +7,8 @@ """ -from os.path import basename from pathlib import Path -from shutil import rmtree -from tempfile import mkdtemp +from tempfile import TemporaryDirectory from harmony_service_lib import BaseHarmonyAdapter from harmony_service_lib.message import Source as HarmonySource @@ -73,7 +71,8 @@ def get_asset_from_item(self, item: Item) -> Asset: This is used to select which asset is used by HyBIG to generate the browse image following these steps: - 1. If found, return the first asset with 'visual' in any of the item's values' roles. + 1. If found, return the first asset with 'visual' in any of the item's + values' roles. 2. If found, return the first asset that has 'data' in its item's values' roles. 3. Raise a StopIteration error. @@ -93,61 +92,59 @@ def get_asset_from_item(self, item: Item) -> Asset: def process_item(self, item: Item, source: HarmonySource) -> Item: """Processes a single input STAC item.""" - try: - working_directory = mkdtemp() - results = item.clone() - results.assets = {} - - asset = self.get_asset_from_item(item) - - color_palette = get_color_palette_from_item(item) - - # Download the input: - input_data_filename = download( - asset.href, - working_directory, - logger=self.logger, - cfg=self.config, - access_token=self.message.accessToken, - ) + with TemporaryDirectory() as working_directory: + try: + results = item.clone() + results.assets = {} + + asset = self.get_asset_from_item(item) + + color_palette = get_color_palette_from_item(item) + + # Download the input: + input_data_filename = download( + asset.href, + working_directory, + logger=self.logger, + cfg=self.config, + access_token=self.message.accessToken, + ) - # Create browse images. - image_file_list = create_browse_imagery( - self.message, - input_data_filename, - source, - color_palette, - logger=self.logger, - ) + # Create browse images. + image_file_list = create_browse_imagery( + self.message, + input_data_filename, + source, + color_palette, + logger=self.logger, + ) - # image_file_list is a list of tuples (image, world, auxiliary) - # we need to stage them each individually, and then add their final - # locations to a list before creating the stac item. - item_assets = [] - - for ( - browse_image_name, - world_file_name, - aux_xml_file_name, - ) in image_file_list: - # Stage the images: - browse_image_url = self.stage_output(browse_image_name, asset.href) - browse_aux_url = self.stage_output(aux_xml_file_name, asset.href) - world_file_url = self.stage_output(world_file_name, asset.href) - item_assets.append(('data', browse_image_url, 'data')) - item_assets.append(('metadata', world_file_url, 'metadata')) - item_assets.append(('auxiliary', browse_aux_url, 'metadata')) - - manifest_url = self.stage_manifest(image_file_list, asset.href) - item_assets.insert(0, ('data', manifest_url, 'metadata')) - - return self.create_output_stac_item(item, item_assets) - - except Exception as exception: - self.logger.exception(exception) - raise HyBIGServiceError from exception - finally: - rmtree(working_directory) + # image_file_list is a list of tuples (image, world, auxiliary) + # we need to stage them each individually, and then add their final + # locations to a list before creating the stac item. + item_assets = [] + + for ( + browse_image_name, + world_file_name, + aux_xml_file_name, + ) in image_file_list: + # Stage the images: + browse_image_url = self.stage_output(browse_image_name, asset.href) + browse_aux_url = self.stage_output(aux_xml_file_name, asset.href) + world_file_url = self.stage_output(world_file_name, asset.href) + item_assets.append(('data', browse_image_url, 'data')) + item_assets.append(('metadata', world_file_url, 'metadata')) + item_assets.append(('auxiliary', browse_aux_url, 'metadata')) + + manifest_url = self.stage_manifest(image_file_list, asset.href) + item_assets.insert(0, ('data', manifest_url, 'metadata')) + + return self.create_output_stac_item(item, item_assets) + + except Exception as exception: + self.logger.exception(exception) + raise HyBIGServiceError(str(exception)) from exception def stage_output(self, transformed_file: Path, input_file: str) -> str: """Generate an output file name based on the input asset URL and the @@ -174,7 +171,7 @@ def create_output_stac_item( """Create an output STAC item used to access the browse imagery and ESRI world file as staged in S3. - asset_items are an array of tuples where the tuples should be: (name, + item_assets is an array of tuples where the tuples should be: (name, url, role) """ @@ -191,7 +188,7 @@ def create_output_stac_item( output_stac_item.assets[asset_name] = Asset( url, - title=basename(url), + title=Path(url).name, media_type=get_file_mime_type(url), roles=[role], ) diff --git a/harmony_service/utilities.py b/harmony_service/utilities.py index cb7e49f..f59d6ed 100644 --- a/harmony_service/utilities.py +++ b/harmony_service/utilities.py @@ -21,7 +21,7 @@ def get_tiled_file_extension(file_name: Path) -> str: generate the correct one to pass in. """ - ext_pattern = r"(\.r\d+c\d+)?\.(png|jpg|pgw|jgw|txt)(.aux.xml)?" + ext_pattern = r'(\.r\d+c\d+)?\.(png|jpg|pgw|jgw|txt)(.aux.xml)?' match = re.search(ext_pattern, file_name.name) return match.group() @@ -34,7 +34,7 @@ def get_asset_name(name: str, url: str) -> str: dictionary. """ - tiled_pattern = r"\.(r\d+c\d+)\." + tiled_pattern = r'\.(r\d+c\d+)\.' tile_id = re.search(tiled_pattern, url) if tile_id is not None: name = f'{name}_{tile_id.groups()[0]}' diff --git a/hybig/browse_utility.py b/hybig/browse_utility.py index 67578f6..b55577c 100644 --- a/hybig/browse_utility.py +++ b/hybig/browse_utility.py @@ -21,14 +21,14 @@ def get_harmony_message_from_params(params: dict | None) -> HarmonyMessage: return HarmonyMessage( { - "format": { - "mime": mime, - "crs": crs, - "srs": crs, - "scaleExtent": scale_extent, - "scaleSize": scale_size, - "height": height, - "width": width, + 'format': { + 'mime': mime, + 'crs': crs, + 'srs': crs, + 'scaleExtent': scale_extent, + 'scaleSize': scale_size, + 'height': height, + 'width': width, }, } ) diff --git a/hybig/crs.py b/hybig/crs.py index 87bedda..8343056 100644 --- a/hybig/crs.py +++ b/hybig/crs.py @@ -54,9 +54,9 @@ def choose_crs_from_srs(srs: SRS): """ try: - if srs.epsg is not None and srs.epsg != "": + if srs.epsg is not None and srs.epsg != '': return CRS.from_string(srs.epsg) - if srs.wkt is not None and srs.wkt != "": + if srs.wkt is not None and srs.wkt != '': return CRS.from_string(srs.wkt) return CRS.from_string(srs.proj4) except Exception as exception: diff --git a/hybig/exceptions.py b/hybig/exceptions.py index dd277ea..a061250 100644 --- a/hybig/exceptions.py +++ b/hybig/exceptions.py @@ -2,7 +2,7 @@ class HyBIGError(Exception): - """Base error class for exceptions rasied by HyBIG library.""" + """Base error class for exceptions raised by HyBIG library.""" def __init__(self, message=None): """All HyBIG errors have a message field.""" diff --git a/hybig/sizes.py b/hybig/sizes.py index 33bfe2f..f1d58f0 100644 --- a/hybig/sizes.py +++ b/hybig/sizes.py @@ -79,14 +79,14 @@ class Dimensions(TypedDict): # This is Table 4.1.8-1 WGS84 (EPSG: 4326 Resolutions) epsg_4326_resolutions = [ - ResolutionInfo("2km", 0.017578125, 10240, 20480, 6, 2), - ResolutionInfo("1km", 0.0087890625, 20480, 40960, 7, 2), - ResolutionInfo("500m", 0.00439453125, 40960, 81920, 8, 2), - ResolutionInfo("250m", 0.002197265625, 81920, 163840, 9, 2), - ResolutionInfo("125m", 0.0010986328125, 163840, 327680, 10, 2), - ResolutionInfo("62.5m", 0.00054931640625, 327680, 655360, 11, 2), - ResolutionInfo("31.25m", 0.000274658203125, 655360, 1310720, 12, 2), - ResolutionInfo("15.625m", 0.0001373291015625, 1310720, 2521440, 13, 2), + ResolutionInfo('2km', 0.017578125, 10240, 20480, 6, 2), + ResolutionInfo('1km', 0.0087890625, 20480, 40960, 7, 2), + ResolutionInfo('500m', 0.00439453125, 40960, 81920, 8, 2), + ResolutionInfo('250m', 0.002197265625, 81920, 163840, 9, 2), + ResolutionInfo('125m', 0.0010986328125, 163840, 327680, 10, 2), + ResolutionInfo('62.5m', 0.00054931640625, 327680, 655360, 11, 2), + ResolutionInfo('31.25m', 0.000274658203125, 655360, 1310720, 12, 2), + ResolutionInfo('15.625m', 0.0001373291015625, 1310720, 2521440, 13, 2), ] # Conversion used above: resolution / pixel_size @@ -94,14 +94,14 @@ class Dimensions(TypedDict): # This is Table 4.1.8-2 NSIDC Sea Ice Polar Stereographic Extent (EPSG:3413) Resolutions epsg_3413_resolutions = [ - ResolutionInfo("2km", 2048, 4096, 4096, 3, 2), - ResolutionInfo("1km", 1024, 8192, 8192, 4, 2), - ResolutionInfo("500m", 512, 16384, 16384, 5, 2), - ResolutionInfo("250m", 256, 32768, 32768, 6, 2), - ResolutionInfo("125m", 128, 65536, 65536, 7, 2), - ResolutionInfo("62.5m", 64, 131072, 131072, 8, 2), - ResolutionInfo("31.25m", 32, 252144, 252144, 9, 2), - ResolutionInfo("15.625m", 16, 524288, 524288, 10, 2), + ResolutionInfo('2km', 2048, 4096, 4096, 3, 2), + ResolutionInfo('1km', 1024, 8192, 8192, 4, 2), + ResolutionInfo('500m', 512, 16384, 16384, 5, 2), + ResolutionInfo('250m', 256, 32768, 32768, 6, 2), + ResolutionInfo('125m', 128, 65536, 65536, 7, 2), + ResolutionInfo('62.5m', 64, 131072, 131072, 8, 2), + ResolutionInfo('31.25m', 32, 252144, 252144, 9, 2), + ResolutionInfo('15.625m', 16, 524288, 524288, 10, 2), ] # The Antarctic resolutions match the northern resolutions precisely. diff --git a/pyproject.toml b/pyproject.toml index e1ea909..794f505 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,3 +50,17 @@ exclude = [ [tool.hatch.build.targets.wheel] packages=["hybig"] + +[tool.ruff] +lint.select = [ + "E", # pycodestyle + "F", # pyflakes + "UP", # pyupgrade + "I", # organize imports +] + +[tool.ruff.format] +quote-style = "single" + +[tool.ruff.lint.pydocstyle] +convention = "google" diff --git a/tests/pip_test_requirements.txt b/tests/pip_test_requirements.txt index f9d53bd..aa05f21 100644 --- a/tests/pip_test_requirements.txt +++ b/tests/pip_test_requirements.txt @@ -1,5 +1,3 @@ -coverage~=7.6.0 -pycodestyle~=2.12.0 -pylint~=3.2.6 -pytest~=8.3.1 -unittest-xml-reporting~=3.2.0 +pylint~=3.3.1 +pytest-cov~=6.0.0 +pytest~=8.3.3 diff --git a/tests/run_tests.sh b/tests/run_tests.sh index ecc70c5..2c1768e 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -18,30 +18,15 @@ # Exit status used to report back to caller STATUS=0 -export HDF5_DISABLE_VERSION_CHECK=1 - # Run the standard set of unit tests, producing JUnit compatible output -coverage run -m pytest tests --junitxml=tests/reports/test-results-"$(date +'%Y%m%d%H%M%S')".xml -RESULT=$? - -if [ "$RESULT" -ne "0" ]; then - STATUS=1 - echo "ERROR: unittest generated errors" -fi - -echo "\n" -echo "Test Coverage Estimates" -coverage report --omit="tests/*" -coverage html --omit="tests/*" -d tests/coverage +pytest --cov=hybig --cov=harmony_service \ + --cov-report=html:reports/coverage \ + --cov-report term \ + --junitxml=reports/test-reports/test-results-"$(date +'%Y%m%d%H%M%S')".xml || STATUS=1 # Run pylint -# Ignored errors/warnings: -# W1203 - use of f-strings in log statements. This warning is leftover from -# using ''.format() vs % notation. For more information, see: -# https://github.com/PyCQA/pylint/issues/2354#issuecomment-414526879 -pylint hybig harmony_service --disable=W1203 -RESULT=$? -RESULT=$((3 & $RESULT)) +pylint hybig harmony_service +RESULT=$((3 & $?)) if [ "$RESULT" -ne "0" ]; then STATUS=1 diff --git a/tests/test_code_format.py b/tests/test_code_format.py deleted file mode 100644 index 0c2a3c8..0000000 --- a/tests/test_code_format.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Ensure code formatting.""" - -from itertools import chain -from pathlib import Path -from unittest import TestCase - -from pycodestyle import StyleGuide - - -class TestCodeFormat(TestCase): - """This test class should ensure all Harmony service Python code adheres - to standard Python code styling. - - Ignored errors and warning: - - * E501: Line length, which defaults to 80 characters. This is a - preferred feature of the code, but not always easily achieved. - * W503: Break before binary operator. Have to ignore one of W503 or - W504 to allow for breaking of some long lines. PEP8 suggests - breaking the line before a binary operator is more "Pythonic". - * E203, E701: This repository uses black code formatting, which deviates - from PEP8 for these errors. - """ - - def test_pycodestyle_adherence(self): - """Check files for PEP8 compliance.""" - python_files = chain( - Path('hybig').rglob('*.py'), - Path('harmony_service').rglob('*.py'), - Path('tests').rglob('*.py'), - ) - - style_guide = StyleGuide(ignore=['E501', 'W503', 'E203', 'E701']) - results = style_guide.check_files(python_files) - self.assertEqual(results.total_errors, 0, 'Found code style issues.') diff --git a/tests/test_service/__init__.py b/tests/test_service/__init__.py new file mode 100644 index 0000000..5451f1d --- /dev/null +++ b/tests/test_service/__init__.py @@ -0,0 +1 @@ +"""Module definition for test_service.""" diff --git a/tests/test_service/test_adapter.py b/tests/test_service/test_adapter.py index aa8ec9a..3e30b7b 100644 --- a/tests/test_service/test_adapter.py +++ b/tests/test_service/test_adapter.py @@ -7,6 +7,7 @@ from unittest.mock import call, patch import numpy as np +from harmony_service_lib.exceptions import ForbiddenException from harmony_service_lib.message import Message from harmony_service_lib.util import config from pystac import Catalog @@ -15,6 +16,7 @@ from rioxarray import open_rasterio from harmony_service.adapter import BrowseImageGeneratorAdapter +from harmony_service.exceptions import HyBIGServiceError from hybig.browse import ( convert_mulitband_to_raster, prepare_raster_for_writing, @@ -98,18 +100,19 @@ def assert_expected_output_catalog( }, ) + @patch('harmony_service.adapter.TemporaryDirectory') @patch('hybig.browse.reproject') - @patch('harmony_service.adapter.rmtree') - @patch('harmony_service.adapter.mkdtemp') @patch('harmony_service.adapter.download') @patch('harmony_service.adapter.stage') def test_valid_request_jpeg( - self, mock_stage, mock_download, mock_mkdtemp, mock_rmtree, mock_reproject + self, mock_stage, mock_download, mock_reproject, mock_temp_dir ): """Ensure a request with a correctly formatted message is fully processed. """ + mock_temp_dir.return_value.__enter__.return_value = self.temp_dir + expected_downloaded_file = self.temp_dir / 'input.tiff' expected_browse_basename = 'input.jpg' @@ -134,8 +137,6 @@ def test_valid_request_jpeg( expected_aux_mime = 'application/xml' expected_manifest_mime = 'text/plain' - mock_mkdtemp.return_value = self.temp_dir - def move_tif(*args, **kwargs): """Copy fixture tiff to download location.""" copy(self.red_tif_fixture, expected_downloaded_file) @@ -330,21 +331,19 @@ def move_tif(*args, **kwargs): ] ) - # Ensure container clean-up was requested: - mock_rmtree.assert_called_once_with(self.temp_dir) - + @patch('harmony_service.adapter.TemporaryDirectory') @patch('hybig.browse.reproject') - @patch('harmony_service.adapter.rmtree') - @patch('harmony_service.adapter.mkdtemp') @patch('harmony_service.adapter.download') @patch('harmony_service.adapter.stage') def test_valid_request_png( - self, mock_stage, mock_download, mock_mkdtemp, mock_rmtree, mock_reproject + self, mock_stage, mock_download, mock_reproject, mock_temp_dir ): """Ensure a request with a correctly formatted message is fully processed. """ + mock_temp_dir.return_value.__enter__.return_value = self.temp_dir + expected_downloaded_file = self.temp_dir / 'input.tiff' expected_browse_basename = 'input.png' @@ -369,8 +368,6 @@ def test_valid_request_png( expected_aux_mime = 'application/xml' expected_manifest_mime = 'text/plain' - mock_mkdtemp.return_value = self.temp_dir - def move_tif(*args, **kwargs): """Copy fixture tiff to download location.""" copy(self.red_tif_fixture, expected_downloaded_file) @@ -550,5 +547,21 @@ def move_tif(*args, **kwargs): ] ) - # Ensure container clean-up was requested: - mock_rmtree.assert_called_once_with(self.temp_dir) + @patch('harmony_service.adapter.download') + def test_forbidden_download(self, mock_download): + mock_download.side_effect = ForbiddenException('You are forbidden to download.') + message = Message( + { + 'sources': [{'collection': 'C1234-EEDTEST', 'shortName': 'test'}], + } + ) + + hybig = BrowseImageGeneratorAdapter( + message, config=self.config, catalog=self.input_stac + ) + + with self.assertRaisesRegex( + HyBIGServiceError, + ('You are forbidden to download.'), + ): + hybig.invoke() diff --git a/tests/unit/test_browse.py b/tests/unit/test_browse.py index b7146a3..6e25ac8 100644 --- a/tests/unit/test_browse.py +++ b/tests/unit/test_browse.py @@ -422,7 +422,6 @@ def test_convert_singleband_to_raster_with_colormap_and_bad_data(self): assert_array_equal(expected_raster, actual_raster) def test_convert_3_multiband_to_raster(self): - bad_data = np.copy(self.data).astype('float64') bad_data[1][1] = np.nan bad_data[1][2] = np.nan @@ -700,10 +699,10 @@ def test_get_color_palette_map_exists_source_does_not(self): ds.colormap.return_value = self.colormap lines = [ - "100 255 0 0 255", - "200 255 255 0 255", - "300 0 255 0 255", - "400 0 0 255 255", + '100 255 0 0 255', + '200 255 255 0 255', + '300 0 255 0 255', + '400 0 0 255 255', ] expected_palette = ColorPalette() @@ -877,17 +876,17 @@ def test_calls_create_browse_with_correct_params(self, mock_create_browse_imager # HarmonyMessage.Format does not have a json representation to compare # to so compare the pieces individually. - self.assertEqual(harmony_format.mime, "image/png") - self.assertEqual(harmony_format['crs'], {"epsg": "EPSG:4326"}) - self.assertEqual(harmony_format['srs'], {"epsg": "EPSG:4326"}) + self.assertEqual(harmony_format.mime, 'image/png') + self.assertEqual(harmony_format['crs'], {'epsg': 'EPSG:4326'}) + self.assertEqual(harmony_format['srs'], {'epsg': 'EPSG:4326'}) self.assertEqual( harmony_format['scaleExtent'], { - "x": {"min": -180, "max": 180}, - "y": {"min": -90, "max": 90}, + 'x': {'min': -180, 'max': 180}, + 'y': {'min': -90, 'max': 90}, }, ) - self.assertEqual(harmony_format['scaleSize'], {"x": 10, "y": 10}) + self.assertEqual(harmony_format['scaleSize'], {'x': 10, 'y': 10}) self.assertIsNone(harmony_message['format']['height']) self.assertIsNone(harmony_message['format']['width']) diff --git a/tests/unit/test_crs.py b/tests/unit/test_crs.py index f69607f..eb23d7a 100644 --- a/tests/unit/test_crs.py +++ b/tests/unit/test_crs.py @@ -54,7 +54,7 @@ ) WKT_EPSG_6050 = dedent( - ''' + """ PROJCS["GR96 / EPSG Arctic zone 1-25", GEOGCS["GR96", DATUM["Greenland_1996", @@ -77,7 +77,7 @@ AXIS["Easting",EAST], AXIS["Northing",NORTH], AUTHORITY["EPSG","6050"]] - ''' + """ ) diff --git a/tests/unit/test_sizes.py b/tests/unit/test_sizes.py index a4d0a65..8415310 100644 --- a/tests/unit/test_sizes.py +++ b/tests/unit/test_sizes.py @@ -4,6 +4,7 @@ from unittest import TestCase from unittest.mock import MagicMock, patch +import pytest import rasterio from harmony_service_lib.message import Message from rasterio import Affine @@ -442,7 +443,6 @@ def test_scale_extent_from_input_image_with_crs_transformation(self): with open_rasterio( self.fixtures / 'RGB.byte.small.tif', mode='r', mask_and_scale=True ) as in_array: - left, bottom, right, top = ( -78.95864996539397, 23.568866283727235, @@ -454,7 +454,9 @@ def test_scale_extent_from_input_image_with_crs_transformation(self): ) actual_scale_extent = choose_scale_extent({}, target_crs, in_array) - self.assertEqual(actual_scale_extent, expected_scale_extent) + assert expected_scale_extent == pytest.approx( + actual_scale_extent, rel=1e-12 + ) class TestChooseTargetDimensions(TestCase): diff --git a/tests/unit/utility.py b/tests/unit/utility.py index 6393956..7465cff 100644 --- a/tests/unit/utility.py +++ b/tests/unit/utility.py @@ -30,7 +30,7 @@ def rasterio_test_file(raster_data=None, **options): } with NamedTemporaryFile(suffix='.tif') as tmp_file: - with rasterio.Env(CHECK_DISK_FREE_SPACE="NO"): + with rasterio.Env(CHECK_DISK_FREE_SPACE='NO'): with rasterio.open( tmp_file.name, 'w', **default_options | options ) as tmp_rasterio_file: