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, testdir):
237 """Test loading ACLs from a file"""
238 _, fname = get_file(testdir)
239 acl1 = posix1e.ACL(file=fname)
242 def test_from_dir(self, testdir):
243 """Test loading ACLs from a directory"""
244 with get_dir(testdir) as dname:
245 acl1 = posix1e.ACL(file=dname)
246 acl2 = posix1e.ACL(filedef=dname)
248 # default ACLs might or might not be valid; missing ones are
249 # not valid, so we don't test acl2 for validity
251 def test_from_fd(self, testdir):
252 """Test loading ACLs from a file descriptor"""
253 fd, _ = get_file(testdir)
254 acl1 = posix1e.ACL(fd=fd)
257 def test_from_nonexisting(self, testdir):
258 _, fname = get_file(testdir)
259 with pytest.raises(IOError):
260 posix1e.ACL(file="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 def test_from_acl(self):
279 """Test creating an ACL from an existing ACL"""
281 acl2 = posix1e.ACL(acl=acl1)
284 def test_invalid_creation_params(self, testdir):
285 """Test that creating an ACL from multiple objects fails"""
286 fd, _ = get_file(testdir)
287 with pytest.raises(ValueError):
288 posix1e.ACL(text=BASIC_ACL_TEXT, fd=fd)
290 def test_invalid_value_creation(self):
291 """Test that creating an ACL from wrong specification fails"""
292 with pytest.raises(EnvironmentError):
293 posix1e.ACL(text="foobar")
294 with pytest.raises(TypeError):
295 posix1e.ACL(foo="bar")
297 def test_uninit(self):
298 """Checks that uninit is actually empty init"""
299 acl = posix1e.ACL.__new__(posix1e.ACL)
300 assert not acl.valid()
305 def test_double_init(self):
306 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
308 acl1.__init__(text=BASIC_ACL_TEXT) # type: ignore
311 @pytest.mark.xfail(reason="Unreliable test, re-init doesn't always invalidate children")
312 def test_double_init_breaks_children(self):
315 e.permset.write = True
316 acl.__init__() # type: ignore
317 with pytest.raises(EnvironmentError):
318 e.permset.write = False
321 class TestAclExtensions:
322 """ACL extensions checks"""
324 @require_acl_from_mode
325 def test_from_mode(self):
326 """Test loading ACLs from an octal mode"""
327 acl1 = posix1e.ACL(mode=0o644)
331 def test_acl_check(self):
332 """Test the acl_check method"""
333 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
334 assert not acl1.check()
337 assert c == (ACL_MISS_ERROR, 0)
338 assert isinstance(c, tuple)
339 assert c[0] == ACL_MISS_ERROR
342 assert c == (ACL_ENTRY_ERROR, 0)
344 def test_applyto(self, subject):
345 """Test the apply_to function"""
346 # TODO: add read/compare with before, once ACL can be init'ed
348 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
349 basic_acl.applyto(subject)
350 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
351 assert enhanced_acl.valid()
352 enhanced_acl.applyto(subject)
354 def test_apply_to_with_wrong_object(self):
355 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
357 with pytest.raises(TypeError):
358 acl1.applyto(object())
359 with pytest.raises(TypeError):
360 acl1.applyto(object(), object()) # type: ignore
362 def test_apply_to_fail(self, testdir):
363 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
365 fd, fname = get_file(testdir)
367 with pytest.raises(IOError):
369 with pytest.raises(IOError, match="no-such-file"):
370 acl1.applyto(fname+".no-such-file")
372 @require_extended_check
373 def test_applyto_extended(self, subject):
374 """Test the acl_extended function"""
375 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
376 basic_acl.applyto(subject)
377 assert not has_extended(subject)
378 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
379 assert enhanced_acl.valid()
380 enhanced_acl.applyto(subject)
381 assert has_extended(subject)
383 @require_extended_check
384 @pytest.mark.parametrize(
385 "gen", [ get_file_and_symlink, get_file_and_fobject ])
386 def test_applyto_extended_mixed(self, testdir, gen):
387 """Test the acl_extended function"""
388 with gen(testdir) as (a, b):
389 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
392 assert not has_extended(item)
393 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
394 assert enhanced_acl.valid()
395 enhanced_acl.applyto(b)
397 assert has_extended(item)
399 @require_extended_check
400 def test_extended_fail(self, testdir):
401 fd, fname = get_file(testdir)
403 with pytest.raises(IOError):
405 with pytest.raises(IOError, match="no-such-file"):
406 has_extended(fname+".no-such-file")
408 @require_extended_check
409 def test_extended_arg_handling(self):
410 with pytest.raises(TypeError):
411 has_extended() # type: ignore
412 with pytest.raises(TypeError):
413 has_extended(object()) # type: ignore
416 def test_equiv_mode(self):
417 """Test the equiv_mode function"""
418 if HAS_ACL_FROM_MODE:
419 for mode in 0o644, 0o755:
420 acl = posix1e.ACL(mode=mode)
421 assert acl.equiv_mode() == mode
422 acl = posix1e.ACL(text="u::rw,g::r,o::r")
423 assert acl.equiv_mode() == 0o644
424 acl = posix1e.ACL(text="u::rx,g::-,o::-")
425 assert acl.equiv_mode() == 0o500
428 @pytest.mark.xfail(reason="It seems equiv mode always passes, even for empty ACLs")
429 def test_equiv_mode_invalid(self):
430 """Test equiv_mode on invalid ACLs"""
432 with pytest.raises(EnvironmentError):
436 def test_to_any_text(self):
437 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
439 acl.to_any_text(options=posix1e.TEXT_ABBREVIATE)
440 assert b"user::" in acl.to_any_text()
443 def test_to_any_text_wrong_args(self):
444 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
445 with pytest.raises(TypeError):
446 acl.to_any_text(foo="bar") # type: ignore
450 def test_rich_compare(self):
451 acl1 = posix1e.ACL(text="u::rw,g::r,o::r")
452 acl2 = posix1e.ACL(acl=acl1)
453 acl3 = posix1e.ACL(text="u::rw,g::rw,o::r")
456 with pytest.raises(TypeError):
457 acl1 < acl2 # type: ignore
458 with pytest.raises(TypeError):
459 acl1 >= acl3 # type: ignore
460 assert acl1 != True # type: ignore
461 assert not (acl1 == 1) # type: ignore
462 with pytest.raises(TypeError):
463 acl1 > True # type: ignore
466 def test_acl_iterator(self):
467 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
469 assert entry.parent is acl
472 def test_acl_copy_ext(self):
473 a = posix1e.ACL(text=BASIC_ACL_TEXT)
475 c = posix1e.ACL(acl=b)
478 state = a.__getstate__()
479 b.__setstate__(state)
487 def test_delete_default(self, testdir):
488 """Test removing the default ACL"""
489 with get_dir(testdir) as dname:
490 posix1e.delete_default(dname)
492 def test_delete_default_fail(self, testdir):
493 """Test removing the default ACL"""
494 with get_file_name(testdir) as fname:
495 with pytest.raises(IOError, match="no-such-file"):
496 posix1e.delete_default(fname+".no-such-file")
499 def test_delete_default_wrong_arg(self):
500 with pytest.raises(TypeError):
501 posix1e.delete_default(object()) # type: ignore
503 def test_reapply(self, testdir):
504 """Test re-applying an ACL"""
505 fd, fname = get_file(testdir)
506 acl1 = posix1e.ACL(fd=fd)
509 with get_dir(testdir) as dname:
510 acl2 = posix1e.ACL(file=fname)
516 class TestModification:
517 """ACL modification tests"""
519 def checkRef(self, obj):
520 """Checks if a given obj has a 'sane' refcount"""
521 if platform.python_implementation() == "PyPy":
523 ref_cnt = sys.getrefcount(obj)
524 # FIXME: hardcoded value for the max ref count... but I've
525 # seen it overflow on bad reference counting, so it's better
527 if ref_cnt < 2 or ref_cnt > 1024:
528 pytest.fail("Wrong reference count, expected 2-1024 and got %d" %
532 """Test str() of an ACL."""
533 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
535 self.checkRef(str_acl)
537 def test_append(self):
538 """Test append a new Entry to the ACL"""
541 e.tag_type = posix1e.ACL_OTHER
542 ignore_ioerror(errno.EINVAL, acl.calc_mask)
544 self.checkRef(str_format)
546 ignore_ioerror(errno.EINVAL, acl.calc_mask)
547 assert not acl.valid()
549 def test_wrong_append(self):
550 """Test append a new Entry to the ACL based on wrong object type"""
552 with pytest.raises(TypeError):
553 acl.append(object()) # type: ignore
555 @pytest.mark.xfail(reason="Behaviour not conform to specification")
556 def test_append_invalid_source(self):
561 with pytest.raises(EnvironmentError):
562 f.permset.write = True
563 with pytest.raises(EnvironmentError):
566 def test_entry_creation(self):
568 e = posix1e.Entry(acl)
569 ignore_ioerror(errno.EINVAL, acl.calc_mask)
571 self.checkRef(str_format)
573 def test_entry_failed_creation(self):
574 # Checks for partial initialisation and deletion on error
576 with pytest.raises(TypeError):
577 posix1e.Entry(object()) # type: ignore
579 def test_entry_reinitialisations(self):
583 e.__init__(a) # type: ignore
584 with pytest.raises(ValueError, match="different parent"):
585 e.__init__(b) # type: ignore
588 def test_entry_reinit_leaks_refcount(self):
591 ref = sys.getrefcount(acl)
592 e.__init__(acl) # type: ignore
593 assert ref == sys.getrefcount(acl), "Uh-oh, ref leaks..."
595 def test_delete(self):
596 """Test delete Entry from the ACL"""
599 e.tag_type = posix1e.ACL_OTHER
600 ignore_ioerror(errno.EINVAL, acl.calc_mask)
602 ignore_ioerror(errno.EINVAL, acl.calc_mask)
604 def test_double_delete(self):
605 """Test delete Entry from the ACL"""
606 # This is not entirely valid/correct, since the entry object
607 # itself is invalid after the first deletion, so we're
608 # actually testing deleting an invalid object, not a
609 # non-existing entry...
612 e.tag_type = posix1e.ACL_OTHER
613 ignore_ioerror(errno.EINVAL, acl.calc_mask)
615 ignore_ioerror(errno.EINVAL, acl.calc_mask)
616 with pytest.raises(EnvironmentError):
619 def test_delete_unowned(self):
620 """Test delete Entry from the ACL"""
624 e.tag_type = posix1e.ACL_OTHER
625 with pytest.raises(ValueError, match="un-owned entry"):
628 # This currently fails as this deletion seems to be accepted :/
629 @pytest.mark.xfail(reason="Entry deletion is unreliable")
630 def testDeleteInvalidEntry(self):
631 """Test delete foreign Entry from the ACL"""
635 e.tag_type = posix1e.ACL_OTHER
636 ignore_ioerror(errno.EINVAL, acl1.calc_mask)
637 with pytest.raises(EnvironmentError):
640 def test_delete_invalid_object(self):
641 """Test delete a non-Entry from the ACL"""
643 with pytest.raises(TypeError):
644 acl.delete_entry(object()) # type: ignore
646 def test_double_entries(self):
647 """Test double entries"""
648 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
650 for tag_type in (posix1e.ACL_USER_OBJ, posix1e.ACL_GROUP_OBJ,
653 e.tag_type = tag_type
655 assert not acl.valid(), ("ACL containing duplicate entries"
656 " should not be valid")
659 def test_multiple_good_entries(self):
660 """Test multiple valid entries"""
661 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
663 for tag_type in (posix1e.ACL_USER,
665 for obj_id in range(5):
667 e.tag_type = tag_type
671 assert acl.valid(), ("ACL should be able to hold multiple"
672 " user/group entries")
674 def test_multiple_bad_entries(self):
675 """Test multiple invalid entries"""
676 for tag_type in (posix1e.ACL_USER,
678 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
681 e1.tag_type = tag_type
685 assert acl.valid(), ("ACL should be able to add a"
688 e2.tag_type = tag_type
691 ignore_ioerror(errno.EINVAL, acl.calc_mask)
692 assert not acl.valid(), ("ACL should not validate when"
693 " containing two duplicate entries")
695 # FreeBSD trips over itself here and can't delete the
696 # entry, even though it still exists.
697 ignore_ioerror(errno.EINVAL, acl.delete_entry, e2)
702 e1.tag_type = ACL_USER
708 e2.tag_type = ACL_GROUP
715 assert e1.tag_type == e2.tag_type
717 def test_copy_wrong_arg(self):
720 with pytest.raises(TypeError):
721 e.copy(object()) # type: ignore
723 def test_set_permset(self):
726 e1.tag_type = ACL_USER
732 e2.tag_type = ACL_GROUP
738 assert e2.permset.write
739 assert e2.tag_type == ACL_GROUP
741 def test_set_permset_wrong_arg(self):
744 with pytest.raises(TypeError):
745 e.permset = object() # type: ignore
747 def test_permset_creation(self):
754 def test_permset_creation_wrong_arg(self):
755 with pytest.raises(TypeError):
756 Permset(object()) # type: ignore
758 def test_permset_reinitialisations(self):
763 p.__init__(e) # type: ignore
764 with pytest.raises(ValueError, match="different parent"):
765 p.__init__(f) # type: ignore
768 def test_permset_reinit_leaks_refcount(self):
772 ref = sys.getrefcount(e)
773 p.__init__(e) # type: ignore
774 assert ref == sys.getrefcount(e), "Uh-oh, ref leaks..."
776 def test_permset(self):
777 """Test permissions"""
783 self.checkRef(str_ps)
784 for perm in PERMSETS:
786 txt = PERMSETS[perm][0]
787 self.checkRef(str_ps)
788 assert not ps.test(perm), ("Empty permission set should not"
789 " have permission '%s'" % txt)
791 assert ps.test(perm), ("Permission '%s' should exist"
792 " after addition" % txt)
794 self.checkRef(str_ps)
796 assert not ps.test(perm), ("Permission '%s' should not exist"
797 " after deletion" % txt)
799 def test_permset_via_accessors(self):
800 """Test permissions"""
806 self.checkRef(str_ps)
808 return PERMSETS[perm][1].__get__(ps) # type: ignore
809 def setter(parm, value):
810 return PERMSETS[perm][1].__set__(ps, value) # type: ignore
811 for perm in PERMSETS:
813 self.checkRef(str_ps)
814 txt = PERMSETS[perm][0]
815 assert not getter(perm), ("Empty permission set should not"
816 " have permission '%s'" % txt)
818 assert ps.test(perm), ("Permission '%s' should exist"
819 " after addition" % txt)
820 assert getter(perm), ("Permission '%s' should exist"
821 " after addition" % txt)
823 self.checkRef(str_ps)
825 assert not ps.test(perm), ("Permission '%s' should not exist"
826 " after deletion" % txt)
827 assert not getter(perm), ("Permission '%s' should not exist"
828 " after deletion" % txt)
830 def test_permset_invalid_type(self):
835 with pytest.raises(TypeError):
836 ps.add("foobar") # type: ignore
837 with pytest.raises(TypeError):
838 ps.delete("foobar") # type: ignore
839 with pytest.raises(TypeError):
840 ps.test("foobar") # type: ignore
841 with pytest.raises(ValueError):
842 ps.write = object() # type: ignore
844 def test_qualifier_values(self):
845 """Tests qualifier correct store/retrieval"""
848 # work around deprecation warnings
849 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
853 if tag == posix1e.ACL_USER:
854 regex = re.compile("user with uid %d" % qualifier)
856 regex = re.compile("group with gid %d" % qualifier)
858 e.qualifier = qualifier
859 except OverflowError:
860 # reached overflow condition, break
862 assert e.qualifier == qualifier
863 assert regex.search(str(e)) is not None
866 def test_qualifier_overflow(self):
867 """Tests qualifier overflow handling"""
870 qualifier = sys.maxsize * 2
871 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
873 with pytest.raises(OverflowError):
874 e.qualifier = qualifier
876 def test_negative_qualifier(self):
877 """Tests negative qualifier handling"""
878 # Note: this presumes that uid_t/gid_t in C are unsigned...
881 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
883 for qualifier in [-10, -5, -1]:
884 with pytest.raises(OverflowError):
885 e.qualifier = qualifier
887 def test_invalid_qualifier(self):
888 """Tests invalid qualifier handling"""
891 with pytest.raises(TypeError):
892 e.qualifier = object() # type: ignore
893 with pytest.raises((TypeError, AttributeError)):
896 def test_qualifier_on_wrong_tag(self):
897 """Tests qualifier setting on wrong tag"""
900 e.tag_type = posix1e.ACL_OTHER
901 with pytest.raises(TypeError):
903 with pytest.raises(TypeError):
906 @pytest.mark.parametrize("tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
907 def test_tag_types(self, tag):
908 """Tests tag type correct set/get"""
912 assert e.tag_type == tag
913 # check we can show all tag types without breaking
916 @pytest.mark.parametrize("src_tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
917 @pytest.mark.parametrize("dst_tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
918 def test_tag_overwrite(self, src_tag, dst_tag):
919 """Tests tag type correct set/get"""
923 assert e.tag_type == src_tag
926 assert e.tag_type == dst_tag
929 def test_invalid_tags(self):
930 """Tests tag type incorrect set/get"""
933 with pytest.raises(TypeError):
934 e.tag_type = object() # type: ignore
935 e.tag_type = posix1e.ACL_USER_OBJ
936 # For some reason, PyPy raises AttributeError. Strange...
937 with pytest.raises((TypeError, AttributeError)):
940 def test_tag_wrong_overwrite(self):
943 e.tag_type = posix1e.ACL_USER_OBJ
944 tag = max(ALL_TAG_VALUES) + 1
945 with pytest.raises(EnvironmentError):
947 # Check tag is still valid.
948 assert e.tag_type == posix1e.ACL_USER_OBJ
950 if __name__ == "__main__":