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::-"
44 # Permset permission information
46 (ACL_READ, "read", Permset.read),
47 (ACL_WRITE, "write", Permset.write),
48 (ACL_EXECUTE, "execute", Permset.execute),
51 PERMSETS_IDS = [p[1] for p in PERMSETS]
54 (posix1e.ACL_USER, "user"),
55 (posix1e.ACL_GROUP, "group"),
56 (posix1e.ACL_USER_OBJ, "user object"),
57 (posix1e.ACL_GROUP_OBJ, "group object"),
58 (posix1e.ACL_MASK, "mask"),
59 (posix1e.ACL_OTHER, "other"),
62 ALL_TAG_VALUES = [i[0] for i in ALL_TAGS]
63 ALL_TAG_DESCS = [i[1] for i in ALL_TAGS]
65 # Fixtures and helpers
67 def ignore_ioerror(errnum, fn, *args, **kwargs):
68 """Call a function while ignoring some IOErrors.
70 This is needed as some OSes (e.g. FreeBSD) return failure (EINVAL)
71 when doing certain operations on an invalid ACL.
76 except IOError as err:
77 if err.errno == errnum:
83 """per-test temp dir based in TEST_DIR"""
84 with tempfile.TemporaryDirectory(dir=TEST_DIR) as dname:
88 fh, fname = tempfile.mkstemp(".test", "xattr-", path)
91 @contextlib.contextmanager
92 def get_file_name(path):
93 fh, fname = get_file(path)
97 @contextlib.contextmanager
98 def get_file_fd(path):
99 fd = get_file(path)[0]
103 @contextlib.contextmanager
104 def get_file_object(path):
105 fd = get_file(path)[0]
106 with os.fdopen(fd) as f:
109 @contextlib.contextmanager
111 yield tempfile.mkdtemp(".test", "xattr-", path)
113 def get_symlink(path, dangling=True):
114 """create a symlink"""
115 fh, fname = get_file(path)
119 sname = fname + ".symlink"
120 os.symlink(fname, sname)
123 @contextlib.contextmanager
124 def get_valid_symlink(path):
125 yield get_symlink(path, dangling=False)[1]
127 @contextlib.contextmanager
128 def get_dangling_symlink(path):
129 yield get_symlink(path, dangling=True)[1]
131 @contextlib.contextmanager
132 def get_file_and_symlink(path):
133 yield get_symlink(path, dangling=False)
135 @contextlib.contextmanager
136 def get_file_and_fobject(path):
137 fh, fname = get_file(path)
138 with os.fdopen(fh) as fo:
141 # Wrappers that build upon existing values
143 def as_wrapper(call, fn, closer=None):
144 @contextlib.contextmanager
146 with call(path) as r:
149 if closer is not None:
154 return as_wrapper(call, lambda r: r.encode())
157 return as_wrapper(call, pathlib.PurePath)
159 def as_iostream(call):
160 opener = lambda f: io.open(f, "r")
161 closer = lambda r: r.close()
162 return as_wrapper(call, opener, closer)
164 NOT_BEFORE_36 = pytest.mark.xfail(condition="sys.version_info < (3,6)",
166 NOT_PYPY = pytest.mark.xfail(condition="platform.python_implementation() == 'PyPy'",
169 require_acl_from_mode = pytest.mark.skipif("not HAS_ACL_FROM_MODE")
170 require_acl_check = pytest.mark.skipif("not HAS_ACL_CHECK")
171 require_acl_entry = pytest.mark.skipif("not HAS_ACL_ENTRY")
172 require_extended_check = pytest.mark.skipif("not HAS_EXTENDED_CHECK")
173 require_equiv_mode = pytest.mark.skipif("not HAS_EQUIV_MODE")
174 require_copy_ext = pytest.mark.skipif("not HAS_COPY_EXT")
176 # Note: ACLs are valid only for files/directories, not symbolic links
177 # themselves, so we only create valid symlinks.
180 as_bytes(get_file_name),
181 pytest.param(as_fspath(get_file_name),
182 marks=[NOT_BEFORE_36, NOT_PYPY]),
185 pytest.param(as_fspath(get_dir),
186 marks=[NOT_BEFORE_36, NOT_PYPY]),
188 as_bytes(get_valid_symlink),
189 pytest.param(as_fspath(get_valid_symlink),
190 marks=[NOT_BEFORE_36, NOT_PYPY]),
201 "file via symlink (bytes)",
202 "file via symlink (path)",
208 as_iostream(get_file_name),
217 ALL_P = FILE_P + FD_P
218 ALL_D = FILE_D + FD_D
220 @pytest.fixture(params=FILE_P, ids=FILE_D)
221 def file_subject(testdir, request):
222 with request.param(testdir) as value:
225 @pytest.fixture(params=FD_P, ids=FD_D)
226 def fd_subject(testdir, request):
227 with request.param(testdir) as value:
230 @pytest.fixture(params=ALL_P, ids=ALL_D)
231 def subject(testdir, request):
232 with request.param(testdir) as value:
237 """Load/create tests"""
238 def test_from_file(self, file_subject):
239 """Test loading ACLs from a file/directory"""
240 acl = posix1e.ACL(file=file_subject)
243 def test_from_dir(self, testdir):
244 """Test loading ACLs from a directory"""
245 with get_dir(testdir) as dname:
246 acl2 = posix1e.ACL(filedef=dname)
247 # default ACLs might or might not be valid; missing ones are
248 # not valid, so we don't test acl2 for validity
250 def test_from_fd(self, fd_subject):
251 """Test loading ACLs from a file descriptor"""
252 acl = posix1e.ACL(fd=fd_subject)
255 def test_from_nonexisting(self, testdir):
256 _, fname = get_file(testdir)
257 with pytest.raises(IOError):
258 posix1e.ACL(file="fname"+".no-such-file")
260 def test_from_invalid_fd(self, testdir):
261 fd, _ = get_file(testdir)
263 with pytest.raises(IOError):
266 def test_from_empty_invalid(self):
267 """Test creating an empty ACL"""
269 assert not acl1.valid()
271 def test_from_text(self):
272 """Test creating an ACL from text"""
273 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
276 # This is acl_check, but should actually be have_linux...
278 def test_from_acl(self):
279 """Test creating an ACL from an existing ACL"""
280 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
281 acl2 = posix1e.ACL(acl=acl1)
284 def test_from_acl_via_str(self):
285 # This is needed for not HAVE_LINUX cases.
286 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
287 acl2 = posix1e.ACL(acl=acl1)
288 assert str(acl1) == str(acl2)
290 def test_invalid_creation_params(self, testdir):
291 """Test that creating an ACL from multiple objects fails"""
292 fd, _ = get_file(testdir)
293 with pytest.raises(ValueError):
294 posix1e.ACL(text=BASIC_ACL_TEXT, fd=fd)
296 def test_invalid_value_creation(self):
297 """Test that creating an ACL from wrong specification fails"""
298 with pytest.raises(EnvironmentError):
299 posix1e.ACL(text="foobar")
300 with pytest.raises(TypeError):
301 posix1e.ACL(foo="bar")
303 def test_uninit(self):
304 """Checks that uninit is actually empty init"""
305 acl = posix1e.ACL.__new__(posix1e.ACL)
306 assert not acl.valid()
311 def test_double_init(self):
312 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
314 acl1.__init__(text=BASIC_ACL_TEXT) # type: ignore
316 acl2 = ACL(mode=0o755)
318 acl1.__init__(acl=acl2) # type: ignore
321 def test_entry_reinit_failure_noop(self):
322 a = posix1e.ACL(mode=0o0755)
323 b = posix1e.ACL(acl=a)
325 with pytest.raises(IOError):
326 a.__init__(text='foobar')
329 @pytest.mark.xfail(reason="Unreliable test, re-init doesn't always invalidate children")
330 def test_double_init_breaks_children(self):
333 e.permset.write = True
334 acl.__init__() # type: ignore
335 with pytest.raises(EnvironmentError):
336 e.permset.write = False
339 class TestAclExtensions:
340 """ACL extensions checks"""
342 @require_acl_from_mode
343 def test_from_mode(self):
344 """Test loading ACLs from an octal mode"""
345 acl1 = posix1e.ACL(mode=0o644)
349 def test_acl_check(self):
350 """Test the acl_check method"""
351 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
352 assert not acl1.check()
355 assert c == (ACL_MISS_ERROR, 0)
356 assert isinstance(c, tuple)
357 assert c[0] == ACL_MISS_ERROR
360 assert c == (ACL_ENTRY_ERROR, 0)
362 def test_applyto(self, subject):
363 """Test the apply_to function"""
364 # TODO: add read/compare with before, once ACL can be init'ed
366 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
367 basic_acl.applyto(subject)
368 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
369 assert enhanced_acl.valid()
370 enhanced_acl.applyto(subject)
372 def test_apply_to_with_wrong_object(self):
373 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
375 with pytest.raises(TypeError):
376 acl1.applyto(object())
377 with pytest.raises(TypeError):
378 acl1.applyto(object(), object()) # type: ignore
380 def test_apply_to_fail(self, testdir):
381 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
383 fd, fname = get_file(testdir)
385 with pytest.raises(IOError):
387 with pytest.raises(IOError, match="no-such-file"):
388 acl1.applyto(fname+".no-such-file")
390 @require_extended_check
391 def test_applyto_extended(self, subject):
392 """Test the acl_extended function"""
393 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
394 basic_acl.applyto(subject)
395 assert not has_extended(subject)
396 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
397 assert enhanced_acl.valid()
398 enhanced_acl.applyto(subject)
399 assert has_extended(subject)
401 @require_extended_check
402 @pytest.mark.parametrize(
403 "gen", [ get_file_and_symlink, get_file_and_fobject ])
404 def test_applyto_extended_mixed(self, testdir, gen):
405 """Test the acl_extended function"""
406 with gen(testdir) as (a, b):
407 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
410 assert not has_extended(item)
411 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
412 assert enhanced_acl.valid()
413 enhanced_acl.applyto(b)
415 assert has_extended(item)
417 @require_extended_check
418 def test_extended_fail(self, testdir):
419 fd, fname = get_file(testdir)
421 with pytest.raises(IOError):
423 with pytest.raises(IOError, match="no-such-file"):
424 has_extended(fname+".no-such-file")
426 @require_extended_check
427 def test_extended_arg_handling(self):
428 with pytest.raises(TypeError):
429 has_extended() # type: ignore
430 with pytest.raises(TypeError):
431 has_extended(object()) # type: ignore
434 def test_equiv_mode(self):
435 """Test the equiv_mode function"""
436 if HAS_ACL_FROM_MODE:
437 for mode in 0o644, 0o755:
438 acl = posix1e.ACL(mode=mode)
439 assert acl.equiv_mode() == mode
440 acl = posix1e.ACL(text="u::rw,g::r,o::r")
441 assert acl.equiv_mode() == 0o644
442 acl = posix1e.ACL(text="u::rx,g::-,o::-")
443 assert acl.equiv_mode() == 0o500
446 @pytest.mark.xfail(reason="It seems equiv mode always passes, even for empty ACLs")
447 def test_equiv_mode_invalid(self):
448 """Test equiv_mode on invalid ACLs"""
450 with pytest.raises(EnvironmentError):
454 def test_to_any_text(self):
455 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
457 acl.to_any_text(options=posix1e.TEXT_ABBREVIATE)
458 assert b"user::" in acl.to_any_text()
461 def test_to_any_text_wrong_args(self):
462 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
463 with pytest.raises(TypeError):
464 acl.to_any_text(foo="bar") # type: ignore
468 def test_rich_compare(self):
469 acl1 = posix1e.ACL(text="u::rw,g::r,o::r")
470 acl2 = posix1e.ACL(acl=acl1)
471 acl3 = posix1e.ACL(text="u::rw,g::rw,o::r")
474 with pytest.raises(TypeError):
475 acl1 < acl2 # type: ignore
476 with pytest.raises(TypeError):
477 acl1 >= acl3 # type: ignore
478 assert acl1 != True # type: ignore
479 assert not (acl1 == 1) # type: ignore
480 with pytest.raises(TypeError):
481 acl1 > True # type: ignore
484 def test_acl_iterator(self):
485 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
487 assert entry.parent is acl
490 def test_acl_copy_ext(self):
491 a = posix1e.ACL(text=BASIC_ACL_TEXT)
493 c = posix1e.ACL(acl=b)
496 state = a.__getstate__()
497 b.__setstate__(state)
502 def test_acl_copy_ext_args(self):
504 with pytest.raises(TypeError):
508 def test_acl_init_copy_ext(self):
509 a = posix1e.ACL(text=BASIC_ACL_TEXT)
511 c = posix1e.ACL(data=a.__getstate__())
516 def test_acl_init_copy_ext_invalid(self):
517 with pytest.raises(IOError):
518 posix1e.ACL(data=b"foobar")
524 def test_delete_default(self, testdir):
525 """Test removing the default ACL"""
526 with get_dir(testdir) as dname:
527 posix1e.delete_default(dname)
529 def test_delete_default_fail(self, testdir):
530 """Test removing the default ACL"""
531 with get_file_name(testdir) as fname:
532 with pytest.raises(IOError, match="no-such-file"):
533 posix1e.delete_default(fname+".no-such-file")
536 def test_delete_default_wrong_arg(self):
537 with pytest.raises(TypeError):
538 posix1e.delete_default(object()) # type: ignore
540 def test_reapply(self, testdir):
541 """Test re-applying an ACL"""
542 fd, fname = get_file(testdir)
543 acl1 = posix1e.ACL(fd=fd)
546 with get_dir(testdir) as dname:
547 acl2 = posix1e.ACL(file=fname)
553 class TestModification:
554 """ACL modification tests"""
556 def checkRef(self, obj):
557 """Checks if a given obj has a 'sane' refcount"""
558 if platform.python_implementation() == "PyPy":
560 ref_cnt = sys.getrefcount(obj)
561 # FIXME: hardcoded value for the max ref count... but I've
562 # seen it overflow on bad reference counting, so it's better
564 if ref_cnt < 2 or ref_cnt > 1024:
565 pytest.fail("Wrong reference count, expected 2-1024 and got %d" %
569 """Test str() of an ACL."""
570 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
572 self.checkRef(str_acl)
574 def test_append(self):
575 """Test append a new Entry to the ACL"""
578 e.tag_type = posix1e.ACL_OTHER
579 ignore_ioerror(errno.EINVAL, acl.calc_mask)
581 self.checkRef(str_format)
583 ignore_ioerror(errno.EINVAL, acl.calc_mask)
584 assert not acl.valid()
586 def test_wrong_append(self):
587 """Test append a new Entry to the ACL based on wrong object type"""
589 with pytest.raises(TypeError):
590 acl.append(object()) # type: ignore
592 @pytest.mark.xfail(reason="Behaviour not conform to specification")
593 def test_append_invalid_source(self):
598 with pytest.raises(EnvironmentError):
599 f.permset.write = True
600 with pytest.raises(EnvironmentError):
603 def test_entry_creation(self):
605 e = posix1e.Entry(acl)
606 ignore_ioerror(errno.EINVAL, acl.calc_mask)
608 self.checkRef(str_format)
610 def test_entry_failed_creation(self):
611 # Checks for partial initialisation and deletion on error
613 with pytest.raises(TypeError):
614 posix1e.Entry(object()) # type: ignore
616 def test_entry_reinitialisations(self):
620 e.__init__(a) # type: ignore
621 with pytest.raises(ValueError, match="different parent"):
622 e.__init__(b) # type: ignore
625 def test_entry_reinit_leaks_refcount(self):
628 ref = sys.getrefcount(acl)
629 e.__init__(acl) # type: ignore
630 assert ref == sys.getrefcount(acl), "Uh-oh, ref leaks..."
632 def test_delete(self):
633 """Test delete Entry from the ACL"""
636 e.tag_type = posix1e.ACL_OTHER
637 ignore_ioerror(errno.EINVAL, acl.calc_mask)
639 ignore_ioerror(errno.EINVAL, acl.calc_mask)
641 def test_double_delete(self):
642 """Test delete Entry from the ACL"""
643 # This is not entirely valid/correct, since the entry object
644 # itself is invalid after the first deletion, so we're
645 # actually testing deleting an invalid object, not a
646 # non-existing entry...
649 e.tag_type = posix1e.ACL_OTHER
650 ignore_ioerror(errno.EINVAL, acl.calc_mask)
652 ignore_ioerror(errno.EINVAL, acl.calc_mask)
653 with pytest.raises(EnvironmentError):
656 def test_delete_unowned(self):
657 """Test delete Entry from the ACL"""
661 e.tag_type = posix1e.ACL_OTHER
662 with pytest.raises(ValueError, match="un-owned entry"):
665 # This currently fails as this deletion seems to be accepted :/
666 @pytest.mark.xfail(reason="Entry deletion is unreliable")
667 def testDeleteInvalidEntry(self):
668 """Test delete foreign Entry from the ACL"""
672 e.tag_type = posix1e.ACL_OTHER
673 ignore_ioerror(errno.EINVAL, acl1.calc_mask)
674 with pytest.raises(EnvironmentError):
677 def test_delete_invalid_object(self):
678 """Test delete a non-Entry from the ACL"""
680 with pytest.raises(TypeError):
681 acl.delete_entry(object()) # type: ignore
683 def test_double_entries(self):
684 """Test double entries"""
685 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
687 for tag_type in (posix1e.ACL_USER_OBJ, posix1e.ACL_GROUP_OBJ,
690 e.tag_type = tag_type
692 assert not acl.valid(), ("ACL containing duplicate entries"
693 " should not be valid")
696 def test_multiple_good_entries(self):
697 """Test multiple valid entries"""
698 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
700 for tag_type in (posix1e.ACL_USER,
702 for obj_id in range(5):
704 e.tag_type = tag_type
708 assert acl.valid(), ("ACL should be able to hold multiple"
709 " user/group entries")
711 def test_multiple_bad_entries(self):
712 """Test multiple invalid entries"""
713 for tag_type in (posix1e.ACL_USER,
715 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
718 e1.tag_type = tag_type
722 assert acl.valid(), ("ACL should be able to add a"
725 e2.tag_type = tag_type
728 ignore_ioerror(errno.EINVAL, acl.calc_mask)
729 assert not acl.valid(), ("ACL should not validate when"
730 " containing two duplicate entries")
732 # FreeBSD trips over itself here and can't delete the
733 # entry, even though it still exists.
734 ignore_ioerror(errno.EINVAL, acl.delete_entry, e2)
739 e1.tag_type = ACL_USER
745 e2.tag_type = ACL_GROUP
752 assert e1.tag_type == e2.tag_type
754 def test_copy_wrong_arg(self):
757 with pytest.raises(TypeError):
758 e.copy(object()) # type: ignore
760 def test_set_permset(self):
763 e1.tag_type = ACL_USER
769 e2.tag_type = ACL_GROUP
775 assert e2.permset.write
776 assert e2.tag_type == ACL_GROUP
778 def test_set_permset_wrong_arg(self):
781 with pytest.raises(TypeError):
782 e.permset = object() # type: ignore
784 def test_permset_creation(self):
791 def test_permset_creation_wrong_arg(self):
792 with pytest.raises(TypeError):
793 Permset(object()) # type: ignore
795 def test_permset_reinitialisations(self):
800 p.__init__(e) # type: ignore
801 with pytest.raises(ValueError, match="different parent"):
802 p.__init__(f) # type: ignore
805 def test_permset_reinit_leaks_refcount(self):
809 ref = sys.getrefcount(e)
810 p.__init__(e) # type: ignore
811 assert ref == sys.getrefcount(e), "Uh-oh, ref leaks..."
813 @pytest.mark.parametrize("perm, txt, accessor",
814 PERMSETS, ids=PERMSETS_IDS)
815 def test_permset(self, perm, txt, accessor):
816 """Test permissions"""
823 self.checkRef(str_ps)
824 assert not ps.test(perm), ("Empty permission set should not"
825 " have permission '%s'" % txt)
827 assert ps.test(perm), ("Permission '%s' should exist"
828 " after addition" % txt)
830 self.checkRef(str_ps)
832 assert not ps.test(perm), ("Permission '%s' should not exist"
833 " after deletion" % txt)
835 assert ps.test(perm), ("Permission '%s' should exist"
836 " after addition" % txt)
838 assert not ps.test(perm), ("Permission '%s' should not exist"
839 " after clearing" % txt)
843 @pytest.mark.parametrize("perm, txt, accessor",
844 PERMSETS, ids=PERMSETS_IDS)
845 def test_permset_via_accessors(self, perm, txt, accessor):
846 """Test permissions"""
852 return accessor.__get__(ps) # type: ignore
854 return accessor.__set__(ps, value) # type: ignore
856 self.checkRef(str_ps)
857 assert not getter(), ("Empty permission set should not"
858 " have permission '%s'" % txt)
860 assert ps.test(perm), ("Permission '%s' should exist"
861 " after addition" % txt)
862 assert getter(), ("Permission '%s' should exist"
863 " after addition" % txt)
865 self.checkRef(str_ps)
867 assert not ps.test(perm), ("Permission '%s' should not exist"
868 " after deletion" % txt)
869 assert not getter(), ("Permission '%s' should not exist"
870 " after deletion" % txt)
876 def test_permset_invalid_type(self):
881 with pytest.raises(TypeError):
882 ps.add("foobar") # type: ignore
883 with pytest.raises(TypeError):
884 ps.delete("foobar") # type: ignore
885 with pytest.raises(TypeError):
886 ps.test("foobar") # type: ignore
887 with pytest.raises(ValueError):
888 ps.write = object() # type: ignore
890 @pytest.mark.parametrize("tag", [ACL_USER, ACL_GROUP],
891 ids=["ACL_USER", "ACL_GROUP"])
892 def test_qualifier_values(self, tag):
893 """Tests qualifier correct store/retrieval"""
899 regex = re.compile("(user|group) with (u|g)id %d" % qualifier)
901 e.qualifier = qualifier
902 except OverflowError:
903 # reached overflow condition, break
905 assert e.qualifier == qualifier
906 assert regex.search(str(e)) is not None
909 def test_qualifier_overflow(self):
910 """Tests qualifier overflow handling"""
913 # the uid_t/gid_t are unsigned, so they can hold slightly more
914 # than sys.maxsize*2 (on Linux).
915 qualifier = (sys.maxsize + 1) * 2
916 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
918 with pytest.raises(OverflowError):
919 e.qualifier = qualifier
921 def test_qualifier_underflow(self):
922 """Tests negative qualifier handling"""
923 # Note: this presumes that uid_t/gid_t in C are unsigned...
926 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
928 for qualifier in [-10, -5, -1]:
929 with pytest.raises(OverflowError):
930 e.qualifier = qualifier
932 def test_invalid_qualifier(self):
933 """Tests invalid qualifier handling"""
936 with pytest.raises(TypeError):
937 e.qualifier = object() # type: ignore
938 with pytest.raises((TypeError, AttributeError)):
941 def test_qualifier_on_wrong_tag(self):
942 """Tests qualifier setting on wrong tag"""
945 e.tag_type = posix1e.ACL_OTHER
946 with pytest.raises(TypeError):
948 with pytest.raises(TypeError):
951 @pytest.mark.parametrize("tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
952 def test_tag_types(self, tag):
953 """Tests tag type correct set/get"""
957 assert e.tag_type == tag
958 # check we can show all tag types without breaking
961 @pytest.mark.parametrize("src_tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
962 @pytest.mark.parametrize("dst_tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
963 def test_tag_overwrite(self, src_tag, dst_tag):
964 """Tests tag type correct set/get"""
968 assert e.tag_type == src_tag
971 assert e.tag_type == dst_tag
974 def test_invalid_tags(self):
975 """Tests tag type incorrect set/get"""
978 with pytest.raises(TypeError):
979 e.tag_type = object() # type: ignore
980 e.tag_type = posix1e.ACL_USER_OBJ
981 # For some reason, PyPy raises AttributeError. Strange...
982 with pytest.raises((TypeError, AttributeError)):
985 def test_tag_wrong_overwrite(self):
988 e.tag_type = posix1e.ACL_USER_OBJ
989 tag = max(ALL_TAG_VALUES) + 1
990 with pytest.raises(EnvironmentError):
992 # Check tag is still valid.
993 assert e.tag_type == posix1e.ACL_USER_OBJ
995 if __name__ == "__main__":