tasty-checklist 1.0.1.0 → 1.0.2.0
raw patch · 5 files changed
+141/−56 lines, 5 filesdep +HUnitdep +doctestdep ~basedep ~parameterized-utilsdep ~textPVP ok
version bump matches the API change (PVP)
Dependencies added: HUnit, doctest
Dependency ranges changed: base, parameterized-utils, text
API changes (from Hackage documentation)
+ Test.Tasty.Checklist: [Got] :: Text -> (i -> Bool) -> DerivedVal i Bool
+ Test.Tasty.Checklist: data CheckResult
Files
- CHANGELOG.md +7/−0
- src/Test/Tasty/Checklist.hs +111/−54
- tasty-checklist.cabal +16/−2
- test/DocTestChecklist.hs +6/−0
- test/TestChecklist.hs +1/−0
CHANGELOG.md view
@@ -1,5 +1,12 @@ # Revision history for tasty-checklist +## 1.0.2.0 -- 2021-07-25+ * Added `Got` specifier for simpler specification of boolean+ `DerivedVal` than using `Val`.+ * Better clarity in failure messages.+ * Cleaned up documentation and added doctest for ensuring+ documentation samples are accurate.+ ## 1.0.1.0 -- 2021-06-27 * ASCII output, no UTF-8.
src/Test/Tasty/Checklist.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE ImplicitParams #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeSynonymInstances #-}@@ -35,13 +36,19 @@ module Test.Tasty.Checklist (+ -- * Checklist testing context withChecklist , CanCheck+ -- * Performing or Disabling checks , check , discardCheck+ -- * Type-safe multi-check specifications+ -- $checkValues+ -- $setup , checkValues- , DerivedVal(Val)+ , DerivedVal(Val, Got) -- * Error reporting+ , CheckResult , ChecklistFailures -- * Displaying tested values , TestShow(testShow)@@ -66,15 +73,15 @@ data ChecklistFailures = ChecklistFailures Text [CheckResult] --- | The 'CheckResult' captures the failure information for a check+-- | The internal 'CheckResult' captures the failure information for a check -data CheckResult = CheckFailed Text Text+data CheckResult = CheckFailed Text Text -- check name from user, fail message instance Exception ChecklistFailures instance Show CheckResult where- show (CheckFailed what val) =- "Failed check of " <> T.unpack what <> " against " <> show val+ show (CheckFailed what msg) =+ "Failed check of " <> T.unpack what <> " with " <> T.unpack msg instance Show ChecklistFailures where show (ChecklistFailures topMsg fails) =@@ -122,6 +129,8 @@ -- value fails the check. The last argument is the value to check. -- -- >>> :set -XOverloadedStrings+-- >>> import Test.Tasty+-- >>> import Test.Tasty.HUnit -- >>> :{ -- >>> defaultMain $ testCase "odd numbers" $ withChecklist "odds" $ do -- >>> let three = 3 :: Int@@ -130,19 +139,22 @@ -- >>> check "7 + 3 is odd" odd $ 7 + three -- >>> check "7 is odd" odd (7 :: Int) -- >>> :}--- tst1: FAIL--- Exception: ERROR: numbers+-- odd numbers: FAIL+-- Exception: ERROR: odds -- 2 checks failed in this checklist:--- -Failed check of "two is odd" with "2"--- -Failed check of "7 + 3 is odd" with "10"+-- -Failed check of two is odd with 2+-- -Failed check of 7 + 3 is odd with 10+-- <BLANKLINE>+-- 1 out of 1 tests failed (...s)+-- *** Exception: ExitFailure 1 -- -- Any check failures are also printed to stdout (and omitted from the -- above for clarity). This is so that those failures are reported -- even if a more standard test assertion is used that prevents--- completion of the checklist. Thus, if an `assertEqual "values"--- three 7` had been added to the above, that would have been the only+-- completion of the checklist. Thus, if an @assertEqual "values"+-- three 7@ had been added to the above, that would have been the only -- actual (and immediate) fail for the test, but any failing 'check's--- appearing before that 'assertEqual' would still have printed.+-- appearing before that @assertEqual@ would still have printed. check :: (CanCheck, TestShow a, MonadIO m) => Text -> (a -> Bool) -> a -> m ()@@ -171,6 +183,57 @@ ---------------------------------------------------------------------- +-- $checkValues+--+-- Implementing a number of discrete 'check' calls can be tedious,+-- especially when they are validating different aspects of the same+-- result value. To facilitate this, the 'checkValues' function can+-- be used along with a type-safe list of checks to perform.+--+-- To demonstrate this, first consider the following sample program,+-- which has code that generates a complex @Struct@ value, along with+-- tests for various fields in that @Struct@.++-- $setup+-- >>> :set -XPatternSynonyms+-- >>> :set -XOverloadedStrings+-- >>>+-- >>> import Data.Parameterized.Context ( pattern Empty, pattern (:>) )+-- >>> import Test.Tasty.Checklist+-- >>> import Test.Tasty+-- >>> import Test.Tasty.HUnit+-- >>>+-- >>> :{+-- >>> data Struct = MyStruct { foo :: Int, bar :: Char, baz :: String }+-- >>>+-- >>> instance Show Struct where+-- >>> show s = baz s <> " is " <> show (foo s) <> [bar s]+-- >>> instance TestShow Struct where testShow = show+-- >>>+-- >>> someFun :: Int -> Struct+-- >>> someFun n = MyStruct (n * 6)+-- >>> (if n * 6 == 42 then '!' else '?')+-- >>> "The answer to the universe"+-- >>>+-- >>> oddAnswer :: Struct -> Bool+-- >>> oddAnswer = odd . foo+-- >>>+-- >>> test = testCase "someFun result" $+-- >>> withChecklist "results for someFun" $+-- >>> someFun 3 `checkValues`+-- >>> (Empty+-- >>> :> Val "foo" foo 42+-- >>> :> Val "baz field" baz "The answer to the universe"+-- >>> :> Val "shown" show "The answer to the universe is 42!"+-- >>> :> Val "odd answer" oddAnswer False+-- >>> :> Got "even answer" (not . oddAnswer)+-- >>> :> Val "double-checking foo" foo 42+-- >>> )+-- >>> :}+--+-- This code will be used below to demonstrate various advanced+-- checklist capabilities.+ -- | The 'checkValues' is a checklist that tests various values that -- can be derived from the input value. The input value is provided, -- along with an 'Data.Parameterized.Context.Assignment' list of@@ -181,43 +244,19 @@ -- This is convenient to gather together a number of validations on a -- single datatype and represent them economically. ----- One example is testing the fields of a record structure:------ > {-# LANGUAGE PatternSynonyms, OverloadedStrings #-}--- >--- > import Data.Parameterized.Context ( pattern Empty, pattern (:>) )--- > import Test.Tasty.Checklist--- >--- > data Struct = MyStruct { foo :: Int, bar :: Char, baz :: String }--- >--- > instance Show Struct where--- > show s = baz s <> " is " <> foo s <> bar s--- >--- > someFun :: Int -> Struct--- > someFun n = MyStruct (n * 6)--- > (if n * 6 == 42 then '!' else '?')--- > "The answer to the universe"--- >--- > oddAnswer :: Struct -> Bool--- > oddAnswer = odd . foo--- >--- > test = testCase "someFun result" $--- > someFun 3 `checkValues`--- > (Empty--- > :> Val "foo" foo 42--- > :> Val "baz field" baz "The answer to the universe"--- > :> Val "shown" show "The answer to the universe is 42!"--- > :> Val "odd answer" oddAnswer False--- > :> Val "double-checking foo" foo 42--- > )------ Running this test:+-- One example is testing the fields of a record structure, given the+-- above code: -- -- >>> defaultMain test--- ERROR: on input "The answer to the universe is 18?"--- 2 checks failed:--- -Failed check of "foo" with "42"--- -Failed check of "shown" with "The answer to the universe is 42!"+-- someFun result: FAIL+-- Exception: ERROR: results for someFun+-- 3 checks failed in this checklist:+-- -Failed check of foo on input <<The answer to the universe is 18?>> expected <<42>> but failed with 18+-- -Failed check of shown on input <<The answer to the universe is 18?>> expected <<"The answer to the universe is 42!">> but failed with "The answer to the universe is 18?"+-- -Failed check of double-checking foo on input <<The answer to the universe is 18?>> expected <<42>> but failed with 18+-- <BLANKLINE>+-- 1 out of 1 tests failed (...s)+-- *** Exception: ExitFailure 1 -- -- In this case, several of the values checked were correct, but more -- than one was wrong. Helpfully, this test output lists /all/ the@@ -233,19 +272,37 @@ chkValue :: CanCheck => TestShow dType => dType -> Ctx.Index idx valType -> DerivedVal dType valType -> IO ()-chkValue got _idx (Val txt fld v) =- let r = fld got- msg = txt <> " on input <<" <> ti <> ">> got <<" <> tr <> ">>"- ti = T.pack (testShow got)- tr = T.pack (testShow r)- in check msg (r ==) v-+chkValue got _idx = \case+ (Val txt fld v) ->+ let r = fld got+ msg = txt <> " on input <<" <> ti <> ">> expected <<" <> tv <> ">> but failed"+ ti = T.pack (testShow got)+ tv = T.pack (testShow v)+ in check msg (v ==) r+ (Got txt fld) ->+ let r = fld got+ msg = txt <> " on input <<" <> ti <> ">>"+ ti = T.pack (testShow got)+ in check msg (True ==) r -- | Each entry in the 'Data.Parameterized.Context.Assignment' list -- for 'checkValues' should be one of these 'DerivedVal' values. data DerivedVal i d where++ -- | Val allows specification of a description string, an extraction+ -- function, and the expected value to be extracted. The+ -- 'checkValues' function will add a Failure if the expected value is+ -- not obtained. Val :: (TestShow d, Eq d) => Text -> (i -> d) -> d -> DerivedVal i d++ -- | Got allow specification of a description string and an+ -- extraction function. The 'checkValues' function will add a+ -- Failure if the extraction result is False.+ --+ -- > Val "what" f True === Got "what" f+ --+ Got :: Text -> (i -> Bool) -> DerivedVal i Bool ----------------------------------------------------------------------
tasty-checklist.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: tasty-checklist-version: 1.0.1.0+version: 1.0.2.0 synopsis: Check multiple items during a tasty test description: Allows the test to check a number of items during a test and@@ -60,4 +60,18 @@ , tasty-hunit , tasty-expected-failure , text- ++test-suite checklistDocTests+ import: bldspec+ type: exitcode-stdio-1.0+ default-language: Haskell2010+ hs-source-dirs: test+ main-is: DocTestChecklist.hs+ build-depends: base+ , HUnit+ , doctest >= 0.10 && < 0.17+ , parameterized-utils+ , tasty+ , tasty-checklist+ , tasty-hunit+ , text
+ test/DocTestChecklist.hs view
@@ -0,0 +1,6 @@+module Main ( main ) where++import qualified Test.DocTest as T++main :: IO ()+main = T.doctest ["-isrc", "src/Test/Tasty/Checklist.hs"]
test/TestChecklist.hs view
@@ -84,6 +84,7 @@ :> Val "baz" baz "The answer to the universe" :> Val "shown" show "The answer to the universe is 42!" :> Val "odd answer" oddAnswer False+ :> Got "even answer" (not . oddAnswer) ) 3 @=? (5 :: Int)