Convert to use pytest

This commit is contained in:
Hugo 2020-03-02 16:02:19 +02:00
parent 54b4b98455
commit 2cc6a9a974
13 changed files with 559 additions and 535 deletions

View File

@ -5,7 +5,6 @@ Helper functions.
import logging import logging
import os import os
import shutil import shutil
import subprocess
import sys import sys
import tempfile import tempfile
import unittest import unittest
@ -192,21 +191,9 @@ class PillowTestCase(unittest.TestCase):
self.addCleanup(self.delete_tempfile, path) self.addCleanup(self.delete_tempfile, path)
return path return path
def open_withImagemagick(self, f):
if not imagemagick_available():
raise OSError()
outfile = self.tempfile("temp.png") @pytest.mark.skipif(sys.platform.startswith("win32"), reason="Requires Unix or macOS")
rc = subprocess.call( class PillowLeakTestCase:
[IMCONVERT, f, outfile], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT
)
if rc:
raise OSError
return Image.open(outfile)
@unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS")
class PillowLeakTestCase(PillowTestCase):
# requires unix/macOS # requires unix/macOS
iterations = 100 # count iterations = 100 # count
mem_limit = 512 # k mem_limit = 512 # k

View File

@ -1,10 +1,9 @@
import sys import sys
import unittest
import pytest import pytest
from PIL import Image from PIL import Image
from .helper import PillowTestCase, is_pypy from .helper import is_pypy
def test_get_stats(): def test_get_stats():
@ -32,8 +31,8 @@ def test_reset_stats():
assert stats["blocks_cached"] == 0 assert stats["blocks_cached"] == 0
class TestCoreMemory(PillowTestCase): class TestCoreMemory:
def tearDown(self): def teardown_method(self):
# Restore default values # Restore default values
Image.core.set_alignment(1) Image.core.set_alignment(1)
Image.core.set_block_size(1024 * 1024) Image.core.set_block_size(1024 * 1024)
@ -114,7 +113,7 @@ class TestCoreMemory(PillowTestCase):
with pytest.raises(ValueError): with pytest.raises(ValueError):
Image.core.set_blocks_max(2 ** 29) Image.core.set_blocks_max(2 ** 29)
@unittest.skipIf(is_pypy(), "images are not collected") @pytest.mark.skipif(is_pypy(), reason="Images not collected")
def test_set_blocks_max_stats(self): def test_set_blocks_max_stats(self):
Image.core.reset_stats() Image.core.reset_stats()
Image.core.set_blocks_max(128) Image.core.set_blocks_max(128)
@ -129,7 +128,7 @@ class TestCoreMemory(PillowTestCase):
assert stats["freed_blocks"] == 0 assert stats["freed_blocks"] == 0
assert stats["blocks_cached"] == 64 assert stats["blocks_cached"] == 64
@unittest.skipIf(is_pypy(), "images are not collected") @pytest.mark.skipif(is_pypy(), reason="Images not collected")
def test_clear_cache_stats(self): def test_clear_cache_stats(self):
Image.core.reset_stats() Image.core.reset_stats()
Image.core.clear_cache() Image.core.clear_cache()
@ -163,8 +162,8 @@ class TestCoreMemory(PillowTestCase):
assert stats["freed_blocks"] >= 16 assert stats["freed_blocks"] >= 16
class TestEnvVars(PillowTestCase): class TestEnvVars:
def tearDown(self): def teardown_method(self):
# Restore default values # Restore default values
Image.core.set_alignment(1) Image.core.set_alignment(1)
Image.core.set_block_size(1024 * 1024) Image.core.set_block_size(1024 * 1024)

View File

