Merge 'EvacNode' and 'NodeEvacMode'
[ganeti-local] / test / hs / Test / Ganeti / HTools / Types.hs
1 {-# LANGUAGE TemplateHaskell, FlexibleInstances, TypeSynonymInstances #-}
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.HTools.Types
30   ( testHTools_Types
31   , Types.AllocPolicy(..)
32   , Types.DiskTemplate(..)
33   , Types.FailMode(..)
34   , Types.ISpec(..)
35   , Types.IPolicy(..)
36   , nullIPolicy
37   ) where
38
39 import Test.QuickCheck hiding (Result)
40 import Test.HUnit
41
42 import Control.Applicative
43 import Control.Monad (replicateM)
44
45 import Test.Ganeti.TestHelper
46 import Test.Ganeti.TestCommon
47 import Test.Ganeti.TestHTools
48 import Test.Ganeti.Types (allDiskTemplates)
49
50 import Ganeti.BasicTypes
51 import qualified Ganeti.Constants as C
52 import Ganeti.ConstantUtils
53 import qualified Ganeti.HTools.Types as Types
54
55 {-# ANN module "HLint: ignore Use camelCase" #-}
56
57 -- * Helpers
58
59 -- * Arbitrary instance
60
61 $(genArbitrary ''Types.FailMode)
62
63 instance Arbitrary a => Arbitrary (Types.OpResult a) where
64   arbitrary = arbitrary >>= \c ->
65               if c
66                 then Ok  <$> arbitrary
67                 else Bad <$> arbitrary
68
69 instance Arbitrary Types.ISpec where
70   arbitrary = do
71     mem_s <- arbitrary::Gen (NonNegative Int)
72     dsk_c <- arbitrary::Gen (NonNegative Int)
73     dsk_s <- arbitrary::Gen (NonNegative Int)
74     cpu_c <- arbitrary::Gen (NonNegative Int)
75     nic_c <- arbitrary::Gen (NonNegative Int)
76     su    <- arbitrary::Gen (NonNegative Int)
77     return Types.ISpec { Types.iSpecMemorySize = fromIntegral mem_s
78                        , Types.iSpecCpuCount   = fromIntegral cpu_c
79                        , Types.iSpecDiskSize   = fromIntegral dsk_s
80                        , Types.iSpecDiskCount  = fromIntegral dsk_c
81                        , Types.iSpecNicCount   = fromIntegral nic_c
82                        , Types.iSpecSpindleUse = fromIntegral su
83                        }
84
85 -- | Generates an ispec bigger than the given one.
86 genBiggerISpec :: Types.ISpec -> Gen Types.ISpec
87 genBiggerISpec imin = do
88   mem_s <- choose (Types.iSpecMemorySize imin, maxBound)
89   dsk_c <- choose (Types.iSpecDiskCount imin, maxBound)
90   dsk_s <- choose (Types.iSpecDiskSize imin, maxBound)
91   cpu_c <- choose (Types.iSpecCpuCount imin, maxBound)
92   nic_c <- choose (Types.iSpecNicCount imin, maxBound)
93   su    <- choose (Types.iSpecSpindleUse imin, maxBound)
94   return Types.ISpec { Types.iSpecMemorySize = fromIntegral mem_s
95                      , Types.iSpecCpuCount   = fromIntegral cpu_c
96                      , Types.iSpecDiskSize   = fromIntegral dsk_s
97                      , Types.iSpecDiskCount  = fromIntegral dsk_c
98                      , Types.iSpecNicCount   = fromIntegral nic_c
99                      , Types.iSpecSpindleUse = fromIntegral su
100                      }
101
102 genMinMaxISpecs :: Gen Types.MinMaxISpecs
103 genMinMaxISpecs = do
104   imin <- arbitrary
105   imax <- genBiggerISpec imin
106   return Types.MinMaxISpecs { Types.minMaxISpecsMinSpec = imin
107                              , Types.minMaxISpecsMaxSpec = imax
108                              }
109
110 instance Arbitrary Types.MinMaxISpecs where
111   arbitrary = genMinMaxISpecs
112
113 genMinMaxStdISpecs :: Gen (Types.MinMaxISpecs, Types.ISpec)
114 genMinMaxStdISpecs = do
115   imin <- arbitrary
116   istd <- genBiggerISpec imin
117   imax <- genBiggerISpec istd
118   return (Types.MinMaxISpecs { Types.minMaxISpecsMinSpec = imin
119                              , Types.minMaxISpecsMaxSpec = imax
120                              },
121           istd)
122
123 genIPolicySpecs :: Gen ([Types.MinMaxISpecs], Types.ISpec)
124 genIPolicySpecs = do
125   num_mm <- choose (1, 6) -- 6 is just an arbitrary limit
126   std_compl <- choose (1, num_mm)
127   mm_head <- replicateM (std_compl - 1) genMinMaxISpecs
128   (mm_middle, istd) <- genMinMaxStdISpecs
129   mm_tail <- replicateM (num_mm - std_compl) genMinMaxISpecs
130   return (mm_head ++ (mm_middle : mm_tail), istd)
131
132
133 instance Arbitrary Types.IPolicy where
134   arbitrary = do
135     (iminmax, istd) <- genIPolicySpecs
136     num_tmpl <- choose (0, length allDiskTemplates)
137     dts  <- genUniquesList num_tmpl arbitrary
138     vcpu_ratio <- choose (1.0, maxVcpuRatio)
139     spindle_ratio <- choose (1.0, maxSpindleRatio)
140     return Types.IPolicy { Types.iPolicyMinMaxISpecs = iminmax
141                          , Types.iPolicyStdSpec = istd
142                          , Types.iPolicyDiskTemplates = dts
143                          , Types.iPolicyVcpuRatio = vcpu_ratio
144                          , Types.iPolicySpindleRatio = spindle_ratio
145                          }
146
147 -- * Test cases
148
149 prop_ISpec_serialisation :: Types.ISpec -> Property
150 prop_ISpec_serialisation = testSerialisation
151
152 prop_IPolicy_serialisation :: Types.IPolicy -> Property
153 prop_IPolicy_serialisation = testSerialisation
154
155 prop_opToResult :: Types.OpResult Int -> Property
156 prop_opToResult op =
157   case op of
158     Bad _ -> printTestCase ("expected bad but got " ++ show r) $ isBad r
159     Ok v  -> case r of
160                Bad msg -> failTest ("expected Ok but got Bad " ++ msg)
161                Ok v' -> v ==? v'
162   where r = Types.opToResult op
163
164 prop_eitherToResult :: Either String Int -> Bool
165 prop_eitherToResult ei =
166   case ei of
167     Left _ -> isBad r
168     Right v -> case r of
169                  Bad _ -> False
170                  Ok v' -> v == v'
171     where r = eitherToResult ei
172
173 -- | Test 'AutoRepairType' ordering is as expected and consistent with Python
174 -- codebase.
175 case_AutoRepairType_sort :: Assertion
176 case_AutoRepairType_sort = do
177   let expected = [ Types.ArFixStorage
178                  , Types.ArMigrate
179                  , Types.ArFailover
180                  , Types.ArReinstall
181                  ]
182       all_hs_raw = mkSet $ map Types.autoRepairTypeToRaw [minBound..maxBound]
183   assertEqual "Haskell order" expected [minBound..maxBound]
184   assertEqual "consistent with Python" C.autoRepairAllTypes all_hs_raw
185
186 -- | Test 'AutoRepairResult' type is equivalent with Python codebase.
187 case_AutoRepairResult_pyequiv :: Assertion
188 case_AutoRepairResult_pyequiv = do
189   let all_py_results = C.autoRepairAllResults
190       all_hs_results = mkSet $
191                        map Types.autoRepairResultToRaw [minBound..maxBound]
192   assertEqual "for AutoRepairResult equivalence" all_py_results all_hs_results
193
194 testSuite "HTools/Types"
195             [ 'prop_ISpec_serialisation
196             , 'prop_IPolicy_serialisation
197             , 'prop_opToResult
198             , 'prop_eitherToResult
199             , 'case_AutoRepairType_sort
200             , 'case_AutoRepairResult_pyequiv
201             ]