Statistics
| Branch: | Tag: | Revision:

root / test / hs / Test / Ganeti / TestCommon.hs @ 5ce621ab

History | View | Annotate | Download (12.7 kB)

1 2733df51 Iustin Pop
{-| Unittest helpers for ganeti-htools.
2 2733df51 Iustin Pop
3 2733df51 Iustin Pop
-}
4 2733df51 Iustin Pop
5 2733df51 Iustin Pop
{-
6 2733df51 Iustin Pop
7 72747d91 Iustin Pop
Copyright (C) 2009, 2010, 2011, 2012, 2013 Google Inc.
8 2733df51 Iustin Pop
9 2733df51 Iustin Pop
This program is free software; you can redistribute it and/or modify
10 2733df51 Iustin Pop
it under the terms of the GNU General Public License as published by
11 2733df51 Iustin Pop
the Free Software Foundation; either version 2 of the License, or
12 2733df51 Iustin Pop
(at your option) any later version.
13 2733df51 Iustin Pop
14 2733df51 Iustin Pop
This program is distributed in the hope that it will be useful, but
15 2733df51 Iustin Pop
WITHOUT ANY WARRANTY; without even the implied warranty of
16 2733df51 Iustin Pop
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 2733df51 Iustin Pop
General Public License for more details.
18 2733df51 Iustin Pop
19 2733df51 Iustin Pop
You should have received a copy of the GNU General Public License
20 2733df51 Iustin Pop
along with this program; if not, write to the Free Software
21 2733df51 Iustin Pop
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 2733df51 Iustin Pop
02110-1301, USA.
23 2733df51 Iustin Pop
24 2733df51 Iustin Pop
-}
25 2733df51 Iustin Pop
26 dde85e1e Iustin Pop
module Test.Ganeti.TestCommon
27 dde85e1e Iustin Pop
  ( maxMem
28 dde85e1e Iustin Pop
  , maxDsk
29 dde85e1e Iustin Pop
  , maxCpu
30 fcfc0c2d Bernardo Dal Seno
  , maxSpindles
31 dde85e1e Iustin Pop
  , maxVcpuRatio
32 dde85e1e Iustin Pop
  , maxSpindleRatio
33 dde85e1e Iustin Pop
  , maxNodes
34 dde85e1e Iustin Pop
  , maxOpCodes
35 dde85e1e Iustin Pop
  , (==?)
36 dde85e1e Iustin Pop
  , (/=?)
37 dde85e1e Iustin Pop
  , failTest
38 dde85e1e Iustin Pop
  , passTest
39 dde85e1e Iustin Pop
  , pythonCmd
40 dde85e1e Iustin Pop
  , runPython
41 dde85e1e Iustin Pop
  , checkPythonResult
42 dde85e1e Iustin Pop
  , DNSChar(..)
43 dde85e1e Iustin Pop
  , genName
44 dde85e1e Iustin Pop
  , genFQDN
45 688f35e6 Michele Tartara
  , genUUID
46 dde85e1e Iustin Pop
  , genMaybe
47 dde85e1e Iustin Pop
  , genTags
48 dde85e1e Iustin Pop
  , genFields
49 dde85e1e Iustin Pop
  , genUniquesList
50 dde85e1e Iustin Pop
  , SmallRatio(..)
51 491d02ca Iustin Pop
  , genSetHelper
52 dde85e1e Iustin Pop
  , genSet
53 4651c69f Jose A. Lopes
  , genListSet
54 6e94b75c Jose A. Lopes
  , genIPv4Address
55 6e94b75c Jose A. Lopes
  , genIPv4Network
56 dde85e1e Iustin Pop
  , genIp6Addr
57 dde85e1e Iustin Pop
  , genIp6Net
58 6e94b75c Jose A. Lopes
  , genOpCodesTagName
59 6e94b75c Jose A. Lopes
  , genLuxiTagName
60 dde85e1e Iustin Pop
  , netmask2NumHosts
61 dde85e1e Iustin Pop
  , testSerialisation
62 dde85e1e Iustin Pop
  , resultProp
63 dde85e1e Iustin Pop
  , readTestData
64 dde85e1e Iustin Pop
  , genSample
65 105266b2 Michele Tartara
  , testParser
66 b5334d80 Michele Tartara
  , genPropParser
67 f8f638fe Michele Tartara
  , genNonNegative
68 33ce4d2d Spyros Trigazis
  , relativeError
69 dde85e1e Iustin Pop
  ) where
