]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-145144: Add more tests for UserList, UserDict, etc (GH-145145) (GH-146296)
authorSerhiy Storchaka <storchaka@gmail.com>
Sun, 22 Mar 2026 16:06:01 +0000 (18:06 +0200)
committerGitHub <noreply@github.com>
Sun, 22 Mar 2026 16:06:01 +0000 (18:06 +0200)
(cherry picked from commit 161329cde2b1ce4e6a6fdd85c0da1d857aebfd2d)

Lib/test/seq_tests.py
Lib/test/string_tests.py
Lib/test/test_bytes.py
Lib/test/test_userdict.py
Lib/test/test_userlist.py
Lib/test/test_userstring.py

index 719c9434a1682047ee1c62817ae7800ecc3d3013..104299502f5cf0d60901046d4a393e4484f8580f 100644 (file)
@@ -261,23 +261,20 @@ class CommonTest(unittest.TestCase):
         self.assertEqual(min(u), 0)
         self.assertEqual(max(u), 2)
 
-    def test_addmul(self):
+    def test_add(self):
         u1 = self.type2test([0])
         u2 = self.type2test([0, 1])
         self.assertEqual(u1, u1 + self.type2test())
         self.assertEqual(u1, self.type2test() + u1)
         self.assertEqual(u1 + self.type2test([1]), u2)
         self.assertEqual(self.type2test([-1]) + u1, self.type2test([-1, 0]))
-        self.assertEqual(self.type2test(), u2*0)
-        self.assertEqual(self.type2test(), 0*u2)
+
+    def test_mul(self):
+        u2 = self.type2test([0, 1])
         self.assertEqual(self.type2test(), u2*0)
         self.assertEqual(self.type2test(), 0*u2)
         self.assertEqual(u2, u2*1)
         self.assertEqual(u2, 1*u2)
-        self.assertEqual(u2, u2*1)
-        self.assertEqual(u2, 1*u2)
-        self.assertEqual(u2+u2, u2*2)
-        self.assertEqual(u2+u2, 2*u2)
         self.assertEqual(u2+u2, u2*2)
         self.assertEqual(u2+u2, 2*u2)
         self.assertEqual(u2+u2+u2, u2*3)
@@ -286,8 +283,9 @@ class CommonTest(unittest.TestCase):
         class subclass(self.type2test):
             pass
         u3 = subclass([0, 1])
-        self.assertEqual(u3, u3*1)
-        self.assertIsNot(u3, u3*1)
+        r = u3*1
+        self.assertEqual(r, u3)
+        self.assertIsNot(r, u3)
 
     def test_iadd(self):
         u = self.type2test([0, 1])
@@ -348,6 +346,21 @@ class CommonTest(unittest.TestCase):
         self.assertRaises(ValueError, a.__getitem__, slice(0, 10, 0))
         self.assertRaises(TypeError, a.__getitem__, 'x')
 
+    def _assert_cmp(self, a, b, r):
+        self.assertIs(a == b, r == 0)
+        self.assertIs(a != b, r != 0)
+        self.assertIs(a > b, r > 0)
+        self.assertIs(a <= b, r <= 0)
+        self.assertIs(a < b, r < 0)
+        self.assertIs(a >= b, r >= 0)
+
+    def test_cmp(self):
+        a = self.type2test([0, 1])
+        self._assert_cmp(a, a, 0)
+        self._assert_cmp(a, self.type2test([0, 1]), 0)
+        self._assert_cmp(a, self.type2test([0]), 1)
+        self._assert_cmp(a, self.type2test([0, 2]), -1)
+
     def test_count(self):
         a = self.type2test([0, 1, 2])*3
         self.assertEqual(a.count(0), 3)
index ed405472574a9358311aa50d0409c943426ee704..d6467d215b640c6dff8eb8fc212e93ed5f677317 100644 (file)
@@ -102,6 +102,43 @@ class BaseTest:
         teststrings = [self.fixtype(ts) for ts in teststrings]
         return teststrings
 
