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