diff --git a/NEWS b/NEWS index 5fa80a5..fea60f9 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,11 @@ Improvements * ``AnyMatch`` is now exported properly in ``testtools.matchers``. (Robert Collins, Rob Kennedy, github #44) +* In Python 3.3, if there are duplicate test ids, tests.sort() will + fail and raise TypeError. Detect the duplicate test ids firstly in + sorted_tests() to ensure that all test ids are unique. + (Kui Shi, #1243922) + * ``json_content`` is now in the ``__all__`` attribute for ``testtools.content``. (Robert Collins) diff --git a/doc/for-framework-folk.rst b/doc/for-framework-folk.rst index e162859..d105b4f 100644 --- a/doc/for-framework-folk.rst +++ b/doc/for-framework-folk.rst @@ -429,6 +429,8 @@ sort_tests, which can be used by non-standard TestSuites to know when they should sort their tests. An example implementation can be seen at ``FixtureSuite.sorted_tests``. +If there are duplicate test ids in a suite, ValueError will be raised. + filter_by_ids ------------- diff --git a/testtools/tests/test_testsuite.py b/testtools/tests/test_testsuite.py index 578bda3..e2c3306 100644 --- a/testtools/tests/test_testsuite.py +++ b/testtools/tests/test_testsuite.py @@ -224,6 +224,19 @@ class TestFixtureSuite(TestCase): suite.run(LoggingResult([])) self.assertEqual(['setUp', 1, 2, 'tearDown'], log) + def test_fixture_suite_sort(self): + log = [] + class Sample(TestCase): + def test_one(self): + log.append(1) + def test_two(self): + log.append(2) + fixture = FunctionFixture( + lambda: log.append('setUp'), + lambda fixture: log.append('tearDown')) + suite = FixtureSuite(fixture, [Sample('test_one'), Sample('test_one')]) + self.assertRaises(ValueError, suite.sort_tests) + class TestSortedTests(TestCase): @@ -253,6 +266,13 @@ class TestSortedTests(TestCase): suite = sorted_tests(unittest.TestSuite([b, a])) self.assertEqual([a, b], list(iterate_tests(suite))) + def test_duplicate_simple_suites(self): + a = PlaceHolder('a') + b = PlaceHolder('b') + c = PlaceHolder('a') + self.assertRaises( + ValueError, sorted_tests, unittest.TestSuite([a, b, c])) + def test_suite(): from unittest import TestLoader diff --git a/testtools/testsuite.py b/testtools/testsuite.py index 04dd968..9e92e0c 100644 --- a/testtools/testsuite.py +++ b/testtools/testsuite.py @@ -303,6 +303,15 @@ def filter_by_ids(suite_or_case, test_ids): def sorted_tests(suite_or_case, unpack_outer=False): """Sort suite_or_case while preserving non-vanilla TestSuites.""" + # Duplicate test id can induce TypeError in Python 3.3. + # Detect the duplicate test id, raise exception when found. + seen = set() + for test_case in iterate_tests(suite_or_case): + test_id = test_case.id() + if test_id not in seen: + seen.add(test_id) + else: + raise ValueError('Duplicate test id detected: %s' % (test_id,)) tests = _flatten_tests(suite_or_case, unpack_outer=unpack_outer) tests.sort() return unittest.TestSuite([test for (sort_key, test) in tests])