+    def test_add(self):
+        s = self.fixtype('ab')
+        self.assertEqual(s + self.fixtype(''), s)
+        self.assertEqual(self.fixtype('') + s, s)
+        self.assertEqual(s + self.fixtype('cd'), self.fixtype('abcd'))
+
+    def test_mul(self):
+        s = self.fixtype('ab')
+        self.assertEqual(s*0, self.fixtype(''))
+        self.assertEqual(0*s, self.fixtype(''))
+        self.assertEqual(s*1, s)
+        self.assertEqual(1*s, s)
+        self.assertEqual(s*2, self.fixtype('abab'))
+        self.assertEqual(2*s, self.fixtype('abab'))
+
+        class subclass(self.type2test):
+            pass
+        s = subclass(self.fixtype('ab'))
+        r = s*1
+        self.assertEqual(r, s)
+        self.assertIsNot(r, s)
+
+    def _assert_cmp(self, a, b, r):
+        self.assertIs(a == b, r == 0)
+        self.assertIs(a != b, r != 0)
+        self.assertIs(a > b, r > 0)
+        self.assertIs(a <= b, r <= 0)
+        self.assertIs(a < b, r < 0)
+        self.assertIs(a >= b, r >= 0)
+
+    def test_cmp(self):
+        a = self.fixtype('ab')
+        self._assert_cmp(a, a, 0)
+        self._assert_cmp(a, self.fixtype('ab'), 0)
+        self._assert_cmp(a, self.fixtype('a'), 1)
+        self._assert_cmp(a, self.fixtype('ac'), -1)
+
     def test_count(self):
         self.checkequal(3, 'aaa', 'count', 'a')
         self.checkequal(0, 'aaa', 'count', 'b')
@@ -1304,6 +1341,7 @@ class StringLikeTest(BaseTest):
                                     slice(start, stop, step))
 
     def test_mul(self):
+        super().test_mul()
         self.checkequal('', 'abc', '__mul__', -1)
         self.checkequal('', 'abc', '__mul__', 0)
         self.checkequal('abc', 'abc', '__mul__', 1)
index 56bdfe373191d438875a2b05ebe0b6b64c28ca1b..9aba6dd033d22d1083ae8062ffe70d86f8303f8f 100644 (file)
@@ -2169,13 +2169,20 @@ class FixedStringTest(test.string_tests.BaseTest):
 
     contains_bytes = True
 
+    def test_mixed_cmp(self):
+        a = self.type2test(b'ab')
+        for t in bytes, bytearray, BytesSubclass, ByteArraySubclass:
+            with self.subTest(t.__name__):
+                self._assert_cmp(a, t(b'ab'), 0)
+                self._assert_cmp(a, t(b'a'), 1)
+                self._assert_cmp(a, t(b'ac'), -1)
+
 class ByteArrayAsStringTest(FixedStringTest, unittest.TestCase):
     type2test = bytearray
 
 class BytesAsStringTest(FixedStringTest, unittest.TestCase):
     type2test = bytes
 
-
 class SubclassTest:
 
     def test_basic(self):
index 61e79f553e8ec9178058bd1d74b74642baae8dc2..601b6d9b0c5abdedbe0578fce9cfd25fc8d8dc7e 100644 (file)
@@ -1,8 +1,18 @@
 # Check every path through every method of UserDict
 
+from collections import UserDict
 from test import mapping_tests, support
 import unittest
 import collections
+import types
+
+
+class UserDictSubclass(UserDict):
+    pass
+
+class UserDictSubclass2(UserDict):
+    pass
+
 
 d0 = {}
 d1 = {"one": 1}
@@ -155,6 +165,25 @@ class UserDictTest(mapping_tests.TestHashMappingProtocol):
         self.assertRaises(TypeError, collections.UserDict, (), ())
         self.assertRaises(TypeError, collections.UserDict.__init__)
 
