daytripper 0.1.0 → 0.2.0
raw patch · 3 files changed
+45/−23 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Test.Daytripper: expectAssertBool :: MonadExpect m => String -> Bool -> m ()
+ Test.Daytripper: expectAssertFailure :: MonadExpect m => String -> m ()
- Test.Daytripper: expectAfter :: Monad m => (a -> b -> c -> m ()) -> Expect m a b c -> Expect m a b c
+ Test.Daytripper: expectAfter :: Monad m => (Maybe a -> b -> c -> m ()) -> Expect m a b c -> Expect m a b c
- Test.Daytripper: expectBefore :: Monad m => (a -> m ()) -> Expect m a b c -> Expect m a b c
+ Test.Daytripper: expectBefore :: Monad m => (Maybe a -> m ()) -> Expect m a b c -> Expect m a b c
- Test.Daytripper: expectDuring :: Monad m => (a -> b -> m ()) -> Expect m a b c -> Expect m a b c
+ Test.Daytripper: expectDuring :: Monad m => (Maybe a -> b -> m ()) -> Expect m a b c -> Expect m a b c
- Test.Daytripper: mkExpect :: (MonadExpect m, Eq (f a), Show (f a), Applicative f) => (a -> m b) -> (b -> m (f a)) -> Expect m a b (f a)
+ Test.Daytripper: mkExpect :: MonadExpect m => (a -> m b) -> (b -> m c) -> (Maybe a -> c -> m ()) -> Expect m a b c
Files
- daytripper.cabal +1/−1
- src/Test/Daytripper.hs +27/−17
- test/Main.hs +17/−5
daytripper.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: daytripper-version: 0.1.0+version: 0.2.0 synopsis: Helpers for round-trip tests description: Please see the README on GitHub at <https://github.com/ejconlon/daytripper#readme> homepage: https://github.com/ejconlon/daytripper#readme
src/Test/Daytripper.hs view
@@ -16,7 +16,7 @@ ) where -import Control.Monad (void)+import Control.Monad (unless, void) import Data.ByteString (ByteString) import Data.ByteString qualified as BS import Data.Foldable (for_)@@ -31,7 +31,7 @@ import Test.Falsify.Property qualified as FP import Test.Tasty (TestName, TestTree, askOption, defaultIngredients, defaultMainWithIngredients, includingOptions) import Test.Tasty.Falsify (testProperty)-import Test.Tasty.HUnit (testCase, (@?=))+import Test.Tasty.HUnit (assertBool, assertFailure, testCase, (@?=)) import Test.Tasty.Ingredients (Ingredient) import Test.Tasty.Options (IsOption (..), OptionDescription (..), safeRead) @@ -40,14 +40,20 @@ class MonadFail m => MonadExpect m where expectLiftIO :: IO a -> m a expectAssertEq :: (Eq a, Show a) => a -> a -> m ()+ expectAssertFailure :: String -> m ()+ expectAssertBool :: String -> Bool -> m ()+ expectAssertBool s b = unless b (expectAssertFailure s) instance MonadExpect IO where expectLiftIO = id expectAssertEq = (@?=)+ expectAssertFailure = assertFailure+ expectAssertBool = assertBool instance MonadExpect Property where expectLiftIO = pure . unsafePerformIO expectAssertEq x y = FP.assert (FR.eq FR..$ ("LHS", x) FR..$ ("RHS", y))+ expectAssertFailure = FP.testFailed -- | A general type of test expectation. Captures two stages of processing an input, -- first encoding, then decoding. The monad is typically something implementing@@ -55,31 +61,35 @@ -- The input is possibly missing, in which case we test decoding only. type Expect m a b c = Either b a -> m (b, m c) +eitherMay :: Either b a -> Maybe a+eitherMay = either (const Nothing) Just+ -- | Assert something before processing (before encoding and before decoding)-expectBefore :: Monad m => (a -> m ()) -> Expect m a b c -> Expect m a b c-expectBefore f ex i = for_ i f >> ex i+expectBefore :: Monad m => (Maybe a -> m ()) -> Expect m a b c -> Expect m a b c+expectBefore f ex i = f (eitherMay i) >> ex i -- | Assert something during processing (after encoding and before decoding)-expectDuring :: Monad m => (a -> b -> m ()) -> Expect m a b c -> Expect m a b c-expectDuring f ex i = ex i >>= \p@(b, _) -> p <$ for_ i (`f` b)+expectDuring :: Monad m => (Maybe a -> b -> m ()) -> Expect m a b c -> Expect m a b c+expectDuring f ex i = ex i >>= \p@(b, _) -> p <$ f (eitherMay i) b -- | Asserting something after processing (after encoding and after decoding)-expectAfter :: Monad m => (a -> b -> c -> m ()) -> Expect m a b c -> Expect m a b c-expectAfter f ex i = ex i >>= \(b, end) -> end >>= \c -> (b, pure c) <$ for_ i (\a -> f a b c)+expectAfter :: Monad m => (Maybe a -> b -> c -> m ()) -> Expect m a b c -> Expect m a b c+expectAfter f ex i = ex i >>= \(b, end) -> end >>= \c -> (b, pure c) <$ f (eitherMay i) b c --- | One way of definining expectations from a pair of encode/decode functions.--- Generalizes decoding in 'Maybe' or 'Either'.+-- | A way of definining expectations from a pair of encode/decode functions and+-- a comparison function. mkExpect- :: (MonadExpect m, Eq (f a), Show (f a), Applicative f)+ :: MonadExpect m => (a -> m b)- -> (b -> m (f a))- -> Expect m a b (f a)-mkExpect f g i = do+ -> (b -> m c)+ -> (Maybe a -> c -> m ())+ -> Expect m a b c+mkExpect f g h i = do b <- either pure f i pure . (b,) $ do- fa <- g b- for_ i (expectAssertEq fa . pure)- pure fa+ c <- g b+ h (either (const Nothing) Just i) c+ pure c -- | Simple way to run an expectation, ignoring the intermediate value. runExpect :: Monad m => Expect m a b c -> a -> m c
test/Main.hs view
@@ -8,11 +8,14 @@ import Data.ByteString (ByteString) import Data.ByteString qualified as BS+import Data.Maybe (isJust, isNothing) import Test.Daytripper (Expect, MonadExpect (..), daytripperMain, mkExpect, mkFileRT, mkPropRT, mkUnitRT, testRT) import Test.Falsify.Generator qualified as Gen import Test.Tasty (testGroup) -expec :: MonadExpect m => Expect m ByteString ByteString (Maybe ByteString)+type Cmp m = Maybe ByteString -> Maybe ByteString -> m ()++expec :: MonadExpect m => Cmp m -> Expect m ByteString ByteString (Maybe ByteString) expec = mkExpect enc dec where enc a = pure (a <> a)@@ -23,14 +26,23 @@ then Just a else Nothing +expecOk, expecFail :: MonadExpect m => Expect m ByteString ByteString (Maybe ByteString)+expecOk =+ expec $+ maybe+ (expectAssertBool "expected Just" . isJust)+ (\a mc -> expectAssertEq mc (Just a))+expecFail = expec (const (expectAssertBool "expected Nothing" . isNothing))+ main :: IO () main = daytripperMain $ testGroup "Daytripper" $ fmap testRT- [ mkPropRT "prop" expec (Gen.choose (pure "a") (pure "b"))- , mkUnitRT "unit" expec "a"- , mkFileRT "file just" expec "testdata/b.txt" (Just "b")- , mkFileRT "file nothing" expec "testdata/c.txt" Nothing+ [ mkPropRT "prop" expecOk (Gen.choose (pure "a") (pure "b"))+ , mkUnitRT "unit" expecOk "a"+ , mkFileRT "file just" expecOk "testdata/b.txt" (Just "b")+ , mkFileRT "file nothing" expecOk "testdata/c.txt" Nothing+ , mkFileRT "file fail" expecFail "testdata/x.txt" Nothing ]