summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/tdb/pytdb.c82
-rw-r--r--lib/tdb/python/tests/simple.py34
-rw-r--r--lib/testtools/NEWS45
-rw-r--r--lib/testtools/testtools/matchers.py9
-rw-r--r--lib/testtools/testtools/testcase.py29
-rw-r--r--lib/testtools/testtools/tests/test_testtools.py32
-rw-r--r--lib/util/charset/charset.h2
-rw-r--r--lib/util/util.c49
-rw-r--r--lib/util/util.h2
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.