Skip to content

Commit

Permalink
feat: add the last regression fixture (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
12rambau authored Dec 12, 2024
2 parents f47ae2a + bc01bf6 commit 38d3f68
Show file tree
Hide file tree
Showing 11 changed files with 136 additions and 35 deletions.
8 changes: 1 addition & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,8 @@ repos:
- id: doc8
stages: [pre-commit]

- repo: https://github.com/FHPythonUtils/LicenseCheck
rev: "2023.5.1"
hooks:
- id: licensecheck
stages: [pre-commit]

- repo: https://github.com/codespell-project/codespell
rev: v2.2.4
rev: v2.3.0
hooks:
- id: codespell
stages: [pre-commit]
Expand Down
2 changes: 1 addition & 1 deletion CODE_OF_CONDUCT.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
identity and expression, level of experience, education, socioeconomic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.

Expand Down
1 change: 0 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
"sphinx.ext.intersphinx",
"sphinx_design",
"autoapi.extension",
"sphinxcontrib.images",
]
exclude_patterns = ["**.ipynb_checkpoints"]
templates_path = ["_template"]
Expand Down
10 changes: 6 additions & 4 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ Paste this content in your CI/CD environment in a ``EARTHENGINE_TOKEN`` variable

Here is a github action example:

.. thumbnail:: _static/github_env_var.png
:title: Github action environment variable setup
.. image:: _static/github_env_var.png
:alt: Github action environment variable setup
:align: center

#. First go to the :guilabel:`settings`` of your Github repository
#. Then to :guilabel:`secretes and variables` -> :guilabel:`Actions`
Expand Down Expand Up @@ -105,8 +106,9 @@ Paste this content of the `private-key.json` in your CI/CD environment in a ``EA

Here is a github action example:

.. thumbnail:: _static/github_env_var.png
:title: Github action environment variable setup
.. image:: _static/github_env_var.png
:alt: Github action environment variable setup
:align: center

#. First go to the :guilabel:`settings`` of your Github repository
#. Then to :guilabel:`secretes and variables` -> :guilabel:`Actions`
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ dependencies = [
"pytest",
"pytest-regressions",
"geopandas",
"pillow",
]

[[project.authors]]
Expand Down Expand Up @@ -60,7 +61,6 @@ doc = [
"sphinx-design",
"sphinx-autoapi",
"sphinxemoji",
"sphinxcontrib-images",
]

[tool.hatch.build.targets.wheel]
Expand Down
57 changes: 57 additions & 0 deletions pytest_gee/image_regression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""implementation of the ``image_regression`` fixture."""
from typing import Optional

import ee
import requests
from pytest_regressions.image_regression import ImageRegressionFixture


class ImageFixture(ImageRegressionFixture):
"""Fixture for regression testing of :py:class:`ee.Image`."""

def check(
self,
data_image: ee.Image,
diff_threshold: float = 0.1,
expect_equal: bool = True,
basename: Optional[str] = None,
scale: Optional[int] = 30,
viz_params: Optional[dict] = None,
):
"""Check the given image against a previously recorded version, or generate a new file.
This method will create a thumnail version of the requested image. It is made to allow a human user to check the result of the
Computation. The thumbnail will be computed on the fly using earthengine. This mean that the test must be reasonable in size and scale.
We will perform no feasibility checks and your computation might crash if you are too greedy.
The input image will be either a single band image (displayed using black&white colormap) or a 3 band image (displayed using as fake RGB bands).
If the ``viz_params`` parameter is omitted then it will detect the available ands, and use default viz params.
Parameters:
data_image: The image to check. The image needs to be clipped to a geometry or have an existing footprint.
diff_threshold: The threshold for the difference between the expected and obtained images.
expect_equal: If ``True`` the images are expected to be equal, otherwise they are expected to be different.
basename: The basename of the file to test/record. If not given the name of the test is used.
scale: The scale to use for the thumbnail.
viz_params: The visualization parameters to use for the thumbnail. If not given, the min and max values of the image will be used.
"""
# grescale the original image
geometry = data_image.geometry()
image = data_image.clipToBoundsAndScale(geometry, scale=scale)

# extract min and max for visualization
minMax = image.reduceRegion(ee.Reducer.minMax(), geometry, scale)

# create visualization parameters based on the computed minMax values
if viz_params is None:
nbBands = ee.Algorithms.If(image.bandNames().size().gte(3), 3, 1)
bands = image.bandNames().slice(0, ee.Number(nbBands))
min = bands.map(lambda b: minMax.get(ee.String(b).cat("_min")))
max = bands.map(lambda b: minMax.get(ee.String(b).cat("_max")))
viz_params = ee.Dictionary({"bands": bands, "min": min, "max": max}).getInfo()

# get the thumbnail image
thumb_url = image.getThumbURL(params=viz_params)
byte_data = requests.get(thumb_url).content

# call the parent check method
super().check(byte_data, diff_threshold, expect_equal, basename=basename)
31 changes: 28 additions & 3 deletions pytest_gee/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from . import utils
from .dictionary_regression import DictionaryFixture
from .feature_collection_regression import FeatureCollectionFixture
from .image_regression import ImageFixture
from .list_regression import ListFixture


Expand Down Expand Up @@ -51,7 +52,7 @@ def gee_test_folder(gee_hash, gee_folder_root, gee_folder_structure):


@pytest.fixture
def list_regression(
def ee_list_regression(
datadir: Path, original_datadir: Path, request: pytest.FixtureRequest
) -> ListFixture:
"""Fixture to test :py:class:`ee.List` objects.
Expand All @@ -75,7 +76,7 @@ def test_list_regression(list_regression):


