Statistics
| Branch: | Tag: | Revision:

root / htest / Test / Ganeti / TestCommon.hs @ aed2325f

History | View | Annotate | Download (5 kB)

1
{-| Unittest helpers for ganeti-htools.
2

    
3
-}
4

    
5
{-
6

    
7
Copyright (C) 2009, 2010, 2011, 2012 Google Inc.
8

    
9
This program is free software; you can redistribute it and/or modify
10
it under the terms of the GNU General Public License as published by
11
the Free Software Foundation; either version 2 of the License, or
12
(at your option) any later version.
13

    
14
This program is distributed in the hope that it will be useful, but
15
WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
General Public License for more details.
18

    
19
You should have received a copy of the GNU General Public License
20
along with this program; if not, write to the Free Software
21
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22
02110-1301, USA.
23

    
24
-}
25

    
26
module Test.Ganeti.TestCommon where
27

    
28
import Control.Applicative
29
import Control.Exception (catchJust)
30
import Control.Monad
31
import Data.List
32
import qualified Test.HUnit as HUnit
33
import Test.QuickCheck
34
import System.Environment (getEnv)
35
import System.Exit (ExitCode(..))
36
import System.IO.Error (isDoesNotExistError)
37
import System.Process (readProcessWithExitCode)
38

    
39
-- * Constants
40

    
41
-- | Maximum memory (1TiB, somewhat random value).
42
maxMem :: Int
43
maxMem = 1024 * 1024
44

    
45
-- | Maximum disk (8TiB, somewhat random value).
46
maxDsk :: Int
47
maxDsk = 1024 * 1024 * 8
48

    
49
-- | Max CPUs (1024, somewhat random value).
50
maxCpu :: Int
51
maxCpu = 1024
52

    
53
-- | Max vcpu ratio (random value).
54
maxVcpuRatio :: Double
55
maxVcpuRatio = 1024.0
56

    
57
-- | Max spindle ratio (random value).
58
maxSpindleRatio :: Double
59
maxSpindleRatio = 1024.0
60

    
61
-- | Max nodes, used just to limit arbitrary instances for smaller
62
-- opcode definitions (e.g. list of nodes in OpTestDelay).
63
maxNodes :: Int
64
maxNodes = 32
65

    
66
-- | Max opcodes or jobs in a submit job and submit many jobs.
67
maxOpCodes :: Int
68
maxOpCodes = 16
69

    
70
-- * Helper functions
71

    
72
-- | Checks for equality with proper annotation.
73
(==?) :: (Show a, Eq a) => a -> a -> Property
74
(==?) x y = printTestCase
75
            ("Expected equality, but '" ++
76
             show x ++ "' /= '" ++ show y ++ "'") (x == y)
77
infix 3 ==?
78

    
79
-- | Show a message and fail the test.
80
failTest :: String -> Property
81
failTest msg = printTestCase msg False
82

    
83
-- | Return the python binary to use. If the PYTHON environment
84
-- variable is defined, use its value, otherwise use just \"python\".
85
pythonCmd :: IO String
86
pythonCmd = catchJust (guard . isDoesNotExistError)
87
            (getEnv "PYTHON") (const (return "python"))
88

    
89
-- | Run Python with an expression, returning the exit code, standard
90
-- output and error.
91
runPython :: String -> String -> IO (ExitCode, String, String)
92
runPython expr stdin = do
93
  py_binary <- pythonCmd
94
  readProcessWithExitCode py_binary ["-c", expr] stdin
95

    
96
-- | Check python exit code, and fail via HUnit assertions if
97
-- non-zero. Otherwise, return the standard output.
98
checkPythonResult :: (ExitCode, String, String) -> IO String
99
checkPythonResult (py_code, py_stdout, py_stderr) = do
100
  HUnit.assertEqual ("python exited with error: " ++ py_stderr)
101
       ExitSuccess py_code
102
  return py_stdout
103

    
104
-- * Arbitrary instances
105

    
106
-- | Defines a DNS name.
107
newtype DNSChar = DNSChar { dnsGetChar::Char }
108

    
109
instance Arbitrary DNSChar where
110
  arbitrary = do
111
    x <- elements (['a'..'z'] ++ ['0'..'9'] ++ "_-")
112
    return (DNSChar x)
113

    
114
instance Show DNSChar where
115
  show = show . dnsGetChar
116

    
117
-- | Generates a single name component.
118
getName :: Gen String
119
getName = do
120
  n <- choose (1, 64)
121
  dn <- vector n
122
  return (map dnsGetChar dn)
123

    
124
-- | Generates an entire FQDN.
125
getFQDN :: Gen String
126
getFQDN = do
127
  ncomps <- choose (1, 4)
128
  names <- vectorOf ncomps getName
129
  return $ intercalate "." names
130

    
131
-- | Combinator that generates a 'Maybe' using a sub-combinator.
132
getMaybe :: Gen a -> Gen (Maybe a)
133
getMaybe subgen = do
134
  bool <- arbitrary
135
  if bool
136
    then Just <$> subgen
137
    else return Nothing
138

    
139
-- | Defines a tag type.
140
newtype TagChar = TagChar { tagGetChar :: Char }
141

    
142
-- | All valid tag chars. This doesn't need to match _exactly_
143
-- Ganeti's own tag regex, just enough for it to be close.
144
tagChar :: [Char]
145
tagChar = ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ ".+*/:@-"
146

    
147
instance Arbitrary TagChar where
148
  arbitrary = do
149
    c <- elements tagChar
150
    return (TagChar c)
151

    
152
-- | Generates a tag
153
genTag :: Gen [TagChar]
154
genTag = do
155
  -- the correct value would be C.maxTagLen, but that's way too
156
  -- verbose in unittests, and at the moment I don't see any possible
157
  -- bugs with longer tags and the way we use tags in htools
158
  n <- choose (1, 10)
159
  vector n
160

    
161
-- | Generates a list of tags (correctly upper bounded).
162
genTags :: Gen [String]
163
genTags = do
164
  -- the correct value would be C.maxTagsPerObj, but per the comment
165
  -- in genTag, we don't use tags enough in htools to warrant testing
166
  -- such big values
167
  n <- choose (0, 10::Int)
168
  tags <- mapM (const genTag) [1..n]
169
  return $ map (map tagGetChar) tags
170

    
171
-- | Generates a fields list. This uses the same character set as a
172
-- DNS name (just for simplicity).
173
getFields :: Gen [String]
174
getFields = do
175
  n <- choose (1, 32)
176
  vectorOf n getName