From c0ed44ab0a68b47abe124bd90058756180753fef Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Sun, 3 Mar 2019 02:40:04 +0100 Subject: [PATCH] Increase test coverage These are old changes from October 2016 which I forgot to commit. I think they're related to test coverage, although a few changes (object structure) seems a bit different. Well, anyway, here they are. --- acl.c | 14 +- test/test_acls.py | 352 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 351 insertions(+), 15 deletions(-) diff --git a/acl.c b/acl.c index 59ab55e..651dc29 100644 --- a/acl.c +++ b/acl.c @@ -367,7 +367,8 @@ static PyObject* ACL_richcompare(PyObject* o1, PyObject* o2, int op) { ret = n == 1 ? Py_True : Py_False; break; default: - ret = Py_NotImplemented; + PyErr_SetString(PyExc_TypeError, "ACLs are not orderable"); + return NULL; } Py_INCREF(ret); return ret; @@ -395,12 +396,14 @@ static PyObject* ACL_equiv_mode(PyObject* obj, PyObject* args) { } #endif +#ifndef IS_PY3K /* Implementation of the compare for ACLs */ static int ACL_nocmp(PyObject* o1, PyObject* o2) { PyErr_SetString(PyExc_TypeError, "cannot compare ACLs using cmp()"); return -1; } +#endif /* Custom methods */ static char __applyto_doc__[] = @@ -1144,8 +1147,8 @@ static int Permset_set_right(PyObject* obj, PyObject* value, void* arg) { int nerr; if(!PyInt_Check(value)) { - PyErr_SetString(PyExc_ValueError, "a maximum of one argument must" - " be passed"); + PyErr_SetString(PyExc_ValueError, "invalid argument, an integer" + " is expected"); return -1; } on = PyInt_AsLong(value); @@ -1316,7 +1319,12 @@ static PyTypeObject ACL_Type = { 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ +#ifdef IS_PY3K + 0, /* formerly tp_compare, in 3.0 deprecated, in + 3.5 tp_as_async */ +#else ACL_nocmp, /* tp_compare */ +#endif 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ diff --git a/test/test_acls.py b/test/test_acls.py index 8eb3e4d..1db881c 100644 --- a/test/test_acls.py +++ b/test/test_acls.py @@ -28,10 +28,16 @@ import sys import platform import re import errno +import operator import posix1e from posix1e import * +try: + import __pypy__ +except ImportError: + __pypy__ = None + TEST_DIR = os.environ.get("TEST_DIR", ".") BASIC_ACL_TEXT = "u::rw,g::r,o::-" @@ -42,22 +48,44 @@ M0500 = 320 # octal 0500 M0644 = 420 # octal 0644 M0755 = 493 # octal 755 +# Permset permission information +PERMSETS = { + posix1e.ACL_READ: ("read", posix1e.Permset.read), + posix1e.ACL_WRITE: ("write", posix1e.Permset.write), + posix1e.ACL_EXECUTE: ("execute", posix1e.Permset.execute), + } + + # Check if running under Python 3 IS_PY_3K = sys.hexversion >= 0x03000000 def _skip_test(fn): - """Wrapper to skip a test""" + """Wrapper to skip a test for python 2.6""" new_fn = lambda x: None new_fn.__doc__ = "SKIPPED %s" % fn.__doc__ return new_fn +def _ignore_test(fn): + """Ignore failures in a test for python 2.6""" + def ignorer(): + try: + fn() + except AssertionError: + print ("Ignoring failure") + return ignorer def has_ext(extension): """Decorator to skip tests based on platform support""" if not extension: - return _skip_test + if hasattr(unittest, 'skip'): + return unittest.skip("Precondition failed") + else: + return _skip_test else: - return lambda x: x + return lambda func: func + +def expectFail(): + return getattr(unittest, 'expectedFailure', _ignore_test) def ignore_ioerror(errnum, fn, *args, **kwargs): """Call a function while ignoring some IOErrors. @@ -74,6 +102,14 @@ def ignore_ioerror(errnum, fn, *args, **kwargs): return raise +def encode(s): + """Encode a string if needed (under Python 3)""" + if IS_PY_3K: + return s.encode() + else: + return s + + class aclTest: """Support functions ACLs""" @@ -145,6 +181,27 @@ class LoadTests(aclTest, unittest.TestCase): self.assertTrue(acl1.valid(), "ACL based on standard description should be valid") + def testFromACL(self): + """Test creating an ACL from an existing ACL""" + acl1 = posix1e.ACL() + acl2 = posix1e.ACL(acl=acl1) + + def testInvalidCreationParams(self): + """Test that creating an ACL from multiple objects fails""" + fd, _ = self._getfile() + self.assertRaises(ValueError, posix1e.ACL, text=BASIC_ACL_TEXT, fd=fd) + + def testInvalidValueCreation(self): + """Test that creating an ACL from wrong specification fails""" + self.assertRaises(EnvironmentError, posix1e.ACL, text="foobar") + self.assertRaises(TypeError, posix1e.ACL, foo="bar") + + def testDoubleInit(self): + acl1 = posix1e.ACL(text=BASIC_ACL_TEXT) + self.assertTrue(acl1.valid()) + acl1.__init__(text=BASIC_ACL_TEXT) + self.assertTrue(acl1.valid()) + class AclExtensions(aclTest, unittest.TestCase): """ACL extensions checks""" @@ -180,6 +237,11 @@ class AclExtensions(aclTest, unittest.TestCase): self.assertTrue(has_extended(item), "An extended ACL should be reported as such") + @has_ext(HAS_EXTENDED_CHECK) + def testExtendedArgHandling(self): + self.assertRaises(TypeError, has_extended) + self.assertRaises(TypeError, has_extended, object()) + @has_ext(HAS_EQUIV_MODE) def testEquivMode(self): """Test the equiv_mode function""" @@ -192,6 +254,50 @@ class AclExtensions(aclTest, unittest.TestCase): acl = posix1e.ACL(text="u::rx,g::-,o::-") self.assertEqual(acl.equiv_mode(), M0500) + @has_ext(HAS_ACL_CHECK) + def testToAnyText(self): + acl = posix1e.ACL(text=BASIC_ACL_TEXT) + self.assertIn(encode("u::"), + acl.to_any_text(options=posix1e.TEXT_ABBREVIATE)) + self.assertIn(encode("user::"), acl.to_any_text()) + + @has_ext(HAS_ACL_CHECK) + def testToAnyTextWrongArgs(self): + acl = posix1e.ACL(text=BASIC_ACL_TEXT) + self.assertRaises(TypeError, acl.to_any_text, foo="bar") + + + @has_ext(HAS_ACL_CHECK) + def testRichCompare(self): + acl1 = posix1e.ACL(text="u::rw,g::r,o::r") + acl2 = posix1e.ACL(acl=acl1) + acl3 = posix1e.ACL(text="u::rw,g::rw,o::r") + self.assertEqual(acl1, acl2) + self.assertNotEqual(acl1, acl3) + self.assertRaises(TypeError, operator.lt, acl1, acl2) + self.assertRaises(TypeError, operator.ge, acl1, acl3) + self.assertTrue(acl1 != True) + self.assertFalse(acl1 == 1) + self.assertRaises(TypeError, operator.gt, acl1, True) + + @has_ext(hasattr(posix1e.ACL, "__cmp__") and __pypy__ is None) + def testCmp(self): + acl1 = posix1e.ACL() + self.assertRaises(TypeError, acl1.__cmp__, acl1) + + def testApplyToWithWrongObject(self): + acl1 = posix1e.ACL(text=BASIC_ACL_TEXT) + self.assertTrue(acl1.valid()) + self.assertRaises(TypeError, acl1.applyto, object()) + self.assertRaises(TypeError, acl1.applyto, object(), object()) + + @has_ext(HAS_ACL_ENTRY) + def testAclIterator(self): + acl = posix1e.ACL(text=BASIC_ACL_TEXT) + #self.assertEqual(len(acl), 3) + for entry in acl: + self.assertIs(entry.parent, acl) + class WriteTests(aclTest, unittest.TestCase): """Write tests""" @@ -201,6 +307,10 @@ class WriteTests(aclTest, unittest.TestCase): dname = self._getdir() posix1e.delete_default(dname) + @has_ext(__pypy__ is None) + def testDeleteDefaultWrongArg(self): + self.assertRaises(TypeError, posix1e.delete_default, object()) + def testReapply(self): """Test re-applying an ACL""" fd, fname = self._getfile() @@ -242,6 +352,29 @@ class ModificationTests(aclTest, unittest.TestCase): ignore_ioerror(errno.EINVAL, acl.calc_mask) str_format = str(e) self.checkRef(str_format) + e2 = acl.append(e) + ignore_ioerror(errno.EINVAL, acl.calc_mask) + self.assertFalse(acl.valid()) + + @has_ext(HAS_ACL_ENTRY) + def testWrongAppend(self): + """Test append a new Entry to the ACL based on wrong object type""" + acl = posix1e.ACL() + self.assertRaises(TypeError, acl.append, object()) + + @has_ext(HAS_ACL_ENTRY) + def testEntryCreation(self): + acl = posix1e.ACL() + e = posix1e.Entry(acl) + ignore_ioerror(errno.EINVAL, acl.calc_mask) + str_format = str(e) + self.checkRef(str_format) + + @has_ext(HAS_ACL_ENTRY) + def testEntryFailedCreation(self): + # Checks for partial initialisation and deletion on error + # path. + self.assertRaises(TypeError, posix1e.Entry, object()) @has_ext(HAS_ACL_ENTRY) def testDelete(self): @@ -253,6 +386,38 @@ class ModificationTests(aclTest, unittest.TestCase): acl.delete_entry(e) ignore_ioerror(errno.EINVAL, acl.calc_mask) + @has_ext(HAS_ACL_ENTRY) + def testDoubleDelete(self): + """Test delete Entry from the ACL""" + # This is not entirely valid/correct, since the entry object + # itself is invalid after the first deletion, so we're + # actually testing deleting an invalid object, not a + # non-existing entry... + acl = posix1e.ACL() + e = acl.append() + e.tag_type = posix1e.ACL_OTHER + ignore_ioerror(errno.EINVAL, acl.calc_mask) + acl.delete_entry(e) + ignore_ioerror(errno.EINVAL, acl.calc_mask) + self.assertRaises(EnvironmentError, acl.delete_entry, e) + + # This currently fails as this deletion seems to be accepted :/ + @has_ext(HAS_ACL_ENTRY and False) + def testDeleteInvalidEntry(self): + """Test delete foreign Entry from the ACL""" + acl1 = posix1e.ACL() + acl2 = posix1e.ACL() + e = acl1.append() + e.tag_type = posix1e.ACL_OTHER + ignore_ioerror(errno.EINVAL, acl1.calc_mask) + self.assertRaises(EnvironmentError, acl2.delete_entry, e) + + @has_ext(HAS_ACL_ENTRY) + def testDeleteInvalidObject(self): + """Test delete a non-Entry from the ACL""" + acl = posix1e.ACL() + self.assertRaises(TypeError, acl.delete_entry, object()) + @has_ext(HAS_ACL_ENTRY) def testDoubleEntries(self): """Test double entries""" @@ -312,6 +477,70 @@ class ModificationTests(aclTest, unittest.TestCase): # entry, even though it still exists. ignore_ioerror(errno.EINVAL, acl.delete_entry, e2) + @has_ext(HAS_ACL_ENTRY) + def testCopy(self): + acl = ACL() + e1 = acl.append() + e1.tag_type = ACL_USER + p1 = e1.permset + p1.clear() + p1.read = True + p1.write = True + e2 = acl.append() + e2.tag_type = ACL_GROUP + p2 = e2.permset + p2.clear() + p2.read = True + self.assertFalse(p2.write) + e2.copy(e1) + self.assertTrue(p2.write) + self.assertEqual(e1.tag_type, e2.tag_type) + + @has_ext(HAS_ACL_ENTRY) + def testCopyWrongArg(self): + acl = ACL() + e = acl.append() + self.assertRaises(TypeError, e.copy, object()) + + @has_ext(HAS_ACL_ENTRY) + def testSetPermset(self): + acl = ACL() + e1 = acl.append() + e1.tag_type = ACL_USER + p1 = e1.permset + p1.clear() + p1.read = True + p1.write = True + e2 = acl.append() + e2.tag_type = ACL_GROUP + p2 = e2.permset + p2.clear() + p2.read = True + self.assertFalse(p2.write) + e2.permset = p1 + self.assertTrue(e2.permset.write) + self.assertEqual(e2.tag_type, ACL_GROUP) + + @has_ext(HAS_ACL_ENTRY) + def testSetPermsetWrongArg(self): + acl = ACL() + e = acl.append() + def setter(v): + e.permset = v + self.assertRaises(TypeError, setter, object()) + + @has_ext(HAS_ACL_ENTRY) + def testPermsetCreation(self): + acl = ACL() + e = acl.append() + p1 = e.permset + p2 = Permset(e) + #self.assertEqual(p1, p2) + + @has_ext(HAS_ACL_ENTRY) + def testPermsetCreationWrongArg(self): + self.assertRaises(TypeError, Permset, object()) + @has_ext(HAS_ACL_ENTRY) def testPermset(self): """Test permissions""" @@ -321,25 +550,65 @@ class ModificationTests(aclTest, unittest.TestCase): ps.clear() str_ps = str(ps) self.checkRef(str_ps) - pmap = { - posix1e.ACL_READ: "read", - posix1e.ACL_WRITE: "write", - posix1e.ACL_EXECUTE: "execute", - } - for perm in pmap: + for perm in PERMSETS: str_ps = str(ps) + txt = PERMSETS[perm][0] self.checkRef(str_ps) self.assertFalse(ps.test(perm), "Empty permission set should not" - " have permission '%s'" % pmap[perm]) + " have permission '%s'" % txt) ps.add(perm) self.assertTrue(ps.test(perm), "Permission '%s' should exist" - " after addition" % pmap[perm]) + " after addition" % txt) str_ps = str(ps) self.checkRef(str_ps) ps.delete(perm) self.assertFalse(ps.test(perm), "Permission '%s' should not exist" - " after deletion" % pmap[perm]) + " after deletion" % txt) + @has_ext(HAS_ACL_ENTRY) + def testPermsetViaAccessors(self): + """Test permissions""" + acl = posix1e.ACL() + e = acl.append() + ps = e.permset + ps.clear() + str_ps = str(ps) + self.checkRef(str_ps) + def getter(perm): + return PERMSETS[perm][1].__get__(ps) + def setter(parm, value): + return PERMSETS[perm][1].__set__(ps, value) + for perm in PERMSETS: + str_ps = str(ps) + self.checkRef(str_ps) + txt = PERMSETS[perm][0] + self.assertFalse(getter(perm), "Empty permission set should not" + " have permission '%s'" % txt) + setter(perm, True) + self.assertTrue(ps.test(perm), "Permission '%s' should exist" + " after addition" % txt) + self.assertTrue(getter(perm), "Permission '%s' should exist" + " after addition" % txt) + str_ps = str(ps) + self.checkRef(str_ps) + setter(perm, False) + self.assertFalse(ps.test(perm), "Permission '%s' should not exist" + " after deletion" % txt) + self.assertFalse(getter(perm), "Permission '%s' should not exist" + " after deletion" % txt) + + @has_ext(HAS_ACL_ENTRY) + def testPermsetInvalidType(self): + acl = posix1e.ACL() + e = acl.append() + ps = e.permset + ps.clear() + def setter(): + ps.write = object() + self.assertRaises(TypeError, ps.add, "foobar") + self.assertRaises(TypeError, ps.delete, "foobar") + self.assertRaises(TypeError, ps.test, "foobar") + self.assertRaises(ValueError, setter) @has_ext(HAS_ACL_ENTRY and IS_PY_3K) def testQualifierValues(self): @@ -391,6 +660,65 @@ class ModificationTests(aclTest, unittest.TestCase): with self.assertRaises(OverflowError): e.qualifier = qualifier + @has_ext(HAS_ACL_ENTRY) + def testInvalidQualifier(self): + """Tests invalid qualifier handling""" + acl = posix1e.ACL() + e = acl.append() + def set_qual(x): + e.qualifier = x + def del_qual(): + del e.qualifier + self.assertRaises(TypeError, set_qual, object()) + self.assertRaises((TypeError, AttributeError), del_qual) + + @has_ext(HAS_ACL_ENTRY) + def testQualifierOnWrongTag(self): + """Tests qualifier setting on wrong tag""" + acl = posix1e.ACL() + e = acl.append() + e.tag_type = posix1e.ACL_OTHER + def set_qual(x): + e.qualifier = x + def get_qual(): + return e.qualifier + self.assertRaises(TypeError, set_qual, 1) + self.assertRaises(TypeError, get_qual) + + + @has_ext(HAS_ACL_ENTRY) + def testTagTypes(self): + """Tests tag type correct set/get""" + acl = posix1e.ACL() + e = acl.append() + for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP, posix1e.ACL_USER_OBJ, + posix1e.ACL_GROUP_OBJ, posix1e.ACL_MASK, + posix1e.ACL_OTHER]: + e.tag_type = tag + self.assertEqual(e.tag_type, tag) + # check we can show all tag types without breaking + self.assertTrue(str(e)) + + @has_ext(HAS_ACL_ENTRY) + def testInvalidTags(self): + """Tests tag type incorrect set/get""" + acl = posix1e.ACL() + e = acl.append() + def set_tag(x): + e.tag_type = x + self.assertRaises(TypeError, set_tag, object()) + def delete_tag(): + del e.tag_type + # For some reason, PyPy raises AttributeError. Strange... + self.assertRaises((TypeError, AttributeError), delete_tag) + + e.tag_type = posix1e.ACL_USER_OBJ + tag = max([posix1e.ACL_USER, posix1e.ACL_GROUP, posix1e.ACL_USER_OBJ, + posix1e.ACL_GROUP_OBJ, posix1e.ACL_MASK, + posix1e.ACL_OTHER]) + 1 + self.assertRaises(EnvironmentError, set_tag, tag) + # Check tag is still valid. + self.assertEqual(e.tag_type, posix1e.ACL_USER_OBJ) if __name__ == "__main__": unittest.main() -- 2.39.2