]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-80198: Improve test_pwd and test_grp (GH-150380)
authorSerhiy Storchaka <storchaka@gmail.com>
Mon, 25 May 2026 13:17:38 +0000 (16:17 +0300)
committerGitHub <noreply@github.com>
Mon, 25 May 2026 13:17:38 +0000 (16:17 +0300)
Fix tests for non-existing names and ids when getpwall()/getgrall()
don't return all users/groups.

Add tests for out-of-range uids, integer float ids, bytes names,
null-terminated names, names with surrogates, empty names, excessive
arguments.

Lib/test/test_grp.py
Lib/test/test_pwd.py

index e52e17b8dc73667bcf6b5f09194fdb74cb611b36..ed86802f069e0f88e0f2253755322d5bc7d583a7 100644 (file)
@@ -1,5 +1,7 @@
 """Test script for the grp module."""
 
+import random
+import string
 import unittest
 from test.support import import_helper
 
@@ -50,61 +52,51 @@ class GroupDatabaseTestCase(unittest.TestCase):
     def test_errors(self):
         self.assertRaises(TypeError, grp.getgrgid)
         self.assertRaises(TypeError, grp.getgrgid, 3.14)
+        self.assertRaises(TypeError, grp.getgrgid, 0.0)
+        self.assertRaises(TypeError, grp.getgrgid, 0, 0)
+        # should be out of gid_t range
+        self.assertRaises(OverflowError, grp.getgrgid, 2**128)
+        self.assertRaises(OverflowError, grp.getgrgid, -2**128)
         self.assertRaises(TypeError, grp.getgrnam)
         self.assertRaises(TypeError, grp.getgrnam, 42)
-        self.assertRaises(TypeError, grp.getgrall, 42)
+        self.assertRaises(TypeError, grp.getgrnam, b'root')
+        self.assertRaises(TypeError, grp.getgrnam, 'root', 0)
         # embedded null character
         self.assertRaisesRegex(ValueError, 'null', grp.getgrnam, 'a\x00b')
+        self.assertRaisesRegex(ValueError, 'null', grp.getgrnam, 'root\x00')
+        self.assertRaises(UnicodeEncodeError, grp.getgrnam, 'roo\udc74')
+        self.assertRaises(KeyError, grp.getgrnam, '')
+        self.assertRaises(TypeError, grp.getgrall, 42)
 
-        # try to get some errors
-        bynames = {}
-        bygids = {}
-        for (n, p, g, mem) in grp.getgrall():
-            if not n or n == '+':
-                continue # skip NIS entries etc.
-            bynames[n] = g
-            bygids[g] = n
-
-        allnames = list(bynames.keys())
-        namei = 0
-        fakename = allnames[namei]
-        while fakename in bynames:
-            chars = list(fakename)
-            for i in range(len(chars)):
-                if chars[i] == 'z':
-                    chars[i] = 'A'
-                    break
-                elif chars[i] == 'Z':
-                    continue
+        # Find a non-existent group name.
+        # getgrall() will not necessarily report all existing groups
+        # (typical for LDAP based directories in big organizations).
+        for _ in range(30):
+            fakename = ''.join(random.choices(string.ascii_lowercase, k=6))
+            try:
+                grp.getgrnam(fakename)
+            except KeyError:
+                break
+        else:
+            self.fail('Cannot find non-existent group name')
+
+        # Find a non-existent gid.
+        maxgid = 2**31
+        for _ in range(30):
+            fakegid = random.randrange(maxgid)
+            try:
+                grp.getgrgid(fakegid)
+            except KeyError:
+                break
+            except OverflowError:
+                if maxgid == 2**31:
+                    maxgid = 2**16-1
+                elif maxgid == 2**16-1:
+                    maxgid = 2**15
                 else:
-                    chars[i] = chr(ord(chars[i]) + 1)
-                    break
-            else:
-                namei = namei + 1
-                try:
-                    fakename = allnames[namei]
-                except IndexError:
-                    # should never happen... if so, just forget it
-                    break
-            fakename = ''.join(chars)
-
-        self.assertRaises(KeyError, grp.getgrnam, fakename)
-
-        # Choose a non-existent gid.
-        fakegid = 4127
-        while fakegid in bygids:
-            fakegid = (fakegid * 3) % 0x10000
-
-        self.assertRaises(KeyError, grp.getgrgid, fakegid)
-
-    def test_noninteger_gid(self):
-        entries = grp.getgrall()
-        if not entries:
-            self.skipTest('no groups')
-        # Choose an existent gid.
-        gid = entries[0][2]
-        self.assertRaises(TypeError, grp.getgrgid, float(gid))
-        self.assertRaises(TypeError, grp.getgrgid, str(gid))
+                    raise
+        else:
+            self.fail('Cannot find non-existent gid')
 
 
 if __name__ == "__main__":
