Merge pull request #4482 from radarhere/pytest

Removed use of PillowTestCase in various tests
This commit is contained in:
Hugo van Kemenade 2020-03-22 23:40:14 +02:00 committed by GitHub
commit 291f1eb1e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1246 additions and 1192 deletions

View File

@ -7,14 +7,13 @@ import time
import pytest import pytest
from PIL import Image, PdfParser from PIL import Image, PdfParser
from .helper import PillowTestCase, hopper from .helper import hopper
class TestFilePdf(PillowTestCase): def helper_save_as_pdf(tmp_path, mode, **kwargs):
def helper_save_as_pdf(self, mode, **kwargs):
# Arrange # Arrange
im = hopper(mode) im = hopper(mode)
outfile = self.tempfile("temp_" + mode + ".pdf") outfile = str(tmp_path / ("temp_" + mode + ".pdf"))
# Act # Act
im.save(outfile, **kwargs) im.save(outfile, **kwargs)
@ -30,63 +29,69 @@ class TestFilePdf(PillowTestCase):
with open(outfile, "rb") as fp: with open(outfile, "rb") as fp:
contents = fp.read() contents = fp.read()
size = tuple( size = tuple(
int(d) int(d) for d in contents.split(b"/MediaBox [ 0 0 ")[1].split(b"]")[0].split()
for d in contents.split(b"/MediaBox [ 0 0 ")[1].split(b"]")[0].split()
) )
assert im.size == size assert im.size == size
return outfile return outfile
def test_monochrome(self):
def test_monochrome(tmp_path):
# Arrange # Arrange
mode = "1" mode = "1"
# Act / Assert # Act / Assert
self.helper_save_as_pdf(mode) helper_save_as_pdf(tmp_path, mode)
def test_greyscale(self):
def test_greyscale(tmp_path):
# Arrange # Arrange
mode = "L" mode = "L"
# Act / Assert # Act / Assert
self.helper_save_as_pdf(mode) helper_save_as_pdf(tmp_path, mode)
def test_rgb(self):
def test_rgb(tmp_path):
# Arrange # Arrange
mode = "RGB" mode = "RGB"
# Act / Assert # Act / Assert
self.helper_save_as_pdf(mode) helper_save_as_pdf(tmp_path, mode)
def test_p_mode(self):
def test_p_mode(tmp_path):
# Arrange # Arrange
mode = "P" mode = "P"
# Act / Assert # Act / Assert
self.helper_save_as_pdf(mode) helper_save_as_pdf(tmp_path, mode)
def test_cmyk_mode(self):
def test_cmyk_mode(tmp_path):
# Arrange # Arrange
mode = "CMYK" mode = "CMYK"
# Act / Assert # Act / Assert
self.helper_save_as_pdf(mode) helper_save_as_pdf(tmp_path, mode)
def test_unsupported_mode(self):
def test_unsupported_mode(tmp_path):
im = hopper("LA") im = hopper("LA")
outfile = self.tempfile("temp_LA.pdf") outfile = str(tmp_path / "temp_LA.pdf")
with pytest.raises(ValueError): with pytest.raises(ValueError):
im.save(outfile) im.save(outfile)
def test_save_all(self):
def test_save_all(tmp_path):
# Single frame image # Single frame image
self.helper_save_as_pdf("RGB", save_all=True) helper_save_as_pdf(tmp_path, "RGB", save_all=True)
# Multiframe image # Multiframe image
with Image.open("Tests/images/dispose_bgnd.gif") as im: with Image.open("Tests/images/dispose_bgnd.gif") as im:
outfile = self.tempfile("temp.pdf") outfile = str(tmp_path / "temp.pdf")
im.save(outfile, save_all=True) im.save(outfile, save_all=True)
assert os.path.isfile(outfile) assert os.path.isfile(outfile)
@ -115,17 +120,19 @@ class TestFilePdf(PillowTestCase):
assert os.path.isfile(outfile) assert os.path.isfile(outfile)
assert os.path.getsize(outfile) > 0 assert os.path.getsize(outfile) > 0
def test_multiframe_normal_save(self):
def test_multiframe_normal_save(tmp_path):
# Test saving a multiframe image without save_all # Test saving a multiframe image without save_all
with Image.open("Tests/images/dispose_bgnd.gif") as im: with Image.open("Tests/images/dispose_bgnd.gif") as im:
outfile = self.tempfile("temp.pdf") outfile = str(tmp_path / "temp.pdf")
im.save(outfile) im.save(outfile)
assert os.path.isfile(outfile) assert os.path.isfile(outfile)
assert os.path.getsize(outfile) > 0 assert os.path.getsize(outfile) > 0
def test_pdf_open(self):
def test_pdf_open(tmp_path):
# fail on a buffer full of null bytes # fail on a buffer full of null bytes
with pytest.raises(PdfParser.PdfFormatError): with pytest.raises(PdfParser.PdfFormatError):
PdfParser.PdfParser(buf=bytearray(65536)) PdfParser.PdfParser(buf=bytearray(65536))
@ -138,7 +145,7 @@ class TestFilePdf(PillowTestCase):
assert not empty_pdf.should_close_file assert not empty_pdf.should_close_file
# make a PDF file # make a PDF file
pdf_filename = self.helper_save_as_pdf("RGB") pdf_filename = helper_save_as_pdf(tmp_path, "RGB")
# open the PDF file # open the PDF file
with PdfParser.PdfParser(filename=pdf_filename) as hopper_pdf: with PdfParser.PdfParser(filename=pdf_filename) as hopper_pdf:
@ -161,13 +168,15 @@ class TestFilePdf(PillowTestCase):
assert hopper_pdf.should_close_buf assert hopper_pdf.should_close_buf
assert not hopper_pdf.should_close_file assert not hopper_pdf.should_close_file
def test_pdf_append_fails_on_nonexistent_file(self):
def test_pdf_append_fails_on_nonexistent_file():
im = hopper("RGB") im = hopper("RGB")
with tempfile.TemporaryDirectory() as temp_dir: with tempfile.TemporaryDirectory() as temp_dir:
with pytest.raises(IOError): with pytest.raises(IOError):
im.save(os.path.join(temp_dir, "nonexistent.pdf"), append=True) im.save(os.path.join(temp_dir, "nonexistent.pdf"), append=True)
def check_pdf_pages_consistency(self, pdf):
def check_pdf_pages_consistency(pdf):
pages_info = pdf.read_indirect(pdf.pages_ref) pages_info = pdf.read_indirect(pdf.pages_ref)
assert b"Parent" not in pages_info assert b"Parent" not in pages_info
assert b"Kids" in pages_info assert b"Kids" in pages_info
@ -184,9 +193,10 @@ class TestFilePdf(PillowTestCase):
assert pdf.pages_ref == page_info[b"Parent"] assert pdf.pages_ref == page_info[b"Parent"]
assert kids_not_used == [] assert kids_not_used == []
def test_pdf_append(self):
def test_pdf_append(tmp_path):
# make a PDF file # make a PDF file
pdf_filename = self.helper_save_as_pdf("RGB", producer="PdfParser") pdf_filename = helper_save_as_pdf(tmp_path, "RGB", producer="PdfParser")
# open it, check pages and info # open it, check pages and info
with PdfParser.PdfParser(pdf_filename, mode="r+b") as pdf: with PdfParser.PdfParser(pdf_filename, mode="r+b") as pdf:
@ -196,7 +206,7 @@ class TestFilePdf(PillowTestCase):
assert pdf.info.Producer == "PdfParser" assert pdf.info.Producer == "PdfParser"
assert b"CreationDate" in pdf.info assert b"CreationDate" in pdf.info
assert b"ModDate" in pdf.info assert b"ModDate" in pdf.info
self.check_pdf_pages_consistency(pdf) check_pdf_pages_consistency(pdf)
# append some info # append some info
pdf.info.Title = "abc" pdf.info.Title = "abc"
@ -214,7 +224,7 @@ class TestFilePdf(PillowTestCase):
assert pdf.info.Title == "abc" assert pdf.info.Title == "abc"
assert b"CreationDate" in pdf.info assert b"CreationDate" in pdf.info
assert b"ModDate" in pdf.info assert b"ModDate" in pdf.info
self.check_pdf_pages_consistency(pdf) check_pdf_pages_consistency(pdf)
# append two images # append two images
mode_CMYK = hopper("CMYK") mode_CMYK = hopper("CMYK")
@ -232,11 +242,13 @@ class TestFilePdf(PillowTestCase):
assert pdf.info.Subject == "ghi\uABCD" assert pdf.info.Subject == "ghi\uABCD"
assert b"CreationDate" in pdf.info assert b"CreationDate" in pdf.info
assert b"ModDate" in pdf.info assert b"ModDate" in pdf.info
self.check_pdf_pages_consistency(pdf) check_pdf_pages_consistency(pdf)
def test_pdf_info(self):
def test_pdf_info(tmp_path):
# make a PDF file # make a PDF file
pdf_filename = self.helper_save_as_pdf( pdf_filename = helper_save_as_pdf(
tmp_path,
"RGB", "RGB",
title="title", title="title",
author="author", author="author",
@ -259,9 +271,10 @@ class TestFilePdf(PillowTestCase):
assert pdf.info.Producer == "producer" assert pdf.info.Producer == "producer"
assert pdf.info.CreationDate == time.strptime("2000", "%Y") assert pdf.info.CreationDate == time.strptime("2000", "%Y")
assert pdf.info.ModDate == time.strptime("2001", "%Y") assert pdf.info.ModDate == time.strptime("2001", "%Y")
self.check_pdf_pages_consistency(pdf) check_pdf_pages_consistency(pdf)
def test_pdf_append_to_bytesio(self):
def test_pdf_append_to_bytesio():
im = hopper("RGB") im = hopper("RGB")
f = io.BytesIO() f = io.BytesIO()
im.save(f, format="PDF") im.save(f, format="PDF")

View File

@ -5,21 +5,36 @@ from itertools import product
import pytest import pytest
from PIL import Image from PIL import Image
from .helper import PillowTestCase, assert_image_equal, hopper from .helper import assert_image_equal, hopper
_TGA_DIR = os.path.join("Tests", "images", "tga") _TGA_DIR = os.path.join("Tests", "images", "tga")
_TGA_DIR_COMMON = os.path.join(_TGA_DIR, "common") _TGA_DIR_COMMON = os.path.join(_TGA_DIR, "common")
class TestFileTga(PillowTestCase): _MODES = ("L", "LA", "P", "RGB", "RGBA")
_ORIGINS = ("tl", "bl")
_MODES = ("L", "LA", "P", "RGB", "RGBA") _ORIGIN_TO_ORIENTATION = {"tl": 1, "bl": -1}
_ORIGINS = ("tl", "bl")
_ORIGIN_TO_ORIENTATION = {"tl": 1, "bl": -1}
def test_sanity(self): def test_sanity(tmp_path):
for mode in self._MODES: for mode in _MODES:
def roundtrip(original_im):
out = str(tmp_path / "temp.tga")
original_im.save(out, rle=rle)
with Image.open(out) as saved_im:
if rle:
assert (
saved_im.info["compression"] == original_im.info["compression"]
)
assert saved_im.info["orientation"] == original_im.info["orientation"]
if mode == "P":
assert saved_im.getpalette() == original_im.getpalette()
assert_image_equal(saved_im, original_im)
png_paths = glob( png_paths = glob(
os.path.join(_TGA_DIR_COMMON, "*x*_{}.png".format(mode.lower())) os.path.join(_TGA_DIR_COMMON, "*x*_{}.png".format(mode.lower()))
) )
@ -29,7 +44,7 @@ class TestFileTga(PillowTestCase):
assert reference_im.mode == mode assert reference_im.mode == mode
path_no_ext = os.path.splitext(png_path)[0] path_no_ext = os.path.splitext(png_path)[0]
for origin, rle in product(self._ORIGINS, (True, False)): for origin, rle in product(_ORIGINS, (True, False)):
tga_path = "{}_{}_{}.tga".format( tga_path = "{}_{}_{}.tga".format(
path_no_ext, origin, "rle" if rle else "raw" path_no_ext, origin, "rle" if rle else "raw"
) )
@ -41,41 +56,17 @@ class TestFileTga(PillowTestCase):
assert original_im.info["compression"] == "tga_rle" assert original_im.info["compression"] == "tga_rle"
assert ( assert (
original_im.info["orientation"] original_im.info["orientation"]
== self._ORIGIN_TO_ORIENTATION[origin] == _ORIGIN_TO_ORIENTATION[origin]
) )
if mode == "P": if mode == "P":
assert ( assert original_im.getpalette() == reference_im.getpalette()
original_im.getpalette()
== reference_im.getpalette()
)
assert_image_equal(original_im, reference_im) assert_image_equal(original_im, reference_im)
# Generate a new test name every time so the roundtrip(original_im)
# test will not fail with permission error
# on Windows.
out = self.tempfile("temp.tga")
original_im.save(out, rle=rle)
with Image.open(out) as saved_im:
if rle:
assert (
saved_im.info["compression"]
== original_im.info["compression"]
)
assert (
saved_im.info["orientation"]
== original_im.info["orientation"]
)
if mode == "P":
assert (
saved_im.getpalette()
== original_im.getpalette()
)
assert_image_equal(saved_im, original_im) def test_id_field():
def test_id_field(self):
# tga file with id field # tga file with id field
test_file = "Tests/images/tga_id_field.tga" test_file = "Tests/images/tga_id_field.tga"
@ -85,7 +76,8 @@ class TestFileTga(PillowTestCase):
# Assert # Assert
assert im.size == (100, 100) assert im.size == (100, 100)
def test_id_field_rle(self):
def test_id_field_rle():
# tga file with id field # tga file with id field
test_file = "Tests/images/rgb32rle.tga" test_file = "Tests/images/rgb32rle.tga"
@ -95,10 +87,11 @@ class TestFileTga(PillowTestCase):
# Assert # Assert
assert im.size == (199, 199) assert im.size == (199, 199)
def test_save(self):
def test_save(tmp_path):
test_file = "Tests/images/tga_id_field.tga" test_file = "Tests/images/tga_id_field.tga"
with Image.open(test_file) as im: with Image.open(test_file) as im:
out = self.tempfile("temp.tga") out = str(tmp_path / "temp.tga")
# Save # Save
im.save(out) im.save(out)
@ -111,17 +104,19 @@ class TestFileTga(PillowTestCase):
with Image.open(out) as test_im: with Image.open(out) as test_im:
assert test_im.size == (100, 100) assert test_im.size == (100, 100)
def test_save_wrong_mode(self):
def test_save_wrong_mode(tmp_path):
im = hopper("PA") im = hopper("PA")
out = self.tempfile("temp.tga") out = str(tmp_path / "temp.tga")
with pytest.raises(OSError): with pytest.raises(OSError):
im.save(out) im.save(out)
def test_save_id_section(self):
def test_save_id_section(tmp_path):
test_file = "Tests/images/rgb32rle.tga" test_file = "Tests/images/rgb32rle.tga"
with Image.open(test_file) as im: with Image.open(test_file) as im:
out = self.tempfile("temp.tga") out = str(tmp_path / "temp.tga")
# Check there is no id section # Check there is no id section
im.save(out) im.save(out)
@ -147,9 +142,10 @@ class TestFileTga(PillowTestCase):
with Image.open(out) as test_im: with Image.open(out) as test_im:
assert "id_section" not in test_im.info assert "id_section" not in test_im.info
def test_save_orientation(self):
def test_save_orientation(tmp_path):
test_file = "Tests/images/rgb32rle.tga" test_file = "Tests/images/rgb32rle.tga"
out = self.tempfile("temp.tga") out = str(tmp_path / "temp.tga")
with Image.open(test_file) as im: with Image.open(test_file) as im:
assert im.info["orientation"] == -1 assert im.info["orientation"] == -1
@ -157,12 +153,13 @@ class TestFileTga(PillowTestCase):
with Image.open(out) as test_im: with Image.open(out) as test_im:
assert test_im.info["orientation"] == 1 assert test_im.info["orientation"] == 1
def test_save_rle(self):
def test_save_rle(tmp_path):
test_file = "Tests/images/rgb32rle.tga" test_file = "Tests/images/rgb32rle.tga"
with Image.open(test_file) as im: with Image.open(test_file) as im:
assert im.info["compression"] == "tga_rle" assert im.info["compression"] == "tga_rle"
out = self.tempfile("temp.tga") out = str(tmp_path / "temp.tga")
# Save # Save
im.save(out) im.save(out)
@ -189,7 +186,8 @@ class TestFileTga(PillowTestCase):
with Image.open(out) as test_im: with Image.open(out) as test_im:
assert test_im.info["compression"] == "tga_rle" assert test_im.info["compression"] == "tga_rle"
def test_save_l_transparency(self):
def test_save_l_transparency(tmp_path):
# There are 559 transparent pixels in la.tga. # There are 559 transparent pixels in la.tga.
num_transparent = 559 num_transparent = 559
@ -198,7 +196,7 @@ class TestFileTga(PillowTestCase):
assert im.mode == "LA" assert im.mode == "LA"
assert im.getchannel("A").getcolors()[0][0] == num_transparent assert im.getchannel("A").getcolors()[0][0] == num_transparent
out = self.tempfile("temp.tga") out = str(tmp_path / "temp.tga")
im.save(out) im.save(out)
with Image.open(out) as test_im: with Image.open(out) as test_im:

View File

@ -2,7 +2,6 @@ import pytest
from PIL import Image, WebPImagePlugin from PIL import Image, WebPImagePlugin
from .helper import ( from .helper import (
PillowTestCase,
assert_image_similar, assert_image_similar,
assert_image_similar_tofile, assert_image_similar_tofile,
hopper, hopper,
@ -17,23 +16,21 @@ except ImportError:
HAVE_WEBP = False HAVE_WEBP = False
class TestUnsupportedWebp(PillowTestCase): class TestUnsupportedWebp:
def test_unsupported(self): def test_unsupported(self):
if HAVE_WEBP: if HAVE_WEBP:
WebPImagePlugin.SUPPORTED = False WebPImagePlugin.SUPPORTED = False
file_path = "Tests/images/hopper.webp" file_path = "Tests/images/hopper.webp"
pytest.warns( pytest.warns(UserWarning, lambda: pytest.raises(IOError, Image.open, file_path))
UserWarning, lambda: self.assertRaises(IOError, Image.open, file_path)
)
if HAVE_WEBP: if HAVE_WEBP:
WebPImagePlugin.SUPPORTED = True WebPImagePlugin.SUPPORTED = True
@skip_unless_feature("webp") @skip_unless_feature("webp")
class TestFileWebp(PillowTestCase): class TestFileWebp:
def setUp(self): def setup_method(self):
self.rgb_mode = "RGB" self.rgb_mode = "RGB"
def test_version(self): def test_version(self):
@ -57,13 +54,13 @@ class TestFileWebp(PillowTestCase):
# dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm # dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm
assert_image_similar_tofile(image, "Tests/images/hopper_webp_bits.ppm", 1.0) assert_image_similar_tofile(image, "Tests/images/hopper_webp_bits.ppm", 1.0)
def test_write_rgb(self): def test_write_rgb(self, tmp_path):
""" """
Can we write a RGB mode file to webp without error. Can we write a RGB mode file to webp without error.
Does it have the bits we expect? Does it have the bits we expect?
""" """
temp_file = self.tempfile("temp.webp") temp_file = str(tmp_path / "temp.webp")
hopper(self.rgb_mode).save(temp_file) hopper(self.rgb_mode).save(temp_file)
with Image.open(temp_file) as image: with Image.open(temp_file) as image:
@ -86,13 +83,13 @@ class TestFileWebp(PillowTestCase):
target = hopper(self.rgb_mode) target = hopper(self.rgb_mode)
assert_image_similar(image, target, 12.0) assert_image_similar(image, target, 12.0)
def test_write_unsupported_mode_L(self): def test_write_unsupported_mode_L(self, tmp_path):
""" """
Saving a black-and-white file to WebP format should work, and be Saving a black-and-white file to WebP format should work, and be
similar to the original file. similar to the original file.
""" """
temp_file = self.tempfile("temp.webp") temp_file = str(tmp_path / "temp.webp")
hopper("L").save(temp_file) hopper("L").save(temp_file)
with Image.open(temp_file) as image: with Image.open(temp_file) as image:
assert image.mode == self.rgb_mode assert image.mode == self.rgb_mode
@ -105,13 +102,13 @@ class TestFileWebp(PillowTestCase):
assert_image_similar(image, target, 10.0) assert_image_similar(image, target, 10.0)
def test_write_unsupported_mode_P(self): def test_write_unsupported_mode_P(self, tmp_path):
""" """
Saving a palette-based file to WebP format should work, and be Saving a palette-based file to WebP format should work, and be
similar to the original file. similar to the original file.
""" """
temp_file = self.tempfile("temp.webp") temp_file = str(tmp_path / "temp.webp")
hopper("P").save(temp_file) hopper("P").save(temp_file)
with Image.open(temp_file) as image: with Image.open(temp_file) as image:
assert image.mode == self.rgb_mode assert image.mode == self.rgb_mode
@ -146,10 +143,10 @@ class TestFileWebp(PillowTestCase):
with pytest.raises(TypeError): with pytest.raises(TypeError):
_webp.WebPDecode() _webp.WebPDecode()
def test_no_resource_warning(self): def test_no_resource_warning(self, tmp_path):
file_path = "Tests/images/hopper.webp" file_path = "Tests/images/hopper.webp"
with Image.open(file_path) as image: with Image.open(file_path) as image:
temp_file = self.tempfile("temp.webp") temp_file = str(tmp_path / "temp.webp")
pytest.warns(None, image.save, temp_file) pytest.warns(None, image.save, temp_file)
def test_file_pointer_could_be_reused(self): def test_file_pointer_could_be_reused(self):
@ -160,16 +157,16 @@ class TestFileWebp(PillowTestCase):
@skip_unless_feature("webp") @skip_unless_feature("webp")
@skip_unless_feature("webp_anim") @skip_unless_feature("webp_anim")
def test_background_from_gif(self): def test_background_from_gif(self, tmp_path):
with Image.open("Tests/images/chi.gif") as im: with Image.open("Tests/images/chi.gif") as im:
original_value = im.convert("RGB").getpixel((1, 1)) original_value = im.convert("RGB").getpixel((1, 1))
# Save as WEBP # Save as WEBP
out_webp = self.tempfile("temp.webp") out_webp = str(tmp_path / "temp.webp")
im.save(out_webp, save_all=True) im.save(out_webp, save_all=True)
# Save as GIF # Save as GIF
out_gif = self.tempfile("temp.gif") out_gif = str(tmp_path / "temp.gif")
Image.open(out_webp).save(out_gif) Image.open(out_webp).save(out_gif)
with Image.open(out_gif) as reread: with Image.open(out_gif) as reread:

View File

@ -1,12 +1,10 @@
import pytest import pytest
from PIL import Image, ImageMath, ImageMode from PIL import Image, ImageMath, ImageMode
from .helper import PillowTestCase, convert_to_comparable from .helper import convert_to_comparable
# There are several internal implementations
class TestImageReduce(PillowTestCase): remarkable_factors = [
# There are several internal implementations
remarkable_factors = [
# special implementations # special implementations
1, 1,
2, 2,
@ -30,14 +28,13 @@ class TestImageReduce(PillowTestCase):
(4, 7), (4, 7),
(5, 7), (5, 7),
(19, 17), (19, 17),
] ]
@classmethod gradients_image = Image.open("Tests/images/radial_gradients.png")
def setUpClass(cls): gradients_image.load()
cls.gradients_image = Image.open("Tests/images/radial_gradients.png")
cls.gradients_image.load()
def test_args_factor(self):
def test_args_factor():
im = Image.new("L", (10, 10)) im = Image.new("L", (10, 10))
assert (4, 4) == im.reduce(3).size assert (4, 4) == im.reduce(3).size
@ -51,7 +48,8 @@ class TestImageReduce(PillowTestCase):
with pytest.raises(ValueError): with pytest.raises(ValueError):
im.reduce((0, 10)) im.reduce((0, 10))
def test_args_box(self):
def test_args_box():
im = Image.new("L", (10, 10)) im = Image.new("L", (10, 10))
assert (5, 5) == im.reduce(2, (0, 0, 10, 10)).size assert (5, 5) == im.reduce(2, (0, 0, 10, 10)).size
@ -74,7 +72,8 @@ class TestImageReduce(PillowTestCase):
with pytest.raises(ValueError): with pytest.raises(ValueError):
im.reduce(2, (5, 0, 5, 10)) im.reduce(2, (5, 0, 5, 10))
def test_unsupported_modes(self):
def test_unsupported_modes():
im = Image.new("P", (10, 10)) im = Image.new("P", (10, 10))
with pytest.raises(ValueError): with pytest.raises(ValueError):
im.reduce(3) im.reduce(3)
@ -87,10 +86,11 @@ class TestImageReduce(PillowTestCase):
with pytest.raises(ValueError): with pytest.raises(ValueError):
im.reduce(3) im.reduce(3)
def get_image(self, mode):
def get_image(mode):
mode_info = ImageMode.getmode(mode) mode_info = ImageMode.getmode(mode)
if mode_info.basetype == "L": if mode_info.basetype == "L":
bands = [self.gradients_image] bands = [gradients_image]
for _ in mode_info.bands[1:]: for _ in mode_info.bands[1:]:
# rotate previous image # rotate previous image
band = bands[-1].transpose(Image.ROTATE_90) band = bands[-1].transpose(Image.ROTATE_90)
@ -102,17 +102,19 @@ class TestImageReduce(PillowTestCase):
im = Image.merge(mode, bands) im = Image.merge(mode, bands)
else: else:
assert len(mode_info.bands) == 1 assert len(mode_info.bands) == 1
im = self.gradients_image.convert(mode) im = gradients_image.convert(mode)
# change the height to make a not-square image # change the height to make a not-square image
return im.crop((0, 0, im.width, im.height - 5)) return im.crop((0, 0, im.width, im.height - 5))
def compare_reduce_with_box(self, im, factor):
def compare_reduce_with_box(im, factor):
box = (11, 13, 146, 164) box = (11, 13, 146, 164)
reduced = im.reduce(factor, box=box) reduced = im.reduce(factor, box=box)
reference = im.crop(box).reduce(factor) reference = im.crop(box).reduce(factor)
assert reduced == reference assert reduced == reference
def compare_reduce_with_reference(self, im, factor, average_diff=0.4, max_diff=1):
def compare_reduce_with_reference(im, factor, average_diff=0.4, max_diff=1):
"""Image.reduce() should look very similar to Image.resize(BOX). """Image.reduce() should look very similar to Image.resize(BOX).
A reference image is compiled from a large source area A reference image is compiled from a large source area
@ -152,9 +154,10 @@ class TestImageReduce(PillowTestCase):
last_pixel = im.resize((1, 1), Image.BOX, last_pixel_box) last_pixel = im.resize((1, 1), Image.BOX, last_pixel_box)
reference.paste(last_pixel, area_size) reference.paste(last_pixel, area_size)
self.assert_compare_images(reduced, reference, average_diff, max_diff) assert_compare_images(reduced, reference, average_diff, max_diff)
def assert_compare_images(self, a, b, max_average_diff, max_diff=255):
def assert_compare_images(a, b, max_average_diff, max_diff=255):
assert a.mode == b.mode, "got mode %r, expected %r" % (a.mode, b.mode) assert a.mode == b.mode, "got mode %r, expected %r" % (a.mode, b.mode)
assert a.size == b.size, "got size %r, expected %r" % (a.size, b.size) assert a.size == b.size, "got size %r, expected %r" % (a.size, b.size)
@ -179,60 +182,68 @@ class TestImageReduce(PillowTestCase):
last_diff, max_diff, band last_diff, max_diff, band
) )
def test_mode_L(self):
im = self.get_image("L")
for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor)
def test_mode_LA(self): def test_mode_L():
im = self.get_image("LA") im = get_image("L")
for factor in self.remarkable_factors: for factor in remarkable_factors:
self.compare_reduce_with_reference(im, factor, 0.8, 5) compare_reduce_with_reference(im, factor)
compare_reduce_with_box(im, factor)
def test_mode_LA():
im = get_image("LA")
for factor in remarkable_factors:
compare_reduce_with_reference(im, factor, 0.8, 5)
# With opaque alpha, an error should be way smaller. # With opaque alpha, an error should be way smaller.
im.putalpha(Image.new("L", im.size, 255)) im.putalpha(Image.new("L", im.size, 255))
for factor in self.remarkable_factors: for factor in remarkable_factors:
self.compare_reduce_with_reference(im, factor) compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor) compare_reduce_with_box(im, factor)
def test_mode_La(self):
im = self.get_image("La")
for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor)
def test_mode_RGB(self): def test_mode_La():
im = self.get_image("RGB") im = get_image("La")
for factor in self.remarkable_factors: for factor in remarkable_factors:
self.compare_reduce_with_reference(im, factor) compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor) compare_reduce_with_box(im, factor)
def test_mode_RGBA(self):
im = self.get_image("RGBA") def test_mode_RGB():
for factor in self.remarkable_factors: im = get_image("RGB")
self.compare_reduce_with_reference(im, factor, 0.8, 5) for factor in remarkable_factors:
compare_reduce_with_reference(im, factor)
compare_reduce_with_box(im, factor)
def test_mode_RGBA():
im = get_image("RGBA")
for factor in remarkable_factors:
compare_reduce_with_reference(im, factor, 0.8, 5)
# With opaque alpha, an error should be way smaller. # With opaque alpha, an error should be way smaller.
im.putalpha(Image.new("L", im.size, 255)) im.putalpha(Image.new("L", im.size, 255))
for factor in self.remarkable_factors: for factor in remarkable_factors:
self.compare_reduce_with_reference(im, factor) compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor) compare_reduce_with_box(im, factor)
def test_mode_RGBa(self):
im = self.get_image("RGBa")
for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor)
def test_mode_I(self): def test_mode_RGBa():
im = self.get_image("I") im = get_image("RGBa")
for factor in self.remarkable_factors: for factor in remarkable_factors:
self.compare_reduce_with_reference(im, factor) compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor) compare_reduce_with_box(im, factor)
def test_mode_F(self):
im = self.get_image("F") def test_mode_I():
for factor in self.remarkable_factors: im = get_image("I")
self.compare_reduce_with_reference(im, factor, 0, 0) for factor in remarkable_factors:
self.compare_reduce_with_box(im, factor) compare_reduce_with_reference(im, factor)
compare_reduce_with_box(im, factor)
def test_mode_F():
im = get_image("F")
for factor in remarkable_factors:
compare_reduce_with_reference(im, factor, 0, 0)
compare_reduce_with_box(im, factor)

