Skip to content

Commit

Permalink
Added type hints to tests (#8203)
Browse files Browse the repository at this point in the history
Co-authored-by: Andrew Murray <radarhere@users.noreply.github.com>
  • Loading branch information
radarhere and radarhere authored Jul 5, 2024
1 parent f3c3e52 commit 4aa24f8
Show file tree
Hide file tree
Showing 19 changed files with 159 additions and 72 deletions.
4 changes: 1 addition & 3 deletions Tests/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,7 @@ def convert_to_comparable(
return new_a, new_b


def assert_deep_equal(
a: Sequence[Any], b: Sequence[Any], msg: str | None = None
) -> None:
def assert_deep_equal(a: Any, b: Any, msg: str | None = None) -> None:
try:
assert len(a) == len(b), msg or f"got length {len(a)}, expected {len(b)}"
except Exception:
Expand Down
6 changes: 5 additions & 1 deletion Tests/test_file_libtiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -1048,7 +1048,11 @@ def test_open_missing_samplesperpixel(self) -> None:
],
)
def test_wrong_bits_per_sample(
self, file_name: str, mode: str, size: tuple[int, int], tile
self,
file_name: str,
mode: str,
size: tuple[int, int],
tile: list[tuple[str, tuple[int, int, int, int], int, tuple[Any, ...]]],
) -> None:
with Image.open("Tests/images/" + file_name) as im:
assert im.mode == mode
Expand Down
5 changes: 4 additions & 1 deletion Tests/test_file_webp_animated.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

from collections.abc import Generator
from pathlib import Path

import pytest
Expand Down Expand Up @@ -96,7 +97,9 @@ def check(temp_file: str) -> None:
check(temp_file1)

# Tests appending using a generator
def im_generator(ims):
def im_generator(
ims: list[Image.Image],
) -> Generator[Image.Image, None, None]:
yield from ims

temp_file2 = str(tmp_path / "temp_generator.webp")
Expand Down
9 changes: 6 additions & 3 deletions Tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,9 @@ def test_alpha_composite(self) -> None:
img = Image.alpha_composite(dst, src)

# Assert
img_colors = sorted(img.getcolors())
assert img_colors == expected_colors
img_colors = img.getcolors()
assert img_colors is not None
assert sorted(img_colors) == expected_colors

def test_alpha_inplace(self) -> None:
src = Image.new("RGBA", (128, 128), "blue")
Expand Down Expand Up @@ -670,7 +671,9 @@ def test_remap_palette_transparency(self) -> None:

im_remapped = im.remap_palette([1, 0])
assert im_remapped.info["transparency"] == 1
assert len(im_remapped.getpalette()) == 6
palette = im_remapped.getpalette()
assert palette is not None
assert len(palette) == 6

# Test unused transparency
im.info["transparency"] = 2
Expand Down
24 changes: 16 additions & 8 deletions Tests/test_image_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ def test_sanity(self) -> None:
for y in range(im1.size[1]):
for x in range(im1.size[0]):
pos = x, y
im2.putpixel(pos, im1.getpixel(pos))
value = im1.getpixel(pos)
assert value is not None
im2.putpixel(pos, value)

assert_image_equal(im1, im2)

Expand All @@ -37,7 +39,9 @@ def test_sanity(self) -> None:
for y in range(im1.size[1]):
for x in range(im1.size[0]):
pos = x, y
im2.putpixel(pos, im1.getpixel(pos))
value = im1.getpixel(pos)
assert value is not None
im2.putpixel(pos, value)

assert not im2.readonly
assert_image_equal(im1, im2)
Expand All @@ -50,9 +54,9 @@ def test_sanity(self) -> None:
assert pix1 is not None
assert pix2 is not None
with pytest.raises(TypeError):
pix1[0, "0"]
pix1[0, "0"] # type: ignore[index]
with pytest.raises(TypeError):
pix1["0", 0]
pix1["0", 0] # type: ignore[index]

