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::-"
43 TEXT_0755 = "u::rwx,g::rx,o::rx"
45 # Permset permission information
47 (ACL_READ, "read", Permset.read),
48 (ACL_WRITE, "write", Permset.write),
49 (ACL_EXECUTE, "execute", Permset.execute),
52 PERMSETS_IDS = [p[1] for p in PERMSETS]
55 (posix1e.ACL_USER, "user"),
56 (posix1e.ACL_GROUP, "group"),
57 (posix1e.ACL_USER_OBJ, "user object"),
58 (posix1e.ACL_GROUP_OBJ, "group object"),
59 (posix1e.ACL_MASK, "mask"),
60 (posix1e.ACL_OTHER, "other"),
63 ALL_TAG_VALUES = [i[0] for i in ALL_TAGS]
64 ALL_TAG_DESCS = [i[1] for i in ALL_TAGS]
66 # Fixtures and helpers
68 def ignore_ioerror(errnum, fn, *args, **kwargs):
69 """Call a function while ignoring some IOErrors.
71 This is needed as some OSes (e.g. FreeBSD) return failure (EINVAL)
72 when doing certain operations on an invalid ACL.
77 except IOError as err:
78 if err.errno == errnum:
84 """per-test temp dir based in TEST_DIR"""
85 with tempfile.TemporaryDirectory(dir=TEST_DIR) as dname:
89 fh, fname = tempfile.mkstemp(".test", "xattr-", path)
92 @contextlib.contextmanager
93 def get_file_name(path):
94 fh, fname = get_file(path)
98 @contextlib.contextmanager
99 def get_file_fd(path):
100 fd = get_file(path)[0]
104 @contextlib.contextmanager
105 def get_file_object(path):
106 fd = get_file(path)[0]
107 with os.fdopen(fd) as f:
110 @contextlib.contextmanager
112 yield tempfile.mkdtemp(".test", "xattr-", path)
114 def get_symlink(path, dangling=True):
115 """create a symlink"""
116 fh, fname = get_file(path)
120 sname = fname + ".symlink"
121 os.symlink(fname, sname)
124 @contextlib.contextmanager
125 def get_valid_symlink(path):
126 yield get_symlink(path, dangling=False)[1]
128 @contextlib.contextmanager
129 def get_dangling_symlink(path):
130 yield get_symlink(path, dangling=True)[1]
132 @contextlib.contextmanager
133 def get_file_and_symlink(path):
134 yield get_symlink(path, dangling=False)
136 @contextlib.contextmanager
137 def get_file_and_fobject(path):
138 fh, fname = get_file(path)
139 with os.fdopen(fh) as fo:
142 # Wrappers that build upon existing values
144 def as_wrapper(call, fn, closer=None):
145 @contextlib.contextmanager
147 with call(path) as r:
150 if closer is not None:
155 return as_wrapper(call, lambda r: r.encode())
158 return as_wrapper(call, pathlib.PurePath)
160 def as_iostream(call):
161 opener = lambda f: io.open(f, "r")
162 closer = lambda r: r.close()
163 return as_wrapper(call, opener, closer)
165 NOT_BEFORE_36 = pytest.mark.xfail(condition="sys.version_info < (3,6)",
167 NOT_PYPY = pytest.mark.xfail(condition="platform.python_implementation() == 'PyPy'",
170 require_acl_from_mode = pytest.mark.skipif("not HAS_ACL_FROM_MODE")
171 require_acl_check = pytest.mark.skipif("not HAS_ACL_CHECK")
172 require_acl_entry = pytest.mark.skipif("not HAS_ACL_ENTRY")
173 require_extended_check = pytest.mark.skipif("not HAS_EXTENDED_CHECK")
174 require_equiv_mode = pytest.mark.skipif("not HAS_EQUIV_MODE")
175 require_copy_ext = pytest.mark.skipif("not HAS_COPY_EXT")
177 # Note: ACLs are valid only for files/directories, not symbolic links
178 # themselves, so we only create valid symlinks.
181 as_bytes(get_file_name),
182 pytest.param(as_fspath(get_file_name),
183 marks=[NOT_BEFORE_36, NOT_PYPY]),
186 pytest.param(as_fspath(get_dir),
187 marks=[NOT_BEFORE_36, NOT_PYPY]),
189 as_bytes(get_valid_symlink),
190 pytest.param(as_fspath(get_valid_symlink),
191 marks=[NOT_BEFORE_36, NOT_PYPY]),
202 "file via symlink (bytes)",
203 "file via symlink (path)",
209 as_iostream(get_file_name),
218 ALL_P = FILE_P + FD_P
219 ALL_D = FILE_D + FD_D
221 @pytest.fixture(params=FILE_P, ids=FILE_D)
222 def file_subject(testdir, request):
223 with request.param(testdir) as value:
226 @pytest.fixture(params=FD_P, ids=FD_D)
227 def fd_subject(testdir, request):
228 with request.param(testdir) as value:
231 @pytest.fixture(params=ALL_P, ids=ALL_D)
232 def subject(testdir, request):
233 with request.param(testdir) as value:
238 """Load/create tests"""
239 def test_from_file(self, file_subject):
240 """Test loading ACLs from a file/directory"""
241 acl = posix1e.ACL(file=file_subject)
244 def test_from_dir(self, testdir):
245 """Test loading ACLs from a directory"""
246 with get_dir(testdir) as dname:
247 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, fd_subject):
252 """Test loading ACLs from a file descriptor"""
253 acl = posix1e.ACL(fd=fd_subject)
256 def test_from_nonexisting(self, testdir):
257 _, fname = get_file(testdir)
258 with pytest.raises(IOError):
259 posix1e.ACL(file="fname"+".no-such-file")
260 with pytest.raises(IOError):
261 posix1e.ACL(filedef="fname"+".no-such-file")
263 def test_from_invalid_fd(self, testdir):
264 fd, _ = get_file(testdir)
266 with pytest.raises(IOError):
269 def test_from_empty_invalid(self):
270 """Test creating an empty ACL"""
272 assert not acl1.valid()
274 def test_from_text(self):
275 """Test creating an ACL from text"""
276 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
279 # This is acl_check, but should actually be have_linux...
281 def test_from_acl(self):
282 """Test creating an ACL from an existing ACL"""
283 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
284 acl2 = posix1e.ACL(acl=acl1)
287 def test_from_acl_via_str(self):
288 # This is needed for not HAVE_LINUX cases.
289 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
290 acl2 = posix1e.ACL(acl=acl1)
291 assert str(acl1) == str(acl2)
293 def test_invalid_creation_params(self, testdir):
294 """Test that creating an ACL from multiple objects fails"""
295 fd, _ = get_file(testdir)
296 with pytest.raises(ValueError):
297 posix1e.ACL(text=BASIC_ACL_TEXT, fd=fd)
299 def test_invalid_value_creation(self):
300 """Test that creating an ACL from wrong specification fails"""
301 with pytest.raises(EnvironmentError):
302 posix1e.ACL(text="foobar")
303 with pytest.raises(TypeError):
304 posix1e.ACL(foo="bar")
306 def test_uninit(self):
307 """Checks that uninit is actually empty init"""
308 acl = posix1e.ACL.__new__(posix1e.ACL)
309 assert not acl.valid()
314 def test_double_init(self):
315 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
317 acl1.__init__(text=BASIC_ACL_TEXT) # type: ignore
319 acl2 = ACL(text=TEXT_0755)
321 acl1.__init__(acl=acl2) # type: ignore
324 def test_reinit_failure_noop(self):
325 a = posix1e.ACL(text=TEXT_0755)
326 b = posix1e.ACL(acl=a)
328 with pytest.raises(IOError):
329 a.__init__(text='foobar')
332 @pytest.mark.xfail(reason="Unreliable test, re-init doesn't always invalidate children")
333 def test_double_init_breaks_children(self):
336 e.permset.write = True
337 acl.__init__() # type: ignore
338 with pytest.raises(EnvironmentError):
339 e.permset.write = False
342 class TestAclExtensions:
343 """ACL extensions checks"""
345 @require_acl_from_mode
346 def test_from_mode(self):
347 """Test loading ACLs from an octal mode"""
348 acl1 = posix1e.ACL(mode=0o644)
352 def test_acl_check(self):
353 """Test the acl_check method"""
354 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
355 assert not acl1.check()
358 assert c == (ACL_MISS_ERROR, 0)
359 assert isinstance(c, tuple)
360 assert c[0] == ACL_MISS_ERROR
363 assert c == (ACL_ENTRY_ERROR, 0)
365 def test_applyto(self, subject):
366 """Test the apply_to function"""
367 # TODO: add read/compare with before, once ACL can be init'ed
369 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
370 basic_acl.applyto(subject)
371 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
372 assert enhanced_acl.valid()
373 enhanced_acl.applyto(subject)
375 def test_apply_to_with_wrong_object(self):
376 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
378 with pytest.raises(TypeError):
379 acl1.applyto(object())
380 with pytest.raises(TypeError):
381 acl1.applyto(object(), object()) # type: ignore
383 def test_apply_to_fail(self, testdir):
384 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
386 fd, fname = get_file(testdir)
388 with pytest.raises(IOError):
390 with pytest.raises(IOError, match="no-such-file"):
391 acl1.applyto(fname+".no-such-file")
393 @require_extended_check
394 def test_applyto_extended(self, subject):
395 """Test the acl_extended function"""
396 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
397 basic_acl.applyto(subject)
398 assert not has_extended(subject)
399 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
400 assert enhanced_acl.valid()
401 enhanced_acl.applyto(subject)
402 assert has_extended(subject)
404 @require_extended_check
405 @pytest.mark.parametrize(
406 "gen", [ get_file_and_symlink, get_file_and_fobject ])
407 def test_applyto_extended_mixed(self, testdir, gen):
408 """Test the acl_extended function"""
409 with gen(testdir) as (a, b):
410 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
413 assert not has_extended(item)
414 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
415 assert enhanced_acl.valid()
416 enhanced_acl.applyto(b)
418 assert has_extended(item)
420 @require_extended_check
421 def test_extended_fail(self, testdir):
422 fd, fname = get_file(testdir)
424 with pytest.raises(IOError):
426 with pytest.raises(IOError, match="no-such-file"):
427 has_extended(fname+".no-such-file")
429 @require_extended_check
430 def test_extended_arg_handling(self):
431 with pytest.raises(TypeError):
432 has_extended() # type: ignore
433 with pytest.raises(TypeError):
434 has_extended(object()) # type: ignore
437 def test_equiv_mode(self):
438 """Test the equiv_mode function"""
439 if HAS_ACL_FROM_MODE:
440 for mode in 0o644, 0o755:
441 acl = posix1e.ACL(mode=mode)
442 assert acl.equiv_mode() == mode
443 acl = posix1e.ACL(text="u::rw,g::r,o::r")
444 assert acl.equiv_mode() == 0o644
445 acl = posix1e.ACL(text="u::rx,g::-,o::-")
446 assert acl.equiv_mode() == 0o500
449 @pytest.mark.xfail(reason="It seems equiv mode always passes, even for empty ACLs")
450 def test_equiv_mode_invalid(self):
451 """Test equiv_mode on invalid ACLs"""
453 with pytest.raises(EnvironmentError):
457 def test_to_any_text(self):
458 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
460 acl.to_any_text(options=posix1e.TEXT_ABBREVIATE)
461 assert b"user::" in acl.to_any_text()
464 def test_to_any_text_wrong_args(self):
465 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
466 with pytest.raises(TypeError):
467 acl.to_any_text(foo="bar") # type: ignore
471 def test_rich_compare(self):
472 acl1 = posix1e.ACL(text="u::rw,g::r,o::r")
473 acl2 = posix1e.ACL(acl=acl1)
474 acl3 = posix1e.ACL(text="u::rw,g::rw,o::r")
477 with pytest.raises(TypeError):
478 acl1 < acl2 # type: ignore
479 with pytest.raises(TypeError):
480 acl1 >= acl3 # type: ignore
481 assert acl1 != True # type: ignore
482 assert not (acl1 == 1) # type: ignore
483 with pytest.raises(TypeError):
484 acl1 > True # type: ignore
487 def test_acl_iterator(self):
488 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
490 assert entry.parent is acl
493 def test_acl_copy_ext(self):
494 a = posix1e.ACL(text=BASIC_ACL_TEXT)
496 c = posix1e.ACL(acl=b)
499 state = a.__getstate__()
500 b.__setstate__(state)
505 def test_acl_copy_ext_args(self):
507 with pytest.raises(TypeError):
511 def test_acl_init_copy_ext(self):
512 a = posix1e.ACL(text=BASIC_ACL_TEXT)
514 c = posix1e.ACL(data=a.__getstate__())
519 def test_acl_init_copy_ext_invalid(self):
520 with pytest.raises(IOError):
521 posix1e.ACL(data=b"foobar")
527 def test_delete_default(self, testdir):
528 """Test removing the default ACL"""
529 with get_dir(testdir) as dname:
530 posix1e.delete_default(dname)
532 def test_delete_default_fail(self, testdir):
533 """Test removing the default ACL"""
534 with get_file_name(testdir) as fname:
535 with pytest.raises(IOError, match="no-such-file"):
536 posix1e.delete_default(fname+".no-such-file")
539 def test_delete_default_wrong_arg(self):
540 with pytest.raises(TypeError):
541 posix1e.delete_default(object()) # type: ignore
543 def test_reapply(self, testdir):
544 """Test re-applying an ACL"""
545 fd, fname = get_file(testdir)
546 acl1 = posix1e.ACL(fd=fd)
549 with get_dir(testdir) as dname:
550 acl2 = posix1e.ACL(file=fname)
556 class TestModification:
557 """ACL modification tests"""
559 def checkRef(self, obj):
560 """Checks if a given obj has a 'sane' refcount"""
561 if platform.python_implementation() == "PyPy":
563 ref_cnt = sys.getrefcount(obj)
564 # FIXME: hardcoded value for the max ref count... but I've
565 # seen it overflow on bad reference counting, so it's better
567 if ref_cnt < 2 or ref_cnt > 1024:
568 pytest.fail("Wrong reference count, expected 2-1024 and got %d" %
572 """Test str() of an ACL."""
573 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
575 self.checkRef(str_acl)
577 def test_append(self):
578 """Test append a new Entry to the ACL"""
581 e.tag_type = posix1e.ACL_OTHER
582 ignore_ioerror(errno.EINVAL, acl.calc_mask)
584 self.checkRef(str_format)
586 ignore_ioerror(errno.EINVAL, acl.calc_mask)
587 assert not acl.valid()
589 def test_wrong_append(self):
590 """Test append a new Entry to the ACL based on wrong object type"""
592 with pytest.raises(TypeError):
593 acl.append(object()) # type: ignore
595 @pytest.mark.xfail(reason="Behaviour not conform to specification")
596 def test_append_invalid_source(self):
601 with pytest.raises(EnvironmentError):
602 f.permset.write = True
603 with pytest.raises(EnvironmentError):
606 def test_entry_creation(self):
608 e = posix1e.Entry(acl)
609 ignore_ioerror(errno.EINVAL, acl.calc_mask)
611 self.checkRef(str_format)
613 def test_entry_failed_creation(self):
614 # Checks for partial initialisation and deletion on error
616 with pytest.raises(TypeError):
617 posix1e.Entry(object()) # type: ignore
619 def test_entry_reinitialisations(self):
623 e.__init__(a) # type: ignore
624 with pytest.raises(ValueError, match="different parent"):
625 e.__init__(b) # type: ignore
628 def test_entry_reinit_leaks_refcount(self):
631 ref = sys.getrefcount(acl)
632 e.__init__(acl) # type: ignore
633 assert ref == sys.getrefcount(acl), "Uh-oh, ref leaks..."
635 def test_delete(self):
636 """Test delete Entry from the ACL"""
639 e.tag_type = posix1e.ACL_OTHER
640 ignore_ioerror(errno.EINVAL, acl.calc_mask)
642 ignore_ioerror(errno.EINVAL, acl.calc_mask)
644 def test_double_delete(self):
645 """Test delete Entry from the ACL"""
646 # This is not entirely valid/correct, since the entry object
647 # itself is invalid after the first deletion, so we're
648 # actually testing deleting an invalid object, not a
649 # non-existing entry...
652 e.tag_type = posix1e.ACL_OTHER
653 ignore_ioerror(errno.EINVAL, acl.calc_mask)
655 ignore_ioerror(errno.EINVAL, acl.calc_mask)
656 with pytest.raises(EnvironmentError):
659 def test_delete_unowned(self):
660 """Test delete Entry from the ACL"""
664 e.tag_type = posix1e.ACL_OTHER
665 with pytest.raises(ValueError, match="un-owned entry"):
668 # This currently fails as this deletion seems to be accepted :/
669 @pytest.mark.xfail(reason="Entry deletion is unreliable")
670 def testDeleteInvalidEntry(self):
671 """Test delete foreign Entry from the ACL"""
675 e.tag_type = posix1e.ACL_OTHER
676 ignore_ioerror(errno.EINVAL, acl1.calc_mask)
677 with pytest.raises(EnvironmentError):
680 def test_delete_invalid_object(self):
681 """Test delete a non-Entry from the ACL"""
683 with pytest.raises(TypeError):
684 acl.delete_entry(object()) # type: ignore
686 def test_double_entries(self):
687 """Test double entries"""
688 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
690 for tag_type in (posix1e.ACL_USER_OBJ, posix1e.ACL_GROUP_OBJ,
693 e.tag_type = tag_type
695 assert not acl.valid(), ("ACL containing duplicate entries"
696 " should not be valid")
699 def test_multiple_good_entries(self):
700 """Test multiple valid entries"""
701 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
703 for tag_type in (posix1e.ACL_USER,
705 for obj_id in range(5):
707 e.tag_type = tag_type
711 assert acl.valid(), ("ACL should be able to hold multiple"
712 " user/group entries")
714 def test_multiple_bad_entries(self):
715 """Test multiple invalid entries"""
716 for tag_type in (posix1e.ACL_USER,
718 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
721 e1.tag_type = tag_type
725 assert acl.valid(), ("ACL should be able to add a"
728 e2.tag_type = tag_type
731 ignore_ioerror(errno.EINVAL, acl.calc_mask)
732 assert not acl.valid(), ("ACL should not validate when"
733 " containing two duplicate entries")
735 # FreeBSD trips over itself here and can't delete the
736 # entry, even though it still exists.
737 ignore_ioerror(errno.EINVAL, acl.delete_entry, e2)
742 e1.tag_type = ACL_USER
748 e2.tag_type = ACL_GROUP
755 assert e1.tag_type == e2.tag_type
757 def test_copy_wrong_arg(self):
760 with pytest.raises(TypeError):
761 e.copy(object()) # type: ignore
763 def test_set_permset(self):
766 e1.tag_type = ACL_USER
772 e2.tag_type = ACL_GROUP
778 assert e2.permset.write
779 assert e2.tag_type == ACL_GROUP
781 def test_set_permset_wrong_arg(self):
784 with pytest.raises(TypeError):
785 e.permset = object() # type: ignore
787 def test_permset_creation(self):
794 def test_permset_creation_wrong_arg(self):
795 with pytest.raises(TypeError):
796 Permset(object()) # type: ignore
798 def test_permset_reinitialisations(self):
803 p.__init__(e) # type: ignore
804 with pytest.raises(ValueError, match="different parent"):
805 p.__init__(f) # type: ignore
808 def test_permset_reinit_leaks_refcount(self):
812 ref = sys.getrefcount(e)
813 p.__init__(e) # type: ignore
814 assert ref == sys.getrefcount(e), "Uh-oh, ref leaks..."
816 @pytest.mark.parametrize("perm, txt, accessor",
817 PERMSETS, ids=PERMSETS_IDS)
818 def test_permset(self, perm, txt, accessor):
819 """Test permissions"""
826 self.checkRef(str_ps)
827 assert not ps.test(perm), ("Empty permission set should not"
828 " have permission '%s'" % txt)
830 assert ps.test(perm), ("Permission '%s' should exist"
831 " after addition" % txt)
833 self.checkRef(str_ps)
835 assert not ps.test(perm), ("Permission '%s' should not exist"
836 " after deletion" % txt)
838 assert ps.test(perm), ("Permission '%s' should exist"
839 " after addition" % txt)
841 assert not ps.test(perm), ("Permission '%s' should not exist"
842 " after clearing" % txt)
846 @pytest.mark.parametrize("perm, txt, accessor",
847 PERMSETS, ids=PERMSETS_IDS)
848 def test_permset_via_accessors(self, perm, txt, accessor):
849 """Test permissions"""
855 return accessor.__get__(ps) # type: ignore
857 return accessor.__set__(ps, value) # type: ignore
859 self.checkRef(str_ps)
860 assert not getter(), ("Empty permission set should not"
861 " have permission '%s'" % txt)
863 assert ps.test(perm), ("Permission '%s' should exist"
864 " after addition" % txt)
865 assert getter(), ("Permission '%s' should exist"
866 " after addition" % txt)
868 self.checkRef(str_ps)
870 assert not ps.test(perm), ("Permission '%s' should not exist"
871 " after deletion" % txt)
872 assert not getter(), ("Permission '%s' should not exist"
873 " after deletion" % txt)
879 def test_permset_invalid_type(self):
884 with pytest.raises(TypeError):
885 ps.add("foobar") # type: ignore
886 with pytest.raises(TypeError):
887 ps.delete("foobar") # type: ignore
888 with pytest.raises(TypeError):
889 ps.test("foobar") # type: ignore
890 with pytest.raises(ValueError):
891 ps.write = object() # type: ignore
893 @pytest.mark.parametrize("tag", [ACL_USER, ACL_GROUP],
894 ids=["ACL_USER", "ACL_GROUP"])
895 def test_qualifier_values(self, tag):
896 """Tests qualifier correct store/retrieval"""
902 regex = re.compile("(user|group) with (u|g)id %d" % qualifier)
904 e.qualifier = qualifier
905 except OverflowError:
906 # reached overflow condition, break
908 assert e.qualifier == qualifier
909 assert regex.search(str(e)) is not None
912 def test_qualifier_overflow(self):
913 """Tests qualifier overflow handling"""
916 # the uid_t/gid_t are unsigned, so they can hold slightly more
917 # than sys.maxsize*2 (on Linux).
918 qualifier = (sys.maxsize + 1) * 2
919 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
921 with pytest.raises(OverflowError):
922 e.qualifier = qualifier
924 def test_qualifier_underflow(self):
925 """Tests negative qualifier handling"""
926 # Note: this presumes that uid_t/gid_t in C are unsigned...
929 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
931 for qualifier in [-10, -5, -1]:
932 with pytest.raises(OverflowError):
933 e.qualifier = qualifier
935 def test_invalid_qualifier(self):
936 """Tests invalid qualifier handling"""
939 with pytest.raises(TypeError):
940 e.qualifier = object() # type: ignore
941 with pytest.raises((TypeError, AttributeError)):
944 def test_qualifier_on_wrong_tag(self):
945 """Tests qualifier setting on wrong tag"""
948 e.tag_type = posix1e.ACL_OTHER
949 with pytest.raises(TypeError):
951 with pytest.raises(TypeError):
954 @pytest.mark.parametrize("tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
955 def test_tag_types(self, tag):
956 """Tests tag type correct set/get"""
960 assert e.tag_type == tag
961 # check we can show all tag types without breaking
964 @pytest.mark.parametrize("src_tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
965 @pytest.mark.parametrize("dst_tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
966 def test_tag_overwrite(self, src_tag, dst_tag):
967 """Tests tag type correct set/get"""
971 assert e.tag_type == src_tag
974 assert e.tag_type == dst_tag
977 def test_invalid_tags(self):
978 """Tests tag type incorrect set/get"""
981 with pytest.raises(TypeError):
982 e.tag_type = object() # type: ignore
983 e.tag_type = posix1e.ACL_USER_OBJ
984 # For some reason, PyPy raises AttributeError. Strange...
985 with pytest.raises((TypeError, AttributeError)):
988 def test_tag_wrong_overwrite(self):
991 e.tag_type = posix1e.ACL_USER_OBJ
992 tag = max(ALL_TAG_VALUES) + 1
993 with pytest.raises(EnvironmentError):
995 # Check tag is still valid.
996 assert e.tag_type == posix1e.ACL_USER_OBJ
998 if __name__ == "__main__":