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