Statistics
| Branch: | Tag: | Revision:

root / htest / Test / Ganeti / OpCodes.hs @ ad1c1e41

History | View | Annotate | Download (22.1 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 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 qualified Test.HUnit as HUnit
35
import Test.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 qualified Ganeti.Constants as C
51
import qualified Ganeti.OpCodes as OpCodes
52
import Ganeti.Types
53
import Ganeti.OpParams
54
import Ganeti.JSON
55

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

    
58
-- * Arbitrary instances
59

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

    
67
$(genArbitrary ''OpCodes.ReplaceDisksMode)
68

    
69
$(genArbitrary ''DiskAccess)
70

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

    
74
instance Arbitrary INicParams where
75
  arbitrary = INicParams <$> genMaybe genNameNE <*> genMaybe genName <*>
76
              genMaybe genNameNE <*> genMaybe genNameNE
77

    
78
instance Arbitrary IDiskParams where
79
  arbitrary = IDiskParams <$> arbitrary <*> arbitrary <*>
80
              genMaybe genNameNE <*> genMaybe genNameNE <*>
81
              genMaybe genNameNE
82

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

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

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

    
100
instance Arbitrary ExportTarget where
101
  arbitrary = oneof [ ExportTargetLocal <$> genNodeNameNE
102
                    , ExportTargetRemote <$> pure []
103
                    ]
104

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

    
341
instance Arbitrary OpCodes.CommonOpParams where
342
  arbitrary = OpCodes.CommonOpParams <$> arbitrary <*> arbitrary <*>
343
                arbitrary <*> resize 5 arbitrary <*> genMaybe genName
344

    
345
-- * Helper functions
346

    
347
-- | Empty JSObject.
348
emptyJSObject :: J.JSObject J.JSValue
349
emptyJSObject = J.toJSObject []
350

    
351
-- | Empty maybe unchecked dictionary.
352
emptyMUD :: Gen (Maybe (J.JSObject J.JSValue))
353
emptyMUD = genMaybe $ pure emptyJSObject
354

    
355
-- | Generates an empty container.
356
genEmptyContainer :: (Ord a) => Gen (GenericContainer a b)
357
genEmptyContainer = pure . GenericContainer $ Map.fromList []
358

    
359
-- | Generates list of disk indices.
360
genDiskIndices :: Gen [DiskIndex]
361
genDiskIndices = do
362
  cnt <- choose (0, C.maxDisks)
363
  genUniquesList cnt
364

    
365
-- | Generates a list of node names.
366
genNodeNames :: Gen [String]
367
genNodeNames = resize maxNodes (listOf genFQDN)
368

    
369
-- | Generates a list of node names in non-empty string type.
370
genNodeNamesNE :: Gen [NonEmptyString]
371
genNodeNamesNE = genNodeNames >>= mapM mkNonEmpty
372

    
373
-- | Gets a node name in non-empty type.
374
genNodeNameNE :: Gen NonEmptyString
375
genNodeNameNE = genFQDN >>= mkNonEmpty
376

    
377
-- | Gets a name (non-fqdn) in non-empty type.
378
genNameNE :: Gen NonEmptyString
379
genNameNE = genName >>= mkNonEmpty
380

    
381
-- | Gets a list of names (non-fqdn) in non-empty type.
382
genNamesNE :: Gen [NonEmptyString]
383
genNamesNE = resize maxNodes (listOf genNameNE)
384

    
385
-- | Returns a list of non-empty fields.
386
genFieldsNE :: Gen [NonEmptyString]
387
genFieldsNE = genFields >>= mapM mkNonEmpty
388

    
389
-- | Generate an arbitrary IPv4 address in textual form.
390
genIp4Addr :: Gen NonEmptyString
391
genIp4Addr = do
392
  a <- choose (1::Int, 255)
393
  b <- choose (0::Int, 255)
394
  c <- choose (0::Int, 255)
395
  d <- choose (0::Int, 255)
396
  mkNonEmpty $ intercalate "." (map show [a, b, c, d])
397

    
398
-- | Generate an arbitrary IPv4 network address in textual form.
399
genIp4Net :: Gen NonEmptyString
400
genIp4Net = do
401
  netmask <- choose (8::Int, 30)
402
  ip <- genIp4Addr
403
  mkNonEmpty $ fromNonEmpty ip ++ "/" ++ show netmask
404

    
405
-- | Generate a 3-byte MAC prefix.
406
genMacPrefix :: Gen NonEmptyString
407
genMacPrefix = do
408
  octets <- vectorOf 3 $ choose (0::Int, 255)
409
  mkNonEmpty . intercalate ":" $ map (printf "%02x") octets
410

    
411
-- | Arbitrary instance for MetaOpCode, defined here due to TH ordering.
412
$(genArbitrary ''OpCodes.MetaOpCode)
413

    
414
-- * Test cases
415

    
416
-- | Check that opcode serialization is idempotent.
417
prop_serialization :: OpCodes.OpCode -> Property
418
prop_serialization = testSerialisation
419

    
420
-- | Check that Python and Haskell defined the same opcode list.
421
case_AllDefined :: HUnit.Assertion
422
case_AllDefined = do
423
  let py_ops = sort C.opcodesOpIds
424
      hs_ops = sort OpCodes.allOpIDs
425
      extra_py = py_ops \\ hs_ops
426
      extra_hs = hs_ops \\ py_ops
427
  HUnit.assertBool ("Missing OpCodes from the Haskell code:\n" ++
428
                    unlines extra_py) (null extra_py)
429
  HUnit.assertBool ("Extra OpCodes in the Haskell code code:\n" ++
430
                    unlines extra_hs) (null extra_hs)
431

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

    
488
-- | Custom HUnit test case that forks a Python process and checks
489
-- correspondence between Haskell OpCodes fields and their Python
490
-- equivalent.
491
case_py_compat_fields :: HUnit.Assertion
492
case_py_compat_fields = do
493
  let hs_fields = sort $ map (\op_id -> (op_id, OpCodes.allOpFields op_id))
494
                         OpCodes.allOpIDs
495
  py_stdout <-
496
     runPython "from ganeti import opcodes\n\
497
               \import sys\n\
498
               \from ganeti import serializer\n\
499
               \fields = [(k, sorted([p[0] for p in v.OP_PARAMS]))\n\
500
               \           for k, v in opcodes.OP_MAPPING.items()]\n\
501
               \print serializer.Dump(fields)" ""
502
     >>= checkPythonResult
503
  let deserialised = J.decode py_stdout::J.Result [(String, [String])]
504
  py_fields <- case deserialised of
505
                 J.Ok v -> return $ sort v
506
                 J.Error msg ->
507
                   HUnit.assertFailure ("Unable to decode op fields: " ++ msg)
508
                   -- this already raised an expection, but we need it
509
                   -- for proper types
510
                   >> fail "Unable to decode op fields"
511
  HUnit.assertEqual "Mismatch in number of returned opcodes"
512
    (length hs_fields) (length py_fields)
513
  HUnit.assertEqual "Mismatch in defined OP_IDs"
514
    (map fst hs_fields) (map fst py_fields)
515
  mapM_ (\((py_id, py_flds), (hs_id, hs_flds)) -> do
516
           HUnit.assertEqual "Mismatch in OP_ID" py_id hs_id
517
           HUnit.assertEqual ("Mismatch in fields for " ++ hs_id)
518
             py_flds hs_flds
519
        ) $ zip py_fields hs_fields
520

    
521
-- | Checks that setOpComment works correctly.
522
prop_setOpComment :: OpCodes.MetaOpCode -> String -> Property
523
prop_setOpComment op comment =
524
  let (OpCodes.MetaOpCode common _) = OpCodes.setOpComment comment op
525
  in OpCodes.opComment common ==? Just comment
526

    
527
testSuite "OpCodes"
528
            [ 'prop_serialization
529
            , 'case_AllDefined
530
            , 'case_py_compat_types
531
            , 'case_py_compat_fields
532
            , 'prop_setOpComment
533
            ]