diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/tdb/pytdb.c | 82 | ||||
-rw-r--r-- | lib/tdb/python/tests/simple.py | 34 | ||||
-rw-r--r-- | lib/testtools/NEWS | 45 | ||||
-rw-r--r-- | lib/testtools/testtools/matchers.py | 9 | ||||
-rw-r--r-- | lib/testtools/testtools/testcase.py | 29 | ||||
-rw-r--r-- | lib/testtools/testtools/tests/test_testtools.py | 32 | ||||
-rw-r--r-- | lib/util/charset/charset.h | 2 | ||||
-rw-r--r-- | lib/util/util.c | 49 | ||||
-rw-r--r-- | lib/util/util.h | 2 |
9 files changed, 231 insertions, 53 deletions
diff --git a/lib/tdb/pytdb.c b/lib/tdb/pytdb.c index 7a9205b815..f2638db492 100644 --- a/lib/tdb/pytdb.c +++ b/lib/tdb/pytdb.c @@ -77,15 +77,19 @@ static PyObject *PyString_FromTDB_DATA(TDB_DATA data) static PyObject *py_tdb_open(PyTypeObject *type, PyObject *args, PyObject *kwargs) { - char *name; + char *name = NULL; int hash_size = 0, tdb_flags = TDB_DEFAULT, flags = O_RDWR, mode = 0600; TDB_CONTEXT *ctx; PyTdbObject *ret; const char *kwnames[] = { "name", "hash_size", "tdb_flags", "flags", "mode", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|iiii", (char **)kwnames, &name, &hash_size, &tdb_flags, &flags, &mode)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|siiii", (char **)kwnames, &name, &hash_size, &tdb_flags, &flags, &mode)) return NULL; + if (name == NULL) { + tdb_flags |= TDB_INTERNAL; + } + ctx = tdb_open(name, hash_size, tdb_flags, flags, mode); if (ctx == NULL) { PyErr_SetFromErrno(PyExc_IOError); @@ -112,6 +116,13 @@ static PyObject *obj_transaction_commit(PyTdbObject *self) Py_RETURN_NONE; } +static PyObject *obj_transaction_prepare_commit(PyTdbObject *self) +{ + int ret = tdb_transaction_prepare_commit(self->ctx); + PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); + Py_RETURN_NONE; +} + static PyObject *obj_transaction_start(PyTdbObject *self) { int ret = tdb_transaction_start(self->ctx); @@ -259,6 +270,27 @@ static PyObject *obj_store(PyTdbObject *self, PyObject *args) Py_RETURN_NONE; } +static PyObject *obj_add_flags(PyTdbObject *self, PyObject *args) +{ + unsigned flags; + + if (!PyArg_ParseTuple(args, "I", &flags)) + return NULL; + + tdb_add_flags(self->ctx, flags); + Py_RETURN_NONE; +} + +static PyObject *obj_remove_flags(PyTdbObject *self, PyObject *args) +{ + unsigned flags; + + if (!PyArg_ParseTuple(args, "I", &flags)) + return NULL; + + tdb_remove_flags(self->ctx, flags); + Py_RETURN_NONE; +} typedef struct { PyObject_HEAD @@ -311,6 +343,18 @@ static PyObject *obj_clear(PyTdbObject *self) Py_RETURN_NONE; } +static PyObject *obj_enable_seqnum(PyTdbObject *self) +{ + tdb_enable_seqnum(self->ctx); + Py_RETURN_NONE; +} + +static PyObject *obj_increment_seqnum_nonblock(PyTdbObject *self) +{ + tdb_increment_seqnum_nonblock(self->ctx); + Py_RETURN_NONE; +} + static PyMethodDef tdb_object_methods[] = { { "transaction_cancel", (PyCFunction)obj_transaction_cancel, METH_NOARGS, "S.transaction_cancel() -> None\n" @@ -318,6 +362,9 @@ static PyMethodDef tdb_object_methods[] = { { "transaction_commit", (PyCFunction)obj_transaction_commit, METH_NOARGS, "S.transaction_commit() -> None\n" "Commit the currently active transaction." }, + { "transaction_prepare_commit", (PyCFunction)obj_transaction_prepare_commit, METH_NOARGS, + "S.transaction_prepare_commit() -> None\n" + "Prepare to commit the currently active transaction" }, { "transaction_start", (PyCFunction)obj_transaction_start, METH_NOARGS, "S.transaction_start() -> None\n" "Start a new transaction." }, @@ -341,9 +388,15 @@ static PyMethodDef tdb_object_methods[] = { "Check whether key exists in this database." }, { "store", (PyCFunction)obj_store, METH_VARARGS, "S.store(key, data, flag=REPLACE) -> None" "Store data." }, + { "add_flags", (PyCFunction)obj_add_flags, METH_VARARGS, "S.add_flags(flags) -> None" }, + { "remove_flags", (PyCFunction)obj_remove_flags, METH_VARARGS, "S.remove_flags(flags) -> None" }, { "iterkeys", (PyCFunction)tdb_object_iter, METH_NOARGS, "S.iterkeys() -> iterator" }, { "clear", (PyCFunction)obj_clear, METH_NOARGS, "S.clear() -> None\n" "Wipe the entire database." }, + { "enable_seqnum", (PyCFunction)obj_enable_seqnum, METH_NOARGS, + "S.enable_seqnum() -> None" }, + { "increment_seqnum_nonblock", (PyCFunction)obj_increment_seqnum_nonblock, METH_NOARGS, + "S.increment_seqnum_nonblock() -> None" }, { NULL } }; @@ -365,6 +418,11 @@ static PyObject *obj_get_map_size(PyTdbObject *self, void *closure) return PyInt_FromLong(tdb_map_size(self->ctx)); } +static PyObject *obj_get_freelist_size(PyTdbObject *self, void *closure) +{ + return PyInt_FromLong(tdb_freelist_size(self->ctx)); +} + static PyObject *obj_get_flags(PyTdbObject *self, void *closure) { return PyInt_FromLong(tdb_get_flags(self->ctx)); @@ -375,18 +433,30 @@ static PyObject *obj_get_filename(PyTdbObject *self, void *closure) return PyString_FromString(tdb_name(self->ctx)); } +static PyObject *obj_get_seqnum(PyTdbObject *self, void *closure) +{ + return PyInt_FromLong(tdb_get_seqnum(self->ctx)); +} + + static PyGetSetDef tdb_object_getsetters[] = { { (char *)"hash_size", (getter)obj_get_hash_size, NULL, NULL }, { (char *)"map_size", (getter)obj_get_map_size, NULL, NULL }, + { (char *)"freelist_size", (getter)obj_get_freelist_size, NULL, NULL }, { (char *)"flags", (getter)obj_get_flags, NULL, NULL }, { (char *)"max_dead", NULL, (setter)obj_set_max_dead, NULL }, { (char *)"filename", (getter)obj_get_filename, NULL, (char *)"The filename of this TDB file."}, + { (char *)"seqnum", (getter)obj_get_seqnum, NULL, NULL }, { NULL } }; static PyObject *tdb_object_repr(PyTdbObject *self) { - return PyString_FromFormat("Tdb('%s')", tdb_name(self->ctx)); + if (tdb_get_flags(self->ctx) & TDB_INTERNAL) { + return PyString_FromString("Tdb(<internal>)"); + } else { + return PyString_FromFormat("Tdb('%s')", tdb_name(self->ctx)); + } } static void tdb_object_dealloc(PyTdbObject *self) @@ -497,6 +567,12 @@ void inittdb(void) PyModule_AddObject(m, "NOMMAP", PyInt_FromLong(TDB_NOMMAP)); PyModule_AddObject(m, "CONVERT", PyInt_FromLong(TDB_CONVERT)); PyModule_AddObject(m, "BIGENDIAN", PyInt_FromLong(TDB_BIGENDIAN)); + PyModule_AddObject(m, "NOSYNC", PyInt_FromLong(TDB_NOSYNC)); + PyModule_AddObject(m, "SEQNUM", PyInt_FromLong(TDB_SEQNUM)); + PyModule_AddObject(m, "VOLATILE", PyInt_FromLong(TDB_VOLATILE)); + PyModule_AddObject(m, "ALLOW_NESTING", PyInt_FromLong(TDB_ALLOW_NESTING)); + PyModule_AddObject(m, "DISALLOW_NESTING", PyInt_FromLong(TDB_DISALLOW_NESTING)); + PyModule_AddObject(m, "__docformat__", PyString_FromString("restructuredText")); Py_INCREF(&PyTdb); diff --git a/lib/tdb/python/tests/simple.py b/lib/tdb/python/tests/simple.py index 6b1e840f32..18180e16bd 100644 --- a/lib/tdb/python/tests/simple.py +++ b/lib/tdb/python/tests/simple.py @@ -18,6 +18,7 @@ class OpenTdbTests(TestCase): tdb.DEFAULT, os.O_RDWR) class CloseTdbTests(TestCase): + def test_double_close(self): self.tdb = tdb.Tdb(tempfile.mkstemp()[1], 0, tdb.DEFAULT, os.O_CREAT|os.O_RDWR) @@ -28,6 +29,15 @@ class CloseTdbTests(TestCase): self.tdb.close() +class InternalTdbTests(TestCase): + + def test_repr(self): + self.tdb = tdb.Tdb() + + # repr used to crash on internal db + self.assertEquals(repr(self.tdb), "Tdb(<internal>)") + + class SimpleTdbTests(TestCase): def setUp(self): @@ -86,6 +96,9 @@ class SimpleTdbTests(TestCase): def test_map_size(self): self.tdb.map_size + def test_freelist_size(self): + self.tdb.freelist_size + def test_name(self): self.tdb.filename @@ -108,11 +121,13 @@ class SimpleTdbTests(TestCase): self.tdb.transaction_commit() self.assertEquals("1", self.tdb["bloe"]) - def test_iterator(self): + def test_transaction_prepare_commit(self): self.tdb["bloe"] = "2" - self.tdb["bla"] = "hoi" - i = iter(self.tdb) - self.assertEquals(set(["bloe", "bla"]), set([i.next(), i.next()])) + self.tdb.transaction_start() + self.tdb["bloe"] = "1" + self.tdb.transaction_prepare_commit() + self.tdb.transaction_commit() + self.assertEquals("1", self.tdb["bloe"]) def test_iterkeys(self): self.tdb["bloe"] = "2" @@ -127,11 +142,22 @@ class SimpleTdbTests(TestCase): self.tdb.clear() self.assertEquals(0, len(list(self.tdb))) + def test_seqnum(self): + self.tdb.enable_seqnum() + seq1 = self.tdb.seqnum + self.tdb.increment_seqnum_nonblock() + seq2 = self.tdb.seqnum + self.assertEquals(seq2-seq1, 1) + def test_len(self): self.assertEquals(0, len(list(self.tdb))) self.tdb["entry"] = "value" self.assertEquals(1, len(list(self.tdb))) + def test_add_flags(self): + self.tdb.add_flags(tdb.NOMMAP) + self.tdb.remove_flags(tdb.NOMMAP) + if __name__ == '__main__': import unittest diff --git a/lib/testtools/NEWS b/lib/testtools/NEWS index dc5e6df8f1..596df0d6a6 100644 --- a/lib/testtools/NEWS +++ b/lib/testtools/NEWS @@ -4,6 +4,15 @@ testtools NEWS NEXT ~~~~ +Improvements +------------ + +* Code duplication between assertEqual and the matcher Equals has been removed. + +* In normal circumstances, a TestCase will no longer share details with clones + of itself. (Andrew Bennetts, bug #637725) + + 0.9.6 ~~~~~ @@ -17,32 +26,32 @@ patches and TestCase.assertEqual gives slightly nicer errors. Improvements ------------ - * 'TestCase.assertEqual' now formats errors a little more nicely, in the - style of bzrlib. +* 'TestCase.assertEqual' now formats errors a little more nicely, in the + style of bzrlib. - * Added `PlaceHolder` and `ErrorHolder`, TestCase-like objects that can be - used to add results to a `TestResult`. +* Added `PlaceHolder` and `ErrorHolder`, TestCase-like objects that can be + used to add results to a `TestResult`. - * 'Mismatch' now takes optional description and details parameters, so - custom Matchers aren't compelled to make their own subclass. +* 'Mismatch' now takes optional description and details parameters, so + custom Matchers aren't compelled to make their own subclass. - * jml added a built-in UTF8_TEXT ContentType to make it slightly easier to - add details to test results. See bug #520044. +* jml added a built-in UTF8_TEXT ContentType to make it slightly easier to + add details to test results. See bug #520044. - * Fix a bug in our built-in matchers where assertThat would blow up if any - of them failed. All built-in mismatch objects now provide get_details(). +* Fix a bug in our built-in matchers where assertThat would blow up if any + of them failed. All built-in mismatch objects now provide get_details(). - * New 'Is' matcher, which lets you assert that a thing is identical to - another thing. +* New 'Is' matcher, which lets you assert that a thing is identical to + another thing. - * New 'LessThan' matcher which lets you assert that a thing is less than - another thing. +* New 'LessThan' matcher which lets you assert that a thing is less than + another thing. - * TestCase now has a 'patch()' method to make it easier to monkey-patching - objects in tests. See the manual for more information. Fixes bug #310770. +* TestCase now has a 'patch()' method to make it easier to monkey-patching + objects in tests. See the manual for more information. Fixes bug #310770. - * MultiTestResult methods now pass back return values from the results it - forwards to. +* MultiTestResult methods now pass back return values from the results it + forwards to. 0.9.5 ~~~~~ diff --git a/lib/testtools/testtools/matchers.py b/lib/testtools/testtools/matchers.py index 6a4c82a2fe..61b5bd74f9 100644 --- a/lib/testtools/testtools/matchers.py +++ b/lib/testtools/testtools/matchers.py @@ -25,6 +25,7 @@ __all__ = [ import doctest import operator +from pprint import pformat class Matcher(object): @@ -178,6 +179,14 @@ class _BinaryMismatch(Mismatch): self.other = other def describe(self): + left = repr(self.expected) + right = repr(self.other) + if len(left) + len(right) > 70: + return "%s:\nreference = %s\nactual = %s\n" % ( + self._mismatch_string, pformat(self.expected), + pformat(self.other)) + else: + return "%s %s %s" % (left, self._mismatch_string,right) return "%r %s %r" % (self.expected, self._mismatch_string, self.other) diff --git a/lib/testtools/testtools/testcase.py b/lib/testtools/testtools/testcase.py index 48eec71d41..959c129691 100644 --- a/lib/testtools/testtools/testcase.py +++ b/lib/testtools/testtools/testcase.py @@ -17,13 +17,16 @@ try: except ImportError: wraps = None import itertools -from pprint import pformat import sys import types import unittest from testtools import content from testtools.compat import advance_iterator +from testtools.matchers import ( + Annotate, + Equals, + ) from testtools.monkey import patch from testtools.runtest import RunTest from testtools.testresult import TestResult @@ -81,7 +84,9 @@ class TestCase(unittest.TestCase): self._traceback_id_gen = itertools.count(0) self.__setup_called = False self.__teardown_called = False - self.__details = {} + # __details is lazy-initialized so that a constructed-but-not-run + # TestCase is safe to use with clone_test_with_new_id. + self.__details = None self.__RunTest = kwargs.get('runTest', RunTest) self.__exception_handlers = [] self.exception_handlers = [ @@ -114,6 +119,8 @@ class TestCase(unittest.TestCase): :param content_object: The content object for this detail. See testtools.content for more detail. """ + if self.__details is None: + self.__details = {} self.__details[name] = content_object def getDetails(self): @@ -121,6 +128,8 @@ class TestCase(unittest.TestCase): For more details see pydoc testtools.TestResult. """ + if self.__details is None: + self.__details = {} return self.__details def patch(self, obj, attribute, value): @@ -230,18 +239,10 @@ class TestCase(unittest.TestCase): :param observed: The observed value. :param message: An optional message to include in the error. """ - try: - return super(TestCase, self).assertEqual(expected, observed) - except self.failureException: - lines = [] - if message: - lines.append(message) - lines.extend( - ["not equal:", - "a = %s" % pformat(expected), - "b = %s" % pformat(observed), - '']) - self.fail('\n'.join(lines)) + matcher = Equals(expected) + if message: + matcher = Annotate(message, matcher) + self.assertThat(observed, matcher) failUnlessEqual = assertEquals = assertEqual diff --git a/lib/testtools/testtools/tests/test_testtools.py b/lib/testtools/testtools/tests/test_testtools.py index 9edc5a5176..5dfb355990 100644 --- a/lib/testtools/testtools/tests/test_testtools.py +++ b/lib/testtools/testtools/tests/test_testtools.py @@ -461,6 +461,15 @@ class TestAssertions(TestCase): 'a = %s' % pformat(a), 'b = %s' % pformat(b), '']) + expected_error = '\n'.join([ + 'Match failed. Matchee: "%r"' % b, + 'Matcher: Annotate(%r, Equals(%r))' % (message, a), + 'Difference: !=:', + 'reference = %s' % pformat(a), + 'actual = %s' % pformat(b), + ': ' + message, + '' + ]) self.assertFails(expected_error, self.assertEqual, a, b, message) self.assertFails(expected_error, self.assertEquals, a, b, message) self.assertFails(expected_error, self.failUnlessEqual, a, b, message) @@ -468,11 +477,12 @@ class TestAssertions(TestCase): def test_assertEqual_formatting_no_message(self): a = "cat" b = "dog" - expected_error = '\n'.join( - ['not equal:', - 'a = %s' % pformat(a), - 'b = %s' % pformat(b), - '']) + expected_error = '\n'.join([ + 'Match failed. Matchee: "dog"', + 'Matcher: Equals(\'cat\')', + 'Difference: \'cat\' != \'dog\'', + '' + ]) self.assertFails(expected_error, self.assertEqual, a, b) self.assertFails(expected_error, self.assertEquals, a, b) self.assertFails(expected_error, self.failUnlessEqual, a, b) @@ -760,6 +770,18 @@ class TestCloneTestWithNewId(TestCase): self.assertEqual(oldName, test.id(), "the original test instance should be unchanged.") + def test_cloned_testcase_does_not_share_details(self): + """A cloned TestCase does not share the details dict.""" + class Test(TestCase): + def test_foo(self): + self.addDetail( + 'foo', content.Content('text/plain', lambda: 'foo')) + orig_test = Test('test_foo') + cloned_test = clone_test_with_new_id(orig_test, self.getUniqueString()) + orig_test.run(unittest.TestResult()) + self.assertEqual('foo', orig_test.getDetails()['foo'].iter_bytes()) + self.assertEqual(None, cloned_test.getDetails().get('foo')) + class TestDetailsProvided(TestWithDetails): diff --git a/lib/util/charset/charset.h b/lib/util/charset/charset.h index 68907aa593..bd08f7efd9 100644 --- a/lib/util/charset/charset.h +++ b/lib/util/charset/charset.h @@ -266,7 +266,7 @@ static size_t CHARSETNAME ## _pull(void *cd, const char **inbuf, size_t *inbytes char **outbuf, size_t *outbytesleft) \ { \ while (*inbytesleft >= 1 && *outbytesleft >= 2) { \ - *(uint16*)(*outbuf) = to_ucs2[((unsigned char*)(*inbuf))[0]]; \ + SSVAL(*outbuf, 0, to_ucs2[((unsigned char*)(*inbuf))[0]]); \ (*inbytesleft) -= 1; \ (*outbytesleft) -= 2; \ (*inbuf) += 1; \ diff --git a/lib/util/util.c b/lib/util/util.c index 076ddf47fc..11bb315176 100644 --- a/lib/util/util.c +++ b/lib/util/util.c @@ -165,15 +165,50 @@ _PUBLIC_ bool directory_create_or_exist(const char *dname, uid_t uid, Sleep for a specified number of milliseconds. **/ -_PUBLIC_ void msleep(unsigned int t) +_PUBLIC_ void smb_msleep(unsigned int t) { - struct timeval tval; +#if defined(HAVE_NANOSLEEP) + struct timespec ts; + int ret; + + ts.tv_sec = t/1000; + ts.tv_nsec = 1000000*(t%1000); + + do { + errno = 0; + ret = nanosleep(&ts, &ts); + } while (ret < 0 && errno == EINTR && (ts.tv_sec > 0 || ts.tv_nsec > 0)); +#else + unsigned int tdiff=0; + struct timeval tval,t1,t2; + fd_set fds; - tval.tv_sec = t/1000; - tval.tv_usec = 1000*(t%1000); - /* this should be the real select - do NOT replace - with sys_select() */ - select(0,NULL,NULL,NULL,&tval); + GetTimeOfDay(&t1); + t2 = t1; + + while (tdiff < t) { + tval.tv_sec = (t-tdiff)/1000; + tval.tv_usec = 1000*((t-tdiff)%1000); + + /* Never wait for more than 1 sec. */ + if (tval.tv_sec > 1) { + tval.tv_sec = 1; + tval.tv_usec = 0; + } + + FD_ZERO(&fds); + errno = 0; + select(0,&fds,NULL,NULL,&tval); + + GetTimeOfDay(&t2); + if (t2.tv_sec < t1.tv_sec) { + /* Someone adjusted time... */ + t1 = t2; + } + + tdiff = usec_time_diff(&t2,&t1)/1000; + } +#endif } /** diff --git a/lib/util/util.h b/lib/util/util.h index 994fad04d3..c613e65adf 100644 --- a/lib/util/util.h +++ b/lib/util/util.h @@ -655,7 +655,7 @@ _PUBLIC_ int set_blocking(int fd, bool set); /** Sleep for a specified number of milliseconds. **/ -_PUBLIC_ void msleep(unsigned int t); +_PUBLIC_ void smb_msleep(unsigned int t); /** Get my own name, return in talloc'ed storage. |