1 """@namespace IMP::test
2 @brief Methods and classes for testing the IMP kernel and modules.
19 from unittest.util
import safe_repr
25 from pathlib
import Path
27 from IMP._compat_pathlib
import Path
31 expectedFailure = unittest.expectedFailure
33 skipIf = unittest.skipIf
34 skipUnless = unittest.skipUnless
37 class _TempDir(object):
38 def __init__(self, dir=None):
39 self.tmpdir = tempfile.mkdtemp(dir=dir)
42 shutil.rmtree(self.tmpdir, ignore_errors=
True)
45 @contextlib.contextmanager
47 """Simple context manager to run in a temporary directory.
48 While the context manager is active (within the 'with' block)
49 the current working directory is set to a temporary directory.
50 When the context manager exists, the working directory is reset
51 and the temporary directory deleted."""
53 tmpdir = tempfile.mkdtemp()
57 shutil.rmtree(tmpdir, ignore_errors=
True)
60 @contextlib.contextmanager
62 """Simple context manager to make a temporary directory.
63 The temporary directory has the same lifetime as the context manager
64 (i.e. it is created at the start of the 'with' block, and deleted
65 at the end of the block).
66 @param dir If given, the temporary directory is made as a subdirectory
67 of that directory, rather than in the default temporary
68 directory location (e.g. /tmp)
69 @return the full path to the temporary directory.
71 tmpdir = tempfile.mkdtemp(dir=dir)
73 shutil.rmtree(tmpdir, ignore_errors=
True)
77 """Calculate the derivative of the single-value function `func` at
78 point `val`. The derivative is calculated using simple finite
79 differences starting with the given `step`; Richardson extrapolation
80 is then used to extrapolate the derivative at step=0."""
88 d = [[(f1 - f2) / (2.0 * step)]]
90 for i
in range(1, maxsteps):
91 d.append([0.] * (i + 1))
95 d[i][0] = (f1 - f2) / (2.0 * step)
97 for j
in range(1, i + 1):
98 d[i][j] = (d[i][j-1] * fac - d[i-1][j-1]) / (fac - 1.)
100 errt = max(abs(d[i][j] - d[i][j-1]),
101 abs(d[i][j] - d[i-1][j-1]))
105 if abs(d[i][i] - d[i-1][i-1]) >= safe * err:
108 raise ValueError(
"Cannot calculate numerical derivative")
113 """Calculate the x,y and z derivatives of the scoring function `sf`
114 on the `xyz` particle. The derivatives are approximated numerically
115 using the numerical_derivatives() function."""
116 class _XYZDerivativeFunc(object):
117 def __init__(self, sf, xyz, basis_vector):
120 self._basis_vector = basis_vector
121 self._starting_coordinates = xyz.get_coordinates()
123 def __call__(self, val):
124 self._xyz.set_coordinates(self._starting_coordinates +
125 self._basis_vector * val)
126 return self._sf.evaluate(
False)
131 for x
in ((1, 0, 0), (0, 1, 0), (0, 0, 1))])
135 """Super class for IMP test cases.
136 This provides a number of useful IMP-specific methods on top of
137 the standard Python `unittest.TestCase` class.
138 Test scripts should generally contain a subclass of this class,
139 conventionally called `Tests` (this makes it easier to run an
140 individual test from the command line) and use IMP::test::main()
141 as their main function."""
145 if not hasattr(unittest.TestCase,
'assertRegex'):
146 assertRegex = unittest.TestCase.assertRegexpMatches
147 assertNotRegex = unittest.TestCase.assertNotRegexpMatches
149 def __init__(self, *args, **keys):
150 unittest.TestCase.__init__(self, *args, **keys)
151 self._progname = Path(sys.argv[0]).absolute()
159 IMP.random_number_generator.seed(hash(time.time()) % 2**30)
165 if hasattr(self,
'_tmpdir'):
169 """Get the full name of an input file in the top-level
171 if self.__module__ ==
'__main__':
172 testdir = self._progname
174 testdir = Path(sys.modules[self.__module__].__file__)
175 for p
in testdir.parents:
178 ret = input / filename
180 raise IOError(
"Test input file %s does not exist" % ret)
182 raise IOError(
"No test input directory found")
185 """Open and return an input file in the top-level test directory."""
189 """Get the full name of an output file in the tmp directory.
190 The directory containing this file will be automatically
191 cleaned up when the test completes."""
192 if not hasattr(self,
'_tmpdir'):
193 self._tmpdir = _TempDir(os.environ.get(
'IMP_TMP_DIR'))
194 tmpdir = self._tmpdir.tmpdir
195 return str(Path(tmpdir) / filename)
198 """Get the magnitude of a list of floats"""
199 return sum(x*x
for x
in vector)**.5
202 """Assert that the given callable object raises UsageException.
203 This differs from unittest's assertRaises in that the test
204 is skipped in fast mode (where usage checks are turned off)."""
209 """Assert that the given callable object raises InternalException.
210 This differs from unittest's assertRaises in that the test
211 is skipped in fast mode (where internal checks are turned off)."""
216 """Assert that the given callable object is not implemented."""
221 """Assert that x,y,z analytical derivatives match numerical within
222 a tolerance, or a percentage (of the analytical value), whichever
223 is larger. `sf` should be a ScoringFunction or Restraint."""
225 derivs = xyz.get_derivatives()
227 pct = percentage / 100.0
228 self.assertAlmostEqual(
231 msg=
"Don't match "+str(derivs) + str(num_derivs))
232 self.assertAlmostEqual(derivs[0], num_derivs[0],
233 delta=max(tolerance, abs(derivs[0]) * pct))
234 self.assertAlmostEqual(derivs[1], num_derivs[1],
235 delta=max(tolerance, abs(derivs[1]) * pct))
236 self.assertAlmostEqual(derivs[2], num_derivs[2],
237 delta=max(tolerance, abs(derivs[2]) * pct))
240 """Fail if the given numpy array doesn't match expected"""
241 if IMP.IMP_KERNEL_HAS_NUMPY:
243 self.assertIsInstance(numpy_array, numpy.ndarray)
244 numpy.testing.assert_array_equal(numpy_array, exp_array)
246 self.assertEqual(numpy_array, exp_array)
250 """Fail if the difference between any two items in the two sequences
251 are exceed the specified number of places or delta. See
254 if delta
is not None and places
is not None:
255 raise TypeError(
"specify delta or places not both")
258 ftypename = ftype.__name__
260 stypename = stype.__name__
262 raise self.failureException(
263 'Sequences are of different types: %s != %s' % (
264 ftypename, stypename))
268 except (NotImplementedError, TypeError):
269 raise self.failureException(
270 'First %s has no length' % (ftypename))
273 except (NotImplementedError, TypeError):
274 raise self.failureException(
275 'Second %s has no length' % (stypename))
278 raise self.failureException(
279 'Sequences have non equal lengths: %d != %d' % (flen, slen))
282 for i
in range(min(flen, slen)):
283 differing =
'%ss differ: %s != %s\n' % (
284 ftypename.capitalize(), safe_repr(first),
289 except (TypeError, IndexError, NotImplementedError):
290 differing += (
'\nUnable to index element %d of first %s\n' %
296 except (TypeError, IndexError, NotImplementedError):
297 differing += (
'\nUnable to index element %d of second %s\n' %
302 self.assertAlmostEqual(
303 f, s, places=places, msg=msg, delta=delta)
304 except (TypeError, ValueError, NotImplementedError,
307 "\nFirst differing element "
308 "%d:\n%s\n%s\n") % (i, safe_repr(f), safe_repr(s))
313 standardMsg = differing
314 diffMsg =
'\n' +
'\n'.join(
315 difflib.ndiff(pprint.pformat(first).splitlines(),
316 pprint.pformat(second).splitlines()))
317 standardMsg = self._truncateMessage(standardMsg, diffMsg)
318 msg = self._formatMessage(msg, standardMsg)
319 raise self.failureException(msg)
321 def _read_cmake_cfg(self, cmake_cfg):
322 """Parse IMPConfig.cmake and extract info on the C++ compiler"""
323 cxx = flags = sysroot =
None
325 with open(cmake_cfg)
as fh:
327 if line.startswith(
'set(IMP_CXX_COMPILER '):
328 cxx = line.split(
'"')[1]
329 elif line.startswith(
'set(IMP_CXX_FLAGS '):
330 flags = line.split(
'"')[1]
331 elif line.startswith(
'set(IMP_OSX_SYSROOT '):
332 sysroot = line.split(
'"')[1]
333 elif line.startswith(
'SET(Boost_INCLUDE_DIR '):
334 includes.append(line.split(
'"')[1])
335 elif line.startswith(
'SET(EIGEN3_INCLUDE_DIR '):
336 includes.append(line.split(
'"')[1])
337 elif line.startswith(
'SET(cereal_INCLUDE_DIRS '):
338 includes.append(line.split(
'"')[1])
339 return cxx, flags, includes, sysroot
342 """Test that the given C++ code fails to compile with a static
344 if sys.platform ==
'win32':
345 self.skipTest(
"No support for Windows yet")
346 libdir = os.path.dirname(IMP.__file__)
347 cmake_cfg = os.path.join(libdir,
'..',
'..',
'IMPConfig.cmake')
348 if not os.path.exists(cmake_cfg):
349 self.skipTest(
"cannot find IMPConfig.cmake")
350 cxx, flags, includes, sysroot = self._read_cmake_cfg(cmake_cfg)
352 if sys.platform ==
'darwin' and sysroot:
353 flags = flags +
" -isysroot" + sysroot
354 includes.append(os.path.join(libdir,
'..',
'..',
'include'))
355 include =
" ".join(
"-I" + inc
for inc
in includes)
357 fname = os.path.join(tmpdir,
'test.cpp')
358 with open(fname,
'w')
as fh:
360 fh.write(
"#include <%s>\n" % h)
361 fh.write(
"\nint main() {\n" + body +
"\n return 0;\n}\n")
362 cmdline =
"%s %s %s %s" % (cxx, flags, include, fname)
364 p = subprocess.Popen(cmdline, shell=
True,
365 stdout=subprocess.PIPE,
366 stderr=subprocess.PIPE,
367 universal_newlines=
True)
368 out, err = p.communicate()
369 self.assertIn(
'error: static assertion failed', err)
372 """Make a particle with optimizable x, y and z attributes, and
373 add it to the model."""
381 """Help handle a test which is expected to fail some fraction of
382 the time. The test is run multiple times and an exception
383 is thrown only if it fails too many times.
384 @note Use of this function should be avoided. If there is a corner
385 case that results in a test 'occasionally' failing, write a
386 new test specifically for that corner case and assert that
387 it fails consistently (and remove the corner case from the
390 prob = chance_of_failure
394 prob = prob*chance_of_failure
395 for i
in range(0, tries):
403 raise AssertionError(
"Too many failures")
406 """Estimate how likely a given block of code is to raise an
410 while failures < 10
and tries < 1000:
416 return failures/tries
419 """Randomize the xyz coordinates of a list of particles"""
425 p.set_value(xkey, random.uniform(-deviation, deviation))
426 p.set_value(ykey, random.uniform(-deviation, deviation))
427 p.set_value(zkey, random.uniform(-deviation, deviation))
430 """Return distance between two given particles"""
434 dx = p1.get_value(xkey) - p2.get_value(xkey)
435 dy = p1.get_value(ykey) - p2.get_value(ykey)
436 dz = p1.get_value(zkey) - p2.get_value(zkey)
437 return math.sqrt(dx*dx + dy*dy + dz*dz)
440 """Check the unary function func's derivatives against numerical
441 approximations between lb and ub"""
442 for f
in [lb + i * step
for i
in range(1, int((ub-lb)/step))]:
443 (v, d) = func.evaluate_with_derivative(f)
445 self.assertAlmostEqual(d, da, delta=max(abs(.1 * d), 0.01))
448 """Make sure that the minimum of the unary function func over the
449 range between lb and ub is at expected_fmin"""
450 fmin, vmin = lb, func.evaluate(lb)
451 for f
in [lb + i * step
for i
in range(1, int((ub-lb)/step))]:
455 self.assertAlmostEqual(fmin, expected_fmin, delta=step)
458 """Check methods that every IMP::Object class should have"""
459 obj.set_was_used(
True)
462 self.assertIsNotNone(cls.get_from(obj))
463 self.assertRaises(ValueError, cls.get_from,
IMP.Model())
465 self.assertIsInstance(str(obj), str)
466 self.assertIsInstance(repr(obj), str)
468 verinf = obj.get_version_info()
477 """Create a bunch of particles in a box"""
482 for i
in range(0, num):
489 def _get_type(self, module, name):
490 return eval(
'type('+module+
"."+name+
')')
493 "Check that all the C++ classes in the module are values or objects."
495 ok = set(exceptions_list + module._value_types + module._object_types
496 + module._raii_types + module._plural_types)
500 if self._get_type(module.__name__, name) == type \
501 and not name.startswith(
"_"):
502 if name.find(
"SwigPyIterator") != -1:
505 if not eval(
'hasattr(%s.%s, "__swig_destroy__")'
506 % (module.__name__, name)):
513 "All IMP classes should be labeled as values or objects to get "
514 "memory management correct in Python. The following are not:\n%s\n"
515 "Please add an IMP_SWIG_OBJECT or IMP_SWIG_VALUE call to the "
516 "Python wrapper, or if the class has a good reason to be "
517 "neither, add the name to the value_object_exceptions list in "
518 "the IMPModuleTest call." % str(bad))
519 for e
in exceptions_list:
521 e
not in module._value_types + module._object_types
522 + module._raii_types + module._plural_types,
523 "Value/Object exception "+e+
" is not an exception")
525 def _check_spelling(self, word, words):
526 """Check that the word is spelled correctly"""
527 if "words" not in dir(self):
529 wordlist = fh.read().split("\n")
531 custom_words = [
"info",
"prechange",
"int",
"ints",
"optimizeds",
532 "graphviz",
"voxel",
"voxels",
"endian",
'rna',
533 'dna',
"xyzr",
"pdbs",
"fft",
"ccc",
"gaussian"]
536 exclude_words = set([
"adapter",
"grey"])
537 self.words = set(wordlist+custom_words) - exclude_words
539 for i
in "0123456789":
544 if word
in self.words:
552 """Check that all the classes in the module follow the IMP
553 naming conventions."""
557 cc = re.compile(
"([A-Z][a-z]*)")
559 if self._get_type(module.__name__, name) == type \
560 and not name.startswith(
"_"):
561 if name.find(
"SwigPyIterator") != -1:
563 for t
in re.findall(cc, name):
564 if not self._check_spelling(t.lower(), words):
565 misspelled.append(t.lower())
570 "All IMP classes should be properly spelled. The following "
571 "are not: %s.\nMisspelled words: %s. Add words to the "
572 "spelling_exceptions variable of the IMPModuleTest if needed."
573 % (str(bad),
", ".join(set(misspelled))))
576 if self._get_type(module.__name__, name) == type \
577 and not name.startswith(
"_"):
578 if name.find(
"SwigPyIterator") != -1:
580 if name.find(
'_') != -1:
582 if name.lower == name:
584 for t
in re.findall(cc, name):
585 if not self._check_spelling(t.lower(), words):
586 print(
"misspelled %s in %s" % (t, name))
590 "All IMP classes should have CamelCase names. The following "
591 "do not: %s." %
"\n".join(bad))
593 def _check_function_name(self, prefix, name, verbs, all, exceptions, words,
596 fullname = prefix+
"."+name
600 'unprotected_evaluate',
"unprotected_evaluate_if_good",
601 "unprotected_evaluate_if_below",
602 'unprotected_evaluate_moved',
"unprotected_evaluate_moved_if_good",
603 "unprotected_evaluate_moved_if_below",
604 "after_evaluate",
"before_evaluate",
"has_attribute",
605 "decorate_particle",
"particle_is_instance"]
606 if name
in old_exceptions:
608 if fullname
in exceptions:
610 if name.endswith(
"swigregister"):
612 if name.lower() != name:
613 if name[0].lower() != name[0]
and name.split(
'_')[0]
in all:
618 tokens = name.split(
"_")
619 if tokens[0]
not in verbs:
622 if not self._check_spelling(t, words):
624 print(
"misspelled %s in %s" % (t, name))
628 def _static_method(self, module, prefix, name):
629 """For static methods of the form Foo.bar SWIG creates free functions
630 named Foo_bar. Exclude these from spelling checks since the method
631 Foo.bar has already been checked."""
632 if prefix
is None and '_' in name:
633 modobj = eval(module)
634 cls, meth = name.split(
'_', 1)
635 if hasattr(modobj, cls):
636 clsobj = eval(module +
'.' + cls)
637 if hasattr(clsobj, meth):
640 def _check_function_names(self, module, prefix, names, verbs, all,
641 exceptions, words, misspelled):
644 typ = self._get_type(module, name)
645 if name.startswith(
"_")
or name ==
"weakref_proxy":
647 if typ
in (types.BuiltinMethodType, types.MethodType) \
648 or (typ == types.FunctionType
and
649 not self._static_method(module, prefix, name)):
650 bad.extend(self._check_function_name(prefix, name, verbs, all,
653 if typ == type
and "SwigPyIterator" not in name:
654 members = eval(
"dir("+module+
"."+name+
")")
655 bad.extend(self._check_function_names(module+
"."+name,
656 name, members, verbs, [],
662 """Check that all the functions in the module follow the IMP
663 naming conventions."""
665 verbs = set([
"add",
"remove",
"get",
"set",
"evaluate",
"compute",
666 "show",
"create",
"destroy",
"push",
"pop",
"write",
667 "read",
"do",
"show",
"load",
"save",
"reset",
"accept",
668 "reject",
"clear",
"handle",
"update",
"apply",
669 "optimize",
"reserve",
"dump",
"propose",
"setup",
670 "teardown",
"visit",
"find",
"run",
"swap",
"link",
671 "validate",
"erase"])
673 bad = self._check_function_names(module.__name__,
None, all, verbs,
674 all, exceptions, words, misspelled)
675 message = (
"All IMP methods and functions should have lower case "
676 "names separated by underscores and beginning with a "
677 "verb, preferably one of ['add', 'remove', 'get', 'set', "
678 "'create', 'destroy']. Each of the words should be a "
679 "properly spelled English word. The following do not "
680 "(given our limited list of verbs that we check for):\n"
681 "%(bad)s\nIf there is a good reason for them not to "
682 "(eg it does start with a verb, just one with a meaning "
683 "that is not covered by the normal list), add them to the "
684 "function_name_exceptions variable in the "
685 "standards_exceptions file. Otherwise, please fix. "
686 "The current verb list is %(verbs)s"
687 % {
"bad":
"\n".join(bad),
"verbs": verbs})
688 if len(misspelled) > 0:
689 message +=
"\nMisspelled words: " +
", ".join(set(misspelled)) \
690 +
". Add words to the spelling_exceptions variable " \
691 +
"of the standards_exceptions file if needed."
692 self.assertEqual(len(bad), 0, message)
695 """Check that all the classes in modulename have a show method"""
696 all = dir(modulename)
697 if hasattr(modulename,
'_raii_types'):
698 excludes = frozenset(
699 modulename._raii_types + modulename._plural_types)
702 excludes = frozenset()
709 if not eval(
'hasattr(%s.%s, "__swig_destroy__")'
710 % (modulename.__name__, f)):
712 if self._get_type(modulename.__name__, f) == type \
713 and not f.startswith(
"_") \
714 and not f.endswith(
"_swigregister")\
715 and f
not in exceptions\
716 and not f.endswith(
"Temp")
and not f.endswith(
"Iterator")\
717 and not f.endswith(
"Exception")
and\
719 if not hasattr(getattr(modulename, f),
'show'):
723 "All IMP classes should support show and __str__. The following "
724 "do not:\n%s\n If there is a good reason for them not to, add "
725 "them to the show_exceptions variable in the IMPModuleTest "
726 "call. Otherwise, please fix." %
"\n".join(not_found))
728 self.assertIn(e, all,
729 "Show exception "+e+
" is not a class in module")
730 self.assertTrue(
not hasattr(getattr(modulename, e),
'show'),
731 "Exception "+e+
" is not really a show exception")
734 """Run the named example script.
735 @return a dictionary of all the script's global variables.
736 This can be queried in a test case to make sure
737 the example performed correctly."""
743 path, name = os.path.split(filename)
744 oldsyspath = sys.path[:]
745 olssysargv = sys.argv[:]
746 sys.path.insert(0, path)
747 sys.argv = [filename]
750 exec(open(filename).read(), vars)
753 except SystemExit
as e:
754 if e.code != 0
and e.code
is not None:
756 "Example exit with code %s" % str(e.code))
759 sys.path = oldsyspath
760 sys.argv = olssysargv
762 return _ExecDictProxy(vars)
765 """Run a Python module as if with "python -m <modname>",
766 with the given list of arguments as sys.argv.
768 If module is an already-imported Python module, run its 'main'
769 function and return the result.
771 If module is a string, run the module in a subprocess and return
772 a subprocess.Popen-like object containing the child stdin,
775 def mock_setup_from_argv(*args, **kwargs):
778 if type(module) == type(os):
781 mod = __import__(module, {}, {}, [
''])
782 modpath = mod.__file__
783 if modpath.endswith(
'.pyc'):
784 modpath = modpath[:-1]
785 if type(module) == type(os):
786 old_sys_argv = sys.argv
788 old_setup = IMP.setup_from_argv
789 IMP.setup_from_argv = mock_setup_from_argv
791 sys.argv = [modpath] + args
794 IMP.setup_from_argv = old_setup
795 sys.argv = old_sys_argv
797 return _SubprocessWrapper(sys.executable, [modpath] + args)
800 """Check a Python module designed to be runnable with 'python -m'
801 to make sure it supports standard command line options."""
804 out, err = r.communicate()
805 self.assertEqual(r.returncode, 0)
806 self.assertNotEqual(err,
"")
807 self.assertEqual(out,
"")
810 class _ExecDictProxy(object):
811 """exec returns a Python dictionary, which contains IMP objects, other
812 Python objects, as well as base Python modules (such as sys and
813 __builtins__). If we just delete this dictionary, it is entirely
814 possible that base Python modules are removed from the dictionary
815 *before* some IMP objects. This will prevent the IMP objects' Python
816 destructors from running properly, so C++ objects will not be
817 cleaned up. This class proxies the base dict class, and on deletion
818 attempts to remove keys from the dictionary in an order that allows
819 IMP destructors to fire."""
820 def __init__(self, d):
825 module_type = type(IMP)
828 if type(d[k]) != module_type:
831 for meth
in [
'__contains__',
'__getitem__',
'__iter__',
'__len__',
832 'get',
'has_key',
'items',
'keys',
'values']:
833 exec(
"def %s(self, *args, **keys): "
834 "return self._d.%s(*args, **keys)" % (meth, meth))
837 class _TestResult(unittest.TextTestResult):
839 def __init__(self, stream=None, descriptions=None, verbosity=None):
840 super(_TestResult, self).__init__(stream, descriptions, verbosity)
843 def stopTestRun(self):
844 if 'IMP_TEST_DETAIL_DIR' in os.environ:
847 protocol = min(pickle.HIGHEST_PROTOCOL, 4)
848 fname = (Path(os.environ[
'IMP_TEST_DETAIL_DIR'])
849 / Path(sys.argv[0]).name)
853 if not fname.exists():
854 fname = Path(
"Z:") / fname
855 with open(str(fname),
'wb')
as fh:
856 pickle.dump(self.all_tests, fh, protocol)
857 super(_TestResult, self).stopTestRun()
859 def startTest(self, test):
860 super(_TestResult, self).startTest(test)
861 test.start_time = datetime.datetime.now()
863 def _test_finished(self, test, state, detail=None):
864 if hasattr(test,
'start_time'):
865 delta = datetime.datetime.now() - test.start_time
867 pv = delta.total_seconds()
868 except AttributeError:
869 pv = (float(delta.microseconds)
871 + delta.days * 24 * 3600) * 10**6) / 10**6
873 self.stream.write(
"in %.3fs ... " % pv)
878 if detail
is not None and not isinstance(detail, str):
879 detail = self._exc_info_to_string(detail, test)
880 test_doc = self.getDescription(test)
881 test_name = test.id()
882 if test_name.startswith(
'__main__.'):
883 test_name = test_name[9:]
884 self.all_tests.append({
'name': test_name,
885 'docstring': test_doc,
886 'time': pv,
'state': state,
'detail': detail})
888 def addSuccess(self, test):
889 self._test_finished(test,
'OK')
890 super(_TestResult, self).addSuccess(test)
892 def addError(self, test, err):
893 self._test_finished(test,
'ERROR', err)
894 super(_TestResult, self).addError(test, err)
896 def addFailure(self, test, err):
897 self._test_finished(test,
'FAIL', err)
898 super(_TestResult, self).addFailure(test, err)
900 def addSkip(self, test, reason):
901 self._test_finished(test,
'SKIP', reason)
902 super(_TestResult, self).addSkip(test, reason)
904 def addExpectedFailure(self, test, err):
905 self._test_finished(test,
'EXPFAIL', err)
906 super(_TestResult, self).addExpectedFailure(test, err)
908 def addUnexpectedSuccess(self, test):
909 self._test_finished(test,
'UNEXPSUC')
910 super(_TestResult, self).addUnexpectedSuccess(test)
912 def getDescription(self, test):
913 doc_first_line = test.shortDescription()
914 if self.descriptions
and doc_first_line:
915 return doc_first_line
920 class _TestRunner(unittest.TextTestRunner):
921 def _makeResult(self):
922 return _TestResult(self.stream, self.descriptions, self.verbosity)
926 """Run a set of tests; similar to unittest.main().
927 Obviates the need to separately import the 'unittest' module, and
928 ensures that main() is from the same unittest module that the
929 IMP.test testcases are. In addition, turns on some extra checks
930 (e.g. trying to use deprecated code will cause an exception
934 return unittest.main(testRunner=_TestRunner, *args, **keys)
937 class _SubprocessWrapper(subprocess.Popen):
938 def __init__(self, app, args, cwd=None):
941 if sys.platform ==
'win32' and app != sys.executable:
943 libdir = os.environ[
'PYTHONPATH'].split(
';')[0]
944 env = os.environ.copy()
945 env[
'PATH'] +=
';' + libdir
948 subprocess.Popen.__init__(self, [app]+list(args),
949 stdin=subprocess.PIPE,
950 stdout=subprocess.PIPE,
951 stderr=subprocess.PIPE, env=env, cwd=cwd,
952 universal_newlines=
True)
956 """Super class for simple IMP application test cases"""
957 def _get_application_file_name(self, filename):
960 if sys.platform ==
'win32':
965 """Run an application with the given list of arguments.
966 @return a subprocess.Popen-like object containing the child stdin,
969 filename = self._get_application_file_name(app)
970 if sys.platform ==
'win32':
972 return _SubprocessWrapper(os.path.join(os.environ[
'IMP_BIN_DIR'],
973 filename), args, cwd=cwd)
975 return _SubprocessWrapper(filename, args, cwd=cwd)
978 """Run a Python application with the given list of arguments.
979 The Python application should be self-runnable (i.e. it should
980 be executable and with a #! on the first line).
981 @return a subprocess.Popen-like object containing the child stdin,
985 if sys.executable !=
'/usr/bin/python' and 'IMP_BIN_DIR' in os.environ:
986 return _SubprocessWrapper(
988 [os.path.join(os.environ[
'IMP_BIN_DIR'], app)] + args)
990 return _SubprocessWrapper(app, args)
993 """Import an installed Python application, rather than running it.
994 This is useful to directly test components of the application.
995 @return the Python module object."""
997 import importlib.machinery
1001 name = os.path.splitext(app)[0]
1002 pathname = os.path.join(os.environ[
'IMP_BIN_DIR'], app)
1004 return importlib.machinery.SourceFileLoader(name,
1005 pathname).load_module()
1007 return imp.load_source(name, pathname)
1010 """Run an application with the given list of arguments.
1011 @return a subprocess.Popen-like object containing the child stdin,
1014 return _SubprocessWrapper(sys.executable, [app]+args)
1017 """Assert that the application exited cleanly (return value = 0)."""
1019 raise OSError(
"Application exited with signal %d\n" % -ret
1024 "Application exited uncleanly, with exit code %d\n" % ret
1028 """Read and return a set of shell commands from a doxygen file.
1029 Each command is assumed to be in a \code{.sh}...\endcode block.
1030 The doxygen file is specified relative to the test file itself.
1031 This is used to make sure the commands shown in an application
1032 example actually work (the testcase can also check the resulting
1033 files for correctness)."""
1034 def win32_normpath(p):
1037 return " ".join([os.path.normpath(x)
for x
in p.split()])
1039 def fix_win32_command(cmd):
1041 if cmd.startswith(
'cp -r '):
1042 return 'xcopy /E ' + win32_normpath(cmd[6:])
1043 elif cmd.startswith(
'cp '):
1044 return 'copy ' + win32_normpath(cmd[3:])
1047 d = os.path.dirname(sys.argv[0])
1048 doc = os.path.join(d, doxfile)
1052 with open(doc)
as fh:
1053 for line
in fh.readlines():
1054 if '\code{.sh}' in line:
1056 elif '\endcode' in line:
1059 cmds.append(line.rstrip(
'\r\n').replace(
1060 '<imp_example_path>', example_path))
1061 if sys.platform ==
'win32':
1062 cmds = [fix_win32_command(x)
for x
in cmds]
1066 "Print and run a shell command, as returned by read_shell_commands()"
1068 p = subprocess.call(cmd, shell=
True)
1070 raise OSError(
"%s failed with exit value %d" % (cmd, p))
1074 """Check to make sure the number of C++ object references is as expected"""
1076 def __init__(self, testcase):
1080 IMP._director_objects.cleanup()
1081 self.__testcase = testcase
1083 self.__basenum = IMP.Object.get_number_of_live_objects()
1087 "Make sure that the number of references matches the expected value."
1089 IMP._director_objects.cleanup()
1092 if x
not in self.__names]
1093 newnum = IMP.Object.get_number_of_live_objects()-self.__basenum
1094 t.assertEqual(newnum, expected,
1095 "Number of objects don't match: "
1096 + str(newnum) +
" != " + str(expected) +
" found "
1101 """Check to make sure the number of director references is as expected"""
1103 def __init__(self, testcase):
1104 IMP._director_objects.cleanup()
1105 self.__testcase = testcase
1106 self.__basenum = IMP._director_objects.get_object_count()
1109 """Make sure that the number of references matches the expected value.
1110 If force_cleanup is set, clean up any unused references first before
1111 doing the assertion.
1115 IMP._director_objects.cleanup()
1116 t.assertEqual(IMP._director_objects.get_object_count()
1117 - self.__basenum, expected)
1126 if sys.platform ==
'win32' and 'PYTHONPATH' in os.environ \
1127 and 'IMP_BIN_DIR' in os.environ:
1128 libdir = os.environ[
'PYTHONPATH'].split(
';')[0]
1129 bindir = os.environ[
'IMP_BIN_DIR']
1130 path = os.environ[
'PATH']
1131 if libdir
not in path
or bindir
not in path:
1132 os.environ[
'PATH'] = bindir +
';' + libdir +
';' + path
1135 __version__ =
"2.21.0"
1138 '''Return the version of this module, as a string'''
1142 '''Return the fully-qualified name of this module'''
1146 '''Return the full path to one of this module's data files'''
1148 return IMP._get_module_data_path(
"test", fname)
1151 '''Return the full path to one of this module's example files'''
1153 return IMP._get_module_example_path(
"test", fname)
def run_python_module
Run a Python module as if with "python -m <modname>", with the given list of arguments as sys...
def temporary_working_directory
Simple context manager to run in a temporary directory.
def assertApplicationExitedCleanly
Assert that the application exited cleanly (return value = 0).
CheckLevel get_check_level()
Get the current audit mode.
def import_python_application
Import an installed Python application, rather than running it.
def open_input_file
Open and return an input file in the top-level test directory.
def run_application
Run an application with the given list of arguments.
def randomize_particles
Randomize the xyz coordinates of a list of particles.
def get_module_version
Return the version of this module, as a string.
A general exception for an internal error in IMP.
def main
Run a set of tests; similar to unittest.main().
An exception for an invalid usage of IMP.
Super class for simple IMP application test cases.
def assertCompileFails
Test that the given C++ code fails to compile with a static assertion.
def assertRaisesInternalException
Assert that the given callable object raises InternalException.
def assert_number
Make sure that the number of references matches the expected value.
Check to make sure the number of director references is as expected.
def assertShow
Check that all the classes in modulename have a show method.
def get_data_path
Return the full path to one of this module's data files.
def get_example_path
Return the full path to one of this module's example files.
def run_shell_command
Print and run a shell command, as returned by read_shell_commands()
def assertRaisesUsageException
Assert that the given callable object raises UsageException.
Vector3D get_random_vector_in(const Cylinder3D &c)
Generate a random vector in a cylinder with uniform density.
def assertSequenceAlmostEqual
Fail if the difference between any two items in the two sequences are exceed the specified number of ...
Class for storing model, its restraints, constraints, and particles.
def run_python_application
Run a Python application with the given list of arguments.
def assert_number
Make sure that the number of references matches the expected value.
Strings get_live_object_names()
Return the names of all live objects.
def check_standard_object_methods
Check methods that every IMP::Object class should have.
def particle_distance
Return distance between two given particles.
def check_unary_function_deriv
Check the unary function func's derivatives against numerical approximations between lb and ub...
def get_tmp_file_name
Get the full name of an output file in the tmp directory.
Version and module information for Objects.
def run_example
Run the named example script.
def get_magnitude
Get the magnitude of a list of floats.
void set_deprecation_exceptions(bool tf)
Toggle whether an exception is thrown when a deprecated method is used.
def check_unary_function_min
Make sure that the minimum of the unary function func over the range between lb and ub is at expected...
def probabilistic_check
Help handle a test which is expected to fail some fraction of the time.
def create_particles_in_box
Create a bunch of particles in a box.
General purpose algebraic and geometric methods that are expected to be used by a wide variety of IMP...
def assertNotImplemented
Assert that the given callable object is not implemented.
The general base class for IMP exceptions.
def numerical_derivative
Calculate the derivative of the single-value function func at point val.
std::string get_example_path(std::string file_name)
Return the full path to one of this module's example files.
def xyz_numerical_derivatives
Calculate the x,y and z derivatives of the scoring function sf on the xyz particle.
def failure_probability
Estimate how likely a given block of code is to raise an AssertionError.
def assertClassNames
Check that all the classes in the module follow the IMP naming conventions.
def create_point_particle
Make a particle with optimizable x, y and z attributes, and add it to the model.
Class to handle individual particles of a Model object.
def read_shell_commands
Read and return a set of shell commands from a doxygen file.
Check to make sure the number of C++ object references is as expected.
def assertFunctionNames
Check that all the functions in the module follow the IMP naming conventions.
def assertValueObjects
Check that all the C++ classes in the module are values or objects.
def assertNumPyArrayEqual
Fail if the given numpy array doesn't match expected.
def get_module_name
Return the fully-qualified name of this module.
Super class for IMP test cases.
def assertXYZDerivativesInTolerance
Assert that x,y,z analytical derivatives match numerical within a tolerance, or a percentage (of the ...
def temporary_directory
Simple context manager to make a temporary directory.
def run_script
Run an application with the given list of arguments.
def get_input_file_name
Get the full name of an input file in the top-level test directory.
def check_runnable_python_module
Check a Python module designed to be runnable with 'python -m' to make sure it supports standard comm...
void set_check_level(CheckLevel tf)
Control runtime checks in the code.