{-# LANGUAGE DataKinds #-}
module Main (main) where
import Data.Algorithm.Diff
import Data.Algorithm.DiffOutput
import qualified Data.ByteString.Lazy as BS
import Data.Char
import Data.List
import Control.Monad
import Control.Exception (evaluate, handle, ErrorCall(..))
import Graphics.SvgTree
import Graphics.SvgTree.Printer
import System.Directory
import System.Environment
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import System.FilePath
import System.Process.Typed
import Test.Tasty
import Test.Tasty.ExpectedFailure
import Test.Tasty.HUnit
import Test.Tasty.Ingredients.Rerun (defaultMainWithRerun)
main :: IO ()
-- main = return ()
main = do
args <- getArgs
case args of
["sort"] -> sortTestCases
["cycle", path] -> do
inp <- readFile path
putStrLn $ cycleSvg inp
[path] -> do
haveFile <- doesFileExist path
if haveFile
then do
passed <- checkCycled path
if passed
then putStrLn "Good"
else putStrLn "Mismatch!"
else runTestSuite
_ -> runTestSuite
runTestSuite :: IO ()
runTestSuite = do
print =<< getCurrentDirectory
good <- unitTestFolder "test/good"
bad <- unitTestFolder "test/bad"
defaultMainWithRerun $ testGroup "tests" [good, expectFail bad]
unitTestFolder :: FilePath -> IO TestTree
unitTestFolder path = do
files <- sort <$> getDirectoryContents path
return $
testGroup
"animate"
[ testSvg fullPath
| file <- files,
let fullPath = path </> file,
takeExtension fullPath == ".svg"
]
sortTestCases :: IO ()
sortTestCases = do
goodFiles <- getDirectoryContents "test/good"
badFiles <- getDirectoryContents "test/bad"
forM_ goodFiles $ \file -> when (takeExtension file == ".svg") $ do
-- putStrLn $ "Processing: " ++ ("test/good" </> file)
passed <- checkCycled ("test/good" </> file)
unless passed $ do
putStrLn $ "Marking bad: " ++ file
renameFile ("test/good" </> file) ("test/bad" </> file)
forM_ badFiles $ \file -> when (takeExtension file == ".svg") $ do
-- putStrLn $ "Processing: " ++ ("test/bad" </> file)
passed <- checkCycled ("test/bad" </> file)
when passed $ do
putStrLn $ "Marking good: " ++ file
renameFile ("test/bad" </> file) ("test/good" </> file)
checkCycled :: FilePath -> IO Bool
checkCycled path =
handle (\ExitCodeException{} -> return False) $
handle (\ErrorCall{} -> return False) $ do
raw <- readFile path
golden <- sanitizeSvg raw
cycled <- sanitizeSvg (cycleSvg raw)
return (golden == cycled)
testSvg :: FilePath -> TestTree
testSvg path = testCase (takeBaseName path) $
assert =<< checkCycled path
-- Sanitize SVG using rsvg-convert
sanitizeSvg :: String -> IO BS.ByteString
sanitizeSvg inp = do
let inpT = T.pack inp
evaluate (T.length inpT)
(stdout, stderr) <- readProcess_ $
setStdin (textInput inpT) $
proc "rsvg-convert" ["--format", "png"]
return stdout
textInput :: T.Text -> StreamSpec 'STInput ()
textInput = byteStringInput . BS.fromStrict . T.encodeUtf8
-- Ugly hack to ignore the differences between #FFF and #fff.
lowerHashValues :: String -> String
lowerHashValues ('#' : more) = '#' : map toLower before ++ lowerHashValues after
where
(before, after) = splitAt 6 more
lowerHashValues (c : cs) = c : lowerHashValues cs
lowerHashValues [] = []
-- prettyPrintSvg :: String -> IO String
-- prettyPrintSvg =
-- fmap lowerHashValues
-- . readProcess
-- "svgo"
-- [ "--input=-",
-- "--output=-",
-- "--pretty",
-- "--enable=sortAttrs,convertColors",
-- "--disable=cleanupIDs,prefixIds,convertTransform,mergePaths,collapseGroups"
-- ]
-- Parse and print SVG with reanimate-svg library.
cycleSvg :: String -> String
cycleSvg =
maybe "Failed to parse" ppDocument . parseSvgFile "input" . T.pack