Revision 20bc5360
b/htest/Test/Ganeti/Confd/Utils.hs | ||
---|---|---|
64 | 64 |
-- | Test that signing messages and checking signatures is correct. It |
65 | 65 |
-- also tests, indirectly the serialisation of messages so we don't |
66 | 66 |
-- need a separate test for that. |
67 |
prop_ConfdUtils_req_sign :: Hash.HashKey -- ^ The hash key
|
|
68 |
-> NonNegative Integer -- ^ The base timestamp
|
|
69 |
-> Positive Integer -- ^ Delta for out of window
|
|
70 |
-> Bool -- ^ Whether delta should be + or -
|
|
71 |
-> Confd.ConfdRequest
|
|
72 |
-> Property
|
|
73 |
prop_ConfdUtils_req_sign key (NonNegative timestamp) (Positive bad_delta)
|
|
67 |
prop_req_sign :: Hash.HashKey -- ^ The hash key |
|
68 |
-> NonNegative Integer -- ^ The base timestamp |
|
69 |
-> Positive Integer -- ^ Delta for out of window |
|
70 |
-> Bool -- ^ Whether delta should be + or - |
|
71 |
-> Confd.ConfdRequest |
|
72 |
-> Property |
|
73 |
prop_req_sign key (NonNegative timestamp) (Positive bad_delta) |
|
74 | 74 |
pm crq = |
75 | 75 |
forAll (choose (0, fromIntegral C.confdMaxClockSkew)) $ \ good_delta -> |
76 | 76 |
let encoded = J.encode crq |
... | ... | |
89 | 89 |
|
90 | 90 |
-- | Tests that signing with a different key fails detects failure |
91 | 91 |
-- correctly. |
92 |
prop_ConfdUtils_bad_key :: String -- ^ Salt
|
|
93 |
-> Confd.ConfdRequest -- ^ Request
|
|
94 |
-> Property
|
|
95 |
prop_ConfdUtils_bad_key salt crq =
|
|
92 |
prop_bad_key :: String -- ^ Salt |
|
93 |
-> Confd.ConfdRequest -- ^ Request |
|
94 |
-> Property |
|
95 |
prop_bad_key salt crq = |
|
96 | 96 |
-- fixme: we hardcode here the expected length of a sha1 key, as |
97 | 97 |
-- otherwise we could have two short keys that differ only in the |
98 | 98 |
-- final zero elements count, and those will be expanded to be the |
... | ... | |
106 | 106 |
Confd.Utils.parseRequest key_verify encoded |
107 | 107 |
|
108 | 108 |
testSuite "ConfdUtils" |
109 |
[ 'prop_ConfdUtils_req_sign
|
|
110 |
, 'prop_ConfdUtils_bad_key
|
|
109 |
[ 'prop_req_sign |
|
110 |
, 'prop_bad_key |
|
111 | 111 |
] |
b/htest/Test/Ganeti/HTools/CLI.hs | ||
---|---|---|
43 | 43 |
import qualified Ganeti.HTools.Types as Types |
44 | 44 |
|
45 | 45 |
-- | Test correct parsing. |
46 |
prop_CLI_parseISpec :: String -> Int -> Int -> Int -> Property
|
|
47 |
prop_CLI_parseISpec descr dsk mem cpu =
|
|
46 |
prop_parseISpec :: String -> Int -> Int -> Int -> Property |
|
47 |
prop_parseISpec descr dsk mem cpu = |
|
48 | 48 |
let str = printf "%d,%d,%d" dsk mem cpu::String |
49 | 49 |
in CLI.parseISpecString descr str ==? Types.Ok (Types.RSpec cpu mem dsk) |
50 | 50 |
|
51 | 51 |
-- | Test parsing failure due to wrong section count. |
52 |
prop_CLI_parseISpecFail :: String -> Property
|
|
53 |
prop_CLI_parseISpecFail descr =
|
|
52 |
prop_parseISpecFail :: String -> Property |
|
53 |
prop_parseISpecFail descr = |
|
54 | 54 |
forAll (choose (0,100) `suchThat` ((/=) 3)) $ \nelems -> |
55 | 55 |
forAll (replicateM nelems arbitrary) $ \values -> |
56 | 56 |
let str = intercalate "," $ map show (values::[Int]) |
... | ... | |
59 | 59 |
_ -> property True |
60 | 60 |
|
61 | 61 |
-- | Test parseYesNo. |
62 |
prop_CLI_parseYesNo :: Bool -> Bool -> [Char] -> Property
|
|
63 |
prop_CLI_parseYesNo def testval val =
|
|
62 |
prop_parseYesNo :: Bool -> Bool -> [Char] -> Property |
|
63 |
prop_parseYesNo def testval val = |
|
64 | 64 |
forAll (elements [val, "yes", "no"]) $ \actual_val -> |
65 | 65 |
if testval |
66 | 66 |
then CLI.parseYesNo def Nothing ==? Types.Ok def |
... | ... | |
84 | 84 |
Right (options, _) -> fn options ==? Just val |
85 | 85 |
|
86 | 86 |
-- | Test a few string arguments. |
87 |
prop_CLI_StringArg :: [Char] -> Property
|
|
88 |
prop_CLI_StringArg argument =
|
|
87 |
prop_StringArg :: [Char] -> Property |
|
88 |
prop_StringArg argument = |
|
89 | 89 |
let args = [ (CLI.oDataFile, CLI.optDataFile) |
90 | 90 |
, (CLI.oDynuFile, CLI.optDynuFile) |
91 | 91 |
, (CLI.oSaveCluster, CLI.optSaveCluster) |
... | ... | |
109 | 109 |
|
110 | 110 |
-- | Test that all binaries support some common options. There is |
111 | 111 |
-- nothing actually random about this test... |
112 |
prop_CLI_stdopts :: Property
|
|
113 |
prop_CLI_stdopts =
|
|
112 |
prop_stdopts :: Property |
|
113 |
prop_stdopts = |
|
114 | 114 |
let params = ["-h", "--help", "-V", "--version"] |
115 | 115 |
opts = map (\(name, (_, o)) -> (name, o)) Program.personalities |
116 | 116 |
-- apply checkEarlyExit across the cartesian product of params and opts |
117 | 117 |
in conjoin [checkEarlyExit n o p | p <- params, (n, o) <- opts] |
118 | 118 |
|
119 | 119 |
testSuite "CLI" |
120 |
[ 'prop_CLI_parseISpec
|
|
121 |
, 'prop_CLI_parseISpecFail
|
|
122 |
, 'prop_CLI_parseYesNo
|
|
123 |
, 'prop_CLI_StringArg
|
|
124 |
, 'prop_CLI_stdopts
|
|
120 |
[ 'prop_parseISpec |
|
121 |
, 'prop_parseISpecFail |
|
122 |
, 'prop_parseYesNo |
|
123 |
, 'prop_StringArg |
|
124 |
, 'prop_stdopts |
|
125 | 125 |
] |
b/htest/Test/Ganeti/HTools/Cluster.hs | ||
---|---|---|
98 | 98 |
|
99 | 99 |
-- | Check that the cluster score is close to zero for a homogeneous |
100 | 100 |
-- cluster. |
101 |
prop_Cluster_Score_Zero :: Node.Node -> Property
|
|
102 |
prop_Cluster_Score_Zero node =
|
|
101 |
prop_Score_Zero :: Node.Node -> Property |
|
102 |
prop_Score_Zero node = |
|
103 | 103 |
forAll (choose (1, 1024)) $ \count -> |
104 | 104 |
(not (Node.offline node) && not (Node.failN1 node) && (count > 0) && |
105 | 105 |
(Node.tDsk node > 0) && (Node.tMem node > 0)) ==> |
... | ... | |
111 | 111 |
in score <= 1e-12 |
112 | 112 |
|
113 | 113 |
-- | Check that cluster stats are sane. |
114 |
prop_Cluster_CStats_sane :: Property
|
|
115 |
prop_Cluster_CStats_sane =
|
|
114 |
prop_CStats_sane :: Property |
|
115 |
prop_CStats_sane = |
|
116 | 116 |
forAll (choose (1, 1024)) $ \count -> |
117 | 117 |
forAll genOnlineNode $ \node -> |
118 | 118 |
let fn = Node.buildPeers node Container.empty |
... | ... | |
124 | 124 |
|
125 | 125 |
-- | Check that one instance is allocated correctly, without |
126 | 126 |
-- rebalances needed. |
127 |
prop_Cluster_Alloc_sane :: Instance.Instance -> Property
|
|
128 |
prop_Cluster_Alloc_sane inst =
|
|
127 |
prop_Alloc_sane :: Instance.Instance -> Property |
|
128 |
prop_Alloc_sane inst = |
|
129 | 129 |
forAll (choose (5, 20)) $ \count -> |
130 | 130 |
forAll genOnlineNode $ \node -> |
131 | 131 |
let (nl, il, inst') = makeSmallEmptyCluster node count inst |
... | ... | |
145 | 145 |
-- instance spec via tiered allocation (whatever the original instance |
146 | 146 |
-- spec), on either one or two nodes. Furthermore, we test that |
147 | 147 |
-- computed allocation statistics are correct. |
148 |
prop_Cluster_CanTieredAlloc :: Instance.Instance -> Property
|
|
149 |
prop_Cluster_CanTieredAlloc inst =
|
|
148 |
prop_CanTieredAlloc :: Instance.Instance -> Property |
|
149 |
prop_CanTieredAlloc inst = |
|
150 | 150 |
forAll (choose (2, 5)) $ \count -> |
151 | 151 |
forAll (genOnlineNode `suchThat` (isNodeBig 4)) $ \node -> |
152 | 152 |
let nl = makeSmallCluster node count |
... | ... | |
193 | 193 |
|
194 | 194 |
-- | Checks that on a 4-8 node cluster, once we allocate an instance, |
195 | 195 |
-- we can also relocate it. |
196 |
prop_Cluster_AllocRelocate :: Property
|
|
197 |
prop_Cluster_AllocRelocate =
|
|
196 |
prop_AllocRelocate :: Property |
|
197 |
prop_AllocRelocate = |
|
198 | 198 |
forAll (choose (4, 8)) $ \count -> |
199 | 199 |
forAll (genOnlineNode `suchThat` (isNodeBig 4)) $ \node -> |
200 | 200 |
forAll (genInstanceSmallerThanNode node `suchThat` isMirrored) $ \inst -> |
... | ... | |
235 | 235 |
|
236 | 236 |
-- | Checks that on a 4-8 node cluster, once we allocate an instance, |
237 | 237 |
-- we can also node-evacuate it. |
238 |
prop_Cluster_AllocEvacuate :: Property
|
|
239 |
prop_Cluster_AllocEvacuate =
|
|
238 |
prop_AllocEvacuate :: Property |
|
239 |
prop_AllocEvacuate = |
|
240 | 240 |
forAll (choose (4, 8)) $ \count -> |
241 | 241 |
forAll (genOnlineNode `suchThat` (isNodeBig 4)) $ \node -> |
242 | 242 |
forAll (genInstanceSmallerThanNode node `suchThat` isMirrored) $ \inst -> |
... | ... | |
252 | 252 |
-- | Checks that on a 4-8 node cluster with two node groups, once we |
253 | 253 |
-- allocate an instance on the first node group, we can also change |
254 | 254 |
-- its group. |
255 |
prop_Cluster_AllocChangeGroup :: Property
|
|
256 |
prop_Cluster_AllocChangeGroup =
|
|
255 |
prop_AllocChangeGroup :: Property |
|
256 |
prop_AllocChangeGroup = |
|
257 | 257 |
forAll (choose (4, 8)) $ \count -> |
258 | 258 |
forAll (genOnlineNode `suchThat` (isNodeBig 4)) $ \node -> |
259 | 259 |
forAll (genInstanceSmallerThanNode node `suchThat` isMirrored) $ \inst -> |
... | ... | |
274 | 274 |
|
275 | 275 |
-- | Check that allocating multiple instances on a cluster, then |
276 | 276 |
-- adding an empty node, results in a valid rebalance. |
277 |
prop_Cluster_AllocBalance :: Property
|
|
278 |
prop_Cluster_AllocBalance =
|
|
277 |
prop_AllocBalance :: Property |
|
278 |
prop_AllocBalance = |
|
279 | 279 |
forAll (genNode (Just 5) (Just 128)) $ \node -> |
280 | 280 |
forAll (choose (3, 5)) $ \count -> |
281 | 281 |
not (Node.offline node) && not (Node.failN1 node) ==> |
... | ... | |
296 | 296 |
canBalance tbl True True False |
297 | 297 |
|
298 | 298 |
-- | Checks consistency. |
299 |
prop_Cluster_CheckConsistency :: Node.Node -> Instance.Instance -> Bool
|
|
300 |
prop_Cluster_CheckConsistency node inst =
|
|
299 |
prop_CheckConsistency :: Node.Node -> Instance.Instance -> Bool |
|
300 |
prop_CheckConsistency node inst = |
|
301 | 301 |
let nl = makeSmallCluster node 3 |
302 | 302 |
[node1, node2, node3] = Container.elems nl |
303 | 303 |
node3' = node3 { Node.group = 1 } |
... | ... | |
311 | 311 |
(not . null $ ccheck [(0, inst3)]) |
312 | 312 |
|
313 | 313 |
-- | For now, we only test that we don't lose instances during the split. |
314 |
prop_Cluster_SplitCluster :: Node.Node -> Instance.Instance -> Property
|
|
315 |
prop_Cluster_SplitCluster node inst =
|
|
314 |
prop_SplitCluster :: Node.Node -> Instance.Instance -> Property |
|
315 |
prop_SplitCluster node inst = |
|
316 | 316 |
forAll (choose (0, 100)) $ \icnt -> |
317 | 317 |
let nl = makeSmallCluster node 2 |
318 | 318 |
(nl', il') = foldl (\(ns, is) _ -> assignInstance ns is inst 0 1) |
... | ... | |
339 | 339 |
-- times, and generates a random instance that can be allocated on |
340 | 340 |
-- this mini-cluster; it then checks that after applying a policy that |
341 | 341 |
-- the instance doesn't fits, the allocation fails. |
342 |
prop_Cluster_AllocPolicy :: Node.Node -> Property
|
|
343 |
prop_Cluster_AllocPolicy node =
|
|
342 |
prop_AllocPolicy :: Node.Node -> Property |
|
343 |
prop_AllocPolicy node = |
|
344 | 344 |
-- rqn is the required nodes (1 or 2) |
345 | 345 |
forAll (choose (1, 2)) $ \rqn -> |
346 | 346 |
forAll (choose (5, 20)) $ \count -> |
... | ... | |
353 | 353 |
in not $ canAllocOn nl rqn inst |
354 | 354 |
|
355 | 355 |
testSuite "Cluster" |
356 |
[ 'prop_Cluster_Score_Zero
|
|
357 |
, 'prop_Cluster_CStats_sane
|
|
358 |
, 'prop_Cluster_Alloc_sane
|
|
359 |
, 'prop_Cluster_CanTieredAlloc
|
|
360 |
, 'prop_Cluster_AllocRelocate
|
|
361 |
, 'prop_Cluster_AllocEvacuate
|
|
362 |
, 'prop_Cluster_AllocChangeGroup
|
|
363 |
, 'prop_Cluster_AllocBalance
|
|
364 |
, 'prop_Cluster_CheckConsistency
|
|
365 |
, 'prop_Cluster_SplitCluster
|
|
366 |
, 'prop_Cluster_AllocPolicy
|
|
356 |
[ 'prop_Score_Zero |
|
357 |
, 'prop_CStats_sane |
|
358 |
, 'prop_Alloc_sane |
|
359 |
, 'prop_CanTieredAlloc |
|
360 |
, 'prop_AllocRelocate |
|
361 |
, 'prop_AllocEvacuate |
|
362 |
, 'prop_AllocChangeGroup |
|
363 |
, 'prop_AllocBalance |
|
364 |
, 'prop_CheckConsistency |
|
365 |
, 'prop_SplitCluster |
|
366 |
, 'prop_AllocPolicy |
|
367 | 367 |
] |
b/htest/Test/Ganeti/HTools/Container.hs | ||
---|---|---|
41 | 41 |
import qualified Ganeti.HTools.Node as Node |
42 | 42 |
|
43 | 43 |
-- we silence the following due to hlint bug fixed in later versions |
44 |
{-# ANN prop_Container_addTwo "HLint: ignore Avoid lambda" #-}
|
|
45 |
prop_Container_addTwo :: [Container.Key] -> Int -> Int -> Bool
|
|
46 |
prop_Container_addTwo cdata i1 i2 =
|
|
44 |
{-# ANN prop_addTwo "HLint: ignore Avoid lambda" #-} |
|
45 |
prop_addTwo :: [Container.Key] -> Int -> Int -> Bool |
|
46 |
prop_addTwo cdata i1 i2 = |
|
47 | 47 |
fn i1 i2 cont == fn i2 i1 cont && |
48 | 48 |
fn i1 i2 cont == fn i1 i2 (fn i1 i2 cont) |
49 | 49 |
where cont = foldl (\c x -> Container.add x x c) Container.empty cdata |
50 | 50 |
fn x1 x2 = Container.addTwo x1 x1 x2 x2 |
51 | 51 |
|
52 |
prop_Container_nameOf :: Node.Node -> Property
|
|
53 |
prop_Container_nameOf node =
|
|
52 |
prop_nameOf :: Node.Node -> Property |
|
53 |
prop_nameOf node = |
|
54 | 54 |
let nl = makeSmallCluster node 1 |
55 | 55 |
fnode = head (Container.elems nl) |
56 | 56 |
in Container.nameOf nl (Node.idx fnode) ==? Node.name fnode |
... | ... | |
58 | 58 |
-- | We test that in a cluster, given a random node, we can find it by |
59 | 59 |
-- its name and alias, as long as all names and aliases are unique, |
60 | 60 |
-- and that we fail to find a non-existing name. |
61 |
prop_Container_findByName :: Property
|
|
62 |
prop_Container_findByName =
|
|
61 |
prop_findByName :: Property |
|
62 |
prop_findByName = |
|
63 | 63 |
forAll (genNode (Just 1) Nothing) $ \node -> |
64 | 64 |
forAll (choose (1, 20)) $ \ cnt -> |
65 | 65 |
forAll (choose (0, cnt - 1)) $ \ fidx -> |
... | ... | |
80 | 80 |
(isNothing (Container.findByName nl' othername)) |
81 | 81 |
|
82 | 82 |
testSuite "Container" |
83 |
[ 'prop_Container_addTwo
|
|
84 |
, 'prop_Container_nameOf
|
|
85 |
, 'prop_Container_findByName
|
|
83 |
[ 'prop_addTwo |
|
84 |
, 'prop_nameOf |
|
85 |
, 'prop_findByName |
|
86 | 86 |
] |
b/htest/Test/Ganeti/HTools/Instance.hs | ||
---|---|---|
72 | 72 |
|
73 | 73 |
-- Simple instance tests, we only have setter/getters |
74 | 74 |
|
75 |
prop_Instance_creat :: Instance.Instance -> Property
|
|
76 |
prop_Instance_creat inst =
|
|
75 |
prop_creat :: Instance.Instance -> Property |
|
76 |
prop_creat inst = |
|
77 | 77 |
Instance.name inst ==? Instance.alias inst |
78 | 78 |
|
79 |
prop_Instance_setIdx :: Instance.Instance -> Types.Idx -> Property
|
|
80 |
prop_Instance_setIdx inst idx =
|
|
79 |
prop_setIdx :: Instance.Instance -> Types.Idx -> Property |
|
80 |
prop_setIdx inst idx = |
|
81 | 81 |
Instance.idx (Instance.setIdx inst idx) ==? idx |
82 | 82 |
|
83 |
prop_Instance_setName :: Instance.Instance -> String -> Bool
|
|
84 |
prop_Instance_setName inst name =
|
|
83 |
prop_setName :: Instance.Instance -> String -> Bool |
|
84 |
prop_setName inst name = |
|
85 | 85 |
Instance.name newinst == name && |
86 | 86 |
Instance.alias newinst == name |
87 | 87 |
where newinst = Instance.setName inst name |
88 | 88 |
|
89 |
prop_Instance_setAlias :: Instance.Instance -> String -> Bool
|
|
90 |
prop_Instance_setAlias inst name =
|
|
89 |
prop_setAlias :: Instance.Instance -> String -> Bool |
|
90 |
prop_setAlias inst name = |
|
91 | 91 |
Instance.name newinst == Instance.name inst && |
92 | 92 |
Instance.alias newinst == name |
93 | 93 |
where newinst = Instance.setAlias inst name |
94 | 94 |
|
95 |
prop_Instance_setPri :: Instance.Instance -> Types.Ndx -> Property
|
|
96 |
prop_Instance_setPri inst pdx =
|
|
95 |
prop_setPri :: Instance.Instance -> Types.Ndx -> Property |
|
96 |
prop_setPri inst pdx = |
|
97 | 97 |
Instance.pNode (Instance.setPri inst pdx) ==? pdx |
98 | 98 |
|
99 |
prop_Instance_setSec :: Instance.Instance -> Types.Ndx -> Property
|
|
100 |
prop_Instance_setSec inst sdx =
|
|
99 |
prop_setSec :: Instance.Instance -> Types.Ndx -> Property |
|
100 |
prop_setSec inst sdx = |
|
101 | 101 |
Instance.sNode (Instance.setSec inst sdx) ==? sdx |
102 | 102 |
|
103 |
prop_Instance_setBoth :: Instance.Instance -> Types.Ndx -> Types.Ndx -> Bool
|
|
104 |
prop_Instance_setBoth inst pdx sdx =
|
|
103 |
prop_setBoth :: Instance.Instance -> Types.Ndx -> Types.Ndx -> Bool |
|
104 |
prop_setBoth inst pdx sdx = |
|
105 | 105 |
Instance.pNode si == pdx && Instance.sNode si == sdx |
106 | 106 |
where si = Instance.setBoth inst pdx sdx |
107 | 107 |
|
108 |
prop_Instance_shrinkMG :: Instance.Instance -> Property
|
|
109 |
prop_Instance_shrinkMG inst =
|
|
108 |
prop_shrinkMG :: Instance.Instance -> Property |
|
109 |
prop_shrinkMG inst = |
|
110 | 110 |
Instance.mem inst >= 2 * Types.unitMem ==> |
111 | 111 |
case Instance.shrinkByType inst Types.FailMem of |
112 | 112 |
Types.Ok inst' -> Instance.mem inst' == Instance.mem inst - Types.unitMem |
113 | 113 |
_ -> False |
114 | 114 |
|
115 |
prop_Instance_shrinkMF :: Instance.Instance -> Property
|
|
116 |
prop_Instance_shrinkMF inst =
|
|
115 |
prop_shrinkMF :: Instance.Instance -> Property |
|
116 |
prop_shrinkMF inst = |
|
117 | 117 |
forAll (choose (0, 2 * Types.unitMem - 1)) $ \mem -> |
118 | 118 |
let inst' = inst { Instance.mem = mem} |
119 | 119 |
in Types.isBad $ Instance.shrinkByType inst' Types.FailMem |
120 | 120 |
|
121 |
prop_Instance_shrinkCG :: Instance.Instance -> Property
|
|
122 |
prop_Instance_shrinkCG inst =
|
|
121 |
prop_shrinkCG :: Instance.Instance -> Property |
|
122 |
prop_shrinkCG inst = |
|
123 | 123 |
Instance.vcpus inst >= 2 * Types.unitCpu ==> |
124 | 124 |
case Instance.shrinkByType inst Types.FailCPU of |
125 | 125 |
Types.Ok inst' -> |
126 | 126 |
Instance.vcpus inst' == Instance.vcpus inst - Types.unitCpu |
127 | 127 |
_ -> False |
128 | 128 |
|
129 |
prop_Instance_shrinkCF :: Instance.Instance -> Property
|
|
130 |
prop_Instance_shrinkCF inst =
|
|
129 |
prop_shrinkCF :: Instance.Instance -> Property |
|
130 |
prop_shrinkCF inst = |
|
131 | 131 |
forAll (choose (0, 2 * Types.unitCpu - 1)) $ \vcpus -> |
132 | 132 |
let inst' = inst { Instance.vcpus = vcpus } |
133 | 133 |
in Types.isBad $ Instance.shrinkByType inst' Types.FailCPU |
134 | 134 |
|
135 |
prop_Instance_shrinkDG :: Instance.Instance -> Property
|
|
136 |
prop_Instance_shrinkDG inst =
|
|
135 |
prop_shrinkDG :: Instance.Instance -> Property |
|
136 |
prop_shrinkDG inst = |
|
137 | 137 |
Instance.dsk inst >= 2 * Types.unitDsk ==> |
138 | 138 |
case Instance.shrinkByType inst Types.FailDisk of |
139 | 139 |
Types.Ok inst' -> |
140 | 140 |
Instance.dsk inst' == Instance.dsk inst - Types.unitDsk |
141 | 141 |
_ -> False |
142 | 142 |
|
143 |
prop_Instance_shrinkDF :: Instance.Instance -> Property
|
|
144 |
prop_Instance_shrinkDF inst =
|
|
143 |
prop_shrinkDF :: Instance.Instance -> Property |
|
144 |
prop_shrinkDF inst = |
|
145 | 145 |
forAll (choose (0, 2 * Types.unitDsk - 1)) $ \dsk -> |
146 | 146 |
let inst' = inst { Instance.dsk = dsk } |
147 | 147 |
in Types.isBad $ Instance.shrinkByType inst' Types.FailDisk |
148 | 148 |
|
149 |
prop_Instance_setMovable :: Instance.Instance -> Bool -> Property
|
|
150 |
prop_Instance_setMovable inst m =
|
|
149 |
prop_setMovable :: Instance.Instance -> Bool -> Property |
|
150 |
prop_setMovable inst m = |
|
151 | 151 |
Instance.movable inst' ==? m |
152 | 152 |
where inst' = Instance.setMovable inst m |
153 | 153 |
|
154 | 154 |
testSuite "Instance" |
155 |
[ 'prop_Instance_creat
|
|
156 |
, 'prop_Instance_setIdx
|
|
157 |
, 'prop_Instance_setName
|
|
158 |
, 'prop_Instance_setAlias
|
|
159 |
, 'prop_Instance_setPri
|
|
160 |
, 'prop_Instance_setSec
|
|
161 |
, 'prop_Instance_setBoth
|
|
162 |
, 'prop_Instance_shrinkMG
|
|
163 |
, 'prop_Instance_shrinkMF
|
|
164 |
, 'prop_Instance_shrinkCG
|
|
165 |
, 'prop_Instance_shrinkCF
|
|
166 |
, 'prop_Instance_shrinkDG
|
|
167 |
, 'prop_Instance_shrinkDF
|
|
168 |
, 'prop_Instance_setMovable
|
|
155 |
[ 'prop_creat |
|
156 |
, 'prop_setIdx |
|
157 |
, 'prop_setName |
|
158 |
, 'prop_setAlias |
|
159 |
, 'prop_setPri |
|
160 |
, 'prop_setSec |
|
161 |
, 'prop_setBoth |
|
162 |
, 'prop_shrinkMG |
|
163 |
, 'prop_shrinkMF |
|
164 |
, 'prop_shrinkCG |
|
165 |
, 'prop_shrinkCF |
|
166 |
, 'prop_shrinkDG |
|
167 |
, 'prop_shrinkDF |
|
168 |
, 'prop_setMovable |
|
169 | 169 |
] |
b/htest/Test/Ganeti/HTools/Loader.hs | ||
---|---|---|
44 | 44 |
import qualified Ganeti.HTools.Node as Node |
45 | 45 |
import qualified Ganeti.HTools.Types as Types |
46 | 46 |
|
47 |
prop_Loader_lookupNode :: [(String, Int)] -> String -> String -> Property
|
|
48 |
prop_Loader_lookupNode ktn inst node =
|
|
47 |
prop_lookupNode :: [(String, Int)] -> String -> String -> Property |
|
48 |
prop_lookupNode ktn inst node = |
|
49 | 49 |
Loader.lookupNode nl inst node ==? Map.lookup node nl |
50 | 50 |
where nl = Map.fromList ktn |
51 | 51 |
|
52 |
prop_Loader_lookupInstance :: [(String, Int)] -> String -> Property
|
|
53 |
prop_Loader_lookupInstance kti inst =
|
|
52 |
prop_lookupInstance :: [(String, Int)] -> String -> Property |
|
53 |
prop_lookupInstance kti inst = |
|
54 | 54 |
Loader.lookupInstance il inst ==? Map.lookup inst il |
55 | 55 |
where il = Map.fromList kti |
56 | 56 |
|
57 |
prop_Loader_assignIndices :: Property
|
|
58 |
prop_Loader_assignIndices =
|
|
57 |
prop_assignIndices :: Property |
|
58 |
prop_assignIndices = |
|
59 | 59 |
-- generate nodes with unique names |
60 | 60 |
forAll (arbitrary `suchThat` |
61 | 61 |
(\nodes -> |
... | ... | |
71 | 71 |
|
72 | 72 |
-- | Checks that the number of primary instances recorded on the nodes |
73 | 73 |
-- is zero. |
74 |
prop_Loader_mergeData :: [Node.Node] -> Bool
|
|
75 |
prop_Loader_mergeData ns =
|
|
74 |
prop_mergeData :: [Node.Node] -> Bool |
|
75 |
prop_mergeData ns = |
|
76 | 76 |
let na = Container.fromList $ map (\n -> (Node.idx n, n)) ns |
77 | 77 |
in case Loader.mergeData [] [] [] [] |
78 | 78 |
(Loader.emptyCluster {Loader.cdNodes = na}) of |
... | ... | |
84 | 84 |
null instances |
85 | 85 |
|
86 | 86 |
-- | Check that compareNameComponent on equal strings works. |
87 |
prop_Loader_compareNameComponent_equal :: String -> Bool
|
|
88 |
prop_Loader_compareNameComponent_equal s =
|
|
87 |
prop_compareNameComponent_equal :: String -> Bool |
|
88 |
prop_compareNameComponent_equal s = |
|
89 | 89 |
BasicTypes.compareNameComponent s s == |
90 | 90 |
BasicTypes.LookupResult BasicTypes.ExactMatch s |
91 | 91 |
|
92 | 92 |
-- | Check that compareNameComponent on prefix strings works. |
93 |
prop_Loader_compareNameComponent_prefix :: NonEmptyList Char -> String -> Bool
|
|
94 |
prop_Loader_compareNameComponent_prefix (NonEmpty s1) s2 =
|
|
93 |
prop_compareNameComponent_prefix :: NonEmptyList Char -> String -> Bool |
|
94 |
prop_compareNameComponent_prefix (NonEmpty s1) s2 = |
|
95 | 95 |
BasicTypes.compareNameComponent (s1 ++ "." ++ s2) s1 == |
96 | 96 |
BasicTypes.LookupResult BasicTypes.PartialMatch s1 |
97 | 97 |
|
98 | 98 |
testSuite "Loader" |
99 |
[ 'prop_Loader_lookupNode
|
|
100 |
, 'prop_Loader_lookupInstance
|
|
101 |
, 'prop_Loader_assignIndices
|
|
102 |
, 'prop_Loader_mergeData
|
|
103 |
, 'prop_Loader_compareNameComponent_equal
|
|
104 |
, 'prop_Loader_compareNameComponent_prefix
|
|
99 |
[ 'prop_lookupNode |
|
100 |
, 'prop_lookupInstance |
|
101 |
, 'prop_assignIndices |
|
102 |
, 'prop_mergeData |
|
103 |
, 'prop_compareNameComponent_equal |
|
104 |
, 'prop_compareNameComponent_prefix |
|
105 | 105 |
] |
b/htest/Test/Ganeti/HTools/Node.hs | ||
---|---|---|
98 | 98 |
|
99 | 99 |
-- * Test cases |
100 | 100 |
|
101 |
prop_Node_setAlias :: Node.Node -> String -> Bool
|
|
102 |
prop_Node_setAlias node name =
|
|
101 |
prop_setAlias :: Node.Node -> String -> Bool |
|
102 |
prop_setAlias node name = |
|
103 | 103 |
Node.name newnode == Node.name node && |
104 | 104 |
Node.alias newnode == name |
105 | 105 |
where newnode = Node.setAlias node name |
106 | 106 |
|
107 |
prop_Node_setOffline :: Node.Node -> Bool -> Property
|
|
108 |
prop_Node_setOffline node status =
|
|
107 |
prop_setOffline :: Node.Node -> Bool -> Property |
|
108 |
prop_setOffline node status = |
|
109 | 109 |
Node.offline newnode ==? status |
110 | 110 |
where newnode = Node.setOffline node status |
111 | 111 |
|
112 |
prop_Node_setXmem :: Node.Node -> Int -> Property
|
|
113 |
prop_Node_setXmem node xm =
|
|
112 |
prop_setXmem :: Node.Node -> Int -> Property |
|
113 |
prop_setXmem node xm = |
|
114 | 114 |
Node.xMem newnode ==? xm |
115 | 115 |
where newnode = Node.setXmem node xm |
116 | 116 |
|
117 |
prop_Node_setMcpu :: Node.Node -> Double -> Property
|
|
118 |
prop_Node_setMcpu node mc =
|
|
117 |
prop_setMcpu :: Node.Node -> Double -> Property |
|
118 |
prop_setMcpu node mc = |
|
119 | 119 |
Types.iPolicyVcpuRatio (Node.iPolicy newnode) ==? mc |
120 | 120 |
where newnode = Node.setMcpu node mc |
121 | 121 |
|
122 | 122 |
-- | Check that an instance add with too high memory or disk will be |
123 | 123 |
-- rejected. |
124 |
prop_Node_addPriFM :: Node.Node -> Instance.Instance -> Property
|
|
125 |
prop_Node_addPriFM node inst =
|
|
124 |
prop_addPriFM :: Node.Node -> Instance.Instance -> Property |
|
125 |
prop_addPriFM node inst = |
|
126 | 126 |
Instance.mem inst >= Node.fMem node && not (Node.failN1 node) && |
127 | 127 |
not (Instance.isOffline inst) ==> |
128 | 128 |
case Node.addPri node inst'' of |
... | ... | |
133 | 133 |
|
134 | 134 |
-- | Check that adding a primary instance with too much disk fails |
135 | 135 |
-- with type FailDisk. |
136 |
prop_Node_addPriFD :: Node.Node -> Instance.Instance -> Property
|
|
137 |
prop_Node_addPriFD node inst =
|
|
136 |
prop_addPriFD :: Node.Node -> Instance.Instance -> Property |
|
137 |
prop_addPriFD node inst = |
|
138 | 138 |
forAll (elements Instance.localStorageTemplates) $ \dt -> |
139 | 139 |
Instance.dsk inst >= Node.fDsk node && not (Node.failN1 node) ==> |
140 | 140 |
let inst' = setInstanceSmallerThanNode node inst |
... | ... | |
146 | 146 |
|
147 | 147 |
-- | Check that adding a primary instance with too many VCPUs fails |
148 | 148 |
-- with type FailCPU. |
149 |
prop_Node_addPriFC :: Property
|
|
150 |
prop_Node_addPriFC =
|
|
149 |
prop_addPriFC :: Property |
|
150 |
prop_addPriFC = |
|
151 | 151 |
forAll (choose (1, maxCpu)) $ \extra -> |
152 | 152 |
forAll genOnlineNode $ \node -> |
153 | 153 |
forAll (arbitrary `suchThat` Instance.notOffline) $ \inst -> |
... | ... | |
159 | 159 |
|
160 | 160 |
-- | Check that an instance add with too high memory or disk will be |
161 | 161 |
-- rejected. |
162 |
prop_Node_addSec :: Node.Node -> Instance.Instance -> Int -> Property
|
|
163 |
prop_Node_addSec node inst pdx =
|
|
162 |
prop_addSec :: Node.Node -> Instance.Instance -> Int -> Property |
|
163 |
prop_addSec node inst pdx = |
|
164 | 164 |
((Instance.mem inst >= (Node.fMem node - Node.rMem node) && |
165 | 165 |
not (Instance.isOffline inst)) || |
166 | 166 |
Instance.dsk inst >= Node.fDsk node) && |
... | ... | |
169 | 169 |
|
170 | 170 |
-- | Check that an offline instance with reasonable disk size but |
171 | 171 |
-- extra mem/cpu can always be added. |
172 |
prop_Node_addOfflinePri :: NonNegative Int -> NonNegative Int -> Property
|
|
173 |
prop_Node_addOfflinePri (NonNegative extra_mem) (NonNegative extra_cpu) =
|
|
172 |
prop_addOfflinePri :: NonNegative Int -> NonNegative Int -> Property |
|
173 |
prop_addOfflinePri (NonNegative extra_mem) (NonNegative extra_cpu) = |
|
174 | 174 |
forAll genOnlineNode $ \node -> |
175 | 175 |
forAll (genInstanceSmallerThanNode node) $ \inst -> |
176 | 176 |
let inst' = inst { Instance.runSt = Types.AdminOffline |
... | ... | |
182 | 182 |
|
183 | 183 |
-- | Check that an offline instance with reasonable disk size but |
184 | 184 |
-- extra mem/cpu can always be added. |
185 |
prop_Node_addOfflineSec :: NonNegative Int -> NonNegative Int
|
|
186 |
-> Types.Ndx -> Property
|
|
187 |
prop_Node_addOfflineSec (NonNegative extra_mem) (NonNegative extra_cpu) pdx =
|
|
185 |
prop_addOfflineSec :: NonNegative Int -> NonNegative Int |
|
186 |
-> Types.Ndx -> Property |
|
187 |
prop_addOfflineSec (NonNegative extra_mem) (NonNegative extra_cpu) pdx = |
|
188 | 188 |
forAll genOnlineNode $ \node -> |
189 | 189 |
forAll (genInstanceSmallerThanNode node) $ \inst -> |
190 | 190 |
let inst' = inst { Instance.runSt = Types.AdminOffline |
... | ... | |
196 | 196 |
v -> failTest $ "Expected OpGood/OpGood, but got: " ++ show v |
197 | 197 |
|
198 | 198 |
-- | Checks for memory reservation changes. |
199 |
prop_Node_rMem :: Instance.Instance -> Property
|
|
200 |
prop_Node_rMem inst =
|
|
199 |
prop_rMem :: Instance.Instance -> Property |
|
200 |
prop_rMem inst = |
|
201 | 201 |
not (Instance.isOffline inst) ==> |
202 | 202 |
forAll (genOnlineNode `suchThat` ((> Types.unitMem) . Node.fMem)) $ \node -> |
203 | 203 |
-- ab = auto_balance, nb = non-auto_balance |
... | ... | |
230 | 230 |
x -> failTest $ "Failed to add/remove instances: " ++ show x |
231 | 231 |
|
232 | 232 |
-- | Check mdsk setting. |
233 |
prop_Node_setMdsk :: Node.Node -> SmallRatio -> Bool
|
|
234 |
prop_Node_setMdsk node mx =
|
|
233 |
prop_setMdsk :: Node.Node -> SmallRatio -> Bool |
|
234 |
prop_setMdsk node mx = |
|
235 | 235 |
Node.loDsk node' >= 0 && |
236 | 236 |
fromIntegral (Node.loDsk node') <= Node.tDsk node && |
237 | 237 |
Node.availDisk node' >= 0 && |
... | ... | |
242 | 242 |
SmallRatio mx' = mx |
243 | 243 |
|
244 | 244 |
-- Check tag maps |
245 |
prop_Node_tagMaps_idempotent :: Property
|
|
246 |
prop_Node_tagMaps_idempotent =
|
|
245 |
prop_tagMaps_idempotent :: Property |
|
246 |
prop_tagMaps_idempotent = |
|
247 | 247 |
forAll genTags $ \tags -> |
248 | 248 |
Node.delTags (Node.addTags m tags) tags ==? m |
249 | 249 |
where m = Map.empty |
250 | 250 |
|
251 |
prop_Node_tagMaps_reject :: Property
|
|
252 |
prop_Node_tagMaps_reject =
|
|
251 |
prop_tagMaps_reject :: Property |
|
252 |
prop_tagMaps_reject = |
|
253 | 253 |
forAll (genTags `suchThat` (not . null)) $ \tags -> |
254 | 254 |
let m = Node.addTags Map.empty tags |
255 | 255 |
in all (\t -> Node.rejectAddTags m [t]) tags |
256 | 256 |
|
257 |
prop_Node_showField :: Node.Node -> Property
|
|
258 |
prop_Node_showField node =
|
|
257 |
prop_showField :: Node.Node -> Property |
|
258 |
prop_showField node = |
|
259 | 259 |
forAll (elements Node.defaultFields) $ \ field -> |
260 | 260 |
fst (Node.showHeader field) /= Types.unknownField && |
261 | 261 |
Node.showField node field /= Types.unknownField |
262 | 262 |
|
263 |
prop_Node_computeGroups :: [Node.Node] -> Bool
|
|
264 |
prop_Node_computeGroups nodes =
|
|
263 |
prop_computeGroups :: [Node.Node] -> Bool |
|
264 |
prop_computeGroups nodes = |
|
265 | 265 |
let ng = Node.computeGroups nodes |
266 | 266 |
onlyuuid = map fst ng |
267 | 267 |
in length nodes == sum (map (length . snd) ng) && |
... | ... | |
270 | 270 |
(null nodes || not (null ng)) |
271 | 271 |
|
272 | 272 |
-- Check idempotence of add/remove operations |
273 |
prop_Node_addPri_idempotent :: Property
|
|
274 |
prop_Node_addPri_idempotent =
|
|
273 |
prop_addPri_idempotent :: Property |
|
274 |
prop_addPri_idempotent = |
|
275 | 275 |
forAll genOnlineNode $ \node -> |
276 | 276 |
forAll (genInstanceSmallerThanNode node) $ \inst -> |
277 | 277 |
case Node.addPri node inst of |
278 | 278 |
Types.OpGood node' -> Node.removePri node' inst ==? node |
279 | 279 |
_ -> failTest "Can't add instance" |
280 | 280 |
|
281 |
prop_Node_addSec_idempotent :: Property
|
|
282 |
prop_Node_addSec_idempotent =
|
|
281 |
prop_addSec_idempotent :: Property |
|
282 |
prop_addSec_idempotent = |
|
283 | 283 |
forAll genOnlineNode $ \node -> |
284 | 284 |
forAll (genInstanceSmallerThanNode node) $ \inst -> |
285 | 285 |
let pdx = Node.idx node + 1 |
... | ... | |
290 | 290 |
_ -> failTest "Can't add instance" |
291 | 291 |
|
292 | 292 |
testSuite "Node" |
293 |
[ 'prop_Node_setAlias
|
|
294 |
, 'prop_Node_setOffline
|
|
295 |
, 'prop_Node_setMcpu
|
|
296 |
, 'prop_Node_setXmem
|
|
297 |
, 'prop_Node_addPriFM
|
|
298 |
, 'prop_Node_addPriFD
|
|
299 |
, 'prop_Node_addPriFC
|
|
300 |
, 'prop_Node_addSec
|
|
301 |
, 'prop_Node_addOfflinePri
|
|
302 |
, 'prop_Node_addOfflineSec
|
|
303 |
, 'prop_Node_rMem
|
|
304 |
, 'prop_Node_setMdsk
|
|
305 |
, 'prop_Node_tagMaps_idempotent
|
|
306 |
, 'prop_Node_tagMaps_reject
|
|
307 |
, 'prop_Node_showField
|
|
308 |
, 'prop_Node_computeGroups
|
|
309 |
, 'prop_Node_addPri_idempotent
|
|
310 |
, 'prop_Node_addSec_idempotent
|
|
293 |
[ 'prop_setAlias |
|
294 |
, 'prop_setOffline |
|
295 |
, 'prop_setMcpu |
|
296 |
, 'prop_setXmem |
|
297 |
, 'prop_addPriFM |
|
298 |
, 'prop_addPriFD |
|
299 |
, 'prop_addPriFC |
|
300 |
, 'prop_addSec |
|
301 |
, 'prop_addOfflinePri |
|
302 |
, 'prop_addOfflineSec |
|
303 |
, 'prop_rMem |
|
304 |
, 'prop_setMdsk |
|
305 |
, 'prop_tagMaps_idempotent |
|
306 |
, 'prop_tagMaps_reject |
|
307 |
, 'prop_showField |
|
308 |
, 'prop_computeGroups |
|
309 |
, 'prop_addPri_idempotent |
|
310 |
, 'prop_addSec_idempotent |
|
311 | 311 |
] |
b/htest/Test/Ganeti/HTools/PeerMap.hs | ||
---|---|---|
36 | 36 |
import qualified Ganeti.HTools.PeerMap as PeerMap |
37 | 37 |
|
38 | 38 |
-- | Make sure add is idempotent. |
39 |
prop_PeerMap_addIdempotent :: PeerMap.PeerMap
|
|
40 |
-> PeerMap.Key -> PeerMap.Elem -> Property
|
|
41 |
prop_PeerMap_addIdempotent pmap key em =
|
|
39 |
prop_addIdempotent :: PeerMap.PeerMap |
|
40 |
-> PeerMap.Key -> PeerMap.Elem -> Property |
|
41 |
prop_addIdempotent pmap key em = |
|
42 | 42 |
fn puniq ==? fn (fn puniq) |
43 | 43 |
where fn = PeerMap.add key em |
44 | 44 |
puniq = PeerMap.accumArray const pmap |
45 | 45 |
|
46 | 46 |
-- | Make sure remove is idempotent. |
47 |
prop_PeerMap_removeIdempotent :: PeerMap.PeerMap -> PeerMap.Key -> Property
|
|
48 |
prop_PeerMap_removeIdempotent pmap key =
|
|
47 |
prop_removeIdempotent :: PeerMap.PeerMap -> PeerMap.Key -> Property |
|
48 |
prop_removeIdempotent pmap key = |
|
49 | 49 |
fn puniq ==? fn (fn puniq) |
50 | 50 |
where fn = PeerMap.remove key |
51 | 51 |
puniq = PeerMap.accumArray const pmap |
52 | 52 |
|
53 | 53 |
-- | Make sure a missing item returns 0. |
54 |
prop_PeerMap_findMissing :: PeerMap.PeerMap -> PeerMap.Key -> Property
|
|
55 |
prop_PeerMap_findMissing pmap key =
|
|
54 |
prop_findMissing :: PeerMap.PeerMap -> PeerMap.Key -> Property |
|
55 |
prop_findMissing pmap key = |
|
56 | 56 |
PeerMap.find key (PeerMap.remove key puniq) ==? 0 |
57 | 57 |
where puniq = PeerMap.accumArray const pmap |
58 | 58 |
|
59 | 59 |
-- | Make sure an added item is found. |
60 |
prop_PeerMap_addFind :: PeerMap.PeerMap
|
|
60 |
prop_addFind :: PeerMap.PeerMap |
|
61 | 61 |
-> PeerMap.Key -> PeerMap.Elem -> Property |
62 |
prop_PeerMap_addFind pmap key em =
|
|
62 |
prop_addFind pmap key em = |
|
63 | 63 |
PeerMap.find key (PeerMap.add key em puniq) ==? em |
64 | 64 |
where puniq = PeerMap.accumArray const pmap |
65 | 65 |
|
66 | 66 |
-- | Manual check that maxElem returns the maximum indeed, or 0 for null. |
67 |
prop_PeerMap_maxElem :: PeerMap.PeerMap -> Property
|
|
68 |
prop_PeerMap_maxElem pmap =
|
|
67 |
prop_maxElem :: PeerMap.PeerMap -> Property |
|
68 |
prop_maxElem pmap = |
|
69 | 69 |
PeerMap.maxElem puniq ==? if null puniq then 0 |
70 | 70 |
else (maximum . snd . unzip) puniq |
71 | 71 |
where puniq = PeerMap.accumArray const pmap |
72 | 72 |
|
73 | 73 |
-- | List of tests for the PeerMap module. |
74 | 74 |
testSuite "PeerMap" |
75 |
[ 'prop_PeerMap_addIdempotent
|
|
76 |
, 'prop_PeerMap_removeIdempotent
|
|
77 |
, 'prop_PeerMap_maxElem
|
|
78 |
, 'prop_PeerMap_addFind
|
|
79 |
, 'prop_PeerMap_findMissing
|
|
75 |
[ 'prop_addIdempotent |
|
76 |
, 'prop_removeIdempotent |
|
77 |
, 'prop_maxElem |
|
78 |
, 'prop_addFind |
|
79 |
, 'prop_findMissing |
|
80 | 80 |
] |
b/htest/Test/Ganeti/HTools/Simu.hs | ||
---|---|---|
62 | 62 |
|
63 | 63 |
-- | Checks that given a set of corrects specs, we can load them |
64 | 64 |
-- successfully, and that at high-level the values look right. |
65 |
prop_Simu_Load :: Property
|
|
66 |
prop_Simu_Load =
|
|
65 |
prop_Load :: Property |
|
66 |
prop_Load = |
|
67 | 67 |
forAll (choose (0, 10)) $ \ngroups -> |
68 | 68 |
forAll (replicateM ngroups genSimuSpec) $ \specs -> |
69 | 69 |
let strspecs = map (\(p, n, d, m, c) -> printf "%s,%d,%d,%d,%d" |
... | ... | |
93 | 93 |
replicate ngroups Types.defIPolicy |
94 | 94 |
|
95 | 95 |
testSuite "Simu" |
96 |
[ 'prop_Simu_Load
|
|
96 |
[ 'prop_Load |
|
97 | 97 |
] |
b/htest/Test/Ganeti/HTools/Text.hs | ||
---|---|---|
52 | 52 |
|
53 | 53 |
-- * Instance text loader tests |
54 | 54 |
|
55 |
prop_Text_Load_Instance :: String -> Int -> Int -> Int -> Types.InstanceStatus
|
|
56 |
-> NonEmptyList Char -> [Char]
|
|
57 |
-> NonNegative Int -> NonNegative Int -> Bool
|
|
58 |
-> Types.DiskTemplate -> Int -> Property
|
|
59 |
prop_Text_Load_Instance name mem dsk vcpus status
|
|
60 |
(NonEmpty pnode) snode
|
|
61 |
(NonNegative pdx) (NonNegative sdx) autobal dt su =
|
|
55 |
prop_Load_Instance :: String -> Int -> Int -> Int -> Types.InstanceStatus |
|
56 |
-> NonEmptyList Char -> [Char] |
|
57 |
-> NonNegative Int -> NonNegative Int -> Bool |
|
58 |
-> Types.DiskTemplate -> Int -> Property |
|
59 |
prop_Load_Instance name mem dsk vcpus status |
|
60 |
(NonEmpty pnode) snode |
|
61 |
(NonNegative pdx) (NonNegative sdx) autobal dt su = |
|
62 | 62 |
pnode /= snode && pdx /= sdx ==> |
63 | 63 |
let vcpus_s = show vcpus |
64 | 64 |
dsk_s = show dsk |
... | ... | |
93 | 93 |
Instance.spindleUse i == su && |
94 | 94 |
Types.isBad fail1 |
95 | 95 |
|
96 |
prop_Text_Load_InstanceFail :: [(String, Int)] -> [String] -> Property
|
|
97 |
prop_Text_Load_InstanceFail ktn fields =
|
|
96 |
prop_Load_InstanceFail :: [(String, Int)] -> [String] -> Property |
|
97 |
prop_Load_InstanceFail ktn fields = |
|
98 | 98 |
length fields /= 10 && length fields /= 11 ==> |
99 | 99 |
case Text.loadInst nl fields of |
100 | 100 |
Types.Ok _ -> failTest "Managed to load instance from invalid data" |
... | ... | |
102 | 102 |
"Invalid/incomplete instance data: '" `isPrefixOf` msg |
103 | 103 |
where nl = Map.fromList ktn |
104 | 104 |
|
105 |
prop_Text_Load_Node :: String -> Int -> Int -> Int -> Int -> Int
|
|
106 |
-> Int -> Bool -> Bool
|
|
107 |
prop_Text_Load_Node name tm nm fm td fd tc fo =
|
|
105 |
prop_Load_Node :: String -> Int -> Int -> Int -> Int -> Int |
|
106 |
-> Int -> Bool -> Bool |
|
107 |
prop_Load_Node name tm nm fm td fd tc fo = |
|
108 | 108 |
let conv v = if v < 0 |
109 | 109 |
then "?" |
110 | 110 |
else show v |
... | ... | |
134 | 134 |
Node.fDsk node == fd && |
135 | 135 |
Node.tCpu node == fromIntegral tc |
136 | 136 |
|
137 |
prop_Text_Load_NodeFail :: [String] -> Property
|
|
138 |
prop_Text_Load_NodeFail fields =
|
|
137 |
prop_Load_NodeFail :: [String] -> Property |
|
138 |
prop_Load_NodeFail fields = |
|
139 | 139 |
length fields /= 8 ==> isNothing $ Text.loadNode Map.empty fields |
140 | 140 |
|
141 |
prop_Text_NodeLSIdempotent :: Property
|
|
142 |
prop_Text_NodeLSIdempotent =
|
|
141 |
prop_NodeLSIdempotent :: Property |
|
142 |
prop_NodeLSIdempotent = |
|
143 | 143 |
forAll (genNode (Just 1) Nothing) $ \node -> |
144 | 144 |
-- override failN1 to what loadNode returns by default |
145 | 145 |
let n = Node.setPolicy Types.defIPolicy $ |
... | ... | |
149 | 149 |
Utils.sepSplit '|' . Text.serializeNode defGroupList) n ==? |
150 | 150 |
Just (Node.name n, n) |
151 | 151 |
|
152 |
prop_Text_ISpecIdempotent :: Types.ISpec -> Property
|
|
153 |
prop_Text_ISpecIdempotent ispec =
|
|
152 |
prop_ISpecIdempotent :: Types.ISpec -> Property |
|
153 |
prop_ISpecIdempotent ispec = |
|
154 | 154 |
case Text.loadISpec "dummy" . Utils.sepSplit ',' . |
155 | 155 |
Text.serializeISpec $ ispec of |
156 | 156 |
Types.Bad msg -> failTest $ "Failed to load ispec: " ++ msg |
157 | 157 |
Types.Ok ispec' -> ispec ==? ispec' |
158 | 158 |
|
159 |
prop_Text_IPolicyIdempotent :: Types.IPolicy -> Property
|
|
160 |
prop_Text_IPolicyIdempotent ipol =
|
|
159 |
prop_IPolicyIdempotent :: Types.IPolicy -> Property |
|
160 |
prop_IPolicyIdempotent ipol = |
|
161 | 161 |
case Text.loadIPolicy . Utils.sepSplit '|' $ |
162 | 162 |
Text.serializeIPolicy owner ipol of |
163 | 163 |
Types.Bad msg -> failTest $ "Failed to load ispec: " ++ msg |
... | ... | |
171 | 171 |
-- allocations, not for the business logic). As such, it's a quite |
172 | 172 |
-- complex and slow test, and that's the reason we restrict it to |
173 | 173 |
-- small cluster sizes. |
174 |
prop_Text_CreateSerialise :: Property
|
|
175 |
prop_Text_CreateSerialise =
|
|
174 |
prop_CreateSerialise :: Property |
|
175 |
prop_CreateSerialise = |
|
176 | 176 |
forAll genTags $ \ctags -> |
177 | 177 |
forAll (choose (1, 20)) $ \maxiter -> |
178 | 178 |
forAll (choose (2, 10)) $ \count -> |
... | ... | |
200 | 200 |
nl' ==? nl2 |
201 | 201 |
|
202 | 202 |
testSuite "Text" |
203 |
[ 'prop_Text_Load_Instance
|
|
204 |
, 'prop_Text_Load_InstanceFail
|
|
205 |
, 'prop_Text_Load_Node
|
|
206 |
, 'prop_Text_Load_NodeFail
|
|
207 |
, 'prop_Text_NodeLSIdempotent
|
|
208 |
, 'prop_Text_ISpecIdempotent
|
|
209 |
, 'prop_Text_IPolicyIdempotent
|
|
210 |
, 'prop_Text_CreateSerialise
|
|
203 |
[ 'prop_Load_Instance |
|
204 |
, 'prop_Load_InstanceFail |
|
205 |
, 'prop_Load_Node |
|
206 |
, 'prop_Load_NodeFail |
|
207 |
, 'prop_NodeLSIdempotent |
|
208 |
, 'prop_ISpecIdempotent |
|
209 |
, 'prop_IPolicyIdempotent |
|
210 |
, 'prop_CreateSerialise |
|
211 | 211 |
] |
b/htest/Test/Ganeti/HTools/Types.hs | ||
---|---|---|
127 | 127 |
|
128 | 128 |
-- * Test cases |
129 | 129 |
|
130 |
prop_Types_AllocPolicy_serialisation :: Types.AllocPolicy -> Property
|
|
131 |
prop_Types_AllocPolicy_serialisation apol =
|
|
130 |
prop_AllocPolicy_serialisation :: Types.AllocPolicy -> Property |
|
131 |
prop_AllocPolicy_serialisation apol = |
|
132 | 132 |
case J.readJSON (J.showJSON apol) of |
133 | 133 |
J.Ok p -> p ==? apol |
134 | 134 |
J.Error s -> failTest $ "Failed to deserialise: " ++ s |
135 | 135 |
|
136 |
prop_Types_DiskTemplate_serialisation :: Types.DiskTemplate -> Property
|
|
137 |
prop_Types_DiskTemplate_serialisation dt =
|
|
136 |
prop_DiskTemplate_serialisation :: Types.DiskTemplate -> Property |
|
137 |
prop_DiskTemplate_serialisation dt = |
|
138 | 138 |
case J.readJSON (J.showJSON dt) of |
139 | 139 |
J.Ok p -> p ==? dt |
140 | 140 |
J.Error s -> failTest $ "Failed to deserialise: " ++ s |
141 | 141 |
|
142 |
prop_Types_ISpec_serialisation :: Types.ISpec -> Property
|
|
143 |
prop_Types_ISpec_serialisation ispec =
|
|
142 |
prop_ISpec_serialisation :: Types.ISpec -> Property |
|
143 |
prop_ISpec_serialisation ispec = |
|
144 | 144 |
case J.readJSON (J.showJSON ispec) of |
145 | 145 |
J.Ok p -> p ==? ispec |
146 | 146 |
J.Error s -> failTest $ "Failed to deserialise: " ++ s |
147 | 147 |
|
148 |
prop_Types_IPolicy_serialisation :: Types.IPolicy -> Property
|
|
149 |
prop_Types_IPolicy_serialisation ipol =
|
|
148 |
prop_IPolicy_serialisation :: Types.IPolicy -> Property |
|
149 |
prop_IPolicy_serialisation ipol = |
|
150 | 150 |
case J.readJSON (J.showJSON ipol) of |
151 | 151 |
J.Ok p -> p ==? ipol |
152 | 152 |
J.Error s -> failTest $ "Failed to deserialise: " ++ s |
153 | 153 |
|
154 |
prop_Types_EvacMode_serialisation :: Types.EvacMode -> Property
|
|
155 |
prop_Types_EvacMode_serialisation em =
|
|
154 |
prop_EvacMode_serialisation :: Types.EvacMode -> Property |
|
155 |
prop_EvacMode_serialisation em = |
|
156 | 156 |
case J.readJSON (J.showJSON em) of |
157 | 157 |
J.Ok p -> p ==? em |
158 | 158 |
J.Error s -> failTest $ "Failed to deserialise: " ++ s |
159 | 159 |
|
160 |
prop_Types_opToResult :: Types.OpResult Int -> Bool
|
|
161 |
prop_Types_opToResult op =
|
|
160 |
prop_opToResult :: Types.OpResult Int -> Bool |
|
161 |
prop_opToResult op = |
|
162 | 162 |
case op of |
163 | 163 |
Types.OpFail _ -> Types.isBad r |
164 | 164 |
Types.OpGood v -> case r of |
... | ... | |
166 | 166 |
Types.Ok v' -> v == v' |
167 | 167 |
where r = Types.opToResult op |
168 | 168 |
|
169 |
prop_Types_eitherToResult :: Either String Int -> Bool
|
|
170 |
prop_Types_eitherToResult ei =
|
|
169 |
prop_eitherToResult :: Either String Int -> Bool |
|
170 |
prop_eitherToResult ei = |
|
171 | 171 |
case ei of |
172 | 172 |
Left _ -> Types.isBad r |
173 | 173 |
Right v -> case r of |
... | ... | |
176 | 176 |
where r = Types.eitherToResult ei |
177 | 177 |
|
178 | 178 |
testSuite "Types" |
179 |
[ 'prop_Types_AllocPolicy_serialisation
|
|
180 |
, 'prop_Types_DiskTemplate_serialisation
|
|
181 |
, 'prop_Types_ISpec_serialisation
|
|
182 |
, 'prop_Types_IPolicy_serialisation
|
|
183 |
, 'prop_Types_EvacMode_serialisation
|
|
184 |
, 'prop_Types_opToResult
|
|
185 |
, 'prop_Types_eitherToResult
|
|
179 |
[ 'prop_AllocPolicy_serialisation |
|
180 |
, 'prop_DiskTemplate_serialisation |
|
181 |
, 'prop_ISpec_serialisation |
|
182 |
, 'prop_IPolicy_serialisation |
|
183 |
, 'prop_EvacMode_serialisation |
|
184 |
, 'prop_opToResult |
|
185 |
, 'prop_eitherToResult |
|
186 | 186 |
] |
b/htest/Test/Ganeti/HTools/Utils.hs | ||
---|---|---|
47 | 47 |
|
48 | 48 |
-- | If the list is not just an empty element, and if the elements do |
49 | 49 |
-- not contain commas, then join+split should be idempotent. |
50 |
prop_Utils_commaJoinSplit :: Property
|
|
51 |
prop_Utils_commaJoinSplit =
|
|
50 |
prop_commaJoinSplit :: Property |
|
51 |
prop_commaJoinSplit = |
|
52 | 52 |
forAll (choose (0, 20)) $ \llen -> |
53 | 53 |
forAll (vectorOf llen genNonCommaString `suchThat` ((/=) [""])) $ \lst -> |
54 | 54 |
Utils.sepSplit ',' (Utils.commaJoin lst) ==? lst |
55 | 55 |
|
56 | 56 |
-- | Split and join should always be idempotent. |
57 |
prop_Utils_commaSplitJoin :: [Char] -> Property
|
|
58 |
prop_Utils_commaSplitJoin s =
|
|
57 |
prop_commaSplitJoin :: [Char] -> Property |
|
58 |
prop_commaSplitJoin s = |
|
59 | 59 |
Utils.commaJoin (Utils.sepSplit ',' s) ==? s |
60 | 60 |
|
61 | 61 |
-- | fromObjWithDefault, we test using the Maybe monad and an integer |
62 | 62 |
-- value. |
63 |
prop_Utils_fromObjWithDefault :: Integer -> String -> Bool
|
|
64 |
prop_Utils_fromObjWithDefault def_value random_key =
|
|
63 |
prop_fromObjWithDefault :: Integer -> String -> Bool |
|
64 |
prop_fromObjWithDefault def_value random_key = |
|
65 | 65 |
-- a missing key will be returned with the default |
66 | 66 |
JSON.fromObjWithDefault [] random_key def_value == Just def_value && |
67 | 67 |
-- a found key will be returned as is, not with default |
... | ... | |
69 | 69 |
random_key (def_value+1) == Just def_value |
70 | 70 |
|
71 | 71 |
-- | Test that functional if' behaves like the syntactic sugar if. |
72 |
prop_Utils_if'if :: Bool -> Int -> Int -> Gen Prop
|
|
73 |
prop_Utils_if'if cnd a b =
|
|
72 |
prop_if'if :: Bool -> Int -> Int -> Gen Prop |
|
73 |
prop_if'if cnd a b = |
|
74 | 74 |
Utils.if' cnd a b ==? if cnd then a else b |
75 | 75 |
|
76 | 76 |
-- | Test basic select functionality |
77 |
prop_Utils_select :: Int -- ^ Default result
|
|
78 |
-> [Int] -- ^ List of False values
|
|
79 |
-> [Int] -- ^ List of True values
|
|
80 |
-> Gen Prop -- ^ Test result
|
|
81 |
prop_Utils_select def lst1 lst2 =
|
|
77 |
prop_select :: Int -- ^ Default result |
|
78 |
-> [Int] -- ^ List of False values |
|
79 |
-> [Int] -- ^ List of True values |
|
80 |
-> Gen Prop -- ^ Test result |
|
81 |
prop_select def lst1 lst2 = |
|
82 | 82 |
Utils.select def (flist ++ tlist) ==? expectedresult |
83 | 83 |
where expectedresult = Utils.if' (null lst2) def (head lst2) |
84 | 84 |
flist = zip (repeat False) lst1 |
85 | 85 |
tlist = zip (repeat True) lst2 |
86 | 86 |
|
87 | 87 |
-- | Test basic select functionality with undefined default |
88 |
prop_Utils_select_undefd :: [Int] -- ^ List of False values
|
|
89 |
-> NonEmptyList Int -- ^ List of True values
|
|
90 |
-> Gen Prop -- ^ Test result
|
|
91 |
prop_Utils_select_undefd lst1 (NonEmpty lst2) =
|
|
88 |
prop_select_undefd :: [Int] -- ^ List of False values |
|
89 |
-> NonEmptyList Int -- ^ List of True values |
|
90 |
-> Gen Prop -- ^ Test result |
|
91 |
prop_select_undefd lst1 (NonEmpty lst2) = |
|
92 | 92 |
Utils.select undefined (flist ++ tlist) ==? head lst2 |
93 | 93 |
where flist = zip (repeat False) lst1 |
94 | 94 |
tlist = zip (repeat True) lst2 |
95 | 95 |
|
96 | 96 |
-- | Test basic select functionality with undefined list values |
97 |
prop_Utils_select_undefv :: [Int] -- ^ List of False values
|
|
98 |
-> NonEmptyList Int -- ^ List of True values
|
|
99 |
-> Gen Prop -- ^ Test result
|
|
100 |
prop_Utils_select_undefv lst1 (NonEmpty lst2) =
|
|
97 |
prop_select_undefv :: [Int] -- ^ List of False values |
|
98 |
-> NonEmptyList Int -- ^ List of True values |
|
99 |
-> Gen Prop -- ^ Test result |
|
100 |
prop_select_undefv lst1 (NonEmpty lst2) = |
|
101 | 101 |
Utils.select undefined cndlist ==? head lst2 |
102 | 102 |
where flist = zip (repeat False) lst1 |
103 | 103 |
tlist = zip (repeat True) lst2 |
104 | 104 |
cndlist = flist ++ tlist ++ [undefined] |
105 | 105 |
|
106 |
prop_Utils_parseUnit :: NonNegative Int -> Property
|
|
107 |
prop_Utils_parseUnit (NonNegative n) =
|
|
106 |
prop_parseUnit :: NonNegative Int -> Property |
|
107 |
prop_parseUnit (NonNegative n) = |
|
108 | 108 |
Utils.parseUnit (show n) ==? Types.Ok n .&&. |
109 | 109 |
Utils.parseUnit (show n ++ "m") ==? Types.Ok n .&&. |
110 | 110 |
Utils.parseUnit (show n ++ "M") ==? Types.Ok (truncate n_mb::Int) .&&. |
... | ... | |
121 | 121 |
|
122 | 122 |
-- | Test list for the Utils module. |
123 | 123 |
testSuite "Utils" |
124 |
[ 'prop_Utils_commaJoinSplit
|
|
125 |
, 'prop_Utils_commaSplitJoin
|
|
126 |
, 'prop_Utils_fromObjWithDefault
|
|
127 |
, 'prop_Utils_if'if
|
|
128 |
, 'prop_Utils_select
|
|
129 |
, 'prop_Utils_select_undefd
|
|
130 |
, 'prop_Utils_select_undefv
|
|
131 |
, 'prop_Utils_parseUnit
|
|
124 |
[ 'prop_commaJoinSplit |
|
125 |
, 'prop_commaSplitJoin |
|
126 |
, 'prop_fromObjWithDefault |
|
127 |
, 'prop_if'if |
|
128 |
, 'prop_select |
|
129 |
, 'prop_select_undefd |
|
130 |
, 'prop_select_undefv |
|
131 |
, 'prop_parseUnit |
|
132 | 132 |
] |
b/htest/Test/Ganeti/JSON.hs | ||
---|---|---|
38 | 38 |
import qualified Ganeti.BasicTypes as BasicTypes |
39 | 39 |
import qualified Ganeti.JSON as JSON |
40 | 40 |
|
41 |
prop_JSON_toArray :: [Int] -> Property
|
|
42 |
prop_JSON_toArray intarr =
|
|
41 |
prop_toArray :: [Int] -> Property |
|
42 |
prop_toArray intarr = |
|
43 | 43 |
let arr = map J.showJSON intarr in |
44 | 44 |
case JSON.toArray (J.JSArray arr) of |
45 | 45 |
BasicTypes.Ok arr' -> arr ==? arr' |
46 | 46 |
BasicTypes.Bad err -> failTest $ "Failed to parse array: " ++ err |
47 | 47 |
|
48 |
prop_JSON_toArrayFail :: Int -> String -> Bool -> Property
|
|
49 |
prop_JSON_toArrayFail i s b =
|
|
48 |
prop_toArrayFail :: Int -> String -> Bool -> Property |
|
49 |
prop_toArrayFail i s b = |
|
50 | 50 |
-- poor man's instance Arbitrary JSValue |
51 | 51 |
forAll (elements [J.showJSON i, J.showJSON s, J.showJSON b]) $ \item -> |
52 | 52 |
case JSON.toArray item of |
... | ... | |
54 | 54 |
BasicTypes.Ok result -> failTest $ "Unexpected parse, got " ++ show result |
55 | 55 |
|
56 | 56 |
testSuite "JSON" |
57 |
[ 'prop_JSON_toArray
|
|
58 |
, 'prop_JSON_toArrayFail
|
|
57 |
[ 'prop_toArray |
|
58 |
, 'prop_toArrayFail |
|
59 | 59 |
] |
b/htest/Test/Ganeti/Jobs.hs | ||
---|---|---|
48 | 48 |
-- * Test cases |
49 | 49 |
|
50 | 50 |
-- | Check that (queued) job\/opcode status serialization is idempotent. |
51 |
prop_Jobs_OpStatus_serialization :: Jobs.OpStatus -> Property
|
|
52 |
prop_Jobs_OpStatus_serialization os =
|
|
51 |
prop_OpStatus_serialization :: Jobs.OpStatus -> Property |
|
52 |
prop_OpStatus_serialization os = |
|
53 | 53 |
case J.readJSON (J.showJSON os) of |
54 | 54 |
J.Error e -> failTest $ "Cannot deserialise: " ++ e |
55 | 55 |
J.Ok os' -> os ==? os' |
56 | 56 |
|
57 |
prop_Jobs_JobStatus_serialization :: Jobs.JobStatus -> Property
|
|
58 |
prop_Jobs_JobStatus_serialization js =
|
|
57 |
prop_JobStatus_serialization :: Jobs.JobStatus -> Property |
|
58 |
prop_JobStatus_serialization js = |
|
59 | 59 |
case J.readJSON (J.showJSON js) of |
60 | 60 |
J.Error e -> failTest $ "Cannot deserialise: " ++ e |
61 | 61 |
J.Ok js' -> js ==? js' |
62 | 62 |
|
63 | 63 |
testSuite "Jobs" |
64 |
[ 'prop_Jobs_OpStatus_serialization
|
|
65 |
, 'prop_Jobs_JobStatus_serialization
|
|
64 |
[ 'prop_OpStatus_serialization |
|
65 |
, 'prop_JobStatus_serialization |
|
66 | 66 |
] |
b/htest/Test/Ganeti/Luxi.hs | ||
---|---|---|
86 | 86 |
Luxi.ReqSetWatcherPause -> Luxi.SetWatcherPause <$> arbitrary |
87 | 87 |
|
88 | 88 |
-- | Simple check that encoding/decoding of LuxiOp works. |
89 |
prop_Luxi_CallEncoding :: Luxi.LuxiOp -> Property
|
|
90 |
prop_Luxi_CallEncoding op =
|
|
89 |
prop_CallEncoding :: Luxi.LuxiOp -> Property |
|
90 |
prop_CallEncoding op = |
|
91 | 91 |
(Luxi.validateCall (Luxi.buildCall op) >>= Luxi.decodeCall) ==? Ok op |
92 | 92 |
|
93 | 93 |
-- | Helper to a get a temporary file name. |
... | ... | |
115 | 115 |
-- | Monadic check that, given a server socket, we can connect via a |
116 | 116 |
-- client to it, and that we can send a list of arbitrary messages and |
117 | 117 |
-- get back what we sent. |
118 |
prop_Luxi_ClientServer :: [[DNSChar]] -> Property
|
|
119 |
prop_Luxi_ClientServer dnschars = monadicIO $ do
|
|
118 |
prop_ClientServer :: [[DNSChar]] -> Property |
|
119 |
prop_ClientServer dnschars = monadicIO $ do |
|
120 | 120 |
let msgs = map (map dnsGetChar) dnschars |
121 | 121 |
fpath <- run $ getTempFileName |
122 | 122 |
-- we need to create the server first, otherwise (if we do it in the |
... | ... | |
137 | 137 |
stop $ replies ==? msgs |
138 | 138 |
|
139 | 139 |
testSuite "Luxi" |
140 |
[ 'prop_Luxi_CallEncoding
|
|
141 |
, 'prop_Luxi_ClientServer
|
|
140 |
[ 'prop_CallEncoding |
|
141 |
, 'prop_ClientServer |
|
142 | 142 |
] |
b/htest/Test/Ganeti/Objects.hs | ||
---|---|---|
55 | 55 |
<*> (Set.fromList <$> genTags) |
56 | 56 |
|
57 | 57 |
-- | Tests that fillDict behaves correctly |
58 |
prop_Objects_fillDict :: [(Int, Int)] -> [(Int, Int)] -> Property
|
|
59 |
prop_Objects_fillDict defaults custom =
|
|
58 |
prop_fillDict :: [(Int, Int)] -> [(Int, Int)] -> Property |
|
59 |
prop_fillDict defaults custom = |
|
60 | 60 |
let d_map = Map.fromList defaults |
61 | 61 |
d_keys = map fst defaults |
62 | 62 |
c_map = Map.fromList custom |
... | ... | |
69 | 69 |
(Objects.fillDict d_map c_map (d_keys++c_keys) == Map.empty) |
70 | 70 |
|
71 | 71 |
testSuite "Objects" |
72 |
[ 'prop_Objects_fillDict
|
|
72 |
[ 'prop_fillDict |
|
73 | 73 |
] |
b/htest/Test/Ganeti/OpCodes.hs | ||
---|---|---|
73 | 73 |
-- * Test cases |
74 | 74 |
|
75 | 75 |
-- | Check that opcode serialization is idempotent. |
76 |
prop_OpCodes_serialization :: OpCodes.OpCode -> Property
|
|
77 |
prop_OpCodes_serialization op =
|
|
76 |
prop_serialization :: OpCodes.OpCode -> Property |
|
77 |
prop_serialization op = |
|
78 | 78 |
case J.readJSON (J.showJSON op) of |
79 | 79 |
J.Error e -> failTest $ "Cannot deserialise: " ++ e |
80 | 80 |
J.Ok op' -> op ==? op' |
81 | 81 |
|
82 | 82 |
-- | Check that Python and Haskell defined the same opcode list. |
83 |
case_OpCodes_AllDefined :: HUnit.Assertion
|
|
84 |
case_OpCodes_AllDefined = do
|
|
83 |
case_AllDefined :: HUnit.Assertion |
|
84 |
case_AllDefined = do |
|
85 | 85 |
py_stdout <- runPython "from ganeti import opcodes\n\ |
86 | 86 |
\print '\\n'.join(opcodes.OP_MAPPING.keys())" "" >>= |
87 | 87 |
checkPythonResult |
... | ... | |
111 | 111 |
-- a better way to do this, for example by having a |
112 | 112 |
-- separately-launched Python process (if not running the tests would |
113 | 113 |
-- be skipped). |
114 |
case_OpCodes_py_compat :: HUnit.Assertion
|
|
115 |
case_OpCodes_py_compat = do
|
|
114 |
case_py_compat :: HUnit.Assertion |
|
115 |
case_py_compat = do |
|
116 | 116 |
let num_opcodes = length OpCodes.allOpIDs * 500 |
117 | 117 |
sample_opcodes <- sample' (vectorOf num_opcodes |
118 | 118 |
(arbitrary::Gen OpCodes.OpCode)) |
... | ... | |
143 | 143 |
) $ zip opcodes decoded |
144 | 144 |
|
145 | 145 |
testSuite "OpCodes" |
146 |
[ 'prop_OpCodes_serialization
|
|
147 |
, 'case_OpCodes_AllDefined
|
|
148 |
, 'case_OpCodes_py_compat
|
|
146 |
[ 'prop_serialization |
|
147 |
, 'case_AllDefined |
|
148 |
, 'case_py_compat |
|
149 | 149 |
] |
b/htest/Test/Ganeti/Query/Language.hs | ||
---|---|---|
81 | 81 |
|
82 | 82 |
-- | Tests that serialisation/deserialisation of filters is |
83 | 83 |
-- idempotent. |
84 |
prop_Qlang_Serialisation :: Property
|
|
85 |
prop_Qlang_Serialisation =
|
|
84 |
prop_Serialisation :: Property |
|
85 |
prop_Serialisation = |
|
86 | 86 |
forAll genFilter $ \flt -> |
87 | 87 |
J.readJSON (J.showJSON flt) ==? J.Ok flt |
88 | 88 |
|
89 |
prop_Qlang_FilterRegex_instances :: Qlang.FilterRegex -> Property
|
|
90 |
prop_Qlang_FilterRegex_instances rex =
|
|
89 |
prop_FilterRegex_instances :: Qlang.FilterRegex -> Property |
|
90 |
prop_FilterRegex_instances rex = |
|
91 | 91 |
printTestCase "failed JSON encoding" |
92 | 92 |
(J.readJSON (J.showJSON rex) ==? J.Ok rex) .&&. |
93 | 93 |
printTestCase "failed read/show instances" (read (show rex) ==? rex) |
94 | 94 |
|
95 | 95 |
testSuite "Qlang" |
96 |
[ 'prop_Qlang_Serialisation
|
|
97 |
, 'prop_Qlang_FilterRegex_instances
|
|
96 |
[ 'prop_Serialisation |
|
97 |
, 'prop_FilterRegex_instances |
|
98 | 98 |
] |
b/htest/Test/Ganeti/Rpc.hs | ||
---|---|---|
53 | 53 |
-- offline nodes, we get a OfflineNodeError response. |
54 | 54 |
-- FIXME: We need a way of generalizing this, running it for |
55 | 55 |
-- every call manually will soon get problematic |
56 |
prop_Rpc_noffl_request_allinstinfo :: Rpc.RpcCallAllInstancesInfo -> Property
|
|
57 |
prop_Rpc_noffl_request_allinstinfo call =
|
|
56 |
prop_noffl_request_allinstinfo :: Rpc.RpcCallAllInstancesInfo -> Property |
|
57 |
prop_noffl_request_allinstinfo call = |
|
58 | 58 |
forAll (arbitrary `suchThat` Objects.nodeOffline) $ \node -> monadicIO $ do |
59 | 59 |
res <- run $ Rpc.executeRpcCall [node] call |
60 | 60 |
stop $ res ==? [(node, Left (Rpc.OfflineNodeError node))] |
61 | 61 |
|
62 |
prop_Rpc_noffl_request_instlist :: Rpc.RpcCallInstanceList -> Property
|
|
63 |
prop_Rpc_noffl_request_instlist call =
|
|
62 |
prop_noffl_request_instlist :: Rpc.RpcCallInstanceList -> Property |
|
63 |
prop_noffl_request_instlist call = |
|
64 | 64 |
forAll (arbitrary `suchThat` Objects.nodeOffline) $ \node -> monadicIO $ do |
65 | 65 |
res <- run $ Rpc.executeRpcCall [node] call |
66 | 66 |
stop $ res ==? [(node, Left (Rpc.OfflineNodeError node))] |
67 | 67 |
|
68 |
prop_Rpc_noffl_request_nodeinfo :: Rpc.RpcCallNodeInfo -> Property
|
|
69 |
prop_Rpc_noffl_request_nodeinfo call =
|
|
68 |
prop_noffl_request_nodeinfo :: Rpc.RpcCallNodeInfo -> Property |
|
69 |
prop_noffl_request_nodeinfo call = |
|
70 | 70 |
forAll (arbitrary `suchThat` Objects.nodeOffline) $ \node -> monadicIO $ do |
71 | 71 |
res <- run $ Rpc.executeRpcCall [node] call |
72 | 72 |
stop $ res ==? [(node, Left (Rpc.OfflineNodeError node))] |
73 | 73 |
|
74 | 74 |
testSuite "Rpc" |
75 |
[ 'prop_Rpc_noffl_request_allinstinfo
|
|
76 |
, 'prop_Rpc_noffl_request_instlist
|
|
77 |
, 'prop_Rpc_noffl_request_nodeinfo
|
|
75 |
[ 'prop_noffl_request_allinstinfo |
|
76 |
, 'prop_noffl_request_instlist |
|
77 |
, 'prop_noffl_request_nodeinfo |
|
78 | 78 |
] |
b/htest/Test/Ganeti/Ssconf.hs | ||
---|---|---|
41 | 41 |
instance Arbitrary Ssconf.SSKey where |
42 | 42 |
arbitrary = elements [minBound..maxBound] |
43 | 43 |
|
44 |
prop_Ssconf_filename :: Ssconf.SSKey -> Property
|
|
45 |
prop_Ssconf_filename key =
|
|
44 |
prop_filename :: Ssconf.SSKey -> Property |
|
45 |
prop_filename key = |
|
46 | 46 |
printTestCase "Key doesn't start with correct prefix" $ |
47 | 47 |
Ssconf.sSFilePrefix `isPrefixOf` Ssconf.keyToFilename (Just "") key |
48 | 48 |
|
49 | 49 |
testSuite "Ssconf" |
50 |
[ 'prop_Ssconf_filename
|
|
50 |
[ 'prop_filename |
|
51 | 51 |
] |
b/htest/Test/Ganeti/TestHelper.hs | ||
---|---|---|
38 | 38 |
import Test.QuickCheck |
39 | 39 |
import Language.Haskell.TH |
40 | 40 |
|
41 |
-- | Test property prefix. |
|
42 |
propPrefix :: String |
|
43 |
propPrefix = "prop_" |
|
44 |
|
|
45 |
-- | Test case prefix. |
|
46 |
casePrefix :: String |
|
47 |
casePrefix = "case_" |
|
48 |
|
|
41 | 49 |
-- | Tries to drop a prefix from a string. |
42 | 50 |
simplifyName :: String -> String -> String |
43 | 51 |
simplifyName pfx string = fromMaybe string (stripPrefix pfx string) |
44 | 52 |
|
45 | 53 |
-- | Builds a test from a QuickCheck property. |
46 |
runQC :: Testable prop => String -> String -> prop -> Test
|
|
47 |
runQC pfx name = testProperty (simplifyName ("prop_" ++ pfx ++ "_") name)
|
|
54 |
runProp :: Testable prop => String -> prop -> Test
|
|
55 |
runProp = testProperty . simplifyName propPrefix
|
|
48 | 56 |
|
49 | 57 |
-- | Builds a test for a HUnit test case. |
50 |
runHUnit :: String -> String -> Assertion -> Test
|
|
51 |
runHUnit pfx name = testCase (simplifyName ("case_" ++ pfx ++ "_") name)
|
|
58 |
runCase :: String -> Assertion -> Test
|
|
59 |
runCase = testCase . simplifyName casePrefix
|
|
52 | 60 |
|
53 | 61 |
-- | Runs the correct test provider for a given test, based on its |
54 | 62 |
-- name (not very nice, but...). |
55 |
run :: String -> Name -> Q Exp
|
|
56 |
run tsname name =
|
|
63 |
run :: Name -> Q Exp |
|
64 |
run name = |
|
57 | 65 |
let str = nameBase name |
58 | 66 |
nameE = varE name |
59 | 67 |
strE = litE (StringL str) |
60 | 68 |
in case () of |
61 |
_ | "prop_" `isPrefixOf` str -> [| runQC tsname $strE $nameE |]
|
|
62 |
| "case_" `isPrefixOf` str -> [| runHUnit tsname $strE $nameE |]
|
|
69 |
_ | propPrefix `isPrefixOf` str -> [| runProp $strE $nameE |]
|
|
70 |
| casePrefix `isPrefixOf` str -> [| runCase $strE $nameE |]
|
|
63 | 71 |
| otherwise -> fail $ "Unsupported test function name '" ++ str ++ "'" |
64 | 72 |
|
65 | 73 |
-- | Builds a test suite. |
66 | 74 |
testSuite :: String -> [Name] -> Q [Dec] |
67 | 75 |
testSuite tsname tdef = do |
68 | 76 |
let fullname = mkName $ "test" ++ tsname |
69 |
tests <- mapM (run tsname) tdef
|
|
77 |
tests <- mapM run tdef
|
|
70 | 78 |
sigtype <- [t| (String, [Test]) |] |
71 | 79 |
return [ SigD fullname sigtype |
72 | 80 |
, ValD (VarP fullname) (NormalB (TupE [LitE (StringL tsname), |
Also available in: Unified diff