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
317 @pytest.mark.xfail(reason="Unreliable test, re-init doesn't always invalidate children")
318 def test_double_init_breaks_children(self):
321 e.permset.write = True
322 acl.__init__() # type: ignore
323 with pytest.raises(EnvironmentError):
324 e.permset.write = False
327 class TestAclExtensions:
328 """ACL extensions checks"""
330 @require_acl_from_mode
331 def test_from_mode(self):
332 """Test loading ACLs from an octal mode"""
333 acl1 = posix1e.ACL(mode=0o644)
337 def test_acl_check(self):
338 """Test the acl_check method"""
339 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
340 assert not acl1.check()
343 assert c == (ACL_MISS_ERROR, 0)
344 assert isinstance(c, tuple)
345 assert c[0] == ACL_MISS_ERROR
348 assert c == (ACL_ENTRY_ERROR, 0)
350 def test_applyto(self, subject):
351 """Test the apply_to function"""
352 # TODO: add read/compare with before, once ACL can be init'ed
354 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
355 basic_acl.applyto(subject)
356 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
357 assert enhanced_acl.valid()
358 enhanced_acl.applyto(subject)
360 def test_apply_to_with_wrong_object(self):
361 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
363 with pytest.raises(TypeError):
364 acl1.applyto(object())
365 with pytest.raises(TypeError):
366 acl1.applyto(object(), object()) # type: ignore
368 def test_apply_to_fail(self, testdir):
369 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
371 fd, fname = get_file(testdir)
373 with pytest.raises(IOError):
375 with pytest.raises(IOError, match="no-such-file"):
376 acl1.applyto(fname+".no-such-file")
378 @require_extended_check
379 def test_applyto_extended(self, subject):
380 """Test the acl_extended function"""
381 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
382 basic_acl.applyto(subject)
383 assert not has_extended(subject)
384 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
385 assert enhanced_acl.valid()
386 enhanced_acl.applyto(subject)
387 assert has_extended(subject)
389 @require_extended_check
390 @pytest.mark.parametrize(
391 "gen", [ get_file_and_symlink, get_file_and_fobject ])
392 def test_applyto_extended_mixed(self, testdir, gen):
393 """Test the acl_extended function"""
394 with gen(testdir) as (a, b):
395 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
398 assert not has_extended(item)
399 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
400 assert enhanced_acl.valid()
401 enhanced_acl.applyto(b)
403 assert has_extended(item)
405 @require_extended_check
406 def test_extended_fail(self, testdir):
407 fd, fname = get_file(testdir)
409 with pytest.raises(IOError):
411 with pytest.raises(IOError, match="no-such-file"):
412 has_extended(fname+".no-such-file")
414 @require_extended_check
415 def test_extended_arg_handling(self):
416 with pytest.raises(TypeError):
417 has_extended() # type: ignore
418 with pytest.raises(TypeError):
419 has_extended(object()) # type: ignore
422 def test_equiv_mode(self):
423 """Test the equiv_mode function"""
424 if HAS_ACL_FROM_MODE:
425 for mode in 0o644, 0o755:
426 acl = posix1e.ACL(mode=mode)
427 assert acl.equiv_mode() == mode
428 acl = posix1e.ACL(text="u::rw,g::r,o::r")
429 assert acl.equiv_mode() == 0o644
430 acl = posix1e.ACL(text="u::rx,g::-,o::-")
431 assert acl.equiv_mode() == 0o500
434 @pytest.mark.xfail(reason="It seems equiv mode always passes, even for empty ACLs")
435 def test_equiv_mode_invalid(self):
436 """Test equiv_mode on invalid ACLs"""
438 with pytest.raises(EnvironmentError):
442 def test_to_any_text(self):
443 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
445 acl.to_any_text(options=posix1e.TEXT_ABBREVIATE)
446 assert b"user::" in acl.to_any_text()
449 def test_to_any_text_wrong_args(self):
450 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
451 with pytest.raises(TypeError):
452 acl.to_any_text(foo="bar") # type: ignore
456 def test_rich_compare(self):
457 acl1 = posix1e.ACL(text="u::rw,g::r,o::r")
458 acl2 = posix1e.ACL(acl=acl1)
459 acl3 = posix1e.ACL(text="u::rw,g::rw,o::r")
462 with pytest.raises(TypeError):
463 acl1 < acl2 # type: ignore
464 with pytest.raises(TypeError):
465 acl1 >= acl3 # type: ignore
466 assert acl1 != True # type: ignore
467 assert not (acl1 == 1) # type: ignore
468 with pytest.raises(TypeError):
469 acl1 > True # type: ignore
472 def test_acl_iterator(self):
473 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
475 assert entry.parent is acl
478 def test_acl_copy_ext(self):
479 a = posix1e.ACL(text=BASIC_ACL_TEXT)
481 c = posix1e.ACL(acl=b)
484 state = a.__getstate__()
485 b.__setstate__(state)
490 def test_acl_copy_ext_args(self):
492 with pytest.raises(TypeError):
496 def test_acl_init_copy_ext(self):
497 a = posix1e.ACL(text=BASIC_ACL_TEXT)
499 c = posix1e.ACL(data=a.__getstate__())
504 def test_acl_init_copy_ext_invalid(self):
505 with pytest.raises(IOError):
506 posix1e.ACL(data=b"foobar")
512 def test_delete_default(self, testdir):
513 """Test removing the default ACL"""
514 with get_dir(testdir) as dname:
515 posix1e.delete_default(dname)
517 def test_delete_default_fail(self, testdir):
518 """Test removing the default ACL"""
519 with get_file_name(testdir) as fname:
520 with pytest.raises(IOError, match="no-such-file"):
521 posix1e.delete_default(fname+".no-such-file")
524 def test_delete_default_wrong_arg(self):
525 with pytest.raises(TypeError):
526 posix1e.delete_default(object()) # type: ignore
528 def test_reapply(self, testdir):
529 """Test re-applying an ACL"""
530 fd, fname = get_file(testdir)
531 acl1 = posix1e.ACL(fd=fd)
534 with get_dir(testdir) as dname:
535 acl2 = posix1e.ACL(file=fname)
541 class TestModification:
542 """ACL modification tests"""
544 def checkRef(self, obj):
545 """Checks if a given obj has a 'sane' refcount"""
546 if platform.python_implementation() == "PyPy":
548 ref_cnt = sys.getrefcount(obj)
549 # FIXME: hardcoded value for the max ref count... but I've
550 # seen it overflow on bad reference counting, so it's better
552 if ref_cnt < 2 or ref_cnt > 1024:
553 pytest.fail("Wrong reference count, expected 2-1024 and got %d" %
557 """Test str() of an ACL."""
558 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
560 self.checkRef(str_acl)
562 def test_append(self):
563 """Test append a new Entry to the ACL"""
566 e.tag_type = posix1e.ACL_OTHER
567 ignore_ioerror(errno.EINVAL, acl.calc_mask)
569 self.checkRef(str_format)
571 ignore_ioerror(errno.EINVAL, acl.calc_mask)
572 assert not acl.valid()
574 def test_wrong_append(self):
575 """Test append a new Entry to the ACL based on wrong object type"""
577 with pytest.raises(TypeError):
578 acl.append(object()) # type: ignore
580 @pytest.mark.xfail(reason="Behaviour not conform to specification")
581 def test_append_invalid_source(self):
586 with pytest.raises(EnvironmentError):
587 f.permset.write = True
588 with pytest.raises(EnvironmentError):
591 def test_entry_creation(self):
593 e = posix1e.Entry(acl)
594 ignore_ioerror(errno.EINVAL, acl.calc_mask)
596 self.checkRef(str_format)
598 def test_entry_failed_creation(self):
599 # Checks for partial initialisation and deletion on error
601 with pytest.raises(TypeError):
602 posix1e.Entry(object()) # type: ignore
604 def test_entry_reinitialisations(self):
608 e.__init__(a) # type: ignore
609 with pytest.raises(ValueError, match="different parent"):
610 e.__init__(b) # type: ignore
613 def test_entry_reinit_leaks_refcount(self):
616 ref = sys.getrefcount(acl)
617 e.__init__(acl) # type: ignore
618 assert ref == sys.getrefcount(acl), "Uh-oh, ref leaks..."
620 def test_delete(self):
621 """Test delete Entry from the ACL"""
624 e.tag_type = posix1e.ACL_OTHER
625 ignore_ioerror(errno.EINVAL, acl.calc_mask)
627 ignore_ioerror(errno.EINVAL, acl.calc_mask)
629 def test_double_delete(self):
630 """Test delete Entry from the ACL"""
631 # This is not entirely valid/correct, since the entry object
632 # itself is invalid after the first deletion, so we're
633 # actually testing deleting an invalid object, not a
634 # non-existing entry...
637 e.tag_type = posix1e.ACL_OTHER
638 ignore_ioerror(errno.EINVAL, acl.calc_mask)
640 ignore_ioerror(errno.EINVAL, acl.calc_mask)
641 with pytest.raises(EnvironmentError):
644 def test_delete_unowned(self):
645 """Test delete Entry from the ACL"""
649 e.tag_type = posix1e.ACL_OTHER
650 with pytest.raises(ValueError, match="un-owned entry"):
653 # This currently fails as this deletion seems to be accepted :/
654 @pytest.mark.xfail(reason="Entry deletion is unreliable")
655 def testDeleteInvalidEntry(self):
656 """Test delete foreign Entry from the ACL"""
660 e.tag_type = posix1e.ACL_OTHER
661 ignore_ioerror(errno.EINVAL, acl1.calc_mask)
662 with pytest.raises(EnvironmentError):
665 def test_delete_invalid_object(self):
666 """Test delete a non-Entry from the ACL"""
668 with pytest.raises(TypeError):
669 acl.delete_entry(object()) # type: ignore
671 def test_double_entries(self):
672 """Test double entries"""
673 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
675 for tag_type in (posix1e.ACL_USER_OBJ, posix1e.ACL_GROUP_OBJ,
678 e.tag_type = tag_type
680 assert not acl.valid(), ("ACL containing duplicate entries"
681 " should not be valid")
684 def test_multiple_good_entries(self):
685 """Test multiple valid entries"""
686 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
688 for tag_type in (posix1e.ACL_USER,
690 for obj_id in range(5):
692 e.tag_type = tag_type
696 assert acl.valid(), ("ACL should be able to hold multiple"
697 " user/group entries")
699 def test_multiple_bad_entries(self):
700 """Test multiple invalid entries"""
701 for tag_type in (posix1e.ACL_USER,
703 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
706 e1.tag_type = tag_type
710 assert acl.valid(), ("ACL should be able to add a"
713 e2.tag_type = tag_type
716 ignore_ioerror(errno.EINVAL, acl.calc_mask)
717 assert not acl.valid(), ("ACL should not validate when"
718 " containing two duplicate entries")
720 # FreeBSD trips over itself here and can't delete the
721 # entry, even though it still exists.
722 ignore_ioerror(errno.EINVAL, acl.delete_entry, e2)
727 e1.tag_type = ACL_USER
733 e2.tag_type = ACL_GROUP
740 assert e1.tag_type == e2.tag_type
742 def test_copy_wrong_arg(self):
745 with pytest.raises(TypeError):
746 e.copy(object()) # type: ignore
748 def test_set_permset(self):
751 e1.tag_type = ACL_USER
757 e2.tag_type = ACL_GROUP
763 assert e2.permset.write
764 assert e2.tag_type == ACL_GROUP
766 def test_set_permset_wrong_arg(self):
769 with pytest.raises(TypeError):
770 e.permset = object() # type: ignore
772 def test_permset_creation(self):
779 def test_permset_creation_wrong_arg(self):
780 with pytest.raises(TypeError):
781 Permset(object()) # type: ignore
783 def test_permset_reinitialisations(self):
788 p.__init__(e) # type: ignore
789 with pytest.raises(ValueError, match="different parent"):
790 p.__init__(f) # type: ignore
793 def test_permset_reinit_leaks_refcount(self):
797 ref = sys.getrefcount(e)
798 p.__init__(e) # type: ignore
799 assert ref == sys.getrefcount(e), "Uh-oh, ref leaks..."
801 @pytest.mark.parametrize("perm, txt, accessor",
802 PERMSETS, ids=PERMSETS_IDS)
803 def test_permset(self, perm, txt, accessor):
804 """Test permissions"""
811 self.checkRef(str_ps)
812 assert not ps.test(perm), ("Empty permission set should not"
813 " have permission '%s'" % txt)
815 assert ps.test(perm), ("Permission '%s' should exist"
816 " after addition" % txt)
818 self.checkRef(str_ps)
820 assert not ps.test(perm), ("Permission '%s' should not exist"
821 " after deletion" % txt)
823 assert ps.test(perm), ("Permission '%s' should exist"
824 " after addition" % txt)
826 assert not ps.test(perm), ("Permission '%s' should not exist"
827 " after clearing" % txt)
831 @pytest.mark.parametrize("perm, txt, accessor",
832 PERMSETS, ids=PERMSETS_IDS)
833 def test_permset_via_accessors(self, perm, txt, accessor):
834 """Test permissions"""
840 return accessor.__get__(ps) # type: ignore
842 return accessor.__set__(ps, value) # type: ignore
844 self.checkRef(str_ps)
845 assert not getter(), ("Empty permission set should not"
846 " have permission '%s'" % txt)
848 assert ps.test(perm), ("Permission '%s' should exist"
849 " after addition" % txt)
850 assert getter(), ("Permission '%s' should exist"
851 " after addition" % txt)
853 self.checkRef(str_ps)
855 assert not ps.test(perm), ("Permission '%s' should not exist"
856 " after deletion" % txt)
857 assert not getter(), ("Permission '%s' should not exist"
858 " after deletion" % txt)
864 def test_permset_invalid_type(self):
869 with pytest.raises(TypeError):
870 ps.add("foobar") # type: ignore
871 with pytest.raises(TypeError):
872 ps.delete("foobar") # type: ignore
873 with pytest.raises(TypeError):
874 ps.test("foobar") # type: ignore
875 with pytest.raises(ValueError):
876 ps.write = object() # type: ignore
878 @pytest.mark.parametrize("tag", [ACL_USER, ACL_GROUP],
879 ids=["ACL_USER", "ACL_GROUP"])
880 def test_qualifier_values(self, tag):
881 """Tests qualifier correct store/retrieval"""
887 regex = re.compile("(user|group) with (u|g)id %d" % qualifier)
889 e.qualifier = qualifier
890 except OverflowError:
891 # reached overflow condition, break
893 assert e.qualifier == qualifier
894 assert regex.search(str(e)) is not None
897 def test_qualifier_overflow(self):
898 """Tests qualifier overflow handling"""
901 # the uid_t/gid_t are unsigned, so they can hold slightly more
902 # than sys.maxsize*2 (on Linux).
903 qualifier = (sys.maxsize + 1) * 2
904 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
906 with pytest.raises(OverflowError):
907 e.qualifier = qualifier
909 def test_qualifier_underflow(self):
910 """Tests negative qualifier handling"""
911 # Note: this presumes that uid_t/gid_t in C are unsigned...
914 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
916 for qualifier in [-10, -5, -1]:
917 with pytest.raises(OverflowError):
918 e.qualifier = qualifier
920 def test_invalid_qualifier(self):
921 """Tests invalid qualifier handling"""
924 with pytest.raises(TypeError):
925 e.qualifier = object() # type: ignore
926 with pytest.raises((TypeError, AttributeError)):
929 def test_qualifier_on_wrong_tag(self):
930 """Tests qualifier setting on wrong tag"""
933 e.tag_type = posix1e.ACL_OTHER
934 with pytest.raises(TypeError):
936 with pytest.raises(TypeError):
939 @pytest.mark.parametrize("tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
940 def test_tag_types(self, tag):
941 """Tests tag type correct set/get"""
945 assert e.tag_type == tag
946 # check we can show all tag types without breaking
949 @pytest.mark.parametrize("src_tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
950 @pytest.mark.parametrize("dst_tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
951 def test_tag_overwrite(self, src_tag, dst_tag):
952 """Tests tag type correct set/get"""
956 assert e.tag_type == src_tag
959 assert e.tag_type == dst_tag
962 def test_invalid_tags(self):
963 """Tests tag type incorrect set/get"""
966 with pytest.raises(TypeError):
967 e.tag_type = object() # type: ignore
968 e.tag_type = posix1e.ACL_USER_OBJ
969 # For some reason, PyPy raises AttributeError. Strange...
970 with pytest.raises((TypeError, AttributeError)):
973 def test_tag_wrong_overwrite(self):
976 e.tag_type = posix1e.ACL_USER_OBJ
977 tag = max(ALL_TAG_VALUES) + 1
978 with pytest.raises(EnvironmentError):
980 # Check tag is still valid.
981 assert e.tag_type == posix1e.ACL_USER_OBJ
983 if __name__ == "__main__":