symantic-parser-0.0.0.20210101: test/Golden.hs
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UnboxedTuples #-}
module Golden where
import Control.Monad (Monad(..))
import Data.Char (Char)
import Data.Either (Either(..))
import Data.Function (($))
import Data.Semigroup (Semigroup(..))
import Data.String (String, IsString(..))
import Data.Text (Text)
import Data.Text.IO (readFile)
import System.IO (IO, FilePath)
import Test.Tasty
import Test.Tasty.Golden
import Text.Show (Show(..))
import qualified Data.ByteString.Lazy as BSL
import qualified Data.IORef as IORef
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Encoding as TL
import qualified Language.Haskell.TH.Syntax as TH
import qualified Symantic.Parser as P
import qualified Symantic.Parser.Haskell as H
import qualified Golden.Grammar as Grammar
--import Golden.Utils
goldensIO :: IO TestTree
goldensIO = return $ testGroup "Golden"
[ goldensGrammar
-- Commented-out for the release
-- because resetTHNameCounter is not enough:
-- TH names still change between runs
-- with and without --accept
-- , goldensMachine
, goldensParser
]
goldensGrammar :: TestTree
goldensGrammar = testGroup "Grammar"
[ testGroup "DumpComb" $ tests $ \name repr ->
let file = "test/Golden/Grammar/"<>name<>".dump" in
goldenVsStringDiff file diffGolden file $ do
resetTHNameCounter
return $ fromString $ show $
P.dumpComb $ P.observeSharing repr
, testGroup "OptimizeComb" $ tests $ \name repr ->
let file = "test/Golden/Grammar/"<>name<>".opt.dump" in
goldenVsStringDiff file diffGolden file $ do
resetTHNameCounter
return $ fromString $ show $
P.dumpComb $ P.optimizeComb $ P.observeSharing repr
]
where
tests :: P.Grammar repr =>
P.Satisfiable repr Char =>
(forall a. String -> repr a -> TestTree) -> [TestTree]
tests test =
[ test "unit" $ P.unit
, test "unit-unit" $ P.unit P.*> P.unit
, test "app" $ P.pure (H.Haskell H.id) P.<*> P.unit
, test "many-a" $ P.many (P.char 'a')
, test "boom" $ Grammar.boom
, test "brainfuck" $ Grammar.brainfuck
, test "many-char-eof" $ P.many (P.char 'r') P.<* P.eof
, test "eof" $ P.eof
]
goldensMachine :: TestTree
goldensMachine = testGroup "Machine"
[ testGroup "DumpInstr" $ tests $ \name repr ->
let file = "test/Golden/Machine/"<>name<>".dump" in
goldenVsStringDiff file diffGolden file $ do
resetTHNameCounter
return $ fromString $ show $
P.dumpInstr $ {-P.machine @() $ -}repr
]
where
tests ::
P.Executable repr =>
P.Readable repr Char =>
(forall vs es ret. String -> repr Text vs es ret -> TestTree) -> [TestTree]
tests test =
[ test "unit" $ P.machine $ P.unit
, test "unit-unit" $ P.machine $ P.unit P.*> P.unit
, test "a-or-b" $ P.machine $ P.char 'a' P.<|> P.char 'b'
, test "app" $ P.machine $ P.pure (H.Haskell H.id) P.<*> P.unit
, test "many-a" $ P.machine $ P.many (P.char 'a')
, test "boom" $ P.machine $ Grammar.boom
, test "brainfuck" $ P.machine $ Grammar.brainfuck
, test "many-char-eof" $ P.machine $ P.many (P.char 'r') P.<* P.eof
, test "eof" $ P.machine $ P.eof
, test "many-char-fail" $ P.machine $ P.many (P.char 'a') P.<* P.char 'b'
]
goldensParser :: TestTree
goldensParser = testGroup "Parser"
[ testGroup "runParser" $ tests $ \name p ->
let file = "test/Golden/Parser/"<>name in
goldenVsStringDiff (file<>".txt") diffGolden (file<>".dump") $ do
input :: Text <- readFile (file<>".txt")
return $ fromString $
case p input of
Left err -> show err
Right a -> show a
]
where
tests :: (forall a. Show a => String -> (Text -> Either (P.ParsingError Text) a) -> TestTree) -> [TestTree]
tests test =
[ test "char" $$(P.runParser $ P.char 'a')
, test "string" $$(P.runParser $ P.string "ab")
, test "many-char" $$(P.runParser $ P.many (P.char 'a'))
, test "alt-right" $$(P.runParser $ P.string "aa" P.<|> P.string "ab")
, test "alt-right-try" $$(P.runParser $ P.try (P.string "aa") P.<|> P.string "ab")
, test "alt-left" $$(P.runParser $ P.string "aa" P.<|> P.string "ab")
, test "many-char-eof" $$(P.runParser $ P.many (P.char 'r') P.<* P.eof)
, test "eof" $$(P.runParser $ P.eof)
, test "eof-fail" $$(P.runParser $ P.eof)
-- , test "alt-char-fail" $$(P.runParser $ P.char 'a' P.<|> P.char 'b')
-- , test "alt-char-fail" $$(P.runParser $ P.some (P.char 'a') P.<|> P.string "b")
, test "many-char-fail" $$(P.runParser $ P.many (P.char 'a') P.<* P.char 'b')
-- , test "alt-char-try-fail" $$(P.runParser $ P.try (P.char 'a') P.<|> P.char 'b')
]
-- | Resetting 'TH.counter' makes 'makeLetName' deterministic,
-- except when profiling is enabled, in this case those tests may fail
-- due to a different numbering of the 'def' and 'ref' combinators.
resetTHNameCounter :: IO ()
resetTHNameCounter = IORef.writeIORef TH.counter 0
-- * Golden testing utilities
diffGolden :: FilePath -> FilePath -> [String]
diffGolden ref new = ["diff", "-u", ref, new]
unLeft :: Either String BSL.ByteString -> IO BSL.ByteString
unLeft = \case
Left err -> return $ TL.encodeUtf8 $ TL.pack err
Right a -> return a