{-# LANGUAGE ExistentialQuantification, OverloadedStrings #-}
module Main where
import qualified Data.ByteString as ByteString
import qualified Data.ByteString.Char8 as ASCII
import Data.ByteString (ByteString)
import Data.Functor.Identity (Identity)
import Data.List (isSuffixOf)
import Data.Maybe (fromMaybe)
import Data.Text (Text)
import qualified Data.Text as Text
import Numeric (showHex)
import System.Directory (doesDirectoryExist, listDirectory)
import System.FilePath.Posix (combine)
import qualified Text.ParserCombinators.Incremental as Incremental
import Text.ParserCombinators.Incremental.LeftBiasedLocal (Parser)
import qualified Data.Attoparsec.ByteString as Atto
import Test.Tasty (TestTree, defaultMain, testGroup)
import Test.Tasty.HUnit (assertFailure, assertEqual, testCase)
import Construct
import qualified MBR
import qualified TAR
import qualified URI
import qualified WMF
import Debug.Trace
data TestFormat = forall f. TestFormat (Format (Parser ByteString) Maybe ByteString (f Identity))
| forall f. LineFormat (Format (Parser ByteString) Maybe ByteString (f Identity))
| forall f. AttoLineFormat (Format Atto.Parser Maybe ByteString (f Identity))
| forall f. AttoFormat (Format Atto.Parser Maybe ByteString (f Identity))
main = exampleTree "" "test/examples" >>= defaultMain . testGroup "examples"
exampleTree :: FilePath -> FilePath -> IO [TestTree]
exampleTree ancestry path =
do let fullPath = combine ancestry path
isDir <- doesDirectoryExist fullPath
if isDir
then (:[]) . testGroup path . concat <$> (listDirectory fullPath >>= mapM (exampleTree fullPath))
else do blob <- ByteString.readFile fullPath
let format
| ".mbr" `isSuffixOf` path = TestFormat MBR.format
| ".tar" `isSuffixOf` path = AttoFormat TAR.archive
| ".uris" `isSuffixOf` path = AttoLineFormat URI.uriReference
| ".wmf" `isSuffixOf` path = TestFormat WMF.fileFormat
roundTrip f t
| ByteString.null t = Just t
| [(structure, remainder)] <- Incremental.completeResults (Incremental.feedEof $
Incremental.feed t $ parse f),
ByteString.null remainder = serialize f structure
| otherwise = Nothing
attoRoundTrip f t
| ByteString.null t = Just t
| Atto.Done remainder structure <- Atto.parse (parse f) t, ByteString.null remainder =
serialize f structure
| Atto.Partial i <- Atto.parse (parse f) t,
Atto.Done remainder structure <- i mempty, ByteString.null remainder = serialize f structure
| otherwise = Nothing
Just blob'
| TestFormat f <- format,
[(structure, remainder)] <- Incremental.completeResults (Incremental.feedEof $
Incremental.feed blob $ parse f) =
(<> remainder) <$> serialize f structure
| LineFormat f <- format = Just (mconcat
$ map ((<> "\n") . fromMaybe "???" . roundTrip f)
$ ASCII.lines blob)
| AttoLineFormat f <- format = Just (mconcat
$ map ((<> "\n") . fromMaybe "???" . attoRoundTrip f)
$ ASCII.lines blob)
| AttoFormat f <- format,
Atto.Done remainder structure <- Atto.parse (parse f) blob =
(<> remainder) <$> serialize f structure
| AttoFormat f <- format, Atto.Partial i <- Atto.parse (parse f) blob,
Atto.Done remainder structure <- i mempty =
(<> remainder) <$> serialize f structure
return . (:[]) . testCase path $ assertEqual "round-trip" (hex format blob) (hex format blob')
hex :: TestFormat -> ByteString -> String
hex LineFormat{} = ASCII.unpack
hex AttoLineFormat{} = ASCII.unpack
hex _ = ByteString.foldr (pad . flip showHex "") ""
where pad [x] s = ['0', x] ++ s
pad [x, y] s = [x, y] ++ s