diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 56479d0..1fd5fe4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,10 @@ +2.9.0 +----- + +*UNRELEASED* + +* `#218 `__: ``ImageRegression.check`` now supports receiving an ``PIL.Image`` object directly. + 2.8.3 ----- diff --git a/src/pytest_regressions/image_regression.py b/src/pytest_regressions/image_regression.py index 13b17ab..ba4df88 100644 --- a/src/pytest_regressions/image_regression.py +++ b/src/pytest_regressions/image_regression.py @@ -5,6 +5,7 @@ from typing import Any from typing import Optional from typing import TYPE_CHECKING +from typing import Union import pytest @@ -13,6 +14,7 @@ if TYPE_CHECKING: from pytest_datadir import LazyDataDir + from PIL import Image class ImageRegressionFixture: @@ -140,7 +142,7 @@ def check_result(equal: bool, manhattan_distance: Optional[float]) -> None: def check( self, - image_data: bytes, + image_data: Union[bytes, "Image.Image"], diff_threshold: float = 0.1, expect_equal: bool = True, basename: Optional[str] = None, @@ -149,7 +151,7 @@ def check( """ Checks that the given image contents are comparable with the ones stored in the data directory. - :param image_data: image data + :param image_data: image data bytes which can be read with PIL, or directly a PIL image object. :param basename: basename to store the information in the data directory. If none, use the name of the test function. :param expect_equal: if the image should considered equal below of the given threshold. If False, the @@ -170,7 +172,11 @@ def check( raise ModuleNotFoundError(import_error_message("Pillow")) def dump_fn(target: Path) -> None: - image = Image.open(io.BytesIO(image_data)) + if isinstance(image_data, Image.Image): + image = image_data + else: + image = Image.open(io.BytesIO(image_data)) + image.save(str(target), "PNG") perform_regression_check( diff --git a/tests/test_image_regression.py b/tests/test_image_regression.py index e358704..5280ec4 100644 --- a/tests/test_image_regression.py +++ b/tests/test_image_regression.py @@ -1,10 +1,14 @@ import io from functools import partial +import pytest +from PIL import Image + from pytest_regressions.testing import check_regression_fixture_workflow -def test_image_regression(image_regression, lazy_datadir): +@pytest.mark.parametrize("image_type", ["pil", "bytes"]) +def test_image_regression(image_regression, lazy_datadir, image_type): import matplotlib # this ensures matplot lib does not use a GUI backend (such as Tk) @@ -29,19 +33,27 @@ def test_image_regression(image_regression, lazy_datadir): image_filename = lazy_datadir / "test.png" fig.savefig(str(image_filename)) - image_regression.check(image_filename.read_bytes(), diff_threshold=1.0) + if image_type == "bytes": + image_data = image_filename.read_bytes() + else: + image_data = Image.open(image_filename) + image_regression.check(image_data, diff_threshold=1.0) -def test_image_regression_workflow(pytester, monkeypatch): +@pytest.mark.parametrize("image_type", ["pil", "bytes"]) +def test_image_regression_workflow(pytester, monkeypatch, image_type): import sys from PIL import Image def get_image(color): - f = io.BytesIO() img = Image.new("RGB", (100, 100), color) - img.save(f, "PNG") - return f.getvalue() + if image_type == "pil": + return img + else: + f = io.BytesIO() + img.save(f, "PNG") + return f.getvalue() monkeypatch.setattr(sys, "get_image", partial(get_image, "white"), raising=False) source = """ @@ -54,7 +66,11 @@ def test_1(image_regression): def get_file_contents(): fn = pytester.path / "test_file" / "test_1.png" assert fn.is_file() - return fn.read_bytes() + if image_type == "pil": + # Copy is necessary because Image.open returns a ImageFile class + return Image.open(fn).copy() + else: + return fn.read_bytes() check_regression_fixture_workflow( pytester,