Merge pull request #7 from jovanovicisidora/branch_deekshu

Deekshu's Merge to Main
This commit is contained in:
Isidora Jovanovic 2024-06-27 01:11:51 +02:00 committed by GitHub
commit 86db022d48
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 153 additions and 130 deletions

Binary file not shown.

View File

@ -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()

View File

@ -39,7 +39,6 @@ HAVE_PROFILE = os.path.exists(SRGB)
def setup_module() -> None: def setup_module() -> None:
try: try:
from PIL import ImageCms from PIL import ImageCms
# need to hit getattr to trigger the delayed import error # need to hit getattr to trigger the delayed import error
ImageCms.core.profile_open ImageCms.core.profile_open
except ImportError as v: except ImportError as v:
@ -699,3 +698,37 @@ def test_deprecation() -> None:
assert ImageCms.VERSION == "1.0.0 pil" assert ImageCms.VERSION == "1.0.0 pil"
with pytest.warns(DeprecationWarning): with pytest.warns(DeprecationWarning):
assert isinstance(ImageCms.FLAGS, dict) 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"

View File

@ -215,19 +215,44 @@ class MockPyDecoder(ImageFile.PyDecoder):
class MockPyEncoder(ImageFile.PyEncoder): class MockPyEncoder(ImageFile.PyEncoder):
last: MockPyEncoder | None last = None
def __init__(self, mode: str, *args: Any) -> None: def __init__(self, mode: str, *args: Any) -> None:
super().__init__(mode, *args)
self._pushes_fd = False
self.cleanup_called = False
MockPyEncoder.last = self MockPyEncoder.last = self
super().__init__(mode, *args)
def encode(self, buffer): def encode(self, buffer):
# Simulate encoding
if buffer is None:
raise NotImplementedError
return 1, 1, b"" return 1, 1, b""
def cleanup(self) -> None: def cleanup(self) -> None:
self.cleanup_called = True self.cleanup_called = True
def test_encode_to_file() -> None:
encoder = MockPyEncoder("RGBA")
with pytest.raises(NotImplementedError):
encoder.encode_to_file(None, None)
encoder._pushes_fd = True
with pytest.raises(NotImplementedError):
encoder.encode_to_file(None, None)
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
encoder.encode = lambda buffer: (_ for _ in ()).throw(NotImplementedError)
with pytest.raises(NotImplementedError):
encoder.encode_to_file(buffer, None)
xoff, yoff, xsize, ysize = 10, 20, 100, 100 xoff, yoff, xsize, ysize = 10, 20, 100, 100

View File

@ -1,25 +1,25 @@
from __future__ import annotations from __future__ import annotations
import pytest import pytest
import tkinter as tk
from unittest import mock
from PIL import Image from PIL import Image
from PIL import ImageTk
from unittest.mock import patch
from .helper import assert_image_equal, hopper from .helper import assert_image_equal, hopper
TK_MODES = ("1", "L", "P", "RGB", "RGBA")
try: try:
import tkinter as tk
from PIL import ImageTk
dir(ImageTk) dir(ImageTk)
HAS_TK = True HAS_TK = True
except (OSError, ImportError): except (OSError, ImportError):
# Skipped via pytestmark
HAS_TK = False HAS_TK = False
TK_MODES = ("1", "L", "P", "RGB", "RGBA")
pytestmark = pytest.mark.skipif(not HAS_TK, reason="Tk not installed") pytestmark = pytest.mark.skipif(not HAS_TK, reason="Tk not installed")
@ -27,7 +27,6 @@ def setup_module() -> None:
try: try:
# setup tk # setup tk
tk.Frame() tk.Frame()
# root = tk.Tk()
except RuntimeError as v: except RuntimeError as v:
pytest.skip(f"RuntimeError: {v}") pytest.skip(f"RuntimeError: {v}")
except tk.TclError as v: except tk.TclError as v:
@ -102,3 +101,5 @@ def test_bitmapimage() -> None:
# reloaded = ImageTk.getimage(im_tk) # reloaded = ImageTk.getimage(im_tk)
# assert_image_equal(reloaded, im) # assert_image_equal(reloaded, im)

