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