lsst.utils  14.0-9-g11010eb+1
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
358 def getTempFilePath(ext, expectOutput=True):
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 a file is remains when it should not, a RuntimeError exception is raised. This
364  exception is also raised if a file is not present on context manager exit when one
365  is expected to exist.
366  If the block exits with an exception the file if left on disk so it can be examined.
367  The file name has a random component such that nested context managers can be used
368  with the same file suffix.
369 
370  @param[in] ext file name extension, e.g. ".fits"
371  @param[in] expectOutput If true, a file should be created within the context manager.
372  If false, a file should not be present when the context manager
373  is exited.
374  @return path for a temporary file. The path is a combination of the caller's file path
375  and the name of the top-level function, as per this simple example:
376  @code
377  # file tests/testFoo.py
378  import unittest
379  import lsst.utils.tests
380  class FooTestCase(unittest.TestCase):
381  def testBasics(self):
382  self.runTest()
383 
384  def runTest(self):
385  with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
386  # if tests/.tests exists then tmpFile = "tests/.tests/testFoo_testBasics.fits"
387  # otherwise tmpFile = "testFoo_testBasics.fits"
388  ...
389  # at the end of this "with" block the path tmpFile will be deleted, but only if
390  # the file exists and the "with" block terminated normally (rather than with an exception)
391  ...
392  @endcode
393  """
394  stack = inspect.stack()
395  # get name of first function in the file
396  for i in range(2, len(stack)):
397  frameInfo = inspect.getframeinfo(stack[i][0])
398  if i == 2:
399  callerFilePath = frameInfo.filename
400  callerFuncName = frameInfo.function
401  elif callerFilePath == frameInfo.filename:
402  # this function called the previous function
403  callerFuncName = frameInfo.function
404  else:
405  break
406 
407  callerDir, callerFileNameWithExt = os.path.split(callerFilePath)
408  callerFileName = os.path.splitext(callerFileNameWithExt)[0]
409  outDir = os.path.join(callerDir, ".tests")
410  if not os.path.isdir(outDir):
411  outDir = ""
412  prefix = "%s_%s-" % (callerFileName, callerFuncName)
413  outPath = tempfile.mktemp(dir=outDir, suffix=ext, prefix=prefix)
414  if os.path.exists(outPath):
415  # There should not be a file there given the randomizer. Warn and remove.
416  # Use stacklevel 3 so that the warning is reported from the end of the with block
417  warnings.warn("Unexpectedly found pre-existing tempfile named %r" % (outPath,),
418  stacklevel=3)
419  try:
420  os.remove(outPath)
421  except OSError:
422  pass
423 
424  yield outPath
425 
426  fileExists = os.path.exists(outPath)
427  if expectOutput:
428  if not fileExists:
429  raise RuntimeError("Temp file expected named {} but none found".format(outPath))
430  else:
431  if fileExists:
432  raise RuntimeError("Unexpectedly discovered temp file named {}".format(outPath))
433  # Try to clean up the file regardless
434  if fileExists:
435  try:
436  os.remove(outPath)
437  except OSError as e:
438  # Use stacklevel 3 so that the warning is reported from the end of the with block
439  warnings.warn("Warning: could not remove file %r: %s" % (outPath, e), stacklevel=3)
440 
441 
442 class TestCase(unittest.TestCase):
443  """!Subclass of unittest.TestCase that adds some custom assertions for
444  convenience.
445  """
446 
447 
448 def inTestCase(func):
449  """!A decorator to add a free function to our custom TestCase class, while also
450  making it available as a free function.
451  """
452  setattr(TestCase, func.__name__, func)
453  return func
454 
455 
456 @inTestCase
457 def assertRaisesLsstCpp(testcase, excClass, callableObj, *args, **kwargs):
458  warnings.warn("assertRaisesLsstCpp is deprecated; please just use TestCase.assertRaises",
459  DeprecationWarning, stacklevel=2)
460  return testcase.assertRaises(excClass, callableObj, *args, **kwargs)
461 
462 
463 def debugger(*exceptions):
464  """!Decorator to enter the debugger when there's an uncaught exception
465 
466  To use, just slap a "@debugger()" on your function.
467 
468  You may provide specific exception classes to catch as arguments to
469  the decorator function, e.g., "@debugger(RuntimeError, NotImplementedError)".
470  This defaults to just 'AssertionError', for use on unittest.TestCase methods.
471 
472  Code provided by "Rosh Oxymoron" on StackOverflow:
473  http://stackoverflow.com/questions/4398967/python-unit-testing-automatically-running-the-debugger-when-a-test-fails
474  """
475  if not exceptions:
476  exceptions = (AssertionError, )
477 
478  def decorator(f):
479  @functools.wraps(f)
480  def wrapper(*args, **kwargs):
481  try:
482  return f(*args, **kwargs)
483  except exceptions:
484  import sys
485  import pdb
486  pdb.post_mortem(sys.exc_info()[2])
487  return wrapper
488  return decorator
489 
490 
491 def plotImageDiff(lhs, rhs, bad=None, diff=None, plotFileName=None):
492  """!Plot the comparison of two 2-d NumPy arrays.
493 
494  NOTE: this method uses matplotlib and imports it internally; it should be
495  wrapped in a try/except block within packages that do not depend on
496  matplotlib (including utils).
497 
498  @param[in] lhs LHS values to compare; a 2-d NumPy array
499  @param[in] rhs RHS values to compare; a 2-d NumPy array
500  @param[in] bad A 2-d boolean NumPy array of values to emphasize in the plots
501  @param[in] diff difference array; a 2-d NumPy array, or None to show lhs-rhs
502  @param[in] plotFileName Filename to save the plot to. If None, the plot will be displayed in a
503  a window.
504  """
505  from matplotlib import pyplot
506  if diff is None:
507  diff = lhs - rhs
508  pyplot.figure()
509  if bad is not None:
510  # make an rgba image that's red and transparent where not bad
511  badImage = numpy.zeros(bad.shape + (4,), dtype=numpy.uint8)
512  badImage[:, :, 0] = 255
513  badImage[:, :, 1] = 0
514  badImage[:, :, 2] = 0
515  badImage[:, :, 3] = 255*bad
516  vmin1 = numpy.minimum(numpy.min(lhs), numpy.min(rhs))
517  vmax1 = numpy.maximum(numpy.max(lhs), numpy.max(rhs))
518  vmin2 = numpy.min(diff)
519  vmax2 = numpy.max(diff)
520  for n, (image, title) in enumerate([(lhs, "lhs"), (rhs, "rhs"), (diff, "diff")]):
521  pyplot.subplot(2, 3, n + 1)
522  im1 = pyplot.imshow(image, cmap=pyplot.cm.gray, interpolation='nearest', origin='lower',
523  vmin=vmin1, vmax=vmax1)
524  if bad is not None:
525  pyplot.imshow(badImage, alpha=0.2, interpolation='nearest', origin='lower')
526  pyplot.axis("off")
527  pyplot.title(title)
528  pyplot.subplot(2, 3, n + 4)
529  im2 = pyplot.imshow(image, cmap=pyplot.cm.gray, interpolation='nearest', origin='lower',
530  vmin=vmin2, vmax=vmax2)
531  if bad is not None:
532  pyplot.imshow(badImage, alpha=0.2, interpolation='nearest', origin='lower')
533  pyplot.axis("off")
534  pyplot.title(title)
535  pyplot.subplots_adjust(left=0.05, bottom=0.05, top=0.92, right=0.75, wspace=0.05, hspace=0.05)
536  cax1 = pyplot.axes([0.8, 0.55, 0.05, 0.4])
537  pyplot.colorbar(im1, cax=cax1)
538  cax2 = pyplot.axes([0.8, 0.05, 0.05, 0.4])
539  pyplot.colorbar(im2, cax=cax2)
540  if plotFileName:
541  pyplot.savefig(plotFileName)
542  else:
543  pyplot.show()
544 
545 
546 @inTestCase
547 def assertFloatsAlmostEqual(testCase, lhs, rhs, rtol=sys.float_info.epsilon,
548  atol=sys.float_info.epsilon, relTo=None,
549  printFailures=True, plotOnFailure=False,
550  plotFileName=None, invert=False, msg=None):
551  """!Highly-configurable floating point comparisons for scalars and arrays.
552 
553  The test assertion will fail if all elements lhs and rhs are not equal to within the tolerances
554  specified by rtol and atol. More precisely, the comparison is:
555 
556  abs(lhs - rhs) <= relTo*rtol OR abs(lhs - rhs) <= atol
557 
558  If rtol or atol is None, that term in the comparison is not performed at all.
559 
560  When not specified, relTo is the elementwise maximum of the absolute values of lhs and rhs. If
561  set manually, it should usually be set to either lhs or rhs, or a scalar value typical of what
562  is expected.
563 
564  @param[in] testCase unittest.TestCase instance the test is part of
565  @param[in] lhs LHS value(s) to compare; may be a scalar or array-like of any dimension
566  @param[in] rhs RHS value(s) to compare; may be a scalar or array-like of any dimension
567  @param[in] rtol Relative tolerance for comparison; defaults to double-precision epsilon.
568  @param[in] atol Absolute tolerance for comparison; defaults to double-precision epsilon.
569  @param[in] relTo Value to which comparison with rtol is relative.
570  @param[in] printFailures Upon failure, print all inequal elements as part of the message.
571  @param[in] plotOnFailure Upon failure, plot the originals and their residual with matplotlib.
572  Only 2-d arrays are supported.
573  @param[in] plotFileName Filename to save the plot to. If None, the plot will be displayed in a
574  a window.
575  @param[in] invert If True, invert the comparison and fail only if any elements *are* equal.
576  Used to implement assertFloatsNotEqual, which should generally be used instead
577  for clarity.
578  @param[in] msg String to append to the error message when assert fails.
579  """
580  if not numpy.isfinite(lhs).all():
581  testCase.fail("Non-finite values in lhs")
582  if not numpy.isfinite(rhs).all():
583  testCase.fail("Non-finite values in rhs")
584  diff = lhs - rhs
585  absDiff = numpy.abs(lhs - rhs)
586  if rtol is not None:
587  if relTo is None:
588  relTo = numpy.maximum(numpy.abs(lhs), numpy.abs(rhs))
589  else:
590  relTo = numpy.abs(relTo)
591  bad = absDiff > rtol*relTo
592  if atol is not None:
593  bad = numpy.logical_and(bad, absDiff > atol)
594  else:
595  if atol is None:
596  raise ValueError("rtol and atol cannot both be None")
597  bad = absDiff > atol
598  failed = numpy.any(bad)
599  if invert:
600  failed = not failed
601  bad = numpy.logical_not(bad)
602  cmpStr = "=="
603  failStr = "are the same"
604  else:
605  cmpStr = "!="
606  failStr = "differ"
607  errMsg = []
608  if failed:
609  if numpy.isscalar(bad):
610  if rtol is None:
611  errMsg = ["%s %s %s; diff=%s with atol=%s"
612  % (lhs, cmpStr, rhs, absDiff, atol)]
613  elif atol is None:
614  errMsg = ["%s %s %s; diff=%s/%s=%s with rtol=%s"
615  % (lhs, cmpStr, rhs, absDiff, relTo, absDiff/relTo, rtol)]
616  else:
617  errMsg = ["%s %s %s; diff=%s/%s=%s with rtol=%s, atol=%s"
618  % (lhs, cmpStr, rhs, absDiff, relTo, absDiff/relTo, rtol, atol)]
619  else:
620  errMsg = ["%d/%d elements %s with rtol=%s, atol=%s"
621  % (bad.sum(), bad.size, failStr, rtol, atol)]
622  if plotOnFailure:
623  if len(lhs.shape) != 2 or len(rhs.shape) != 2:
624  raise ValueError("plotOnFailure is only valid for 2-d arrays")
625  try:
626  plotImageDiff(lhs, rhs, bad, diff=diff, plotFileName=plotFileName)
627  except ImportError:
628  errMsg.append("Failure plot requested but matplotlib could not be imported.")
629  if printFailures:
630  # Make sure everything is an array if any of them are, so we can treat
631  # them the same (diff and absDiff are arrays if either rhs or lhs is),
632  # and we don't get here if neither is.
633  if numpy.isscalar(relTo):
634  relTo = numpy.ones(bad.shape, dtype=float) * relTo
635  if numpy.isscalar(lhs):
636  lhs = numpy.ones(bad.shape, dtype=float) * lhs
637  if numpy.isscalar(rhs):
638  rhs = numpy.ones(bad.shape, dtype=float) * rhs
639  if rtol is None:
640  for a, b, diff in zip(lhs[bad], rhs[bad], absDiff[bad]):
641  errMsg.append("%s %s %s (diff=%s)" % (a, cmpStr, b, diff))
642  else:
643  for a, b, diff, rel in zip(lhs[bad], rhs[bad], absDiff[bad], relTo[bad]):
644  errMsg.append("%s %s %s (diff=%s/%s=%s)" % (a, cmpStr, b, diff, rel, diff/rel))
645 
646  if msg is not None:
647  errMsg.append(msg)
648  testCase.assertFalse(failed, msg="\n".join(errMsg))
649 
650 
651 @inTestCase
652 def assertFloatsNotEqual(testCase, lhs, rhs, **kwds):
653  """
654  Fail a test if the given floating point values are equal to within the given tolerances.
655 
656  See assertClose for more information.
657  """
658  return assertFloatsAlmostEqual(testCase, lhs, rhs, invert=True, **kwds)
659 
660 
661 @inTestCase
662 def assertFloatsEqual(testCase, lhs, rhs, **kwargs):
663  """
664  Assert that lhs == rhs (both numeric types, whether scalar or array).
665 
666  See assertClose (called with rtol=atol=0) for more information.
667  """
668  return assertFloatsAlmostEqual(testCase, lhs, rhs, rtol=0, atol=0, **kwargs)
669 
670 
671 @inTestCase
672 def assertClose(*args, **kwargs):
673  warnings.warn("assertClose is deprecated; please use TestCase.assertFloatsAlmostEqual",
674  DeprecationWarning, stacklevel=2)
675  return assertFloatsAlmostEqual(*args, **kwargs)
676 
677 
678 @inTestCase
679 def assertNotClose(*args, **kwargs):
680  warnings.warn("assertNotClose is deprecated; please use TestCase.assertFloatsNotEqual",
681  DeprecationWarning, stacklevel=2)
682  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:662
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:491
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:448
def assertClose(args, kwargs)
Definition: tests.py:672
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:550
def assertFloatsNotEqual(testCase, lhs, rhs, kwds)
Definition: tests.py:652
Subclass of unittest.TestCase that adds some custom assertions for convenience.
Definition: tests.py:442
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 getTempFilePath(ext, expectOutput=True)
Return a path suitable for a temporary file and try to delete the file on success.
Definition: tests.py:358
def debugger(exceptions)
Decorator to enter the debugger when there&#39;s an uncaught exception.
Definition: tests.py:463
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:679
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:457
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