View File

@ -1,26 +1,26 @@
import pytest import pytest
from PIL import PdfParser from PIL import PdfParser
def test_delitem_new_entries(): def test_delitem_new_entries():
parser = PdfParser.XrefTable() parser = PdfParser.XrefTable()
parser.new_entries["test_key"] = ("value", 0) parser.new_entries["test_key"] = ("value", 0)
del parser["test_key"] del parser["test_key"]
assert "test_key" not in parser.new_entries assert "test_key" not in parser.new_entries
assert parser.deleted_entries["test_key"] == 1 assert parser.deleted_entries["test_key"] == 1
def test_delitem_deleted_entries(): def test_delitem_deleted_entries():
parser = PdfParser.XrefTable() parser = PdfParser.XrefTable()
parser.deleted_entries["test_key"] = 0 parser.deleted_entries["test_key"] = 0
del parser["test_key"] del parser["test_key"]
assert parser.deleted_entries["test_key"] == 0 assert parser.deleted_entries["test_key"] == 0
def test_delitem_nonexistent_key(): def test_delitem_nonexistent_key():
parser = PdfParser.XrefTable() parser = PdfParser.XrefTable()
with pytest.raises(IndexError): with pytest.raises(IndexError):
del parser["nonexistent_key"] del parser["nonexistent_key"]

View File

@ -7,19 +7,19 @@ from PIL import PdfParser
from PIL import SpiderImagePlugin from PIL import SpiderImagePlugin
from PIL import MpegImagePlugin from PIL import MpegImagePlugin
from PIL import ImageCms from PIL import ImageCms
from PIL import McIdasImagePlugin from PIL import ImageFile
pytest_plugins = ["Tests.helper"] pytest_plugins = ["Tests.helper"]
def calculate_coverage(test_name): def calculate_coverage(test_name):
all_branches = { all_branches = {
"branches1": Image.branches, "branches1": Image.branches, # duru
"branches2": PdfParser.XrefTable.branches, "branches2": PdfParser.XrefTable.branches, # duru
"branches3": SpiderImagePlugin.branches, "branches3": SpiderImagePlugin.branches, # isidora
"branches4": MpegImagePlugin.BitStream.branches, "branches4": MpegImagePlugin.BitStream.branches, # isidora
"branches5": ImageCms.ImageCmsProfile.branches, "branches5": ImageCms.branches, # deekshu
"branches6": McIdasImagePlugin.McIdasImageFile.branches, "branches6": ImageFile.PyEncoder.branches, # deekshu
# Add more # Add more
} }
@ -54,4 +54,3 @@ def pytest_sessionfinish(session, exitstatus):
coverage = calculate_coverage(test_name) coverage = calculate_coverage(test_name)
print("\nBRANCH COVERAGE for", test_name, ":", coverage, "%\n") print("\nBRANCH COVERAGE for", test_name, ":", coverage, "%\n")

View File