70 2733df51 Iustin Pop
71 2733df51 Iustin Pop
import Control.Applicative
72 aed2325f Iustin Pop
import Control.Exception (catchJust)
73 aed2325f Iustin Pop
import Control.Monad
74 105266b2 Michele Tartara
import Data.Attoparsec.Text (Parser, parseOnly)
75 2733df51 Iustin Pop
import Data.List
76 105266b2 Michele Tartara
import Data.Text (pack)
77 3a991f2d Iustin Pop
import Data.Word
78 2a8efa13 Iustin Pop
import qualified Data.Set as Set
79 aed2325f Iustin Pop
import System.Environment (getEnv)
80 aed2325f Iustin Pop
import System.Exit (ExitCode(..))
81 aed2325f Iustin Pop
import System.IO.Error (isDoesNotExistError)
82 aed2325f Iustin Pop
import System.Process (readProcessWithExitCode)
83 2a8efa13 Iustin Pop
import qualified Test.HUnit as HUnit
84 2a8efa13 Iustin Pop
import Test.QuickCheck
85 2a8efa13 Iustin Pop
import Test.QuickCheck.Monadic
86 2a8efa13 Iustin Pop
import qualified Text.JSON as J
87 0b288282 Helga Velroyen
import Numeric
88 2733df51 Iustin Pop
89 b9bdc10e Iustin Pop
import qualified Ganeti.BasicTypes as BasicTypes
90 0b288282 Helga Velroyen
import Ganeti.Types
91 b9bdc10e Iustin Pop
92 2733df51 Iustin Pop
-- * Constants
93 2733df51 Iustin Pop
94 2733df51 Iustin Pop
-- | Maximum memory (1TiB, somewhat random value).
95 2733df51 Iustin Pop
maxMem :: Int
96 2733df51 Iustin Pop
maxMem = 1024 * 1024
97 2733df51 Iustin Pop
98 2733df51 Iustin Pop
-- | Maximum disk (8TiB, somewhat random value).
99 2733df51 Iustin Pop
maxDsk :: Int
100 2733df51 Iustin Pop
maxDsk = 1024 * 1024 * 8
101 2733df51 Iustin Pop
102 2733df51 Iustin Pop
-- | Max CPUs (1024, somewhat random value).
103 2733df51 Iustin Pop
maxCpu :: Int
104 2733df51 Iustin Pop
maxCpu = 1024
105 2733df51 Iustin Pop
106 fcfc0c2d Bernardo Dal Seno
-- | Max spindles (1024, somewhat random value).
107 fcfc0c2d Bernardo Dal Seno
maxSpindles :: Int
108 fcfc0c2d Bernardo Dal Seno
maxSpindles = 1024
109 fcfc0c2d Bernardo Dal Seno
110 2733df51 Iustin Pop
-- | Max vcpu ratio (random value).
111 2733df51 Iustin Pop
maxVcpuRatio :: Double
112 2733df51 Iustin Pop
maxVcpuRatio = 1024.0
113 2733df51 Iustin Pop
114 2733df51 Iustin Pop
-- | Max spindle ratio (random value).
115 2733df51 Iustin Pop
maxSpindleRatio :: Double
116 2733df51 Iustin Pop
maxSpindleRatio = 1024.0
117 2733df51 Iustin Pop
118 2733df51 Iustin Pop
-- | Max nodes, used just to limit arbitrary instances for smaller
119 2733df51 Iustin Pop
-- opcode definitions (e.g. list of nodes in OpTestDelay).
120 2733df51 Iustin Pop
maxNodes :: Int
121 2733df51 Iustin Pop
maxNodes = 32
122 2733df51 Iustin Pop
123 2733df51 Iustin Pop
-- | Max opcodes or jobs in a submit job and submit many jobs.
124 2733df51 Iustin Pop
maxOpCodes :: Int
125 2733df51 Iustin Pop
maxOpCodes = 16
126 2733df51 Iustin Pop
127 2733df51 Iustin Pop
-- * Helper functions
128 2733df51 Iustin Pop
129 41eb900e Iustin Pop
-- | Checks for equality with proper annotation. The first argument is
130 41eb900e Iustin Pop
-- the computed value, the second one the expected value.
131 2733df51 Iustin Pop
(==?) :: (Show a, Eq a) => a -> a -> Property
132 2733df51 Iustin Pop
(==?) x y = printTestCase
133 41eb900e Iustin Pop
            ("Expected equality, but got mismatch\nexpected: " ++
134 a309a3b4 Iustin Pop
             show y ++ "\n but got: " ++ show x) (x == y)
