tests: port all unit tests to tasty, first cut (#1090)
easytest is not actively maintained and requires an old version of hedgehog which does not support base-compat 0.11 & ghc 8.8. This is still using the old easytest helpers, and not displaying test names properly.
This commit is contained in:
parent
8952dc9a93
commit
13a3542464
@ -1324,16 +1324,16 @@ tests_Journal = tests "Journal" [
|
|||||||
|
|
||||||
,tests "journalBalanceTransactions" [
|
,tests "journalBalanceTransactions" [
|
||||||
|
|
||||||
test "balance-assignment" $ do
|
test "balance-assignment" $ testCaseSteps "sometests" $ \_step -> do
|
||||||
let ej = journalBalanceTransactions True $
|
let ej = journalBalanceTransactions True $
|
||||||
--2019/01/01
|
--2019/01/01
|
||||||
-- (a) = 1
|
-- (a) = 1
|
||||||
nulljournal{ jtxns = [
|
nulljournal{ jtxns = [
|
||||||
transaction "2019/01/01" [ vpost' "a" missingamt (balassert (num 1)) ]
|
transaction "2019/01/01" [ vpost' "a" missingamt (balassert (num 1)) ]
|
||||||
]}
|
]}
|
||||||
expectRight ej
|
assertRight ej
|
||||||
let Right j = ej
|
let Right j = ej
|
||||||
(jtxns j & head & tpostings & head & pamount) `is` Mixed [num 1]
|
(jtxns j & head & tpostings & head & pamount) @?= Mixed [num 1]
|
||||||
|
|
||||||
,test "same-day-1" $ do
|
,test "same-day-1" $ do
|
||||||
expectRight $ journalBalanceTransactions True $
|
expectRight $ journalBalanceTransactions True $
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import "base-compat-batteries" Prelude.Compat
|
|||||||
import Numeric
|
import Numeric
|
||||||
import Data.Char (isPrint)
|
import Data.Char (isPrint)
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import qualified Data.Text as T
|
-- import qualified Data.Text as T
|
||||||
import Text.Megaparsec
|
import Text.Megaparsec
|
||||||
import Text.Megaparsec.Char
|
import Text.Megaparsec.Char
|
||||||
|
|
||||||
@ -157,7 +157,7 @@ tests_StringFormat = tests "StringFormat" [
|
|||||||
]
|
]
|
||||||
|
|
||||||
,tests "parseStringFormat" $
|
,tests "parseStringFormat" $
|
||||||
let s `gives` expected = test (T.pack s) $ parseStringFormat s `is` Right expected
|
let s `gives` expected = test s $ parseStringFormat s `is` Right expected
|
||||||
in [
|
in [
|
||||||
"" `gives` (defaultStringFormatStyle [])
|
"" `gives` (defaultStringFormatStyle [])
|
||||||
, "D" `gives` (defaultStringFormatStyle [FormatLiteral "D"])
|
, "D" `gives` (defaultStringFormatStyle [FormatLiteral "D"])
|
||||||
|
|||||||
@ -115,32 +115,31 @@ entryFromTimeclockInOut i o
|
|||||||
-- tests
|
-- tests
|
||||||
|
|
||||||
tests_Timeclock = tests "Timeclock" [
|
tests_Timeclock = tests "Timeclock" [
|
||||||
do
|
testCaseSteps "timeclockEntriesToTransactions tests" $ \step -> do
|
||||||
today <- io getCurrentDay
|
step "gathering data"
|
||||||
now' <- io getCurrentTime
|
today <- getCurrentDay
|
||||||
tz <- io getCurrentTimeZone
|
now' <- getCurrentTime
|
||||||
let now = utcToLocalTime tz now'
|
tz <- getCurrentTimeZone
|
||||||
nowstr = showtime now
|
let now = utcToLocalTime tz now'
|
||||||
yesterday = prevday today
|
nowstr = showtime now
|
||||||
clockin = TimeclockEntry nullsourcepos In
|
yesterday = prevday today
|
||||||
mktime d = LocalTime d . fromMaybe midnight .
|
clockin = TimeclockEntry nullsourcepos In
|
||||||
|
mktime d = LocalTime d . fromMaybe midnight .
|
||||||
#if MIN_VERSION_time(1,5,0)
|
#if MIN_VERSION_time(1,5,0)
|
||||||
parseTimeM True defaultTimeLocale "%H:%M:%S"
|
parseTimeM True defaultTimeLocale "%H:%M:%S"
|
||||||
#else
|
#else
|
||||||
parseTime defaultTimeLocale "%H:%M:%S"
|
parseTime defaultTimeLocale "%H:%M:%S"
|
||||||
#endif
|
#endif
|
||||||
showtime = formatTime defaultTimeLocale "%H:%M"
|
showtime = formatTime defaultTimeLocale "%H:%M"
|
||||||
txndescs = map (T.unpack . tdescription) . timeclockEntriesToTransactions now
|
txndescs = map (T.unpack . tdescription) . timeclockEntriesToTransactions now
|
||||||
future = utcToLocalTime tz $ addUTCTime 100 now'
|
future = utcToLocalTime tz $ addUTCTime 100 now'
|
||||||
futurestr = showtime future
|
futurestr = showtime future
|
||||||
tests "timeclockEntriesToTransactions" [
|
step "started yesterday, split session at midnight"
|
||||||
test "started yesterday, split session at midnight" $
|
txndescs [clockin (mktime yesterday "23:00:00") "" ""] @?= ["23:00-23:59","00:00-"++nowstr]
|
||||||
txndescs [clockin (mktime yesterday "23:00:00") "" ""] `is` ["23:00-23:59","00:00-"++nowstr]
|
step "split multi-day sessions at each midnight"
|
||||||
,test "split multi-day sessions at each midnight" $
|
txndescs [clockin (mktime (addDays (-2) today) "23:00:00") "" ""] @?= ["23:00-23:59","00:00-23:59","00:00-"++nowstr]
|
||||||
txndescs [clockin (mktime (addDays (-2) today) "23:00:00") "" ""] `is `["23:00-23:59","00:00-23:59","00:00-"++nowstr]
|
step "auto-clock-out if needed"
|
||||||
,test "auto-clock-out if needed" $
|
txndescs [clockin (mktime today "00:00:00") "" ""] @?= ["00:00-"++nowstr]
|
||||||
txndescs [clockin (mktime today "00:00:00") "" ""] `is` ["00:00-"++nowstr]
|
step "use the clockin time for auto-clockout if it's in the future"
|
||||||
,test "use the clockin time for auto-clockout if it's in the future" $
|
txndescs [clockin future "" ""] @?= [printf "%s-%s" futurestr futurestr]
|
||||||
txndescs [clockin future "" ""] `is` [printf "%s-%s" futurestr futurestr]
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
|
|||||||
@ -596,7 +596,7 @@ tests_Transaction =
|
|||||||
-- one missing amount, not the last one
|
-- one missing amount, not the last one
|
||||||
t3 = nulltransaction {tpostings = ["a" `post` usd 1, "b" `post` missingamt, "c" `post` usd (-1)]}
|
t3 = nulltransaction {tpostings = ["a" `post` usd 1, "b" `post` missingamt, "c" `post` usd (-1)]}
|
||||||
-- unbalanced amounts when precision is limited (#931)
|
-- unbalanced amounts when precision is limited (#931)
|
||||||
t4 = nulltransaction {tpostings = ["a" `post` usd (-0.01), "b" `post` usd (0.005), "c" `post` usd (0.005)]}
|
-- t4 = nulltransaction {tpostings = ["a" `post` usd (-0.01), "b" `post` usd (0.005), "c" `post` usd (0.005)]}
|
||||||
in tests
|
in tests
|
||||||
"postingsAsLines"
|
"postingsAsLines"
|
||||||
[ test "null-transaction" $
|
[ test "null-transaction" $
|
||||||
@ -635,10 +635,11 @@ tests_Transaction =
|
|||||||
let t = t3
|
let t = t3
|
||||||
in postingsAsLines False (tpostings t) `is`
|
in postingsAsLines False (tpostings t) `is`
|
||||||
[" a $1.00", " b", " c $-1.00"]
|
[" a $1.00", " b", " c $-1.00"]
|
||||||
, _test "ensure-visibly-balanced" $
|
-- , _test "ensure-visibly-balanced" $
|
||||||
let t = t4
|
-- let t = t4
|
||||||
in postingsAsLines False (tpostings t) `is`
|
-- in postingsAsLines False (tpostings t) `is`
|
||||||
[" a $-0.01", " b $0.005", " c $0.005"]
|
-- [" a $-0.01", " b $0.005", " c $0.005"]
|
||||||
|
|
||||||
]
|
]
|
||||||
, tests
|
, tests
|
||||||
"inferBalancingAmount"
|
"inferBalancingAmount"
|
||||||
|
|||||||
@ -723,7 +723,7 @@ tests_Query = tests "Query" [
|
|||||||
,parseAmountQueryTerm "0.23" `is` (AbsEq,0.23)
|
,parseAmountQueryTerm "0.23" `is` (AbsEq,0.23)
|
||||||
,parseAmountQueryTerm "<=+0.23" `is` (LtEq,0.23)
|
,parseAmountQueryTerm "<=+0.23" `is` (LtEq,0.23)
|
||||||
,parseAmountQueryTerm "-0.23" `is` (Eq,(-0.23))
|
,parseAmountQueryTerm "-0.23" `is` (Eq,(-0.23))
|
||||||
,_test "number beginning with decimal mark" $ parseAmountQueryTerm "=.23" `is` (AbsEq,0.23) -- XXX
|
-- ,_test "number beginning with decimal mark" $ parseAmountQueryTerm "=.23" `is` (AbsEq,0.23) -- XXX
|
||||||
]
|
]
|
||||||
|
|
||||||
,tests "matchesAccount" [
|
,tests "matchesAccount" [
|
||||||
|
|||||||
@ -1363,7 +1363,7 @@ tests_Common = tests "Common" [
|
|||||||
,tests "spaceandamountormissingp" [
|
,tests "spaceandamountormissingp" [
|
||||||
test "space and amount" $ expectParseEq spaceandamountormissingp " $47.18" (Mixed [usd 47.18])
|
test "space and amount" $ expectParseEq spaceandamountormissingp " $47.18" (Mixed [usd 47.18])
|
||||||
,test "empty string" $ expectParseEq spaceandamountormissingp "" missingmixedamt
|
,test "empty string" $ expectParseEq spaceandamountormissingp "" missingmixedamt
|
||||||
,_test "just space" $ expectParseEq spaceandamountormissingp " " missingmixedamt -- XXX should it ?
|
-- ,_test "just space" $ expectParseEq spaceandamountormissingp " " missingmixedamt -- XXX should it ?
|
||||||
-- ,test "just amount" $ expectParseError spaceandamountormissingp "$47.18" "" -- succeeds, consuming nothing
|
-- ,test "just amount" $ expectParseError spaceandamountormissingp "$47.18" "" -- succeeds, consuming nothing
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -63,8 +63,8 @@ module Hledger.Read.JournalReader (
|
|||||||
)
|
)
|
||||||
where
|
where
|
||||||
--- * imports
|
--- * imports
|
||||||
import qualified Prelude (fail)
|
-- import qualified Prelude (fail)
|
||||||
import "base-compat-batteries" Prelude.Compat hiding (fail, readFile)
|
-- import "base-compat-batteries" Prelude.Compat hiding (fail, readFile)
|
||||||
import qualified "base-compat-batteries" Control.Monad.Fail.Compat as Fail (fail)
|
import qualified "base-compat-batteries" Control.Monad.Fail.Compat as Fail (fail)
|
||||||
import qualified Control.Exception as C
|
import qualified Control.Exception as C
|
||||||
import Control.Monad (forM_, when, void)
|
import Control.Monad (forM_, when, void)
|
||||||
@ -668,41 +668,43 @@ tests_JournalReader = tests "JournalReader" [
|
|||||||
let p = lift accountnamep :: JournalParser IO AccountName in
|
let p = lift accountnamep :: JournalParser IO AccountName in
|
||||||
tests "accountnamep" [
|
tests "accountnamep" [
|
||||||
test "basic" $ expectParse p "a:b:c"
|
test "basic" $ expectParse p "a:b:c"
|
||||||
,_test "empty inner component" $ expectParseError p "a::c" "" -- TODO
|
-- ,_test "empty inner component" $ expectParseError p "a::c" "" -- TODO
|
||||||
,_test "empty leading component" $ expectParseError p ":b:c" "x"
|
-- ,_test "empty leading component" $ expectParseError p ":b:c" "x"
|
||||||
,_test "empty trailing component" $ expectParseError p "a:b:" "x"
|
-- ,_test "empty trailing component" $ expectParseError p "a:b:" "x"
|
||||||
]
|
]
|
||||||
|
|
||||||
-- "Parse a date in YYYY/MM/DD format.
|
-- "Parse a date in YYYY/MM/DD format.
|
||||||
-- Hyphen (-) and period (.) are also allowed as separators.
|
-- Hyphen (-) and period (.) are also allowed as separators.
|
||||||
-- The year may be omitted if a default year has been set.
|
-- The year may be omitted if a default year has been set.
|
||||||
-- Leading zeroes may be omitted."
|
-- Leading zeroes may be omitted."
|
||||||
,test "datep" $ do
|
,tests "datep" [
|
||||||
test "YYYY/MM/DD" $ expectParseEq datep "2018/01/01" (fromGregorian 2018 1 1)
|
test "YYYY/MM/DD" $ expectParseEq datep "2018/01/01" (fromGregorian 2018 1 1)
|
||||||
test "YYYY-MM-DD" $ expectParse datep "2018-01-01"
|
,test "YYYY-MM-DD" $ expectParse datep "2018-01-01"
|
||||||
test "YYYY.MM.DD" $ expectParse datep "2018.01.01"
|
,test "YYYY.MM.DD" $ expectParse datep "2018.01.01"
|
||||||
test "yearless date with no default year" $ expectParseError datep "1/1" "current year is unknown"
|
,test "yearless date with no default year" $ expectParseError datep "1/1" "current year is unknown"
|
||||||
test "yearless date with default year" $ do
|
,testCaseSteps "yearless date with default year" $ \_step -> do
|
||||||
let s = "1/1"
|
let s = "1/1"
|
||||||
ep <- parseWithState mempty{jparsedefaultyear=Just 2018} datep s
|
ep <- parseWithState mempty{jparsedefaultyear=Just 2018} datep s
|
||||||
either (Prelude.fail . ("parse error at "++) . customErrorBundlePretty) (const ok) ep
|
either (assertFailure . ("parse error at "++) . customErrorBundlePretty) (const $ return ()) ep
|
||||||
test "no leading zero" $ expectParse datep "2018/1/1"
|
,test "no leading zero" $ expectParse datep "2018/1/1"
|
||||||
|
]
|
||||||
,test "datetimep" $ do
|
,let
|
||||||
let
|
good = expectParse datetimep
|
||||||
good = expectParse datetimep
|
bad = (\t -> expectParseError datetimep t "")
|
||||||
bad = (\t -> expectParseError datetimep t "")
|
in tests "datetimep" [
|
||||||
good "2011/1/1 00:00"
|
good "2011/1/1 00:00"
|
||||||
good "2011/1/1 23:59:59"
|
,good "2011/1/1 23:59:59"
|
||||||
bad "2011/1/1"
|
,bad "2011/1/1"
|
||||||
bad "2011/1/1 24:00:00"
|
,bad "2011/1/1 24:00:00"
|
||||||
bad "2011/1/1 00:60:00"
|
,bad "2011/1/1 00:60:00"
|
||||||
bad "2011/1/1 00:00:60"
|
,bad "2011/1/1 00:00:60"
|
||||||
bad "2011/1/1 3:5:7"
|
,bad "2011/1/1 3:5:7"
|
||||||
test "timezone is parsed but ignored" $ do
|
,let t = LocalTime (fromGregorian 2018 1 1) (TimeOfDay 0 0 (fromIntegral 0))
|
||||||
let t = LocalTime (fromGregorian 2018 1 1) (TimeOfDay 0 0 (fromIntegral 0))
|
in tests "timezone is parsed but ignored" [
|
||||||
expectParseEq datetimep "2018/1/1 00:00-0800" t
|
expectParseEq datetimep "2018/1/1 00:00-0800" t
|
||||||
expectParseEq datetimep "2018/1/1 00:00+1234" t
|
,expectParseEq datetimep "2018/1/1 00:00+1234" t
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
,tests "periodictransactionp" [
|
,tests "periodictransactionp" [
|
||||||
|
|
||||||
@ -883,41 +885,46 @@ tests_JournalReader = tests "JournalReader" [
|
|||||||
-- directives
|
-- directives
|
||||||
|
|
||||||
,tests "directivep" [
|
,tests "directivep" [
|
||||||
test "supports !" $ do
|
tests "supports !" [
|
||||||
expectParseE directivep "!account a\n"
|
expectParseE directivep "!account a\n"
|
||||||
expectParseE directivep "!D 1.0\n"
|
,expectParseE directivep "!D 1.0\n"
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
,test "accountdirectivep" $ do
|
,tests "accountdirectivep" [
|
||||||
test "with-comment" $ expectParse accountdirectivep "account a:b ; a comment\n"
|
test "with-comment" $ expectParse accountdirectivep "account a:b ; a comment\n"
|
||||||
test "does-not-support-!" $ expectParseError accountdirectivep "!account a:b\n" ""
|
,test "does-not-support-!" $ expectParseError accountdirectivep "!account a:b\n" ""
|
||||||
test "account-type-code" $ expectParse accountdirectivep "account a:b A\n"
|
,test "account-type-code" $ expectParse accountdirectivep "account a:b A\n"
|
||||||
test "account-type-tag" $ expectParseStateOn accountdirectivep "account a:b ; type:asset\n"
|
,test "account-type-tag" $ expectParseStateOn accountdirectivep "account a:b ; type:asset\n"
|
||||||
jdeclaredaccounts
|
jdeclaredaccounts
|
||||||
[("a:b", AccountDeclarationInfo{adicomment = "type:asset\n"
|
[("a:b", AccountDeclarationInfo{adicomment = "type:asset\n"
|
||||||
,aditags = [("type","asset")]
|
,aditags = [("type","asset")]
|
||||||
,adideclarationorder = 1
|
,adideclarationorder = 1
|
||||||
})
|
})
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
,test "commodityconversiondirectivep" $ do
|
,test "commodityconversiondirectivep" $ do
|
||||||
expectParse commodityconversiondirectivep "C 1h = $50.00\n"
|
expectParse commodityconversiondirectivep "C 1h = $50.00\n"
|
||||||
|
|
||||||
,test "defaultcommoditydirectivep" $ do
|
,tests "defaultcommoditydirectivep" [
|
||||||
expectParse defaultcommoditydirectivep "D $1,000.0\n"
|
expectParse defaultcommoditydirectivep "D $1,000.0\n"
|
||||||
expectParseError defaultcommoditydirectivep "D $1000\n" "please include a decimal separator"
|
,expectParseError defaultcommoditydirectivep "D $1000\n" "please include a decimal separator"
|
||||||
|
]
|
||||||
|
|
||||||
,test "defaultyeardirectivep" $ do
|
,tests "defaultyeardirectivep" [
|
||||||
test "1000" $ expectParse defaultyeardirectivep "Y 1000" -- XXX no \n like the others
|
test "1000" $ expectParse defaultyeardirectivep "Y 1000" -- XXX no \n like the others
|
||||||
test "999" $ expectParseError defaultyeardirectivep "Y 999" "bad year number"
|
,test "999" $ expectParseError defaultyeardirectivep "Y 999" "bad year number"
|
||||||
test "12345" $ expectParse defaultyeardirectivep "Y 12345"
|
,test "12345" $ expectParse defaultyeardirectivep "Y 12345"
|
||||||
|
]
|
||||||
|
|
||||||
,test "ignoredpricecommoditydirectivep" $ do
|
,test "ignoredpricecommoditydirectivep" $ do
|
||||||
expectParse ignoredpricecommoditydirectivep "N $\n"
|
expectParse ignoredpricecommoditydirectivep "N $\n"
|
||||||
|
|
||||||
,test "includedirectivep" $ do
|
,tests "includedirectivep" [
|
||||||
test "include" $ expectParseErrorE 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 "glob" $ expectParseErrorE includedirectivep "include nosuchfile*\n" "No existing files match pattern: nosuchfile*"
|
||||||
|
]
|
||||||
|
|
||||||
,test "marketpricedirectivep" $ expectParseEq marketpricedirectivep
|
,test "marketpricedirectivep" $ expectParseEq marketpricedirectivep
|
||||||
"P 2017/01/30 BTC $922.83\n"
|
"P 2017/01/30 BTC $922.83\n"
|
||||||
@ -930,10 +937,10 @@ tests_JournalReader = tests "JournalReader" [
|
|||||||
,test "tagdirectivep" $ do
|
,test "tagdirectivep" $ do
|
||||||
expectParse tagdirectivep "tag foo \n"
|
expectParse tagdirectivep "tag foo \n"
|
||||||
|
|
||||||
,test "endtagdirectivep" $ do
|
,tests "endtagdirectivep" [
|
||||||
expectParse endtagdirectivep "end tag \n"
|
expectParse endtagdirectivep "end tag \n"
|
||||||
expectParse endtagdirectivep "pop \n"
|
,expectParse endtagdirectivep "pop \n"
|
||||||
|
]
|
||||||
|
|
||||||
,tests "journalp" [
|
,tests "journalp" [
|
||||||
test "empty file" $ expectParseEqE journalp "" nulljournal
|
test "empty file" $ expectParseEqE journalp "" nulljournal
|
||||||
@ -941,10 +948,10 @@ tests_JournalReader = tests "JournalReader" [
|
|||||||
|
|
||||||
-- these are defined here rather than in Common so they can use journalp
|
-- these are defined here rather than in Common so they can use journalp
|
||||||
,tests "parseAndFinaliseJournal" [
|
,tests "parseAndFinaliseJournal" [
|
||||||
test "basic" $ do
|
testCaseSteps "basic" $ \_step -> do
|
||||||
ej <- io $ runExceptT $ parseAndFinaliseJournal journalp definputopts "" "2019-1-1\n"
|
ej <- runExceptT $ parseAndFinaliseJournal journalp definputopts "" "2019-1-1\n"
|
||||||
let Right j = ej
|
let Right j = ej
|
||||||
expectEqPP [""] $ journalFilePaths j
|
assertEq [""] $ journalFilePaths j
|
||||||
]
|
]
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|||||||
@ -248,16 +248,15 @@ Right samplejournal2 =
|
|||||||
}
|
}
|
||||||
|
|
||||||
tests_BalanceReport = tests "BalanceReport" [
|
tests_BalanceReport = tests "BalanceReport" [
|
||||||
tests "balanceReport" $
|
let
|
||||||
let
|
(opts,journal) `gives` r = testCaseSteps "sometest" $ \_step -> do
|
||||||
(opts,journal) `gives` r = do
|
let (eitems, etotal) = r
|
||||||
let (eitems, etotal) = r
|
(aitems, atotal) = balanceReport opts (queryFromOpts nulldate opts) journal
|
||||||
(aitems, atotal) = balanceReport opts (queryFromOpts nulldate opts) journal
|
showw (acct,acct',indent,amt) = (acct, acct', indent, showMixedAmountDebug amt)
|
||||||
showw (acct,acct',indent,amt) = (acct, acct', indent, showMixedAmountDebug amt)
|
(map showw eitems) @?= (map showw aitems)
|
||||||
(map showw eitems) `is` (map showw aitems)
|
(showMixedAmountDebug etotal) @?= (showMixedAmountDebug atotal)
|
||||||
(showMixedAmountDebug etotal) `is` (showMixedAmountDebug atotal)
|
usd0 = usd 0
|
||||||
usd0 = usd 0
|
in tests "balanceReport" [
|
||||||
in [
|
|
||||||
|
|
||||||
test "balanceReport with no args on null journal" $
|
test "balanceReport with no args on null journal" $
|
||||||
(defreportopts, nulljournal) `gives` ([], Mixed [nullamt])
|
(defreportopts, nulljournal) `gives` ([], Mixed [nullamt])
|
||||||
|
|||||||
@ -417,13 +417,13 @@ tableAsText (ReportOpts{pretty_tables_ = pretty}) showcell =
|
|||||||
|
|
||||||
tests_MultiBalanceReport = tests "MultiBalanceReport" [
|
tests_MultiBalanceReport = tests "MultiBalanceReport" [
|
||||||
let
|
let
|
||||||
(opts,journal) `gives` r = do
|
(opts,journal) `gives` r = testCaseSteps "sometest" $ \_step -> do
|
||||||
let (eitems, etotal) = r
|
let (eitems, etotal) = r
|
||||||
(MultiBalanceReport (_, aitems, atotal)) = multiBalanceReport opts (queryFromOpts nulldate opts) journal
|
(MultiBalanceReport (_, aitems, atotal)) = multiBalanceReport opts (queryFromOpts nulldate opts) journal
|
||||||
showw (acct,acct',indent,lAmt,amt,amt') = (acct, acct', indent, map showMixedAmountDebug lAmt, showMixedAmountDebug amt, showMixedAmountDebug amt')
|
showw (acct,acct',indent,lAmt,amt,amt') = (acct, acct', indent, map showMixedAmountDebug lAmt, showMixedAmountDebug amt, showMixedAmountDebug amt')
|
||||||
(map showw aitems) `is` (map showw eitems)
|
(map showw aitems) @?= (map showw eitems)
|
||||||
((\(_, b, _) -> showMixedAmountDebug b) atotal) `is` (showMixedAmountDebug etotal) -- we only check the sum of the totals
|
((\(_, b, _) -> showMixedAmountDebug b) atotal) @?= (showMixedAmountDebug etotal) -- we only check the sum of the totals
|
||||||
usd0 = usd 0
|
-- usd0 = usd 0
|
||||||
amount0 = Amount {acommodity="$", aquantity=0, aprice=Nothing, astyle=AmountStyle {ascommodityside = L, ascommodityspaced = False, asprecision = 2, asdecimalpoint = Just '.', asdigitgroups = Nothing}, aismultiplier=False}
|
amount0 = Amount {acommodity="$", aquantity=0, aprice=Nothing, astyle=AmountStyle {ascommodityside = L, ascommodityspaced = False, asprecision = 2, asdecimalpoint = Just '.', asdigitgroups = Nothing}, aismultiplier=False}
|
||||||
in
|
in
|
||||||
tests "multiBalanceReport" [
|
tests "multiBalanceReport" [
|
||||||
@ -439,27 +439,27 @@ tests_MultiBalanceReport = tests "MultiBalanceReport" [
|
|||||||
],
|
],
|
||||||
Mixed [nullamt])
|
Mixed [nullamt])
|
||||||
|
|
||||||
,_test "a valid history on an empty period" $
|
-- ,_test "a valid history on an empty period" $
|
||||||
(defreportopts{period_= PeriodBetween (fromGregorian 2008 1 2) (fromGregorian 2008 1 3), balancetype_=HistoricalBalance}, samplejournal) `gives`
|
-- (defreportopts{period_= PeriodBetween (fromGregorian 2008 1 2) (fromGregorian 2008 1 3), balancetype_=HistoricalBalance}, samplejournal) `gives`
|
||||||
(
|
-- (
|
||||||
[
|
-- [
|
||||||
("assets:bank:checking","checking",3, [mamountp' "$1.00"], mamountp' "$1.00",Mixed [amount0 {aquantity=1}])
|
-- ("assets:bank:checking","checking",3, [mamountp' "$1.00"], mamountp' "$1.00",Mixed [amount0 {aquantity=1}])
|
||||||
,("income:salary","salary",2, [mamountp' "$-1.00"], mamountp' "$-1.00",Mixed [amount0 {aquantity=(-1)}])
|
-- ,("income:salary","salary",2, [mamountp' "$-1.00"], mamountp' "$-1.00",Mixed [amount0 {aquantity=(-1)}])
|
||||||
],
|
-- ],
|
||||||
Mixed [usd0])
|
-- Mixed [usd0])
|
||||||
|
|
||||||
,_test "a valid history on an empty period (more complex)" $
|
-- ,_test "a valid history on an empty period (more complex)" $
|
||||||
(defreportopts{period_= PeriodBetween (fromGregorian 2009 1 1) (fromGregorian 2009 1 2), balancetype_=HistoricalBalance}, samplejournal) `gives`
|
-- (defreportopts{period_= PeriodBetween (fromGregorian 2009 1 1) (fromGregorian 2009 1 2), balancetype_=HistoricalBalance}, samplejournal) `gives`
|
||||||
(
|
-- (
|
||||||
[
|
-- [
|
||||||
("assets:bank:checking","checking",3, [mamountp' "$1.00"], mamountp' "$1.00",Mixed [amount0 {aquantity=1}])
|
-- ("assets:bank:checking","checking",3, [mamountp' "$1.00"], mamountp' "$1.00",Mixed [amount0 {aquantity=1}])
|
||||||
,("assets:bank:saving","saving",3, [mamountp' "$1.00"], mamountp' "$1.00",Mixed [amount0 {aquantity=1}])
|
-- ,("assets:bank:saving","saving",3, [mamountp' "$1.00"], mamountp' "$1.00",Mixed [amount0 {aquantity=1}])
|
||||||
,("assets:cash","cash",2, [mamountp' "$-2.00"], mamountp' "$-2.00",Mixed [amount0 {aquantity=(-2)}])
|
-- ,("assets:cash","cash",2, [mamountp' "$-2.00"], mamountp' "$-2.00",Mixed [amount0 {aquantity=(-2)}])
|
||||||
,("expenses:food","food",2, [mamountp' "$1.00"], mamountp' "$1.00",Mixed [amount0 {aquantity=(1)}])
|
-- ,("expenses:food","food",2, [mamountp' "$1.00"], mamountp' "$1.00",Mixed [amount0 {aquantity=(1)}])
|
||||||
,("expenses:supplies","supplies",2, [mamountp' "$1.00"], mamountp' "$1.00",Mixed [amount0 {aquantity=(1)}])
|
-- ,("expenses:supplies","supplies",2, [mamountp' "$1.00"], mamountp' "$1.00",Mixed [amount0 {aquantity=(1)}])
|
||||||
,("income:gifts","gifts",2, [mamountp' "$-1.00"], mamountp' "$-1.00",Mixed [amount0 {aquantity=(-1)}])
|
-- ,("income:gifts","gifts",2, [mamountp' "$-1.00"], mamountp' "$-1.00",Mixed [amount0 {aquantity=(-1)}])
|
||||||
,("income:salary","salary",2, [mamountp' "$-1.00"], mamountp' "$-1.00",Mixed [amount0 {aquantity=(-1)}])
|
-- ,("income:salary","salary",2, [mamountp' "$-1.00"], mamountp' "$-1.00",Mixed [amount0 {aquantity=(-1)}])
|
||||||
],
|
-- ],
|
||||||
Mixed [usd0])
|
-- Mixed [usd0])
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@ -4,229 +4,225 @@
|
|||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
|
||||||
module Hledger.Utils.Test (
|
module Hledger.Utils.Test (
|
||||||
HasCallStack
|
module Test.Tasty
|
||||||
,module EasyTest
|
,module Test.Tasty.HUnit
|
||||||
,runEasytests
|
-- ,module QC
|
||||||
|
-- ,module SC
|
||||||
,tests
|
,tests
|
||||||
,_tests
|
|
||||||
,test
|
,test
|
||||||
,_test
|
|
||||||
,it
|
|
||||||
,_it
|
|
||||||
,is
|
,is
|
||||||
,expectEqPP
|
,expect
|
||||||
|
,assertEq
|
||||||
|
,expectEq
|
||||||
|
,assertLeft
|
||||||
|
,expectLeft
|
||||||
|
,assertRight
|
||||||
|
,expectRight
|
||||||
,expectParse
|
,expectParse
|
||||||
,expectParseE
|
|
||||||
,expectParseError
|
|
||||||
,expectParseErrorE
|
|
||||||
,expectParseEq
|
,expectParseEq
|
||||||
,expectParseEqE
|
|
||||||
,expectParseEqOn
|
,expectParseEqOn
|
||||||
,expectParseEqOnE
|
,expectParseError
|
||||||
|
,expectParseE
|
||||||
|
,expectParseEqE
|
||||||
|
,expectParseErrorE
|
||||||
,expectParseStateOn
|
,expectParseStateOn
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Control.Exception
|
import Test.Tasty
|
||||||
|
import Test.Tasty.HUnit
|
||||||
|
-- import Test.Tasty.QuickCheck as QC
|
||||||
|
-- import Test.Tasty.SmallCheck as SC
|
||||||
|
|
||||||
import Control.Monad.Except (ExceptT, runExceptT)
|
import Control.Monad.Except (ExceptT, runExceptT)
|
||||||
import Control.Monad.State.Strict (StateT, evalStateT, execStateT)
|
import Control.Monad.State.Strict (StateT, evalStateT, execStateT)
|
||||||
#if !(MIN_VERSION_base(4,11,0))
|
-- #if !(MIN_VERSION_base(4,11,0))
|
||||||
import Data.Monoid ((<>))
|
-- import Data.Monoid ((<>))
|
||||||
#endif
|
-- #endif
|
||||||
import Data.CallStack
|
-- import Data.CallStack
|
||||||
import Data.List
|
import Data.List (isInfixOf)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Safe
|
|
||||||
import System.Exit
|
|
||||||
import Text.Megaparsec
|
import Text.Megaparsec
|
||||||
import Text.Megaparsec.Custom
|
import Text.Megaparsec.Custom
|
||||||
|
|
||||||
import EasyTest hiding (char, char', tests) -- reexported
|
|
||||||
import qualified EasyTest as E -- used here
|
|
||||||
|
|
||||||
import Hledger.Utils.Debug (pshow)
|
import Hledger.Utils.Debug (pshow)
|
||||||
import Hledger.Utils.UTF8IOCompat (error')
|
-- import Hledger.Utils.UTF8IOCompat (error')
|
||||||
|
|
||||||
-- * easytest helpers
|
-- * tasty helpers
|
||||||
|
|
||||||
-- | Name the given test(s). A readability synonym for easytest's "scope".
|
-- | Name and group a list of tests.
|
||||||
test :: T.Text -> E.Test a -> E.Test a
|
tests :: String -> [TestTree] -> TestTree
|
||||||
test = E.scope
|
tests = testGroup
|
||||||
|
|
||||||
|
-- | Name the given test(s).
|
||||||
|
-- test :: T.Text -> E.Test a -> E.Test a
|
||||||
|
-- test :: String -> Assertion -> TestTree
|
||||||
|
test :: String -> TestTree -> TestTree
|
||||||
|
test _name = id
|
||||||
|
|
||||||
-- | Skip the given test(s), with the same type signature as "test".
|
-- | Skip the given test(s), with the same type signature as "test".
|
||||||
-- If called in a monadic sequence of tests, also skips following tests.
|
-- If called in a monadic sequence of tests, also skips following tests. (?)
|
||||||
_test :: T.Text -> E.Test a -> E.Test a
|
-- _test :: T.Text -> E.Test a -> E.Test a
|
||||||
_test _name = (E.skip >>)
|
-- _test _name = (E.skip >>)
|
||||||
|
|
||||||
-- | Name the given test(s). A synonym for "test".
|
-- | Short equality test constructor. Actual value on the left, expected on the right.
|
||||||
it :: T.Text -> E.Test a -> E.Test a
|
is :: (Eq a, Show a, HasCallStack) => a -> a -> TestTree
|
||||||
it = test
|
is actual expected = testCase "sometest" $ actual @?= expected
|
||||||
|
|
||||||
-- | Skip the given test(s), and any following tests in a monadic sequence.
|
-- | Expect True.
|
||||||
-- A synonym for "_test".
|
expect :: HasCallStack => Bool -> TestTree
|
||||||
_it :: T.Text -> E.Test a -> E.Test a
|
expect val = testCase "sometest" $ assertBool "was false" val
|
||||||
_it = _test
|
|
||||||
|
|
||||||
-- | Name and group a list of tests. Combines easytest's "scope" and "tests".
|
-- | Assert equality. Expected first, actual second.
|
||||||
tests :: T.Text -> [E.Test ()] -> E.Test ()
|
assertEq :: (HasCallStack, Eq a, Show a) => a -> a -> Assertion
|
||||||
tests name = E.scope name . E.tests
|
assertEq expected actual = assertEqual "was not equal" expected actual
|
||||||
|
|
||||||
-- | Skip the given list of tests, and any following tests in a monadic sequence,
|
-- | Test for equality. Expected first, actual second.
|
||||||
-- with the same type signature as "group".
|
expectEq :: (HasCallStack, Eq a, Show a) => a -> a -> TestTree
|
||||||
_tests :: T.Text -> [E.Test ()] -> E.Test ()
|
expectEq a b = testCase "sometest" $ assertEq a b
|
||||||
_tests _name = (E.skip >>) . E.tests
|
|
||||||
|
|
||||||
-- | Run some easytest tests, catching easytest's ExitCode exception,
|
-- | Assert any Left value.
|
||||||
-- returning True if there was a problem.
|
assertLeft :: (HasCallStack, Eq b, Show b) => Either a b -> Assertion
|
||||||
-- With arguments, runs only the scope (or single test) named by the first argument
|
assertLeft (Left _) = return ()
|
||||||
-- (exact, case sensitive).
|
assertLeft (Right b) = assertFailure $ "expected Left, got (Right " ++ show b ++ ")"
|
||||||
-- If there is a second argument, it should be an integer and will be used
|
|
||||||
-- as the seed for randomness.
|
|
||||||
runEasytests :: [String] -> E.Test () -> IO Bool
|
|
||||||
runEasytests args tests = (do
|
|
||||||
case args of
|
|
||||||
[] -> E.run tests
|
|
||||||
[a] -> E.runOnly (T.pack a) tests
|
|
||||||
a:b:_ -> do
|
|
||||||
case readMay b :: Maybe Int of
|
|
||||||
Nothing -> error' "the second argument should be an integer (a seed for easytest)"
|
|
||||||
Just seed -> E.rerunOnly seed (T.pack a) tests
|
|
||||||
return False
|
|
||||||
)
|
|
||||||
`catch` (\(_::ExitCode) -> return True)
|
|
||||||
|
|
||||||
-- | Like easytest's expectEq (asserts the second (actual) value equals the first (expected) value)
|
-- | Test for any Left value.
|
||||||
-- but pretty-prints the values in the failure output.
|
expectLeft :: (HasCallStack, Eq a, Show a) => Either e a -> TestTree
|
||||||
expectEqPP :: (Eq a, Show a, HasCallStack) => a -> a -> E.Test ()
|
expectLeft = testCase "sometest" . assertLeft
|
||||||
expectEqPP expected actual = if expected == actual then E.ok else E.crash $
|
|
||||||
"\nexpected:\n" <> T.pack (pshow expected) <> "\nbut got:\n" <> T.pack (pshow actual) <> "\n"
|
|
||||||
|
|
||||||
-- | Shorter and flipped version of expectEqPP. The expected value goes last.
|
-- | Assert any Right value.
|
||||||
is :: (Eq a, Show a, HasCallStack) => a -> a -> Test ()
|
assertRight :: (HasCallStack, Eq a, Show a) => Either a b -> Assertion
|
||||||
is = flip expectEqPP
|
assertRight (Right _) = return ()
|
||||||
|
assertRight (Left a) = assertFailure $ "expected Right, got (Left " ++ show a ++ ")"
|
||||||
|
|
||||||
|
-- | Test for any Right value.
|
||||||
|
expectRight :: (HasCallStack, Eq a, Show a) => Either a b -> TestTree
|
||||||
|
expectRight = testCase "sometest" . assertRight
|
||||||
|
|
||||||
-- | Test that this stateful parser runnable in IO successfully parses
|
-- | Test that this stateful parser runnable in IO successfully parses
|
||||||
-- all of the given input text, showing the parse error if it fails.
|
-- all of the given input text, showing the parse error if it fails.
|
||||||
-- Suitable for hledger's JournalParser parsers.
|
-- Suitable for hledger's JournalParser parsers.
|
||||||
expectParse :: (Monoid st, Eq a, Show a, HasCallStack) =>
|
-- expectParse :: (Monoid st, Eq a, Show a, HasCallStack) =>
|
||||||
StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> E.Test ()
|
-- StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> E.Test ()
|
||||||
expectParse parser input = do
|
expectParse :: (HasCallStack, Eq a, Show a, Monoid st) =>
|
||||||
ep <- E.io (runParserT (evalStateT (parser <* eof) mempty) "" input)
|
StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> TestTree
|
||||||
either (fail.(++"\n").("\nparse error at "++).customErrorBundlePretty)
|
expectParse parser input = testCaseSteps "sometest" $ \_step -> do
|
||||||
(const ok)
|
ep <- runParserT (evalStateT (parser <* eof) mempty) "" input
|
||||||
|
either (assertFailure.(++"\n").("\nparse error at "++).customErrorBundlePretty)
|
||||||
|
(const $ return ())
|
||||||
ep
|
ep
|
||||||
|
|
||||||
|
-- -- pretty-printing both if it fails.
|
||||||
|
-- | Like expectParse, but also test the parse result is an expected value.
|
||||||
|
expectParseEq :: (HasCallStack, Eq a, Show a, Monoid st) =>
|
||||||
|
StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> a -> TestTree
|
||||||
|
expectParseEq parser input expected = expectParseEqOn parser input id expected
|
||||||
|
|
||||||
|
-- | Like expectParseEq, but transform the parse result with the given function
|
||||||
|
-- before comparing it.
|
||||||
|
expectParseEqOn :: (HasCallStack, Eq b, Show b, Monoid st) =>
|
||||||
|
StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> (a -> b) -> b -> TestTree
|
||||||
|
expectParseEqOn parser input f expected = testCaseSteps "sometest" $ \_step -> do
|
||||||
|
ep <- runParserT (evalStateT (parser <* eof) mempty) "" input
|
||||||
|
either (assertFailure . (++"\n") . ("\nparse error at "++) . customErrorBundlePretty)
|
||||||
|
(assertEq expected . f)
|
||||||
|
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 :: (HasCallStack, Eq a, Show a, Monoid st) =>
|
||||||
|
StateT st (ParsecT CustomErr T.Text IO) a -> String -> String -> TestTree
|
||||||
|
expectParseError parser input errstr = testCaseSteps "sometest" $ \_step -> 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"
|
||||||
|
|
||||||
-- Suitable for hledger's ErroringJournalParser parsers.
|
-- Suitable for hledger's ErroringJournalParser parsers.
|
||||||
expectParseE
|
expectParseE
|
||||||
:: (Monoid st, Eq a, Show a, HasCallStack)
|
:: (HasCallStack, Eq a, Show a, Monoid st)
|
||||||
=> StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
|
=> StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
|
||||||
-> T.Text
|
-> T.Text
|
||||||
-> E.Test ()
|
-> TestTree
|
||||||
expectParseE parser input = do
|
expectParseE parser input = testCaseSteps "sometest" $ \_step -> do
|
||||||
let filepath = ""
|
let filepath = ""
|
||||||
eep <- E.io $ runExceptT $
|
eep <- runExceptT $
|
||||||
runParserT (evalStateT (parser <* eof) mempty) filepath input
|
runParserT (evalStateT (parser <* eof) mempty) filepath input
|
||||||
case eep of
|
case eep of
|
||||||
Left finalErr ->
|
Left finalErr ->
|
||||||
let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr
|
let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr
|
||||||
in fail $ "parse error at " <> prettyErr
|
in assertFailure $ "parse error at " <> prettyErr
|
||||||
Right ep ->
|
Right ep ->
|
||||||
either (fail.(++"\n").("\nparse error at "++).customErrorBundlePretty)
|
either (assertFailure.(++"\n").("\nparse error at "++).customErrorBundlePretty)
|
||||||
(const ok)
|
(const $ return ())
|
||||||
ep
|
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) =>
|
|
||||||
StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> String -> E.Test ()
|
|
||||||
expectParseError parser input errstr = do
|
|
||||||
ep <- E.io (runParserT (evalStateT parser mempty) "" input)
|
|
||||||
case ep of
|
|
||||||
Right v -> fail $ "\nparse succeeded unexpectedly, producing:\n" ++ pshow v ++ "\n"
|
|
||||||
Left e -> do
|
|
||||||
let e' = customErrorBundlePretty e
|
|
||||||
if errstr `isInfixOf` e'
|
|
||||||
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 = finalErrorBundlePretty $ 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' = customErrorBundlePretty 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
|
expectParseEqE
|
||||||
:: (Monoid st, Eq a, Show a, HasCallStack)
|
:: (Monoid st, Eq a, Show a, HasCallStack)
|
||||||
=> StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
|
=> StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
|
||||||
-> T.Text
|
-> T.Text
|
||||||
-> a
|
-> a
|
||||||
-> E.Test ()
|
-> TestTree
|
||||||
expectParseEqE parser input expected = expectParseEqOnE parser input id expected
|
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) =>
|
|
||||||
StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> (a -> b) -> b -> E.Test ()
|
|
||||||
expectParseEqOn parser input f expected = do
|
|
||||||
ep <- E.io $ runParserT (evalStateT (parser <* eof) mempty) "" input
|
|
||||||
either (fail . (++"\n") . ("\nparse error at "++) . customErrorBundlePretty)
|
|
||||||
(expectEqPP expected . f)
|
|
||||||
ep
|
|
||||||
|
|
||||||
expectParseEqOnE
|
expectParseEqOnE
|
||||||
:: (Monoid st, Eq b, Show b, HasCallStack)
|
:: (HasCallStack, Eq b, Show b, Monoid st)
|
||||||
=> StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
|
=> StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
|
||||||
-> T.Text
|
-> T.Text
|
||||||
-> (a -> b)
|
-> (a -> b)
|
||||||
-> b
|
-> b
|
||||||
-> E.Test ()
|
-> TestTree
|
||||||
expectParseEqOnE parser input f expected = do
|
expectParseEqOnE parser input f expected = testCaseSteps "sometest" $ \_step -> do
|
||||||
let filepath = ""
|
let filepath = ""
|
||||||
eep <- E.io $ runExceptT $
|
eep <- runExceptT $ runParserT (evalStateT (parser <* eof) mempty) filepath input
|
||||||
runParserT (evalStateT (parser <* eof) mempty) filepath input
|
|
||||||
case eep of
|
case eep of
|
||||||
Left finalErr ->
|
Left finalErr ->
|
||||||
let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr
|
let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr
|
||||||
in fail $ "parse error at " <> prettyErr
|
in assertFailure $ "parse error at " <> prettyErr
|
||||||
Right ep ->
|
Right ep ->
|
||||||
either (fail . (++"\n") . ("\nparse error at "++) . customErrorBundlePretty)
|
either (assertFailure . (++"\n") . ("\nparse error at "++) . customErrorBundlePretty)
|
||||||
(expectEqPP expected . f)
|
(assertEq expected . f)
|
||||||
ep
|
ep
|
||||||
|
|
||||||
|
expectParseErrorE
|
||||||
|
:: (Monoid st, Eq a, Show a, HasCallStack)
|
||||||
|
=> StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
|
||||||
|
-> T.Text
|
||||||
|
-> String
|
||||||
|
-> TestTree
|
||||||
|
expectParseErrorE parser input errstr = testCaseSteps "sometest" $ \_step -> 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"
|
||||||
|
|
||||||
-- | Run a stateful parser in IO like expectParse, then compare the
|
-- | Run a stateful parser in IO like expectParse, then compare the
|
||||||
-- final state (the wrapped state, not megaparsec's internal state),
|
-- final state (the wrapped state, not megaparsec's internal state),
|
||||||
-- transformed by the given function, with the given expected value.
|
-- transformed by the given function, with the given expected value.
|
||||||
expectParseStateOn :: (HasCallStack, Monoid st, Eq b, Show b) =>
|
expectParseStateOn :: (HasCallStack, Eq b, Show b, Monoid st) =>
|
||||||
StateT st (ParsecT CustomErr T.Text IO) a
|
StateT st (ParsecT CustomErr T.Text IO) a
|
||||||
-> T.Text
|
-> T.Text
|
||||||
-> (st -> b)
|
-> (st -> b)
|
||||||
-> b
|
-> b
|
||||||
-> E.Test ()
|
-> TestTree
|
||||||
expectParseStateOn parser input f expected = do
|
expectParseStateOn parser input f expected = testCaseSteps "sometest" $ \_step -> do
|
||||||
es <- E.io $ runParserT (execStateT (parser <* eof) mempty) "" input
|
es <- runParserT (execStateT (parser <* eof) mempty) "" input
|
||||||
case es of
|
case es of
|
||||||
Left err -> fail $ (++"\n") $ ("\nparse error at "++) $ customErrorBundlePretty err
|
Left err -> assertFailure $ (++"\n") $ ("\nparse error at "++) $ customErrorBundlePretty err
|
||||||
Right s -> expectEqPP expected $ f s
|
Right s -> assertEq expected $ f s
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ cabal-version: 1.12
|
|||||||
--
|
--
|
||||||
-- see: https://github.com/sol/hpack
|
-- see: https://github.com/sol/hpack
|
||||||
--
|
--
|
||||||
-- hash: 4b32c89e49ba64c66ca8552bb3ac2d54099cff23f9950b7fe294a32297a9b01a
|
-- hash: f8ee8c9fd0412cc0a8cd5c6286b7ef4f9a33ae2a30989dfc0b99c3f79bd55622
|
||||||
|
|
||||||
name: hledger-lib
|
name: hledger-lib
|
||||||
version: 1.15.99
|
version: 1.15.99
|
||||||
@ -107,7 +107,7 @@ library
|
|||||||
, Glob >=0.9
|
, Glob >=0.9
|
||||||
, ansi-terminal >=0.6.2.3
|
, ansi-terminal >=0.6.2.3
|
||||||
, array
|
, array
|
||||||
, base >=4.9 && <4.13
|
, base >=4.9 && <4.14
|
||||||
, base-compat-batteries >=0.10.1 && <0.12
|
, base-compat-batteries >=0.10.1 && <0.12
|
||||||
, blaze-markup >=0.5.1
|
, blaze-markup >=0.5.1
|
||||||
, bytestring
|
, bytestring
|
||||||
@ -119,7 +119,6 @@ library
|
|||||||
, data-default >=0.5
|
, data-default >=0.5
|
||||||
, deepseq
|
, deepseq
|
||||||
, directory
|
, directory
|
||||||
, easytest >=0.2.1 && <0.3
|
|
||||||
, extra >=1.6.3
|
, extra >=1.6.3
|
||||||
, fgl >=5.5.4.0
|
, fgl >=5.5.4.0
|
||||||
, file-embed >=0.0.10
|
, file-embed >=0.0.10
|
||||||
@ -136,6 +135,8 @@ library
|
|||||||
, safe >=0.2
|
, safe >=0.2
|
||||||
, split >=0.1
|
, split >=0.1
|
||||||
, tabular >=0.2
|
, tabular >=0.2
|
||||||
|
, tasty >=1.2.3
|
||||||
|
, tasty-hunit >=0.10.0.2
|
||||||
, template-haskell
|
, template-haskell
|
||||||
, text >=1.2
|
, text >=1.2
|
||||||
, time >=1.5
|
, time >=1.5
|
||||||
@ -160,7 +161,7 @@ test-suite doctests
|
|||||||
, Glob >=0.7
|
, Glob >=0.7
|
||||||
, ansi-terminal >=0.6.2.3
|
, ansi-terminal >=0.6.2.3
|
||||||
, array
|
, array
|
||||||
, base >=4.9 && <4.13
|
, base >=4.9 && <4.14
|
||||||
, base-compat-batteries >=0.10.1 && <0.12
|
, base-compat-batteries >=0.10.1 && <0.12
|
||||||
, blaze-markup >=0.5.1
|
, blaze-markup >=0.5.1
|
||||||
, bytestring
|
, bytestring
|
||||||
@ -173,7 +174,6 @@ test-suite doctests
|
|||||||
, deepseq
|
, deepseq
|
||||||
, directory
|
, directory
|
||||||
, doctest >=0.16
|
, doctest >=0.16
|
||||||
, easytest >=0.2.1 && <0.3
|
|
||||||
, extra >=1.6.3
|
, extra >=1.6.3
|
||||||
, fgl >=5.5.4.0
|
, fgl >=5.5.4.0
|
||||||
, file-embed >=0.0.10
|
, file-embed >=0.0.10
|
||||||
@ -190,6 +190,8 @@ test-suite doctests
|
|||||||
, safe >=0.2
|
, safe >=0.2
|
||||||
, split >=0.1
|
, split >=0.1
|
||||||
, tabular >=0.2
|
, tabular >=0.2
|
||||||
|
, tasty >=1.2.3
|
||||||
|
, tasty-hunit >=0.10.0.2
|
||||||
, template-haskell
|
, template-haskell
|
||||||
, text >=1.2
|
, text >=1.2
|
||||||
, time >=1.5
|
, time >=1.5
|
||||||
@ -204,58 +206,3 @@ test-suite doctests
|
|||||||
if (impl(ghc < 8.2))
|
if (impl(ghc < 8.2))
|
||||||
buildable: False
|
buildable: False
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
||||||
test-suite easytests
|
|
||||||
type: exitcode-stdio-1.0
|
|
||||||
main-is: easytests.hs
|
|
||||||
hs-source-dirs:
|
|
||||||
./.
|
|
||||||
test
|
|
||||||
ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans
|
|
||||||
build-depends:
|
|
||||||
Decimal
|
|
||||||
, Glob >=0.9
|
|
||||||
, ansi-terminal >=0.6.2.3
|
|
||||||
, array
|
|
||||||
, base >=4.9 && <4.13
|
|
||||||
, base-compat-batteries >=0.10.1 && <0.12
|
|
||||||
, blaze-markup >=0.5.1
|
|
||||||
, bytestring
|
|
||||||
, call-stack
|
|
||||||
, cassava
|
|
||||||
, cassava-megaparsec
|
|
||||||
, cmdargs >=0.10
|
|
||||||
, containers
|
|
||||||
, data-default >=0.5
|
|
||||||
, deepseq
|
|
||||||
, directory
|
|
||||||
, easytest >=0.2.1 && <0.3
|
|
||||||
, extra >=1.6.3
|
|
||||||
, fgl >=5.5.4.0
|
|
||||||
, file-embed >=0.0.10
|
|
||||||
, filepath
|
|
||||||
, hashtables >=1.2.3.1
|
|
||||||
, hledger-lib
|
|
||||||
, megaparsec >=7.0.0 && <8
|
|
||||||
, mtl
|
|
||||||
, mtl-compat
|
|
||||||
, old-time
|
|
||||||
, parsec >=3
|
|
||||||
, parser-combinators >=0.4.0
|
|
||||||
, pretty-show >=1.6.4
|
|
||||||
, regex-tdfa
|
|
||||||
, safe >=0.2
|
|
||||||
, split >=0.1
|
|
||||||
, tabular >=0.2
|
|
||||||
, template-haskell
|
|
||||||
, text >=1.2
|
|
||||||
, time >=1.5
|
|
||||||
, timeit
|
|
||||||
, transformers >=0.2
|
|
||||||
, uglymemo
|
|
||||||
, utf8-string >=0.3.5
|
|
||||||
buildable: True
|
|
||||||
if (!impl(ghc >= 8.0))
|
|
||||||
build-depends:
|
|
||||||
semigroups ==0.18.*
|
|
||||||
default-language: Haskell2010
|
|
||||||
|
|||||||
@ -39,7 +39,7 @@ extra-source-files:
|
|||||||
#data-files:
|
#data-files:
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
- base >=4.9 && <4.13
|
- base >=4.9 && <4.14
|
||||||
- base-compat-batteries >=0.10.1 && <0.12
|
- base-compat-batteries >=0.10.1 && <0.12
|
||||||
- ansi-terminal >=0.6.2.3
|
- ansi-terminal >=0.6.2.3
|
||||||
- array
|
- array
|
||||||
@ -54,7 +54,6 @@ dependencies:
|
|||||||
- Decimal
|
- Decimal
|
||||||
- deepseq
|
- deepseq
|
||||||
- directory
|
- directory
|
||||||
- easytest >= 0.2.1 && <0.3
|
|
||||||
- fgl >=5.5.4.0
|
- fgl >=5.5.4.0
|
||||||
- file-embed >=0.0.10
|
- file-embed >=0.0.10
|
||||||
- filepath
|
- filepath
|
||||||
@ -70,6 +69,8 @@ dependencies:
|
|||||||
- safe >=0.2
|
- safe >=0.2
|
||||||
- split >=0.1
|
- split >=0.1
|
||||||
- tabular >=0.2
|
- tabular >=0.2
|
||||||
|
- tasty >=1.2.3
|
||||||
|
- tasty-hunit >=0.10.0.2
|
||||||
- template-haskell
|
- template-haskell
|
||||||
- text >=1.2
|
- text >=1.2
|
||||||
- time >=1.5
|
- time >=1.5
|
||||||
@ -186,10 +187,10 @@ tests:
|
|||||||
buildable: false
|
buildable: false
|
||||||
|
|
||||||
|
|
||||||
easytests:
|
# easytests:
|
||||||
buildable: true
|
# buildable: true
|
||||||
source-dirs: test
|
# source-dirs: test
|
||||||
main: easytests.hs
|
# main: easytests.hs
|
||||||
other-modules: [] # prevent double compilation, https://github.com/sol/hpack/issues/188
|
# other-modules: [] # prevent double compilation, https://github.com/sol/hpack/issues/188
|
||||||
dependencies:
|
# dependencies:
|
||||||
- hledger-lib
|
# - hledger-lib
|
||||||
|
|||||||
@ -55,9 +55,8 @@ import Data.Monoid ((<>))
|
|||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Data.Time.Calendar
|
import Data.Time.Calendar
|
||||||
import qualified EasyTest
|
import System.Environment (withArgs)
|
||||||
import System.Console.CmdArgs.Explicit as C
|
import System.Console.CmdArgs.Explicit as C
|
||||||
import System.Exit
|
|
||||||
|
|
||||||
import Hledger
|
import Hledger
|
||||||
import Hledger.Cli.CliOptions
|
import Hledger.Cli.CliOptions
|
||||||
@ -267,19 +266,14 @@ testmode = hledgerCommandMode
|
|||||||
-- not be used (and would raise an error).
|
-- not be used (and would raise an error).
|
||||||
testcmd :: CliOpts -> Journal -> IO ()
|
testcmd :: CliOpts -> Journal -> IO ()
|
||||||
testcmd opts _undefined = do
|
testcmd opts _undefined = do
|
||||||
let args = words' $ query_ $ reportopts_ opts
|
withArgs (words' $ query_ $ reportopts_ opts) $
|
||||||
-- workaround for https://github.com/joelburget/easytest/issues/11
|
defaultMain $ tests "sometests" [ -- Test.Tasty.defaultMain from Hledger.Util.Tests
|
||||||
-- import System.IO (hSetEncoding, stdout, stderr, utf8)
|
tests_Hledger
|
||||||
-- hSetEncoding stdout utf8
|
,tests "Hledger.Cli" [
|
||||||
-- hSetEncoding stderr utf8
|
tests_Cli_Utils
|
||||||
e <- runEasytests args $ EasyTest.tests [
|
,tests_Commands
|
||||||
tests_Hledger
|
]
|
||||||
,tests "Hledger.Cli" [
|
|
||||||
tests_Cli_Utils
|
|
||||||
,tests_Commands
|
|
||||||
]
|
]
|
||||||
]
|
|
||||||
if e then exitFailure else exitSuccess
|
|
||||||
|
|
||||||
|
|
||||||
tests_Commands = tests "Commands" [
|
tests_Commands = tests "Commands" [
|
||||||
@ -288,60 +282,62 @@ tests_Commands = tests "Commands" [
|
|||||||
|
|
||||||
-- some more tests easiest to define here:
|
-- some more tests easiest to define here:
|
||||||
|
|
||||||
,test "apply account directive" $ do
|
,test "apply account directive" $ let
|
||||||
let ignoresourcepos j = j{jtxns=map (\t -> t{tsourcepos=nullsourcepos}) (jtxns j)}
|
ignoresourcepos j = j{jtxns=map (\t -> t{tsourcepos=nullsourcepos}) (jtxns j)}
|
||||||
let sameParse str1 str2 = do j1 <- io $ readJournal def Nothing str1 >>= either error' (return . ignoresourcepos)
|
sameParse str1 str2 = testCaseSteps "sometest" $ \_step -> do
|
||||||
j2 <- io $ readJournal def Nothing str2 >>= either error' (return . ignoresourcepos)
|
j1 <- readJournal def Nothing str1 >>= either error' (return . ignoresourcepos)
|
||||||
j1 `is` j2{jlastreadtime=jlastreadtime j1, jfiles=jfiles j1} --, jparsestate=jparsestate j1}
|
j2 <- readJournal def Nothing str2 >>= either error' (return . ignoresourcepos)
|
||||||
sameParse
|
j1 @?= j2{jlastreadtime=jlastreadtime j1, jfiles=jfiles j1} --, jparsestate=jparsestate j1}
|
||||||
("2008/12/07 One\n alpha $-1\n beta $1\n" <>
|
in sameParse
|
||||||
"apply account outer\n2008/12/07 Two\n aigh $-2\n bee $2\n" <>
|
("2008/12/07 One\n alpha $-1\n beta $1\n" <>
|
||||||
"apply account inner\n2008/12/07 Three\n gamma $-3\n delta $3\n" <>
|
"apply account outer\n2008/12/07 Two\n aigh $-2\n bee $2\n" <>
|
||||||
"end apply account\n2008/12/07 Four\n why $-4\n zed $4\n" <>
|
"apply account inner\n2008/12/07 Three\n gamma $-3\n delta $3\n" <>
|
||||||
"end apply account\n2008/12/07 Five\n foo $-5\n bar $5\n"
|
"end apply account\n2008/12/07 Four\n why $-4\n zed $4\n" <>
|
||||||
)
|
"end apply account\n2008/12/07 Five\n foo $-5\n bar $5\n"
|
||||||
("2008/12/07 One\n alpha $-1\n beta $1\n" <>
|
)
|
||||||
"2008/12/07 Two\n outer:aigh $-2\n outer:bee $2\n" <>
|
("2008/12/07 One\n alpha $-1\n beta $1\n" <>
|
||||||
"2008/12/07 Three\n outer:inner:gamma $-3\n outer:inner:delta $3\n" <>
|
"2008/12/07 Two\n outer:aigh $-2\n outer:bee $2\n" <>
|
||||||
"2008/12/07 Four\n outer:why $-4\n outer:zed $4\n" <>
|
"2008/12/07 Three\n outer:inner:gamma $-3\n outer:inner:delta $3\n" <>
|
||||||
"2008/12/07 Five\n foo $-5\n bar $5\n"
|
"2008/12/07 Four\n outer:why $-4\n outer:zed $4\n" <>
|
||||||
)
|
"2008/12/07 Five\n foo $-5\n bar $5\n"
|
||||||
|
)
|
||||||
|
|
||||||
,test "apply account directive should preserve \"virtual\" posting type" $ do
|
,testCaseSteps "apply account directive should preserve \"virtual\" posting type" $ \_step -> do
|
||||||
j <- io $ readJournal def Nothing "apply account test\n2008/12/07 One\n (from) $-1\n (to) $1\n" >>= either error' return
|
j <- readJournal def Nothing "apply account test\n2008/12/07 One\n (from) $-1\n (to) $1\n" >>= either error' return
|
||||||
let p = head $ tpostings $ head $ jtxns j
|
let p = head $ tpostings $ head $ jtxns j
|
||||||
paccount p `is` "test:from"
|
paccount p @?= "test:from"
|
||||||
ptype p `is` VirtualPosting
|
ptype p @?= VirtualPosting
|
||||||
|
|
||||||
,test "account aliases" $ do
|
,testCaseSteps "account aliases" $ \_step -> do
|
||||||
j <- io $ readJournal def Nothing "!alias expenses = equity:draw:personal\n1/1\n (expenses:food) 1\n" >>= either error' return
|
j <- readJournal def Nothing "!alias expenses = equity:draw:personal\n1/1\n (expenses:food) 1\n" >>= either error' return
|
||||||
let p = head $ tpostings $ head $ jtxns j
|
let p = head $ tpostings $ head $ jtxns j
|
||||||
paccount p `is` "equity:draw:personal:food"
|
paccount p @?= "equity:draw:personal:food"
|
||||||
|
|
||||||
,test "ledgerAccountNames" $
|
,testCase "ledgerAccountNames" $
|
||||||
ledgerAccountNames ledger7 `is`
|
(ledgerAccountNames ledger7)
|
||||||
["assets","assets:cash","assets:checking","assets:saving","equity","equity:opening balances",
|
@?=
|
||||||
"expenses","expenses:food","expenses:food:dining","expenses:phone","expenses:vacation",
|
["assets","assets:cash","assets:checking","assets:saving","equity","equity:opening balances",
|
||||||
"liabilities","liabilities:credit cards","liabilities:credit cards:discover"]
|
"expenses","expenses:food","expenses:food:dining","expenses:phone","expenses:vacation",
|
||||||
|
"liabilities","liabilities:credit cards","liabilities:credit cards:discover"]
|
||||||
|
|
||||||
-- ,test "journalCanonicaliseAmounts" ~:
|
-- ,test "journalCanonicaliseAmounts" ~:
|
||||||
-- "use the greatest precision" ~:
|
-- "use the greatest precision" ~:
|
||||||
-- (map asprecision $ journalAmountAndPriceCommodities $ journalCanonicaliseAmounts $ journalWithAmounts ["1","2.00"]) `is` [2,2]
|
-- (map asprecision $ journalAmountAndPriceCommodities $ journalCanonicaliseAmounts $ journalWithAmounts ["1","2.00"]) @?= [2,2]
|
||||||
|
|
||||||
-- don't know what this should do
|
-- don't know what this should do
|
||||||
-- ,test "elideAccountName" ~: do
|
-- ,test "elideAccountName" ~: do
|
||||||
-- (elideAccountName 50 "aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa"
|
-- (elideAccountName 50 "aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa"
|
||||||
-- `is` "aa:aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa")
|
-- @?= "aa:aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa")
|
||||||
-- (elideAccountName 20 "aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa"
|
-- (elideAccountName 20 "aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa"
|
||||||
-- `is` "aa:aa:aaaaaaaaaaaaaa")
|
-- @?= "aa:aa:aaaaaaaaaaaaaa")
|
||||||
|
|
||||||
,test "default year" $ do
|
,testCaseSteps "default year" $ \_step -> do
|
||||||
j <- io $ readJournal def Nothing defaultyear_journal_txt >>= either error' return
|
j <- readJournal def Nothing defaultyear_journal_txt >>= either error' return
|
||||||
tdate (head $ jtxns j) `is` fromGregorian 2009 1 1
|
tdate (head $ jtxns j) @?= fromGregorian 2009 1 1
|
||||||
|
|
||||||
,test "show dollars" $ showAmount (usd 1) `is` "$1.00"
|
,testCase "show dollars" $ showAmount (usd 1) @?= "$1.00"
|
||||||
|
|
||||||
,test "show hours" $ showAmount (hrs 1) `is` "1.00h"
|
,testCase "show hours" $ showAmount (hrs 1) @?= "1.00h"
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -640,10 +640,10 @@ balanceReportTableAsText ropts = tableAsText ropts showamt
|
|||||||
tests_Balance = tests "Balance" [
|
tests_Balance = tests "Balance" [
|
||||||
|
|
||||||
tests "balanceReportAsText" [
|
tests "balanceReportAsText" [
|
||||||
test "unicode in balance layout" $ do
|
testCaseSteps "unicode in balance layout" $ \_step -> do
|
||||||
j <- io $ readJournal' "2009/01/01 * медвежья шкура\n расходы:покупки 100\n актив:наличные\n"
|
j <- readJournal' "2009/01/01 * медвежья шкура\n расходы:покупки 100\n актив:наличные\n"
|
||||||
let opts = defreportopts
|
let opts = defreportopts
|
||||||
balanceReportAsText opts (balanceReport opts (queryFromOpts (parsedate "2008/11/26") opts) j) `is`
|
balanceReportAsText opts (balanceReport opts (queryFromOpts (parsedate "2008/11/26") opts) j) @?=
|
||||||
unlines
|
unlines
|
||||||
[" -100 актив:наличные"
|
[" -100 актив:наличные"
|
||||||
," 100 расходы:покупки"
|
," 100 расходы:покупки"
|
||||||
|
|||||||
@ -194,10 +194,10 @@ postingsReportItemAsText opts preferredamtwidth preferredbalwidth (mdate, mendda
|
|||||||
tests_Register = tests "Register" [
|
tests_Register = tests "Register" [
|
||||||
|
|
||||||
tests "postingsReportAsText" [
|
tests "postingsReportAsText" [
|
||||||
test "unicode in register layout" $ do
|
testCaseSteps "unicode in register layout" $ \_step -> do
|
||||||
j <- io $ readJournal' "2009/01/01 * медвежья шкура\n расходы:покупки 100\n актив:наличные\n"
|
j <- readJournal' "2009/01/01 * медвежья шкура\n расходы:покупки 100\n актив:наличные\n"
|
||||||
let opts = defreportopts
|
let opts = defreportopts
|
||||||
(postingsReportAsText defcliopts $ postingsReport opts (queryFromOpts (parsedate "2008/11/26") opts) j) `is` unlines
|
(postingsReportAsText defcliopts $ postingsReport opts (queryFromOpts (parsedate "2008/11/26") opts) j) @?= unlines
|
||||||
["2009/01/01 медвежья шкура расходы:покупки 100 100"
|
["2009/01/01 медвежья шкура расходы:покупки 100 100"
|
||||||
," актив:наличные -100 0"]
|
," актив:наличные -100 0"]
|
||||||
]
|
]
|
||||||
|
|||||||
@ -4,7 +4,7 @@ cabal-version: 1.12
|
|||||||
--
|
--
|
||||||
-- see: https://github.com/sol/hpack
|
-- see: https://github.com/sol/hpack
|
||||||
--
|
--
|
||||||
-- hash: 6bdd5e55ef3db9761bde530f23cd7fe97dc63e707e18129732a4d827505c6aa4
|
-- hash: a8cb399c2c97d23c9fc48e12709494216df3069cce7ec448277be8fde91f72d0
|
||||||
|
|
||||||
name: hledger
|
name: hledger
|
||||||
version: 1.15.99
|
version: 1.15.99
|
||||||
@ -156,7 +156,6 @@ library
|
|||||||
, containers
|
, containers
|
||||||
, data-default >=0.5
|
, data-default >=0.5
|
||||||
, directory
|
, directory
|
||||||
, easytest >=0.2.1 && <0.3
|
|
||||||
, extra >=1.6.3
|
, extra >=1.6.3
|
||||||
, filepath
|
, filepath
|
||||||
, hashable >=1.2.4
|
, hashable >=1.2.4
|
||||||
@ -208,7 +207,6 @@ executable hledger
|
|||||||
, containers
|
, containers
|
||||||
, data-default >=0.5
|
, data-default >=0.5
|
||||||
, directory
|
, directory
|
||||||
, easytest >=0.2.1 && <0.3
|
|
||||||
, extra >=1.6.3
|
, extra >=1.6.3
|
||||||
, filepath
|
, filepath
|
||||||
, haskeline >=0.6
|
, haskeline >=0.6
|
||||||
@ -260,7 +258,6 @@ test-suite test
|
|||||||
, containers
|
, containers
|
||||||
, data-default >=0.5
|
, data-default >=0.5
|
||||||
, directory
|
, directory
|
||||||
, easytest >=0.2.1 && <0.3
|
|
||||||
, extra >=1.6.3
|
, extra >=1.6.3
|
||||||
, filepath
|
, filepath
|
||||||
, haskeline >=0.6
|
, haskeline >=0.6
|
||||||
@ -312,7 +309,6 @@ benchmark bench
|
|||||||
, criterion
|
, criterion
|
||||||
, data-default >=0.5
|
, data-default >=0.5
|
||||||
, directory
|
, directory
|
||||||
, easytest >=0.2.1 && <0.3
|
|
||||||
, extra >=1.6.3
|
, extra >=1.6.3
|
||||||
, filepath
|
, filepath
|
||||||
, haskeline >=0.6
|
, haskeline >=0.6
|
||||||
|
|||||||
@ -115,7 +115,6 @@ dependencies:
|
|||||||
- data-default >=0.5
|
- data-default >=0.5
|
||||||
- Decimal
|
- Decimal
|
||||||
- directory
|
- directory
|
||||||
- easytest >= 0.2.1 && <0.3
|
|
||||||
- extra >=1.6.3
|
- extra >=1.6.3
|
||||||
- filepath
|
- filepath
|
||||||
- haskeline >=0.6
|
- haskeline >=0.6
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user