PageRenderTime 50ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 1ms

/django/test/simple.py

https://bitbucket.org/elissawolf92/hackpack
Python | 372 lines | 249 code | 40 blank | 83 comment | 76 complexity | e66a9fc694d7d98efa2b2163c4957628 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0
  1. import unittest as real_unittest
  2. from django.conf import settings
  3. from django.core.exceptions import ImproperlyConfigured
  4. from django.db.models import get_app, get_apps
  5. from django.test import _doctest as doctest
  6. from django.test.utils import setup_test_environment, teardown_test_environment
  7. from django.test.testcases import OutputChecker, DocTestRunner, TestCase
  8. from django.utils import unittest
  9. try:
  10. all
  11. except NameError:
  12. from django.utils.itercompat import all
  13. __all__ = ('DjangoTestRunner', 'DjangoTestSuiteRunner', 'run_tests')
  14. # The module name for tests outside models.py
  15. TEST_MODULE = 'tests'
  16. doctestOutputChecker = OutputChecker()
  17. class DjangoTestRunner(unittest.TextTestRunner):
  18. def __init__(self, *args, **kwargs):
  19. import warnings
  20. warnings.warn(
  21. "DjangoTestRunner is deprecated; it's functionality is indistinguishable from TextTestRunner",
  22. PendingDeprecationWarning
  23. )
  24. super(DjangoTestRunner, self).__init__(*args, **kwargs)
  25. def get_tests(app_module):
  26. try:
  27. app_path = app_module.__name__.split('.')[:-1]
  28. test_module = __import__('.'.join(app_path + [TEST_MODULE]), {}, {}, TEST_MODULE)
  29. except ImportError, e:
  30. # Couldn't import tests.py. Was it due to a missing file, or
  31. # due to an import error in a tests.py that actually exists?
  32. import os.path
  33. from imp import find_module
  34. try:
  35. mod = find_module(TEST_MODULE, [os.path.dirname(app_module.__file__)])
  36. except ImportError:
  37. # 'tests' module doesn't exist. Move on.
  38. test_module = None
  39. else:
  40. # The module exists, so there must be an import error in the
  41. # test module itself. We don't need the module; so if the
  42. # module was a single file module (i.e., tests.py), close the file
  43. # handle returned by find_module. Otherwise, the test module
  44. # is a directory, and there is nothing to close.
  45. if mod[0]:
  46. mod[0].close()
  47. raise
  48. return test_module
  49. def build_suite(app_module):
  50. "Create a complete Django test suite for the provided application module"
  51. suite = unittest.TestSuite()
  52. # Load unit and doctests in the models.py module. If module has
  53. # a suite() method, use it. Otherwise build the test suite ourselves.
  54. if hasattr(app_module, 'suite'):
  55. suite.addTest(app_module.suite())
  56. else:
  57. suite.addTest(unittest.defaultTestLoader.loadTestsFromModule(app_module))
  58. try:
  59. suite.addTest(doctest.DocTestSuite(app_module,
  60. checker=doctestOutputChecker,
  61. runner=DocTestRunner))
  62. except ValueError:
  63. # No doc tests in models.py
  64. pass
  65. # Check to see if a separate 'tests' module exists parallel to the
  66. # models module
  67. test_module = get_tests(app_module)
  68. if test_module:
  69. # Load unit and doctests in the tests.py module. If module has
  70. # a suite() method, use it. Otherwise build the test suite ourselves.
  71. if hasattr(test_module, 'suite'):
  72. suite.addTest(test_module.suite())
  73. else:
  74. suite.addTest(unittest.defaultTestLoader.loadTestsFromModule(test_module))
  75. try:
  76. suite.addTest(doctest.DocTestSuite(test_module,
  77. checker=doctestOutputChecker,
  78. runner=DocTestRunner))
  79. except ValueError:
  80. # No doc tests in tests.py
  81. pass
  82. return suite
  83. def build_test(label):
  84. """Construct a test case with the specified label. Label should be of the
  85. form model.TestClass or model.TestClass.test_method. Returns an
  86. instantiated test or test suite corresponding to the label provided.
  87. """
  88. parts = label.split('.')
  89. if len(parts) < 2 or len(parts) > 3:
  90. raise ValueError("Test label '%s' should be of the form app.TestCase or app.TestCase.test_method" % label)
  91. #
  92. # First, look for TestCase instances with a name that matches
  93. #
  94. app_module = get_app(parts[0])
  95. test_module = get_tests(app_module)
  96. TestClass = getattr(app_module, parts[1], None)
  97. # Couldn't find the test class in models.py; look in tests.py
  98. if TestClass is None:
  99. if test_module:
  100. TestClass = getattr(test_module, parts[1], None)
  101. try:
  102. if issubclass(TestClass, (unittest.TestCase, real_unittest.TestCase)):
  103. if len(parts) == 2: # label is app.TestClass
  104. try:
  105. return unittest.TestLoader().loadTestsFromTestCase(TestClass)
  106. except TypeError:
  107. raise ValueError("Test label '%s' does not refer to a test class" % label)
  108. else: # label is app.TestClass.test_method
  109. return TestClass(parts[2])
  110. except TypeError:
  111. # TestClass isn't a TestClass - it must be a method or normal class
  112. pass
  113. #
  114. # If there isn't a TestCase, look for a doctest that matches
  115. #
  116. tests = []
  117. for module in app_module, test_module:
  118. try:
  119. doctests = doctest.DocTestSuite(module,
  120. checker=doctestOutputChecker,
  121. runner=DocTestRunner)
  122. # Now iterate over the suite, looking for doctests whose name
  123. # matches the pattern that was given
  124. for test in doctests:
  125. if test._dt_test.name in (
  126. '%s.%s' % (module.__name__, '.'.join(parts[1:])),
  127. '%s.__test__.%s' % (module.__name__, '.'.join(parts[1:]))):
  128. tests.append(test)
  129. except ValueError:
  130. # No doctests found.
  131. pass
  132. # If no tests were found, then we were given a bad test label.
  133. if not tests:
  134. raise ValueError("Test label '%s' does not refer to a test" % label)
  135. # Construct a suite out of the tests that matched.
  136. return unittest.TestSuite(tests)
  137. def partition_suite(suite, classes, bins):
  138. """
  139. Partitions a test suite by test type.
  140. classes is a sequence of types
  141. bins is a sequence of TestSuites, one more than classes
  142. Tests of type classes[i] are added to bins[i],
  143. tests with no match found in classes are place in bins[-1]
  144. """
  145. for test in suite:
  146. if isinstance(test, unittest.TestSuite):
  147. partition_suite(test, classes, bins)
  148. else:
  149. for i in range(len(classes)):
  150. if isinstance(test, classes[i]):
  151. bins[i].addTest(test)
  152. break
  153. else:
  154. bins[-1].addTest(test)
  155. def reorder_suite(suite, classes):
  156. """
  157. Reorders a test suite by test type.
  158. classes is a sequence of types
  159. All tests of type clases[0] are placed first, then tests of type classes[1], etc.
  160. Tests with no match in classes are placed last.
  161. """
  162. class_count = len(classes)
  163. bins = [unittest.TestSuite() for i in range(class_count+1)]
  164. partition_suite(suite, classes, bins)
  165. for i in range(class_count):
  166. bins[0].addTests(bins[i+1])
  167. return bins[0]
  168. def dependency_ordered(test_databases, dependencies):
  169. """Reorder test_databases into an order that honors the dependencies
  170. described in TEST_DEPENDENCIES.
  171. """
  172. ordered_test_databases = []
  173. resolved_databases = set()
  174. while test_databases:
  175. changed = False
  176. deferred = []
  177. while test_databases:
  178. signature, (db_name, aliases) = test_databases.pop()
  179. dependencies_satisfied = True
  180. for alias in aliases:
  181. if alias in dependencies:
  182. if all(a in resolved_databases for a in dependencies[alias]):
  183. # all dependencies for this alias are satisfied
  184. dependencies.pop(alias)
  185. resolved_databases.add(alias)
  186. else:
  187. dependencies_satisfied = False
  188. else:
  189. resolved_databases.add(alias)
  190. if dependencies_satisfied:
  191. ordered_test_databases.append((signature, (db_name, aliases)))
  192. changed = True
  193. else:
  194. deferred.append((signature, (db_name, aliases)))
  195. if not changed:
  196. raise ImproperlyConfigured("Circular dependency in TEST_DEPENDENCIES")
  197. test_databases = deferred
  198. return ordered_test_databases
  199. class DjangoTestSuiteRunner(object):
  200. def __init__(self, verbosity=1, interactive=True, failfast=True, **kwargs):
  201. self.verbosity = verbosity
  202. self.interactive = interactive
  203. self.failfast = failfast
  204. def setup_test_environment(self, **kwargs):
  205. setup_test_environment()
  206. settings.DEBUG = False
  207. unittest.installHandler()
  208. def build_suite(self, test_labels, extra_tests=None, **kwargs):
  209. suite = unittest.TestSuite()
  210. if test_labels:
  211. for label in test_labels:
  212. if '.' in label:
  213. suite.addTest(build_test(label))
  214. else:
  215. app = get_app(label)
  216. suite.addTest(build_suite(app))
  217. else:
  218. for app in get_apps():
  219. suite.addTest(build_suite(app))
  220. if extra_tests:
  221. for test in extra_tests:
  222. suite.addTest(test)
  223. return reorder_suite(suite, (TestCase,))
  224. def setup_databases(self, **kwargs):
  225. from django.db import connections, DEFAULT_DB_ALIAS
  226. # First pass -- work out which databases actually need to be created,
  227. # and which ones are test mirrors or duplicate entries in DATABASES
  228. mirrored_aliases = {}
  229. test_databases = {}
  230. dependencies = {}
  231. for alias in connections:
  232. connection = connections[alias]
  233. if connection.settings_dict['TEST_MIRROR']:
  234. # If the database is marked as a test mirror, save
  235. # the alias.
  236. mirrored_aliases[alias] = connection.settings_dict['TEST_MIRROR']
  237. else:
  238. # Store a tuple with DB parameters that uniquely identify it.
  239. # If we have two aliases with the same values for that tuple,
  240. # we only need to create the test database once.
  241. item = test_databases.setdefault(
  242. connection.creation.test_db_signature(),
  243. (connection.settings_dict['NAME'], [])
  244. )
  245. item[1].append(alias)
  246. if 'TEST_DEPENDENCIES' in connection.settings_dict:
  247. dependencies[alias] = connection.settings_dict['TEST_DEPENDENCIES']
  248. else:
  249. if alias != DEFAULT_DB_ALIAS:
  250. dependencies[alias] = connection.settings_dict.get('TEST_DEPENDENCIES', [DEFAULT_DB_ALIAS])
  251. # Second pass -- actually create the databases.
  252. old_names = []
  253. mirrors = []
  254. for signature, (db_name, aliases) in dependency_ordered(test_databases.items(), dependencies):
  255. # Actually create the database for the first connection
  256. connection = connections[aliases[0]]
  257. old_names.append((connection, db_name, True))
  258. test_db_name = connection.creation.create_test_db(self.verbosity, autoclobber=not self.interactive)
  259. for alias in aliases[1:]:
  260. connection = connections[alias]
  261. if db_name:
  262. old_names.append((connection, db_name, False))
  263. connection.settings_dict['NAME'] = test_db_name
  264. else:
  265. # If settings_dict['NAME'] isn't defined, we have a backend where
  266. # the name isn't important -- e.g., SQLite, which uses :memory:.
  267. # Force create the database instead of assuming it's a duplicate.
  268. old_names.append((connection, db_name, True))
  269. connection.creation.create_test_db(self.verbosity, autoclobber=not self.interactive)
  270. for alias, mirror_alias in mirrored_aliases.items():
  271. mirrors.append((alias, connections[alias].settings_dict['NAME']))
  272. connections[alias].settings_dict['NAME'] = connections[mirror_alias].settings_dict['NAME']
  273. return old_names, mirrors
  274. def run_suite(self, suite, **kwargs):
  275. return unittest.TextTestRunner(verbosity=self.verbosity, failfast=self.failfast).run(suite)
  276. def teardown_databases(self, old_config, **kwargs):
  277. from django.db import connections
  278. old_names, mirrors = old_config
  279. # Point all the mirrors back to the originals
  280. for alias, old_name in mirrors:
  281. connections[alias].settings_dict['NAME'] = old_name
  282. # Destroy all the non-mirror databases
  283. for connection, old_name, destroy in old_names:
  284. if destroy:
  285. connection.creation.destroy_test_db(old_name, self.verbosity)
  286. else:
  287. connection.settings_dict['NAME'] = old_name
  288. def teardown_test_environment(self, **kwargs):
  289. unittest.removeHandler()
  290. teardown_test_environment()
  291. def suite_result(self, suite, result, **kwargs):
  292. return len(result.failures) + len(result.errors)
  293. def run_tests(self, test_labels, extra_tests=None, **kwargs):
  294. """
  295. Run the unit tests for all the test labels in the provided list.
  296. Labels must be of the form:
  297. - app.TestClass.test_method
  298. Run a single specific test method
  299. - app.TestClass
  300. Run all the test methods in a given class
  301. - app
  302. Search for doctests and unittests in the named application.
  303. When looking for tests, the test runner will look in the models and
  304. tests modules for the application.
  305. A list of 'extra' tests may also be provided; these tests
  306. will be added to the test suite.
  307. Returns the number of tests that failed.
  308. """
  309. self.setup_test_environment()
  310. suite = self.build_suite(test_labels, extra_tests)
  311. old_config = self.setup_databases()
  312. result = self.run_suite(suite)
  313. self.teardown_databases(old_config)
  314. self.teardown_test_environment()
  315. return self.suite_result(suite, result)
  316. def run_tests(test_labels, verbosity=1, interactive=True, failfast=False, extra_tests=None):
  317. import warnings
  318. warnings.warn(
  319. 'The run_tests() test runner has been deprecated in favor of DjangoTestSuiteRunner.',
  320. DeprecationWarning
  321. )
  322. test_runner = DjangoTestSuiteRunner(verbosity=verbosity, interactive=interactive, failfast=failfast)
  323. return test_runner.run_tests(test_labels, extra_tests=extra_tests)