4 """Unittests for the posix1e module"""
6 # Copyright (C) 2002-2009, 2012, 2014, 2015 Iustin Pop <iustin@k1024.org>
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # Lesser General Public License for more details.
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
32 import pytest # type: ignore
40 TEST_DIR = os.environ.get("TEST_DIR", ".")
42 BASIC_ACL_TEXT = "u::rw,g::r,o::-"
43 TEXT_0755 = "u::rwx,g::rx,o::rx"
45 # Permset permission information
47 (ACL_READ, "read", Permset.read),
48 (ACL_WRITE, "write", Permset.write),
49 (ACL_EXECUTE, "execute", Permset.execute),
52 PERMSETS_IDS = [p[1] for p in PERMSETS]
55 (posix1e.ACL_USER, "user"),
56 (posix1e.ACL_GROUP, "group"),
57 (posix1e.ACL_USER_OBJ, "user object"),
58 (posix1e.ACL_GROUP_OBJ, "group object"),
59 (posix1e.ACL_MASK, "mask"),
60 (posix1e.ACL_OTHER, "other"),
63 ALL_TAG_VALUES = [i[0] for i in ALL_TAGS]
64 ALL_TAG_DESCS = [i[1] for i in ALL_TAGS]
66 # Fixtures and helpers
68 def ignore_ioerror(errnum, fn, *args, **kwargs):
69 """Call a function while ignoring some IOErrors.
71 This is needed as some OSes (e.g. FreeBSD) return failure (EINVAL)
72 when doing certain operations on an invalid ACL.
77 except IOError as err:
78 if err.errno == errnum:
82 def assert_acl_eq(a, b):
85 assert str(a) == str(b)
89 """per-test temp dir based in TEST_DIR"""
90 with tempfile.TemporaryDirectory(dir=TEST_DIR) as dname:
94 fh, fname = tempfile.mkstemp(".test", "xattr-", path)
97 @contextlib.contextmanager
98 def get_file_name(path):
99 fh, fname = get_file(path)
103 @contextlib.contextmanager
104 def get_file_fd(path):
105 fd = get_file(path)[0]
109 @contextlib.contextmanager
110 def get_file_object(path):
111 fd = get_file(path)[0]
112 with os.fdopen(fd) as f:
115 @contextlib.contextmanager
117 yield tempfile.mkdtemp(".test", "xattr-", path)
119 def get_symlink(path, dangling=True):
120 """create a symlink"""
121 fh, fname = get_file(path)
125 sname = fname + ".symlink"
126 os.symlink(fname, sname)
129 @contextlib.contextmanager
130 def get_valid_symlink(path):
131 yield get_symlink(path, dangling=False)[1]
133 @contextlib.contextmanager
134 def get_dangling_symlink(path):
135 yield get_symlink(path, dangling=True)[1]
137 @contextlib.contextmanager
138 def get_file_and_symlink(path):
139 yield get_symlink(path, dangling=False)
141 @contextlib.contextmanager
142 def get_file_and_fobject(path):
143 fh, fname = get_file(path)
144 with os.fdopen(fh) as fo:
147 # Wrappers that build upon existing values
149 def as_wrapper(call, fn, closer=None):
150 @contextlib.contextmanager
152 with call(path) as r:
155 if closer is not None:
160 return as_wrapper(call, lambda r: r.encode())
163 return as_wrapper(call, pathlib.PurePath)
165 def as_iostream(call):
166 opener = lambda f: io.open(f, "r")
167 closer = lambda r: r.close()
168 return as_wrapper(call, opener, closer)
170 NOT_BEFORE_36 = pytest.mark.xfail(condition="sys.version_info < (3,6)",
172 NOT_PYPY = pytest.mark.xfail(condition="platform.python_implementation() == 'PyPy'",
175 require_acl_from_mode = pytest.mark.skipif("not HAS_ACL_FROM_MODE")
176 require_acl_check = pytest.mark.skipif("not HAS_ACL_CHECK")
177 require_acl_entry = pytest.mark.skipif("not HAS_ACL_ENTRY")
178 require_extended_check = pytest.mark.skipif("not HAS_EXTENDED_CHECK")
179 require_equiv_mode = pytest.mark.skipif("not HAS_EQUIV_MODE")
180 require_copy_ext = pytest.mark.skipif("not HAS_COPY_EXT")
182 # Note: ACLs are valid only for files/directories, not symbolic links
183 # themselves, so we only create valid symlinks.
186 as_bytes(get_file_name),
187 pytest.param(as_fspath(get_file_name),
188 marks=[NOT_BEFORE_36, NOT_PYPY]),
191 pytest.param(as_fspath(get_dir),
192 marks=[NOT_BEFORE_36, NOT_PYPY]),
194 as_bytes(get_valid_symlink),
195 pytest.param(as_fspath(get_valid_symlink),
196 marks=[NOT_BEFORE_36, NOT_PYPY]),
207 "file via symlink (bytes)",
208 "file via symlink (path)",
214 as_iostream(get_file_name),
223 ALL_P = FILE_P + FD_P
224 ALL_D = FILE_D + FD_D
226 @pytest.fixture(params=FILE_P, ids=FILE_D)
227 def file_subject(testdir, request):
228 with request.param(testdir) as value:
231 @pytest.fixture(params=FD_P, ids=FD_D)
232 def fd_subject(testdir, request):
233 with request.param(testdir) as value:
236 @pytest.fixture(params=ALL_P, ids=ALL_D)
237 def subject(testdir, request):
238 with request.param(testdir) as value:
243 """Load/create tests"""
244 def test_from_file(self, file_subject):
245 """Test loading ACLs from a file/directory"""
246 acl = posix1e.ACL(file=file_subject)
249 def test_from_dir(self, testdir):
250 """Test loading ACLs from a directory"""
251 with get_dir(testdir) as dname:
252 acl2 = posix1e.ACL(filedef=dname)
253 # default ACLs might or might not be valid; missing ones are
254 # not valid, so we don't test acl2 for validity
256 def test_from_fd(self, fd_subject):
257 """Test loading ACLs from a file descriptor"""
258 acl = posix1e.ACL(fd=fd_subject)
261 def test_from_nonexisting(self, testdir):
262 _, fname = get_file(testdir)
263 with pytest.raises(IOError):
264 posix1e.ACL(file="fname"+".no-such-file")
265 with pytest.raises(IOError):
266 posix1e.ACL(filedef="fname"+".no-such-file")
268 def test_from_invalid_fd(self, testdir):
269 fd, _ = get_file(testdir)
271 with pytest.raises(IOError):
274 def test_from_empty_invalid(self):
275 """Test creating an empty ACL"""
277 assert not acl1.valid()
279 def test_from_text(self):
280 """Test creating an ACL from text"""
281 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
284 # This is acl_check, but should actually be have_linux...
286 def test_from_acl(self):
287 """Test creating an ACL from an existing ACL"""
288 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
289 acl2 = posix1e.ACL(acl=acl1)
292 def test_from_acl_via_str(self):
293 # This is needed for not HAVE_LINUX cases.
294 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
295 acl2 = posix1e.ACL(acl=acl1)
296 assert str(acl1) == str(acl2)
298 def test_invalid_creation_params(self, testdir):
299 """Test that creating an ACL from multiple objects fails"""
300 fd, _ = get_file(testdir)
301 with pytest.raises(ValueError):
302 posix1e.ACL(text=BASIC_ACL_TEXT, fd=fd)
304 def test_invalid_value_creation(self):
305 """Test that creating an ACL from wrong specification fails"""
306 with pytest.raises(EnvironmentError):
307 posix1e.ACL(text="foobar")
308 with pytest.raises(TypeError):
309 posix1e.ACL(foo="bar")
311 def test_uninit(self):
312 """Checks that uninit is actually empty init"""
313 acl = posix1e.ACL.__new__(posix1e.ACL)
314 assert not acl.valid()
319 def test_double_init(self):
320 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
322 acl1.__init__(text=BASIC_ACL_TEXT) # type: ignore
324 acl2 = ACL(text=TEXT_0755)
326 acl1.__init__(acl=acl2) # type: ignore
327 assert_acl_eq(acl1, acl2)
329 def test_reinit_failure_noop(self):
330 a = posix1e.ACL(text=TEXT_0755)
331 b = posix1e.ACL(acl=a)
333 with pytest.raises(IOError):
334 a.__init__(text='foobar')
337 @pytest.mark.xfail(reason="Unreliable test, re-init doesn't always invalidate children")
338 def test_double_init_breaks_children(self):
341 e.permset.write = True
342 acl.__init__() # type: ignore
343 with pytest.raises(EnvironmentError):
344 e.permset.write = False
347 class TestAclExtensions:
348 """ACL extensions checks"""
350 @require_acl_from_mode
351 def test_from_mode(self):
352 """Test loading ACLs from an octal mode"""
353 acl1 = posix1e.ACL(mode=0o644)
357 def test_acl_check(self):
358 """Test the acl_check method"""
359 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
360 assert not acl1.check()
363 assert c == (ACL_MISS_ERROR, 0)
364 assert isinstance(c, tuple)
365 assert c[0] == ACL_MISS_ERROR
368 assert c == (ACL_ENTRY_ERROR, 0)
370 def test_applyto(self, subject):
371 """Test the apply_to function"""
372 # TODO: add read/compare with before, once ACL can be init'ed
374 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
375 basic_acl.applyto(subject)
376 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
377 assert enhanced_acl.valid()
378 enhanced_acl.applyto(subject)
380 def test_apply_to_with_wrong_object(self):
381 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
383 with pytest.raises(TypeError):
384 acl1.applyto(object())
385 with pytest.raises(TypeError):
386 acl1.applyto(object(), object()) # type: ignore
388 def test_apply_to_fail(self, testdir):
389 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
391 fd, fname = get_file(testdir)
393 with pytest.raises(IOError):
395 with pytest.raises(IOError, match="no-such-file"):
396 acl1.applyto(fname+".no-such-file")
398 @require_extended_check
399 def test_applyto_extended(self, subject):
400 """Test the acl_extended function"""
401 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
402 basic_acl.applyto(subject)
403 assert not has_extended(subject)
404 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
405 assert enhanced_acl.valid()
406 enhanced_acl.applyto(subject)
407 assert has_extended(subject)
409 @require_extended_check
410 @pytest.mark.parametrize(
411 "gen", [ get_file_and_symlink, get_file_and_fobject ])
412 def test_applyto_extended_mixed(self, testdir, gen):
413 """Test the acl_extended function"""
414 with gen(testdir) as (a, b):
415 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
418 assert not has_extended(item)
419 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
420 assert enhanced_acl.valid()
421 enhanced_acl.applyto(b)
423 assert has_extended(item)
425 @require_extended_check
426 def test_extended_fail(self, testdir):
427 fd, fname = get_file(testdir)
429 with pytest.raises(IOError):
431 with pytest.raises(IOError, match="no-such-file"):
432 has_extended(fname+".no-such-file")
434 @require_extended_check
435 def test_extended_arg_handling(self):
436 with pytest.raises(TypeError):
437 has_extended() # type: ignore
438 with pytest.raises(TypeError):
439 has_extended(object()) # type: ignore
442 def test_equiv_mode(self):
443 """Test the equiv_mode function"""
444 if HAS_ACL_FROM_MODE:
445 for mode in 0o644, 0o755:
446 acl = posix1e.ACL(mode=mode)
447 assert acl.equiv_mode() == mode
448 acl = posix1e.ACL(text="u::rw,g::r,o::r")
449 assert acl.equiv_mode() == 0o644
450 acl = posix1e.ACL(text="u::rx,g::-,o::-")
451 assert acl.equiv_mode() == 0o500
454 @pytest.mark.xfail(reason="It seems equiv mode always passes, even for empty ACLs")
455 def test_equiv_mode_invalid(self):
456 """Test equiv_mode on invalid ACLs"""
458 with pytest.raises(EnvironmentError):
462 def test_to_any_text(self):
463 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
465 acl.to_any_text(options=posix1e.TEXT_ABBREVIATE)
466 assert b"user::" in acl.to_any_text()
469 def test_to_any_text_wrong_args(self):
470 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
471 with pytest.raises(TypeError):
472 acl.to_any_text(foo="bar") # type: ignore
476 def test_rich_compare(self):
477 acl1 = posix1e.ACL(text="u::rw,g::r,o::r")
478 acl2 = posix1e.ACL(acl=acl1)
479 acl3 = posix1e.ACL(text="u::rw,g::rw,o::r")
482 with pytest.raises(TypeError):
483 acl1 < acl2 # type: ignore
484 with pytest.raises(TypeError):
485 acl1 >= acl3 # type: ignore
486 assert acl1 != True # type: ignore
487 assert not (acl1 == 1) # type: ignore
488 with pytest.raises(TypeError):
489 acl1 > True # type: ignore
492 def test_acl_iterator(self):
493 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
495 assert entry.parent is acl
498 def test_acl_copy_ext(self):
499 a = posix1e.ACL(text=BASIC_ACL_TEXT)
501 c = posix1e.ACL(acl=b)
504 state = a.__getstate__()
505 b.__setstate__(state)
510 def test_acl_copy_ext_args(self):
512 with pytest.raises(TypeError):
516 def test_acl_init_copy_ext(self):
517 a = posix1e.ACL(text=BASIC_ACL_TEXT)
519 c = posix1e.ACL(data=a.__getstate__())
524 def test_acl_init_copy_ext_invalid(self):
525 with pytest.raises(IOError):
526 posix1e.ACL(data=b"foobar")
532 def test_delete_default(self, testdir):
533 """Test removing the default ACL"""
534 with get_dir(testdir) as dname:
535 posix1e.delete_default(dname)
537 def test_delete_default_fail(self, testdir):
538 """Test removing the default ACL"""
539 with get_file_name(testdir) as fname:
540 with pytest.raises(IOError, match="no-such-file"):
541 posix1e.delete_default(fname+".no-such-file")
544 def test_delete_default_wrong_arg(self):
545 with pytest.raises(TypeError):
546 posix1e.delete_default(object()) # type: ignore
548 def test_reapply(self, testdir):
549 """Test re-applying an ACL"""
550 fd, fname = get_file(testdir)
551 acl1 = posix1e.ACL(fd=fd)
554 with get_dir(testdir) as dname:
555 acl2 = posix1e.ACL(file=fname)
561 class TestModification:
562 """ACL modification tests"""
564 def checkRef(self, obj):
565 """Checks if a given obj has a 'sane' refcount"""
566 if platform.python_implementation() == "PyPy":
568 ref_cnt = sys.getrefcount(obj)
569 # FIXME: hardcoded value for the max ref count... but I've
570 # seen it overflow on bad reference counting, so it's better
572 if ref_cnt < 2 or ref_cnt > 1024:
573 pytest.fail("Wrong reference count, expected 2-1024 and got %d" %
577 """Test str() of an ACL."""
578 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
580 self.checkRef(str_acl)
582 def test_append(self):
583 """Test append a new Entry to the ACL"""
586 e.tag_type = posix1e.ACL_OTHER
587 ignore_ioerror(errno.EINVAL, acl.calc_mask)
589 self.checkRef(str_format)
591 ignore_ioerror(errno.EINVAL, acl.calc_mask)
592 assert not acl.valid()
594 def test_wrong_append(self):
595 """Test append a new Entry to the ACL based on wrong object type"""
597 with pytest.raises(TypeError):
598 acl.append(object()) # type: ignore
600 @pytest.mark.xfail(reason="Behaviour not conform to specification")
601 def test_append_invalid_source(self):
606 with pytest.raises(EnvironmentError):
607 f.permset.write = True
608 with pytest.raises(EnvironmentError):
611 def test_entry_creation(self):
613 e = posix1e.Entry(acl)
614 ignore_ioerror(errno.EINVAL, acl.calc_mask)
616 self.checkRef(str_format)
618 def test_entry_failed_creation(self):
619 # Checks for partial initialisation and deletion on error
621 with pytest.raises(TypeError):
622 posix1e.Entry(object()) # type: ignore
624 def test_entry_reinitialisations(self):
628 e.__init__(a) # type: ignore
629 with pytest.raises(ValueError, match="different parent"):
630 e.__init__(b) # type: ignore
633 def test_entry_reinit_leaks_refcount(self):
636 ref = sys.getrefcount(acl)
637 e.__init__(acl) # type: ignore
638 assert ref == sys.getrefcount(acl), "Uh-oh, ref leaks..."
640 def test_delete(self):
641 """Test delete Entry from the ACL"""
644 e.tag_type = posix1e.ACL_OTHER
645 ignore_ioerror(errno.EINVAL, acl.calc_mask)
647 ignore_ioerror(errno.EINVAL, acl.calc_mask)
649 def test_double_delete(self):
650 """Test delete Entry from the ACL"""
651 # This is not entirely valid/correct, since the entry object
652 # itself is invalid after the first deletion, so we're
653 # actually testing deleting an invalid object, not a
654 # non-existing entry...
657 e.tag_type = posix1e.ACL_OTHER
658 ignore_ioerror(errno.EINVAL, acl.calc_mask)
660 ignore_ioerror(errno.EINVAL, acl.calc_mask)
661 with pytest.raises(EnvironmentError):
664 def test_delete_unowned(self):
665 """Test delete Entry from the ACL"""
669 e.tag_type = posix1e.ACL_OTHER
670 with pytest.raises(ValueError, match="un-owned entry"):
673 # This currently fails as this deletion seems to be accepted :/
674 @pytest.mark.xfail(reason="Entry deletion is unreliable")
675 def testDeleteInvalidEntry(self):
676 """Test delete foreign Entry from the ACL"""
680 e.tag_type = posix1e.ACL_OTHER
681 ignore_ioerror(errno.EINVAL, acl1.calc_mask)
682 with pytest.raises(EnvironmentError):
685 def test_delete_invalid_object(self):
686 """Test delete a non-Entry from the ACL"""
688 with pytest.raises(TypeError):
689 acl.delete_entry(object()) # type: ignore
691 def test_double_entries(self):
692 """Test double entries"""
693 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
695 for tag_type in (posix1e.ACL_USER_OBJ, posix1e.ACL_GROUP_OBJ,
698 e.tag_type = tag_type
700 assert not acl.valid(), ("ACL containing duplicate entries"
701 " should not be valid")
704 def test_multiple_good_entries(self):
705 """Test multiple valid entries"""
706 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
708 for tag_type in (posix1e.ACL_USER,
710 for obj_id in range(5):
712 e.tag_type = tag_type
716 assert acl.valid(), ("ACL should be able to hold multiple"
717 " user/group entries")
719 def test_multiple_bad_entries(self):
720 """Test multiple invalid entries"""
721 for tag_type in (posix1e.ACL_USER,
723 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
726 e1.tag_type = tag_type
730 assert acl.valid(), ("ACL should be able to add a"
733 e2.tag_type = tag_type
736 ignore_ioerror(errno.EINVAL, acl.calc_mask)
737 assert not acl.valid(), ("ACL should not validate when"
738 " containing two duplicate entries")
740 # FreeBSD trips over itself here and can't delete the
741 # entry, even though it still exists.
742 ignore_ioerror(errno.EINVAL, acl.delete_entry, e2)
747 e1.tag_type = ACL_USER
753 e2.tag_type = ACL_GROUP
760 assert e1.tag_type == e2.tag_type
762 def test_copy_wrong_arg(self):
765 with pytest.raises(TypeError):
766 e.copy(object()) # type: ignore
768 def test_set_permset(self):
771 e1.tag_type = ACL_USER
777 e2.tag_type = ACL_GROUP
783 assert e2.permset.write
784 assert e2.tag_type == ACL_GROUP
786 def test_set_permset_wrong_arg(self):
789 with pytest.raises(TypeError):
790 e.permset = object() # type: ignore
792 def test_permset_creation(self):
799 def test_permset_creation_wrong_arg(self):
800 with pytest.raises(TypeError):
801 Permset(object()) # type: ignore
803 def test_permset_reinitialisations(self):
808 p.__init__(e) # type: ignore
809 with pytest.raises(ValueError, match="different parent"):
810 p.__init__(f) # type: ignore
813 def test_permset_reinit_leaks_refcount(self):
817 ref = sys.getrefcount(e)
818 p.__init__(e) # type: ignore
819 assert ref == sys.getrefcount(e), "Uh-oh, ref leaks..."
821 @pytest.mark.parametrize("perm, txt, accessor",
822 PERMSETS, ids=PERMSETS_IDS)
823 def test_permset(self, perm, txt, accessor):
824 """Test permissions"""
831 self.checkRef(str_ps)
832 assert not ps.test(perm), ("Empty permission set should not"
833 " have permission '%s'" % txt)
835 assert ps.test(perm), ("Permission '%s' should exist"
836 " after addition" % txt)
838 self.checkRef(str_ps)
840 assert not ps.test(perm), ("Permission '%s' should not exist"
841 " after deletion" % txt)
843 assert ps.test(perm), ("Permission '%s' should exist"
844 " after addition" % txt)
846 assert not ps.test(perm), ("Permission '%s' should not exist"
847 " after clearing" % txt)
851 @pytest.mark.parametrize("perm, txt, accessor",
852 PERMSETS, ids=PERMSETS_IDS)
853 def test_permset_via_accessors(self, perm, txt, accessor):
854 """Test permissions"""
860 return accessor.__get__(ps) # type: ignore
862 return accessor.__set__(ps, value) # type: ignore
864 self.checkRef(str_ps)
865 assert not getter(), ("Empty permission set should not"
866 " have permission '%s'" % txt)
868 assert ps.test(perm), ("Permission '%s' should exist"
869 " after addition" % txt)
870 assert getter(), ("Permission '%s' should exist"
871 " after addition" % txt)
873 self.checkRef(str_ps)
875 assert not ps.test(perm), ("Permission '%s' should not exist"
876 " after deletion" % txt)
877 assert not getter(), ("Permission '%s' should not exist"
878 " after deletion" % txt)
884 def test_permset_invalid_type(self):
889 with pytest.raises(TypeError):
890 ps.add("foobar") # type: ignore
891 with pytest.raises(TypeError):
892 ps.delete("foobar") # type: ignore
893 with pytest.raises(TypeError):
894 ps.test("foobar") # type: ignore
895 with pytest.raises(ValueError):
896 ps.write = object() # type: ignore
898 @pytest.mark.parametrize("tag", [ACL_USER, ACL_GROUP],
899 ids=["ACL_USER", "ACL_GROUP"])
900 def test_qualifier_values(self, tag):
901 """Tests qualifier correct store/retrieval"""
907 regex = re.compile("(user|group) with (u|g)id %d" % qualifier)
909 e.qualifier = qualifier
910 except OverflowError:
911 # reached overflow condition, break
913 assert e.qualifier == qualifier
914 assert regex.search(str(e)) is not None
917 def test_qualifier_overflow(self):
918 """Tests qualifier overflow handling"""
921 # the uid_t/gid_t are unsigned, so they can hold slightly more
922 # than sys.maxsize*2 (on Linux).
923 qualifier = (sys.maxsize + 1) * 2
924 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
926 with pytest.raises(OverflowError):
927 e.qualifier = qualifier
929 def test_qualifier_underflow(self):
930 """Tests negative qualifier handling"""
931 # Note: this presumes that uid_t/gid_t in C are unsigned...
934 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
936 for qualifier in [-10, -5, -1]:
937 with pytest.raises(OverflowError):
938 e.qualifier = qualifier
940 def test_invalid_qualifier(self):
941 """Tests invalid qualifier handling"""
944 with pytest.raises(TypeError):
945 e.qualifier = object() # type: ignore
946 with pytest.raises((TypeError, AttributeError)):
949 def test_qualifier_on_wrong_tag(self):
950 """Tests qualifier setting on wrong tag"""
953 e.tag_type = posix1e.ACL_OTHER
954 with pytest.raises(TypeError):
956 with pytest.raises(TypeError):
959 @pytest.mark.parametrize("tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
960 def test_tag_types(self, tag):
961 """Tests tag type correct set/get"""
965 assert e.tag_type == tag
966 # check we can show all tag types without breaking
969 @pytest.mark.parametrize("src_tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
970 @pytest.mark.parametrize("dst_tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
971 def test_tag_overwrite(self, src_tag, dst_tag):
972 """Tests tag type correct set/get"""
976 assert e.tag_type == src_tag
979 assert e.tag_type == dst_tag
982 def test_invalid_tags(self):
983 """Tests tag type incorrect set/get"""
986 with pytest.raises(TypeError):
987 e.tag_type = object() # type: ignore
988 e.tag_type = posix1e.ACL_USER_OBJ
989 # For some reason, PyPy raises AttributeError. Strange...
990 with pytest.raises((TypeError, AttributeError)):
993 def test_tag_wrong_overwrite(self):
996 e.tag_type = posix1e.ACL_USER_OBJ
997 tag = max(ALL_TAG_VALUES) + 1
998 with pytest.raises(EnvironmentError):
1000 # Check tag is still valid.
1001 assert e.tag_type == posix1e.ACL_USER_OBJ
1003 if __name__ == "__main__":