{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Hledger.Utils.Test (
module Test.Tasty
,module Test.Tasty.HUnit
,tests
,test
,assertLeft
,assertRight
,assertParse
,assertParseEq
,assertParseEqOn
,assertParseError
,assertParseE
,assertParseEqE
,assertParseErrorE
,assertParseStateOn
)
where
import Control.Monad.Except (ExceptT, runExceptT)
import Control.Monad.State.Strict (StateT, evalStateT, execStateT)
#if !(MIN_VERSION_base(4,11,0))
import Data.Monoid ((<>))
#endif
import Data.List (isInfixOf)
import qualified Data.Text as T
import Test.Tasty hiding (defaultMain)
import Test.Tasty.HUnit
import Text.Megaparsec
import Text.Megaparsec.Custom
( CustomErr,
FinalParseError,
attachSource,
customErrorBundlePretty,
finalErrorBundlePretty,
)
import Hledger.Utils.Debug (pshow)
tests :: String -> [TestTree] -> TestTree
tests = testGroup
test :: String -> Assertion -> TestTree
test = testCase
assertLeft :: (HasCallStack, Eq b, Show b) => Either a b -> Assertion
assertLeft (Left _) = return ()
assertLeft (Right b) = assertFailure $ "expected Left, got (Right " ++ show b ++ ")"
assertRight :: (HasCallStack, Eq a, Show a) => Either a b -> Assertion
assertRight (Right _) = return ()
assertRight (Left a) = assertFailure $ "expected Right, got (Left " ++ show a ++ ")"
assertParse :: (HasCallStack, Eq a, Show a, Monoid st) =>
StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> Assertion
assertParse parser input = do
ep <- runParserT (evalStateT (parser <* eof) mempty) "" input
either (assertFailure.(++"\n").("\nparse error at "++).customErrorBundlePretty)
(const $ return ())
ep
assertParseEq :: (HasCallStack, Eq a, Show a, Monoid st) =>
StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> a -> Assertion
assertParseEq parser input expected = assertParseEqOn parser input id expected
assertParseEqOn :: (HasCallStack, Eq b, Show b, Monoid st) =>
StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> (a -> b) -> b -> Assertion
assertParseEqOn parser input f expected = do
ep <- runParserT (evalStateT (parser <* eof) mempty) "" input
either (assertFailure . (++"\n") . ("\nparse error at "++) . customErrorBundlePretty)
(assertEqual "" expected . f)
ep
assertParseError :: (HasCallStack, Eq a, Show a, Monoid st) =>
StateT st (ParsecT CustomErr T.Text IO) a -> String -> String -> Assertion
assertParseError parser input errstr = do
ep <- runParserT (evalStateT parser mempty) "" (T.pack input)
case ep of
Right v -> assertFailure $ "\nparse succeeded unexpectedly, producing:\n" ++ pshow v ++ "\n"
Left e -> do
let e' = customErrorBundlePretty e
if errstr `isInfixOf` e'
then return ()
else assertFailure $ "\nparse error is not as expected:\n" ++ e' ++ "\n"
assertParseStateOn :: (HasCallStack, Eq b, Show b, Monoid st) =>
StateT st (ParsecT CustomErr T.Text IO) a
-> T.Text
-> (st -> b)
-> b
-> Assertion
assertParseStateOn parser input f expected = do
es <- runParserT (execStateT (parser <* eof) mempty) "" input
case es of
Left err -> assertFailure $ (++"\n") $ ("\nparse error at "++) $ customErrorBundlePretty err
Right s -> assertEqual "" expected $ f s
assertParseE
:: (HasCallStack, Eq a, Show a, Monoid st)
=> StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
-> T.Text
-> Assertion
assertParseE parser input = do
let filepath = ""
eep <- runExceptT $
runParserT (evalStateT (parser <* eof) mempty) filepath input
case eep of
Left finalErr ->
let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr
in assertFailure $ "parse error at " <> prettyErr
Right ep ->
either (assertFailure.(++"\n").("\nparse error at "++).customErrorBundlePretty)
(const $ return ())
ep
assertParseEqE
:: (Monoid st, Eq a, Show a, HasCallStack)
=> StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
-> T.Text
-> a
-> Assertion
assertParseEqE parser input expected = assertParseEqOnE parser input id expected
assertParseEqOnE
:: (HasCallStack, Eq b, Show b, Monoid st)
=> StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
-> T.Text
-> (a -> b)
-> b
-> Assertion
assertParseEqOnE parser input f expected = do
let filepath = ""
eep <- runExceptT $ runParserT (evalStateT (parser <* eof) mempty) filepath input
case eep of
Left finalErr ->
let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr
in assertFailure $ "parse error at " <> prettyErr
Right ep ->
either (assertFailure . (++"\n") . ("\nparse error at "++) . customErrorBundlePretty)
(assertEqual "" expected . f)
ep
assertParseErrorE
:: (Monoid st, Eq a, Show a, HasCallStack)
=> StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
-> T.Text
-> String
-> Assertion
assertParseErrorE parser input errstr = do
let filepath = ""
eep <- runExceptT $ runParserT (evalStateT parser mempty) filepath input
case eep of
Left finalErr -> do
let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr
if errstr `isInfixOf` prettyErr
then return ()
else assertFailure $ "\nparse error is not as expected:\n" ++ prettyErr ++ "\n"
Right ep -> case ep of
Right v -> assertFailure $ "\nparse succeeded unexpectedly, producing:\n" ++ pshow v ++ "\n"
Left e -> do
let e' = customErrorBundlePretty e
if errstr `isInfixOf` e'
then return ()
else assertFailure $ "\nparse error is not as expected:\n" ++ e' ++ "\n"