+    def test_data(self):
+        u = UserDict()
+        self.assertEqual(u.data, {})
+        self.assertIs(type(u.data), dict)
+        d = {'a': 1, 'b': 2}
+        u = UserDict(d)
+        self.assertEqual(u.data, d)
+        self.assertIsNot(u.data, d)
+        self.assertIs(type(u.data), dict)
+        u = UserDict(u)
+        self.assertEqual(u.data, d)
+        self.assertIs(type(u.data), dict)
+        u = UserDict([('a', 1), ('b', 2)])
+        self.assertEqual(u.data, d)
+        self.assertIs(type(u.data), dict)
+        u = UserDict(a=1, b=2)
+        self.assertEqual(u.data, d)
+        self.assertIs(type(u.data), dict)
+
     def test_update(self):
         for kw in 'self', 'dict', 'other', 'iterable':
             d = collections.UserDict()
@@ -219,6 +248,69 @@ class UserDictTest(mapping_tests.TestHashMappingProtocol):
         mapping_tests.TestHashMappingProtocol.test_repr_deep,
     )
 
+    def test_mixed_or(self):
+        for t in UserDict, dict, types.MappingProxyType:
+            with self.subTest(t.__name__):
+                u = UserDict({0: 'a', 1: 'b'}) | t({1: 'c', 2: 'd'})
+                self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'})
+                self.assertIs(type(u), UserDict)
+
+                u = t({0: 'a', 1: 'b'}) | UserDict({1: 'c', 2: 'd'})
+                self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'})
+                self.assertIs(type(u), UserDict)
+
+        u = UserDict({0: 'a', 1: 'b'}) | UserDictSubclass({1: 'c', 2: 'd'})
+        self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'})
+        self.assertIs(type(u), UserDict)
+
+        u = UserDictSubclass({0: 'a', 1: 'b'}) | UserDict({1: 'c', 2: 'd'})
+        self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'})
+        self.assertIs(type(u), UserDictSubclass)
+
+        u = UserDictSubclass({0: 'a', 1: 'b'}) |  UserDictSubclass2({1: 'c', 2: 'd'})
+        self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'})
+        self.assertIs(type(u), UserDictSubclass)
+
+        u = UserDict({1: 'c', 2: 'd'}).__ror__(UserDict({0: 'a', 1: 'b'}))
+        self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'})
+        self.assertIs(type(u), UserDict)
+
+        u = UserDictSubclass({1: 'c', 2: 'd'}).__ror__(UserDictSubclass2({0: 'a', 1: 'b'}))
+        self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'})
+        self.assertIs(type(u), UserDictSubclass)
+
+    def test_mixed_ior(self):
+        for t in UserDict, dict, types.MappingProxyType:
+            with self.subTest(t.__name__):
+                u = u2 = UserDict({0: 'a', 1: 'b'})
+                u |= t({1: 'c', 2: 'd'})
+                self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'})
+                self.assertIs(type(u), UserDict)
+                self.assertIs(u, u2)
+
+        u = dict({0: 'a', 1: 'b'})
+        u |= UserDict({1: 'c', 2: 'd'})
+        self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'})
+        self.assertIs(type(u), dict)
+
+        u = u2 = UserDict({0: 'a', 1: 'b'})
+        u |= UserDictSubclass({1: 'c', 2: 'd'})
+        self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'})
+        self.assertIs(type(u), UserDict)
+        self.assertIs(u, u2)
+
+        u = u2 = UserDictSubclass({0: 'a', 1: 'b'})
+        u |= UserDict({1: 'c', 2: 'd'})
+        self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'})
+        self.assertIs(type(u), UserDictSubclass)
+        self.assertIs(u, u2)
+
+        u = u2 = UserDictSubclass({0: 'a', 1: 'b'})
+        u |= UserDictSubclass2({1: 'c', 2: 'd'})
+        self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'})
+        self.assertIs(type(u), UserDictSubclass)
+        self.assertIs(u, u2)
+
 
 if __name__ == "__main__":
     unittest.main()
