lib: make 'includedirectivep' an 'ErroringJournalParser'
- Update tests as well, requiring test utilities in Utils/Test.hs analogous to the those for 'JournalParser' but instead for 'ErroringJournalParser'
This commit is contained in:
		
							parent
							
								
									855a8f1985
								
							
						
					
					
						commit
						3e54fc77a4
					
				| @ -106,7 +106,7 @@ reader = Reader | ||||
| -- | Parse and post-process a "Journal" from hledger's journal file | ||||
| -- format, or give an error. | ||||
| parse :: InputOpts -> FilePath -> Text -> ExceptT String IO Journal | ||||
| parse iopts = parseAndFinaliseJournal' journalp' iopts | ||||
| parse iopts = parseAndFinaliseJournal journalp' iopts | ||||
|   where | ||||
|     journalp' = do  | ||||
|       -- reverse parsed aliases to ensure that they are applied in order given on commandline | ||||
| @ -124,10 +124,10 @@ aliasesFromOpts = map (\a -> fromparse $ runParser accountaliasp ("--alias "++qu | ||||
| -- | A journal parser. Accumulates and returns a "ParsedJournal", | ||||
| -- which should be finalised/validated before use. | ||||
| -- | ||||
| -- >>> rjp (journalp <* eof) "2015/1/1\n a  0\n" | ||||
| -- Right Journal  with 1 transactions, 1 accounts | ||||
| -- >>> rejp (journalp <* eof) "2015/1/1\n a  0\n" | ||||
| -- Right (Right Journal  with 1 transactions, 1 accounts) | ||||
| -- | ||||
| journalp :: MonadIO m => JournalParser m ParsedJournal | ||||
| journalp :: MonadIO m => ErroringJournalParser m ParsedJournal | ||||
| journalp = do | ||||
|   many addJournalItemP | ||||
|   eof | ||||
| @ -135,7 +135,7 @@ journalp = do | ||||
| 
 | ||||
| -- | A side-effecting parser; parses any kind of journal item | ||||
| -- and updates the parse state accordingly. | ||||
| addJournalItemP :: MonadIO m => JournalParser m () | ||||
| addJournalItemP :: MonadIO m => ErroringJournalParser m () | ||||
| addJournalItemP = | ||||
|   -- all journal line types can be distinguished by the first | ||||
|   -- character, can use choice without backtracking | ||||
| @ -154,7 +154,7 @@ addJournalItemP = | ||||
| -- | Parse any journal directive and update the parse state accordingly. | ||||
| -- Cf http://hledger.org/manual.html#directives, | ||||
| -- http://ledger-cli.org/3.0/doc/ledger3.html#Command-Directives | ||||
| directivep :: MonadIO m => JournalParser m () | ||||
| directivep :: MonadIO m => ErroringJournalParser m () | ||||
| directivep = (do | ||||
|   optional $ char '!' | ||||
|   choice [ | ||||
| @ -174,7 +174,7 @@ directivep = (do | ||||
|    ] | ||||
|   ) <?> "directive" | ||||
| 
 | ||||
| includedirectivep :: MonadIO m => JournalParser m () | ||||
| includedirectivep :: MonadIO m => ErroringJournalParser m () | ||||
| includedirectivep = do | ||||
|   string "include" | ||||
|   lift (skipSome spacenonewline) | ||||
| @ -784,8 +784,8 @@ tests_JournalReader = tests "JournalReader" [ | ||||
| 
 | ||||
|   ,tests "directivep" [ | ||||
|     test "supports !" $ do  | ||||
|       expectParse directivep "!account a\n" | ||||
|       expectParse directivep "!D 1.0\n" | ||||
|       expectParseE directivep "!account a\n" | ||||
|       expectParseE directivep "!D 1.0\n" | ||||
|     ] | ||||
| 
 | ||||
|   ,test "accountdirectivep" $ do | ||||
| @ -808,8 +808,8 @@ tests_JournalReader = tests "JournalReader" [ | ||||
|      expectParse ignoredpricecommoditydirectivep "N $\n" | ||||
| 
 | ||||
|   ,test "includedirectivep" $ do | ||||
|     test "include" $ expectParseError includedirectivep "include nosuchfile\n" "No existing files match pattern: nosuchfile" | ||||
|     test "glob" $ expectParseError includedirectivep "include nosuchfile*\n" "No existing files match pattern: nosuchfile*" | ||||
|     test "include" $ expectParseErrorE includedirectivep "include nosuchfile\n" "No existing files match pattern: nosuchfile" | ||||
|     test "glob" $ expectParseErrorE includedirectivep "include nosuchfile*\n" "No existing files match pattern: nosuchfile*" | ||||
| 
 | ||||
|   ,test "marketpricedirectivep" $ expectParseEq marketpricedirectivep | ||||
|     "P 2017/01/30 BTC $922.83\n" | ||||
| @ -828,7 +828,7 @@ tests_JournalReader = tests "JournalReader" [ | ||||
| 
 | ||||
| 
 | ||||
|   ,tests "journalp" [ | ||||
|     test "empty file" $ expectParseEq journalp "" nulljournal | ||||
|     test "empty file" $ expectParseEqE journalp "" nulljournal | ||||
|     ] | ||||
| 
 | ||||
|   ] | ||||
|  | ||||
| @ -16,13 +16,18 @@ module Hledger.Utils.Test ( | ||||
|   ,is | ||||
|   ,expectEqPP | ||||
|   ,expectParse | ||||
|   ,expectParseE | ||||
|   ,expectParseError | ||||
|   ,expectParseErrorE | ||||
|   ,expectParseEq | ||||
|   ,expectParseEqE | ||||
|   ,expectParseEqOn | ||||
|   ,expectParseEqOnE | ||||
| )  | ||||
| where | ||||
| 
 | ||||
| import Control.Exception | ||||
| import Control.Monad.Except (ExceptT, runExceptT) | ||||
| import Control.Monad.State.Strict (StateT, evalStateT) | ||||
| #if !(MIN_VERSION_base(4,11,0)) | ||||
| import Data.Monoid ((<>)) | ||||
| @ -101,6 +106,7 @@ is = flip expectEqPP | ||||
| 
 | ||||
| -- | Test that this stateful parser runnable in IO successfully parses  | ||||
| -- all of the given input text, showing the parse error if it fails.  | ||||
| 
 | ||||
| -- Suitable for hledger's JournalParser parsers. | ||||
| expectParse :: (Monoid st, Eq a, Show a, HasCallStack) =>  | ||||
|   StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> E.Test () | ||||
| @ -108,6 +114,24 @@ expectParse parser input = do | ||||
|   ep <- E.io (runParserT (evalStateT (parser <* eof) mempty) "" input) | ||||
|   either (fail.(++"\n").("\nparse error at "++).parseErrorPretty) (const ok) ep | ||||
| 
 | ||||
| -- Suitable for hledger's ErroringJournalParser parsers. | ||||
| expectParseE | ||||
|   :: (Monoid st, Eq a, Show a, HasCallStack) | ||||
|   => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a | ||||
|   -> T.Text | ||||
|   -> E.Test () | ||||
| expectParseE parser input = do | ||||
|   let filepath = "" | ||||
|   eep <- E.io $ runExceptT $ | ||||
|            runParserT (evalStateT (parser <* eof) mempty) filepath input | ||||
|   case eep of | ||||
|     Left finalErr -> | ||||
|       let prettyErr = finalParseErrorPretty $ attachSource filepath input finalErr | ||||
|       in  fail $ "parse error at " <> prettyErr | ||||
|     Right ep -> either (fail.(++"\n").("\nparse error at "++).parseErrorPretty) | ||||
|                        (const ok) | ||||
|                        ep | ||||
| 
 | ||||
| -- | Test that this stateful parser runnable in IO fails to parse  | ||||
| -- the given input text, with a parse error containing the given string.  | ||||
| expectParseError :: (Monoid st, Eq a, Show a, HasCallStack) =>  | ||||
| @ -122,12 +146,43 @@ expectParseError parser input errstr = do | ||||
|       then ok | ||||
|       else fail $ "\nparse error is not as expected:\n" ++ e' ++ "\n" | ||||
| 
 | ||||
| expectParseErrorE | ||||
|   :: (Monoid st, Eq a, Show a, HasCallStack) | ||||
|   => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a | ||||
|   -> T.Text | ||||
|   -> String | ||||
|   -> E.Test () | ||||
| expectParseErrorE parser input errstr = do | ||||
|   let filepath = "" | ||||
|   eep <- E.io $ runExceptT $ runParserT (evalStateT parser mempty) filepath input | ||||
|   case eep of | ||||
|     Left finalErr -> do | ||||
|       let prettyErr = finalParseErrorPretty $ attachSource filepath input finalErr | ||||
|       if errstr `isInfixOf` prettyErr | ||||
|       then ok | ||||
|       else fail $ "\nparse error is not as expected:\n" ++ prettyErr ++ "\n" | ||||
|     Right ep -> case ep of | ||||
|       Right v -> fail $ "\nparse succeeded unexpectedly, producing:\n" ++ pshow v ++ "\n" | ||||
|       Left e  -> do | ||||
|         let e' = parseErrorPretty e | ||||
|         if errstr `isInfixOf` e' | ||||
|         then ok | ||||
|         else fail $ "\nparse error is not as expected:\n" ++ e' ++ "\n" | ||||
| 
 | ||||
| -- | Like expectParse, but also test the parse result is an expected value, | ||||
| -- pretty-printing both if it fails.  | ||||
| expectParseEq :: (Monoid st, Eq a, Show a, HasCallStack) =>  | ||||
|   StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> a -> E.Test () | ||||
| expectParseEq parser input expected = expectParseEqOn parser input id expected | ||||
| 
 | ||||
| expectParseEqE | ||||
|   :: (Monoid st, Eq a, Show a, HasCallStack) | ||||
|   => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a | ||||
|   -> T.Text | ||||
|   -> a | ||||
|   -> E.Test () | ||||
| expectParseEqE parser input expected = expectParseEqOnE parser input id expected | ||||
| 
 | ||||
| -- | Like expectParseEq, but transform the parse result with the given function  | ||||
| -- before comparing it. | ||||
| expectParseEqOn :: (Monoid st, Eq b, Show b, HasCallStack) =>  | ||||
| @ -136,3 +191,23 @@ expectParseEqOn parser input f expected = do | ||||
|   ep <- E.io $ runParserT (evalStateT (parser <* eof) mempty) "" input | ||||
|   either (fail . (++"\n") . ("\nparse error at "++) . parseErrorPretty) (expectEqPP expected . f) ep | ||||
| 
 | ||||
| expectParseEqOnE | ||||
|   :: (Monoid st, Eq b, Show b, HasCallStack) | ||||
|   => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a | ||||
|   -> T.Text | ||||
|   -> (a -> b) | ||||
|   -> b | ||||
|   -> E.Test () | ||||
| expectParseEqOnE parser input f expected = do | ||||
|   let filepath = "" | ||||
|   eep <- E.io $ runExceptT $ | ||||
|            runParserT (evalStateT (parser <* eof) mempty) filepath input | ||||
|   case eep of | ||||
|     Left finalErr -> | ||||
|       let prettyErr = finalParseErrorPretty $ attachSource filepath input finalErr | ||||
|       in  fail $ "parse error at " <> prettyErr | ||||
|     Right ep -> | ||||
|       either (fail . (++"\n") . ("\nparse error at "++) . parseErrorPretty) | ||||
|              (expectEqPP expected . f) | ||||
|              ep | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user