View File

@ -3,10 +3,10 @@ import math
import pytest import pytest
from PIL import Image, ImageTransform from PIL import Image, ImageTransform
from .helper import PillowTestCase, assert_image_equal, assert_image_similar, hopper from .helper import assert_image_equal, assert_image_similar, hopper
class TestImageTransform(PillowTestCase): class TestImageTransform:
def test_sanity(self): def test_sanity(self):
im = Image.new("L", (100, 100)) im = Image.new("L", (100, 100))
@ -177,7 +177,7 @@ class TestImageTransform(PillowTestCase):
im.transform((100, 100), Image.EXTENT, (0, 0, w, h), resample) im.transform((100, 100), Image.EXTENT, (0, 0, w, h), resample)
class TestImageTransformAffine(PillowTestCase): class TestImageTransformAffine:
transform = Image.AFFINE transform = Image.AFFINE
def _test_image(self): def _test_image(self):

View File

@ -4,7 +4,6 @@ import pytest
from PIL import EpsImagePlugin, Image, ImageFile, features from PIL import EpsImagePlugin, Image, ImageFile, features
from .helper import ( from .helper import (
PillowTestCase,
assert_image, assert_image,
assert_image_equal, assert_image_equal,
assert_image_similar, assert_image_similar,
@ -19,7 +18,7 @@ MAXBLOCK = ImageFile.MAXBLOCK
SAFEBLOCK = ImageFile.SAFEBLOCK SAFEBLOCK = ImageFile.SAFEBLOCK
class TestImageFile(PillowTestCase): class TestImageFile:
def test_parser(self): def test_parser(self):
def roundtrip(format): def roundtrip(format):
@ -163,7 +162,7 @@ class MockImageFile(ImageFile.ImageFile):
self.tile = [("MOCK", (xoff, yoff, xoff + xsize, yoff + ysize), 32, None)] self.tile = [("MOCK", (xoff, yoff, xoff + xsize, yoff + ysize), 32, None)]
class TestPyDecoder(PillowTestCase): class TestPyDecoder:
def get_decoder(self): def get_decoder(self):
decoder = MockPyDecoder(None) decoder = MockPyDecoder(None)
@ -239,7 +238,7 @@ class TestPyDecoder(PillowTestCase):
assert im.format is None assert im.format is None
assert im.get_format_mimetype() is None assert im.get_format_mimetype() is None
def test_exif_jpeg(self): def test_exif_jpeg(self, tmp_path):
with Image.open("Tests/images/exif-72dpi-int.jpg") as im: # Little endian with Image.open("Tests/images/exif-72dpi-int.jpg") as im: # Little endian
exif = im.getexif() exif = im.getexif()
assert 258 not in exif assert 258 not in exif
@ -247,7 +246,7 @@ class TestPyDecoder(PillowTestCase):
assert exif[40963] == 450 assert exif[40963] == 450
assert exif[11] == "gThumb 3.0.1" assert exif[11] == "gThumb 3.0.1"
out = self.tempfile("temp.jpg") out = str(tmp_path / "temp.jpg")
exif[258] = 8 exif[258] = 8
del exif[40960] del exif[40960]
exif[40963] = 455 exif[40963] = 455
@ -267,7 +266,7 @@ class TestPyDecoder(PillowTestCase):
assert exif[40963] == 200 assert exif[40963] == 200
assert exif[305] == "Adobe Photoshop CC 2017 (Macintosh)" assert exif[305] == "Adobe Photoshop CC 2017 (Macintosh)"
out = self.tempfile("temp.jpg") out = str(tmp_path / "temp.jpg")
exif[258] = 8 exif[258] = 8
del exif[34665] del exif[34665]
exif[40963] = 455 exif[40963] = 455
@ -282,12 +281,12 @@ class TestPyDecoder(PillowTestCase):
@skip_unless_feature("webp") @skip_unless_feature("webp")
@skip_unless_feature("webp_anim") @skip_unless_feature("webp_anim")
def test_exif_webp(self): def test_exif_webp(self, tmp_path):
with Image.open("Tests/images/hopper.webp") as im: with Image.open("Tests/images/hopper.webp") as im:
exif = im.getexif() exif = im.getexif()
assert exif == {} assert exif == {}
out = self.tempfile("temp.webp") out = str(tmp_path / "temp.webp")
exif[258] = 8 exif[258] = 8
exif[40963] = 455 exif[40963] = 455
exif[305] = "Pillow test" exif[305] = "Pillow test"
@ -304,12 +303,12 @@ class TestPyDecoder(PillowTestCase):
im.save(out, exif=exif, save_all=True) im.save(out, exif=exif, save_all=True)
check_exif() check_exif()
def test_exif_png(self): def test_exif_png(self, tmp_path):
with Image.open("Tests/images/exif.png") as im: with Image.open("Tests/images/exif.png") as im:
exif = im.getexif() exif = im.getexif()
assert exif == {274: 1} assert exif == {274: 1}
out = self.tempfile("temp.png") out = str(tmp_path / "temp.png")
exif[258] = 8 exif[258] = 8
del exif[274] del exif[274]
exif[40963] = 455 exif[40963] = 455

View File

@ -3,7 +3,6 @@ import distutils.version
import os import os
import re import re
import shutil import shutil
import unittest
from io import BytesIO from io import BytesIO
from unittest import mock from unittest import mock
@ -441,7 +440,7 @@ class TestImageFont(PillowTestCase):
with pytest.raises(UnicodeEncodeError): with pytest.raises(UnicodeEncodeError):
font.getsize("") font.getsize("")
@unittest.skipIf(is_pypy(), "failing on PyPy") @pytest.mark.skipif(is_pypy(), reason="failing on PyPy")
def test_unicode_extended(self): def test_unicode_extended(self):
# issue #3777 # issue #3777
text = "A\u278A\U0001F12B" text = "A\u278A\U0001F12B"
@ -478,7 +477,7 @@ class TestImageFont(PillowTestCase):
name = font.getname() name = font.getname()
assert ("FreeMono", "Regular") == name assert ("FreeMono", "Regular") == name
@unittest.skipIf(is_win32(), "requires Unix or macOS") @pytest.mark.skipif(is_win32(), reason="requires Unix or macOS")
def test_find_linux_font(self): def test_find_linux_font(self):
# A lot of mocking here - this is more for hitting code and # A lot of mocking here - this is more for hitting code and
# catching syntax like errors # catching syntax like errors
@ -523,7 +522,7 @@ class TestImageFont(PillowTestCase):
font_directory + "/Duplicate.ttf", "Duplicate" font_directory + "/Duplicate.ttf", "Duplicate"
) )
@unittest.skipIf(is_win32(), "requires Unix or macOS") @pytest.mark.skipif(is_win32(), reason="requires Unix or macOS")
def test_find_macos_font(self): def test_find_macos_font(self):
# Like the linux test, more cover hitting code rather than testing # Like the linux test, more cover hitting code rather than testing
# correctness. # correctness.

