4 """Unittests for the posix1e module"""
6 # Copyright (C) 2002-2009, 2012, 2014 Iustin Pop <iusty@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
34 TEST_DIR = os.environ.get("TEST_DIR", ".")
36 BASIC_ACL_TEXT = "u::rw,g::r,o::-"
38 # This is to workaround python 2/3 differences at syntactic level
39 # (which can't be worked around via if's)
40 M0500 = 320 # octal 0500
41 M0644 = 420 # octal 0644
42 M0755 = 493 # octal 755
44 # Check if running under Python 3
45 IS_PY_3K = sys.hexversion >= 0x03000000
48 """Wrapper to skip a test"""
49 new_fn = lambda x: None
50 new_fn.__doc__ = "SKIPPED %s" % fn.__doc__
54 def has_ext(extension):
55 """Decorator to skip tests based on platform support"""
63 """Support functions ACLs"""
71 """tear down function"""
72 for fname in self.rmfiles:
74 for dname in self.rmdirs:
78 """create a temp file"""
79 fh, fname = tempfile.mkstemp(".test", "xattr-", TEST_DIR)
80 self.rmfiles.append(fname)
84 """create a temp dir"""
85 dname = tempfile.mkdtemp(".test", "xattr-", TEST_DIR)
86 self.rmdirs.append(dname)
89 def _getsymlink(self):
90 """create a symlink"""
91 fh, fname = self._getfile()
94 os.symlink(fname + ".non-existent", fname)
98 class LoadTests(aclTest, unittest.TestCase):
99 """Load/create tests"""
100 def testFromFile(self):
101 """Test loading ACLs from a file"""
102 _, fname = self._getfile()
103 acl1 = posix1e.ACL(file=fname)
104 self.assertTrue(acl1.valid(), "ACL read from file should be valid")
106 def testFromDir(self):
107 """Test loading ACLs from a directory"""
108 dname = self._getdir()
109 acl1 = posix1e.ACL(file=dname)
110 acl2 = posix1e.ACL(filedef=dname)
111 self.assertTrue(acl1.valid(),
112 "ACL read from directory should be valid")
113 # default ACLs might or might not be valid; missing ones are
114 # not valid, so we don't test acl2 for validity
116 def testFromFd(self):
117 """Test loading ACLs from a file descriptor"""
118 fd, _ = self._getfile()
119 acl1 = posix1e.ACL(fd=fd)
120 self.assertTrue(acl1.valid(), "ACL read from fd should be valid")
122 def testFromEmpty(self):
123 """Test creating an empty ACL"""
125 self.assertFalse(acl1.valid(), "Empty ACL should not be valid")
127 def testFromText(self):
128 """Test creating an ACL from text"""
129 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
130 self.assertTrue(acl1.valid(),
131 "ACL based on standard description should be valid")
133 class AclExtensions(aclTest, unittest.TestCase):
134 """ACL extensions checks"""
136 @has_ext(HAS_ACL_FROM_MODE)
137 def testFromMode(self):
138 """Test loading ACLs from an octal mode"""
139 acl1 = posix1e.ACL(mode=M0644)
140 self.assertTrue(acl1.valid(),
141 "ACL created via octal mode shoule be valid")
143 @has_ext(HAS_ACL_CHECK)
144 def testAclCheck(self):
145 """Test the acl_check method"""
146 acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
147 self.assertFalse(acl1.check(), "ACL is not valid")
149 self.assertTrue(acl2.check(), "Empty ACL should not be valid")
151 @has_ext(HAS_EXTENDED_CHECK)
152 def testExtended(self):
153 """Test the acl_extended function"""
154 fd, fname = self._getfile()
155 basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
156 basic_acl.applyto(fd)
157 for item in fd, fname:
158 self.assertFalse(has_extended(item),
159 "A simple ACL should not be reported as extended")
160 enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
161 self.assertTrue(enhanced_acl.valid(),
162 "Failure to build an extended ACL")
163 enhanced_acl.applyto(fd)
164 for item in fd, fname:
165 self.assertTrue(has_extended(item),
166 "An extended ACL should be reported as such")
168 @has_ext(HAS_EQUIV_MODE)
169 def testEquivMode(self):
170 """Test the equiv_mode function"""
171 if HAS_ACL_FROM_MODE:
172 for mode in M0644, M0755:
173 acl = posix1e.ACL(mode=mode)
174 self.assertEqual(acl.equiv_mode(), mode)
175 acl = posix1e.ACL(text="u::rw,g::r,o::r")
176 self.assertEqual(acl.equiv_mode(), M0644)
177 acl = posix1e.ACL(text="u::rx,g::-,o::-")
178 self.assertEqual(acl.equiv_mode(), M0500)
181 class WriteTests(aclTest, unittest.TestCase):
184 def testDeleteDefault(self):
185 """Test removing the default ACL"""
186 dname = self._getdir()
187 posix1e.delete_default(dname)
189 def testReapply(self):
190 """Test re-applying an ACL"""
191 fd, fname = self._getfile()
192 acl1 = posix1e.ACL(fd=fd)
195 dname = self._getdir()
196 acl2 = posix1e.ACL(file=fname)
200 class ModificationTests(aclTest, unittest.TestCase):
201 """ACL modification tests"""
203 def checkRef(self, obj):
204 """Checks if a given obj has a 'sane' refcount"""
205 if platform.python_implementation() == "PyPy":
207 ref_cnt = sys.getrefcount(obj)
208 # FIXME: hardcoded value for the max ref count... but I've
209 # seen it overflow on bad reference counting, so it's better
211 if ref_cnt < 2 or ref_cnt > 1024:
212 self.fail("Wrong reference count, expected 2-1024 and got %d" %
216 """Test str() of an ACL."""
219 self.checkRef(str_acl)
221 @has_ext(HAS_ACL_ENTRY)
222 def testAppend(self):
223 """Test append a new Entry to the ACL"""
226 e.tag_type = posix1e.ACL_OTHER
229 self.checkRef(str_format)
231 @has_ext(HAS_ACL_ENTRY)
232 def testDelete(self):
233 """Test delete Entry from the ACL"""
236 e.tag_type = posix1e.ACL_OTHER
241 @has_ext(HAS_ACL_ENTRY)
242 def testDoubleEntries(self):
243 """Test double entries"""
244 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
245 self.assertTrue(acl.valid(), "ACL is not valid")
246 for tag_type in (posix1e.ACL_USER_OBJ, posix1e.ACL_GROUP_OBJ,
249 e.tag_type = tag_type
251 self.assertFalse(acl.valid(),
252 "ACL containing duplicate entries"
253 " should not be valid")
256 @has_ext(HAS_ACL_ENTRY)
257 def testMultipleGoodEntries(self):
258 """Test multiple valid entries"""
259 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
260 self.assertTrue(acl.valid(), "ACL is not valid")
261 for tag_type in (posix1e.ACL_USER,
263 for obj_id in range(5):
265 e.tag_type = tag_type
269 self.assertTrue(acl.valid(),
270 "ACL should be able to hold multiple"
271 " user/group entries")
273 @has_ext(HAS_ACL_ENTRY)
274 def testMultipleBadEntries(self):
275 """Test multiple invalid entries"""
276 acl = posix1e.ACL(text=BASIC_ACL_TEXT)
277 self.assertTrue(acl.valid(), "ACL built from standard description"
279 for tag_type in (posix1e.ACL_USER,
282 e1.tag_type = tag_type
286 self.assertTrue(acl.valid(), "ACL should be able to add a"
289 e2.tag_type = tag_type
293 self.assertFalse(acl.valid(), "ACL should not validate when"
294 " containing two duplicate entries")
298 @has_ext(HAS_ACL_ENTRY)
299 def testPermset(self):
300 """Test permissions"""
306 self.checkRef(str_ps)
308 posix1e.ACL_READ: "read",
309 posix1e.ACL_WRITE: "write",
310 posix1e.ACL_EXECUTE: "execute",
314 self.checkRef(str_ps)
315 self.assertFalse(ps.test(perm), "Empty permission set should not"
316 " have permission '%s'" % pmap[perm])
318 self.assertTrue(ps.test(perm), "Permission '%s' should exist"
319 " after addition" % pmap[perm])
321 self.checkRef(str_ps)
323 self.assertFalse(ps.test(perm), "Permission '%s' should not exist"
324 " after deletion" % pmap[perm])
327 @has_ext(HAS_ACL_ENTRY and IS_PY_3K)
328 def testQualifierValues(self):
329 """Tests qualifier correct store/retrieval"""
332 # work around deprecation warnings
333 if hasattr(self, 'assertRegex'):
334 fn = self.assertRegex
336 fn = self.assertRegexpMatches
337 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
341 if tag == posix1e.ACL_USER:
342 regex = re.compile("user with uid %d" % qualifier)
344 regex = re.compile("group with gid %d" % qualifier)
346 e.qualifier = qualifier
347 except OverflowError:
348 # reached overflow condition, break
350 self.assertEqual(e.qualifier, qualifier)
354 @has_ext(HAS_ACL_ENTRY and IS_PY_3K)
355 def testQualifierOverflow(self):
356 """Tests qualifier overflow handling"""
359 qualifier = sys.maxsize * 2
360 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
362 with self.assertRaises(OverflowError):
363 e.qualifier = qualifier
365 @has_ext(HAS_ACL_ENTRY and IS_PY_3K)
366 def testNegativeQualifier(self):
367 """Tests negative qualifier handling"""
368 # Note: this presumes that uid_t/gid_t in C are unsigned...
371 for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
373 for qualifier in [-10, -5, -1]:
374 with self.assertRaises(OverflowError):
375 e.qualifier = qualifier
378 if __name__ == "__main__":