1 {-# LANGUAGE NoMonomorphismRestriction #-}
2 {-# LANGUAGE Rank2Types #-}
3 {-# LANGUAGE TemplateHaskell #-}
4 {-# LANGUAGE UnboxedTuples #-}
7 import Control.Monad (Monad(..))
8 import Data.Char (Char)
9 import Data.Either (Either(..))
10 import Data.Function (($))
11 import Data.Semigroup (Semigroup(..))
12 import Data.String (String, IsString(..))
13 import Data.Text (Text)
14 import Data.Text.IO (readFile)
15 import System.IO (IO, FilePath)
17 import Test.Tasty.Golden
18 import Text.Show (Show(..))
19 import qualified Data.ByteString.Lazy as BSL
20 import qualified Data.IORef as IORef
21 import qualified Data.Text.Lazy as TL
22 import qualified Data.Text.Lazy.Encoding as TL
23 import qualified Language.Haskell.TH.Syntax as TH
25 import qualified Symantic.Parser as P
26 import qualified Symantic.Parser.Staging as H
27 import qualified Golden.Grammar as Grammar
30 goldensIO :: IO TestTree
31 goldensIO = return $ testGroup "Golden"
37 goldensGrammar :: TestTree
38 goldensGrammar = testGroup "Grammar"
39 [ testGroup "DumpComb" $ tests $ \name repr ->
40 let file = "test/Golden/Grammar/"<>name<>".dump" in
41 goldenVsStringDiff file diffGolden file $ do
43 return $ fromString $ show $
44 P.dumpComb $ P.observeSharing repr
45 , testGroup "OptimizeComb" $ tests $ \name repr ->
46 let file = "test/Golden/Grammar/"<>name<>".opt.dump" in
47 goldenVsStringDiff file diffGolden file $ do
49 return $ fromString $ show $
50 P.dumpComb $ P.optimizeComb $ P.observeSharing repr
53 tests :: P.Grammar repr =>
54 P.Satisfiable repr Char =>
55 (forall a. String -> repr a -> TestTree) -> [TestTree]
57 [ test "unit" $ P.unit
58 , test "unit-unit" $ P.unit P.*> P.unit
59 , test "app" $ P.pure (H.Haskell H.id) P.<*> P.unit
60 , test "many-a" $ P.many (P.char 'a')
61 , test "boom" $ Grammar.boom
62 , test "brainfuck" $ Grammar.brainfuck
63 , test "many-char-eof" $ P.many (P.char 'r') P.<* P.eof
67 goldensMachine :: TestTree
68 goldensMachine = testGroup "Machine"
69 [ testGroup "DumpInstr" $ tests $ \name repr ->
70 let file = "test/Golden/Machine/"<>name<>".dump" in
71 goldenVsStringDiff file diffGolden file $ do
73 return $ fromString $ show $
74 P.dumpInstr $ {-P.machine @() $ -}repr
79 P.Readable repr Char =>
80 (forall vs es ret. String -> repr Text vs es ret -> TestTree) -> [TestTree]
82 [ test "unit" $ P.machine $ P.unit
83 , test "unit-unit" $ P.machine $ P.unit P.*> P.unit
84 , test "a-or-b" $ P.machine $ P.char 'a' P.<|> P.char 'b'
85 , test "app" $ P.machine $ P.pure (H.Haskell H.id) P.<*> P.unit
86 , test "many-a" $ P.machine $ P.many (P.char 'a')
87 , test "boom" $ P.machine $ Grammar.boom
88 , test "brainfuck" $ P.machine $ Grammar.brainfuck
89 , test "many-char-eof" $ P.machine $ P.many (P.char 'r') P.<* P.eof
90 , test "eof" $ P.machine $ P.eof
91 , test "many-char-fail" $ P.machine $ P.many (P.char 'a') P.<* P.char 'b'
94 goldensParser :: TestTree
95 goldensParser = testGroup "Parser"
96 [ testGroup "runParser" $ tests $ \name p ->
97 let file = "test/Golden/Parser/"<>name in
98 goldenVsStringDiff (file<>".txt") diffGolden (file<>".dump") $ do
99 input :: Text <- readFile (file<>".txt")
100 return $ fromString $
106 tests :: (forall a. Show a => String -> (Text -> Either (P.ParsingError Text) a) -> TestTree) -> [TestTree]
108 [ test "char" $$(P.runParser $ P.char 'a')
109 , test "string" $$(P.runParser $ P.string "ab")
110 , test "many-char" $$(P.runParser $ P.many (P.char 'a'))
111 , test "alt-right" $$(P.runParser $ P.string "aa" P.<|> P.string "ab")
112 , test "alt-right-try" $$(P.runParser $ P.try (P.string "aa") P.<|> P.string "ab")
113 , test "alt-left" $$(P.runParser $ P.string "aa" P.<|> P.string "ab")
114 , test "many-char-eof" $$(P.runParser $ P.many (P.char 'r') P.<* P.eof)
115 , test "eof" $$(P.runParser $ P.eof)
116 , test "eof-fail" $$(P.runParser $ P.eof)
117 -- , test "alt-char-fail" $$(P.runParser $ P.char 'a' P.<|> P.char 'b')
118 -- , test "alt-char-fail" $$(P.runParser $ P.some (P.char 'a') P.<|> P.string "b")
119 , test "many-char-fail" $$(P.runParser $ P.many (P.char 'a') P.<* P.char 'b')
120 -- , test "alt-char-try-fail" $$(P.runParser $ P.try (P.char 'a') P.<|> P.char 'b')
123 -- | Resetting 'TH.counter' makes 'makeLetName' deterministic,
124 -- except when profiling is enabled, in this case those tests may fail
125 -- due to a different numbering of the 'def' and 'ref' combinators.
126 resetTHNameCounter :: IO ()
127 resetTHNameCounter = IORef.writeIORef TH.counter 0
129 -- * Golden testing utilities
131 diffGolden :: FilePath -> FilePath -> [String]
132 diffGolden ref new = ["diff", "-u", ref, new]
134 unLeft :: Either String BSL.ByteString -> IO BSL.ByteString
136 Left err -> return $ TL.encodeUtf8 $ TL.pack err