View File

@ -1,21 +1,23 @@
import pytest
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw, ImageFont
from .helper import PillowTestCase, assert_image_similar, skip_unless_feature from .helper import assert_image_similar, skip_unless_feature
FONT_SIZE = 20 FONT_SIZE = 20
FONT_PATH = "Tests/fonts/DejaVuSans.ttf" FONT_PATH = "Tests/fonts/DejaVuSans.ttf"
pytestmark = skip_unless_feature("raqm")
@skip_unless_feature("raqm")
class TestImagecomplextext(PillowTestCase): def test_english():
def test_english(self):
# smoke test, this should not fail # smoke test, this should not fail
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100)) im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im) draw = ImageDraw.Draw(im)
draw.text((0, 0), "TEST", font=ttf, fill=500, direction="ltr") draw.text((0, 0), "TEST", font=ttf, fill=500, direction="ltr")
def test_complex_text(self):
def test_complex_text():
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100)) im = Image.new(mode="RGB", size=(300, 100))
@ -26,7 +28,8 @@ class TestImagecomplextext(PillowTestCase):
with Image.open(target) as target_img: with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5) assert_image_similar(im, target_img, 0.5)
def test_y_offset(self):
def test_y_offset():
ttf = ImageFont.truetype("Tests/fonts/NotoNastaliqUrdu-Regular.ttf", FONT_SIZE) ttf = ImageFont.truetype("Tests/fonts/NotoNastaliqUrdu-Regular.ttf", FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100)) im = Image.new(mode="RGB", size=(300, 100))
@ -37,7 +40,8 @@ class TestImagecomplextext(PillowTestCase):
with Image.open(target) as target_img: with Image.open(target) as target_img:
assert_image_similar(im, target_img, 1.7) assert_image_similar(im, target_img, 1.7)
def test_complex_unicode_text(self):
def test_complex_unicode_text():
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100)) im = Image.new(mode="RGB", size=(300, 100))
@ -58,7 +62,8 @@ class TestImagecomplextext(PillowTestCase):
with Image.open(target) as target_img: with Image.open(target) as target_img:
assert_image_similar(im, target_img, 2.3) assert_image_similar(im, target_img, 2.3)
def test_text_direction_rtl(self):
def test_text_direction_rtl():
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100)) im = Image.new(mode="RGB", size=(300, 100))
@ -69,7 +74,8 @@ class TestImagecomplextext(PillowTestCase):
with Image.open(target) as target_img: with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5) assert_image_similar(im, target_img, 0.5)
def test_text_direction_ltr(self):
def test_text_direction_ltr():
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100)) im = Image.new(mode="RGB", size=(300, 100))
@ -80,7 +86,8 @@ class TestImagecomplextext(PillowTestCase):
with Image.open(target) as target_img: with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5) assert_image_similar(im, target_img, 0.5)
def test_text_direction_rtl2(self):
def test_text_direction_rtl2():
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100)) im = Image.new(mode="RGB", size=(300, 100))
@ -91,7 +98,8 @@ class TestImagecomplextext(PillowTestCase):
with Image.open(target) as target_img: with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5) assert_image_similar(im, target_img, 0.5)
def test_text_direction_ttb(self):
def test_text_direction_ttb():
ttf = ImageFont.truetype("Tests/fonts/NotoSansJP-Regular.otf", FONT_SIZE) ttf = ImageFont.truetype("Tests/fonts/NotoSansJP-Regular.otf", FONT_SIZE)
im = Image.new(mode="RGB", size=(100, 300)) im = Image.new(mode="RGB", size=(100, 300))
@ -100,13 +108,14 @@ class TestImagecomplextext(PillowTestCase):
draw.text((0, 0), "English あい", font=ttf, fill=500, direction="ttb") draw.text((0, 0), "English あい", font=ttf, fill=500, direction="ttb")
except ValueError as ex: except ValueError as ex:
if str(ex) == "libraqm 0.7 or greater required for 'ttb' direction": if str(ex) == "libraqm 0.7 or greater required for 'ttb' direction":
self.skipTest("libraqm 0.7 or greater not available") pytest.skip("libraqm 0.7 or greater not available")
target = "Tests/images/test_direction_ttb.png" target = "Tests/images/test_direction_ttb.png"
with Image.open(target) as target_img: with Image.open(target) as target_img:
assert_image_similar(im, target_img, 1.15) assert_image_similar(im, target_img, 1.15)
def test_text_direction_ttb_stroke(self):
def test_text_direction_ttb_stroke():
ttf = ImageFont.truetype("Tests/fonts/NotoSansJP-Regular.otf", 50) ttf = ImageFont.truetype("Tests/fonts/NotoSansJP-Regular.otf", 50)
im = Image.new(mode="RGB", size=(100, 300)) im = Image.new(mode="RGB", size=(100, 300))
@ -123,13 +132,14 @@ class TestImagecomplextext(PillowTestCase):
) )
except ValueError as ex: except ValueError as ex:
if str(ex) == "libraqm 0.7 or greater required for 'ttb' direction": if str(ex) == "libraqm 0.7 or greater required for 'ttb' direction":
self.skipTest("libraqm 0.7 or greater not available") pytest.skip("libraqm 0.7 or greater not available")
target = "Tests/images/test_direction_ttb_stroke.png" target = "Tests/images/test_direction_ttb_stroke.png"
with Image.open(target) as target_img: with Image.open(target) as target_img:
assert_image_similar(im, target_img, 12.4) assert_image_similar(im, target_img, 12.4)
def test_ligature_features(self):
def test_ligature_features():
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100)) im = Image.new(mode="RGB", size=(300, 100))
@ -142,7 +152,8 @@ class TestImagecomplextext(PillowTestCase):
liga_size = ttf.getsize("fi", features=["-liga"]) liga_size = ttf.getsize("fi", features=["-liga"])
assert liga_size == (13, 19) assert liga_size == (13, 19)
def test_kerning_features(self):
def test_kerning_features():
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100)) im = Image.new(mode="RGB", size=(300, 100))
@ -153,7 +164,8 @@ class TestImagecomplextext(PillowTestCase):
with Image.open(target) as target_img: with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5) assert_image_similar(im, target_img, 0.5)
def test_arabictext_features(self):
def test_arabictext_features():
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100)) im = Image.new(mode="RGB", size=(300, 100))
@ -170,7 +182,8 @@ class TestImagecomplextext(PillowTestCase):
with Image.open(target) as target_img: with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5) assert_image_similar(im, target_img, 0.5)
def test_x_max_and_y_offset(self):
def test_x_max_and_y_offset():
ttf = ImageFont.truetype("Tests/fonts/ArefRuqaa-Regular.ttf", 40) ttf = ImageFont.truetype("Tests/fonts/ArefRuqaa-Regular.ttf", 40)
im = Image.new(mode="RGB", size=(50, 100)) im = Image.new(mode="RGB", size=(50, 100))
@ -181,7 +194,8 @@ class TestImagecomplextext(PillowTestCase):
with Image.open(target) as target_img: with Image.open(target) as target_img:
assert_image_similar(im, target_img, 0.5) assert_image_similar(im, target_img, 0.5)
def test_language(self):
def test_language():
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
im = Image.new(mode="RGB", size=(300, 100)) im = Image.new(mode="RGB", size=(300, 100))