index d0ef30ec493084338c3e3574b884d73b28fe5c98..bdf57776c82be13e7b721f77ad8fb21e1aa27320 100644 (file)
@@ -1,3 +1,5 @@
+import random
+import string
 import sys
 import unittest
 from test.support import import_helper
@@ -56,61 +58,57 @@ class PwdTest(unittest.TestCase):
     def test_errors(self):
         self.assertRaises(TypeError, pwd.getpwuid)
         self.assertRaises(TypeError, pwd.getpwuid, 3.14)
+        self.assertRaises(TypeError, pwd.getpwuid, 0.0)
+        self.assertRaises(TypeError, pwd.getpwuid, 0, 0)
+        # should be out of uid_t range
+        self.assertRaises(KeyError, pwd.getpwuid, 2**128)
+        self.assertRaises(KeyError, pwd.getpwuid, -2**128)
         self.assertRaises(TypeError, pwd.getpwnam)
         self.assertRaises(TypeError, pwd.getpwnam, 42)
-        self.assertRaises(TypeError, pwd.getpwall, 42)
+        self.assertRaises(TypeError, pwd.getpwnam, b'root')
+        self.assertRaises(TypeError, pwd.getpwnam, 'root', 0)
         # embedded null character
         self.assertRaisesRegex(ValueError, 'null', pwd.getpwnam, 'a\x00b')
+        self.assertRaisesRegex(ValueError, 'null', pwd.getpwnam, 'root\x00')
+        self.assertRaises(UnicodeEncodeError, pwd.getpwnam, 'roo\udc74')
+        self.assertRaises(KeyError, pwd.getpwnam, '')
+        self.assertRaises(TypeError, pwd.getpwall, 42)
 
-        # try to get some errors
-        bynames = {}
-        byuids = {}
-        for (n, p, u, g, gecos, d, s) in pwd.getpwall():
-            bynames[n] = u
-            byuids[u] = n
-
-        allnames = list(bynames.keys())
-        namei = 0
-        fakename = allnames[namei] if allnames else "invaliduser"
-        while fakename in bynames:
-            chars = list(fakename)
-            for i in range(len(chars)):
-                if chars[i] == 'z':
-                    chars[i] = 'A'
-                    break
-                elif chars[i] == 'Z':
-                    continue
-                else:
-                    chars[i] = chr(ord(chars[i]) + 1)
-                    break
-            else:
-                namei = namei + 1
-                try:
-                    fakename = allnames[namei]
-                except IndexError:
-                    # should never happen... if so, just forget it
-                    break
-            fakename = ''.join(chars)
-
-        self.assertRaises(KeyError, pwd.getpwnam, fakename)
-
-        # In some cases, byuids isn't a complete list of all users in the
-        # system, so if we try to pick a value not in byuids (via a perturbing
-        # loop, say), pwd.getpwuid() might still be able to find data for that
-        # uid. Using sys.maxint may provoke the same problems, but hopefully
-        # it will be a more repeatable failure.
-        fakeuid = sys.maxsize
-        self.assertNotIn(fakeuid, byuids)
-        self.assertRaises(KeyError, pwd.getpwuid, fakeuid)
+        # Find a non-existent user name.
+        # getpwall() will not necessarily report all existing users
+        # (typical for LDAP based directories in big organizations).
+        for _ in range(30):
+            fakename = ''.join(random.choices(string.ascii_lowercase, k=6))
+            try:
+                pwd.getpwnam(fakename)
+            except KeyError:
+                break
+        else:
+            self.fail('Cannot find non-existent user name')
+
+        # Find a non-existent uid.
+        maxuid = max(e.pw_uid for e in pwd.getpwall())
+        if maxuid < 2**15:
+            maxuid = 2**15
+        elif maxuid < 2**16:
+            maxuid = 2**16-1
+        else:
+            maxuid = 2**31
+        for _ in range(30):
+            fakeuid = random.randrange(maxuid)
+            try:
+                pwd.getpwuid(fakeuid)
+            except KeyError:
+                break
+        else:
+            self.fail('Cannot find non-existent uid')
 
         # On Cygwin, getpwuid(-1) returns 'Unknown+User' user
         if sys.platform != 'cygwin':
             # -1 shouldn't be a valid uid because it has a special meaning in many
             # uid-related functions
             self.assertRaises(KeyError, pwd.getpwuid, -1)
-        # should be out of uid_t range
-        self.assertRaises(KeyError, pwd.getpwuid, 2**128)
-        self.assertRaises(KeyError, pwd.getpwuid, -2**128)
+
 
 if __name__ == "__main__":
     unittest.main()