packages feed

hspec-expectations-match 0.1.1.0 → 0.2.0.0

raw patch · 5 files changed

+113/−21 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Test.Hspec.Expectations.Match: assertDo :: Q Exp -> Q Exp

Files

CHANGELOG.md view
@@ -1,3 +1,7 @@+# 0.2.0.0 (November 17th, 2017)++- Added `assertDo`, which automatically instruments a `do` block with uses of `shouldReturnAndMatch`.+ # 0.1.1.0 (November 15th, 2017)  - Added `shouldReturnAndMatch`, which combines the action-running behavior of `shouldReturn` with the pattern-matching behavior of `shouldMatch`.
hspec-expectations-match.cabal view
@@ -3,7 +3,7 @@ -- see: https://github.com/sol/hpack  name:           hspec-expectations-match-version:        0.1.1.0+version:        0.2.0.0 synopsis:       An hspec expectation that asserts a value matches a pattern. description:    This package provides expectations for use with @hspec@ that use Template                 Haskell to assert that a value matches a particular pattern.
package.yaml view
@@ -1,5 +1,5 @@ name: hspec-expectations-match-version: 0.1.1.0+version: 0.2.0.0 category: Testing synopsis: An hspec expectation that asserts a value matches a pattern. description: |
src/Test/Hspec/Expectations/Match.hs view
@@ -14,6 +14,7 @@ module Test.Hspec.Expectations.Match   ( shouldMatch   , shouldReturnAndMatch+  , assertDo   ) where  import Control.Monad.Base (MonadBase, liftBase)@@ -89,25 +90,29 @@  patBindingsToTupleExp :: Pat -> Exp patBindingsToTupleExp = TupE . map VarE . patBindings-  where-    patBindings (LitP _) = []-    patBindings (VarP nm) = [nm]-    patBindings (TupP pats) = concatMap patBindings pats-    patBindings (UnboxedTupP pats) = concatMap patBindings pats-    patBindings (ConP _ pats) = concatMap patBindings pats-    patBindings (InfixP patA _ patB) = patBindings patA ++ patBindings patB-    patBindings (UInfixP patA _ patB) = patBindings patA ++ patBindings patB-    patBindings (ParensP pat) = patBindings pat-    patBindings (TildeP pat) = patBindings pat-    patBindings (BangP pat) = patBindings pat-    patBindings (AsP nm pat) = nm : patBindings pat-    patBindings WildP = []-    patBindings (RecP _ fieldPats) = concatMap (patBindings . snd) fieldPats-    patBindings (ListP pats) = concatMap patBindings pats-    patBindings (SigP pat _) = patBindings pat-    patBindings (ViewP _ pat) = patBindings pat++patBindingsToTuplePat :: Pat -> Pat+patBindingsToTuplePat = TupP . map VarP . patBindings++patBindings :: Pat -> [Name]+patBindings (LitP _) = []+patBindings (VarP nm) = [nm]+patBindings (TupP pats) = concatMap patBindings pats+patBindings (UnboxedTupP pats) = concatMap patBindings pats+patBindings (ConP _ pats) = concatMap patBindings pats+patBindings (InfixP patA _ patB) = patBindings patA ++ patBindings patB+patBindings (UInfixP patA _ patB) = patBindings patA ++ patBindings patB+patBindings (ParensP pat) = patBindings pat+patBindings (TildeP pat) = patBindings pat+patBindings (BangP pat) = patBindings pat+patBindings (AsP nm pat) = nm : patBindings pat+patBindings WildP = []+patBindings (RecP _ fieldPats) = concatMap (patBindings . snd) fieldPats+patBindings (ListP pats) = concatMap patBindings pats+patBindings (SigP pat _) = patBindings pat+patBindings (ViewP _ pat) = patBindings pat #if MIN_VERSION_GLASGOW_HASKELL(8,2,1,0)-    patBindings (UnboxedSumP pat _ _) = patBindings pat+patBindings (UnboxedSumP pat _ _) = patBindings pat #endif  -- The pretty-printer provided by template-haskell always prints things with@@ -185,3 +190,72 @@ showLit (DoublePrimL r) = show (fromRational r :: Double) ++ "##" showLit (StringPrimL s) = show s ++ "#" showLit (CharPrimL c) = show c ++ "#"++-- | Instruments a @do@ block by automatically inserting uses of+-- 'shouldReturnAndMatch' for monadic bindings. Specifically, the transformation+-- converts this:+--+-- @+-- \$('assertDo' [|do+--   [x, y] <- 'return' [1, 2]+--   x `'Test.Hspec.Expectations.shouldBe`` 1+--   y `'Test.Hspec.Expectations.shouldBe`` 2|])+-- @+--+-- ...into this:+--+-- @+-- do (x, y) <- $([|'return' [1, 2]|] `'shouldReturnAndMatch'` [p|[x, y]|])+--    x `'Test.Hspec.Expectations.shouldBe`` 1+--    y `'Test.Hspec.Expectations.shouldBe`` 2+-- @+--+-- This makes it much easier to read @do@ blocks that make significant use of+-- 'shouldReturnAndMatch'.+--+-- Note that this transformation /only/ applies to monadic bindings (not @let@+-- bindings), and it does not occur when the pattern on the left hand side+-- already fully covers all potential values (that is, when the pattern could+-- not possibly fail to match).+assertDo :: Q Exp -> Q Exp+assertDo qDoExp = qDoExp >>= \case+    DoE stmts -> DoE <$> traverse annotateStatement stmts+    _ -> fail "assertDo: expected a do block"+  where+    annotateStatement stmt@(BindS pat expr) = case pat of+      VarP _ -> pure stmt+      WildP -> pure stmt+      _ -> do+        hasOtherCases <- patternHasOtherCases pat+        if hasOtherCases+          then BindS (patBindingsToTuplePat pat) <$> shouldReturnAndMatch (pure expr) (pure pat)+          else pure stmt+    annotateStatement stmt = pure stmt++    patternHasOtherCases (LitP _) = pure True+    patternHasOtherCases (VarP _) = pure False+    patternHasOtherCases (TupP pats) = or <$> traverse patternHasOtherCases pats+    patternHasOtherCases (UnboxedTupP pats) = or <$> traverse patternHasOtherCases pats+    patternHasOtherCases (ConP nm pats) = do+      DataConI _ _ tyNm <- reify nm+      tyInfo <- reify tyNm+      conHasOtherCases <- case tyInfo of+        TyConI dec -> case dec of+          DataD _ _ _ _ cons _ -> pure (length cons > 1)+          NewtypeD{} -> pure False+          _ -> fail ("patternHasOtherCases: internal error; unexpected declaration in TyConI: " ++ show dec)+        _ -> fail ("patternHasOtherCases: internal error; unexpected Info in DataConI: " ++ show tyInfo)+      if conHasOtherCases+        then pure True+        else or <$> traverse patternHasOtherCases pats+    patternHasOtherCases (InfixP patA nm patB) = patternHasOtherCases (ConP nm [patA, patB])+    patternHasOtherCases (UInfixP patA nm patB) = patternHasOtherCases (ConP nm [patA, patB])+    patternHasOtherCases (ParensP pat) = patternHasOtherCases pat+    patternHasOtherCases (TildeP pat) = patternHasOtherCases pat+    patternHasOtherCases (BangP pat) = patternHasOtherCases pat+    patternHasOtherCases (AsP _ pat) = patternHasOtherCases pat+    patternHasOtherCases WildP = pure False+    patternHasOtherCases (RecP _ fieldPats) = or <$> traverse (patternHasOtherCases . snd) fieldPats+    patternHasOtherCases (ListP _) = pure True+    patternHasOtherCases (SigP pat _) = patternHasOtherCases pat+    patternHasOtherCases (ViewP _ _) = pure True
test/Test/Hspec/Expectations/MatchSpec.hs view
@@ -31,10 +31,24 @@       c `shouldBe` 'y'    describe "shouldReturnAndMatch" $-    it "matches the result of an action against a pattern" $ example $ do+    it "matches the result of an action against a pattern" $ do       $([|pure (Just True)|] `shouldReturnAndMatch` [p|Just _|])        $([|pure Nothing|] `shouldReturnAndMatch` [p|Just True|]) `shouldThrow` anyException        a <- $([|pure (Just True)|] `shouldReturnAndMatch` [p|Just x|])       a `shouldBe` True++  describe "assertDo" $+    it "automatically annotates pattern binds with shouldReturnAndMatch" $ do+      $(assertDo [|do+        () <- pure ()+        pure ()|])++      $(assertDo [|do+        Just x <- pure $ Just True+        x `shouldBe` True|])++      $(assertDo [|do+        Just x <- pure Nothing+        x `shouldBe` True|]) `shouldThrow` anyException