index 312702c8e398b9bdb072bf4e60d3aa2f8193868a..abc0ff2142f9a6bddcee7a76212dad758010a534 100644 (file)
@@ -2,13 +2,36 @@
 
 from collections import UserList
 from test import list_tests
-import unittest
 from test import support
+import unittest
+
+
+class UserListSubclass(UserList):
+    pass
+
+class UserListSubclass2(UserList):
+    pass
 
 
 class UserListTest(list_tests.CommonTest):
     type2test = UserList
 
+    def test_data(self):
+        u = UserList()
+        self.assertEqual(u.data, [])
+        self.assertIs(type(u.data), list)
+        a = [1, 2]
+        u = UserList(a)
+        self.assertEqual(u.data, a)
+        self.assertIsNot(u.data, a)
+        self.assertIs(type(u.data), list)
+        u = UserList(u)
+        self.assertEqual(u.data, a)
+        self.assertIs(type(u.data), list)
+        u = UserList("spam")
+        self.assertEqual(u.data, list("spam"))
+        self.assertIs(type(u.data), list)
+
     def test_getslice(self):
         super().test_getslice()
         l = [0, 1, 2, 3, 4]
@@ -25,34 +48,74 @@ class UserListTest(list_tests.CommonTest):
         self.assertIsInstance(u[:], u.__class__)
         self.assertEqual(u[:],u)
 
-    def test_add_specials(self):
-        u = UserList("spam")
-        u2 = u + "eggs"
-        self.assertEqual(u2, list("spameggs"))
+    def test_mixed_add(self):
+        for t in UserList, list, str, tuple, iter:
+            with self.subTest(t.__name__):
+                u = UserList("spam") + t("eggs")
+                self.assertEqual(u, list("spameggs"))
+                self.assertIs(type(u), UserList)
+
+                u = t("spam") + UserList("eggs")
+                self.assertEqual(u, list("spameggs"))
+                self.assertIs(type(u), UserList)
 
-    def test_radd_specials(self):
-        u = UserList("eggs")
-        u2 = "spam" + u
+        u = UserList("spam") + UserListSubclass("eggs")
+        self.assertEqual(u, list("spameggs"))
+        self.assertIs(type(u), UserList)
+
+        u = UserListSubclass("spam") + UserList("eggs")
+        self.assertEqual(u, list("spameggs"))
+        self.assertIs(type(u), UserListSubclass)
+
+        u = UserListSubclass("spam") + UserListSubclass2("eggs")
+        self.assertEqual(u, list("spameggs"))
+        self.assertIs(type(u), UserListSubclass)
+
+        u2 = UserList("eggs").__radd__(UserList("spam"))
         self.assertEqual(u2, list("spameggs"))
-        u2 = u.__radd__(UserList("spam"))
+        self.assertIs(type(u), UserListSubclass)
+
+        u2 = UserListSubclass("eggs").__radd__(UserListSubclass2("spam"))
         self.assertEqual(u2, list("spameggs"))
+        self.assertIs(type(u), UserListSubclass)
 
-    def test_iadd(self):
-        super().test_iadd()
-        u = [0, 1]
-        u += UserList([0, 1])
-        self.assertEqual(u, [0, 1, 0, 1])
+    def test_mixed_iadd(self):
+        for t in UserList, list, str, tuple, iter:
+            with self.subTest(t.__name__):
+                u = u2 = UserList("spam")
+                u += t("eggs")
+                self.assertEqual(u, list("spameggs"))
+                self.assertIs(type(u), UserList)
+                self.assertIs(u, u2)
 
-    def test_mixedcmp(self):
-        u = self.type2test([0, 1])
-        self.assertEqual(u, [0, 1])
-        self.assertNotEqual(u, [0])
-        self.assertNotEqual(u, [0, 2])
+                u = t("spam")
+                u += UserList("eggs")
+                self.assertEqual(u, list("spameggs"))
+                self.assertIs(type(u), UserList)
 
