def build_suite(self, test_labels=None, extra_tests=None, **kwargs): test_labels = test_labels or ['.'] extra_tests = extra_tests or [] self.test_loader.testNamePatterns = self.test_name_patterns discover_kwargs = {} if self.pattern is not None: discover_kwargs['pattern'] = self.pattern if self.top_level is not None: discover_kwargs['top_level_dir'] = self.top_level all_tests = [] for label in test_labels: tests = self.load_tests_for_label(label, discover_kwargs) all_tests.extend(iter_test_cases(tests)) all_tests.extend(iter_test_cases(extra_tests)) if self.tags or self.exclude_tags: if self.tags: self.log( 'Including test tag(s): %s.' % ', '.join(sorted(self.tags)), level=logging.DEBUG, ) if self.exclude_tags: self.log( 'Excluding test tag(s): %s.' % ', '.join(sorted(self.exclude_tags)), level=logging.DEBUG, ) all_tests = filter_tests_by_tags(all_tests, self.tags, self.exclude_tags) # Put the failures detected at load time first for quicker feedback. # _FailedTest objects include things like test modules that couldn't be # found or that couldn't be loaded due to syntax errors. test_types = (unittest.loader._FailedTest, *self.reorder_by) all_tests = list(reorder_tests(all_tests, test_types, self.reverse)) self.log('Found %d test(s).' % len(all_tests)) suite = self.test_suite(all_tests) if self.parallel > 1: parallel_suite = self.parallel_test_suite( suite, self.parallel, self.failfast, self.buffer, ) # Since tests are distributed across processes on a per-TestCase # basis, there's no need for more processes than TestCases. parallel_units = len(parallel_suite.subsuites) self.parallel = min(self.parallel, parallel_units) # If there's only one TestCase, parallelization isn't needed. if self.parallel > 1: suite = parallel_suite return suite
def build_suite(self, test_labels=None, extra_tests=None, **kwargs): test_labels = test_labels or ['.'] extra_tests = extra_tests or [] self.test_loader.testNamePatterns = self.test_name_patterns discover_kwargs = {} if self.pattern is not None: discover_kwargs['pattern'] = self.pattern if self.top_level is not None: discover_kwargs['top_level_dir'] = self.top_level all_tests = [] for label in test_labels: label_as_path = os.path.abspath(label) tests = None # if a module, or "module.ClassName[.method_name]", just run those if not os.path.exists(label_as_path): tests = self.test_loader.loadTestsFromName(label) # Try discovery if "label" is a package or directory. if not (tests and tests.countTestCases()) and is_discoverable(label): kwargs = discover_kwargs.copy() if os.path.isdir(label_as_path) and not self.top_level: kwargs['top_level_dir'] = find_top_level(label_as_path) tests = self.test_loader.discover(start_dir=label, **kwargs) # Make unittest forget the top-level dir it calculated from this # run, to support running tests from two different top-levels. self.test_loader._top_level_dir = None all_tests.extend(iter_test_cases(tests)) all_tests.extend(iter_test_cases(extra_tests)) if self.tags or self.exclude_tags: if self.verbosity >= 2: if self.tags: print('Including test tag(s): %s.' % ', '.join(sorted(self.tags))) if self.exclude_tags: print('Excluding test tag(s): %s.' % ', '.join(sorted(self.exclude_tags))) all_tests = filter_tests_by_tags(all_tests, self.tags, self.exclude_tags) all_tests = reorder_tests(all_tests, self.reorder_by, self.reverse) suite = self.test_suite(all_tests) if self.parallel > 1: parallel_suite = self.parallel_test_suite(suite, self.parallel, self.failfast) # Since tests are distributed across processes on a per-TestCase # basis, there's no need for more processes than TestCases. parallel_units = len(parallel_suite.subsuites) self.parallel = min(self.parallel, parallel_units) # If there's only one TestCase, parallelization isn't needed. if self.parallel > 1: suite = parallel_suite return suite
def reorder_suite(suite, classes, reverse=False): """ Reorder a test suite by test type, removing any duplicates. `classes` is a sequence of types All tests of type classes[0] are placed first, then tests of type classes[1], etc. Tests with no match in classes are placed last. If `reverse` is True, sort tests within classes in opposite order but don't reverse test classes. """ bins = [OrderedSet() for i in range(len(classes) + 1)] *class_bins, last_bin = bins for test in iter_test_cases(suite): for test_bin, test_class in zip(class_bins, classes): if isinstance(test, test_class): break else: test_bin = last_bin test_bin.add(test) if reverse: bins = (reversed(tests) for tests in bins) suite_class = type(suite) return suite_class(itertools.chain(*bins))
def partition_suite_by_case(suite): """Partition a test suite by test case, preserving the order of tests.""" suite_class = type(suite) all_tests = iter_test_cases(suite) return [ suite_class(tests) for _, tests in itertools.groupby(all_tests, type) ]
def test_reorder_tests_reverse_with_duplicates(self): class Tests1(unittest.TestCase): def test1(self): pass class Tests2(unittest.TestCase): def test2(self): pass def test3(self): pass suite = self.build_test_suite((Tests1, Tests2)) subsuite = list(suite)[0] suite.addTest(subsuite) tests = list(iter_test_cases(suite)) self.assertTestNames(tests, expected=[ 'Tests1.test1', 'Tests2.test2', 'Tests2.test3', 'Tests1.test1', ]) reordered_tests = reorder_tests(tests, classes=[]) self.assertTestNames(reordered_tests, expected=[ 'Tests1.test1', 'Tests2.test2', 'Tests2.test3', ]) reordered_tests = reorder_tests(tests, classes=[], reverse=True) self.assertTestNames(reordered_tests, expected=[ 'Tests2.test3', 'Tests2.test2', 'Tests1.test1', ])
def test_iter_test_cases_mixed_test_suite_classes(self): suite = self.make_test_suite(suite=MySuite()) child_suite = list(suite)[0] self.assertNotIsInstance(child_suite, MySuite) tests = list(iter_test_cases(suite)) self.assertEqual(len(tests), 4) self.assertNotIsInstance(tests[0], unittest.TestSuite)
def test_iter_test_cases_string_input(self): msg = ( "Test 'a' must be a test case or test suite not string (was found " "in 'abc')." ) with self.assertRaisesMessage(TypeError, msg): list(iter_test_cases("abc"))
def test_iter_test_cases_custom_test_suite_class(self): suite = self.make_test_suite(suite_class=MySuite) tests = iter_test_cases(suite) self.assertTestNames(tests, expected=[ 'Tests1.test1', 'Tests1.test2', 'Tests2.test1', 'Tests2.test2', ])
def test_iter_test_cases_basic(self): suite = self.make_test_suite() tests = iter_test_cases(suite) self.assertTestNames(tests, expected=[ 'Tests1.test1', 'Tests1.test2', 'Tests2.test1', 'Tests2.test2', ])
def partition_suite_by_case(suite): """Partition a test suite by test case, preserving the order of tests.""" subsuites = [] suite_class = type(suite) tests = iter_test_cases(suite) for test_type, test_group in itertools.groupby(tests, type): subsuite = suite_class(test_group) subsuites.append(subsuite) return subsuites
def _get_databases(self, suite): databases = {} for test in iter_test_cases(suite): test_databases = getattr(test, 'databases', None) if test_databases == '__all__': test_databases = connections if test_databases: serialized_rollback = getattr(test, 'serialized_rollback', False) databases.update( (alias, serialized_rollback or databases.get(alias, False)) for alias in test_databases) return databases
def test_iter_test_cases_iterable_of_tests(self): class Tests(unittest.TestCase): def test1(self): pass def test2(self): pass tests = list(unittest.defaultTestLoader.loadTestsFromTestCase(Tests)) actual_tests = iter_test_cases(tests) self.assertTestNames(actual_tests, expected=[ 'Tests.test1', 'Tests.test2', ])
def filter_tests_by_tags(suite, tags, exclude_tags): suite_class = type(suite) filtered_suite = suite_class() for test in iter_test_cases(suite): test_tags = set(getattr(test, 'tags', set())) test_fn_name = getattr(test, '_testMethodName', str(test)) test_fn = getattr(test, test_fn_name, test) test_fn_tags = set(getattr(test_fn, 'tags', set())) all_tags = test_tags.union(test_fn_tags) matched_tags = all_tags.intersection(tags) if (matched_tags or not tags) and not all_tags.intersection(exclude_tags): filtered_suite.addTest(test) return filtered_suite
def partition_suite_by_type(suite, classes, bins, reverse=False): """ Partition a test suite by test type. Also prevent duplicated tests. classes is a sequence of types bins is a sequence of TestSuites, one more than classes reverse changes the ordering of tests within bins Tests of type classes[i] are added to bins[i], tests with no match found in classes are place in bins[-1] """ for test in iter_test_cases(suite, reverse=reverse): for i in range(len(classes)): if isinstance(test, classes[i]): bins[i].add(test) break else: bins[-1].add(test)
def build_suite(self, test_labels=None, extra_tests=None, **kwargs): test_labels = test_labels or ['.'] extra_tests = extra_tests or [] self.test_loader.testNamePatterns = self.test_name_patterns discover_kwargs = {} if self.pattern is not None: discover_kwargs['pattern'] = self.pattern if self.top_level is not None: discover_kwargs['top_level_dir'] = self.top_level all_tests = [] for label in test_labels: kwargs = discover_kwargs.copy() tests = None label_as_path = os.path.abspath(label) # if a module, or "module.ClassName[.method_name]", just run those if not os.path.exists(label_as_path): tests = self.test_loader.loadTestsFromName(label) elif os.path.isdir(label_as_path) and not self.top_level: # Try to be a bit smarter than unittest about finding the # default top-level for a given directory path, to avoid # breaking relative imports. (Unittest's default is to set # top-level equal to the path, which means relative imports # will result in "Attempted relative import in non-package."). # We'd be happy to skip this and require dotted module paths # (which don't cause this problem) instead of file paths (which # do), but in the case of a directory in the cwd, which would # be equally valid if considered as a top-level module or as a # directory path, unittest unfortunately prefers the latter. top_level = label_as_path while True: init_py = os.path.join(top_level, '__init__.py') if not os.path.exists(init_py): break try_next = os.path.dirname(top_level) if try_next == top_level: # __init__.py all the way down? give up. break top_level = try_next kwargs['top_level_dir'] = top_level if not (tests and tests.countTestCases()) and is_discoverable(label): # Try discovery if path is a package or directory tests = self.test_loader.discover(start_dir=label, **kwargs) # Make unittest forget the top-level dir it calculated from this # run, to support running tests from two different top-levels. self.test_loader._top_level_dir = None all_tests.extend(iter_test_cases(tests)) all_tests.extend(iter_test_cases(extra_tests)) if self.tags or self.exclude_tags: if self.verbosity >= 2: if self.tags: print('Including test tag(s): %s.' % ', '.join(sorted(self.tags))) if self.exclude_tags: print('Excluding test tag(s): %s.' % ', '.join(sorted(self.exclude_tags))) all_tests = filter_tests_by_tags(all_tests, self.tags, self.exclude_tags) all_tests = reorder_tests(all_tests, self.reorder_by, self.reverse) suite = self.test_suite(all_tests) if self.parallel > 1: parallel_suite = self.parallel_test_suite(suite, self.parallel, self.failfast) # Since tests are distributed across processes on a per-TestCase # basis, there's no need for more processes than TestCases. parallel_units = len(parallel_suite.subsuites) self.parallel = min(self.parallel, parallel_units) # If there's only one TestCase, parallelization isn't needed. if self.parallel > 1: suite = parallel_suite return suite
def filter_tests_by_tags(suite, tags, exclude_tags): suite_class = type(suite) return suite_class(test for test in iter_test_cases(suite) if test_match_tags(test, tags, exclude_tags))
def test_reverse(self): suite = self.make_test_suite() tests = iter_test_cases(suite, reverse=True) self.assertTestNames(tests, expected=[ 'Tests2.test2', 'Tests2.test1', 'Tests1.test2', 'Tests1.test1', ])
def build_suite(self, test_labels=None, extra_tests=None, **kwargs): if extra_tests is not None: warnings.warn( 'The extra_tests argument is deprecated.', RemovedInDjango50Warning, stacklevel=2, ) test_labels = test_labels or ['.'] extra_tests = extra_tests or [] discover_kwargs = {} if self.pattern is not None: discover_kwargs['pattern'] = self.pattern if self.top_level is not None: discover_kwargs['top_level_dir'] = self.top_level self.setup_shuffler() all_tests = [] for label in test_labels: tests = self.load_tests_for_label(label, discover_kwargs) all_tests.extend(iter_test_cases(tests)) all_tests.extend(iter_test_cases(extra_tests)) if self.tags or self.exclude_tags: if self.tags: self.log( 'Including test tag(s): %s.' % ', '.join(sorted(self.tags)), level=logging.DEBUG, ) if self.exclude_tags: self.log( 'Excluding test tag(s): %s.' % ', '.join(sorted(self.exclude_tags)), level=logging.DEBUG, ) all_tests = filter_tests_by_tags(all_tests, self.tags, self.exclude_tags) # Put the failures detected at load time first for quicker feedback. # _FailedTest objects include things like test modules that couldn't be # found or that couldn't be loaded due to syntax errors. test_types = (unittest.loader._FailedTest, *self.reorder_by) all_tests = list( reorder_tests( all_tests, test_types, shuffler=self._shuffler, reverse=self.reverse, )) self.log('Found %d test(s).' % len(all_tests)) suite = self.test_suite(all_tests) if self.parallel > 1: subsuites = partition_suite_by_case(suite) # Since tests are distributed across processes on a per-TestCase # basis, there's no need for more processes than TestCases. processes = min(self.parallel, len(subsuites)) if processes > 1: suite = self.parallel_test_suite( subsuites, processes, self.failfast, self.buffer, ) return suite
def make_tests(self): """Return an iterable of tests.""" suite = self.make_test_suite() tests = list(iter_test_cases(suite)) return tests