Safer MVar usage (#49)

* Fix stack.yaml file

* Remove unfixable stack files

* Resolve ambiguous import in GHCi

* handle MVars without error

* Consistent messaging

* Add comment
This commit is contained in:
Matt Parsons 2018-05-10 10:08:06 -06:00 committed by GitHub
parent 78f30bc997
commit d66c2d278a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 39 additions and 37 deletions

View File

@ -93,7 +93,7 @@ test-suite spec
other-modules: Servant.QuickCheck.InternalSpec other-modules: Servant.QuickCheck.InternalSpec
build-tool-depends: hspec-discover:hspec-discover build-tool-depends: hspec-discover:hspec-discover
build-depends: base build-depends: base
, base-compat , base-compat-batteries
, aeson , aeson
, servant-quickcheck , servant-quickcheck
, bytestring , bytestring

View File

@ -2,7 +2,7 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
module Servant.QuickCheck.Internal.QuickCheck where module Servant.QuickCheck.Internal.QuickCheck where
import Control.Concurrent (modifyMVar_, newMVar, readMVar) import Control.Concurrent (tryReadMVar, newEmptyMVar, tryPutMVar)
import Control.Monad (unless) import Control.Monad (unless)
import qualified Data.ByteString.Lazy as LBS import qualified Data.ByteString.Lazy as LBS
import Data.Proxy (Proxy) import Data.Proxy (Proxy)
@ -73,18 +73,23 @@ serversEqual api burl1 burl2 args req = do
let reqs = (\f -> (f burl1, f burl2)) <$> runGenRequest api let reqs = (\f -> (f burl1, f burl2)) <$> runGenRequest api
-- This MVar stuff is clunky! But there doesn't seem to be an easy way to -- This MVar stuff is clunky! But there doesn't seem to be an easy way to
-- return results when a test fails, since an exception is throw. -- return results when a test fails, since an exception is throw.
deetsMVar <- newMVar $ error "should not be called" deetsMVar <- newEmptyMVar
r <- quickCheckWithResult args { chatty = False } $ monadicIO $ forAllM reqs $ \(req1, req2) -> do r <- quickCheckWithResult args { chatty = False } $ monadicIO $ forAllM reqs $ \(req1, req2) -> do
resp1 <- run $ C.httpLbs (noCheckStatus req1) defManager resp1 <- run $ C.httpLbs (noCheckStatus req1) defManager
resp2 <- run $ C.httpLbs (noCheckStatus req2) defManager resp2 <- run $ C.httpLbs (noCheckStatus req2) defManager
unless (getResponseEquality req resp1 resp2) $ do unless (getResponseEquality req resp1 resp2) $ do
monitor (counterexample "hi" ) monitor (counterexample "hi" )
run $ modifyMVar_ deetsMVar $ const $ return $ _ <- run $ tryPutMVar deetsMVar $ ServerEqualityFailure req1 resp1 resp2
ServerEqualityFailure req1 resp1 resp2
assert False assert False
case r of case r of
Success {} -> return () Success {} -> return ()
Failure{..} -> readMVar deetsMVar >>= \x -> expectationFailure $ "Failed:\n" ++ show x Failure{..} -> do
mx <- tryReadMVar deetsMVar
case mx of
Just x ->
expectationFailure $ "Failed:\n" ++ show x
Nothing ->
expectationFailure $ "We failed to record a reason for failure: " <> show r
GaveUp { numTests = n } -> expectationFailure $ "Gave up after " ++ show n ++ " tests" GaveUp { numTests = n } -> expectationFailure $ "Gave up after " ++ show n ++ " tests"
NoExpectedFailure {} -> expectationFailure "No expected failure" NoExpectedFailure {} -> expectationFailure "No expected failure"
InsufficientCoverage {} -> expectationFailure "Insufficient coverage" InsufficientCoverage {} -> expectationFailure "Insufficient coverage"
@ -112,21 +117,32 @@ serverSatisfies :: (HasGenRequest a) =>
Proxy a -> BaseUrl -> Args -> Predicates -> Expectation Proxy a -> BaseUrl -> Args -> Predicates -> Expectation
serverSatisfies api = serverSatisfiesMgr api defManager serverSatisfies api = serverSatisfiesMgr api defManager
-- | Check that a server satisfies the set of properties specified, and
-- accept a 'Manager' for running the HTTP requests through.
--
-- See 'serverSatisfies' for more details.
--
-- @since 0.0.7.2
serverSatisfiesMgr :: (HasGenRequest a) => serverSatisfiesMgr :: (HasGenRequest a) =>
Proxy a -> C.Manager -> BaseUrl -> Args -> Predicates -> Expectation Proxy a -> C.Manager -> BaseUrl -> Args -> Predicates -> Expectation
serverSatisfiesMgr api manager burl args preds = do serverSatisfiesMgr api manager burl args preds = do
let reqs = ($ burl) <$> runGenRequest api let reqs = ($ burl) <$> runGenRequest api
deetsMVar <- newMVar $ error "should not be called" deetsMVar <- newEmptyMVar
r <- quickCheckWithResult args { chatty = False } $ monadicIO $ forAllM reqs $ \req -> do r <- quickCheckWithResult args { chatty = False } $ monadicIO $ forAllM reqs $ \req -> do
v <- run $ finishPredicates preds (noCheckStatus req) manager v <- run $ finishPredicates preds (noCheckStatus req) manager
run $ modifyMVar_ deetsMVar $ const $ return v _ <- run $ tryPutMVar deetsMVar v
case v of case v of
Just _ -> assert False Just _ -> assert False
_ -> return () _ -> return ()
case r of case r of
Success {} -> return () Success {} -> return ()
Failure{..} -> readMVar deetsMVar >>= \x -> expectationFailure $ Failure {..} -> do
"Failed:\n" ++ show x mx <- tryReadMVar deetsMVar
case mx of
Just x ->
expectationFailure $ "Failed:\n" ++ show x
Nothing ->
expectationFailure $ "We failed to record a reason for failure: " <> show r
GaveUp { numTests = n } -> expectationFailure $ "Gave up after " ++ show n ++ " tests" GaveUp { numTests = n } -> expectationFailure $ "Gave up after " ++ show n ++ " tests"
NoExpectedFailure {} -> expectationFailure $ "No expected failure" NoExpectedFailure {} -> expectationFailure $ "No expected failure"
InsufficientCoverage {} -> expectationFailure $ "Insufficient coverage" InsufficientCoverage {} -> expectationFailure $ "Insufficient coverage"

View File

@ -1,6 +0,0 @@
resolver: lts-6.30
packages:
- '.'
extra-deps: []
flags: {}
extra-package-dbs: []

View File

@ -1,6 +0,0 @@
resolver: lts-7.19
packages:
- '.'
extra-deps: []
flags: {}
extra-package-dbs: []

View File

@ -1,6 +0,0 @@
resolver: lts-9.1
packages:
- '.'
extra-deps: []
flags: {}
extra-package-dbs: []

View File

@ -1,10 +1,14 @@
resolver: lts-8.4 resolver: lts-11.8
packages: packages:
- '.' - '.'
extra-deps: extra-deps:
- hspec-2.4.4 - base-compat-batteries-0.10.1
- hspec-core-2.4.4 - base-compat-0.10.1
- hspec-discover-2.4.4 - hspec-discover-2.5.0
- quickcheck-io-0.2.0 - hspec-core-2.5.0
- hspec-2.5.0
# aeson pre-1.3.1.0 has an upper bound on `base-compat-batteries` that preclude
# the 0.10.1 that we depend on
- aeson-1.3.1.0
flags: {} flags: {}
extra-package-dbs: [] extra-package-dbs: []