-    def test_mixedadd(self):
+        u = u2 = UserList("spam")
+        u += UserListSubclass("eggs")
+        self.assertEqual(u, list("spameggs"))
+        self.assertIs(type(u), UserList)
+        self.assertIs(u, u2)
+
+        u = u2 = UserListSubclass("spam")
+        u += UserList("eggs")
+        self.assertEqual(u, list("spameggs"))
+        self.assertIs(type(u), UserListSubclass)
+        self.assertIs(u, u2)
+
+        u = u2 = UserListSubclass("spam")
+        u += UserListSubclass2("eggs")
+        self.assertEqual(u, list("spameggs"))
+        self.assertIs(type(u), UserListSubclass)
+        self.assertIs(u, u2)
+
+    def test_mixed_cmp(self):
         u = self.type2test([0, 1])
-        self.assertEqual(u + [], u)
-        self.assertEqual(u + [2], [0, 1, 2])
+        self._assert_cmp(u, [0, 1], 0)
+        self._assert_cmp(u, [0], 1)
+        self._assert_cmp(u, [0, 2], -1)
 
     def test_getitemoverwriteiter(self):
         # Verify that __getitem__ overrides *are* recognized by __iter__
@@ -61,6 +124,43 @@ class UserListTest(list_tests.CommonTest):
                 return str(key) + '!!!'
         self.assertEqual(next(iter(T((1,2)))), "0!!!")
 
+    def test_implementation(self):
+        u = UserList([1])
+        with (support.swap_attr(UserList, '__len__', None),
+              support.swap_attr(UserList, 'insert', None)):
+            u.append(2)
+        self.assertEqual(u, [1, 2])
+        with support.swap_attr(UserList, 'append', None):
+            u.extend([3, 4])
+        self.assertEqual(u, [1, 2, 3, 4])
+        with support.swap_attr(UserList, 'append', None):
+            u.extend(UserList([3, 4]))
+        self.assertEqual(u, [1, 2, 3, 4, 3, 4])
+        with support.swap_attr(UserList, '__iter__', None):
+            c = u.count(3)
+        self.assertEqual(c, 2)
+        with (support.swap_attr(UserList, '__iter__', None),
+              support.swap_attr(UserList, '__getitem__', None)):
+            i = u.index(4)
+        self.assertEqual(i, 3)
+        with (support.swap_attr(UserList, 'index', None),
+              support.swap_attr(UserList, '__getitem__', None)):
+            u.remove(3)
+        self.assertEqual(u, [1, 2, 4, 3, 4])
+        with (support.swap_attr(UserList, '__getitem__', None),
+              support.swap_attr(UserList, '__delitem__', None)):
+            u.pop()
+        self.assertEqual(u, [1, 2, 4, 3])
+        with (support.swap_attr(UserList, '__len__', None),
+              support.swap_attr(UserList, '__getitem__', None),
+              support.swap_attr(UserList, '__setitem__', None)):
+            u.reverse()
+        self.assertEqual(u, [3, 4, 2, 1])
+        with (support.swap_attr(UserList, '__len__', None),
+              support.swap_attr(UserList, 'pop', None)):
+            u.clear()
+        self.assertEqual(u, [])
+
     def test_userlist_copy(self):
         u = self.type2test([6, 8, 1, 9, 1])
         v = u.copy()
index 74df52f5412af01b216ce574ca3a13c99f3c20ec..cc85c06bf93363334d9eb57a63c79dee2ab3d1e5 100644 (file)
@@ -3,9 +3,18 @@
 
 import unittest
 from test import string_tests
+from test import support
 
 from collections import UserString
 
