1"Test posix functions"
2
3from test import test_support
4
5# Skip these tests if there is no posix module.
6posix = test_support.import_module('posix')
7
8import errno
9import sys
10import time
11import os
12import platform
13import pwd
14import shutil
15import stat
16import sys
17import tempfile
18import unittest
19import warnings
20
21_DUMMY_SYMLINK = os.path.join(tempfile.gettempdir(),
22                              test_support.TESTFN + '-dummy-symlink')
23
24warnings.filterwarnings('ignore', '.* potential security risk .*',
25                        RuntimeWarning)
26
27class PosixTester(unittest.TestCase):
28
29    def setUp(self):
30        # create empty file
31        fp = open(test_support.TESTFN, 'w+')
32        fp.close()
33        self.teardown_files = [ test_support.TESTFN ]
34
35    def tearDown(self):
36        for teardown_file in self.teardown_files:
37            os.unlink(teardown_file)
38
39    def testNoArgFunctions(self):
40        # test posix functions which take no arguments and have
41        # no side-effects which we need to cleanup (e.g., fork, wait, abort)
42        NO_ARG_FUNCTIONS = [ "ctermid", "getcwd", "getcwdu", "uname",
43                             "times", "getloadavg", "tmpnam",
44                             "getegid", "geteuid", "getgid", "getgroups",
45                             "getpid", "getpgrp", "getppid", "getuid",
46                           ]
47
48        with warnings.catch_warnings():
49            warnings.filterwarnings("ignore", "", DeprecationWarning)
50            for name in NO_ARG_FUNCTIONS:
51                posix_func = getattr(posix, name, None)
52                if posix_func is not None:
53                    posix_func()
54                    self.assertRaises(TypeError, posix_func, 1)
55
56    @unittest.skipUnless(hasattr(posix, 'getresuid'),
57                         'test needs posix.getresuid()')
58    def test_getresuid(self):
59        user_ids = posix.getresuid()
60        self.assertEqual(len(user_ids), 3)
61        for val in user_ids:
62            self.assertGreaterEqual(val, 0)
63
64    @unittest.skipUnless(hasattr(posix, 'getresgid'),
65                         'test needs posix.getresgid()')
66    def test_getresgid(self):
67        group_ids = posix.getresgid()
68        self.assertEqual(len(group_ids), 3)
69        for val in group_ids:
70            self.assertGreaterEqual(val, 0)
71
72    @unittest.skipUnless(hasattr(posix, 'setresuid'),
73                         'test needs posix.setresuid()')
74    def test_setresuid(self):
75        current_user_ids = posix.getresuid()
76        self.assertIsNone(posix.setresuid(*current_user_ids))
77        # -1 means don't change that value.
78        self.assertIsNone(posix.setresuid(-1, -1, -1))
79
80    @unittest.skipUnless(hasattr(posix, 'setresuid'),
81                         'test needs posix.setresuid()')
82    def test_setresuid_exception(self):
83        # Don't do this test if someone is silly enough to run us as root.
84        current_user_ids = posix.getresuid()
85        if 0 not in current_user_ids:
86            new_user_ids = (current_user_ids[0]+1, -1, -1)
87            self.assertRaises(OSError, posix.setresuid, *new_user_ids)
88
89    @unittest.skipUnless(hasattr(posix, 'setresgid'),
90                         'test needs posix.setresgid()')
91    def test_setresgid(self):
92        current_group_ids = posix.getresgid()
93        self.assertIsNone(posix.setresgid(*current_group_ids))
94        # -1 means don't change that value.
95        self.assertIsNone(posix.setresgid(-1, -1, -1))
96
97    @unittest.skipUnless(hasattr(posix, 'setresgid'),
98                         'test needs posix.setresgid()')
99    def test_setresgid_exception(self):
100        # Don't do this test if someone is silly enough to run us as root.
101        current_group_ids = posix.getresgid()
102        if 0 not in current_group_ids:
103            new_group_ids = (current_group_ids[0]+1, -1, -1)
104            self.assertRaises(OSError, posix.setresgid, *new_group_ids)
105
106    @unittest.skipUnless(hasattr(posix, 'initgroups'),
107                         "test needs os.initgroups()")
108    def test_initgroups(self):
109        # It takes a string and an integer; check that it raises a TypeError
110        # for other argument lists.
111        self.assertRaises(TypeError, posix.initgroups)
112        self.assertRaises(TypeError, posix.initgroups, None)
113        self.assertRaises(TypeError, posix.initgroups, 3, "foo")
114        self.assertRaises(TypeError, posix.initgroups, "foo", 3, object())
115
116        # If a non-privileged user invokes it, it should fail with OSError
117        # EPERM.
118        if os.getuid() != 0:
119            try:
120                name = pwd.getpwuid(posix.getuid()).pw_name
121            except KeyError:
122                # the current UID may not have a pwd entry
123                raise unittest.SkipTest("need a pwd entry")
124            try:
125                posix.initgroups(name, 13)
126            except OSError as e:
127                self.assertEqual(e.errno, errno.EPERM)
128            else:
129                self.fail("Expected OSError to be raised by initgroups")
130
131    @unittest.skipUnless(hasattr(posix, 'statvfs'),
132                         'test needs posix.statvfs()')
133    def test_statvfs(self):
134        self.assertTrue(posix.statvfs(os.curdir))
135
136    @unittest.skipUnless(hasattr(posix, 'fstatvfs'),
137                         'test needs posix.fstatvfs()')
138    def test_fstatvfs(self):
139        fp = open(test_support.TESTFN)
140        try:
141            self.assertTrue(posix.fstatvfs(fp.fileno()))
142        finally:
143            fp.close()
144
145    @unittest.skipUnless(hasattr(posix, 'ftruncate'),
146                         'test needs posix.ftruncate()')
147    def test_ftruncate(self):
148        fp = open(test_support.TESTFN, 'w+')
149        try:
150            # we need to have some data to truncate
151            fp.write('test')
152            fp.flush()
153            posix.ftruncate(fp.fileno(), 0)
154        finally:
155            fp.close()
156
157    @unittest.skipUnless(hasattr(posix, 'dup'),
158                         'test needs posix.dup()')
159    def test_dup(self):
160        fp = open(test_support.TESTFN)
161        try:
162            fd = posix.dup(fp.fileno())
163            self.assertIsInstance(fd, int)
164            os.close(fd)
165        finally:
166            fp.close()
167
168    @unittest.skipUnless(hasattr(posix, 'confstr'),
169                         'test needs posix.confstr()')
170    def test_confstr(self):
171        self.assertRaises(ValueError, posix.confstr, "CS_garbage")
172        self.assertEqual(len(posix.confstr("CS_PATH")) > 0, True)
173
174    @unittest.skipUnless(hasattr(posix, 'dup2'),
175                         'test needs posix.dup2()')
176    def test_dup2(self):
177        fp1 = open(test_support.TESTFN)
178        fp2 = open(test_support.TESTFN)
179        try:
180            posix.dup2(fp1.fileno(), fp2.fileno())
181        finally:
182            fp1.close()
183            fp2.close()
184
185    def fdopen_helper(self, *args):
186        fd = os.open(test_support.TESTFN, os.O_RDONLY)
187        fp2 = posix.fdopen(fd, *args)
188        fp2.close()
189
190    @unittest.skipUnless(hasattr(posix, 'fdopen'),
191                         'test needs posix.fdopen()')
192    def test_fdopen(self):
193        self.fdopen_helper()
194        self.fdopen_helper('r')
195        self.fdopen_helper('r', 100)
196
197    @unittest.skipUnless(hasattr(posix, 'fdopen'),
198                         'test needs posix.fdopen()')
199    def test_fdopen_directory(self):
200        try:
201            fd = os.open('.', os.O_RDONLY)
202            self.addCleanup(os.close, fd)
203        except OSError as e:
204            self.assertEqual(e.errno, errno.EACCES)
205            self.skipTest("system cannot open directories")
206        with self.assertRaises(IOError) as cm:
207            os.fdopen(fd, 'r')
208        self.assertEqual(cm.exception.errno, errno.EISDIR)
209
210    @unittest.skipUnless(hasattr(posix, 'fdopen') and
211                         not sys.platform.startswith("sunos"),
212                         'test needs posix.fdopen()')
213    def test_fdopen_keeps_fd_open_on_errors(self):
214        fd = os.open(test_support.TESTFN, os.O_RDONLY)
215        self.assertRaises(OSError, posix.fdopen, fd, 'w')
216        os.close(fd) # fd should not be closed.
217
218    @unittest.skipUnless(hasattr(posix, 'O_EXLOCK'),
219                         'test needs posix.O_EXLOCK')
220    def test_osexlock(self):
221        fd = os.open(test_support.TESTFN,
222                     os.O_WRONLY|os.O_EXLOCK|os.O_CREAT)
223        self.assertRaises(OSError, os.open, test_support.TESTFN,
224                          os.O_WRONLY|os.O_EXLOCK|os.O_NONBLOCK)
225        os.close(fd)
226
227        if hasattr(posix, "O_SHLOCK"):
228            fd = os.open(test_support.TESTFN,
229                         os.O_WRONLY|os.O_SHLOCK|os.O_CREAT)
230            self.assertRaises(OSError, os.open, test_support.TESTFN,
231                              os.O_WRONLY|os.O_EXLOCK|os.O_NONBLOCK)
232            os.close(fd)
233
234    @unittest.skipUnless(hasattr(posix, 'O_SHLOCK'),
235                         'test needs posix.O_SHLOCK')
236    def test_osshlock(self):
237        fd1 = os.open(test_support.TESTFN,
238                      os.O_WRONLY|os.O_SHLOCK|os.O_CREAT)
239        fd2 = os.open(test_support.TESTFN,
240                      os.O_WRONLY|os.O_SHLOCK|os.O_CREAT)
241        os.close(fd2)
242        os.close(fd1)
243
244        if hasattr(posix, "O_EXLOCK"):
245            fd = os.open(test_support.TESTFN,
246                         os.O_WRONLY|os.O_SHLOCK|os.O_CREAT)
247            self.assertRaises(OSError, os.open, test_support.TESTFN,
248                              os.O_RDONLY|os.O_EXLOCK|os.O_NONBLOCK)
249            os.close(fd)
250
251    @unittest.skipUnless(hasattr(posix, 'fstat'),
252                         'test needs posix.fstat()')
253    def test_fstat(self):
254        fp = open(test_support.TESTFN)
255        try:
256            self.assertTrue(posix.fstat(fp.fileno()))
257        finally:
258            fp.close()
259
260    @unittest.skipUnless(hasattr(posix, 'stat'),
261                         'test needs posix.stat()')
262    def test_stat(self):
263        self.assertTrue(posix.stat(test_support.TESTFN))
264
265    @unittest.skipUnless(hasattr(posix, 'stat'), 'test needs posix.stat()')
266    @unittest.skipUnless(hasattr(posix, 'makedev'), 'test needs posix.makedev()')
267    def test_makedev(self):
268        st = posix.stat(test_support.TESTFN)
269        dev = st.st_dev
270        self.assertIsInstance(dev, (int, long))
271        self.assertGreaterEqual(dev, 0)
272
273        major = posix.major(dev)
274        self.assertIsInstance(major, (int, long))
275        self.assertGreaterEqual(major, 0)
276        self.assertEqual(posix.major(int(dev)), major)
277        self.assertEqual(posix.major(long(dev)), major)
278        self.assertRaises(TypeError, posix.major, float(dev))
279        self.assertRaises(TypeError, posix.major)
280        self.assertRaises((ValueError, OverflowError), posix.major, -1)
281
282        minor = posix.minor(dev)
283        self.assertIsInstance(minor, (int, long))
284        self.assertGreaterEqual(minor, 0)
285        self.assertEqual(posix.minor(int(dev)), minor)
286        self.assertEqual(posix.minor(long(dev)), minor)
287        self.assertRaises(TypeError, posix.minor, float(dev))
288        self.assertRaises(TypeError, posix.minor)
289        self.assertRaises((ValueError, OverflowError), posix.minor, -1)
290
291        if sys.platform.startswith('freebsd') and dev >= 0x100000000:
292            self.skipTest("bpo-31044: on FreeBSD CURRENT, minor() truncates "
293                          "64-bit dev to 32-bit")
294
295        self.assertEqual(posix.makedev(major, minor), dev)
296        self.assertEqual(posix.makedev(int(major), int(minor)), dev)
297        self.assertEqual(posix.makedev(long(major), long(minor)), dev)
298        self.assertRaises(TypeError, posix.makedev, float(major), minor)
299        self.assertRaises(TypeError, posix.makedev, major, float(minor))
300        self.assertRaises(TypeError, posix.makedev, major)
301        self.assertRaises(TypeError, posix.makedev)
302
303    def _test_all_chown_common(self, chown_func, first_param, stat_func):
304        """Common code for chown, fchown and lchown tests."""
305        def check_stat(uid, gid):
306            if stat_func is not None:
307                stat = stat_func(first_param)
308                self.assertEqual(stat.st_uid, uid)
309                self.assertEqual(stat.st_gid, gid)
310        uid = os.getuid()
311        gid = os.getgid()
312        # test a successful chown call
313        chown_func(first_param, uid, gid)
314        check_stat(uid, gid)
315        chown_func(first_param, -1, gid)
316        check_stat(uid, gid)
317        chown_func(first_param, uid, -1)
318        check_stat(uid, gid)
319
320        if uid == 0:
321            # Try an amusingly large uid/gid to make sure we handle
322            # large unsigned values.  (chown lets you use any
323            # uid/gid you like, even if they aren't defined.)
324            #
325            # This problem keeps coming up:
326            #   http://bugs.python.org/issue1747858
327            #   http://bugs.python.org/issue4591
328            #   http://bugs.python.org/issue15301
329            # Hopefully the fix in 4591 fixes it for good!
330            #
331            # This part of the test only runs when run as root.
332            # Only scary people run their tests as root.
333
334            big_value = 2**31
335            chown_func(first_param, big_value, big_value)
336            check_stat(big_value, big_value)
337            chown_func(first_param, -1, -1)
338            check_stat(big_value, big_value)
339            chown_func(first_param, uid, gid)
340            check_stat(uid, gid)
341        elif platform.system() in ('HP-UX', 'SunOS'):
342            # HP-UX and Solaris can allow a non-root user to chown() to root
343            # (issue #5113)
344            raise unittest.SkipTest("Skipping because of non-standard chown() "
345                                    "behavior")
346        else:
347            # non-root cannot chown to root, raises OSError
348            self.assertRaises(OSError, chown_func, first_param, 0, 0)
349            check_stat(uid, gid)
350            self.assertRaises(OSError, chown_func, first_param, 0, -1)
351            check_stat(uid, gid)
352            if 0 not in os.getgroups():
353                self.assertRaises(OSError, chown_func, first_param, -1, 0)
354                check_stat(uid, gid)
355        # test illegal types
356        for t in str, float:
357            self.assertRaises(TypeError, chown_func, first_param, t(uid), gid)
358            check_stat(uid, gid)
359            self.assertRaises(TypeError, chown_func, first_param, uid, t(gid))
360            check_stat(uid, gid)
361
362    @unittest.skipUnless(hasattr(posix, 'chown'), "test needs os.chown()")
363    def test_chown(self):
364        # raise an OSError if the file does not exist
365        os.unlink(test_support.TESTFN)
366        self.assertRaises(OSError, posix.chown, test_support.TESTFN, -1, -1)
367
368        # re-create the file
369        open(test_support.TESTFN, 'w').close()
370        self._test_all_chown_common(posix.chown, test_support.TESTFN,
371                                    getattr(posix, 'stat', None))
372
373    @unittest.skipUnless(hasattr(posix, 'fchown'), "test needs os.fchown()")
374    def test_fchown(self):
375        os.unlink(test_support.TESTFN)
376
377        # re-create the file
378        test_file = open(test_support.TESTFN, 'w')
379        try:
380            fd = test_file.fileno()
381            self._test_all_chown_common(posix.fchown, fd,
382                                        getattr(posix, 'fstat', None))
383        finally:
384            test_file.close()
385
386    @unittest.skipUnless(hasattr(posix, 'lchown'), "test needs os.lchown()")
387    def test_lchown(self):
388        os.unlink(test_support.TESTFN)
389        # create a symlink
390        os.symlink(_DUMMY_SYMLINK, test_support.TESTFN)
391        self._test_all_chown_common(posix.lchown, test_support.TESTFN,
392                                    getattr(posix, 'lstat', None))
393
394    @unittest.skipUnless(hasattr(posix, 'chdir'), 'test needs posix.chdir()')
395    def test_chdir(self):
396        posix.chdir(os.curdir)
397        self.assertRaises(OSError, posix.chdir, test_support.TESTFN)
398
399    @unittest.skipUnless(hasattr(posix, 'lsdir'), 'test needs posix.lsdir()')
400    def test_lsdir(self):
401        self.assertIn(test_support.TESTFN, posix.lsdir(os.curdir))
402
403    @unittest.skipUnless(hasattr(posix, 'access'), 'test needs posix.access()')
404    def test_access(self):
405        self.assertTrue(posix.access(test_support.TESTFN, os.R_OK))
406
407    @unittest.skipUnless(hasattr(posix, 'umask'), 'test needs posix.umask()')
408    def test_umask(self):
409        old_mask = posix.umask(0)
410        self.assertIsInstance(old_mask, int)
411        posix.umask(old_mask)
412
413    @unittest.skipUnless(hasattr(posix, 'strerror'),
414                         'test needs posix.strerror()')
415    def test_strerror(self):
416        self.assertTrue(posix.strerror(0))
417
418    @unittest.skipUnless(hasattr(posix, 'pipe'), 'test needs posix.pipe()')
419    def test_pipe(self):
420        reader, writer = posix.pipe()
421        os.close(reader)
422        os.close(writer)
423
424    @unittest.skipUnless(hasattr(posix, 'tempnam'),
425                         'test needs posix.tempnam()')
426    def test_tempnam(self):
427        with warnings.catch_warnings():
428            warnings.filterwarnings("ignore", "tempnam", DeprecationWarning)
429            self.assertTrue(posix.tempnam())
430            self.assertTrue(posix.tempnam(os.curdir))
431            self.assertTrue(posix.tempnam(os.curdir, 'blah'))
432
433    @unittest.skipUnless(hasattr(posix, 'tmpfile'),
434                         'test needs posix.tmpfile()')
435    def test_tmpfile(self):
436        with warnings.catch_warnings():
437            warnings.filterwarnings("ignore", "tmpfile", DeprecationWarning)
438            fp = posix.tmpfile()
439            fp.close()
440
441    @unittest.skipUnless(hasattr(posix, 'utime'), 'test needs posix.utime()')
442    def test_utime(self):
443        now = time.time()
444        posix.utime(test_support.TESTFN, None)
445        self.assertRaises(TypeError, posix.utime, test_support.TESTFN, (None, None))
446        self.assertRaises(TypeError, posix.utime, test_support.TESTFN, (now, None))
447        self.assertRaises(TypeError, posix.utime, test_support.TESTFN, (None, now))
448        posix.utime(test_support.TESTFN, (int(now), int(now)))
449        posix.utime(test_support.TESTFN, (now, now))
450
451    def _test_chflags_regular_file(self, chflags_func, target_file):
452        st = os.stat(target_file)
453        self.assertTrue(hasattr(st, 'st_flags'))
454
455        # ZFS returns EOPNOTSUPP when attempting to set flag UF_IMMUTABLE.
456        try:
457            chflags_func(target_file, st.st_flags | stat.UF_IMMUTABLE)
458        except OSError as err:
459            if err.errno != errno.EOPNOTSUPP:
460                raise
461            msg = 'chflag UF_IMMUTABLE not supported by underlying fs'
462            self.skipTest(msg)
463
464        try:
465            new_st = os.stat(target_file)
466            self.assertEqual(st.st_flags | stat.UF_IMMUTABLE, new_st.st_flags)
467            try:
468                fd = open(target_file, 'w+')
469            except IOError as e:
470                self.assertEqual(e.errno, errno.EPERM)
471        finally:
472            posix.chflags(target_file, st.st_flags)
473
474    @unittest.skipUnless(hasattr(posix, 'chflags'), 'test needs os.chflags()')
475    def test_chflags(self):
476        self._test_chflags_regular_file(posix.chflags, test_support.TESTFN)
477
478    @unittest.skipUnless(hasattr(posix, 'lchflags'), 'test needs os.lchflags()')
479    def test_lchflags_regular_file(self):
480        self._test_chflags_regular_file(posix.lchflags, test_support.TESTFN)
481
482    @unittest.skipUnless(hasattr(posix, 'lchflags'), 'test needs os.lchflags()')
483    def test_lchflags_symlink(self):
484        testfn_st = os.stat(test_support.TESTFN)
485
486        self.assertTrue(hasattr(testfn_st, 'st_flags'))
487
488        os.symlink(test_support.TESTFN, _DUMMY_SYMLINK)
489        self.teardown_files.append(_DUMMY_SYMLINK)
490        dummy_symlink_st = os.lstat(_DUMMY_SYMLINK)
491
492        # ZFS returns EOPNOTSUPP when attempting to set flag UF_IMMUTABLE.
493        try:
494            posix.lchflags(_DUMMY_SYMLINK,
495                           dummy_symlink_st.st_flags | stat.UF_IMMUTABLE)
496        except OSError as err:
497            if err.errno != errno.EOPNOTSUPP:
498                raise
499            msg = 'chflag UF_IMMUTABLE not supported by underlying fs'
500            self.skipTest(msg)
501
502        try:
503            new_testfn_st = os.stat(test_support.TESTFN)
504            new_dummy_symlink_st = os.lstat(_DUMMY_SYMLINK)
505
506            self.assertEqual(testfn_st.st_flags, new_testfn_st.st_flags)
507            self.assertEqual(dummy_symlink_st.st_flags | stat.UF_IMMUTABLE,
508                             new_dummy_symlink_st.st_flags)
509        finally:
510            posix.lchflags(_DUMMY_SYMLINK, dummy_symlink_st.st_flags)
511
512    @unittest.skipUnless(hasattr(os, "putenv"), "requires os.putenv()")
513    def test_putenv(self):
514        with self.assertRaises(TypeError):
515            os.putenv('FRUIT\0VEGETABLE', 'cabbage')
516        with self.assertRaises(TypeError):
517            os.putenv('FRUIT', 'orange\0VEGETABLE=cabbage')
518        with self.assertRaises(ValueError):
519            os.putenv('FRUIT=ORANGE', 'lemon')
520
521    @unittest.skipUnless(hasattr(posix, 'getcwd'),
522                         'test needs posix.getcwd()')
523    def test_getcwd_long_pathnames(self):
524        dirname = 'getcwd-test-directory-0123456789abcdef-01234567890abcdef'
525        curdir = os.getcwd()
526        base_path = os.path.abspath(test_support.TESTFN) + '.getcwd'
527
528        try:
529            os.mkdir(base_path)
530            os.chdir(base_path)
531        except:
532            self.skipTest("cannot create directory for testing")
533
534        try:
535            def _create_and_do_getcwd(dirname, current_path_length = 0):
536                try:
537                    os.mkdir(dirname)
538                except:
539                    self.skipTest("mkdir cannot create directory sufficiently "
540                                  "deep for getcwd test")
541
542                os.chdir(dirname)
543                try:
544                    os.getcwd()
545                    if current_path_length < 4099:
546                        _create_and_do_getcwd(dirname, current_path_length + len(dirname) + 1)
547                except OSError as e:
548                    expected_errno = errno.ENAMETOOLONG
549                    # The following platforms have quirky getcwd()
550                    # behaviour -- see issue 9185 and 15765 for
551                    # more information.
552                    quirky_platform = (
553                        'sunos' in sys.platform or
554                        'netbsd' in sys.platform or
555                        'openbsd' in sys.platform
556                    )
557                    if quirky_platform:
558                        expected_errno = errno.ERANGE
559                    self.assertEqual(e.errno, expected_errno)
560                finally:
561                    os.chdir('..')
562                    os.rmdir(dirname)
563
564            _create_and_do_getcwd(dirname)
565
566        finally:
567            os.chdir(curdir)
568            shutil.rmtree(base_path)
569
570    @unittest.skipUnless(hasattr(os, 'getegid'), "test needs os.getegid()")
571    def test_getgroups(self):
572        with os.popen('id -G 2>/dev/null') as idg:
573            groups = idg.read().strip()
574            ret = idg.close()
575
576        if ret != None or not groups:
577            raise unittest.SkipTest("need working 'id -G'")
578
579        # Issues 16698: OS X ABIs prior to 10.6 have limits on getgroups()
580        if sys.platform == 'darwin':
581            import sysconfig
582            dt = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') or '10.0'
583            if tuple(int(n) for n in dt.split('.')[0:2]) < (10, 6):
584                raise unittest.SkipTest("getgroups(2) is broken prior to 10.6")
585
586        # 'id -G' and 'os.getgroups()' should return the same
587        # groups, ignoring order and duplicates.
588        # #10822 - it is implementation defined whether posix.getgroups()
589        # includes the effective gid so we include it anyway, since id -G does
590        self.assertEqual(
591                set([int(x) for x in groups.split()]),
592                set(posix.getgroups() + [posix.getegid()]))
593
594    @test_support.requires_unicode
595    def test_path_with_null_unicode(self):
596        fn = test_support.TESTFN_UNICODE
597        try:
598            fn.encode(test_support.TESTFN_ENCODING)
599        except (UnicodeError, TypeError):
600            self.skipTest("Requires unicode filenames support")
601        fn_with_NUL = fn + u'\0'
602        self.addCleanup(test_support.unlink, fn)
603        test_support.unlink(fn)
604        fd = None
605        try:
606            with self.assertRaises(TypeError):
607                fd = os.open(fn_with_NUL, os.O_WRONLY | os.O_CREAT) # raises
608        finally:
609            if fd is not None:
610                os.close(fd)
611        self.assertFalse(os.path.exists(fn))
612        self.assertRaises(TypeError, os.mkdir, fn_with_NUL)
613        self.assertFalse(os.path.exists(fn))
614        open(fn, 'wb').close()
615        self.assertRaises(TypeError, os.stat, fn_with_NUL)
616
617    def test_path_with_null_byte(self):
618        fn = test_support.TESTFN
619        fn_with_NUL = fn + '\0'
620        self.addCleanup(test_support.unlink, fn)
621        test_support.unlink(fn)
622        fd = None
623        try:
624            with self.assertRaises(TypeError):
625                fd = os.open(fn_with_NUL, os.O_WRONLY | os.O_CREAT) # raises
626        finally:
627            if fd is not None:
628                os.close(fd)
629        self.assertFalse(os.path.exists(fn))
630        self.assertRaises(TypeError, os.mkdir, fn_with_NUL)
631        self.assertFalse(os.path.exists(fn))
632        open(fn, 'wb').close()
633        self.assertRaises(TypeError, os.stat, fn_with_NUL)
634
635
636class PosixGroupsTester(unittest.TestCase):
637
638    def setUp(self):
639        if posix.getuid() != 0:
640            raise unittest.SkipTest("not enough privileges")
641        if not hasattr(posix, 'getgroups'):
642            raise unittest.SkipTest("need posix.getgroups")
643        if sys.platform == 'darwin':
644            raise unittest.SkipTest("getgroups(2) is broken on OSX")
645        self.saved_groups = posix.getgroups()
646
647    def tearDown(self):
648        if hasattr(posix, 'setgroups'):
649            posix.setgroups(self.saved_groups)
650        elif hasattr(posix, 'initgroups'):
651            name = pwd.getpwuid(posix.getuid()).pw_name
652            posix.initgroups(name, self.saved_groups[0])
653
654    @unittest.skipUnless(hasattr(posix, 'initgroups'),
655                         'test needs posix.initgroups()')
656    def test_initgroups(self):
657        # find missing group
658
659        g = max(self.saved_groups or [0]) + 1
660        name = pwd.getpwuid(posix.getuid()).pw_name
661        posix.initgroups(name, g)
662        self.assertIn(g, posix.getgroups())
663
664    @unittest.skipUnless(hasattr(posix, 'setgroups'),
665                         'test needs posix.setgroups()')
666    def test_setgroups(self):
667        for groups in [[0], range(16)]:
668            posix.setgroups(groups)
669            self.assertListEqual(groups, posix.getgroups())
670
671
672def test_main():
673    test_support.run_unittest(PosixTester, PosixGroupsTester)
674
675if __name__ == '__main__':
676    test_main()
677