@pytest.fixture
def feature_collection_regression(
def ee_feature_collection_regression(
datadir: Path, original_datadir: Path, request: pytest.FixtureRequest
) -> FeatureCollectionFixture:
"""Fixture to test :py:class:`ee.FeatureCollection` objects.
Expand All @@ -99,7 +100,7 @@ def test_feature_collection_regression(feature_collection_regression):


@pytest.fixture
def dictionary_regression(
def ee_dictionary_regression(
datadir: Path, original_datadir: Path, request: pytest.FixtureRequest
) -> DictionaryFixture:
"""Fixture to test `ee.Dictionary` objects.
Expand All @@ -120,3 +121,27 @@ def test_dictionary_regression(dictionary_regression):
dictionary_regression.check(data)
"""
return DictionaryFixture(datadir, original_datadir, request)


@pytest.fixture
def ee_image_regression(
datadir: Path, original_datadir: Path, request: pytest.FixtureRequest
) -> ImageFixture:
"""Fixture to test :py:class:`ee.Image` objects.
Args:
datadir: The directory where the data files are stored.
original_datadir: The original data directory.
request: The pytest request object.
Returns:
The ImageFixture object.
Example:
.. code-block:: python
def test_image_regression(image_regression):
data = ee.Image("LANDSAT/LC08/C02/T1_L2/LC08_191031_20210514")
image_regression.check(data, scale=1000)
"""
return ImageFixture(datadir, original_datadir, request)
60 changes: 42 additions & 18 deletions tests/test_pytest_gee.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

import pytest_gee

landsat_image = "LANDSAT/LC08/C02/T1_L2/LC08_191031_20240607"
"landsat image from 2024-06-07 on top of Rome"


def test_hash_fixture(gee_hash):
"""Test the hash fixture."""
Expand Down Expand Up @@ -44,37 +47,58 @@ def test_init_tree(gee_folder_root, gee_test_folder):
assert str(feature_collection) in asset_list


def test_list_regression(list_regression):
"""Test the list_regression fixture."""
def test_list_regression(ee_list_regression):
"""Test the ee_list_regression fixture."""
data = ee.List([1, 2, 3])
list_regression.check(data)
ee_list_regression.check(data)


def test_list_regression_prescision(list_regression):
"""Test the list_regression fixture with a different precision."""
def test_list_regression_prescision(ee_list_regression):
"""Test the ee_list_regression fixture with a different precision."""
data = ee.List([1.123456789, 2.123456789, 3.123456789])
list_regression.check(data, prescision=3)
ee_list_regression.check(data, prescision=3)


def test_feature_collection_regression(feature_collection_regression):
"""Test the feature_collection_regression fixture."""
def test_feature_collection_regression(ee_feature_collection_regression):
"""Test the ee_feature_collection_regression fixture."""
fc = ee.FeatureCollection("FAO/GAUL/2015/level0").filter(ee.Filter.eq("ADM0_NAME", "Holy See"))
feature_collection_regression.check(fc)
ee_feature_collection_regression.check(fc)


def test_feature_collection_regression_prescision(feature_collection_regression):
"""Test the feature_collection_regression fixture."""
def test_feature_collection_regression_prescision(ee_feature_collection_regression):
"""Test the ee_feature_collection_regression fixture."""
fc = ee.FeatureCollection("FAO/GAUL/2015/level0").filter(ee.Filter.eq("ADM0_NAME", "Holy See"))
feature_collection_regression.check(fc, prescision=4)
ee_feature_collection_regression.check(fc, prescision=4)


def test_dictionary_regression(dictionary_regression):
"""Test the dictionary_regression fixture."""
def test_dictionary_regression(ee_dictionary_regression):
"""Test the ee_dictionary_regression fixture."""
data = ee.Dictionary({"a": 1, "b": 2})
dictionary_regression.check(data)
ee_dictionary_regression.check(data)


def test_dictionary_regression_prescision(dictionary_regression):
"""Test the dictionary_regression fixture with a different precision."""
def test_dictionary_regression_prescision(ee_dictionary_regression):
"""Test the ee_dictionary_regression fixture with a different precision."""
data = ee.Dictionary({"a": 1.123456789, "b": 2.123456789})
dictionary_regression.check(data, prescision=3)
ee_dictionary_regression.check(data, prescision=3)


def test_image_regression_3_bands(ee_image_regression):
"""Test the image_regression fixture."""
image = ee.Image(landsat_image).select(["SR_B4", "SR_B3", "SR_B2"])
ee_image_regression.check(image, scale=1000)


def test_image_regression_1_band(ee_image_regression):
"""Test the image_regression fixture."""
image = ee.Image(landsat_image).normalizedDifference(["SR_B5", "SR_B4"])
ee_image_regression.check(image, scale=1000)


def test_image_regression_with_viz(ee_image_regression):
"""Test the image_regression fixture."""
image = ee.Image(landsat_image).normalizedDifference(["SR_B5", "SR_B4"])
# use magma palette and stretched to 2 sigma
palette = ["#000004", "#2C105C", "#711F81", "#B63679", "#EE605E", "#FDAE78", "#FCFDBF"]
viz = {"bands": ["nd"], "min": 0.0122, "max": 1.237, "palette": palette} # codespell:ignore nd
ee_image_regression.check(image, scale=1000, viz_params=viz)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 38d3f68

Please sign in to comment.