1 {-# LANGUAGE TemplateHaskell #-}
2 {-# OPTIONS_GHC -fno-warn-orphans #-}
4 {-| Unittests for 'Ganeti.Types'.
10 Copyright (C) 2012, 2013 Google Inc.
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.
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.
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
29 module Test.Ganeti.Types
40 import Data.List (delete, sort)
41 import Test.QuickCheck as QuickCheck hiding (Result)
43 import qualified Text.JSON as J
45 import Test.Ganeti.TestHelper
46 import Test.Ganeti.TestCommon
48 import Ganeti.BasicTypes
49 import qualified Ganeti.Constants as C
50 import Ganeti.Types as Types
53 {-# ANN module "HLint: ignore Use camelCase" #-}
55 -- * Arbitrary instance
57 instance (Arbitrary a, Ord a, Num a, Show a) =>
58 Arbitrary (Types.Positive a) where
60 (QuickCheck.Positive i) <- arbitrary
63 instance (Arbitrary a, Ord a, Num a, Show a) =>
64 Arbitrary (Types.NonNegative a) where
66 (QuickCheck.NonNegative i) <- arbitrary
69 instance (Arbitrary a, Ord a, Num a, Show a) =>
70 Arbitrary (Types.Negative a) where
72 (QuickCheck.Positive i) <- arbitrary
73 Types.mkNegative $ negate i
75 instance (Arbitrary a) => Arbitrary (Types.NonEmpty a) where
77 QuickCheck.NonEmpty lst <- arbitrary
80 $(genArbitrary ''AllocPolicy)
82 -- | Valid disk templates (depending on configure options).
83 allDiskTemplates :: [DiskTemplate]
85 let all_vals = [minBound..maxBound]::[DiskTemplate]
86 sel1 = if C.enableFileStorage
88 else delete DTFile all_vals
89 sel2 = if C.enableSharedFileStorage
91 else delete DTSharedFile sel1
94 -- | Custom 'Arbitrary' instance for 'DiskTemplate', which needs to
95 -- handle the case of file storage being disabled at configure time.
96 instance Arbitrary DiskTemplate where
97 arbitrary = elements allDiskTemplates
99 $(genArbitrary ''InstanceStatus)
101 $(genArbitrary ''MigrationMode)
103 $(genArbitrary ''VerifyOptionalChecks)
105 $(genArbitrary ''DdmSimple)
107 $(genArbitrary ''DdmFull)
109 $(genArbitrary ''CVErrorCode)
111 $(genArbitrary ''Hypervisor)
113 $(genArbitrary ''OobCommand)
115 -- | Valid storage types.
116 allStorageTypes :: [StorageType]
118 let all_vals = [minBound..maxBound]::[StorageType]
119 in if C.enableFileStorage
121 else delete StorageFile all_vals
123 -- | Custom 'Arbitrary' instance for 'StorageType', which needs to
124 -- handle the case of file storage being disabled at configure time.
125 instance Arbitrary StorageType where
126 arbitrary = elements allStorageTypes
128 $(genArbitrary ''NodeEvacMode)
130 $(genArbitrary ''FileDriver)
132 $(genArbitrary ''InstCreateMode)
134 $(genArbitrary ''RebootType)
136 $(genArbitrary ''ExportMode)
138 $(genArbitrary ''IAllocatorTestDir)
140 $(genArbitrary ''IAllocatorMode)
142 $(genArbitrary ''NICMode)
144 $(genArbitrary ''JobStatus)
146 $(genArbitrary ''FinalizedJobStatus)
148 instance Arbitrary JobId where
150 (Positive i) <- arbitrary
153 $(genArbitrary ''JobIdDep)
155 $(genArbitrary ''JobDependency)
157 $(genArbitrary ''OpSubmitPriority)
159 $(genArbitrary ''OpStatus)
161 $(genArbitrary ''ELogType)
165 prop_AllocPolicy_serialisation :: AllocPolicy -> Property
166 prop_AllocPolicy_serialisation = testSerialisation
168 -- | Test 'AllocPolicy' ordering is as expected.
169 case_AllocPolicy_order :: Assertion
170 case_AllocPolicy_order =
171 assertEqual "sort order" [ Types.AllocPreferred
172 , Types.AllocLastResort
173 , Types.AllocUnallocable
174 ] [minBound..maxBound]
176 prop_DiskTemplate_serialisation :: DiskTemplate -> Property
177 prop_DiskTemplate_serialisation = testSerialisation
179 prop_InstanceStatus_serialisation :: InstanceStatus -> Property
180 prop_InstanceStatus_serialisation = testSerialisation
182 -- | Tests building non-negative numbers.
183 prop_NonNeg_pass :: QuickCheck.NonNegative Int -> Property
184 prop_NonNeg_pass (QuickCheck.NonNegative i) =
185 case mkNonNegative i of
186 Bad msg -> failTest $ "Fail to build non-negative: " ++ msg
187 Ok nn -> fromNonNegative nn ==? i
189 -- | Tests building non-negative numbers.
190 prop_NonNeg_fail :: QuickCheck.Positive Int -> Property
191 prop_NonNeg_fail (QuickCheck.Positive i) =
192 case mkNonNegative (negate i)::Result (Types.NonNegative Int) of
194 Ok nn -> failTest $ "Built non-negative number '" ++ show nn ++
195 "' from negative value " ++ show i
197 -- | Tests building positive numbers.
198 prop_Positive_pass :: QuickCheck.Positive Int -> Property
199 prop_Positive_pass (QuickCheck.Positive i) =
201 Bad msg -> failTest $ "Fail to build positive: " ++ msg
202 Ok nn -> fromPositive nn ==? i
204 -- | Tests building positive numbers.
205 prop_Positive_fail :: QuickCheck.NonNegative Int -> Property
206 prop_Positive_fail (QuickCheck.NonNegative i) =
207 case mkPositive (negate i)::Result (Types.Positive Int) of
209 Ok nn -> failTest $ "Built positive number '" ++ show nn ++
210 "' from negative or zero value " ++ show i
212 -- | Tests building negative numbers.
213 prop_Neg_pass :: QuickCheck.Positive Int -> Property
214 prop_Neg_pass (QuickCheck.Positive i) =
215 case mkNegative i' of
216 Bad msg -> failTest $ "Fail to build negative: " ++ msg
217 Ok nn -> fromNegative nn ==? i'
220 -- | Tests building negative numbers.
221 prop_Neg_fail :: QuickCheck.NonNegative Int -> Property
222 prop_Neg_fail (QuickCheck.NonNegative i) =
223 case mkNegative i::Result (Types.Negative Int) of
225 Ok nn -> failTest $ "Built negative number '" ++ show nn ++
226 "' from non-negative value " ++ show i
228 -- | Tests building non-empty lists.
229 prop_NonEmpty_pass :: QuickCheck.NonEmptyList String -> Property
230 prop_NonEmpty_pass (QuickCheck.NonEmpty xs) =
231 case mkNonEmpty xs of
232 Bad msg -> failTest $ "Fail to build non-empty list: " ++ msg
233 Ok nn -> fromNonEmpty nn ==? xs
235 -- | Tests building positive numbers.
236 case_NonEmpty_fail :: Assertion
238 assertEqual "building non-empty list from an empty list"
239 (Bad "Received empty value for non-empty list") (mkNonEmpty ([]::[Int]))
241 -- | Tests migration mode serialisation.
242 prop_MigrationMode_serialisation :: MigrationMode -> Property
243 prop_MigrationMode_serialisation = testSerialisation
245 -- | Tests verify optional checks serialisation.
246 prop_VerifyOptionalChecks_serialisation :: VerifyOptionalChecks -> Property
247 prop_VerifyOptionalChecks_serialisation = testSerialisation
249 -- | Tests 'DdmSimple' serialisation.
250 prop_DdmSimple_serialisation :: DdmSimple -> Property
251 prop_DdmSimple_serialisation = testSerialisation
253 -- | Tests 'DdmFull' serialisation.
254 prop_DdmFull_serialisation :: DdmFull -> Property
255 prop_DdmFull_serialisation = testSerialisation
257 -- | Tests 'CVErrorCode' serialisation.
258 prop_CVErrorCode_serialisation :: CVErrorCode -> Property
259 prop_CVErrorCode_serialisation = testSerialisation
261 -- | Tests equivalence with Python, based on Constants.hs code.
262 case_CVErrorCode_pyequiv :: Assertion
263 case_CVErrorCode_pyequiv = do
264 let all_py_codes = sort C.cvAllEcodesStrings
265 all_hs_codes = sort $ map Types.cVErrorCodeToRaw [minBound..maxBound]
266 assertEqual "for CVErrorCode equivalence" all_py_codes all_hs_codes
268 -- | Test 'Hypervisor' serialisation.
269 prop_Hypervisor_serialisation :: Hypervisor -> Property
270 prop_Hypervisor_serialisation = testSerialisation
272 -- | Test 'OobCommand' serialisation.
273 prop_OobCommand_serialisation :: OobCommand -> Property
274 prop_OobCommand_serialisation = testSerialisation
276 -- | Test 'StorageType' serialisation.
277 prop_StorageType_serialisation :: StorageType -> Property
278 prop_StorageType_serialisation = testSerialisation
280 -- | Test 'NodeEvacMode' serialisation.
281 prop_NodeEvacMode_serialisation :: NodeEvacMode -> Property
282 prop_NodeEvacMode_serialisation = testSerialisation
284 -- | Test 'FileDriver' serialisation.
285 prop_FileDriver_serialisation :: FileDriver -> Property
286 prop_FileDriver_serialisation = testSerialisation
288 -- | Test 'InstCreate' serialisation.
289 prop_InstCreateMode_serialisation :: InstCreateMode -> Property
290 prop_InstCreateMode_serialisation = testSerialisation
292 -- | Test 'RebootType' serialisation.
293 prop_RebootType_serialisation :: RebootType -> Property
294 prop_RebootType_serialisation = testSerialisation
296 -- | Test 'ExportMode' serialisation.
297 prop_ExportMode_serialisation :: ExportMode -> Property
298 prop_ExportMode_serialisation = testSerialisation
300 -- | Test 'IAllocatorTestDir' serialisation.
301 prop_IAllocatorTestDir_serialisation :: IAllocatorTestDir -> Property
302 prop_IAllocatorTestDir_serialisation = testSerialisation
304 -- | Test 'IAllocatorMode' serialisation.
305 prop_IAllocatorMode_serialisation :: IAllocatorMode -> Property
306 prop_IAllocatorMode_serialisation = testSerialisation
308 -- | Tests equivalence with Python, based on Constants.hs code.
309 case_IAllocatorMode_pyequiv :: Assertion
310 case_IAllocatorMode_pyequiv = do
311 let all_py_codes = sort C.validIallocatorModes
312 all_hs_codes = sort $ map Types.iAllocatorModeToRaw [minBound..maxBound]
313 assertEqual "for IAllocatorMode equivalence" all_py_codes all_hs_codes
315 -- | Test 'NICMode' serialisation.
316 prop_NICMode_serialisation :: NICMode -> Property
317 prop_NICMode_serialisation = testSerialisation
319 -- | Test 'OpStatus' serialisation.
320 prop_OpStatus_serialization :: OpStatus -> Property
321 prop_OpStatus_serialization = testSerialisation
323 -- | Test 'JobStatus' serialisation.
324 prop_JobStatus_serialization :: JobStatus -> Property
325 prop_JobStatus_serialization = testSerialisation
327 -- | Test 'JobStatus' ordering is as expected.
328 case_JobStatus_order :: Assertion
329 case_JobStatus_order =
330 assertEqual "sort order" [ Types.JOB_STATUS_QUEUED
331 , Types.JOB_STATUS_WAITING
332 , Types.JOB_STATUS_CANCELING
333 , Types.JOB_STATUS_RUNNING
334 , Types.JOB_STATUS_CANCELED
335 , Types.JOB_STATUS_SUCCESS
336 , Types.JOB_STATUS_ERROR
337 ] [minBound..maxBound]
339 -- | Tests equivalence with Python, based on Constants.hs code.
340 case_NICMode_pyequiv :: Assertion
341 case_NICMode_pyequiv = do
342 let all_py_codes = sort C.nicValidModes
343 all_hs_codes = sort $ map Types.nICModeToRaw [minBound..maxBound]
344 assertEqual "for NICMode equivalence" all_py_codes all_hs_codes
346 -- | Test 'FinalizedJobStatus' serialisation.
347 prop_FinalizedJobStatus_serialisation :: FinalizedJobStatus -> Property
348 prop_FinalizedJobStatus_serialisation = testSerialisation
350 -- | Tests equivalence with Python, based on Constants.hs code.
351 case_FinalizedJobStatus_pyequiv :: Assertion
352 case_FinalizedJobStatus_pyequiv = do
353 let all_py_codes = sort C.jobsFinalized
354 all_hs_codes = sort $ map Types.finalizedJobStatusToRaw
356 assertEqual "for FinalizedJobStatus equivalence" all_py_codes all_hs_codes
358 -- | Tests JobId serialisation (both from string and ints).
359 prop_JobId_serialisation :: JobId -> Property
360 prop_JobId_serialisation jid =
361 conjoin [ testSerialisation jid
362 , (J.readJSON . J.showJSON . show $ fromJobId jid) ==? J.Ok jid
363 , case (fromJVal . J.showJSON . negate $
364 fromJobId jid)::Result JobId of
366 Ok jid' -> failTest $ "Parsed negative job id as id " ++
367 show (fromJobId jid')
370 -- | Tests that fractional job IDs are not accepted.
371 prop_JobId_fractional :: Property
372 prop_JobId_fractional =
373 forAll (arbitrary `suchThat`
374 (\d -> fromIntegral (truncate d::Int) /= d)) $ \d ->
375 case J.readJSON (J.showJSON (d::Double)) of
376 J.Error _ -> passTest
377 J.Ok jid -> failTest $ "Parsed fractional value " ++ show d ++
378 " as job id " ++ show (fromJobId jid)
380 -- | Tests that a job ID is not parseable from \"bad\" JSON values.
381 case_JobId_BadTypes :: Assertion
382 case_JobId_BadTypes = do
383 let helper jsval = case J.readJSON jsval of
384 J.Error _ -> return ()
385 J.Ok jid -> assertFailure $ "Parsed " ++ show jsval
386 ++ " as job id " ++ show (fromJobId jid)
388 helper (J.JSBool True)
389 helper (J.JSBool False)
390 helper (J.JSArray [])
392 -- | Test 'JobDependency' serialisation.
393 prop_JobDependency_serialisation :: JobDependency -> Property
394 prop_JobDependency_serialisation = testSerialisation
396 -- | Test 'OpSubmitPriority' serialisation.
397 prop_OpSubmitPriority_serialisation :: OpSubmitPriority -> Property
398 prop_OpSubmitPriority_serialisation = testSerialisation
400 -- | Tests string formatting for 'OpSubmitPriority'.
401 prop_OpSubmitPriority_string :: OpSubmitPriority -> Property
402 prop_OpSubmitPriority_string prio =
403 parseSubmitPriority (fmtSubmitPriority prio) ==? Just prio
405 -- | Test 'ELogType' serialisation.
406 prop_ELogType_serialisation :: ELogType -> Property
407 prop_ELogType_serialisation = testSerialisation
410 [ 'prop_AllocPolicy_serialisation
411 , 'case_AllocPolicy_order
412 , 'prop_DiskTemplate_serialisation
413 , 'prop_InstanceStatus_serialisation
416 , 'prop_Positive_pass
417 , 'prop_Positive_fail
420 , 'prop_NonEmpty_pass
421 , 'case_NonEmpty_fail
422 , 'prop_MigrationMode_serialisation
423 , 'prop_VerifyOptionalChecks_serialisation
424 , 'prop_DdmSimple_serialisation
425 , 'prop_DdmFull_serialisation
426 , 'prop_CVErrorCode_serialisation
427 , 'case_CVErrorCode_pyequiv
428 , 'prop_Hypervisor_serialisation
429 , 'prop_OobCommand_serialisation
430 , 'prop_StorageType_serialisation
431 , 'prop_NodeEvacMode_serialisation
432 , 'prop_FileDriver_serialisation
433 , 'prop_InstCreateMode_serialisation
434 , 'prop_RebootType_serialisation
435 , 'prop_ExportMode_serialisation
436 , 'prop_IAllocatorTestDir_serialisation
437 , 'prop_IAllocatorMode_serialisation
438 , 'case_IAllocatorMode_pyequiv
439 , 'prop_NICMode_serialisation
440 , 'prop_OpStatus_serialization
441 , 'prop_JobStatus_serialization
442 , 'case_JobStatus_order
443 , 'case_NICMode_pyequiv
444 , 'prop_FinalizedJobStatus_serialisation
445 , 'case_FinalizedJobStatus_pyequiv
446 , 'prop_JobId_serialisation
447 , 'prop_JobId_fractional
448 , 'case_JobId_BadTypes
449 , 'prop_JobDependency_serialisation
450 , 'prop_OpSubmitPriority_serialisation
451 , 'prop_OpSubmitPriority_string
452 , 'prop_ELogType_serialisation