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 # This is acl_check, but should actually be have_linux...
276 def test_from_acl(self):
277 """Test creating an ACL from an existing ACL"""
278 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
279 acl2 = posix1e.ACL(acl=acl1)
282 def test_from_acl_via_str(self):
283 # This is needed for not HAVE_LINUX cases.
284 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
285 acl2 = posix1e.ACL(acl=acl1)
286 assert str(acl1) == str(acl2)
288 def test_invalid_creation_params(self, testdir):
289 """Test that creating an ACL from multiple objects fails"""
290 fd, _ = get_file(testdir)
291 with pytest.raises(ValueError):
292 posix1e.ACL(text=BASIC_ACL_TEXT, fd=fd)
294 def test_invalid_value_creation(self):
295 """Test that creating an ACL from wrong specification fails"""
296 with pytest.raises(EnvironmentError):
297 posix1e.ACL(text="foobar")
298 with pytest.raises(TypeError):
299 posix1e.ACL(foo="bar")
301 def test_uninit(self):
302 """Checks that uninit is actually empty init"""
303 acl = posix1e.ACL.__new__(posix1e.ACL)
304 assert not acl.valid()
309 def test_double_init(self):
310 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
312 acl1.__init__(text=BASIC_ACL_TEXT) # type: ignore
315 @pytest.mark.xfail(reason="Unreliable test, re-init doesn't always invalidate children")
316 def test_double_init_breaks_children(self):
319 e.permset.write = True
320 acl.__init__() # type: ignore
321 with pytest.raises(EnvironmentError):
322 e.permset.write = False
325 class TestAclExtensions:
326 """ACL extensions checks"""
328 @require_acl_from_mode
329 def test_from_mode(self):
330 """Test loading ACLs from an octal mode"""
331 acl1 = posix1e.ACL(mode=0o644)
335 def test_acl_check(self):
336 """Test the acl_check method"""
337 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
338 assert not acl1.check()
341 assert c == (ACL_MISS_ERROR, 0)
342 assert isinstance(c, tuple)
343 assert c[0] == ACL_MISS_ERROR
346 assert c == (ACL_ENTRY_ERROR, 0)
348 def test_applyto(self, subject):
349 """Test the apply_to function"""
350 # TODO: add read/compare with before, once ACL can be init'ed
352 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
353 basic_acl.applyto(subject)
354 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
355 assert enhanced_acl.valid()
356 enhanced_acl.applyto(subject)
358 def test_apply_to_with_wrong_object(self):
359 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
361 with pytest.raises(TypeError):
362 acl1.applyto(object())
363 with pytest.raises(TypeError):
364 acl1.applyto(object(), object()) # type: ignore
366 def test_apply_to_fail(self, testdir):
367 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
369 fd, fname = get_file(testdir)
371 with pytest.raises(IOError):
373 with pytest.raises(IOError, match="no-such-file"):
374 acl1.applyto(fname+".no-such-file")
376 @require_extended_check
377 def test_applyto_extended(self, subject):
378 """Test the acl_extended function"""
379 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
380 basic_acl.applyto(subject)
381 assert not has_extended(subject)
382 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
383 assert enhanced_acl.valid()
384 enhanced_acl.applyto(subject)
385 assert has_extended(subject)
387 @require_extended_check
388 @pytest.mark.parametrize(
389 "gen", [ get_file_and_symlink, get_file_and_fobject ])
390 def test_applyto_extended_mixed(self, testdir, gen):
391 """Test the acl_extended function"""
392 with gen(testdir) as (a, b):
393 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
396 assert not has_extended(item)
397 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
398 assert enhanced_acl.valid()
399 enhanced_acl.applyto(b)
401 assert has_extended(item)
403 @require_extended_check
404 def test_extended_fail(self, testdir):
405 fd, fname = get_file(testdir)
407 with pytest.raises(IOError):
409 with pytest.raises(IOError, match="no-such-file"):
410 has_extended(fname+".no-such-file")
412 @require_extended_check
413 def test_extended_arg_handling(self):
414 with pytest.raises(TypeError):
415 has_extended() # type: ignore
416 with pytest.raises(TypeError):
417 has_extended(object()) # type: ignore
420 def test_equiv_mode(self):
421 """Test the equiv_mode function"""
422 if HAS_ACL_FROM_MODE:
423 for mode in 0o644, 0o755:
424 acl = posix1e.ACL(mode=mode)
425 assert acl.equiv_mode() == mode
426 acl = posix1e.ACL(text="u::rw,g::r,o::r")
427 assert acl.equiv_mode() == 0o644
428 acl = posix1e.ACL(text="u::rx,g::-,o::-")
429 assert acl.equiv_mode() == 0o500
432 @pytest.mark.xfail(reason="It seems equiv mode always passes, even for empty ACLs")
433 def test_equiv_mode_invalid(self):
434 """Test equiv_mode on invalid ACLs"""
436 with pytest.raises(EnvironmentError):
440 def test_to_any_text(self):
441 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
443 acl.to_any_text(options=posix1e.TEXT_ABBREVIATE)
444 assert b"user::" in acl.to_any_text()
447 def test_to_any_text_wrong_args(self):
448 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
449 with pytest.raises(TypeError):
450 acl.to_any_text(foo="bar") # type: ignore
454 def test_rich_compare(self):
455 acl1 = posix1e.ACL(text="u::rw,g::r,o::r")
456 acl2 = posix1e.ACL(acl=acl1)
457 acl3 = posix1e.ACL(text="u::rw,g::rw,o::r")
460 with pytest.raises(TypeError):
461 acl1 < acl2 # type: ignore
462 with pytest.raises(TypeError):
463 acl1 >= acl3 # type: ignore
464 assert acl1 != True # type: ignore
465 assert not (acl1 == 1) # type: ignore
466 with pytest.raises(TypeError):
467 acl1 > True # type: ignore
470 def test_acl_iterator(self):
471 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
473 assert entry.parent is acl
476 def test_acl_copy_ext(self):
477 a = posix1e.ACL(text=BASIC_ACL_TEXT)
479 c = posix1e.ACL(acl=b)
482 state = a.__getstate__()
483 b.__setstate__(state)
491 def test_delete_default(self, testdir):
492 """Test removing the default ACL"""
493 with get_dir(testdir) as dname:
494 posix1e.delete_default(dname)
496 def test_delete_default_fail(self, testdir):
497 """Test removing the default ACL"""
498 with get_file_name(testdir) as fname:
499 with pytest.raises(IOError, match="no-such-file"):
500 posix1e.delete_default(fname+".no-such-file")
503 def test_delete_default_wrong_arg(self):
504 with pytest.raises(TypeError):
505 posix1e.delete_default(object()) # type: ignore
507 def test_reapply(self, testdir):
508 """Test re-applying an ACL"""
509 fd, fname = get_file(testdir)
510 acl1 = posix1e.ACL(fd=fd)
513 with get_dir(testdir) as dname:
514 acl2 = posix1e.ACL(file=fname)
520 class TestModification:
521 """ACL modification tests"""
523 def checkRef(self, obj):
524 """Checks if a given obj has a 'sane' refcount"""
525 if platform.python_implementation() == "PyPy":
527 ref_cnt = sys.getrefcount(obj)
528 # FIXME: hardcoded value for the max ref count... but I've
529 # seen it overflow on bad reference counting, so it's better
531 if ref_cnt < 2 or ref_cnt > 1024:
532 pytest.fail("Wrong reference count, expected 2-1024 and got %d" %
536 """Test str() of an ACL."""
537 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
539 self.checkRef(str_acl)
541 def test_append(self):
542 """Test append a new Entry to the ACL"""
545 e.tag_type = posix1e.ACL_OTHER
546 ignore_ioerror(errno.EINVAL, acl.calc_mask)
548 self.checkRef(str_format)
550 ignore_ioerror(errno.EINVAL, acl.calc_mask)
551 assert not acl.valid()
553 def test_wrong_append(self):
554 """Test append a new Entry to the ACL based on wrong object type"""
556 with pytest.raises(TypeError):
557 acl.append(object()) # type: ignore
559 @pytest.mark.xfail(reason="Behaviour not conform to specification")
560 def test_append_invalid_source(self):
565 with pytest.raises(EnvironmentError):
566 f.permset.write = True
567 with pytest.raises(EnvironmentError):
570 def test_entry_creation(self):
572 e = posix1e.Entry(acl)
573 ignore_ioerror(errno.EINVAL, acl.calc_mask)
575 self.checkRef(str_format)
577 def test_entry_failed_creation(self):
578 # Checks for partial initialisation and deletion on error
580 with pytest.raises(TypeError):
581 posix1e.Entry(object()) # type: ignore
583 def test_entry_reinitialisations(self):
587 e.__init__(a) # type: ignore
588 with pytest.raises(ValueError, match="different parent"):
589 e.__init__(b) # type: ignore
592 def test_entry_reinit_leaks_refcount(self):
595 ref = sys.getrefcount(acl)
596 e.__init__(acl) # type: ignore
597 assert ref == sys.getrefcount(acl), "Uh-oh, ref leaks..."
599 def test_delete(self):
600 """Test delete Entry from the ACL"""
603 e.tag_type = posix1e.ACL_OTHER
604 ignore_ioerror(errno.EINVAL, acl.calc_mask)
606 ignore_ioerror(errno.EINVAL, acl.calc_mask)
608 def test_double_delete(self):
609 """Test delete Entry from the ACL"""
610 # This is not entirely valid/correct, since the entry object
611 # itself is invalid after the first deletion, so we're
612 # actually testing deleting an invalid object, not a
613 # non-existing entry...
616 e.tag_type = posix1e.ACL_OTHER
617 ignore_ioerror(errno.EINVAL, acl.calc_mask)
619 ignore_ioerror(errno.EINVAL, acl.calc_mask)
620 with pytest.raises(EnvironmentError):
623 def test_delete_unowned(self):
624 """Test delete Entry from the ACL"""
628 e.tag_type = posix1e.ACL_OTHER
629 with pytest.raises(ValueError, match="un-owned entry"):
632 # This currently fails as this deletion seems to be accepted :/
633 @pytest.mark.xfail(reason="Entry deletion is unreliable")
634 def testDeleteInvalidEntry(self):
635 """Test delete foreign Entry from the ACL"""
639 e.tag_type = posix1e.ACL_OTHER
640 ignore_ioerror(errno.EINVAL, acl1.calc_mask)
641 with pytest.raises(EnvironmentError):
644 def test_delete_invalid_object(self):
645 """Test delete a non-Entry from the ACL"""
647 with pytest.raises(TypeError):
648 acl.delete_entry(object()) # type: ignore
650 def test_double_entries(self):
651 """Test double entries"""
652 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
654 for tag_type in (posix1e.ACL_USER_OBJ, posix1e.ACL_GROUP_OBJ,
657 e.tag_type = tag_type
659 assert not acl.valid(), ("ACL containing duplicate entries"
660 " should not be valid")
663 def test_multiple_good_entries(self):
664 """Test multiple valid entries"""
665 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
667 for tag_type in (posix1e.ACL_USER,
669 for obj_id in range(5):
671 e.tag_type = tag_type
675 assert acl.valid(), ("ACL should be able to hold multiple"
676 " user/group entries")
678 def test_multiple_bad_entries(self):
679 """Test multiple invalid entries"""
680 for tag_type in (posix1e.ACL_USER,
682 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
685 e1.tag_type = tag_type
689 assert acl.valid(), ("ACL should be able to add a"
692 e2.tag_type = tag_type
695 ignore_ioerror(errno.EINVAL, acl.calc_mask)
696 assert not acl.valid(), ("ACL should not validate when"
697 " containing two duplicate entries")
699 # FreeBSD trips over itself here and can't delete the
700 # entry, even though it still exists.
701 ignore_ioerror(errno.EINVAL, acl.delete_entry, e2)
706 e1.tag_type = ACL_USER
712 e2.tag_type = ACL_GROUP
719 assert e1.tag_type == e2.tag_type
721 def test_copy_wrong_arg(self):
724 with pytest.raises(TypeError):
725 e.copy(object()) # type: ignore
727 def test_set_permset(self):
730 e1.tag_type = ACL_USER
736 e2.tag_type = ACL_GROUP
742 assert e2.permset.write
743 assert e2.tag_type == ACL_GROUP
745 def test_set_permset_wrong_arg(self):
748 with pytest.raises(TypeError):
749 e.permset = object() # type: ignore
751 def test_permset_creation(self):
758 def test_permset_creation_wrong_arg(self):
759 with pytest.raises(TypeError):
760 Permset(object()) # type: ignore
762 def test_permset_reinitialisations(self):
767 p.__init__(e) # type: ignore
768 with pytest.raises(ValueError, match="different parent"):
769 p.__init__(f) # type: ignore
772 def test_permset_reinit_leaks_refcount(self):
776 ref = sys.getrefcount(e)
777 p.__init__(e) # type: ignore
778 assert ref == sys.getrefcount(e), "Uh-oh, ref leaks..."
780 def test_permset(self):
781 """Test permissions"""
787 self.checkRef(str_ps)
788 for perm in PERMSETS:
790 txt = PERMSETS[perm][0]
791 self.checkRef(str_ps)
792 assert not ps.test(perm), ("Empty permission set should not"
793 " have permission '%s'" % txt)
795 assert ps.test(perm), ("Permission '%s' should exist"
796 " after addition" % txt)
798 self.checkRef(str_ps)
800 assert not ps.test(perm), ("Permission '%s' should not exist"
801 " after deletion" % txt)
803 def test_permset_via_accessors(self):
804 """Test permissions"""
810 self.checkRef(str_ps)
812 return PERMSETS[perm][1].__get__(ps) # type: ignore
813 def setter(parm, value):
814 return PERMSETS[perm][1].__set__(ps, value) # type: ignore
815 for perm in PERMSETS:
817 self.checkRef(str_ps)
818 txt = PERMSETS[perm][0]
819 assert not getter(perm), ("Empty permission set should not"
820 " have permission '%s'" % txt)
822 assert ps.test(perm), ("Permission '%s' should exist"
823 " after addition" % txt)
824 assert getter(perm), ("Permission '%s' should exist"
825 " after addition" % txt)
827 self.checkRef(str_ps)
829 assert not ps.test(perm), ("Permission '%s' should not exist"
830 " after deletion" % txt)
831 assert not getter(perm), ("Permission '%s' should not exist"
832 " after deletion" % txt)
834 def test_permset_invalid_type(self):
839 with pytest.raises(TypeError):
840 ps.add("foobar") # type: ignore
841 with pytest.raises(TypeError):
842 ps.delete("foobar") # type: ignore
843 with pytest.raises(TypeError):
844 ps.test("foobar") # type: ignore
845 with pytest.raises(ValueError):
846 ps.write = object() # type: ignore
848 def test_qualifier_values(self):
849 """Tests qualifier correct store/retrieval"""
852 # work around deprecation warnings
853 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
857 regex = re.compile("(user|group) with (u|g)id %d" % qualifier)
859 e.qualifier = qualifier
860 except OverflowError:
861 # reached overflow condition, break
863 assert e.qualifier == qualifier
864 assert regex.search(str(e)) is not None
867 def test_qualifier_overflow(self):
868 """Tests qualifier overflow handling"""
871 # the uid_t/gid_t are unsigned, so they can hold slightly more
872 # than sys.maxsize*2 (on Linux).
873 qualifier = (sys.maxsize + 1) * 2
874 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
876 with pytest.raises(OverflowError):
877 e.qualifier = qualifier
879 def test_qualifier_underflow(self):
880 """Tests negative qualifier handling"""
881 # Note: this presumes that uid_t/gid_t in C are unsigned...
884 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
886 for qualifier in [-10, -5, -1]:
887 with pytest.raises(OverflowError):
888 e.qualifier = qualifier
890 def test_invalid_qualifier(self):
891 """Tests invalid qualifier handling"""
894 with pytest.raises(TypeError):
895 e.qualifier = object() # type: ignore
896 with pytest.raises((TypeError, AttributeError)):
899 def test_qualifier_on_wrong_tag(self):
900 """Tests qualifier setting on wrong tag"""
903 e.tag_type = posix1e.ACL_OTHER
904 with pytest.raises(TypeError):
906 with pytest.raises(TypeError):
909 @pytest.mark.parametrize("tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
910 def test_tag_types(self, tag):
911 """Tests tag type correct set/get"""
915 assert e.tag_type == tag
916 # check we can show all tag types without breaking
919 @pytest.mark.parametrize("src_tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
920 @pytest.mark.parametrize("dst_tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
921 def test_tag_overwrite(self, src_tag, dst_tag):
922 """Tests tag type correct set/get"""
926 assert e.tag_type == src_tag
929 assert e.tag_type == dst_tag
932 def test_invalid_tags(self):
933 """Tests tag type incorrect set/get"""
936 with pytest.raises(TypeError):
937 e.tag_type = object() # type: ignore
938 e.tag_type = posix1e.ACL_USER_OBJ
939 # For some reason, PyPy raises AttributeError. Strange...
940 with pytest.raises((TypeError, AttributeError)):
943 def test_tag_wrong_overwrite(self):
946 e.tag_type = posix1e.ACL_USER_OBJ
947 tag = max(ALL_TAG_VALUES) + 1
948 with pytest.raises(EnvironmentError):
950 # Check tag is still valid.
951 assert e.tag_type == posix1e.ACL_USER_OBJ
953 if __name__ == "__main__":