135 2733df51 Iustin Pop
infix 3 ==?
136 2733df51 Iustin Pop
137 41eb900e Iustin Pop
-- | Checks for inequality with proper annotation. The first argument
138 41eb900e Iustin Pop
-- is the computed value, the second one the expected (not equal)
139 41eb900e Iustin Pop
-- value.
140 dddb2bc9 Helga Velroyen
(/=?) :: (Show a, Eq a) => a -> a -> Property
141 dddb2bc9 Helga Velroyen
(/=?) x y = printTestCase
142 dddb2bc9 Helga Velroyen
            ("Expected inequality, but got equality: '" ++
143 dddb2bc9 Helga Velroyen
             show x ++ "'.") (x /= y)
144 dddb2bc9 Helga Velroyen
infix 3 /=?
145 dddb2bc9 Helga Velroyen
146 2733df51 Iustin Pop
-- | Show a message and fail the test.
147 2733df51 Iustin Pop
failTest :: String -> Property
148 2733df51 Iustin Pop
failTest msg = printTestCase msg False
149 2733df51 Iustin Pop
150 2e0bb81d Iustin Pop
-- | A 'True' property.
151 2e0bb81d Iustin Pop
passTest :: Property
152 2e0bb81d Iustin Pop
passTest = property True
153 2e0bb81d Iustin Pop
154 aed2325f Iustin Pop
-- | Return the python binary to use. If the PYTHON environment
155 aed2325f Iustin Pop
-- variable is defined, use its value, otherwise use just \"python\".
156 aed2325f Iustin Pop
pythonCmd :: IO String
157 aed2325f Iustin Pop
pythonCmd = catchJust (guard . isDoesNotExistError)
158 aed2325f Iustin Pop
            (getEnv "PYTHON") (const (return "python"))
159 aed2325f Iustin Pop
160 aed2325f Iustin Pop
-- | Run Python with an expression, returning the exit code, standard
161 aed2325f Iustin Pop
-- output and error.
162 aed2325f Iustin Pop
runPython :: String -> String -> IO (ExitCode, String, String)
163 aed2325f Iustin Pop
runPython expr stdin = do
164 aed2325f Iustin Pop
  py_binary <- pythonCmd
165 aed2325f Iustin Pop
  readProcessWithExitCode py_binary ["-c", expr] stdin
166 aed2325f Iustin Pop
167 aed2325f Iustin Pop
-- | Check python exit code, and fail via HUnit assertions if
168 aed2325f Iustin Pop
-- non-zero. Otherwise, return the standard output.
169 aed2325f Iustin Pop
checkPythonResult :: (ExitCode, String, String) -> IO String
170 aed2325f Iustin Pop
checkPythonResult (py_code, py_stdout, py_stderr) = do
171 aed2325f Iustin Pop
  HUnit.assertEqual ("python exited with error: " ++ py_stderr)
172 aed2325f Iustin Pop
       ExitSuccess py_code
