From: Serhiy Storchaka <storchaka@gmail.com>
Date: Fri, 23 Jun 2017 18:47:39 +0000 (+0300)
Subject: bpo-30664: The description of a unittest subtest now preserves the (#2265)
X-Git-Tag: v3.7.0a1~534
X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=48fbe52ac71ea711a4701db909ad1ce2647b09fd;p=python

bpo-30664: The description of a unittest subtest now preserves the (#2265)

order of keyword arguments of TestCase.subTest().
---

diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py
index 993aaec194..f19afef48d 100644
--- a/Lib/unittest/case.py
+++ b/Lib/unittest/case.py
@@ -338,6 +338,16 @@ class _AssertLogsContext(_BaseTestCaseContext):
                 .format(logging.getLevelName(self.level), self.logger.name))
 
 
+class _OrderedChainMap(collections.ChainMap):
+    def __iter__(self):
+        seen = set()
+        for mapping in self.maps:
+            for k in mapping:
+                if k not in seen:
+                    seen.add(k)
+                    yield k
+
+
 class TestCase(object):
     """A class whose instances are single test cases.
 
@@ -514,7 +524,7 @@ class TestCase(object):
             return
         parent = self._subtest
         if parent is None:
-            params_map = collections.ChainMap(params)
+            params_map = _OrderedChainMap(params)
         else:
             params_map = parent.params.new_child(params)
         self._subtest = _SubTest(self, msg, params_map)
@@ -1418,7 +1428,7 @@ class _SubTest(TestCase):
         if self.params:
             params_desc = ', '.join(
                 "{}={!r}".format(k, v)
-                for (k, v) in sorted(self.params.items()))
+                for (k, v) in self.params.items())
             parts.append("({})".format(params_desc))
         return " ".join(parts) or '(<subtest>)'
 
diff --git a/Lib/unittest/test/test_result.py b/Lib/unittest/test/test_result.py
index 0a61553563..0ffb87b402 100644
--- a/Lib/unittest/test/test_result.py
+++ b/Lib/unittest/test/test_result.py
@@ -307,7 +307,7 @@ class Test_TestResult(unittest.TestCase):
             self.assertEqual(
                     result.getDescription(self._subtest),
                     'testGetSubTestDescriptionWithoutDocstring (' + __name__ +
-                    '.Test_TestResult) (bar=2, foo=1)')
+                    '.Test_TestResult) (foo=1, bar=2)')
         with self.subTest('some message'):
             result = unittest.TextTestResult(None, True, 1)
             self.assertEqual(
@@ -335,12 +335,21 @@ class Test_TestResult(unittest.TestCase):
 
     def testGetNestedSubTestDescriptionWithoutDocstring(self):
         with self.subTest(foo=1):
-            with self.subTest(bar=2):
+            with self.subTest(baz=2, bar=3):
                 result = unittest.TextTestResult(None, True, 1)
                 self.assertEqual(
                         result.getDescription(self._subtest),
                         'testGetNestedSubTestDescriptionWithoutDocstring '
-                        '(' + __name__ + '.Test_TestResult) (bar=2, foo=1)')
+                        '(' + __name__ + '.Test_TestResult) (baz=2, bar=3, foo=1)')
+
+    def testGetDuplicatedNestedSubTestDescriptionWithoutDocstring(self):
+        with self.subTest(foo=1, bar=2):
+            with self.subTest(baz=3, bar=4):
+                result = unittest.TextTestResult(None, True, 1)
+                self.assertEqual(
+                        result.getDescription(self._subtest),
+                        'testGetDuplicatedNestedSubTestDescriptionWithoutDocstring '
+                        '(' + __name__ + '.Test_TestResult) (baz=3, bar=4, foo=1)')
 
     @unittest.skipIf(sys.flags.optimize >= 2,
                      "Docstrings are omitted with -O2 and above")
@@ -362,7 +371,7 @@ class Test_TestResult(unittest.TestCase):
             self.assertEqual(
                 result.getDescription(self._subtest),
                ('testGetSubTestDescriptionWithOneLineDocstring '
-                '(' + __name__ + '.Test_TestResult) (bar=2, foo=1)\n'
+                '(' + __name__ + '.Test_TestResult) (foo=1, bar=2)\n'
                 'Tests getDescription() for a method with a docstring.'))
 
     @unittest.skipIf(sys.flags.optimize >= 2,
@@ -390,7 +399,7 @@ class Test_TestResult(unittest.TestCase):
             self.assertEqual(
                 result.getDescription(self._subtest),
                ('testGetSubTestDescriptionWithMultiLineDocstring '
-                '(' + __name__ + '.Test_TestResult) (bar=2, foo=1)\n'
+                '(' + __name__ + '.Test_TestResult) (foo=1, bar=2)\n'
                 'Tests getDescription() for a method with a longer '
                 'docstring.'))
 
diff --git a/Misc/NEWS b/Misc/NEWS
index fc9e1dee6f..f33589d008 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -374,6 +374,9 @@ Extension Modules
 Library
 -------
 
+- bpo-30664: The description of a unittest subtest now preserves the order of
+  keyword arguments of TestCase.subTest().
+
 - [Security] bpo-30730: Prevent environment variables injection in subprocess on
   Windows.  Prevent passing other environment variables and command arguments.