for y in range(im1.size[1]):
for x in range(im1.size[0]):
Expand All @@ -71,7 +75,9 @@ def test_sanity_negative_index(self) -> None:
for y in range(-1, -im1.size[1] - 1, -1):
for x in range(-1, -im1.size[0] - 1, -1):
pos = x, y
im2.putpixel(pos, im1.getpixel(pos))
value = im1.getpixel(pos)
assert value is not None
im2.putpixel(pos, value)

assert_image_equal(im1, im2)

Expand All @@ -81,7 +87,9 @@ def test_sanity_negative_index(self) -> None:
for y in range(-1, -im1.size[1] - 1, -1):
for x in range(-1, -im1.size[0] - 1, -1):
pos = x, y
im2.putpixel(pos, im1.getpixel(pos))
value = im1.getpixel(pos)
assert value is not None
im2.putpixel(pos, value)

assert not im2.readonly
assert_image_equal(im1, im2)
Expand Down Expand Up @@ -219,7 +227,7 @@ def test_putpixel_type_error1(self, mode: str) -> None:
im = hopper(mode)
for v in self.INVALID_TYPES:
with pytest.raises(TypeError, match="color must be int or tuple"):
im.putpixel((0, 0), v)
im.putpixel((0, 0), v) # type: ignore[arg-type]

@pytest.mark.parametrize(
("mode", "band_numbers", "match"),
Expand Down Expand Up @@ -253,7 +261,7 @@ def test_putpixel_type_error2(self, mode: str) -> None:
with pytest.raises(
TypeError, match="color must be int or single-element tuple"
):
im.putpixel((0, 0), v)
im.putpixel((0, 0), v) # type: ignore[arg-type]

@pytest.mark.parametrize("mode", IMAGE_MODES1 + IMAGE_MODES2)
def test_putpixel_overflow_error(self, mode: str) -> None:
Expand Down
2 changes: 1 addition & 1 deletion Tests/test_image_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ def test_l_macro_rounding(convert_mode: str) -> None:
assert px is not None
converted_color = px[0, 0]
if convert_mode == "LA":
assert converted_color is not None
assert isinstance(converted_color, tuple)
converted_color = converted_color[0]
assert converted_color == 1

Expand Down
4 changes: 4 additions & 0 deletions Tests/test_image_getcolors.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,21 @@ def test_pack() -> None:
assert A is None

A = im.getcolors(maxcolors=3)
assert A is not None
A.sort()
assert A == expected

A = im.getcolors(maxcolors=4)
assert A is not None
A.sort()
assert A == expected

A = im.getcolors(maxcolors=8)
assert A is not None
A.sort()
assert A == expected

A = im.getcolors(maxcolors=16)
assert A is not None
A.sort()
assert A == expected
2 changes: 1 addition & 1 deletion Tests/test_image_putdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def test_sanity() -> None:

def test_long_integers() -> None:
# see bug-200802-systemerror
def put(value: int) -> tuple[int, int, int, int]:
def put(value: int) -> float | tuple[int, ...] | None:
im = Image.new("RGBA", (1, 1))
im.putdata([value])
return im.getpixel((0, 0))
Expand Down
12 changes: 9 additions & 3 deletions Tests/test_image_quantize.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,19 @@ def test_libimagequant_quantize() -> None:
converted = image.quantize(100, Image.Quantize.LIBIMAGEQUANT)
assert converted.mode == "P"
assert_image_similar(converted.convert("RGB"), image, 15)
assert len(converted.getcolors()) == 100
colors = converted.getcolors()
assert colors is not None
assert len(colors) == 100


def test_octree_quantize() -> None:
image = hopper()
converted = image.quantize(100, Image.Quantize.FASTOCTREE)
assert converted.mode == "P"
assert_image_similar(converted.convert("RGB"), image, 20)
assert len(converted.getcolors()) == 100
colors = converted.getcolors()
assert colors is not None
assert len(colors) == 100


def test_rgba_quantize() -> None:
Expand Down Expand Up @@ -158,4 +162,6 @@ def test_small_palette() -> None:
im = im.quantize(palette=p)

# Assert
assert len(im.getcolors()) == 2
quantized_colors = im.getcolors()
assert quantized_colors is not None
assert len(quantized_colors) == 2
20 changes: 13 additions & 7 deletions Tests/test_image_resample.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,13 @@ def test_box_filter_correct_range(self) -> None:
class TestCoreResampleConsistency:
def make_case(
self, mode: str, fill: tuple[int, int, int] | float
) -> tuple[Image.Image, tuple[int, ...]]:
) -> tuple[Image.Image, float | tuple[int, ...]]:
im = Image.new(mode, (512, 9), fill)
px = im.load()
assert px is not None
return im.resize((9, 512), Image.Resampling.LANCZOS), px[0, 0]