View File

@ -1,12 +1,14 @@
import subprocess import subprocess
import sys import sys
from .helper import PillowTestCase, assert_image import pytest
from .helper import assert_image
try: try:
from PIL import ImageGrab from PIL import ImageGrab
class TestImageGrab(PillowTestCase): class TestImageGrab:
def test_grab(self): def test_grab(self):
for im in [ for im in [
ImageGrab.grab(), ImageGrab.grab(),
@ -39,12 +41,13 @@ $bmp = New-Object Drawing.Bitmap 200, 200
except ImportError: except ImportError:
class TestImageGrab(PillowTestCase): class TestImageGrab:
@pytest.mark.skip(reason="ImageGrab ImportError")
def test_skip(self): def test_skip(self):
self.skipTest("ImportError") pass
class TestImageGrabImport(PillowTestCase): class TestImageGrabImport:
def test_import(self): def test_import(self):
# Arrange # Arrange
exception = None exception = None

View File

@ -2,33 +2,10 @@
import pytest import pytest
from PIL import Image, ImageMorph, _imagingmorph from PIL import Image, ImageMorph, _imagingmorph
from .helper import PillowTestCase, assert_image_equal, hopper from .helper import assert_image_equal, hopper
class MorphTests(PillowTestCase): def string_to_img(image_string):
def setUp(self):
self.A = self.string_to_img(
"""
.......
.......
..111..
..111..
..111..
.......
.......
"""
)
def img_to_string(self, im):
"""Turn a (small) binary image into a string representation"""
chars = ".1"
width, height = im.size
return "\n".join(
"".join(chars[im.getpixel((c, r)) > 0] for c in range(width))
for r in range(height)
)
def string_to_img(self, image_string):
"""Turn a string image representation into a binary image""" """Turn a string image representation into a binary image"""
rows = [s for s in image_string.replace(" ", "").split("\n") if len(s)] rows = [s for s in image_string.replace(" ", "").split("\n") if len(s)]
height = len(rows) height = len(rows)
@ -42,28 +19,57 @@ class MorphTests(PillowTestCase):
return im return im
def img_string_normalize(self, im):
return self.img_to_string(self.string_to_img(im))
def assert_img_equal(self, A, B): A = string_to_img(
assert self.img_to_string(A) == self.img_to_string(B) """
.......
.......
..111..
..111..
..111..
.......
.......
"""
)
def assert_img_equal_img_string(self, A, Bstring):
assert self.img_to_string(A) == self.img_string_normalize(Bstring)
def test_str_to_img(self): def img_to_string(im):
"""Turn a (small) binary image into a string representation"""
chars = ".1"
width, height = im.size
return "\n".join(
"".join(chars[im.getpixel((c, r)) > 0] for c in range(width))
for r in range(height)
)
def img_string_normalize(im):
return img_to_string(string_to_img(im))
def assert_img_equal(A, B):
assert img_to_string(A) == img_to_string(B)
def assert_img_equal_img_string(A, Bstring):
assert img_to_string(A) == img_string_normalize(Bstring)
def test_str_to_img():
with Image.open("Tests/images/morph_a.png") as im: with Image.open("Tests/images/morph_a.png") as im:
assert_image_equal(self.A, im) assert_image_equal(A, im)
def create_lut(self):
def create_lut():
for op in ("corner", "dilation4", "dilation8", "erosion4", "erosion8", "edge"): for op in ("corner", "dilation4", "dilation8", "erosion4", "erosion8", "edge"):
lb = ImageMorph.LutBuilder(op_name=op) lb = ImageMorph.LutBuilder(op_name=op)
lut = lb.build_lut() lut = lb.build_lut()
with open("Tests/images/%s.lut" % op, "wb") as f: with open("Tests/images/%s.lut" % op, "wb") as f:
f.write(lut) f.write(lut)
# create_lut()
def test_lut(self): # create_lut()
def test_lut():
for op in ("corner", "dilation4", "dilation8", "erosion4", "erosion8", "edge"): for op in ("corner", "dilation4", "dilation8", "erosion4", "erosion8", "edge"):
lb = ImageMorph.LutBuilder(op_name=op) lb = ImageMorph.LutBuilder(op_name=op)
assert lb.get_lut() is None assert lb.get_lut() is None
@ -72,7 +78,8 @@ class MorphTests(PillowTestCase):
with open("Tests/images/%s.lut" % op, "rb") as f: with open("Tests/images/%s.lut" % op, "rb") as f:
assert lut == bytearray(f.read()) assert lut == bytearray(f.read())
def test_no_operator_loaded(self):
def test_no_operator_loaded():
mop = ImageMorph.MorphOp() mop = ImageMorph.MorphOp()
with pytest.raises(Exception) as e: with pytest.raises(Exception) as e:
mop.apply(None) mop.apply(None)
@ -84,13 +91,14 @@ class MorphTests(PillowTestCase):
mop.save_lut(None) mop.save_lut(None)
assert str(e.value) == "No operator loaded" assert str(e.value) == "No operator loaded"
# Test the named patterns
def test_erosion8(self): # Test the named patterns
def test_erosion8():
# erosion8 # erosion8
mop = ImageMorph.MorphOp(op_name="erosion8") mop = ImageMorph.MorphOp(op_name="erosion8")
count, Aout = mop.apply(self.A) count, Aout = mop.apply(A)
assert count == 8 assert count == 8
self.assert_img_equal_img_string( assert_img_equal_img_string(
Aout, Aout,
""" """
....... .......
@ -103,12 +111,13 @@ class MorphTests(PillowTestCase):
""", """,
) )
def test_dialation8(self):
def test_dialation8():
# dialation8 # dialation8
mop = ImageMorph.MorphOp(op_name="dilation8") mop = ImageMorph.MorphOp(op_name="dilation8")
count, Aout = mop.apply(self.A) count, Aout = mop.apply(A)
assert count == 16 assert count == 16
self.assert_img_equal_img_string( assert_img_equal_img_string(
Aout, Aout,
""" """
....... .......
@ -121,12 +130,13 @@ class MorphTests(PillowTestCase):
""", """,
) )
def test_erosion4(self):
def test_erosion4():
# erosion4 # erosion4
mop = ImageMorph.MorphOp(op_name="dilation4") mop = ImageMorph.MorphOp(op_name="dilation4")
count, Aout = mop.apply(self.A) count, Aout = mop.apply(A)
assert count == 12 assert count == 12
self.assert_img_equal_img_string( assert_img_equal_img_string(
Aout, Aout,
""" """
....... .......
@ -139,12 +149,13 @@ class MorphTests(PillowTestCase):
""", """,
) )
def test_edge(self):
def test_edge():
# edge # edge
mop = ImageMorph.MorphOp(op_name="edge") mop = ImageMorph.MorphOp(op_name="edge")
count, Aout = mop.apply(self.A) count, Aout = mop.apply(A)
assert count == 1 assert count == 1
self.assert_img_equal_img_string( assert_img_equal_img_string(
Aout, Aout,
""" """
....... .......
@ -157,12 +168,13 @@ class MorphTests(PillowTestCase):
""", """,
) )
def test_corner(self):
def test_corner():
# Create a corner detector pattern # Create a corner detector pattern
mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "4:(00. 01. ...)->1"]) mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "4:(00. 01. ...)->1"])
count, Aout = mop.apply(self.A) count, Aout = mop.apply(A)
assert count == 5 assert count == 5
self.assert_img_equal_img_string( assert_img_equal_img_string(
Aout, Aout,
""" """
....... .......
@ -176,7 +188,7 @@ class MorphTests(PillowTestCase):
) )
# Test the coordinate counting with the same operator # Test the coordinate counting with the same operator
coords = mop.match(self.A) coords = mop.match(A)
assert len(coords) == 4 assert len(coords) == 4
assert tuple(coords) == ((2, 2), (4, 2), (2, 4), (4, 4)) assert tuple(coords) == ((2, 2), (4, 2), (2, 4), (4, 4))
@ -184,12 +196,13 @@ class MorphTests(PillowTestCase):
assert len(coords) == 4 assert len(coords) == 4
assert tuple(coords) == ((2, 2), (4, 2), (2, 4), (4, 4)) assert tuple(coords) == ((2, 2), (4, 2), (2, 4), (4, 4))
def test_mirroring(self):
def test_mirroring():
# Test 'M' for mirroring # Test 'M' for mirroring
mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "M:(00. 01. ...)->1"]) mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "M:(00. 01. ...)->1"])
count, Aout = mop.apply(self.A) count, Aout = mop.apply(A)
assert count == 7 assert count == 7
self.assert_img_equal_img_string( assert_img_equal_img_string(
Aout, Aout,
""" """
....... .......
@ -202,12 +215,13 @@ class MorphTests(PillowTestCase):
""", """,
) )
def test_negate(self):
def test_negate():
# Test 'N' for negate # Test 'N' for negate
mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "N:(00. 01. ...)->1"]) mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "N:(00. 01. ...)->1"])
count, Aout = mop.apply(self.A) count, Aout = mop.apply(A)
assert count == 8 assert count == 8
self.assert_img_equal_img_string( assert_img_equal_img_string(
Aout, Aout,
""" """
....... .......
@ -220,7 +234,8 @@ class MorphTests(PillowTestCase):
""", """,
) )
def test_non_binary_images(self):
def test_non_binary_images():
im = hopper("RGB") im = hopper("RGB")
mop = ImageMorph.MorphOp(op_name="erosion8") mop = ImageMorph.MorphOp(op_name="erosion8")
@ -234,7 +249,8 @@ class MorphTests(PillowTestCase):
mop.get_on_pixels(im) mop.get_on_pixels(im)
assert str(e.value) == "Image must be binary, meaning it must use mode L" assert str(e.value) == "Image must be binary, meaning it must use mode L"
def test_add_patterns(self):
def test_add_patterns():
# Arrange # Arrange
lb = ImageMorph.LutBuilder(op_name="corner") lb = ImageMorph.LutBuilder(op_name="corner")
assert lb.patterns == ["1:(... ... ...)->0", "4:(00. 01. ...)->1"] assert lb.patterns == ["1:(... ... ...)->0", "4:(00. 01. ...)->1"]
@ -251,11 +267,13 @@ class MorphTests(PillowTestCase):
"N:(00. 01. ...)->1", "N:(00. 01. ...)->1",
] ]
def test_unknown_pattern(self):
def test_unknown_pattern():
with pytest.raises(Exception): with pytest.raises(Exception):
ImageMorph.LutBuilder(op_name="unknown") ImageMorph.LutBuilder(op_name="unknown")
def test_pattern_syntax_error(self):
def test_pattern_syntax_error():
# Arrange # Arrange
lb = ImageMorph.LutBuilder(op_name="corner") lb = ImageMorph.LutBuilder(op_name="corner")
new_patterns = ["a pattern with a syntax error"] new_patterns = ["a pattern with a syntax error"]
@ -266,7 +284,8 @@ class MorphTests(PillowTestCase):
lb.build_lut() lb.build_lut()
assert str(e.value) == 'Syntax error in pattern "a pattern with a syntax error"' assert str(e.value) == 'Syntax error in pattern "a pattern with a syntax error"'
def test_load_invalid_mrl(self):
def test_load_invalid_mrl():
# Arrange # Arrange
invalid_mrl = "Tests/images/hopper.png" invalid_mrl = "Tests/images/hopper.png"
mop = ImageMorph.MorphOp() mop = ImageMorph.MorphOp()
@ -276,9 +295,10 @@ class MorphTests(PillowTestCase):
mop.load_lut(invalid_mrl) mop.load_lut(invalid_mrl)
assert str(e.value) == "Wrong size operator file!" assert str(e.value) == "Wrong size operator file!"
def test_roundtrip_mrl(self):
def test_roundtrip_mrl(tmp_path):
# Arrange # Arrange
tempfile = self.tempfile("temp.mrl") tempfile = str(tmp_path / "temp.mrl")
mop = ImageMorph.MorphOp(op_name="corner") mop = ImageMorph.MorphOp(op_name="corner")
initial_lut = mop.lut initial_lut = mop.lut
@ -289,7 +309,8 @@ class MorphTests(PillowTestCase):
# Act / Assert # Act / Assert
assert mop.lut == initial_lut assert mop.lut == initial_lut
def test_set_lut(self):
def test_set_lut():
# Arrange # Arrange
lb = ImageMorph.LutBuilder(op_name="corner") lb = ImageMorph.LutBuilder(op_name="corner")
lut = lb.build_lut() lut = lb.build_lut()
@ -301,7 +322,8 @@ class MorphTests(PillowTestCase):
# Assert # Assert
assert mop.lut == lut assert mop.lut == lut
def test_wrong_mode(self):
def test_wrong_mode():
lut = ImageMorph.LutBuilder(op_name="corner").build_lut() lut = ImageMorph.LutBuilder(op_name="corner").build_lut()
imrgb = Image.new("RGB", (10, 10)) imrgb = Image.new("RGB", (10, 10))
iml = Image.new("L", (10, 10)) iml = Image.new("L", (10, 10))

