LSSTApplications  16.0+22,16.0-1-g14407aa+7,16.0-1-g16dcc35+1,16.0-1-g928b041+1,16.0-1-g9884240+1,16.0-1-gce273f5+1,16.0-10-g0b41441+3,16.0-10-g549e159,16.0-11-ge4eb5c1,16.0-11-gf74ee13d,16.0-11-gf9130ea+4,16.0-2-g0febb12,16.0-2-g636dfb1,16.0-2-g839ba83+11,16.0-2-g9d5294e+8,16.0-25-g88abbd732,16.0-3-g0e3a094+1,16.0-3-g19f2ef8,16.0-3-g3806c63+1,16.0-3-g5dc86b7+11,16.0-3-g6923fb6+1,16.0-3-g9515088+5,16.0-3-gcfd6c53+6,16.0-4-g347813e+5,16.0-4-g50d071e+1,16.0-4-g5f3a788,16.0-4-g6c14c47+7,16.0-4-g7690030+6,16.0-4-gd6aeece+1,16.0-4-ge3254b7,16.0-5-gb3f8a4b+5,16.0-6-g9134ce5,16.0-6-gbe2f956+3,16.0-6-gf0acd13,16.0-7-g3dac777+5,16.0-8-g4aca173+6,16.0-9-g377a976,16.0-9-gc30114dc+3,16.0-9-gf78d8dd,w.2018.32
LSSTDataManagementBasePackage
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 import contextlib
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 __all__ = ["init", "run", "MemoryTestCase", "ExecutablesTestCase", "getTempFilePath",
42  "TestCase", "assertFloatsAlmostEqual", "assertFloatsNotEqual", "assertFloatsEqual"]
43 
44 # File descriptor leak test will be skipped if psutil can not be imported
45 try:
46  import psutil
47 except ImportError:
48  psutil = None
49 
50 try:
51  import lsst.daf.base as dafBase
52 except ImportError:
53  dafBase = None
54 
55 try:
56  type(memId0)
57 except NameError:
58  memId0 = 0 # ignore leaked blocks with IDs before memId0
59  nleakPrintMax = 20 # maximum number of leaked blocks to print
60 
61 # Initialize the list of open files to an empty set
62 open_files = set()
63 
64 
65 def _get_open_files():
66  """Return a set containing the list of files currently open in this
67  process.
68 
69  Returns
70  -------
71  open_files : `set`
72  Set containing the list of open files.
73  """
74  if psutil is None:
75  return set()
76  return set(p.path for p in psutil.Process().open_files())
77 
78 
79 def init():
80  """Initialize the memory tester and file descriptor leak tester."""
81  global memId0
82  global open_files
83  if dafBase:
84  memId0 = dafBase.Citizen.getNextMemId() # used by MemoryTestCase
85  # Reset the list of open files
86  open_files = _get_open_files()
87 
88 
89 def run(suite, exit=True):
90  """Run a test suite and report the test return status to caller or shell.
91 
92  .. note:: Deprecated in 13_0
93  Use `unittest.main()` instead, which automatically detects
94  all tests in a test case and does not require a test suite.
95 
96  Parameters
97  ----------
98  suite : `unittest.TestSuite`
99  Test suite to run.
100  exit : `bool`, optional
101  If `True`, Python process will exit with the test exit status.
102 
103  Returns
104  -------
105  status : `int`
106  If ``exit`` is `False`, will return 0 if the tests passed, or 1 if
107  the tests failed.
108  """
109 
110  warnings.warn("lsst.utils.tests.run() is deprecated; please use unittest.main() instead",
111  DeprecationWarning, stacklevel=2)
112 
113  if unittest.TextTestRunner().run(suite).wasSuccessful():
114  status = 0
115  else:
116  status = 1
117 
118  if exit:
119  sys.exit(status)
120  else:
121  return status
122 
123 
124 def sort_tests(tests):
125  """Sort supplied test suites such that MemoryTestCases are at the end.
126 
127  `lsst.utils.tests.MemoryTestCase` tests should always run after any other
128  tests in the module.
129 
130  Parameters
131  ----------
132  tests : sequence
133  Sequence of test suites.
134 
135  Returns
136  -------
137  suite : `unittest.TestSuite`
138  A combined `~unittest.TestSuite` with
139  `~lsst.utils.tests.MemoryTestCase` at the end.
140  """
141 
142  suite = unittest.TestSuite()
143  memtests = []
144  for test_suite in tests:
145  try:
146  # Just test the first test method in the suite for MemoryTestCase
147  # Use loop rather than next as it is possible for a test class
148  # to not have any test methods and the Python community prefers
149  # for loops over catching a StopIteration exception.
150  bases = None
151  for method in test_suite:
152  bases = inspect.getmro(method.__class__)
153  break
154  if bases is not None and MemoryTestCase in bases:
155  memtests.append(test_suite)
156  else:
157  suite.addTests(test_suite)
158  except TypeError:
159  if isinstance(test_suite, MemoryTestCase):
160  memtests.append(test_suite)
161  else:
162  suite.addTest(test_suite)
163  suite.addTests(memtests)
164  return suite
165 
166 
167 def suiteClassWrapper(tests):
168  return unittest.TestSuite(sort_tests(tests))
169 
170 
171 # Replace the suiteClass callable in the defaultTestLoader
172 # so that we can reorder the test ordering. This will have
173 # no effect if no memory test cases are found.
174 unittest.defaultTestLoader.suiteClass = suiteClassWrapper
175 
176 
177 class MemoryTestCase(unittest.TestCase):
178  """Check for memory leaks since memId0 was allocated"""
179 
180  def setUp(self):
181  pass
182 
183  @classmethod
184  def tearDownClass(cls):
185  """Reset the leak counter when the tests have been completed"""
186  init()
187 
188  def testLeaks(self):
189  """Check for memory leaks in the preceding tests"""
190  if dafBase:
191  gc.collect()
192  global memId0, nleakPrintMax
193  nleak = dafBase.Citizen.census(0, memId0)
194  if nleak != 0:
195  plural = "s" if nleak != 1 else ""
196  print("\n%d Object%s leaked:" % (nleak, plural))
197 
198  if nleak <= nleakPrintMax:
199  print(dafBase.Citizen.census(memId0))
200  else:
201  census = dafBase.Citizen.census()
202  print("...")
203  for i in range(nleakPrintMax - 1, -1, -1):
204  print(census[i].repr())
205 
206  self.fail("Leaked %d block%s" % (nleak, plural))
207 
209  """Check if any file descriptors are open since init() called."""
210  if psutil is None:
211  self.skipTest("Unable to test file descriptor leaks. psutil unavailable.")
212  gc.collect()
213  global open_files
214  now_open = _get_open_files()
215 
216  # Some files are opened out of the control of the stack.
217  now_open = set(f for f in now_open if not f.endswith(".car") and
218  not f.startswith("/proc/") and
219  not f.endswith(".ttf") and
220  not (f.startswith("/var/lib/") and f.endswith("/passwd")) and
221  not f.endswith("astropy.log"))
222 
223  diff = now_open.difference(open_files)
224  if diff:
225  for f in diff:
226  print("File open: %s" % f)
227  self.fail("Failed to close %d file%s" % (len(diff), "s" if len(diff) != 1 else ""))
228 
229 
230 class ExecutablesTestCase(unittest.TestCase):
231  """Test that executables can be run and return good status.
232 
233  The test methods are dynamically created. Callers
234  must subclass this class in their own test file and invoke
235  the create_executable_tests() class method to register the tests.
236  """
237  TESTS_DISCOVERED = -1
238 
239  @classmethod
240  def setUpClass(cls):
241  """Abort testing if automated test creation was enabled and
242  no tests were found."""
243 
244  if cls.TESTS_DISCOVERED == 0:
245  raise Exception("No executables discovered.")
246 
247  def testSanity(self):
248  """This test exists to ensure that there is at least one test to be
249  executed. This allows the test runner to trigger the class set up
250  machinery to test whether there are some executables to test."""
251  pass
252 
253  def assertExecutable(self, executable, root_dir=None, args=None, msg=None):
254  """Check an executable runs and returns good status.
255 
256  Prints output to standard out. On bad exit status the test
257  fails. If the executable can not be located the test is skipped.
258 
259  Parameters
260  ----------
261  executable : `str`
262  Path to an executable. ``root_dir`` is not used if this is an
263  absolute path.
264  root_dir : `str`, optional
265  Directory containing executable. Ignored if `None`.
266  args : `list` or `tuple`, optional
267  Arguments to be provided to the executable.
268  msg : `str`, optional
269  Message to use when the test fails. Can be `None` for default
270  message.
271 
272  Raises
273  ------
274  AssertionError
275  The executable did not return 0 exit status.
276  """
277 
278  if root_dir is not None and not os.path.isabs(executable):
279  executable = os.path.join(root_dir, executable)
280 
281  # Form the argument list for subprocess
282  sp_args = [executable]
283  argstr = "no arguments"
284  if args is not None:
285  sp_args.extend(args)
286  argstr = 'arguments "' + " ".join(args) + '"'
287 
288  print("Running executable '{}' with {}...".format(executable, argstr))
289  if not os.path.exists(executable):
290  self.skipTest("Executable {} is unexpectedly missing".format(executable))
291  failmsg = None
292  try:
293  output = subprocess.check_output(sp_args)
294  except subprocess.CalledProcessError as e:
295  output = e.output
296  failmsg = "Bad exit status from '{}': {}".format(executable, e.returncode)
297  print(output.decode('utf-8'))
298  if failmsg:
299  if msg is None:
300  msg = failmsg
301  self.fail(msg)
302 
303  @classmethod
304  def _build_test_method(cls, executable, root_dir):
305  """Build a test method and attach to class.
306 
307  A test method is created for the supplied excutable located
308  in the supplied root directory. This method is attached to the class
309  so that the test runner will discover the test and run it.
310 
311  Parameters
312  ----------
313  cls : `object`
314  The class in which to create the tests.
315  executable : `str`
316  Name of executable. Can be absolute path.
317  root_dir : `str`
318  Path to executable. Not used if executable path is absolute.
319  """
320  if not os.path.isabs(executable):
321  executable = os.path.abspath(os.path.join(root_dir, executable))
322 
323  # Create the test name from the executable path.
324  test_name = "test_exe_" + executable.replace("/", "_")
325 
326  # This is the function that will become the test method
327  def test_executable_runs(*args):
328  self = args[0]
329  self.assertExecutable(executable)
330 
331  # Give it a name and attach it to the class
332  test_executable_runs.__name__ = test_name
333  setattr(cls, test_name, test_executable_runs)
334 
335  @classmethod
336  def create_executable_tests(cls, ref_file, executables=None):
337  """Discover executables to test and create corresponding test methods.
338 
339  Scans the directory containing the supplied reference file
340  (usually ``__file__`` supplied from the test class) to look for
341  executables. If executables are found a test method is created
342  for each one. That test method will run the executable and
343  check the returned value.
344 
345  Executable scripts with a ``.py`` extension and shared libraries
346  are ignored by the scanner.
347 
348  This class method must be called before test discovery.
349 
350  Parameters
351  ----------
352  ref_file : `str`
353  Path to a file within the directory to be searched.
354  If the files are in the same location as the test file, then
355  ``__file__`` can be used.
356  executables : `list` or `tuple`, optional
357  Sequence of executables that can override the automated
358  detection. If an executable mentioned here is not found, a
359  skipped test will be created for it, rather than a failed
360  test.
361 
362  Examples
363  --------
364  >>> cls.create_executable_tests(__file__)
365  """
366 
367  # Get the search directory from the reference file
368  ref_dir = os.path.abspath(os.path.dirname(ref_file))
369 
370  if executables is None:
371  # Look for executables to test by walking the tree
372  executables = []
373  for root, dirs, files in os.walk(ref_dir):
374  for f in files:
375  # Skip Python files. Shared libraries are executable.
376  if not f.endswith(".py") and not f.endswith(".so"):
377  full_path = os.path.join(root, f)
378  if os.access(full_path, os.X_OK):
379  executables.append(full_path)
380 
381  # Store the number of tests found for later assessment.
382  # Do not raise an exception if we have no executables as this would
383  # cause the testing to abort before the test runner could properly
384  # integrate it into the failure report.
385  cls.TESTS_DISCOVERED = len(executables)
386 
387  # Create the test functions and attach them to the class
388  for e in executables:
389  cls._build_test_method(e, ref_dir)
390 
391 
392 @contextlib.contextmanager
393 def getTempFilePath(ext, expectOutput=True):
394  """Return a path suitable for a temporary file and try to delete the
395  file on success
396 
397  If the with block completes successfully then the file is deleted,
398  if possible; failure results in a printed warning.
399  If a file is remains when it should not, a RuntimeError exception is
400  raised. This exception is also raised if a file is not present on context
401  manager exit when one is expected to exist.
402  If the block exits with an exception the file if left on disk so it can be
403  examined. The file name has a random component such that nested context
404  managers can be used with the same file suffix.
405 
406  Parameters
407  ----------
408 
409  ext : `str`
410  File name extension, e.g. ``.fits``.
411  expectOutput : `bool`, optional
412  If `True`, a file should be created within the context manager.
413  If `False`, a file should not be present when the context manager
414  exits.
415 
416  Returns
417  -------
418  `str`
419  Path for a temporary file. The path is a combination of the caller's
420  file path and the name of the top-level function
421 
422  Notes
423  -----
424  ::
425 
426  # file tests/testFoo.py
427  import unittest
428  import lsst.utils.tests
429  class FooTestCase(unittest.TestCase):
430  def testBasics(self):
431  self.runTest()
432 
433  def runTest(self):
434  with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
435  # if tests/.tests exists then
436  # tmpFile = "tests/.tests/testFoo_testBasics.fits"
437  # otherwise tmpFile = "testFoo_testBasics.fits"
438  ...
439  # at the end of this "with" block the path tmpFile will be
440  # deleted, but only if the file exists and the "with"
441  # block terminated normally (rather than with an exception)
442  ...
443  """
444  stack = inspect.stack()
445  # get name of first function in the file
446  for i in range(2, len(stack)):
447  frameInfo = inspect.getframeinfo(stack[i][0])
448  if i == 2:
449  callerFilePath = frameInfo.filename
450  callerFuncName = frameInfo.function
451  elif callerFilePath == frameInfo.filename:
452  # this function called the previous function
453  callerFuncName = frameInfo.function
454  else:
455  break
456 
457  callerDir, callerFileNameWithExt = os.path.split(callerFilePath)
458  callerFileName = os.path.splitext(callerFileNameWithExt)[0]
459  outDir = os.path.join(callerDir, ".tests")
460  if not os.path.isdir(outDir):
461  outDir = ""
462  prefix = "%s_%s-" % (callerFileName, callerFuncName)
463  outPath = tempfile.mktemp(dir=outDir, suffix=ext, prefix=prefix)
464  if os.path.exists(outPath):
465  # There should not be a file there given the randomizer. Warn and remove.
466  # Use stacklevel 3 so that the warning is reported from the end of the with block
467  warnings.warn("Unexpectedly found pre-existing tempfile named %r" % (outPath,),
468  stacklevel=3)
469  try:
470  os.remove(outPath)
471  except OSError:
472  pass
473 
474  yield outPath
475 
476  fileExists = os.path.exists(outPath)
477  if expectOutput:
478  if not fileExists:
479  raise RuntimeError("Temp file expected named {} but none found".format(outPath))
480  else:
481  if fileExists:
482  raise RuntimeError("Unexpectedly discovered temp file named {}".format(outPath))
483  # Try to clean up the file regardless
484  if fileExists:
485  try:
486  os.remove(outPath)
487  except OSError as e:
488  # Use stacklevel 3 so that the warning is reported from the end of the with block
489  warnings.warn("Warning: could not remove file %r: %s" % (outPath, e), stacklevel=3)
490 
491 
492 class TestCase(unittest.TestCase):
493  """Subclass of unittest.TestCase that adds some custom assertions for
494  convenience.
495  """
496 
497 
498 def inTestCase(func):
499  """A decorator to add a free function to our custom TestCase class, while also
500  making it available as a free function.
501  """
502  setattr(TestCase, func.__name__, func)
503  return func
504 
505 
506 @inTestCase
507 def assertRaisesLsstCpp(testcase, excClass, callableObj, *args, **kwargs):
508  """.. note:: Deprecated in 12_0"""
509  warnings.warn("assertRaisesLsstCpp is deprecated; please just use TestCase.assertRaises",
510  DeprecationWarning, stacklevel=2)
511  return testcase.assertRaises(excClass, callableObj, *args, **kwargs)
512 
513 
514 def debugger(*exceptions):
515  """Decorator to enter the debugger when there's an uncaught exception
516 
517  To use, just slap a ``@debugger()`` on your function.
518 
519  You may provide specific exception classes to catch as arguments to
520  the decorator function, e.g.,
521  ``@debugger(RuntimeError, NotImplementedError)``.
522  This defaults to just `AssertionError`, for use on `unittest.TestCase`
523  methods.
524 
525  Code provided by "Rosh Oxymoron" on StackOverflow:
526  http://stackoverflow.com/questions/4398967/python-unit-testing-automatically-running-the-debugger-when-a-test-fails
527 
528  Notes
529  -----
530  Consider using ``pytest --pdb`` instead of this decorator.
531  """
532  if not exceptions:
533  exceptions = (AssertionError, )
534 
535  def decorator(f):
536  @functools.wraps(f)
537  def wrapper(*args, **kwargs):
538  try:
539  return f(*args, **kwargs)
540  except exceptions:
541  import sys
542  import pdb
543  pdb.post_mortem(sys.exc_info()[2])
544  return wrapper
545  return decorator
546 
547 
548 def plotImageDiff(lhs, rhs, bad=None, diff=None, plotFileName=None):
549  """Plot the comparison of two 2-d NumPy arrays.
550 
551  Parameters
552  ----------
553  lhs : `numpy.ndarray`
554  LHS values to compare; a 2-d NumPy array
555  rhs : `numpy.ndarray`
556  RHS values to compare; a 2-d NumPy array
557  bad : `numpy.ndarray`
558  A 2-d boolean NumPy array of values to emphasize in the plots
559  diff : `numpy.ndarray`
560  difference array; a 2-d NumPy array, or None to show lhs-rhs
561  plotFileName : `str`
562  Filename to save the plot to. If None, the plot will be displayed in
563  a window.
564 
565  Notes
566  -----
567  This method uses `matplotlib` and imports it internally; it should be
568  wrapped in a try/except block within packages that do not depend on
569  `matplotlib` (including `~lsst.utils`).
570  """
571  from matplotlib import pyplot
572  if diff is None:
573  diff = lhs - rhs
574  pyplot.figure()
575  if bad is not None:
576  # make an rgba image that's red and transparent where not bad
577  badImage = numpy.zeros(bad.shape + (4,), dtype=numpy.uint8)
578  badImage[:, :, 0] = 255
579  badImage[:, :, 1] = 0
580  badImage[:, :, 2] = 0
581  badImage[:, :, 3] = 255*bad
582  vmin1 = numpy.minimum(numpy.min(lhs), numpy.min(rhs))
583  vmax1 = numpy.maximum(numpy.max(lhs), numpy.max(rhs))
584  vmin2 = numpy.min(diff)
585  vmax2 = numpy.max(diff)
586  for n, (image, title) in enumerate([(lhs, "lhs"), (rhs, "rhs"), (diff, "diff")]):
587  pyplot.subplot(2, 3, n + 1)
588  im1 = pyplot.imshow(image, cmap=pyplot.cm.gray, interpolation='nearest', origin='lower',
589  vmin=vmin1, vmax=vmax1)
590  if bad is not None:
591  pyplot.imshow(badImage, alpha=0.2, interpolation='nearest', origin='lower')
592  pyplot.axis("off")
593  pyplot.title(title)
594  pyplot.subplot(2, 3, n + 4)
595  im2 = pyplot.imshow(image, cmap=pyplot.cm.gray, interpolation='nearest', origin='lower',
596  vmin=vmin2, vmax=vmax2)
597  if bad is not None:
598  pyplot.imshow(badImage, alpha=0.2, interpolation='nearest', origin='lower')
599  pyplot.axis("off")
600  pyplot.title(title)
601  pyplot.subplots_adjust(left=0.05, bottom=0.05, top=0.92, right=0.75, wspace=0.05, hspace=0.05)
602  cax1 = pyplot.axes([0.8, 0.55, 0.05, 0.4])
603  pyplot.colorbar(im1, cax=cax1)
604  cax2 = pyplot.axes([0.8, 0.05, 0.05, 0.4])
605  pyplot.colorbar(im2, cax=cax2)
606  if plotFileName:
607  pyplot.savefig(plotFileName)
608  else:
609  pyplot.show()
610 
611 
612 @inTestCase
613 def assertFloatsAlmostEqual(testCase, lhs, rhs, rtol=sys.float_info.epsilon,
614  atol=sys.float_info.epsilon, relTo=None,
615  printFailures=True, plotOnFailure=False,
616  plotFileName=None, invert=False, msg=None):
617  """Highly-configurable floating point comparisons for scalars and arrays.
618 
619  The test assertion will fail if all elements ``lhs`` and ``rhs`` are not
620  equal to within the tolerances specified by ``rtol`` and ``atol``.
621  More precisely, the comparison is:
622 
623  ``abs(lhs - rhs) <= relTo*rtol OR abs(lhs - rhs) <= atol``
624 
625  If ``rtol`` or ``atol`` is `None`, that term in the comparison is not
626  performed at all.
627 
628  When not specified, ``relTo`` is the elementwise maximum of the absolute
629  values of ``lhs`` and ``rhs``. If set manually, it should usually be set
630  to either ``lhs`` or ``rhs``, or a scalar value typical of what is
631  expected.
632 
633  Parameters
634  ----------
635  testCase : `unittest.TestCase`
636  Instance the test is part of.
637  lhs : scalar or array-like
638  LHS value(s) to compare; may be a scalar or array-like of any
639  dimension.
640  rhs : scalar or array-like
641  RHS value(s) to compare; may be a scalar or array-like of any
642  dimension.
643  rtol : `float`, optional
644  Relative tolerance for comparison; defaults to double-precision
645  epsilon.
646  atol : `float`, optional
647  Absolute tolerance for comparison; defaults to double-precision
648  epsilon.
649  relTo : `float`, optional
650  Value to which comparison with rtol is relative.
651  printFailures : `bool`, optional
652  Upon failure, print all inequal elements as part of the message.
653  plotOnFailure : `bool`, optional
654  Upon failure, plot the originals and their residual with matplotlib.
655  Only 2-d arrays are supported.
656  plotFileName : `str`, optional
657  Filename to save the plot to. If `None`, the plot will be displayed in
658  a window.
659  invert : `bool`, optional
660  If `True`, invert the comparison and fail only if any elements *are*
661  equal. Used to implement `~lsst.utils.tests.assertFloatsNotEqual`,
662  which should generally be used instead for clarity.
663  msg : `str`, optional
664  String to append to the error message when assert fails.
665 
666  Raises
667  ------
668  AssertionError
669  The values are not almost equal.
670  """
671  if not numpy.isfinite(lhs).all():
672  testCase.fail("Non-finite values in lhs")
673  if not numpy.isfinite(rhs).all():
674  testCase.fail("Non-finite values in rhs")
675  diff = lhs - rhs
676  absDiff = numpy.abs(lhs - rhs)
677  if rtol is not None:
678  if relTo is None:
679  relTo = numpy.maximum(numpy.abs(lhs), numpy.abs(rhs))
680  else:
681  relTo = numpy.abs(relTo)
682  bad = absDiff > rtol*relTo
683  if atol is not None:
684  bad = numpy.logical_and(bad, absDiff > atol)
685  else:
686  if atol is None:
687  raise ValueError("rtol and atol cannot both be None")
688  bad = absDiff > atol
689  failed = numpy.any(bad)
690  if invert:
691  failed = not failed
692  bad = numpy.logical_not(bad)
693  cmpStr = "=="
694  failStr = "are the same"
695  else:
696  cmpStr = "!="
697  failStr = "differ"
698  errMsg = []
699  if failed:
700  if numpy.isscalar(bad):
701  if rtol is None:
702  errMsg = ["%s %s %s; diff=%s with atol=%s"
703  % (lhs, cmpStr, rhs, absDiff, atol)]
704  elif atol is None:
705  errMsg = ["%s %s %s; diff=%s/%s=%s with rtol=%s"
706  % (lhs, cmpStr, rhs, absDiff, relTo, absDiff/relTo, rtol)]
707  else:
708  errMsg = ["%s %s %s; diff=%s/%s=%s with rtol=%s, atol=%s"
709  % (lhs, cmpStr, rhs, absDiff, relTo, absDiff/relTo, rtol, atol)]
710  else:
711  errMsg = ["%d/%d elements %s with rtol=%s, atol=%s"
712  % (bad.sum(), bad.size, failStr, rtol, atol)]
713  if plotOnFailure:
714  if len(lhs.shape) != 2 or len(rhs.shape) != 2:
715  raise ValueError("plotOnFailure is only valid for 2-d arrays")
716  try:
717  plotImageDiff(lhs, rhs, bad, diff=diff, plotFileName=plotFileName)
718  except ImportError:
719  errMsg.append("Failure plot requested but matplotlib could not be imported.")
720  if printFailures:
721  # Make sure everything is an array if any of them are, so we can treat
722  # them the same (diff and absDiff are arrays if either rhs or lhs is),
723  # and we don't get here if neither is.
724  if numpy.isscalar(relTo):
725  relTo = numpy.ones(bad.shape, dtype=float) * relTo
726  if numpy.isscalar(lhs):
727  lhs = numpy.ones(bad.shape, dtype=float) * lhs
728  if numpy.isscalar(rhs):
729  rhs = numpy.ones(bad.shape, dtype=float) * rhs
730  if rtol is None:
731  for a, b, diff in zip(lhs[bad], rhs[bad], absDiff[bad]):
732  errMsg.append("%s %s %s (diff=%s)" % (a, cmpStr, b, diff))
733  else:
734  for a, b, diff, rel in zip(lhs[bad], rhs[bad], absDiff[bad], relTo[bad]):
735  errMsg.append("%s %s %s (diff=%s/%s=%s)" % (a, cmpStr, b, diff, rel, diff/rel))
736 
737  if msg is not None:
738  errMsg.append(msg)
739  testCase.assertFalse(failed, msg="\n".join(errMsg))
740 
741 
742 @inTestCase
743 def assertFloatsNotEqual(testCase, lhs, rhs, **kwds):
744  """Fail a test if the given floating point values are equal to within the
745  given tolerances.
746 
747  See `~lsst.utils.tests.assertFloatsAlmostEqual` (called with
748  ``rtol=atol=0``) for more information.
749 
750  Parameters
751  ----------
752  testCase : `unittest.TestCase`
753  Instance the test is part of.
754  lhs : scalar or array-like
755  LHS value(s) to compare; may be a scalar or array-like of any
756  dimension.
757  rhs : scalar or array-like
758  RHS value(s) to compare; may be a scalar or array-like of any
759  dimension.
760 
761  Raises
762  ------
763  AssertionError
764  The values are almost equal.
765  """
766  return assertFloatsAlmostEqual(testCase, lhs, rhs, invert=True, **kwds)
767 
768 
769 @inTestCase
770 def assertFloatsEqual(testCase, lhs, rhs, **kwargs):
771  """
772  Assert that lhs == rhs (both numeric types, whether scalar or array).
773 
774  See `~lsst.utils.tests.assertFloatsAlmostEqual` (called with
775  ``rtol=atol=0``) for more information.
776 
777  Parameters
778  ----------
779  testCase : `unittest.TestCase`
780  Instance the test is part of.
781  lhs : scalar or array-like
782  LHS value(s) to compare; may be a scalar or array-like of any
783  dimension.
784  rhs : scalar or array-like
785  RHS value(s) to compare; may be a scalar or array-like of any
786  dimension.
787 
788  Raises
789  ------
790  AssertionError
791  The values are not equal.
792  """
793  return assertFloatsAlmostEqual(testCase, lhs, rhs, rtol=0, atol=0, **kwargs)
794 
795 
796 @inTestCase
797 def assertClose(*args, **kwargs):
798  """.. note:: Deprecated in 12_0"""
799  warnings.warn("assertClose is deprecated; please use TestCase.assertFloatsAlmostEqual",
800  DeprecationWarning, stacklevel=2)
801  return assertFloatsAlmostEqual(*args, **kwargs)
802 
803 
804 @inTestCase
805 def assertNotClose(*args, **kwargs):
806  """.. note:: Deprecated in 12_0"""
807  warnings.warn("assertNotClose is deprecated; please use TestCase.assertFloatsNotEqual",
808  DeprecationWarning, stacklevel=2)
809  return assertFloatsNotEqual(*args, **kwargs)
def suiteClassWrapper(tests)
Definition: tests.py:167
def assertExecutable(self, executable, root_dir=None, args=None, msg=None)
Definition: tests.py:253
def assertFloatsEqual(testCase, lhs, rhs, kwargs)
Definition: tests.py:770
def init()
Definition: tests.py:79
def plotImageDiff(lhs, rhs, bad=None, diff=None, plotFileName=None)
Definition: tests.py:548
def inTestCase(func)
Definition: tests.py:498
def assertClose(args, kwargs)
Definition: tests.py:797
daf::base::PropertySet * set
Definition: fits.cc:818
bool all(CoordinateExpr< N > const &expr) noexcept
Return true if all elements are true.
def _build_test_method(cls, executable, root_dir)
Definition: tests.py:304
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)
Definition: tests.py:616
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:134
def assertFloatsNotEqual(testCase, lhs, rhs, kwds)
Definition: tests.py:743
def run(suite, exit=True)
Definition: tests.py:89
def getTempFilePath(ext, expectOutput=True)
Definition: tests.py:393
def debugger(exceptions)
Definition: tests.py:514
def assertNotClose(args, kwargs)
Definition: tests.py:805
def create_executable_tests(cls, ref_file, executables=None)
Definition: tests.py:336
def assertRaisesLsstCpp(testcase, excClass, callableObj, args, kwargs)
Definition: tests.py:507
def sort_tests(tests)
Definition: tests.py:124