{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} module Read.Test where import Control.Applicative (Applicative(..), (<*)) import Control.Arrow (right) import Control.Monad.IO.Class (MonadIO(..)) import Data.Bool import Data.Char (Char) import qualified Data.Char as Char import Data.Data () import Data.Decimal (DecimalRaw(..)) import Data.Either (either, rights) import qualified Data.Foldable as Foldable import Data.Function (($), (.), id, const, flip) import Data.Functor ((<$>)) import Data.Functor.Identity (Identity(..)) import qualified Data.List as List import qualified Data.List.NonEmpty as NonEmpty import qualified Data.Map.Strict as Map import Data.Maybe (Maybe(..)) import Data.Monoid (Monoid(..), (<>)) import qualified Data.NonNull as NonNull import qualified Data.TreeMap.Strict as TreeMap import Data.Ord (Ord(..)) import Data.String (String) import Data.Text (Text) import qualified Data.Text as Text import qualified Data.Time.Calendar as Time import qualified Data.Time.LocalTime as Time import Data.Tuple (snd) import Prelude (error) import Test.Tasty import Test.Tasty.HUnit import qualified Text.Parsec as R hiding ( char , anyChar , crlf , newline , noneOf , oneOf , satisfy , space , spaces , string , tab ) import qualified Text.Parsec.Combinator.CorrectSourcePosWithTab as R import qualified Text.Parsec.Error.Custom as R import qualified Hcompta.LCC.Lib.Parsec as R import qualified Text.Parsec.Pos as R import Text.Show (Show(..)) import qualified Hcompta as H import qualified Hcompta.LCC as LCC test :: String -> Assertion -> TestTree test = testCase . elide . Foldable.foldMap escapeChar escapeChar :: Char -> String escapeChar c | Char.isPrint c = [c] escapeChar c = Char.showLitChar c "" elide :: String -> String elide s | List.length s > 42 = List.take 42 s List.++ ['…'] elide s = s account :: [Text] -> LCC.Account account = LCC.Account . NonNull.impureNonNull . (LCC.Name <$>) tag :: [Text] -> Text -> LCC.Tag tag p v = LCC.Tag (LCC.Tag_Path $ NonNull.impureNonNull (LCC.Name <$> p)) (LCC.Tag_Value v) tags :: [([Text], Text)] -> LCC.Tags tags l = LCC.Tags $ Map.fromListWith (flip mappend) $ (<$> l) $ \(p, v) -> (LCC.Tag_Path $ NonNull.impureNonNull (LCC.Name <$> p), [LCC.Tag_Value v]) amounts :: [(Text, LCC.Quantity)] -> LCC.Amounts amounts l = LCC.Amounts $ Map.fromList $ (<$> l) $ \(u, q) -> (LCC.Unit u, q) postings :: [LCC.Posting] -> LCC.Postings postings l = LCC.Postings $ Map.fromListWith (flip mappend) $ (<$> l) $ \p -> (LCC.posting_account p, [p]) comments :: [Text] -> [LCC.Comment] comments = (LCC.Comment <$>) tests :: TestTree tests = testGroup "Read" [ {-testGroup "read_date" $ (let (==>) (txt::Text) = test (Text.unpack txt) . (@?=) (rights [R.runParserWithError (LCC.read_date id Nothing <* R.eof) () "" txt]) in [ "2000-01-01" ==> [ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 0 0 0)) (Time.utc) ] , "2000/01/01" ==> [] , "2000-01-01_12:34" ==> [ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 12 34 0)) (Time.utc) ] , "2000-01-01_12:34:56" ==> [ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 12 34 56)) (Time.utc) ] , "2000-01-01_12:34_CET" ==> [ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 12 34 0)) (Time.TimeZone 60 True "CET") ] , "2000-01-01_12:34+01:30" ==> [ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 12 34 0)) (Time.TimeZone 90 False "+01:30") ] , "2000-01-01_12:34:56_CET" ==> [ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 12 34 56)) (Time.TimeZone 60 True "CET") ] , "2001-02-29" ==> [] ]) <> (let (==>) (txt::Text, def) = test (Text.unpack txt) . (@?=) (rights [R.runParserWithError (LCC.read_date id (Just def) <* R.eof) () "" txt]) in [ ("01-01", 2000) ==> [ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 0 0 0)) (Time.utc)] ]) , testGroup "read_account_section" $ let (==>) (txt::Text) b = test (Text.unpack txt) $ (@?=) (rights [R.runParser (LCC.read_account_section <* R.eof) () "" txt]) [LCC.Name txt | b] in [ "" ==> False , "A" ==> True , "AA" ==> True , " " ==> False , "/" ==> False , "A/" ==> False , "/A" ==> False , "A " ==> False , "A A" ==> False , "A " ==> False , "A\t" ==> False , "A \n" ==> False , "(A)A" ==> True , "( )A" ==> False , "(A) A" ==> False , "[ ] A" ==> False , "(A) " ==> False , "(A)" ==> True , "A(A)" ==> True , "[A]A" ==> True , "[A] A" ==> False , "[A] " ==> False , "[A]" ==> True , test "\"A \"" $ (rights [R.runParser (LCC.read_account_section) () "" ("A "::Text)]) @?= [LCC.Name "A"] ] , testGroup "read_account" $ let (==>) (txt::Text) expected = test (Text.unpack txt) $ (@?=) (rights [R.runParser (LCC.read_account <* R.eof) () "" txt]) (account <$> expected) in [ "" ==> [] , "A" ==> [] , "A/" ==> [] , "/A" ==> [ ["A"] ] , "A " ==> [] , " A" ==> [] , "/A/B" ==> [ ["A", "B"] ] , "/A/B/C" ==> [ ["A", "B","C"] ] , "/Aa/Bbb/Cccc" ==> [ ["Aa", "Bbb", "Cccc"] ] , "/A a / B b b / C c c c" ==> [] , "/A/ /C" ==> [] , "/A//C" ==> [] , "/A/B/(C)" ==> [ ["A", "B", "(C)"] ] ] , testGroup "read_amount" $ let (==>) (txt::Text) = test (Text.unpack txt) . (@?=) (rights [R.runParser (LCC.read_amount <* R.eof) () "" txt]) in [ "" ==> [] , "0" ==> [( mempty , LCC.amount { LCC.amount_quantity = Decimal 0 0 } )] , "00" ==> [( mempty , LCC.amount { LCC.amount_quantity = Decimal 0 0 } )] , "0." ==> [( mempty { LCC.amount_style_fractioning = Just '.' } , LCC.amount { LCC.amount_quantity = Decimal 0 0 } )] , ".0" ==> [( mempty { LCC.amount_style_fractioning = Just '.' } , LCC.amount { LCC.amount_quantity = Decimal 1 0 } )] , "0," ==> [( mempty { LCC.amount_style_fractioning = Just ',' } , LCC.amount { LCC.amount_quantity = Decimal 0 0 } )] , ",0" ==> [( mempty { LCC.amount_style_fractioning = Just ',' } , LCC.amount { LCC.amount_quantity = Decimal 1 0 } )] , "0_" ==> [] , "_0" ==> [] , "0.0" ==> [( mempty { LCC.amount_style_fractioning = Just '.' } , LCC.amount { LCC.amount_quantity = Decimal 1 0 } )] , "00.00" ==> [( mempty { LCC.amount_style_fractioning = Just '.' } , LCC.amount { LCC.amount_quantity = Decimal 2 0 } )] , "0,0" ==> [( mempty { LCC.amount_style_fractioning = Just ',' } , LCC.amount { LCC.amount_quantity = Decimal 1 0 } )] , "00,00" ==> [( mempty { LCC.amount_style_fractioning = Just ',' } , LCC.amount { LCC.amount_quantity = Decimal 2 0 } )] , "0_0" ==> [( mempty { LCC.amount_style_grouping_integral = Just $ LCC.Amount_Style_Grouping '_' [1] } , LCC.amount { LCC.amount_quantity = Decimal 0 0 } )] , "00_00" ==> [( mempty { LCC.amount_style_grouping_integral = Just $ LCC.Amount_Style_Grouping '_' [2] } , LCC.amount { LCC.amount_quantity = Decimal 0 0 } )] , "0,000.00" ==> [( mempty { LCC.amount_style_fractioning = Just '.' , LCC.amount_style_grouping_integral = Just $ LCC.Amount_Style_Grouping ',' [3] } , LCC.amount { LCC.amount_quantity = Decimal 2 0 } )] , "0.000,00" ==> [( mempty { LCC.amount_style_fractioning = Just ',' , LCC.amount_style_grouping_integral = Just $ LCC.Amount_Style_Grouping '.' [3] } , LCC.amount { LCC.amount_quantity = Decimal 2 0 } )] , "1,000.00" ==> [( mempty { LCC.amount_style_fractioning = Just '.' , LCC.amount_style_grouping_integral = Just $ LCC.Amount_Style_Grouping ',' [3] } , LCC.amount { LCC.amount_quantity = Decimal 2 100000 } )] , "1.000,00" ==> [( mempty { LCC.amount_style_fractioning = Just ',' , LCC.amount_style_grouping_integral = Just $ LCC.Amount_Style_Grouping '.' [3] } , LCC.amount { LCC.amount_quantity = Decimal 2 100000 } )] , "1,000.00." ==> [] , "1.000,00," ==> [] , "1,000.00_" ==> [] , "123" ==> [( mempty , LCC.amount { LCC.amount_quantity = Decimal 0 123 } )] , "1.2" ==> [( mempty { LCC.amount_style_fractioning = Just '.' } , LCC.amount { LCC.amount_quantity = Decimal 1 12 } )] , "1,2" ==> [( mempty { LCC.amount_style_fractioning = Just ',' } , LCC.amount { LCC.amount_quantity = Decimal 1 12 } )] , "12.34" ==> [( mempty { LCC.amount_style_fractioning = Just '.' } , LCC.amount { LCC.amount_quantity = Decimal 2 1234 } )] , "12,34" ==> [( mempty { LCC.amount_style_fractioning = Just ',' } , LCC.amount { LCC.amount_quantity = Decimal 2 1234 } )] , "1_2" ==> [( mempty { LCC.amount_style_grouping_integral = Just $ LCC.Amount_Style_Grouping '_' [1] } , LCC.amount { LCC.amount_quantity = Decimal 0 12 } )] , "1_23" ==> [( mempty { LCC.amount_style_grouping_integral = Just $ LCC.Amount_Style_Grouping '_' [2] } , LCC.amount { LCC.amount_quantity = Decimal 0 123 } )] , "1_23_456" ==> [( mempty { LCC.amount_style_grouping_integral = Just $ LCC.Amount_Style_Grouping '_' [3, 2] } , LCC.amount { LCC.amount_quantity = Decimal 0 123456 } )] , "1_23_456,7890_12345_678901" ==> [( mempty { LCC.amount_style_fractioning = Just ',' , LCC.amount_style_grouping_integral = Just $ LCC.Amount_Style_Grouping '_' [3, 2] , LCC.amount_style_grouping_fractional = Just $ LCC.Amount_Style_Grouping '_' [4, 5, 6] } , LCC.amount { LCC.amount_quantity = Decimal 15 123456789012345678901 } )] , "1_23_456.7890_12345_678901" ==> [( mempty { LCC.amount_style_fractioning = Just '.' , LCC.amount_style_grouping_integral = Just $ LCC.Amount_Style_Grouping '_' [3, 2] , LCC.amount_style_grouping_fractional = Just $ LCC.Amount_Style_Grouping '_' [4, 5, 6] } , LCC.amount { LCC.amount_quantity = Decimal 15 123456789012345678901 } )] , "1,23,456.7890_12345_678901" ==> [( mempty { LCC.amount_style_fractioning = Just '.' , LCC.amount_style_grouping_integral = Just $ LCC.Amount_Style_Grouping ',' [3, 2] , LCC.amount_style_grouping_fractional = Just $ LCC.Amount_Style_Grouping '_' [4, 5, 6] } , LCC.amount { LCC.amount_quantity = Decimal 15 123456789012345678901 } )] , "1.23.456,7890_12345_678901" ==> [( mempty { LCC.amount_style_fractioning = Just ',' , LCC.amount_style_grouping_integral = Just $ LCC.Amount_Style_Grouping '.' [3, 2] , LCC.amount_style_grouping_fractional = Just $ LCC.Amount_Style_Grouping '_' [4, 5, 6] } , LCC.amount { LCC.amount_quantity = Decimal 15 123456789012345678901 } )] , "123456_78901_2345.678_90_1" ==> [( mempty { LCC.amount_style_fractioning = Just '.' , LCC.amount_style_grouping_integral = Just $ LCC.Amount_Style_Grouping '_' [4, 5, 6] , LCC.amount_style_grouping_fractional = Just $ LCC.Amount_Style_Grouping '_' [3, 2] } , LCC.amount { LCC.amount_quantity = Decimal 6 123456789012345678901 } )] , "$1" ==> [( mempty { LCC.amount_style_unit_side = Just LCC.Amount_Style_Side_Left , LCC.amount_style_unit_spaced = Just False } , LCC.amount { LCC.amount_quantity = Decimal 0 1 , LCC.amount_unit = "$" } )] , "1$" ==> [( mempty { LCC.amount_style_unit_side = Just LCC.Amount_Style_Side_Right , LCC.amount_style_unit_spaced = Just False } , LCC.amount { LCC.amount_quantity = Decimal 0 1 , LCC.amount_unit = "$" } )] , "$ 1" ==> [( mempty { LCC.amount_style_unit_side = Just LCC.Amount_Style_Side_Left , LCC.amount_style_unit_spaced = Just True } , LCC.amount { LCC.amount_quantity = Decimal 0 1 , LCC.amount_unit = "$" } )] , "1 $" ==> [( mempty { LCC.amount_style_unit_side = Just LCC.Amount_Style_Side_Right , LCC.amount_style_unit_spaced = Just True } , LCC.amount { LCC.amount_quantity = Decimal 0 1 , LCC.amount_unit = "$" } )] , "-$1" ==> [( mempty { LCC.amount_style_unit_side = Just LCC.Amount_Style_Side_Left , LCC.amount_style_unit_spaced = Just False } , LCC.amount { LCC.amount_quantity = Decimal 0 (-1) , LCC.amount_unit = "$" } )] , "\"4 2\"1" ==> [( mempty { LCC.amount_style_unit_side = Just LCC.Amount_Style_Side_Left , LCC.amount_style_unit_spaced = Just False } , LCC.amount { LCC.amount_quantity = Decimal 0 1 , LCC.amount_unit = "4 2" } )] , "1\"4 2\"" ==> [( mempty { LCC.amount_style_unit_side = Just LCC.Amount_Style_Side_Right , LCC.amount_style_unit_spaced = Just False } , LCC.amount { LCC.amount_quantity = Decimal 0 1 , LCC.amount_unit = "4 2" } )] , "$1.000,00" ==> [( mempty { LCC.amount_style_fractioning = Just ',' , LCC.amount_style_grouping_integral = Just $ LCC.Amount_Style_Grouping '.' [3] , LCC.amount_style_unit_side = Just LCC.Amount_Style_Side_Left , LCC.amount_style_unit_spaced = Just False } , LCC.amount { LCC.amount_quantity = Decimal 2 100000 , LCC.amount_unit = "$" } )] , "1.000,00$" ==> [( mempty { LCC.amount_style_fractioning = Just ',' , LCC.amount_style_grouping_integral = Just $ LCC.Amount_Style_Grouping '.' [3] , LCC.amount_style_unit_side = Just LCC.Amount_Style_Side_Right , LCC.amount_style_unit_spaced = Just False } , LCC.amount { LCC.amount_quantity = Decimal 2 100000 , LCC.amount_unit = "$" } )] ] , testGroup "read_comment" $ let (==>) (txt::Text, end) expected = test (Text.unpack txt) $ (@?=) (rights [R.runParser (LCC.read_comment <* end) () "" txt]) (LCC.Comment <$> expected) in [ ("; some comment", R.eof) ==> ["some comment"] , ("; some comment \n", R.string " \n" <* R.eof) ==> [ "some comment" ] , ("; some comment \r\n", R.string " \r\n" <* R.eof) ==> [ "some comment" ] ] , testGroup "read_comments" $ let (==>) (txt::Text, end) expected = test (Text.unpack txt) $ (@?=) (rights [R.runParser (LCC.read_comments <* end) () "" txt]) ((LCC.Comment <$>) <$> expected) in [ ("; some comment\n ; some other comment", R.eof) ==> [ ["some comment", "some other comment"] ] , ("; some comment \n", R.string " \n" <* R.eof) ==> [ ["some comment"] ] ] , testGroup "read_transaction_tag" $ let (==>) (txt::Text, end) = test (Text.unpack txt) . (@?=) ((\(LCC.Transaction_Tag t) -> t) <$> rights [R.runParser (LCC.read_transaction_tag <* end) () "" txt]) in [ ("#Name" , R.eof) ==> [ tag ["Name"] "" ] , ("#Name:" , R.eof) ==> [] , ("#Name:name" , R.eof) ==> [ tag ["Name", "name"] "" ] , ("#Name=Value" , R.eof) ==> [ tag ["Name"] "Value" ] , ("#Name = Value" , R.eof) ==> [ tag ["Name"] "Value" ] , ("#Name=Value\n" , R.string "\n" <* R.eof) ==> [ tag ["Name"] "Value" ] , ("#Name=Val ue" , R.eof) ==> [ tag ["Name"] "Val ue" ] , ("#Name=," , R.eof) ==> [ tag ["Name"] "," ] , ("#Name=Val,ue" , R.eof) ==> [ tag ["Name"] "Val,ue" ] , ("#Name=Val,ue:" , R.eof) ==> [ tag ["Name"] "Val,ue:" ] , ("#Name=Val,ue :", R.eof) ==> [ tag ["Name"] "Val,ue :" ] ] , testGroup "read_posting" $ let (==>) (txt::Text) = let context_read = ( LCC.context_read (const ()) LCC.journal ::LCC.Context_Read () ()) in test (Text.unpack txt) . (@?=) ( either (const []) -- (error . show) pure $ R.runParserWithError (LCC.read_posting <* R.eof) context_read "" txt) . ((\p -> p { LCC.posting_sourcepos = R.newPos "" 1 1 }) <$>) in [ "/A/B/C" ==> [LCC.posting (account ["A", "B", "C"])] , "/A/B/C $1" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_amounts = amounts [("$", 1)] }] , "/A/B/C $1" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_amounts = amounts [("$", 1)] }] , "/A/B/C 1€" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_amounts = amounts [("€", 1)] }] , "/A/B/C $1; some comment" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_amounts = amounts [("$", 1)] , LCC.posting_comments = comments ["some comment"] }] , "/A/B/C; not a comment" ==> [] , "/A/B/C ; some comment" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_amounts = amounts [] , LCC.posting_comments = comments ["some comment"] }] , "/A/B/C ; some comment\n ; some other comment" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_amounts = amounts [] , LCC.posting_comments = comments ["some comment", "some other comment"] }] , "/A/B/C $1 ; some comment" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_amounts = amounts [("$", 1)] , LCC.posting_comments = comments ["some comment"] }] , "/A/B/C #N=V" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_tags = LCC.Posting_Tags $ tags [ (["N"], "V") ] }] , "/A/B/C #N:O=V" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_tags = LCC.Posting_Tags $ tags [ (["N", "O"], "V") ] }] , "/A/B/C #N=Val;ue" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_tags = LCC.Posting_Tags $ tags [ (["N"], "Val;ue") ] }] , "/A/B/C #N=Val#ue" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_tags = LCC.Posting_Tags $ tags [ (["N"], "Val#ue") ] }] , "/A/B/C #N=V ; not a comment" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_tags = LCC.Posting_Tags $ tags [ (["N"], "V ; not a comment") ] }] , "/A/B/C #N=V #O" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_tags = LCC.Posting_Tags $ tags [ (["N"], "V #O") ] }] , "/A/B/C #N#O" ==> [] , "/A/B/C #N; #O" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_tags = LCC.Posting_Tags $ tags [ (["N;"], ""), (["O"], "") ] }] , "/A/B/C #N #O" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_tags = LCC.Posting_Tags $ tags [ (["N"], ""), (["O"], "") ] }] , "/A/B/C \n #N=V" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_tags = LCC.Posting_Tags $ tags [ (["N"], "V") ] }] , "/A/B/C ; some comment\n #N=V" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_comments = comments ["some comment"] , LCC.posting_tags = LCC.Posting_Tags $ tags [ (["N"], "V") ] }] , "/A/B/C ; some comment\n #N=V v\n #N2=V2 v2" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_comments = comments ["some comment"] , LCC.posting_tags = LCC.Posting_Tags $ tags [ (["N"], "V v") , (["N2"], "V2 v2") ] }] , "/A/B/C\n #N=V\n #N=V2" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_tags = LCC.Posting_Tags $ tags [ (["N"], "V") , (["N"], "V2") ] }] , "/A/B/C\n #N=V\n #N2=V" ==> [(LCC.posting (account ["A", "B", "C"])) { LCC.posting_tags = LCC.Posting_Tags $ tags [ (["N"], "V") , (["N2"], "V") ] }] ] , testGroup "read_transaction" $ let (==>) (txt::Text) = let context_read = ( LCC.context_read (const ()) LCC.journal ::LCC.Context_Read () ()) in test (Text.unpack txt) . (@?=) ( either (error . show) pure $ R.runParserWithError (LCC.read_transaction <* R.newline <* R.eof) context_read "" txt) . ((\t -> t { LCC.transaction_sourcepos = R.newPos "" 1 1 }) <$>) in [ Text.unlines [ "2000-01-01 some wording" , " /A/B/C $1" , " /a/b/c $-1" ] ==> [LCC.transaction { LCC.transaction_dates= (`NonNull.ncons` []) $ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 0 0 0)) (Time.utc) , LCC.transaction_wording="some wording" , LCC.transaction_postings = postings [ (LCC.posting (account ["A", "B", "C"])) { LCC.posting_amounts = amounts [ ("$", 1) ] , LCC.posting_sourcepos = R.newPos "" 2 2 } , (LCC.posting (account ["a", "b", "c"])) { LCC.posting_amounts = amounts [ ("$", -1) ] , LCC.posting_sourcepos = R.newPos "" 3 2 } ] }] , Text.unlines [ "2000-01-01 some wording ; not a comment" , "; some other;comment" , " ; some last comment" , " /A/B/C $1" , " /a/b/c" ] ==> [LCC.transaction { LCC.transaction_comments = comments [ "some other;comment" , "some last comment" ] , LCC.transaction_dates= (`NonNull.ncons` []) $ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 0 0 0)) (Time.utc) , LCC.transaction_wording="some wording ; not a comment" , LCC.transaction_postings = postings [ (LCC.posting (account ["A", "B", "C"])) { LCC.posting_amounts = amounts [ ("$", 1) ] , LCC.posting_sourcepos = R.newPos "" 4 2 } , (LCC.posting (account ["a", "b", "c"])) { LCC.posting_amounts = amounts [ ("$", -1) ] , LCC.posting_sourcepos = R.newPos "" 5 2 } ] }] ] , testGroup "read_journal" $ let (==>) (lines::[Text]) e = let txt = Text.unlines lines in test (Text.unpack txt) $ do res <- liftIO $ right (\j -> j{LCC.journal_last_read_time=H.date_epoch}) <$> R.runParserTWithError (LCC.read_journal "" <* R.eof) ( LCC.context_read id LCC.journal ::LCC.Context_Read (LCC.Charted LCC.Transaction) [LCC.Charted LCC.Transaction]) "" txt (@?=) (rights [res]) e in [ [ "2000-01-01 1° wording" , " /A/B/C $1" , " /a/b/c" ] ==> [ LCC.journal { LCC.journal_content = (LCC.Charted mempty <$>) $ [ LCC.transaction { LCC.transaction_dates = (`NonNull.ncons` []) $ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 0 0 0)) (Time.utc) , LCC.transaction_wording="1° wording" , LCC.transaction_postings = postings [ (LCC.posting (account ["A", "B", "C"])) { LCC.posting_amounts = amounts [ ("$", 1) ] , LCC.posting_sourcepos = R.newPos "" 2 2 } , (LCC.posting (account ["a", "b", "c"])) { LCC.posting_amounts = amounts [ ("$", -1) ] , LCC.posting_sourcepos = R.newPos "" 3 2 } ] , LCC.transaction_sourcepos = R.newPos "" 1 1 } ] , LCC.journal_files = [""] , LCC.journal_amount_styles = LCC.Amount_Styles $ Map.fromList [ ( LCC.Unit "$" , mempty { LCC.amount_style_unit_side = Just LCC.Amount_Style_Side_Left , LCC.amount_style_unit_spaced = Just False } ) ] } ] , [ "2000-01-01 1° wording" , " /A/B/C $1" , " /a/b/c" , "2000-01-02 2° wording" , " /A/B/C $1" , " /x/y/z" ] ==> [ LCC.journal { LCC.journal_content = (LCC.Charted mempty <$>) $ [ LCC.transaction { LCC.transaction_dates = (`NonNull.ncons` []) $ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 02) (Time.TimeOfDay 0 0 0)) (Time.utc) , LCC.transaction_wording = "2° wording" , LCC.transaction_postings = postings [ (LCC.posting (account ["A", "B", "C"])) { LCC.posting_amounts = amounts [ ("$", 1) ] , LCC.posting_sourcepos = R.newPos "" 5 2 } , (LCC.posting (account ["x", "y", "z"])) { LCC.posting_amounts = amounts [ ("$", -1) ] , LCC.posting_sourcepos = R.newPos "" 6 2 } ] , LCC.transaction_sourcepos = R.newPos "" 4 1 } , LCC.transaction { LCC.transaction_dates = (`NonNull.ncons` []) $ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 0 0 0)) (Time.utc) , LCC.transaction_wording="1° wording" , LCC.transaction_postings = postings [ (LCC.posting (account ["A", "B", "C"])) { LCC.posting_amounts = amounts [ ("$", 1) ] , LCC.posting_sourcepos = R.newPos "" 2 2 } , (LCC.posting (account ["a", "b", "c"])) { LCC.posting_amounts = amounts [ ("$", -1) ] , LCC.posting_sourcepos = R.newPos "" 3 2 } ] , LCC.transaction_sourcepos = R.newPos "" 1 1 } ] , LCC.journal_files = [""] , LCC.journal_amount_styles = LCC.Amount_Styles $ Map.fromList [ ( LCC.Unit "$" , mempty { LCC.amount_style_unit_side = Just LCC.Amount_Style_Side_Left , LCC.amount_style_unit_spaced = Just False } ) ] } ] ] ,-} testGroup "read_chart" $ let (==>) (lines::[Text]) expected = let txt = Text.unlines lines in let context_read :: LCC.Context_Read () () = LCC.context_read (const ()) LCC.journal in test (Text.unpack txt) $ let res = runIdentity $ ((LCC.journal_chart . LCC.context_read_journal . snd <$>) <$>) $ R.runParserTWithError (R.and_state (LCC.read_chart <* R.eof)) context_read "" txt in rights [res] @?= expected in -- show res @?= show expected in let acct_path = NonEmpty.fromList . (LCC.Name <$>) in let acct_tags = LCC.Account_Tags . tags in [ [ "/A/B/C" , "/a/b/c" ] ==> [ LCC.Chart { LCC.chart_accounts = TreeMap.from_List mappend [ (acct_path ["A", "B", "C"], acct_tags []) , (acct_path ["a", "b", "c"], acct_tags []) ] , LCC.chart_anchors = Map.empty } ] , [ "/A/B/C" , " .N0:N1" , "/a/b/c" , " .N0:N1 = V0" , " .N0:N1 = V1" ] ==> [ LCC.Chart { LCC.chart_accounts = TreeMap.from_List mappend [ (acct_path ["A", "B", "C"], acct_tags [(["N0", "N1"], "")]) , (acct_path ["a", "b", "c"], acct_tags [ (["N0", "N1"], "V0") , (["N0", "N1"], "V1") ]) ] , LCC.chart_anchors = Map.empty } ] ] ]