@ -6,7 +6,6 @@ import pytest
from PIL import ExifTags, Image, ImageFile, JpegImagePlugin from PIL import ExifTags, Image, ImageFile, JpegImagePlugin
from .helper import ( from .helper import (
PillowTestCase,
assert_image, assert_image,
assert_image_equal, assert_image_equal,
assert_image_similar, assert_image_similar,
@ -15,14 +14,13 @@ from .helper import (
hopper, hopper,
is_win32, is_win32,
skip_unless_feature, skip_unless_feature,
unittest,
) )
TEST_FILE = "Tests/images/hopper.jpg" TEST_FILE = "Tests/images/hopper.jpg"
@skip_unless_feature("jpg") @skip_unless_feature("jpg")
class TestFileJpeg(PillowTestCase): class TestFileJpeg:
def roundtrip(self, im, **options): def roundtrip(self, im, **options):
out = BytesIO() out = BytesIO()
im.save(out, "JPEG", **options) im.save(out, "JPEG", **options)
@ -101,13 +99,13 @@ class TestFileJpeg(PillowTestCase):
assert test(100, 200) == (100, 200) assert test(100, 200) == (100, 200)
assert test(0) is None # square pixels assert test(0) is None # square pixels
def test_icc(self): def test_icc(self, tmp_path):
# Test ICC support # Test ICC support
with Image.open("Tests/images/rgb.jpg") as im1: with Image.open("Tests/images/rgb.jpg") as im1:
icc_profile = im1.info["icc_profile"] icc_profile = im1.info["icc_profile"]
assert len(icc_profile) == 3144 assert len(icc_profile) == 3144
# Roundtrip via physical file. # Roundtrip via physical file.
f = self.tempfile("temp.jpg") f = str(tmp_path / "temp.jpg")
im1.save(f, icc_profile=icc_profile) im1.save(f, icc_profile=icc_profile)
with Image.open(f) as im2: with Image.open(f) as im2:
assert im2.info.get("icc_profile") == icc_profile assert im2.info.get("icc_profile") == icc_profile
@ -140,12 +138,12 @@ class TestFileJpeg(PillowTestCase):
test(ImageFile.MAXBLOCK + 1) # full buffer block plus one byte test(ImageFile.MAXBLOCK + 1) # full buffer block plus one byte
test(ImageFile.MAXBLOCK * 4 + 3) # large block test(ImageFile.MAXBLOCK * 4 + 3) # large block
def test_large_icc_meta(self): def test_large_icc_meta(self, tmp_path):
# https://github.com/python-pillow/Pillow/issues/148 # https://github.com/python-pillow/Pillow/issues/148
# Sometimes the meta data on the icc_profile block is bigger than # Sometimes the meta data on the icc_profile block is bigger than
# Image.MAXBLOCK or the image size. # Image.MAXBLOCK or the image size.
with Image.open("Tests/images/icc_profile_big.jpg") as im: with Image.open("Tests/images/icc_profile_big.jpg") as im:
f = self.tempfile("temp.jpg") f = str(tmp_path / "temp.jpg")
icc_profile = im.info["icc_profile"] icc_profile = im.info["icc_profile"]
# Should not raise IOError for image with icc larger than image size. # Should not raise IOError for image with icc larger than image size.
im.save( im.save(
@ -166,9 +164,9 @@ class TestFileJpeg(PillowTestCase):
assert im1.bytes >= im2.bytes assert im1.bytes >= im2.bytes
assert im1.bytes >= im3.bytes assert im1.bytes >= im3.bytes
def test_optimize_large_buffer(self): def test_optimize_large_buffer(self, tmp_path):
# https://github.com/python-pillow/Pillow/issues/148 # https://github.com/python-pillow/Pillow/issues/148
f = self.tempfile("temp.jpg") f = str(tmp_path / "temp.jpg")
# this requires ~ 1.5x Image.MAXBLOCK # this requires ~ 1.5x Image.MAXBLOCK
im = Image.new("RGB", (4096, 4096), 0xFF3333) im = Image.new("RGB", (4096, 4096), 0xFF3333)
im.save(f, format="JPEG", optimize=True) im.save(f, format="JPEG", optimize=True)
@ -184,14 +182,14 @@ class TestFileJpeg(PillowTestCase):
assert_image_equal(im1, im3) assert_image_equal(im1, im3)
assert im1.bytes >= im3.bytes assert im1.bytes >= im3.bytes
def test_progressive_large_buffer(self): def test_progressive_large_buffer(self, tmp_path):
f = self.tempfile("temp.jpg") f = str(tmp_path / "temp.jpg")
# this requires ~ 1.5x Image.MAXBLOCK # this requires ~ 1.5x Image.MAXBLOCK
im = Image.new("RGB", (4096, 4096), 0xFF3333) im = Image.new("RGB", (4096, 4096), 0xFF3333)
im.save(f, format="JPEG", progressive=True) im.save(f, format="JPEG", progressive=True)
def test_progressive_large_buffer_highest_quality(self): def test_progressive_large_buffer_highest_quality(self, tmp_path):
f = self.tempfile("temp.jpg") f = str(tmp_path / "temp.jpg")
im = self.gen_random_image((255, 255)) im = self.gen_random_image((255, 255))
# this requires more bytes than pixels in the image # this requires more bytes than pixels in the image
im.save(f, format="JPEG", progressive=True, quality=100) im.save(f, format="JPEG", progressive=True, quality=100)
@ -202,9 +200,9 @@ class TestFileJpeg(PillowTestCase):
im = self.gen_random_image((256, 256), "CMYK") im = self.gen_random_image((256, 256), "CMYK")
im.save(f, format="JPEG", progressive=True, quality=94) im.save(f, format="JPEG", progressive=True, quality=94)
def test_large_exif(self): def test_large_exif(self, tmp_path):
# https://github.com/python-pillow/Pillow/issues/148 # https://github.com/python-pillow/Pillow/issues/148
f = self.tempfile("temp.jpg") f = str(tmp_path / "temp.jpg")
im = hopper() im = hopper()
im.save(f, "JPEG", quality=90, exif=b"1" * 65532) im.save(f, "JPEG", quality=90, exif=b"1" * 65532)
@ -301,7 +299,7 @@ class TestFileJpeg(PillowTestCase):
im3 = self.roundtrip(hopper(), quality=0) im3 = self.roundtrip(hopper(), quality=0)
assert_image(im1, im3.mode, im3.size) assert_image(im1, im3.mode, im3.size)
self.assertGreater(im2.bytes, im3.bytes) assert im2.bytes > im3.bytes
def test_smooth(self): def test_smooth(self):
im1 = self.roundtrip(hopper()) im1 = self.roundtrip(hopper())
@ -346,18 +344,18 @@ class TestFileJpeg(PillowTestCase):
with Image.open("Tests/images/pil_sample_rgb.jpg") as im: with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
assert im._getmp() is None assert im._getmp() is None
def test_quality_keep(self): def test_quality_keep(self, tmp_path):
# RGB # RGB
with Image.open("Tests/images/hopper.jpg") as im: with Image.open("Tests/images/hopper.jpg") as im:
f = self.tempfile("temp.jpg") f = str(tmp_path / "temp.jpg")
im.save(f, quality="keep") im.save(f, quality="keep")
# Grayscale # Grayscale
with Image.open("Tests/images/hopper_gray.jpg") as im: with Image.open("Tests/images/hopper_gray.jpg") as im:
f = self.tempfile("temp.jpg") f = str(tmp_path / "temp.jpg")
im.save(f, quality="keep") im.save(f, quality="keep")
# CMYK # CMYK
with Image.open("Tests/images/pil_sample_cmyk.jpg") as im: with Image.open("Tests/images/pil_sample_cmyk.jpg") as im:
f = self.tempfile("temp.jpg") f = str(tmp_path / "temp.jpg")
im.save(f, quality="keep") im.save(f, quality="keep")
def test_junk_jpeg_header(self): def test_junk_jpeg_header(self):
@ -389,16 +387,16 @@ class TestFileJpeg(PillowTestCase):
with pytest.raises(IOError): with pytest.raises(IOError):
im.load() im.load()
def _n_qtables_helper(self, n, test_file): def test_qtables(self, tmp_path):
with Image.open(test_file) as im: def _n_qtables_helper(n, test_file):
f = self.tempfile("temp.jpg") with Image.open(test_file) as im:
im.save(f, qtables=[[n] * 64] * n) f = str(tmp_path / "temp.jpg")
with Image.open(f) as im: im.save(f, qtables=[[n] * 64] * n)
assert len(im.quantization) == n with Image.open(f) as im:
reloaded = self.roundtrip(im, qtables="keep") assert len(im.quantization) == n
assert im.quantization == reloaded.quantization reloaded = self.roundtrip(im, qtables="keep")
assert im.quantization == reloaded.quantization
def test_qtables(self):
with Image.open("Tests/images/hopper.jpg") as im: with Image.open("Tests/images/hopper.jpg") as im:
qtables = im.quantization qtables = im.quantization
reloaded = self.roundtrip(im, qtables=qtables, subsampling=0) reloaded = self.roundtrip(im, qtables=qtables, subsampling=0)
@ -470,14 +468,14 @@ class TestFileJpeg(PillowTestCase):
30, 30,
) )
self._n_qtables_helper(1, "Tests/images/hopper_gray.jpg") _n_qtables_helper(1, "Tests/images/hopper_gray.jpg")
self._n_qtables_helper(1, "Tests/images/pil_sample_rgb.jpg") _n_qtables_helper(1, "Tests/images/pil_sample_rgb.jpg")
self._n_qtables_helper(2, "Tests/images/pil_sample_rgb.jpg") _n_qtables_helper(2, "Tests/images/pil_sample_rgb.jpg")
self._n_qtables_helper(3, "Tests/images/pil_sample_rgb.jpg") _n_qtables_helper(3, "Tests/images/pil_sample_rgb.jpg")
self._n_qtables_helper(1, "Tests/images/pil_sample_cmyk.jpg") _n_qtables_helper(1, "Tests/images/pil_sample_cmyk.jpg")
self._n_qtables_helper(2, "Tests/images/pil_sample_cmyk.jpg") _n_qtables_helper(2, "Tests/images/pil_sample_cmyk.jpg")
self._n_qtables_helper(3, "Tests/images/pil_sample_cmyk.jpg") _n_qtables_helper(3, "Tests/images/pil_sample_cmyk.jpg")
self._n_qtables_helper(4, "Tests/images/pil_sample_cmyk.jpg") _n_qtables_helper(4, "Tests/images/pil_sample_cmyk.jpg")
# not a sequence # not a sequence
with pytest.raises(ValueError): with pytest.raises(ValueError):
@ -496,16 +494,16 @@ class TestFileJpeg(PillowTestCase):
with pytest.raises(ValueError): with pytest.raises(ValueError):
self.roundtrip(im, qtables=[[1, 2, 3, 4]]) self.roundtrip(im, qtables=[[1, 2, 3, 4]])
@unittest.skipUnless(djpeg_available(), "djpeg not available") @pytest.mark.skipif(not djpeg_available(), reason="djpeg not available")
def test_load_djpeg(self): def test_load_djpeg(self):
with Image.open(TEST_FILE) as img: with Image.open(TEST_FILE) as img:
img.load_djpeg() img.load_djpeg()
assert_image_similar(img, Image.open(TEST_FILE), 0) assert_image_similar(img, Image.open(TEST_FILE), 0)
@unittest.skipUnless(cjpeg_available(), "cjpeg not available") @pytest.mark.skipif(not cjpeg_available(), reason="cjpeg not available")
def test_save_cjpeg(self): def test_save_cjpeg(self, tmp_path):
with Image.open(TEST_FILE) as img: with Image.open(TEST_FILE) as img:
tempfile = self.tempfile("temp.jpg") tempfile = str(tmp_path / "temp.jpg")
JpegImagePlugin._save_cjpeg(img, 0, tempfile) JpegImagePlugin._save_cjpeg(img, 0, tempfile)
# Default save quality is 75%, so a tiny bit of difference is alright # Default save quality is 75%, so a tiny bit of difference is alright
assert_image_similar(img, Image.open(tempfile), 17) assert_image_similar(img, Image.open(tempfile), 17)
@ -518,9 +516,9 @@ class TestFileJpeg(PillowTestCase):
assert tag_ids["RelatedImageWidth"] == 0x1001 assert tag_ids["RelatedImageWidth"] == 0x1001
assert tag_ids["RelatedImageLength"] == 0x1002 assert tag_ids["RelatedImageLength"] == 0x1002
def test_MAXBLOCK_scaling(self): def test_MAXBLOCK_scaling(self, tmp_path):
im = self.gen_random_image((512, 512)) im = self.gen_random_image((512, 512))
f = self.tempfile("temp.jpeg") f = str(tmp_path / "temp.jpeg")
im.save(f, quality=100, optimize=True) im.save(f, quality=100, optimize=True)
with Image.open(f) as reloaded: with Image.open(f) as reloaded:
@ -555,9 +553,9 @@ class TestFileJpeg(PillowTestCase):
with pytest.raises(IOError): with pytest.raises(IOError):
img.save(out, "JPEG") img.save(out, "JPEG")
def test_save_tiff_with_dpi(self): def test_save_tiff_with_dpi(self, tmp_path):
# Arrange # Arrange
outfile = self.tempfile("temp.tif") outfile = str(tmp_path / "temp.tif")
with Image.open("Tests/images/hopper.tif") as im: with Image.open("Tests/images/hopper.tif") as im:
# Act # Act
@ -577,8 +575,8 @@ class TestFileJpeg(PillowTestCase):
with Image.open("Tests/images/iptc_roundDown.jpg") as im: with Image.open("Tests/images/iptc_roundDown.jpg") as im:
assert im.info["dpi"] == (2, 2) assert im.info["dpi"] == (2, 2)
def test_save_dpi_rounding(self): def test_save_dpi_rounding(self, tmp_path):
outfile = self.tempfile("temp.jpg") outfile = str(tmp_path / "temp.jpg")
with Image.open("Tests/images/hopper.jpg") as im: with Image.open("Tests/images/hopper.jpg") as im:
im.save(outfile, dpi=(72.2, 72.2)) im.save(outfile, dpi=(72.2, 72.2))
@ -690,11 +688,11 @@ class TestFileJpeg(PillowTestCase):
assert [65504, 24] == apps_13_lengths assert [65504, 24] == apps_13_lengths
@unittest.skipUnless(is_win32(), "Windows only") @pytest.mark.skipif(not is_win32(), reason="Windows only")
@skip_unless_feature("jpg") @skip_unless_feature("jpg")
class TestFileCloseW32(PillowTestCase): class TestFileCloseW32:
def test_fd_leak(self): def test_fd_leak(self, tmp_path):
tmpfile = self.tempfile("temp.jpg") tmpfile = str(tmp_path / "temp.jpg")
with Image.open("Tests/images/hopper.jpg") as im: with Image.open("Tests/images/hopper.jpg") as im:
im.save(tmpfile) im.save(tmpfile)

View File

@ -5,7 +5,6 @@ import pytest
from PIL import Image, ImageFile, Jpeg2KImagePlugin from PIL import Image, ImageFile, Jpeg2KImagePlugin
from .helper import ( from .helper import (
PillowTestCase,
assert_image_equal, assert_image_equal,
assert_image_similar, assert_image_similar,
is_big_endian, is_big_endian,
@ -13,6 +12,8 @@ from .helper import (
skip_unless_feature, skip_unless_feature,
) )
pytestmark = skip_unless_feature("jpg_2000")
test_card = Image.open("Tests/images/test-card.png") test_card = Image.open("Tests/images/test-card.png")
test_card.load() test_card.load()
@ -21,190 +22,207 @@ test_card.load()
# 'Not enough memory to handle tile data' # 'Not enough memory to handle tile data'
@skip_unless_feature("jpg_2000") def roundtrip(im, **options):
class TestFileJpeg2k(PillowTestCase): out = BytesIO()
def roundtrip(self, im, **options): im.save(out, "JPEG2000", **options)
out = BytesIO() test_bytes = out.tell()
im.save(out, "JPEG2000", **options) out.seek(0)
test_bytes = out.tell() im = Image.open(out)
out.seek(0) im.bytes = test_bytes # for testing only
im = Image.open(out) im.load()
im.bytes = test_bytes # for testing only return im
def test_sanity():
# Internal version number
assert re.search(r"\d+\.\d+\.\d+$", Image.core.jp2klib_version)
with Image.open("Tests/images/test-card-lossless.jp2") as im:
px = im.load()
assert px[0, 0] == (0, 0, 0)
assert im.mode == "RGB"
assert im.size == (640, 480)
assert im.format == "JPEG2000"
assert im.get_format_mimetype() == "image/jp2"
def test_jpf():
with Image.open("Tests/images/balloon.jpf") as im:
assert im.format == "JPEG2000"
assert im.get_format_mimetype() == "image/jpx"
def test_invalid_file():
invalid_file = "Tests/images/flower.jpg"
with pytest.raises(SyntaxError):
Jpeg2KImagePlugin.Jpeg2KImageFile(invalid_file)
def test_bytesio():
with open("Tests/images/test-card-lossless.jp2", "rb") as f:
data = BytesIO(f.read())
with Image.open(data) as im:
im.load() im.load()
return im
def test_sanity(self):
# Internal version number
assert re.search(r"\d+\.\d+\.\d+$", Image.core.jp2klib_version)
with Image.open("Tests/images/test-card-lossless.jp2") as im:
px = im.load()
assert px[0, 0] == (0, 0, 0)
assert im.mode == "RGB"
assert im.size == (640, 480)
assert im.format == "JPEG2000"
assert im.get_format_mimetype() == "image/jp2"
def test_jpf(self):
with Image.open("Tests/images/balloon.jpf") as im:
assert im.format == "JPEG2000"
assert im.get_format_mimetype() == "image/jpx"
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
with pytest.raises(SyntaxError):
Jpeg2KImagePlugin.Jpeg2KImageFile(invalid_file)
def test_bytesio(self):
with open("Tests/images/test-card-lossless.jp2", "rb") as f:
data = BytesIO(f.read())
with Image.open(data) as im:
im.load()
assert_image_similar(im, test_card, 1.0e-3)
# These two test pre-written JPEG 2000 files that were not written with
# PIL (they were made using Adobe Photoshop)
def test_lossless(self):
with Image.open("Tests/images/test-card-lossless.jp2") as im:
im.load()
outfile = self.tempfile("temp_test-card.png")
im.save(outfile)
assert_image_similar(im, test_card, 1.0e-3) assert_image_similar(im, test_card, 1.0e-3)
def test_lossy_tiled(self):
with Image.open("Tests/images/test-card-lossy-tiled.jp2") as im:
im.load()
assert_image_similar(im, test_card, 2.0)
def test_lossless_rt(self): # These two test pre-written JPEG 2000 files that were not written with
im = self.roundtrip(test_card) # PIL (they were made using Adobe Photoshop)
assert_image_equal(im, test_card)
def test_lossy_rt(self):
im = self.roundtrip(test_card, quality_layers=[20]) def test_lossless(tmp_path):
with Image.open("Tests/images/test-card-lossless.jp2") as im:
im.load()
outfile = str(tmp_path / "temp_test-card.png")
im.save(outfile)
assert_image_similar(im, test_card, 1.0e-3)
def test_lossy_tiled():
with Image.open("Tests/images/test-card-lossy-tiled.jp2") as im:
im.load()
assert_image_similar(im, test_card, 2.0) assert_image_similar(im, test_card, 2.0)
def test_tiled_rt(self):
im = self.roundtrip(test_card, tile_size=(128, 128))
assert_image_equal(im, test_card)
def test_tiled_offset_rt(self): def test_lossless_rt():
im = self.roundtrip( im = roundtrip(test_card)
test_card, tile_size=(128, 128), tile_offset=(0, 0), offset=(32, 32) assert_image_equal(im, test_card)
)
assert_image_equal(im, test_card)
def test_tiled_offset_too_small(self):
def test_lossy_rt():
im = roundtrip(test_card, quality_layers=[20])
assert_image_similar(im, test_card, 2.0)
def test_tiled_rt():
im = roundtrip(test_card, tile_size=(128, 128))
assert_image_equal(im, test_card)
def test_tiled_offset_rt():
im = roundtrip(test_card, tile_size=(128, 128), tile_offset=(0, 0), offset=(32, 32))
assert_image_equal(im, test_card)
def test_tiled_offset_too_small():
with pytest.raises(ValueError):
roundtrip(test_card, tile_size=(128, 128), tile_offset=(0, 0), offset=(128, 32))
def test_irreversible_rt():
im = roundtrip(test_card, irreversible=True, quality_layers=[20])
assert_image_similar(im, test_card, 2.0)
def test_prog_qual_rt():
im = roundtrip(test_card, quality_layers=[60, 40, 20], progression="LRCP")
assert_image_similar(im, test_card, 2.0)
def test_prog_res_rt():
im = roundtrip(test_card, num_resolutions=8, progression="RLCP")
assert_image_equal(im, test_card)
def test_reduce():
with Image.open("Tests/images/test-card-lossless.jp2") as im:
im.reduce = 2
im.load()
assert im.size == (160, 120)
def test_layers_type(tmp_path):
outfile = str(tmp_path / "temp_layers.jp2")
for quality_layers in [[100, 50, 10], (100, 50, 10), None]:
test_card.save(outfile, quality_layers=quality_layers)
for quality_layers in ["quality_layers", ("100", "50", "10")]:
with pytest.raises(ValueError): with pytest.raises(ValueError):
self.roundtrip(
test_card, tile_size=(128, 128), tile_offset=(0, 0), offset=(128, 32)
)
def test_irreversible_rt(self):
im = self.roundtrip(test_card, irreversible=True, quality_layers=[20])
assert_image_similar(im, test_card, 2.0)
def test_prog_qual_rt(self):
im = self.roundtrip(test_card, quality_layers=[60, 40, 20], progression="LRCP")
assert_image_similar(im, test_card, 2.0)
def test_prog_res_rt(self):
im = self.roundtrip(test_card, num_resolutions=8, progression="RLCP")
assert_image_equal(im, test_card)
def test_reduce(self):
with Image.open("Tests/images/test-card-lossless.jp2") as im:
im.reduce = 2
im.load()
assert im.size == (160, 120)
def test_layers_type(self):
outfile = self.tempfile("temp_layers.jp2")
for quality_layers in [[100, 50, 10], (100, 50, 10), None]:
test_card.save(outfile, quality_layers=quality_layers) test_card.save(outfile, quality_layers=quality_layers)
for quality_layers in ["quality_layers", ("100", "50", "10")]:
with pytest.raises(ValueError):
test_card.save(outfile, quality_layers=quality_layers)
def test_layers(self): def test_layers():
out = BytesIO() out = BytesIO()
test_card.save( test_card.save(out, "JPEG2000", quality_layers=[100, 50, 10], progression="LRCP")
out, "JPEG2000", quality_layers=[100, 50, 10], progression="LRCP" out.seek(0)
)
out.seek(0)
with Image.open(out) as im: with Image.open(out) as im:
im.layers = 1 im.layers = 1
im.load() im.load()
assert_image_similar(im, test_card, 13) assert_image_similar(im, test_card, 13)
out.seek(0) out.seek(0)
with Image.open(out) as im: with Image.open(out) as im:
im.layers = 3 im.layers = 3
im.load() im.load()
assert_image_similar(im, test_card, 0.4) assert_image_similar(im, test_card, 0.4)
def test_rgba(self):
# Arrange
with Image.open("Tests/images/rgb_trns_ycbc.j2k") as j2k:
with Image.open("Tests/images/rgb_trns_ycbc.jp2") as jp2:
# Act def test_rgba():
j2k.load() # Arrange
jp2.load() with Image.open("Tests/images/rgb_trns_ycbc.j2k") as j2k:
with Image.open("Tests/images/rgb_trns_ycbc.jp2") as jp2:
# Assert # Act
assert j2k.mode == "RGBA"
assert jp2.mode == "RGBA"
def test_16bit_monochrome_has_correct_mode(self):
with Image.open("Tests/images/16bit.cropped.j2k") as j2k:
j2k.load() j2k.load()
assert j2k.mode == "I;16"
with Image.open("Tests/images/16bit.cropped.jp2") as jp2:
jp2.load() jp2.load()
assert jp2.mode == "I;16"
@pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian") # Assert
def test_16bit_monochrome_jp2_like_tiff(self): assert j2k.mode == "RGBA"
with Image.open("Tests/images/16bit.cropped.tif") as tiff_16bit: assert jp2.mode == "RGBA"
with Image.open("Tests/images/16bit.cropped.jp2") as jp2:
assert_image_similar(jp2, tiff_16bit, 1e-3)
@pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian")
def test_16bit_monochrome_j2k_like_tiff(self):
with Image.open("Tests/images/16bit.cropped.tif") as tiff_16bit:
with Image.open("Tests/images/16bit.cropped.j2k") as j2k:
assert_image_similar(j2k, tiff_16bit, 1e-3)
def test_16bit_j2k_roundtrips(self): def test_16bit_monochrome_has_correct_mode():
with Image.open("Tests/images/16bit.cropped.j2k") as j2k: with Image.open("Tests/images/16bit.cropped.j2k") as j2k:
im = self.roundtrip(j2k) j2k.load()
assert_image_equal(im, j2k) assert j2k.mode == "I;16"
def test_16bit_jp2_roundtrips(self): with Image.open("Tests/images/16bit.cropped.jp2") as jp2:
jp2.load()
assert jp2.mode == "I;16"
@pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian")
def test_16bit_monochrome_jp2_like_tiff():
with Image.open("Tests/images/16bit.cropped.tif") as tiff_16bit:
with Image.open("Tests/images/16bit.cropped.jp2") as jp2: with Image.open("Tests/images/16bit.cropped.jp2") as jp2:
im = self.roundtrip(jp2) assert_image_similar(jp2, tiff_16bit, 1e-3)
assert_image_equal(im, jp2)
def test_unbound_local(self):
# prepatch, a malformed jp2 file could cause an UnboundLocalError
# exception.
with pytest.raises(IOError):
Image.open("Tests/images/unbound_variable.jp2")
def test_parser_feed(self): @pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian")
# Arrange def test_16bit_monochrome_j2k_like_tiff():
with open("Tests/images/test-card-lossless.jp2", "rb") as f: with Image.open("Tests/images/16bit.cropped.tif") as tiff_16bit:
data = f.read() with Image.open("Tests/images/16bit.cropped.j2k") as j2k:
assert_image_similar(j2k, tiff_16bit, 1e-3)
# Act
p = ImageFile.Parser()
p.feed(data)
# Assert def test_16bit_j2k_roundtrips():
assert p.image.size == (640, 480) with Image.open("Tests/images/16bit.cropped.j2k") as j2k:
im = roundtrip(j2k)
assert_image_equal(im, j2k)
def test_16bit_jp2_roundtrips():
with Image.open("Tests/images/16bit.cropped.jp2") as jp2:
im = roundtrip(jp2)
assert_image_equal(im, jp2)
def test_unbound_local():
# prepatch, a malformed jp2 file could cause an UnboundLocalError exception.
with pytest.raises(IOError):
Image.open("Tests/images/unbound_variable.jp2")
def test_parser_feed():
# Arrange
with open("Tests/images/test-card-lossless.jp2", "rb") as f:
data = f.read()
# Act
p = ImageFile.Parser()
p.feed(data)
# Assert
assert p.image.size == (640, 480)

View File

@ -10,7 +10,6 @@ import pytest
from PIL import Image, ImageFilter, TiffImagePlugin, TiffTags from PIL import Image, ImageFilter, TiffImagePlugin, TiffTags
from .helper import ( from .helper import (
PillowTestCase,
assert_image_equal, assert_image_equal,
assert_image_equal_tofile, assert_image_equal_tofile,
assert_image_similar, assert_image_similar,
@ -23,8 +22,8 @@ logger = logging.getLogger(__name__)
@skip_unless_feature("libtiff") @skip_unless_feature("libtiff")
class LibTiffTestCase(PillowTestCase): class LibTiffTestCase:
def _assert_noerr(self, im): def _assert_noerr(self, tmp_path, im):
"""Helper tests that assert basic sanity about the g4 tiff reading""" """Helper tests that assert basic sanity about the g4 tiff reading"""
# 1 bit # 1 bit
assert im.mode == "1" assert im.mode == "1"
@ -40,7 +39,7 @@ class LibTiffTestCase(PillowTestCase):
print(dir(im)) print(dir(im))
# can we write it back out, in a different form. # can we write it back out, in a different form.
out = self.tempfile("temp.png") out = str(tmp_path / "temp.png")
im.save(out) im.save(out)
out_bytes = io.BytesIO() out_bytes = io.BytesIO()
@ -48,29 +47,29 @@ class LibTiffTestCase(PillowTestCase):
class TestFileLibTiff(LibTiffTestCase): class TestFileLibTiff(LibTiffTestCase):
def test_g4_tiff(self): def test_g4_tiff(self, tmp_path):
"""Test the ordinary file path load path""" """Test the ordinary file path load path"""
test_file = "Tests/images/hopper_g4_500.tif" test_file = "Tests/images/hopper_g4_500.tif"
with Image.open(test_file) as im: with Image.open(test_file) as im:
assert im.size == (500, 500) assert im.size == (500, 500)
self._assert_noerr(im) self._assert_noerr(tmp_path, im)
def test_g4_large(self): def test_g4_large(self, tmp_path):
test_file = "Tests/images/pport_g4.tif" test_file = "Tests/images/pport_g4.tif"
with Image.open(test_file) as im: with Image.open(test_file) as im:
self._assert_noerr(im) self._assert_noerr(tmp_path, im)
def test_g4_tiff_file(self): def test_g4_tiff_file(self, tmp_path):
"""Testing the string load path""" """Testing the string load path"""
test_file = "Tests/images/hopper_g4_500.tif" test_file = "Tests/images/hopper_g4_500.tif"
with open(test_file, "rb") as f: with open(test_file, "rb") as f:
with Image.open(f) as im: with Image.open(f) as im:
assert im.size == (500, 500) assert im.size == (500, 500)
self._assert_noerr(im) self._assert_noerr(tmp_path, im)
def test_g4_tiff_bytesio(self): def test_g4_tiff_bytesio(self, tmp_path):
"""Testing the stringio loading code path""" """Testing the stringio loading code path"""
test_file = "Tests/images/hopper_g4_500.tif" test_file = "Tests/images/hopper_g4_500.tif"
s = io.BytesIO() s = io.BytesIO()
@ -79,9 +78,9 @@ class TestFileLibTiff(LibTiffTestCase):
s.seek(0) s.seek(0)
with Image.open(s) as im: with Image.open(s) as im:
assert im.size == (500, 500) assert im.size == (500, 500)
self._assert_noerr(im) self._assert_noerr(tmp_path, im)
def test_g4_non_disk_file_object(self): def test_g4_non_disk_file_object(self, tmp_path):
"""Testing loading from non-disk non-BytesIO file object""" """Testing loading from non-disk non-BytesIO file object"""
test_file = "Tests/images/hopper_g4_500.tif" test_file = "Tests/images/hopper_g4_500.tif"
s = io.BytesIO() s = io.BytesIO()
@ -91,7 +90,7 @@ class TestFileLibTiff(LibTiffTestCase):
r = io.BufferedReader(s) r = io.BufferedReader(s)
with Image.open(r) as im: with Image.open(r) as im:
assert im.size == (500, 500) assert im.size == (500, 500)
self._assert_noerr(im) self._assert_noerr(tmp_path, im)
def test_g4_eq_png(self): def test_g4_eq_png(self):
""" Checking that we're actually getting the data that we expect""" """ Checking that we're actually getting the data that we expect"""
@ -106,18 +105,18 @@ class TestFileLibTiff(LibTiffTestCase):
with Image.open("Tests/images/g4-fillorder-test.tif") as g4: with Image.open("Tests/images/g4-fillorder-test.tif") as g4:
assert_image_equal(g4, png) assert_image_equal(g4, png)
def test_g4_write(self): def test_g4_write(self, tmp_path):
"""Checking to see that the saved image is the same as what we wrote""" """Checking to see that the saved image is the same as what we wrote"""
test_file = "Tests/images/hopper_g4_500.tif" test_file = "Tests/images/hopper_g4_500.tif"
with Image.open(test_file) as orig: with Image.open(test_file) as orig:
out = self.tempfile("temp.tif") out = str(tmp_path / "temp.tif")
rot = orig.transpose(Image.ROTATE_90) rot = orig.transpose(Image.ROTATE_90)
assert rot.size == (500, 500) assert rot.size == (500, 500)
rot.save(out) rot.save(out)
with Image.open(out) as reread: with Image.open(out) as reread:
assert reread.size == (500, 500) assert reread.size == (500, 500)
self._assert_noerr(reread) self._assert_noerr(tmp_path, reread)
assert_image_equal(reread, rot) assert_image_equal(reread, rot)
assert reread.info["compression"] == "group4" assert reread.info["compression"] == "group4"
@ -135,10 +134,10 @@ class TestFileLibTiff(LibTiffTestCase):
assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png") assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
def test_write_metadata(self): def test_write_metadata(self, tmp_path):
""" Test metadata writing through libtiff """ """ Test metadata writing through libtiff """
for legacy_api in [False, True]: for legacy_api in [False, True]:
f = self.tempfile("temp.tiff") f = str(tmp_path / "temp.tiff")
with Image.open("Tests/images/hopper_g4.tif") as img: with Image.open("Tests/images/hopper_g4.tif") as img:
img.save(f, tiffinfo=img.tag) img.save(f, tiffinfo=img.tag)
@ -183,7 +182,7 @@ class TestFileLibTiff(LibTiffTestCase):
for field in requested_fields: for field in requested_fields:
assert field in reloaded, "%s not in metadata" % field assert field in reloaded, "%s not in metadata" % field
def test_additional_metadata(self): def test_additional_metadata(self, tmp_path):
# these should not crash. Seriously dummy data, most of it doesn't make # these should not crash. Seriously dummy data, most of it doesn't make
# any sense, so we're running up against limits where we're asking # any sense, so we're running up against limits where we're asking
# libtiff to do stupid things. # libtiff to do stupid things.
@ -232,14 +231,14 @@ class TestFileLibTiff(LibTiffTestCase):
# Extra samples really doesn't make sense in this application. # Extra samples really doesn't make sense in this application.
del new_ifd[338] del new_ifd[338]
out = self.tempfile("temp.tif") out = str(tmp_path / "temp.tif")
TiffImagePlugin.WRITE_LIBTIFF = True TiffImagePlugin.WRITE_LIBTIFF = True
im.save(out, tiffinfo=new_ifd) im.save(out, tiffinfo=new_ifd)
TiffImagePlugin.WRITE_LIBTIFF = False TiffImagePlugin.WRITE_LIBTIFF = False
def test_custom_metadata(self): def test_custom_metadata(self, tmp_path):
tc = namedtuple("test_case", "value,type,supported_by_default") tc = namedtuple("test_case", "value,type,supported_by_default")
custom = { custom = {
37000 + k: v 37000 + k: v
@ -284,7 +283,7 @@ class TestFileLibTiff(LibTiffTestCase):
def check_tags(tiffinfo): def check_tags(tiffinfo):
im = hopper() im = hopper()
out = self.tempfile("temp.tif") out = str(tmp_path / "temp.tif")
im.save(out, tiffinfo=tiffinfo) im.save(out, tiffinfo=tiffinfo)
with Image.open(out) as reloaded: with Image.open(out) as reloaded:
@ -323,26 +322,26 @@ class TestFileLibTiff(LibTiffTestCase):
) )
TiffImagePlugin.WRITE_LIBTIFF = False TiffImagePlugin.WRITE_LIBTIFF = False
def test_int_dpi(self): def test_int_dpi(self, tmp_path):
# issue #1765 # issue #1765
im = hopper("RGB") im = hopper("RGB")
out = self.tempfile("temp.tif") out = str(tmp_path / "temp.tif")
TiffImagePlugin.WRITE_LIBTIFF = True TiffImagePlugin.WRITE_LIBTIFF = True
im.save(out, dpi=(72, 72)) im.save(out, dpi=(72, 72))
TiffImagePlugin.WRITE_LIBTIFF = False TiffImagePlugin.WRITE_LIBTIFF = False
with Image.open(out) as reloaded: with Image.open(out) as reloaded:
assert reloaded.info["dpi"] == (72.0, 72.0) assert reloaded.info["dpi"] == (72.0, 72.0)
def test_g3_compression(self): def test_g3_compression(self, tmp_path):
with Image.open("Tests/images/hopper_g4_500.tif") as i: with Image.open("Tests/images/hopper_g4_500.tif") as i:
out = self.tempfile("temp.tif") out = str(tmp_path / "temp.tif")
i.save(out, compression="group3") i.save(out, compression="group3")
with Image.open(out) as reread: with Image.open(out) as reread:
assert reread.info["compression"] == "group3" assert reread.info["compression"] == "group3"
assert_image_equal(reread, i) assert_image_equal(reread, i)
def test_little_endian(self): def test_little_endian(self, tmp_path):
with Image.open("Tests/images/16bit.deflate.tif") as im: with Image.open("Tests/images/16bit.deflate.tif") as im:
assert im.getpixel((0, 0)) == 480 assert im.getpixel((0, 0)) == 480
assert im.mode == "I;16" assert im.mode == "I;16"
@ -352,7 +351,7 @@ class TestFileLibTiff(LibTiffTestCase):
assert b[0] == ord(b"\xe0") assert b[0] == ord(b"\xe0")
assert b[1] == ord(b"\x01") assert b[1] == ord(b"\x01")
out = self.tempfile("temp.tif") out = str(tmp_path / "temp.tif")
# out = "temp.le.tif" # out = "temp.le.tif"
im.save(out) im.save(out)
with Image.open(out) as reread: with Image.open(out) as reread:
@ -361,7 +360,7 @@ class TestFileLibTiff(LibTiffTestCase):
# UNDONE - libtiff defaults to writing in native endian, so # UNDONE - libtiff defaults to writing in native endian, so
# on big endian, we'll get back mode = 'I;16B' here. # on big endian, we'll get back mode = 'I;16B' here.
def test_big_endian(self): def test_big_endian(self, tmp_path):
with Image.open("Tests/images/16bit.MM.deflate.tif") as im: with Image.open("Tests/images/16bit.MM.deflate.tif") as im:
assert im.getpixel((0, 0)) == 480 assert im.getpixel((0, 0)) == 480
assert im.mode == "I;16B" assert im.mode == "I;16B"
@ -372,17 +371,17 @@ class TestFileLibTiff(LibTiffTestCase):
assert b[0] == ord(b"\x01") assert b[0] == ord(b"\x01")
assert b[1] == ord(b"\xe0") assert b[1] == ord(b"\xe0")
out = self.tempfile("temp.tif") out = str(tmp_path / "temp.tif")
im.save(out) im.save(out)
with Image.open(out) as reread: with Image.open(out) as reread:
assert reread.info["compression"] == im.info["compression"] assert reread.info["compression"] == im.info["compression"]
assert reread.getpixel((0, 0)) == 480 assert reread.getpixel((0, 0)) == 480
def test_g4_string_info(self): def test_g4_string_info(self, tmp_path):
"""Tests String data in info directory""" """Tests String data in info directory"""
test_file = "Tests/images/hopper_g4_500.tif" test_file = "Tests/images/hopper_g4_500.tif"
with Image.open(test_file) as orig: with Image.open(test_file) as orig:
out = self.tempfile("temp.tif") out = str(tmp_path / "temp.tif")
orig.tag[269] = "temp.tif" orig.tag[269] = "temp.tif"
orig.save(out) orig.save(out)
@ -406,10 +405,10 @@ class TestFileLibTiff(LibTiffTestCase):
assert_image_equal_tofile(im, "Tests/images/12in16bit.tif") assert_image_equal_tofile(im, "Tests/images/12in16bit.tif")
def test_blur(self): def test_blur(self, tmp_path):
# test case from irc, how to do blur on b/w image # test case from irc, how to do blur on b/w image
# and save to compressed tif. # and save to compressed tif.
out = self.tempfile("temp.tif") out = str(tmp_path / "temp.tif")
with Image.open("Tests/images/pport_g4.tif") as im: with Image.open("Tests/images/pport_g4.tif") as im:
im = im.convert("L") im = im.convert("L")
@ -421,11 +420,11 @@ class TestFileLibTiff(LibTiffTestCase):
assert_image_equal(im, im2) assert_image_equal(im, im2)
def test_compressions(self): def test_compressions(self, tmp_path):
# Test various tiff compressions and assert similar image content but reduced # Test various tiff compressions and assert similar image content but reduced
# file sizes. # file sizes.
im = hopper("RGB") im = hopper("RGB")
out = self.tempfile("temp.tif") out = str(tmp_path / "temp.tif")
im.save(out) im.save(out)
size_raw = os.path.getsize(out) size_raw = os.path.getsize(out)
@ -449,9 +448,9 @@ class TestFileLibTiff(LibTiffTestCase):
assert size_compressed > size_jpeg assert size_compressed > size_jpeg
assert size_jpeg > size_jpeg_30 assert size_jpeg > size_jpeg_30
def test_quality(self): def test_quality(self, tmp_path):
im = hopper("RGB") im = hopper("RGB")
out = self.tempfile("temp.tif") out = str(tmp_path / "temp.tif")
with pytest.raises(ValueError): with pytest.raises(ValueError):
im.save(out, compression="tiff_lzw", quality=50) im.save(out, compression="tiff_lzw", quality=50)
@ -464,21 +463,21 @@ class TestFileLibTiff(LibTiffTestCase):
im.save(out, compression="jpeg", quality=0) im.save(out, compression="jpeg", quality=0)
im.save(out, compression="jpeg", quality=100) im.save(out, compression="jpeg", quality=100)
def test_cmyk_save(self): def test_cmyk_save(self, tmp_path):
im = hopper("CMYK") im = hopper("CMYK")
out = self.tempfile("temp.tif") out = str(tmp_path / "temp.tif")
im.save(out, compression="tiff_adobe_deflate") im.save(out, compression="tiff_adobe_deflate")
with Image.open(out) as im2: with Image.open(out) as im2:
assert_image_equal(im, im2) assert_image_equal(im, im2)
def xtest_bw_compression_w_rgb(self): def xtest_bw_compression_w_rgb(self, tmp_path):
""" This test passes, but when running all tests causes a failure due """ This test passes, but when running all tests causes a failure due
to output on stderr from the error thrown by libtiff. We need to to output on stderr from the error thrown by libtiff. We need to
capture that but not now""" capture that but not now"""
im = hopper("RGB") im = hopper("RGB")
out = self.tempfile("temp.tif") out = str(tmp_path / "temp.tif")
with pytest.raises(IOError): with pytest.raises(IOError):
im.save(out, compression="tiff_ccitt") im.save(out, compression="tiff_ccitt")
@ -619,22 +618,22 @@ class TestFileLibTiff(LibTiffTestCase):
TiffImagePlugin.WRITE_LIBTIFF = False TiffImagePlugin.WRITE_LIBTIFF = False
TiffImagePlugin.READ_LIBTIFF = False TiffImagePlugin.READ_LIBTIFF = False
def test_crashing_metadata(self): def test_crashing_metadata(self, tmp_path):
# issue 1597 # issue 1597
with Image.open("Tests/images/rdf.tif") as im: with Image.open("Tests/images/rdf.tif") as im:
out = self.tempfile("temp.tif") out = str(tmp_path / "temp.tif")
TiffImagePlugin.WRITE_LIBTIFF = True TiffImagePlugin.WRITE_LIBTIFF = True
# this shouldn't crash # this shouldn't crash
im.save(out, format="TIFF") im.save(out, format="TIFF")
TiffImagePlugin.WRITE_LIBTIFF = False TiffImagePlugin.WRITE_LIBTIFF = False
def test_page_number_x_0(self): def test_page_number_x_0(self, tmp_path):
# Issue 973 # Issue 973
# Test TIFF with tag 297 (Page Number) having value of 0 0. # Test TIFF with tag 297 (Page Number) having value of 0 0.
# The first number is the current page number. # The first number is the current page number.
# The second is the total number of pages, zero means not available. # The second is the total number of pages, zero means not available.
outfile = self.tempfile("temp.tif") outfile = str(tmp_path / "temp.tif")
# Created by printing a page in Chrome to PDF, then: # Created by printing a page in Chrome to PDF, then:
# /usr/bin/gs -q -sDEVICE=tiffg3 -sOutputFile=total-pages-zero.tif # /usr/bin/gs -q -sDEVICE=tiffg3 -sOutputFile=total-pages-zero.tif
# -dNOPAUSE /tmp/test.pdf -c quit # -dNOPAUSE /tmp/test.pdf -c quit
@ -643,10 +642,10 @@ class TestFileLibTiff(LibTiffTestCase):
# Should not divide by zero # Should not divide by zero
im.save(outfile) im.save(outfile)
def test_fd_duplication(self): def test_fd_duplication(self, tmp_path):
# https://github.com/python-pillow/Pillow/issues/1651 # https://github.com/python-pillow/Pillow/issues/1651
tmpfile = self.tempfile("temp.tif") tmpfile = str(tmp_path / "temp.tif")
with open(tmpfile, "wb") as f: with open(tmpfile, "wb") as f:
with open("Tests/images/g4-multi.tiff", "rb") as src: with open("Tests/images/g4-multi.tiff", "rb") as src:
f.write(src.read()) f.write(src.read())
@ -685,9 +684,9 @@ class TestFileLibTiff(LibTiffTestCase):
assert im.size == (10, 10) assert im.size == (10, 10)
im.load() im.load()
def test_save_tiff_with_jpegtables(self): def test_save_tiff_with_jpegtables(self, tmp_path):
# Arrange # Arrange
outfile = self.tempfile("temp.tif") outfile = str(tmp_path / "temp.tif")
# Created with ImageMagick: convert hopper.jpg hopper_jpg.tif # Created with ImageMagick: convert hopper.jpg hopper_jpg.tif
# Contains JPEGTables (347) tag # Contains JPEGTables (347) tag

View File

@ -15,16 +15,16 @@ class TestFileLibTiffSmall(LibTiffTestCase):
file just before reading in libtiff. These tests remain file just before reading in libtiff. These tests remain
to ensure that it stays fixed. """ to ensure that it stays fixed. """
def test_g4_hopper_file(self): def test_g4_hopper_file(self, tmp_path):
"""Testing the open file load path""" """Testing the open file load path"""
test_file = "Tests/images/hopper_g4.tif" test_file = "Tests/images/hopper_g4.tif"
with open(test_file, "rb") as f: with open(test_file, "rb") as f:
with Image.open(f) as im: with Image.open(f) as im:
assert im.size == (128, 128) assert im.size == (128, 128)
self._assert_noerr(im) self._assert_noerr(tmp_path, im)
def test_g4_hopper_bytesio(self): def test_g4_hopper_bytesio(self, tmp_path):
"""Testing the bytesio loading code path""" """Testing the bytesio loading code path"""
test_file = "Tests/images/hopper_g4.tif" test_file = "Tests/images/hopper_g4.tif"
s = BytesIO() s = BytesIO()
@ -33,12 +33,12 @@ class TestFileLibTiffSmall(LibTiffTestCase):
s.seek(0) s.seek(0)
with Image.open(s) as im: with Image.open(s) as im:
assert im.size == (128, 128) assert im.size == (128, 128)
self._assert_noerr(im) self._assert_noerr(tmp_path, im)
def test_g4_hopper(self): def test_g4_hopper(self, tmp_path):
"""The 128x128 lena image failed for some reason.""" """The 128x128 lena image failed for some reason."""
test_file = "Tests/images/hopper_g4.tif" test_file = "Tests/images/hopper_g4.tif"
with Image.open(test_file) as im: with Image.open(test_file) as im:
assert im.size == (128, 128) assert im.size == (128, 128)
self._assert_noerr(im) self._assert_noerr(tmp_path, im)

View File

@ -1,71 +1,90 @@
import os.path import os.path
import subprocess
import pytest import pytest
from PIL import Image
from .helper import ( from .helper import (
PillowTestCase, IMCONVERT,
assert_image_equal, assert_image_equal,
hopper, hopper,
imagemagick_available, imagemagick_available,
skip_known_bad_test, skip_known_bad_test,
) )
_roundtrip = imagemagick_available()
class TestFilePalm(PillowTestCase):
_roundtrip = imagemagick_available()
def helper_save_as_palm(self, mode): def helper_save_as_palm(tmp_path, mode):
# Arrange # Arrange
im = hopper(mode) im = hopper(mode)
outfile = self.tempfile("temp_" + mode + ".palm") outfile = str(tmp_path / ("temp_" + mode + ".palm"))
# Act # Act
im.save(outfile) im.save(outfile)
# Assert # Assert
assert os.path.isfile(outfile) assert os.path.isfile(outfile)
assert os.path.getsize(outfile) > 0 assert os.path.getsize(outfile) > 0
def roundtrip(self, mode):
if not self._roundtrip:
return
im = hopper(mode) def open_with_imagemagick(tmp_path, f):
outfile = self.tempfile("temp.palm") if not imagemagick_available():
raise OSError()
im.save(outfile) outfile = str(tmp_path / "temp.png")
converted = self.open_withImagemagick(outfile) rc = subprocess.call(
assert_image_equal(converted, im) [IMCONVERT, f, outfile], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT
)
if rc:
raise OSError
return Image.open(outfile)
def test_monochrome(self):
# Arrange
mode = "1"
# Act / Assert def roundtrip(tmp_path, mode):
self.helper_save_as_palm(mode) if not _roundtrip:
self.roundtrip(mode) return
def test_p_mode(self): im = hopper(mode)
# Arrange outfile = str(tmp_path / "temp.palm")
mode = "P"
# Act / Assert im.save(outfile)
self.helper_save_as_palm(mode) converted = open_with_imagemagick(tmp_path, outfile)
skip_known_bad_test("Palm P image is wrong") assert_image_equal(converted, im)
self.roundtrip(mode)
def test_l_ioerror(self):
# Arrange
mode = "L"
# Act / Assert def test_monochrome(tmp_path):
with pytest.raises(IOError): # Arrange
self.helper_save_as_palm(mode) mode = "1"
def test_rgb_ioerror(self): # Act / Assert
# Arrange helper_save_as_palm(tmp_path, mode)
mode = "RGB" roundtrip(tmp_path, mode)
# Act / Assert
with pytest.raises(IOError): def test_p_mode(tmp_path):
self.helper_save_as_palm(mode) # Arrange
mode = "P"
# Act / Assert
helper_save_as_palm(tmp_path, mode)
skip_known_bad_test("Palm P image is wrong")
roundtrip(tmp_path, mode)
def test_l_ioerror(tmp_path):
# Arrange
mode = "L"
# Act / Assert
with pytest.raises(IOError):
helper_save_as_palm(tmp_path, mode)
def test_rgb_ioerror(tmp_path):
# Arrange
mode = "RGB"
# Act / Assert
with pytest.raises(IOError):
helper_save_as_palm(tmp_path, mode)

View File

@ -1,130 +1,142 @@
import pytest import pytest
from PIL import Image, ImageFile, PcxImagePlugin from PIL import Image, ImageFile, PcxImagePlugin
from .helper import PillowTestCase, assert_image_equal, hopper from .helper import assert_image_equal, hopper
class TestFilePcx(PillowTestCase): def _roundtrip(tmp_path, im):
def _roundtrip(self, im): f = str(tmp_path / "temp.pcx")
f = self.tempfile("temp.pcx") im.save(f)
with Image.open(f) as im2:
assert im2.mode == im.mode
assert im2.size == im.size
assert im2.format == "PCX"
assert im2.get_format_mimetype() == "image/x-pcx"
assert_image_equal(im2, im)
def test_sanity(tmp_path):
for mode in ("1", "L", "P", "RGB"):
_roundtrip(tmp_path, hopper(mode))
# Test an unsupported mode
f = str(tmp_path / "temp.pcx")
im = hopper("RGBA")
with pytest.raises(ValueError):
im.save(f) im.save(f)
with Image.open(f) as im2:
assert im2.mode == im.mode
assert im2.size == im.size
assert im2.format == "PCX"
assert im2.get_format_mimetype() == "image/x-pcx"
assert_image_equal(im2, im)
def test_sanity(self):
for mode in ("1", "L", "P", "RGB"):
self._roundtrip(hopper(mode))
# Test an unsupported mode def test_invalid_file():
f = self.tempfile("temp.pcx") invalid_file = "Tests/images/flower.jpg"
im = hopper("RGBA")
with pytest.raises(ValueError):
im.save(f)
def test_invalid_file(self): with pytest.raises(SyntaxError):
invalid_file = "Tests/images/flower.jpg" PcxImagePlugin.PcxImageFile(invalid_file)
with pytest.raises(SyntaxError):
PcxImagePlugin.PcxImageFile(invalid_file)
def test_odd(self): def test_odd(tmp_path):
# see issue #523, odd sized images should have a stride that's even. # See issue #523, odd sized images should have a stride that's even.
# not that imagemagick or gimp write pcx that way. # Not that ImageMagick or GIMP write PCX that way.
# we were not handling properly. # We were not handling properly.
for mode in ("1", "L", "P", "RGB"): for mode in ("1", "L", "P", "RGB"):
# larger, odd sized images are better here to ensure that # larger, odd sized images are better here to ensure that
# we handle interrupted scan lines properly. # we handle interrupted scan lines properly.
self._roundtrip(hopper(mode).resize((511, 511))) _roundtrip(tmp_path, hopper(mode).resize((511, 511)))
def test_pil184(self):
# Check reading of files where xmin/xmax is not zero.
test_file = "Tests/images/pil184.pcx" def test_pil184():
with Image.open(test_file) as im: # Check reading of files where xmin/xmax is not zero.
assert im.size == (447, 144)
assert im.tile[0][1] == (0, 0, 447, 144)
# Make sure all pixels are either 0 or 255. test_file = "Tests/images/pil184.pcx"
assert im.histogram()[0] + im.histogram()[255] == 447 * 144 with Image.open(test_file) as im:
assert im.size == (447, 144)
assert im.tile[0][1] == (0, 0, 447, 144)
def test_1px_width(self): # Make sure all pixels are either 0 or 255.
im = Image.new("L", (1, 256)) assert im.histogram()[0] + im.histogram()[255] == 447 * 144
px = im.load()
for y in range(256):
px[0, y] = y
self._roundtrip(im)
def test_large_count(self):
im = Image.new("L", (256, 1)) def test_1px_width(tmp_path):
px = im.load() im = Image.new("L", (1, 256))
px = im.load()
for y in range(256):
px[0, y] = y
_roundtrip(tmp_path, im)
def test_large_count(tmp_path):
im = Image.new("L", (256, 1))
px = im.load()
for x in range(256):
px[x, 0] = x // 67 * 67
_roundtrip(tmp_path, im)
def _test_buffer_overflow(tmp_path, im, size=1024):
_last = ImageFile.MAXBLOCK
ImageFile.MAXBLOCK = size
try:
_roundtrip(tmp_path, im)
finally:
ImageFile.MAXBLOCK = _last
def test_break_in_count_overflow(tmp_path):
im = Image.new("L", (256, 5))
px = im.load()
for y in range(4):
for x in range(256): for x in range(256):
px[x, 0] = x // 67 * 67 px[x, y] = x % 128
self._roundtrip(im) _test_buffer_overflow(tmp_path, im)
def _test_buffer_overflow(self, im, size=1024):
_last = ImageFile.MAXBLOCK
ImageFile.MAXBLOCK = size
try:
self._roundtrip(im)
finally:
ImageFile.MAXBLOCK = _last
def test_break_in_count_overflow(self): def test_break_one_in_loop(tmp_path):
im = Image.new("L", (256, 5)) im = Image.new("L", (256, 5))
px = im.load() px = im.load()
for y in range(4): for y in range(5):
for x in range(256): for x in range(256):
px[x, y] = x % 128 px[x, y] = x % 128
self._test_buffer_overflow(im) _test_buffer_overflow(tmp_path, im)
def test_break_one_in_loop(self):
im = Image.new("L", (256, 5))
px = im.load()
for y in range(5):
for x in range(256):
px[x, y] = x % 128
self._test_buffer_overflow(im)
def test_break_many_in_loop(self): def test_break_many_in_loop(tmp_path):
im = Image.new("L", (256, 5)) im = Image.new("L", (256, 5))
px = im.load() px = im.load()
for y in range(4): for y in range(4):
for x in range(256): for x in range(256):
px[x, y] = x % 128 px[x, y] = x % 128
for x in range(8): for x in range(8):
px[x, 4] = 16 px[x, 4] = 16
self._test_buffer_overflow(im) _test_buffer_overflow(tmp_path, im)
def test_break_one_at_end(self):
im = Image.new("L", (256, 5))
px = im.load()
for y in range(5):
for x in range(256):
px[x, y] = x % 128
px[0, 3] = 128 + 64
self._test_buffer_overflow(im)
def test_break_many_at_end(self): def test_break_one_at_end(tmp_path):
im = Image.new("L", (256, 5)) im = Image.new("L", (256, 5))
px = im.load() px = im.load()
for y in range(5): for y in range(5):
for x in range(256): for x in range(256):
px[x, y] = x % 128 px[x, y] = x % 128
for x in range(4): px[0, 3] = 128 + 64
px[x * 2, 3] = 128 + 64 _test_buffer_overflow(tmp_path, im)
px[x + 256 - 4, 3] = 0
self._test_buffer_overflow(im)
def test_break_padding(self):
im = Image.new("L", (257, 5)) def test_break_many_at_end(tmp_path):
px = im.load() im = Image.new("L", (256, 5))
for y in range(5): px = im.load()
for x in range(257): for y in range(5):
px[x, y] = x % 128 for x in range(256):
for x in range(5): px[x, y] = x % 128
px[x, 3] = 0 for x in range(4):
self._test_buffer_overflow(im) px[x * 2, 3] = 128 + 64
px[x + 256 - 4, 3] = 0
_test_buffer_overflow(tmp_path, im)
def test_break_padding(tmp_path):
im = Image.new("L", (257, 5))
px = im.load()
for y in range(5):
for x in range(257):
px[x, y] = x % 128
for x in range(5):
px[x, 3] = 0
_test_buffer_overflow(tmp_path, im)

View File

@ -1,5 +1,4 @@
import re import re
import unittest
import zlib import zlib
from io import BytesIO from io import BytesIO
@ -8,7 +7,6 @@ from PIL import Image, ImageFile, PngImagePlugin
from .helper import ( from .helper import (
PillowLeakTestCase, PillowLeakTestCase,
PillowTestCase,
assert_image, assert_image,
assert_image_equal, assert_image_equal,
assert_image_similar, assert_image_similar,
@ -56,7 +54,7 @@ def roundtrip(im, **options):
@skip_unless_feature("zlib") @skip_unless_feature("zlib")
class TestFilePng(PillowTestCase): class TestFilePng:
def get_chunks(self, filename): def get_chunks(self, filename):
chunks = [] chunks = []
with open(filename, "rb") as fp: with open(filename, "rb") as fp:
@ -73,12 +71,12 @@ class TestFilePng(PillowTestCase):
return chunks return chunks
@pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian") @pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian")
def test_sanity(self): def test_sanity(self, tmp_path):
# internal version number # internal version number
assert re.search(r"\d+\.\d+\.\d+(\.\d+)?$", Image.core.zlib_version) assert re.search(r"\d+\.\d+\.\d+(\.\d+)?$", Image.core.zlib_version)
test_file = self.tempfile("temp.png") test_file = str(tmp_path / "temp.png")
hopper("RGB").save(test_file) hopper("RGB").save(test_file)
@ -233,14 +231,14 @@ class TestFilePng(PillowTestCase):
# image has 876 transparent pixels # image has 876 transparent pixels
assert im.getchannel("A").getcolors()[0][0] == 876 assert im.getchannel("A").getcolors()[0][0] == 876
def test_save_p_transparent_palette(self): def test_save_p_transparent_palette(self, tmp_path):
in_file = "Tests/images/pil123p.png" in_file = "Tests/images/pil123p.png"
with Image.open(in_file) as im: with Image.open(in_file) as im:
# 'transparency' contains a byte string with the opacity for # 'transparency' contains a byte string with the opacity for
# each palette entry # each palette entry
assert len(im.info["transparency"]) == 256 assert len(im.info["transparency"]) == 256
test_file = self.tempfile("temp.png") test_file = str(tmp_path / "temp.png")
im.save(test_file) im.save(test_file)
# check if saved image contains same transparency # check if saved image contains same transparency
@ -254,14 +252,14 @@ class TestFilePng(PillowTestCase):
# image has 124 unique alpha values # image has 124 unique alpha values
assert len(im.getchannel("A").getcolors()) == 124 assert len(im.getchannel("A").getcolors()) == 124
def test_save_p_single_transparency(self): def test_save_p_single_transparency(self, tmp_path):
in_file = "Tests/images/p_trns_single.png" in_file = "Tests/images/p_trns_single.png"
with Image.open(in_file) as im: with Image.open(in_file) as im:
# pixel value 164 is full transparent # pixel value 164 is full transparent
assert im.info["transparency"] == 164 assert im.info["transparency"] == 164
assert im.getpixel((31, 31)) == 164 assert im.getpixel((31, 31)) == 164
test_file = self.tempfile("temp.png") test_file = str(tmp_path / "temp.png")
im.save(test_file) im.save(test_file)
# check if saved image contains same transparency # check if saved image contains same transparency
@ -277,14 +275,14 @@ class TestFilePng(PillowTestCase):
# image has 876 transparent pixels # image has 876 transparent pixels
assert im.getchannel("A").getcolors()[0][0] == 876 assert im.getchannel("A").getcolors()[0][0] == 876
def test_save_p_transparent_black(self): def test_save_p_transparent_black(self, tmp_path):
# check if solid black image with full transparency # check if solid black image with full transparency
# is supported (check for #1838) # is supported (check for #1838)
im = Image.new("RGBA", (10, 10), (0, 0, 0, 0)) im = Image.new("RGBA", (10, 10), (0, 0, 0, 0))
assert im.getcolors() == [(100, (0, 0, 0, 0))] assert im.getcolors() == [(100, (0, 0, 0, 0))]
im = im.convert("P") im = im.convert("P")
test_file = self.tempfile("temp.png") test_file = str(tmp_path / "temp.png")
im.save(test_file) im.save(test_file)
# check if saved image contains same transparency # check if saved image contains same transparency
@ -295,7 +293,7 @@ class TestFilePng(PillowTestCase):
assert_image(im, "RGBA", (10, 10)) assert_image(im, "RGBA", (10, 10))
assert im.getcolors() == [(100, (0, 0, 0, 0))] assert im.getcolors() == [(100, (0, 0, 0, 0))]
def test_save_greyscale_transparency(self): def test_save_greyscale_transparency(self, tmp_path):
for mode, num_transparent in {"1": 1994, "L": 559, "I": 559}.items(): for mode, num_transparent in {"1": 1994, "L": 559, "I": 559}.items():
in_file = "Tests/images/" + mode.lower() + "_trns.png" in_file = "Tests/images/" + mode.lower() + "_trns.png"
with Image.open(in_file) as im: with Image.open(in_file) as im:
@ -305,7 +303,7 @@ class TestFilePng(PillowTestCase):
im_rgba = im.convert("RGBA") im_rgba = im.convert("RGBA")
assert im_rgba.getchannel("A").getcolors()[0][0] == num_transparent assert im_rgba.getchannel("A").getcolors()[0][0] == num_transparent
test_file = self.tempfile("temp.png") test_file = str(tmp_path / "temp.png")
im.save(test_file) im.save(test_file)
with Image.open(test_file) as test_im: with Image.open(test_file) as test_im:
@ -316,10 +314,10 @@ class TestFilePng(PillowTestCase):
test_im_rgba = test_im.convert("RGBA") test_im_rgba = test_im.convert("RGBA")
assert test_im_rgba.getchannel("A").getcolors()[0][0] == num_transparent assert test_im_rgba.getchannel("A").getcolors()[0][0] == num_transparent
def test_save_rgb_single_transparency(self): def test_save_rgb_single_transparency(self, tmp_path):
in_file = "Tests/images/caption_6_33_22.png" in_file = "Tests/images/caption_6_33_22.png"
with Image.open(in_file) as im: with Image.open(in_file) as im:
test_file = self.tempfile("temp.png") test_file = str(tmp_path / "temp.png")
im.save(test_file) im.save(test_file)
def test_load_verify(self): def test_load_verify(self):
@ -484,12 +482,12 @@ class TestFilePng(PillowTestCase):
im = roundtrip(im, transparency=(0, 1, 2)) im = roundtrip(im, transparency=(0, 1, 2))
assert im.info["transparency"] == (0, 1, 2) assert im.info["transparency"] == (0, 1, 2)
def test_trns_p(self): def test_trns_p(self, tmp_path):
# Check writing a transparency of 0, issue #528 # Check writing a transparency of 0, issue #528
im = hopper("P") im = hopper("P")
im.info["transparency"] = 0 im.info["transparency"] = 0
f = self.tempfile("temp.png") f = str(tmp_path / "temp.png")
im.save(f) im.save(f)
with Image.open(f) as im2: with Image.open(f) as im2:
@ -540,9 +538,9 @@ class TestFilePng(PillowTestCase):
assert repr_png.format == "PNG" assert repr_png.format == "PNG"
assert_image_equal(im, repr_png) assert_image_equal(im, repr_png)
def test_chunk_order(self): def test_chunk_order(self, tmp_path):
with Image.open("Tests/images/icc_profile.png") as im: with Image.open("Tests/images/icc_profile.png") as im:
test_file = self.tempfile("temp.png") test_file = str(tmp_path / "temp.png")
im.convert("P").save(test_file, dpi=(100, 100)) im.convert("P").save(test_file, dpi=(100, 100))
chunks = self.get_chunks(test_file) chunks = self.get_chunks(test_file)
@ -599,27 +597,27 @@ class TestFilePng(PillowTestCase):
exif = im._getexif() exif = im._getexif()
assert exif[274] == 1 assert exif[274] == 1
def test_exif_save(self): def test_exif_save(self, tmp_path):
with Image.open("Tests/images/exif.png") as im: with Image.open("Tests/images/exif.png") as im:
test_file = self.tempfile("temp.png") test_file = str(tmp_path / "temp.png")
im.save(test_file) im.save(test_file)
with Image.open(test_file) as reloaded: with Image.open(test_file) as reloaded:
exif = reloaded._getexif() exif = reloaded._getexif()
assert exif[274] == 1 assert exif[274] == 1
def test_exif_from_jpg(self): def test_exif_from_jpg(self, tmp_path):
with Image.open("Tests/images/pil_sample_rgb.jpg") as im: with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
test_file = self.tempfile("temp.png") test_file = str(tmp_path / "temp.png")
im.save(test_file) im.save(test_file)
with Image.open(test_file) as reloaded: with Image.open(test_file) as reloaded:
exif = reloaded._getexif() exif = reloaded._getexif()
assert exif[305] == "Adobe Photoshop CS Macintosh" assert exif[305] == "Adobe Photoshop CS Macintosh"
def test_exif_argument(self): def test_exif_argument(self, tmp_path):
with Image.open(TEST_PNG_FILE) as im: with Image.open(TEST_PNG_FILE) as im:
test_file = self.tempfile("temp.png") test_file = str(tmp_path / "temp.png")
im.save(test_file, exif=b"exifstring") im.save(test_file, exif=b"exifstring")
with Image.open(test_file) as reloaded: with Image.open(test_file) as reloaded:
@ -636,7 +634,7 @@ class TestFilePng(PillowTestCase):
assert_image_similar(im, expected, 0.23) assert_image_similar(im, expected, 0.23)
@unittest.skipIf(is_win32(), "requires Unix or macOS") @pytest.mark.skipif(is_win32(), reason="Requires Unix or macOS")
@skip_unless_feature("zlib") @skip_unless_feature("zlib")
class TestTruncatedPngPLeaks(PillowLeakTestCase): class TestTruncatedPngPLeaks(PillowLeakTestCase):
mem_limit = 2 * 1024 # max increase in K mem_limit = 2 * 1024 # max increase in K

View File

@ -1,6 +1,5 @@
import logging import logging
import os import os
import unittest
from io import BytesIO from io import BytesIO
import pytest import pytest
@ -8,7 +7,6 @@ from PIL import Image, TiffImagePlugin
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
from .helper import ( from .helper import (
PillowTestCase,
assert_image_equal, assert_image_equal,
assert_image_equal_tofile, assert_image_equal_tofile,
assert_image_similar, assert_image_similar,
@ -21,10 +19,10 @@ from .helper import (
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class TestFileTiff(PillowTestCase): class TestFileTiff:
def test_sanity(self): def test_sanity(self, tmp_path):
filename = self.tempfile("temp.tif") filename = str(tmp_path / "temp.tif")
hopper("RGB").save(filename) hopper("RGB").save(filename)
@ -54,7 +52,7 @@ class TestFileTiff(PillowTestCase):
with Image.open(filename): with Image.open(filename):
pass pass
@unittest.skipIf(is_pypy(), "Requires CPython") @pytest.mark.skipif(is_pypy(), reason="Requires CPython")
def test_unclosed_file(self): def test_unclosed_file(self):
def open(): def open():
im = Image.open("Tests/images/multipage.tiff") im = Image.open("Tests/images/multipage.tiff")
@ -155,8 +153,8 @@ class TestFileTiff(PillowTestCase):
assert im.tag_v2.get(RESOLUTION_UNIT) == resolutionUnit assert im.tag_v2.get(RESOLUTION_UNIT) == resolutionUnit
assert im.info["dpi"] == (dpi[1], dpi[1]) assert im.info["dpi"] == (dpi[1], dpi[1])
def test_save_dpi_rounding(self): def test_save_dpi_rounding(self, tmp_path):
outfile = self.tempfile("temp.tif") outfile = str(tmp_path / "temp.tif")
with Image.open("Tests/images/hopper.tif") as im: with Image.open("Tests/images/hopper.tif") as im:
for dpi in (72.2, 72.8): for dpi in (72.2, 72.8):
im.save(outfile, dpi=(dpi, dpi)) im.save(outfile, dpi=(dpi, dpi))
@ -190,14 +188,14 @@ class TestFileTiff(PillowTestCase):
# Should not raise struct.error. # Should not raise struct.error.
pytest.warns(UserWarning, i._getexif) pytest.warns(UserWarning, i._getexif)
def test_save_rgba(self): def test_save_rgba(self, tmp_path):
im = hopper("RGBA") im = hopper("RGBA")
outfile = self.tempfile("temp.tif") outfile = str(tmp_path / "temp.tif")
im.save(outfile) im.save(outfile)
def test_save_unsupported_mode(self): def test_save_unsupported_mode(self, tmp_path):
im = hopper("HSV") im = hopper("HSV")
outfile = self.tempfile("temp.tif") outfile = str(tmp_path / "temp.tif")
with pytest.raises(IOError): with pytest.raises(IOError):
im.save(outfile) im.save(outfile)
@ -459,9 +457,9 @@ class TestFileTiff(PillowTestCase):
assert im2.mode == "L" assert im2.mode == "L"
assert_image_equal(im, im2) assert_image_equal(im, im2)
def test_with_underscores(self): def test_with_underscores(self, tmp_path):
kwargs = {"resolution_unit": "inch", "x_resolution": 72, "y_resolution": 36} kwargs = {"resolution_unit": "inch", "x_resolution": 72, "y_resolution": 36}
filename = self.tempfile("temp.tif") filename = str(tmp_path / "temp.tif")
hopper("RGB").save(filename, **kwargs) hopper("RGB").save(filename, **kwargs)
with Image.open(filename) as im: with Image.open(filename) as im:
@ -473,14 +471,14 @@ class TestFileTiff(PillowTestCase):
assert im.tag_v2[X_RESOLUTION] == 72 assert im.tag_v2[X_RESOLUTION] == 72
assert im.tag_v2[Y_RESOLUTION] == 36 assert im.tag_v2[Y_RESOLUTION] == 36
def test_roundtrip_tiff_uint16(self): def test_roundtrip_tiff_uint16(self, tmp_path):
# Test an image of all '0' values # Test an image of all '0' values
pixel_value = 0x1234 pixel_value = 0x1234
infile = "Tests/images/uint16_1_4660.tif" infile = "Tests/images/uint16_1_4660.tif"
with Image.open(infile) as im: with Image.open(infile) as im:
assert im.getpixel((0, 0)) == pixel_value assert im.getpixel((0, 0)) == pixel_value
tmpfile = self.tempfile("temp.tif") tmpfile = str(tmp_path / "temp.tif")
im.save(tmpfile) im.save(tmpfile)
with Image.open(tmpfile) as reloaded: with Image.open(tmpfile) as reloaded:
@ -512,13 +510,16 @@ class TestFileTiff(PillowTestCase):
with Image.open(infile) as im: with Image.open(infile) as im:
assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png") assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
def test_palette(self): def test_palette(self, tmp_path):
for mode in ["P", "PA"]: def roundtrip(mode):
outfile = self.tempfile("temp.tif") outfile = str(tmp_path / "temp.tif")
im = hopper(mode) im = hopper(mode)
im.save(outfile) im.save(outfile)
for mode in ["P", "PA"]:
roundtrip(mode)
with Image.open(outfile) as reloaded: with Image.open(outfile) as reloaded:
assert_image_equal(im.convert("RGB"), reloaded.convert("RGB")) assert_image_equal(im.convert("RGB"), reloaded.convert("RGB"))
@ -552,7 +553,7 @@ class TestFileTiff(PillowTestCase):
with Image.open(mp) as reread: with Image.open(mp) as reread:
assert reread.n_frames == 3 assert reread.n_frames == 3
def test_saving_icc_profile(self): def test_saving_icc_profile(self, tmp_path):
# Tests saving TIFF with icc_profile set. # Tests saving TIFF with icc_profile set.
# At the time of writing this will only work for non-compressed tiffs # At the time of writing this will only work for non-compressed tiffs
# as libtiff does not support embedded ICC profiles, # as libtiff does not support embedded ICC profiles,
@ -561,14 +562,14 @@ class TestFileTiff(PillowTestCase):
im.info["icc_profile"] = "Dummy value" im.info["icc_profile"] = "Dummy value"
# Try save-load round trip to make sure both handle icc_profile. # Try save-load round trip to make sure both handle icc_profile.
tmpfile = self.tempfile("temp.tif") tmpfile = str(tmp_path / "temp.tif")
im.save(tmpfile, "TIFF", compression="raw") im.save(tmpfile, "TIFF", compression="raw")
with Image.open(tmpfile) as reloaded: with Image.open(tmpfile) as reloaded:
assert b"Dummy value" == reloaded.info["icc_profile"] assert b"Dummy value" == reloaded.info["icc_profile"]
def test_close_on_load_exclusive(self): def test_close_on_load_exclusive(self, tmp_path):
# similar to test_fd_leak, but runs on unixlike os # similar to test_fd_leak, but runs on unixlike os
tmpfile = self.tempfile("temp.tif") tmpfile = str(tmp_path / "temp.tif")
with Image.open("Tests/images/uint16_1_4660.tif") as im: with Image.open("Tests/images/uint16_1_4660.tif") as im:
im.save(tmpfile) im.save(tmpfile)
@ -579,8 +580,8 @@ class TestFileTiff(PillowTestCase):
im.load() im.load()
assert fp.closed assert fp.closed
def test_close_on_load_nonexclusive(self): def test_close_on_load_nonexclusive(self, tmp_path):
tmpfile = self.tempfile("temp.tif") tmpfile = str(tmp_path / "temp.tif")
with Image.open("Tests/images/uint16_1_4660.tif") as im: with Image.open("Tests/images/uint16_1_4660.tif") as im:
im.save(tmpfile) im.save(tmpfile)
@ -601,10 +602,10 @@ class TestFileTiff(PillowTestCase):
Image.open("Tests/images/string_dimension.tiff") Image.open("Tests/images/string_dimension.tiff")
@unittest.skipUnless(is_win32(), "Windows only") @pytest.mark.skipif(not is_win32(), reason="Windows only")
class TestFileTiffW32(PillowTestCase): class TestFileTiffW32:
def test_fd_leak(self): def test_fd_leak(self, tmp_path):
tmpfile = self.tempfile("temp.tif") tmpfile = str(tmp_path / "temp.tif")
# this is an mmaped file. # this is an mmaped file.
with Image.open("Tests/images/uint16_1_4660.tif") as im: with Image.open("Tests/images/uint16_1_4660.tif") as im:

View File

@ -2,13 +2,12 @@ import ctypes
import os import os
import subprocess import subprocess
import sys import sys
import unittest
from distutils import ccompiler, sysconfig from distutils import ccompiler, sysconfig
import pytest import pytest
from PIL import Image from PIL import Image
from .helper import PillowTestCase, assert_image_equal, hopper, is_win32, on_ci from .helper import assert_image_equal, hopper, is_win32, on_ci
# CFFI imports pycparser which doesn't support PYTHONOPTIMIZE=2 # CFFI imports pycparser which doesn't support PYTHONOPTIMIZE=2
# https://github.com/eliben/pycparser/pull/198#issuecomment-317001670 # https://github.com/eliben/pycparser/pull/198#issuecomment-317001670
@ -22,7 +21,7 @@ else:
cffi = None cffi = None
class AccessTest(PillowTestCase): class AccessTest:
# initial value # initial value
_init_cffi_access = Image.USE_CFFI_ACCESS _init_cffi_access = Image.USE_CFFI_ACCESS
_need_cffi_access = False _need_cffi_access = False
@ -200,17 +199,17 @@ class TestImageGetPixel(AccessTest):
assert im.convert("RGB").getpixel((0, 0)) == (255, 0, 0) assert im.convert("RGB").getpixel((0, 0)) == (255, 0, 0)
@unittest.skipIf(cffi is None, "No cffi") @pytest.mark.skipif(cffi is None, reason="No CFFI")
class TestCffiPutPixel(TestImagePutPixel): class TestCffiPutPixel(TestImagePutPixel):
_need_cffi_access = True _need_cffi_access = True
@unittest.skipIf(cffi is None, "No cffi") @pytest.mark.skipif(cffi is None, reason="No CFFI")
class TestCffiGetPixel(TestImageGetPixel): class TestCffiGetPixel(TestImageGetPixel):
_need_cffi_access = True _need_cffi_access = True
@unittest.skipIf(cffi is None, "No cffi") @pytest.mark.skipif(cffi is None, reason="No CFFI")
class TestCffi(AccessTest): class TestCffi(AccessTest):
_need_cffi_access = True _need_cffi_access = True
@ -326,10 +325,11 @@ class TestCffi(AccessTest):
assert im.convert("RGB").getpixel((0, 0)) == (255, 0, 0) assert im.convert("RGB").getpixel((0, 0)) == (255, 0, 0)
class TestEmbeddable(unittest.TestCase): class TestEmbeddable:
@unittest.skipIf( @pytest.mark.skipif(
not is_win32() or on_ci(), not is_win32() or on_ci(),
"Failing on AppVeyor / GitHub Actions when run from subprocess, not from shell", reason="Failing on AppVeyor / GitHub Actions when run from subprocess, "
"not from shell",
) )
def test_embeddable(self): def test_embeddable(self):
with open("embed_pil.c", "w") as fh: with open("embed_pil.c", "w") as fh:

View File

@ -1,11 +1,10 @@
import unittest import pytest
from PIL import ImageWin from PIL import ImageWin
from .helper import PillowTestCase, hopper, is_win32 from .helper import hopper, is_win32
class TestImageWin(PillowTestCase): class TestImageWin:
def test_sanity(self): def test_sanity(self):
dir(ImageWin) dir(ImageWin)
@ -32,8 +31,8 @@ class TestImageWin(PillowTestCase):
assert wnd2 == 50 assert wnd2 == 50
@unittest.skipUnless(is_win32(), "Windows only") @pytest.mark.skipif(not is_win32(), reason="Windows only")
class TestImageWinDib(PillowTestCase): class TestImageWinDib:
def test_dib_image(self): def test_dib_image(self):
# Arrange # Arrange
im = hopper() im = hopper()

View File

@ -1,15 +1,9 @@
import shutil import shutil
import pytest
from PIL import GifImagePlugin, Image, JpegImagePlugin from PIL import GifImagePlugin, Image, JpegImagePlugin
from .helper import ( from .helper import cjpeg_available, djpeg_available, is_win32, netpbm_available
PillowTestCase,
cjpeg_available,
djpeg_available,
is_win32,
netpbm_available,
unittest,
)
TEST_JPG = "Tests/images/hopper.jpg" TEST_JPG = "Tests/images/hopper.jpg"
TEST_GIF = "Tests/images/hopper.gif" TEST_GIF = "Tests/images/hopper.gif"
@ -17,38 +11,38 @@ TEST_GIF = "Tests/images/hopper.gif"
test_filenames = ("temp_';", 'temp_";', "temp_'\"|", "temp_'\"||", "temp_'\"&&") test_filenames = ("temp_';", 'temp_";', "temp_'\"|", "temp_'\"||", "temp_'\"&&")
@unittest.skipIf(is_win32(), "requires Unix or macOS") @pytest.mark.skipif(is_win32(), reason="Requires Unix or macOS")
class TestShellInjection(PillowTestCase): class TestShellInjection:
def assert_save_filename_check(self, src_img, save_func): def assert_save_filename_check(self, tmp_path, src_img, save_func):
for filename in test_filenames: for filename in test_filenames:
dest_file = self.tempfile(filename) dest_file = str(tmp_path / filename)
save_func(src_img, 0, dest_file) save_func(src_img, 0, dest_file)
# If file can't be opened, shell injection probably occurred # If file can't be opened, shell injection probably occurred
with Image.open(dest_file) as im: with Image.open(dest_file) as im:
im.load() im.load()
@unittest.skipUnless(djpeg_available(), "djpeg not available") @pytest.mark.skipif(not djpeg_available(), reason="djpeg not available")
def test_load_djpeg_filename(self): def test_load_djpeg_filename(self, tmp_path):
for filename in test_filenames: for filename in test_filenames:
src_file = self.tempfile(filename) src_file = str(tmp_path / filename)
shutil.copy(TEST_JPG, src_file) shutil.copy(TEST_JPG, src_file)
with Image.open(src_file) as im: with Image.open(src_file) as im:
im.load_djpeg() im.load_djpeg()
@unittest.skipUnless(cjpeg_available(), "cjpeg not available") @pytest.mark.skipif(not cjpeg_available(), reason="cjpeg not available")
def test_save_cjpeg_filename(self): def test_save_cjpeg_filename(self, tmp_path):
with Image.open(TEST_JPG) as im: with Image.open(TEST_JPG) as im:
self.assert_save_filename_check(im, JpegImagePlugin._save_cjpeg) self.assert_save_filename_check(tmp_path, im, JpegImagePlugin._save_cjpeg)
@unittest.skipUnless(netpbm_available(), "netpbm not available") @pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
def test_save_netpbm_filename_bmp_mode(self): def test_save_netpbm_filename_bmp_mode(self, tmp_path):
with Image.open(TEST_GIF) as im: with Image.open(TEST_GIF) as im:
im = im.convert("RGB") im = im.convert("RGB")
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm) self.assert_save_filename_check(tmp_path, im, GifImagePlugin._save_netpbm)
@unittest.skipUnless(netpbm_available(), "netpbm not available") @pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
def test_save_netpbm_filename_l_mode(self): def test_save_netpbm_filename_l_mode(self, tmp_path):
with Image.open(TEST_GIF) as im: with Image.open(TEST_GIF) as im:
im = im.convert("L") im = im.convert("L")
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm) self.assert_save_filename_check(tmp_path, im, GifImagePlugin._save_netpbm)