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 <pviktori@redhat.com>
Reviewed-by: Andreas Schneider <asn@samba.org>
Reviewed-by: Jelmer Vernooij <jelmer@samba.org>
--- /dev/null
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <Python.h>
+#include <talloc.h>
+#include <pytalloc.h>
+
+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);
+}
--- /dev/null
+#!/usr/bin/env python
+# Simple tests for the talloc python bindings.
+# Copyright (C) 2015 Petr Viktorin <pviktori@redhat.com>
+
+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 = '<talloc.Object talloc object at'
+ self.assertTrue(repr(obj).startswith(prefix))
+ self.assertEqual(repr(obj), str(obj))
+
+ def test_destructor(self):
+ # Check correct lifetime of the talloc'd data
+ lst = []
+ obj = _test_pytalloc.DObject(lambda: lst.append('dead'))
+ self.assertEqual(lst, [])
+ del obj
+ gc.collect()
+ self.assertEqual(lst, ['dead'])
+
+
+class TallocComparisonTests(unittest.TestCase):
+
+ def test_compare_same(self):
+ obj1 = _test_pytalloc.new()
+ self.assertTrue(obj1 == obj1)
+ self.assertFalse(obj1 != obj1)
+ self.assertTrue(obj1 <= obj1)
+ self.assertFalse(obj1 < obj1)
+ self.assertTrue(obj1 >= 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()
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'''