diff --git a/Tests/test_file_apng.py b/Tests/test_file_apng.py index ad61a07ccc5..0ff05f608c2 100644 --- a/Tests/test_file_apng.py +++ b/Tests/test_file_apng.py @@ -325,8 +325,9 @@ def open(): pytest.warns(UserWarning, open) -def test_apng_sequence_errors(): - test_files = [ +@pytest.mark.parametrize( + "test_file", + ( "sequence_start.png", "sequence_gap.png", "sequence_repeat.png", @@ -334,12 +335,13 @@ def test_apng_sequence_errors(): "sequence_reorder.png", "sequence_reorder_chunk.png", "sequence_fdat_fctl.png", - ] - for f in test_files: - with pytest.raises(SyntaxError): - with Image.open(f"Tests/images/apng/{f}") as im: - im.seek(im.n_frames - 1) - im.load() + ), +) +def test_apng_sequence_errors(test_file): + with pytest.raises(SyntaxError): + with Image.open(f"Tests/images/apng/{test_file}") as im: + im.seek(im.n_frames - 1) + im.load() def test_apng_save(tmp_path): diff --git a/Tests/test_file_container.py b/Tests/test_file_container.py index b752e217faa..65cf6a75ea3 100644 --- a/Tests/test_file_container.py +++ b/Tests/test_file_container.py @@ -1,3 +1,5 @@ +import pytest + from PIL import ContainerIO, Image from .helper import hopper @@ -59,89 +61,89 @@ def test_seek_mode_2(): assert container.tell() == 100 -def test_read_n0(): +@pytest.mark.parametrize("bytesmode", (True, False)) +def test_read_n0(bytesmode): # Arrange - for bytesmode in (True, False): - with open(TEST_FILE, "rb" if bytesmode else "r") as fh: - container = ContainerIO.ContainerIO(fh, 22, 100) + with open(TEST_FILE, "rb" if bytesmode else "r") as fh: + container = ContainerIO.ContainerIO(fh, 22, 100) - # Act - container.seek(81) - data = container.read() + # Act + container.seek(81) + data = container.read() - # Assert - if bytesmode: - data = data.decode() - assert data == "7\nThis is line 8\n" + # Assert + if bytesmode: + data = data.decode() + assert data == "7\nThis is line 8\n" -def test_read_n(): +@pytest.mark.parametrize("bytesmode", (True, False)) +def test_read_n(bytesmode): # Arrange - for bytesmode in (True, False): - with open(TEST_FILE, "rb" if bytesmode else "r") as fh: - container = ContainerIO.ContainerIO(fh, 22, 100) + with open(TEST_FILE, "rb" if bytesmode else "r") as fh: + container = ContainerIO.ContainerIO(fh, 22, 100) - # Act - container.seek(81) - data = container.read(3) + # Act + container.seek(81) + data = container.read(3) - # Assert - if bytesmode: - data = data.decode() - assert data == "7\nT" + # Assert + if bytesmode: + data = data.decode() + assert data == "7\nT" -def test_read_eof(): +@pytest.mark.parametrize("bytesmode", (True, False)) +def test_read_eof(bytesmode): # Arrange - for bytesmode in (True, False): - with open(TEST_FILE, "rb" if bytesmode else "r") as fh: - container = ContainerIO.ContainerIO(fh, 22, 100) + with open(TEST_FILE, "rb" if bytesmode else "r") as fh: + container = ContainerIO.ContainerIO(fh, 22, 100) - # Act - container.seek(100) - data = container.read() + # Act + container.seek(100) + data = container.read() - # Assert - if bytesmode: - data = data.decode() - assert data == "" + # Assert + if bytesmode: + data = data.decode() + assert data == "" -def test_readline(): +@pytest.mark.parametrize("bytesmode", (True, False)) +def test_readline(bytesmode): # Arrange - for bytesmode in (True, False): - with open(TEST_FILE, "rb" if bytesmode else "r") as fh: - container = ContainerIO.ContainerIO(fh, 0, 120) + with open(TEST_FILE, "rb" if bytesmode else "r") as fh: + container = ContainerIO.ContainerIO(fh, 0, 120) - # Act - data = container.readline() + # Act + data = container.readline() - # Assert - if bytesmode: - data = data.decode() - assert data == "This is line 1\n" + # Assert + if bytesmode: + data = data.decode() + assert data == "This is line 1\n" -def test_readlines(): +@pytest.mark.parametrize("bytesmode", (True, False)) +def test_readlines(bytesmode): # Arrange - for bytesmode in (True, False): - expected = [ - "This is line 1\n", - "This is line 2\n", - "This is line 3\n", - "This is line 4\n", - "This is line 5\n", - "This is line 6\n", - "This is line 7\n", - "This is line 8\n", - ] - with open(TEST_FILE, "rb" if bytesmode else "r") as fh: - container = ContainerIO.ContainerIO(fh, 0, 120) - - # Act - data = container.readlines() - - # Assert - if bytesmode: - data = [line.decode() for line in data] - assert data == expected + expected = [ + "This is line 1\n", + "This is line 2\n", + "This is line 3\n", + "This is line 4\n", + "This is line 5\n", + "This is line 6\n", + "This is line 7\n", + "This is line 8\n", + ] + with open(TEST_FILE, "rb" if bytesmode else "r") as fh: + container = ContainerIO.ContainerIO(fh, 0, 120) + + # Act + data = container.readlines() + + # Assert + if bytesmode: + data = [line.decode() for line in data] + assert data == expected diff --git a/Tests/test_file_im.py b/Tests/test_file_im.py index 675210c30d2..e458a197ca4 100644 --- a/Tests/test_file_im.py +++ b/Tests/test_file_im.py @@ -78,15 +78,12 @@ def test_eoferror(): im.seek(n_frames - 1) -def test_roundtrip(tmp_path): - def roundtrip(mode): - out = str(tmp_path / "temp.im") - im = hopper(mode) - im.save(out) - assert_image_equal_tofile(im, out) - - for mode in ["RGB", "P", "PA"]: - roundtrip(mode) +@pytest.mark.parametrize("mode", ("RGB", "P", "PA")) +def test_roundtrip(mode, tmp_path): + out = str(tmp_path / "temp.im") + im = hopper(mode) + im.save(out) + assert_image_equal_tofile(im, out) def test_save_unsupported_mode(tmp_path): diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index f9d8e282647..86a0fda04e5 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -135,50 +135,50 @@ def test_adobe_deflate_tiff(self): assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png") - def test_write_metadata(self, tmp_path): + @pytest.mark.parametrize("legacy_api", (False, True)) + def test_write_metadata(self, legacy_api, tmp_path): """Test metadata writing through libtiff""" - for legacy_api in [False, True]: - f = str(tmp_path / "temp.tiff") - with Image.open("Tests/images/hopper_g4.tif") as img: - img.save(f, tiffinfo=img.tag) - - if legacy_api: - original = img.tag.named() - else: - original = img.tag_v2.named() - - # PhotometricInterpretation is set from SAVE_INFO, - # not the original image. - ignored = [ - "StripByteCounts", - "RowsPerStrip", - "PageNumber", - "PhotometricInterpretation", - ] - - with Image.open(f) as loaded: - if legacy_api: - reloaded = loaded.tag.named() - else: - reloaded = loaded.tag_v2.named() - - for tag, value in itertools.chain(reloaded.items(), original.items()): - if tag not in ignored: - val = original[tag] - if tag.endswith("Resolution"): - if legacy_api: - assert val[0][0] / val[0][1] == ( - 4294967295 / 113653537 - ), f"{tag} didn't roundtrip" - else: - assert val == 37.79000115940079, f"{tag} didn't roundtrip" + f = str(tmp_path / "temp.tiff") + with Image.open("Tests/images/hopper_g4.tif") as img: + img.save(f, tiffinfo=img.tag) + + if legacy_api: + original = img.tag.named() + else: + original = img.tag_v2.named() + + # PhotometricInterpretation is set from SAVE_INFO, + # not the original image. + ignored = [ + "StripByteCounts", + "RowsPerStrip", + "PageNumber", + "PhotometricInterpretation", + ] + + with Image.open(f) as loaded: + if legacy_api: + reloaded = loaded.tag.named() + else: + reloaded = loaded.tag_v2.named() + + for tag, value in itertools.chain(reloaded.items(), original.items()): + if tag not in ignored: + val = original[tag] + if tag.endswith("Resolution"): + if legacy_api: + assert val[0][0] / val[0][1] == ( + 4294967295 / 113653537 + ), f"{tag} didn't roundtrip" else: - assert val == value, f"{tag} didn't roundtrip" + assert val == 37.79000115940079, f"{tag} didn't roundtrip" + else: + assert val == value, f"{tag} didn't roundtrip" - # https://github.com/python-pillow/Pillow/issues/1561 - requested_fields = ["StripByteCounts", "RowsPerStrip", "StripOffsets"] - for field in requested_fields: - assert field in reloaded, f"{field} not in metadata" + # https://github.com/python-pillow/Pillow/issues/1561 + requested_fields = ["StripByteCounts", "RowsPerStrip", "StripOffsets"] + for field in requested_fields: + assert field in reloaded, f"{field} not in metadata" @pytest.mark.valgrind_known_error(reason="Known invalid metadata") def test_additional_metadata(self, tmp_path): diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index 849857d31d6..d94bdaa96c9 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -27,13 +27,13 @@ def roundtrip(im, **options): return im -def test_sanity(): - for test_file in test_files: - with Image.open(test_file) as im: - im.load() - assert im.mode == "RGB" - assert im.size == (640, 480) - assert im.format == "MPO" +@pytest.mark.parametrize("test_file", test_files) +def test_sanity(test_file): + with Image.open(test_file) as im: + im.load() + assert im.mode == "RGB" + assert im.size == (640, 480) + assert im.format == "MPO" @pytest.mark.skipif(is_pypy(), reason="Requires CPython") @@ -66,26 +66,25 @@ def test_context_manager(): im.load() -def test_app(): - for test_file in test_files: - # Test APP/COM reader (@PIL135) - with Image.open(test_file) as im: - assert im.applist[0][0] == "APP1" - assert im.applist[1][0] == "APP2" - assert ( - im.applist[1][1][:16] - == b"MPF\x00MM\x00*\x00\x00\x00\x08\x00\x03\xb0\x00" - ) - assert len(im.applist) == 2 +@pytest.mark.parametrize("test_file", test_files) +def test_app(test_file): + # Test APP/COM reader (@PIL135) + with Image.open(test_file) as im: + assert im.applist[0][0] == "APP1" + assert im.applist[1][0] == "APP2" + assert ( + im.applist[1][1][:16] == b"MPF\x00MM\x00*\x00\x00\x00\x08\x00\x03\xb0\x00" + ) + assert len(im.applist) == 2 -def test_exif(): - for test_file in test_files: - with Image.open(test_file) as im: - info = im._getexif() - assert info[272] == "Nintendo 3DS" - assert info[296] == 2 - assert info[34665] == 188 +@pytest.mark.parametrize("test_file", test_files) +def test_exif(test_file): + with Image.open(test_file) as im: + info = im._getexif() + assert info[272] == "Nintendo 3DS" + assert info[296] == 2 + assert info[34665] == 188 def test_frame_size(): @@ -137,12 +136,12 @@ def test_reload_exif_after_seek(): assert 296 in exif -def test_mp(): - for test_file in test_files: - with Image.open(test_file) as im: - mpinfo = im._getmp() - assert mpinfo[45056] == b"0100" - assert mpinfo[45057] == 2 +@pytest.mark.parametrize("test_file", test_files) +def test_mp(test_file): + with Image.open(test_file) as im: + mpinfo = im._getmp() + assert mpinfo[45056] == b"0100" + assert mpinfo[45057] == 2 def test_mp_offset(): @@ -162,48 +161,48 @@ def test_mp_no_data(): im.seek(1) -def test_mp_attribute(): - for test_file in test_files: - with Image.open(test_file) as im: - mpinfo = im._getmp() - frame_number = 0 - for mpentry in mpinfo[0xB002]: - mpattr = mpentry["Attribute"] - if frame_number: - assert not mpattr["RepresentativeImageFlag"] - else: - assert mpattr["RepresentativeImageFlag"] - assert not mpattr["DependentParentImageFlag"] - assert not mpattr["DependentChildImageFlag"] - assert mpattr["ImageDataFormat"] == "JPEG" - assert mpattr["MPType"] == "Multi-Frame Image: (Disparity)" - assert mpattr["Reserved"] == 0 - frame_number += 1 - - -def test_seek(): - for test_file in test_files: - with Image.open(test_file) as im: - assert im.tell() == 0 - # prior to first image raises an error, both blatant and borderline - with pytest.raises(EOFError): - im.seek(-1) - with pytest.raises(EOFError): - im.seek(-523) - # after the final image raises an error, - # both blatant and borderline - with pytest.raises(EOFError): - im.seek(2) - with pytest.raises(EOFError): - im.seek(523) - # bad calls shouldn't change the frame - assert im.tell() == 0 - # this one will work - im.seek(1) - assert im.tell() == 1 - # and this one, too - im.seek(0) - assert im.tell() == 0 +@pytest.mark.parametrize("test_file", test_files) +def test_mp_attribute(test_file): + with Image.open(test_file) as im: + mpinfo = im._getmp() + frame_number = 0 + for mpentry in mpinfo[0xB002]: + mpattr = mpentry["Attribute"] + if frame_number: + assert not mpattr["RepresentativeImageFlag"] + else: + assert mpattr["RepresentativeImageFlag"] + assert not mpattr["DependentParentImageFlag"] + assert not mpattr["DependentChildImageFlag"] + assert mpattr["ImageDataFormat"] == "JPEG" + assert mpattr["MPType"] == "Multi-Frame Image: (Disparity)" + assert mpattr["Reserved"] == 0 + frame_number += 1 + + +@pytest.mark.parametrize("test_file", test_files) +def test_seek(test_file): + with Image.open(test_file) as im: + assert im.tell() == 0 + # prior to first image raises an error, both blatant and borderline + with pytest.raises(EOFError): + im.seek(-1) + with pytest.raises(EOFError): + im.seek(-523) + # after the final image raises an error, + # both blatant and borderline + with pytest.raises(EOFError): + im.seek(2) + with pytest.raises(EOFError): + im.seek(523) + # bad calls shouldn't change the frame + assert im.tell() == 0 + # this one will work + im.seek(1) + assert im.tell() == 1 + # and this one, too + im.seek(0) + assert im.tell() == 0 def test_n_frames(): @@ -225,31 +224,31 @@ def test_eoferror(): im.seek(n_frames - 1) -def test_image_grab(): - for test_file in test_files: - with Image.open(test_file) as im: - assert im.tell() == 0 - im0 = im.tobytes() - im.seek(1) - assert im.tell() == 1 - im1 = im.tobytes() - im.seek(0) - assert im.tell() == 0 - im02 = im.tobytes() - assert im0 == im02 - assert im0 != im1 - - -def test_save(): - for test_file in test_files: - with Image.open(test_file) as im: - assert im.tell() == 0 - jpg0 = roundtrip(im) - assert_image_similar(im, jpg0, 30) - im.seek(1) - assert im.tell() == 1 - jpg1 = roundtrip(im) - assert_image_similar(im, jpg1, 30) +@pytest.mark.parametrize("test_file", test_files) +def test_image_grab(test_file): + with Image.open(test_file) as im: + assert im.tell() == 0 + im0 = im.tobytes() + im.seek(1) + assert im.tell() == 1 + im1 = im.tobytes() + im.seek(0) + assert im.tell() == 0 + im02 = im.tobytes() + assert im0 == im02 + assert im0 != im1 + + +@pytest.mark.parametrize("test_file", test_files) +def test_save(test_file): + with Image.open(test_file) as im: + assert im.tell() == 0 + jpg0 = roundtrip(im) + assert_image_similar(im, jpg0, 30) + im.seek(1) + assert im.tell() == 1 + jpg1 = roundtrip(im) + assert_image_similar(im, jpg1, 30) def test_save_all(): diff --git a/Tests/test_file_tga.py b/Tests/test_file_tga.py index 0c8c9f30485..cbbb7df1d12 100644 --- a/Tests/test_file_tga.py +++ b/Tests/test_file_tga.py @@ -18,51 +18,48 @@ _ORIGIN_TO_ORIENTATION = {"tl": 1, "bl": -1} -def test_sanity(tmp_path): - for mode in _MODES: - - def roundtrip(original_im): - out = str(tmp_path / "temp.tga") +@pytest.mark.parametrize("mode", _MODES) +def test_sanity(mode, tmp_path): + def roundtrip(original_im): + out = str(tmp_path / "temp.tga") - original_im.save(out, rle=rle) - with Image.open(out) as saved_im: - if rle: + original_im.save(out, rle=rle) + with Image.open(out) as saved_im: + if rle: + assert saved_im.info["compression"] == original_im.info["compression"] + assert saved_im.info["orientation"] == original_im.info["orientation"] + if mode == "P": + assert saved_im.getpalette() == original_im.getpalette() + + assert_image_equal(saved_im, original_im) + + png_paths = glob(os.path.join(_TGA_DIR_COMMON, f"*x*_{mode.lower()}.png")) + + for png_path in png_paths: + with Image.open(png_path) as reference_im: + assert reference_im.mode == mode + + path_no_ext = os.path.splitext(png_path)[0] + for origin, rle in product(_ORIGINS, (True, False)): + tga_path = "{}_{}_{}.tga".format( + path_no_ext, origin, "rle" if rle else "raw" + ) + + with Image.open(tga_path) as original_im: + assert original_im.format == "TGA" + assert original_im.get_format_mimetype() == "image/x-tga" + if rle: + assert original_im.info["compression"] == "tga_rle" assert ( - saved_im.info["compression"] == original_im.info["compression"] + original_im.info["orientation"] + == _ORIGIN_TO_ORIENTATION[origin] ) - assert saved_im.info["orientation"] == original_im.info["orientation"] - if mode == "P": - assert saved_im.getpalette() == original_im.getpalette() - - assert_image_equal(saved_im, original_im) + if mode == "P": + assert original_im.getpalette() == reference_im.getpalette() - png_paths = glob(os.path.join(_TGA_DIR_COMMON, f"*x*_{mode.lower()}.png")) - - for png_path in png_paths: - with Image.open(png_path) as reference_im: - assert reference_im.mode == mode - - path_no_ext = os.path.splitext(png_path)[0] - for origin, rle in product(_ORIGINS, (True, False)): - tga_path = "{}_{}_{}.tga".format( - path_no_ext, origin, "rle" if rle else "raw" - ) + assert_image_equal(original_im, reference_im) - with Image.open(tga_path) as original_im: - assert original_im.format == "TGA" - assert original_im.get_format_mimetype() == "image/x-tga" - if rle: - assert original_im.info["compression"] == "tga_rle" - assert ( - original_im.info["orientation"] - == _ORIGIN_TO_ORIENTATION[origin] - ) - if mode == "P": - assert original_im.getpalette() == reference_im.getpalette() - - assert_image_equal(original_im, reference_im) - - roundtrip(original_im) + roundtrip(original_im) def test_palette_depth_16(tmp_path): diff --git a/Tests/test_file_wmf.py b/Tests/test_file_wmf.py index d6769a24b0b..439cb15bca9 100644 --- a/Tests/test_file_wmf.py +++ b/Tests/test_file_wmf.py @@ -66,10 +66,10 @@ def test_load_set_dpi(): assert_image_similar_tofile(im, "Tests/images/drawing_wmf_ref_144.png", 2.1) -def test_save(tmp_path): +@pytest.mark.parametrize("ext", (".wmf", ".emf")) +def test_save(ext, tmp_path): im = hopper() - for ext in [".wmf", ".emf"]: - tmpfile = str(tmp_path / ("temp" + ext)) - with pytest.raises(OSError): - im.save(tmpfile) + tmpfile = str(tmp_path / ("temp" + ext)) + with pytest.raises(OSError): + im.save(tmpfile) diff --git a/Tests/test_image.py b/Tests/test_image.py index 6dc89918f05..7cebed127d9 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -22,8 +22,9 @@ class TestImage: - def test_image_modes_success(self): - for mode in [ + @pytest.mark.parametrize( + "mode", + ( "1", "P", "PA", @@ -44,22 +45,18 @@ def test_image_modes_success(self): "YCbCr", "LAB", "HSV", - ]: - Image.new(mode, (1, 1)) + ), + ) + def test_image_modes_success(self, mode): + Image.new(mode, (1, 1)) - def test_image_modes_fail(self): - for mode in [ - "", - "bad", - "very very long", - "BGR;15", - "BGR;16", - "BGR;24", - "BGR;32", - ]: - with pytest.raises(ValueError) as e: - Image.new(mode, (1, 1)) - assert str(e.value) == "unrecognized image mode" + @pytest.mark.parametrize( + "mode", ("", "bad", "very very long", "BGR;15", "BGR;16", "BGR;24", "BGR;32") + ) + def test_image_modes_fail(self, mode): + with pytest.raises(ValueError) as e: + Image.new(mode, (1, 1)) + assert str(e.value) == "unrecognized image mode" def test_exception_inheritance(self): assert issubclass(UnidentifiedImageError, OSError) @@ -539,23 +536,22 @@ def test_linear_gradient_wrong_mode(self): with pytest.raises(ValueError): Image.linear_gradient(wrong_mode) - def test_linear_gradient(self): - + @pytest.mark.parametrize("mode", ("L", "P", "I", "F")) + def test_linear_gradient(self, mode): # Arrange target_file = "Tests/images/linear_gradient.png" - for mode in ["L", "P", "I", "F"]: - # Act - im = Image.linear_gradient(mode) + # Act + im = Image.linear_gradient(mode) - # Assert - assert im.size == (256, 256) - assert im.mode == mode - assert im.getpixel((0, 0)) == 0 - assert im.getpixel((255, 255)) == 255 - with Image.open(target_file) as target: - target = target.convert(mode) - assert_image_equal(im, target) + # Assert + assert im.size == (256, 256) + assert im.mode == mode + assert im.getpixel((0, 0)) == 0 + assert im.getpixel((255, 255)) == 255 + with Image.open(target_file) as target: + target = target.convert(mode) + assert_image_equal(im, target) def test_radial_gradient_wrong_mode(self): # Arrange @@ -565,23 +561,22 @@ def test_radial_gradient_wrong_mode(self): with pytest.raises(ValueError): Image.radial_gradient(wrong_mode) - def test_radial_gradient(self): - + @pytest.mark.parametrize("mode", ("L", "P", "I", "F")) + def test_radial_gradient(self, mode): # Arrange target_file = "Tests/images/radial_gradient.png" - for mode in ["L", "P", "I", "F"]: - - # Act - im = Image.radial_gradient(mode) - - # Assert - assert im.size == (256, 256) - assert im.mode == mode - assert im.getpixel((0, 0)) == 255 - assert im.getpixel((128, 128)) == 0 - with Image.open(target_file) as target: - target = target.convert(mode) - assert_image_equal(im, target) + + # Act + im = Image.radial_gradient(mode) + + # Assert + assert im.size == (256, 256) + assert im.mode == mode + assert im.getpixel((0, 0)) == 255 + assert im.getpixel((128, 128)) == 0 + with Image.open(target_file) as target: + target = target.convert(mode) + assert_image_equal(im, target) def test_register_extensions(self): test_format = "a" diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index 617274a576d..bb75eb0b5a9 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -184,8 +184,9 @@ def check(self, mode, c=None): with pytest.raises(error): im.getpixel((-1, -1)) - def test_basic(self): - for mode in ( + @pytest.mark.parametrize( + "mode", + ( "1", "L", "LA", @@ -200,23 +201,25 @@ def test_basic(self): "RGBX", "CMYK", "YCbCr", - ): - self.check(mode) + ), + ) + def test_basic(self, mode): + self.check(mode) - def test_signedness(self): + @pytest.mark.parametrize("mode", ("I;16", "I;16B")) + def test_signedness(self, mode): # see https://github.com/python-pillow/Pillow/issues/452 # pixelaccess is using signed int* instead of uint* - for mode in ("I;16", "I;16B"): - self.check(mode, 2**15 - 1) - self.check(mode, 2**15) - self.check(mode, 2**15 + 1) - self.check(mode, 2**16 - 1) - - def test_p_putpixel_rgb_rgba(self): - for color in [(255, 0, 0), (255, 0, 0, 255)]: - im = Image.new("P", (1, 1), 0) - im.putpixel((0, 0), color) - assert im.convert("RGB").getpixel((0, 0)) == (255, 0, 0) + self.check(mode, 2**15 - 1) + self.check(mode, 2**15) + self.check(mode, 2**15 + 1) + self.check(mode, 2**16 - 1) + + @pytest.mark.parametrize("color", ((255, 0, 0), (255, 0, 0, 255))) + def test_p_putpixel_rgb_rgba(self, color): + im = Image.new("P", (1, 1), 0) + im.putpixel((0, 0), color) + assert im.convert("RGB").getpixel((0, 0)) == (255, 0, 0) @pytest.mark.skipif(cffi is None, reason="No CFFI") diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index e5639e10533..8f4b8b43c64 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -268,36 +268,33 @@ def test_matrix_wrong_mode(): im.convert(mode="L", matrix=matrix) -def test_matrix_xyz(): - def matrix_convert(mode): - # Arrange - im = hopper("RGB") - im.info["transparency"] = (255, 0, 0) - # fmt: off - matrix = ( - 0.412453, 0.357580, 0.180423, 0, - 0.212671, 0.715160, 0.072169, 0, - 0.019334, 0.119193, 0.950227, 0) - # fmt: on - assert im.mode == "RGB" - - # Act - # Convert an RGB image to the CIE XYZ colour space - converted_im = im.convert(mode=mode, matrix=matrix) - - # Assert - assert converted_im.mode == mode - assert converted_im.size == im.size - with Image.open("Tests/images/hopper-XYZ.png") as target: - if converted_im.mode == "RGB": - assert_image_similar(converted_im, target, 3) - assert converted_im.info["transparency"] == (105, 54, 4) - else: - assert_image_similar(converted_im, target.getchannel(0), 1) - assert converted_im.info["transparency"] == 105 - - matrix_convert("RGB") - matrix_convert("L") +@pytest.mark.parametrize("mode", ("RGB", "L")) +def test_matrix_xyz(mode): + # Arrange + im = hopper("RGB") + im.info["transparency"] = (255, 0, 0) + # fmt: off + matrix = ( + 0.412453, 0.357580, 0.180423, 0, + 0.212671, 0.715160, 0.072169, 0, + 0.019334, 0.119193, 0.950227, 0) + # fmt: on + assert im.mode == "RGB" + + # Act + # Convert an RGB image to the CIE XYZ colour space + converted_im = im.convert(mode=mode, matrix=matrix) + + # Assert + assert converted_im.mode == mode + assert converted_im.size == im.size + with Image.open("Tests/images/hopper-XYZ.png") as target: + if converted_im.mode == "RGB": + assert_image_similar(converted_im, target, 3) + assert converted_im.info["transparency"] == (105, 54, 4) + else: + assert_image_similar(converted_im, target.getchannel(0), 1) + assert converted_im.info["transparency"] == 105 def test_matrix_identity(): diff --git a/Tests/test_image_copy.py b/Tests/test_image_copy.py index 21e438654b1..591832147d7 100644 --- a/Tests/test_image_copy.py +++ b/Tests/test_image_copy.py @@ -1,37 +1,40 @@ import copy +import pytest + from PIL import Image from .helper import hopper -def test_copy(): +@pytest.mark.parametrize("mode", ("1", "P", "L", "RGB", "I", "F")) +def test_copy(mode): cropped_coordinates = (10, 10, 20, 20) cropped_size = (10, 10) - for mode in "1", "P", "L", "RGB", "I", "F": - # Internal copy method - im = hopper(mode) - out = im.copy() - assert out.mode == im.mode - assert out.size == im.size - - # Python's copy method - im = hopper(mode) - out = copy.copy(im) - assert out.mode == im.mode - assert out.size == im.size - - # Internal copy method on a cropped image - im = hopper(mode) - out = im.crop(cropped_coordinates).copy() - assert out.mode == im.mode - assert out.size == cropped_size - - # Python's copy method on a cropped image - im = hopper(mode) - out = copy.copy(im.crop(cropped_coordinates)) - assert out.mode == im.mode - assert out.size == cropped_size + + # Internal copy method + im = hopper(mode) + out = im.copy() + assert out.mode == im.mode + assert out.size == im.size + + # Python's copy method + im = hopper(mode) + out = copy.copy(im) + assert out.mode == im.mode + assert out.size == im.size + + # Internal copy method on a cropped image + im = hopper(mode) + out = im.crop(cropped_coordinates).copy() + assert out.mode == im.mode + assert out.size == cropped_size + + # Python's copy method on a cropped image + im = hopper(mode) + out = copy.copy(im.crop(cropped_coordinates)) + assert out.mode == im.mode + assert out.size == cropped_size def test_copy_zero(): diff --git a/Tests/test_image_crop.py b/Tests/test_image_crop.py index 6574e6efd1a..4aa41de2792 100644 --- a/Tests/test_image_crop.py +++ b/Tests/test_image_crop.py @@ -5,17 +5,14 @@ from .helper import assert_image_equal, hopper -def test_crop(): - def crop(mode): - im = hopper(mode) - assert_image_equal(im.crop(), im) - - cropped = im.crop((50, 50, 100, 100)) - assert cropped.mode == mode - assert cropped.size == (50, 50) - - for mode in "1", "P", "L", "RGB", "I", "F": - crop(mode) +@pytest.mark.parametrize("mode", ("1", "P", "L", "RGB", "I", "F")) +def test_crop(mode): + im = hopper(mode) + assert_image_equal(im.crop(), im) + + cropped = im.crop((50, 50, 100, 100)) + assert cropped.mode == mode + assert cropped.size == (50, 50) def test_wide_crop(): diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 6d050efccba..5ce98a23568 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -100,40 +100,41 @@ def serialize_image(self, image): for y in range(image.size[1]) ) - def test_reduce_box(self): - for mode in ["RGBX", "RGB", "La", "L"]: - case = self.make_case(mode, (8, 8), 0xE1) - case = case.resize((4, 4), Image.Resampling.BOX) - # fmt: off - data = ("e1 e1" - "e1 e1") - # fmt: on - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (4, 4))) - - def test_reduce_bilinear(self): - for mode in ["RGBX", "RGB", "La", "L"]: - case = self.make_case(mode, (8, 8), 0xE1) - case = case.resize((4, 4), Image.Resampling.BILINEAR) - # fmt: off - data = ("e1 c9" - "c9 b7") - # fmt: on - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (4, 4))) - - def test_reduce_hamming(self): - for mode in ["RGBX", "RGB", "La", "L"]: - case = self.make_case(mode, (8, 8), 0xE1) - case = case.resize((4, 4), Image.Resampling.HAMMING) - # fmt: off - data = ("e1 da" - "da d3") - # fmt: on - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (4, 4))) - - def test_reduce_bicubic(self): + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_reduce_box(self, mode): + case = self.make_case(mode, (8, 8), 0xE1) + case = case.resize((4, 4), Image.Resampling.BOX) + # fmt: off + data = ("e1 e1" + "e1 e1") + # fmt: on + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (4, 4))) + + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_reduce_bilinear(self, mode): + case = self.make_case(mode, (8, 8), 0xE1) + case = case.resize((4, 4), Image.Resampling.BILINEAR) + # fmt: off + data = ("e1 c9" + "c9 b7") + # fmt: on + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (4, 4))) + + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_reduce_hamming(self, mode): + case = self.make_case(mode, (8, 8), 0xE1) + case = case.resize((4, 4), Image.Resampling.HAMMING) + # fmt: off + data = ("e1 da" + "da d3") + # fmt: on + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (4, 4))) + + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_reduce_bicubic(self, mode): for mode in ["RGBX", "RGB", "La", "L"]: case = self.make_case(mode, (12, 12), 0xE1) case = case.resize((6, 6), Image.Resampling.BICUBIC) @@ -145,79 +146,79 @@ def test_reduce_bicubic(self): for channel in case.split(): self.check_case(channel, self.make_sample(data, (6, 6))) - def test_reduce_lanczos(self): - for mode in ["RGBX", "RGB", "La", "L"]: - case = self.make_case(mode, (16, 16), 0xE1) - case = case.resize((8, 8), Image.Resampling.LANCZOS) - # fmt: off - data = ("e1 e0 e4 d7" - "e0 df e3 d6" - "e4 e3 e7 da" - "d7 d6 d9 ce") - # fmt: on - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (8, 8))) - - def test_enlarge_box(self): - for mode in ["RGBX", "RGB", "La", "L"]: - case = self.make_case(mode, (2, 2), 0xE1) - case = case.resize((4, 4), Image.Resampling.BOX) - # fmt: off - data = ("e1 e1" - "e1 e1") - # fmt: on - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (4, 4))) - - def test_enlarge_bilinear(self): - for mode in ["RGBX", "RGB", "La", "L"]: - case = self.make_case(mode, (2, 2), 0xE1) - case = case.resize((4, 4), Image.Resampling.BILINEAR) - # fmt: off - data = ("e1 b0" - "b0 98") - # fmt: on - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (4, 4))) - - def test_enlarge_hamming(self): - for mode in ["RGBX", "RGB", "La", "L"]: - case = self.make_case(mode, (2, 2), 0xE1) - case = case.resize((4, 4), Image.Resampling.HAMMING) - # fmt: off - data = ("e1 d2" - "d2 c5") - # fmt: on - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (4, 4))) - - def test_enlarge_bicubic(self): - for mode in ["RGBX", "RGB", "La", "L"]: - case = self.make_case(mode, (4, 4), 0xE1) - case = case.resize((8, 8), Image.Resampling.BICUBIC) - # fmt: off - data = ("e1 e5 ee b9" - "e5 e9 f3 bc" - "ee f3 fd c1" - "b9 bc c1 a2") - # fmt: on - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (8, 8))) - - def test_enlarge_lanczos(self): - for mode in ["RGBX", "RGB", "La", "L"]: - case = self.make_case(mode, (6, 6), 0xE1) - case = case.resize((12, 12), Image.Resampling.LANCZOS) - data = ( - "e1 e0 db ed f5 b8" - "e0 df da ec f3 b7" - "db db d6 e7 ee b5" - "ed ec e6 fb ff bf" - "f5 f4 ee ff ff c4" - "b8 b7 b4 bf c4 a0" - ) - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (12, 12))) + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_reduce_lanczos(self, mode): + case = self.make_case(mode, (16, 16), 0xE1) + case = case.resize((8, 8), Image.Resampling.LANCZOS) + # fmt: off + data = ("e1 e0 e4 d7" + "e0 df e3 d6" + "e4 e3 e7 da" + "d7 d6 d9 ce") + # fmt: on + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (8, 8))) + + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_enlarge_box(self, mode): + case = self.make_case(mode, (2, 2), 0xE1) + case = case.resize((4, 4), Image.Resampling.BOX) + # fmt: off + data = ("e1 e1" + "e1 e1") + # fmt: on + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (4, 4))) + + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_enlarge_bilinear(self, mode): + case = self.make_case(mode, (2, 2), 0xE1) + case = case.resize((4, 4), Image.Resampling.BILINEAR) + # fmt: off + data = ("e1 b0" + "b0 98") + # fmt: on + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (4, 4))) + + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_enlarge_hamming(self, mode): + case = self.make_case(mode, (2, 2), 0xE1) + case = case.resize((4, 4), Image.Resampling.HAMMING) + # fmt: off + data = ("e1 d2" + "d2 c5") + # fmt: on + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (4, 4))) + + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_enlarge_bicubic(self, mode): + case = self.make_case(mode, (4, 4), 0xE1) + case = case.resize((8, 8), Image.Resampling.BICUBIC) + # fmt: off + data = ("e1 e5 ee b9" + "e5 e9 f3 bc" + "ee f3 fd c1" + "b9 bc c1 a2") + # fmt: on + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (8, 8))) + + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_enlarge_lanczos(self, mode): + case = self.make_case(mode, (6, 6), 0xE1) + case = case.resize((12, 12), Image.Resampling.LANCZOS) + data = ( + "e1 e0 db ed f5 b8" + "e0 df da ec f3 b7" + "db db d6 e7 ee b5" + "ed ec e6 fb ff bf" + "f5 f4 ee ff ff c4" + "b8 b7 b4 bf c4 a0" + ) + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (12, 12))) def test_box_filter_correct_range(self): im = Image.new("RGB", (8, 8), "#1688ff").resize( @@ -419,40 +420,43 @@ def test_nonzero_coefficients(self): class TestCoreResampleBox: - def test_wrong_arguments(self): - im = hopper() - for resample in ( + @pytest.mark.parametrize( + "resample", + ( Image.Resampling.NEAREST, Image.Resampling.BOX, Image.Resampling.BILINEAR, Image.Resampling.HAMMING, Image.Resampling.BICUBIC, Image.Resampling.LANCZOS, - ): - im.resize((32, 32), resample, (0, 0, im.width, im.height)) - im.resize((32, 32), resample, (20, 20, im.width, im.height)) - im.resize((32, 32), resample, (20, 20, 20, 100)) - im.resize((32, 32), resample, (20, 20, 100, 20)) - - with pytest.raises(TypeError, match="must be sequence of length 4"): - im.resize((32, 32), resample, (im.width, im.height)) - - with pytest.raises(ValueError, match="can't be negative"): - im.resize((32, 32), resample, (-20, 20, 100, 100)) - with pytest.raises(ValueError, match="can't be negative"): - im.resize((32, 32), resample, (20, -20, 100, 100)) - - with pytest.raises(ValueError, match="can't be empty"): - im.resize((32, 32), resample, (20.1, 20, 20, 100)) - with pytest.raises(ValueError, match="can't be empty"): - im.resize((32, 32), resample, (20, 20.1, 100, 20)) - with pytest.raises(ValueError, match="can't be empty"): - im.resize((32, 32), resample, (20.1, 20.1, 20, 20)) - - with pytest.raises(ValueError, match="can't exceed"): - im.resize((32, 32), resample, (0, 0, im.width + 1, im.height)) - with pytest.raises(ValueError, match="can't exceed"): - im.resize((32, 32), resample, (0, 0, im.width, im.height + 1)) + ), + ) + def test_wrong_arguments(self, resample): + im = hopper() + im.resize((32, 32), resample, (0, 0, im.width, im.height)) + im.resize((32, 32), resample, (20, 20, im.width, im.height)) + im.resize((32, 32), resample, (20, 20, 20, 100)) + im.resize((32, 32), resample, (20, 20, 100, 20)) + + with pytest.raises(TypeError, match="must be sequence of length 4"): + im.resize((32, 32), resample, (im.width, im.height)) + + with pytest.raises(ValueError, match="can't be negative"): + im.resize((32, 32), resample, (-20, 20, 100, 100)) + with pytest.raises(ValueError, match="can't be negative"): + im.resize((32, 32), resample, (20, -20, 100, 100)) + + with pytest.raises(ValueError, match="can't be empty"): + im.resize((32, 32), resample, (20.1, 20, 20, 100)) + with pytest.raises(ValueError, match="can't be empty"): + im.resize((32, 32), resample, (20, 20.1, 100, 20)) + with pytest.raises(ValueError, match="can't be empty"): + im.resize((32, 32), resample, (20.1, 20.1, 20, 20)) + + with pytest.raises(ValueError, match="can't exceed"): + im.resize((32, 32), resample, (0, 0, im.width + 1, im.height)) + with pytest.raises(ValueError, match="can't exceed"): + im.resize((32, 32), resample, (0, 0, im.width, im.height + 1)) def resize_tiled(self, im, dst_size, xtiles, ytiles): def split_range(size, tiles): @@ -509,14 +513,16 @@ def test_subsample(self): with pytest.raises(AssertionError, match=r"difference 29\."): assert_image_similar(reference, without_box, 5) - def test_formats(self): - for resample in [Image.Resampling.NEAREST, Image.Resampling.BILINEAR]: - for mode in ["RGB", "L", "RGBA", "LA", "I", ""]: - im = hopper(mode) - box = (20, 20, im.size[0] - 20, im.size[1] - 20) - with_box = im.resize((32, 32), resample, box) - cropped = im.crop(box).resize((32, 32), resample) - assert_image_similar(cropped, with_box, 0.4) + @pytest.mark.parametrize("mode", ("RGB", "L", "RGBA", "LA", "I", "")) + @pytest.mark.parametrize( + "resample", (Image.Resampling.NEAREST, Image.Resampling.BILINEAR) + ) + def test_formats(self, mode, resample): + im = hopper(mode) + box = (20, 20, im.size[0] - 20, im.size[1] - 20) + with_box = im.resize((32, 32), resample, box) + cropped = im.crop(box).resize((32, 32), resample) + assert_image_similar(cropped, with_box, 0.4) def test_passthrough(self): # When no resize is required diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py index 8347fabb9e5..83c54cf6211 100644 --- a/Tests/test_image_resize.py +++ b/Tests/test_image_resize.py @@ -22,24 +22,15 @@ def resize(self, im, size, f): im.load() return im._new(im.im.resize(size, f)) - def test_nearest_mode(self): - for mode in [ - "1", - "P", - "L", - "I", - "F", - "RGB", - "RGBA", - "CMYK", - "YCbCr", - "I;16", - ]: # exotic mode - im = hopper(mode) - r = self.resize(im, (15, 12), Image.Resampling.NEAREST) - assert r.mode == mode - assert r.size == (15, 12) - assert r.im.bands == im.im.bands + @pytest.mark.parametrize( + "mode", ("1", "P", "L", "I", "F", "RGB", "RGBA", "CMYK", "YCbCr", "I;16") + ) + def test_nearest_mode(self, mode): + im = hopper(mode) + r = self.resize(im, (15, 12), Image.Resampling.NEAREST) + assert r.mode == mode + assert r.size == (15, 12) + assert r.im.bands == im.im.bands def test_convolution_modes(self): with pytest.raises(ValueError): @@ -55,33 +46,58 @@ def test_convolution_modes(self): assert r.size == (15, 12) assert r.im.bands == im.im.bands - def test_reduce_filters(self): - for f in [ + @pytest.mark.parametrize( + "resample", + ( Image.Resampling.NEAREST, Image.Resampling.BOX, Image.Resampling.BILINEAR, Image.Resampling.HAMMING, Image.Resampling.BICUBIC, Image.Resampling.LANCZOS, - ]: - r = self.resize(hopper("RGB"), (15, 12), f) - assert r.mode == "RGB" - assert r.size == (15, 12) - - def test_enlarge_filters(self): - for f in [ + ), + ) + def test_reduce_filters(self, resample): + r = self.resize(hopper("RGB"), (15, 12), resample) + assert r.mode == "RGB" + assert r.size == (15, 12) + + @pytest.mark.parametrize( + "resample", + ( Image.Resampling.NEAREST, Image.Resampling.BOX, Image.Resampling.BILINEAR, Image.Resampling.HAMMING, Image.Resampling.BICUBIC, Image.Resampling.LANCZOS, - ]: - r = self.resize(hopper("RGB"), (212, 195), f) - assert r.mode == "RGB" - assert r.size == (212, 195) - - def test_endianness(self): + ), + ) + def test_enlarge_filters(self, resample): + r = self.resize(hopper("RGB"), (212, 195), resample) + assert r.mode == "RGB" + assert r.size == (212, 195) + + @pytest.mark.parametrize( + "resample", + ( + Image.Resampling.NEAREST, + Image.Resampling.BOX, + Image.Resampling.BILINEAR, + Image.Resampling.HAMMING, + Image.Resampling.BICUBIC, + Image.Resampling.LANCZOS, + ), + ) + @pytest.mark.parametrize( + "mode, channels_set", + ( + ("RGB", ("blank", "filled", "dirty")), + ("RGBA", ("blank", "blank", "filled", "dirty")), + ("LA", ("filled", "dirty")), + ), + ) + def test_endianness(self, resample, mode, channels_set): # Make an image with one colored pixel, in one channel. # When resized, that channel should be the same as a GS image. # Other channels should be unaffected. @@ -95,47 +111,37 @@ def test_endianness(self): } samples["dirty"].putpixel((1, 1), 128) - for f in [ - Image.Resampling.NEAREST, - Image.Resampling.BOX, - Image.Resampling.BILINEAR, - Image.Resampling.HAMMING, - Image.Resampling.BICUBIC, - Image.Resampling.LANCZOS, - ]: - # samples resized with current filter - references = { - name: self.resize(ch, (4, 4), f) for name, ch in samples.items() - } - - for mode, channels_set in [ - ("RGB", ("blank", "filled", "dirty")), - ("RGBA", ("blank", "blank", "filled", "dirty")), - ("LA", ("filled", "dirty")), - ]: - for channels in set(permutations(channels_set)): - # compile image from different channels permutations - im = Image.merge(mode, [samples[ch] for ch in channels]) - resized = self.resize(im, (4, 4), f) - - for i, ch in enumerate(resized.split()): - # check what resized channel in image is the same - # as separately resized channel - assert_image_equal(ch, references[channels[i]]) - - def test_enlarge_zero(self): - for f in [ + # samples resized with current filter + references = { + name: self.resize(ch, (4, 4), resample) for name, ch in samples.items() + } + + for channels in set(permutations(channels_set)): + # compile image from different channels permutations + im = Image.merge(mode, [samples[ch] for ch in channels]) + resized = self.resize(im, (4, 4), resample) + + for i, ch in enumerate(resized.split()): + # check what resized channel in image is the same + # as separately resized channel + assert_image_equal(ch, references[channels[i]]) + + @pytest.mark.parametrize( + "resample", + ( Image.Resampling.NEAREST, Image.Resampling.BOX, Image.Resampling.BILINEAR, Image.Resampling.HAMMING, Image.Resampling.BICUBIC, Image.Resampling.LANCZOS, - ]: - r = self.resize(Image.new("RGB", (0, 0), "white"), (212, 195), f) - assert r.mode == "RGB" - assert r.size == (212, 195) - assert r.getdata()[0] == (0, 0, 0) + ), + ) + def test_enlarge_zero(self, resample): + r = self.resize(Image.new("RGB", (0, 0), "white"), (212, 195), resample) + assert r.mode == "RGB" + assert r.size == (212, 195) + assert r.getdata()[0] == (0, 0, 0) def test_unknown_filter(self): with pytest.raises(ValueError): @@ -179,74 +185,71 @@ def test_reducing_gap_values(self, gradients_image): (52, 34), Image.Resampling.BICUBIC, reducing_gap=0.99 ) - def test_reducing_gap_1(self, gradients_image): - for box, epsilon in [ - (None, 4), - ((1.1, 2.2, 510.8, 510.9), 4), - ((3, 10, 410, 256), 10), - ]: - ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) - im = gradients_image.resize( - (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=1.0 - ) + @pytest.mark.parametrize( + "box, epsilon", + ((None, 4), ((1.1, 2.2, 510.8, 510.9), 4), ((3, 10, 410, 256), 10)), + ) + def test_reducing_gap_1(self, gradients_image, box, epsilon): + ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) + im = gradients_image.resize( + (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=1.0 + ) - with pytest.raises(AssertionError): - assert_image_equal(ref, im) + with pytest.raises(AssertionError): + assert_image_equal(ref, im) - assert_image_similar(ref, im, epsilon) + assert_image_similar(ref, im, epsilon) - def test_reducing_gap_2(self, gradients_image): - for box, epsilon in [ - (None, 1.5), - ((1.1, 2.2, 510.8, 510.9), 1.5), - ((3, 10, 410, 256), 1), - ]: - ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) - im = gradients_image.resize( - (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=2.0 - ) + @pytest.mark.parametrize( + "box, epsilon", + ((None, 1.5), ((1.1, 2.2, 510.8, 510.9), 1.5), ((3, 10, 410, 256), 1)), + ) + def test_reducing_gap_2(self, gradients_image, box, epsilon): + ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) + im = gradients_image.resize( + (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=2.0 + ) - with pytest.raises(AssertionError): - assert_image_equal(ref, im) + with pytest.raises(AssertionError): + assert_image_equal(ref, im) - assert_image_similar(ref, im, epsilon) + assert_image_similar(ref, im, epsilon) - def test_reducing_gap_3(self, gradients_image): - for box, epsilon in [ - (None, 1), - ((1.1, 2.2, 510.8, 510.9), 1), - ((3, 10, 410, 256), 0.5), - ]: - ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) - im = gradients_image.resize( - (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=3.0 - ) + @pytest.mark.parametrize( + "box, epsilon", + ((None, 1), ((1.1, 2.2, 510.8, 510.9), 1), ((3, 10, 410, 256), 0.5)), + ) + def test_reducing_gap_3(self, gradients_image, box, epsilon): + ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) + im = gradients_image.resize( + (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=3.0 + ) - with pytest.raises(AssertionError): - assert_image_equal(ref, im) + with pytest.raises(AssertionError): + assert_image_equal(ref, im) - assert_image_similar(ref, im, epsilon) + assert_image_similar(ref, im, epsilon) - def test_reducing_gap_8(self, gradients_image): - for box in [None, (1.1, 2.2, 510.8, 510.9), (3, 10, 410, 256)]: - ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) - im = gradients_image.resize( - (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=8.0 - ) + @pytest.mark.parametrize("box", (None, (1.1, 2.2, 510.8, 510.9), (3, 10, 410, 256))) + def test_reducing_gap_8(self, gradients_image, box): + ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) + im = gradients_image.resize( + (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=8.0 + ) - assert_image_equal(ref, im) + assert_image_equal(ref, im) - def test_box_filter(self, gradients_image): - for box, epsilon in [ - ((0, 0, 512, 512), 5.5), - ((0.9, 1.7, 128, 128), 9.5), - ]: - ref = gradients_image.resize((52, 34), Image.Resampling.BOX, box=box) - im = gradients_image.resize( - (52, 34), Image.Resampling.BOX, box=box, reducing_gap=1.0 - ) + @pytest.mark.parametrize( + "box, epsilon", + (((0, 0, 512, 512), 5.5), ((0.9, 1.7, 128, 128), 9.5)), + ) + def test_box_filter(self, gradients_image, box, epsilon): + ref = gradients_image.resize((52, 34), Image.Resampling.BOX, box=box) + im = gradients_image.resize( + (52, 34), Image.Resampling.BOX, box=box, reducing_gap=1.0 + ) - assert_image_similar(ref, im, epsilon) + assert_image_similar(ref, im, epsilon) class TestImageResize: @@ -273,15 +276,14 @@ def test_load_first(self): im = im.resize((64, 64)) assert im.size == (64, 64) - def test_default_filter(self): - for mode in "L", "RGB", "I", "F": - im = hopper(mode) - assert im.resize((20, 20), Image.Resampling.BICUBIC) == im.resize((20, 20)) - - for mode in "1", "P": - im = hopper(mode) - assert im.resize((20, 20), Image.Resampling.NEAREST) == im.resize((20, 20)) - - for mode in "I;16", "I;16L", "I;16B", "BGR;15", "BGR;16": - im = hopper(mode) - assert im.resize((20, 20), Image.Resampling.NEAREST) == im.resize((20, 20)) + @pytest.mark.parametrize("mode", ("L", "RGB", "I", "F")) + def test_default_filter_bicubic(self, mode): + im = hopper(mode) + assert im.resize((20, 20), Image.Resampling.BICUBIC) == im.resize((20, 20)) + + @pytest.mark.parametrize( + "mode", ("1", "P", "I;16", "I;16L", "I;16B", "BGR;15", "BGR;16") + ) + def test_default_filter_nearest(self, mode): + im = hopper(mode) + assert im.resize((20, 20), Image.Resampling.NEAREST) == im.resize((20, 20)) diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py index f96864c53df..a19f19831fd 100644 --- a/Tests/test_image_rotate.py +++ b/Tests/test_image_rotate.py @@ -1,3 +1,5 @@ +import pytest + from PIL import Image from .helper import ( @@ -22,25 +24,25 @@ def rotate(im, mode, angle, center=None, translate=None): assert out.size != im.size -def test_mode(): - for mode in ("1", "P", "L", "RGB", "I", "F"): - im = hopper(mode) - rotate(im, mode, 45) +@pytest.mark.parametrize("mode", ("1", "P", "L", "RGB", "I", "F")) +def test_mode(mode): + im = hopper(mode) + rotate(im, mode, 45) -def test_angle(): - for angle in (0, 90, 180, 270): - with Image.open("Tests/images/test-card.png") as im: - rotate(im, im.mode, angle) +@pytest.mark.parametrize("angle", (0, 90, 180, 270)) +def test_angle(angle): + with Image.open("Tests/images/test-card.png") as im: + rotate(im, im.mode, angle) - im = hopper() - assert_image_equal(im.rotate(angle), im.rotate(angle, expand=1)) + im = hopper() + assert_image_equal(im.rotate(angle), im.rotate(angle, expand=1)) -def test_zero(): - for angle in (0, 45, 90, 180, 270): - im = Image.new("RGB", (0, 0)) - rotate(im, im.mode, angle) +@pytest.mark.parametrize("angle", (0, 45, 90, 180, 270)) +def test_zero(angle): + im = Image.new("RGB", (0, 0)) + rotate(im, im.mode, angle) def test_resample(): diff --git a/Tests/test_image_transpose.py b/Tests/test_image_transpose.py index 6408e156491..877f439ca26 100644 --- a/Tests/test_image_transpose.py +++ b/Tests/test_image_transpose.py @@ -1,3 +1,5 @@ +import pytest + from PIL.Image import Transpose from . import helper @@ -9,157 +11,136 @@ } -def test_flip_left_right(): - def transpose(mode): - im = HOPPER[mode] - out = im.transpose(Transpose.FLIP_LEFT_RIGHT) - assert out.mode == mode - assert out.size == im.size - - x, y = im.size - assert im.getpixel((1, 1)) == out.getpixel((x - 2, 1)) - assert im.getpixel((x - 2, 1)) == out.getpixel((1, 1)) - assert im.getpixel((1, y - 2)) == out.getpixel((x - 2, y - 2)) - assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, y - 2)) - - for mode in HOPPER: - transpose(mode) - - -def test_flip_top_bottom(): - def transpose(mode): - im = HOPPER[mode] - out = im.transpose(Transpose.FLIP_TOP_BOTTOM) - assert out.mode == mode - assert out.size == im.size - - x, y = im.size - assert im.getpixel((1, 1)) == out.getpixel((1, y - 2)) - assert im.getpixel((x - 2, 1)) == out.getpixel((x - 2, y - 2)) - assert im.getpixel((1, y - 2)) == out.getpixel((1, 1)) - assert im.getpixel((x - 2, y - 2)) == out.getpixel((x - 2, 1)) - - for mode in HOPPER: - transpose(mode) - - -def test_rotate_90(): - def transpose(mode): - im = HOPPER[mode] - out = im.transpose(Transpose.ROTATE_90) - assert out.mode == mode - assert out.size == im.size[::-1] - - x, y = im.size - assert im.getpixel((1, 1)) == out.getpixel((1, x - 2)) - assert im.getpixel((x - 2, 1)) == out.getpixel((1, 1)) - assert im.getpixel((1, y - 2)) == out.getpixel((y - 2, x - 2)) - assert im.getpixel((x - 2, y - 2)) == out.getpixel((y - 2, 1)) - - for mode in HOPPER: - transpose(mode) - - -def test_rotate_180(): - def transpose(mode): - im = HOPPER[mode] - out = im.transpose(Transpose.ROTATE_180) - assert out.mode == mode - assert out.size == im.size - - x, y = im.size - assert im.getpixel((1, 1)) == out.getpixel((x - 2, y - 2)) - assert im.getpixel((x - 2, 1)) == out.getpixel((1, y - 2)) - assert im.getpixel((1, y - 2)) == out.getpixel((x - 2, 1)) - assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, 1)) - - for mode in HOPPER: - transpose(mode) - - -def test_rotate_270(): - def transpose(mode): - im = HOPPER[mode] - out = im.transpose(Transpose.ROTATE_270) - assert out.mode == mode - assert out.size == im.size[::-1] - - x, y = im.size - assert im.getpixel((1, 1)) == out.getpixel((y - 2, 1)) - assert im.getpixel((x - 2, 1)) == out.getpixel((y - 2, x - 2)) - assert im.getpixel((1, y - 2)) == out.getpixel((1, 1)) - assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, x - 2)) - - for mode in HOPPER: - transpose(mode) - - -def test_transpose(): - def transpose(mode): - im = HOPPER[mode] - out = im.transpose(Transpose.TRANSPOSE) - assert out.mode == mode - assert out.size == im.size[::-1] - - x, y = im.size - assert im.getpixel((1, 1)) == out.getpixel((1, 1)) - assert im.getpixel((x - 2, 1)) == out.getpixel((1, x - 2)) - assert im.getpixel((1, y - 2)) == out.getpixel((y - 2, 1)) - assert im.getpixel((x - 2, y - 2)) == out.getpixel((y - 2, x - 2)) - - for mode in HOPPER: - transpose(mode) - - -def test_tranverse(): - def transpose(mode): - im = HOPPER[mode] - out = im.transpose(Transpose.TRANSVERSE) - assert out.mode == mode - assert out.size == im.size[::-1] - - x, y = im.size - assert im.getpixel((1, 1)) == out.getpixel((y - 2, x - 2)) - assert im.getpixel((x - 2, 1)) == out.getpixel((y - 2, 1)) - assert im.getpixel((1, y - 2)) == out.getpixel((1, x - 2)) - assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, 1)) - - for mode in HOPPER: - transpose(mode) - - -def test_roundtrip(): - for mode in HOPPER: - im = HOPPER[mode] - - def transpose(first, second): - return im.transpose(first).transpose(second) - - assert_image_equal( - im, transpose(Transpose.FLIP_LEFT_RIGHT, Transpose.FLIP_LEFT_RIGHT) - ) - assert_image_equal( - im, transpose(Transpose.FLIP_TOP_BOTTOM, Transpose.FLIP_TOP_BOTTOM) - ) - assert_image_equal(im, transpose(Transpose.ROTATE_90, Transpose.ROTATE_270)) - assert_image_equal(im, transpose(Transpose.ROTATE_180, Transpose.ROTATE_180)) - assert_image_equal( - im.transpose(Transpose.TRANSPOSE), - transpose(Transpose.ROTATE_90, Transpose.FLIP_TOP_BOTTOM), - ) - assert_image_equal( - im.transpose(Transpose.TRANSPOSE), - transpose(Transpose.ROTATE_270, Transpose.FLIP_LEFT_RIGHT), - ) - assert_image_equal( - im.transpose(Transpose.TRANSVERSE), - transpose(Transpose.ROTATE_90, Transpose.FLIP_LEFT_RIGHT), - ) - assert_image_equal( - im.transpose(Transpose.TRANSVERSE), - transpose(Transpose.ROTATE_270, Transpose.FLIP_TOP_BOTTOM), - ) - assert_image_equal( - im.transpose(Transpose.TRANSVERSE), - transpose(Transpose.ROTATE_180, Transpose.TRANSPOSE), - ) +@pytest.mark.parametrize("mode", HOPPER) +def test_flip_left_right(mode): + im = HOPPER[mode] + out = im.transpose(Transpose.FLIP_LEFT_RIGHT) + assert out.mode == mode + assert out.size == im.size + + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((x - 2, 1)) + assert im.getpixel((x - 2, 1)) == out.getpixel((1, 1)) + assert im.getpixel((1, y - 2)) == out.getpixel((x - 2, y - 2)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, y - 2)) + + +@pytest.mark.parametrize("mode", HOPPER) +def test_flip_top_bottom(mode): + im = HOPPER[mode] + out = im.transpose(Transpose.FLIP_TOP_BOTTOM) + assert out.mode == mode + assert out.size == im.size + + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((1, y - 2)) + assert im.getpixel((x - 2, 1)) == out.getpixel((x - 2, y - 2)) + assert im.getpixel((1, y - 2)) == out.getpixel((1, 1)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((x - 2, 1)) + + +@pytest.mark.parametrize("mode", HOPPER) +def test_rotate_90(mode): + im = HOPPER[mode] + out = im.transpose(Transpose.ROTATE_90) + assert out.mode == mode + assert out.size == im.size[::-1] + + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((1, x - 2)) + assert im.getpixel((x - 2, 1)) == out.getpixel((1, 1)) + assert im.getpixel((1, y - 2)) == out.getpixel((y - 2, x - 2)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((y - 2, 1)) + + +@pytest.mark.parametrize("mode", HOPPER) +def test_rotate_180(mode): + im = HOPPER[mode] + out = im.transpose(Transpose.ROTATE_180) + assert out.mode == mode + assert out.size == im.size + + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((x - 2, y - 2)) + assert im.getpixel((x - 2, 1)) == out.getpixel((1, y - 2)) + assert im.getpixel((1, y - 2)) == out.getpixel((x - 2, 1)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, 1)) + + +@pytest.mark.parametrize("mode", HOPPER) +def test_rotate_270(mode): + im = HOPPER[mode] + out = im.transpose(Transpose.ROTATE_270) + assert out.mode == mode + assert out.size == im.size[::-1] + + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((y - 2, 1)) + assert im.getpixel((x - 2, 1)) == out.getpixel((y - 2, x - 2)) + assert im.getpixel((1, y - 2)) == out.getpixel((1, 1)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, x - 2)) + + +@pytest.mark.parametrize("mode", HOPPER) +def test_transpose(mode): + im = HOPPER[mode] + out = im.transpose(Transpose.TRANSPOSE) + assert out.mode == mode + assert out.size == im.size[::-1] + + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((1, 1)) + assert im.getpixel((x - 2, 1)) == out.getpixel((1, x - 2)) + assert im.getpixel((1, y - 2)) == out.getpixel((y - 2, 1)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((y - 2, x - 2)) + + +@pytest.mark.parametrize("mode", HOPPER) +def test_tranverse(mode): + im = HOPPER[mode] + out = im.transpose(Transpose.TRANSVERSE) + assert out.mode == mode + assert out.size == im.size[::-1] + + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((y - 2, x - 2)) + assert im.getpixel((x - 2, 1)) == out.getpixel((y - 2, 1)) + assert im.getpixel((1, y - 2)) == out.getpixel((1, x - 2)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, 1)) + + +@pytest.mark.parametrize("mode", HOPPER) +def test_roundtrip(mode): + im = HOPPER[mode] + + def transpose(first, second): + return im.transpose(first).transpose(second) + + assert_image_equal( + im, transpose(Transpose.FLIP_LEFT_RIGHT, Transpose.FLIP_LEFT_RIGHT) + ) + assert_image_equal( + im, transpose(Transpose.FLIP_TOP_BOTTOM, Transpose.FLIP_TOP_BOTTOM) + ) + assert_image_equal(im, transpose(Transpose.ROTATE_90, Transpose.ROTATE_270)) + assert_image_equal(im, transpose(Transpose.ROTATE_180, Transpose.ROTATE_180)) + assert_image_equal( + im.transpose(Transpose.TRANSPOSE), + transpose(Transpose.ROTATE_90, Transpose.FLIP_TOP_BOTTOM), + ) + assert_image_equal( + im.transpose(Transpose.TRANSPOSE), + transpose(Transpose.ROTATE_270, Transpose.FLIP_LEFT_RIGHT), + ) + assert_image_equal( + im.transpose(Transpose.TRANSVERSE), + transpose(Transpose.ROTATE_90, Transpose.FLIP_LEFT_RIGHT), + ) + assert_image_equal( + im.transpose(Transpose.TRANSVERSE), + transpose(Transpose.ROTATE_270, Transpose.FLIP_TOP_BOTTOM), + ) + assert_image_equal( + im.transpose(Transpose.TRANSVERSE), + transpose(Transpose.ROTATE_180, Transpose.TRANSPOSE), + ) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 961b4d08130..d1dd1e47c1c 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -625,20 +625,20 @@ def test_polygon2(): helper_polygon(POINTS2) -def test_polygon_kite(): +@pytest.mark.parametrize("mode", ("RGB", "L")) +def test_polygon_kite(mode): # Test drawing lines of different gradients (dx>dy, dy>dx) and # vertical (dx==0) and horizontal (dy==0) lines - for mode in ["RGB", "L"]: - # Arrange - im = Image.new(mode, (W, H)) - draw = ImageDraw.Draw(im) - expected = f"Tests/images/imagedraw_polygon_kite_{mode}.png" + # Arrange + im = Image.new(mode, (W, H)) + draw = ImageDraw.Draw(im) + expected = f"Tests/images/imagedraw_polygon_kite_{mode}.png" - # Act - draw.polygon(KITE_POINTS, fill="blue", outline="yellow") + # Act + draw.polygon(KITE_POINTS, fill="blue", outline="yellow") - # Assert - assert_image_equal_tofile(im, expected) + # Assert + assert_image_equal_tofile(im, expected) def test_polygon_1px_high(): diff --git a/Tests/test_qt_image_toqimage.py b/Tests/test_qt_image_toqimage.py index 60bfaeb9b75..c1983031a14 100644 --- a/Tests/test_qt_image_toqimage.py +++ b/Tests/test_qt_image_toqimage.py @@ -16,32 +16,32 @@ from PIL.ImageQt import QImage -def test_sanity(tmp_path): - for mode in ("RGB", "RGBA", "L", "P", "1"): - src = hopper(mode) - data = ImageQt.toqimage(src) - - assert isinstance(data, QImage) - assert not data.isNull() - - # reload directly from the qimage - rt = ImageQt.fromqimage(data) - if mode in ("L", "P", "1"): - assert_image_equal(rt, src.convert("RGB")) - else: - assert_image_equal(rt, src) - - if mode == "1": - # BW appears to not save correctly on QT4 and QT5 - # kicks out errors on console: - # libpng warning: Invalid color type/bit depth combination - # in IHDR - # libpng error: Invalid IHDR data - continue - - # Test saving the file - tempfile = str(tmp_path / f"temp_{mode}.png") - data.save(tempfile) - - # Check that it actually worked. - assert_image_equal_tofile(src, tempfile) +@pytest.mark.parametrize("mode", ("RGB", "RGBA", "L", "P", "1")) +def test_sanity(mode, tmp_path): + src = hopper(mode) + data = ImageQt.toqimage(src) + + assert isinstance(data, QImage) + assert not data.isNull() + + # reload directly from the qimage + rt = ImageQt.fromqimage(data) + if mode in ("L", "P", "1"): + assert_image_equal(rt, src.convert("RGB")) + else: + assert_image_equal(rt, src) + + if mode == "1": + # BW appears to not save correctly on QT5 + # kicks out errors on console: + # libpng warning: Invalid color type/bit depth combination + # in IHDR + # libpng error: Invalid IHDR data + return + + # Test saving the file + tempfile = str(tmp_path / f"temp_{mode}.png") + data.save(tempfile) + + # Check that it actually worked. + assert_image_equal_tofile(src, tempfile)