View File

@ -4,10 +4,8 @@ import struct
import pytest import pytest
from PIL import Image, ImagePath from PIL import Image, ImagePath
from .helper import PillowTestCase
class TestImagePath:
class TestImagePath(PillowTestCase):
def test_path(self): def test_path(self):
p = ImagePath.Path(list(range(10))) p = ImagePath.Path(list(range(10)))

View File

@ -3,12 +3,10 @@ import sys
import pytest import pytest
from PIL import Image from PIL import Image
from .helper import PillowTestCase
X = 255 X = 255
class TestLibPack(PillowTestCase): class TestLibPack:
def assert_pack(self, mode, rawmode, data, *pixels): def assert_pack(self, mode, rawmode, data, *pixels):
""" """
data - either raw bytes with data or just number of bytes in rawmode. data - either raw bytes with data or just number of bytes in rawmode.
@ -223,7 +221,7 @@ class TestLibPack(PillowTestCase):
) )
class TestLibUnpack(PillowTestCase): class TestLibUnpack:
def assert_unpack(self, mode, rawmode, data, *pixels): def assert_unpack(self, mode, rawmode, data, *pixels):
""" """
data - either raw bytes with data or just number of bytes in rawmode. data - either raw bytes with data or just number of bytes in rawmode.

View File

