packages feed

microspec (empty) → 0.1.0.0

raw patch · 4 files changed

+360/−0 lines, 4 filesdep +QuickCheckdep +basesetup-changed

Dependencies added: QuickCheck, base

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2016, Tom Murphy++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Tom Murphy nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ Test/Microspec.hs view
@@ -0,0 +1,272 @@+-- | Tests can be structured as nested 'it' / 'describe' statements+-- +--   E.g.+-- +--   > microspec $ do+--   >    describe "plus" $ do+--   >       it "adds positive numbers" $ do+--   >          it "does 1 + 1" $+--   >             1 + 1 == 2+--   >          it "does 2 + 2" $+--   >             2 + 2 == 4+--   >       it "is commutative" $+--   >          \x y -> x + y == y + (x :: Int)+-- +--   ...which will return, nicely in green instead of bold:+-- +--   @+--   plus+--     adds positive numbers+--       __does 1 + 1__+--       __does 2 + 2__+--     __is commutative__+--   @++{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase #-}++module Test.Microspec (+     -- * Specification+     microspec+   , microspecWith+   , describe+   , it+   , pending+   , prop+   , Microspec+   , MTestable++     -- * Configuration+   , MArgs(..)+   , defaultMArgs++     -- * Compatibility+   , shouldBe++     -- Reexports+   , module Test.QuickCheck+   , module Test.QuickCheck.Modifiers+   , module Test.QuickCheck.Monadic+   -- , module Test.QuickCheck.Property+   ) where++import Control.Monad+-- import Data.Time (getCurrentTime, diffUTCTime)+import Test.QuickCheck as QC+import Test.QuickCheck+import Test.QuickCheck.Modifiers+import Test.QuickCheck.Monadic+-- import Test.QuickCheck.Property++---------- User-facing:++-- | Run your spec. Put this at the top level, e.g.:+-- +--   > main = microspec $ do+--   >    describe "plus 1" $+--   >       3 + 1 == 4+microspec :: Microspec () -> IO ()+microspec = microspecWith defaultMArgs++-- | 'microspec' with 'MArgs'+microspecWith :: MArgs -> Microspec () -> IO ()+microspecWith args spec = do+   putStrLn ""+   forM_ (buildTestTrees spec) $ \test -> do+      (runTestWith args {- 1 -} 0) test+      putStrLn ""++runTestWith :: MArgs -> Int -> TestTree -> IO ()+runTestWith args depth = \case+   TestLeaf testLabel (Right aProp) -> do+      let timeoutMaybe = case _mArgs_timeoutSecs args of+           Nothing -> id+           Just numSecs -> within $ fromEnum $ numSecs * (10^(6::Int))+      result <- quickCheckWithResult (_mArgs_qcArgs args) $ timeoutMaybe aProp+      putStrLn $ showResult testLabel result+   TestLeaf testLabel (Left Pending) ->+      putStrLn $ indentationFor depth ++ inYellow (testLabel ++ " - PENDING")+   TestBranch testLabel forest -> do+      putStrLn $ indentationFor depth ++ testLabel+      forM_ forest $ runTestWith args (depth + 1)+ where+   indentationFor :: Int -> String+   indentationFor n = replicate (n*2) ' ' -- ++ "- "++   showResult :: String -> QC.Result -> String+   showResult testLabel = \case+       -- note: if we wanted to show quickcheck labels, this is where we would:+      Success _ _ _ ->+         indentationFor depth ++ inGreen testLabel+      failure@(Failure{theException=Nothing}) ->+         indentationFor depth+           ++ inRed (testLabel ++ " - "++replaceNewline (output failure))+{-+           ++ "\n"+           ++ indentationFor (depth + 1) ++ inRed (show $ usedSeed failure)+-}+      failure {- @(Failure{}) -} ->+         indentationFor depth+           ++ inRed (testLabel ++" - "++replaceNewline (output failure))+   replaceNewline = concatMap $ \case '\n' -> " | " ; x -> [x]+++-- If you like the word 'pending', this is the place for you!:+data Pending = Pending+-- | Describe a test as unwritten, e.g.:+-- +--   > describe "meaning of life" $ pending+pending :: Pending+pending = Pending+++---------- Handy++-- | An alias for 'describe'. Usually used inside a 'describe' block:+-- +--   >  describe "replicate" $ do+--   >     it "doubles with 2" $+--   >        replicate 2 'x' === "xx"+--   >     it "creates a list of the right size" $+--   >        \(Positive n) -> length (replicate n 'x') == n+it :: MTestable t => String -> t -> Microspec ()+it = describe++-- | Make a test case from a QuickCheck function. Alias for 'Test.QuickCheck.property'.+-- +--   Note that you don't need to use this to create a test, e.g.:+-- +--   > describe "reverse preserves length" $+--   >    \l -> length (reverse l) == length l+prop :: Testable prop => prop -> Property+prop = property++++---------- Constructing a test tree:++addTestTree :: TestTree -> Microspec ()+addTestTree tree = Microspec [tree] ()++-- | Something which can be tested+-- +--   Note both Bools and Properties can be tested, but only Properties show+--   the values that weren't equal+-- +--   For both unit tests and property tests, if you want to see the outputs+--   of failed tests use 'Test.QuickCheck.==='. If you just want to test for+--   equality, use 'Prelude.=='.+-- +--   For example, the outputs of running:+-- +--   @+--   microspec $ do+--      describe "baddies" $ do+--         it "isn't 1 =="  $ 0 == (1 :: Int)+--         it "isn't 1 ===" $ 0 === (1 :: Int)+--         it "isn't always 1 =="  $ \x -> x == (1 :: Int)+--         it "isn't always 1 ===" $ \x -> x === (1 :: Int)+--   @+-- +--   are:+-- +--   @+--   isn't 1 == - *** Failed! Falsifiable (after 1 test)+--   isn't 1 === - *** Failed! Falsifiable (after 1 test):  | 0 /= 1+--   isn't always 1 == - *** Failed! Falsifiable (after 1 test):  | 0+--   isn't always 1 === - *** Failed! Falsifiable (after 1 test):  | 0 | 0 /= 1+--   @+++class MTestable t where+   -- | Describe a test, e.g.:+   -- +   --   > describe "reverse 'foo' is 'oof'" $+   --   >    reverse "foo" === "oof"+   describe :: String -> t -> Microspec ()+instance MTestable Property where+   describe testLabel aProp = addTestTree $ TestLeaf testLabel (Right aProp)+instance MTestable Bool where+   describe testLabel bool = describe testLabel $ property bool+instance MTestable TestTree where+   describe testLabel x = addTestTree $ TestBranch testLabel [x]+instance MTestable Pending where+   describe testLabel pend = addTestTree $ TestLeaf testLabel (Left pend)+instance MTestable (Microspec ()) where+   describe testLabel x =+      let forest = buildTestTrees x+      in addTestTree $ TestBranch testLabel forest+instance (Arbitrary a, Show a, Testable prop) => MTestable (a -> prop) where+   describe testLabel f = describe testLabel $ property f++data TestTree+   = TestBranch String [TestTree]+   | TestLeaf String (Either Pending Property)++instance Show TestTree where+ show = \case+   TestBranch testLabel subs -> "Branch "++show testLabel++" "++show subs+   TestLeaf testLabel _ -> "Leaf " ++ show testLabel++-- Basically a writer monad:++-- | A series of tests, to run with 'microspec'+data Microspec a = Microspec [TestTree] a++instance Functor Microspec where+   fmap f (Microspec forest a) = Microspec forest (f a)+instance Applicative Microspec where+   pure a = Microspec [] a+   f <*> a =+      let Microspec forest0 f' = f+          Microspec forest1 a' = a+      in Microspec (forest0 ++ forest1) (f' a')+instance Monad Microspec where+   return = pure+   ma >>= f =+      let Microspec forest0 a = ma+          Microspec forest1 b = f a+      in Microspec (forest0 ++ forest1) b++buildTestTrees :: Microspec () -> [TestTree]+buildTestTrees (Microspec x _) = x+++++---------- Configuration:++-- | Default arguments. Calling \"microspec\" is the same as calling+--   \"microspecWith defaultMArgs\".+defaultMArgs :: MArgs+defaultMArgs = MArgs {+    _mArgs_timeoutSecs = Nothing -- Just 60+   ,_mArgs_qcArgs = QC.stdArgs { chatty = False }+   }++-- | Tweak how tests are run, with 'microspecWith'.+data MArgs = MArgs {+    _mArgs_timeoutSecs :: Maybe Double -- ^ Number of seconds before each+                                        --   test times out. If you want to+                                        --   do this on a per-test basis, try+                                        --   'Test.QuickCheck.Property.within'+   ,_mArgs_qcArgs :: QC.Args -- ^ Arguments to use with QuickCheck tests+   }+ deriving (Show, Read) -- , Eq, Ord)++++---------- Pretty-printing:++inRed, inGreen, inYellow :: String -> String+[inRed,inGreen, inYellow] = (`map` [31,32,33]) $ \colorNum ->+   \s -> "\ESC["++show (colorNum::Int)++"m"++s++"\ESC[m"++++---------- HSpec compatibility++-- | Hspec compatibility. Equivalent to using 'Test.QuickCheck.==='+shouldBe :: (Eq x, Show x) => x -> x -> Property+shouldBe = (===)+
+ microspec.cabal view
@@ -0,0 +1,56 @@+name:                microspec+version:             0.1.0.0+synopsis:            Tiny QuickCheck test library with minimal dependencies+description:         +  A tiny (1 module, <500 lines) property-based (and unit) testing library with minimal dependencies.+  .+  Don't add a bunch of transitive dependencies just to test your code!+  .+  Instead of reinventing the wheel (<https://xkcd.com/927>), we use a+  RSpec/HSpec-like API and run tests with QuickCheck.+  .+  > import Test.Microspec+  > +  > main :: IO ()+  > main = microspec $ do+  >    describe "replicate" $ do+  >       it "doubles with 2" $+  >          replicate 2 'x' == "xx"+  >       it "creates a list of the right size" $+  >          \(Positive n) -> length (replicate n 'x') == n+  > +  >    describe "reverse" $ do+  >       it "reverse . reverse == id" $ \l ->+  >          reverse (reverse l) == (l :: [Int])+  > +  >    describe "tail" $+  >       it "length is -1" $ \(NonEmpty l) ->+  >          length (tail l :: [Int]) == length l - 1+  > +  >    describe "solve the halting problem" $+  >       pending+license:             BSD3+license-file:        LICENSE+author:              Tom Murphy+maintainer:          Tom Murphy+-- copyright:           +category:            Test, Testing+build-type:          Simple+cabal-version:       >=1.10+stability:           experimental++library+  exposed-modules:+    Test.Microspec+  -- other-modules:       +  other-extensions:+      FlexibleInstances+    , LambdaCase+  build-depends:+      -- PVP compat:+    --   base >=4.9 && <5+    -- , QuickCheck >=2.9 && <2.10+      base <5+    , QuickCheck+  -- hs-source-dirs:      +  default-language:    Haskell2010