From 5320e45a11c5db8d0abe751d94cde47b211475fb Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Fri, 21 Jun 2024 12:10:16 +0200 Subject: [PATCH 01/26] conftest changes --- conftest.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conftest.py b/conftest.py index 57a5a80db..4d99059ea 100644 --- a/conftest.py +++ b/conftest.py @@ -12,6 +12,8 @@ def calculate_coverage(test_name): all_branches = { "branches1": Image.branches, "branches2": PdfParser.XrefTable.branches, + "branches3": ImageCms.branches, + "branches4": McIdasImagePlugin.branches, # Add more } From 0a88a142c031e1edd37cca8077e9cfde4692ce25 Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Fri, 21 Jun 2024 12:11:14 +0200 Subject: [PATCH 02/26] function 1 added flags --- src/PIL/McIdasImagePlugin.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/PIL/McIdasImagePlugin.py b/src/PIL/McIdasImagePlugin.py index d2a8f32e7..6d8273894 100644 --- a/src/PIL/McIdasImagePlugin.py +++ b/src/PIL/McIdasImagePlugin.py @@ -21,6 +21,13 @@ import struct from . import Image, ImageFile +branches = { + "1": False, + "2": False, + "3": False, + "4": False, + "5": False, + } def _accept(prefix: bytes) -> bool: return prefix[:8] == b"\x00\x00\x00\x00\x00\x00\x00\x04" @@ -40,6 +47,7 @@ class McIdasImageFile(ImageFile.ImageFile): s = self.fp.read(256) if not _accept(s) or len(s) != 256: + branches["1"] = True msg = "not an McIdas area file" raise SyntaxError(msg) @@ -48,16 +56,20 @@ class McIdasImageFile(ImageFile.ImageFile): # get mode if w[11] == 1: + branches["2"] = True mode = rawmode = "L" elif w[11] == 2: + branches["3"] = True # FIXME: add memory map support mode = "I" rawmode = "I;16B" elif w[11] == 4: + branches["4"] = True # FIXME: add memory map support mode = "I" rawmode = "I;32B" else: + branches["5"] = True msg = "unsupported McIdas format" raise SyntaxError(msg) From 4d9e87c31aa460b2f9f9534058b1439157ef6971 Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Fri, 21 Jun 2024 12:11:48 +0200 Subject: [PATCH 03/26] function 2 added flags --- src/PIL/ImageCms.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 2eedf952f..31ed11629 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -29,6 +29,17 @@ from . import Image, __version__ from ._deprecate import deprecate from ._typing import SupportsRead +branches = { + "1": False, + "2": False, + "3": False, + "4": False, + "5": False, + "6": False, + "7": False, + "8": False, +} + try: from . import _imagingcms as core except ImportError as ex: @@ -246,20 +257,28 @@ class ImageCmsProfile: """ if isinstance(profile, str): + branches["1"] = True if sys.platform == "win32": + branches["2"] = True profile_bytes_path = profile.encode() try: + branches["3"] = True profile_bytes_path.decode("ascii") except UnicodeDecodeError: + branches["4"] = True with open(profile, "rb") as f: + branches["5"] = True self._set(core.profile_frombytes(f.read())) return self._set(core.profile_open(profile), profile) elif hasattr(profile, "read"): + branches["6"] = True self._set(core.profile_frombytes(profile.read())) elif isinstance(profile, core.CmsProfile): + branches["7"] = True self._set(profile) else: + branches["8"] = True msg = "Invalid type for Profile" # type: ignore[unreachable] raise TypeError(msg) From db5b92778d0d578b42cfc4cdee5d6a856763b798 Mon Sep 17 00:00:00 2001 From: dutcu Date: Thu, 20 Jun 2024 12:16:19 +0200 Subject: [PATCH 04/26] 2 functions coverage tool and improvements done --- Tests/test_frombytes_image.py | 64 +++++++++++++++++++++++++++++++++++ Tests/test_image_merge.py | 30 ---------------- Tests/test_new_pdfparser.py | 26 ++++++++++++++ 3 files changed, 90 insertions(+), 30 deletions(-) create mode 100644 Tests/test_frombytes_image.py delete mode 100644 Tests/test_image_merge.py create mode 100644 Tests/test_new_pdfparser.py diff --git a/Tests/test_frombytes_image.py b/Tests/test_frombytes_image.py new file mode 100644 index 000000000..77e3363a1 --- /dev/null +++ b/Tests/test_frombytes_image.py @@ -0,0 +1,64 @@ +import unittest +from PIL import Image +import unittest + + +class TestFromBytes(unittest.TestCase): + def test_frombytes(self): + # Test case 1: Empty bytes + data = b"" + image = Image.frombytes("RGB", (0, 0), data) + self.assertEqual(image.size, (0, 0)) + + # Test case 2: Non-empty bytes + data = b"\x00\x00\xFF\xFF\x00\x00" + image = Image.frombytes("RGB", (2, 1), data) + self.assertEqual(image.size, (2, 1)) + self.assertEqual(image.getpixel((0, 0)), (0, 0, 255)) + self.assertEqual(image.getpixel((1, 0)), (255, 0, 0)) + + # Test case 3: Invalid mode + data = b"\x00\x00\xFF\xFF\x00\x00" + with self.assertRaises(ValueError): + Image.frombytes("RGBA", (2, 1), data) + + # Test case 4: Non-RGB mode + data = b"\x00\x00\xFF\xFF\x00\x00" + image = Image.frombytes("L", (2, 1), data) + self.assertEqual(image.size, (2, 1)) + self.assertEqual(image.getpixel((0, 0)), 0) + # self.assertEqual(image.getpixel((1, 0)), 255) + + # Test case 5: Zero width + data = b"" + image = Image.frombytes("RGB", (0, 1), data) + self.assertEqual(image.size, (0, 1)) + + # Test case 6: Zero height + data = b"" + image = Image.frombytes("RGB", (1, 0), data) + self.assertEqual(image.size, (1, 0)) + + # Test case 7: s[0] < 0 + data = b"\x00\x00\xFF\xFF\x00\x00" + s = (-1, 1) + with self.assertRaises(ValueError): + Image.frombytes("RGB", s, data) + + # Test case 8: s[1] == 0 + data = b"\x00\x00\xFF\xFF\x00\x00" + s = (2, 0) + # with self.assertRaises(ValueError): + # Image.frombytes("RGB", s, data) + + # Test case 5: Different size + data = b"\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + image = Image.frombytes("RGB", (3, 1), data) + self.assertEqual(image.size, (3, 1)) + self.assertEqual(image.getpixel((0, 0)), (0, 0, 255)) + self.assertEqual(image.getpixel((1, 0)), (255, 0, 0)) + # self.assertEqual(image.getpixel((2, 0)), (255, 0, 0)) + +if __name__ == "__main__": + unittest.main() + diff --git a/Tests/test_image_merge.py b/Tests/test_image_merge.py deleted file mode 100644 index 21b8a1bfd..000000000 --- a/Tests/test_image_merge.py +++ /dev/null @@ -1,30 +0,0 @@ -import pytest - -from PIL import Image - -def calculate_branch_coverage(): - b = Image.Branches - print("Branches covered:", sum(b.values())) - - -def test_merge_wrong_number_of_bands(): - R = Image.new('L', (100, 100), color=255) - G = Image.new('L', (100, 100), color=128) - with pytest.raises(ValueError, match="wrong number of bands"): - Image.merge('RGB', [R, G]) - -def test_merge_mode_mismatch(): - R = Image.new('L', (100, 100), color=255) - G = Image.new('L', (100, 100), color=128) - B = Image.new('1', (100, 100)) # Incorrect mode - with pytest.raises(ValueError, match="mode mismatch"): - Image.merge('RGB', [R, G, B]) - -def test_merge_size_mismatch(): - R = Image.new('L', (100, 100), color=255) - G = Image.new('L', (200, 100), color=128) # Different size - B = Image.new('L', (100, 100), color=0) - with pytest.raises(ValueError, match="size mismatch"): - Image.merge('RGB', [R, G, B]) - - diff --git a/Tests/test_new_pdfparser.py b/Tests/test_new_pdfparser.py new file mode 100644 index 000000000..cbefc04ea --- /dev/null +++ b/Tests/test_new_pdfparser.py @@ -0,0 +1,26 @@ +import pytest +from PIL import PdfParser + +def test_delitem_new_entries(): + parser = PdfParser.XrefTable() + parser.new_entries["test_key"] = ("value", 0) + + del parser["test_key"] + + assert "test_key" not in parser.new_entries + assert parser.deleted_entries["test_key"] == 1 + + +def test_delitem_deleted_entries(): + parser = PdfParser.XrefTable() + parser.deleted_entries["test_key"] = 0 + + del parser["test_key"] + + assert parser.deleted_entries["test_key"] == 0 + +def test_delitem_nonexistent_key(): + parser = PdfParser.XrefTable() + + with pytest.raises(IndexError): + del parser["nonexistent_key"] \ No newline at end of file From b129900e4828ae38b28c97403072630bed9f8d42 Mon Sep 17 00:00:00 2001 From: Duru Date: Fri, 21 Jun 2024 14:58:01 +0200 Subject: [PATCH 05/26] deekshu coverage tool and duru second func improv --- conftest.py | 10 +++++----- src/PIL/ImageCms.py | 28 ++++++++++++++++++++-------- src/PIL/McIdasImagePlugin.py | 19 ++++++++++++++----- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/conftest.py b/conftest.py index 4d99059ea..260330c28 100644 --- a/conftest.py +++ b/conftest.py @@ -4,6 +4,8 @@ import sys from PIL import Image from PIL import PdfParser +from PIL import ImageCms +from PIL import McIdasImagePlugin pytest_plugins = ["Tests.helper"] @@ -12,8 +14,8 @@ def calculate_coverage(test_name): all_branches = { "branches1": Image.branches, "branches2": PdfParser.XrefTable.branches, - "branches3": ImageCms.branches, - "branches4": McIdasImagePlugin.branches, + "branches3": ImageCms.ImageCmsProfile.branches, + "branches4": McIdasImagePlugin.McIdasImageFile.branches, # Add more } @@ -47,6 +49,4 @@ def pytest_sessionfinish(session, exitstatus): global test_name coverage = calculate_coverage(test_name) - print("\nBRANCH COVERAGE for", test_name, ":", coverage, "%\n") - - \ No newline at end of file + print("\nBRANCH COVERAGE for", test_name, ":", coverage, "%\n") \ No newline at end of file diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 31ed11629..0b2702d70 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -248,6 +248,17 @@ _FLAGS = { class ImageCmsProfile: + branches = { + "1": False, + "2": False, + "3": False, + "4": False, + "5": False, + "6": False, + "7": False, + "8": False, + } + def __init__(self, profile: str | SupportsRead[bytes] | core.CmsProfile) -> None: """ :param profile: Either a string representing a filename, @@ -257,31 +268,32 @@ class ImageCmsProfile: """ if isinstance(profile, str): - branches["1"] = True + ImageCmsProfile.branches["1"] = True if sys.platform == "win32": - branches["2"] = True + ImageCmsProfile.branches["2"] = True profile_bytes_path = profile.encode() try: - branches["3"] = True + ImageCmsProfile.branches["3"] = True profile_bytes_path.decode("ascii") except UnicodeDecodeError: - branches["4"] = True + ImageCmsProfile.branches["4"] = True with open(profile, "rb") as f: - branches["5"] = True + ImageCmsProfile.branches["5"] = True self._set(core.profile_frombytes(f.read())) return self._set(core.profile_open(profile), profile) elif hasattr(profile, "read"): - branches["6"] = True + ImageCmsProfile.branches["6"] = True self._set(core.profile_frombytes(profile.read())) elif isinstance(profile, core.CmsProfile): - branches["7"] = True + ImageCmsProfile.branches["7"] = True self._set(profile) else: - branches["8"] = True + ImageCmsProfile.branches["8"] = True msg = "Invalid type for Profile" # type: ignore[unreachable] raise TypeError(msg) + def _set(self, profile: core.CmsProfile, filename: str | None = None) -> None: self.profile = profile self.filename = filename diff --git a/src/PIL/McIdasImagePlugin.py b/src/PIL/McIdasImagePlugin.py index 6d8273894..296e818ed 100644 --- a/src/PIL/McIdasImagePlugin.py +++ b/src/PIL/McIdasImagePlugin.py @@ -38,6 +38,14 @@ def _accept(prefix: bytes) -> bool: class McIdasImageFile(ImageFile.ImageFile): + branches = { + "1": False, + "2": False, + "3": False, + "4": False, + "5": False, + } + format = "MCIDAS" format_description = "McIdas area file" @@ -47,7 +55,7 @@ class McIdasImageFile(ImageFile.ImageFile): s = self.fp.read(256) if not _accept(s) or len(s) != 256: - branches["1"] = True + McIdasImageFile.branches["1"] = True msg = "not an McIdas area file" raise SyntaxError(msg) @@ -56,20 +64,20 @@ class McIdasImageFile(ImageFile.ImageFile): # get mode if w[11] == 1: - branches["2"] = True + McIdasImageFile.branches["2"] = True mode = rawmode = "L" elif w[11] == 2: - branches["3"] = True + McIdasImageFile.branches["3"] = True # FIXME: add memory map support mode = "I" rawmode = "I;16B" elif w[11] == 4: - branches["4"] = True + McIdasImageFile.branches["4"] = True # FIXME: add memory map support mode = "I" rawmode = "I;32B" else: - branches["5"] = True + McIdasImageFile.branches["5"] = True msg = "unsupported McIdas format" raise SyntaxError(msg) @@ -88,3 +96,4 @@ class McIdasImageFile(ImageFile.ImageFile): Image.register_open(McIdasImageFile.format, McIdasImageFile, _accept) # no default extension + From 90c0661e8d703219f41666eca068bd06a92ed85b Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Fri, 21 Jun 2024 16:02:30 +0200 Subject: [PATCH 06/26] mcidas test case changes implemented --- Tests/test_file_mcidas.py | 61 ++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/Tests/test_file_mcidas.py b/Tests/test_file_mcidas.py index 1046e3dec..8a010bc00 100644 --- a/Tests/test_file_mcidas.py +++ b/Tests/test_file_mcidas.py @@ -1,32 +1,79 @@ from __future__ import annotations import pytest - from PIL import Image, McIdasImagePlugin - from .helper import assert_image_equal_tofile +from io import BytesIO +import struct +def create_mock_mcidas_file(w11_value: int) -> BytesIO: + """ + Creates a mock McIdas file with a given w[11] value. + """ + header = struct.pack( + "!64i", + 0, 0, 0, 0, 0, 0, 0, 4, # Prefix (first 8 bytes) + 0, 400, 1800, # Width, Height (w[9], w[10]) + w11_value, # Mode (w[11]) + 0, 0, 0, # w[12] to w[14] + 16, # w[15] (assuming some stride value) + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # w[16] to w[25] + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # w[26] to w[35] + 256, # w[34] (assuming some offset) + ) + # Ensure header length is 256 bytes + header = b"\x00" * (256 - len(header)) + header + + # Create a valid file + return BytesIO(header) def test_invalid_file() -> None: invalid_file = "Tests/images/flower.jpg" - with pytest.raises(SyntaxError): McIdasImagePlugin.McIdasImageFile(invalid_file) - def test_valid_file() -> None: # Arrange - # https://ghrc.nsstc.nasa.gov/hydro/details/cmx3g8 - # https://ghrc.nsstc.nasa.gov/pub/fieldCampaigns/camex3/cmx3g8/browse/ test_file = "Tests/images/cmx3g8_wv_1998.260_0745_mcidas.ara" saved_file = "Tests/images/cmx3g8_wv_1998.260_0745_mcidas.tiff" # Act with Image.open(test_file) as im: im.load() - # Assert assert im.format == "MCIDAS" assert im.mode == "I" assert im.size == (1800, 400) assert_image_equal_tofile(im, saved_file) + +def test_8bit_file() -> None: + # Arrange + test_file = create_mock_mcidas_file(1) + + # Act + with Image.open(test_file) as im: + im.load() + # Assert + assert im.format == "MCIDAS" + assert im.mode == "L" + assert im.size == (1800, 400) + +def test_32bit_file() -> None: + # Arrange + test_file = create_mock_mcidas_file(4) + + # Act + with Image.open(test_file) as im: + im.load() + # Assert + assert im.format == "MCIDAS" + assert im.mode == "I" + assert im.size == (1800, 400) + +def test_unsupported_file() -> None: + # Arrange + test_file = create_mock_mcidas_file(3) + + # Act & Assert + with pytest.raises(SyntaxError, match="unsupported McIdas format"): + Image.open(test_file) From 27390e0451c8e27676068551ba7bdbb7fd42441b Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Fri, 21 Jun 2024 16:10:03 +0200 Subject: [PATCH 07/26] mcidas improvement draft --- Tests/test_file_mcidas.py | 93 ++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 40 deletions(-) diff --git a/Tests/test_file_mcidas.py b/Tests/test_file_mcidas.py index 8a010bc00..69bfd653c 100644 --- a/Tests/test_file_mcidas.py +++ b/Tests/test_file_mcidas.py @@ -1,79 +1,92 @@ from __future__ import annotations -import pytest -from PIL import Image, McIdasImagePlugin -from .helper import assert_image_equal_tofile -from io import BytesIO import struct +import pytest -def create_mock_mcidas_file(w11_value: int) -> BytesIO: - """ - Creates a mock McIdas file with a given w[11] value. - """ - header = struct.pack( - "!64i", - 0, 0, 0, 0, 0, 0, 0, 4, # Prefix (first 8 bytes) - 0, 400, 1800, # Width, Height (w[9], w[10]) - w11_value, # Mode (w[11]) - 0, 0, 0, # w[12] to w[14] - 16, # w[15] (assuming some stride value) - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # w[16] to w[25] - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # w[26] to w[35] - 256, # w[34] (assuming some offset) - ) - # Ensure header length is 256 bytes - header = b"\x00" * (256 - len(header)) + header +from PIL import Image, McIdasImagePlugin + +from .helper import assert_image_equal_tofile + + +def create_mock_file(filename: str, w11_value: int, width: int, height: int) -> None: + """ + Create a mock McIdas area file with specified w[11] value, width, and height. + """ + area_descriptor = [0] * 64 + area_descriptor[10] = width + area_descriptor[9] = height + area_descriptor[11] = w11_value + area_descriptor[34] = 256 # arbitrary valid offset + area_descriptor[15] = 0 # stride (arbitrary valid value) + header = b"\x00" * 8 + struct.pack("!64i", *area_descriptor) + + with open(filename, "wb") as f: + f.write(header) + # Write image data (size based on width, height, and mode) + if w11_value == 1: + f.write(b"\x00" * (width * height)) # 8-bit data for mode L + elif w11_value == 2: + f.write(b"\x00" * (2 * width * height)) # 16-bit data for mode I;16B + elif w11_value == 4: + f.write(b"\x00" * (4 * width * height)) # 32-bit data for mode I;32B - # Create a valid file - return BytesIO(header) def test_invalid_file() -> None: invalid_file = "Tests/images/flower.jpg" + with pytest.raises(SyntaxError): McIdasImagePlugin.McIdasImageFile(invalid_file) + def test_valid_file() -> None: # Arrange + # https://ghrc.nsstc.nasa.gov/hydro/details/cmx3g8 + # https://ghrc.nsstc.nasa.gov/pub/fieldCampaigns/camex3/cmx3g8/browse/ test_file = "Tests/images/cmx3g8_wv_1998.260_0745_mcidas.ara" saved_file = "Tests/images/cmx3g8_wv_1998.260_0745_mcidas.tiff" # Act with Image.open(test_file) as im: im.load() + # Assert assert im.format == "MCIDAS" assert im.mode == "I" assert im.size == (1800, 400) assert_image_equal_tofile(im, saved_file) -def test_8bit_file() -> None: - # Arrange - test_file = create_mock_mcidas_file(1) - # Act +def test_mode_L() -> None: + test_file = "Tests/images/mcidas_mode_L.ara" + + # Create mock file for mode L + create_mock_file(test_file, w11_value=1, width=100, height=100) + with Image.open(test_file) as im: im.load() - # Assert assert im.format == "MCIDAS" assert im.mode == "L" - assert im.size == (1800, 400) + assert im.size == (100, 100) -def test_32bit_file() -> None: - # Arrange - test_file = create_mock_mcidas_file(4) - # Act +def test_mode_I_32B() -> None: + test_file = "Tests/images/mcidas_mode_I_32B.ara" + + # Create mock file for mode I;32B + create_mock_file(test_file, w11_value=4, width=100, height=100) + with Image.open(test_file) as im: im.load() - # Assert assert im.format == "MCIDAS" assert im.mode == "I" - assert im.size == (1800, 400) + assert im.size == (100, 100) -def test_unsupported_file() -> None: - # Arrange - test_file = create_mock_mcidas_file(3) - # Act & Assert +def test_unsupported_format() -> None: + test_file = "Tests/images/mcidas_unsupported.ara" + + # Create mock file for unsupported format + create_mock_file(test_file, w11_value=3, width=100, height=100) + with pytest.raises(SyntaxError, match="unsupported McIdas format"): - Image.open(test_file) + McIdasImagePlugin.McIdasImageFile(test_file) From 61107fc29d048b7173b36dd2ae3e4b6838134ef7 Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Fri, 21 Jun 2024 18:07:27 +0200 Subject: [PATCH 08/26] drafting mcidas improvement --- Tests/test_file_mcidas.py | 71 ++++++++++----------------------------- 1 file changed, 18 insertions(+), 53 deletions(-) diff --git a/Tests/test_file_mcidas.py b/Tests/test_file_mcidas.py index 69bfd653c..0381145a0 100644 --- a/Tests/test_file_mcidas.py +++ b/Tests/test_file_mcidas.py @@ -1,49 +1,17 @@ -from __future__ import annotations - -import struct import pytest - from PIL import Image, McIdasImagePlugin -from .helper import assert_image_equal_tofile - - -def create_mock_file(filename: str, w11_value: int, width: int, height: int) -> None: - """ - Create a mock McIdas area file with specified w[11] value, width, and height. - """ - area_descriptor = [0] * 64 - area_descriptor[10] = width - area_descriptor[9] = height - area_descriptor[11] = w11_value - area_descriptor[34] = 256 # arbitrary valid offset - area_descriptor[15] = 0 # stride (arbitrary valid value) - header = b"\x00" * 8 + struct.pack("!64i", *area_descriptor) - - with open(filename, "wb") as f: - f.write(header) - # Write image data (size based on width, height, and mode) - if w11_value == 1: - f.write(b"\x00" * (width * height)) # 8-bit data for mode L - elif w11_value == 2: - f.write(b"\x00" * (2 * width * height)) # 16-bit data for mode I;16B - elif w11_value == 4: - f.write(b"\x00" * (4 * width * height)) # 32-bit data for mode I;32B - - -def test_invalid_file() -> None: +def test_open_invalid_file(): + # Arrange invalid_file = "Tests/images/flower.jpg" + # Act & Assert with pytest.raises(SyntaxError): McIdasImagePlugin.McIdasImageFile(invalid_file) - -def test_valid_file() -> None: +def test_open_supported_file(): # Arrange - # https://ghrc.nsstc.nasa.gov/hydro/details/cmx3g8 - # https://ghrc.nsstc.nasa.gov/pub/fieldCampaigns/camex3/cmx3g8/browse/ test_file = "Tests/images/cmx3g8_wv_1998.260_0745_mcidas.ara" - saved_file = "Tests/images/cmx3g8_wv_1998.260_0745_mcidas.tiff" # Act with Image.open(test_file) as im: @@ -53,40 +21,37 @@ def test_valid_file() -> None: assert im.format == "MCIDAS" assert im.mode == "I" assert im.size == (1800, 400) - assert_image_equal_tofile(im, saved_file) - -def test_mode_L() -> None: +def test_open_mode_L(): + # Arrange test_file = "Tests/images/mcidas_mode_L.ara" - # Create mock file for mode L - create_mock_file(test_file, w11_value=1, width=100, height=100) - + # Act with Image.open(test_file) as im: im.load() + + # Assert assert im.format == "MCIDAS" assert im.mode == "L" assert im.size == (100, 100) - -def test_mode_I_32B() -> None: +def test_open_mode_I_32B(): + # Arrange test_file = "Tests/images/mcidas_mode_I_32B.ara" - # Create mock file for mode I;32B - create_mock_file(test_file, w11_value=4, width=100, height=100) - + # Act with Image.open(test_file) as im: im.load() + + # Assert assert im.format == "MCIDAS" assert im.mode == "I" assert im.size == (100, 100) - -def test_unsupported_format() -> None: +def test_open_unsupported_format(): + # Arrange test_file = "Tests/images/mcidas_unsupported.ara" - # Create mock file for unsupported format - create_mock_file(test_file, w11_value=3, width=100, height=100) - + # Act & Assert with pytest.raises(SyntaxError, match="unsupported McIdas format"): - McIdasImagePlugin.McIdasImageFile(test_file) + McIdasImagePlugin.McIdasImageFile(test_file) \ No newline at end of file From d320f708eeb69d35ff7faf8f7b11d49b55538cb6 Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Fri, 21 Jun 2024 19:14:02 +0200 Subject: [PATCH 09/26] draft mcidas testing --- Tests/test_file_mcidas.py | 66 ++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/Tests/test_file_mcidas.py b/Tests/test_file_mcidas.py index 0381145a0..3a695a1c6 100644 --- a/Tests/test_file_mcidas.py +++ b/Tests/test_file_mcidas.py @@ -1,17 +1,24 @@ +from __future__ import annotations + import pytest + from PIL import Image, McIdasImagePlugin -def test_open_invalid_file(): - # Arrange +from .helper import assert_image_equal_tofile + +def test_invalid_file() -> None: invalid_file = "Tests/images/flower.jpg" - # Act & Assert with pytest.raises(SyntaxError): McIdasImagePlugin.McIdasImageFile(invalid_file) -def test_open_supported_file(): + +def test_valid_file() -> None: # Arrange + # https://ghrc.nsstc.nasa.gov/hydro/details/cmx3g8 + # https://ghrc.nsstc.nasa.gov/pub/fieldCampaigns/camex3/cmx3g8/browse/ test_file = "Tests/images/cmx3g8_wv_1998.260_0745_mcidas.ara" + saved_file = "Tests/images/cmx3g8_wv_1998.260_0745_mcidas.tiff" # Act with Image.open(test_file) as im: @@ -21,37 +28,52 @@ def test_open_supported_file(): assert im.format == "MCIDAS" assert im.mode == "I" assert im.size == (1800, 400) + assert_image_equal_tofile(im, saved_file) -def test_open_mode_L(): - # Arrange - test_file = "Tests/images/mcidas_mode_L.ara" - # Act +def test_open_invalid_file() -> None: + invalid_file = "Tests/images/drawing_wmf_ref_144.png" + + with pytest.raises(SyntaxError): + McIdasImagePlugin.McIdasImageFile(invalid_file) + + +def test_open_8bit_file() -> None: + test_file = "Tests/images/8bit.s.tif" + with Image.open(test_file) as im: im.load() - # Assert assert im.format == "MCIDAS" assert im.mode == "L" - assert im.size == (100, 100) + assert im.size == (800, 600) -def test_open_mode_I_32B(): - # Arrange - test_file = "Tests/images/mcidas_mode_I_32B.ara" - # Act +def test_open_16bit_file() -> None: + test_file = "Tests/images/16_bit_binary_pgm.tiff" + with Image.open(test_file) as im: im.load() - # Assert assert im.format == "MCIDAS" assert im.mode == "I" - assert im.size == (100, 100) + assert im.size == (1024, 768) -def test_open_unsupported_format(): - # Arrange - test_file = "Tests/images/mcidas_unsupported.ara" - # Act & Assert - with pytest.raises(SyntaxError, match="unsupported McIdas format"): - McIdasImagePlugin.McIdasImageFile(test_file) \ No newline at end of file +def test_open_32bit_file() -> None: + test_file = "Tests/images/10ct_32bit_128.tiff" + + with Image.open(test_file) as im: + im.load() + + assert im.format == "MCIDAS" + assert im.mode == "I" + assert im.size == (1280, 1024) + + +def test_open_unsupported_format() -> None: + test_file = "Tests/images/standard_embedded.png" + + with pytest.raises(SyntaxError): + McIdasImagePlugin.McIdasImageFile(test_file) + From c1d7543156911fc0386008fdd7024e7b51f98bd6 Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Fri, 21 Jun 2024 22:36:39 +0200 Subject: [PATCH 10/26] test_file_mcidas draft --- Tests/test_file_mcidas.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/test_file_mcidas.py b/Tests/test_file_mcidas.py index 3a695a1c6..756903cdd 100644 --- a/Tests/test_file_mcidas.py +++ b/Tests/test_file_mcidas.py @@ -39,7 +39,7 @@ def test_open_invalid_file() -> None: def test_open_8bit_file() -> None: - test_file = "Tests/images/8bit.s.tif" + test_file = "Tests/images/l_trns.png" with Image.open(test_file) as im: im.load() @@ -50,7 +50,7 @@ def test_open_8bit_file() -> None: def test_open_16bit_file() -> None: - test_file = "Tests/images/16_bit_binary_pgm.tiff" + test_file = "Tests/images/rgb_trns.png" with Image.open(test_file) as im: im.load() @@ -61,7 +61,7 @@ def test_open_16bit_file() -> None: def test_open_32bit_file() -> None: - test_file = "Tests/images/10ct_32bit_128.tiff" + test_file = "Tests/images/tga/common/200x32_la.png" with Image.open(test_file) as im: im.load() @@ -72,7 +72,7 @@ def test_open_32bit_file() -> None: def test_open_unsupported_format() -> None: - test_file = "Tests/images/standard_embedded.png" + test_file = "Tests/images/exif_text.png" with pytest.raises(SyntaxError): McIdasImagePlugin.McIdasImageFile(test_file) From f18d64c267bc076688aba349be06da8dcaca4a4c Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Fri, 21 Jun 2024 23:02:29 +0200 Subject: [PATCH 11/26] imagecms testing --- Tests/test_imagecms_init.py | 46 +++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Tests/test_imagecms_init.py diff --git a/Tests/test_imagecms_init.py b/Tests/test_imagecms_init.py new file mode 100644 index 000000000..2f4fc8d00 --- /dev/null +++ b/Tests/test_imagecms_init.py @@ -0,0 +1,46 @@ +import pytest +from PIL import ImageCms +from PIL.ImageCms import ImageCmsProfile + +def test_ImageCmsProfile_init(): + # Test with a filename + profile_filename = "path/to/profile.icc" + profile = ImageCmsProfile(profile_filename) + assert profile.filename == profile_filename + + # Test with a file-like object + profile_file = open("path/to/profile.icc", "rb") + profile = ImageCmsProfile(profile_file) + assert profile.filename is None + + # Test with a low-level profile object + low_level_profile = core.profile_open(profile_filename) + profile = ImageCmsProfile(low_level_profile) + assert profile.filename is None + + # Test with an invalid type + with pytest.raises(TypeError): + ImageCmsProfile(123) + + + + +# def test_ImageCmsProfile_init_win32(): +# profile_path = "path/to/profile.icc" +# profile_bytes = b"ICC_PROFILE_DATA" + +# with open(profile_path, "rb") as f: +# profile_data = f.read() + +# with pytest.raises(TypeError): +# ImageCmsProfile(profile_path) + +# with pytest.raises(TypeError): +# ImageCmsProfile(profile_bytes) + +# profile = ImageCmsProfile(profile_data) + +# assert profile.filename is None +# assert profile.profile is not None +# assert profile.product_name is None +# assert profile.product_info is None \ No newline at end of file From 4f31cd7a31a6be2ac38773bab6f0a5451480f5b8 Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Fri, 21 Jun 2024 23:16:51 +0200 Subject: [PATCH 12/26] imagefile flags added --- conftest.py | 2 ++ src/PIL/ImageFile.py | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/conftest.py b/conftest.py index 260330c28..8d1320209 100644 --- a/conftest.py +++ b/conftest.py @@ -6,6 +6,7 @@ from PIL import Image from PIL import PdfParser from PIL import ImageCms from PIL import McIdasImagePlugin +from PIL import ImageFile pytest_plugins = ["Tests.helper"] @@ -16,6 +17,7 @@ def calculate_coverage(test_name): "branches2": PdfParser.XrefTable.branches, "branches3": ImageCms.ImageCmsProfile.branches, "branches4": McIdasImagePlugin.McIdasImageFile.branches, + "branches5": ImageFile.PyEncoder.branches, # Add more } diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 3b6100e13..08c746efe 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -39,6 +39,11 @@ from . import Image from ._deprecate import deprecate from ._util import is_path +branches = { + "1": False, + "2": False, +} + MAXBLOCK = 65536 SAFEBLOCK = 1024 * 1024 @@ -750,6 +755,11 @@ class PyDecoder(PyCodec): class PyEncoder(PyCodec): + + branches = { + "1": False, + "2": False, + } """ Python implementation of a format encoder. Override this class and add the decoding logic in the :meth:`encode` method. @@ -801,7 +811,9 @@ class PyEncoder(PyCodec): """ errcode = 0 while errcode == 0: + PyEncoder.branches["1"] = True status, errcode, buf = self.encode(bufsize) if status > 0: + PyEncoder.branches["2"] = True fh.write(buf[status:]) return errcode From 8a11f09e718b1a52637f8908a04c1bdab7d511d7 Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Fri, 21 Jun 2024 23:27:12 +0200 Subject: [PATCH 13/26] original test mcidas --- Tests/test_file_mcidas.py | 49 +-------------------------------------- 1 file changed, 1 insertion(+), 48 deletions(-) diff --git a/Tests/test_file_mcidas.py b/Tests/test_file_mcidas.py index 756903cdd..1046e3dec 100644 --- a/Tests/test_file_mcidas.py +++ b/Tests/test_file_mcidas.py @@ -6,6 +6,7 @@ from PIL import Image, McIdasImagePlugin from .helper import assert_image_equal_tofile + def test_invalid_file() -> None: invalid_file = "Tests/images/flower.jpg" @@ -29,51 +30,3 @@ def test_valid_file() -> None: assert im.mode == "I" assert im.size == (1800, 400) assert_image_equal_tofile(im, saved_file) - - -def test_open_invalid_file() -> None: - invalid_file = "Tests/images/drawing_wmf_ref_144.png" - - with pytest.raises(SyntaxError): - McIdasImagePlugin.McIdasImageFile(invalid_file) - - -def test_open_8bit_file() -> None: - test_file = "Tests/images/l_trns.png" - - with Image.open(test_file) as im: - im.load() - - assert im.format == "MCIDAS" - assert im.mode == "L" - assert im.size == (800, 600) - - -def test_open_16bit_file() -> None: - test_file = "Tests/images/rgb_trns.png" - - with Image.open(test_file) as im: - im.load() - - assert im.format == "MCIDAS" - assert im.mode == "I" - assert im.size == (1024, 768) - - -def test_open_32bit_file() -> None: - test_file = "Tests/images/tga/common/200x32_la.png" - - with Image.open(test_file) as im: - im.load() - - assert im.format == "MCIDAS" - assert im.mode == "I" - assert im.size == (1280, 1024) - - -def test_open_unsupported_format() -> None: - test_file = "Tests/images/exif_text.png" - - with pytest.raises(SyntaxError): - McIdasImagePlugin.McIdasImageFile(test_file) - From 1bd1526ae668af0e2631a76f61c3578a6158d3a8 Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Fri, 21 Jun 2024 23:51:34 +0200 Subject: [PATCH 14/26] test_imagefile.py improvement --- Tests/test_imagecms.py | 2 ++ Tests/test_imagecms_init.py | 46 ------------------------------------- Tests/test_imagefile.py | 9 ++++++++ src/PIL/test_ImageFile.py | 25 ++++++++++++++++++++ 4 files changed, 36 insertions(+), 46 deletions(-) delete mode 100644 Tests/test_imagecms_init.py create mode 100644 src/PIL/test_ImageFile.py diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 968667892..2dac43ee5 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -699,3 +699,5 @@ def test_deprecation() -> None: assert ImageCms.VERSION == "1.0.0 pil" with pytest.warns(DeprecationWarning): assert isinstance(ImageCms.FLAGS, dict) + + diff --git a/Tests/test_imagecms_init.py b/Tests/test_imagecms_init.py deleted file mode 100644 index 2f4fc8d00..000000000 --- a/Tests/test_imagecms_init.py +++ /dev/null @@ -1,46 +0,0 @@ -import pytest -from PIL import ImageCms -from PIL.ImageCms import ImageCmsProfile - -def test_ImageCmsProfile_init(): - # Test with a filename - profile_filename = "path/to/profile.icc" - profile = ImageCmsProfile(profile_filename) - assert profile.filename == profile_filename - - # Test with a file-like object - profile_file = open("path/to/profile.icc", "rb") - profile = ImageCmsProfile(profile_file) - assert profile.filename is None - - # Test with a low-level profile object - low_level_profile = core.profile_open(profile_filename) - profile = ImageCmsProfile(low_level_profile) - assert profile.filename is None - - # Test with an invalid type - with pytest.raises(TypeError): - ImageCmsProfile(123) - - - - -# def test_ImageCmsProfile_init_win32(): -# profile_path = "path/to/profile.icc" -# profile_bytes = b"ICC_PROFILE_DATA" - -# with open(profile_path, "rb") as f: -# profile_data = f.read() - -# with pytest.raises(TypeError): -# ImageCmsProfile(profile_path) - -# with pytest.raises(TypeError): -# ImageCmsProfile(profile_bytes) - -# profile = ImageCmsProfile(profile_data) - -# assert profile.filename is None -# assert profile.profile is not None -# assert profile.product_name is None -# assert profile.product_info is None \ No newline at end of file diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 1863c20a9..d68cdfb0f 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -397,3 +397,12 @@ class TestPyEncoder(CodecsTest): def test_zero_height(self) -> None: with pytest.raises(UnidentifiedImageError): Image.open("Tests/images/zero_height.j2k") + + def test_encode_to_file_branches(self) -> None: + mock_file = BytesIO() + encoder = ImageFile.PyEncoder("RGB") + encoder.branches = {"1": False, "2": False} + errcode = encoder.encode_to_file(mock_file, 1024) + assert encoder.branches["1"] is True + assert encoder.branches["2"] is True + assert errcode == 0 diff --git a/src/PIL/test_ImageFile.py b/src/PIL/test_ImageFile.py new file mode 100644 index 000000000..1c43a38d6 --- /dev/null +++ b/src/PIL/test_ImageFile.py @@ -0,0 +1,25 @@ +from io import BytesIO +import pytest +from PIL import ImageFile + +def test_encode_to_file_branches(): + # Create a mock file object + mock_file = io.BytesIO() + + # Create a PyEncoder instance + encoder = ImageFile.PyEncoder("RGB") + + # Set the branches dictionary to False to ensure both branches are covered + encoder.branches = {"1": False, "2": False} + + # Call the encode_to_file method + errcode = encoder.encode_to_file(mock_file, 1024) + + # Check that the branches dictionary has been updated + assert encoder.branches["1"] is True + assert encoder.branches["2"] is True + + # Check that the error code is 0, indicating successful encoding + assert errcode == 0 + + mock_file = BytesIO() \ No newline at end of file From 1881f22e89a23a4f54f5118e1ef7351d5fe8753b Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Sat, 22 Jun 2024 00:31:48 +0200 Subject: [PATCH 15/26] changes made to imagefile --- Tests/test_imagefile.py | 57 +++++++++++++++++++++++++++++++++-------- conftest.py | 2 +- src/PIL/ImageCms.py | 31 +++++----------------- 3 files changed, 53 insertions(+), 37 deletions(-) diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index d68cdfb0f..697b1a24c 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -218,16 +218,60 @@ class MockPyEncoder(ImageFile.PyEncoder): last: MockPyEncoder | None def __init__(self, mode: str, *args: Any) -> None: - MockPyEncoder.last = self - super().__init__(mode, *args) + self._pushes_fd = False + self.cleanup_called = False def encode(self, buffer): + # Simulate encoding + if buffer is None: + raise NotImplementedError return 1, 1, b"" def cleanup(self) -> None: self.cleanup_called = True +def test_encode_to_file() -> None: + encoder = MockPyEncoder("RGBA") + + # Case: _pushes_fd is False + with pytest.raises(NotImplementedError): + encoder.encode_to_file(None, None) + + # Case: _pushes_fd is True + encoder._pushes_fd = True + with pytest.raises(NotImplementedError): + encoder.encode_to_file(None, None) + + # Case: encode method called with buffer (no exception) + buffer = BytesIO(b"\x00" * 10) + encoder._pushes_fd = False + encoder.encode = lambda buffer: (1, 1, b"") + try: + encoder.encode_to_file(buffer, None) + except NotImplementedError: + pass # NotImplementedError is expected + + # Case: encode method raises NotImplementedError + encoder.encode = lambda buffer: (_ for _ in ()).throw(NotImplementedError) + with pytest.raises(NotImplementedError): + encoder.encode_to_file(buffer, None) + + # Case: cleanup is called after exception + encoder.encode = lambda buffer: (_ for _ in ()).throw(ValueError) + with pytest.raises(ValueError): + encoder.encode_to_file(buffer, None) + assert encoder.cleanup_called + + # Case: encode returns unexpected values + encoder.encode = lambda buffer: (None, None, None) + with pytest.raises(ValueError): + encoder.encode_to_file(buffer, None) + + # Case: UnidentifiedImageError + with pytest.raises(UnidentifiedImageError): + encoder.encode_to_file(buffer, BytesIO(b"\x00" * 10)) + xoff, yoff, xsize, ysize = 10, 20, 100, 100 @@ -397,12 +441,3 @@ class TestPyEncoder(CodecsTest): def test_zero_height(self) -> None: with pytest.raises(UnidentifiedImageError): Image.open("Tests/images/zero_height.j2k") - - def test_encode_to_file_branches(self) -> None: - mock_file = BytesIO() - encoder = ImageFile.PyEncoder("RGB") - encoder.branches = {"1": False, "2": False} - errcode = encoder.encode_to_file(mock_file, 1024) - assert encoder.branches["1"] is True - assert encoder.branches["2"] is True - assert errcode == 0 diff --git a/conftest.py b/conftest.py index 8d1320209..e61b5a270 100644 --- a/conftest.py +++ b/conftest.py @@ -15,7 +15,7 @@ def calculate_coverage(test_name): all_branches = { "branches1": Image.branches, "branches2": PdfParser.XrefTable.branches, - "branches3": ImageCms.ImageCmsProfile.branches, + "branches3": ImageCms.PyCMSError.branches, "branches4": McIdasImagePlugin.McIdasImageFile.branches, "branches5": ImageFile.PyEncoder.branches, # Add more diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 0b2702d70..ae4e4353d 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -32,12 +32,6 @@ from ._typing import SupportsRead branches = { "1": False, "2": False, - "3": False, - "4": False, - "5": False, - "6": False, - "7": False, - "8": False, } try: @@ -248,17 +242,6 @@ _FLAGS = { class ImageCmsProfile: - branches = { - "1": False, - "2": False, - "3": False, - "4": False, - "5": False, - "6": False, - "7": False, - "8": False, - } - def __init__(self, profile: str | SupportsRead[bytes] | core.CmsProfile) -> None: """ :param profile: Either a string representing a filename, @@ -268,28 +251,20 @@ class ImageCmsProfile: """ if isinstance(profile, str): - ImageCmsProfile.branches["1"] = True if sys.platform == "win32": - ImageCmsProfile.branches["2"] = True profile_bytes_path = profile.encode() try: - ImageCmsProfile.branches["3"] = True profile_bytes_path.decode("ascii") except UnicodeDecodeError: - ImageCmsProfile.branches["4"] = True with open(profile, "rb") as f: - ImageCmsProfile.branches["5"] = True self._set(core.profile_frombytes(f.read())) return self._set(core.profile_open(profile), profile) elif hasattr(profile, "read"): - ImageCmsProfile.branches["6"] = True self._set(core.profile_frombytes(profile.read())) elif isinstance(profile, core.CmsProfile): - ImageCmsProfile.branches["7"] = True self._set(profile) else: - ImageCmsProfile.branches["8"] = True msg = "Invalid type for Profile" # type: ignore[unreachable] raise TypeError(msg) @@ -403,6 +378,10 @@ _CmsProfileCompatible = Union[ class PyCMSError(Exception): + branches = { + "1": False, + "2": False, + } """(pyCMS) Exception class. This is used for all errors in the pyCMS API.""" @@ -524,8 +503,10 @@ def getOpenProfile( """ try: + PyCMSError.branches["1"] = True return ImageCmsProfile(profileFilename) except (OSError, TypeError, ValueError) as v: + PyCMSError.branches["2"] = True raise PyCMSError(v) from v From b72a607b01dc6c4d144fa2b041c594ad63e42801 Mon Sep 17 00:00:00 2001 From: Duru Date: Sat, 22 Jun 2024 01:30:13 +0200 Subject: [PATCH 16/26] branchcov fix --- Tests/images/mcidas_mode_L.ara | Bin 0 -> 10264 bytes Tests/test_imagefile.py | 19 ++----------------- 2 files changed, 2 insertions(+), 17 deletions(-) create mode 100644 Tests/images/mcidas_mode_L.ara diff --git a/Tests/images/mcidas_mode_L.ara b/Tests/images/mcidas_mode_L.ara new file mode 100644 index 0000000000000000000000000000000000000000..a5bc09f3eb0afd6fb22ec67a6979a2c88a9125aa GIT binary patch literal 10264 zcmeIuu?YY$2n0agHT%zyWP~p)gcI!kMXoBE^U{AiYDM9})6pNo1PBlyK!5-N0t5&U kAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyKwzK11*OOU%K!iX literal 0 HcmV?d00001 diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 697b1a24c..b5e42391b 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -215,12 +215,13 @@ class MockPyDecoder(ImageFile.PyDecoder): class MockPyEncoder(ImageFile.PyEncoder): - last: MockPyEncoder | None + last = None # Add this line def __init__(self, mode: str, *args: Any) -> None: super().__init__(mode, *args) self._pushes_fd = False self.cleanup_called = False + MockPyEncoder.last = self # Update this line def encode(self, buffer): # Simulate encoding @@ -257,22 +258,6 @@ def test_encode_to_file() -> None: with pytest.raises(NotImplementedError): encoder.encode_to_file(buffer, None) - # Case: cleanup is called after exception - encoder.encode = lambda buffer: (_ for _ in ()).throw(ValueError) - with pytest.raises(ValueError): - encoder.encode_to_file(buffer, None) - assert encoder.cleanup_called - - # Case: encode returns unexpected values - encoder.encode = lambda buffer: (None, None, None) - with pytest.raises(ValueError): - encoder.encode_to_file(buffer, None) - - # Case: UnidentifiedImageError - with pytest.raises(UnidentifiedImageError): - encoder.encode_to_file(buffer, BytesIO(b"\x00" * 10)) - - xoff, yoff, xsize, ysize = 10, 20, 100, 100 From 8e3cf219543fa482df8dd4ec29a1e2ae95bb50d8 Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Sat, 22 Jun 2024 01:31:43 +0200 Subject: [PATCH 17/26] test fixing --- Tests/test_imagecms.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 2dac43ee5..8a9e82d99 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -39,7 +39,6 @@ HAVE_PROFILE = os.path.exists(SRGB) def setup_module() -> None: try: from PIL import ImageCms - # need to hit getattr to trigger the delayed import error ImageCms.core.profile_open except ImportError as v: @@ -701,3 +700,26 @@ def test_deprecation() -> None: assert isinstance(ImageCms.FLAGS, dict) +class TestGetOpenProfile: + def test_get_open_profile_with_string_path(self, tmp_path: Path) -> None: + # Create a dummy profile file + dummy_profile_path = tmp_path / "dummy.icc" + dummy_profile_path.write_bytes(b"Dummy ICC content") + + # Test with a string path + profile = ImageCms.getOpenProfile(str(dummy_profile_path)) + assert isinstance(profile, ImageCms.ImageCmsProfile) + + def test_get_open_profile_with_file_object(self, tmp_path: Path) -> None: + # Create a dummy profile file content + dummy_profile_content = b"Dummy ICC content" + dummy_profile_file = BytesIO(dummy_profile_content) + + # Test with a file-like object + profile = ImageCms.getOpenProfile(dummy_profile_file) + assert isinstance(profile, ImageCms.ImageCmsProfile) + + def test_get_open_profile_with_invalid_input(self) -> None: + # Test with invalid input + with pytest.raises(TypeError): + ImageCms.getOpenProfile(123) # Assuming passing an integer is invalid \ No newline at end of file From f206a1c6179b939a8ec1607ef0f8a2fd434916df Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Sat, 22 Jun 2024 01:33:15 +0200 Subject: [PATCH 18/26] imagefile testing improvement final --- Tests/test_imagefile.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index b5e42391b..a4e2b5152 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -235,25 +235,21 @@ class MockPyEncoder(ImageFile.PyEncoder): def test_encode_to_file() -> None: encoder = MockPyEncoder("RGBA") - # Case: _pushes_fd is False with pytest.raises(NotImplementedError): encoder.encode_to_file(None, None) - # Case: _pushes_fd is True encoder._pushes_fd = True with pytest.raises(NotImplementedError): encoder.encode_to_file(None, None) - # Case: encode method called with buffer (no exception) buffer = BytesIO(b"\x00" * 10) encoder._pushes_fd = False encoder.encode = lambda buffer: (1, 1, b"") try: encoder.encode_to_file(buffer, None) except NotImplementedError: - pass # NotImplementedError is expected + pass - # Case: encode method raises NotImplementedError encoder.encode = lambda buffer: (_ for _ in ()).throw(NotImplementedError) with pytest.raises(NotImplementedError): encoder.encode_to_file(buffer, None) From 293ea08ece7fc5294f009422ce8f7134c3a25263 Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Sat, 22 Jun 2024 14:23:01 +0200 Subject: [PATCH 19/26] imagetk.py testing imp draft --- Tests/test_imagecms.py | 25 ---------------------- Tests/test_imagefile.py | 4 ++-- Tests/test_imagetk.py | 40 ++++++++++++++++++++++++++++-------- conftest.py | 8 +++----- src/PIL/ImageTk.py | 10 +++++++++ src/PIL/McIdasImagePlugin.py | 21 ------------------- 6 files changed, 46 insertions(+), 62 deletions(-) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 8a9e82d99..6b237378d 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -698,28 +698,3 @@ def test_deprecation() -> None: assert ImageCms.VERSION == "1.0.0 pil" with pytest.warns(DeprecationWarning): assert isinstance(ImageCms.FLAGS, dict) - - -class TestGetOpenProfile: - def test_get_open_profile_with_string_path(self, tmp_path: Path) -> None: - # Create a dummy profile file - dummy_profile_path = tmp_path / "dummy.icc" - dummy_profile_path.write_bytes(b"Dummy ICC content") - - # Test with a string path - profile = ImageCms.getOpenProfile(str(dummy_profile_path)) - assert isinstance(profile, ImageCms.ImageCmsProfile) - - def test_get_open_profile_with_file_object(self, tmp_path: Path) -> None: - # Create a dummy profile file content - dummy_profile_content = b"Dummy ICC content" - dummy_profile_file = BytesIO(dummy_profile_content) - - # Test with a file-like object - profile = ImageCms.getOpenProfile(dummy_profile_file) - assert isinstance(profile, ImageCms.ImageCmsProfile) - - def test_get_open_profile_with_invalid_input(self) -> None: - # Test with invalid input - with pytest.raises(TypeError): - ImageCms.getOpenProfile(123) # Assuming passing an integer is invalid \ No newline at end of file diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index a4e2b5152..1d528b49f 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -215,13 +215,13 @@ class MockPyDecoder(ImageFile.PyDecoder): class MockPyEncoder(ImageFile.PyEncoder): - last = None # Add this line + last = None def __init__(self, mode: str, *args: Any) -> None: super().__init__(mode, *args) self._pushes_fd = False self.cleanup_called = False - MockPyEncoder.last = self # Update this line + MockPyEncoder.last = self def encode(self, buffer): # Simulate encoding diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py index 027ab74aa..ec296ca88 100644 --- a/Tests/test_imagetk.py +++ b/Tests/test_imagetk.py @@ -1,25 +1,23 @@ from __future__ import annotations +import gc import pytest +import tkinter as tk +from unittest import mock from PIL import Image +from PIL import ImageTk from .helper import assert_image_equal, hopper +TK_MODES = ("1", "L", "P", "RGB", "RGBA") + try: - import tkinter as tk - - from PIL import ImageTk - dir(ImageTk) HAS_TK = True except (OSError, ImportError): - # Skipped via pytestmark HAS_TK = False -TK_MODES = ("1", "L", "P", "RGB", "RGBA") - - pytestmark = pytest.mark.skipif(not HAS_TK, reason="Tk not installed") @@ -27,7 +25,6 @@ def setup_module() -> None: try: # setup tk tk.Frame() - # root = tk.Tk() except RuntimeError as v: pytest.skip(f"RuntimeError: {v}") except tk.TclError as v: @@ -102,3 +99,28 @@ def test_bitmapimage() -> None: # reloaded = ImageTk.getimage(im_tk) # assert_image_equal(reloaded, im) + + +def test_bitmapimage_del() -> None: + # Set up an image + im = Image.new("1", (10, 10)) + + # Mock the tkinter PhotoImage to track calls + with mock.patch.object(tk, 'BitmapImage', wraps=tk.BitmapImage) as mock_bitmapimage: + # Create an instance of BitmapImage + bitmap_image = ImageTk.BitmapImage(im) + + # Ensure the BitmapImage was created + assert mock_bitmapimage.call_count == 1 + + # Get the internal Tkinter image object + tk_image = bitmap_image._BitmapImage__photo + + # Mock the tk.call method to track the 'image delete' call + with mock.patch.object(tk_image.tk, 'call', wraps=tk_image.tk.call) as mock_tk_call: + # Delete the instance and force garbage collection + del bitmap_image + gc.collect() + + # Check that the 'image delete' command was called + mock_tk_call.assert_any_call("image", "delete", tk_image.name) diff --git a/conftest.py b/conftest.py index e61b5a270..72eb24dba 100644 --- a/conftest.py +++ b/conftest.py @@ -4,8 +4,7 @@ import sys from PIL import Image from PIL import PdfParser -from PIL import ImageCms -from PIL import McIdasImagePlugin +from PIL import ImageTk from PIL import ImageFile pytest_plugins = ["Tests.helper"] @@ -15,9 +14,8 @@ def calculate_coverage(test_name): all_branches = { "branches1": Image.branches, "branches2": PdfParser.XrefTable.branches, - "branches3": ImageCms.PyCMSError.branches, - "branches4": McIdasImagePlugin.McIdasImageFile.branches, - "branches5": ImageFile.PyEncoder.branches, + "branches3": ImageTk.BitmapImage.branches, + "branches4": ImageFile.PyEncoder.branches, # Add more } diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index 87176ae54..222428ed8 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -31,6 +31,10 @@ from io import BytesIO from . import Image +branches = { + "1": False, + "2": False, +} # -------------------------------------------------------------------- # Check for Tkinter interface hooks @@ -188,6 +192,10 @@ class PhotoImage: class BitmapImage: + branches = { + "1": False, + "2": False, +} """ A Tkinter-compatible bitmap image. This can be used everywhere Tkinter expects an image object. @@ -223,8 +231,10 @@ class BitmapImage: name = self.__photo.name self.__photo.name = None try: + BitmapImage.branches["1"] = True self.__photo.tk.call("image", "delete", name) except Exception: + BitmapImage pass # ignore internal errors def width(self) -> int: diff --git a/src/PIL/McIdasImagePlugin.py b/src/PIL/McIdasImagePlugin.py index 296e818ed..d7511b3bb 100644 --- a/src/PIL/McIdasImagePlugin.py +++ b/src/PIL/McIdasImagePlugin.py @@ -21,14 +21,6 @@ import struct from . import Image, ImageFile -branches = { - "1": False, - "2": False, - "3": False, - "4": False, - "5": False, - } - def _accept(prefix: bytes) -> bool: return prefix[:8] == b"\x00\x00\x00\x00\x00\x00\x00\x04" @@ -38,14 +30,6 @@ def _accept(prefix: bytes) -> bool: class McIdasImageFile(ImageFile.ImageFile): - branches = { - "1": False, - "2": False, - "3": False, - "4": False, - "5": False, - } - format = "MCIDAS" format_description = "McIdas area file" @@ -55,7 +39,6 @@ class McIdasImageFile(ImageFile.ImageFile): s = self.fp.read(256) if not _accept(s) or len(s) != 256: - McIdasImageFile.branches["1"] = True msg = "not an McIdas area file" raise SyntaxError(msg) @@ -64,20 +47,16 @@ class McIdasImageFile(ImageFile.ImageFile): # get mode if w[11] == 1: - McIdasImageFile.branches["2"] = True mode = rawmode = "L" elif w[11] == 2: - McIdasImageFile.branches["3"] = True # FIXME: add memory map support mode = "I" rawmode = "I;16B" elif w[11] == 4: - McIdasImageFile.branches["4"] = True # FIXME: add memory map support mode = "I" rawmode = "I;32B" else: - McIdasImageFile.branches["5"] = True msg = "unsupported McIdas format" raise SyntaxError(msg) From 78f600acc3d285a0b6fc3ac3829688ba4de1c89e Mon Sep 17 00:00:00 2001 From: dutcu Date: Sat, 22 Jun 2024 16:42:14 +0200 Subject: [PATCH 20/26] branches fix --- src/PIL/ImageTk.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index 222428ed8..157dff57f 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -234,6 +234,7 @@ class BitmapImage: BitmapImage.branches["1"] = True self.__photo.tk.call("image", "delete", name) except Exception: + BitmapImage.branches["2"] = True BitmapImage pass # ignore internal errors From 379b3922f18e03485c2c73b1e81dd552f6f36bf4 Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Sat, 22 Jun 2024 16:49:55 +0200 Subject: [PATCH 21/26] imagetk testing draft --- Tests/test_imagetk.py | 29 ++++++++--------------------- src/PIL/ImageTk.py | 1 - 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py index ec296ca88..b8ca9f122 100644 --- a/Tests/test_imagetk.py +++ b/Tests/test_imagetk.py @@ -1,6 +1,5 @@ from __future__ import annotations -import gc import pytest import tkinter as tk from unittest import mock @@ -101,26 +100,14 @@ def test_bitmapimage() -> None: # assert_image_equal(reloaded, im) -def test_bitmapimage_del() -> None: - # Set up an image - im = Image.new("1", (10, 10)) +def test_photoimage_del() -> None: + # Create a dummy PIL image + im = Image.new("RGB", (100, 100)) - # Mock the tkinter PhotoImage to track calls - with mock.patch.object(tk, 'BitmapImage', wraps=tk.BitmapImage) as mock_bitmapimage: - # Create an instance of BitmapImage - bitmap_image = ImageTk.BitmapImage(im) - - # Ensure the BitmapImage was created - assert mock_bitmapimage.call_count == 1 + # Create a PhotoImage object + photo = ImageTk.PhotoImage(im) - # Get the internal Tkinter image object - tk_image = bitmap_image._BitmapImage__photo - - # Mock the tk.call method to track the 'image delete' call - with mock.patch.object(tk_image.tk, 'call', wraps=tk_image.tk.call) as mock_tk_call: - # Delete the instance and force garbage collection - del bitmap_image - gc.collect() + # Delete the PhotoImage object + del photo - # Check that the 'image delete' command was called - mock_tk_call.assert_any_call("image", "delete", tk_image.name) + # No assertion needed, just ensuring that the __del__ method is executed \ No newline at end of file diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index 157dff57f..ebe33705a 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -235,7 +235,6 @@ class BitmapImage: self.__photo.tk.call("image", "delete", name) except Exception: BitmapImage.branches["2"] = True - BitmapImage pass # ignore internal errors def width(self) -> int: From dfea906a80b77f36bf50a88787d127d0aa32ee62 Mon Sep 17 00:00:00 2001 From: dutcu Date: Sat, 22 Jun 2024 17:10:55 +0200 Subject: [PATCH 22/26] easda --- Tests/test_imagetk.py | 14 +++----------- conftest.py | 1 - src/PIL/ImageTk.py | 11 +---------- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py index b8ca9f122..f6bbe39fd 100644 --- a/Tests/test_imagetk.py +++ b/Tests/test_imagetk.py @@ -7,6 +7,9 @@ from unittest import mock from PIL import Image from PIL import ImageTk +from unittest.mock import patch + + from .helper import assert_image_equal, hopper TK_MODES = ("1", "L", "P", "RGB", "RGBA") @@ -100,14 +103,3 @@ def test_bitmapimage() -> None: # assert_image_equal(reloaded, im) -def test_photoimage_del() -> None: - # Create a dummy PIL image - im = Image.new("RGB", (100, 100)) - - # Create a PhotoImage object - photo = ImageTk.PhotoImage(im) - - # Delete the PhotoImage object - del photo - - # No assertion needed, just ensuring that the __del__ method is executed \ No newline at end of file diff --git a/conftest.py b/conftest.py index dbb166eda..581940539 100644 --- a/conftest.py +++ b/conftest.py @@ -20,7 +20,6 @@ def calculate_coverage(test_name): "branches2": PdfParser.XrefTable.branches, # duru "branches3": SpiderImagePlugin.branches, # isidora "branches4": MpegImagePlugin.BitStream.branches, # isidora - "branches5": ImageTk.BitmapImage.branches, # deekshu "branches6": ImageFile.PyEncoder.branches, # deekshu # Add more } diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index ebe33705a..f5ca8862f 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -31,10 +31,6 @@ from io import BytesIO from . import Image -branches = { - "1": False, - "2": False, -} # -------------------------------------------------------------------- # Check for Tkinter interface hooks @@ -192,10 +188,7 @@ class PhotoImage: class BitmapImage: - branches = { - "1": False, - "2": False, -} + """ A Tkinter-compatible bitmap image. This can be used everywhere Tkinter expects an image object. @@ -231,10 +224,8 @@ class BitmapImage: name = self.__photo.name self.__photo.name = None try: - BitmapImage.branches["1"] = True self.__photo.tk.call("image", "delete", name) except Exception: - BitmapImage.branches["2"] = True pass # ignore internal errors def width(self) -> int: From 26848c48a717b7757699e8d0e45b5f55c7459b64 Mon Sep 17 00:00:00 2001 From: dutcu Date: Sat, 22 Jun 2024 17:28:37 +0200 Subject: [PATCH 23/26] branch --- Tests/test_new_cms.py | 38 ++++++++++++++++++++++++++++++++++++++ conftest.py | 3 ++- 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 Tests/test_new_cms.py diff --git a/Tests/test_new_cms.py b/Tests/test_new_cms.py new file mode 100644 index 000000000..024a30213 --- /dev/null +++ b/Tests/test_new_cms.py @@ -0,0 +1,38 @@ +import pytest +from PIL import ImageCms + +def test_buildTransform_flags_non_integer(): + with pytest.raises(ImageCms.PyCMSError): + ImageCms.buildTransform( + inputProfile="path/to/input/profile", + outputProfile="path/to/output/profile", + inMode="RGB", + outMode="CMYK", + renderingIntent=ImageCms.Intent.PERCEPTUAL, + flags="not_an_integer" # This should not be an integer + ) + +def test_buildTransform_flags_out_of_range(): + with pytest.raises(ImageCms.PyCMSError): + ImageCms.buildTransform( + inputProfile="path/to/input/profile", + outputProfile="path/to/output/profile", + inMode="RGB", + outMode="CMYK", + renderingIntent=ImageCms.Intent.PERCEPTUAL, + flags=999999 # Assuming this value is outside the valid range + ) + +def test_renderingIntent_non_integer(): + with pytest.raises(ImageCms.PyCMSError) as exc_info: + ImageCms.buildTransform( + inputProfile="path/to/input/profile", + outputProfile="path/to/output/profile", + inMode="RGB", + outMode="CMYK", + renderingIntent="not an integer", # This should trigger the error + flags=0 + ) + assert str(exc_info.value) == "renderingIntent must be an integer between 0 and 3" + + \ No newline at end of file diff --git a/conftest.py b/conftest.py index 581940539..adf552ed8 100644 --- a/conftest.py +++ b/conftest.py @@ -20,7 +20,8 @@ def calculate_coverage(test_name): "branches2": PdfParser.XrefTable.branches, # duru "branches3": SpiderImagePlugin.branches, # isidora "branches4": MpegImagePlugin.BitStream.branches, # isidora - "branches6": ImageFile.PyEncoder.branches, # deekshu + "branches5": ImageCms.branches, # deekshu + "branches6": ImageFile.branches, # deekshu # Add more } From b235d0e1c19c64870155094e98477ea9377655f2 Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Sat, 22 Jun 2024 17:30:46 +0200 Subject: [PATCH 24/26] imagecms.py test drafting --- conftest.py | 4 +--- src/PIL/ImageCms.py | 36 +++++++++++------------------------- src/PIL/ImageTk.py | 1 - 3 files changed, 12 insertions(+), 29 deletions(-) diff --git a/conftest.py b/conftest.py index adf552ed8..31630fb64 100644 --- a/conftest.py +++ b/conftest.py @@ -7,8 +7,6 @@ from PIL import PdfParser from PIL import SpiderImagePlugin from PIL import MpegImagePlugin from PIL import ImageCms -from PIL import McIdasImagePlugin -from PIL import ImageTk from PIL import ImageFile pytest_plugins = ["Tests.helper"] @@ -21,7 +19,7 @@ def calculate_coverage(test_name): "branches3": SpiderImagePlugin.branches, # isidora "branches4": MpegImagePlugin.BitStream.branches, # isidora "branches5": ImageCms.branches, # deekshu - "branches6": ImageFile.branches, # deekshu + "branches6": ImageFile.PyEncoder.branches, # deekshu # Add more } diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 96160c563..dad70f3e9 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -32,6 +32,10 @@ from ._typing import SupportsRead branches = { "1": False, "2": False, + "3": False, + "4": False, + "5": False, + "6": False, } try: @@ -242,17 +246,6 @@ _FLAGS = { class ImageCmsProfile: - branches = { - "1": False, - "2": False, - "3": False, - "4": False, - "5": False, - "6": False, - "7": False, - "8": False, - } - def __init__(self, profile: str | SupportsRead[bytes] | core.CmsProfile) -> None: """ :param profile: Either a string representing a filename, @@ -262,28 +255,21 @@ class ImageCmsProfile: """ if isinstance(profile, str): - ImageCmsProfile.branches["1"] = True if sys.platform == "win32": - ImageCmsProfile.branches["2"] = True profile_bytes_path = profile.encode() try: - ImageCmsProfile.branches["3"] = True profile_bytes_path.decode("ascii") except UnicodeDecodeError: - ImageCmsProfile.branches["4"] = True with open(profile, "rb") as f: - ImageCmsProfile.branches["5"] = True self._set(core.profile_frombytes(f.read())) return self._set(core.profile_open(profile), profile) elif hasattr(profile, "read"): - ImageCmsProfile.branches["6"] = True self._set(core.profile_frombytes(profile.read())) elif isinstance(profile, core.CmsProfile): - ImageCmsProfile.branches["7"] = True self._set(profile) else: - ImageCmsProfile.branches["8"] = True + msg = "Invalid type for Profile" # type: ignore[unreachable] raise TypeError(msg) @@ -397,10 +383,6 @@ _CmsProfileCompatible = Union[ class PyCMSError(Exception): - branches = { - "1": False, - "2": False, - } """(pyCMS) Exception class. This is used for all errors in the pyCMS API.""" @@ -522,10 +504,8 @@ def getOpenProfile( """ try: - PyCMSError.branches["1"] = True return ImageCmsProfile(profileFilename) except (OSError, TypeError, ValueError) as v: - PyCMSError.branches["2"] = True raise PyCMSError(v) from v @@ -593,22 +573,28 @@ def buildTransform( """ if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): + branches["1"] = True msg = "renderingIntent must be an integer between 0 and 3" raise PyCMSError(msg) if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): + branches["2"] = True msg = f"flags must be an integer between 0 and {_MAX_FLAG}" raise PyCMSError(msg) try: + branches["3"] = True if not isinstance(inputProfile, ImageCmsProfile): + branches["4"] = True inputProfile = ImageCmsProfile(inputProfile) if not isinstance(outputProfile, ImageCmsProfile): + branches["5"] = True outputProfile = ImageCmsProfile(outputProfile) return ImageCmsTransform( inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags ) except (OSError, TypeError, ValueError) as v: + branches["6"] = True raise PyCMSError(v) from v diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index f5ca8862f..87176ae54 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -188,7 +188,6 @@ class PhotoImage: class BitmapImage: - """ A Tkinter-compatible bitmap image. This can be used everywhere Tkinter expects an image object. From 8d623b9fd104712c14c82c5365c54ab2307225d5 Mon Sep 17 00:00:00 2001 From: dutcu Date: Sat, 22 Jun 2024 17:39:51 +0200 Subject: [PATCH 25/26] branches fix --- src/PIL/ImageFile.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 08c746efe..a2283a1d9 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -39,10 +39,6 @@ from . import Image from ._deprecate import deprecate from ._util import is_path -branches = { - "1": False, - "2": False, -} MAXBLOCK = 65536 From aad549687a1cf9528453eec5e9344192dce388e1 Mon Sep 17 00:00:00 2001 From: Deekshu Kare Date: Wed, 26 Jun 2024 21:40:26 +0200 Subject: [PATCH 26/26] last minute clean ups --- Tests/test_frombytes_image.py | 64 ----------------------------------- Tests/test_imagecms.py | 34 +++++++++++++++++++ Tests/test_new_cms.py | 38 --------------------- 3 files changed, 34 insertions(+), 102 deletions(-) delete mode 100644 Tests/test_frombytes_image.py delete mode 100644 Tests/test_new_cms.py diff --git a/Tests/test_frombytes_image.py b/Tests/test_frombytes_image.py deleted file mode 100644 index 77e3363a1..000000000 --- a/Tests/test_frombytes_image.py +++ /dev/null @@ -1,64 +0,0 @@ -import unittest -from PIL import Image -import unittest - - -class TestFromBytes(unittest.TestCase): - def test_frombytes(self): - # Test case 1: Empty bytes - data = b"" - image = Image.frombytes("RGB", (0, 0), data) - self.assertEqual(image.size, (0, 0)) - - # Test case 2: Non-empty bytes - data = b"\x00\x00\xFF\xFF\x00\x00" - image = Image.frombytes("RGB", (2, 1), data) - self.assertEqual(image.size, (2, 1)) - self.assertEqual(image.getpixel((0, 0)), (0, 0, 255)) - self.assertEqual(image.getpixel((1, 0)), (255, 0, 0)) - - # Test case 3: Invalid mode - data = b"\x00\x00\xFF\xFF\x00\x00" - with self.assertRaises(ValueError): - Image.frombytes("RGBA", (2, 1), data) - - # Test case 4: Non-RGB mode - data = b"\x00\x00\xFF\xFF\x00\x00" - image = Image.frombytes("L", (2, 1), data) - self.assertEqual(image.size, (2, 1)) - self.assertEqual(image.getpixel((0, 0)), 0) - # self.assertEqual(image.getpixel((1, 0)), 255) - - # Test case 5: Zero width - data = b"" - image = Image.frombytes("RGB", (0, 1), data) - self.assertEqual(image.size, (0, 1)) - - # Test case 6: Zero height - data = b"" - image = Image.frombytes("RGB", (1, 0), data) - self.assertEqual(image.size, (1, 0)) - - # Test case 7: s[0] < 0 - data = b"\x00\x00\xFF\xFF\x00\x00" - s = (-1, 1) - with self.assertRaises(ValueError): - Image.frombytes("RGB", s, data) - - # Test case 8: s[1] == 0 - data = b"\x00\x00\xFF\xFF\x00\x00" - s = (2, 0) - # with self.assertRaises(ValueError): - # Image.frombytes("RGB", s, data) - - # Test case 5: Different size - data = b"\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" - image = Image.frombytes("RGB", (3, 1), data) - self.assertEqual(image.size, (3, 1)) - self.assertEqual(image.getpixel((0, 0)), (0, 0, 255)) - self.assertEqual(image.getpixel((1, 0)), (255, 0, 0)) - # self.assertEqual(image.getpixel((2, 0)), (255, 0, 0)) - -if __name__ == "__main__": - unittest.main() - diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 6b237378d..899ec5b01 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -698,3 +698,37 @@ def test_deprecation() -> None: assert ImageCms.VERSION == "1.0.0 pil" with pytest.warns(DeprecationWarning): assert isinstance(ImageCms.FLAGS, dict) + +def test_buildTransform_flags_non_integer(): + with pytest.raises(ImageCms.PyCMSError): + ImageCms.buildTransform( + inputProfile="path/to/input/profile", + outputProfile="path/to/output/profile", + inMode="RGB", + outMode="CMYK", + renderingIntent=ImageCms.Intent.PERCEPTUAL, + flags=123 + ) + +def test_buildTransform_flags_invalid(): + with pytest.raises(ImageCms.PyCMSError): + ImageCms.buildTransform( + inputProfile="path/to/input/profile", + outputProfile="path/to/output/profile", + inMode="RGB", + outMode="CMYK", + renderingIntent=ImageCms.Intent.PERCEPTUAL, + flags=999999 + ) + +def test_rendering_intent_non_integer(): + with pytest.raises(ImageCms.PyCMSError) as exc_info: + ImageCms.buildTransform( + inputProfile="path/to/input/profile", + outputProfile="path/to/output/profile", + inMode="RGB", + outMode="CMYK", + renderingIntent="not an integer", + flags=0 + ) + assert str(exc_info.value) == "renderingIntent must be an integer between 0 and 3" diff --git a/Tests/test_new_cms.py b/Tests/test_new_cms.py deleted file mode 100644 index 024a30213..000000000 --- a/Tests/test_new_cms.py +++ /dev/null @@ -1,38 +0,0 @@ -import pytest -from PIL import ImageCms - -def test_buildTransform_flags_non_integer(): - with pytest.raises(ImageCms.PyCMSError): - ImageCms.buildTransform( - inputProfile="path/to/input/profile", - outputProfile="path/to/output/profile", - inMode="RGB", - outMode="CMYK", - renderingIntent=ImageCms.Intent.PERCEPTUAL, - flags="not_an_integer" # This should not be an integer - ) - -def test_buildTransform_flags_out_of_range(): - with pytest.raises(ImageCms.PyCMSError): - ImageCms.buildTransform( - inputProfile="path/to/input/profile", - outputProfile="path/to/output/profile", - inMode="RGB", - outMode="CMYK", - renderingIntent=ImageCms.Intent.PERCEPTUAL, - flags=999999 # Assuming this value is outside the valid range - ) - -def test_renderingIntent_non_integer(): - with pytest.raises(ImageCms.PyCMSError) as exc_info: - ImageCms.buildTransform( - inputProfile="path/to/input/profile", - outputProfile="path/to/output/profile", - inMode="RGB", - outMode="CMYK", - renderingIntent="not an integer", # This should trigger the error - flags=0 - ) - assert str(exc_info.value) == "renderingIntent must be an integer between 0 and 3" - - \ No newline at end of file