From 8ac21ec4aa76beb48c9c26cf1fd45495324d74cc Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 5 Mar 2015 10:06:05 +0100 Subject: [PATCH] pytalloc: Add tests Add tests for pytalloc. Since talloc objects can't be created from Python, a C extension with helpers is added. Signed-off-by: Petr Viktorin Reviewed-by: Andreas Schneider Reviewed-by: Jelmer Vernooij --- lib/talloc/test_pytalloc.c | 128 ++++++++++++++++++++++++++++++++++++ lib/talloc/test_pytalloc.py | 114 ++++++++++++++++++++++++++++++++ lib/talloc/wscript | 19 +++++- 3 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 lib/talloc/test_pytalloc.c create mode 100644 lib/talloc/test_pytalloc.py diff --git a/lib/talloc/test_pytalloc.c b/lib/talloc/test_pytalloc.c new file mode 100644 index 00000000000..2eaa7c31fb2 --- /dev/null +++ b/lib/talloc/test_pytalloc.c @@ -0,0 +1,128 @@ +/* + Samba Unix SMB/CIFS implementation. + + C utilities for the pytalloc test suite. + Provides the "_test_pytalloc" Python module. + + NOTE: Please read talloc_guide.txt for full documentation + + Copyright (C) Petr Viktorin 2015 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#include +#include +#include + +static PyObject *testpytalloc_new(PyTypeObject *mod) +{ + char *obj = talloc_strdup(NULL, "This is a test string");; + return pytalloc_steal(pytalloc_GetObjectType(), obj); +} + +static PyObject *testpytalloc_get_object_type(PyObject *mod) { + PyObject *type = (PyObject *)pytalloc_GetObjectType(); + Py_INCREF(type); + return type; +} + +static PyObject *testpytalloc_reference(PyObject *mod, PyObject *args) { + pytalloc_Object *source = NULL; + void *ptr; + + if (!PyArg_ParseTuple(args, "O!", pytalloc_GetObjectType(), &source)) + return NULL; + + ptr = source->ptr; + return pytalloc_reference_ex(pytalloc_GetObjectType(), ptr, ptr); +} + +static PyMethodDef test_talloc_methods[] = { + { "new", (PyCFunction)testpytalloc_new, METH_NOARGS, + "create a talloc Object with a testing string"}, + { "get_object_type", (PyCFunction)testpytalloc_get_object_type, METH_NOARGS, + "call pytalloc_GetObjectType"}, + { "reference", (PyCFunction)testpytalloc_reference, METH_VARARGS, + "call pytalloc_reference_ex"}, + { NULL } +}; + +static PyTypeObject DObject_Type; + +static int dobject_destructor(void *ptr) +{ + PyObject *destructor_func = *talloc_get_type(ptr, PyObject*); + PyObject *ret; + ret = PyObject_CallObject(destructor_func, NULL); + Py_DECREF(destructor_func); + if (ret == NULL) { + PyErr_Print(); + } else { + Py_DECREF(ret); + } + return 0; +} + +static PyObject *dobject_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *destructor_func = NULL; + PyObject **obj; + + if (!PyArg_ParseTuple(args, "O", &destructor_func)) + return NULL; + Py_INCREF(destructor_func); + + obj = talloc(NULL, PyObject*); + *obj = destructor_func; + + talloc_set_destructor((void*)obj, dobject_destructor); + return pytalloc_steal(&DObject_Type, obj); +} + +static PyTypeObject DObject_Type = { + .tp_name = "_test_pytalloc.DObject", + .tp_basicsize = sizeof(pytalloc_Object), + .tp_methods = NULL, + .tp_new = dobject_new, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = "test talloc object that calls a function when underlying data is freed\n", +}; + +#define MODULE_DOC "Test utility module for pytalloc" + +void init_test_pytalloc(void); +void init_test_pytalloc(void) +{ + PyObject *m; + + DObject_Type.tp_base = pytalloc_GetObjectType(); + if (PyType_Ready(&DObject_Type) < 0) { + return; + } + + m = Py_InitModule3("_test_pytalloc", test_talloc_methods, MODULE_DOC); + + if (m == NULL) { + return; + } + + Py_INCREF(&DObject_Type); + Py_INCREF(DObject_Type.tp_base); + PyModule_AddObject(m, "DObject", (PyObject *)&DObject_Type); +} diff --git a/lib/talloc/test_pytalloc.py b/lib/talloc/test_pytalloc.py new file mode 100644 index 00000000000..961bfcb51b6 --- /dev/null +++ b/lib/talloc/test_pytalloc.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python +# Simple tests for the talloc python bindings. +# Copyright (C) 2015 Petr Viktorin + +import unittest +import subprocess +import sys +import re +import gc + +import talloc +import _test_pytalloc + +def dummy_func(): + pass + + +class TallocTests(unittest.TestCase): + + def test_report_full(self): + # report_full is hardcoded to print to stdout, so use a subprocess + process = subprocess.Popen([ + sys.executable, '-c', + """if True: + import talloc, _test_pytalloc + obj = _test_pytalloc.new() + talloc.report_full(obj) + """ + ], stdout=subprocess.PIPE) + output, stderr = process.communicate() + output = str(output) + self.assertTrue("full talloc report on 'talloc.Object" in output) + self.assertTrue("This is a test string" in output) + + def test_totalblocks(self): + obj = _test_pytalloc.new() + # Two blocks: the string, and the name + self.assertEqual(talloc.total_blocks(obj), 2) + + def test_repr(self): + obj = _test_pytalloc.new() + prefix = '= obj1) + self.assertFalse(obj1 > obj1) + + def test_compare_different(self): + # object comparison is consistent + obj1, obj2 = sorted([ + _test_pytalloc.new(), + _test_pytalloc.new()]) + self.assertFalse(obj1 == obj2) + self.assertTrue(obj1 != obj2) + self.assertTrue(obj1 <= obj2) + self.assertTrue(obj1 < obj2) + self.assertFalse(obj1 >= obj2) + self.assertFalse(obj1 > obj2) + + def test_compare_different_types(self): + # object comparison falls back to comparing types + if talloc.Object < _test_pytalloc.DObject: + obj1 = _test_pytalloc.new() + obj2 = _test_pytalloc.DObject(dummy_func) + else: + obj2 = _test_pytalloc.new() + obj1 = _test_pytalloc.DObject(dummy_func) + self.assertFalse(obj1 == obj2) + self.assertTrue(obj1 != obj2) + self.assertTrue(obj1 <= obj2) + self.assertTrue(obj1 < obj2) + self.assertFalse(obj1 >= obj2) + self.assertFalse(obj1 > obj2) + + +class TallocUtilTests(unittest.TestCase): + + def test_get_type(self): + self.assertTrue(talloc.Object is _test_pytalloc.get_object_type()) + + def test_refrence(self): + # Check correct lifetime of the talloc'd data with multiple references + lst = [] + obj = _test_pytalloc.DObject(lambda: lst.append('dead')) + ref = _test_pytalloc.reference(obj) + del obj + gc.collect() + self.assertEqual(lst, []) + del ref + gc.collect() + self.assertEqual(lst, ['dead']) + + +if __name__ == '__main__': + unittest.TestProgram() diff --git a/lib/talloc/wscript b/lib/talloc/wscript index a1b72a07613..13679883984 100644 --- a/lib/talloc/wscript +++ b/lib/talloc/wscript @@ -136,13 +136,30 @@ def build(bld): enabled=True, realname='talloc.so') + bld.SAMBA_PYTHON('test_pytalloc', + 'test_pytalloc.c', + deps='pytalloc', + enabled=True, + realname='_test_pytalloc.so', + install=False) + def test(ctx): '''run talloc testsuite''' import Utils, samba_utils + env = samba_utils.LOAD_ENVIRONMENT() cmd = os.path.join(Utils.g_module.blddir, 'talloc_testsuite') ret = samba_utils.RUN_COMMAND(cmd) print("testsuite returned %d" % ret) - sys.exit(ret) + if 'USING_SYSTEM_PYTALLOC_UTIL' not in env.defines and not env.disable_python: + cmd = "PYTHONPATH=%s %s test_pytalloc.py" % ( + os.path.join(Utils.g_module.blddir, 'python'), + env['PYTHON'], + ) + pyret = samba_utils.RUN_COMMAND(cmd) + else: + pyret = 0 + print("python testsuite returned %d" % pyret) + sys.exit(ret or pyret) def dist(): '''makes a tarball for distribution''' -- 2.34.1