173 aed2325f Iustin Pop
  return py_stdout
174 2733df51 Iustin Pop
175 2733df51 Iustin Pop
-- * Arbitrary instances
176 2733df51 Iustin Pop
177 2733df51 Iustin Pop
-- | Defines a DNS name.
178 2733df51 Iustin Pop
newtype DNSChar = DNSChar { dnsGetChar::Char }
179 2733df51 Iustin Pop
180 2733df51 Iustin Pop
instance Arbitrary DNSChar where
181 32f2e1e1 Iustin Pop
  arbitrary = liftM DNSChar $ elements (['a'..'z'] ++ ['0'..'9'] ++ "_-")
182 2733df51 Iustin Pop
183 2733df51 Iustin Pop
instance Show DNSChar where
184 2733df51 Iustin Pop
  show = show . dnsGetChar
185 2733df51 Iustin Pop
186 2733df51 Iustin Pop
-- | Generates a single name component.
187 5006418e Iustin Pop
genName :: Gen String
188 5006418e Iustin Pop
genName = do
189 086ad4cf Iustin Pop
  n <- choose (1, 16)
190 2733df51 Iustin Pop
  dn <- vector n
191 2733df51 Iustin Pop
  return (map dnsGetChar dn)
192 2733df51 Iustin Pop
193 2733df51 Iustin Pop
-- | Generates an entire FQDN.
194 5006418e Iustin Pop
genFQDN :: Gen String
195 5006418e Iustin Pop
genFQDN = do
196 2733df51 Iustin Pop
  ncomps <- choose (1, 4)
197 5006418e Iustin Pop
  names <- vectorOf ncomps genName
198 2733df51 Iustin Pop
  return $ intercalate "." names
199 2733df51 Iustin Pop
200 688f35e6 Michele Tartara
-- | Generates a UUID-like string.
201 688f35e6 Michele Tartara
--
202 688f35e6 Michele Tartara
-- Only to be used for QuickCheck testing. For obtaining actual UUIDs use
203 688f35e6 Michele Tartara
-- the newUUID function in Ganeti.Utils
204 688f35e6 Michele Tartara
genUUID :: Gen String
205 688f35e6 Michele Tartara
genUUID = do
206 688f35e6 Michele Tartara
  c1 <- vector 6
207 688f35e6 Michele Tartara
  c2 <- vector 4
208 688f35e6 Michele Tartara
  c3 <- vector 4
209 688f35e6 Michele Tartara
  c4 <- vector 4
210 688f35e6 Michele Tartara
  c5 <- vector 4
211 688f35e6 Michele Tartara
  c6 <- vector 4
212 688f35e6 Michele Tartara
  c7 <- vector 6
213 688f35e6 Michele Tartara
  return $ map dnsGetChar c1 ++ "-" ++ map dnsGetChar c2 ++ "-" ++
214 688f35e6 Michele Tartara
    map dnsGetChar c3 ++ "-" ++ map dnsGetChar c4 ++ "-" ++
215 688f35e6 Michele Tartara
    map dnsGetChar c5 ++ "-" ++ map dnsGetChar c6 ++ "-" ++
216 688f35e6 Michele Tartara
    map dnsGetChar c7
217 688f35e6 Michele Tartara
218 2733df51 Iustin Pop
-- | Combinator that generates a 'Maybe' using a sub-combinator.
219 5006418e Iustin Pop
genMaybe :: Gen a -> Gen (Maybe a)
220 4c49b965 Guido Trotter
genMaybe subgen = frequency [ (1, pure Nothing), (3, Just <$> subgen) ]
221 305e174c Iustin Pop
222 305e174c Iustin Pop
-- | Defines a tag type.
223 305e174c Iustin Pop
newtype TagChar = TagChar { tagGetChar :: Char }
224 305e174c Iustin Pop
225 305e174c Iustin Pop
-- | All valid tag chars. This doesn't need to match _exactly_
226 305e174c Iustin Pop
-- Ganeti's own tag regex, just enough for it to be close.
227 5b11f8db Iustin Pop
tagChar :: String
228 305e174c Iustin Pop
tagChar = ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ ".+*/:@-"
229 305e174c Iustin Pop
230 305e174c Iustin Pop
instance Arbitrary TagChar where
231 32f2e1e1 Iustin Pop
  arbitrary = liftM TagChar $ elements tagChar
