Statistics
| Branch: | Tag: | Revision:

root / test / hs / Test / Ganeti / Types.hs @ 44c15fa3

History | View | Annotate | Download (14.7 kB)

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

    
4
{-| Unittests for 'Ganeti.Types'.
5

    
6
-}
7

    
8
{-
9

    
10
Copyright (C) 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.Types
30
  ( testTypes
31
  , AllocPolicy(..)
32
  , DiskTemplate(..)
33
  , allDiskTemplates
34
  , InstanceStatus(..)
35
  , NonEmpty(..)
36
  , Hypervisor(..)
37
  , JobId(..)
38
  ) where
39

    
40
import Data.List (delete, sort)
41
import Test.QuickCheck as QuickCheck hiding (Result)
42
import Test.HUnit
43
import qualified Text.JSON as J
44

    
45
import Test.Ganeti.TestHelper
46
import Test.Ganeti.TestCommon
47

    
48
import Ganeti.BasicTypes
49
import qualified Ganeti.Constants as C
50
import Ganeti.Types as Types
51
import Ganeti.JSON
52

    
53
{-# ANN module "HLint: ignore Use camelCase" #-}
54

    
55
-- * Arbitrary instance
56

    
57
instance (Arbitrary a, Ord a, Num a, Show a) =>
58
  Arbitrary (Types.Positive a) where
59
  arbitrary = do
60
    (QuickCheck.Positive i) <- arbitrary
61
    Types.mkPositive i
62

    
63
instance (Arbitrary a, Ord a, Num a, Show a) =>
64
  Arbitrary (Types.NonNegative a) where
65
  arbitrary = do
66
    (QuickCheck.NonNegative i) <- arbitrary
67
    Types.mkNonNegative i
68

    
69
instance (Arbitrary a, Ord a, Num a, Show a) =>
70
  Arbitrary (Types.Negative a) where
71
  arbitrary = do
72
    (QuickCheck.Positive i) <- arbitrary
73
    Types.mkNegative $ negate i
74

    
75
instance (Arbitrary a) => Arbitrary (Types.NonEmpty a) where
76
  arbitrary = do
77
    QuickCheck.NonEmpty lst <- arbitrary
78
    Types.mkNonEmpty lst
79

    
80
$(genArbitrary ''AllocPolicy)
81

    
82
-- | Valid disk templates (depending on configure options).
83
allDiskTemplates :: [DiskTemplate]
84
allDiskTemplates =
85
  let all_vals = [minBound..maxBound]::[DiskTemplate]
86
      sel1 = if C.enableSharedFileStorage
87
               then all_vals
88
               else delete DTSharedFile all_vals
89
  in sel1
90

    
91
-- | Custom 'Arbitrary' instance for 'DiskTemplate', which needs to
92
-- handle the case of file storage being disabled at configure time.
93
instance Arbitrary DiskTemplate where
94
  arbitrary = elements allDiskTemplates
95

    
96
$(genArbitrary ''InstanceStatus)
97

    
98
$(genArbitrary ''MigrationMode)
99

    
100
$(genArbitrary ''VerifyOptionalChecks)
101

    
102
$(genArbitrary ''DdmSimple)
103

    
104
$(genArbitrary ''DdmFull)
105

    
106
$(genArbitrary ''CVErrorCode)
107

    
108
$(genArbitrary ''Hypervisor)
109

    
110
$(genArbitrary ''TagKind)
111

    
112
$(genArbitrary ''OobCommand)
113

    
114
-- | Valid storage types.
115
allStorageTypes :: [StorageType]
116
allStorageTypes = [minBound..maxBound]::[StorageType]
117

    
118
-- | Custom 'Arbitrary' instance for 'StorageType', which needs to
119
-- handle the case of file storage being disabled at configure time.
120
instance Arbitrary StorageType where
121
  arbitrary = elements allStorageTypes
122

    
123
$(genArbitrary ''NodeEvacMode)
124

    
125
$(genArbitrary ''FileDriver)
126

    
127
$(genArbitrary ''InstCreateMode)
128

    
129
$(genArbitrary ''RebootType)
130

    
131
$(genArbitrary ''ExportMode)
132

    
133
$(genArbitrary ''IAllocatorTestDir)
134

    
135
$(genArbitrary ''IAllocatorMode)
136

    
137
$(genArbitrary ''NICMode)
138

    
139
$(genArbitrary ''JobStatus)
140

    
141
$(genArbitrary ''FinalizedJobStatus)
142

    
143
instance Arbitrary JobId where
144
  arbitrary = do
145
    (Positive i) <- arbitrary
146
    makeJobId i
147

    
148
$(genArbitrary ''JobIdDep)
149

    
150
$(genArbitrary ''JobDependency)
151

    
152
$(genArbitrary ''OpSubmitPriority)
153

    
154
$(genArbitrary ''OpStatus)
155

    
156
$(genArbitrary ''ELogType)
157

    
158
-- * Properties
159

    
160
prop_AllocPolicy_serialisation :: AllocPolicy -> Property
161
prop_AllocPolicy_serialisation = testSerialisation
162

    
163
-- | Test 'AllocPolicy' ordering is as expected.
164
case_AllocPolicy_order :: Assertion
165
case_AllocPolicy_order =
166
  assertEqual "sort order" [ Types.AllocPreferred
167
                           , Types.AllocLastResort
168
                           , Types.AllocUnallocable
169
                           ] [minBound..maxBound]
170

    
171
prop_DiskTemplate_serialisation :: DiskTemplate -> Property
172
prop_DiskTemplate_serialisation = testSerialisation
173

    
174
prop_InstanceStatus_serialisation :: InstanceStatus -> Property
175
prop_InstanceStatus_serialisation = testSerialisation
176

    
177
-- | Tests building non-negative numbers.
178
prop_NonNeg_pass :: QuickCheck.NonNegative Int -> Property
179
prop_NonNeg_pass (QuickCheck.NonNegative i) =
180
  case mkNonNegative i of
181
    Bad msg -> failTest $ "Fail to build non-negative: " ++ msg
182
    Ok nn -> fromNonNegative nn ==? i
183

    
184
-- | Tests building non-negative numbers.
185
prop_NonNeg_fail :: QuickCheck.Positive Int -> Property
186
prop_NonNeg_fail (QuickCheck.Positive i) =
187
  case mkNonNegative (negate i)::Result (Types.NonNegative Int) of
188
    Bad _ -> passTest
189
    Ok nn -> failTest $ "Built non-negative number '" ++ show nn ++
190
             "' from negative value " ++ show i
191

    
192
-- | Tests building positive numbers.
193
prop_Positive_pass :: QuickCheck.Positive Int -> Property
194
prop_Positive_pass (QuickCheck.Positive i) =
195
  case mkPositive i of
196
    Bad msg -> failTest $ "Fail to build positive: " ++ msg
197
    Ok nn -> fromPositive nn ==? i
198

    
199
-- | Tests building positive numbers.
200
prop_Positive_fail :: QuickCheck.NonNegative Int -> Property
201
prop_Positive_fail (QuickCheck.NonNegative i) =
202
  case mkPositive (negate i)::Result (Types.Positive Int) of
203
    Bad _ -> passTest
204
    Ok nn -> failTest $ "Built positive number '" ++ show nn ++
205
             "' from negative or zero value " ++ show i
206

    
207
-- | Tests building negative numbers.
208
prop_Neg_pass :: QuickCheck.Positive Int -> Property
209
prop_Neg_pass (QuickCheck.Positive i) =
210
  case mkNegative i' of
211
    Bad msg -> failTest $ "Fail to build negative: " ++ msg
212
    Ok nn -> fromNegative nn ==? i'
213
  where i' = negate i
214

    
215
-- | Tests building negative numbers.
216
prop_Neg_fail :: QuickCheck.NonNegative Int -> Property
217
prop_Neg_fail (QuickCheck.NonNegative i) =
218
  case mkNegative i::Result (Types.Negative Int) of
219
    Bad _ -> passTest
220
    Ok nn -> failTest $ "Built negative number '" ++ show nn ++
221
             "' from non-negative value " ++ show i
222

    
223
-- | Tests building non-empty lists.
224
prop_NonEmpty_pass :: QuickCheck.NonEmptyList String -> Property
225
prop_NonEmpty_pass (QuickCheck.NonEmpty xs) =
226
  case mkNonEmpty xs of
227
    Bad msg -> failTest $ "Fail to build non-empty list: " ++ msg
228
    Ok nn -> fromNonEmpty nn ==? xs
229

    
230
-- | Tests building positive numbers.
231
case_NonEmpty_fail :: Assertion
232
case_NonEmpty_fail =
233
  assertEqual "building non-empty list from an empty list"
234
    (Bad "Received empty value for non-empty list") (mkNonEmpty ([]::[Int]))
235

    
236
-- | Tests migration mode serialisation.
237
prop_MigrationMode_serialisation :: MigrationMode -> Property
238
prop_MigrationMode_serialisation = testSerialisation
239

    
240
-- | Tests verify optional checks serialisation.
241
prop_VerifyOptionalChecks_serialisation :: VerifyOptionalChecks -> Property
242
prop_VerifyOptionalChecks_serialisation = testSerialisation
243

    
244
-- | Tests 'DdmSimple' serialisation.
245
prop_DdmSimple_serialisation :: DdmSimple -> Property
246
prop_DdmSimple_serialisation = testSerialisation
247

    
248
-- | Tests 'DdmFull' serialisation.
249
prop_DdmFull_serialisation :: DdmFull -> Property
250
prop_DdmFull_serialisation = testSerialisation
251

    
252
-- | Tests 'CVErrorCode' serialisation.
253
prop_CVErrorCode_serialisation :: CVErrorCode -> Property
254
prop_CVErrorCode_serialisation = testSerialisation
255

    
256
-- | Tests equivalence with Python, based on Constants.hs code.
257
case_CVErrorCode_pyequiv :: Assertion
258
case_CVErrorCode_pyequiv = do
259
  let all_py_codes = sort C.cvAllEcodesStrings
260
      all_hs_codes = sort $ map Types.cVErrorCodeToRaw [minBound..maxBound]
261
  assertEqual "for CVErrorCode equivalence" all_py_codes all_hs_codes
262

    
263
-- | Test 'Hypervisor' serialisation.
264
prop_Hypervisor_serialisation :: Hypervisor -> Property
265
prop_Hypervisor_serialisation = testSerialisation
266

    
267
-- | Test 'OobCommand' serialisation.
268
prop_OobCommand_serialisation :: OobCommand -> Property
269
prop_OobCommand_serialisation = testSerialisation
270

    
271
-- | Test 'StorageType' serialisation.
272
prop_StorageType_serialisation :: StorageType -> Property
273
prop_StorageType_serialisation = testSerialisation
274

    
275
-- | Test 'NodeEvacMode' serialisation.
276
prop_NodeEvacMode_serialisation :: NodeEvacMode -> Property
277
prop_NodeEvacMode_serialisation = testSerialisation
278

    
279
-- | Test 'FileDriver' serialisation.
280
prop_FileDriver_serialisation :: FileDriver -> Property
281
prop_FileDriver_serialisation = testSerialisation
282

    
283
-- | Test 'InstCreate' serialisation.
284
prop_InstCreateMode_serialisation :: InstCreateMode -> Property
285
prop_InstCreateMode_serialisation = testSerialisation
286

    
287
-- | Test 'RebootType' serialisation.
288
prop_RebootType_serialisation :: RebootType -> Property
289
prop_RebootType_serialisation = testSerialisation
290

    
291
-- | Test 'ExportMode' serialisation.
292
prop_ExportMode_serialisation :: ExportMode -> Property
293
prop_ExportMode_serialisation = testSerialisation
294

    
295
-- | Test 'IAllocatorTestDir' serialisation.
296
prop_IAllocatorTestDir_serialisation :: IAllocatorTestDir -> Property
297
prop_IAllocatorTestDir_serialisation = testSerialisation
298

    
299
-- | Test 'IAllocatorMode' serialisation.
300
prop_IAllocatorMode_serialisation :: IAllocatorMode -> Property
301
prop_IAllocatorMode_serialisation = testSerialisation
302

    
303
-- | Tests equivalence with Python, based on Constants.hs code.
304
case_IAllocatorMode_pyequiv :: Assertion
305
case_IAllocatorMode_pyequiv = do
306
  let all_py_codes = sort C.validIallocatorModes
307
      all_hs_codes = sort $ map Types.iAllocatorModeToRaw [minBound..maxBound]
308
  assertEqual "for IAllocatorMode equivalence" all_py_codes all_hs_codes
309

    
310
-- | Test 'NICMode' serialisation.
311
prop_NICMode_serialisation :: NICMode -> Property
312
prop_NICMode_serialisation = testSerialisation
313

    
314
-- | Test 'OpStatus' serialisation.
315
prop_OpStatus_serialization :: OpStatus -> Property
316
prop_OpStatus_serialization = testSerialisation
317

    
318
-- | Test 'JobStatus' serialisation.
319
prop_JobStatus_serialization :: JobStatus -> Property
320
prop_JobStatus_serialization = testSerialisation
321

    
322
-- | Test 'JobStatus' ordering is as expected.
323
case_JobStatus_order :: Assertion
324
case_JobStatus_order =
325
  assertEqual "sort order" [ Types.JOB_STATUS_QUEUED
326
                           , Types.JOB_STATUS_WAITING
327
                           , Types.JOB_STATUS_CANCELING
328
                           , Types.JOB_STATUS_RUNNING
329
                           , Types.JOB_STATUS_CANCELED
330
                           , Types.JOB_STATUS_SUCCESS
331
                           , Types.JOB_STATUS_ERROR
332
                           ] [minBound..maxBound]
333

    
334
-- | Tests equivalence with Python, based on Constants.hs code.
335
case_NICMode_pyequiv :: Assertion
336
case_NICMode_pyequiv = do
337
  let all_py_codes = sort C.nicValidModes
338
      all_hs_codes = sort $ map Types.nICModeToRaw [minBound..maxBound]
339
  assertEqual "for NICMode equivalence" all_py_codes all_hs_codes
340

    
341
-- | Test 'FinalizedJobStatus' serialisation.
342
prop_FinalizedJobStatus_serialisation :: FinalizedJobStatus -> Property
343
prop_FinalizedJobStatus_serialisation = testSerialisation
344

    
345
-- | Tests equivalence with Python, based on Constants.hs code.
346
case_FinalizedJobStatus_pyequiv :: Assertion
347
case_FinalizedJobStatus_pyequiv = do
348
  let all_py_codes = sort C.jobsFinalized
349
      all_hs_codes = sort $ map Types.finalizedJobStatusToRaw
350
                            [minBound..maxBound]
351
  assertEqual "for FinalizedJobStatus equivalence" all_py_codes all_hs_codes
352

    
353
-- | Tests JobId serialisation (both from string and ints).
354
prop_JobId_serialisation :: JobId -> Property
355
prop_JobId_serialisation jid =
356
  conjoin [ testSerialisation jid
357
          , (J.readJSON . J.showJSON . show $ fromJobId jid) ==? J.Ok jid
358
          , case (fromJVal . J.showJSON . negate $
359
                  fromJobId jid)::Result JobId of
360
              Bad _ -> passTest
361
              Ok jid' -> failTest $ "Parsed negative job id as id " ++
362
                         show (fromJobId jid')
363
          ]
364

    
365
-- | Tests that fractional job IDs are not accepted.
366
prop_JobId_fractional :: Property
367
prop_JobId_fractional =
368
  forAll (arbitrary `suchThat`
369
          (\d -> fromIntegral (truncate d::Int) /= d)) $ \d ->
370
  case J.readJSON (J.showJSON (d::Double)) of
371
    J.Error _ -> passTest
372
    J.Ok jid -> failTest $ "Parsed fractional value " ++ show d ++
373
                " as job id " ++ show (fromJobId jid)
374

    
375
-- | Tests that a job ID is not parseable from \"bad\" JSON values.
376
case_JobId_BadTypes :: Assertion
377
case_JobId_BadTypes = do
378
  let helper jsval = case J.readJSON jsval of
379
                       J.Error _ -> return ()
380
                       J.Ok jid -> assertFailure $ "Parsed " ++ show jsval
381
                                   ++ " as job id " ++ show (fromJobId jid)
382
  helper J.JSNull
383
  helper (J.JSBool True)
384
  helper (J.JSBool False)
385
  helper (J.JSArray [])
386

    
387
-- | Test 'JobDependency' serialisation.
388
prop_JobDependency_serialisation :: JobDependency -> Property
389
prop_JobDependency_serialisation = testSerialisation
390

    
391
-- | Test 'OpSubmitPriority' serialisation.
392
prop_OpSubmitPriority_serialisation :: OpSubmitPriority -> Property
393
prop_OpSubmitPriority_serialisation = testSerialisation
394

    
395
-- | Tests string formatting for 'OpSubmitPriority'.
396
prop_OpSubmitPriority_string :: OpSubmitPriority -> Property
397
prop_OpSubmitPriority_string prio =
398
  parseSubmitPriority (fmtSubmitPriority prio) ==? Just prio
399

    
400
-- | Test 'ELogType' serialisation.
401
prop_ELogType_serialisation :: ELogType -> Property
402
prop_ELogType_serialisation = testSerialisation
403

    
404
testSuite "Types"
405
  [ 'prop_AllocPolicy_serialisation
406
  , 'case_AllocPolicy_order
407
  , 'prop_DiskTemplate_serialisation
408
  , 'prop_InstanceStatus_serialisation
409
  , 'prop_NonNeg_pass
410
  , 'prop_NonNeg_fail
411
  , 'prop_Positive_pass
412
  , 'prop_Positive_fail
413
  , 'prop_Neg_pass
414
  , 'prop_Neg_fail
415
  , 'prop_NonEmpty_pass
416
  , 'case_NonEmpty_fail
417
  , 'prop_MigrationMode_serialisation
418
  , 'prop_VerifyOptionalChecks_serialisation
419
  , 'prop_DdmSimple_serialisation
420
  , 'prop_DdmFull_serialisation
421
  , 'prop_CVErrorCode_serialisation
422
  , 'case_CVErrorCode_pyequiv
423
  , 'prop_Hypervisor_serialisation
424
  , 'prop_OobCommand_serialisation
425
  , 'prop_StorageType_serialisation
426
  , 'prop_NodeEvacMode_serialisation
427
  , 'prop_FileDriver_serialisation
428
  , 'prop_InstCreateMode_serialisation
429
  , 'prop_RebootType_serialisation
430
  , 'prop_ExportMode_serialisation
431
  , 'prop_IAllocatorTestDir_serialisation
432
  , 'prop_IAllocatorMode_serialisation
433
  , 'case_IAllocatorMode_pyequiv
434
  , 'prop_NICMode_serialisation
435
  , 'prop_OpStatus_serialization
436
  , 'prop_JobStatus_serialization
437
  , 'case_JobStatus_order
438
  , 'case_NICMode_pyequiv
439
  , 'prop_FinalizedJobStatus_serialisation
440
  , 'case_FinalizedJobStatus_pyequiv
441
  , 'prop_JobId_serialisation
442
  , 'prop_JobId_fractional
443
  , 'case_JobId_BadTypes
444
  , 'prop_JobDependency_serialisation
445
  , 'prop_OpSubmitPriority_serialisation
446
  , 'prop_OpSubmitPriority_string
447
  , 'prop_ELogType_serialisation
448
  ]