Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 19 additions & 19 deletions Tests/test_file_pcx.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,36 +119,36 @@ def test_large_count(tmp_path: Path) -> None:
_roundtrip(tmp_path, im)


def _test_buffer_overflow(tmp_path: Path, im: Image.Image, size: int = 1024) -> None:
_last = ImageFile.MAXBLOCK
ImageFile.MAXBLOCK = size
try:
_roundtrip(tmp_path, im)
finally:
ImageFile.MAXBLOCK = _last
def _test_buffer_overflow(
tmp_path: Path, im: Image.Image, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setattr(ImageFile, "MAXBLOCK", 1024)
_roundtrip(tmp_path, im)


def test_break_in_count_overflow(tmp_path: Path) -> None:
def test_break_in_count_overflow(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
im = Image.new("L", (256, 5))
px = im.load()
assert px is not None
for y in range(4):
for x in range(256):
px[x, y] = x % 128
_test_buffer_overflow(tmp_path, im)
_test_buffer_overflow(tmp_path, im, monkeypatch)


def test_break_one_in_loop(tmp_path: Path) -> None:
def test_break_one_in_loop(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
im = Image.new("L", (256, 5))
px = im.load()
assert px is not None
for y in range(5):
for x in range(256):
px[x, y] = x % 128
_test_buffer_overflow(tmp_path, im)
_test_buffer_overflow(tmp_path, im, monkeypatch)


def test_break_many_in_loop(tmp_path: Path) -> None:
def test_break_many_in_loop(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
im = Image.new("L", (256, 5))
px = im.load()
assert px is not None
Expand All @@ -157,21 +157,21 @@ def test_break_many_in_loop(tmp_path: Path) -> None:
px[x, y] = x % 128
for x in range(8):
px[x, 4] = 16
_test_buffer_overflow(tmp_path, im)
_test_buffer_overflow(tmp_path, im, monkeypatch)


def test_break_one_at_end(tmp_path: Path) -> None:
def test_break_one_at_end(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
im = Image.new("L", (256, 5))
px = im.load()
assert px is not None
for y in range(5):
for x in range(256):
px[x, y] = x % 128
px[0, 3] = 128 + 64
_test_buffer_overflow(tmp_path, im)
_test_buffer_overflow(tmp_path, im, monkeypatch)


def test_break_many_at_end(tmp_path: Path) -> None:
def test_break_many_at_end(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
im = Image.new("L", (256, 5))
px = im.load()
assert px is not None
Expand All @@ -181,10 +181,10 @@ def test_break_many_at_end(tmp_path: Path) -> None:
for x in range(4):
px[x * 2, 3] = 128 + 64
px[x + 256 - 4, 3] = 0
_test_buffer_overflow(tmp_path, im)
_test_buffer_overflow(tmp_path, im, monkeypatch)


def test_break_padding(tmp_path: Path) -> None:
def test_break_padding(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
im = Image.new("L", (257, 5))
px = im.load()
assert px is not None
Expand All @@ -193,4 +193,4 @@ def test_break_padding(tmp_path: Path) -> None:
px[x, y] = x % 128
for x in range(5):
px[x, 3] = 0
_test_buffer_overflow(tmp_path, im)
_test_buffer_overflow(tmp_path, im, monkeypatch)
10 changes: 3 additions & 7 deletions Tests/test_file_png.py
Original file line number Diff line number Diff line change
Expand Up @@ -654,21 +654,17 @@ def test_unknown_compression_method(self) -> None:
with pytest.raises(SyntaxError, match="Unknown compression method"):
PngImagePlugin.PngImageFile("Tests/images/unknown_compression_method.png")

def test_padded_idat(self) -> None:
def test_padded_idat(self, monkeypatch: pytest.MonkeyPatch) -> None:
# This image has been manually hexedited
# so that the IDAT chunk has padding at the end
# Set MAXBLOCK to the length of the actual data
# so that the decoder finishes reading before the chunk ends
MAXBLOCK = ImageFile.MAXBLOCK
ImageFile.MAXBLOCK = 45
ImageFile.LOAD_TRUNCATED_IMAGES = True
monkeypatch.setattr(ImageFile, "MAXBLOCK", 45)
monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)

with Image.open("Tests/images/padded_idat.png") as im:
im.load()

ImageFile.MAXBLOCK = MAXBLOCK
ImageFile.LOAD_TRUNCATED_IMAGES = False

assert_image_equal_tofile(im, "Tests/images/bw_gradient.png")

@pytest.mark.parametrize(
Expand Down
30 changes: 17 additions & 13 deletions Tests/test_font_leaks.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
from __future__ import annotations

import pytest

from PIL import Image, ImageDraw, ImageFont, _util

from .helper import PillowLeakTestCase, features, skip_unless_feature

original_core = ImageFont.core


class TestTTypeFontLeak(PillowLeakTestCase):
# fails at iteration 3 in main
iterations = 10
mem_limit = 4096 # k

class TestFontLeak(PillowLeakTestCase):
def _test_font(self, font: ImageFont.FreeTypeFont | ImageFont.ImageFont) -> None:
im = Image.new("RGB", (255, 255), "white")
draw = ImageDraw.ImageDraw(im)
Expand All @@ -21,23 +19,29 @@ def _test_font(self, font: ImageFont.FreeTypeFont | ImageFont.ImageFont) -> None
)
)


class TestTTypeFontLeak(TestFontLeak):
# fails at iteration 3 in main
iterations = 10
mem_limit = 4096 # k

@skip_unless_feature("freetype2")
def test_leak(self) -> None:
ttype = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 20)
self._test_font(ttype)


class TestDefaultFontLeak(TestTTypeFontLeak):
class TestDefaultFontLeak(TestFontLeak):
# fails at iteration 37 in main
iterations = 100
mem_limit = 1024 # k

def test_leak(self) -> None:
def test_leak(self, monkeypatch: pytest.MonkeyPatch) -> None:
if features.check_module("freetype2"):
ImageFont.core = _util.DeferredError(ImportError("Disabled for testing"))
try:
default_font = ImageFont.load_default()
finally:
ImageFont.core = original_core

monkeypatch.setattr(
ImageFont,
"core",
_util.DeferredError(ImportError("Disabled for testing")),
)
default_font = ImageFont.load_default()
self._test_font(default_font)
6 changes: 4 additions & 2 deletions Tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,9 +456,11 @@ def test_register_open_duplicates(self) -> None:
# Assert
assert len(Image.ID) == id_length

def test_registered_extensions_uninitialized(self) -> None:
def test_registered_extensions_uninitialized(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
# Arrange
Image._initialized = 0
monkeypatch.setattr(Image, "_initialized", 0)

# Act
Image.registered_extensions()
Expand Down
18 changes: 6 additions & 12 deletions Tests/test_imagedraw.py
Original file line number Diff line number Diff line change
Expand Up @@ -1485,21 +1485,15 @@ def test_stroke_multiline() -> None:


@skip_unless_feature("freetype2")
def test_setting_default_font() -> None:
# Arrange
def test_setting_default_font(monkeypatch: pytest.MonkeyPatch) -> None:
im = Image.new("RGB", (100, 250))
draw = ImageDraw.Draw(im)
font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 120)

# Act
ImageDraw.ImageDraw.font = font
assert isinstance(draw.getfont(), ImageFont.load_default().__class__)

# Assert
try:
assert draw.getfont() == font
finally:
ImageDraw.ImageDraw.font = None
assert isinstance(draw.getfont(), ImageFont.load_default().__class__)
draw = ImageDraw.Draw(im)
font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 120)
monkeypatch.setattr(ImageDraw.ImageDraw, "font", font)
assert draw.getfont() == font


def test_default_font_size() -> None:
Expand Down
20 changes: 7 additions & 13 deletions Tests/test_imagefile.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@


class TestImageFile:
def test_parser(self) -> None:
def test_parser(self, monkeypatch: pytest.MonkeyPatch) -> None:
def roundtrip(format: str) -> tuple[Image.Image, Image.Image]:
im = hopper("L").resize((1000, 1000), Image.Resampling.NEAREST)
if format in ("MSP", "XBM"):
Expand All @@ -55,12 +55,9 @@ def roundtrip(format: str) -> tuple[Image.Image, Image.Image]:
assert_image_equal(*roundtrip("IM"))
assert_image_equal(*roundtrip("MSP"))
if features.check("zlib"):
try:
# force multiple blocks in PNG driver
ImageFile.MAXBLOCK = 8192
assert_image_equal(*roundtrip("PNG"))
finally:
ImageFile.MAXBLOCK = MAXBLOCK
# force multiple blocks in PNG driver
monkeypatch.setattr(ImageFile, "MAXBLOCK", 8192)
assert_image_equal(*roundtrip("PNG"))
assert_image_equal(*roundtrip("PPM"))
assert_image_equal(*roundtrip("TIFF"))
assert_image_equal(*roundtrip("XBM"))
Expand Down Expand Up @@ -120,14 +117,11 @@ def test_incremental_webp(self) -> None:
assert (128, 128) == p.image.size

@skip_unless_feature("zlib")
def test_safeblock(self) -> None:
def test_safeblock(self, monkeypatch: pytest.MonkeyPatch) -> None:
im1 = hopper()

try:
ImageFile.SAFEBLOCK = 1
im2 = fromstring(tostring(im1, "PNG"))
finally:
ImageFile.SAFEBLOCK = SAFEBLOCK
monkeypatch.setattr(ImageFile, "SAFEBLOCK", 1)
im2 = fromstring(tostring(im1, "PNG"))

assert_image_equal(im1, im2)

Expand Down
20 changes: 9 additions & 11 deletions Tests/test_imagefontpil.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,18 @@ def test_invalid_mode() -> None:
font._load_pilfont_data(fp, im)


def test_without_freetype() -> None:
original_core = ImageFont.core
def test_without_freetype(monkeypatch: pytest.MonkeyPatch) -> None:
if features.check_module("freetype2"):
ImageFont.core = _util.DeferredError(ImportError("Disabled for testing"))
try:
with pytest.raises(ImportError):
ImageFont.truetype("Tests/fonts/FreeMono.ttf")
monkeypatch.setattr(
ImageFont, "core", _util.DeferredError(ImportError("Disabled for testing"))
)
with pytest.raises(ImportError):
ImageFont.truetype("Tests/fonts/FreeMono.ttf")

assert isinstance(ImageFont.load_default(), ImageFont.ImageFont)
assert isinstance(ImageFont.load_default(), ImageFont.ImageFont)

with pytest.raises(ImportError):
ImageFont.load_default(size=14)
finally:
ImageFont.core = original_core
with pytest.raises(ImportError):
ImageFont.load_default(size=14)


@pytest.mark.parametrize("font", fonts)
Expand Down
11 changes: 4 additions & 7 deletions Tests/test_psdraw.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from io import BytesIO
from pathlib import Path

import pytest

from PIL import Image, PSDraw


Expand Down Expand Up @@ -47,21 +49,16 @@ def test_draw_postscript(tmp_path: Path) -> None:
assert os.path.getsize(tempfile) > 0


def test_stdout() -> None:
def test_stdout(monkeypatch: pytest.MonkeyPatch) -> None:
# Temporarily redirect stdout
old_stdout = sys.stdout

class MyStdOut:
buffer = BytesIO()

mystdout = MyStdOut()

sys.stdout = mystdout
monkeypatch.setattr(sys, "stdout", mystdout)

ps = PSDraw.PSDraw()
_create_document(ps)

# Reset stdout
sys.stdout = old_stdout

assert mystdout.buffer.getvalue() != b""
Loading