packages feed

penny-0.32.0.10: tests/Copper/Parser.hs

{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
module Copper.Parser where

import Control.Applicative ((<*))
import qualified Copper.Gen.Parsers as GP
import qualified Penny.Copper.Parsec as CP
import qualified Penny.Copper as C
import qualified Penny.Lincoln as L
import Penny.Lincoln ((==~))
import qualified Test.QuickCheck as Q
import qualified Test.QuickCheck.Property as QCP
import Test.QuickCheck (Gen)
import qualified Text.Parsec as P
import qualified Text.Parsec.Text as P
import qualified Data.Text as X
import Test.Tasty.QuickCheck (testProperty)
import Test.Tasty (testGroup, TestTree)

parseProp
  :: (Eq b, Show b)
  => P.Parser a
  -> Gen (b, X.Text)
  -> (a -> b)
  -> Q.Property
parseProp p g f = Q.forAll g $ \(b, x) ->
  case P.parse (p <* P.eof) "" x of
    Left e -> QCP.failed { QCP.reason = "parse failed: " ++ show e }
    Right gd ->
      if f gd == b
      then QCP.succeeded
      else QCP.failed { QCP.reason = "result not identical to expected: "
                                     ++ show (f gd) }

parsePropEv
  :: (L.Equivalent b, Show b)
  => P.Parser a
  -> Gen (b, X.Text)
  -> (a -> b)
  -> Q.Property
parsePropEv p g f = Q.forAll g $ \(b, x) ->
  case P.parse (p <* P.eof) "" x of
    Left e -> QCP.failed { QCP.reason = "parse failed: " ++ show e }
    Right gd ->
      if f gd ==~ b
      then QCP.succeeded
      else QCP.failed { QCP.reason = "result not identical to expected: "
                                     ++ show (f gd) }

prop_lvl1SubAcct = parseProp CP.lvl1SubAcct GP.lvl1SubAcct id
prop_lvl1FirstSubAcct =
  parseProp CP.lvl1FirstSubAcct GP.lvl1FirstSubAcct id

prop_lvl1OtherSubAcct =
  parseProp CP.lvl1OtherSubAcct GP.lvl1OtherSubAcct id

prop_lvl1Acct =
  parseProp CP.lvl1Acct GP.lvl1Acct id

prop_quotedLvl1Acct = parseProp CP.quotedLvl1Acct GP.quotedLvl1Acct id

prop_lvl2FirstSubAcct =
  parseProp CP.lvl2FirstSubAcct GP.lvl2FirstSubAcct id

prop_lvl2OtherSubAcct =
  parseProp CP.lvl2OtherSubAcct GP.lvl2OtherSubAcct id

prop_lvl2Acct =
  parseProp CP.lvl2Acct GP.lvl2Acct id

prop_ledgerAcct =
  parseProp CP.ledgerAcct GP.ledgerAcct id

prop_lvl1Cmdty =
  parseProp CP.lvl1Cmdty GP.lvl1Cmdty id

prop_quotedLvl1Cmdty =
  let gen = fmap (\(GP.QuotedLvl1Cmdty c x) -> (c, x)) GP.quotedLvl1Cmdty
  in parseProp CP.quotedLvl1Cmdty gen id

prop_lvl2Cmdty =
  let gen = fmap (\(GP.Lvl2Cmdty c x) -> (c, x)) GP.lvl2Cmdty
  in parseProp CP.lvl2Cmdty gen id


prop_lvl3Cmdty =
  let gen = fmap (\(GP.Lvl3Cmdty c x) -> (c, x)) GP.lvl3Cmdty
  in parseProp CP.lvl3Cmdty gen id


prop_qtyRep = parsePropEv CP.qtyRep GP.ast id

doParse
  :: (Show a, Eq a)
  => P.Parser a
  -> a
  -> X.Text
  -> QCP.Result
doParse p a x = doParse' p (show a) (== a) x

doParseEv
  :: (Show a, L.Equivalent a)
  => P.Parser a
  -> a
  -> X.Text
  -> QCP.Result
doParseEv p a x = doParse' p (show a) (==~ a) x

doParse'
  :: Show a
  => P.Parser a
  -> String
  -> (a -> Bool)
  -> X.Text
  -> QCP.Result
doParse' p b pd x = case P.parse (p <* P.eof) "" x of
  Left e -> QCP.failed
    { QCP.reason = "parse failed: " ++ show e }
  Right g -> if pd g then QCP.succeeded
             else QCP.failed { QCP.reason = "items do not match."
                               ++ "parsed item: " ++ show g
                               ++ "expected item: " ++ b }

prop_leftCmdtyLvl1Amt = do
  cy <- GP.quotedLvl1Cmdty
  q <- Q.arbitrary
  qr <- GP.renderQty q
  ((a, x), sb) <- GP.leftCmdtyLvl1Amt cy qr
  return $ doParseEv CP.leftCmdtyLvl1Amt (a, L.CommodityOnLeft, sb) x


prop_leftCmdtyLvl3Amt = do
  cy <- GP.lvl3Cmdty
  q <- Q.arbitrary
  qr <- GP.renderQty q
  ((a, x), sb) <- GP.leftCmdtyLvl3Amt cy qr
  return $ doParseEv CP.leftCmdtyLvl3Amt (a, L.CommodityOnLeft, sb) x

prop_leftSideCmdtyAmt = do
  cy <- Q.oneof [ fmap Left GP.quotedLvl1Cmdty
                , fmap Right GP.lvl3Cmdty ]
  q <- Q.arbitrary
  qr <- GP.renderQty q
  ((a, x), sb) <- GP.leftSideCmdtyAmt cy qr
  return $ doParseEv CP.leftSideCmdtyAmt (a, L.CommodityOnLeft, sb) x

prop_rightSideCmdtyAmt = do
  cy <- Q.oneof [ fmap Left GP.quotedLvl1Cmdty
                , fmap Right GP.lvl2Cmdty ]
  q <- Q.arbitrary
  qr <- GP.renderQty q
  ((a, x), sb) <- GP.rightSideCmdtyAmt cy qr
  return $ doParseEv CP.rightSideCmdtyAmt (a, L.CommodityOnRight, sb) x

prop_amount = do
  cy <- GP.genCmdty
  q <- Q.arbitrary
  qr <- GP.renderQty q
  ((a, x), sb, sd) <- GP.amount cy qr
  return $ doParseEv CP.amount (a, sd, sb) x

prop_comment =
  parseProp CP.comment GP.comment id

prop_year =
  parseProp CP.year GP.year id

prop_month =
  parseProp CP.month GP.month id

prop_day =
  parseProp CP.day GP.day id

prop_date =
  parseProp CP.date GP.date id

prop_hours =
  parseProp CP.hours GP.hours id

prop_minutes =
  parseProp CP.minutes GP.minutes id

prop_seconds =
  parseProp CP.seconds GP.seconds id

prop_time =
  parseProp CP.time GP.time id

prop_tzNumber =
  parseProp CP.tzNumber GP.tzNumber id

prop_timeZone =
  parseProp CP.timeZone GP.timeZone id

prop_timeWithZone =
  parseProp CP.timeWithZone GP.timeWithZone id

prop_dateTime =
  parseProp CP.dateTime GP.dateTime id

prop_debit =
  parseProp CP.debit GP.debit id

prop_credit =
  parseProp CP.credit GP.credit id

prop_drCr =
  parseProp CP.drCr GP.drCr id

prop_entry = do
  cy <- GP.genCmdty
  dc <- GP.drCr
  q <- Q.arbitrary
  qr <- GP.renderQty q
  ((en, x), sb, sd) <- GP.entry cy dc qr
  return $ doParseEv CP.entry (en, sd, sb) x

prop_flag =
  parseProp CP.flag GP.flag id

prop_postingMemoLine =
  parseProp CP.postingMemoLine GP.postingMemoLine id

prop_postingMemo =
  parseProp CP.postingMemo GP.postingMemo id

prop_transactionMemoLine =
  parseProp CP.transactionMemoLine GP.transactionMemoLine id

prop_transactionMemo =
  parseProp CP.transactionMemo GP.transactionMemo snd

prop_number =
  parseProp CP.number GP.number id

prop_lvl1Payee =
  parseProp CP.lvl1Payee GP.lvl1Payee id

prop_quotedLvl1Payee =
  parseProp CP.quotedLvl1Payee GP.quotedLvl1Payee id

prop_lvl2Payee =
  parseProp CP.lvl2Payee GP.lvl2Payee id

prop_fromCmdty = do
  cy <- Q.oneof [ fmap Left GP.quotedLvl1Cmdty
                , fmap Right GP.lvl2Cmdty ]
  let (fr, x) = GP.fromCmdty cy
  return $ doParse CP.fromCmdty fr x

prop_price = Q.forAll GP.price $ \(pp, x) ->
  let pd pp' = L.dateTime pp == L.dateTime pp'
               && L.price pp ==~ L.price pp'
               && L.ppSide pp == L.ppSide pp'
               && L.ppSpaceBetween pp == L.ppSpaceBetween pp'
  in doParse' CP.price (show pp) pd x

prop_tag =
  parseProp CP.tag GP.tag id

prop_tags =
  parseProp CP.tags GP.tags id

prop_topLinePayee =
  parseProp CP.topLinePayee GP.topLinePayee id

prop_topLineFlagNum =
  parseProp CP.topLineFlagNum GP.topLineFlagNum id

parsedTopLineToCore :: C.ParsedTopLine -> L.TopLineCore
parsedTopLineToCore (C.ParsedTopLine dt nu fl pa me _) =
  L.TopLineCore dt nu fl pa (fmap fst me)

prop_topLineCore = parseProp CP.topLine GP.topLineCore
                             parsedTopLineToCore

prop_flagNumPayee =
  parseProp CP.flagNumPayee GP.flagNumPayee id

prop_postingAcct =
  parseProp CP.postingAcct GP.postingAcct id

prop_posting = do
  doJust <- Q.frequency [(3, return True), (1, return False)]
  ((pc, x), mayEn) <- if doJust
    then do
      cy <- GP.genCmdty
      dc <- GP.drCr
      q <- Q.arbitrary
      qr <- GP.renderQty q
      en <- GP.entry cy dc qr
      GP.posting (Just en)
    else GP.posting Nothing
  let pd (pc', _, mayEn') = pc' == pc && mayEn' ==~ mayEn
  return $ doParse' CP.posting (show (pc, mayEn)) pd x

prop_transaction = do
  ((tlc, es), x) <- GP.transaction
  let pd (ptl, es') =
        parsedTopLineToCore ptl == tlc
        && (fmap fst es' ==~ es)
  return $ doParse' CP.transaction (show (tlc, es)) pd x

testTree :: TestTree
testTree = testGroup "Parser"
  [ testProperty "prop_lvl1SubAcct" prop_lvl1SubAcct
  , testProperty "prop_lvl1FirstSubAcct" prop_lvl1FirstSubAcct
  , testProperty "prop_lvl1OtherSubAcct" prop_lvl1OtherSubAcct
  , testProperty "prop_lvl1Acct" prop_lvl1Acct
  , testProperty "prop_quotedLvl1Acct" prop_quotedLvl1Acct
  , testProperty "prop_lvl2FirstSubAcct" prop_lvl2FirstSubAcct
  , testProperty "prop_lvl2OtherSubAcct" prop_lvl2OtherSubAcct
  , testProperty "prop_lvl2Acct" prop_lvl2Acct
  , testProperty "prop_ledgerAcct" prop_ledgerAcct
  , testProperty "prop_lvl1Cmdty" prop_lvl1Cmdty
  , testProperty "prop_quotedLvl1Cmdty" prop_quotedLvl1Cmdty
  , testProperty "prop_lvl2Cmdty" prop_lvl2Cmdty
  , testProperty "prop_lvl3Cmdty" prop_lvl3Cmdty
  , testProperty "prop_qtyRep" prop_qtyRep
  , testProperty "prop_leftCmdtyLvl1Amt" prop_leftCmdtyLvl1Amt
  , testProperty "prop_leftCmdtyLvl3Amt" prop_leftCmdtyLvl3Amt
  , testProperty "prop_leftSideCmdtyAmt" prop_leftSideCmdtyAmt
  , testProperty "prop_rightSideCmdtyAmt" prop_rightSideCmdtyAmt
  , testProperty "prop_amount" prop_amount
  , testProperty "prop_comment" prop_comment
  , testProperty "prop_year" prop_year
  , testProperty "prop_month" prop_month
  , testProperty "prop_day" prop_day
  , testProperty "prop_date" prop_date
  , testProperty "prop_hours" prop_hours
  , testProperty "prop_minutes" prop_minutes
  , testProperty "prop_seconds" prop_seconds
  , testProperty "prop_time" prop_time
  , testProperty "prop_tzNumber" prop_tzNumber
  , testProperty "prop_timeZone" prop_timeZone
  , testProperty "prop_timeWithZone" prop_timeWithZone
  , testProperty "prop_dateTime" prop_dateTime
  , testProperty "prop_debit" prop_debit
  , testProperty "prop_credit" prop_credit
  , testProperty "prop_drCr" prop_drCr
  , testProperty "prop_entry" prop_entry
  , testProperty "prop_flag" prop_flag
  , testProperty "prop_postingMemoLine" prop_postingMemoLine
  , testProperty "prop_postingMemo" prop_postingMemo
  , testProperty "prop_transactionMemoLine" prop_transactionMemoLine
  , testProperty "prop_transactionMemo" prop_transactionMemo
  , testProperty "prop_number" prop_number
  , testProperty "prop_lvl1Payee" prop_lvl1Payee
  , testProperty "prop_quotedLvl1Payee" prop_quotedLvl1Payee
  , testProperty "prop_lvl2Payee" prop_lvl2Payee
  , testProperty "prop_fromCmdty" prop_fromCmdty
  , testProperty "prop_price" prop_price
  , testProperty "prop_tag" prop_tag
  , testProperty "prop_tags" prop_tags
  , testProperty "prop_topLinePayee" prop_topLinePayee
  , testProperty "prop_topLineFlagNum" prop_topLineFlagNum
  , testProperty "prop_topLineCore" prop_topLineCore
  , testProperty "prop_flagNumPayee" prop_flagNumPayee
  , testProperty "prop_postingAcct" prop_postingAcct
  , testProperty "prop_posting" prop_posting
  , testProperty "prop_transaction" prop_transaction
  ]