blob: 590e227c34c325185d3e0bbff4c412b6bded6cbf [file] [log] [blame]
Benjamin Petersonbed7d042009-07-19 21:01:52 +00001"""Loading unittests."""
2
3import os
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +00004import re
Benjamin Petersonbed7d042009-07-19 21:01:52 +00005import sys
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +00006import traceback
Benjamin Petersonbed7d042009-07-19 21:01:52 +00007import types
Raymond Hettingerc50846a2010-04-05 18:56:31 +00008import functools
Barry Warsawd78742a2014-09-08 14:21:37 -04009import warnings
Benjamin Petersonbed7d042009-07-19 21:01:52 +000010
11from fnmatch import fnmatch
12
13from . import case, suite, util
14
Benjamin Petersondccc1fc2010-03-22 00:15:53 +000015__unittest = True
Benjamin Petersonbed7d042009-07-19 21:01:52 +000016
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +000017# what about .pyc or .pyo (etc)
18# we would need to avoid loading the same tests multiple times
19# from '.py', '.pyc' *and* '.pyo'
20VALID_MODULE_NAME = re.compile(r'[_a-z]\w*\.py$', re.IGNORECASE)
21
22
23def _make_failed_import_test(name, suiteClass):
Benjamin Peterson434ae772010-03-22 01:46:47 +000024 message = 'Failed to import test module: %s\n%s' % (name, traceback.format_exc())
Benjamin Peterson886af962010-03-21 23:13:07 +000025 return _make_failed_test('ModuleImportFailure', name, ImportError(message),
26 suiteClass)
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +000027
Benjamin Peterson886af962010-03-21 23:13:07 +000028def _make_failed_load_tests(name, exception, suiteClass):
29 return _make_failed_test('LoadTestsFailure', name, exception, suiteClass)
30
31def _make_failed_test(classname, methodname, exception, suiteClass):
32 def testFailure(self):
33 raise exception
34 attrs = {methodname: testFailure}
35 TestClass = type(classname, (case.TestCase,), attrs)
36 return suiteClass((TestClass(methodname),))
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +000037
Ezio Melottieae2b382013-03-01 14:47:50 +020038def _make_skipped_test(methodname, exception, suiteClass):
39 @case.skip(str(exception))
40 def testSkipped(self):
41 pass
42 attrs = {methodname: testSkipped}
43 TestClass = type("ModuleSkipped", (case.TestCase,), attrs)
44 return suiteClass((TestClass(methodname),))
45
Michael Foorde01c62c2012-03-13 00:09:54 -070046def _jython_aware_splitext(path):
47 if path.lower().endswith('$py.class'):
48 return path[:-9]
49 return os.path.splitext(path)[0]
50
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +000051
Benjamin Petersonbed7d042009-07-19 21:01:52 +000052class TestLoader(object):
53 """
54 This class is responsible for loading tests according to various criteria
55 and returning them wrapped in a TestSuite
56 """
57 testMethodPrefix = 'test'
58 sortTestMethodsUsing = staticmethod(util.three_way_cmp)
59 suiteClass = suite.TestSuite
60 _top_level_dir = None
61
62 def loadTestsFromTestCase(self, testCaseClass):
63 """Return a suite of all tests cases contained in testCaseClass"""
64 if issubclass(testCaseClass, suite.TestSuite):
Michael Foorde28bb152013-11-23 13:29:23 +000065 raise TypeError("Test cases should not be derived from "
66 "TestSuite. Maybe you meant to derive from "
67 "TestCase?")
Benjamin Petersonbed7d042009-07-19 21:01:52 +000068 testCaseNames = self.getTestCaseNames(testCaseClass)
69 if not testCaseNames and hasattr(testCaseClass, 'runTest'):
70 testCaseNames = ['runTest']
71 loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames))
72 return loaded_suite
73
Barry Warsawd78742a2014-09-08 14:21:37 -040074 # XXX After Python 3.5, remove backward compatibility hacks for
75 # use_load_tests deprecation via *args and **kws. See issue 16662.
76 def loadTestsFromModule(self, module, *args, pattern=None, **kws):
Benjamin Petersonbed7d042009-07-19 21:01:52 +000077 """Return a suite of all tests cases contained in the given module"""
Barry Warsawd78742a2014-09-08 14:21:37 -040078 # This method used to take an undocumented and unofficial
79 # use_load_tests argument. For backward compatibility, we still
80 # accept the argument (which can also be the first position) but we
81 # ignore it and issue a deprecation warning if it's present.
82 if len(args) == 1 or 'use_load_tests' in kws:
83 warnings.warn('use_load_tests is deprecated and ignored',
84 DeprecationWarning)
85 kws.pop('use_load_tests', None)
86 if len(args) > 1:
87 raise TypeError('loadTestsFromModule() takes 1 positional argument but {} were given'.format(len(args)))
88 if len(kws) != 0:
89 # Since the keyword arguments are unsorted (see PEP 468), just
90 # pick the alphabetically sorted first argument to complain about,
91 # if multiple were given. At least the error message will be
92 # predictable.
93 complaint = sorted(kws)[0]
94 raise TypeError("loadTestsFromModule() got an unexpected keyword argument '{}'".format(complaint))
Benjamin Petersonbed7d042009-07-19 21:01:52 +000095 tests = []
96 for name in dir(module):
97 obj = getattr(module, name)
98 if isinstance(obj, type) and issubclass(obj, case.TestCase):
99 tests.append(self.loadTestsFromTestCase(obj))
100
101 load_tests = getattr(module, 'load_tests', None)
Michael Foord41647d62010-02-06 00:26:13 +0000102 tests = self.suiteClass(tests)
Barry Warsawd78742a2014-09-08 14:21:37 -0400103 if load_tests is not None:
Benjamin Peterson886af962010-03-21 23:13:07 +0000104 try:
Barry Warsawd78742a2014-09-08 14:21:37 -0400105 return load_tests(self, tests, pattern)
Benjamin Peterson886af962010-03-21 23:13:07 +0000106 except Exception as e:
107 return _make_failed_load_tests(module.__name__, e,
108 self.suiteClass)
Michael Foord41647d62010-02-06 00:26:13 +0000109 return tests
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000110
111 def loadTestsFromName(self, name, module=None):
112 """Return a suite of all tests cases given a string specifier.
113
114 The name may resolve either to a module, a test case class, a
115 test method within a test case class, or a callable object which
116 returns a TestCase or TestSuite instance.
117
118 The method optionally resolves the names relative to a given module.
119 """
120 parts = name.split('.')
121 if module is None:
122 parts_copy = parts[:]
123 while parts_copy:
124 try:
125 module = __import__('.'.join(parts_copy))
126 break
127 except ImportError:
128 del parts_copy[-1]
129 if not parts_copy:
130 raise
131 parts = parts[1:]
132 obj = module
133 for part in parts:
134 parent, obj = obj, getattr(obj, part)
135
136 if isinstance(obj, types.ModuleType):
137 return self.loadTestsFromModule(obj)
138 elif isinstance(obj, type) and issubclass(obj, case.TestCase):
139 return self.loadTestsFromTestCase(obj)
140 elif (isinstance(obj, types.FunctionType) and
141 isinstance(parent, type) and
142 issubclass(parent, case.TestCase)):
R David Murray5e2f5932013-04-11 08:55:45 -0400143 name = parts[-1]
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000144 inst = parent(name)
145 # static methods follow a different path
146 if not isinstance(getattr(inst, name), types.FunctionType):
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000147 return self.suiteClass([inst])
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000148 elif isinstance(obj, suite.TestSuite):
149 return obj
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200150 if callable(obj):
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000151 test = obj()
152 if isinstance(test, suite.TestSuite):
153 return test
154 elif isinstance(test, case.TestCase):
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000155 return self.suiteClass([test])
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000156 else:
157 raise TypeError("calling %s returned %s, not a test" %
158 (obj, test))
159 else:
160 raise TypeError("don't know how to make test from: %s" % obj)
161
162 def loadTestsFromNames(self, names, module=None):
163 """Return a suite of all tests cases found using the given sequence
164 of string specifiers. See 'loadTestsFromName()'.
165 """
166 suites = [self.loadTestsFromName(name, module) for name in names]
167 return self.suiteClass(suites)
168
169 def getTestCaseNames(self, testCaseClass):
170 """Return a sorted sequence of method names found within testCaseClass
171 """
172 def isTestMethod(attrname, testCaseClass=testCaseClass,
173 prefix=self.testMethodPrefix):
174 return attrname.startswith(prefix) and \
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200175 callable(getattr(testCaseClass, attrname))
Senthil Kumaranf27be5c2011-11-25 02:08:39 +0800176 testFnNames = list(filter(isTestMethod, dir(testCaseClass)))
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000177 if self.sortTestMethodsUsing:
Raymond Hettingerc50846a2010-04-05 18:56:31 +0000178 testFnNames.sort(key=functools.cmp_to_key(self.sortTestMethodsUsing))
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000179 return testFnNames
180
181 def discover(self, start_dir, pattern='test*.py', top_level_dir=None):
182 """Find and return all test modules from the specified start
Michael Foord6bcfade2010-11-20 17:22:21 +0000183 directory, recursing into subdirectories to find them and return all
184 tests found within them. Only test files that match the pattern will
185 be loaded. (Using shell style pattern matching.)
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000186
187 All test modules must be importable from the top level of the project.
188 If the start directory is not the top level directory then the top
189 level directory must be specified separately.
190
191 If a test package name (directory with '__init__.py') matches the
192 pattern then the package will be checked for a 'load_tests' function. If
193 this exists then it will be called with loader, tests, pattern.
194
195 If load_tests exists then discovery does *not* recurse into the package,
196 load_tests is responsible for loading all tests in the package.
197
198 The pattern is deliberately not stored as a loader attribute so that
199 packages can continue discovery themselves. top_level_dir is stored so
200 load_tests does not need to pass this argument in to loader.discover().
Michael Foord80cbc9e2013-03-18 17:50:12 -0700201
202 Paths are sorted before being imported to ensure reproducible execution
203 order even on filesystems with non-alphabetical ordering like ext3/4.
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000204 """
Benjamin Petersonb48af542010-04-11 20:43:16 +0000205 set_implicit_top = False
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000206 if top_level_dir is None and self._top_level_dir is not None:
207 # make top_level_dir optional if called from load_tests in a package
208 top_level_dir = self._top_level_dir
209 elif top_level_dir is None:
Benjamin Petersonb48af542010-04-11 20:43:16 +0000210 set_implicit_top = True
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000211 top_level_dir = start_dir
212
Benjamin Petersonb48af542010-04-11 20:43:16 +0000213 top_level_dir = os.path.abspath(top_level_dir)
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000214
215 if not top_level_dir in sys.path:
216 # all test modules must be importable from the top level directory
Michael Foord3b2494f2010-05-07 23:42:40 +0000217 # should we *unconditionally* put the start directory in first
218 # in sys.path to minimise likelihood of conflicts between installed
219 # modules and development versions?
220 sys.path.insert(0, top_level_dir)
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000221 self._top_level_dir = top_level_dir
222
Benjamin Petersonb48af542010-04-11 20:43:16 +0000223 is_not_importable = False
Michael Foorde28bb152013-11-23 13:29:23 +0000224 is_namespace = False
225 tests = []
Benjamin Petersonb48af542010-04-11 20:43:16 +0000226 if os.path.isdir(os.path.abspath(start_dir)):
227 start_dir = os.path.abspath(start_dir)
228 if start_dir != top_level_dir:
229 is_not_importable = not os.path.isfile(os.path.join(start_dir, '__init__.py'))
230 else:
231 # support for discovery from dotted module names
232 try:
233 __import__(start_dir)
234 except ImportError:
235 is_not_importable = True
236 else:
237 the_module = sys.modules[start_dir]
238 top_part = start_dir.split('.')[0]
Michael Foorde28bb152013-11-23 13:29:23 +0000239 try:
240 start_dir = os.path.abspath(
241 os.path.dirname((the_module.__file__)))
242 except AttributeError:
243 # look for namespace packages
244 try:
245 spec = the_module.__spec__
246 except AttributeError:
247 spec = None
248
249 if spec and spec.loader is None:
250 if spec.submodule_search_locations is not None:
251 is_namespace = True
252
253 for path in the_module.__path__:
254 if (not set_implicit_top and
255 not path.startswith(top_level_dir)):
256 continue
257 self._top_level_dir = \
258 (path.split(the_module.__name__
259 .replace(".", os.path.sep))[0])
260 tests.extend(self._find_tests(path,
261 pattern,
262 namespace=True))
263 elif the_module.__name__ in sys.builtin_module_names:
264 # builtin module
265 raise TypeError('Can not use builtin modules '
266 'as dotted module names') from None
267 else:
268 raise TypeError(
269 'don\'t know how to discover from {!r}'
270 .format(the_module)) from None
271
Benjamin Petersonb48af542010-04-11 20:43:16 +0000272 if set_implicit_top:
Michael Foorde28bb152013-11-23 13:29:23 +0000273 if not is_namespace:
274 self._top_level_dir = \
275 self._get_directory_containing_module(top_part)
276 sys.path.remove(top_level_dir)
277 else:
278 sys.path.remove(top_level_dir)
Benjamin Petersonb48af542010-04-11 20:43:16 +0000279
280 if is_not_importable:
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000281 raise ImportError('Start directory is not importable: %r' % start_dir)
282
Michael Foorde28bb152013-11-23 13:29:23 +0000283 if not is_namespace:
284 tests = list(self._find_tests(start_dir, pattern))
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000285 return self.suiteClass(tests)
286
Benjamin Petersonb48af542010-04-11 20:43:16 +0000287 def _get_directory_containing_module(self, module_name):
288 module = sys.modules[module_name]
289 full_path = os.path.abspath(module.__file__)
290
291 if os.path.basename(full_path).lower().startswith('__init__.py'):
292 return os.path.dirname(os.path.dirname(full_path))
293 else:
294 # here we have been given a module rather than a package - so
295 # all we can do is search the *same* directory the module is in
296 # should an exception be raised instead
297 return os.path.dirname(full_path)
298
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000299 def _get_name_from_path(self, path):
Michael Foorde01c62c2012-03-13 00:09:54 -0700300 path = _jython_aware_splitext(os.path.normpath(path))
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000301
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000302 _relpath = os.path.relpath(path, self._top_level_dir)
303 assert not os.path.isabs(_relpath), "Path must be within the project"
304 assert not _relpath.startswith('..'), "Path must be within the project"
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000305
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000306 name = _relpath.replace(os.path.sep, '.')
307 return name
308
309 def _get_module_from_name(self, name):
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000310 __import__(name)
311 return sys.modules[name]
312
Michael Foord4107d312010-06-05 10:45:41 +0000313 def _match_path(self, path, full_path, pattern):
314 # override this method to use alternative matching strategy
315 return fnmatch(path, pattern)
316
Michael Foorde28bb152013-11-23 13:29:23 +0000317 def _find_tests(self, start_dir, pattern, namespace=False):
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000318 """Used by discovery. Yields test suites it loads."""
Michael Foord80cbc9e2013-03-18 17:50:12 -0700319 paths = sorted(os.listdir(start_dir))
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000320
321 for path in paths:
322 full_path = os.path.join(start_dir, path)
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000323 if os.path.isfile(full_path):
324 if not VALID_MODULE_NAME.match(path):
325 # valid Python identifiers only
326 continue
Michael Foord4107d312010-06-05 10:45:41 +0000327 if not self._match_path(path, full_path, pattern):
328 continue
329 # if the test file matches, load it
330 name = self._get_name_from_path(full_path)
331 try:
332 module = self._get_module_from_name(name)
Ezio Melottieae2b382013-03-01 14:47:50 +0200333 except case.SkipTest as e:
334 yield _make_skipped_test(name, e, self.suiteClass)
Michael Foord4107d312010-06-05 10:45:41 +0000335 except:
336 yield _make_failed_import_test(name, self.suiteClass)
337 else:
338 mod_file = os.path.abspath(getattr(module, '__file__', full_path))
Antoine Pitroud5d0bc32013-10-23 19:11:29 +0200339 realpath = _jython_aware_splitext(os.path.realpath(mod_file))
340 fullpath_noext = _jython_aware_splitext(os.path.realpath(full_path))
Michael Foord4107d312010-06-05 10:45:41 +0000341 if realpath.lower() != fullpath_noext.lower():
342 module_dir = os.path.dirname(realpath)
Michael Foorde01c62c2012-03-13 00:09:54 -0700343 mod_name = _jython_aware_splitext(os.path.basename(full_path))
Michael Foord4107d312010-06-05 10:45:41 +0000344 expected_dir = os.path.dirname(full_path)
345 msg = ("%r module incorrectly imported from %r. Expected %r. "
346 "Is this module globally installed?")
347 raise ImportError(msg % (mod_name, module_dir, expected_dir))
Barry Warsawd78742a2014-09-08 14:21:37 -0400348 yield self.loadTestsFromModule(module, pattern=pattern)
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000349 elif os.path.isdir(full_path):
Michael Foorde28bb152013-11-23 13:29:23 +0000350 if (not namespace and
351 not os.path.isfile(os.path.join(full_path, '__init__.py'))):
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000352 continue
353
354 load_tests = None
355 tests = None
Barry Warsawd78742a2014-09-08 14:21:37 -0400356 name = self._get_name_from_path(full_path)
357 try:
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000358 package = self._get_module_from_name(name)
Barry Warsawd78742a2014-09-08 14:21:37 -0400359 except case.SkipTest as e:
360 yield _make_skipped_test(name, e, self.suiteClass)
361 except:
362 yield _make_failed_import_test(name, self.suiteClass)
363 else:
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000364 load_tests = getattr(package, 'load_tests', None)
Barry Warsawd78742a2014-09-08 14:21:37 -0400365 tests = self.loadTestsFromModule(package, pattern=pattern)
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000366 if tests is not None:
367 # tests loaded from package file
368 yield tests
Barry Warsawd78742a2014-09-08 14:21:37 -0400369
370 if load_tests is not None:
371 # loadTestsFromModule(package) has load_tests for us.
372 continue
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000373 # recurse into the package
Michael Foorde28bb152013-11-23 13:29:23 +0000374 yield from self._find_tests(full_path, pattern,
375 namespace=namespace)
Barry Warsawd78742a2014-09-08 14:21:37 -0400376
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000377
378defaultTestLoader = TestLoader()
379
380
381def _makeLoader(prefix, sortUsing, suiteClass=None):
382 loader = TestLoader()
383 loader.sortTestMethodsUsing = sortUsing
384 loader.testMethodPrefix = prefix
385 if suiteClass:
386 loader.suiteClass = suiteClass
387 return loader
388
389def getTestCaseNames(testCaseClass, prefix, sortUsing=util.three_way_cmp):
390 return _makeLoader(prefix, sortUsing).getTestCaseNames(testCaseClass)
391
392def makeSuite(testCaseClass, prefix='test', sortUsing=util.three_way_cmp,
393 suiteClass=suite.TestSuite):
394 return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(
395 testCaseClass)
396
397def findTestCases(module, prefix='test', sortUsing=util.three_way_cmp,
398 suiteClass=suite.TestSuite):
399 return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(\
400 module)