+
+class UserStringSubclass(UserString):
+    pass
+
+class UserStringSubclass2(UserString):
+    pass
+
+
 class UserStringTest(
     string_tests.StringLikeTest,
     unittest.TestCase
@@ -40,6 +49,78 @@ class UserStringTest(
         # we don't fix the arguments, because UserString can't cope with it
         getattr(object, methodname)(*args)
 
+    def test_data(self):
+        u = UserString("spam")
+        self.assertEqual(u.data, "spam")
+        self.assertIs(type(u.data), str)
+        u = UserString(u)
+        self.assertEqual(u.data, "spam")
+        self.assertIs(type(u.data), str)
+        u = UserString(42)
+        self.assertEqual(u.data, "42")
+        self.assertIs(type(u.data), str)
+
+    def test_mixed_add(self):
+        u = UserString("spam") + "eggs"
+        self.assertEqual(u, "spameggs")
+        self.assertIs(type(u), UserString)
+
+        u = "spam" + UserString("eggs")
+        self.assertEqual(u, "spameggs")
+        self.assertIs(type(u), UserString)
+
+        u = UserString("spam") + UserStringSubclass("eggs")
+        self.assertEqual(u, "spameggs")
+        self.assertIs(type(u), UserString)
+
+        u = UserStringSubclass("spam") + UserString("eggs")
+        self.assertEqual(u, "spameggs")
+        self.assertIs(type(u), UserStringSubclass)
+
+        u = UserStringSubclass("spam") + UserStringSubclass2("eggs")
+        self.assertEqual(u, "spameggs")
+        self.assertIs(type(u), UserStringSubclass)
+
+        u2 = UserString("eggs").__radd__(UserString("spam"))
+        self.assertEqual(u2, "spameggs")
+        self.assertIs(type(u), UserStringSubclass)
+
+        u2 = UserStringSubclass("eggs").__radd__(UserStringSubclass2("spam"))
+        self.assertEqual(u2, "spameggs")
+        self.assertIs(type(u), UserStringSubclass)
+
+    def test_mixed_iadd(self):
+        u = UserString("spam")
+        u += "eggs"
+        self.assertEqual(u, "spameggs")
+        self.assertIs(type(u), UserString)
+
+        u = "spam"
+        u += UserString("eggs")
+        self.assertEqual(u, "spameggs")
+        self.assertIs(type(u), UserString)
+
+        u = UserString("spam")
+        u += UserStringSubclass("eggs")
+        self.assertEqual(u, "spameggs")
+        self.assertIs(type(u), UserString)
+
+        u = UserStringSubclass("spam")
+        u += UserString("eggs")
+        self.assertEqual(u, "spameggs")
+        self.assertIs(type(u), UserStringSubclass)
+
+        u = UserStringSubclass("spam")
+        u += UserStringSubclass2("eggs")
+        self.assertEqual(u, "spameggs")
+        self.assertIs(type(u), UserStringSubclass)
+
+    def test_mixed_cmp(self):
+        a = self.fixtype('ab')
+        self._assert_cmp(a, 'ab', 0)
+        self._assert_cmp(a, 'a', 1)
+        self._assert_cmp(a, 'ac', -1)
+
     def test_rmod(self):
         class ustr2(UserString):
             pass
@@ -66,6 +147,20 @@ class UserStringTest(
         # Check that errors defaults to 'strict'
         self.checkraises(UnicodeError, '\ud800', 'encode', None, None)
 
+    def test_implementation(self):
+        s = UserString('ababahalamaha')
+        with support.swap_attr(UserString, '__iter__', None):
+            c = s.count('a')
+            c2 = s.count(UserString('a'))
+        self.assertEqual(c, 7)
+        self.assertEqual(c2, 7)
+        with (support.swap_attr(UserString, '__iter__', None),
+              support.swap_attr(UserString, '__getitem__', None)):
+            i = s.index('h')
+            i2 = s.index(UserString('h'))
+        self.assertEqual(i, 5)
+        self.assertEqual(i2, 5)
+
 
 if __name__ == "__main__":
     unittest.main()