232 305e174c Iustin Pop
233 305e174c Iustin Pop
-- | Generates a tag
234 305e174c Iustin Pop
genTag :: Gen [TagChar]
235 305e174c Iustin Pop
genTag = do
236 305e174c Iustin Pop
  -- the correct value would be C.maxTagLen, but that's way too
237 305e174c Iustin Pop
  -- verbose in unittests, and at the moment I don't see any possible
238 305e174c Iustin Pop
  -- bugs with longer tags and the way we use tags in htools
239 305e174c Iustin Pop
  n <- choose (1, 10)
240 305e174c Iustin Pop
  vector n
241 305e174c Iustin Pop
242 305e174c Iustin Pop
-- | Generates a list of tags (correctly upper bounded).
243 305e174c Iustin Pop
genTags :: Gen [String]
244 305e174c Iustin Pop
genTags = do
245 305e174c Iustin Pop
  -- the correct value would be C.maxTagsPerObj, but per the comment
246 305e174c Iustin Pop
  -- in genTag, we don't use tags enough in htools to warrant testing
247 305e174c Iustin Pop
  -- such big values
248 305e174c Iustin Pop
  n <- choose (0, 10::Int)
249 305e174c Iustin Pop
  tags <- mapM (const genTag) [1..n]
250 305e174c Iustin Pop
  return $ map (map tagGetChar) tags
251 aed2325f Iustin Pop
252 aed2325f Iustin Pop
-- | Generates a fields list. This uses the same character set as a
253 aed2325f Iustin Pop
-- DNS name (just for simplicity).
254 5006418e Iustin Pop
genFields :: Gen [String]
255 5006418e Iustin Pop
genFields = do
256 aed2325f Iustin Pop
  n <- choose (1, 32)
257 5006418e Iustin Pop
  vectorOf n genName
258 e1ee7d5a Iustin Pop
259 e1ee7d5a Iustin Pop
-- | Generates a list of a given size with non-duplicate elements.
260 df8578fb Iustin Pop
genUniquesList :: (Eq a, Arbitrary a, Ord a) => Int -> Gen a -> Gen [a]
261 df8578fb Iustin Pop
genUniquesList cnt generator = do
262 df8578fb Iustin Pop
  set <- foldM (\set _ -> do
263 df8578fb Iustin Pop
                  newelem <- generator `suchThat` (`Set.notMember` set)
264 df8578fb Iustin Pop
                  return (Set.insert newelem set)) Set.empty [1..cnt]
265 df8578fb Iustin Pop
  return $ Set.toList set
266 e1ee7d5a Iustin Pop
267 e1ee7d5a Iustin Pop
newtype SmallRatio = SmallRatio Double deriving Show
268 e1ee7d5a Iustin Pop
instance Arbitrary SmallRatio where
269 32f2e1e1 Iustin Pop
  arbitrary = liftM SmallRatio $ choose (0, 1)
270 63b068c1 Iustin Pop
271 2a8efa13 Iustin Pop
-- | Helper for 'genSet', declared separately due to type constraints.
272 2a8efa13 Iustin Pop
genSetHelper :: (Ord a) => [a] -> Maybe Int -> Gen (Set.Set a)
273 2a8efa13 Iustin Pop
genSetHelper candidates size = do
274 2a8efa13 Iustin Pop
  size' <- case size of
275 2a8efa13 Iustin Pop
             Nothing -> choose (0, length candidates)
276 2a8efa13 Iustin Pop
             Just s | s > length candidates ->
277 2a8efa13 Iustin Pop
                        error $ "Invalid size " ++ show s ++ ", maximum is " ++
278 2a8efa13 Iustin Pop
                                show (length candidates)
