-- | Tests which require particular prelude files. module System.Console.Hawk.PreludeTests () where import System.FilePath import System.Console.Hawk -- | A helper for specifying which arguments to pass to Hawk. -- Simplifies testing. testBuilder :: FilePath -> FilePath -> [String] -> String -> FilePath -> IO () testBuilder preludeBase preludeBasename flags expr inputBasename = processArgs args where args = preludeArgs preludeBasename ++ flags ++ [expr] ++ inputArgs inputBasename preludePath f = preludeBase f inputPath f = "tests" "inputs" f preludeArgs f = ["-c", preludePath f] inputArgs "" = [] inputArgs f = [inputPath f] -- | A version of `testBuilder` without a custom prelude. -- -- We still need to specify a prelude file, because the user running these -- tests might have installed a custom prelude. test :: [String] -> String -> FilePath -> IO () test = testBuilder ("tests" "preludes") "default" -- | A version of `test` without a custom input file either. testEval :: [String] -> String -> IO () testEval flags expr = test flags expr "" -- | A version of `testBuilder` using the preludes from "tests/preludes". -- -- The first example from the README: -- -- >>> test ["-d:", "-m"] "head" "passwd" -- root -- -- -- The second example, which adds `takeLast` to the user prelude: -- -- >>> testPrelude "readme" ["-a"] "takeLast 3" "0-100" -- 98 -- 99 -- 100 -- -- -- The last example from the README, a quick test to validate that Hawk was -- properly installed: -- -- >>> testEval [] "[1..3]" -- 1 -- 2 -- 3 -- -- Making sure that we support multi-line, whitespace-sensitive expressions: -- -- >>> testEval [] "do x <- [1,2,3]\n y <- [10,20,30]\n return (x+y)" -- 11 -- 21 -- 31 -- 12 -- 22 -- 32 -- 13 -- 23 -- 33 -- -- Making sure that we report errors in the user expression, not our wrapper code: -- -- >>> testEval [] "[[]," -- error: Won't compile: -- user expression:1:5: -- parse error (possibly incorrect indentation or mismatched brackets) -- *** Exception: ExitFailure 1 -- -- Making sure that setting line-mode or stream-mode doesn't affect the output format -- unless specifically requested: -- -- >>> testPrelude "default" ["-d", "-m"] "\\x -> ('a', x, 'b')" "1-3" -- a 1 b -- a 2 b -- a 3 b -- -- >>> testPrelude "default" ["-d", "-o", "-m"] "\\x -> ('a', x, 'b')" "1-3" -- a1b -- a2b -- a3b -- -- >>> testPrelude "default" ["-D", "-a"] "\\x -> ('a', x, 'b')" "equation" -- a x1*y1*z1 + x2*y2*z2 b -- -- >>> testPrelude "default" ["-D", "-O", "-a"] "\\x -> ('a', x, 'b')" "equation" -- ax1*y1*z1 + x2*y2*z2b -- -- Making sure that we don't assume the user prelude exports "map": -- -- >>> testPrelude "set" ["-m"] "const $ \"hello\"" "1-3" -- hello -- hello -- hello -- -- Making sure that we can find "map" even with NoImplicitPrelude: -- -- >>> testPrelude "noImplicitPrelude" ["-m"] "\\_ -> hello" "1-3" -- hello -- hello -- hello -- -- Making sure sequences of whitespace count as one delimiter: -- -- >>> testPrelude "default" ["-a"] "L.transpose" "1-12" -- 1 4 7 10 -- 2 5 8 11 -- 3 6 9 12 testPrelude :: FilePath -> [String] -> String -> FilePath -> IO () testPrelude = testBuilder ("tests" "preludes") -- | A version of `testBuilder` using the preludes from the documentation. -- -- All the examples from the documentation: -- -- >>> test ["-a"] "L.reverse" "1-3" -- 3 -- 2 -- 1 -- -- >>> test ["-ad"] "L.takeWhile (/=\"7\") . L.dropWhile (/=\"3\")" "1-10" -- 3 -- 4 -- 5 -- 6 -- -- >>> testDoc "between" ["-ad"] "between \"3\" \"7\"" "1-10" -- 3 -- 4 -- 5 -- 6 -- -- >>> test ["-a"] "L.take 3" "1-10" -- 1 -- 2 -- 3 -- -- >>> test ["-m"] "L.reverse" "1-9" -- 3 2 1 -- 6 5 4 -- 9 8 7 -- -- >>> testDoc "postorder" ["-ad"] "postorder (\\x -> printf \"(%s)\" . L.intercalate \" \" . (unpack x:))" "example.in" -- (foo (bar1) (bar2 (baz)) (bar3)) -- -- >>> test ["-ad"] "sum . L.map (read . B.unpack)" "1-3" -- 6 -- -- >>> testDoc "conversions" ["-ad"] "sum . L.map toInt" "1-3" -- 6 -- -- >>> testEval [] "2 ^ 100" -- 1267650600228229401496703205376 -- -- >>> test ["-a"] "L.take 2" "1-9" -- 1 2 3 -- 4 5 6 -- -- >>> test ["-m"] "L.take 2" "1-9" -- 1 2 -- 4 5 -- 7 8 -- -- >>> test ["-a"] "show" "1-9" -- [["1","2","3"],["4","5","6"],["7","8","9"]] -- -- >>> test ["-a"] "id :: [[B.ByteString]] -> [[B.ByteString]]" "1-9" -- 1 2 3 -- 4 5 6 -- 7 8 9 -- -- >>> test ["-a", "-d\\t"] "L.transpose" "1-9tabs" -- 1 4 7 -- 2 5 8 -- 3 6 9 -- -- >>> test ["-ad,"] "L.transpose" "1-9commas" -- 1,4,7 -- 2,5,8 -- 3,6,9 -- -- >>> test ["-D + ", "-d*", "-a"] "L.transpose" "equation" -- x1*x2 + y1*y2 + z1*z2 -- -- >>> testDoc "aeson" ["-aD"] "show . P.parse J.json" "json" -- Done "\n" Object (fromList [("code",Number 200.0),("message",String "OK")]) -- -- >>> test ["-d", "-a"] "show :: [B.ByteString] -> String" "1-3" -- ["1","2","3"] -- -- >>> test ["-d", "-D", "-a"] "show :: B.ByteString -> String" "1-3" -- "1\n2\n3\n" -- -- >>> testEval [] "[[B.pack \"1\",B.pack \"2\"], [B.pack \"3\",B.pack \"4\"]]" -- 1 2 -- 3 4 -- -- >>> testEval [] "[[\"1\",\"2\"], [\"3\",\"4\"]]" -- 1 2 -- 3 4 -- -- >>> testEval [] "[[1,2], [3,4]] :: [[Float]]" -- 1.0 2.0 -- 3.0 4.0 -- -- >>> testEval [] "1 :: Double" -- 1.0 -- -- >>> testEval [] "(True,False)" -- True -- False -- -- >>> testEval [] "[(1,2),(3,4)] :: [(Int,Float)]" -- 1 2.0 -- 3 4.0 -- -- >>> testEval ["-O or "] "(True,False)" -- True or False -- -- >>> testEval ["-o\\t"] "[(1,2),(3,4.0)] :: [(Int,Float)]" -- 1 2.0 -- 3 4.0 -- -- >>> test ["-m", "-d ", "-o*", "-D\\n", "-O+"] "id" "1-6" -- 1*2*3+4*5*6 -- -- >> testEval ["-a"] "L.length" -- 3 testDoc :: String -> [String] -> String -> FilePath -> IO () testDoc = testBuilder "doc"