lsst.utils  13.0-8-gb7ca535+4
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 discover_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) and 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  cls.discover_tests(__file__)
295 
296  The list of executables can be overridden by passing in a
297  sequence of explicit executables that should be tested.
298  If an item in the sequence can not be found the
299  test will be configured to skip rather than fail.
300  """
301 
302  # Get the search directory from the reference file
303  ref_dir = os.path.abspath(os.path.dirname(ref_file))
304 
305  if executables is None:
306  # Look for executables to test by walking the tree
307  executables = []
308  for root, dirs, files in os.walk(ref_dir):
309  for f in files:
310  # Skip Python files. Shared libraries are exectuable.
311  if not f.endswith(".py") and not f.endswith(".so"):
312  full_path = os.path.join(root, f)
313  if os.access(full_path, os.X_OK):
314  executables.append(full_path)
315 
316  # Store the number of tests found for later assessment.
317  # Do not raise an exception if we have no executables as this would
318  # cause the testing to abort before the test runner could properly
319  # integrate it into the failure report.
320  cls.TESTS_DISCOVERED = len(executables)
321 
322  # Create the test functions and attach them to the class
323  for e in executables:
324  cls._build_test_method(e, ref_dir)
325 
326 
327 def findFileFromRoot(ifile):
328  """!Find file which is specified as a path relative to the toplevel directory;
329  we start in $cwd and walk up until we find the file (or throw IOError if it doesn't exist)
330 
331  This is useful for running tests that may be run from _dir_/tests or _dir_"""
332 
333  if os.path.isfile(ifile):
334  return ifile
335 
336  ofile = None
337  file = ifile
338  while file != "":
339  dirname, basename = os.path.split(file)
340  if ofile:
341  ofile = os.path.join(basename, ofile)
342  else:
343  ofile = basename
344 
345  if os.path.isfile(ofile):
346  return ofile
347 
348  file = dirname
349 
350  raise IOError("Can't find %s" % ifile)
351 
352 
353 @contextmanager
355  """!Return a path suitable for a temporary file and try to delete the file on success
356 
357  If the with block completes successfully then the file is deleted, if possible;
358  failure results in a printed warning.
359  If the block exits with an exception the file if left on disk so it can be examined.
360 
361  @param[in] ext file name extension, e.g. ".fits"
362  @return path for a temporary file. The path is a combination of the caller's file path
363  and the name of the top-level function, as per this simple example:
364  @code
365  # file tests/testFoo.py
366  import unittest
367  import lsst.utils.tests
368  class FooTestCase(unittest.TestCase):
369  def testBasics(self):
370  self.runTest()
371 
372  def runTest(self):
373  with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
374  # if tests/.tests exists then tmpFile = "tests/.tests/testFoo_testBasics.fits"
375  # otherwise tmpFile = "testFoo_testBasics.fits"
376  ...
377  # at the end of this "with" block the path tmpFile will be deleted, but only if
378  # the file exists and the "with" block terminated normally (rather than with an exception)
379  ...
380  @endcode
381  """
382  stack = inspect.stack()
383  # get name of first function in the file
384  for i in range(2, len(stack)):
385  frameInfo = inspect.getframeinfo(stack[i][0])
386  if i == 2:
387  callerFilePath = frameInfo.filename
388  callerFuncName = frameInfo.function
389  elif callerFilePath == frameInfo.filename:
390  # this function called the previous function
391  callerFuncName = frameInfo.function
392  else:
393  break
394 
395  callerDir, callerFileNameWithExt = os.path.split(callerFilePath)
396  callerFileName = os.path.splitext(callerFileNameWithExt)[0]
397  outDir = os.path.join(callerDir, ".tests")
398  if not os.path.isdir(outDir):
399  outDir = ""
400  outName = "%s_%s%s" % (callerFileName, callerFuncName, ext)
401  outPath = os.path.join(outDir, outName)
402  yield outPath
403  if os.path.isfile(outPath):
404  try:
405  os.remove(outPath)
406  except OSError as e:
407  print("Warning: could not remove file %r: %s" % (outPath, e))
408  else:
409  print("Warning: could not find file %r" % (outPath,))
410 
411 
412 class TestCase(unittest.TestCase):
413  """!Subclass of unittest.TestCase that adds some custom assertions for
414  convenience.
415  """
416 
417 
418 def inTestCase(func):
419  """!A decorator to add a free function to our custom TestCase class, while also
420  making it available as a free function.
421  """
422  setattr(TestCase, func.__name__, func)
423  return func
424 
425 
426 @inTestCase
427 def assertRaisesLsstCpp(testcase, excClass, callableObj, *args, **kwargs):
428  warnings.warn("assertRaisesLsstCpp is deprecated; please just use TestCase.assertRaises",
429  DeprecationWarning)
430  return testcase.assertRaises(excClass, callableObj, *args, **kwargs)
431 
432 
433 def debugger(*exceptions):
434  """!Decorator to enter the debugger when there's an uncaught exception
435 
436  To use, just slap a "@debugger()" on your function.
437 
438  You may provide specific exception classes to catch as arguments to
439  the decorator function, e.g., "@debugger(RuntimeError, NotImplementedError)".
440  This defaults to just 'AssertionError', for use on unittest.TestCase methods.
441 
442  Code provided by "Rosh Oxymoron" on StackOverflow:
443  http://stackoverflow.com/questions/4398967/python-unit-testing-automatically-running-the-debugger-when-a-test-fails
444  """
445  if not exceptions:
446  exceptions = (AssertionError, )
447 
448  def decorator(f):
449  @functools.wraps(f)
450  def wrapper(*args, **kwargs):
451  try:
452  return f(*args, **kwargs)
453  except exceptions:
454  import sys
455  import pdb
456  pdb.post_mortem(sys.exc_info()[2])
457  return wrapper
458  return decorator
459 
460 
461 def plotImageDiff(lhs, rhs, bad=None, diff=None, plotFileName=None):
462  """!Plot the comparison of two 2-d NumPy arrays.
463 
464  NOTE: this method uses matplotlib and imports it internally; it should be
465  wrapped in a try/except block within packages that do not depend on
466  matplotlib (including utils).
467 
468  @param[in] lhs LHS values to compare; a 2-d NumPy array
469  @param[in] rhs RHS values to compare; a 2-d NumPy array
470  @param[in] bad A 2-d boolean NumPy array of values to emphasize in the plots
471  @param[in] diff difference array; a 2-d NumPy array, or None to show lhs-rhs
472  @param[in] plotFileName Filename to save the plot to. If None, the plot will be displayed in a
473  a window.
474  """
475  from matplotlib import pyplot
476  if diff is None:
477  diff = lhs - rhs
478  pyplot.figure()
479  if bad is not None:
480  # make an rgba image that's red and transparent where not bad
481  badImage = numpy.zeros(bad.shape + (4,), dtype=numpy.uint8)
482  badImage[:, :, 0] = 255
483  badImage[:, :, 1] = 0
484  badImage[:, :, 2] = 0
485  badImage[:, :, 3] = 255*bad
486  vmin1 = numpy.minimum(numpy.min(lhs), numpy.min(rhs))
487  vmax1 = numpy.maximum(numpy.max(lhs), numpy.max(rhs))
488  vmin2 = numpy.min(diff)
489  vmax2 = numpy.max(diff)
490  for n, (image, title) in enumerate([(lhs, "lhs"), (rhs, "rhs"), (diff, "diff")]):
491  pyplot.subplot(2, 3, n + 1)
492  im1 = pyplot.imshow(image, cmap=pyplot.cm.gray, interpolation='nearest', origin='lower',
493  vmin=vmin1, vmax=vmax1)
494  if bad is not None:
495  pyplot.imshow(badImage, alpha=0.2, interpolation='nearest', origin='lower')
496  pyplot.axis("off")
497  pyplot.title(title)
498  pyplot.subplot(2, 3, n + 4)
499  im2 = pyplot.imshow(image, cmap=pyplot.cm.gray, interpolation='nearest', origin='lower',
500  vmin=vmin2, vmax=vmax2)
501  if bad is not None:
502  pyplot.imshow(badImage, alpha=0.2, interpolation='nearest', origin='lower')
503  pyplot.axis("off")
504  pyplot.title(title)
505  pyplot.subplots_adjust(left=0.05, bottom=0.05, top=0.92, right=0.75, wspace=0.05, hspace=0.05)
506  cax1 = pyplot.axes([0.8, 0.55, 0.05, 0.4])
507  pyplot.colorbar(im1, cax=cax1)
508  cax2 = pyplot.axes([0.8, 0.05, 0.05, 0.4])
509  pyplot.colorbar(im2, cax=cax2)
510  if plotFileName:
511  pyplot.savefig(plotFileName)
512  else:
513  pyplot.show()
514 
515 
516 @inTestCase
517 def assertFloatsAlmostEqual(testCase, lhs, rhs, rtol=sys.float_info.epsilon,
518  atol=sys.float_info.epsilon, relTo=None,
519  printFailures=True, plotOnFailure=False,
520  plotFileName=None, invert=False, msg=None):
521  """!Highly-configurable floating point comparisons for scalars and arrays.
522 
523  The test assertion will fail if all elements lhs and rhs are not equal to within the tolerances
524  specified by rtol and atol. More precisely, the comparison is:
525 
526  abs(lhs - rhs) <= relTo*rtol OR abs(lhs - rhs) <= atol
527 
528  If rtol or atol is None, that term in the comparison is not performed at all.
529 
530  When not specified, relTo is the elementwise maximum of the absolute values of lhs and rhs. If
531  set manually, it should usually be set to either lhs or rhs, or a scalar value typical of what
532  is expected.
533 
534  @param[in] testCase unittest.TestCase instance the test is part of
535  @param[in] lhs LHS value(s) to compare; may be a scalar or array-like of any dimension
536  @param[in] rhs RHS value(s) to compare; may be a scalar or array-like of any dimension
537  @param[in] rtol Relative tolerance for comparison; defaults to double-precision epsilon.
538  @param[in] atol Absolute tolerance for comparison; defaults to double-precision epsilon.
539  @param[in] relTo Value to which comparison with rtol is relative.
540  @param[in] printFailures Upon failure, print all inequal elements as part of the message.
541  @param[in] plotOnFailure Upon failure, plot the originals and their residual with matplotlib.
542  Only 2-d arrays are supported.
543  @param[in] plotFileName Filename to save the plot to. If None, the plot will be displayed in a
544  a window.
545  @param[in] invert If True, invert the comparison and fail only if any elements *are* equal.
546  Used to implement assertFloatsNotEqual, which should generally be used instead
547  for clarity.
548  @param[in] msg String to append to the error message when assert fails.
549  """
550  if not numpy.isfinite(lhs).all():
551  testCase.fail("Non-finite values in lhs")
552  if not numpy.isfinite(rhs).all():
553  testCase.fail("Non-finite values in rhs")
554  diff = lhs - rhs
555  absDiff = numpy.abs(lhs - rhs)
556  if rtol is not None:
557  if relTo is None:
558  relTo = numpy.maximum(numpy.abs(lhs), numpy.abs(rhs))
559  else:
560  relTo = numpy.abs(relTo)
561  bad = absDiff > rtol*relTo
562  if atol is not None:
563  bad = numpy.logical_and(bad, absDiff > atol)
564  else:
565  if atol is None:
566  raise ValueError("rtol and atol cannot both be None")
567  bad = absDiff > atol
568  failed = numpy.any(bad)
569  if invert:
570  failed = not failed
571  bad = numpy.logical_not(bad)
572  cmpStr = "=="
573  failStr = "are the same"
574  else:
575  cmpStr = "!="
576  failStr = "differ"
577  errMsg = []
578  if failed:
579  if numpy.isscalar(bad):
580  if rtol is None:
581  errMsg = ["%s %s %s; diff=%s with atol=%s"
582  % (lhs, cmpStr, rhs, absDiff, atol)]
583  elif atol is None:
584  errMsg = ["%s %s %s; diff=%s/%s=%s with rtol=%s"
585  % (lhs, cmpStr, rhs, absDiff, relTo, absDiff/relTo, rtol)]
586  else:
587  errMsg = ["%s %s %s; diff=%s/%s=%s with rtol=%s, atol=%s"
588  % (lhs, cmpStr, rhs, absDiff, relTo, absDiff/relTo, rtol, atol)]
589  else:
590  errMsg = ["%d/%d elements %s with rtol=%s, atol=%s"
591  % (bad.sum(), bad.size, failStr, rtol, atol)]
592  if plotOnFailure:
593  if len(lhs.shape) != 2 or len(rhs.shape) != 2:
594  raise ValueError("plotOnFailure is only valid for 2-d arrays")
595  try:
596  plotImageDiff(lhs, rhs, bad, diff=diff, plotFileName=plotFileName)
597  except ImportError:
598  errMsg.append("Failure plot requested but matplotlib could not be imported.")
599  if printFailures:
600  # Make sure everything is an array if any of them are, so we can treat
601  # them the same (diff and absDiff are arrays if either rhs or lhs is),
602  # and we don't get here if neither is.
603  if numpy.isscalar(relTo):
604  relTo = numpy.ones(bad.shape, dtype=float) * relTo
605  if numpy.isscalar(lhs):
606  lhs = numpy.ones(bad.shape, dtype=float) * lhs
607  if numpy.isscalar(rhs):
608  rhs = numpy.ones(bad.shape, dtype=float) * rhs
609  if rtol is None:
610  for a, b, diff in zip(lhs[bad], rhs[bad], absDiff[bad]):
611  errMsg.append("%s %s %s (diff=%s)" % (a, cmpStr, b, diff))
612  else:
613  for a, b, diff, rel in zip(lhs[bad], rhs[bad], absDiff[bad], relTo[bad]):
614  errMsg.append("%s %s %s (diff=%s/%s=%s)" % (a, cmpStr, b, diff, rel, diff/rel))
615 
616  if msg is not None:
617  errMsg.append(msg)
618  testCase.assertFalse(failed, msg="\n".join(errMsg))
619 
620 
621 @inTestCase
622 def assertFloatsNotEqual(testCase, lhs, rhs, **kwds):
623  """
624  Fail a test if the given floating point values are equal to within the given tolerances.
625 
626  See assertClose for more information.
627  """
628  return assertFloatsAlmostEqual(testCase, lhs, rhs, invert=True, **kwds)
629 
630 
631 @inTestCase
632 def assertFloatsEqual(testCase, lhs, rhs, **kwargs):
633  """
634  Assert that lhs == rhs (both numeric types, whether scalar or array).
635 
636  See assertClose (called with rtol=atol=0) for more information.
637  """
638  return assertFloatsAlmostEqual(testCase, lhs, rhs, rtol=0, atol=0, **kwargs)
639 
640 
641 @inTestCase
642 def assertClose(*args, **kwargs):
643  warnings.warn("assertClose is deprecated; please use TestCase.assertFloatsAlmostEqual",
644  DeprecationWarning)
645  return assertFloatsAlmostEqual(*args, **kwargs)
646 
647 
648 @inTestCase
649 def assertNotClose(*args, **kwargs):
650  warnings.warn("assertNotClose is deprecated; please use TestCase.assertFloatsNotEqual",
651  DeprecationWarning)
652  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:632
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:461
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:418
def assertClose(args, kwargs)
Definition: tests.py:642
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:520
def assertFloatsNotEqual(testCase, lhs, rhs, kwds)
Definition: tests.py:622
Subclass of unittest.TestCase that adds some custom assertions for convenience.
Definition: tests.py:412
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:433
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:327
def assertNotClose(args, kwargs)
Definition: tests.py:649
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:427
def getTempFilePath(ext)
Return a path suitable for a temporary file and try to delete the file on success.
Definition: tests.py:354
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