imagemagick-0.0.4: test/ImageTest.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Main (main) where
import Control.Applicative
import Control.Exception.Lifted
import Control.Monad
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Resource
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.Maybe
import qualified Data.Vector.Storable as V
import qualified Data.Text as T
import Data.Word
import Prelude
import System.Directory (getTemporaryDirectory,
removeFile)
import System.IO (hClose, openTempFile)
import Test.Tasty
import Test.Tasty.HUnit
import Graphics.ImageMagick.MagickWand
main :: IO ()
main = defaultMain tests
-- tests mostly taken from wand(http://dahlia.kr/wand/) source code
tests :: TestTree
tests = localOption (mkTimeout 1000000) $ testGroup "Behaves to spec"
[ testCase "reading file" test_readImage
-- , testCase "stripping" test_strip
-- , testCase "trimming" test_trim
-- , testCase "format to MIME conversion" test_mime
-- , testCase "iterate" test_iterate
-- , testCase "getitng pixel" test_pixel
-- , testCase "cropping image" test_crop
-- , testCase "resizing image" test_resize
-- , testCase "rotating image" test_rotate
-- , testCase "image signature" test_signature
-- , testCase "getting alpha channel" test_getImageAlphaChannel
-- , testCase "setting alpha channel" test_setImageAlphaChannel
-- , testCase "unsetting alpha channel" test_unsetImageAlphaChannel
-- , testCase "getting background color" test_getImageBackgroundColor
-- , testCase "setting background color" test_setImageBackgroundColor
-- , testCase "watermark" test_watermark
-- -- Creates black background instead of transparent
-- -- , testCase "reset" test_reset
-- , testCase "getting an imageS blob for an animated GIF" test_getImagesBlobForSequence
-- , testCase "getting an imageS blob for a single image" test_getImagesBlobForSingle
]
test_readImage :: IO ()
test_readImage = withImage "mona-lisa.jpg" $ \w -> do
width <- getImageWidth w
liftIO $ width @?= 402
test_readImageBlob :: IO ()
test_readImageBlob = withWand $ \w -> do
blob <- liftIO $ BS.readFile (dataFile "mona-lisa.jpg")
readImageBlob w blob
width <- getImageWidth w
liftIO $ width @?= 402
test_cloneWand :: IO ()
test_cloneWand = withImage "mona-lisa.jpg" $ \w -> do
size <- getImageSize w
(_,w') <- cloneMagickWand w
size' <- getImageSize w'
liftIO $ size' @?= size
test_writeImage :: IO ()
test_writeImage = withImage "mona-lisa.jpg" $ \w -> do
size <- getImageSize w
d <- liftIO $ getTemporaryDirectory
(tmpName, hTemp) <- liftIO $ openTempFile d ""
liftIO $ hClose hTemp
writeImage w (Just $ T.pack tmpName)
(_,w') <- magickWand
readImage w' (T.pack tmpName)
size' <- getImageSize w'
liftIO $ removeFile tmpName
liftIO $ size' @?= size
test_getReadImageBlob :: IO ()
test_getReadImageBlob = withImage "mona-lisa.jpg" $ \w -> do
size <- getImageSize w
blob <- getImageBlob w
(_,w') <- magickWand
readImageBlob w' blob
size' <- getImageSize w'
liftIO $ size' @?= size
test_size :: IO ()
test_size = withImage "mona-lisa.jpg" $ \w -> do
size <- getImageSize w
liftIO $ size @?= (402,599)
test_getDepth :: IO ()
test_getDepth = withImage "mona-lisa.jpg" $ \w -> do
depth <- getImageDepth w
liftIO $ depth @?= 8
test_setDepth :: IO ()
test_setDepth = withImage "mona-lisa.jpg" $ \w -> do
let newDepth = 16
setImageDepth w newDepth
depth <- getImageDepth w
liftIO $ depth @?= newDepth
test_formatJpeg :: IO ()
test_formatJpeg = withImage "mona-lisa.jpg" $ \w -> do
format <- getImageFormat w
liftIO $ format @?= "JPEG"
test_formatPng :: IO ()
test_formatPng = withImage "croptest.png" $ \w -> do
format <- getImageFormat w
liftIO $ format @?= "PNG"
test_setFormat :: IO ()
test_setFormat = withImage "mona-lisa.jpg" $ \w -> do
setImageFormat w "PNG"
format <- getImageFormat w
liftIO $ format @?= "PNG"
bs <- getImageBlob w
(_,w') <- magickWand
readImageBlob w' bs
format' <- getImageFormat w'
liftIO $ format' @?= format
test_setBadFormat :: IO ()
test_setBadFormat = withImage "mona-lisa.jpg" $
\w -> assertMagickWandException $ setImageFormat w "HANKY"
test_getCompressionQuality :: IO ()
test_getCompressionQuality = withImage "mona-lisa.jpg" $ \w -> do
quality <- getImageCompressionQuality w
liftIO $ quality @?= 80
test_setCompressionQuality :: IO ()
test_setCompressionQuality = withImage "mona-lisa.jpg" $ \w -> do
let quality = 50
setImageCompressionQuality w quality
quality' <- getImageCompressionQuality w
liftIO $ quality' @?= quality
bs <- getImageBlob w
(_,w') <- magickWand
readImageBlob w' bs
quality'' <- getImageCompressionQuality w'
liftIO $ quality'' @?= quality
test_strip :: IO ()
test_strip = withImage "beach.jpg"$ \w -> do
originalLength <- BS.length <$> getImageBlob w
stripImage w
strippedLength <- BS.length <$> getImageBlob w
liftIO $ assertBool "stripped should be shorter than original" $ strippedLength < originalLength
test_trim :: IO ()
test_trim = withImage "trimtest.png" $ \w -> do
(width,height) <- getImageSize w
trimImage w fuzz
(width',height') <- getImageSize w
liftIO $ assertBool "width should be trimmed" $ width' < width
liftIO $ assertBool "height should be trimmed" $ height' < height
test_mime :: IO ()
test_mime = withMagickWandGenesis $ do
mime <- toMime "JPG"
liftIO $ assertBool "jpg should convert to either image/jpeg or image/x-jpeg" $
mime `elem` ["image/jpeg", "image/x-jpeg"]
mime' <- toMime "PNG"
liftIO $ assertBool "jpg should convert to either image/png or image/x-png" $
mime' `elem` ["image/png", "image/x-png"]
test_iterate :: IO ()
test_iterate = withImage "croptest.png" $ \w -> do
black <- getColorPW "#000"
transparent <- getColorPW "transparent"
(_,it) <- pixelIterator w
rows <- pixelIterateList it
forM_ (zip [0..] rows) $ \(i, row) -> do
when (i `mod` 3 == 0) $ do -- condition to make test a bit faster
let rowLength = V.length row
liftIO $ rowLength @?= 300
if i >= 100 && i < 200
then do
let
assertPixel j pw | j >= 100 && j < 200 = assertEqualPW black pw
assertPixel _ pw = assertEqualPW transparent pw
V.zipWithM_ assertPixel (V.enumFromN (0::Word16) rowLength) row
else do
V.forM_ row $ assertEqualPW transparent
liftIO $ (length rows) @?= 300
test_pixel :: IO ()
test_pixel = withImage "croptest.png" $ \w -> do
transparent <- getColorPW "transparent"
black <- getColorPW "#000"
pw <- pixelWand
getImagePixelColor w 0 0 pw
assertEqualPW pw transparent
getImagePixelColor w 99 99 pw
assertEqualPW pw transparent
getImagePixelColor w 100 100 pw
assertEqualPW pw black
getImagePixelColor w 150 150 pw
assertEqualPW pw black
getImagePixelColor w 201 201 pw
assertEqualPW pw transparent
test_crop :: IO ()
test_crop = withImage "croptest.png" $ \w -> do
black <- getColorPW "#000"
cropImage w 100 100 100 100
size <- getImageSize w
liftIO $ size @?= (100,100)
(_,it) <- pixelIterator w
rows <- pixelIterateList it
forM_ rows $ \row -> V.forM_ row (assertEqualPW black)
test_resize :: IO ()
test_resize = withImage "mona-lisa.jpg" $ \w -> do
size <- getImageSize w
liftIO $ size @?= (402,599)
resizeImage w 100 100 undefinedFilter 1
size' <- getImageSize w
liftIO $ size' @?= (100,100)
test_rotate :: IO ()
test_rotate = withImage "rotatetest.gif" $ \w -> do
transparent <- getColorPW "transparent"
black <- getColorPW "black"
white <- getColorPW "white"
red <- getColorPW "red"
size <- getImageSize w
liftIO $ size @?= (150,100)
localGenesis $ do
(_,w') <- cloneMagickWand w
rotateImage w' transparent 360
size' <- getImageSize w'
liftIO $ size' @?= size
assertColorAtXY w' black ( 0,50)
assertColorAtXY w' black (74,50)
assertColorAtXY w' black ( 0,99)
assertColorAtXY w' black (74,99)
assertColorAtXY w' white (75,50)
assertColorAtXY w' white (75,99)
localGenesis $ do
(_,w') <- cloneMagickWand w
rotateImage w' transparent 90
size' <- getImageSize w'
liftIO $ size' @?= (100,150)
(_,it) <- pixelIterator w'
rows <- pixelIterateList it
forM_ (zip [0..] rows) $ \(y,row) -> do
let
assertPixel x pw | x < 50 && y < 75 = assertEqualPW black pw
assertPixel _ pw = assertEqualPW white pw
V.zipWithM_ assertPixel (V.enumFromN (0::Word16) (V.length row)) row
localGenesis $ do
(_,w') <- cloneMagickWand w
rotateImage w' red 45
(width', height') <- getImageSize w'
liftIO $ assertBool "size should be (>=176)x(<=178)" $
width' >= 176 && height' <= 178
assertColorAtXY w' red ( 0, 0)
assertColorAtXY w' red ( 0, height'-1)
assertColorAtXY w' red (width'-1, 0)
assertColorAtXY w' red (width'-1, height'-1)
assertColorAtXY w' black ( 2, 70)
assertColorAtXY w' black (35, 37)
assertColorAtXY w' black (85, 88)
assertColorAtXY w' black (52,120)
test_signature :: IO ()
test_signature = withImage "mona-lisa.jpg" $ \w -> do
let signature = "f7695e173f691f59c5939e1898eafa6491bdf1439c60ecce7edfe4b3d101bf22"
testedSignature <- getImageSignature w
liftIO $ testedSignature @?= signature
test_getImageAlphaChannel :: IO ()
test_getImageAlphaChannel = do
withImage "watermark.png" $ \w -> do
alphaCh <- getImageAlphaChannel w
liftIO $ alphaCh @?= True
withImage "mona-lisa.jpg" $ \w -> do
alphaCh <- getImageAlphaChannel w
liftIO $ alphaCh @?= False
test_setImageAlphaChannel :: IO ()
test_setImageAlphaChannel = withImage "mona-lisa.jpg" $ \w -> do
setImageAlphaChannel w deactivateAlphaChannel
alphaCh <- getImageAlphaChannel w
liftIO $ alphaCh @?= False
setImageAlphaChannel w activateAlphaChannel
alphaCh' <- getImageAlphaChannel w
liftIO $ alphaCh' @?= True
test_unsetImageAlphaChannel :: IO ()
test_unsetImageAlphaChannel = withImage "watermark.png" $ \w -> do
alphaCh <- getImageAlphaChannel w
liftIO $ alphaCh @?= True
setImageAlphaChannel w deactivateAlphaChannel
alphaCh' <- getImageAlphaChannel w
liftIO $ alphaCh' @?= False
test_getImageBackgroundColor :: IO ()
test_getImageBackgroundColor = withImage "mona-lisa.jpg" $ \w -> do
white <- getColorPW "white"
bg <- getImageBackgroundColor w
assertEqualPW bg white
test_setImageBackgroundColor :: IO ()
test_setImageBackgroundColor = withImage "croptest.png" $ \w -> do
transparent <- getColorPW "transparent"
setImageBackgroundColor w transparent
bg <- getImageBackgroundColor w
assertEqualPW bg transparent
test_watermark :: IO ()
test_watermark = withImage "beach.jpg" $ \w -> do
(_,watermark) <- magickWand
readImage watermark (T.pack $ dataFile "watermark.png")
setIteratorIndex watermark 0
setImageType watermark trueColorMatteType
evaluateImageChannel watermark opacityChannel subtractEvaluateOperator (0.3 * quantumRange)
compositeImage w watermark overCompositeOp 0 0
(_,marked) <- magickWand
readImage marked (T.pack $ dataFile "marked.png")
sig1 <- getImageSignature marked
sig2 <- getImageSignature w
liftIO $ sig2 @?= sig1
test_reset :: IO ()
test_reset = withImage "sasha.jpg" $ \w -> do
transparent <- getColorPW "transparent"
rotateImage w transparent 45
resetImagePage w Nothing
cropImage w 170 170 0 0
resetImagePage w Nothing
sig1 <- getImageSignature w
(_,control) <- magickWand
readImage control (T.pack $ dataFile "resettest.png")
sig2 <- getImageSignature control
liftIO $ sig1 @?= sig2
-- helpers
fuzz :: Double
fuzz = 10
dataFile :: String -> FilePath
dataFile name = "data/" ++ name
withWand f = withMagickWandGenesis $ do
(_,w) <- magickWand
f w
withImage name f = withWand $ \w -> do
readImage w (T.pack $ dataFile name)
f w
getImageSize :: (MonadResource m) => PMagickWand -> m (Int, Int)
getImageSize w = do
width <- getImageWidth w
height <- getImageHeight w
return (width,height)
-- TODO fix to Text
getColorPW :: (MonadResource m) => ByteString -> m PPixelWand
getColorPW color = do
pw <- pixelWand
pw `setColor` color
return pw
assertMagickWandException :: (MonadResource m, MonadBaseControl IO m) => m a -> m ()
assertMagickWandException action =
catch (action >> (liftIO $ assertFailure "Expected MagickWandException"))
(\(_::MagickWandException) -> return ())
assertEqualPW :: (MonadResource m) => PPixelWand -> PPixelWand -> m ()
assertEqualPW pw1 pw2 = do
a1 <- getAlpha pw1
a2 <- getAlpha pw2
similar <- isPixelWandSimilar pw1 pw2 fuzz
-- let x = show (a1,a2,similar)
liftIO $ assertBool "colors should be similar" $ (a1 == a2) && similar
assertColorAtXY :: (MonadResource m) => PMagickWand -> PPixelWand -> (Int,Int) -> m ()
assertColorAtXY w pw1 (x,y) = do
pw2 <- pixelWand
getImagePixelColor w x y pw2
assertEqualPW pw1 pw2
test_getImagesBlobForSequence :: IO()
test_getImagesBlobForSequence = testGetImagesBlob "newtons-cradle.gif"
test_getImagesBlobForSingle :: IO()
test_getImagesBlobForSingle = testGetImagesBlob "mona-lisa.jpg"
testGetImagesBlob :: String -> IO()
testGetImagesBlob imgName = withImage imgName $ \w -> do
numImgs <- getNumberImages w
size <- getImageSize w
blob <- getImagesBlob w
(_,w') <- magickWand
readImageBlob w' blob
numImgs' <- getNumberImages w'
size' <- getImageSize w'
liftIO $ numImgs @?= numImgs'
liftIO $ size' @?= size