lsst.utils  14.0-7-g6a9b684
tests.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 #
4 # Copyright 2008-2017 AURA/LSST.
5 #
6 # This product includes software developed by the
7 # LSST Project (http://www.lsst.org/).
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 3 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,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the LSST License Statement and
20 # the GNU General Public License along with this program. If not,
21 # see <https://www.lsstcorp.org/LegalNotices/>.
22 #
23 """Support code for running unit tests"""
24 from __future__ import print_function
25 from __future__ import division
26 from builtins import zip
27 from builtins import range
28 
29 from contextlib import contextmanager
30 import gc
31 import inspect
32 import os
33 import subprocess
34 import sys
35 import unittest
36 import warnings
37 import numpy
38 import functools
39 import tempfile
40 
41 # File descriptor leak test will be skipped if psutil can not be imported
42 try:
43  import psutil
44 except ImportError:
45  psutil = None
46 
47 try:
48  import lsst.daf.base as dafBase
49 except ImportError:
50  dafBase = None
51 
52 try:
53  type(memId0)
54 except NameError:
55  memId0 = 0 # ignore leaked blocks with IDs before memId0
56  nleakPrintMax = 20 # maximum number of leaked blocks to print
57 
58 # Initialize the list of open files to an empty set
59 open_files = set()
60 
61 
62 def _get_open_files():
63  """Return a set containing the list of open files."""
64  if psutil is None:
65  return set()
66  return set(p.path for p in psutil.Process().open_files())
67 
68 
69 def init():
70  """Initialize the memory tester"""
71  global memId0
72  global open_files
73  if dafBase:
74  memId0 = dafBase.Citizen.getNextMemId() # used by MemoryTestCase
75  # Reset the list of open files
76  open_files = _get_open_files()
77 
78 
79 def run(suite, exit=True):
80  """!Exit with the status code resulting from running the provided test suite"""
81 
82  if unittest.TextTestRunner().run(suite).wasSuccessful():
83  status = 0
84  else:
85  status = 1
86 
87  if exit:
88  sys.exit(status)
89  else:
90  return status
91 
92 
93 def sort_tests(tests):
94  """!Go through the supplied sequence of test suites and sort them to ensure that
95  MemoryTestCases are at the end of the test list. Returns a combined
96  TestSuite."""
97 
98  suite = unittest.TestSuite()
99  memtests = []
100  for test_suite in tests:
101  try:
102  # Just test the first test method in the suite for MemoryTestCase
103  # Use loop rather than next as it is possible for a test class
104  # to not have any test methods and the Python community prefers
105  # for loops over catching a StopIteration exception.
106  bases = None
107  for method in test_suite:
108  bases = inspect.getmro(method.__class__)
109  break
110  if bases is not None and MemoryTestCase in bases:
111  memtests.append(test_suite)
112  else:
113  suite.addTests(test_suite)
114  except TypeError:
115  if isinstance(test_suite, MemoryTestCase):
116  memtests.append(test_suite)
117  else:
118  suite.addTest(test_suite)
119  suite.addTests(memtests)
120  return suite
121 
122 
123 def suiteClassWrapper(tests):
124  return unittest.TestSuite(sort_tests(tests))
125 
126 
127 # Replace the suiteClass callable in the defaultTestLoader
128 # so that we can reorder the test ordering. This will have
129 # no effect if no memory test cases are found.
130 unittest.defaultTestLoader.suiteClass = suiteClassWrapper
131 
132 
133 class MemoryTestCase(unittest.TestCase):
134  """!Check for memory leaks since memId0 was allocated"""
135 
136  def setUp(self):
137  pass
138 
139  @classmethod
140  def tearDownClass(cls):
141  """!Reset the leak counter when the tests have been completed"""
142  init()
143 
144  def testLeaks(self):
145  """!Check for memory leaks in the preceding tests"""
146  if dafBase:
147  gc.collect()
148  global memId0, nleakPrintMax
149  nleak = dafBase.Citizen.census(0, memId0)
150  if nleak != 0:
151  plural = "s" if nleak != 1 else ""
152  print("\n%d Object%s leaked:" % (nleak, plural))
153 
154  if nleak <= nleakPrintMax:
155  print(dafBase.Citizen.census(memId0))
156  else:
157  census = dafBase.Citizen.census()
158  print("...")
159  for i in range(nleakPrintMax - 1, -1, -1):
160  print(census[i].repr())
161 
162  self.fail("Leaked %d block%s" % (nleak, plural))
163 
165  if psutil is None:
166  self.skipTest("Unable to test file descriptor leaks. psutil unavailable.")
167  gc.collect()
168  global open_files
169  now_open = _get_open_files()
170 
171  # Some files are opened out of the control of the stack.
172  now_open = set(f for f in now_open if not f.endswith(".car") and
173  not f.startswith("/proc/") and
174  not f.endswith(".ttf") and
175  f != "/var/lib/sss/mc/passwd" and
176  not f.endswith("astropy.log"))
177 
178  diff = now_open.difference(open_files)
179  if diff:
180  for f in diff:
181  print("File open: %s" % f)
182  self.fail("Failed to close %d file%s" % (len(diff), "s" if len(diff) != 1 else ""))
183 
184 
185 class ExecutablesTestCase(unittest.TestCase):
186  """!Test that executables can be run and return good status.
187 
188  The test methods are dynamically created. Callers
189  must subclass this class in their own test file and invoke
190  the create_executable_tests() class method to register the tests.
191  """
192  TESTS_DISCOVERED = -1
193 
194  @classmethod
195  def setUpClass(cls):
196  """Abort testing if automated test creation was enabled and
197  yet not tests were found."""
198 
199  if cls.TESTS_DISCOVERED == 0:
200  raise Exception("No executables discovered.")
201 
202  def testSanity(self):
203  """This test exists to ensure that there is at least one test to be
204  executed. This allows the test runner to trigger the class set up
205  machinery to test whether there are some executables to test."""
206  pass
207 
208  def assertExecutable(self, executable, root_dir=None, args=None, msg=None):
209  """!Check an executable runs and returns good status.
210 
211  @param executable: Path to an executable. root_dir is not used
212  if this is an absolute path.
213 
214  @param root_dir: Directory containing exe. Ignored if None.
215 
216  @param args: List or tuple of arguments to be provided to the
217  executable.
218 
219  @param msg: Message to use when the test fails. Can be None for
220  default message.
221 
222  Prints output to standard out. On bad exit status the test
223  fails. If the executable can not be located the test is skipped.
224  """
225 
226  if root_dir is not None and not os.path.isabs(executable):
227  executable = os.path.join(root_dir, executable)
228 
229  # Form the argument list for subprocess
230  sp_args = [executable]
231  argstr = "no arguments"
232  if args is not None:
233  sp_args.extend(args)
234  argstr = 'arguments "' + " ".join(args) + '"'
235 
236  print("Running executable '{}' with {}...".format(executable, argstr))
237  if not os.path.exists(executable):
238  self.skipTest("Executable {} is unexpectedly missing".format(executable))
239  failmsg = None
240  try:
241  output = subprocess.check_output(sp_args)
242  except subprocess.CalledProcessError as e:
243  output = e.output
244  failmsg = "Bad exit status from '{}': {}".format(executable, e.returncode)
245  print(output.decode('utf-8'))
246  if failmsg:
247  if msg is None:
248  msg = failmsg
249  self.fail(msg)
250 
251  @classmethod
252  def _build_test_method(cls, executable, root_dir):
253  """!Build a test method and attach to class.
254 
255  The method is built for the supplied excutable located
256  in the supplied root directory.
257 
258  cls._build_test_method(root_dir, executable)
259 
260  @param cls The class in which to create the tests.
261 
262  @param executable Name of executable. Can be absolute path.
263 
264  @param root_dir Path to executable. Not used if executable path is absolute.
265  """
266  if not os.path.isabs(executable):
267  executable = os.path.abspath(os.path.join(root_dir, executable))
268 
269  # Create the test name from the executable path.
270  test_name = "test_exe_" + executable.replace("/", "_")
271 
272  # This is the function that will become the test method
273  def test_executable_runs(*args):
274  self = args[0]
275  self.assertExecutable(executable)
276 
277  # Give it a name and attach it to the class
278  test_executable_runs.__name__ = test_name
279  setattr(cls, test_name, test_executable_runs)
280 
281  @classmethod
282  def create_executable_tests(cls, ref_file, executables=None):
283  """!Discover executables to test and create corresponding test methods.
284 
285  Scans the directory containing the supplied reference file
286  (usually __file__ supplied from the test class) to look for
287  executables. If executables are found a test method is created
288  for each one. That test method will run the executable and
289  check the returned value.
290 
291  Executable scripts with a .py extension and shared libraries
292  are ignored by the scanner.
293 
294  This class method must be called before test discovery.
295 
296  Example:
297 
298  cls.create_executable_tests(__file__)
299 
300  The list of executables can be overridden by passing in a
301  sequence of explicit executables that should be tested.
302  If an item in the sequence can not be found the
303  test will be configured to skip rather than fail.
304  """
305 
306  # Get the search directory from the reference file
307  ref_dir = os.path.abspath(os.path.dirname(ref_file))
308 
309  if executables is None:
310  # Look for executables to test by walking the tree
311  executables = []
312  for root, dirs, files in os.walk(ref_dir):
313  for f in files:
314  # Skip Python files. Shared libraries are executable.
315  if not f.endswith(".py") and not f.endswith(".so"):
316  full_path = os.path.join(root, f)
317  if os.access(full_path, os.X_OK):
318  executables.append(full_path)
319 
320  # Store the number of tests found for later assessment.
321  # Do not raise an exception if we have no executables as this would
322  # cause the testing to abort before the test runner could properly
323  # integrate it into the failure report.
324  cls.TESTS_DISCOVERED = len(executables)
325 
326  # Create the test functions and attach them to the class
327  for e in executables:
328  cls._build_test_method(e, ref_dir)
329 
330 
331 def findFileFromRoot(ifile):
332  """!Find file which is specified as a path relative to the toplevel directory;
333  we start in $cwd and walk up until we find the file (or throw IOError if it doesn't exist)
334 
335  This is useful for running tests that may be run from _dir_/tests or _dir_"""
336 
337  if os.path.isfile(ifile):
338  return ifile
339 
340  ofile = None
341  file = ifile
342  while file != "":
343  dirname, basename = os.path.split(file)
344  if ofile:
345  ofile = os.path.join(basename, ofile)
346  else:
347  ofile = basename
348 
349  if os.path.isfile(ofile):
350  return ofile
351 
352  file = dirname
353 
354  raise IOError("Can't find %s" % ifile)
355 
356 
357 @contextmanager
359  """!Return a path suitable for a temporary file and try to delete the file on success
360 
361  If the with block completes successfully then the file is deleted, if possible;
362  failure results in a printed warning.
363  If the block exits with an exception the file if left on disk so it can be examined.
364  The file name has a random component such that nested context managers can be used
365  with the same file suffix.
366 
367  @param[in] ext file name extension, e.g. ".fits"
368  @return path for a temporary file. The path is a combination of the caller's file path
369  and the name of the top-level function, as per this simple example:
370  @code
371  # file tests/testFoo.py
372  import unittest
373  import lsst.utils.tests
374  class FooTestCase(unittest.TestCase):
375  def testBasics(self):
376  self.runTest()
377 
378  def runTest(self):
379  with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
380  # if tests/.tests exists then tmpFile = "tests/.tests/testFoo_testBasics.fits"
381  # otherwise tmpFile = "testFoo_testBasics.fits"
382  ...
383  # at the end of this "with" block the path tmpFile will be deleted, but only if
384  # the file exists and the "with" block terminated normally (rather than with an exception)
385  ...
386  @endcode
387  """
388  stack = inspect.stack()
389  # get name of first function in the file
390  for i in range(2, len(stack)):
391  frameInfo = inspect.getframeinfo(stack[i][0])
392  if i == 2:
393  callerFilePath = frameInfo.filename
394  callerFuncName = frameInfo.function
395  elif callerFilePath == frameInfo.filename:
396  # this function called the previous function
397  callerFuncName = frameInfo.function
398  else:
399  break
400 
401  callerDir, callerFileNameWithExt = os.path.split(callerFilePath)
402  callerFileName = os.path.splitext(callerFileNameWithExt)[0]
403  outDir = os.path.join(callerDir, ".tests")
404  if not os.path.isdir(outDir):
405  outDir = ""
406  prefix = "%s_%s-" % (callerFileName, callerFuncName)
407  outPath = tempfile.mktemp(dir=outDir, suffix=ext, prefix=prefix)
408  yield outPath
409  if os.path.isfile(outPath):
410  try:
411  os.remove(outPath)
412  except OSError as e:
413  print("Warning: could not remove file %r: %s" % (outPath, e))
414  else:
415  print("Warning: could not find file %r" % (outPath,))
416 
417 
418 class TestCase(unittest.TestCase):
419  """!Subclass of unittest.TestCase that adds some custom assertions for
420  convenience.
421  """
422 
423 
424 def inTestCase(func):
425  """!A decorator to add a free function to our custom TestCase class, while also
426  making it available as a free function.
427  """
428  setattr(TestCase, func.__name__, func)
429  return func
430 
431 
432 @inTestCase
433 def assertRaisesLsstCpp(testcase, excClass, callableObj, *args, **kwargs):
434  warnings.warn("assertRaisesLsstCpp is deprecated; please just use TestCase.assertRaises",
435  DeprecationWarning, stacklevel=2)
436  return testcase.assertRaises(excClass, callableObj, *args, **kwargs)
437 
438 
439 def debugger(*exceptions):
440  """!Decorator to enter the debugger when there's an uncaught exception
441 
442  To use, just slap a "@debugger()" on your function.
443 
444  You may provide specific exception classes to catch as arguments to
445  the decorator function, e.g., "@debugger(RuntimeError, NotImplementedError)".
446  This defaults to just 'AssertionError', for use on unittest.TestCase methods.
447 
448  Code provided by "Rosh Oxymoron" on StackOverflow:
449  http://stackoverflow.com/questions/4398967/python-unit-testing-automatically-running-the-debugger-when-a-test-fails
450  """
451  if not exceptions:
452  exceptions = (AssertionError, )
453 
454  def decorator(f):
455  @functools.wraps(f)
456  def wrapper(*args, **kwargs):
457  try:
458  return f(*args, **kwargs)
459  except exceptions:
460  import sys
461  import pdb
462  pdb.post_mortem(sys.exc_info()[2])
463  return wrapper
464  return decorator
465 
466 
467 def plotImageDiff(lhs, rhs, bad=None, diff=None, plotFileName=None):
468  """!Plot the comparison of two 2-d NumPy arrays.
469 
470  NOTE: this method uses matplotlib and imports it internally; it should be
471  wrapped in a try/except block within packages that do not depend on
472  matplotlib (including utils).
473 
474  @param[in] lhs LHS values to compare; a 2-d NumPy array
475  @param[in] rhs RHS values to compare; a 2-d NumPy array
476  @param[in] bad A 2-d boolean NumPy array of values to emphasize in the plots
477  @param[in] diff difference array; a 2-d NumPy array, or None to show lhs-rhs
478  @param[in] plotFileName Filename to save the plot to. If None, the plot will be displayed in a
479  a window.
480  """
481  from matplotlib import pyplot
482  if diff is None:
483  diff = lhs - rhs
484  pyplot.figure()
485  if bad is not None:
486  # make an rgba image that's red and transparent where not bad
487  badImage = numpy.zeros(bad.shape + (4,), dtype=numpy.uint8)
488  badImage[:, :, 0] = 255
489  badImage[:, :, 1] = 0
490  badImage[:, :, 2] = 0
491  badImage[:, :, 3] = 255*bad
492  vmin1 = numpy.minimum(numpy.min(lhs), numpy.min(rhs))
493  vmax1 = numpy.maximum(numpy.max(lhs), numpy.max(rhs))
494  vmin2 = numpy.min(diff)
495  vmax2 = numpy.max(diff)
496  for n, (image, title) in enumerate([(lhs, "lhs"), (rhs, "rhs"), (diff, "diff")]):
497  pyplot.subplot(2, 3, n + 1)
498  im1 = pyplot.imshow(image, cmap=pyplot.cm.gray, interpolation='nearest', origin='lower',
499  vmin=vmin1, vmax=vmax1)
500  if bad is not None:
501  pyplot.imshow(badImage, alpha=0.2, interpolation='nearest', origin='lower')
502  pyplot.axis("off")
503  pyplot.title(title)
504  pyplot.subplot(2, 3, n + 4)
505  im2 = pyplot.imshow(image, cmap=pyplot.cm.gray, interpolation='nearest', origin='lower',
506  vmin=vmin2, vmax=vmax2)
507  if bad is not None:
508  pyplot.imshow(badImage, alpha=0.2, interpolation='nearest', origin='lower')
509  pyplot.axis("off")
510  pyplot.title(title)
511  pyplot.subplots_adjust(left=0.05, bottom=0.05, top=0.92, right=0.75, wspace=0.05, hspace=0.05)
512  cax1 = pyplot.axes([0.8, 0.55, 0.05, 0.4])
513  pyplot.colorbar(im1, cax=cax1)
514  cax2 = pyplot.axes([0.8, 0.05, 0.05, 0.4])
515  pyplot.colorbar(im2, cax=cax2)
516  if plotFileName:
517  pyplot.savefig(plotFileName)
518  else:
519  pyplot.show()
520 
521 
522 @inTestCase
523 def assertFloatsAlmostEqual(testCase, lhs, rhs, rtol=sys.float_info.epsilon,
524  atol=sys.float_info.epsilon, relTo=None,
525  printFailures=True, plotOnFailure=False,
526  plotFileName=None, invert=False, msg=None):
527  """!Highly-configurable floating point comparisons for scalars and arrays.
528 
529  The test assertion will fail if all elements lhs and rhs are not equal to within the tolerances
530  specified by rtol and atol. More precisely, the comparison is:
531 
532  abs(lhs - rhs) <= relTo*rtol OR abs(lhs - rhs) <= atol
533 
534  If rtol or atol is None, that term in the comparison is not performed at all.
535 
536  When not specified, relTo is the elementwise maximum of the absolute values of lhs and rhs. If
537  set manually, it should usually be set to either lhs or rhs, or a scalar value typical of what
538  is expected.
539 
540  @param[in] testCase unittest.TestCase instance the test is part of
541  @param[in] lhs LHS value(s) to compare; may be a scalar or array-like of any dimension
542  @param[in] rhs RHS value(s) to compare; may be a scalar or array-like of any dimension
543  @param[in] rtol Relative tolerance for comparison; defaults to double-precision epsilon.
544  @param[in] atol Absolute tolerance for comparison; defaults to double-precision epsilon.
545  @param[in] relTo Value to which comparison with rtol is relative.
546  @param[in] printFailures Upon failure, print all inequal elements as part of the message.
547  @param[in] plotOnFailure Upon failure, plot the originals and their residual with matplotlib.
548  Only 2-d arrays are supported.
549  @param[in] plotFileName Filename to save the plot to. If None, the plot will be displayed in a
550  a window.
551  @param[in] invert If True, invert the comparison and fail only if any elements *are* equal.
552  Used to implement assertFloatsNotEqual, which should generally be used instead
553  for clarity.
554  @param[in] msg String to append to the error message when assert fails.
555  """
556  if not numpy.isfinite(lhs).all():
557  testCase.fail("Non-finite values in lhs")
558  if not numpy.isfinite(rhs).all():
559  testCase.fail("Non-finite values in rhs")
560  diff = lhs - rhs
561  absDiff = numpy.abs(lhs - rhs)
562  if rtol is not None:
563  if relTo is None:
564  relTo = numpy.maximum(numpy.abs(lhs), numpy.abs(rhs))
565  else:
566  relTo = numpy.abs(relTo)
567  bad = absDiff > rtol*relTo
568  if atol is not None:
569  bad = numpy.logical_and(bad, absDiff > atol)
570  else:
571  if atol is None:
572  raise ValueError("rtol and atol cannot both be None")
573  bad = absDiff > atol
574  failed = numpy.any(bad)
575  if invert:
576  failed = not failed
577  bad = numpy.logical_not(bad)
578  cmpStr = "=="
579  failStr = "are the same"
580  else:
581  cmpStr = "!="
582  failStr = "differ"
583  errMsg = []
584  if failed:
585  if numpy.isscalar(bad):
586  if rtol is None:
587  errMsg = ["%s %s %s; diff=%s with atol=%s"
588  % (lhs, cmpStr, rhs, absDiff, atol)]
589  elif atol is None:
590  errMsg = ["%s %s %s; diff=%s/%s=%s with rtol=%s"
591  % (lhs, cmpStr, rhs, absDiff, relTo, absDiff/relTo, rtol)]
592  else:
593  errMsg = ["%s %s %s; diff=%s/%s=%s with rtol=%s, atol=%s"
594  % (lhs, cmpStr, rhs, absDiff, relTo, absDiff/relTo, rtol, atol)]
595  else:
596  errMsg = ["%d/%d elements %s with rtol=%s, atol=%s"
597  % (bad.sum(), bad.size, failStr, rtol, atol)]
598  if plotOnFailure:
599  if len(lhs.shape) != 2 or len(rhs.shape) != 2:
600  raise ValueError("plotOnFailure is only valid for 2-d arrays")
601  try:
602  plotImageDiff(lhs, rhs, bad, diff=diff, plotFileName=plotFileName)
603  except ImportError:
604  errMsg.append("Failure plot requested but matplotlib could not be imported.")
605  if printFailures:
606  # Make sure everything is an array if any of them are, so we can treat
607  # them the same (diff and absDiff are arrays if either rhs or lhs is),
608  # and we don't get here if neither is.
609  if numpy.isscalar(relTo):
610  relTo = numpy.ones(bad.shape, dtype=float) * relTo
611  if numpy.isscalar(lhs):
612  lhs = numpy.ones(bad.shape, dtype=float) * lhs
613  if numpy.isscalar(rhs):
614  rhs = numpy.ones(bad.shape, dtype=float) * rhs
615  if rtol is None:
616  for a, b, diff in zip(lhs[bad], rhs[bad], absDiff[bad]):
617  errMsg.append("%s %s %s (diff=%s)" % (a, cmpStr, b, diff))
618  else:
619  for a, b, diff, rel in zip(lhs[bad], rhs[bad], absDiff[bad], relTo[bad]):
620  errMsg.append("%s %s %s (diff=%s/%s=%s)" % (a, cmpStr, b, diff, rel, diff/rel))
621 
622  if msg is not None:
623  errMsg.append(msg)
624  testCase.assertFalse(failed, msg="\n".join(errMsg))
625 
626 
627 @inTestCase
628 def assertFloatsNotEqual(testCase, lhs, rhs, **kwds):
629  """
630  Fail a test if the given floating point values are equal to within the given tolerances.
631 
632  See assertClose for more information.
633  """
634  return assertFloatsAlmostEqual(testCase, lhs, rhs, invert=True, **kwds)
635 
636 
637 @inTestCase
638 def assertFloatsEqual(testCase, lhs, rhs, **kwargs):
639  """
640  Assert that lhs == rhs (both numeric types, whether scalar or array).
641 
642  See assertClose (called with rtol=atol=0) for more information.
643  """
644  return assertFloatsAlmostEqual(testCase, lhs, rhs, rtol=0, atol=0, **kwargs)
645 
646 
647 @inTestCase
648 def assertClose(*args, **kwargs):
649  warnings.warn("assertClose is deprecated; please use TestCase.assertFloatsAlmostEqual",
650  DeprecationWarning, stacklevel=2)
651  return assertFloatsAlmostEqual(*args, **kwargs)
652 
653 
654 @inTestCase
655 def assertNotClose(*args, **kwargs):
656  warnings.warn("assertNotClose is deprecated; please use TestCase.assertFloatsNotEqual",
657  DeprecationWarning, stacklevel=2)
658  return assertFloatsNotEqual(*args, **kwargs)
def suiteClassWrapper(tests)
Definition: tests.py:123
def assertExecutable(self, executable, root_dir=None, args=None, msg=None)
Check an executable runs and returns good status.
Definition: tests.py:208
def testLeaks(self)
Check for memory leaks in the preceding tests.
Definition: tests.py:144
def assertFloatsEqual(testCase, lhs, rhs, kwargs)
Definition: tests.py:638
def init()
Definition: tests.py:69
def tearDownClass(cls)
Reset the leak counter when the tests have been completed.
Definition: tests.py:140
def plotImageDiff(lhs, rhs, bad=None, diff=None, plotFileName=None)
Plot the comparison of two 2-d NumPy arrays.
Definition: tests.py:467
def inTestCase(func)
A decorator to add a free function to our custom TestCase class, while also making it available as a ...
Definition: tests.py:424
def assertClose(args, kwargs)
Definition: tests.py:648
def _build_test_method(cls, executable, root_dir)
Build a test method and attach to class.
Definition: tests.py:252
def assertFloatsAlmostEqual(testCase, lhs, rhs, rtol=sys.float_info.epsilon, atol=sys.float_info.epsilon, relTo=None, printFailures=True, plotOnFailure=False, plotFileName=None, invert=False, msg=None)
Highly-configurable floating point comparisons for scalars and arrays.
Definition: tests.py:526
def assertFloatsNotEqual(testCase, lhs, rhs, kwds)
Definition: tests.py:628
Subclass of unittest.TestCase that adds some custom assertions for convenience.
Definition: tests.py:418
def run(suite, exit=True)
Exit with the status code resulting from running the provided test suite.
Definition: tests.py:79
Test that executables can be run and return good status.
Definition: tests.py:185
def debugger(exceptions)
Decorator to enter the debugger when there&#39;s an uncaught exception.
Definition: tests.py:439
def findFileFromRoot(ifile)
Find file which is specified as a path relative to the toplevel directory; we start in $cwd and walk ...
Definition: tests.py:331
def assertNotClose(args, kwargs)
Definition: tests.py:655
def create_executable_tests(cls, ref_file, executables=None)
Discover executables to test and create corresponding test methods.
Definition: tests.py:282
def assertRaisesLsstCpp(testcase, excClass, callableObj, args, kwargs)
Definition: tests.py:433
def getTempFilePath(ext)
Return a path suitable for a temporary file and try to delete the file on success.
Definition: tests.py:358
def sort_tests(tests)
Go through the supplied sequence of test suites and sort them to ensure that MemoryTestCases are at t...
Definition: tests.py:93
Check for memory leaks since memId0 was allocated.
Definition: tests.py:133