Index: build.conf =================================================================== --- build.conf (revision 16364) +++ build.conf (working copy) @@ -353,7 +353,7 @@ type = swig_lib lang = python path = subversion/bindings/swig/python/libsvn_swig_py -libs = libsvn_subr libsvn_delta aprutil apriconv apr +libs = libsvn_subr libsvn_delta libsvn_wc aprutil apriconv apr link-cmd = $(LINK) $(SWIG_PY_LIBS) install = swig-py-lib # need special build rule to include -DSWIGPYTHON Index: subversion/libsvn_fs_base/reps-strings.c =================================================================== --- subversion/libsvn_fs_base/reps-strings.c (revision 16364) +++ subversion/libsvn_fs_base/reps-strings.c (working copy) @@ -228,7 +228,7 @@ /* Copy the (first) window into the baton. */ apr_pool_t *window_pool = svn_pool_create (cb->trail->pool); assert (cb->window_pool == NULL); - cb->window = svn_txdelta__copy_window (window, window_pool); + cb->window = svn_txdelta_window_dup (window, window_pool); cb->window_pool = window_pool; cb->done = (window->sview_len == 0 || window->src_ops == 0); } Index: subversion/include/svn_delta.h =================================================================== --- subversion/include/svn_delta.h (revision 16364) +++ subversion/include/svn_delta.h (working copy) @@ -175,6 +175,13 @@ } svn_txdelta_window_t; +/** + * Return a deep copy of @a window, allocated in @a pool. + * + * @since New in 1.3. + */ +svn_txdelta_window_t *svn_txdelta_window_dup ( + const svn_txdelta_window_t *window, apr_pool_t *pool); /** A typedef for functions that consume a series of delta windows, for * use in caller-pushes interfaces. Such functions will typically Index: subversion/include/svn_types.h =================================================================== --- subversion/include/svn_types.h (revision 16364) +++ subversion/include/svn_types.h (working copy) @@ -354,6 +354,15 @@ } svn_log_changed_path_t; +/** + * Return a deep copy of @a changed_path, allocated in @a pool. + * + * @since New in 1.3. + */ +svn_log_changed_path_t *svn_log_changed_path_dup ( + const svn_log_changed_path_t *changed_path, apr_pool_t *pool); + + /** The callback invoked by log message loopers, such as * @c svn_ra_plugin_t.get_log() and svn_repos_get_logs(). * Index: subversion/include/svn_auth.h =================================================================== --- subversion/include/svn_auth.h (revision 16364) +++ subversion/include/svn_auth.h (working copy) @@ -294,6 +294,14 @@ const char *ascii_cert; } svn_auth_ssl_server_cert_info_t; +/** + * Return a deep copy of @a info, allocated in @a pool. + * + * @since New in 1.3. + */ +svn_auth_ssl_server_cert_info_t *svn_auth_ssl_server_cert_info_dup ( + const svn_auth_ssl_server_cert_info_t *info, apr_pool_t *pool); + /** @c SVN_AUTH_CRED_SSL_SERVER_TRUST credentials. */ typedef struct svn_auth_cred_ssl_server_trust_t { Index: subversion/libsvn_subr/constructors.c =================================================================== --- subversion/libsvn_subr/constructors.c (revision 16364) +++ subversion/libsvn_subr/constructors.c (working copy) @@ -17,6 +17,7 @@ */ #include +#include #include "svn_types.h" @@ -33,3 +34,18 @@ return commit_info; } +svn_log_changed_path_t * +svn_log_changed_path_dup (const svn_log_changed_path_t *changed_path, + apr_pool_t *pool) +{ + svn_log_changed_path_t *new_changed_path + = apr_palloc (pool, sizeof (*new_changed_path)); + + *new_changed_path = *changed_path; + + if (new_changed_path->copyfrom_path) + new_changed_path->copyfrom_path = + apr_pstrdup (pool, new_changed_path->copyfrom_path); + + return new_changed_path; +} Index: subversion/libsvn_subr/auth.c =================================================================== --- subversion/libsvn_subr/auth.c (revision 16364) +++ subversion/libsvn_subr/auth.c (working copy) @@ -349,3 +349,22 @@ return SVN_NO_ERROR; } + +svn_auth_ssl_server_cert_info_t * +svn_auth_ssl_server_cert_info_dup ( + const svn_auth_ssl_server_cert_info_t *info, apr_pool_t *pool) +{ + svn_auth_ssl_server_cert_info_t *new_info + = apr_palloc (pool, sizeof (*new_info)); + + *new_info = *info; + + new_info->hostname = apr_pstrdup (pool, new_info->hostname); + new_info->fingerprint = apr_pstrdup (pool, new_info->fingerprint); + new_info->valid_from = apr_pstrdup (pool, new_info->valid_from); + new_info->valid_until = apr_pstrdup (pool, new_info->valid_until); + new_info->issuer_dname = apr_pstrdup (pool, new_info->issuer_dname); + new_info->ascii_cert = apr_pstrdup (pool, new_info->ascii_cert); + + return new_info; +} Index: subversion/bindings/swig/python/tests/run_all.py =================================================================== --- subversion/bindings/swig/python/tests/run_all.py (revision 16364) +++ subversion/bindings/swig/python/tests/run_all.py (working copy) @@ -4,6 +4,7 @@ "%s/.." % bindir, "%s/../.libs" % bindir ] import unittest import pool +import repository import trac.versioncontrol.tests # Run all tests @@ -12,6 +13,7 @@ """Run all tests""" suite = unittest.TestSuite() suite.addTest(pool.suite()) + suite.addTest(repository.suite()) suite.addTest(trac.versioncontrol.tests.suite()); return suite Index: subversion/bindings/swig/python/tests/repository.py =================================================================== --- subversion/bindings/swig/python/tests/repository.py (revision 0) +++ subversion/bindings/swig/python/tests/repository.py (revision 0) @@ -0,0 +1,152 @@ +import unittest + +from svn import core, repos, fs, delta + +from trac.versioncontrol.tests.svn_fs import SubversionRepositoryTestSetup, \ + REPOS_PATH + +class ChangeReceiver(delta.Editor): + """A delta editor which saves textdeltas for later use""" + + def __init__(self, src_root, tgt_root): + self.src_root = src_root + self.tgt_root = tgt_root + self.textdeltas = [] + + def apply_textdelta(self, file_baton, base_checksum): + def textdelta_handler(textdelta): + if textdelta is not None: + self.textdeltas.append(textdelta) + return textdelta_handler + +class SubversionRepositoryTestCase(unittest.TestCase): + """Test cases for the Subversion repository layer""" + + def setUp(self): + """Load a Subversion repository""" + self.repos = repos.open(REPOS_PATH) + self.fs = repos.fs(self.repos) + self.rev = fs.youngest_rev(self.fs) + + def test_get_logs(self): + """Test scope of get_logs callbacks""" + logs = [] + def addLog( paths, revision, author, date, message, pool ): + if paths is not None: + logs.append(paths) + + # Run get_logs + repos.get_logs(self.repos, ['/'], self.rev, 0, True, 0, addLog) + + # Count and verify changes + change_count = 0 + for log in logs: + for path_changed in log.values(): + change_count += 1 + path_changed.assert_valid() + self.assertEqual(logs[2]["/tags/v1.1"].action, "A") + self.assertEqual(logs[2]["/tags/v1.1"].copyfrom_path, "/branches/v1x") + self.assertEqual(len(logs), 12) + self.assertEqual(change_count, 19) + + def test_dir_delta(self): + """Test scope of dir_delta callbacks""" + def authz_cb(root, path, pool): + return 1 + + # Run dir_delta + this_root = fs.revision_root(self.fs, self.rev) + prev_root = fs.revision_root(self.fs, self.rev-1) + editor = ChangeReceiver(this_root, prev_root) + e_ptr, e_baton = delta.make_editor(editor) + repos.dir_delta(prev_root, '', '', this_root, '', e_ptr, e_baton, + authz_cb, 1, 1, 0, 0) + + # Check results + self.assertEqual(editor.textdeltas[0].new_data, "This is a test.\n") + self.assertEqual(editor.textdeltas[1].new_data, "A test.\n") + self.assertEqual(len(editor.textdeltas),2) + +def suite(): + return unittest.makeSuite(SubversionRepositoryTestCase, 'test', + suiteClass=SubversionRepositoryTestSetup) + +if __name__ == '__main__': + runner = unittest.TextTestRunner() + runner.run(suite()) +import unittest + +from svn import core, repos, fs, delta + +from trac.versioncontrol.tests.svn_fs import SubversionRepositoryTestSetup, \ + REPOS_PATH + +class ChangeReceiver(delta.Editor): + """A delta editor which saves textdeltas for later use""" + + def __init__(self, src_root, tgt_root): + self.src_root = src_root + self.tgt_root = tgt_root + self.textdeltas = [] + + def apply_textdelta(self, file_baton, base_checksum): + def textdelta_handler(textdelta): + if textdelta is not None: + self.textdeltas.append(textdelta) + return textdelta_handler + +class SubversionRepositoryTestCase(unittest.TestCase): + """Test cases for the Subversion repository layer""" + + def setUp(self): + """Load a Subversion repository""" + self.repos = repos.open(REPOS_PATH) + self.fs = repos.fs(self.repos) + self.rev = fs.youngest_rev(self.fs) + + def test_get_logs(self): + """Test scope of get_logs callbacks""" + logs = [] + def addLog( paths, revision, author, date, message, pool ): + if paths is not None: + logs.append(paths) + + # Run get_logs + repos.get_logs(self.repos, ['/'], self.rev, 0, True, 0, addLog) + + # Count and verify changes + change_count = 0 + for log in logs: + for path_changed in log.values(): + change_count += 1 + path_changed.assert_valid() + self.assertEqual(logs[2]["/tags/v1.1"].action, "A") + self.assertEqual(logs[2]["/tags/v1.1"].copyfrom_path, "/branches/v1x") + self.assertEqual(len(logs), 12) + self.assertEqual(change_count, 19) + + def test_dir_delta(self): + """Test scope of dir_delta callbacks""" + def authz_cb(root, path, pool): + return 1 + + # Run dir_delta + this_root = fs.revision_root(self.fs, self.rev) + prev_root = fs.revision_root(self.fs, self.rev-1) + editor = ChangeReceiver(this_root, prev_root) + e_ptr, e_baton = delta.make_editor(editor) + repos.dir_delta(prev_root, '', '', this_root, '', e_ptr, e_baton, + authz_cb, 1, 1, 0, 0) + + # Check results + self.assertEqual(editor.textdeltas[0].new_data, "This is a test.\n") + self.assertEqual(editor.textdeltas[1].new_data, "A test.\n") + self.assertEqual(len(editor.textdeltas),2) + +def suite(): + return unittest.makeSuite(SubversionRepositoryTestCase, 'test', + suiteClass=SubversionRepositoryTestSetup) + +if __name__ == '__main__': + runner = unittest.TextTestRunner() + runner.run(suite()) Index: subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c =================================================================== --- subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c (revision 16364) +++ subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c (working copy) @@ -34,6 +34,7 @@ #include "svn_opt.h" #include "svn_delta.h" #include "svn_auth.h" +#include "svn_pools.h" #include "svn_private_config.h" /* for SVN_APR_INT64_T_PYCFMT */ @@ -389,28 +390,10 @@ return py_pool; } -static PyObject *make_ob_window(svn_txdelta_window_t *ptr, PyObject *py_pool) -{ - return svn_swig_NewPointerObjString(ptr, "svn_txdelta_window_t *", py_pool); -} -static PyObject *make_ob_status(svn_wc_status_t *ptr, PyObject *py_pool) -{ - return svn_swig_NewPointerObjString(ptr, "svn_wc_status_t *", py_pool); -} -static PyObject *make_ob_lock(svn_lock_t *ptr, PyObject *py_pool) -{ - return svn_swig_NewPointerObjString(ptr, "svn_lock_t *", py_pool); -} static PyObject *make_ob_fs_root(svn_fs_root_t *ptr, PyObject *py_pool) { return svn_swig_NewPointerObjString(ptr, "svn_fs_root_t *", py_pool); } -static PyObject *make_ob_server_cert_info( - const svn_auth_ssl_server_cert_info_t *ptr, PyObject *py_pool) -{ - return svn_swig_NewPointerObjString((void *)ptr, - "svn_auth_ssl_server_cert_info_t *", py_pool); -} /***/ /* Conversion from Python single objects (not hashes/lists/etc.) to @@ -601,7 +584,33 @@ PyObject *py_pool) { return convert_hash(hash, convert_to_swigtype, type, py_pool); +} + +#define DECLARE_SWIG_CONSTRUCTOR(type, dup) \ +static PyObject *make_ob_##type(void *value) \ +{ \ + apr_pool_t *new_pool = svn_pool_create(_global_pool); \ + PyObject *new_py_pool = svn_swig_NewPointerObj(new_pool, \ + svn_swig_TypeQuery("apr_pool_t *"), _global_svn_swig_py_pool); \ + svn_##type##_t *new_value = dup(value, new_pool); \ + return svn_swig_NewPointerObjString(new_value, "svn_" #type "_t *", \ + new_py_pool); \ +} \ +static PyObject *convert_##type(void *value, void *ctx, PyObject *py_pool) \ +{ \ + return make_ob_##type(value); \ +} \ +PyObject *svn_swig_py_convert_##type##_hash(apr_hash_t *hash) \ +{ \ + return convert_hash(hash, convert_##type, NULL, NULL); \ } + +DECLARE_SWIG_CONSTRUCTOR(txdelta_window, svn_txdelta_window_dup) +DECLARE_SWIG_CONSTRUCTOR(log_changed_path, svn_log_changed_path_dup) +DECLARE_SWIG_CONSTRUCTOR(wc_status, svn_wc_dup_status) +DECLARE_SWIG_CONSTRUCTOR(lock, svn_lock_dup) +DECLARE_SWIG_CONSTRUCTOR(auth_ssl_server_cert_info, + svn_auth_ssl_server_cert_info_dup) PyObject *svn_swig_py_c_strings_to_list(char **strings) { @@ -1206,8 +1215,8 @@ { /* invoke the handler with the window */ /* ### python doesn't have 'const' on the format */ - result = PyObject_CallFunction(handler, - (char *)"O&", make_ob_window, window); + result = PyObject_CallFunction(handler, (char *)"O&", + make_ob_txdelta_window, window); } if (result == NULL) @@ -1567,7 +1576,7 @@ svn_swig_py_acquire_py_lock(); if ((result = PyObject_CallFunction(function, (char *)"sO&", path, - make_ob_status, status)) == NULL) + make_ob_wc_status, status)) == NULL) { err = callback_exception_error(); } @@ -1640,15 +1649,9 @@ err = callback_exception_error(); goto finished; } - py_lock = make_ob_lock(lock, py_pool); - if (py_lock == NULL) { - Py_DECREF(py_pool); - err = callback_exception_error(); - goto finished; - } - if ((result = PyObject_CallFunction(function, (char *)"OO", - py_lock, py_pool)) == NULL) + if ((result = PyObject_CallFunction(function, (char *)"O&O", + make_ob_lock, py_lock, py_pool)) == NULL) { err = callback_exception_error(); } @@ -1846,8 +1849,7 @@ if (changed_paths) { - swig_type_info *tinfo = svn_swig_TypeQuery("svn_log_changed_path_t *"); - chpaths = svn_swig_py_convert_hash (changed_paths, tinfo, py_pool); + chpaths = svn_swig_py_convert_log_changed_path_hash(changed_paths); } else { @@ -2040,18 +2042,10 @@ err = callback_exception_error(); goto finished; } - py_cert_info = make_ob_server_cert_info(cert_info, py_pool); - if (py_pool == NULL) { - Py_DECREF(py_pool); - err = callback_exception_error(); - goto finished; - } - if ((result = PyObject_CallFunction(function, - (char *)"slOlO", - realm, failures, - py_cert_info, - may_save, py_pool)) == NULL) + if ((result = PyObject_CallFunction(function, (char *)"slO&lO", + realm, failures, make_ob_auth_ssl_server_cert_info, + cert_info, may_save, py_pool)) == NULL) { err = callback_exception_error(); } Index: subversion/libsvn_delta/delta.h =================================================================== --- subversion/libsvn_delta/delta.h (revision 16364) +++ subversion/libsvn_delta/delta.h (working copy) @@ -69,12 +69,6 @@ svn_txdelta__make_window (const svn_txdelta__ops_baton_t *build_baton, apr_pool_t *pool); -/* Return a copy of WINDOW, allocated from POOL. */ -svn_txdelta_window_t * -svn_txdelta__copy_window (const svn_txdelta_window_t *window, - apr_pool_t *pool); - - /* Create vdelta window data. Allocate temporary data from POOL. */ void svn_txdelta__vdelta (svn_txdelta__ops_baton_t *build_baton, const char *start, Index: subversion/libsvn_delta/text_delta.c =================================================================== --- subversion/libsvn_delta/text_delta.c (revision 16364) +++ subversion/libsvn_delta/text_delta.c (working copy) @@ -154,8 +154,8 @@ svn_txdelta_window_t * -svn_txdelta__copy_window (const svn_txdelta_window_t *window, - apr_pool_t *pool) +svn_txdelta_window_dup (const svn_txdelta_window_t *window, + apr_pool_t *pool) { svn_txdelta__ops_baton_t build_baton = { 0 }; svn_txdelta_window_t *new_window;