@ -29,6 +29,15 @@ from . import Image, __version__
from ._deprecate import deprecate from ._deprecate import deprecate
from ._typing import SupportsRead from ._typing import SupportsRead
branches = {
"1": False,
"2": False,
"3": False,
"4": False,
"5": False,
"6": False,
}
try: try:
from . import _imagingcms as core from . import _imagingcms as core
except ImportError as ex: except ImportError as ex:
@ -237,17 +246,6 @@ _FLAGS = {
class ImageCmsProfile: 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: def __init__(self, profile: str | SupportsRead[bytes] | core.CmsProfile) -> None:
""" """
:param profile: Either a string representing a filename, :param profile: Either a string representing a filename,
@ -257,28 +255,21 @@ class ImageCmsProfile:
""" """
if isinstance(profile, str): if isinstance(profile, str):
ImageCmsProfile.branches["1"] = True
if sys.platform == "win32": if sys.platform == "win32":
ImageCmsProfile.branches["2"] = True
profile_bytes_path = profile.encode() profile_bytes_path = profile.encode()
try: try:
ImageCmsProfile.branches["3"] = True
profile_bytes_path.decode("ascii") profile_bytes_path.decode("ascii")
except UnicodeDecodeError: except UnicodeDecodeError:
ImageCmsProfile.branches["4"] = True
with open(profile, "rb") as f: with open(profile, "rb") as f:
ImageCmsProfile.branches["5"] = True
self._set(core.profile_frombytes(f.read())) self._set(core.profile_frombytes(f.read()))
return return
self._set(core.profile_open(profile), profile) self._set(core.profile_open(profile), profile)
elif hasattr(profile, "read"): elif hasattr(profile, "read"):
ImageCmsProfile.branches["6"] = True
self._set(core.profile_frombytes(profile.read())) self._set(core.profile_frombytes(profile.read()))
elif isinstance(profile, core.CmsProfile): elif isinstance(profile, core.CmsProfile):
ImageCmsProfile.branches["7"] = True
self._set(profile) self._set(profile)
else: else:
ImageCmsProfile.branches["8"] = True
msg = "Invalid type for Profile" # type: ignore[unreachable] msg = "Invalid type for Profile" # type: ignore[unreachable]
raise TypeError(msg) raise TypeError(msg)
@ -582,22 +573,28 @@ def buildTransform(
""" """
if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
branches["1"] = True
msg = "renderingIntent must be an integer between 0 and 3" msg = "renderingIntent must be an integer between 0 and 3"
raise PyCMSError(msg) raise PyCMSError(msg)
if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): 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}" msg = f"flags must be an integer between 0 and {_MAX_FLAG}"
raise PyCMSError(msg) raise PyCMSError(msg)
try: try:
branches["3"] = True
if not isinstance(inputProfile, ImageCmsProfile): if not isinstance(inputProfile, ImageCmsProfile):
branches["4"] = True
inputProfile = ImageCmsProfile(inputProfile) inputProfile = ImageCmsProfile(inputProfile)
if not isinstance(outputProfile, ImageCmsProfile): if not isinstance(outputProfile, ImageCmsProfile):
branches["5"] = True
outputProfile = ImageCmsProfile(outputProfile) outputProfile = ImageCmsProfile(outputProfile)
return ImageCmsTransform( return ImageCmsTransform(
inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags
) )
except (OSError, TypeError, ValueError) as v: except (OSError, TypeError, ValueError) as v:
branches["6"] = True
raise PyCMSError(v) from v raise PyCMSError(v) from v

View File

@ -39,6 +39,7 @@ from . import Image
from ._deprecate import deprecate from ._deprecate import deprecate
from ._util import is_path from ._util import is_path
MAXBLOCK = 65536 MAXBLOCK = 65536
SAFEBLOCK = 1024 * 1024 SAFEBLOCK = 1024 * 1024
@ -750,6 +751,11 @@ class PyDecoder(PyCodec):
class PyEncoder(PyCodec): class PyEncoder(PyCodec):
branches = {
"1": False,
"2": False,
}
""" """
Python implementation of a format encoder. Override this class and Python implementation of a format encoder. Override this class and
add the decoding logic in the :meth:`encode` method. add the decoding logic in the :meth:`encode` method.
@ -801,7 +807,9 @@ class PyEncoder(PyCodec):
""" """
errcode = 0 errcode = 0
while errcode == 0: while errcode == 0:
PyEncoder.branches["1"] = True
status, errcode, buf = self.encode(bufsize) status, errcode, buf = self.encode(bufsize)
if status > 0: if status > 0:
PyEncoder.branches["2"] = True
fh.write(buf[status:]) fh.write(buf[status:])
return errcode return errcode

View File

@ -21,7 +21,6 @@ import struct
from . import Image, ImageFile from . import Image, ImageFile
def _accept(prefix: bytes) -> bool: def _accept(prefix: bytes) -> bool:
return prefix[:8] == b"\x00\x00\x00\x00\x00\x00\x00\x04" return prefix[:8] == b"\x00\x00\x00\x00\x00\x00\x00\x04"

25
src/PIL/test_ImageFile.py Normal file
View File

@ -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()