diff options
Diffstat (limited to 'subversion/tests/libsvn_ra/ra-test.c')
-rw-r--r-- | subversion/tests/libsvn_ra/ra-test.c | 696 |
1 files changed, 678 insertions, 18 deletions
diff --git a/subversion/tests/libsvn_ra/ra-test.c b/subversion/tests/libsvn_ra/ra-test.c index 584da3d..83ac59f 100644 --- a/subversion/tests/libsvn_ra/ra-test.c +++ b/subversion/tests/libsvn_ra/ra-test.c @@ -25,7 +25,8 @@ #include <apr_general.h> #include <apr_pools.h> - +#include <apr_file_io.h> +#include <assert.h> #define SVN_DEPRECATED #include "svn_error.h" @@ -33,6 +34,9 @@ #include "svn_ra.h" #include "svn_time.h" #include "svn_pools.h" +#include "svn_cmdline.h" +#include "svn_dirent_uri.h" +#include "svn_hash.h" #include "../svn_test.h" #include "../svn_test_fs.h" @@ -44,23 +48,22 @@ static svn_error_t * -make_and_open_local_repos(svn_ra_session_t **session, - const char *repos_name, - const svn_test_opts_t *opts, - apr_pool_t *pool) +make_and_open_repos(svn_ra_session_t **session, + const char *repos_name, + const svn_test_opts_t *opts, + apr_pool_t *pool) { - svn_repos_t *repos; const char *url; svn_ra_callbacks2_t *cbtable; SVN_ERR(svn_ra_create_callbacks(&cbtable, pool)); + SVN_ERR(svn_test__init_auth_baton(&cbtable->auth_baton, pool)); - SVN_ERR(svn_test__create_repos(&repos, repos_name, opts, pool)); + SVN_ERR(svn_test__create_repos2(NULL, &url, NULL, repos_name, opts, + pool, pool)); SVN_ERR(svn_ra_initialize(pool)); - SVN_ERR(svn_uri_get_file_url_from_dirent(&url, repos_name, pool)); - - SVN_ERR(svn_ra_open3(session, url, NULL, cbtable, NULL, NULL, pool)); + SVN_ERR(svn_ra_open4(session, NULL, url, NULL, cbtable, NULL, NULL, pool)); return SVN_NO_ERROR; } @@ -90,6 +93,174 @@ commit_changes(svn_ra_session_t *session, return SVN_NO_ERROR; } +static svn_error_t * +commit_tree(svn_ra_session_t *session, + apr_pool_t *pool) +{ + apr_hash_t *revprop_table = apr_hash_make(pool); + const svn_delta_editor_t *editor; + void *edit_baton; + const char *repos_root_url; + void *root_baton, *A_baton, *B_baton, *file_baton; + + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + revprop_table, + NULL, NULL, NULL, TRUE, pool)); + SVN_ERR(svn_ra_get_repos_root(session, &repos_root_url, pool)); + + SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM, + pool, &root_baton)); + SVN_ERR(editor->add_directory("A", root_baton, NULL, SVN_INVALID_REVNUM, + pool, &A_baton)); + SVN_ERR(editor->add_directory("A/B", A_baton, NULL, SVN_INVALID_REVNUM, + pool, &B_baton)); + SVN_ERR(editor->add_file("A/B/f", B_baton, NULL, SVN_INVALID_REVNUM, + pool, &file_baton)); + SVN_ERR(editor->close_file(file_baton, NULL, pool)); + SVN_ERR(editor->add_file("A/B/g", B_baton, NULL, SVN_INVALID_REVNUM, + pool, &file_baton)); + SVN_ERR(editor->close_file(file_baton, NULL, pool)); + SVN_ERR(editor->close_directory(B_baton, pool)); + SVN_ERR(editor->add_directory("A/BB", A_baton, NULL, SVN_INVALID_REVNUM, + pool, &B_baton)); + SVN_ERR(editor->add_file("A/BB/f", B_baton, NULL, SVN_INVALID_REVNUM, + pool, &file_baton)); + SVN_ERR(editor->close_file(file_baton, NULL, pool)); + SVN_ERR(editor->add_file("A/BB/g", B_baton, NULL, SVN_INVALID_REVNUM, + pool, &file_baton)); + SVN_ERR(editor->close_file(file_baton, NULL, pool)); + SVN_ERR(editor->close_directory(B_baton, pool)); + SVN_ERR(editor->close_directory(A_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + return SVN_NO_ERROR; +} + +/* Baton for opening tunnels */ +typedef struct tunnel_baton_t +{ + int magic; /* TUNNEL_MAGIC */ + int open_count; + svn_boolean_t last_check; +} tunnel_baton_t; + +#define TUNNEL_MAGIC 0xF00DF00F + +/* Baton for closing a specific tunnel */ +typedef struct close_baton_t +{ + int magic; + tunnel_baton_t *tb; + apr_proc_t *proc; +} close_baton_t; + +#define CLOSE_MAGIC 0x1BADBAD1 + +static svn_boolean_t +check_tunnel(void *tunnel_baton, const char *tunnel_name) +{ + tunnel_baton_t *b = tunnel_baton; + + if (b->magic != TUNNEL_MAGIC) + abort(); + + b->last_check = (0 == strcmp(tunnel_name, "test")); + return b->last_check; +} + +static void +close_tunnel(void *tunnel_context, void *tunnel_baton); + +static svn_error_t * +open_tunnel(svn_stream_t **request, svn_stream_t **response, + svn_ra_close_tunnel_func_t *close_func, void **close_baton, + void *tunnel_baton, + const char *tunnel_name, const char *user, + const char *hostname, int port, + svn_cancel_func_t cancel_func, void *cancel_baton, + apr_pool_t *pool) +{ + svn_node_kind_t kind; + apr_proc_t *proc; + apr_procattr_t *attr; + apr_status_t status; + const char *args[] = { "svnserve", "-t", "-r", ".", NULL }; + const char *svnserve; + tunnel_baton_t *b = tunnel_baton; + close_baton_t *cb; + + SVN_TEST_ASSERT(b->magic == TUNNEL_MAGIC); + + SVN_ERR(svn_dirent_get_absolute(&svnserve, "../../svnserve/svnserve", pool)); +#ifdef WIN32 + svnserve = apr_pstrcat(pool, svnserve, ".exe", SVN_VA_NULL); +#endif + SVN_ERR(svn_io_check_path(svnserve, &kind, pool)); + if (kind != svn_node_file) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Could not find svnserve at %s", + svn_dirent_local_style(svnserve, pool)); + + status = apr_procattr_create(&attr, pool); + if (status == APR_SUCCESS) + status = apr_procattr_io_set(attr, 1, 1, 0); + if (status == APR_SUCCESS) + status = apr_procattr_cmdtype_set(attr, APR_PROGRAM); + proc = apr_palloc(pool, sizeof(*proc)); + if (status == APR_SUCCESS) + status = apr_proc_create(proc, + svn_dirent_local_style(svnserve, pool), + args, NULL, attr, pool); + if (status != APR_SUCCESS) + return svn_error_wrap_apr(status, "Could not run svnserve"); + apr_pool_note_subprocess(pool, proc, APR_KILL_NEVER); + + /* APR pipe objects inherit by default. But we don't want the + * tunnel agent's pipes held open by future child processes + * (such as other ra_svn sessions), so turn that off. */ + apr_file_inherit_unset(proc->in); + apr_file_inherit_unset(proc->out); + + cb = apr_pcalloc(pool, sizeof(*cb)); + cb->magic = CLOSE_MAGIC; + cb->tb = b; + cb->proc = proc; + + *request = svn_stream_from_aprfile2(proc->in, FALSE, pool); + *response = svn_stream_from_aprfile2(proc->out, FALSE, pool); + *close_func = close_tunnel; + *close_baton = cb; + ++b->open_count; + return SVN_NO_ERROR; +} + +static void +close_tunnel(void *tunnel_context, void *tunnel_baton) +{ + close_baton_t *b = tunnel_context; + + if (b->magic != CLOSE_MAGIC) + abort(); + if (--b->tb->open_count == 0) + { + apr_status_t child_exit_status; + int child_exit_code; + apr_exit_why_e child_exit_why; + + SVN_TEST_ASSERT_NO_RETURN(0 == apr_file_close(b->proc->in)); + SVN_TEST_ASSERT_NO_RETURN(0 == apr_file_close(b->proc->out)); + + child_exit_status = + apr_proc_wait(b->proc, &child_exit_code, &child_exit_why, APR_WAIT); + + SVN_TEST_ASSERT_NO_RETURN(child_exit_status == APR_CHILD_DONE); + SVN_TEST_ASSERT_NO_RETURN(child_exit_code == 0); + SVN_TEST_ASSERT_NO_RETURN(child_exit_why == APR_PROC_EXIT); + } +} + + + + /*-------------------------------------------------------------------*/ /** The tests **/ @@ -130,9 +301,9 @@ location_segments_test(const svn_test_opts_t *opts, b.segments = segments; b.pool = pool; - SVN_ERR(make_and_open_local_repos(&session, - "test-repo-locsegs", opts, - pool)); + SVN_ERR(make_and_open_repos(&session, + "test-repo-locsegs", opts, + pool)); /* ### This currently tests only a small subset of what's possible. */ SVN_ERR(commit_changes(session, pool)); @@ -153,6 +324,294 @@ location_segments_test(const svn_test_opts_t *opts, } +/* Test ra_svn tunnel callbacks. */ + +static svn_error_t * +check_tunnel_callback_test(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + tunnel_baton_t *b = apr_pcalloc(pool, sizeof(*b)); + svn_ra_callbacks2_t *cbtable; + svn_ra_session_t *session; + + b->magic = TUNNEL_MAGIC; + + SVN_ERR(svn_ra_create_callbacks(&cbtable, pool)); + cbtable->check_tunnel_func = check_tunnel; + cbtable->open_tunnel_func = open_tunnel; + cbtable->tunnel_baton = b; + SVN_ERR(svn_cmdline_create_auth_baton(&cbtable->auth_baton, + TRUE /* non_interactive */, + "jrandom", "rayjandom", + NULL, + TRUE /* no_auth_cache */, + FALSE /* trust_server_cert */, + NULL, NULL, NULL, pool)); + + b->last_check = TRUE; + SVN_TEST_ASSERT_ERROR(svn_ra_open4(&session, NULL, + "svn+foo://localhost/no-repo", + NULL, cbtable, NULL, NULL, pool), + SVN_ERR_RA_CANNOT_CREATE_SESSION); + SVN_TEST_ASSERT(!b->last_check); + return SVN_NO_ERROR; +} + +static svn_error_t * +tunnel_callback_test(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + tunnel_baton_t *b = apr_pcalloc(pool, sizeof(*b)); + apr_pool_t *scratch_pool = svn_pool_create(pool); + const char *url; + svn_ra_callbacks2_t *cbtable; + svn_ra_session_t *session; + const char tunnel_repos_name[] = "test-repo-tunnel"; + + b->magic = TUNNEL_MAGIC; + + SVN_ERR(svn_test__create_repos(NULL, tunnel_repos_name, opts, scratch_pool)); + + /* Immediately close the repository to avoid race condition with svnserve + (and then the cleanup code) with BDB when our pool is cleared. */ + svn_pool_clear(scratch_pool); + + url = apr_pstrcat(pool, "svn+test://localhost/", tunnel_repos_name, + SVN_VA_NULL); + SVN_ERR(svn_ra_create_callbacks(&cbtable, pool)); + cbtable->check_tunnel_func = check_tunnel; + cbtable->open_tunnel_func = open_tunnel; + cbtable->tunnel_baton = b; + SVN_ERR(svn_cmdline_create_auth_baton(&cbtable->auth_baton, + TRUE /* non_interactive */, + "jrandom", "rayjandom", + NULL, + TRUE /* no_auth_cache */, + FALSE /* trust_server_cert */, + NULL, NULL, NULL, pool)); + + b->last_check = FALSE; + SVN_ERR(svn_ra_open4(&session, NULL, url, NULL, cbtable, NULL, NULL, + scratch_pool)); + SVN_TEST_ASSERT(b->last_check); + SVN_TEST_ASSERT(b->open_count > 0); + svn_pool_destroy(scratch_pool); + SVN_TEST_ASSERT(b->open_count == 0); + return SVN_NO_ERROR; +} + +struct lock_result_t { + svn_lock_t *lock; + svn_error_t *err; +}; + +struct lock_baton_t { + apr_hash_t *results; + apr_pool_t *pool; +}; + +/* Implements svn_ra_lock_callback_t. */ +static svn_error_t * +lock_cb(void *baton, + const char *path, + svn_boolean_t do_lock, + const svn_lock_t *lock, + svn_error_t *ra_err, + apr_pool_t *pool) +{ + struct lock_baton_t *b = baton; + struct lock_result_t *result = apr_palloc(b->pool, + sizeof(struct lock_result_t)); + + if (lock) + { + result->lock = apr_palloc(b->pool, sizeof(svn_lock_t)); + *result->lock = *lock; + result->lock->path = apr_pstrdup(b->pool, lock->path); + result->lock->token = apr_pstrdup(b->pool, lock->token); + result->lock->owner = apr_pstrdup(b->pool, lock->owner); + result->lock->comment = apr_pstrdup(b->pool, lock->comment); + } + else + result->lock = NULL; + result->err = ra_err; + + svn_hash_sets(b->results, apr_pstrdup(b->pool, path), result); + + return SVN_NO_ERROR; +} + +static svn_error_t * +expect_lock(const char *path, + apr_hash_t *results, + svn_ra_session_t *session, + apr_pool_t *scratch_pool) +{ + svn_lock_t *lock; + struct lock_result_t *result = svn_hash_gets(results, path); + + SVN_TEST_ASSERT(result && result->lock && !result->err); + SVN_ERR(svn_ra_get_lock(session, &lock, path, scratch_pool)); + SVN_TEST_ASSERT(lock); + return SVN_NO_ERROR; +} + +static svn_error_t * +expect_error(const char *path, + apr_hash_t *results, + svn_ra_session_t *session, + apr_pool_t *scratch_pool) +{ + svn_lock_t *lock; + struct lock_result_t *result = svn_hash_gets(results, path); + + SVN_TEST_ASSERT(result && result->err); + SVN_TEST_ASSERT(!result->lock); + /* RA layers shouldn't report SVN_ERR_FS_NOT_FOUND */ + SVN_ERR(svn_ra_get_lock(session, &lock, path, scratch_pool)); + + SVN_TEST_ASSERT(!lock); + return SVN_NO_ERROR; +} + +static svn_error_t * +expect_unlock(const char *path, + apr_hash_t *results, + svn_ra_session_t *session, + apr_pool_t *scratch_pool) +{ + svn_lock_t *lock; + struct lock_result_t *result = svn_hash_gets(results, path); + + SVN_TEST_ASSERT(result && !result->err); + SVN_ERR(svn_ra_get_lock(session, &lock, path, scratch_pool)); + SVN_TEST_ASSERT(!lock); + return SVN_NO_ERROR; +} + +static svn_error_t * +expect_unlock_error(const char *path, + apr_hash_t *results, + svn_ra_session_t *session, + apr_pool_t *scratch_pool) +{ + svn_lock_t *lock; + struct lock_result_t *result = svn_hash_gets(results, path); + + SVN_TEST_ASSERT(result && result->err); + SVN_ERR(svn_ra_get_lock(session, &lock, path, scratch_pool)); + SVN_TEST_ASSERT(lock); + return SVN_NO_ERROR; +} + +/* Test svn_ra_lock(). */ +static svn_error_t * +lock_test(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_ra_session_t *session; + apr_hash_t *lock_targets = apr_hash_make(pool); + apr_hash_t *unlock_targets = apr_hash_make(pool); + svn_revnum_t rev = 1; + struct lock_result_t *result; + struct lock_baton_t baton; + apr_hash_index_t *hi; + + SVN_ERR(make_and_open_repos(&session, "test-repo-lock", opts, pool)); + SVN_ERR(commit_tree(session, pool)); + + baton.results = apr_hash_make(pool); + baton.pool = pool; + + svn_hash_sets(lock_targets, "A/B/f", &rev); + svn_hash_sets(lock_targets, "A/B/g", &rev); + svn_hash_sets(lock_targets, "A/B/z", &rev); + svn_hash_sets(lock_targets, "A/BB/f", &rev); + svn_hash_sets(lock_targets, "X/z", &rev); + + /* Lock some paths. */ + SVN_ERR(svn_ra_lock(session, lock_targets, "foo", FALSE, lock_cb, &baton, + pool)); + + SVN_ERR(expect_lock("A/B/f", baton.results, session, pool)); + SVN_ERR(expect_lock("A/B/g", baton.results, session, pool)); + SVN_ERR(expect_error("A/B/z", baton.results, session, pool)); + SVN_ERR(expect_lock("A/BB/f", baton.results, session, pool)); + SVN_ERR(expect_error("X/z", baton.results, session, pool)); + + /* Unlock without force and wrong lock tokens */ + for (hi = apr_hash_first(pool, lock_targets); hi; hi = apr_hash_next(hi)) + svn_hash_sets(unlock_targets, apr_hash_this_key(hi), "wrong-token"); + apr_hash_clear(baton.results); + SVN_ERR(svn_ra_unlock(session, unlock_targets, FALSE, lock_cb, &baton, pool)); + + SVN_ERR(expect_unlock_error("A/B/f", baton.results, session, pool)); + SVN_ERR(expect_unlock_error("A/B/g", baton.results, session, pool)); + SVN_ERR(expect_error("A/B/z", baton.results, session, pool)); + SVN_ERR(expect_unlock_error("A/BB/f", baton.results, session, pool)); + SVN_ERR(expect_error("X/z", baton.results, session, pool)); + + /* Force unlock */ + for (hi = apr_hash_first(pool, lock_targets); hi; hi = apr_hash_next(hi)) + svn_hash_sets(unlock_targets, apr_hash_this_key(hi), ""); + apr_hash_clear(baton.results); + SVN_ERR(svn_ra_unlock(session, unlock_targets, TRUE, lock_cb, &baton, pool)); + + SVN_ERR(expect_unlock("A/B/f", baton.results, session, pool)); + SVN_ERR(expect_unlock("A/B/g", baton.results, session, pool)); + SVN_ERR(expect_error("A/B/z", baton.results, session, pool)); + SVN_ERR(expect_unlock("A/BB/f", baton.results, session, pool)); + SVN_ERR(expect_error("X/z", baton.results, session, pool)); + + /* Lock again. */ + apr_hash_clear(baton.results); + SVN_ERR(svn_ra_lock(session, lock_targets, "foo", FALSE, lock_cb, &baton, + pool)); + + SVN_ERR(expect_lock("A/B/f", baton.results, session, pool)); + SVN_ERR(expect_lock("A/B/g", baton.results, session, pool)); + SVN_ERR(expect_error("A/B/z", baton.results, session, pool)); + SVN_ERR(expect_lock("A/BB/f", baton.results, session, pool)); + SVN_ERR(expect_error("X/z", baton.results, session, pool)); + + for (hi = apr_hash_first(pool, baton.results); hi; hi = apr_hash_next(hi)) + { + result = apr_hash_this_val(hi); + svn_hash_sets(unlock_targets, apr_hash_this_key(hi), + result->lock ? result->lock->token : "non-existent-token"); + } + apr_hash_clear(baton.results); + SVN_ERR(svn_ra_unlock(session, unlock_targets, FALSE, lock_cb, &baton, pool)); + + SVN_ERR(expect_unlock("A/B/f", baton.results, session, pool)); + SVN_ERR(expect_unlock("A/B/g", baton.results, session, pool)); + SVN_ERR(expect_error("A/B/z", baton.results, session, pool)); + SVN_ERR(expect_unlock("A/BB/f", baton.results, session, pool)); + SVN_ERR(expect_error("X/z", baton.results, session, pool)); + + return SVN_NO_ERROR; +} + +/* Test svn_ra_get_dir2(). */ +static svn_error_t * +get_dir_test(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_ra_session_t *session; + apr_hash_t *dirents; + + SVN_ERR(make_and_open_repos(&session, "test-get-dir", opts, pool)); + SVN_ERR(commit_tree(session, pool)); + + /* This call used to block on ra-svn for 1.8.0...r1656713 */ + SVN_TEST_ASSERT_ERROR(svn_ra_get_dir2(session, &dirents, NULL, NULL, + "non/existing/relpath", 1, + SVN_DIRENT_KIND, pool), + SVN_ERR_FS_NOT_FOUND); + + return SVN_NO_ERROR; +} + /* Implements svn_commit_callback2_t for commit_callback_failure() */ static svn_error_t * commit_callback_with_failure(const svn_commit_info_t *info, @@ -162,11 +621,11 @@ commit_callback_with_failure(const svn_commit_info_t *info, apr_time_t timetemp; SVN_TEST_ASSERT(info != NULL); - SVN_TEST_STRING_ASSERT(info->author, ""); /* No auth baton supplied. */ + SVN_TEST_STRING_ASSERT(info->author, "jrandom"); SVN_TEST_STRING_ASSERT(info->post_commit_err, NULL); SVN_ERR(svn_time_from_cstring(&timetemp, info->date, scratch_pool)); - SVN_TEST_ASSERT(info->date != 0); + SVN_TEST_ASSERT(timetemp != 0); SVN_TEST_ASSERT(info->repos_root != NULL); SVN_TEST_ASSERT(info->revision == 1); @@ -181,13 +640,13 @@ commit_callback_failure(const svn_test_opts_t *opts, const svn_delta_editor_t *editor; void *edit_baton; void *root_baton; - SVN_ERR(make_and_open_local_repos(&ra_session, "commit_cb_failure", opts, pool)); + SVN_ERR(make_and_open_repos(&ra_session, "commit_cb_failure", opts, pool)); SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton, apr_hash_make(pool), commit_callback_with_failure, NULL, NULL, FALSE, pool)); - SVN_ERR(editor->open_root(edit_baton, 1, pool, &root_baton)); + SVN_ERR(editor->open_root(edit_baton, 0, pool, &root_baton)); SVN_ERR(editor->change_dir_prop(root_baton, "A", svn_string_create("B", pool), pool)); SVN_ERR(editor->close_directory(root_baton, pool)); @@ -200,14 +659,215 @@ commit_callback_failure(const svn_test_opts_t *opts, return SVN_NO_ERROR; } +static svn_error_t * +base_revision_above_youngest(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_ra_session_t *ra_session; + const svn_delta_editor_t *editor; + void *edit_baton; + void *root_baton; + svn_error_t *err; + SVN_ERR(make_and_open_repos(&ra_session, "base_revision_above_youngest", + opts, pool)); + + SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton, + apr_hash_make(pool), NULL, + NULL, NULL, FALSE, pool)); + + /* r1 doesn't exist, but we say we want to apply changes against this + revision to see how the ra layers behave. + + Some will see an error directly on open_root, others in a later + state. */ + + /* ra-local and http pre-v2 will see the error here */ + err = editor->open_root(edit_baton, 1, pool, &root_baton); + + if (!err) + err = editor->change_dir_prop(root_baton, "A", + svn_string_create("B", pool), pool); + + /* http v2 will notice it here (PROPPATCH) */ + if (!err) + err = editor->close_directory(root_baton, pool); + + /* ra svn only notes it at some later point. Typically here */ + if (!err) + err = editor->close_edit(edit_baton, pool); + + SVN_TEST_ASSERT_ERROR(err, + SVN_ERR_FS_NO_SUCH_REVISION); + + SVN_ERR(editor->abort_edit(edit_baton, pool)); + return SVN_NO_ERROR; +} + + +static svn_error_t * +ra_list_has_props(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_ra_session_t *ra_session; + const svn_delta_editor_t *editor; + apr_pool_t *iterpool = svn_pool_create(pool); + int i; + void *edit_baton; + const char *trunk_url; + + SVN_ERR(make_and_open_repos(&ra_session, "ra_list_has_props", + opts, pool)); + + SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton, + apr_hash_make(pool), NULL, + NULL, NULL, FALSE, iterpool)); + + /* Create initial layout*/ + { + void *root_baton; + void *dir_baton; + + SVN_ERR(editor->open_root(edit_baton, 0, pool, &root_baton)); + SVN_ERR(editor->add_directory("trunk", root_baton, NULL, SVN_INVALID_REVNUM, + iterpool, &dir_baton)); + SVN_ERR(editor->close_directory(dir_baton, iterpool)); + SVN_ERR(editor->add_directory("tags", root_baton, NULL, SVN_INVALID_REVNUM, + iterpool, &dir_baton)); + SVN_ERR(editor->close_directory(dir_baton, iterpool)); + SVN_ERR(editor->close_directory(root_baton, iterpool)); + SVN_ERR(editor->close_edit(edit_baton, iterpool)); + } + + SVN_ERR(svn_ra_get_repos_root2(ra_session, &trunk_url, pool)); + trunk_url = svn_path_url_add_component2(trunk_url, "trunk", pool); + + /* Create a few tags. Using a value like 8000 will take too long for a normal + testrun, but produces more realistic problems */ + for (i = 0; i < 50; i++) + { + void *root_baton; + void *tags_baton; + void *dir_baton; + + svn_pool_clear(iterpool); + + SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton, + apr_hash_make(pool), NULL, + NULL, NULL, FALSE, iterpool)); + + SVN_ERR(editor->open_root(edit_baton, i+1, pool, &root_baton)); + SVN_ERR(editor->open_directory("tags", root_baton, i+1, iterpool, + &tags_baton)); + SVN_ERR(editor->add_directory(apr_psprintf(iterpool, "tags/T%05d", i+1), + tags_baton, trunk_url, 1, iterpool, + &dir_baton)); + + SVN_ERR(editor->close_directory(dir_baton, iterpool)); + SVN_ERR(editor->close_directory(tags_baton, iterpool)); + SVN_ERR(editor->close_directory(root_baton, iterpool)); + SVN_ERR(editor->close_edit(edit_baton, iterpool)); + } + + { + apr_hash_t *dirents; + svn_revnum_t fetched_rev; + apr_hash_t *props; + + SVN_ERR(svn_ra_get_dir2(ra_session, &dirents, &fetched_rev, &props, + "tags", SVN_INVALID_REVNUM, + SVN_DIRENT_ALL, pool)); + } + + return SVN_NO_ERROR; +} + +/* Test ra_svn tunnel editor handling, including polling. */ + +static svn_error_t * +tunnel_run_checkout(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + tunnel_baton_t *b = apr_pcalloc(pool, sizeof(*b)); + apr_pool_t *scratch_pool = svn_pool_create(pool); + const char *url; + svn_ra_callbacks2_t *cbtable; + svn_ra_session_t *session; + const char tunnel_repos_name[] = "test-run_checkout"; + const svn_ra_reporter3_t *reporter; + void *report_baton; + + b->magic = TUNNEL_MAGIC; + + SVN_ERR(svn_test__create_repos(NULL, tunnel_repos_name, opts, scratch_pool)); + + /* Immediately close the repository to avoid race condition with svnserve + (and then the cleanup code) with BDB when our pool is cleared. */ + svn_pool_clear(scratch_pool); + + url = apr_pstrcat(pool, "svn+test://localhost/", tunnel_repos_name, + SVN_VA_NULL); + SVN_ERR(svn_ra_create_callbacks(&cbtable, pool)); + cbtable->check_tunnel_func = check_tunnel; + cbtable->open_tunnel_func = open_tunnel; + cbtable->tunnel_baton = b; + SVN_ERR(svn_cmdline_create_auth_baton2(&cbtable->auth_baton, + TRUE /* non_interactive */, + "jrandom", "rayjandom", + NULL, + TRUE /* no_auth_cache */, + FALSE /* trust_server_cert */, + FALSE, FALSE, FALSE, FALSE, + NULL, NULL, NULL, pool)); + + b->last_check = FALSE; + + SVN_ERR(svn_ra_open4(&session, NULL, url, NULL, cbtable, NULL, NULL, + scratch_pool)); + + SVN_ERR(commit_changes(session, pool)); + + SVN_ERR(svn_ra_do_update3(session, + &reporter, &report_baton, + 1, "", + svn_depth_infinity, FALSE, FALSE, + svn_delta_default_editor(pool), NULL, + pool, pool)); + + SVN_ERR(reporter->set_path(report_baton, "", 0, svn_depth_infinity, FALSE, + NULL, pool)); + + SVN_ERR(reporter->finish_report(report_baton, pool)); + + return SVN_NO_ERROR; +} + /* The test table. */ -struct svn_test_descriptor_t test_funcs[] = + +static int max_threads = 2; + +static struct svn_test_descriptor_t test_funcs[] = { SVN_TEST_NULL, SVN_TEST_OPTS_PASS(location_segments_test, "test svn_ra_get_location_segments"), + SVN_TEST_OPTS_PASS(check_tunnel_callback_test, + "test ra_svn tunnel callback check"), + SVN_TEST_OPTS_PASS(tunnel_callback_test, + "test ra_svn tunnel creation callbacks"), + SVN_TEST_OPTS_PASS(lock_test, + "lock multiple paths"), + SVN_TEST_OPTS_PASS(get_dir_test, + "test ra_get_dir2"), SVN_TEST_OPTS_PASS(commit_callback_failure, "commit callback failure"), + SVN_TEST_OPTS_PASS(base_revision_above_youngest, + "base revision newer than youngest"), + SVN_TEST_OPTS_PASS(ra_list_has_props, + "check list has_props performance"), + SVN_TEST_OPTS_PASS(tunnel_run_checkout, + "verify checkout over a tunnel"), SVN_TEST_NULL }; + +SVN_TEST_MAIN |