]> git.k1024.org Git - pylibacl.git/blob - tests/test_acls.py
Convert WriteTests to pytest
[pylibacl.git] / tests / test_acls.py
1 #
2 #
3
4 """Unittests for the posix1e module"""
5
6 #  Copyright (C) 2002-2009, 2012, 2014, 2015 Iustin Pop <iustin@k1024.org>
7 #
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.
12 #
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.
17 #
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
21 #  02110-1301  USA
22
23
24 import unittest
25 import os
26 import tempfile
27 import sys
28 import platform
29 import re
30 import errno
31 import operator
32 import pytest
33 import contextlib
34
35 import posix1e
36 from posix1e import *
37
38 try:
39   import __pypy__
40 except ImportError:
41   __pypy__ = None
42
43 TEST_DIR = os.environ.get("TEST_DIR", ".")
44
45 BASIC_ACL_TEXT = "u::rw,g::r,o::-"
46
47 # This is to workaround python 2/3 differences at syntactic level
48 # (which can't be worked around via if's)
49 M0500 = 320 # octal 0500
50 M0644 = 420 # octal 0644
51 M0755 = 493 # octal 755
52
53 # Permset permission information
54 PERMSETS = {
55   posix1e.ACL_READ: ("read", posix1e.Permset.read),
56   posix1e.ACL_WRITE: ("write", posix1e.Permset.write),
57   posix1e.ACL_EXECUTE: ("execute", posix1e.Permset.execute),
58   }
59
60
61 # Check if running under Python 3
62 IS_PY_3K = sys.hexversion >= 0x03000000
63
64 # Fixtures and helpers
65
66 def ignore_ioerror(errnum, fn, *args, **kwargs):
67     """Call a function while ignoring some IOErrors.
68
69     This is needed as some OSes (e.g. FreeBSD) return failure (EINVAL)
70     when doing certain operations on an invalid ACL.
71
72     """
73     try:
74         fn(*args, **kwargs)
75     except IOError:
76         err = sys.exc_info()[1]
77         if err.errno == errnum:
78             return
79         raise
80
81 def encode(s):
82     """Encode a string if needed (under Python 3)"""
83     if IS_PY_3K:
84         return s.encode()
85     else:
86         return s
87
88 @pytest.fixture
89 def testdir():
90     """per-test temp dir based in TEST_DIR"""
91     with tempfile.TemporaryDirectory(dir=TEST_DIR) as dname:
92         yield dname
93
94 def get_file(path):
95     fh, fname = tempfile.mkstemp(".test", "xattr-", path)
96     return fh, fname
97
98 @contextlib.contextmanager
99 def get_file_name(path):
100     fh, fname = get_file(path)
101     os.close(fh)
102     yield fname
103
104 @contextlib.contextmanager
105 def get_file_fd(path):
106     fd = get_file(path)[0]
107     yield fd
108     os.close(fd)
109
110 @contextlib.contextmanager
111 def get_file_object(path):
112     fd = get_file(path)[0]
113     with os.fdopen(fd) as f:
114         yield f
115
116 @contextlib.contextmanager
117 def get_dir(path):
118     yield tempfile.mkdtemp(".test", "xattr-", path)
119
120 def get_symlink(path, dangling=True):
121     """create a symlink"""
122     fh, fname = get_file(path)
123     os.close(fh)
124     if dangling:
125         os.unlink(fname)
126     sname = fname + ".symlink"
127     os.symlink(fname, sname)
128     return fname, sname
129
130 @contextlib.contextmanager
131 def get_valid_symlink(path):
132     yield get_symlink(path, dangling=False)[1]
133
134 @contextlib.contextmanager
135 def get_dangling_symlink(path):
136     yield get_symlink(path, dangling=True)[1]
137
138 @contextlib.contextmanager
139 def get_file_and_symlink(path):
140     yield get_symlink(path, dangling=False)
141
142 @contextlib.contextmanager
143 def get_file_and_fobject(path):
144     fh, fname = get_file(path)
145     with os.fdopen(fh) as fo:
146         yield fname, fo
147
148 # Wrappers that build upon existing values
149
150 def as_wrapper(call, fn, closer=None):
151     @contextlib.contextmanager
152     def f(path):
153         with call(path) as r:
154             val = fn(r)
155             yield val
156             if closer is not None:
157                 closer(val)
158     return f
159
160 def as_bytes(call):
161     return as_wrapper(call, lambda r: r.encode())
162
163 def as_fspath(call):
164     return as_wrapper(call, pathlib.PurePath)
165
166 def as_iostream(call):
167     opener = lambda f: io.open(f, "r")
168     closer = lambda r: r.close()
169     return as_wrapper(call, opener, closer)
170
171 NOT_BEFORE_36 = pytest.mark.xfail(condition="sys.version_info < (3,6)",
172                                   strict=True)
173 NOT_PYPY = pytest.mark.xfail(condition="platform.python_implementation() == 'PyPy'",
174                                   strict=False)
175
176 class aclTest:
177     """Support functions ACLs"""
178
179     def setUp(self):
180         """set up function"""
181         self.rmfiles = []
182         self.rmdirs = []
183
184     def tearDown(self):
185         """tear down function"""
186         for fname in self.rmfiles:
187             os.unlink(fname)
188         for dname in self.rmdirs:
189             os.rmdir(dname)
190
191     def _getfile(self):
192         """create a temp file"""
193         fh, fname = tempfile.mkstemp(".test", "xattr-", TEST_DIR)
194         self.rmfiles.append(fname)
195         return fh, fname
196
197     def _getdir(self):
198         """create a temp dir"""
199         dname = tempfile.mkdtemp(".test", "xattr-", TEST_DIR)
200         self.rmdirs.append(dname)
201         return dname
202
203     def _getsymlink(self):
204         """create a symlink"""
205         fh, fname = self._getfile()
206         os.close(fh)
207         os.unlink(fname)
208         os.symlink(fname + ".non-existent", fname)
209         return fname
210
211
212 class TestLoad:
213     """Load/create tests"""
214     def test_from_file(self, testdir):
215         """Test loading ACLs from a file"""
216         _, fname = get_file(testdir)
217         acl1 = posix1e.ACL(file=fname)
218         assert acl1.valid()
219
220     def test_from_dir(self, testdir):
221         """Test loading ACLs from a directory"""
222         with get_dir(testdir) as dname:
223           acl1 = posix1e.ACL(file=dname)
224           acl2 = posix1e.ACL(filedef=dname)
225           assert acl1.valid()
226         # default ACLs might or might not be valid; missing ones are
227         # not valid, so we don't test acl2 for validity
228
229     def test_from_fd(self, testdir):
230         """Test loading ACLs from a file descriptor"""
231         fd, _ = get_file(testdir)
232         acl1 = posix1e.ACL(fd=fd)
233         assert acl1.valid()
234
235     def test_from_empty_invalid(self):
236         """Test creating an empty ACL"""
237         acl1 = posix1e.ACL()
238         assert not acl1.valid()
239
240     def test_from_text(self):
241         """Test creating an ACL from text"""
242         acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
243         assert acl1.valid()
244
245     def test_from_acl(self):
246         """Test creating an ACL from an existing ACL"""
247         acl1 = posix1e.ACL()
248         acl2 = posix1e.ACL(acl=acl1)
249         assert acl1 == acl2
250
251     def test_invalid_creation_params(self, testdir):
252         """Test that creating an ACL from multiple objects fails"""
253         fd, _ = get_file(testdir)
254         with pytest.raises(ValueError):
255           posix1e.ACL(text=BASIC_ACL_TEXT, fd=fd)
256
257     def test_invalid_value_creation(self):
258         """Test that creating an ACL from wrong specification fails"""
259         with pytest.raises(EnvironmentError):
260           posix1e.ACL(text="foobar")
261         with pytest.raises(TypeError):
262           posix1e.ACL(foo="bar")
263
264     def test_double_init(self):
265         acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
266         assert acl1.valid()
267         acl1.__init__(text=BASIC_ACL_TEXT)
268         assert acl1.valid()
269
270 class AclExtensions(aclTest, unittest.TestCase):
271     """ACL extensions checks"""
272
273     @unittest.skipUnless(HAS_ACL_FROM_MODE, "Missing HAS_ACL_FROM_MODE")
274     def testFromMode(self):
275         """Test loading ACLs from an octal mode"""
276         acl1 = posix1e.ACL(mode=M0644)
277         self.assertTrue(acl1.valid(),
278                         "ACL created via octal mode shoule be valid")
279
280     @unittest.skipUnless(HAS_ACL_CHECK, "ACL check not supported")
281     def testAclCheck(self):
282         """Test the acl_check method"""
283         acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
284         self.assertFalse(acl1.check(), "ACL is not valid")
285         acl2 = posix1e.ACL()
286         self.assertTrue(acl2.check(), "Empty ACL should not be valid")
287
288     @unittest.skipUnless(HAS_EXTENDED_CHECK, "Extended ACL check not supported")
289     def testExtended(self):
290         """Test the acl_extended function"""
291         fd, fname = self._getfile()
292         basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
293         basic_acl.applyto(fd)
294         for item in fd, fname:
295             self.assertFalse(has_extended(item),
296                              "A simple ACL should not be reported as extended")
297         enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
298         self.assertTrue(enhanced_acl.valid(),
299                         "Failure to build an extended ACL")
300         enhanced_acl.applyto(fd)
301         for item in fd, fname:
302             self.assertTrue(has_extended(item),
303                             "An extended ACL should be reported as such")
304
305     @unittest.skipUnless(HAS_EXTENDED_CHECK, "Extended ACL check not supported")
306     def testExtendedArgHandling(self):
307       self.assertRaises(TypeError, has_extended)
308       self.assertRaises(TypeError, has_extended, object())
309
310     @unittest.skipUnless(HAS_EQUIV_MODE, "equiv_mode not supported")
311     def testEquivMode(self):
312         """Test the equiv_mode function"""
313         if HAS_ACL_FROM_MODE:
314             for mode in M0644, M0755:
315                 acl = posix1e.ACL(mode=mode)
316                 self.assertEqual(acl.equiv_mode(), mode)
317         acl = posix1e.ACL(text="u::rw,g::r,o::r")
318         self.assertEqual(acl.equiv_mode(), M0644)
319         acl = posix1e.ACL(text="u::rx,g::-,o::-")
320         self.assertEqual(acl.equiv_mode(), M0500)
321
322     @unittest.skipUnless(HAS_ACL_CHECK, "ACL check not supported")
323     def testToAnyText(self):
324         acl = posix1e.ACL(text=BASIC_ACL_TEXT)
325         self.assertIn(encode("u::"),
326                           acl.to_any_text(options=posix1e.TEXT_ABBREVIATE))
327         self.assertIn(encode("user::"), acl.to_any_text())
328
329     @unittest.skipUnless(HAS_ACL_CHECK, "ACL check not supported")
330     def testToAnyTextWrongArgs(self):
331         acl = posix1e.ACL(text=BASIC_ACL_TEXT)
332         self.assertRaises(TypeError, acl.to_any_text, foo="bar")
333
334
335     @unittest.skipUnless(HAS_ACL_CHECK, "ACL check not supported")
336     def testRichCompare(self):
337         acl1 = posix1e.ACL(text="u::rw,g::r,o::r")
338         acl2 = posix1e.ACL(acl=acl1)
339         acl3 = posix1e.ACL(text="u::rw,g::rw,o::r")
340         self.assertEqual(acl1, acl2)
341         self.assertNotEqual(acl1, acl3)
342         self.assertRaises(TypeError, operator.lt, acl1, acl2)
343         self.assertRaises(TypeError, operator.ge, acl1, acl3)
344         self.assertTrue(acl1 != True)
345         self.assertFalse(acl1 == 1)
346         self.assertRaises(TypeError, operator.gt, acl1, True)
347
348     @unittest.skipUnless(hasattr(posix1e.ACL, "__cmp__"), "__cmp__ is missing")
349     @unittest.skipUnless(__pypy__ is None, "Disabled under pypy")
350     def testCmp(self):
351         acl1 = posix1e.ACL()
352         self.assertRaises(TypeError, acl1.__cmp__, acl1)
353
354     def testApplyToWithWrongObject(self):
355         acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
356         self.assertTrue(acl1.valid())
357         self.assertRaises(TypeError, acl1.applyto, object())
358         self.assertRaises(TypeError, acl1.applyto, object(), object())
359
360     @unittest.skipUnless(HAS_ACL_ENTRY, "ACL entries not supported")
361     def testAclIterator(self):
362         acl = posix1e.ACL(text=BASIC_ACL_TEXT)
363         #self.assertEqual(len(acl), 3)
364         for entry in acl:
365             self.assertIs(entry.parent, acl)
366
367
368 class TestWrite:
369     """Write tests"""
370
371     def test_delete_default(self, testdir):
372         """Test removing the default ACL"""
373         with get_dir(testdir) as dname:
374           posix1e.delete_default(dname)
375
376     @pytest.mark.skipif(__pypy__, reason="Disabled under pypy")
377     def test_delete_default_wrong_arg(self):
378         with pytest.raises(TypeError):
379           posix1e.delete_default(object())
380
381     def test_reapply(self, testdir):
382         """Test re-applying an ACL"""
383         fd, fname = get_file(testdir)
384         acl1 = posix1e.ACL(fd=fd)
385         acl1.applyto(fd)
386         acl1.applyto(fname)
387         with get_dir(testdir) as dname:
388           acl2 = posix1e.ACL(file=fname)
389           acl2.applyto(dname)
390
391
392 @unittest.skipUnless(HAS_ACL_ENTRY, "ACL entries not supported")
393 class ModificationTests(aclTest, unittest.TestCase):
394     """ACL modification tests"""
395
396     def checkRef(self, obj):
397         """Checks if a given obj has a 'sane' refcount"""
398         if platform.python_implementation() == "PyPy":
399             return
400         ref_cnt = sys.getrefcount(obj)
401         # FIXME: hardcoded value for the max ref count... but I've
402         # seen it overflow on bad reference counting, so it's better
403         # to be safe
404         if ref_cnt < 2 or ref_cnt > 1024:
405             self.fail("Wrong reference count, expected 2-1024 and got %d" %
406                       ref_cnt)
407
408     def testStr(self):
409         """Test str() of an ACL."""
410         acl = posix1e.ACL(text=BASIC_ACL_TEXT)
411         str_acl = str(acl)
412         self.checkRef(str_acl)
413
414     def testAppend(self):
415         """Test append a new Entry to the ACL"""
416         acl = posix1e.ACL()
417         e = acl.append()
418         e.tag_type = posix1e.ACL_OTHER
419         ignore_ioerror(errno.EINVAL, acl.calc_mask)
420         str_format = str(e)
421         self.checkRef(str_format)
422         e2 = acl.append(e)
423         ignore_ioerror(errno.EINVAL, acl.calc_mask)
424         self.assertFalse(acl.valid())
425
426     def testWrongAppend(self):
427         """Test append a new Entry to the ACL based on wrong object type"""
428         acl = posix1e.ACL()
429         self.assertRaises(TypeError, acl.append, object())
430
431     def testEntryCreation(self):
432         acl = posix1e.ACL()
433         e = posix1e.Entry(acl)
434         ignore_ioerror(errno.EINVAL, acl.calc_mask)
435         str_format = str(e)
436         self.checkRef(str_format)
437
438     def testEntryFailedCreation(self):
439         # Checks for partial initialisation and deletion on error
440         # path.
441         self.assertRaises(TypeError, posix1e.Entry, object())
442
443     def testDelete(self):
444         """Test delete Entry from the ACL"""
445         acl = posix1e.ACL()
446         e = acl.append()
447         e.tag_type = posix1e.ACL_OTHER
448         ignore_ioerror(errno.EINVAL, acl.calc_mask)
449         acl.delete_entry(e)
450         ignore_ioerror(errno.EINVAL, acl.calc_mask)
451
452     def testDoubleDelete(self):
453         """Test delete Entry from the ACL"""
454         # This is not entirely valid/correct, since the entry object
455         # itself is invalid after the first deletion, so we're
456         # actually testing deleting an invalid object, not a
457         # non-existing entry...
458         acl = posix1e.ACL()
459         e = acl.append()
460         e.tag_type = posix1e.ACL_OTHER
461         ignore_ioerror(errno.EINVAL, acl.calc_mask)
462         acl.delete_entry(e)
463         ignore_ioerror(errno.EINVAL, acl.calc_mask)
464         self.assertRaises(EnvironmentError, acl.delete_entry, e)
465
466     # This currently fails as this deletion seems to be accepted :/
467     @unittest.skip("Entry deletion is unreliable")
468     def testDeleteInvalidEntry(self):
469         """Test delete foreign Entry from the ACL"""
470         acl1 = posix1e.ACL()
471         acl2 = posix1e.ACL()
472         e = acl1.append()
473         e.tag_type = posix1e.ACL_OTHER
474         ignore_ioerror(errno.EINVAL, acl1.calc_mask)
475         self.assertRaises(EnvironmentError, acl2.delete_entry, e)
476
477     def testDeleteInvalidObject(self):
478         """Test delete a non-Entry from the ACL"""
479         acl = posix1e.ACL()
480         self.assertRaises(TypeError, acl.delete_entry, object())
481
482     def testDoubleEntries(self):
483         """Test double entries"""
484         acl = posix1e.ACL(text=BASIC_ACL_TEXT)
485         self.assertTrue(acl.valid(), "ACL is not valid")
486         for tag_type in (posix1e.ACL_USER_OBJ, posix1e.ACL_GROUP_OBJ,
487                          posix1e.ACL_OTHER):
488             e = acl.append()
489             e.tag_type = tag_type
490             e.permset.clear()
491             self.assertFalse(acl.valid(),
492                 "ACL containing duplicate entries"
493                 " should not be valid")
494             acl.delete_entry(e)
495
496     def testMultipleGoodEntries(self):
497         """Test multiple valid entries"""
498         acl = posix1e.ACL(text=BASIC_ACL_TEXT)
499         self.assertTrue(acl.valid(), "ACL is not valid")
500         for tag_type in (posix1e.ACL_USER,
501                          posix1e.ACL_GROUP):
502             for obj_id in range(5):
503                 e = acl.append()
504                 e.tag_type = tag_type
505                 e.qualifier = obj_id
506                 e.permset.clear()
507                 acl.calc_mask()
508                 self.assertTrue(acl.valid(),
509                     "ACL should be able to hold multiple"
510                     " user/group entries")
511
512     def testMultipleBadEntries(self):
513         """Test multiple invalid entries"""
514         for tag_type in (posix1e.ACL_USER,
515                          posix1e.ACL_GROUP):
516             acl = posix1e.ACL(text=BASIC_ACL_TEXT)
517             self.assertTrue(acl.valid(), "ACL built from standard description"
518                                          " should be valid")
519             e1 = acl.append()
520             e1.tag_type = tag_type
521             e1.qualifier = 0
522             e1.permset.clear()
523             acl.calc_mask()
524             self.assertTrue(acl.valid(), "ACL should be able to add a"
525                 " user/group entry")
526             e2 = acl.append()
527             e2.tag_type = tag_type
528             e2.qualifier = 0
529             e2.permset.clear()
530             ignore_ioerror(errno.EINVAL, acl.calc_mask)
531             self.assertFalse(acl.valid(), "ACL should not validate when"
532                 " containing two duplicate entries")
533             acl.delete_entry(e1)
534             # FreeBSD trips over itself here and can't delete the
535             # entry, even though it still exists.
536             ignore_ioerror(errno.EINVAL, acl.delete_entry, e2)
537
538     def testCopy(self):
539         acl = ACL()
540         e1 = acl.append()
541         e1.tag_type = ACL_USER
542         p1 = e1.permset
543         p1.clear()
544         p1.read = True
545         p1.write = True
546         e2 = acl.append()
547         e2.tag_type = ACL_GROUP
548         p2 = e2.permset
549         p2.clear()
550         p2.read = True
551         self.assertFalse(p2.write)
552         e2.copy(e1)
553         self.assertTrue(p2.write)
554         self.assertEqual(e1.tag_type, e2.tag_type)
555
556     def testCopyWrongArg(self):
557         acl = ACL()
558         e = acl.append()
559         self.assertRaises(TypeError, e.copy, object())
560
561     def testSetPermset(self):
562         acl = ACL()
563         e1 = acl.append()
564         e1.tag_type = ACL_USER
565         p1 = e1.permset
566         p1.clear()
567         p1.read = True
568         p1.write = True
569         e2 = acl.append()
570         e2.tag_type = ACL_GROUP
571         p2 = e2.permset
572         p2.clear()
573         p2.read = True
574         self.assertFalse(p2.write)
575         e2.permset = p1
576         self.assertTrue(e2.permset.write)
577         self.assertEqual(e2.tag_type, ACL_GROUP)
578
579     def testSetPermsetWrongArg(self):
580         acl = ACL()
581         e = acl.append()
582         def setter(v):
583             e.permset = v
584         self.assertRaises(TypeError, setter, object())
585
586     def testPermsetCreation(self):
587         acl = ACL()
588         e = acl.append()
589         p1 = e.permset
590         p2 = Permset(e)
591         #self.assertEqual(p1, p2)
592
593     def testPermsetCreationWrongArg(self):
594         self.assertRaises(TypeError, Permset, object())
595
596     def testPermset(self):
597         """Test permissions"""
598         acl = posix1e.ACL()
599         e = acl.append()
600         ps = e.permset
601         ps.clear()
602         str_ps = str(ps)
603         self.checkRef(str_ps)
604         for perm in PERMSETS:
605             str_ps = str(ps)
606             txt = PERMSETS[perm][0]
607             self.checkRef(str_ps)
608             self.assertFalse(ps.test(perm), "Empty permission set should not"
609                 " have permission '%s'" % txt)
610             ps.add(perm)
611             self.assertTrue(ps.test(perm), "Permission '%s' should exist"
612                 " after addition" % txt)
613             str_ps = str(ps)
614             self.checkRef(str_ps)
615             ps.delete(perm)
616             self.assertFalse(ps.test(perm), "Permission '%s' should not exist"
617                 " after deletion" % txt)
618
619     def testPermsetViaAccessors(self):
620         """Test permissions"""
621         acl = posix1e.ACL()
622         e = acl.append()
623         ps = e.permset
624         ps.clear()
625         str_ps = str(ps)
626         self.checkRef(str_ps)
627         def getter(perm):
628             return PERMSETS[perm][1].__get__(ps)
629         def setter(parm, value):
630             return PERMSETS[perm][1].__set__(ps, value)
631         for perm in PERMSETS:
632             str_ps = str(ps)
633             self.checkRef(str_ps)
634             txt = PERMSETS[perm][0]
635             self.assertFalse(getter(perm), "Empty permission set should not"
636                 " have permission '%s'" % txt)
637             setter(perm, True)
638             self.assertTrue(ps.test(perm), "Permission '%s' should exist"
639                 " after addition" % txt)
640             self.assertTrue(getter(perm), "Permission '%s' should exist"
641                 " after addition" % txt)
642             str_ps = str(ps)
643             self.checkRef(str_ps)
644             setter(perm, False)
645             self.assertFalse(ps.test(perm), "Permission '%s' should not exist"
646                 " after deletion" % txt)
647             self.assertFalse(getter(perm), "Permission '%s' should not exist"
648                 " after deletion" % txt)
649
650     def testPermsetInvalidType(self):
651         acl = posix1e.ACL()
652         e = acl.append()
653         ps = e.permset
654         ps.clear()
655         def setter():
656             ps.write = object()
657         self.assertRaises(TypeError, ps.add, "foobar")
658         self.assertRaises(TypeError, ps.delete, "foobar")
659         self.assertRaises(TypeError, ps.test, "foobar")
660         self.assertRaises(ValueError, setter)
661
662     @unittest.skipUnless(IS_PY_3K, "Only supported under Python 3")
663     def testQualifierValues(self):
664         """Tests qualifier correct store/retrieval"""
665         acl = posix1e.ACL()
666         e = acl.append()
667         # work around deprecation warnings
668         if hasattr(self, 'assertRegex'):
669             fn = self.assertRegex
670         else:
671             fn = self.assertRegexpMatches
672         for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
673             qualifier = 1
674             e.tag_type = tag
675             while True:
676                 if tag == posix1e.ACL_USER:
677                     regex = re.compile("user with uid %d" % qualifier)
678                 else:
679                     regex = re.compile("group with gid %d" % qualifier)
680                 try:
681                     e.qualifier = qualifier
682                 except OverflowError:
683                     # reached overflow condition, break
684                     break
685                 self.assertEqual(e.qualifier, qualifier)
686                 fn(str(e), regex)
687                 qualifier *= 2
688
689     @unittest.skipUnless(IS_PY_3K, "Only supported under Python 3")
690     def testQualifierOverflow(self):
691         """Tests qualifier overflow handling"""
692         acl = posix1e.ACL()
693         e = acl.append()
694         qualifier = sys.maxsize * 2
695         for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
696             e.tag_type = tag
697             with self.assertRaises(OverflowError):
698                 e.qualifier = qualifier
699
700     @unittest.skipUnless(IS_PY_3K, "Only supported under Python 3")
701     def testNegativeQualifier(self):
702         """Tests negative qualifier handling"""
703         # Note: this presumes that uid_t/gid_t in C are unsigned...
704         acl = posix1e.ACL()
705         e = acl.append()
706         for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
707             e.tag_type = tag
708             for qualifier in [-10, -5, -1]:
709                 with self.assertRaises(OverflowError):
710                     e.qualifier = qualifier
711
712     def testInvalidQualifier(self):
713         """Tests invalid qualifier handling"""
714         acl = posix1e.ACL()
715         e = acl.append()
716         def set_qual(x):
717             e.qualifier = x
718         def del_qual():
719             del e.qualifier
720         self.assertRaises(TypeError, set_qual, object())
721         self.assertRaises((TypeError, AttributeError), del_qual)
722
723     def testQualifierOnWrongTag(self):
724         """Tests qualifier setting on wrong tag"""
725         acl = posix1e.ACL()
726         e = acl.append()
727         e.tag_type = posix1e.ACL_OTHER
728         def set_qual(x):
729             e.qualifier = x
730         def get_qual():
731             return e.qualifier
732         self.assertRaises(TypeError, set_qual, 1)
733         self.assertRaises(TypeError, get_qual)
734
735
736     def testTagTypes(self):
737         """Tests tag type correct set/get"""
738         acl = posix1e.ACL()
739         e = acl.append()
740         for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP, posix1e.ACL_USER_OBJ,
741                     posix1e.ACL_GROUP_OBJ, posix1e.ACL_MASK,
742                     posix1e.ACL_OTHER]:
743             e.tag_type = tag
744             self.assertEqual(e.tag_type, tag)
745             # check we can show all tag types without breaking
746             self.assertTrue(str(e))
747
748     def testInvalidTags(self):
749         """Tests tag type incorrect set/get"""
750         acl = posix1e.ACL()
751         e = acl.append()
752         def set_tag(x):
753           e.tag_type = x
754         self.assertRaises(TypeError, set_tag, object())
755         def delete_tag():
756           del e.tag_type
757         # For some reason, PyPy raises AttributeError. Strange...
758         self.assertRaises((TypeError, AttributeError), delete_tag)
759
760         e.tag_type = posix1e.ACL_USER_OBJ
761         tag = max([posix1e.ACL_USER, posix1e.ACL_GROUP, posix1e.ACL_USER_OBJ,
762                    posix1e.ACL_GROUP_OBJ, posix1e.ACL_MASK,
763                    posix1e.ACL_OTHER]) + 1
764         self.assertRaises(EnvironmentError, set_tag, tag)
765         # Check tag is still valid.
766         self.assertEqual(e.tag_type, posix1e.ACL_USER_OBJ)
767
768 if __name__ == "__main__":
769     unittest.main()