mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-10 19:56:47 +03:00
Convert to use pytest
This commit is contained in:
parent
699a9dadf1
commit
44e661f25a
|
@ -1,10 +1,9 @@
|
|||
import unittest
|
||||
from array import array
|
||||
|
||||
import pytest
|
||||
from PIL import Image, ImageFilter
|
||||
|
||||
from .helper import PillowTestCase, assert_image_equal
|
||||
from .helper import assert_image_equal
|
||||
|
||||
try:
|
||||
import numpy
|
||||
|
@ -12,7 +11,7 @@ except ImportError:
|
|||
numpy = None
|
||||
|
||||
|
||||
class TestColorLut3DCoreAPI(PillowTestCase):
|
||||
class TestColorLut3DCoreAPI:
|
||||
def generate_identity_table(self, channels, size):
|
||||
if isinstance(size, tuple):
|
||||
size1D, size2D, size3D = size
|
||||
|
@ -271,7 +270,7 @@ class TestColorLut3DCoreAPI(PillowTestCase):
|
|||
assert transformed[205, 205] == (255, 255, 0)
|
||||
|
||||
|
||||
class TestColorLut3DFilter(PillowTestCase):
|
||||
class TestColorLut3DFilter:
|
||||
def test_wrong_args(self):
|
||||
with pytest.raises(ValueError, match="should be either an integer"):
|
||||
ImageFilter.Color3DLUT("small", [1])
|
||||
|
@ -317,7 +316,7 @@ class TestColorLut3DFilter(PillowTestCase):
|
|||
assert tuple(lut.size) == (2, 2, 2)
|
||||
assert lut.table == list(range(4)) * 8
|
||||
|
||||
@unittest.skipIf(numpy is None, "Numpy is not installed")
|
||||
@pytest.mark.skipif(numpy is None, reason="NumPy not installed")
|
||||
def test_numpy_sources(self):
|
||||
table = numpy.ones((5, 6, 7, 3), dtype=numpy.float16)
|
||||
with pytest.raises(ValueError, match="should have either channels"):
|
||||
|
@ -350,7 +349,7 @@ class TestColorLut3DFilter(PillowTestCase):
|
|||
table[0] = 33
|
||||
assert lut.table[0] == 33
|
||||
|
||||
@unittest.skipIf(numpy is None, "Numpy is not installed")
|
||||
@pytest.mark.skipif(numpy is None, reason="NumPy not installed")
|
||||
def test_numpy_formats(self):
|
||||
g = Image.linear_gradient("L")
|
||||
im = Image.merge(
|
||||
|
@ -402,7 +401,7 @@ class TestColorLut3DFilter(PillowTestCase):
|
|||
)
|
||||
|
||||
|
||||
class TestGenerateColorLut3D(PillowTestCase):
|
||||
class TestGenerateColorLut3D:
|
||||
def test_wrong_channels_count(self):
|
||||
with pytest.raises(ValueError, match="3 or 4 output channels"):
|
||||
ImageFilter.Color3DLUT.generate(
|
||||
|
@ -450,7 +449,7 @@ class TestGenerateColorLut3D(PillowTestCase):
|
|||
assert im == im.filter(lut)
|
||||
|
||||
|
||||
class TestTransformColorLut3D(PillowTestCase):
|
||||
class TestTransformColorLut3D:
|
||||
def test_wrong_args(self):
|
||||
source = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b))
|
||||
|
||||
|
|
|
@ -1,190 +1,207 @@
|
|||
import io
|
||||
import unittest
|
||||
|
||||
import pytest
|
||||
from PIL import EpsImagePlugin, Image, features
|
||||
|
||||
from .helper import PillowTestCase, assert_image_similar, hopper, skip_unless_feature
|
||||
from .helper import assert_image_similar, hopper, skip_unless_feature
|
||||
|
||||
HAS_GHOSTSCRIPT = EpsImagePlugin.has_ghostscript()
|
||||
|
||||
# Our two EPS test files (they are identical except for their bounding boxes)
|
||||
file1 = "Tests/images/zero_bb.eps"
|
||||
file2 = "Tests/images/non_zero_bb.eps"
|
||||
FILE1 = "Tests/images/zero_bb.eps"
|
||||
FILE2 = "Tests/images/non_zero_bb.eps"
|
||||
|
||||
# Due to palletization, we'll need to convert these to RGB after load
|
||||
file1_compare = "Tests/images/zero_bb.png"
|
||||
file1_compare_scale2 = "Tests/images/zero_bb_scale2.png"
|
||||
FILE1_COMPARE = "Tests/images/zero_bb.png"
|
||||
FILE1_COMPARE_SCALE2 = "Tests/images/zero_bb_scale2.png"
|
||||
|
||||
file2_compare = "Tests/images/non_zero_bb.png"
|
||||
file2_compare_scale2 = "Tests/images/non_zero_bb_scale2.png"
|
||||
FILE2_COMPARE = "Tests/images/non_zero_bb.png"
|
||||
FILE2_COMPARE_SCALE2 = "Tests/images/non_zero_bb_scale2.png"
|
||||
|
||||
# EPS test files with binary preview
|
||||
file3 = "Tests/images/binary_preview_map.eps"
|
||||
FILE3 = "Tests/images/binary_preview_map.eps"
|
||||
|
||||
|
||||
class TestFileEps(PillowTestCase):
|
||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||
def test_sanity(self):
|
||||
# Regular scale
|
||||
with Image.open(file1) as image1:
|
||||
image1.load()
|
||||
assert image1.mode == "RGB"
|
||||
assert image1.size == (460, 352)
|
||||
assert image1.format == "EPS"
|
||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||
def test_sanity():
|
||||
# Regular scale
|
||||
with Image.open(FILE1) as image1:
|
||||
image1.load()
|
||||
assert image1.mode == "RGB"
|
||||
assert image1.size == (460, 352)
|
||||
assert image1.format == "EPS"
|
||||
|
||||
with Image.open(file2) as image2:
|
||||
image2.load()
|
||||
assert image2.mode == "RGB"
|
||||
assert image2.size == (360, 252)
|
||||
assert image2.format == "EPS"
|
||||
with Image.open(FILE2) as image2:
|
||||
image2.load()
|
||||
assert image2.mode == "RGB"
|
||||
assert image2.size == (360, 252)
|
||||
assert image2.format == "EPS"
|
||||
|
||||
# Double scale
|
||||
with Image.open(file1) as image1_scale2:
|
||||
image1_scale2.load(scale=2)
|
||||
assert image1_scale2.mode == "RGB"
|
||||
assert image1_scale2.size == (920, 704)
|
||||
assert image1_scale2.format == "EPS"
|
||||
# Double scale
|
||||
with Image.open(FILE1) as image1_scale2:
|
||||
image1_scale2.load(scale=2)
|
||||
assert image1_scale2.mode == "RGB"
|
||||
assert image1_scale2.size == (920, 704)
|
||||
assert image1_scale2.format == "EPS"
|
||||
|
||||
with Image.open(file2) as image2_scale2:
|
||||
image2_scale2.load(scale=2)
|
||||
assert image2_scale2.mode == "RGB"
|
||||
assert image2_scale2.size == (720, 504)
|
||||
assert image2_scale2.format == "EPS"
|
||||
with Image.open(FILE2) as image2_scale2:
|
||||
image2_scale2.load(scale=2)
|
||||
assert image2_scale2.mode == "RGB"
|
||||
assert image2_scale2.size == (720, 504)
|
||||
assert image2_scale2.format == "EPS"
|
||||
|
||||
def test_invalid_file(self):
|
||||
invalid_file = "Tests/images/flower.jpg"
|
||||
|
||||
with pytest.raises(SyntaxError):
|
||||
EpsImagePlugin.EpsImageFile(invalid_file)
|
||||
def test_invalid_file():
|
||||
invalid_file = "Tests/images/flower.jpg"
|
||||
|
||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||
def test_cmyk(self):
|
||||
with Image.open("Tests/images/pil_sample_cmyk.eps") as cmyk_image:
|
||||
with pytest.raises(SyntaxError):
|
||||
EpsImagePlugin.EpsImageFile(invalid_file)
|
||||
|
||||
assert cmyk_image.mode == "CMYK"
|
||||
assert cmyk_image.size == (100, 100)
|
||||
assert cmyk_image.format == "EPS"
|
||||
|
||||
cmyk_image.load()
|
||||
assert cmyk_image.mode == "RGB"
|
||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||
def test_cmyk():
|
||||
with Image.open("Tests/images/pil_sample_cmyk.eps") as cmyk_image:
|
||||
|
||||
if features.check("jpg"):
|
||||
with Image.open("Tests/images/pil_sample_rgb.jpg") as target:
|
||||
assert_image_similar(cmyk_image, target, 10)
|
||||
assert cmyk_image.mode == "CMYK"
|
||||
assert cmyk_image.size == (100, 100)
|
||||
assert cmyk_image.format == "EPS"
|
||||
|
||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||
def test_showpage(self):
|
||||
# See https://github.com/python-pillow/Pillow/issues/2615
|
||||
with Image.open("Tests/images/reqd_showpage.eps") as plot_image:
|
||||
with Image.open("Tests/images/reqd_showpage.png") as target:
|
||||
# should not crash/hang
|
||||
plot_image.load()
|
||||
# fonts could be slightly different
|
||||
assert_image_similar(plot_image, target, 6)
|
||||
cmyk_image.load()
|
||||
assert cmyk_image.mode == "RGB"
|
||||
|
||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||
def test_file_object(self):
|
||||
# issue 479
|
||||
with Image.open(file1) as image1:
|
||||
with open(self.tempfile("temp_file.eps"), "wb") as fh:
|
||||
image1.save(fh, "EPS")
|
||||
if features.check("jpg"):
|
||||
with Image.open("Tests/images/pil_sample_rgb.jpg") as target:
|
||||
assert_image_similar(cmyk_image, target, 10)
|
||||
|
||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||
def test_iobase_object(self):
|
||||
# issue 479
|
||||
with Image.open(file1) as image1:
|
||||
with open(self.tempfile("temp_iobase.eps"), "wb") as fh:
|
||||
image1.save(fh, "EPS")
|
||||
|
||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||
def test_bytesio_object(self):
|
||||
with open(file1, "rb") as f:
|
||||
img_bytes = io.BytesIO(f.read())
|
||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||
def test_showpage():
|
||||
# See https://github.com/python-pillow/Pillow/issues/2615
|
||||
with Image.open("Tests/images/reqd_showpage.eps") as plot_image:
|
||||
with Image.open("Tests/images/reqd_showpage.png") as target:
|
||||
# should not crash/hang
|
||||
plot_image.load()
|
||||
# fonts could be slightly different
|
||||
assert_image_similar(plot_image, target, 6)
|
||||
|
||||
with Image.open(img_bytes) as img:
|
||||
img.load()
|
||||
|
||||
with Image.open(file1_compare) as image1_scale1_compare:
|
||||
image1_scale1_compare = image1_scale1_compare.convert("RGB")
|
||||
image1_scale1_compare.load()
|
||||
assert_image_similar(img, image1_scale1_compare, 5)
|
||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||
def test_file_object(tmp_path):
|
||||
# issue 479
|
||||
with Image.open(FILE1) as image1:
|
||||
with open(str(tmp_path / "temp.eps"), "wb") as fh:
|
||||
image1.save(fh, "EPS")
|
||||
|
||||
def test_image_mode_not_supported(self):
|
||||
im = hopper("RGBA")
|
||||
tmpfile = self.tempfile("temp.eps")
|
||||
with pytest.raises(ValueError):
|
||||
im.save(tmpfile)
|
||||
|
||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||
@skip_unless_feature("zlib")
|
||||
def test_render_scale1(self):
|
||||
# We need png support for these render test
|
||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||
def test_iobase_object(tmp_path):
|
||||
# issue 479
|
||||
with Image.open(FILE1) as image1:
|
||||
with open(str(tmp_path / "temp_iobase.eps"), "wb") as fh:
|
||||
image1.save(fh, "EPS")
|
||||
|
||||
# Zero bounding box
|
||||
with Image.open(file1) as image1_scale1:
|
||||
image1_scale1.load()
|
||||
with Image.open(file1_compare) as image1_scale1_compare:
|
||||
image1_scale1_compare = image1_scale1_compare.convert("RGB")
|
||||
image1_scale1_compare.load()
|
||||
assert_image_similar(image1_scale1, image1_scale1_compare, 5)
|
||||
|
||||
# Non-Zero bounding box
|
||||
with Image.open(file2) as image2_scale1:
|
||||
image2_scale1.load()
|
||||
with Image.open(file2_compare) as image2_scale1_compare:
|
||||
image2_scale1_compare = image2_scale1_compare.convert("RGB")
|
||||
image2_scale1_compare.load()
|
||||
assert_image_similar(image2_scale1, image2_scale1_compare, 10)
|
||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||
def test_bytesio_object():
|
||||
with open(FILE1, "rb") as f:
|
||||
img_bytes = io.BytesIO(f.read())
|
||||
|
||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||
@skip_unless_feature("zlib")
|
||||
def test_render_scale2(self):
|
||||
# We need png support for these render test
|
||||
with Image.open(img_bytes) as img:
|
||||
img.load()
|
||||
|
||||
# Zero bounding box
|
||||
with Image.open(file1) as image1_scale2:
|
||||
image1_scale2.load(scale=2)
|
||||
with Image.open(file1_compare_scale2) as image1_scale2_compare:
|
||||
image1_scale2_compare = image1_scale2_compare.convert("RGB")
|
||||
image1_scale2_compare.load()
|
||||
assert_image_similar(image1_scale2, image1_scale2_compare, 5)
|
||||
with Image.open(FILE1_COMPARE) as image1_scale1_compare:
|
||||
image1_scale1_compare = image1_scale1_compare.convert("RGB")
|
||||
image1_scale1_compare.load()
|
||||
assert_image_similar(img, image1_scale1_compare, 5)
|
||||
|
||||
# Non-Zero bounding box
|
||||
with Image.open(file2) as image2_scale2:
|
||||
image2_scale2.load(scale=2)
|
||||
with Image.open(file2_compare_scale2) as image2_scale2_compare:
|
||||
image2_scale2_compare = image2_scale2_compare.convert("RGB")
|
||||
image2_scale2_compare.load()
|
||||
assert_image_similar(image2_scale2, image2_scale2_compare, 10)
|
||||
|
||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||
def test_resize(self):
|
||||
files = [file1, file2, "Tests/images/illu10_preview.eps"]
|
||||
for fn in files:
|
||||
with Image.open(fn) as im:
|
||||
new_size = (100, 100)
|
||||
im = im.resize(new_size)
|
||||
assert im.size == new_size
|
||||
def test_image_mode_not_supported(tmp_path):
|
||||
im = hopper("RGBA")
|
||||
tmpfile = str(tmp_path / "temp.eps")
|
||||
with pytest.raises(ValueError):
|
||||
im.save(tmpfile)
|
||||
|
||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||
def test_thumbnail(self):
|
||||
# Issue #619
|
||||
# Arrange
|
||||
files = [file1, file2]
|
||||
for fn in files:
|
||||
with Image.open(file1) as im:
|
||||
new_size = (100, 100)
|
||||
im.thumbnail(new_size)
|
||||
assert max(im.size) == max(new_size)
|
||||
|
||||
def test_read_binary_preview(self):
|
||||
# Issue 302
|
||||
# open image with binary preview
|
||||
with Image.open(file3):
|
||||
pass
|
||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||
@skip_unless_feature("zlib")
|
||||
def test_render_scale1():
|
||||
# We need png support for these render test
|
||||
|
||||
def _test_readline(self, t, ending):
|
||||
# Zero bounding box
|
||||
with Image.open(FILE1) as image1_scale1:
|
||||
image1_scale1.load()
|
||||
with Image.open(FILE1_COMPARE) as image1_scale1_compare:
|
||||
image1_scale1_compare = image1_scale1_compare.convert("RGB")
|
||||
image1_scale1_compare.load()
|
||||
assert_image_similar(image1_scale1, image1_scale1_compare, 5)
|
||||
|
||||
# Non-Zero bounding box
|
||||
with Image.open(FILE2) as image2_scale1:
|
||||
image2_scale1.load()
|
||||
with Image.open(FILE2_COMPARE) as image2_scale1_compare:
|
||||
image2_scale1_compare = image2_scale1_compare.convert("RGB")
|
||||
image2_scale1_compare.load()
|
||||
assert_image_similar(image2_scale1, image2_scale1_compare, 10)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||
@skip_unless_feature("zlib")
|
||||
def test_render_scale2():
|
||||
# We need png support for these render test
|
||||
|
||||
# Zero bounding box
|
||||
with Image.open(FILE1) as image1_scale2:
|
||||
image1_scale2.load(scale=2)
|
||||
with Image.open(FILE1_COMPARE_SCALE2) as image1_scale2_compare:
|
||||
image1_scale2_compare = image1_scale2_compare.convert("RGB")
|
||||
image1_scale2_compare.load()
|
||||
assert_image_similar(image1_scale2, image1_scale2_compare, 5)
|
||||
|
||||
# Non-Zero bounding box
|
||||
with Image.open(FILE2) as image2_scale2:
|
||||
image2_scale2.load(scale=2)
|
||||
with Image.open(FILE2_COMPARE_SCALE2) as image2_scale2_compare:
|
||||
image2_scale2_compare = image2_scale2_compare.convert("RGB")
|
||||
image2_scale2_compare.load()
|
||||
assert_image_similar(image2_scale2, image2_scale2_compare, 10)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||
def test_resize():
|
||||
files = [FILE1, FILE2, "Tests/images/illu10_preview.eps"]
|
||||
for fn in files:
|
||||
with Image.open(fn) as im:
|
||||
new_size = (100, 100)
|
||||
im = im.resize(new_size)
|
||||
assert im.size == new_size
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||
def test_thumbnail():
|
||||
# Issue #619
|
||||
# Arrange
|
||||
files = [FILE1, FILE2]
|
||||
for fn in files:
|
||||
with Image.open(FILE1) as im:
|
||||
new_size = (100, 100)
|
||||
im.thumbnail(new_size)
|
||||
assert max(im.size) == max(new_size)
|
||||
|
||||
|
||||
def test_read_binary_preview():
|
||||
# Issue 302
|
||||
# open image with binary preview
|
||||
with Image.open(FILE3):
|
||||
pass
|
||||
|
||||
|
||||
def test_readline(tmp_path):
|
||||
# check all the freaking line endings possible from the spec
|
||||
# test_string = u'something\r\nelse\n\rbaz\rbif\n'
|
||||
line_endings = ["\r\n", "\n", "\n\r", "\r"]
|
||||
strings = ["something", "else", "baz", "bif"]
|
||||
|
||||
def _test_readline(t, ending):
|
||||
ending = "Failure with line ending: %s" % (
|
||||
"".join("%s" % ord(s) for s in ending)
|
||||
)
|
||||
|
@ -193,53 +210,49 @@ class TestFileEps(PillowTestCase):
|
|||
assert t.readline().strip("\r\n") == "baz", ending
|
||||
assert t.readline().strip("\r\n") == "bif", ending
|
||||
|
||||
def _test_readline_io_psfile(self, test_string, ending):
|
||||
def _test_readline_io_psfile(test_string, ending):
|
||||
f = io.BytesIO(test_string.encode("latin-1"))
|
||||
t = EpsImagePlugin.PSFile(f)
|
||||
self._test_readline(t, ending)
|
||||
_test_readline(t, ending)
|
||||
|
||||
def _test_readline_file_psfile(self, test_string, ending):
|
||||
f = self.tempfile("temp.txt")
|
||||
def _test_readline_file_psfile(test_string, ending):
|
||||
f = str(tmp_path / "temp.bufr")
|
||||
with open(f, "wb") as w:
|
||||
w.write(test_string.encode("latin-1"))
|
||||
|
||||
with open(f, "rb") as r:
|
||||
t = EpsImagePlugin.PSFile(r)
|
||||
self._test_readline(t, ending)
|
||||
_test_readline(t, ending)
|
||||
|
||||
def test_readline(self):
|
||||
# check all the freaking line endings possible from the spec
|
||||
# test_string = u'something\r\nelse\n\rbaz\rbif\n'
|
||||
line_endings = ["\r\n", "\n", "\n\r", "\r"]
|
||||
strings = ["something", "else", "baz", "bif"]
|
||||
for ending in line_endings:
|
||||
s = ending.join(strings)
|
||||
_test_readline_io_psfile(s, ending)
|
||||
_test_readline_file_psfile(s, ending)
|
||||
|
||||
for ending in line_endings:
|
||||
s = ending.join(strings)
|
||||
self._test_readline_io_psfile(s, ending)
|
||||
self._test_readline_file_psfile(s, ending)
|
||||
|
||||
def test_open_eps(self):
|
||||
# https://github.com/python-pillow/Pillow/issues/1104
|
||||
# Arrange
|
||||
FILES = [
|
||||
"Tests/images/illu10_no_preview.eps",
|
||||
"Tests/images/illu10_preview.eps",
|
||||
"Tests/images/illuCS6_no_preview.eps",
|
||||
"Tests/images/illuCS6_preview.eps",
|
||||
]
|
||||
def test_open_eps():
|
||||
# https://github.com/python-pillow/Pillow/issues/1104
|
||||
# Arrange
|
||||
FILES = [
|
||||
"Tests/images/illu10_no_preview.eps",
|
||||
"Tests/images/illu10_preview.eps",
|
||||
"Tests/images/illuCS6_no_preview.eps",
|
||||
"Tests/images/illuCS6_preview.eps",
|
||||
]
|
||||
|
||||
# Act / Assert
|
||||
for filename in FILES:
|
||||
with Image.open(filename) as img:
|
||||
assert img.mode == "RGB"
|
||||
# Act / Assert
|
||||
for filename in FILES:
|
||||
with Image.open(filename) as img:
|
||||
assert img.mode == "RGB"
|
||||
|
||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||
def test_emptyline(self):
|
||||
# Test file includes an empty line in the header data
|
||||
emptyline_file = "Tests/images/zero_bb_emptyline.eps"
|
||||
|
||||
with Image.open(emptyline_file) as image:
|
||||
image.load()
|
||||
assert image.mode == "RGB"
|
||||
assert image.size == (460, 352)
|
||||
assert image.format == "EPS"
|
||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||
def test_emptyline():
|
||||
# Test file includes an empty line in the header data
|
||||
emptyline_file = "Tests/images/zero_bb_emptyline.eps"
|
||||
|
||||
with Image.open(emptyline_file) as image:
|
||||
image.load()
|
||||
assert image.mode == "RGB"
|
||||
assert image.size == (460, 352)
|
||||
assert image.format == "EPS"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,122 +1,127 @@
|
|||
import io
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
import pytest
|
||||
from PIL import IcnsImagePlugin, Image
|
||||
|
||||
from .helper import PillowTestCase, assert_image_equal, assert_image_similar
|
||||
from .helper import assert_image_equal, assert_image_similar
|
||||
|
||||
# sample icon file
|
||||
TEST_FILE = "Tests/images/pillow.icns"
|
||||
|
||||
enable_jpeg2k = hasattr(Image.core, "jp2klib_version")
|
||||
ENABLE_JPEG2K = hasattr(Image.core, "jp2klib_version")
|
||||
|
||||
|
||||
class TestFileIcns(PillowTestCase):
|
||||
def test_sanity(self):
|
||||
# Loading this icon by default should result in the largest size
|
||||
# (512x512@2x) being loaded
|
||||
with Image.open(TEST_FILE) as im:
|
||||
def test_sanity():
|
||||
# Loading this icon by default should result in the largest size
|
||||
# (512x512@2x) being loaded
|
||||
with Image.open(TEST_FILE) as im:
|
||||
|
||||
# Assert that there is no unclosed file warning
|
||||
pytest.warns(None, im.load)
|
||||
# Assert that there is no unclosed file warning
|
||||
pytest.warns(None, im.load)
|
||||
|
||||
assert im.mode == "RGBA"
|
||||
assert im.size == (1024, 1024)
|
||||
assert im.format == "ICNS"
|
||||
assert im.mode == "RGBA"
|
||||
assert im.size == (1024, 1024)
|
||||
assert im.format == "ICNS"
|
||||
|
||||
@unittest.skipIf(sys.platform != "darwin", "requires macOS")
|
||||
def test_save(self):
|
||||
temp_file = self.tempfile("temp.icns")
|
||||
|
||||
with Image.open(TEST_FILE) as im:
|
||||
im.save(temp_file)
|
||||
@pytest.mark.skipif(sys.platform != "darwin", reason="Requires macOS")
|
||||
def test_save(tmp_path):
|
||||
temp_file = str(tmp_path / "temp.icns")
|
||||
|
||||
with Image.open(TEST_FILE) as im:
|
||||
im.save(temp_file)
|
||||
|
||||
with Image.open(temp_file) as reread:
|
||||
assert reread.mode == "RGBA"
|
||||
assert reread.size == (1024, 1024)
|
||||
assert reread.format == "ICNS"
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.platform != "darwin", reason="Requires macOS")
|
||||
def test_save_append_images(tmp_path):
|
||||
temp_file = str(tmp_path / "temp.icns")
|
||||
provided_im = Image.new("RGBA", (32, 32), (255, 0, 0, 128))
|
||||
|
||||
with Image.open(TEST_FILE) as im:
|
||||
im.save(temp_file, append_images=[provided_im])
|
||||
|
||||
with Image.open(temp_file) as reread:
|
||||
assert reread.mode == "RGBA"
|
||||
assert reread.size == (1024, 1024)
|
||||
assert reread.format == "ICNS"
|
||||
assert_image_similar(reread, im, 1)
|
||||
|
||||
@unittest.skipIf(sys.platform != "darwin", "requires macOS")
|
||||
def test_save_append_images(self):
|
||||
temp_file = self.tempfile("temp.icns")
|
||||
provided_im = Image.new("RGBA", (32, 32), (255, 0, 0, 128))
|
||||
with Image.open(temp_file) as reread:
|
||||
reread.size = (16, 16, 2)
|
||||
reread.load()
|
||||
assert_image_equal(reread, provided_im)
|
||||
|
||||
with Image.open(TEST_FILE) as im:
|
||||
im.save(temp_file, append_images=[provided_im])
|
||||
|
||||
with Image.open(temp_file) as reread:
|
||||
assert_image_similar(reread, im, 1)
|
||||
|
||||
with Image.open(temp_file) as reread:
|
||||
reread.size = (16, 16, 2)
|
||||
reread.load()
|
||||
assert_image_equal(reread, provided_im)
|
||||
|
||||
def test_sizes(self):
|
||||
# Check that we can load all of the sizes, and that the final pixel
|
||||
# dimensions are as expected
|
||||
with Image.open(TEST_FILE) as im:
|
||||
for w, h, r in im.info["sizes"]:
|
||||
wr = w * r
|
||||
hr = h * r
|
||||
im.size = (w, h, r)
|
||||
im.load()
|
||||
assert im.mode == "RGBA"
|
||||
assert im.size == (wr, hr)
|
||||
|
||||
# Check that we cannot load an incorrect size
|
||||
with pytest.raises(ValueError):
|
||||
im.size = (1, 1)
|
||||
|
||||
def test_older_icon(self):
|
||||
# This icon was made with Icon Composer rather than iconutil; it still
|
||||
# uses PNG rather than JP2, however (since it was made on 10.9).
|
||||
with Image.open("Tests/images/pillow2.icns") as im:
|
||||
for w, h, r in im.info["sizes"]:
|
||||
wr = w * r
|
||||
hr = h * r
|
||||
with Image.open("Tests/images/pillow2.icns") as im2:
|
||||
im2.size = (w, h, r)
|
||||
im2.load()
|
||||
assert im2.mode == "RGBA"
|
||||
assert im2.size == (wr, hr)
|
||||
|
||||
def test_jp2_icon(self):
|
||||
# This icon was made by using Uli Kusterer's oldiconutil to replace
|
||||
# the PNG images with JPEG 2000 ones. The advantage of doing this is
|
||||
# that OS X 10.5 supports JPEG 2000 but not PNG; some commercial
|
||||
# software therefore does just this.
|
||||
|
||||
# (oldiconutil is here: https://github.com/uliwitness/oldiconutil)
|
||||
|
||||
if not enable_jpeg2k:
|
||||
return
|
||||
|
||||
with Image.open("Tests/images/pillow3.icns") as im:
|
||||
for w, h, r in im.info["sizes"]:
|
||||
wr = w * r
|
||||
hr = h * r
|
||||
with Image.open("Tests/images/pillow3.icns") as im2:
|
||||
im2.size = (w, h, r)
|
||||
im2.load()
|
||||
assert im2.mode == "RGBA"
|
||||
assert im2.size == (wr, hr)
|
||||
|
||||
def test_getimage(self):
|
||||
with open(TEST_FILE, "rb") as fp:
|
||||
icns_file = IcnsImagePlugin.IcnsFile(fp)
|
||||
|
||||
im = icns_file.getimage()
|
||||
def test_sizes():
|
||||
# Check that we can load all of the sizes, and that the final pixel
|
||||
# dimensions are as expected
|
||||
with Image.open(TEST_FILE) as im:
|
||||
for w, h, r in im.info["sizes"]:
|
||||
wr = w * r
|
||||
hr = h * r
|
||||
im.size = (w, h, r)
|
||||
im.load()
|
||||
assert im.mode == "RGBA"
|
||||
assert im.size == (1024, 1024)
|
||||
assert im.size == (wr, hr)
|
||||
|
||||
im = icns_file.getimage((512, 512))
|
||||
assert im.mode == "RGBA"
|
||||
assert im.size == (512, 512)
|
||||
# Check that we cannot load an incorrect size
|
||||
with pytest.raises(ValueError):
|
||||
im.size = (1, 1)
|
||||
|
||||
def test_not_an_icns_file(self):
|
||||
with io.BytesIO(b"invalid\n") as fp:
|
||||
with pytest.raises(SyntaxError):
|
||||
IcnsImagePlugin.IcnsFile(fp)
|
||||
|
||||
def test_older_icon():
|
||||
# This icon was made with Icon Composer rather than iconutil; it still
|
||||
# uses PNG rather than JP2, however (since it was made on 10.9).
|
||||
with Image.open("Tests/images/pillow2.icns") as im:
|
||||
for w, h, r in im.info["sizes"]:
|
||||
wr = w * r
|
||||
hr = h * r
|
||||
with Image.open("Tests/images/pillow2.icns") as im2:
|
||||
im2.size = (w, h, r)
|
||||
im2.load()
|
||||
assert im2.mode == "RGBA"
|
||||
assert im2.size == (wr, hr)
|
||||
|
||||
|
||||
def test_jp2_icon():
|
||||
# This icon was made by using Uli Kusterer's oldiconutil to replace
|
||||
# the PNG images with JPEG 2000 ones. The advantage of doing this is
|
||||
# that OS X 10.5 supports JPEG 2000 but not PNG; some commercial
|
||||
# software therefore does just this.
|
||||
|
||||
# (oldiconutil is here: https://github.com/uliwitness/oldiconutil)
|
||||
|
||||
if not ENABLE_JPEG2K:
|
||||
return
|
||||
|
||||
with Image.open("Tests/images/pillow3.icns") as im:
|
||||
for w, h, r in im.info["sizes"]:
|
||||
wr = w * r
|
||||
hr = h * r
|
||||
with Image.open("Tests/images/pillow3.icns") as im2:
|
||||
im2.size = (w, h, r)
|
||||
im2.load()
|
||||
assert im2.mode == "RGBA"
|
||||
assert im2.size == (wr, hr)
|
||||
|
||||
|
||||
def test_getimage():
|
||||
with open(TEST_FILE, "rb") as fp:
|
||||
icns_file = IcnsImagePlugin.IcnsFile(fp)
|
||||
|
||||
im = icns_file.getimage()
|
||||
assert im.mode == "RGBA"
|
||||
assert im.size == (1024, 1024)
|
||||
|
||||
im = icns_file.getimage((512, 512))
|
||||
assert im.mode == "RGBA"
|
||||
assert im.size == (512, 512)
|
||||
|
||||
|
||||
def test_not_an_icns_file():
|
||||
with io.BytesIO(b"invalid\n") as fp:
|
||||
with pytest.raises(SyntaxError):
|
||||
IcnsImagePlugin.IcnsFile(fp)
|
||||
|
|
|
@ -3,101 +3,107 @@ import io
|
|||
import pytest
|
||||
from PIL import IcoImagePlugin, Image, ImageDraw
|
||||
|
||||
from .helper import PillowTestCase, assert_image_equal, hopper
|
||||
from .helper import assert_image_equal, hopper
|
||||
|
||||
TEST_ICO_FILE = "Tests/images/hopper.ico"
|
||||
|
||||
|
||||
class TestFileIco(PillowTestCase):
|
||||
def test_sanity(self):
|
||||
with Image.open(TEST_ICO_FILE) as im:
|
||||
im.load()
|
||||
assert im.mode == "RGBA"
|
||||
assert im.size == (16, 16)
|
||||
assert im.format == "ICO"
|
||||
assert im.get_format_mimetype() == "image/x-icon"
|
||||
def test_sanity():
|
||||
with Image.open(TEST_ICO_FILE) as im:
|
||||
im.load()
|
||||
assert im.mode == "RGBA"
|
||||
assert im.size == (16, 16)
|
||||
assert im.format == "ICO"
|
||||
assert im.get_format_mimetype() == "image/x-icon"
|
||||
|
||||
def test_invalid_file(self):
|
||||
with open("Tests/images/flower.jpg", "rb") as fp:
|
||||
with pytest.raises(SyntaxError):
|
||||
IcoImagePlugin.IcoImageFile(fp)
|
||||
|
||||
def test_save_to_bytes(self):
|
||||
output = io.BytesIO()
|
||||
im = hopper()
|
||||
im.save(output, "ico", sizes=[(32, 32), (64, 64)])
|
||||
def test_invalid_file():
|
||||
with open("Tests/images/flower.jpg", "rb") as fp:
|
||||
with pytest.raises(SyntaxError):
|
||||
IcoImagePlugin.IcoImageFile(fp)
|
||||
|
||||
# the default image
|
||||
output.seek(0)
|
||||
with Image.open(output) as reloaded:
|
||||
assert reloaded.info["sizes"] == {(32, 32), (64, 64)}
|
||||
|
||||
assert im.mode == reloaded.mode
|
||||
assert (64, 64) == reloaded.size
|
||||
assert reloaded.format == "ICO"
|
||||
assert_image_equal(reloaded, hopper().resize((64, 64), Image.LANCZOS))
|
||||
def test_save_to_bytes():
|
||||
output = io.BytesIO()
|
||||
im = hopper()
|
||||
im.save(output, "ico", sizes=[(32, 32), (64, 64)])
|
||||
|
||||
# the other one
|
||||
output.seek(0)
|
||||
with Image.open(output) as reloaded:
|
||||
reloaded.size = (32, 32)
|
||||
# The default image
|
||||
output.seek(0)
|
||||
with Image.open(output) as reloaded:
|
||||
assert reloaded.info["sizes"] == {(32, 32), (64, 64)}
|
||||
|
||||
assert im.mode == reloaded.mode
|
||||
assert (32, 32) == reloaded.size
|
||||
assert reloaded.format == "ICO"
|
||||
assert_image_equal(reloaded, hopper().resize((32, 32), Image.LANCZOS))
|
||||
assert im.mode == reloaded.mode
|
||||
assert (64, 64) == reloaded.size
|
||||
assert reloaded.format == "ICO"
|
||||
assert_image_equal(reloaded, hopper().resize((64, 64), Image.LANCZOS))
|
||||
|
||||
def test_incorrect_size(self):
|
||||
with Image.open(TEST_ICO_FILE) as im:
|
||||
with pytest.raises(ValueError):
|
||||
im.size = (1, 1)
|
||||
# The other one
|
||||
output.seek(0)
|
||||
with Image.open(output) as reloaded:
|
||||
reloaded.size = (32, 32)
|
||||
|
||||
def test_save_256x256(self):
|
||||
"""Issue #2264 https://github.com/python-pillow/Pillow/issues/2264"""
|
||||
# Arrange
|
||||
with Image.open("Tests/images/hopper_256x256.ico") as im:
|
||||
outfile = self.tempfile("temp_saved_hopper_256x256.ico")
|
||||
assert im.mode == reloaded.mode
|
||||
assert (32, 32) == reloaded.size
|
||||
assert reloaded.format == "ICO"
|
||||
assert_image_equal(reloaded, hopper().resize((32, 32), Image.LANCZOS))
|
||||
|
||||
# Act
|
||||
im.save(outfile)
|
||||
with Image.open(outfile) as im_saved:
|
||||
|
||||
# Assert
|
||||
assert im_saved.size == (256, 256)
|
||||
def test_incorrect_size():
|
||||
with Image.open(TEST_ICO_FILE) as im:
|
||||
with pytest.raises(ValueError):
|
||||
im.size = (1, 1)
|
||||
|
||||
def test_only_save_relevant_sizes(self):
|
||||
"""Issue #2266 https://github.com/python-pillow/Pillow/issues/2266
|
||||
Should save in 16x16, 24x24, 32x32, 48x48 sizes
|
||||
and not in 16x16, 24x24, 32x32, 48x48, 48x48, 48x48, 48x48 sizes
|
||||
"""
|
||||
# Arrange
|
||||
with Image.open("Tests/images/python.ico") as im: # 16x16, 32x32, 48x48
|
||||
outfile = self.tempfile("temp_saved_python.ico")
|
||||
# Act
|
||||
im.save(outfile)
|
||||
|
||||
with Image.open(outfile) as im_saved:
|
||||
# Assert
|
||||
assert im_saved.info["sizes"] == {(16, 16), (24, 24), (32, 32), (48, 48)}
|
||||
def test_save_256x256(tmp_path):
|
||||
"""Issue #2264 https://github.com/python-pillow/Pillow/issues/2264"""
|
||||
# Arrange
|
||||
with Image.open("Tests/images/hopper_256x256.ico") as im:
|
||||
outfile = str(tmp_path / "temp_saved_hopper_256x256.ico")
|
||||
|
||||
def test_unexpected_size(self):
|
||||
# This image has been manually hexedited to state that it is 16x32
|
||||
# while the image within is still 16x16
|
||||
def open():
|
||||
with Image.open("Tests/images/hopper_unexpected.ico") as im:
|
||||
assert im.size == (16, 16)
|
||||
# Act
|
||||
im.save(outfile)
|
||||
with Image.open(outfile) as im_saved:
|
||||
|
||||
pytest.warns(UserWarning, open)
|
||||
# Assert
|
||||
assert im_saved.size == (256, 256)
|
||||
|
||||
def test_draw_reloaded(self):
|
||||
with Image.open(TEST_ICO_FILE) as im:
|
||||
outfile = self.tempfile("temp_saved_hopper_draw.ico")
|
||||
|
||||
draw = ImageDraw.Draw(im)
|
||||
draw.line((0, 0) + im.size, "#f00")
|
||||
im.save(outfile)
|
||||
def test_only_save_relevant_sizes(tmp_path):
|
||||
"""Issue #2266 https://github.com/python-pillow/Pillow/issues/2266
|
||||
Should save in 16x16, 24x24, 32x32, 48x48 sizes
|
||||
and not in 16x16, 24x24, 32x32, 48x48, 48x48, 48x48, 48x48 sizes
|
||||
"""
|
||||
# Arrange
|
||||
with Image.open("Tests/images/python.ico") as im: # 16x16, 32x32, 48x48
|
||||
outfile = str(tmp_path / "temp_saved_python.ico")
|
||||
# Act
|
||||
im.save(outfile)
|
||||
|
||||
with Image.open(outfile) as im:
|
||||
im.save("Tests/images/hopper_draw.ico")
|
||||
with Image.open("Tests/images/hopper_draw.ico") as reloaded:
|
||||
assert_image_equal(im, reloaded)
|
||||
with Image.open(outfile) as im_saved:
|
||||
# Assert
|
||||
assert im_saved.info["sizes"] == {(16, 16), (24, 24), (32, 32), (48, 48)}
|
||||
|
||||
|
||||
def test_unexpected_size():
|
||||
# This image has been manually hexedited to state that it is 16x32
|
||||
# while the image within is still 16x16
|
||||
def open():
|
||||
with Image.open("Tests/images/hopper_unexpected.ico") as im:
|
||||
assert im.size == (16, 16)
|
||||
|
||||
pytest.warns(UserWarning, open)
|
||||
|
||||
|
||||
def test_draw_reloaded(tmp_path):
|
||||
with Image.open(TEST_ICO_FILE) as im:
|
||||
outfile = str(tmp_path / "temp_saved_hopper_draw.ico")
|
||||
|
||||
draw = ImageDraw.Draw(im)
|
||||
draw.line((0, 0) + im.size, "#f00")
|
||||
im.save(outfile)
|
||||
|
||||
with Image.open(outfile) as im:
|
||||
im.save("Tests/images/hopper_draw.ico")
|
||||
with Image.open("Tests/images/hopper_draw.ico") as reloaded:
|
||||
assert_image_equal(im, reloaded)
|
||||
|
|
|
@ -1,83 +1,90 @@
|
|||
import os
|
||||
import unittest
|
||||
|
||||
import pytest
|
||||
from PIL import Image, MspImagePlugin
|
||||
|
||||
from .helper import PillowTestCase, assert_image_equal, hopper
|
||||
from .helper import assert_image_equal, hopper
|
||||
|
||||
TEST_FILE = "Tests/images/hopper.msp"
|
||||
EXTRA_DIR = "Tests/images/picins"
|
||||
YA_EXTRA_DIR = "Tests/images/msp"
|
||||
|
||||
|
||||
class TestFileMsp(PillowTestCase):
|
||||
def test_sanity(self):
|
||||
test_file = self.tempfile("temp.msp")
|
||||
def test_sanity(tmp_path):
|
||||
test_file = str(tmp_path / "temp.msp")
|
||||
|
||||
hopper("1").save(test_file)
|
||||
hopper("1").save(test_file)
|
||||
|
||||
with Image.open(test_file) as im:
|
||||
im.load()
|
||||
assert im.mode == "1"
|
||||
assert im.size == (128, 128)
|
||||
assert im.format == "MSP"
|
||||
with Image.open(test_file) as im:
|
||||
im.load()
|
||||
assert im.mode == "1"
|
||||
assert im.size == (128, 128)
|
||||
assert im.format == "MSP"
|
||||
|
||||
def test_invalid_file(self):
|
||||
invalid_file = "Tests/images/flower.jpg"
|
||||
|
||||
with pytest.raises(SyntaxError):
|
||||
MspImagePlugin.MspImageFile(invalid_file)
|
||||
def test_invalid_file():
|
||||
invalid_file = "Tests/images/flower.jpg"
|
||||
|
||||
def test_bad_checksum(self):
|
||||
# Arrange
|
||||
# This was created by forcing Pillow to save with checksum=0
|
||||
bad_checksum = "Tests/images/hopper_bad_checksum.msp"
|
||||
with pytest.raises(SyntaxError):
|
||||
MspImagePlugin.MspImageFile(invalid_file)
|
||||
|
||||
# Act / Assert
|
||||
with pytest.raises(SyntaxError):
|
||||
MspImagePlugin.MspImageFile(bad_checksum)
|
||||
|
||||
def test_open_windows_v1(self):
|
||||
# Arrange
|
||||
# Act
|
||||
with Image.open(TEST_FILE) as im:
|
||||
def test_bad_checksum():
|
||||
# Arrange
|
||||
# This was created by forcing Pillow to save with checksum=0
|
||||
bad_checksum = "Tests/images/hopper_bad_checksum.msp"
|
||||
|
||||
# Assert
|
||||
assert_image_equal(im, hopper("1"))
|
||||
assert isinstance(im, MspImagePlugin.MspImageFile)
|
||||
# Act / Assert
|
||||
with pytest.raises(SyntaxError):
|
||||
MspImagePlugin.MspImageFile(bad_checksum)
|
||||
|
||||
def _assert_file_image_equal(self, source_path, target_path):
|
||||
with Image.open(source_path) as im:
|
||||
with Image.open(target_path) as target:
|
||||
assert_image_equal(im, target)
|
||||
|
||||
@unittest.skipUnless(os.path.exists(EXTRA_DIR), "Extra image files not installed")
|
||||
def test_open_windows_v2(self):
|
||||
def test_open_windows_v1():
|
||||
# Arrange
|
||||
# Act
|
||||
with Image.open(TEST_FILE) as im:
|
||||
|
||||
files = (
|
||||
os.path.join(EXTRA_DIR, f)
|
||||
for f in os.listdir(EXTRA_DIR)
|
||||
if os.path.splitext(f)[1] == ".msp"
|
||||
)
|
||||
for path in files:
|
||||
self._assert_file_image_equal(path, path.replace(".msp", ".png"))
|
||||
# Assert
|
||||
assert_image_equal(im, hopper("1"))
|
||||
assert isinstance(im, MspImagePlugin.MspImageFile)
|
||||
|
||||
@unittest.skipIf(
|
||||
not os.path.exists(YA_EXTRA_DIR), "Even More Extra image files not installed"
|
||||
|
||||
def _assert_file_image_equal(source_path, target_path):
|
||||
with Image.open(source_path) as im:
|
||||
with Image.open(target_path) as target:
|
||||
assert_image_equal(im, target)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.path.exists(EXTRA_DIR), reason="Extra image files not installed"
|
||||
)
|
||||
def test_open_windows_v2():
|
||||
|
||||
files = (
|
||||
os.path.join(EXTRA_DIR, f)
|
||||
for f in os.listdir(EXTRA_DIR)
|
||||
if os.path.splitext(f)[1] == ".msp"
|
||||
)
|
||||
def test_msp_v2(self):
|
||||
for f in os.listdir(YA_EXTRA_DIR):
|
||||
if ".MSP" not in f:
|
||||
continue
|
||||
path = os.path.join(YA_EXTRA_DIR, f)
|
||||
self._assert_file_image_equal(path, path.replace(".MSP", ".png"))
|
||||
for path in files:
|
||||
_assert_file_image_equal(path, path.replace(".msp", ".png"))
|
||||
|
||||
def test_cannot_save_wrong_mode(self):
|
||||
# Arrange
|
||||
im = hopper()
|
||||
filename = self.tempfile("temp.msp")
|
||||
|
||||
# Act/Assert
|
||||
with pytest.raises(IOError):
|
||||
im.save(filename)
|
||||
@pytest.mark.skipif(
|
||||
not os.path.exists(YA_EXTRA_DIR), reason="Even More Extra image files not installed"
|
||||
)
|
||||
def test_msp_v2():
|
||||
for f in os.listdir(YA_EXTRA_DIR):
|
||||
if ".MSP" not in f:
|
||||
continue
|
||||
path = os.path.join(YA_EXTRA_DIR, f)
|
||||
_assert_file_image_equal(path, path.replace(".MSP", ".png"))
|
||||
|
||||
|
||||
def test_cannot_save_wrong_mode(tmp_path):
|
||||
# Arrange
|
||||
im = hopper()
|
||||
filename = str(tmp_path / "temp.msp")
|
||||
|
||||
# Act/Assert
|
||||
with pytest.raises(IOError):
|
||||
im.save(filename)
|
||||
|
|
|
@ -1,149 +1,162 @@
|
|||
import tempfile
|
||||
import unittest
|
||||
from io import BytesIO
|
||||
|
||||
import pytest
|
||||
from PIL import Image, ImageSequence, SpiderImagePlugin
|
||||
|
||||
from .helper import PillowTestCase, assert_image_equal, hopper, is_pypy
|
||||
from .helper import assert_image_equal, hopper, is_pypy
|
||||
|
||||
TEST_FILE = "Tests/images/hopper.spider"
|
||||
|
||||
|
||||
class TestImageSpider(PillowTestCase):
|
||||
def test_sanity(self):
|
||||
def test_sanity():
|
||||
with Image.open(TEST_FILE) as im:
|
||||
im.load()
|
||||
assert im.mode == "F"
|
||||
assert im.size == (128, 128)
|
||||
assert im.format == "SPIDER"
|
||||
|
||||
|
||||
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
||||
def test_unclosed_file():
|
||||
def open():
|
||||
im = Image.open(TEST_FILE)
|
||||
im.load()
|
||||
|
||||
pytest.warns(ResourceWarning, open)
|
||||
|
||||
|
||||
def test_closed_file():
|
||||
def open():
|
||||
im = Image.open(TEST_FILE)
|
||||
im.load()
|
||||
im.close()
|
||||
|
||||
pytest.warns(None, open)
|
||||
|
||||
|
||||
def test_context_manager():
|
||||
def open():
|
||||
with Image.open(TEST_FILE) as im:
|
||||
im.load()
|
||||
assert im.mode == "F"
|
||||
assert im.size == (128, 128)
|
||||
assert im.format == "SPIDER"
|
||||
|
||||
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||
def test_unclosed_file(self):
|
||||
def open():
|
||||
im = Image.open(TEST_FILE)
|
||||
im.load()
|
||||
pytest.warns(None, open)
|
||||
|
||||
pytest.warns(ResourceWarning, open)
|
||||
|
||||
def test_closed_file(self):
|
||||
def open():
|
||||
im = Image.open(TEST_FILE)
|
||||
im.load()
|
||||
im.close()
|
||||
def test_save(tmp_path):
|
||||
# Arrange
|
||||
temp = str(tmp_path / "temp.spider")
|
||||
im = hopper()
|
||||
|
||||
pytest.warns(None, open)
|
||||
# Act
|
||||
im.save(temp, "SPIDER")
|
||||
|
||||
def test_context_manager(self):
|
||||
def open():
|
||||
with Image.open(TEST_FILE) as im:
|
||||
im.load()
|
||||
# Assert
|
||||
with Image.open(temp) as im2:
|
||||
assert im2.mode == "F"
|
||||
assert im2.size == (128, 128)
|
||||
assert im2.format == "SPIDER"
|
||||
|
||||
pytest.warns(None, open)
|
||||
|
||||
def test_save(self):
|
||||
# Arrange
|
||||
temp = self.tempfile("temp.spider")
|
||||
im = hopper()
|
||||
def test_tempfile():
|
||||
# Arrange
|
||||
im = hopper()
|
||||
|
||||
# Act
|
||||
im.save(temp, "SPIDER")
|
||||
# Act
|
||||
with tempfile.TemporaryFile() as fp:
|
||||
im.save(fp, "SPIDER")
|
||||
|
||||
# Assert
|
||||
with Image.open(temp) as im2:
|
||||
assert im2.mode == "F"
|
||||
assert im2.size == (128, 128)
|
||||
assert im2.format == "SPIDER"
|
||||
fp.seek(0)
|
||||
with Image.open(fp) as reloaded:
|
||||
assert reloaded.mode == "F"
|
||||
assert reloaded.size == (128, 128)
|
||||
assert reloaded.format == "SPIDER"
|
||||
|
||||
def test_tempfile(self):
|
||||
# Arrange
|
||||
im = hopper()
|
||||
|
||||
def test_is_spider_image():
|
||||
assert SpiderImagePlugin.isSpiderImage(TEST_FILE)
|
||||
|
||||
|
||||
def test_tell():
|
||||
# Arrange
|
||||
with Image.open(TEST_FILE) as im:
|
||||
|
||||
# Act
|
||||
with tempfile.TemporaryFile() as fp:
|
||||
im.save(fp, "SPIDER")
|
||||
|
||||
# Assert
|
||||
fp.seek(0)
|
||||
with Image.open(fp) as reloaded:
|
||||
assert reloaded.mode == "F"
|
||||
assert reloaded.size == (128, 128)
|
||||
assert reloaded.format == "SPIDER"
|
||||
|
||||
def test_isSpiderImage(self):
|
||||
assert SpiderImagePlugin.isSpiderImage(TEST_FILE)
|
||||
|
||||
def test_tell(self):
|
||||
# Arrange
|
||||
with Image.open(TEST_FILE) as im:
|
||||
|
||||
# Act
|
||||
index = im.tell()
|
||||
|
||||
# Assert
|
||||
assert index == 0
|
||||
|
||||
def test_n_frames(self):
|
||||
with Image.open(TEST_FILE) as im:
|
||||
assert im.n_frames == 1
|
||||
assert not im.is_animated
|
||||
|
||||
def test_loadImageSeries(self):
|
||||
# Arrange
|
||||
not_spider_file = "Tests/images/hopper.ppm"
|
||||
file_list = [TEST_FILE, not_spider_file, "path/not_found.ext"]
|
||||
|
||||
# Act
|
||||
img_list = SpiderImagePlugin.loadImageSeries(file_list)
|
||||
index = im.tell()
|
||||
|
||||
# Assert
|
||||
assert len(img_list) == 1
|
||||
assert isinstance(img_list[0], Image.Image)
|
||||
assert img_list[0].size == (128, 128)
|
||||
assert index == 0
|
||||
|
||||
def test_loadImageSeries_no_input(self):
|
||||
# Arrange
|
||||
file_list = None
|
||||
|
||||
# Act
|
||||
img_list = SpiderImagePlugin.loadImageSeries(file_list)
|
||||
def test_n_frames():
|
||||
with Image.open(TEST_FILE) as im:
|
||||
assert im.n_frames == 1
|
||||
assert not im.is_animated
|
||||
|
||||
# Assert
|
||||
assert img_list is None
|
||||
|
||||
def test_isInt_not_a_number(self):
|
||||
# Arrange
|
||||
not_a_number = "a"
|
||||
def test_load_image_series():
|
||||
# Arrange
|
||||
not_spider_file = "Tests/images/hopper.ppm"
|
||||
file_list = [TEST_FILE, not_spider_file, "path/not_found.ext"]
|
||||
|
||||
# Act
|
||||
ret = SpiderImagePlugin.isInt(not_a_number)
|
||||
# Act
|
||||
img_list = SpiderImagePlugin.loadImageSeries(file_list)
|
||||
|
||||
# Assert
|
||||
assert ret == 0
|
||||
# Assert
|
||||
assert len(img_list) == 1
|
||||
assert isinstance(img_list[0], Image.Image)
|
||||
assert img_list[0].size == (128, 128)
|
||||
|
||||
def test_invalid_file(self):
|
||||
invalid_file = "Tests/images/invalid.spider"
|
||||
|
||||
with pytest.raises(IOError):
|
||||
Image.open(invalid_file)
|
||||
def test_load_image_series_no_input():
|
||||
# Arrange
|
||||
file_list = None
|
||||
|
||||
def test_nonstack_file(self):
|
||||
with Image.open(TEST_FILE) as im:
|
||||
with pytest.raises(EOFError):
|
||||
im.seek(0)
|
||||
# Act
|
||||
img_list = SpiderImagePlugin.loadImageSeries(file_list)
|
||||
|
||||
def test_nonstack_dos(self):
|
||||
with Image.open(TEST_FILE) as im:
|
||||
for i, frame in enumerate(ImageSequence.Iterator(im)):
|
||||
assert i <= 1, "Non-stack DOS file test failed"
|
||||
# Assert
|
||||
assert img_list is None
|
||||
|
||||
# for issue #4093
|
||||
def test_odd_size(self):
|
||||
data = BytesIO()
|
||||
width = 100
|
||||
im = Image.new("F", (width, 64))
|
||||
im.save(data, format="SPIDER")
|
||||
|
||||
data.seek(0)
|
||||
with Image.open(data) as im2:
|
||||
assert_image_equal(im, im2)
|
||||
def test_is_int_not_a_number():
|
||||
# Arrange
|
||||
not_a_number = "a"
|
||||
|
||||
# Act
|
||||
ret = SpiderImagePlugin.isInt(not_a_number)
|
||||
|
||||
# Assert
|
||||
assert ret == 0
|
||||
|
||||
|
||||
def test_invalid_file():
|
||||
invalid_file = "Tests/images/invalid.spider"
|
||||
|
||||
with pytest.raises(IOError):
|
||||
Image.open(invalid_file)
|
||||
|
||||
|
||||
def test_nonstack_file():
|
||||
with Image.open(TEST_FILE) as im:
|
||||
with pytest.raises(EOFError):
|
||||
im.seek(0)
|
||||
|
||||
|
||||
def test_nonstack_dos():
|
||||
with Image.open(TEST_FILE) as im:
|
||||
for i, frame in enumerate(ImageSequence.Iterator(im)):
|
||||
assert i <= 1, "Non-stack DOS file test failed"
|
||||
|
||||
|
||||
# for issue #4093
|
||||
def test_odd_size():
|
||||
data = BytesIO()
|
||||
width = 100
|
||||
im = Image.new("F", (width, 64))
|
||||
im.save(data, format="SPIDER")
|
||||
|
||||
data.seek(0)
|
||||
with Image.open(data) as im2:
|
||||
assert_image_equal(im, im2)
|
||||
|
|
|
@ -2,13 +2,11 @@ import io
|
|||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import pytest
|
||||
from PIL import Image, ImageDraw, ImagePalette, UnidentifiedImageError
|
||||
|
||||
from .helper import (
|
||||
PillowTestCase,
|
||||
assert_image_equal,
|
||||
assert_image_similar,
|
||||
assert_not_all_same,
|
||||
|
@ -17,7 +15,7 @@ from .helper import (
|
|||
)
|
||||
|
||||
|
||||
class TestImage(PillowTestCase):
|
||||
class TestImage:
|
||||
def test_image_modes_success(self):
|
||||
for mode in [
|
||||
"1",
|
||||
|
@ -109,7 +107,7 @@ class TestImage(PillowTestCase):
|
|||
with pytest.raises(ValueError):
|
||||
Image.open(io.StringIO())
|
||||
|
||||
def test_pathlib(self):
|
||||
def test_pathlib(self, tmp_path):
|
||||
from PIL.Image import Path
|
||||
|
||||
with Image.open(Path("Tests/images/multipage-mmap.tiff")) as im:
|
||||
|
@ -120,13 +118,13 @@ class TestImage(PillowTestCase):
|
|||
assert im.mode == "RGB"
|
||||
assert im.size == (128, 128)
|
||||
|
||||
temp_file = self.tempfile("temp.jpg")
|
||||
temp_file = str(tmp_path / "temp.jpg")
|
||||
if os.path.exists(temp_file):
|
||||
os.remove(temp_file)
|
||||
im.save(Path(temp_file))
|
||||
|
||||
def test_fp_name(self):
|
||||
temp_file = self.tempfile("temp.jpg")
|
||||
def test_fp_name(self, tmp_path):
|
||||
temp_file = str(tmp_path / "temp.jpg")
|
||||
|
||||
class FP:
|
||||
def write(a, b):
|
||||
|
@ -148,9 +146,9 @@ class TestImage(PillowTestCase):
|
|||
with Image.open(fp) as reloaded:
|
||||
assert_image_similar(im, reloaded, 20)
|
||||
|
||||
def test_unknown_extension(self):
|
||||
def test_unknown_extension(self, tmp_path):
|
||||
im = hopper()
|
||||
temp_file = self.tempfile("temp.unknown")
|
||||
temp_file = str(tmp_path / "temp.unknown")
|
||||
with pytest.raises(ValueError):
|
||||
im.save(temp_file)
|
||||
|
||||
|
@ -164,25 +162,25 @@ class TestImage(PillowTestCase):
|
|||
im.paste(0, (0, 0, 100, 100))
|
||||
assert not im.readonly
|
||||
|
||||
@unittest.skipIf(is_win32(), "Test requires opening tempfile twice")
|
||||
def test_readonly_save(self):
|
||||
temp_file = self.tempfile("temp.bmp")
|
||||
@pytest.mark.skipif(is_win32(), reason="Test requires opening tempfile twice")
|
||||
def test_readonly_save(self, tmp_path):
|
||||
temp_file = str(tmp_path / "temp.bmp")
|
||||
shutil.copy("Tests/images/rgb32bf-rgba.bmp", temp_file)
|
||||
|
||||
with Image.open(temp_file) as im:
|
||||
assert im.readonly
|
||||
im.save(temp_file)
|
||||
|
||||
def test_dump(self):
|
||||
def test_dump(self, tmp_path):
|
||||
im = Image.new("L", (10, 10))
|
||||
im._dump(self.tempfile("temp_L.ppm"))
|
||||
im._dump(str(tmp_path / "temp_L.ppm"))
|
||||
|
||||
im = Image.new("RGB", (10, 10))
|
||||
im._dump(self.tempfile("temp_RGB.ppm"))
|
||||
im._dump(str(tmp_path / "temp_RGB.ppm"))
|
||||
|
||||
im = Image.new("HSV", (10, 10))
|
||||
with pytest.raises(ValueError):
|
||||
im._dump(self.tempfile("temp_HSV.ppm"))
|
||||
im._dump(str(tmp_path / "temp_HSV.ppm"))
|
||||
|
||||
def test_comparison_with_other_type(self):
|
||||
# Arrange
|
||||
|
@ -434,8 +432,7 @@ class TestImage(PillowTestCase):
|
|||
assert_image_similar(im2, im3, 110)
|
||||
|
||||
def test_check_size(self):
|
||||
# Checking that the _check_size function throws value errors
|
||||
# when we want it to.
|
||||
# Checking that the _check_size function throws value errors when we want it to
|
||||
with pytest.raises(ValueError):
|
||||
Image.new("RGB", 0) # not a tuple
|
||||
with pytest.raises(ValueError):
|
||||
|
@ -587,11 +584,11 @@ class TestImage(PillowTestCase):
|
|||
expected = Image.new(mode, (100, 100), color)
|
||||
assert_image_equal(im.convert(mode), expected)
|
||||
|
||||
def test_no_resource_warning_on_save(self):
|
||||
def test_no_resource_warning_on_save(self, tmp_path):
|
||||
# https://github.com/python-pillow/Pillow/issues/835
|
||||
# Arrange
|
||||
test_file = "Tests/images/hopper.png"
|
||||
temp_file = self.tempfile("temp.jpg")
|
||||
temp_file = str(tmp_path / "temp.jpg")
|
||||
|
||||
# Act/Assert
|
||||
with Image.open(test_file) as im:
|
||||
|
@ -623,14 +620,14 @@ class TestImage(PillowTestCase):
|
|||
with Image.open(os.path.join("Tests/images", file)) as im:
|
||||
try:
|
||||
im.load()
|
||||
self.assertFail()
|
||||
assert False
|
||||
except OSError as e:
|
||||
assert str(e) == "buffer overrun when reading image file"
|
||||
|
||||
with Image.open("Tests/images/fli_overrun2.bin") as im:
|
||||
try:
|
||||
im.seek(1)
|
||||
self.assertFail()
|
||||
assert False
|
||||
except OSError as e:
|
||||
assert str(e) == "buffer overrun when reading image file"
|
||||
|
||||
|
@ -645,7 +642,7 @@ def mock_encode(*args):
|
|||
return encoder
|
||||
|
||||
|
||||
class TestRegistry(PillowTestCase):
|
||||
class TestRegistry:
|
||||
def test_encode_registry(self):
|
||||
|
||||
Image.register_encoder("MOCK", mock_encode)
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import unittest
|
||||
from contextlib import contextmanager
|
||||
|
||||
import pytest
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
from .helper import PillowTestCase, assert_image_equal, assert_image_similar, hopper
|
||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||
|
||||
|
||||
class TestImagingResampleVulnerability(PillowTestCase):
|
||||
class TestImagingResampleVulnerability:
|
||||
# see https://github.com/python-pillow/Pillow/issues/1710
|
||||
def test_overflow(self):
|
||||
im = hopper("L")
|
||||
|
@ -43,7 +42,7 @@ class TestImagingResampleVulnerability(PillowTestCase):
|
|||
assert im.tobytes() != copy.tobytes()
|
||||
|
||||
|
||||
class TestImagingCoreResampleAccuracy(PillowTestCase):
|
||||
class TestImagingCoreResampleAccuracy:
|
||||
def make_case(self, mode, size, color):
|
||||
"""Makes a sample image with two dark and two bright squares.
|
||||
For example:
|
||||
|
@ -219,7 +218,7 @@ class TestImagingCoreResampleAccuracy(PillowTestCase):
|
|||
assert_image_equal(im, ref)
|
||||
|
||||
|
||||
class CoreResampleConsistencyTest(PillowTestCase):
|
||||
class CoreResampleConsistencyTest:
|
||||
def make_case(self, mode, fill):
|
||||
im = Image.new(mode, (512, 9), fill)
|
||||
return im.resize((9, 512), Image.LANCZOS), im.load()[0, 0]
|
||||
|
@ -254,7 +253,7 @@ class CoreResampleConsistencyTest(PillowTestCase):
|
|||
self.run_case(self.make_case("F", 1.192093e-07))
|
||||
|
||||
|
||||
class CoreResampleAlphaCorrectTest(PillowTestCase):
|
||||
class CoreResampleAlphaCorrectTest:
|
||||
def make_levels_case(self, mode):
|
||||
i = Image.new(mode, (256, 16))
|
||||
px = i.load()
|
||||
|
@ -275,7 +274,7 @@ class CoreResampleAlphaCorrectTest(PillowTestCase):
|
|||
len(used_colors), y
|
||||
)
|
||||
|
||||
@unittest.skip("current implementation isn't precise enough")
|
||||
@pytest.mark.skip("Current implementation isn't precise enough")
|
||||
def test_levels_rgba(self):
|
||||
case = self.make_levels_case("RGBA")
|
||||
self.run_levels_case(case.resize((512, 32), Image.BOX))
|
||||
|
@ -284,7 +283,7 @@ class CoreResampleAlphaCorrectTest(PillowTestCase):
|
|||
self.run_levels_case(case.resize((512, 32), Image.BICUBIC))
|
||||
self.run_levels_case(case.resize((512, 32), Image.LANCZOS))
|
||||
|
||||
@unittest.skip("current implementation isn't precise enough")
|
||||
@pytest.mark.skip("Current implementation isn't precise enough")
|
||||
def test_levels_la(self):
|
||||
case = self.make_levels_case("LA")
|
||||
self.run_levels_case(case.resize((512, 32), Image.BOX))
|
||||
|
@ -330,7 +329,7 @@ class CoreResampleAlphaCorrectTest(PillowTestCase):
|
|||
self.run_dirty_case(case.resize((20, 20), Image.LANCZOS), (255,))
|
||||
|
||||
|
||||
class CoreResamplePassesTest(PillowTestCase):
|
||||
class CoreResamplePassesTest:
|
||||
@contextmanager
|
||||
def count(self, diff):
|
||||
count = Image.core.get_stats()["new_count"]
|
||||
|
@ -373,7 +372,7 @@ class CoreResamplePassesTest(PillowTestCase):
|
|||
assert_image_similar(with_box, cropped, 0.1)
|
||||
|
||||
|
||||
class CoreResampleCoefficientsTest(PillowTestCase):
|
||||
class CoreResampleCoefficientsTest:
|
||||
def test_reduce(self):
|
||||
test_color = 254
|
||||
|
||||
|
@ -402,7 +401,7 @@ class CoreResampleCoefficientsTest(PillowTestCase):
|
|||
assert histogram[0x100 * 3 + 0xFF] == 0x10000
|
||||
|
||||
|
||||
class CoreResampleBoxTest(PillowTestCase):
|
||||
class CoreResampleBoxTest:
|
||||
def test_wrong_arguments(self):
|
||||
im = hopper()
|
||||
for resample in (
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import unittest
|
||||
|
||||
import pytest
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
from .helper import PillowTestCase, assert_image_similar
|
||||
from .helper import assert_image_similar
|
||||
|
||||
image_font_installed = True
|
||||
try:
|
||||
|
@ -11,35 +10,29 @@ except ImportError:
|
|||
image_font_installed = False
|
||||
|
||||
|
||||
@unittest.skipUnless(image_font_installed, "image font not installed")
|
||||
class TestImageFontBitmap(PillowTestCase):
|
||||
def test_similar(self):
|
||||
text = "EmbeddedBitmap"
|
||||
font_outline = ImageFont.truetype(font="Tests/fonts/DejaVuSans.ttf", size=24)
|
||||
font_bitmap = ImageFont.truetype(
|
||||
font="Tests/fonts/DejaVuSans-bitmap.ttf", size=24
|
||||
)
|
||||
size_outline = font_outline.getsize(text)
|
||||
size_bitmap = font_bitmap.getsize(text)
|
||||
size_final = (
|
||||
max(size_outline[0], size_bitmap[0]),
|
||||
max(size_outline[1], size_bitmap[1]),
|
||||
)
|
||||
im_bitmap = Image.new("RGB", size_final, (255, 255, 255))
|
||||
im_outline = im_bitmap.copy()
|
||||
draw_bitmap = ImageDraw.Draw(im_bitmap)
|
||||
draw_outline = ImageDraw.Draw(im_outline)
|
||||
@pytest.mark.skipif(not image_font_installed, reason="Image font not installed")
|
||||
def test_similar():
|
||||
text = "EmbeddedBitmap"
|
||||
font_outline = ImageFont.truetype(font="Tests/fonts/DejaVuSans.ttf", size=24)
|
||||
font_bitmap = ImageFont.truetype(font="Tests/fonts/DejaVuSans-bitmap.ttf", size=24)
|
||||
size_outline = font_outline.getsize(text)
|
||||
size_bitmap = font_bitmap.getsize(text)
|
||||
size_final = (
|
||||
max(size_outline[0], size_bitmap[0]),
|
||||
max(size_outline[1], size_bitmap[1]),
|
||||
)
|
||||
im_bitmap = Image.new("RGB", size_final, (255, 255, 255))
|
||||
im_outline = im_bitmap.copy()
|
||||
draw_bitmap = ImageDraw.Draw(im_bitmap)
|
||||
draw_outline = ImageDraw.Draw(im_outline)
|
||||
|
||||
# Metrics are different on the bitmap and ttf fonts,
|
||||
# more so on some platforms and versions of freetype than others.
|
||||
# Mac has a 1px difference, linux doesn't.
|
||||
draw_bitmap.text(
|
||||
(0, size_final[1] - size_bitmap[1]), text, fill=(0, 0, 0), font=font_bitmap
|
||||
)
|
||||
draw_outline.text(
|
||||
(0, size_final[1] - size_outline[1]),
|
||||
text,
|
||||
fill=(0, 0, 0),
|
||||
font=font_outline,
|
||||
)
|
||||
assert_image_similar(im_bitmap, im_outline, 20)
|
||||
# Metrics are different on the bitmap and TTF fonts,
|
||||
# more so on some platforms and versions of FreeType than others.
|
||||
# Mac has a 1px difference, Linux doesn't.
|
||||
draw_bitmap.text(
|
||||
(0, size_final[1] - size_bitmap[1]), text, fill=(0, 0, 0), font=font_bitmap
|
||||
)
|
||||
draw_outline.text(
|
||||
(0, size_final[1] - size_outline[1]), text, fill=(0, 0, 0), font=font_outline,
|
||||
)
|
||||
assert_image_similar(im_bitmap, im_outline, 20)
|
||||
|
|
Loading…
Reference in New Issue
Block a user