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")
259 with pytest.raises(IOError):
260 posix1e.ACL(filedef="fname"+".no-such-file")
262 def test_from_invalid_fd(self, testdir):
263 fd, _ = get_file(testdir)
265 with pytest.raises(IOError):
268 def test_from_empty_invalid(self):
269 """Test creating an empty ACL"""
271 assert not acl1.valid()
273 def test_from_text(self):
274 """Test creating an ACL from text"""
275 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
278 # This is acl_check, but should actually be have_linux...
280 def test_from_acl(self):
281 """Test creating an ACL from an existing ACL"""
282 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
283 acl2 = posix1e.ACL(acl=acl1)
286 def test_from_acl_via_str(self):
287 # This is needed for not HAVE_LINUX cases.
288 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
289 acl2 = posix1e.ACL(acl=acl1)
290 assert str(acl1) == str(acl2)
292 def test_invalid_creation_params(self, testdir):
293 """Test that creating an ACL from multiple objects fails"""
294 fd, _ = get_file(testdir)
295 with pytest.raises(ValueError):
296 posix1e.ACL(text=BASIC_ACL_TEXT, fd=fd)
298 def test_invalid_value_creation(self):
299 """Test that creating an ACL from wrong specification fails"""
300 with pytest.raises(EnvironmentError):
301 posix1e.ACL(text="foobar")
302 with pytest.raises(TypeError):
303 posix1e.ACL(foo="bar")
305 def test_uninit(self):
306 """Checks that uninit is actually empty init"""
307 acl = posix1e.ACL.__new__(posix1e.ACL)
308 assert not acl.valid()
313 def test_double_init(self):
314 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
316 acl1.__init__(text=BASIC_ACL_TEXT) # type: ignore
318 acl2 = ACL(mode=0o755)
320 acl1.__init__(acl=acl2) # type: ignore
323 def test_entry_reinit_failure_noop(self):
324 a = posix1e.ACL(mode=0o0755)
325 b = posix1e.ACL(acl=a)
327 with pytest.raises(IOError):
328 a.__init__(text='foobar')
331 @pytest.mark.xfail(reason="Unreliable test, re-init doesn't always invalidate children")
332 def test_double_init_breaks_children(self):
335 e.permset.write = True
336 acl.__init__() # type: ignore
337 with pytest.raises(EnvironmentError):
338 e.permset.write = False
341 class TestAclExtensions:
342 """ACL extensions checks"""
344 @require_acl_from_mode
345 def test_from_mode(self):
346 """Test loading ACLs from an octal mode"""
347 acl1 = posix1e.ACL(mode=0o644)
351 def test_acl_check(self):
352 """Test the acl_check method"""
353 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
354 assert not acl1.check()
357 assert c == (ACL_MISS_ERROR, 0)
358 assert isinstance(c, tuple)
359 assert c[0] == ACL_MISS_ERROR
362 assert c == (ACL_ENTRY_ERROR, 0)
364 def test_applyto(self, subject):
365 """Test the apply_to function"""
366 # TODO: add read/compare with before, once ACL can be init'ed
368 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
369 basic_acl.applyto(subject)
370 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
371 assert enhanced_acl.valid()
372 enhanced_acl.applyto(subject)
374 def test_apply_to_with_wrong_object(self):
375 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
377 with pytest.raises(TypeError):
378 acl1.applyto(object())
379 with pytest.raises(TypeError):
380 acl1.applyto(object(), object()) # type: ignore
382 def test_apply_to_fail(self, testdir):
383 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
385 fd, fname = get_file(testdir)
387 with pytest.raises(IOError):
389 with pytest.raises(IOError, match="no-such-file"):
390 acl1.applyto(fname+".no-such-file")
392 @require_extended_check
393 def test_applyto_extended(self, subject):
394 """Test the acl_extended function"""
395 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
396 basic_acl.applyto(subject)
397 assert not has_extended(subject)
398 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
399 assert enhanced_acl.valid()
400 enhanced_acl.applyto(subject)
401 assert has_extended(subject)
403 @require_extended_check
404 @pytest.mark.parametrize(
405 "gen", [ get_file_and_symlink, get_file_and_fobject ])
406 def test_applyto_extended_mixed(self, testdir, gen):
407 """Test the acl_extended function"""
408 with gen(testdir) as (a, b):
409 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
412 assert not has_extended(item)
413 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
414 assert enhanced_acl.valid()
415 enhanced_acl.applyto(b)
417 assert has_extended(item)
419 @require_extended_check
420 def test_extended_fail(self, testdir):
421 fd, fname = get_file(testdir)
423 with pytest.raises(IOError):
425 with pytest.raises(IOError, match="no-such-file"):
426 has_extended(fname+".no-such-file")
428 @require_extended_check
429 def test_extended_arg_handling(self):
430 with pytest.raises(TypeError):
431 has_extended() # type: ignore
432 with pytest.raises(TypeError):
433 has_extended(object()) # type: ignore
436 def test_equiv_mode(self):
437 """Test the equiv_mode function"""
438 if HAS_ACL_FROM_MODE:
439 for mode in 0o644, 0o755:
440 acl = posix1e.ACL(mode=mode)
441 assert acl.equiv_mode() == mode
442 acl = posix1e.ACL(text="u::rw,g::r,o::r")
443 assert acl.equiv_mode() == 0o644
444 acl = posix1e.ACL(text="u::rx,g::-,o::-")
445 assert acl.equiv_mode() == 0o500
448 @pytest.mark.xfail(reason="It seems equiv mode always passes, even for empty ACLs")
449 def test_equiv_mode_invalid(self):
450 """Test equiv_mode on invalid ACLs"""
452 with pytest.raises(EnvironmentError):
456 def test_to_any_text(self):
457 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
459 acl.to_any_text(options=posix1e.TEXT_ABBREVIATE)
460 assert b"user::" in acl.to_any_text()
463 def test_to_any_text_wrong_args(self):
464 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
465 with pytest.raises(TypeError):
466 acl.to_any_text(foo="bar") # type: ignore
470 def test_rich_compare(self):
471 acl1 = posix1e.ACL(text="u::rw,g::r,o::r")
472 acl2 = posix1e.ACL(acl=acl1)
473 acl3 = posix1e.ACL(text="u::rw,g::rw,o::r")
476 with pytest.raises(TypeError):
477 acl1 < acl2 # type: ignore
478 with pytest.raises(TypeError):
479 acl1 >= acl3 # type: ignore
480 assert acl1 != True # type: ignore
481 assert not (acl1 == 1) # type: ignore
482 with pytest.raises(TypeError):
483 acl1 > True # type: ignore
486 def test_acl_iterator(self):
487 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
489 assert entry.parent is acl
492 def test_acl_copy_ext(self):
493 a = posix1e.ACL(text=BASIC_ACL_TEXT)
495 c = posix1e.ACL(acl=b)
498 state = a.__getstate__()
499 b.__setstate__(state)
504 def test_acl_copy_ext_args(self):
506 with pytest.raises(TypeError):
510 def test_acl_init_copy_ext(self):
511 a = posix1e.ACL(text=BASIC_ACL_TEXT)
513 c = posix1e.ACL(data=a.__getstate__())
518 def test_acl_init_copy_ext_invalid(self):
519 with pytest.raises(IOError):
520 posix1e.ACL(data=b"foobar")
526 def test_delete_default(self, testdir):
527 """Test removing the default ACL"""
528 with get_dir(testdir) as dname:
529 posix1e.delete_default(dname)
531 def test_delete_default_fail(self, testdir):
532 """Test removing the default ACL"""
533 with get_file_name(testdir) as fname:
534 with pytest.raises(IOError, match="no-such-file"):
535 posix1e.delete_default(fname+".no-such-file")
538 def test_delete_default_wrong_arg(self):
539 with pytest.raises(TypeError):
540 posix1e.delete_default(object()) # type: ignore
542 def test_reapply(self, testdir):
543 """Test re-applying an ACL"""
544 fd, fname = get_file(testdir)
545 acl1 = posix1e.ACL(fd=fd)
548 with get_dir(testdir) as dname:
549 acl2 = posix1e.ACL(file=fname)
555 class TestModification:
556 """ACL modification tests"""
558 def checkRef(self, obj):
559 """Checks if a given obj has a 'sane' refcount"""
560 if platform.python_implementation() == "PyPy":
562 ref_cnt = sys.getrefcount(obj)
563 # FIXME: hardcoded value for the max ref count... but I've
564 # seen it overflow on bad reference counting, so it's better
566 if ref_cnt < 2 or ref_cnt > 1024:
567 pytest.fail("Wrong reference count, expected 2-1024 and got %d" %
571 """Test str() of an ACL."""
572 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
574 self.checkRef(str_acl)
576 def test_append(self):
577 """Test append a new Entry to the ACL"""
580 e.tag_type = posix1e.ACL_OTHER
581 ignore_ioerror(errno.EINVAL, acl.calc_mask)
583 self.checkRef(str_format)
585 ignore_ioerror(errno.EINVAL, acl.calc_mask)
586 assert not acl.valid()
588 def test_wrong_append(self):
589 """Test append a new Entry to the ACL based on wrong object type"""
591 with pytest.raises(TypeError):
592 acl.append(object()) # type: ignore
594 @pytest.mark.xfail(reason="Behaviour not conform to specification")
595 def test_append_invalid_source(self):
600 with pytest.raises(EnvironmentError):
601 f.permset.write = True
602 with pytest.raises(EnvironmentError):
605 def test_entry_creation(self):
607 e = posix1e.Entry(acl)
608 ignore_ioerror(errno.EINVAL, acl.calc_mask)
610 self.checkRef(str_format)
612 def test_entry_failed_creation(self):
613 # Checks for partial initialisation and deletion on error
615 with pytest.raises(TypeError):
616 posix1e.Entry(object()) # type: ignore
618 def test_entry_reinitialisations(self):
622 e.__init__(a) # type: ignore
623 with pytest.raises(ValueError, match="different parent"):
624 e.__init__(b) # type: ignore
627 def test_entry_reinit_leaks_refcount(self):
630 ref = sys.getrefcount(acl)
631 e.__init__(acl) # type: ignore
632 assert ref == sys.getrefcount(acl), "Uh-oh, ref leaks..."
634 def test_delete(self):
635 """Test delete Entry from the ACL"""
638 e.tag_type = posix1e.ACL_OTHER
639 ignore_ioerror(errno.EINVAL, acl.calc_mask)
641 ignore_ioerror(errno.EINVAL, acl.calc_mask)
643 def test_double_delete(self):
644 """Test delete Entry from the ACL"""
645 # This is not entirely valid/correct, since the entry object
646 # itself is invalid after the first deletion, so we're
647 # actually testing deleting an invalid object, not a
648 # non-existing entry...
651 e.tag_type = posix1e.ACL_OTHER
652 ignore_ioerror(errno.EINVAL, acl.calc_mask)
654 ignore_ioerror(errno.EINVAL, acl.calc_mask)
655 with pytest.raises(EnvironmentError):
658 def test_delete_unowned(self):
659 """Test delete Entry from the ACL"""
663 e.tag_type = posix1e.ACL_OTHER
664 with pytest.raises(ValueError, match="un-owned entry"):
667 # This currently fails as this deletion seems to be accepted :/
668 @pytest.mark.xfail(reason="Entry deletion is unreliable")
669 def testDeleteInvalidEntry(self):
670 """Test delete foreign Entry from the ACL"""
674 e.tag_type = posix1e.ACL_OTHER
675 ignore_ioerror(errno.EINVAL, acl1.calc_mask)
676 with pytest.raises(EnvironmentError):
679 def test_delete_invalid_object(self):
680 """Test delete a non-Entry from the ACL"""
682 with pytest.raises(TypeError):
683 acl.delete_entry(object()) # type: ignore
685 def test_double_entries(self):
686 """Test double entries"""
687 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
689 for tag_type in (posix1e.ACL_USER_OBJ, posix1e.ACL_GROUP_OBJ,
692 e.tag_type = tag_type
694 assert not acl.valid(), ("ACL containing duplicate entries"
695 " should not be valid")
698 def test_multiple_good_entries(self):
699 """Test multiple valid entries"""
700 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
702 for tag_type in (posix1e.ACL_USER,
704 for obj_id in range(5):
706 e.tag_type = tag_type
710 assert acl.valid(), ("ACL should be able to hold multiple"
711 " user/group entries")
713 def test_multiple_bad_entries(self):
714 """Test multiple invalid entries"""
715 for tag_type in (posix1e.ACL_USER,
717 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
720 e1.tag_type = tag_type
724 assert acl.valid(), ("ACL should be able to add a"
727 e2.tag_type = tag_type
730 ignore_ioerror(errno.EINVAL, acl.calc_mask)
731 assert not acl.valid(), ("ACL should not validate when"
732 " containing two duplicate entries")
734 # FreeBSD trips over itself here and can't delete the
735 # entry, even though it still exists.
736 ignore_ioerror(errno.EINVAL, acl.delete_entry, e2)
741 e1.tag_type = ACL_USER
747 e2.tag_type = ACL_GROUP
754 assert e1.tag_type == e2.tag_type
756 def test_copy_wrong_arg(self):
759 with pytest.raises(TypeError):
760 e.copy(object()) # type: ignore
762 def test_set_permset(self):
765 e1.tag_type = ACL_USER
771 e2.tag_type = ACL_GROUP
777 assert e2.permset.write
778 assert e2.tag_type == ACL_GROUP
780 def test_set_permset_wrong_arg(self):
783 with pytest.raises(TypeError):
784 e.permset = object() # type: ignore
786 def test_permset_creation(self):
793 def test_permset_creation_wrong_arg(self):
794 with pytest.raises(TypeError):
795 Permset(object()) # type: ignore
797 def test_permset_reinitialisations(self):
802 p.__init__(e) # type: ignore
803 with pytest.raises(ValueError, match="different parent"):
804 p.__init__(f) # type: ignore
807 def test_permset_reinit_leaks_refcount(self):
811 ref = sys.getrefcount(e)
812 p.__init__(e) # type: ignore
813 assert ref == sys.getrefcount(e), "Uh-oh, ref leaks..."
815 @pytest.mark.parametrize("perm, txt, accessor",
816 PERMSETS, ids=PERMSETS_IDS)
817 def test_permset(self, perm, txt, accessor):
818 """Test permissions"""
825 self.checkRef(str_ps)
826 assert not ps.test(perm), ("Empty permission set should not"
827 " have permission '%s'" % txt)
829 assert ps.test(perm), ("Permission '%s' should exist"
830 " after addition" % txt)
832 self.checkRef(str_ps)
834 assert not ps.test(perm), ("Permission '%s' should not exist"
835 " after deletion" % txt)
837 assert ps.test(perm), ("Permission '%s' should exist"
838 " after addition" % txt)
840 assert not ps.test(perm), ("Permission '%s' should not exist"
841 " after clearing" % txt)
845 @pytest.mark.parametrize("perm, txt, accessor",
846 PERMSETS, ids=PERMSETS_IDS)
847 def test_permset_via_accessors(self, perm, txt, accessor):
848 """Test permissions"""
854 return accessor.__get__(ps) # type: ignore
856 return accessor.__set__(ps, value) # type: ignore
858 self.checkRef(str_ps)
859 assert not getter(), ("Empty permission set should not"
860 " have permission '%s'" % txt)
862 assert ps.test(perm), ("Permission '%s' should exist"
863 " after addition" % txt)
864 assert getter(), ("Permission '%s' should exist"
865 " after addition" % txt)
867 self.checkRef(str_ps)
869 assert not ps.test(perm), ("Permission '%s' should not exist"
870 " after deletion" % txt)
871 assert not getter(), ("Permission '%s' should not exist"
872 " after deletion" % txt)
878 def test_permset_invalid_type(self):
883 with pytest.raises(TypeError):
884 ps.add("foobar") # type: ignore
885 with pytest.raises(TypeError):
886 ps.delete("foobar") # type: ignore
887 with pytest.raises(TypeError):
888 ps.test("foobar") # type: ignore
889 with pytest.raises(ValueError):
890 ps.write = object() # type: ignore
892 @pytest.mark.parametrize("tag", [ACL_USER, ACL_GROUP],
893 ids=["ACL_USER", "ACL_GROUP"])
894 def test_qualifier_values(self, tag):
895 """Tests qualifier correct store/retrieval"""
901 regex = re.compile("(user|group) with (u|g)id %d" % qualifier)
903 e.qualifier = qualifier
904 except OverflowError:
905 # reached overflow condition, break
907 assert e.qualifier == qualifier
908 assert regex.search(str(e)) is not None
911 def test_qualifier_overflow(self):
912 """Tests qualifier overflow handling"""
915 # the uid_t/gid_t are unsigned, so they can hold slightly more
916 # than sys.maxsize*2 (on Linux).
917 qualifier = (sys.maxsize + 1) * 2
918 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
920 with pytest.raises(OverflowError):
921 e.qualifier = qualifier
923 def test_qualifier_underflow(self):
924 """Tests negative qualifier handling"""
925 # Note: this presumes that uid_t/gid_t in C are unsigned...
928 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
930 for qualifier in [-10, -5, -1]:
931 with pytest.raises(OverflowError):
932 e.qualifier = qualifier
934 def test_invalid_qualifier(self):
935 """Tests invalid qualifier handling"""
938 with pytest.raises(TypeError):
939 e.qualifier = object() # type: ignore
940 with pytest.raises((TypeError, AttributeError)):
943 def test_qualifier_on_wrong_tag(self):
944 """Tests qualifier setting on wrong tag"""
947 e.tag_type = posix1e.ACL_OTHER
948 with pytest.raises(TypeError):
950 with pytest.raises(TypeError):
953 @pytest.mark.parametrize("tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
954 def test_tag_types(self, tag):
955 """Tests tag type correct set/get"""
959 assert e.tag_type == tag
960 # check we can show all tag types without breaking
963 @pytest.mark.parametrize("src_tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
964 @pytest.mark.parametrize("dst_tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
965 def test_tag_overwrite(self, src_tag, dst_tag):
966 """Tests tag type correct set/get"""
970 assert e.tag_type == src_tag
973 assert e.tag_type == dst_tag
976 def test_invalid_tags(self):
977 """Tests tag type incorrect set/get"""
980 with pytest.raises(TypeError):
981 e.tag_type = object() # type: ignore
982 e.tag_type = posix1e.ACL_USER_OBJ
983 # For some reason, PyPy raises AttributeError. Strange...
984 with pytest.raises((TypeError, AttributeError)):
987 def test_tag_wrong_overwrite(self):
990 e.tag_type = posix1e.ACL_USER_OBJ
991 tag = max(ALL_TAG_VALUES) + 1
992 with pytest.raises(EnvironmentError):
994 # Check tag is still valid.
995 assert e.tag_type == posix1e.ACL_USER_OBJ
997 if __name__ == "__main__":