Statistics
| Branch: | Tag: | Revision:

root / test / hs / Test / Ganeti / OpCodes.hs @ 06c2fb4a

History | View | Annotate | Download (24.8 kB)

1
{-# LANGUAGE TemplateHaskell #-}
2
{-# OPTIONS_GHC -fno-warn-orphans #-}
3

    
4
{-| Unittests for ganeti-htools.
5

    
6
-}
7

    
8
{-
9

    
10
Copyright (C) 2009, 2010, 2011, 2012, 2013 Google Inc.
11

    
12
This program is free software; you can redistribute it and/or modify
13
it under the terms of the GNU General Public License as published by
14
the Free Software Foundation; either version 2 of the License, or
15
(at your option) any later version.
16

    
17
This program is distributed in the hope that it will be useful, but
18
WITHOUT ANY WARRANTY; without even the implied warranty of
19
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20
General Public License for more details.
21

    
22
You should have received a copy of the GNU General Public License
23
along with this program; if not, write to the Free Software
24
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25
02110-1301, USA.
26

    
27
-}
28

    
29
module Test.Ganeti.OpCodes
30
  ( testOpCodes
31
  , OpCodes.OpCode(..)
32
  ) where
33

    
34
import Test.HUnit as HUnit
35
import Test.QuickCheck as QuickCheck
36

    
37
import Control.Applicative
38
import Control.Monad
39
import Data.Char
40
import Data.List
41
import qualified Data.Map as Map
42
import qualified Text.JSON as J
43
import Text.Printf (printf)
44

    
45
import Test.Ganeti.TestHelper
46
import Test.Ganeti.TestCommon
47
import Test.Ganeti.Types ()
48
import Test.Ganeti.Query.Language
49

    
50
import Ganeti.BasicTypes
51
import qualified Ganeti.Constants as C
52
import qualified Ganeti.OpCodes as OpCodes
53
import Ganeti.Types
54
import Ganeti.OpParams
55
import Ganeti.JSON
56

    
57
{-# ANN module "HLint: ignore Use camelCase" #-}
58

    
59
-- * Arbitrary instances
60

    
61
instance Arbitrary OpCodes.TagObject where
62
  arbitrary = oneof [ OpCodes.TagInstance <$> genFQDN
63
                    , OpCodes.TagNode     <$> genFQDN
64
                    , OpCodes.TagGroup    <$> genFQDN
65
                    , OpCodes.TagNetwork  <$> genFQDN
66
                    , pure OpCodes.TagCluster
67
                    ]
68

    
69
$(genArbitrary ''OpCodes.ReplaceDisksMode)
70

    
71
$(genArbitrary ''DiskAccess)
72

    
73
instance Arbitrary OpCodes.DiskIndex where
74
  arbitrary = choose (0, C.maxDisks - 1) >>= OpCodes.mkDiskIndex
75

    
76
instance Arbitrary INicParams where
77
  arbitrary = INicParams <$> genMaybe genNameNE <*> genMaybe genName <*>
78
              genMaybe genNameNE <*> genMaybe genNameNE <*> genMaybe genNameNE
79

    
80
instance Arbitrary IDiskParams where
81
  arbitrary = IDiskParams <$> arbitrary <*> arbitrary <*>
82
              genMaybe genNameNE <*> genMaybe genNameNE <*>
83
              genMaybe genNameNE <*> genMaybe genNameNE
84

    
85
instance Arbitrary RecreateDisksInfo where
86
  arbitrary = oneof [ pure RecreateDisksAll
87
                    , RecreateDisksIndices <$> arbitrary
88
                    , RecreateDisksParams <$> arbitrary
89
                    ]
90

    
91
instance Arbitrary DdmOldChanges where
92
  arbitrary = oneof [ DdmOldIndex <$> arbitrary
93
                    , DdmOldMod   <$> arbitrary
94
                    ]
95

    
96
instance (Arbitrary a) => Arbitrary (SetParamsMods a) where
97
  arbitrary = oneof [ pure SetParamsEmpty
98
                    , SetParamsDeprecated <$> arbitrary
99
                    , SetParamsNew        <$> arbitrary
100
                    ]
101

    
102
instance Arbitrary ISnapParams where
103
  arbitrary = ISnapParams <$> genNameNE
104

    
105
instance (Arbitrary a) => Arbitrary (SetSnapParams a) where
106
  arbitrary = oneof [ pure SetSnapParamsEmpty
107
                    , SetSnapParamsValid <$> arbitrary
108
                    ]
109

    
110
instance Arbitrary ExportTarget where
111
  arbitrary = oneof [ ExportTargetLocal <$> genNodeNameNE
112
                    , ExportTargetRemote <$> pure []
113
                    ]
114

    
115
instance Arbitrary OpCodes.OpCode where
116
  arbitrary = do
117
    op_id <- elements OpCodes.allOpIDs
118
    case op_id of
119
      "OP_TEST_DELAY" ->
120
        OpCodes.OpTestDelay <$> arbitrary <*> arbitrary <*>
121
          genNodeNamesNE <*> arbitrary
122
      "OP_INSTANCE_REPLACE_DISKS" ->
123
        OpCodes.OpInstanceReplaceDisks <$> genFQDN <*> arbitrary <*>
124
          arbitrary <*> arbitrary <*> genDiskIndices <*>
125
          genMaybe genNodeNameNE <*> genMaybe genNameNE
126
      "OP_INSTANCE_FAILOVER" ->
127
        OpCodes.OpInstanceFailover <$> genFQDN <*> arbitrary <*> arbitrary <*>
128
          genMaybe genNodeNameNE <*> arbitrary <*> genMaybe genNameNE <*>
129
          arbitrary
130
      "OP_INSTANCE_MIGRATE" ->
131
        OpCodes.OpInstanceMigrate <$> genFQDN <*> arbitrary <*> arbitrary <*>
132
          genMaybe genNodeNameNE <*> arbitrary <*>
133
          arbitrary <*> arbitrary <*> genMaybe genNameNE <*> arbitrary
134
      "OP_TAGS_GET" ->
135
        OpCodes.OpTagsGet <$> arbitrary <*> arbitrary
136
      "OP_TAGS_SEARCH" ->
137
        OpCodes.OpTagsSearch <$> genNameNE
138
      "OP_TAGS_SET" ->
139
        OpCodes.OpTagsSet <$> arbitrary <*> genTags
140
      "OP_TAGS_DEL" ->
141
        OpCodes.OpTagsSet <$> arbitrary <*> genTags
142
      "OP_CLUSTER_POST_INIT" -> pure OpCodes.OpClusterPostInit
143
      "OP_CLUSTER_DESTROY" -> pure OpCodes.OpClusterDestroy
144
      "OP_CLUSTER_QUERY" -> pure OpCodes.OpClusterQuery
145
      "OP_CLUSTER_VERIFY" ->
146
        OpCodes.OpClusterVerify <$> arbitrary <*> arbitrary <*>
147
          genSet Nothing <*> genSet Nothing <*> arbitrary <*>
148
          genMaybe genNameNE
149
      "OP_CLUSTER_VERIFY_CONFIG" ->
150
        OpCodes.OpClusterVerifyConfig <$> arbitrary <*> arbitrary <*>
151
          genSet Nothing <*> arbitrary
152
      "OP_CLUSTER_VERIFY_GROUP" ->
153
        OpCodes.OpClusterVerifyGroup <$> genNameNE <*> arbitrary <*>
154
          arbitrary <*> genSet Nothing <*> genSet Nothing <*> arbitrary
155
      "OP_CLUSTER_VERIFY_DISKS" -> pure OpCodes.OpClusterVerifyDisks
156
      "OP_GROUP_VERIFY_DISKS" ->
157
        OpCodes.OpGroupVerifyDisks <$> genNameNE
158
      "OP_CLUSTER_REPAIR_DISK_SIZES" ->
159
        OpCodes.OpClusterRepairDiskSizes <$> genNodeNamesNE
160
      "OP_CLUSTER_CONFIG_QUERY" ->
161
        OpCodes.OpClusterConfigQuery <$> genFieldsNE
162
      "OP_CLUSTER_RENAME" ->
163
        OpCodes.OpClusterRename <$> genNameNE
164
      "OP_CLUSTER_SET_PARAMS" ->
165
        OpCodes.OpClusterSetParams <$> arbitrary <*> emptyMUD <*> emptyMUD <*>
166
          arbitrary <*> genMaybe (listOf1 arbitrary >>= mkNonEmpty) <*>
167
          genMaybe genEmptyContainer <*> emptyMUD <*>
168
          genMaybe genEmptyContainer <*> genMaybe genEmptyContainer <*>
169
          genMaybe genEmptyContainer <*> genMaybe arbitrary <*>
170
          arbitrary <*> arbitrary <*> arbitrary <*>
171
          arbitrary <*> arbitrary <*> arbitrary <*>
172
          emptyMUD <*> emptyMUD <*> arbitrary <*>
173
          arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*>
174
          arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
175
      "OP_CLUSTER_REDIST_CONF" -> pure OpCodes.OpClusterRedistConf
176
      "OP_CLUSTER_ACTIVATE_MASTER_IP" ->
177
        pure OpCodes.OpClusterActivateMasterIp
178
      "OP_CLUSTER_DEACTIVATE_MASTER_IP" ->
179
        pure OpCodes.OpClusterDeactivateMasterIp
180
      "OP_QUERY" ->
181
        OpCodes.OpQuery <$> arbitrary <*> arbitrary <*> arbitrary <*> genFilter
182
      "OP_QUERY_FIELDS" ->
183
        OpCodes.OpQueryFields <$> arbitrary <*> arbitrary
184
      "OP_OOB_COMMAND" ->
185
        OpCodes.OpOobCommand <$> genNodeNamesNE <*> arbitrary <*>
186
          arbitrary <*> arbitrary <*> (arbitrary `suchThat` (>0))
187
      "OP_NODE_REMOVE" -> OpCodes.OpNodeRemove <$> genNodeNameNE
188
      "OP_NODE_ADD" ->
189
        OpCodes.OpNodeAdd <$> genNodeNameNE <*> emptyMUD <*> emptyMUD <*>
190
          genMaybe genName <*> genMaybe genNameNE <*> arbitrary <*>
191
          genMaybe genNameNE <*> arbitrary <*> arbitrary <*> emptyMUD
192
      "OP_NODE_QUERY" ->
193
        OpCodes.OpNodeQuery <$> genFieldsNE <*> genNamesNE <*> arbitrary
194
      "OP_NODE_QUERYVOLS" ->
195
        OpCodes.OpNodeQueryvols <$> arbitrary <*> genNodeNamesNE
196
      "OP_NODE_QUERY_STORAGE" ->
197
        OpCodes.OpNodeQueryStorage <$> arbitrary <*> arbitrary <*>
198
          genNodeNamesNE <*> genNameNE
199
      "OP_NODE_MODIFY_STORAGE" ->
200
        OpCodes.OpNodeModifyStorage <$> genNodeNameNE <*> arbitrary <*>
201
          genNameNE <*> pure emptyJSObject
202
      "OP_REPAIR_NODE_STORAGE" ->
203
        OpCodes.OpRepairNodeStorage <$> genNodeNameNE <*> arbitrary <*>
204
          genNameNE <*> arbitrary
205
      "OP_NODE_SET_PARAMS" ->
206
        OpCodes.OpNodeSetParams <$> genNodeNameNE <*> arbitrary <*>
207
          emptyMUD <*> emptyMUD <*> arbitrary <*> arbitrary <*> arbitrary <*>
208
          arbitrary <*> arbitrary <*> arbitrary <*> genMaybe genNameNE <*>
209
          emptyMUD <*> arbitrary
210
      "OP_NODE_POWERCYCLE" ->
211
        OpCodes.OpNodePowercycle <$> genNodeNameNE <*> arbitrary
212
      "OP_NODE_MIGRATE" ->
213
        OpCodes.OpNodeMigrate <$> genNodeNameNE <*> arbitrary <*>
214
          arbitrary <*> genMaybe genNodeNameNE <*> arbitrary <*>
215
          arbitrary <*> genMaybe genNameNE
216
      "OP_NODE_EVACUATE" ->
217
        OpCodes.OpNodeEvacuate <$> arbitrary <*> genNodeNameNE <*>
218
          genMaybe genNodeNameNE <*> genMaybe genNameNE <*> arbitrary
219
      "OP_INSTANCE_CREATE" ->
220
        OpCodes.OpInstanceCreate <$> genFQDN <*> arbitrary <*>
221
          arbitrary <*> arbitrary <*> arbitrary <*> pure emptyJSObject <*>
222
          arbitrary <*> arbitrary <*> arbitrary <*> genMaybe genNameNE <*>
223
          pure emptyJSObject <*> arbitrary <*> genMaybe genNameNE <*>
224
          arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*>
225
          arbitrary <*> arbitrary <*> pure emptyJSObject <*>
226
          genMaybe genNameNE <*>
227
          genMaybe genNodeNameNE <*> genMaybe genNodeNameNE <*>
228
          genMaybe (pure []) <*> genMaybe genNodeNameNE <*>
229
          arbitrary <*> genMaybe genNodeNameNE <*>
230
          genMaybe genNodeNameNE <*> genMaybe genNameNE <*>
231
          arbitrary <*> arbitrary <*> (genTags >>= mapM mkNonEmpty)
232
      "OP_INSTANCE_MULTI_ALLOC" ->
233
        OpCodes.OpInstanceMultiAlloc <$> genMaybe genNameNE <*> pure [] <*>
234
          arbitrary
235
      "OP_INSTANCE_REINSTALL" ->
236
        OpCodes.OpInstanceReinstall <$> genFQDN <*> arbitrary <*>
237
          genMaybe genNameNE <*> genMaybe (pure emptyJSObject)
238
      "OP_INSTANCE_REMOVE" ->
239
        OpCodes.OpInstanceRemove <$> genFQDN <*> arbitrary <*>
240
          arbitrary <*> arbitrary
241
      "OP_INSTANCE_RENAME" ->
242
        OpCodes.OpInstanceRename <$> genFQDN <*> genNodeNameNE <*>
243
          arbitrary <*> arbitrary
244
      "OP_INSTANCE_STARTUP" ->
245
        OpCodes.OpInstanceStartup <$> genFQDN <*> arbitrary <*> arbitrary <*>
246
          pure emptyJSObject <*> pure emptyJSObject <*>
247
          arbitrary <*> arbitrary
248
      "OP_INSTANCE_SHUTDOWN" ->
249
        OpCodes.OpInstanceShutdown <$> genFQDN <*> arbitrary <*> arbitrary <*>
250
          arbitrary <*> arbitrary
251
      "OP_INSTANCE_REBOOT" ->
252
        OpCodes.OpInstanceReboot <$> genFQDN <*> arbitrary <*>
253
          arbitrary <*> arbitrary
254
      "OP_INSTANCE_MOVE" ->
255
        OpCodes.OpInstanceMove <$> genFQDN <*> arbitrary <*> arbitrary <*>
256
          genNodeNameNE <*> arbitrary
257
      "OP_INSTANCE_CONSOLE" -> OpCodes.OpInstanceConsole <$> genFQDN
258
      "OP_INSTANCE_ACTIVATE_DISKS" ->
259
        OpCodes.OpInstanceActivateDisks <$> genFQDN <*>
260
          arbitrary <*> arbitrary
261
      "OP_INSTANCE_DEACTIVATE_DISKS" ->
262
        OpCodes.OpInstanceDeactivateDisks <$> genFQDN <*> arbitrary
263
      "OP_INSTANCE_RECREATE_DISKS" ->
264
        OpCodes.OpInstanceRecreateDisks <$> genFQDN <*> arbitrary <*>
265
          genNodeNamesNE <*> genMaybe genNameNE
266
      "OP_INSTANCE_QUERY" ->
267
        OpCodes.OpInstanceQuery <$> genFieldsNE <*> genNamesNE <*> arbitrary
268
      "OP_INSTANCE_QUERY_DATA" ->
269
        OpCodes.OpInstanceQueryData <$> arbitrary <*>
270
          genNodeNamesNE <*> arbitrary
271
      "OP_INSTANCE_SET_PARAMS" ->
272
        OpCodes.OpInstanceSetParams <$> genFQDN <*> arbitrary <*>
273
          arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*>
274
          pure emptyJSObject <*> arbitrary <*> pure emptyJSObject <*>
275
          arbitrary <*> genMaybe genNodeNameNE <*> genMaybe genNodeNameNE <*>
276
          genMaybe genNameNE <*> pure emptyJSObject <*> arbitrary <*>
277
          arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
278
      "OP_INSTANCE_GROW_DISK" ->
279
        OpCodes.OpInstanceGrowDisk <$> genFQDN <*> arbitrary <*>
280
          arbitrary <*> arbitrary <*> arbitrary
281
      "OP_INSTANCE_CHANGE_GROUP" ->
282
        OpCodes.OpInstanceChangeGroup <$> genFQDN <*> arbitrary <*>
283
          genMaybe genNameNE <*> genMaybe (resize maxNodes (listOf genNameNE))
284
      "OP_GROUP_ADD" ->
285
        OpCodes.OpGroupAdd <$> genNameNE <*> arbitrary <*>
286
          emptyMUD <*> genMaybe genEmptyContainer <*>
287
          emptyMUD <*> emptyMUD <*> emptyMUD
288
      "OP_GROUP_ASSIGN_NODES" ->
289
        OpCodes.OpGroupAssignNodes <$> genNameNE <*> arbitrary <*>
290
          genNodeNamesNE
291
      "OP_GROUP_QUERY" ->
292
        OpCodes.OpGroupQuery <$> genFieldsNE <*> genNamesNE
293
      "OP_GROUP_SET_PARAMS" ->
294
        OpCodes.OpGroupSetParams <$> genNameNE <*> arbitrary <*>
295
          emptyMUD <*> genMaybe genEmptyContainer <*>
296
          emptyMUD <*> emptyMUD <*> emptyMUD
297
      "OP_GROUP_REMOVE" ->
298
        OpCodes.OpGroupRemove <$> genNameNE
299
      "OP_GROUP_RENAME" ->
300
        OpCodes.OpGroupRename <$> genNameNE <*> genNameNE
301
      "OP_GROUP_EVACUATE" ->
302
        OpCodes.OpGroupEvacuate <$> genNameNE <*> arbitrary <*>
303
          genMaybe genNameNE <*> genMaybe genNamesNE
304
      "OP_OS_DIAGNOSE" ->
305
        OpCodes.OpOsDiagnose <$> genFieldsNE <*> genNamesNE
306
      "OP_EXT_STORAGE_DIAGNOSE" ->
307
        OpCodes.OpOsDiagnose <$> genFieldsNE <*> genNamesNE
308
      "OP_BACKUP_QUERY" ->
309
        OpCodes.OpBackupQuery <$> arbitrary <*> genNodeNamesNE
310
      "OP_BACKUP_PREPARE" ->
311
        OpCodes.OpBackupPrepare <$> genFQDN <*> arbitrary
312
      "OP_BACKUP_EXPORT" ->
313
        OpCodes.OpBackupExport <$> genFQDN <*> arbitrary <*>
314
          arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*>
315
          arbitrary <*> genMaybe (pure []) <*> genMaybe genNameNE
316
      "OP_BACKUP_REMOVE" ->
317
        OpCodes.OpBackupRemove <$> genFQDN
318
      "OP_TEST_ALLOCATOR" ->
319
        OpCodes.OpTestAllocator <$> arbitrary <*> arbitrary <*>
320
          genNameNE <*> pure [] <*> pure [] <*>
321
          arbitrary <*> genMaybe genNameNE <*>
322
          (genTags >>= mapM mkNonEmpty) <*>
323
          arbitrary <*> arbitrary <*> genMaybe genNameNE <*>
324
          arbitrary <*> genMaybe genNodeNamesNE <*> arbitrary <*>
325
          genMaybe genNamesNE <*> arbitrary <*> arbitrary
326
      "OP_TEST_JQUEUE" ->
327
        OpCodes.OpTestJqueue <$> arbitrary <*> arbitrary <*>
328
          resize 20 (listOf genFQDN) <*> arbitrary
329
      "OP_TEST_DUMMY" ->
330
        OpCodes.OpTestDummy <$> pure J.JSNull <*> pure J.JSNull <*>
331
          pure J.JSNull <*> pure J.JSNull
332
      "OP_NETWORK_ADD" ->
333
        OpCodes.OpNetworkAdd <$> genNameNE <*> genIp4Net <*>
334
          genMaybe genIp4Addr <*> pure Nothing <*> pure Nothing <*>
335
          genMaybe genMacPrefix <*> genMaybe (listOf genIp4Addr) <*>
336
          arbitrary <*> (genTags >>= mapM mkNonEmpty)
337
      "OP_NETWORK_REMOVE" ->
338
        OpCodes.OpNetworkRemove <$> genNameNE <*> arbitrary
339
      "OP_NETWORK_SET_PARAMS" ->
340
        OpCodes.OpNetworkSetParams <$> genNameNE <*>
341
          genMaybe genIp4Addr <*> pure Nothing <*> pure Nothing <*>
342
          genMaybe genMacPrefix <*> genMaybe (listOf genIp4Addr) <*>
343
          genMaybe (listOf genIp4Addr)
344
      "OP_NETWORK_CONNECT" ->
345
        OpCodes.OpNetworkConnect <$> genNameNE <*> genNameNE <*>
346
          arbitrary <*> genNameNE <*> arbitrary
347
      "OP_NETWORK_DISCONNECT" ->
348
        OpCodes.OpNetworkDisconnect <$> genNameNE <*> genNameNE
349
      "OP_NETWORK_QUERY" ->
350
        OpCodes.OpNetworkQuery <$> genFieldsNE <*> genNamesNE <*> arbitrary
351
      "OP_INSTANCE_SNAPSHOT" ->
352
        OpCodes.OpInstanceSnapshot <$> genFQDN <*> arbitrary
353
      "OP_RESTRICTED_COMMAND" ->
354
        OpCodes.OpRestrictedCommand <$> arbitrary <*> genNodeNamesNE <*>
355
          genNameNE
356
      _ -> fail $ "Undefined arbitrary for opcode " ++ op_id
357

    
358
-- | Generates one element of a reason trail
359
genReasonElem :: Gen ReasonElem
360
genReasonElem = (,,) <$> genFQDN <*> genFQDN <*> arbitrary
361

    
362
-- | Generates a reason trail
363
genReasonTrail :: Gen ReasonTrail
364
genReasonTrail = do
365
  size <- choose (0, 10)
366
  vectorOf size genReasonElem
367

    
368
instance Arbitrary OpCodes.CommonOpParams where
369
  arbitrary = OpCodes.CommonOpParams <$> arbitrary <*> arbitrary <*>
370
                arbitrary <*> resize 5 arbitrary <*> genMaybe genName <*>
371
                genReasonTrail
372

    
373
-- * Helper functions
374

    
375
-- | Empty JSObject.
376
emptyJSObject :: J.JSObject J.JSValue
377
emptyJSObject = J.toJSObject []
378

    
379
-- | Empty maybe unchecked dictionary.
380
emptyMUD :: Gen (Maybe (J.JSObject J.JSValue))
381
emptyMUD = genMaybe $ pure emptyJSObject
382

    
383
-- | Generates an empty container.
384
genEmptyContainer :: (Ord a) => Gen (GenericContainer a b)
385
genEmptyContainer = pure . GenericContainer $ Map.fromList []
386

    
387
-- | Generates list of disk indices.
388
genDiskIndices :: Gen [DiskIndex]
389
genDiskIndices = do
390
  cnt <- choose (0, C.maxDisks)
391
  genUniquesList cnt arbitrary
392

    
393
-- | Generates a list of node names.
394
genNodeNames :: Gen [String]
395
genNodeNames = resize maxNodes (listOf genFQDN)
396

    
397
-- | Generates a list of node names in non-empty string type.
398
genNodeNamesNE :: Gen [NonEmptyString]
399
genNodeNamesNE = genNodeNames >>= mapM mkNonEmpty
400

    
401
-- | Gets a node name in non-empty type.
402
genNodeNameNE :: Gen NonEmptyString
403
genNodeNameNE = genFQDN >>= mkNonEmpty
404

    
405
-- | Gets a name (non-fqdn) in non-empty type.
406
genNameNE :: Gen NonEmptyString
407
genNameNE = genName >>= mkNonEmpty
408

    
409
-- | Gets a list of names (non-fqdn) in non-empty type.
410
genNamesNE :: Gen [NonEmptyString]
411
genNamesNE = resize maxNodes (listOf genNameNE)
412

    
413
-- | Returns a list of non-empty fields.
414
genFieldsNE :: Gen [NonEmptyString]
415
genFieldsNE = genFields >>= mapM mkNonEmpty
416

    
417
-- | Generate a 3-byte MAC prefix.
418
genMacPrefix :: Gen NonEmptyString
419
genMacPrefix = do
420
  octets <- vectorOf 3 $ choose (0::Int, 255)
421
  mkNonEmpty . intercalate ":" $ map (printf "%02x") octets
422

    
423
-- | Arbitrary instance for MetaOpCode, defined here due to TH ordering.
424
$(genArbitrary ''OpCodes.MetaOpCode)
425

    
426
-- | Small helper to check for a failed JSON deserialisation
427
isJsonError :: J.Result a -> Bool
428
isJsonError (J.Error _) = True
429
isJsonError _           = False
430

    
431
-- * Test cases
432

    
433
-- | Check that opcode serialization is idempotent.
434
prop_serialization :: OpCodes.OpCode -> Property
435
prop_serialization = testSerialisation
436

    
437
-- | Check that Python and Haskell defined the same opcode list.
438
case_AllDefined :: HUnit.Assertion
439
case_AllDefined = do
440
  let py_ops = sort C.opcodesOpIds
441
      hs_ops = sort OpCodes.allOpIDs
442
      extra_py = py_ops \\ hs_ops
443
      extra_hs = hs_ops \\ py_ops
444
  HUnit.assertBool ("Missing OpCodes from the Haskell code:\n" ++
445
                    unlines extra_py) (null extra_py)
446
  HUnit.assertBool ("Extra OpCodes in the Haskell code code:\n" ++
447
                    unlines extra_hs) (null extra_hs)
448

    
449
-- | Custom HUnit test case that forks a Python process and checks
450
-- correspondence between Haskell-generated OpCodes and their Python
451
-- decoded, validated and re-encoded version.
452
--
453
-- Note that we have a strange beast here: since launching Python is
454
-- expensive, we don't do this via a usual QuickProperty, since that's
455
-- slow (I've tested it, and it's indeed quite slow). Rather, we use a
456
-- single HUnit assertion, and in it we manually use QuickCheck to
457
-- generate 500 opcodes times the number of defined opcodes, which
458
-- then we pass in bulk to Python. The drawbacks to this method are
459
-- two fold: we cannot control the number of generated opcodes, since
460
-- HUnit assertions don't get access to the test options, and for the
461
-- same reason we can't run a repeatable seed. We should probably find
462
-- a better way to do this, for example by having a
463
-- separately-launched Python process (if not running the tests would
464
-- be skipped).
465
case_py_compat_types :: HUnit.Assertion
466
case_py_compat_types = do
467
  let num_opcodes = length OpCodes.allOpIDs * 100
468
  opcodes <- genSample (vectorOf num_opcodes
469
                                   (arbitrary::Gen OpCodes.MetaOpCode))
470
  let with_sum = map (\o -> (OpCodes.opSummary $
471
                             OpCodes.metaOpCode o, o)) opcodes
472
      serialized = J.encode opcodes
473
  -- check for non-ASCII fields, usually due to 'arbitrary :: String'
474
  mapM_ (\op -> when (any (not . isAscii) (J.encode op)) .
475
                HUnit.assertFailure $
476
                  "OpCode has non-ASCII fields: " ++ show op
477
        ) opcodes
478
  py_stdout <-
479
     runPython "from ganeti import opcodes\n\
480
               \import sys\n\
481
               \from ganeti import serializer\n\
482
               \op_data = serializer.Load(sys.stdin.read())\n\
483
               \decoded = [opcodes.OpCode.LoadOpCode(o) for o in op_data]\n\
484
               \for op in decoded:\n\
485
               \  op.Validate(True)\n\
486
               \encoded = [(op.Summary(), op.__getstate__())\n\
487
               \           for op in decoded]\n\
488
               \print serializer.Dump(encoded)" serialized
489
     >>= checkPythonResult
490
  let deserialised =
491
        J.decode py_stdout::J.Result [(String, OpCodes.MetaOpCode)]
492
  decoded <- case deserialised of
493
               J.Ok ops -> return ops
494
               J.Error msg ->
495
                 HUnit.assertFailure ("Unable to decode opcodes: " ++ msg)
496
                 -- this already raised an expection, but we need it
497
                 -- for proper types
498
                 >> fail "Unable to decode opcodes"
499
  HUnit.assertEqual "Mismatch in number of returned opcodes"
500
    (length decoded) (length with_sum)
501
  mapM_ (uncurry (HUnit.assertEqual "Different result after encoding/decoding")
502
        ) $ zip decoded with_sum
503

    
504
-- | Custom HUnit test case that forks a Python process and checks
505
-- correspondence between Haskell OpCodes fields and their Python
506
-- equivalent.
507
case_py_compat_fields :: HUnit.Assertion
508
case_py_compat_fields = do
509
  let hs_fields = sort $ map (\op_id -> (op_id, OpCodes.allOpFields op_id))
510
                         OpCodes.allOpIDs
511
  py_stdout <-
512
     runPython "from ganeti import opcodes\n\
513
               \import sys\n\
514
               \from ganeti import serializer\n\
515
               \fields = [(k, sorted([p[0] for p in v.OP_PARAMS]))\n\
516
               \           for k, v in opcodes.OP_MAPPING.items()]\n\
517
               \print serializer.Dump(fields)" ""
518
     >>= checkPythonResult
519
  let deserialised = J.decode py_stdout::J.Result [(String, [String])]
520
  py_fields <- case deserialised of
521
                 J.Ok v -> return $ sort v
522
                 J.Error msg ->
523
                   HUnit.assertFailure ("Unable to decode op fields: " ++ msg)
524
                   -- this already raised an expection, but we need it
525
                   -- for proper types
526
                   >> fail "Unable to decode op fields"
527
  HUnit.assertEqual "Mismatch in number of returned opcodes"
528
    (length hs_fields) (length py_fields)
529
  HUnit.assertEqual "Mismatch in defined OP_IDs"
530
    (map fst hs_fields) (map fst py_fields)
531
  mapM_ (\((py_id, py_flds), (hs_id, hs_flds)) -> do
532
           HUnit.assertEqual "Mismatch in OP_ID" py_id hs_id
533
           HUnit.assertEqual ("Mismatch in fields for " ++ hs_id)
534
             py_flds hs_flds
535
        ) $ zip py_fields hs_fields
536

    
537
-- | Checks that setOpComment works correctly.
538
prop_setOpComment :: OpCodes.MetaOpCode -> String -> Property
539
prop_setOpComment op comment =
540
  let (OpCodes.MetaOpCode common _) = OpCodes.setOpComment comment op
541
  in OpCodes.opComment common ==? Just comment
542

    
543
-- | Tests wrong tag object building (cluster takes only jsnull, the
544
-- other take a string, so we test the opposites).
545
case_TagObject_fail :: Assertion
546
case_TagObject_fail =
547
  mapM_ (\(t, j) -> assertEqual (show t ++ "/" ++ J.encode j) Nothing $
548
                    tagObjectFrom t j)
549
    [ (TagTypeCluster,  J.showJSON "abc")
550
    , (TagTypeInstance, J.JSNull)
551
    , (TagTypeNode,     J.JSNull)
552
    , (TagTypeGroup,    J.JSNull)
553
    , (TagTypeNetwork,  J.JSNull)
554
    ]
555

    
556
-- | Tests wrong (negative) disk index.
557
prop_mkDiskIndex_fail :: QuickCheck.Positive Int -> Property
558
prop_mkDiskIndex_fail (Positive i) =
559
  case mkDiskIndex (negate i) of
560
    Bad msg -> printTestCase "error message " $
561
               "Invalid value" `isPrefixOf` msg
562
    Ok v -> failTest $ "Succeeded to build disk index '" ++ show v ++
563
                       "' from negative value " ++ show (negate i)
564

    
565
-- | Tests a few invalid 'readRecreateDisks' cases.
566
case_readRecreateDisks_fail :: Assertion
567
case_readRecreateDisks_fail = do
568
  assertBool "null" $
569
    isJsonError (J.readJSON J.JSNull::J.Result RecreateDisksInfo)
570
  assertBool "string" $
571
    isJsonError (J.readJSON (J.showJSON "abc")::J.Result RecreateDisksInfo)
572

    
573
-- | Tests a few invalid 'readDdmOldChanges' cases.
574
case_readDdmOldChanges_fail :: Assertion
575
case_readDdmOldChanges_fail = do
576
  assertBool "null" $
577
    isJsonError (J.readJSON J.JSNull::J.Result DdmOldChanges)
578
  assertBool "string" $
579
    isJsonError (J.readJSON (J.showJSON "abc")::J.Result DdmOldChanges)
580

    
581
-- | Tests a few invalid 'readExportTarget' cases.
582
case_readExportTarget_fail :: Assertion
583
case_readExportTarget_fail = do
584
  assertBool "null" $
585
    isJsonError (J.readJSON J.JSNull::J.Result ExportTarget)
586
  assertBool "int" $
587
    isJsonError (J.readJSON (J.showJSON (5::Int))::J.Result ExportTarget)
588

    
589
testSuite "OpCodes"
590
            [ 'prop_serialization
591
            , 'case_AllDefined
592
            , 'case_py_compat_types
593
            , 'case_py_compat_fields
594
            , 'prop_setOpComment
595
            , 'case_TagObject_fail
596
            , 'prop_mkDiskIndex_fail
597
            , 'case_readRecreateDisks_fail
598
            , 'case_readDdmOldChanges_fail
599
            , 'case_readExportTarget_fail
600
            ]