Skip to content

Commit b9bc46b

Browse files
Merge #2194
2194: Add 10 min timeout to `Test.Hspec.Extra.it` r=Anviking a=Anviking # Issue Number #2192 # Overview - [x] Add 10 min timeout to `it`. # Comments <!-- Additional comments or screenshots to attach if any --> <!-- Don't forget to: ✓ Self-review your changes to make sure nothing unexpected slipped through ✓ Assign yourself to the PR ✓ Assign one or several reviewer(s) ✓ Once created, link this PR to its corresponding ticket ✓ Assign the PR to a corresponding milestone ✓ Acknowledge any changes required to the Wiki --> Co-authored-by: Johannes Lund <johannes.lund@iohk.io>
2 parents 3a56413 + e9b6db0 commit b9bc46b

File tree

2 files changed

+54
-23
lines changed

2 files changed

+54
-23
lines changed

lib/test-utils/src/Test/Hspec/Extra.hs

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,19 @@
1313
module Test.Hspec.Extra
1414
( aroundAll
1515
, it
16+
, itWithCustomTimeout
1617
) where
1718

1819
import Prelude
1920

21+
import Control.Concurrent
22+
( threadDelay )
2023
import Control.Concurrent.Async
21-
( async, race, wait )
24+
( AsyncCancelled, async, race, wait )
2225
import Control.Concurrent.MVar
2326
( MVar, newEmptyMVar, putMVar, takeMVar )
2427
import Control.Exception
25-
( SomeException, throwIO, try )
28+
( SomeException, catch, throwIO )
2629
import Test.Hspec
2730
( ActionWith
2831
, HasCallStack
@@ -31,6 +34,7 @@ import Test.Hspec
3134
, afterAll
3235
, beforeAll
3336
, beforeWith
37+
, expectationFailure
3438
, specify
3539
)
3640

@@ -103,18 +107,34 @@ aroundAll acquire =
103107

104108
-- | A drop-in replacement for 'it' that'll automatically retry a scenario once
105109
-- if it fails, to cope with potentially flaky tests.
110+
--
111+
-- It also has a timeout of 10 minutes.
106112
it :: HasCallStack => String -> ActionWith ctx -> SpecWith ctx
107-
it title action = specify title $ \ctx -> do
108-
res1 <- try $ action ctx
109-
case res1 of
110-
Right r1 -> return r1
111-
Left (e1 :: SomeException) -> do
112-
res2 <- try $ action ctx
113-
case res2 of
114-
-- If the second try fails, return the first error. The
115-
-- second error might not be helpful.
116-
Left (_e2 :: SomeException) -> throwIO e1
117-
Right r2 -> return r2
113+
it = itWithCustomTimeout (10*minute)
114+
where
115+
minute = 60
116+
117+
-- | Like @it@ but with a custom timeout, which makes it realistic to test.
118+
itWithCustomTimeout
119+
:: HasCallStack
120+
=> Int -- ^ Timeout in seconds.
121+
-> String
122+
-> ActionWith ctx
123+
-> SpecWith ctx
124+
itWithCustomTimeout sec title action = specify title $ \ctx -> timeout sec $ do
125+
action ctx
126+
`catch` (\(_ :: AsyncCancelled) -> return ())
127+
`catch` (\(e :: SomeException) -> action ctx
128+
`catch` (\(_ :: SomeException) -> throwIO e))
129+
where
130+
timeout t act =
131+
race (threadDelay (micro t)) act >>= \case
132+
Right () ->
133+
return ()
134+
Left () ->
135+
expectationFailure $ "timed out in " <> show t <> " seconds"
136+
where
137+
micro = (* 1000) . (* 1000)
118138

119139
-- | Some helper to help readability on the thread synchronization above.
120140
await :: MVar () -> IO ()

lib/test-utils/test/Test/Hspec/ExtraSpec.hs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ module Test.Hspec.ExtraSpec where
22

33
import Prelude
44

5+
import Control.Concurrent
6+
( threadDelay )
57
import Data.IORef
68
( IORef, newIORef, readIORef, writeIORef )
79
import Data.List
@@ -18,6 +20,7 @@ import Test.Hspec
1820
, expectationFailure
1921
, it
2022
, shouldBe
23+
, shouldContain
2124
)
2225
import Test.Hspec.Core.Runner
2326
( defaultConfig, runSpec )
@@ -52,6 +55,14 @@ spec = do
5255
let noRetry = expectationFailure "test can't be retried"
5356
outcomes <- newIORef [failure, noRetry]
5457
(dynamically outcomes) `shouldMatchHSpecIt` failure
58+
it "can time out" $ do
59+
let micro = (1000*1000 *)
60+
let timeout = do
61+
threadDelay (micro 10)
62+
expectationFailure "should have timed out"
63+
res <- run (Extra.itWithCustomTimeout 2) timeout
64+
res `shouldContain` "timed out in 2 seconds"
65+
5566
where
5667
-- | lhs `shouldMatchHSpecIt` rhs asserts that the output of running
5768
-- (Extra.it "" lhs) and (Hspec.it "" rhs) are equal. Modulo random seed-
@@ -61,17 +72,17 @@ spec = do
6172
extraRes <- run Extra.it extraTest
6273
hspecRes <- run it hspecTest
6374
extraRes `shouldBe` hspecRes
64-
where
65-
run
66-
:: (String -> ActionWith () -> SpecWith ()) -- ^ it version
67-
-> IO () -- ^ test body
68-
-> IO String -- ^ hspec output
69-
run anyIt prop = fmap stripTime
70-
$ capture_
71-
$ flip runSpec defaultConfig
72-
$ beforeAll (return ())
73-
$ anyIt "<test spec>" (const prop)
7475

76+
run
77+
:: (String -> ActionWith () -> SpecWith ()) -- ^ it version
78+
-> IO () -- ^ test body
79+
-> IO String -- ^ hspec output
80+
run anyIt prop = fmap stripTime
81+
$ capture_
82+
$ flip runSpec defaultConfig
83+
$ beforeAll (return ())
84+
$ anyIt "<test spec>" (const prop)
85+
where
7586
-- | Remove time and seed such that we can compare the captured stdout
7687
-- of two different hspec runs.
7788
stripTime :: String -> String

0 commit comments

Comments
 (0)