/[thuban]/branches/WIP-pyshapelib-bramz/test/support.py
ViewVC logotype

Contents of /branches/WIP-pyshapelib-bramz/test/support.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1673 - (show annotations)
Thu Aug 28 13:04:44 2003 UTC (21 years, 6 months ago) by bh
Original Path: trunk/thuban/test/support.py
File MIME type: text/x-python
File size: 11318 byte(s)
(print_garbage_information): Call initthuban
here because it may be called indirectly from test cases that test
test support modules which do not use anything from thuban itself
(ThubanTestProgram.runTests): Remove unnecessary debug print

1 # Copyright (c) 2002, 2003 by Intevation GmbH
2 # Authors:
3 # Bernhard Herzog <[email protected]>
4 #
5 # This program is free software under the GPL (>=v2)
6 # Read the file COPYING coming with Thuban for details.
7
8 """
9 Support classes and function for the test suite
10 """
11
12 __version__ = "$Revision$"
13 # $Source$
14 # $Id$
15
16 import os, sys
17 import unittest
18 import traceback
19
20 import postgissupport
21
22
23 def thuban_dir():
24 """Return the directory containing the Thuban package"""
25 thisdir = os.path.dirname(__file__)
26 return os.path.join(thisdir, os.pardir)
27
28 def resource_dir():
29 return os.path.join(thuban_dir(), "Resources")
30
31 def add_thuban_dir_to_path():
32 """Insert the Thuban directory at the beginning of the python path.
33
34 If it's already part of the path, remove later occurrences.
35 """
36 dir = thuban_dir()
37 while 1:
38 try:
39 sys.path.remove(dir)
40 except ValueError:
41 break
42 sys.path.insert(0, dir)
43
44
45 _initthuban_done = 0
46 def initthuban():
47 """Initialize the interpreter for using Thuban modules
48 """
49 global _initthuban_done
50 if not _initthuban_done:
51 add_thuban_dir_to_path()
52 import thubaninit
53 _initthuban_done = 1
54
55
56 #
57 # Special test runner and result that support skipping tests
58 #
59
60 class SkipTest(Exception):
61 """Exception to raise in tests that are skipped for some reason
62
63 For instance, since gdal support is optional, test cases that
64 require gdal raise this exception to indicate that they are skipped.
65 Skipped is different from failure or error in that it is expected
66 under certain circumstances.
67 """
68
69 class ThubanTestResult(unittest._TextTestResult):
70
71 def __init__(self, stream, descriptions, verbosity):
72 unittest._TextTestResult.__init__(self, stream, descriptions,
73 verbosity)
74 self.skipped_tests = {}
75
76 def add_skipped_test(self, test, exc):
77 reason = str(exc)
78 self.skipped_tests.setdefault(reason, []).append(test)
79
80 def count_skipped(self):
81 sum = 0
82 for tests in self.skipped_tests.values():
83 sum += len(tests)
84 return sum
85
86 def addError(self, test, err):
87 """Extend inherited method to handle SkipTest exceptions specially
88 """
89 #print "addError", test, err
90 if isinstance(err[1], SkipTest):
91 self.add_skipped_test(test, err[1])
92 if self.showAll:
93 self.stream.writeln("skipped")
94 elif self.dots:
95 self.stream.write('S')
96 else:
97 unittest._TextTestResult.addError(self, test, err)
98
99 def printErrors(self):
100 if self.skipped_tests:
101 if self.dots or self.showAll:
102 self.stream.writeln()
103 self.stream.writeln("Skipped tests:")
104 for reason, tests in self.skipped_tests.items():
105 self.stream.writeln(" %s:" % reason)
106 for test in tests:
107 self.stream.writeln(" " + test.id())
108 unittest._TextTestResult.printErrors(self)
109
110
111 class ThubanTestRunner(unittest.TextTestRunner):
112
113 def _makeResult(self):
114 return ThubanTestResult(self.stream, self.descriptions, self.verbosity)
115
116 def run(self, test):
117 result = unittest.TextTestRunner.run(self, test)
118 self.stream.writeln("skipped = %d" % result.count_skipped())
119 return result
120
121
122 class ThubanTestProgram(unittest.TestProgram):
123
124 def runTests(self):
125 """Extend inherited method so that we use a ThubanTestRunner"""
126 self.testRunner = ThubanTestRunner(verbosity = self.verbosity)
127 unittest.TestProgram.runTests(self)
128
129
130 def execute_as_testsuite(callable, *args, **kw):
131 """Call callable with args as if it were the entry point to the test suite
132
133 Return watever callable returns.
134
135 This is a helper function for run_tests and runtests.py. Call
136 callable in a try-finally block and run some cleanup and print some
137 additional information in the finally block.
138
139 The additionaly information include:
140
141 - A list of uncollected objects (after an explicit garbage
142 collector call)
143
144 - any unsubscribed messages
145 """
146 try:
147 return callable(*args, **kw)
148 finally:
149 # This has to be in a finally clause because unittest.main()
150 # ends with a sys.exit to make sure that the process exits with
151 # an appropriate exit code
152
153 # Shutdown the postgis server if it's running
154 try:
155 postgissupport.shutdown_test_server()
156 except:
157 traceback.print_exc()
158
159 # Print additional information
160 print_additional_summary()
161
162 def run_tests():
163 """Frontend for unittest.main that prints some additional debug information
164
165 After calling unittest.main, run the garbage collector and print
166 uncollected objects. Also print any un-unsubscribed messages.
167 """
168 execute_as_testsuite(ThubanTestProgram)
169
170
171 def print_additional_summary():
172 """Print some additional summary information after tests have been run"""
173 print_garbage_information()
174 import xmlsupport
175 xmlsupport.print_summary_message()
176
177 def print_garbage_information():
178 """Print information about things that haven't been cleaned up.
179
180 Run the garbage collector and print uncollected objects. Also print
181 any un-unsubscribed messages.
182 """
183 # this function may be called indirectly from test cases that test
184 # test support modules which do not use anything from thuban itself,
185 # so we call initthuban so that we can import the connector module
186 initthuban()
187 import gc, Thuban.Lib.connector
188 gc.collect()
189 if gc.garbage:
190 print
191 print "There are %d uncollected objects:" % len(gc.garbage)
192 print gc.garbage
193 Thuban.Lib.connector._the_connector.print_connections()
194
195 #
196
197 def create_temp_dir():
198 """Create a temporary directory and return its name.
199
200 The temporary directory is always called temp and is created in the
201 directory where support module is located.
202
203 If the temp directory already exists, just return the name.
204 """
205 name = os.path.abspath(os.path.join(os.path.dirname(__file__), "temp"))
206
207 # if the directory already exists, we're done
208 if os.path.isdir(name):
209 return name
210
211 # create the directory
212 os.mkdir(name)
213 return name
214
215
216 class FileTestMixin:
217
218 """Mixin class for tests that use files in the temporary directory
219 """
220
221 def temp_file_name(self, basename):
222 """Return the full name of the file named basename in the temp. dir"""
223 return os.path.join(create_temp_dir(), basename)
224
225 def temp_dir(self):
226 """Return the name of the directory for the temporary files"""
227 return create_temp_dir()
228
229
230
231 class FileLoadTestCase(unittest.TestCase, FileTestMixin):
232
233 """Base class for test case that test loading files.
234
235 This base class provides some common infrastructure for testing the
236 reading of files.
237
238 Each test case should be its own class derived from this one. There
239 is one file associated with each class. The contents are defined by
240 the file_contents class attribute and its name by the filename
241 method.
242
243 Derived classes usually only have to provide appropriate values for
244 the file_contents and file_extension class attributes.
245 """
246
247 file_contents = None
248 file_extension = ""
249
250 def filename(self):
251 """Return the name of the test file to use.
252
253 The default implementation simply calls self.volatile_file_name
254 with a basename derived from the class name by stripping off a
255 leading 'test_' and appending self.file_extension.
256 """
257 name = self.__class__.__name__
258 if name.startswith("test_"):
259 name = name[5:]
260 return self.temp_file_name(name + self.file_extension)
261
262 def setUp(self):
263 """Create the volatile file for the test.
264
265 Write self.contents (which should be a string) to the file named
266 by self.filename().
267 """
268 filename = self.filename()
269 file = open(filename, "w")
270 file.write(self.file_contents)
271 file.close()
272
273
274 class FloatComparisonMixin:
275
276 """
277 Mixin class for tests comparing floating point numbers.
278
279 This class provides a few methods for testing floating point
280 operations.
281 """
282
283 fp_epsilon = 1e-6
284 fp_inf = float('1e1000') # FIXME: hack for infinite
285
286 def assertFloatEqual(self, test, value, epsilon = None):
287 """Assert equality of test and value with some tolerance.
288
289 Assert that the absolute difference between test and value is
290 less than self.fp_epsilon.
291 """
292 if epsilon is None:
293 epsilon = self.fp_epsilon
294 if abs(test) == self.fp_inf:
295 self.assertEqual(test, value)
296 else:
297 self.assert_(epsilon > abs(test - value),
298 "abs(%g - %g) >= %g" % (test, value, epsilon))
299
300 def assertFloatSeqEqual(self, test, value, epsilon = None):
301 """Assert equality of the sequences test and value with some tolerance.
302
303 Assert that the absolute difference between each corresponding
304 value in test and value is less than the optional parameter
305 epsilon. If epsilon is not given use self.fp_epsilon.
306 """
307 for i in range(len(test)):
308 self.assertFloatEqual(test[i], value[i], epsilon)
309
310 def assertPointListEquals(self, test, value):
311 """Assert equality of two lists of lists of tuples of float
312
313 This assertion is usually used to compare the geometry of shapes
314 as returned by a Shape object's Points() method, hence the name.
315 """
316 for i in range(len(test)):
317 self.assertEquals(len(test[i]), len(value[i]))
318 for j in range(len(test[i])):
319 self.assertFloatSeqEqual(test[i][j], value[i][j])
320
321
322 class SubscriberMixin:
323
324 """Mixin class for tests for messages sent through the Connector
325
326 The SubscriberMixin has some methods that can be used as subscribers
327 of events that when called append information about the message into
328 a list of messages received.
329
330 A derived class should call the clear_messages() method in both its
331 setUp and tearDown methods to clear the list of messages received.
332 """
333
334 def clear_messages(self):
335 """Clear the list of received messages.
336
337 Call this at least in the tests setUp and tearDown methods. It's
338 important to do it in tearDown too because otherwise there may
339 be cyclic references.
340 """
341 self.received_messages = []
342
343 def subscribe_no_params(self):
344 """Method for subscriptions without parameters.
345
346 Add an empty tuple to the list of received messages.
347 """
348 self.received_messages.append(())
349
350 def subscribe_with_params(self, *args):
351 """Method for subscriptions with parameters.
352
353 Append the tuple will all arguments to this function (except for
354 the self argument) to the list of received messages.
355 """
356 self.received_messages.append(args)
357
358 def check_messages(self, messages):
359 """Check whether the messages received match the list messages"""
360 self.assertEquals(messages, self.received_messages)
361

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26