-
Notifications
You must be signed in to change notification settings - Fork 0
/
CommonSpec.hs
151 lines (127 loc) · 5.71 KB
/
CommonSpec.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
{-# LANGUAGE AllowAmbiguousTypes #-}
-- | Common properties for classic and fast versions of algorithm.
module Test.Sdn.Overall.CommonSpec
( spec
) where
import Universum
import qualified Control.TimeWarp.Rpc as D
import Control.TimeWarp.Timed (Millisecond, Second, hour, interval, sec)
import Data.Default
import Data.Typeable (typeRep)
import Test.Hspec (Spec, describe)
import Test.Hspec.QuickCheck (prop)
import Test.QuickCheck (Positive (..), Small (..), arbitrary, oneof,
(==>))
import Sdn.Base
import qualified Sdn.Extra.Schedule as S
import Sdn.Policy.Fake
import Sdn.Protocol
import Test.Sdn.Overall.Launcher
import Test.Sdn.Overall.Properties
spec :: Spec
spec = describe "common" $ do
let checkVersion
:: (HasVersionTopologyActions pv, Default (CustomTopologySettings pv), Typeable pv)
=> Proxy pv -> Spec
checkVersion (pv :: Proxy pv) = do
-- artifical scenarious which check whether protocol at least slightly works
describe "primitive cases" $ do
prop "simple" $
-- launch with default test settings
-- search for @instance Default TestLaunchParams@ for their definition
-- generally default config is simpliest one - no network delays,
-- 1 ballot and 1 policy proposed, most basic properties checked.
-- see 'Test.Sdn.Overall.Launcher' module for details.
testLaunch @pv def
-- check for classic version only, for fast version there is a special case of this
when (typeRep pv == typeRep (Proxy @Classic)) $
prop "acceptor unavailable" $
testLaunch @pv def
{ testDelays =
D.forAddress (processAddress (Acceptor 1))
D.blackout
}
prop "too many acceptors unavailable" $
testLaunch @pv def
{ testDelays =
D.forAddressesList (processAddress . Acceptor <$> [1, 2])
D.blackout
, testProperties =
[ fails (eventually proposedPoliciesWereLearned)
]
}
prop "good and bad policies" $
-- TODO: optimize algorithm and get rid of 'Small'
\(Small (n :: Word)) ->
testLaunch @pv def
{ testSettings = defTopologySettings
{ topologyProposalSchedule = do
S.times n
S.generate . oneof $
[ GoodPolicy <$> arbitrary
, BadPolicy <$> arbitrary
]
}
}
prop "all conflicting policies" $
\(Positive (Small n :: Small Word)) ->
testLaunch @pv def
{ testSettings = defTopologySettings
{ topologyProposalSchedule = do
S.times n
S.generate (BadPolicy <$> arbitrary)
}
, testProperties =
[ invariant learnedPoliciesWereProposed
, eventually learnersAgree
, eventually $ numberOfLearnedPolicies _Accepted (== 1)
]
}
prop "network delays" $
testLaunch @pv def
{ testDelays = D.uniform (0, 1 :: Second)
, testSettings = def
{ topologyBallotsSchedule = S.delayed (interval 2 sec)
-- not to miss proposed policy
}
}
prop "temporaly no quorum of acceptors is accessible" $
testLaunch @pv def
{ testDelays =
D.forAddressesList (processAddress . Acceptor <$> [1, 2]) $
D.temporal (interval 15 sec) $
D.blackout
, testSettings = def
{ topologyLifetime = interval 30 sec
, topologyBallotsSchedule = S.periodic (interval 10 sec)
, topologyProposerInsistance = \balSchedule -> balSchedule
}
}
-- bunch of complex scenarious involving introduction of many policies
describe "real life cases" $
prop "no conflicts" $
\(Positive (Small proposalsNum :: Small Word)) ->
\(Positive (Small balDelay)) ->
proposalsNum > balDelay ==>
testLaunch @pv def
{ testSettings = defTopologySettings
{ topologyProposalSchedule = do
S.repeating proposalsNum (interval 1 sec)
S.generate (GoodPolicy <$> arbitrary)
, topologyBallotsSchedule = mconcat
[ S.repeating
(proposalsNum `div` balDelay)
(interval (fromIntegral balDelay) sec)
, finalBallot
]
}
, testDelays = D.uniform (0, 50 :: Millisecond)
}
describe "classic" $
checkVersion $ Proxy @Classic
describe "fast" $
checkVersion $ Proxy @Fast
where
-- one final ballot for all proposals which weren't made in time
finalBallot :: MonadTopology m => S.Schedule m ()
finalBallot = S.delayed (interval 1 hour)