tasty-checklist 1.0.2.0 → 1.0.3.0
raw patch · 4 files changed
+123/−8 lines, 4 filesdep ~doctestPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: doctest
API changes (from Hackage documentation)
+ Test.Tasty.Checklist: [Observe] :: Eq d => Text -> (i -> d) -> d -> (d -> d -> String) -> DerivedVal i d
+ Test.Tasty.Checklist: multiLineDiff :: Text -> Text -> String
Files
- CHANGELOG.md +7/−0
- src/Test/Tasty/Checklist.hs +61/−5
- tasty-checklist.cabal +2/−2
- test/TestChecklist.hs +53/−1
CHANGELOG.md view
@@ -1,5 +1,12 @@ # Revision history for tasty-checklist +## 1.0.3.0 -- 2021-08-13+ * Bump doctest upper constraints (contributed by Felix Yan)+ * Added missing doctest source+ * Added `Observe` to `DerivedVal` to allow using a user-supplied+ observation function on failure.+ * Added `multiLineDiff` helper function+ ## 1.0.2.0 -- 2021-07-25 * Added `Got` specifier for simpler specification of boolean `DerivedVal` than using `Val`.
src/Test/Tasty/Checklist.hs view
@@ -46,13 +46,14 @@ -- $checkValues -- $setup , checkValues- , DerivedVal(Val, Got)+ , DerivedVal(Val, Got, Observe) -- * Error reporting , CheckResult , ChecklistFailures -- * Displaying tested values , TestShow(testShow) , testShowList+ , multiLineDiff ) where @@ -158,10 +159,14 @@ check :: (CanCheck, TestShow a, MonadIO m) => Text -> (a -> Bool) -> a -> m ()-check what eval val = do+check = checkShow testShow++checkShow :: (CanCheck, MonadIO m)+ => (a -> String) -> Text -> (a -> Bool) -> a -> m ()+checkShow showit what eval val = do r <- liftIO $ evaluate (eval val) unless r $ do- let chk = CheckFailed what $ T.pack $ testShow val+ let chk = CheckFailed what $ T.pack $ showit val liftIO $ modifyIORef ?checker (chk:) @@ -279,6 +284,10 @@ ti = T.pack (testShow got) tv = T.pack (testShow v) in check msg (v ==) r+ (Observe txt fld v observationReport) ->+ let r = fld got+ msg = txt <> " observation failure"+ in checkShow (observationReport v) msg (v ==) r (Got txt fld) -> let r = fld got msg = txt <> " on input <<" <> ti <> ">>"@@ -287,6 +296,9 @@ -- | Each entry in the 'Data.Parameterized.Context.Assignment' list -- for 'checkValues' should be one of these 'DerivedVal' values.+--+-- The @i@ type parameter is the input type, and the @d@ is the value+-- derived from that input type. data DerivedVal i d where @@ -296,7 +308,7 @@ -- not obtained. Val :: (TestShow d, Eq d) => Text -> (i -> d) -> d -> DerivedVal i d - -- | Got allow specification of a description string and an+ -- | Got allows specification of a description string and an -- extraction function. The 'checkValues' function will add a -- Failure if the extraction result is False. --@@ -304,12 +316,22 @@ -- Got :: Text -> (i -> Bool) -> DerivedVal i Bool + -- | Observe performs the same checking as Val except the TestShow+ -- information for the actual and expected values are not as useful+ -- (e.g. they are lengthy, multi-line, or gibberish) so instead this+ -- allows the specification of a function that will take the+ -- supplied expected value and the result of the extraction function+ -- (the actual), respectively, and generate its own description of+ -- the failure.+ --+ Observe :: (Eq d) => Text -> (i -> d) -> d -> (d -> d -> String) -> DerivedVal i d+ ---------------------------------------------------------------------- -- | The 'TestShow' class is defined to provide a way for the various -- data objects tested by this module to be displayed when tests fail. -- The default 'testShow' will use a 'Show' instance, but this can be--- overridden if there are alternate ways t o display a particular+-- overridden if there are alternate ways to display a particular -- object (e.g. pretty-printing, etc.) class TestShow v where@@ -337,3 +359,37 @@ testShowList :: TestShow v => [v] -> String testShowList l = "[ " <> (List.intercalate ", " (testShow <$> l)) <> " ]"+++-- | The multiLineDiff is another helper function that can be used to+-- format a line-by-line difference display of two Text+-- representations. This is provided as a convenience function to+-- help format large text regions for easier comparison.++multiLineDiff :: T.Text -> T.Text -> String+multiLineDiff expected actual =+ let dl (e,a) = if e == a then db e else de " ↱" e <> "\n " <> da " ↳" a+ db b = "| > " <> b+ de m e = "|" <> m <> "expect> " <> e+ da m a = "|" <> m <> "actual> " <> a+ el = T.lines expected+ al = T.lines actual+ addnum :: Int -> T.Text -> T.Text+ addnum n l = let nt = T.pack (show n)+ nl = T.length nt+ in T.take (4 - nl) " " <> nt <> l+ banner = "MISMATCH between "+ <> T.pack (show $ length el) <> " expected and "+ <> T.pack (show $ length al) <> " actual"+ diffReport = fmap (uncurry addnum) $+ zip [1..] $ concat $+ -- Highly simplistic "diff" output assumes+ -- correlated lines: added or removed lines just+ -- cause everything to shown as different from that+ -- point forward.+ [ fmap dl $ zip el al+ , fmap (de "∌ ") $ drop (length al) el+ , fmap (da "∹ ") $ drop (length el) al+ ]+ details = banner : diffReport+ in if expected == actual then "<no difference>" else T.unpack (T.unlines details)
tasty-checklist.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: tasty-checklist-version: 1.0.2.0+version: 1.0.3.0 synopsis: Check multiple items during a tasty test description: Allows the test to check a number of items during a test and@@ -69,7 +69,7 @@ main-is: DocTestChecklist.hs build-depends: base , HUnit- , doctest >= 0.10 && < 0.17+ , doctest >= 0.10 && < 0.19 , parameterized-utils , tasty , tasty-checklist
test/TestChecklist.hs view
@@ -1,13 +1,16 @@+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-}+{-# OPTIONS_GHC -fno-warn-orphans #-} module Main where import Data.Parameterized.Context ( pattern Empty, pattern (:>) )+import qualified Data.Text as T import Test.Tasty+import Test.Tasty.Checklist import Test.Tasty.ExpectedFailure import Test.Tasty.HUnit-import Test.Tasty.Checklist main :: IO ()@@ -112,6 +115,32 @@ :> Val "the answer" answer 19 ) + ----------------------------------------++ , testCase "Compare identical multi-line results" $+ withChecklist "multi-line" $+ let result = fmap (T.replace " " " ") soliloquy+ in result `checkValues`+ (Empty+ :> Val "length" length (length soliloquy)+ :> Val "word count" (length . T.words . T.unlines) 114+ :> Observe "unchanged" id soliloquy+ (\expect actual ->+ multiLineDiff (T.unlines expect) (T.unlines actual))+ )++ , expectFailBecause "there are changes" $+ testCase "Compare changed multi-line results" $+ withChecklist "multi-line" $+ let result = fmap (T.replace " " " ") soliloquy+ in result `checkValues`+ (Empty+ :> Val "length" length (length soliloquy)+ :> Val "word count" (length . T.words . T.unlines) 113+ :> Observe "unchanged" id soliloquy+ (\expect actual ->+ multiLineDiff (T.unlines expect) (T.unlines actual))+ ) ] ----------------------------------------------------------------------@@ -151,3 +180,26 @@ instance TestShow Opaque where testShow = display+++----------------------------------------------------------------------++soliloquy :: [T.Text]+soliloquy =+ [ "To be, or not to be--that is the question:"+ , "Whether 'tis nobler in the mind to suffer"+ , "The slings and arrows of outrageous fortune"+ , "Or to take arms against a sea of troubles"+ , "And by opposing end them. To die, to sleep--"+ , "No more--and by a sleep to say we end"+ , "The heartache, and the thousand natural shocks"+ , "That flesh is heir to. 'Tis a consummation"+ , "Devoutly to be wished. To die, to sleep--"+ , "To sleep--perchance to dream: ay, there's the rub,"+ , "For in that sleep of death what dreams may come"+ , "When we have shuffled off this mortal coil,"+ , "Must give us pause. There's the respect"+ , "That makes calamity of so long life."+ ]++instance TestShow [T.Text] where testShow = show . T.unlines