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:
82 def assert_acl_eq(a, b):
85 assert str(a) == str(b)
89 """per-test temp dir based in TEST_DIR"""
90 with tempfile.TemporaryDirectory(dir=TEST_DIR) as dname:
94 fh, fname = tempfile.mkstemp(".test", "xattr-", path)
97 @contextlib.contextmanager
98 def get_file_name(path):
99 fh, fname = get_file(path)
103 @contextlib.contextmanager
104 def get_file_fd(path):
105 fd = get_file(path)[0]
109 @contextlib.contextmanager
110 def get_file_object(path):
111 fd = get_file(path)[0]
112 with os.fdopen(fd) as f:
115 @contextlib.contextmanager
117 yield tempfile.mkdtemp(".test", "xattr-", path)
119 def get_symlink(path, dangling=True):
120 """create a symlink"""
121 fh, fname = get_file(path)
125 sname = fname + ".symlink"
126 os.symlink(fname, sname)
129 @contextlib.contextmanager
130 def get_valid_symlink(path):
131 yield get_symlink(path, dangling=False)[1]
133 @contextlib.contextmanager
134 def get_dangling_symlink(path):
135 yield get_symlink(path, dangling=True)[1]
137 @contextlib.contextmanager
138 def get_file_and_symlink(path):
139 yield get_symlink(path, dangling=False)
141 @contextlib.contextmanager
142 def get_file_and_fobject(path):
143 fh, fname = get_file(path)
144 with os.fdopen(fh) as fo:
147 # Wrappers that build upon existing values
149 def as_wrapper(call, fn, closer=None):
150 @contextlib.contextmanager
152 with call(path) as r:
155 if closer is not None:
160 return as_wrapper(call, lambda r: r.encode())
163 return as_wrapper(call, pathlib.PurePath)
165 def as_iostream(call):
166 opener = lambda f: io.open(f, "r")
167 closer = lambda r: r.close()
168 return as_wrapper(call, opener, closer)
170 NOT_BEFORE_36 = pytest.mark.xfail(condition="sys.version_info < (3,6)",
172 NOT_PYPY = pytest.mark.xfail(condition="platform.python_implementation() == 'PyPy'",
175 require_acl_from_mode = pytest.mark.skipif("not HAS_ACL_FROM_MODE")
176 require_acl_check = pytest.mark.skipif("not HAS_ACL_CHECK")
177 require_acl_entry = pytest.mark.skipif("not HAS_ACL_ENTRY")
178 require_extended_check = pytest.mark.skipif("not HAS_EXTENDED_CHECK")
179 require_equiv_mode = pytest.mark.skipif("not HAS_EQUIV_MODE")
180 require_copy_ext = pytest.mark.skipif("not HAS_COPY_EXT")
182 # Note: ACLs are valid only for files/directories, not symbolic links
183 # themselves, so we only create valid symlinks.
186 as_bytes(get_file_name),
187 pytest.param(as_fspath(get_file_name),
188 marks=[NOT_BEFORE_36, NOT_PYPY]),
191 pytest.param(as_fspath(get_dir),
192 marks=[NOT_BEFORE_36, NOT_PYPY]),
194 as_bytes(get_valid_symlink),
195 pytest.param(as_fspath(get_valid_symlink),
196 marks=[NOT_BEFORE_36, NOT_PYPY]),
207 "file via symlink (bytes)",
208 "file via symlink (path)",
214 as_iostream(get_file_name),
226 "directory (path object)",
232 pytest.param(as_fspath(get_dir),
233 marks=[NOT_BEFORE_36, NOT_PYPY]),
236 ALL_P = FILE_P + FD_P
237 ALL_D = FILE_D + FD_D
239 @pytest.fixture(params=FILE_P, ids=FILE_D)
240 def file_subject(testdir, request):
241 with request.param(testdir) as value:
244 @pytest.fixture(params=FD_P, ids=FD_D)
245 def fd_subject(testdir, request):
246 with request.param(testdir) as value:
249 @pytest.fixture(params=DIR_P, ids=DIR_D)
250 def dir_subject(testdir, request):
251 with request.param(testdir) as value:
254 @pytest.fixture(params=ALL_P, ids=ALL_D)
255 def subject(testdir, request):
256 with request.param(testdir) as value:
261 """Load/create tests"""
262 def test_from_file(self, file_subject):
263 """Test loading ACLs from a file/directory"""
264 acl = posix1e.ACL(file=file_subject)
267 def test_from_dir(self, dir_subject):
268 """Test loading ACLs from a directory"""
269 acl2 = posix1e.ACL(filedef=dir_subject)
270 # default ACLs might or might not be valid; missing ones are
271 # not valid, so we don't test acl2 for validity
273 def test_from_fd(self, fd_subject):
274 """Test loading ACLs from a file descriptor"""
275 acl = posix1e.ACL(fd=fd_subject)
278 def test_from_nonexisting(self, testdir):
279 _, fname = get_file(testdir)
280 with pytest.raises(IOError):
281 posix1e.ACL(file="fname"+".no-such-file")
282 with pytest.raises(IOError):
283 posix1e.ACL(filedef="fname"+".no-such-file")
285 def test_from_invalid_fd(self, testdir):
286 fd, _ = get_file(testdir)
288 with pytest.raises(IOError):
291 def test_from_empty_invalid(self):
292 """Test creating an empty ACL"""
294 assert not acl1.valid()
296 def test_from_text(self):
297 """Test creating an ACL from text"""
298 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
301 # This is acl_check, but should actually be have_linux...
303 def test_from_acl(self):
304 """Test creating an ACL from an existing ACL"""
305 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
306 acl2 = posix1e.ACL(acl=acl1)
309 def test_from_acl_via_str(self):
310 # This is needed for not HAVE_LINUX cases.
311 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
312 acl2 = posix1e.ACL(acl=acl1)
313 assert str(acl1) == str(acl2)
315 def test_invalid_creation_params(self, testdir):
316 """Test that creating an ACL from multiple objects fails"""
317 fd, _ = get_file(testdir)
318 with pytest.raises(ValueError):
319 posix1e.ACL(text=BASIC_ACL_TEXT, fd=fd)
321 def test_invalid_value_creation(self):
322 """Test that creating an ACL from wrong specification fails"""
323 with pytest.raises(EnvironmentError):
324 posix1e.ACL(text="foobar")
325 with pytest.raises(TypeError):
326 posix1e.ACL(foo="bar")
328 def test_uninit(self):
329 """Checks that uninit is actually empty init"""
330 acl = posix1e.ACL.__new__(posix1e.ACL)
331 assert not acl.valid()
336 def test_double_init(self):
337 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
339 acl1.__init__(text=BASIC_ACL_TEXT) # type: ignore
341 acl2 = ACL(text=TEXT_0755)
343 acl1.__init__(acl=acl2) # type: ignore
344 assert_acl_eq(acl1, acl2)
346 def test_reinit_failure_noop(self):
347 a = posix1e.ACL(text=TEXT_0755)
348 b = posix1e.ACL(acl=a)
350 with pytest.raises(IOError):
351 a.__init__(text='foobar')
354 @pytest.mark.xfail(reason="Unreliable test, re-init doesn't always invalidate children")
355 def test_double_init_breaks_children(self):
358 e.permset.write = True
359 acl.__init__() # type: ignore
360 with pytest.raises(EnvironmentError):
361 e.permset.write = False
364 class TestAclExtensions:
365 """ACL extensions checks"""
367 @require_acl_from_mode
368 def test_from_mode(self):
369 """Test loading ACLs from an octal mode"""
370 acl1 = posix1e.ACL(mode=0o644)
374 def test_acl_check(self):
375 """Test the acl_check method"""
376 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
377 assert not acl1.check()
380 assert c == (ACL_MISS_ERROR, 0)
381 assert isinstance(c, tuple)
382 assert c[0] == ACL_MISS_ERROR
385 assert c == (ACL_ENTRY_ERROR, 0)
387 def test_applyto(self, subject):
388 """Test the apply_to function"""
389 # TODO: add read/compare with before, once ACL can be init'ed
391 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
392 basic_acl.applyto(subject)
393 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
394 assert enhanced_acl.valid()
395 enhanced_acl.applyto(subject)
397 def test_apply_to_with_wrong_object(self):
398 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
400 with pytest.raises(TypeError):
401 acl1.applyto(object())
402 with pytest.raises(TypeError):
403 acl1.applyto(object(), object()) # type: ignore
405 def test_apply_to_fail(self, testdir):
406 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
408 fd, fname = get_file(testdir)
410 with pytest.raises(IOError):
412 with pytest.raises(IOError, match="no-such-file"):
413 acl1.applyto(fname+".no-such-file")
415 @require_extended_check
416 def test_applyto_extended(self, subject):
417 """Test the acl_extended function"""
418 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
419 basic_acl.applyto(subject)
420 assert not has_extended(subject)
421 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
422 assert enhanced_acl.valid()
423 enhanced_acl.applyto(subject)
424 assert has_extended(subject)
426 @require_extended_check
427 @pytest.mark.parametrize(
428 "gen", [ get_file_and_symlink, get_file_and_fobject ])
429 def test_applyto_extended_mixed(self, testdir, gen):
430 """Test the acl_extended function"""
431 with gen(testdir) as (a, b):
432 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
435 assert not has_extended(item)
436 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
437 assert enhanced_acl.valid()
438 enhanced_acl.applyto(b)
440 assert has_extended(item)
442 @require_extended_check
443 def test_extended_fail(self, testdir):
444 fd, fname = get_file(testdir)
446 with pytest.raises(IOError):
448 with pytest.raises(IOError, match="no-such-file"):
449 has_extended(fname+".no-such-file")
451 @require_extended_check
452 def test_extended_arg_handling(self):
453 with pytest.raises(TypeError):
454 has_extended() # type: ignore
455 with pytest.raises(TypeError):
456 has_extended(object()) # type: ignore
459 def test_equiv_mode(self):
460 """Test the equiv_mode function"""
461 if HAS_ACL_FROM_MODE:
462 for mode in 0o644, 0o755:
463 acl = posix1e.ACL(mode=mode)
464 assert acl.equiv_mode() == mode
465 acl = posix1e.ACL(text="u::rw,g::r,o::r")
466 assert acl.equiv_mode() == 0o644
467 acl = posix1e.ACL(text="u::rx,g::-,o::-")
468 assert acl.equiv_mode() == 0o500
471 @pytest.mark.xfail(reason="It seems equiv mode always passes, even for empty ACLs")
472 def test_equiv_mode_invalid(self):
473 """Test equiv_mode on invalid ACLs"""
475 with pytest.raises(EnvironmentError):
479 def test_to_any_text(self):
480 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
482 acl.to_any_text(options=posix1e.TEXT_ABBREVIATE)
483 assert b"user::" in acl.to_any_text()
486 def test_to_any_text_wrong_args(self):
487 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
488 with pytest.raises(TypeError):
489 acl.to_any_text(foo="bar") # type: ignore
493 def test_rich_compare(self):
494 acl1 = posix1e.ACL(text="u::rw,g::r,o::r")
495 acl2 = posix1e.ACL(acl=acl1)
496 acl3 = posix1e.ACL(text="u::rw,g::rw,o::r")
499 with pytest.raises(TypeError):
500 acl1 < acl2 # type: ignore
501 with pytest.raises(TypeError):
502 acl1 >= acl3 # type: ignore
503 assert acl1 != True # type: ignore
504 assert not (acl1 == 1) # type: ignore
505 with pytest.raises(TypeError):
506 acl1 > True # type: ignore
509 def test_acl_iterator(self):
510 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
512 assert entry.parent is acl
515 def test_acl_copy_ext(self):
516 a = posix1e.ACL(text=BASIC_ACL_TEXT)
518 c = posix1e.ACL(acl=b)
521 state = a.__getstate__()
522 b.__setstate__(state)
527 def get_nulled_state(src=None):
528 """Generate a mostly-valid external serialization
530 Passing arbitrary state into acl_copy_int() is dangerous. That
531 C function gets a void * buffer, and then casts that to an ACL
532 structure, irrespective of buffer length; this can lead to
533 segfaults (via unallocated memory indexing). Depending on the
534 exact buffer, the same code might segfault on all
535 architectures, some architectures, all C compiler versions, or
536 some C compilers, or any combination of the above :(
538 To mitigate this, pass a much larger buffer size as returned
539 from the state, just nulled out - in the Linux version of the
540 library, the first byte is the structure size and is tested
541 for correct size, and a null byte will cause failure.
546 state = src.__getstate__()
547 nulled = b'\x00' * (10 * len(state))
551 def test_acl_copy_int_failure(self):
553 nulled = self.get_nulled_state(a)
554 with pytest.raises(IOError):
555 a.__setstate__(nulled)
558 def test_acl_copy_int_failure_is_noop(self):
559 a = posix1e.ACL(text=BASIC_ACL_TEXT)
561 c = posix1e.ACL(acl=a)
564 nulled = self.get_nulled_state(b)
565 with pytest.raises(IOError):
566 a.__setstate__(nulled)
567 # Assert that 'a' didn't change in the attempt to restore
572 def test_acl_copy_int_args(self):
574 with pytest.raises(TypeError):
578 def test_acl_init_copy_int(self):
579 a = posix1e.ACL(text=BASIC_ACL_TEXT)
581 c = posix1e.ACL(data=a.__getstate__())
586 def test_acl_init_copy_int_invalid(self):
587 with pytest.raises(IOError):
588 posix1e.ACL(data=self.get_nulled_state())
594 def test_delete_default(self, testdir):
595 """Test removing the default ACL"""
596 with get_dir(testdir) as dname:
597 posix1e.delete_default(dname)
599 def test_delete_default_fail(self, testdir):
600 """Test removing the default ACL"""
601 with get_file_name(testdir) as fname:
602 with pytest.raises(IOError, match="no-such-file"):
603 posix1e.delete_default(fname+".no-such-file")
606 def test_delete_default_wrong_arg(self):
607 with pytest.raises(TypeError):
608 posix1e.delete_default(object()) # type: ignore
610 def test_reapply(self, testdir):
611 """Test re-applying an ACL"""
612 fd, fname = get_file(testdir)
613 acl1 = posix1e.ACL(fd=fd)
616 with get_dir(testdir) as dname:
617 acl2 = posix1e.ACL(file=fname)
623 class TestModification:
624 """ACL modification tests"""
626 def checkRef(self, obj):
627 """Checks if a given obj has a 'sane' refcount"""
628 if platform.python_implementation() == "PyPy":
630 ref_cnt = sys.getrefcount(obj)
631 # FIXME: hardcoded value for the max ref count... but I've
632 # seen it overflow on bad reference counting, so it's better
634 if ref_cnt < 2 or ref_cnt > 1024:
635 pytest.fail("Wrong reference count, expected 2-1024 and got %d" %
639 """Test str() of an ACL."""
640 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
642 self.checkRef(str_acl)
644 def test_append(self):
645 """Test append a new Entry to the ACL"""
648 e.tag_type = posix1e.ACL_OTHER
649 ignore_ioerror(errno.EINVAL, acl.calc_mask)
651 self.checkRef(str_format)
653 ignore_ioerror(errno.EINVAL, acl.calc_mask)
654 assert not acl.valid()
656 def test_wrong_append(self):
657 """Test append a new Entry to the ACL based on wrong object type"""
659 with pytest.raises(TypeError):
660 acl.append(object()) # type: ignore
662 @pytest.mark.xfail(reason="Behaviour not conform to specification")
663 def test_append_invalid_source(self):
668 with pytest.raises(EnvironmentError):
669 f.permset.write = True
670 with pytest.raises(EnvironmentError):
673 def test_entry_creation(self):
675 e = posix1e.Entry(acl)
676 ignore_ioerror(errno.EINVAL, acl.calc_mask)
678 self.checkRef(str_format)
680 def test_entry_failed_creation(self):
681 # Checks for partial initialisation and deletion on error
683 with pytest.raises(TypeError):
684 posix1e.Entry(object()) # type: ignore
686 def test_entry_reinitialisations(self):
690 e.__init__(a) # type: ignore
691 with pytest.raises(ValueError, match="different parent"):
692 e.__init__(b) # type: ignore
695 def test_entry_reinit_leaks_refcount(self):
698 ref = sys.getrefcount(acl)
699 e.__init__(acl) # type: ignore
700 assert ref == sys.getrefcount(acl), "Uh-oh, ref leaks..."
702 def test_delete(self):
703 """Test delete Entry from the ACL"""
706 e.tag_type = posix1e.ACL_OTHER
707 ignore_ioerror(errno.EINVAL, acl.calc_mask)
709 ignore_ioerror(errno.EINVAL, acl.calc_mask)
711 def test_double_delete(self):
712 """Test delete Entry from the ACL"""
713 # This is not entirely valid/correct, since the entry object
714 # itself is invalid after the first deletion, so we're
715 # actually testing deleting an invalid object, not a
716 # non-existing entry...
719 e.tag_type = posix1e.ACL_OTHER
720 ignore_ioerror(errno.EINVAL, acl.calc_mask)
722 ignore_ioerror(errno.EINVAL, acl.calc_mask)
723 with pytest.raises(EnvironmentError):
726 def test_delete_unowned(self):
727 """Test delete Entry from the ACL"""
731 e.tag_type = posix1e.ACL_OTHER
732 with pytest.raises(ValueError, match="un-owned entry"):
735 # This currently fails as this deletion seems to be accepted :/
736 @pytest.mark.xfail(reason="Entry deletion is unreliable")
737 def testDeleteInvalidEntry(self):
738 """Test delete foreign Entry from the ACL"""
742 e.tag_type = posix1e.ACL_OTHER
743 ignore_ioerror(errno.EINVAL, acl1.calc_mask)
744 with pytest.raises(EnvironmentError):
747 def test_delete_invalid_object(self):
748 """Test delete a non-Entry from the ACL"""
750 with pytest.raises(TypeError):
751 acl.delete_entry(object()) # type: ignore
753 def test_double_entries(self):
754 """Test double entries"""
755 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
757 for tag_type in (posix1e.ACL_USER_OBJ, posix1e.ACL_GROUP_OBJ,
760 e.tag_type = tag_type
762 assert not acl.valid(), ("ACL containing duplicate entries"
763 " should not be valid")
766 def test_multiple_good_entries(self):
767 """Test multiple valid entries"""
768 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
770 for tag_type in (posix1e.ACL_USER,
772 for obj_id in range(5):
774 e.tag_type = tag_type
778 assert acl.valid(), ("ACL should be able to hold multiple"
779 " user/group entries")
781 def test_multiple_bad_entries(self):
782 """Test multiple invalid entries"""
783 for tag_type in (posix1e.ACL_USER,
785 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
788 e1.tag_type = tag_type
792 assert acl.valid(), ("ACL should be able to add a"
795 e2.tag_type = tag_type
798 ignore_ioerror(errno.EINVAL, acl.calc_mask)
799 assert not acl.valid(), ("ACL should not validate when"
800 " containing two duplicate entries")
802 # FreeBSD trips over itself here and can't delete the
803 # entry, even though it still exists.
804 ignore_ioerror(errno.EINVAL, acl.delete_entry, e2)
809 e1.tag_type = ACL_USER
815 e2.tag_type = ACL_GROUP
822 assert e1.tag_type == e2.tag_type
824 def test_copy_wrong_arg(self):
827 with pytest.raises(TypeError):
828 e.copy(object()) # type: ignore
830 def test_set_permset(self):
833 e1.tag_type = ACL_USER
839 e2.tag_type = ACL_GROUP
845 assert e2.permset.write
846 assert e2.tag_type == ACL_GROUP
848 def test_set_permset_wrong_arg(self):
851 with pytest.raises(TypeError):
852 e.permset = object() # type: ignore
854 def test_permset_creation(self):
861 def test_permset_creation_wrong_arg(self):
862 with pytest.raises(TypeError):
863 Permset(object()) # type: ignore
865 def test_permset_reinitialisations(self):
870 p.__init__(e) # type: ignore
871 with pytest.raises(ValueError, match="different parent"):
872 p.__init__(f) # type: ignore
875 def test_permset_reinit_leaks_refcount(self):
879 ref = sys.getrefcount(e)
880 p.__init__(e) # type: ignore
881 assert ref == sys.getrefcount(e), "Uh-oh, ref leaks..."
883 @pytest.mark.parametrize("perm, txt, accessor",
884 PERMSETS, ids=PERMSETS_IDS)
885 def test_permset(self, perm, txt, accessor):
886 """Test permissions"""
893 self.checkRef(str_ps)
894 assert not ps.test(perm), ("Empty permission set should not"
895 " have permission '%s'" % txt)
897 assert ps.test(perm), ("Permission '%s' should exist"
898 " after addition" % txt)
900 self.checkRef(str_ps)
902 assert not ps.test(perm), ("Permission '%s' should not exist"
903 " after deletion" % txt)
905 assert ps.test(perm), ("Permission '%s' should exist"
906 " after addition" % txt)
908 assert not ps.test(perm), ("Permission '%s' should not exist"
909 " after clearing" % txt)
913 @pytest.mark.parametrize("perm, txt, accessor",
914 PERMSETS, ids=PERMSETS_IDS)
915 def test_permset_via_accessors(self, perm, txt, accessor):
916 """Test permissions"""
922 return accessor.__get__(ps) # type: ignore
924 return accessor.__set__(ps, value) # type: ignore
926 self.checkRef(str_ps)
927 assert not getter(), ("Empty permission set should not"
928 " have permission '%s'" % txt)
930 assert ps.test(perm), ("Permission '%s' should exist"
931 " after addition" % txt)
932 assert getter(), ("Permission '%s' should exist"
933 " after addition" % txt)
935 self.checkRef(str_ps)
937 assert not ps.test(perm), ("Permission '%s' should not exist"
938 " after deletion" % txt)
939 assert not getter(), ("Permission '%s' should not exist"
940 " after deletion" % txt)
946 def test_permset_invalid_type(self):
951 with pytest.raises(TypeError):
952 ps.add("foobar") # type: ignore
953 with pytest.raises(TypeError):
954 ps.delete("foobar") # type: ignore
955 with pytest.raises(TypeError):
956 ps.test("foobar") # type: ignore
957 with pytest.raises(ValueError):
958 ps.write = object() # type: ignore
960 @pytest.mark.parametrize("tag", [ACL_USER, ACL_GROUP],
961 ids=["ACL_USER", "ACL_GROUP"])
962 def test_qualifier_values(self, tag):
963 """Tests qualifier correct store/retrieval"""
969 regex = re.compile("(user|group) with (u|g)id %d" % qualifier)
971 e.qualifier = qualifier
972 except OverflowError:
973 # reached overflow condition, break
975 assert e.qualifier == qualifier
976 assert regex.search(str(e)) is not None
979 def test_qualifier_overflow(self):
980 """Tests qualifier overflow handling"""
983 # the uid_t/gid_t are unsigned, so they can hold slightly more
984 # than sys.maxsize*2 (on Linux).
985 qualifier = (sys.maxsize + 1) * 2
986 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
988 with pytest.raises(OverflowError):
989 e.qualifier = qualifier
991 def test_qualifier_underflow(self):
992 """Tests negative qualifier handling"""
993 # Note: this presumes that uid_t/gid_t in C are unsigned...
996 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
998 for qualifier in [-10, -5, -1]:
999 with pytest.raises(OverflowError):
1000 e.qualifier = qualifier
1002 def test_invalid_qualifier(self):
1003 """Tests invalid qualifier handling"""
1006 with pytest.raises(TypeError):
1007 e.qualifier = object() # type: ignore
1008 with pytest.raises((TypeError, AttributeError)):
1011 def test_qualifier_on_wrong_tag(self):
1012 """Tests qualifier setting on wrong tag"""
1015 e.tag_type = posix1e.ACL_OTHER
1016 with pytest.raises(TypeError):
1018 with pytest.raises(TypeError):
1021 @pytest.mark.parametrize("tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
1022 def test_tag_types(self, tag):
1023 """Tests tag type correct set/get"""
1027 assert e.tag_type == tag
1028 # check we can show all tag types without breaking
1031 @pytest.mark.parametrize("src_tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
1032 @pytest.mark.parametrize("dst_tag", ALL_TAG_VALUES, ids=ALL_TAG_DESCS)
1033 def test_tag_overwrite(self, src_tag, dst_tag):
1034 """Tests tag type correct set/get"""
1037 e.tag_type = src_tag
1038 assert e.tag_type == src_tag
1040 e.tag_type = dst_tag
1041 assert e.tag_type == dst_tag
1044 def test_invalid_tags(self):
1045 """Tests tag type incorrect set/get"""
1048 with pytest.raises(TypeError):
1049 e.tag_type = object() # type: ignore
1050 e.tag_type = posix1e.ACL_USER_OBJ
1051 # For some reason, PyPy raises AttributeError. Strange...
1052 with pytest.raises((TypeError, AttributeError)):
1055 def test_tag_wrong_overwrite(self):
1058 e.tag_type = posix1e.ACL_USER_OBJ
1059 tag = max(ALL_TAG_VALUES) + 1
1060 with pytest.raises(EnvironmentError):
1062 # Check tag is still valid.
1063 assert e.tag_type == posix1e.ACL_USER_OBJ
1065 if __name__ == "__main__":