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 posix1e.ACL_READ: ("read", posix1e.Permset.read),
47 posix1e.ACL_WRITE: ("write", posix1e.Permset.write),
48 posix1e.ACL_EXECUTE: ("execute", posix1e.Permset.execute),
52 (posix1e.ACL_USER, "user"),
53 (posix1e.ACL_GROUP, "group"),
54 (posix1e.ACL_USER_OBJ, "user object"),
55 (posix1e.ACL_GROUP_OBJ, "group object"),
56 (posix1e.ACL_MASK, "mask"),
57 (posix1e.ACL_OTHER, "other"),
60 ALL_TAG_VALUES = [i[0] for i in ALL_TAGS]
61 ALL_TAG_DESCS = [i[1] for i in ALL_TAGS]
63 # Fixtures and helpers
65 def ignore_ioerror(errnum, fn, *args, **kwargs):
66 """Call a function while ignoring some IOErrors.
68 This is needed as some OSes (e.g. FreeBSD) return failure (EINVAL)
69 when doing certain operations on an invalid ACL.
74 except IOError as err:
75 if err.errno == errnum:
81 """per-test temp dir based in TEST_DIR"""
82 with tempfile.TemporaryDirectory(dir=TEST_DIR) as dname:
86 fh, fname = tempfile.mkstemp(".test", "xattr-", path)
89 @contextlib.contextmanager
90 def get_file_name(path):
91 fh, fname = get_file(path)
95 @contextlib.contextmanager
96 def get_file_fd(path):
97 fd = get_file(path)[0]
101 @contextlib.contextmanager
102 def get_file_object(path):
103 fd = get_file(path)[0]
104 with os.fdopen(fd) as f:
107 @contextlib.contextmanager
109 yield tempfile.mkdtemp(".test", "xattr-", path)
111 def get_symlink(path, dangling=True):
112 """create a symlink"""
113 fh, fname = get_file(path)
117 sname = fname + ".symlink"
118 os.symlink(fname, sname)
121 @contextlib.contextmanager
122 def get_valid_symlink(path):
123 yield get_symlink(path, dangling=False)[1]
125 @contextlib.contextmanager
126 def get_dangling_symlink(path):
127 yield get_symlink(path, dangling=True)[1]
129 @contextlib.contextmanager
130 def get_file_and_symlink(path):
131 yield get_symlink(path, dangling=False)
133 @contextlib.contextmanager
134 def get_file_and_fobject(path):
135 fh, fname = get_file(path)
136 with os.fdopen(fh) as fo:
139 # Wrappers that build upon existing values
141 def as_wrapper(call, fn, closer=None):
142 @contextlib.contextmanager
144 with call(path) as r:
147 if closer is not None:
152 return as_wrapper(call, lambda r: r.encode())
155 return as_wrapper(call, pathlib.PurePath)
157 def as_iostream(call):
158 opener = lambda f: io.open(f, "r")
159 closer = lambda r: r.close()
160 return as_wrapper(call, opener, closer)
162 NOT_BEFORE_36 = pytest.mark.xfail(condition="sys.version_info < (3,6)",
164 NOT_PYPY = pytest.mark.xfail(condition="platform.python_implementation() == 'PyPy'",
167 require_acl_from_mode = pytest.mark.skipif("not HAS_ACL_FROM_MODE")
168 require_acl_check = pytest.mark.skipif("not HAS_ACL_CHECK")
169 require_acl_entry = pytest.mark.skipif("not HAS_ACL_ENTRY")
170 require_extended_check = pytest.mark.skipif("not HAS_EXTENDED_CHECK")
171 require_equiv_mode = pytest.mark.skipif("not HAS_EQUIV_MODE")
172 require_copy_ext = pytest.mark.skipif("not HAS_COPY_EXT")
174 # Note: ACLs are valid only for files/directories, not symbolic links
175 # themselves, so we only create valid symlinks.
178 as_bytes(get_file_name),
179 pytest.param(as_fspath(get_file_name),
180 marks=[NOT_BEFORE_36, NOT_PYPY]),
183 pytest.param(as_fspath(get_dir),
184 marks=[NOT_BEFORE_36, NOT_PYPY]),
186 as_bytes(get_valid_symlink),
187 pytest.param(as_fspath(get_valid_symlink),
188 marks=[NOT_BEFORE_36, NOT_PYPY]),
199 "file via symlink (bytes)",
200 "file via symlink (path)",
206 as_iostream(get_file_name),
215 ALL_P = FILE_P + FD_P
216 ALL_D = FILE_D + FD_D
218 @pytest.fixture(params=FILE_P, ids=FILE_D)
219 def file_subject(testdir, request):
220 with request.param(testdir) as value:
223 @pytest.fixture(params=FD_P, ids=FD_D)
224 def fd_subject(testdir, request):
225 with request.param(testdir) as value:
228 @pytest.fixture(params=ALL_P, ids=ALL_D)
229 def subject(testdir, request):
230 with request.param(testdir) as value:
235 """Load/create tests"""
236 def test_from_file(self, file_subject):
237 """Test loading ACLs from a file/directory"""
238 acl = posix1e.ACL(file=file_subject)
241 def test_from_dir(self, testdir):
242 """Test loading ACLs from a directory"""
243 with get_dir(testdir) as dname:
244 acl2 = posix1e.ACL(filedef=dname)
245 # default ACLs might or might not be valid; missing ones are
246 # not valid, so we don't test acl2 for validity
248 def test_from_fd(self, fd_subject):
249 """Test loading ACLs from a file descriptor"""
250 acl = posix1e.ACL(fd=fd_subject)
253 def test_from_nonexisting(self, testdir):
254 _, fname = get_file(testdir)
255 with pytest.raises(IOError):
256 posix1e.ACL(file="fname"+".no-such-file")
258 def test_from_invalid_fd(self, testdir):
259 fd, _ = get_file(testdir)
261 with pytest.raises(IOError):
264 def test_from_empty_invalid(self):
265 """Test creating an empty ACL"""
267 assert not acl1.valid()
269 def test_from_text(self):
270 """Test creating an ACL from text"""
271 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
274 def test_from_acl(self):
275 """Test creating an ACL from an existing ACL"""
277 acl2 = posix1e.ACL(acl=acl1)
280 def test_invalid_creation_params(self, testdir):
281 """Test that creating an ACL from multiple objects fails"""
282 fd, _ = get_file(testdir)
283 with pytest.raises(ValueError):
284 posix1e.ACL(text=BASIC_ACL_TEXT, fd=fd)
286 def test_invalid_value_creation(self):
287 """Test that creating an ACL from wrong specification fails"""
288 with pytest.raises(EnvironmentError):
289 posix1e.ACL(text="foobar")
290 with pytest.raises(TypeError):
291 posix1e.ACL(foo="bar")
293 def test_uninit(self):
294 """Checks that uninit is actually empty init"""
295 acl = posix1e.ACL.__new__(posix1e.ACL)
296 assert not acl.valid()
301 def test_double_init(self):
302 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
304 acl1.__init__(text=BASIC_ACL_TEXT) # type: ignore
307 @pytest.mark.xfail(reason="Unreliable test, re-init doesn't always invalidate children")
308 def test_double_init_breaks_children(self):
311 e.permset.write = True
312 acl.__init__() # type: ignore
313 with pytest.raises(EnvironmentError):
314 e.permset.write = False
317 class TestAclExtensions:
318 """ACL extensions checks"""
320 @require_acl_from_mode
321 def test_from_mode(self):
322 """Test loading ACLs from an octal mode"""
323 acl1 = posix1e.ACL(mode=0o644)
327 def test_acl_check(self):
328 """Test the acl_check method"""
329 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
330 assert not acl1.check()
333 assert c == (ACL_MISS_ERROR, 0)
334 assert isinstance(c, tuple)
335 assert c[0] == ACL_MISS_ERROR
338 assert c == (ACL_ENTRY_ERROR, 0)
340 def test_applyto(self, subject):
341 """Test the apply_to function"""
342 # TODO: add read/compare with before, once ACL can be init'ed
344 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
345 basic_acl.applyto(subject)
346 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
347 assert enhanced_acl.valid()
348 enhanced_acl.applyto(subject)
350 def test_apply_to_with_wrong_object(self):
351 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
353 with pytest.raises(TypeError):
354 acl1.applyto(object())
355 with pytest.raises(TypeError):
356 acl1.applyto(object(), object()) # type: ignore
358 def test_apply_to_fail(self, testdir):
359 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
361 fd, fname = get_file(testdir)
363 with pytest.raises(IOError):
365 with pytest.raises(IOError, match="no-such-file"):
366 acl1.applyto(fname+".no-such-file")
368 @require_extended_check
369 def test_applyto_extended(self, subject):
370 """Test the acl_extended function"""
371 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
372 basic_acl.applyto(subject)
373 assert not has_extended(subject)
374 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
375 assert enhanced_acl.valid()
376 enhanced_acl.applyto(subject)
377 assert has_extended(subject)
379 @require_extended_check
380 @pytest.mark.parametrize(
381 "gen", [ get_file_and_symlink, get_file_and_fobject ])
382 def test_applyto_extended_mixed(self, testdir, gen):
383 """Test the acl_extended function"""
384 with gen(testdir) as (a, b):
385 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
388 assert not has_extended(item)
389 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
390 assert enhanced_acl.valid()
391 enhanced_acl.applyto(b)
393 assert has_extended(item)
395 @require_extended_check
396 def test_extended_fail(self, testdir):
397 fd, fname = get_file(testdir)
399 with pytest.raises(IOError):
401 with pytest.raises(IOError, match="no-such-file"):
402 has_extended(fname+".no-such-file")
404 @require_extended_check
405 def test_extended_arg_handling(self):
406 with pytest.raises(TypeError):
407 has_extended() # type: ignore
408 with pytest.raises(TypeError):
409 has_extended(object()) # type: ignore
412 def test_equiv_mode(self):
413 """Test the equiv_mode function"""
414 if HAS_ACL_FROM_MODE:
415 for mode in 0o644, 0o755:
416 acl = posix1e.ACL(mode=mode)
417 assert acl.equiv_mode() == mode
418 acl = posix1e.ACL(text="u::rw,g::r,o::r")
419 assert acl.equiv_mode() == 0o644
420 acl = posix1e.ACL(text="u::rx,g::-,o::-")
421 assert acl.equiv_mode() == 0o500
424 @pytest.mark.xfail(reason="It seems equiv mode always passes, even for empty ACLs")
425 def test_equiv_mode_invalid(self):
426 """Test equiv_mode on invalid ACLs"""
428 with pytest.raises(EnvironmentError):
432 def test_to_any_text(self):
433 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
435 acl.to_any_text(options=posix1e.TEXT_ABBREVIATE)
436 assert b"user::" in acl.to_any_text()
439 def test_to_any_text_wrong_args(self):
440 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
441 with pytest.raises(TypeError):
442 acl.to_any_text(foo="bar") # type: ignore
446 def test_rich_compare(self):
447 acl1 = posix1e.ACL(text="u::rw,g::r,o::r")
448 acl2 = posix1e.ACL(acl=acl1)
449 acl3 = posix1e.ACL(text="u::rw,g::rw,o::r")
452 with pytest.raises(TypeError):
453 acl1 < acl2 # type: ignore
454 with pytest.raises(TypeError):
455 acl1 >= acl3 # type: ignore
456 assert acl1 != True # type: ignore
457 assert not (acl1 == 1) # type: ignore
458 with pytest.raises(TypeError):
459 acl1 > True # type: ignore
462 def test_acl_iterator(self):
463 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
465 assert entry.parent is acl
468 def test_acl_copy_ext(self):
469 a = posix1e.ACL(text=BASIC_ACL_TEXT)
471 c = posix1e.ACL(acl=b)
474 state = a.__getstate__()
475 b.__setstate__(state)
483 def test_delete_default(self, testdir):
484 """Test removing the default ACL"""
485 with get_dir(testdir) as dname:
486 posix1e.delete_default(dname)
488 def test_delete_default_fail(self, testdir):
489 """Test removing the default ACL"""
490 with get_file_name(testdir) as fname:
491 with pytest.raises(IOError, match="no-such-file"):
492 posix1e.delete_default(fname+".no-such-file")
495 def test_delete_default_wrong_arg(self):
496 with pytest.raises(TypeError):
497 posix1e.delete_default(object()) # type: ignore
499 def test_reapply(self, testdir):
500 """Test re-applying an ACL"""
501 fd, fname = get_file(testdir)
502 acl1 = posix1e.ACL(fd=fd)
505 with get_dir(testdir) as dname:
506 acl2 = posix1e.ACL(file=fname)
512 class TestModification:
513 """ACL modification tests"""
515 def checkRef(self, obj):
516 """Checks if a given obj has a 'sane' refcount"""
517 if platform.python_implementation() == "PyPy":
519 ref_cnt = sys.getrefcount(obj)
520 # FIXME: hardcoded value for the max ref count... but I've
521 # seen it overflow on bad reference counting, so it's better
523 if ref_cnt < 2 or ref_cnt > 1024:
524 pytest.fail("Wrong reference count, expected 2-1024 and got %d" %
528 """Test str() of an ACL."""
529 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
531 self.checkRef(str_acl)
533 def test_append(self):
534 """Test append a new Entry to the ACL"""
537 e.tag_type = posix1e.ACL_OTHER
538 ignore_ioerror(errno.EINVAL, acl.calc_mask)
540 self.checkRef(str_format)
542 ignore_ioerror(errno.EINVAL, acl.calc_mask)
543 assert not acl.valid()
545 def test_wrong_append(self):
546 """Test append a new Entry to the ACL based on wrong object type"""
548 with pytest.raises(TypeError):
549 acl.append(object()) # type: ignore
551 @pytest.mark.xfail(reason="Behaviour not conform to specification")
552 def test_append_invalid_source(self):
557 with pytest.raises(EnvironmentError):
558 f.permset.write = True
559 with pytest.raises(EnvironmentError):
562 def test_entry_creation(self):
564 e = posix1e.Entry(acl)
565 ignore_ioerror(errno.EINVAL, acl.calc_mask)
567 self.checkRef(str_format)
569 def test_entry_failed_creation(self):
570 # Checks for partial initialisation and deletion on error
572 with pytest.raises(TypeError):
573 posix1e.Entry(object()) # type: ignore
575 def test_entry_reinitialisations(self):
579 e.__init__(a) # type: ignore
580 with pytest.raises(ValueError, match="different parent"):
581 e.__init__(b) # type: ignore
584 def test_entry_reinit_leaks_refcount(self):
587 ref = sys.getrefcount(acl)
588 e.__init__(acl) # type: ignore
589 assert ref == sys.getrefcount(acl), "Uh-oh, ref leaks..."
591 def test_delete(self):
592 """Test delete Entry from the ACL"""
595 e.tag_type = posix1e.ACL_OTHER
596 ignore_ioerror(errno.EINVAL, acl.calc_mask)
598 ignore_ioerror(errno.EINVAL, acl.calc_mask)
600 def test_double_delete(self):
601 """Test delete Entry from the ACL"""
602 # This is not entirely valid/correct, since the entry object
603 # itself is invalid after the first deletion, so we're
604 # actually testing deleting an invalid object, not a
605 # non-existing entry...
608 e.tag_type = posix1e.ACL_OTHER
609 ignore_ioerror(errno.EINVAL, acl.calc_mask)
611 ignore_ioerror(errno.EINVAL, acl.calc_mask)
612 with pytest.raises(EnvironmentError):
615 def test_delete_unowned(self):
616 """Test delete Entry from the ACL"""
620 e.tag_type = posix1e.ACL_OTHER
621 with pytest.raises(ValueError, match="un-owned entry"):
624 # This currently fails as this deletion seems to be accepted :/
625 @pytest.mark.xfail(reason="Entry deletion is unreliable")
626 def testDeleteInvalidEntry(self):
627 """Test delete foreign Entry from the ACL"""
631 e.tag_type = posix1e.ACL_OTHER
632 ignore_ioerror(errno.EINVAL, acl1.calc_mask)
633 with pytest.raises(EnvironmentError):
636 def test_delete_invalid_object(self):
637 """Test delete a non-Entry from the ACL"""
639 with pytest.raises(TypeError):
640 acl.delete_entry(object()) # type: ignore
642 def test_double_entries(self):
643 """Test double entries"""
644 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
646 for tag_type in (posix1e.ACL_USER_OBJ, posix1e.ACL_GROUP_OBJ,
649 e.tag_type = tag_type
651 assert not acl.valid(), ("ACL containing duplicate entries"
652 " should not be valid")
655 def test_multiple_good_entries(self):
656 """Test multiple valid entries"""
657 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
659 for tag_type in (posix1e.ACL_USER,
661 for obj_id in range(5):
663 e.tag_type = tag_type
667 assert acl.valid(), ("ACL should be able to hold multiple"
668 " user/group entries")
670 def test_multiple_bad_entries(self):
671 """Test multiple invalid entries"""
672 for tag_type in (posix1e.ACL_USER,
674 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
677 e1.tag_type = tag_type
681 assert acl.valid(), ("ACL should be able to add a"
684 e2.tag_type = tag_type
687 ignore_ioerror(errno.EINVAL, acl.calc_mask)
688 assert not acl.valid(), ("ACL should not validate when"
689 " containing two duplicate entries")
691 # FreeBSD trips over itself here and can't delete the
692 # entry, even though it still exists.
693 ignore_ioerror(errno.EINVAL, acl.delete_entry, e2)
698 e1.tag_type = ACL_USER
704 e2.tag_type = ACL_GROUP
711 assert e1.tag_type == e2.tag_type
713 def test_copy_wrong_arg(self):
716 with pytest.raises(TypeError):
717 e.copy(object()) # type: ignore
719 def test_set_permset(self):
722 e1.tag_type = ACL_USER
728 e2.tag_type = ACL_GROUP
734 assert e2.permset.write
735 assert e2.tag_type == ACL_GROUP
737 def test_set_permset_wrong_arg(self):
740 with pytest.raises(TypeError):
741 e.permset = object() # type: ignore
743 def test_permset_creation(self):
750 def test_permset_creation_wrong_arg(self):
751 with pytest.raises(TypeError):
752 Permset(object()) # type: ignore
754 def test_permset_reinitialisations(self):
759 p.__init__(e) # type: ignore
760 with pytest.raises(ValueError, match="different parent"):
761 p.__init__(f) # type: ignore
764 def test_permset_reinit_leaks_refcount(self):
768 ref = sys.getrefcount(e)
769 p.__init__(e) # type: ignore
770 assert ref == sys.getrefcount(e), "Uh-oh, ref leaks..."
772 def test_permset(self):
773 """Test permissions"""
779 self.checkRef(str_ps)
780 for perm in PERMSETS:
782 txt = PERMSETS[perm][0]
783 self.checkRef(str_ps)
784 assert not ps.test(perm), ("Empty permission set should not"
785 " have permission '%s'" % txt)
787 assert ps.test(perm), ("Permission '%s' should exist"
788 " after addition" % txt)
790 self.checkRef(str_ps)
792 assert not ps.test(perm), ("Permission '%s' should not exist"
793 " after deletion" % txt)
795 def test_permset_via_accessors(self):
796 """Test permissions"""
802 self.checkRef(str_ps)
804 return PERMSETS[perm][1].__get__(ps) # type: ignore
805 def setter(parm, value):
806 return PERMSETS[perm][1].__set__(ps, value) # type: ignore
807 for perm in PERMSETS:
809 self.checkRef(str_ps)
810 txt = PERMSETS[perm][0]
811 assert not getter(perm), ("Empty permission set should not"
812 " have permission '%s'" % txt)
814 assert ps.test(perm), ("Permission '%s' should exist"
815 " after addition" % txt)
816 assert getter(perm), ("Permission '%s' should exist"
817 " after addition" % txt)
819 self.checkRef(str_ps)
821 assert not ps.test(perm), ("Permission '%s' should not exist"
822 " after deletion" % txt)
823 assert not getter(perm), ("Permission '%s' should not exist"
824 " after deletion" % txt)
826 def test_permset_invalid_type(self):
831 with pytest.raises(TypeError):
832 ps.add("foobar") # type: ignore
833 with pytest.raises(TypeError):
834 ps.delete("foobar") # type: ignore
835 with pytest.raises(TypeError):
836 ps.test("foobar") # type: ignore
837 with pytest.raises(ValueError):
838 ps.write = object() # type: ignore
840 def test_qualifier_values(self):
841 """Tests qualifier correct store/retrieval"""
844 # work around deprecation warnings
845 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
849 if tag == posix1e.ACL_USER:
850 regex = re.compile("user with uid %d" % qualifier)
852 regex = re.compile("group with gid %d" % qualifier)
854 e.qualifier = qualifier
855 except OverflowError:
856 # reached overflow condition, break
858 assert e.qualifier == qualifier
859 assert regex.search(str(e)) is not None
862 def test_qualifier_overflow(self):
863 """Tests qualifier overflow handling"""
866 qualifier = sys.maxsize * 2
867 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
869 with pytest.raises(OverflowError):
870 e.qualifier = qualifier
872 def test_negative_qualifier(self):
873 """Tests negative qualifier handling"""
874 # Note: this presumes that uid_t/gid_t in C are unsigned...
877 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
879 for qualifier in [-10, -5, -1]:
880 with pytest.raises(OverflowError):
881 e.qualifier = qualifier
883 def test_invalid_qualifier(self):
884 """Tests invalid qualifier handling"""
887 with pytest.raises(TypeError):
888 e.qualifier = object() # type: ignore
889 with pytest.raises((TypeError, AttributeError)):
892 def test_qualifier_on_wrong_tag(self):
893 """Tests qualifier setting on wrong tag"""
896 e.tag_type = posix1e.ACL_OTHER
897 with pytest.raises(TypeError):
899 with pytest.raises(TypeError):
902 @pytest.mark.parametrize("tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
903 def test_tag_types(self, tag):
904 """Tests tag type correct set/get"""
908 assert e.tag_type == tag
909 # check we can show all tag types without breaking
912 @pytest.mark.parametrize("src_tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
913 @pytest.mark.parametrize("dst_tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
914 def test_tag_overwrite(self, src_tag, dst_tag):
915 """Tests tag type correct set/get"""
919 assert e.tag_type == src_tag
922 assert e.tag_type == dst_tag
925 def test_invalid_tags(self):
926 """Tests tag type incorrect set/get"""
929 with pytest.raises(TypeError):
930 e.tag_type = object() # type: ignore
931 e.tag_type = posix1e.ACL_USER_OBJ
932 # For some reason, PyPy raises AttributeError. Strange...
933 with pytest.raises((TypeError, AttributeError)):
936 def test_tag_wrong_overwrite(self):
939 e.tag_type = posix1e.ACL_USER_OBJ
940 tag = max(ALL_TAG_VALUES) + 1
941 with pytest.raises(EnvironmentError):
943 # Check tag is still valid.
944 assert e.tag_type == posix1e.ACL_USER_OBJ
946 if __name__ == "__main__":