23 """Support code for running unit tests"""
47 memId0 = dafBase.Citizen_getNextMemId()
49 def run(suite, exit=True):
50 """Exit with the status code resulting from running the provided test suite"""
52 if unittest.TextTestRunner().
run(suite).wasSuccessful():
65 """Check for memory leaks since memId0 was allocated"""
70 """Check for memory leaks in the preceding tests"""
73 global memId0, nleakPrintMax
74 nleak = dafBase.Citizen_census(0, memId0)
76 print "\n%d Objects leaked:" % dafBase.Citizen_census(0, memId0)
78 if nleak <= nleakPrintMax:
79 print dafBase.Citizen_census(dafBase.cout, memId0)
81 census = dafBase.Citizen_census()
83 for i
in range(nleakPrintMax - 1, -1, -1):
84 print census[i].repr()
86 self.fail(
"Leaked %d blocks" % dafBase.Citizen_census(0, memId0))
91 """Find file which is specified as a path relative to the toplevel directory;
92 we start in $cwd and walk up until we find the file (or throw IOError if it doesn't exist)
94 This is useful for running tests that may be run from <dir>/tests or <dir>"""
96 if os.path.isfile(ifile):
102 dirname, basename = os.path.split(file)
104 ofile = os.path.join(basename, ofile)
108 if os.path.isfile(ofile):
113 raise IOError,
"Can't find %s" % ifile
118 """A class to be used with a with statement to ensure that a file is deleted
121 with temporaryFile("foo.fits") as filename:
122 image.writeFits(filename)
123 readImage = Image(filename)
141 """Subclass of unittest.TestCase that adds some custom assertions for
146 """A decorator to add a free function to our custom TestCase class, while also
147 making it available as a free function.
149 setattr(TestCase, func.__name__, func)
156 warnings.warn(
"assertRaisesLsstCpp is deprecated; please just use TestCase.assertRaises",
158 return testcase.assertRaises(excClass, callableObj, *args, **kwargs)
164 """Decorator to enter the debugger when there's an uncaught exception
166 To use, just slap a "@debugger()" on your function.
168 You may provide specific exception classes to catch as arguments to
169 the decorator function, e.g., "@debugger(RuntimeError, NotImplementedError)".
170 This defaults to just 'AssertionError', for use on unittest.TestCase methods.
172 Code provided by "Rosh Oxymoron" on StackOverflow:
173 http://stackoverflow.com/questions/4398967/python-unit-testing-automatically-running-the-debugger-when-a-test-fails
176 exceptions = (AssertionError, )
179 def wrapper(*args, **kwargs):
181 return f(*args, **kwargs)
184 pdb.post_mortem(sys.exc_info()[2])
191 """Plot the comparison of two 2-d NumPy arrays.
193 NOTE: this method uses matplotlib and imports it internally; it should be
194 wrapped in a try/except block within packages that do not depend on
195 matplotlib (including utils).
197 @param[in] lhs LHS values to compare; a 2-d NumPy array
198 @param[in] rhs RHS values to compare; a 2-d NumPy array
199 @param[in] bad A 2-d boolean NumPy array of values to emphasize in the plots
200 @param[in] plotFileName Filename to save the plot to. If None, the plot will be displayed in a
203 from matplotlib
import pyplot
209 badImage = numpy.zeros(bad.shape + (4,), dtype=numpy.uint8)
210 badImage[:,:,0] = 255
213 badImage[:,:,3] = 255*bad
214 vmin1 = numpy.minimum(numpy.min(lhs), numpy.min(rhs))
215 vmax1 = numpy.maximum(numpy.max(lhs), numpy.max(rhs))
216 vmin2 = numpy.min(diff)
217 vmax2 = numpy.max(diff)
218 for n, (image, title)
in enumerate([(lhs,
"lhs"), (rhs,
"rhs"), (diff,
"diff")]):
219 pyplot.subplot(2,3,n+1)
220 im1 = pyplot.imshow(image, cmap=pyplot.cm.gray, interpolation=
'nearest', origin=
'lower',
221 vmin=vmin1, vmax=vmax1)
223 pyplot.imshow(badImage, alpha=0.2, interpolation=
'nearest', origin=
'lower')
226 pyplot.subplot(2,3,n+4)
227 im2 = pyplot.imshow(image, cmap=pyplot.cm.gray, interpolation=
'nearest', origin=
'lower',
228 vmin=vmin2, vmax=vmax2)
230 pyplot.imshow(badImage, alpha=0.2, interpolation=
'nearest', origin=
'lower')
233 pyplot.subplots_adjust(left=0.05, bottom=0.05, top=0.92, right=0.75, wspace=0.05, hspace=0.05)
234 cax1 = pyplot.axes([0.8, 0.55, 0.05, 0.4])
235 pyplot.colorbar(im1, cax=cax1)
236 cax2 = pyplot.axes([0.8, 0.05, 0.05, 0.4])
237 pyplot.colorbar(im2, cax=cax2)
239 pyplot.savefig(plotFileName)
244 def assertClose(testCase, lhs, rhs, rtol=sys.float_info.epsilon, atol=sys.float_info.epsilon, relTo=None,
245 printFailures=
True, plotOnFailure=
False, plotFileName=
None, invert=
False):
246 """Highly-configurable floating point comparisons for scalars and arrays.
248 The test assertion will fail if all elements lhs and rhs are not equal to within the tolerances
249 specified by rtol and atol. More precisely, the comparison is:
251 abs(lhs - rhs) <= relTo*rtol OR abs(lhs - rhs) <= atol
253 If rtol or atol is None, that term in the comparison is not performed at all.
255 When not specified, relTo is the elementwise maximum of the absolute values of lhs and rhs. If
256 set manually, it should usually be set to either lhs or rhs, or a scalar value typical of what
259 @param[in] testCase unittest.TestCase instance the test is part of
260 @param[in] lhs LHS value(s) to compare; may be a scalar or a numpy array of any dimension
261 @param[in] rhs RHS value(s) to compare; may be a scalar or a numpy array of any dimension
262 @param[in] rtol Relative tolerance for comparison; defaults to double-precision epsilon.
263 @param[in] atol Absolute tolerance for comparison; defaults to double-precision epsilon.
264 @param[in] relTo Value to which comparison with rtol is relative.
265 @param[in] printFailures Upon failure, print all inequal elements as part of the message.
266 @param[in] plotOnFailure Upon failure, plot the originals and their residual with matplotlib.
267 Only 2-d arrays are supported.
268 @param[in] plotFileName Filename to save the plot to. If None, the plot will be displayed in a
270 @param[in] invert If True, invert the comparison and fail only if any elements *are* equal.
271 Used to implement assertNotClose, which should generally be used instead
274 if not numpy.isfinite(lhs).
all():
275 testCase.fail(
"Non-finite values in lhs")
276 if not numpy.isfinite(rhs).
all():
277 testCase.fail(
"Non-finite values in rhs")
279 absDiff = numpy.abs(lhs - rhs)
282 relTo = numpy.maximum(numpy.abs(lhs), numpy.abs(rhs))
284 relTo = numpy.abs(relTo)
285 bad = absDiff > rtol*relTo
287 bad = numpy.logical_and(bad, absDiff > atol)
290 raise ValueError(
"rtol and atol cannot both be None")
292 failed = numpy.any(bad)
295 bad = numpy.logical_not(bad)
297 failStr =
"are the same"
303 if numpy.isscalar(bad):
304 msg = [
"%s %s %s; diff=%s/%s=%s with rtol=%s, atol=%s"
305 % (lhs, cmpStr, rhs, absDiff, relTo, absDiff/relTo, rtol, atol)]
307 msg = [
"%d/%d elements %s with rtol=%s, atol=%s"
308 % (bad.sum(), bad.size, failStr, rtol, atol)]
310 if len(lhs.shape) != 2
or len(rhs.shape) != 2:
311 raise ValueError(
"plotOnFailure is only valid for 2-d arrays")
313 plotImageDiff(lhs, rhs, bad, diff=diff, plotFileName=plotFileName)
315 msg.append(
"Failure plot requested but matplotlib could not be imported.")
320 if numpy.isscalar(relTo):
321 relTo = numpy.ones(bad.shape, dtype=float) * relTo
322 if numpy.isscalar(lhs):
323 lhs = numpy.ones(bad.shape, dtype=float) * lhs
324 if numpy.isscalar(rhs):
325 rhs = numpy.ones(bad.shape, dtype=float) * rhs
326 for a, b, diff, rel
in zip(lhs[bad], rhs[bad], absDiff[bad], relTo[bad]):
327 msg.append(
"%s %s %s (diff=%s/%s=%s)" % (a, cmpStr, b, diff, rel, diff/rel))
328 testCase.assertFalse(failed, msg=
"\n".join(msg))
332 """Fail a test if the given floating point values are completely equal to within the given tolerances.
334 See assertClose for more information.
336 return assertClose(testCase, lhs, rhs, invert=
True, **kwds)
boost::enable_if< typename ExpressionTraits< Scalar >::IsScalar, bool >::type all(Scalar const &scalar)