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], 0)**.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,
224 although for backwards compatibility a Model is also accepted."""
226 derivs = xyz.get_derivatives()
228 pct = percentage / 100.0
229 self.assertAlmostEqual(
232 msg=
"Don't match "+str(derivs) + str(num_derivs))
233 self.assertAlmostEqual(derivs[0], num_derivs[0],
234 delta=max(tolerance, abs(derivs[0]) * pct))
235 self.assertAlmostEqual(derivs[1], num_derivs[1],
236 delta=max(tolerance, abs(derivs[1]) * pct))
237 self.assertAlmostEqual(derivs[2], num_derivs[2],
238 delta=max(tolerance, abs(derivs[2]) * pct))
241 """Fail if the given numpy array doesn't match expected"""
242 if IMP.IMP_KERNEL_HAS_NUMPY:
244 self.assertIsInstance(numpy_array, numpy.ndarray)
245 numpy.testing.assert_array_equal(numpy_array, exp_array)
247 self.assertEqual(numpy_array, exp_array)
251 """Fail if the difference between any two items in the two sequences
252 are exceed the specified number of places or delta. See
255 if delta
is not None and places
is not None:
256 raise TypeError(
"specify delta or places not both")
259 ftypename = ftype.__name__
261 stypename = stype.__name__
263 raise self.failureException(
264 'Sequences are of different types: %s != %s' % (
265 ftypename, stypename))
269 except (NotImplementedError, TypeError):
270 raise self.failureException(
271 'First %s has no length' % (ftypename))
274 except (NotImplementedError, TypeError):
275 raise self.failureException(
276 'Second %s has no length' % (stypename))
279 raise self.failureException(
280 'Sequences have non equal lengths: %d != %d' % (flen, slen))
283 for i
in range(min(flen, slen)):
284 differing =
'%ss differ: %s != %s\n' % (
285 ftypename.capitalize(), safe_repr(first),
290 except (TypeError, IndexError, NotImplementedError):
291 differing += (
'\nUnable to index element %d of first %s\n' %
297 except (TypeError, IndexError, NotImplementedError):
298 differing += (
'\nUnable to index element %d of second %s\n' %
303 self.assertAlmostEqual(
304 f, s, places=places, msg=msg, delta=delta)
305 except (TypeError, ValueError, NotImplementedError,
308 "\nFirst differing element "
309 "%d:\n%s\n%s\n") % (i, safe_repr(f), safe_repr(s))
314 standardMsg = differing
315 diffMsg =
'\n' +
'\n'.join(
316 difflib.ndiff(pprint.pformat(first).splitlines(),
317 pprint.pformat(second).splitlines()))
318 standardMsg = self._truncateMessage(standardMsg, diffMsg)
319 msg = self._formatMessage(msg, standardMsg)
320 raise self.failureException(msg)
323 """Make a particle with optimizable x, y and z attributes, and
324 add it to the model."""
332 """Help handle a test which is expected to fail some fraction of
333 the time. The test is run multiple times and an exception
334 is thrown only if it fails too many times.
335 @note Use of this function should be avoided. If there is a corner
336 case that results in a test 'occasionally' failing, write a
337 new test specifically for that corner case and assert that
338 it fails consistently (and remove the corner case from the
341 prob = chance_of_failure
345 prob = prob*chance_of_failure
346 for i
in range(0, tries):
354 raise AssertionError(
"Too many failures")
357 """Estimate how likely a given block of code is to raise an
361 while failures < 10
and tries < 1000:
367 return failures/tries
370 """Randomize the xyz coordinates of a list of particles"""
376 p.set_value(xkey, random.uniform(-deviation, deviation))
377 p.set_value(ykey, random.uniform(-deviation, deviation))
378 p.set_value(zkey, random.uniform(-deviation, deviation))
381 """Return distance between two given particles"""
385 dx = p1.get_value(xkey) - p2.get_value(xkey)
386 dy = p1.get_value(ykey) - p2.get_value(ykey)
387 dz = p1.get_value(zkey) - p2.get_value(zkey)
388 return math.sqrt(dx*dx + dy*dy + dz*dz)
391 """Check the unary function func's derivatives against numerical
392 approximations between lb and ub"""
393 for f
in [lb + i * step
for i
in range(1, int((ub-lb)/step))]:
394 (v, d) = func.evaluate_with_derivative(f)
396 self.assertAlmostEqual(d, da, delta=max(abs(.1 * d), 0.01))
399 """Make sure that the minimum of the unary function func over the
400 range between lb and ub is at expected_fmin"""
401 fmin, vmin = lb, func.evaluate(lb)
402 for f
in [lb + i * step
for i
in range(1, int((ub-lb)/step))]:
406 self.assertAlmostEqual(fmin, expected_fmin, delta=step)
409 """Check methods that every IMP::Object class should have"""
410 obj.set_was_used(
True)
413 self.assertIsNotNone(cls.get_from(obj))
414 self.assertRaises(ValueError, cls.get_from,
IMP.Model())
416 self.assertIsInstance(str(obj), str)
417 self.assertIsInstance(repr(obj), str)
419 verinf = obj.get_version_info()
428 """Create a bunch of particles in a box"""
433 for i
in range(0, num):
440 def _get_type(self, module, name):
441 return eval(
'type('+module+
"."+name+
')')
444 "Check that all the C++ classes in the module are values or objects."
446 ok = set(exceptions_list + module._value_types + module._object_types
447 + module._raii_types + module._plural_types)
451 if self._get_type(module.__name__, name) == type \
452 and not name.startswith(
"_"):
453 if name.find(
"SwigPyIterator") != -1:
456 if not eval(
'hasattr(%s.%s, "__swig_destroy__")'
457 % (module.__name__, name)):
464 "All IMP classes should be labeled as values or objects to get "
465 "memory management correct in Python. The following are not:\n%s\n"
466 "Please add an IMP_SWIG_OBJECT or IMP_SWIG_VALUE call to the "
467 "Python wrapper, or if the class has a good reason to be "
468 "neither, add the name to the value_object_exceptions list in "
469 "the IMPModuleTest call." % str(bad))
470 for e
in exceptions_list:
472 e
not in module._value_types + module._object_types
473 + module._raii_types + module._plural_types,
474 "Value/Object exception "+e+
" is not an exception")
476 def _check_spelling(self, word, words):
477 """Check that the word is spelled correctly"""
478 if "words" not in dir(self):
480 wordlist = fh.read().split("\n")
482 custom_words = [
"info",
"prechange",
"int",
"ints",
"optimizeds",
483 "graphviz",
"voxel",
"voxels",
"endian",
'rna',
484 'dna',
"xyzr",
"pdbs",
"fft",
"ccc",
"gaussian"]
487 exclude_words = set([
"adapter",
"grey"])
488 self.words = set(wordlist+custom_words) - exclude_words
490 for i
in "0123456789":
495 if word
in self.words:
503 """Check that all the classes in the module follow the IMP
504 naming conventions."""
508 cc = re.compile(
"([A-Z][a-z]*)")
510 if self._get_type(module.__name__, name) == type \
511 and not name.startswith(
"_"):
512 if name.find(
"SwigPyIterator") != -1:
514 for t
in re.findall(cc, name):
515 if not self._check_spelling(t.lower(), words):
516 misspelled.append(t.lower())
521 "All IMP classes should be properly spelled. The following "
522 "are not: %s.\nMisspelled words: %s. Add words to the "
523 "spelling_exceptions variable of the IMPModuleTest if needed."
524 % (str(bad),
", ".join(set(misspelled))))
527 if self._get_type(module.__name__, name) == type \
528 and not name.startswith(
"_"):
529 if name.find(
"SwigPyIterator") != -1:
531 if name.find(
'_') != -1:
533 if name.lower == name:
535 for t
in re.findall(cc, name):
536 if not self._check_spelling(t.lower(), words):
537 print(
"misspelled %s in %s" % (t, name))
541 "All IMP classes should have CamelCase names. The following "
542 "do not: %s." %
"\n".join(bad))
544 def _check_function_name(self, prefix, name, verbs, all, exceptions, words,
547 fullname = prefix+
"."+name
551 'unprotected_evaluate',
"unprotected_evaluate_if_good",
552 "unprotected_evaluate_if_below",
553 'unprotected_evaluate_moved',
"unprotected_evaluate_moved_if_good",
554 "unprotected_evaluate_moved_if_below",
555 "after_evaluate",
"before_evaluate",
"has_attribute",
556 "decorate_particle",
"particle_is_instance"]
557 if name
in old_exceptions:
559 if fullname
in exceptions:
561 if name.endswith(
"swigregister"):
563 if name.lower() != name:
564 if name[0].lower() != name[0]
and name.split(
'_')[0]
in all:
569 tokens = name.split(
"_")
570 if tokens[0]
not in verbs:
573 if not self._check_spelling(t, words):
575 print(
"misspelled %s in %s" % (t, name))
579 def _static_method(self, module, prefix, name):
580 """For static methods of the form Foo.bar SWIG creates free functions
581 named Foo_bar. Exclude these from spelling checks since the method
582 Foo.bar has already been checked."""
583 if prefix
is None and '_' in name:
584 modobj = eval(module)
585 cls, meth = name.split(
'_', 1)
586 if hasattr(modobj, cls):
587 clsobj = eval(module +
'.' + cls)
588 if hasattr(clsobj, meth):
591 def _check_function_names(self, module, prefix, names, verbs, all,
592 exceptions, words, misspelled):
595 typ = self._get_type(module, name)
596 if name.startswith(
"_")
or name ==
"weakref_proxy":
598 if typ
in (types.BuiltinMethodType, types.MethodType) \
599 or (typ == types.FunctionType
and
600 not self._static_method(module, prefix, name)):
601 bad.extend(self._check_function_name(prefix, name, verbs, all,
604 if typ == type
and "SwigPyIterator" not in name:
605 members = eval(
"dir("+module+
"."+name+
")")
606 bad.extend(self._check_function_names(module+
"."+name,
607 name, members, verbs, [],
613 """Check that all the functions in the module follow the IMP
614 naming conventions."""
616 verbs = set([
"add",
"remove",
"get",
"set",
"evaluate",
"compute",
617 "show",
"create",
"destroy",
"push",
"pop",
"write",
618 "read",
"do",
"show",
"load",
"save",
"reset",
"accept",
619 "reject",
"clear",
"handle",
"update",
"apply",
620 "optimize",
"reserve",
"dump",
"propose",
"setup",
621 "teardown",
"visit",
"find",
"run",
"swap",
"link",
622 "validate",
"erase"])
624 bad = self._check_function_names(module.__name__,
None, all, verbs,
625 all, exceptions, words, misspelled)
626 message = (
"All IMP methods and functions should have lower case "
627 "names separated by underscores and beginning with a "
628 "verb, preferably one of ['add', 'remove', 'get', 'set', "
629 "'create', 'destroy']. Each of the words should be a "
630 "properly spelled English word. The following do not "
631 "(given our limited list of verbs that we check for):\n"
632 "%(bad)s\nIf there is a good reason for them not to "
633 "(eg it does start with a verb, just one with a meaning "
634 "that is not covered by the normal list), add them to the "
635 "function_name_exceptions variable in the "
636 "standards_exceptions file. Otherwise, please fix. "
637 "The current verb list is %(verbs)s"
638 % {
"bad":
"\n".join(bad),
"verbs": verbs})
639 if len(misspelled) > 0:
640 message +=
"\nMisspelled words: " +
", ".join(set(misspelled)) \
641 +
". Add words to the spelling_exceptions variable " \
642 +
"of the standards_exceptions file if needed."
643 self.assertEqual(len(bad), 0, message)
646 """Check that all the classes in modulename have a show method"""
647 all = dir(modulename)
654 if not eval(
'hasattr(%s.%s, "__swig_destroy__")'
655 % (modulename.__name__, f)):
657 if self._get_type(modulename.__name__, f) == type \
658 and not f.startswith(
"_") \
659 and not f.endswith(
"_swigregister")\
660 and f
not in exceptions\
661 and not f.endswith(
"Temp")
and not f.endswith(
"Iterator")\
662 and not f.endswith(
"Exception")
and\
663 f
not in modulename._raii_types
and \
664 f
not in modulename._plural_types:
665 if not hasattr(getattr(modulename, f),
'show'):
669 "All IMP classes should support show and __str__. The following "
670 "do not:\n%s\n If there is a good reason for them not to, add "
671 "them to the show_exceptions variable in the IMPModuleTest "
672 "call. Otherwise, please fix." %
"\n".join(not_found))
674 self.assertIn(e, all,
675 "Show exception "+e+
" is not a class in module")
676 self.assertTrue(
not hasattr(getattr(modulename, e),
'show'),
677 "Exception "+e+
" is not really a show exception")
680 """Run the named example script.
681 @return a dictionary of all the script's global variables.
682 This can be queried in a test case to make sure
683 the example performed correctly."""
689 path, name = os.path.split(filename)
690 oldsyspath = sys.path[:]
691 olssysargv = sys.argv[:]
692 sys.path.insert(0, path)
693 sys.argv = [filename]
697 exec(open(filename).read(), vars)
700 except SystemExit
as e:
701 if e.code != 0
and e.code
is not None:
703 "Example exit with code %s" % str(e.code))
707 sys.path = oldsyspath
708 sys.argv = olssysargv
710 return _ExecDictProxy(vars)
713 """Run a Python module as if with "python -m <modname>",
714 with the given list of arguments as sys.argv.
716 If module is an already-imported Python module, run its 'main'
717 function and return the result.
719 If module is a string, run the module in a subprocess and return
720 a subprocess.Popen-like object containing the child stdin,
723 def mock_setup_from_argv(*args, **kwargs):
726 if type(module) == type(os):
729 mod = __import__(module, {}, {}, [
''])
730 modpath = mod.__file__
731 if modpath.endswith(
'.pyc'):
732 modpath = modpath[:-1]
733 if type(module) == type(os):
734 old_sys_argv = sys.argv
736 old_setup = IMP.setup_from_argv
737 IMP.setup_from_argv = mock_setup_from_argv
739 sys.argv = [modpath] + args
742 IMP.setup_from_argv = old_setup
743 sys.argv = old_sys_argv
745 return _SubprocessWrapper(sys.executable, [modpath] + args)
748 """Check a Python module designed to be runnable with 'python -m'
749 to make sure it supports standard command line options."""
752 out, err = r.communicate()
753 self.assertEqual(r.returncode, 0)
754 self.assertNotEqual(err,
"")
755 self.assertEqual(out,
"")
758 class _ExecDictProxy(object):
759 """exec returns a Python dictionary, which contains IMP objects, other
760 Python objects, as well as base Python modules (such as sys and
761 __builtins__). If we just delete this dictionary, it is entirely
762 possible that base Python modules are removed from the dictionary
763 *before* some IMP objects. This will prevent the IMP objects' Python
764 destructors from running properly, so C++ objects will not be
765 cleaned up. This class proxies the base dict class, and on deletion
766 attempts to remove keys from the dictionary in an order that allows
767 IMP destructors to fire."""
768 def __init__(self, d):
773 module_type = type(IMP)
776 if type(d[k]) != module_type:
779 for meth
in [
'__contains__',
'__getitem__',
'__iter__',
'__len__',
780 'get',
'has_key',
'items',
'keys',
'values']:
781 exec(
"def %s(self, *args, **keys): "
782 "return self._d.%s(*args, **keys)" % (meth, meth))
785 class _TestResult(unittest.TextTestResult):
787 def __init__(self, stream=None, descriptions=None, verbosity=None):
788 super(_TestResult, self).__init__(stream, descriptions, verbosity)
791 def stopTestRun(self):
792 if 'IMP_TEST_DETAIL_DIR' in os.environ:
793 fname = (Path(os.environ[
'IMP_TEST_DETAIL_DIR'])
794 / Path(sys.argv[0]).name)
795 with open(str(fname),
'wb')
as fh:
796 pickle.dump(self.all_tests, fh, -1)
797 super(_TestResult, self).stopTestRun()
799 def startTest(self, test):
800 super(_TestResult, self).startTest(test)
801 test.start_time = datetime.datetime.now()
803 def _test_finished(self, test, state, detail=None):
804 delta = datetime.datetime.now() - test.start_time
806 pv = delta.total_seconds()
807 except AttributeError:
808 pv = (float(delta.microseconds)
809 + (delta.seconds + delta.days * 24 * 3600) * 10**6) / 10**6
811 self.stream.write(
"in %.3fs ... " % pv)
812 if detail
is not None and not isinstance(detail, str):
813 detail = self._exc_info_to_string(detail, test)
814 test_doc = self.getDescription(test)
815 test_name = test.id()
816 if test_name.startswith(
'__main__.'):
817 test_name = test_name[9:]
818 self.all_tests.append({
'name': test_name,
819 'docstring': test_doc,
820 'time': pv,
'state': state,
'detail': detail})
822 def addSuccess(self, test):
823 self._test_finished(test,
'OK')
824 super(_TestResult, self).addSuccess(test)
826 def addError(self, test, err):
827 self._test_finished(test,
'ERROR', err)
828 super(_TestResult, self).addError(test, err)
830 def addFailure(self, test, err):
831 self._test_finished(test,
'FAIL', err)
832 super(_TestResult, self).addFailure(test, err)
834 def addSkip(self, test, reason):
835 self._test_finished(test,
'SKIP', reason)
836 super(_TestResult, self).addSkip(test, reason)
838 def addExpectedFailure(self, test, err):
839 self._test_finished(test,
'EXPFAIL', err)
840 super(_TestResult, self).addExpectedFailure(test, err)
842 def addUnexpectedSuccess(self, test):
843 self._test_finished(test,
'UNEXPSUC')
844 super(_TestResult, self).addUnexpectedSuccess(test)
846 def getDescription(self, test):
847 doc_first_line = test.shortDescription()
848 if self.descriptions
and doc_first_line:
849 return doc_first_line
854 class _TestRunner(unittest.TextTestRunner):
855 def _makeResult(self):
856 return _TestResult(self.stream, self.descriptions, self.verbosity)
860 """Run a set of tests; similar to unittest.main().
861 Obviates the need to separately import the 'unittest' module, and
862 ensures that main() is from the same unittest module that the
863 IMP.test testcases are. In addition, turns on some extra checks
864 (e.g. trying to use deprecated code will cause an exception
868 return unittest.main(testRunner=_TestRunner, *args, **keys)
871 class _SubprocessWrapper(subprocess.Popen):
872 def __init__(self, app, args, cwd=None):
875 if sys.platform ==
'win32' and app != sys.executable:
877 libdir = os.environ[
'PYTHONPATH'].split(
';')[0]
878 env = os.environ.copy()
879 env[
'PATH'] +=
';' + libdir
882 subprocess.Popen.__init__(self, [app]+list(args),
883 stdin=subprocess.PIPE,
884 stdout=subprocess.PIPE,
885 stderr=subprocess.PIPE, env=env, cwd=cwd,
886 universal_newlines=
True)
890 """Super class for simple IMP application test cases"""
891 def _get_application_file_name(self, filename):
894 if sys.platform ==
'win32':
899 """Run an application with the given list of arguments.
900 @return a subprocess.Popen-like object containing the child stdin,
903 filename = self._get_application_file_name(app)
904 if sys.platform ==
'win32':
906 return _SubprocessWrapper(os.path.join(os.environ[
'IMP_BIN_DIR'],
907 filename), args, cwd=cwd)
909 return _SubprocessWrapper(filename, args, cwd=cwd)
912 """Run a Python application with the given list of arguments.
913 The Python application should be self-runnable (i.e. it should
914 be executable and with a #! on the first line).
915 @return a subprocess.Popen-like object containing the child stdin,
919 if sys.executable !=
'/usr/bin/python' and 'IMP_BIN_DIR' in os.environ:
920 return _SubprocessWrapper(
922 [os.path.join(os.environ[
'IMP_BIN_DIR'], app)] + args)
924 return _SubprocessWrapper(app, args)
927 """Import an installed Python application, rather than running it.
928 This is useful to directly test components of the application.
929 @return the Python module object."""
931 import importlib.machinery
935 name = os.path.splitext(app)[0]
936 pathname = os.path.join(os.environ[
'IMP_BIN_DIR'], app)
938 return importlib.machinery.SourceFileLoader(name,
939 pathname).load_module()
941 return imp.load_source(name, pathname)
944 """Run an application with the given list of arguments.
945 @return a subprocess.Popen-like object containing the child stdin,
948 return _SubprocessWrapper(sys.executable, [app]+args)
951 """Assert that the application exited cleanly (return value = 0)."""
953 raise OSError(
"Application exited with signal %d\n" % -ret
958 "Application exited uncleanly, with exit code %d\n" % ret
962 """Read and return a set of shell commands from a doxygen file.
963 Each command is assumed to be in a \code{.sh}...\endcode block.
964 The doxygen file is specified relative to the test file itself.
965 This is used to make sure the commands shown in an application
966 example actually work (the testcase can also check the resulting
967 files for correctness)."""
968 def win32_normpath(p):
971 return " ".join([os.path.normpath(x)
for x
in p.split()])
973 def fix_win32_command(cmd):
975 if cmd.startswith(
'cp -r '):
976 return 'xcopy /E ' + win32_normpath(cmd[6:])
977 elif cmd.startswith(
'cp '):
978 return 'copy ' + win32_normpath(cmd[3:])
981 d = os.path.dirname(sys.argv[0])
982 doc = os.path.join(d, doxfile)
986 with open(doc)
as fh:
987 for line
in fh.readlines():
988 if '\code{.sh}' in line:
990 elif '\endcode' in line:
993 cmds.append(line.rstrip(
'\r\n').replace(
994 '<imp_example_path>', example_path))
995 if sys.platform ==
'win32':
996 cmds = [fix_win32_command(x)
for x
in cmds]
1000 "Print and run a shell command, as returned by read_shell_commands()"
1002 p = subprocess.call(cmd, shell=
True)
1004 raise OSError(
"%s failed with exit value %d" % (cmd, p))
1008 """Check to make sure the number of C++ object references is as expected"""
1010 def __init__(self, testcase):
1014 IMP._director_objects.cleanup()
1015 self.__testcase = testcase
1017 self.__basenum = IMP.Object.get_number_of_live_objects()
1021 "Make sure that the number of references matches the expected value."
1023 IMP._director_objects.cleanup()
1026 if x
not in self.__names]
1027 newnum = IMP.Object.get_number_of_live_objects()-self.__basenum
1028 t.assertEqual(newnum, expected,
1029 "Number of objects don't match: "
1030 + str(newnum) +
" != " + str(expected) +
" found "
1035 """Check to make sure the number of director references is as expected"""
1037 def __init__(self, testcase):
1038 IMP._director_objects.cleanup()
1039 self.__testcase = testcase
1040 self.__basenum = IMP._director_objects.get_object_count()
1043 """Make sure that the number of references matches the expected value.
1044 If force_cleanup is set, clean up any unused references first before
1045 doing the assertion.
1049 IMP._director_objects.cleanup()
1050 t.assertEqual(IMP._director_objects.get_object_count()
1051 - self.__basenum, expected)
1060 if sys.platform ==
'win32' and 'PYTHONPATH' in os.environ \
1061 and 'IMP_BIN_DIR' in os.environ:
1062 libdir = os.environ[
'PYTHONPATH'].split(
';')[0]
1063 bindir = os.environ[
'IMP_BIN_DIR']
1064 path = os.environ[
'PATH']
1065 if libdir
not in path
or bindir
not in path:
1066 os.environ[
'PATH'] = bindir +
';' + libdir +
';' + path
1069 __version__ =
"2.18.0"
1072 '''Return the version of this module, as a string'''
1076 '''Return the fully-qualified name of this module'''
1080 '''Return the full path to one of this module's data files'''
1082 return IMP._get_module_data_path(
"test", fname)
1085 '''Return the full path to one of this module's example files'''
1087 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 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.