def run_case(self, case: tuple[Image.Image, int | tuple[int, ...]]) -> None:
def run_case(self, case: tuple[Image.Image, float | tuple[int, ...]]) -> None:
channel, color = case
px = channel.load()
assert px is not None
Expand All @@ -256,6 +256,7 @@ def run_case(self, case: tuple[Image.Image, int | tuple[int, ...]]) -> None:
def test_8u(self) -> None:
im, color = self.make_case("RGB", (0, 64, 255))
r, g, b = im.split()
assert isinstance(color, tuple)
self.run_case((r, color[0]))
self.run_case((g, color[1]))
self.run_case((b, color[2]))
Expand Down Expand Up @@ -290,7 +291,11 @@ def run_levels_case(self, i: Image.Image) -> None:
px = i.load()
assert px is not None
for y in range(i.size[1]):
used_colors = {px[x, y][0] for x in range(i.size[0])}
used_colors = set()
for x in range(i.size[0]):
value = px[x, y]
assert isinstance(value, tuple)
used_colors.add(value[0])
assert 256 == len(used_colors), (
"All colors should be present in resized image. "
f"Only {len(used_colors)} on line {y}."
Expand Down Expand Up @@ -332,12 +337,13 @@ def run_dirty_case(self, i: Image.Image, clean_pixel: tuple[int, ...]) -> None:
assert px is not None
for y in range(i.size[1]):
for x in range(i.size[0]):
if px[x, y][-1] != 0 and px[x, y][:-1] != clean_pixel:
value = px[x, y]
assert isinstance(value, tuple)
if value[-1] != 0 and value[:-1] != clean_pixel:
message = (
f"pixel at ({x}, {y}) is different:\n"
f"{px[x, y]}\n{clean_pixel}"
f"pixel at ({x}, {y}) is different:\n{value}\n{clean_pixel}"
)
assert px[x, y][:3] == clean_pixel, message
assert value[:3] == clean_pixel, message

def test_dirty_pixels_rgba(self) -> None:
case = self.make_dirty_case("RGBA", (255, 255, 0, 128), (0, 0, 255, 0))
Expand Down
5 changes: 3 additions & 2 deletions Tests/test_image_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,9 @@ def _test_nearest(

im = op(im, (40, 10))

colors = sorted(im.getcolors())
assert colors == sorted(
colors = im.getcolors()
assert colors is not None
assert sorted(colors) == sorted(
(
(20 * 10, opaque),
(20 * 10, transparent),
Expand Down
32 changes: 17 additions & 15 deletions Tests/test_imagechops.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,23 +391,25 @@ def test_overlay() -> None:
def test_logical() -> None:
def table(
op: Callable[[Image.Image, Image.Image], Image.Image], a: int, b: int
) -> tuple[int, int, int, int]:
) -> list[float]:
out = []
for x in (a, b):
imx = Image.new("1", (1, 1), x)
for y in (a, b):
imy = Image.new("1", (1, 1), y)
out.append(op(imx, imy).getpixel((0, 0)))
return tuple(out)

assert table(ImageChops.logical_and, 0, 1) == (0, 0, 0, 255)
assert table(ImageChops.logical_or, 0, 1) == (0, 255, 255, 255)
assert table(ImageChops.logical_xor, 0, 1) == (0, 255, 255, 0)

assert table(ImageChops.logical_and, 0, 128) == (0, 0, 0, 255)
assert table(ImageChops.logical_or, 0, 128) == (0, 255, 255, 255)
assert table(ImageChops.logical_xor, 0, 128) == (0, 255, 255, 0)

assert table(ImageChops.logical_and, 0, 255) == (0, 0, 0, 255)
assert table(ImageChops.logical_or, 0, 255) == (0, 255, 255, 255)
assert table(ImageChops.logical_xor, 0, 255) == (0, 255, 255, 0)
value = op(imx, imy).getpixel((0, 0))
assert not isinstance(value, tuple) and value is not None
out.append(value)
return out

assert table(ImageChops.logical_and, 0, 1) == [0, 0, 0, 255]
assert table(ImageChops.logical_or, 0, 1) == [0, 255, 255, 255]
assert table(ImageChops.logical_xor, 0, 1) == [0, 255, 255, 0]

assert table(ImageChops.logical_and, 0, 128) == [0, 0, 0, 255]
assert table(ImageChops.logical_or, 0, 128) == [0, 255, 255, 255]
assert table(ImageChops.logical_xor, 0, 128) == [0, 255, 255, 0]

assert table(ImageChops.logical_and, 0, 255) == [0, 0, 0, 255]
assert table(ImageChops.logical_or, 0, 255) == [0, 255, 255, 255]
assert table(ImageChops.logical_xor, 0, 255) == [0, 255, 255, 0]
4 changes: 3 additions & 1 deletion Tests/test_imagecms.py
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,9 @@ def test_rgb_lab(mode: str) -> None:

im = Image.new("LAB", (1, 1), (255, 0, 0))
converted_im = im.convert(mode)
assert converted_im.getpixel((0, 0))[:3] == (0, 255, 255)
value = converted_im.getpixel((0, 0))
assert isinstance(value, tuple)
assert value[:3] == (0, 255, 255)


def test_deprecation() -> None:
Expand Down
31 changes: 25 additions & 6 deletions Tests/test_imagedraw.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from __future__ import annotations

import contextlib
import os.path
from collections.abc import Sequence
from typing import Callable

import pytest

Expand Down Expand Up @@ -1422,25 +1422,44 @@ def test_default_font_size() -> None:

im = Image.new("RGB", (220, 25))
draw = ImageDraw.Draw(im)
with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError):

def check(func: Callable[[], None]) -> None:
if freetype_support:
func()
else:
with pytest.raises(ImportError):
func()

def draw_text() -> None:
draw.text((0, 0), text, font_size=16)
assert_image_equal_tofile(im, "Tests/images/imagedraw_default_font_size.png")

with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError):
check(draw_text)

def draw_textlength() -> None:
assert draw.textlength(text, font_size=16) == 216

with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError):
check(draw_textlength)

def draw_textbbox() -> None:
assert draw.textbbox((0, 0), text, font_size=16) == (0, 3, 216, 19)

check(draw_textbbox)

im = Image.new("RGB", (220, 25))
draw = ImageDraw.Draw(im)
with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError):

def draw_multiline_text() -> None:
draw.multiline_text((0, 0), text, font_size=16)
assert_image_equal_tofile(im, "Tests/images/imagedraw_default_font_size.png")

with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError):
check(draw_multiline_text)

def draw_multiline_textbbox() -> None:
assert draw.multiline_textbbox((0, 0), text, font_size=16) == (0, 3, 216, 19)

check(draw_multiline_textbbox)


@pytest.mark.parametrize("bbox", BBOX)
def test_same_color_outline(bbox: Coords) -> None:
Expand Down
2 changes: 1 addition & 1 deletion Tests/test_imagefile.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ def test_oversize(self) -> None:
def test_decode(self) -> None:
decoder = ImageFile.PyDecoder(None)
with pytest.raises(NotImplementedError):
decoder.decode(None)
decoder.decode(b"")


class TestPyEncoder(CodecsTest):
Expand Down
Loading

0 comments on commit 4aa24f8

Please # to comment.