Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
Co-Authored-By: zerolab <[email protected]>
  • Loading branch information
salty-ivy and zerolab committed Jul 8, 2023
1 parent 5b62f87 commit 0a8ffe6
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 32 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
.vscode
.ruff_cache
.coverage*
.DS_Store
Binary file added tests/images/tree.avif
Binary file not shown.
21 changes: 21 additions & 0 deletions tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import filetype

from willow.image import (
AvifImageFile,
BMPImageFile,
GIFImageFile,
HeicImageFile,
Expand Down Expand Up @@ -169,6 +170,16 @@ def test_heic(self):
self.assertEqual(height, 241)
self.assertEqual(image.mime_type, "image/heiс")

def test_avif(self):
with open("tests/images/tree.avif", "rb") as f:
image = Image.open(f)
width, height = image.get_size()

self.assertIsInstance(image, AvifImageFile)
self.assertEqual(width, 320)
self.assertEqual(height, 241)
self.assertEqual(image.mime_type, "image/avif")


class TestSaveImage(unittest.TestCase):
"""
Expand All @@ -194,6 +205,16 @@ def test_save_as_heic(self):
self.assertIsInstance(image, HeicImageFile)
self.assertEqual(image.mime_type, "image/heiс")

def test_save_as_avif(self):
with open("tests/images/sails.bmp", "rb") as f:
image = Image.open(f)
buf = io.BytesIO()
image.save("avif", buf)
buf.seek(0)
image = Image.open(buf)
self.assertIsInstance(image, AvifImageFile)
self.assertEqual(image.mime_type, "image/avif")

def test_save_as_foo(self):
image = Image()
image.save_as_jpeg = mock.MagicMock()
Expand Down
56 changes: 43 additions & 13 deletions tests/test_pillow.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

import filetype
from PIL import Image as PILImage
from PIL import ImageChops

from willow.image import (
AvifImageFile,
BadImageOperationError,
GIFImageFile,
JPEGImageFile,
Expand All @@ -14,6 +16,7 @@
from willow.plugins.pillow import PillowImage, UnsupportedRotation, _PIL_Image

no_webp_support = not PillowImage.is_format_supported("WEBP")
no_avif_support = not PillowImage.is_format_supported("AVIF")


class TestPillowOperations(unittest.TestCase):
Expand Down Expand Up @@ -298,27 +301,54 @@ def test_open_webp_w_alpha(self):
self.assertFalse(image.has_animation())

@unittest.skipIf(no_webp_support, "Pillow does not have WebP support")
def test_open_webp_quality(self):
def test_save_webp_quality(self):
high_quality = self.image.save_as_webp(io.BytesIO(), quality=90)
low_quality = self.image.save_as_webp(io.BytesIO(), quality=30)
self.assertTrue(low_quality.f.tell() < high_quality.f.tell())

@unittest.skipIf(no_webp_support, "Pillow does not have WebP support")
def test_open_webp_lossless(self):
def test_save_webp_lossless(self):
original_image = self.image.image

lossless_file = self.image.save_as_webp(io.BytesIO(), lossless=True)
lossless_image = PillowImage.open(lossless_file).image
identically = True
for x in range(original_image.width):
for y in range(original_image.height):
original_pixel = original_image.getpixel((x, y))
# don't compare fully transparent pixels
if original_pixel[3] == 0:
continue
if original_pixel != lossless_image.getpixel((x, y)):
identically = False
break
self.assertTrue(identically)

diff = ImageChops.difference(original_image, lossless_image)
self.assertIsNone(diff.getbbox())

@unittest.skipIf(no_avif_support, "Pillow does not have AVIF support")
def test_save_as_avif(self):
output = io.BytesIO()
return_value = self.image.save_as_avif(output)
output.seek(0)

self.assertEqual(filetype.guess_extension(output), "avif")
self.assertIsInstance(return_value, AvifImageFile)
self.assertEqual(return_value.f, output)

@unittest.skipIf(no_avif_support, "Pillow does not have AVIF support")
def test_open_avif(self):
with open("tests/images/tree.webp", "rb") as f:
image = PillowImage.open(AvifImageFile(f))

self.assertFalse(image.has_alpha())
self.assertFalse(image.has_animation())

@unittest.skipIf(no_avif_support, "Pillow does not have AVIF support")
def test_save_avif_quality(self):
high_quality = self.image.save_as_avif(io.BytesIO(), quality=90)
low_quality = self.image.save_as_avif(io.BytesIO(), quality=30)
self.assertTrue(low_quality.f.tell() < high_quality.f.tell())

@unittest.skipIf(no_avif_support, "Pillow does not have AVIF support")
def test_save_avif_lossless(self):
original_image = self.image.image

lossless_file = self.image.save_as_avif(io.BytesIO(), lossless=True)
lossless_image = PillowImage.open(lossless_file).image

diff = ImageChops.difference(original_image, lossless_image)
self.assertIsNone(diff.getbbox())


class TestPillowImageOrientation(unittest.TestCase):
Expand Down
101 changes: 82 additions & 19 deletions tests/test_wand.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

import filetype
from PIL import Image as PILImage
from wand import version as WAND_VERSION

from willow.image import (
AvifImageFile,
BadImageOperationError,
GIFImageFile,
JPEGImageFile,
Expand All @@ -14,6 +16,7 @@
from willow.plugins.wand import UnsupportedRotation, WandImage, _wand_image

no_webp_support = not WandImage.is_format_supported("WEBP")
no_avif_support = not WandImage.is_format_supported("AVIF")


class TestWandOperations(unittest.TestCase):
Expand Down Expand Up @@ -242,7 +245,58 @@ def test_get_wand_image(self):

self.assertIsInstance(wand_image, _wand_image().Image)

@unittest.skipIf(no_webp_support, "imagemagic was not built with WebP support")
@unittest.skipIf(no_avif_support, "ImageMagick was built without AVIF support")
def test_open_avif(self):
with open("tests/images/tree.avif", "rb") as f:
image = WandImage.open(AvifImageFile(f))

self.assertFalse(image.has_alpha())
self.assertFalse(image.has_animation())

@unittest.skipIf(no_avif_support, "ImageMagick was built without AVIF support")
def test_save_as_avif(self):
output = io.BytesIO()
return_value = self.image.save_as_avif(output)
output.seek(0)

self.assertEqual(filetype.guess_extension(output), "avif")
self.assertIsInstance(return_value, AvifImageFile)
self.assertEqual(return_value.f, output)

@unittest.skipIf(no_avif_support, "ImageMagick was built without AVIF support")
def test_save_avif_quality(self):
high_quality = self.image.save_as_avif(io.BytesIO(), quality=90)
low_quality = self.image.save_as_avif(io.BytesIO(), quality=30)
self.assertTrue(low_quality.f.tell() < high_quality.f.tell())

@unittest.skipIf(no_webp_support, "ImageMagick was built without AVIF support")
def test_save_avif_lossless(self):
original_image = self.image.image

lossless_file = self.image.save_as_avif(io.BytesIO(), lossless=True)
lossless_image = WandImage.open(lossless_file).image

magick_version = WAND_VERSION.MAGICK_VERSION_INFO
if magick_version >= (7, 1):
# we allow a small margin of error to account for OS/library version differences
# Ref: https://github.com/bigcat88/pillow_heif/blob/3798f0df6b12c19dfa8fd76dd6259b329bf88029/tests/write_test.py#L415-L422
_, result_metric = original_image.compare(
lossless_image, metric="root_mean_square"
)
self.assertTrue(result_metric <= 0.02)
else:
identical = True
for x in range(original_image.width):
for y in range(original_image.height):
original_pixel = original_image[x, y]
# don't compare fully transparent pixels
if original_pixel.alpha == 0.0:
continue
if original_pixel != lossless_image[x, y]:
break
self.assertTrue(identical)

@unittest.skipIf(no_webp_support, "ImageMagick was built without WebP support")
def test_save_as_webp(self):
output = io.BytesIO()
return_value = self.image.save_as_webp(output)
Expand All @@ -252,44 +306,53 @@ def test_save_as_webp(self):
self.assertIsInstance(return_value, WebPImageFile)
self.assertEqual(return_value.f, output)

@unittest.skipIf(no_webp_support, "imagemagic was not built with WebP support")
@unittest.skipIf(no_webp_support, "ImageMagick was built without WebP support")
def test_open_webp(self):
with open("tests/images/tree.webp", "rb") as f:
image = WandImage.open(WebPImageFile(f))

self.assertFalse(image.has_alpha())
self.assertFalse(image.has_animation())

@unittest.skipIf(no_webp_support, "imagemagic was not built with WebP support")
@unittest.skipIf(no_webp_support, "ImageMagick was built without WebP support")
def test_open_webp_w_alpha(self):
with open("tests/images/tux_w_alpha.webp", "rb") as f:
image = WandImage.open(WebPImageFile(f))

self.assertTrue(image.has_alpha())
self.assertFalse(image.has_animation())

@unittest.skipIf(no_webp_support, "imagemagic does not have WebP support")
def test_open_webp_quality(self):
@unittest.skipIf(no_webp_support, "ImageMagick was built without WebP support")
def test_save_webp_quality(self):
high_quality = self.image.save_as_webp(io.BytesIO(), quality=90)
low_quality = self.image.save_as_webp(io.BytesIO(), quality=30)
self.assertTrue(low_quality.f.tell() < high_quality.f.tell())

@unittest.skipIf(no_webp_support, "imagemagic does not have WebP support")
def test_open_webp_lossless(self):
@unittest.skipIf(no_webp_support, "ImageMagick was built without WebP support")
def test_save_webp_lossless(self):
original_image = self.image.image
lossless_file = self.image.save_as_webp(io.BytesIO(), lossless=True)

new_f = io.BytesIO()
lossless_file = self.image.save_as_webp(new_f, lossless=True)
lossless_image = WandImage.open(lossless_file).image
identically = True
for x in range(original_image.width):
for y in range(original_image.height):
original_pixel = original_image[x, y]
# don't compare fully transparent pixels
if original_pixel.alpha == 0.0:
continue
if original_pixel != lossless_image[x, y]:
identically = False
break
self.assertTrue(identically)

magick_version = WAND_VERSION.MAGICK_VERSION_INFO
if magick_version >= (7, 1):
_, result_metric = original_image.compare(
lossless_image, metric="root_mean_square"
)
self.assertTrue(result_metric <= 0.001)
else:
identical = True
for x in range(original_image.width):
for y in range(original_image.height):
original_pixel = original_image[x, y]
# don't compare fully transparent pixels
if original_pixel.alpha == 0.0:
continue
if original_pixel != lossless_image[x, y]:
break
self.assertTrue(identical)


class TestWandImageOrientation(unittest.TestCase):
Expand Down

0 comments on commit 0a8ffe6

Please sign in to comment.