@ -1,14 +1,12 @@
from PIL import Image from PIL import Image
from .helper import PillowTestCase, hopper from .helper import hopper
original = hopper().resize((32, 32)).convert("I")
class TestModeI16(PillowTestCase): def verify(im1):
im2 = original.copy()
original = hopper().resize((32, 32)).convert("I")
def verify(self, im1):
im2 = self.original.copy()
assert im1.size == im2.size assert im1.size == im2.size
pix1 = im1.load() pix1 = im1.load()
pix2 = im2.load() pix2 = im2.load()
@ -21,40 +19,41 @@ class TestModeI16(PillowTestCase):
p1, im1.mode, xy, p2 p1, im1.mode, xy, p2
) )
def test_basic(self):
def test_basic(tmp_path):
# PIL 1.1 has limited support for 16-bit image data. Check that # PIL 1.1 has limited support for 16-bit image data. Check that
# create/copy/transform and save works as expected. # create/copy/transform and save works as expected.
def basic(mode): def basic(mode):
imIn = self.original.convert(mode) imIn = original.convert(mode)
self.verify(imIn) verify(imIn)
w, h = imIn.size w, h = imIn.size
imOut = imIn.copy() imOut = imIn.copy()
self.verify(imOut) # copy verify(imOut) # copy
imOut = imIn.transform((w, h), Image.EXTENT, (0, 0, w, h)) imOut = imIn.transform((w, h), Image.EXTENT, (0, 0, w, h))
self.verify(imOut) # transform verify(imOut) # transform
filename = self.tempfile("temp.im") filename = str(tmp_path / "temp.im")
imIn.save(filename) imIn.save(filename)
with Image.open(filename) as imOut: with Image.open(filename) as imOut:
self.verify(imIn) verify(imIn)
self.verify(imOut) verify(imOut)
imOut = imIn.crop((0, 0, w, h)) imOut = imIn.crop((0, 0, w, h))
self.verify(imOut) verify(imOut)
imOut = Image.new(mode, (w, h), None) imOut = Image.new(mode, (w, h), None)
imOut.paste(imIn.crop((0, 0, w // 2, h)), (0, 0)) imOut.paste(imIn.crop((0, 0, w // 2, h)), (0, 0))
imOut.paste(imIn.crop((w // 2, 0, w, h)), (w // 2, 0)) imOut.paste(imIn.crop((w // 2, 0, w, h)), (w // 2, 0))
self.verify(imIn) verify(imIn)
self.verify(imOut) verify(imOut)
imIn = Image.new(mode, (1, 1), 1) imIn = Image.new(mode, (1, 1), 1)
assert imIn.getpixel((0, 0)) == 1 assert imIn.getpixel((0, 0)) == 1
@ -81,7 +80,8 @@ class TestModeI16(PillowTestCase):
basic("I") basic("I")
def test_tobytes(self):
def test_tobytes():
def tobytes(mode): def tobytes(mode):
return Image.new(mode, (1, 1), 1).tobytes() return Image.new(mode, (1, 1), 1).tobytes()
@ -92,14 +92,15 @@ class TestModeI16(PillowTestCase):
assert tobytes("I;16B") == b"\x00\x01" assert tobytes("I;16B") == b"\x00\x01"
assert tobytes("I") == b"\x01\x00\x00\x00"[::order] assert tobytes("I") == b"\x01\x00\x00\x00"[::order]
def test_convert(self):
im = self.original.copy() def test_convert():
self.verify(im.convert("I;16")) im = original.copy()
self.verify(im.convert("I;16").convert("L"))
self.verify(im.convert("I;16").convert("I"))
self.verify(im.convert("I;16B")) verify(im.convert("I;16"))
self.verify(im.convert("I;16B").convert("L")) verify(im.convert("I;16").convert("L"))
self.verify(im.convert("I;16B").convert("I")) verify(im.convert("I;16").convert("I"))
verify(im.convert("I;16B"))
verify(im.convert("I;16B").convert("L"))
verify(im.convert("I;16B").convert("I"))

View File

@ -2,14 +2,11 @@ import pickle
from PIL import Image from PIL import Image
from .helper import PillowTestCase
def helper_pickle_file(tmp_path, pickle, protocol=0, mode=None):
class TestPickle(PillowTestCase):
def helper_pickle_file(self, pickle, protocol=0, mode=None):
# Arrange # Arrange
with Image.open("Tests/images/hopper.jpg") as im: with Image.open("Tests/images/hopper.jpg") as im:
filename = self.tempfile("temp.pkl") filename = str(tmp_path / "temp.pkl")
if mode: if mode:
im = im.convert(mode) im = im.convert(mode)
@ -22,9 +19,10 @@ class TestPickle(PillowTestCase):
# Assert # Assert
assert im == loaded_im assert im == loaded_im
def helper_pickle_string(
self, pickle, protocol=0, test_file="Tests/images/hopper.jpg", mode=None def helper_pickle_string(
): pickle, protocol=0, test_file="Tests/images/hopper.jpg", mode=None
):
with Image.open(test_file) as im: with Image.open(test_file) as im:
if mode: if mode:
im = im.convert(mode) im = im.convert(mode)
@ -36,13 +34,15 @@ class TestPickle(PillowTestCase):
# Assert # Assert
assert im == loaded_im assert im == loaded_im
def test_pickle_image(self):
def test_pickle_image(tmp_path):
# Act / Assert # Act / Assert
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1): for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
self.helper_pickle_string(pickle, protocol) helper_pickle_string(pickle, protocol)
self.helper_pickle_file(pickle, protocol) helper_pickle_file(tmp_path, pickle, protocol)
def test_pickle_p_mode(self):
def test_pickle_p_mode():
# Act / Assert # Act / Assert
for test_file in [ for test_file in [
"Tests/images/test-card.png", "Tests/images/test-card.png",
@ -55,25 +55,26 @@ class TestPickle(PillowTestCase):
"Tests/images/itxt_chunks.png", "Tests/images/itxt_chunks.png",
]: ]:
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1): for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
self.helper_pickle_string( helper_pickle_string(pickle, protocol=protocol, test_file=test_file)
pickle, protocol=protocol, test_file=test_file
)
def test_pickle_pa_mode(self):
def test_pickle_pa_mode(tmp_path):
# Act / Assert # Act / Assert
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1): for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
self.helper_pickle_string(pickle, protocol, mode="PA") helper_pickle_string(pickle, protocol, mode="PA")
self.helper_pickle_file(pickle, protocol, mode="PA") helper_pickle_file(tmp_path, pickle, protocol, mode="PA")
def test_pickle_l_mode(self):
def test_pickle_l_mode(tmp_path):
# Act / Assert # Act / Assert
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1): for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
self.helper_pickle_string(pickle, protocol, mode="L") helper_pickle_string(pickle, protocol, mode="L")
self.helper_pickle_file(pickle, protocol, mode="L") helper_pickle_file(tmp_path, pickle, protocol, mode="L")
def test_pickle_la_mode_with_palette(self):
def test_pickle_la_mode_with_palette(tmp_path):
# Arrange # Arrange
filename = self.tempfile("temp.pkl") filename = str(tmp_path / "temp.pkl")
with Image.open("Tests/images/hopper.jpg") as im: with Image.open("Tests/images/hopper.jpg") as im:
im = im.convert("PA") im = im.convert("PA")