279 2a8efa13 Iustin Pop
                    | otherwise -> return s
280 2a8efa13 Iustin Pop
  foldM (\set _ -> do
281 2a8efa13 Iustin Pop
           newelem <- elements candidates `suchThat` (`Set.notMember` set)
282 2a8efa13 Iustin Pop
           return (Set.insert newelem set)) Set.empty [1..size']
283 2a8efa13 Iustin Pop
284 4651c69f Jose A. Lopes
-- | Generates a 'Set' of arbitrary elements.
285 2a8efa13 Iustin Pop
genSet :: (Ord a, Bounded a, Enum a) => Maybe Int -> Gen (Set.Set a)
286 2a8efa13 Iustin Pop
genSet = genSetHelper [minBound..maxBound]
287 2a8efa13 Iustin Pop
288 4651c69f Jose A. Lopes
-- | Generates a 'Set' of arbitrary elements wrapped in a 'ListSet'
289 4651c69f Jose A. Lopes
genListSet :: (Ord a, Bounded a, Enum a) => Maybe Int
290 4651c69f Jose A. Lopes
              -> Gen (BasicTypes.ListSet a)
291 4651c69f Jose A. Lopes
genListSet is = BasicTypes.ListSet <$> genSet is
292 4651c69f Jose A. Lopes
293 0b288282 Helga Velroyen
-- | Generate an arbitrary IPv4 address in textual form.
294 6e94b75c Jose A. Lopes
genIPv4 :: Gen String
295 6e94b75c Jose A. Lopes
genIPv4 = do
296 0b288282 Helga Velroyen
  a <- choose (1::Int, 255)
297 0b288282 Helga Velroyen
  b <- choose (0::Int, 255)
298 0b288282 Helga Velroyen
  c <- choose (0::Int, 255)
299 0b288282 Helga Velroyen
  d <- choose (0::Int, 255)
300 6e94b75c Jose A. Lopes
  return . intercalate "." $ map show [a, b, c, d]
301 0b288282 Helga Velroyen
302 6e94b75c Jose A. Lopes
genIPv4Address :: Gen IPv4Address
303 6e94b75c Jose A. Lopes
genIPv4Address = mkIPv4Address =<< genIPv4
304 0b288282 Helga Velroyen
305 0b288282 Helga Velroyen
-- | Generate an arbitrary IPv4 network in textual form.
306 6e94b75c Jose A. Lopes
genIPv4AddrRange :: Gen String
307 6e94b75c Jose A. Lopes
genIPv4AddrRange = do
308 6e94b75c Jose A. Lopes
  ip <- genIPv4
309 0b288282 Helga Velroyen
  netmask <- choose (8::Int, 30)
310 6e94b75c Jose A. Lopes
  return $ ip ++ "/" ++ show netmask
311 6e94b75c Jose A. Lopes
312 6e94b75c Jose A. Lopes
genIPv4Network :: Gen IPv4Network
313 6e94b75c Jose A. Lopes
genIPv4Network = mkIPv4Network =<< genIPv4AddrRange
314 0b288282 Helga Velroyen
315 0b288282 Helga Velroyen
-- | Helper function to compute the number of hosts in a network
316 0b288282 Helga Velroyen
-- given the netmask. (For IPv4 only.)
317 3a991f2d Iustin Pop
netmask2NumHosts :: Word8 -> Int
318 712da82f Helga Velroyen
netmask2NumHosts n = 2^(32-n)
319 0b288282 Helga Velroyen
320 0b288282 Helga Velroyen
-- | Generates an arbitrary IPv6 network address in textual form.
321 0b288282 Helga Velroyen
-- The generated address is not simpflified, e. g. an address like
322 0b288282 Helga Velroyen
-- "2607:f0d0:1002:0051:0000:0000:0000:0004" does not become
323 0b288282 Helga Velroyen
-- "2607:f0d0:1002:51::4"
324 0b288282 Helga Velroyen
genIp6Addr :: Gen String
325 0b288282 Helga Velroyen
genIp6Addr = do
326 0b288282 Helga Velroyen
  rawIp <- vectorOf 8 $ choose (0::Integer, 65535)
327 0b288282 Helga Velroyen
  return $ intercalate ":" (map (`showHex` "") rawIp)
328 0b288282 Helga Velroyen
329 0b288282 Helga Velroyen
-- | Generates an arbitrary IPv6 network in textual form.
330 0b288282 Helga Velroyen
genIp6Net :: Gen String
331 0b288282 Helga Velroyen
genIp6Net = do
332 0b288282 Helga Velroyen
  netmask <- choose (8::Int, 126)
333 0b288282 Helga Velroyen
  ip <- genIp6Addr
334 0b288282 Helga Velroyen
  return $ ip ++ "/" ++ show netmask
335 0b288282 Helga Velroyen
336 6e94b75c Jose A. Lopes
-- | Generates a valid, arbitrary tag name with respect to the given
337 6e94b75c Jose A. Lopes
-- 'TagKind' for opcodes.
338 5cbf7832 Jose A. Lopes
genOpCodesTagName :: TagKind -> Gen (Maybe String)
339 6e94b75c Jose A. Lopes
genOpCodesTagName TagKindCluster = return Nothing
340 5cbf7832 Jose A. Lopes
genOpCodesTagName _ = Just <$> genFQDN
341 6e94b75c Jose A. Lopes
342 6e94b75c Jose A. Lopes
-- | Generates a valid, arbitrary tag name with respect to the given
343 6e94b75c Jose A. Lopes
-- 'TagKind' for Luxi.
344 6e94b75c Jose A. Lopes
genLuxiTagName :: TagKind -> Gen String
345 6e94b75c Jose A. Lopes
genLuxiTagName TagKindCluster = return ""
346 6e94b75c Jose A. Lopes
genLuxiTagName _ = genFQDN
347 6e94b75c Jose A. Lopes
348 2a8efa13 Iustin Pop
-- * Helper functions
349 2a8efa13 Iustin Pop
350 63b068c1 Iustin Pop
-- | Checks for serialisation idempotence.
351 63b068c1 Iustin Pop
testSerialisation :: (Eq a, Show a, J.JSON a) => a -> Property
352 63b068c1 Iustin Pop
testSerialisation a =
353 63b068c1 Iustin Pop
  case J.readJSON (J.showJSON a) of
354 63b068c1 Iustin Pop
    J.Error msg -> failTest $ "Failed to deserialise: " ++ msg
355 63b068c1 Iustin Pop
    J.Ok a' -> a ==? a'
356 b9bdc10e Iustin Pop
357 b9bdc10e Iustin Pop
-- | Result to PropertyM IO.
358 93be1ced Iustin Pop
resultProp :: (Show a) => BasicTypes.GenericResult a b -> PropertyM IO b
359 93be1ced Iustin Pop
resultProp (BasicTypes.Bad err) = stop . failTest $ show err
360 b9bdc10e Iustin Pop
resultProp (BasicTypes.Ok  val) = return val
361 7b0476cf Michele Tartara
362 7b0476cf Michele Tartara
-- | Return the source directory of Ganeti.
363 7b0476cf Michele Tartara
getSourceDir :: IO FilePath
364 7b0476cf Michele Tartara
getSourceDir = catchJust (guard . isDoesNotExistError)
365 7b0476cf Michele Tartara
            (getEnv "TOP_SRCDIR")
366 7b0476cf Michele Tartara
            (const (return "."))
367 7b0476cf Michele Tartara
368 7b0476cf Michele Tartara
-- | Returns the path of a file in the test data directory, given its name.
369 7b0476cf Michele Tartara
testDataFilename :: String -> String -> IO FilePath
370 7b0476cf Michele Tartara
testDataFilename datadir name = do
371 7b0476cf Michele Tartara
        src <- getSourceDir
372 7b0476cf Michele Tartara
        return $ src ++ datadir ++ name
373 7b0476cf Michele Tartara
374 7b0476cf Michele Tartara
-- | Returns the content of the specified haskell test data file.
375 7b0476cf Michele Tartara
readTestData :: String -> IO String
376 7b0476cf Michele Tartara
readTestData filename = do
377 1c0f9d12 Iustin Pop
    name <- testDataFilename "/test/data/" filename
378 7b0476cf Michele Tartara
    readFile name
379 72747d91 Iustin Pop
380 72747d91 Iustin Pop
-- | Generate arbitrary values in the IO monad. This is a simple
381 72747d91 Iustin Pop
-- wrapper over 'sample''.
382 72747d91 Iustin Pop
genSample :: Gen a -> IO a
383 72747d91 Iustin Pop
genSample gen = do
384 72747d91 Iustin Pop
  values <- sample' gen
385 72747d91 Iustin Pop
  case values of
386 72747d91 Iustin Pop
    [] -> error "sample' returned an empty list of values??"
387 72747d91 Iustin Pop
    x:_ -> return x
388 105266b2 Michele Tartara
389 105266b2 Michele Tartara
-- | Function for testing whether a file is parsed correctly.
390 105266b2 Michele Tartara
testParser :: (Show a, Eq a) => Parser a -> String -> a -> HUnit.Assertion
391 105266b2 Michele Tartara
testParser parser fileName expectedContent = do
392 105266b2 Michele Tartara
  fileContent <- readTestData fileName
393 105266b2 Michele Tartara
  case parseOnly parser $ pack fileContent of
394 105266b2 Michele Tartara
    Left msg -> HUnit.assertFailure $ "Parsing failed: " ++ msg
395 105266b2 Michele Tartara
    Right obtained -> HUnit.assertEqual fileName expectedContent obtained
396 f8f638fe Michele Tartara
397 b5334d80 Michele Tartara
-- | Generate a property test for parsers.
398 b5334d80 Michele Tartara
genPropParser :: (Show a, Eq a) => Parser a -> String -> a -> Property
399 b5334d80 Michele Tartara
genPropParser parser s expected =
400 b5334d80 Michele Tartara
  case parseOnly parser $ pack s of
401 b5334d80 Michele Tartara
    Left msg -> failTest $ "Parsing failed: " ++ msg
402 b5334d80 Michele Tartara
    Right obtained -> expected ==? obtained
403 b5334d80 Michele Tartara
404 f8f638fe Michele Tartara
-- | Generate an arbitrary non negative integer number
405 f8f638fe Michele Tartara
genNonNegative :: Gen Int
406 f8f638fe Michele Tartara
genNonNegative =
407 f8f638fe Michele Tartara
  fmap fromIntegral (arbitrary::Gen (Test.QuickCheck.NonNegative Int))
408 33ce4d2d Spyros Trigazis
409 33ce4d2d Spyros Trigazis
-- | Computes the relative error of two 'Double' numbers.
410 33ce4d2d Spyros Trigazis
--
411 33ce4d2d Spyros Trigazis
-- This is the \"relative error\" algorithm in
412 33ce4d2d Spyros Trigazis
-- http:\/\/randomascii.wordpress.com\/2012\/02\/25\/
413 33ce4d2d Spyros Trigazis
-- comparing-floating-point-numbers-2012-edition (URL split due to too
414 33ce4d2d Spyros Trigazis
-- long line).
415 33ce4d2d Spyros Trigazis
relativeError :: Double -> Double -> Double
416 33ce4d2d Spyros Trigazis
relativeError d1 d2 =
417 33ce4d2d Spyros Trigazis
  let delta = abs $ d1 - d2
418 33ce4d2d Spyros Trigazis
      a1 = abs d1
419 33ce4d2d Spyros Trigazis
      a2 = abs d2
420 33ce4d2d Spyros Trigazis
      greatest = max a1 a2
421 33ce4d2d Spyros Trigazis
  in if delta == 0
422 33ce4d2d Spyros Trigazis
       then 0
423 33ce4d2d Spyros Trigazis
       else delta / greatest