Skip to content

Commit 5b3e7ba

Browse files
HeinrichApfelmusrvl
authored andcommitted
Add unit tests for Control.Concurrent.Concierge
1 parent fc717b0 commit 5b3e7ba

File tree

3 files changed

+86
-0
lines changed

3 files changed

+86
-0
lines changed

Diff for: lib/core/cardano-wallet-core.cabal

+2
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ test-suite unit
300300
, http-types
301301
, iohk-monitoring
302302
, io-classes
303+
, io-sim
303304
, lattices
304305
, lens
305306
, memory
@@ -408,6 +409,7 @@ test-suite unit
408409
Cardano.Wallet.RegistrySpec
409410
Cardano.Wallet.TransactionSpec
410411
Cardano.WalletSpec
412+
Control.Concurrent.ConciergeSpec
411413
Data.Function.UtilsSpec
412414
Data.QuantitySpec
413415
Data.Time.TextSpec
+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
{-# LANGUAGE RankNTypes #-}
2+
module Control.Concurrent.ConciergeSpec
3+
( spec
4+
) where
5+
6+
import Prelude
7+
8+
import Control.Concurrent.Concierge
9+
( atomicallyWithLifted, newConcierge )
10+
import Control.Monad.Class.MonadFork
11+
( forkIO )
12+
import Control.Monad.Class.MonadSay
13+
( say )
14+
import Control.Monad.Class.MonadTimer
15+
( threadDelay )
16+
import Control.Monad.IOSim
17+
( IOSim, runSimTrace, selectTraceEventsSay )
18+
import Control.Monad.Trans.Class
19+
( lift )
20+
import Control.Monad.Trans.Except
21+
( ExceptT, catchE, runExceptT, throwE )
22+
import Test.Hspec
23+
( Spec, describe, it, parallel )
24+
import Test.QuickCheck
25+
( Property, (===) )
26+
27+
spec :: Spec
28+
spec = do
29+
parallel $ describe "Control.Concurrent.Concierge" $ do
30+
it "Atomic operations do not interleave"
31+
unit_atomic
32+
33+
it "throwE in ExceptT releases lock"
34+
unit_exceptT_release_lock
35+
36+
{-------------------------------------------------------------------------------
37+
Properties
38+
-------------------------------------------------------------------------------}
39+
-- | Deterministic test for atomicity.
40+
-- We have to compare a program run that interleaves
41+
-- against one that is atomic.
42+
unit_atomic :: Bool
43+
unit_atomic =
44+
("ABAB" == sayings testInterleave) && ("AABB" == sayings testAtomic)
45+
where
46+
sayings :: (forall s. IOSim s a) -> String
47+
sayings x = concat . selectTraceEventsSay $ runSimTrace x
48+
49+
testAtomic = do
50+
concierge <- newConcierge
51+
test $ atomicallyWithLifted id concierge ()
52+
testInterleave = test id
53+
54+
test :: (forall a. IOSim s a -> IOSim s a) -> IOSim s ()
55+
test atomically = do
56+
_ <- forkIO $ atomically (delay 1 >> action "B")
57+
atomically $ action "A"
58+
delay 4
59+
60+
action :: String -> IOSim s ()
61+
action s = say s >> delay 2 >> say s
62+
63+
delay :: Int -> IOSim s ()
64+
delay n = threadDelay (fromIntegral n*0.1)
65+
66+
-- | Check that using 'throwE' in the 'ExceptE' monad releases the lock
67+
unit_exceptT_release_lock :: Property
68+
unit_exceptT_release_lock =
69+
["A"] === selectTraceEventsSay (runSimTrace $ runExceptT test)
70+
where
71+
liftE :: IOSim s a -> ExceptT String (IOSim s) a
72+
liftE = lift
73+
74+
test :: ExceptT String (IOSim s) ()
75+
test = do
76+
concierge <- liftE newConcierge
77+
let atomically = atomicallyWithLifted liftE concierge ()
78+
_ <- tryE $ atomically $ throwE "X"
79+
atomically $ liftE $ say "A"
80+
81+
-- not exported in transformers <= 0.5.6
82+
tryE :: Monad m => ExceptT e m a -> ExceptT e m (Either e a)
83+
tryE action = (Right <$> action) `catchE` (pure . Left)

Diff for: nix/.stack.nix/cardano-wallet-core.nix

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)