Add unit tests for LUGroupRename
[ganeti-local] / test / py / testutils.py
1 #
2 #
3
4 # Copyright (C) 2006, 2007, 2008 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21
22 """Utilities for unit testing"""
23
24 import os
25 import sys
26 import stat
27 import tempfile
28 import unittest
29 import logging
30
31 from ganeti import utils
32
33
34 def GetSourceDir():
35   return os.environ.get("TOP_SRCDIR", ".")
36
37
38 def TestDataFilename(name):
39   """Returns the filename of a given test data file.
40
41   @type name: str
42   @param name: the 'base' of the file name, as present in
43       the test/data directory
44   @rtype: str
45   @return: the full path to the filename, such that it can
46       be used in 'make distcheck' rules
47
48   """
49   return "%s/test/data/%s" % (GetSourceDir(), name)
50
51
52 def ReadTestData(name):
53   """Returns the content of a test data file.
54
55   This is just a very simple wrapper over utils.ReadFile with the
56   proper test file name.
57
58   """
59   return utils.ReadFile(TestDataFilename(name))
60
61
62 def _SetupLogging(verbose):
63   """Setupup logging infrastructure.
64
65   """
66   fmt = logging.Formatter("%(asctime)s: %(threadName)s"
67                           " %(levelname)s %(message)s")
68
69   if verbose:
70     handler = logging.StreamHandler()
71   else:
72     handler = logging.FileHandler(os.devnull, "a")
73
74   handler.setLevel(logging.NOTSET)
75   handler.setFormatter(fmt)
76
77   root_logger = logging.getLogger("")
78   root_logger.setLevel(logging.NOTSET)
79   root_logger.addHandler(handler)
80
81
82 class GanetiTestProgram(unittest.TestProgram):
83   def runTests(self):
84     """Runs all tests.
85
86     """
87     _SetupLogging("LOGTOSTDERR" in os.environ)
88
89     sys.stderr.write("Running %s\n" % self.progName)
90     sys.stderr.flush()
91
92     # Ensure assertions will be evaluated
93     if not __debug__:
94       raise Exception("Not running in debug mode, assertions would not be"
95                       " evaluated")
96
97     # Check again, this time with a real assertion
98     try:
99       assert False
100     except AssertionError:
101       pass
102     else:
103       raise Exception("Assertion not evaluated")
104
105     return unittest.TestProgram.runTests(self)
106
107
108 class GanetiTestCase(unittest.TestCase):
109   """Helper class for unittesting.
110
111   This class defines a few utility functions that help in building
112   unittests. Child classes must call the parent setup and cleanup.
113
114   """
115   def setUp(self):
116     self._temp_files = []
117
118   def tearDown(self):
119     while self._temp_files:
120       try:
121         utils.RemoveFile(self._temp_files.pop())
122       except EnvironmentError:
123         pass
124
125   def assertFileContent(self, file_name, expected_content):
126     """Checks that the content of a file is what we expect.
127
128     @type file_name: str
129     @param file_name: the file whose contents we should check
130     @type expected_content: str
131     @param expected_content: the content we expect
132
133     """
134     actual_content = utils.ReadFile(file_name)
135     self.assertEqual(actual_content, expected_content)
136
137   def assertFileMode(self, file_name, expected_mode):
138     """Checks that the mode of a file is what we expect.
139
140     @type file_name: str
141     @param file_name: the file whose contents we should check
142     @type expected_mode: int
143     @param expected_mode: the mode we expect
144
145     """
146     st = os.stat(file_name)
147     actual_mode = stat.S_IMODE(st.st_mode)
148     self.assertEqual(actual_mode, expected_mode)
149
150   def assertFileUid(self, file_name, expected_uid):
151     """Checks that the user id of a file is what we expect.
152
153     @type file_name: str
154     @param file_name: the file whose contents we should check
155     @type expected_uid: int
156     @param expected_uid: the user id we expect
157
158     """
159     st = os.stat(file_name)
160     actual_uid = st.st_uid
161     self.assertEqual(actual_uid, expected_uid)
162
163   def assertFileGid(self, file_name, expected_gid):
164     """Checks that the group id of a file is what we expect.
165
166     @type file_name: str
167     @param file_name: the file whose contents we should check
168     @type expected_gid: int
169     @param expected_gid: the group id we expect
170
171     """
172     st = os.stat(file_name)
173     actual_gid = st.st_gid
174     self.assertEqual(actual_gid, expected_gid)
175
176   def assertEqualValues(self, first, second, msg=None):
177     """Compares two values whether they're equal.
178
179     Tuples are automatically converted to lists before comparing.
180
181     """
182     return self.assertEqual(UnifyValueType(first),
183                             UnifyValueType(second),
184                             msg=msg)
185
186   def _CreateTempFile(self):
187     """Creates a temporary file and adds it to the internal cleanup list.
188
189     This method simplifies the creation and cleanup of temporary files
190     during tests.
191
192     """
193     fh, fname = tempfile.mkstemp(prefix="ganeti-test", suffix=".tmp")
194     os.close(fh)
195     self._temp_files.append(fname)
196     return fname
197
198
199 def patch_object(*args, **kwargs):
200   """Unified patch_object for various versions of Python Mock.
201
202   Different Python Mock versions provide incompatible versions of patching an
203   object. More recent versions use _patch_object, older ones used patch_object.
204   This function unifies the different variations.
205
206   """
207   import mock
208   try:
209     # pylint: disable=W0212
210     return mock._patch_object(*args, **kwargs)
211   except AttributeError:
212     # pylint: disable=E1101
213     return mock.patch_object(*args, **kwargs)
214
215
216 def UnifyValueType(data):
217   """Converts all tuples into lists.
218
219   This is useful for unittests where an external library doesn't keep types.
220
221   """
222   if isinstance(data, (tuple, list)):
223     return [UnifyValueType(i) for i in data]
224
225   elif isinstance(data, dict):
226     return dict([(UnifyValueType(key), UnifyValueType(value))
227                  for (key, value) in data.iteritems()])
228
229   return data
230
231
232 class CallCounter(object):
233   """Utility class to count number of calls to a function/method.
234
235   """
236   def __init__(self, fn):
237     """Initializes this class.
238
239     @type fn: Callable
240
241     """
242     self._fn = fn
243     self._count = 0
244
245   def __call__(self, *args, **kwargs):
246     """Calls wrapped function with given parameters.
247
248     """
249     self._count += 1
250     return self._fn(*args, **kwargs)
251
252   def Count(self):
253     """Returns number of calls.
254
255     @rtype: number
256
257     """
258     return self._count