packages feed

hakyll-images-1.3.0: tests/Hakyll/Images/Resize/Tests.hs

{-# LANGUAGE OverloadedStrings #-}

module Hakyll.Images.Resize.Tests
  ( tests,
  )
where

import Codec.Picture
  ( convertRGBA8,
    decodeJpeg,
    dynamicMap,
    imageHeight,
    imageWidth,
  )
import qualified Data.ByteString as B
import Data.Ratio ((%))
import Hakyll (Item (Item))
import Hakyll.Images
import Hakyll.Images.Internal (Image (Image), ImageContent, WithMetadata (..), decodeContent)
import Hakyll.Images.Tests.Utils
import Test.HUnit.Approx (assertApproxEqual)
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (Assertion, assertBool, assertEqual, testCase)
import Text.Printf (printf)

-- Original test image "piccolo.jpg" has shape 1170 x 647px
testJpg :: IO ImageContent
testJpg = decodeContent <$> B.readFile "tests/data/piccolo.jpg"

testGif :: IO ImageContent
testGif = decodeContent <$> B.readFile "tests/data/donkey.gif"

fromAssertions ::
  -- | Name
  String ->
  -- | Cases
  [Assertion] ->
  -- | Result tests
  [TestTree]
fromAssertions name =
  zipWith testCase [printf "[%2d] %s" n name | n <- [1 :: Int ..]]

-- Test that the rescaled image is of the appropriate scale
testResizeJpg :: Assertion
testResizeJpg = do
  image <- testJpg
  let scaledImage = resize 48 64 image
      MkWithMetadata converted _ = fmap convertRGBA8 scaledImage
      (width, height) =
        ( imageWidth converted,
          imageHeight converted
        )
  assertEqual "Image width was not resized properly" width 48
  assertEqual "Image height was not resized properly" height 64

-- Test that the rescaled image is of the appropriate scale
testResizeGif :: Assertion
testResizeGif = do
  image <- testGif
  let scaledImage = resize 48 64 image
      MkWithMetadata converted _ = fmap convertRGBA8 scaledImage
      (width, height) =
        ( imageWidth converted,
          imageHeight converted
        )
  assertEqual "Image width was not resized properly" width 48
  assertEqual "Image height was not resized properly" height 64

-- Test that the rescaled image fits in the appropriate box
testScale :: Assertion
testScale = do
  image <- testJpg
  let scaledImage = scale 600 400 image
      MkWithMetadata converted _ = fmap convertRGBA8 scaledImage
      (width, height) =
        ( imageWidth converted,
          imageHeight converted
        )
  assertBool "Image width was not scaled properly" (width <= 600)
  assertBool "Image height was not scaled properly" (height <= 400)
  assertBool "Image overall was not scaled properly" (width == 600 || height == 400)

-- Test that the images that already fit in dimensions are not scaled
testEnsureFit :: Assertion
testEnsureFit = do
  image <- testJpg
  let (originalWidth, originalHeight) = (imageWidth . convertRGBA8 . getData $ image, imageHeight . convertRGBA8 . getData $ image)
      scaledImage = ensureFit 2000 2000 image
      MkWithMetadata converted _ = fmap convertRGBA8 scaledImage
      (width, height) =
        ( imageWidth converted,
          imageHeight converted
        )
  assertEqual "Image width was not scaled properly" width originalWidth
  assertEqual "Image height was not scaled property" height originalHeight

-- Test that the rescaled image has the same aspect ratio
testScalePreservesAspectRatio :: Assertion
testScalePreservesAspectRatio = do
  image <- testJpg

  let initialAspectRatio = imageWidth (convertRGBA8 $ getData image) % imageHeight (convertRGBA8 $ getData image)
      scaledImage = scale 600 400 image
      finalAspectRatio = imageWidth (convertRGBA8 $ getData scaledImage) % imageHeight (convertRGBA8 $ getData scaledImage)

  assertApproxEqual "Aspect ratio was not preserved" 0.02 initialAspectRatio finalAspectRatio

-- Test that issue #11 is fixed
testJpgInadvertentRotation :: TestTree
testJpgInadvertentRotation = testCase "resizing a JPEG does not rotate it (issue #11)" $ do
  Item _ (Image _ raw) <-
    testCompilerDone "issue11-2.jpg" $ loadImage

  let original = either error id $ decodeJpeg raw
      initialWidth = dynamicMap imageWidth original
      initialHeight = dynamicMap imageHeight original

  Item _ (Image _ bts) <-
    testCompilerDone "issue11-2.jpg" $ loadImage >>= ensureFitCompiler 400 400

  let resized = either error id $ decodeJpeg bts
      measuredWidth = dynamicMap imageWidth resized
      measuredHeight = dynamicMap imageHeight resized

  -- The image *appears* to be w=300, h=400,
  -- but an exif tag that rotates it means that we expect
  -- the height to be smaller than the width
  assertBool mempty (initialHeight < initialWidth)
  -- The same must hold for the resized image
  assertBool mempty (measuredHeight < measuredWidth)

tests :: TestTree
tests =
  testGroup "Hakyll.Images.Resize.Tests" $
    concat
      [ fromAssertions
          "rescale"
          [ testScale,
            testScalePreservesAspectRatio
          ],
        fromAssertions
          "ensureFit"
          [ testEnsureFit
          ],
        fromAssertions
          "resize"
          [testResizeJpg, testResizeGif],
        [testJpgInadvertentRotation]
      ]