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