Modified: subversion/branches/addremove/subversion/tests/libsvn_client/mtcc-test.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_client/mtcc-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_client/mtcc-test.c (original) +++ subversion/branches/addremove/subversion/tests/libsvn_client/mtcc-test.c Sat May 23 14:16:56 2020 @@ -693,7 +693,7 @@ test_file_revs_both_ways(const svn_test_ subpool)); SVN_TEST_ASSERT(hrb.last == 6); - /* Ressurect mu */ + /* Resurrect mu */ svn_pool_clear(subpool); SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 7, ctx, subpool, subpool)); SVN_ERR(svn_client__mtcc_add_copy("mu", 6, "mu", mtcc, subpool));
Propchange: subversion/branches/addremove/subversion/tests/libsvn_delta/ ------------------------------------------------------------------------------ --- svn:ignore (original) +++ svn:ignore Sat May 23 14:16:56 2020 @@ -8,6 +8,7 @@ vdelta-test random-test xml-output-test svndiff-test +svndiff-stream-test window-test editor-test combined Modified: subversion/branches/addremove/subversion/tests/libsvn_fs/fs-test.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_fs/fs-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_fs/fs-test.c (original) +++ subversion/branches/addremove/subversion/tests/libsvn_fs/fs-test.c Sat May 23 14:16:56 2020 @@ -5479,7 +5479,7 @@ commit_timestamp(const svn_test_opts_t * APR_HASH_KEY_STRING); SVN_TEST_ASSERT(!svn_date); - /* Commit that overwites a missing svn:date. */ + /* Commit that overwrites a missing svn:date. */ SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, pool)); SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); SVN_ERR(svn_fs_make_dir(txn_root, "/zig", pool)); @@ -7219,7 +7219,7 @@ test_cache_clear_during_stream(const svn * Just to be sure, make it not too uniform to keep self-txdelta at bay. */ SVN_ERR(svn_fs_apply_textdelta(&consumer_func, &consumer_baton, txn_root, "/foo", NULL, NULL, subpool)); - stream = svn_txdelta_target_push(consumer_func, consumer_baton, + stream = svn_txdelta_target_push(consumer_func, consumer_baton, svn_stream_empty(subpool), subpool); for (i = 0; i < 10000; ++ i) { @@ -7369,6 +7369,106 @@ closest_copy_test_svn_4677(const svn_tes return SVN_NO_ERROR; } +static svn_error_t * +test_closest_copy_file_replaced_with_dir(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_fs_t *fs; + svn_fs_txn_t *txn; + svn_fs_root_t *txn_root; + svn_fs_root_t *rev_root; + svn_revnum_t youngest_rev; + svn_fs_root_t *copy_root; + const char *copy_path; + + /* Prepare a filesystem. */ + SVN_ERR(svn_test__create_fs(&fs, "test-closest-copy-file-replaced-with-dir", + opts, pool)); + + youngest_rev = 0; + + /* Modeled after the case described in the thread: + "[PATCH] A test for "Can't get entries" error" + https://lists.apache.org/thread.html/693a95b0da834387e78a7f08df2392b634397d32f37428c81c02f8c5@%3Cdev.subversion.apache.org%3E + */ + /* r1: Add a directory with a file. */ + SVN_ERR(svn_fs_begin_txn2(&txn, fs, youngest_rev, 0, pool)); + SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); + SVN_ERR(svn_fs_make_dir(txn_root, "/A", pool)); + SVN_ERR(svn_fs_make_file(txn_root, "/A/mu", pool)); + SVN_ERR(test_commit_txn(&youngest_rev, txn, NULL, pool)); + SVN_TEST_INT_ASSERT(youngest_rev, 1); + + /* r2: Copy the directory. */ + SVN_ERR(svn_fs_begin_txn2(&txn, fs, youngest_rev, 0, pool)); + SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); + SVN_ERR(svn_fs_revision_root(&rev_root, fs, 1, pool)); + SVN_ERR(svn_fs_copy(rev_root, "/A", txn_root, "/B", pool)); + SVN_ERR(test_commit_txn(&youngest_rev, txn, NULL, pool)); + SVN_TEST_INT_ASSERT(youngest_rev, 2); + + /* r3: Delete the file. */ + SVN_ERR(svn_fs_begin_txn2(&txn, fs, youngest_rev, 0, pool)); + SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); + SVN_ERR(svn_fs_delete(txn_root, "/B/mu", pool)); + SVN_ERR(test_commit_txn(&youngest_rev, txn, NULL, pool)); + SVN_TEST_INT_ASSERT(youngest_rev, 3); + + /* r4: Replace the file with a new directory containing a file. */ + SVN_ERR(svn_fs_begin_txn2(&txn, fs, youngest_rev, 0, pool)); + SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); + SVN_ERR(svn_fs_make_dir(txn_root, "/B/mu", pool)); + SVN_ERR(svn_fs_make_file(txn_root, "/B/mu/iota", pool)); + SVN_ERR(test_commit_txn(&youngest_rev, txn, NULL, pool)); + SVN_TEST_INT_ASSERT(youngest_rev, 4); + + /* Test a couple of svn_fs_closest_copy() calls; the second call used + to fail with an unexpected SVN_ERR_FS_NOT_DIRECTORY error. */ + + SVN_ERR(svn_fs_revision_root(&rev_root, fs, 2, pool)); + SVN_ERR(svn_fs_closest_copy(©_root, ©_path, rev_root, "/B/mu", pool)); + + SVN_TEST_ASSERT(copy_root != NULL); + SVN_TEST_INT_ASSERT(svn_fs_revision_root_revision(copy_root), 2); + SVN_TEST_STRING_ASSERT(copy_path, "/B"); + + SVN_ERR(svn_fs_revision_root(&rev_root, fs, 4, pool)); + SVN_ERR(svn_fs_closest_copy(©_root, ©_path, rev_root, "/B/mu/iota", pool)); + + SVN_TEST_ASSERT(copy_root == NULL); + SVN_TEST_ASSERT(copy_path == NULL); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_unrecognized_ioctl(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_fs_t *fs; + svn_error_t *err; + svn_fs_ioctl_code_t code = {0}; + + SVN_ERR(svn_test__create_fs(&fs, "test-unrecognized-ioctl", opts, pool)); + + code.fs_type = "NON-EXISTING"; + code.code = 98765; + err = svn_fs_ioctl(fs, code, NULL, NULL, NULL, NULL, pool, pool); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE); + + code.fs_type = "NON-EXISTING"; + code.code = 98765; + err = svn_fs_ioctl(NULL, code, NULL, NULL, NULL, NULL, pool, pool); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_UNKNOWN_FS_TYPE); + + code.fs_type = opts->fs_type; + code.code = 98765; + err = svn_fs_ioctl(NULL, code, NULL, NULL, NULL, NULL, pool, pool); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE); + + return SVN_NO_ERROR; +} + /* ------------------------------------------------------------------------ */ /* The test table. */ @@ -7513,6 +7613,10 @@ static struct svn_test_descriptor_t test "test rep-sharing on content rather than SHA1"), SVN_TEST_OPTS_PASS(closest_copy_test_svn_4677, "test issue SVN-4677 regression"), + SVN_TEST_OPTS_PASS(test_closest_copy_file_replaced_with_dir, + "svn_fs_closest_copy after replacing file with dir"), + SVN_TEST_OPTS_PASS(test_unrecognized_ioctl, + "test svn_fs_ioctl with unrecognized code"), SVN_TEST_NULL }; Modified: subversion/branches/addremove/subversion/tests/libsvn_fs/locks-test.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_fs/locks-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_fs/locks-test.c (original) +++ subversion/branches/addremove/subversion/tests/libsvn_fs/locks-test.c Sat May 23 14:16:56 2020 @@ -1089,6 +1089,9 @@ lock_cb_error(const svn_test_opts_t *opt return SVN_NO_ERROR; } +/* XXX NOTE: + This test will fail on most Unix-like systems when run as the + root user, because flock() will ignore file permissions. */ static svn_error_t * obtain_write_lock_failure(const svn_test_opts_t *opts, apr_pool_t *pool) Modified: subversion/branches/addremove/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c (original) +++ subversion/branches/addremove/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c Sat May 23 14:16:56 2020 @@ -59,7 +59,8 @@ static const char * get_rev_contents(svn_revnum_t rev, apr_pool_t *pool) { /* Toss in a bunch of magic numbers for spice. */ - apr_int64_t num = ((rev * 1234353 + 4358) * 4583 + ((rev % 4) << 1)) / 42; + apr_int64_t rev64 = rev; + apr_int64_t num = ((rev64 * 1234353 + 4358) * 4583 + ((rev64 % 4) << 1)) / 42; return apr_psprintf(pool, "%" APR_INT64_T_FMT "\n", num); } @@ -1631,8 +1632,8 @@ delta_chain_with_plain(const svn_test_op svn_hash_sets(props, "p", svn_string_create(prop_value->data, pool)); hash_rep = svn_stringbuf_create_empty(pool); - svn_hash_write2(props, svn_stream_from_stringbuf(hash_rep, pool), "END", - pool); + SVN_ERR(svn_hash_write2(props, svn_stream_from_stringbuf(hash_rep, pool), + "END", pool)); SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, pool)); SVN_ERR(svn_fs_txn_root(&root, txn, pool)); @@ -1693,7 +1694,7 @@ compare_0_length_rep(const svn_test_opts enum { COUNT = 5 }; const char *file_names[COUNT] = { no_rep_file, - empty_plain_file, + empty_plain_file, plain_file, empty_delta_file, delta_file }; Modified: subversion/branches/addremove/subversion/tests/libsvn_fs_fs/fs-fs-private-test.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_fs_fs/fs-fs-private-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_fs_fs/fs-fs-private-test.c (original) +++ subversion/branches/addremove/subversion/tests/libsvn_fs_fs/fs-fs-private-test.c Sat May 23 14:16:56 2020 @@ -35,6 +35,8 @@ #include "private/svn_subr_private.h" #include "../../libsvn_fs_fs/index.h" +#include "../../libsvn_fs_fs/rep-cache.h" +#include "../../libsvn_fs/fs-loader.h" #include "../svn_test_fs.h" @@ -199,8 +201,10 @@ get_repo_stats(const svn_test_opts_t *op svn_repos_t *repos; svn_revnum_t rev; apr_size_t i; - svn_fs_fs__stats_t *stats; svn_fs_fs__extension_info_t *extension_info; + svn_fs_fs__ioctl_get_stats_input_t input = {0}; + svn_fs_fs__ioctl_get_stats_output_t *output; + const svn_fs_fs__stats_t *stats; /* Bail (with success) on known-untestable scenarios */ if (strcmp(opts->fs_type, "fsfs") != 0) @@ -211,8 +215,9 @@ get_repo_stats(const svn_test_opts_t *op SVN_ERR(create_greek_repo(&repos, &rev, opts, REPO_NAME, pool, pool)); /* Gather statistics info on that repo. */ - SVN_ERR(svn_fs_fs__get_stats(&stats, svn_repos_fs(repos), NULL, NULL, - NULL, NULL, pool, pool)); + SVN_ERR(svn_fs_ioctl(svn_repos_fs(repos), SVN_FS_FS__IOCTL_GET_STATS, + &input, (void**)&output, NULL, NULL, pool, pool)); + stats = output->stats; /* Check that the stats make sense. */ SVN_TEST_ASSERT(stats->total_size > 1000 && stats->total_size < 10000); @@ -324,6 +329,7 @@ dump_index(const svn_test_opts_t *opts, svn_repos_t *repos; svn_revnum_t rev; dump_baton_t baton; + svn_fs_fs__ioctl_dump_index_input_t input = {0}; /* Bail (with success) on known-untestable scenarios */ if (strcmp(opts->fs_type, "fsfs") != 0) @@ -342,8 +348,12 @@ dump_index(const svn_test_opts_t *opts, baton.offset = 0; baton.revision = rev; baton.numbers_seen = svn_bit_array__create(100, pool); - SVN_ERR(svn_fs_fs__dump_index(svn_repos_fs(repos), rev, dump_index_entry, - &baton, NULL, NULL, pool)); + + input.revision = rev; + input.callback_func = dump_index_entry; + input.callback_baton = &baton; + SVN_ERR(svn_fs_ioctl(svn_repos_fs(repos), SVN_FS_FS__IOCTL_DUMP_INDEX, + &input, NULL, NULL, NULL, pool, pool)); /* Check that we've got all data (20 noderevs + 20 reps + 1 changes list). */ SVN_TEST_ASSERT(baton.invocations == 41); @@ -377,6 +387,8 @@ load_index(const svn_test_opts_t *opts, apr_array_header_t *entries = apr_array_make(pool, 41, sizeof(void *)); apr_array_header_t *alt_entries = apr_array_make(pool, 1, sizeof(void *)); svn_fs_fs__p2l_entry_t entry; + svn_fs_fs__ioctl_dump_index_input_t dump_input = {0}; + svn_fs_fs__ioctl_load_index_input_t load_input = {0}; /* Bail (with success) on known-untestable scenarios */ if (strcmp(opts->fs_type, "fsfs") != 0) @@ -391,8 +403,11 @@ load_index(const svn_test_opts_t *opts, SVN_ERR(create_greek_repo(&repos, &rev, opts, REPO_NAME, pool, pool)); /* Read the original index contents for REV in ENTRIES. */ - SVN_ERR(svn_fs_fs__dump_index(svn_repos_fs(repos), rev, receive_index, - entries, NULL, NULL, pool)); + dump_input.revision = rev; + dump_input.callback_func = receive_index; + dump_input.callback_baton = entries; + SVN_ERR(svn_fs_ioctl(svn_repos_fs(repos), SVN_FS_FS__IOCTL_DUMP_INDEX, + &dump_input, NULL, NULL, NULL, pool, pool)); /* Replace it with an index that declares the whole revision contents as * "unused". */ @@ -404,14 +419,21 @@ load_index(const svn_test_opts_t *opts, entry.item.revision = SVN_INVALID_REVNUM; APR_ARRAY_PUSH(alt_entries, svn_fs_fs__p2l_entry_t *) = &entry; - SVN_ERR(svn_fs_fs__load_index(svn_repos_fs(repos), rev, alt_entries, pool)); + load_input.revision = rev; + load_input.entries = alt_entries; + SVN_ERR(svn_fs_ioctl(svn_repos_fs(repos), SVN_FS_FS__IOCTL_LOAD_INDEX, + &load_input, NULL, NULL, NULL, pool, pool)); + SVN_TEST_ASSERT_ERROR(svn_repos_verify_fs3(repos, rev, rev, FALSE, FALSE, NULL, NULL, NULL, NULL, NULL, NULL, pool), SVN_ERR_FS_INDEX_CORRUPTION); /* Restore the original index. */ - SVN_ERR(svn_fs_fs__load_index(svn_repos_fs(repos), rev, entries, pool)); + load_input.revision = rev; + load_input.entries = entries; + SVN_ERR(svn_fs_ioctl(svn_repos_fs(repos), SVN_FS_FS__IOCTL_LOAD_INDEX, + &load_input, NULL, NULL, NULL, pool, pool)); SVN_ERR(svn_repos_verify_fs3(repos, rev, rev, FALSE, FALSE, NULL, NULL, NULL, NULL, NULL, NULL, pool)); @@ -420,6 +442,63 @@ load_index(const svn_test_opts_t *opts, #undef REPO_NAME +/* ------------------------------------------------------------------------ */ + +static svn_error_t * +build_rep_cache(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_fs_t *fs; + fs_fs_data_t *ffd; + svn_fs_txn_t *txn; + svn_fs_root_t *txn_root; + svn_revnum_t rev; + svn_boolean_t exists; + const char *fs_path; + svn_fs_fs__ioctl_build_rep_cache_input_t input = {0}; + + /* Bail (with success) on known-untestable scenarios */ + if (strcmp(opts->fs_type, "fsfs") != 0) + return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL, + "this will test FSFS repositories only"); + + if (opts->server_minor_version && (opts->server_minor_version < 6)) + return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL, + "pre-1.6 SVN doesn't support FSFS rep-sharing"); + + /* Create a filesystem and explicitly disable rep-sharing. */ + fs_path = "test-repo-build-rep-cache-test"; + SVN_ERR(svn_test__create_fs2(&fs, fs_path, opts, NULL, pool)); + ffd = fs->fsap_data; + ffd->rep_sharing_allowed = FALSE; + + /* Add the Greek tree. */ + SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool)); + SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); + SVN_ERR(svn_test__create_greek_tree(txn_root, pool)); + SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, pool)); + SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(rev)); + + /* Make sure the rep-cache does not exist. */ + SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, fs, pool)); + SVN_TEST_ASSERT(!exists); + + /* Build and verify the rep-cache. */ + ffd->rep_sharing_allowed = TRUE; + + input.start_rev = rev; + input.end_rev = rev; + SVN_ERR(svn_fs_ioctl(fs, SVN_FS_FS__IOCTL_BUILD_REP_CACHE, + &input, NULL, NULL, NULL, pool, pool)); + + SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, fs, pool)); + SVN_TEST_ASSERT(exists); + + SVN_ERR(svn_fs_verify(fs_path, NULL, 0, SVN_INVALID_REVNUM, + NULL, NULL, NULL, NULL, pool)); + + return SVN_NO_ERROR; +} + /* The test table. */ @@ -435,6 +514,8 @@ static struct svn_test_descriptor_t test "dump the P2L index"), SVN_TEST_OPTS_PASS(load_index, "load the P2L index"), + SVN_TEST_OPTS_PASS(build_rep_cache, + "build the representation cache"), SVN_TEST_NULL }; Modified: subversion/branches/addremove/subversion/tests/libsvn_fs_x/fs-x-pack-test.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_fs_x/fs-x-pack-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_fs_x/fs-x-pack-test.c (original) +++ subversion/branches/addremove/subversion/tests/libsvn_fs_x/fs-x-pack-test.c Sat May 23 14:16:56 2020 @@ -680,7 +680,7 @@ recover_fully_packed(const svn_test_opts /* ------------------------------------------------------------------------ */ /* Regression test for issue #4320 (fsfs file-hinting fails when reading a rep - from the transaction that is commiting rev = SHARD_SIZE). */ + from the transaction that is committing rev = SHARD_SIZE). */ #define REPO_NAME "test-repo-file-hint-at-shard-boundary" #define SHARD_SIZE 4 #define MAX_REV (SHARD_SIZE - 1) Propchange: subversion/branches/addremove/subversion/tests/libsvn_ra/ ------------------------------------------------------------------------------ --- svn:ignore (original) +++ svn:ignore Sat May 23 14:16:56 2020 @@ -1,4 +1,13 @@ *.lo .libs +base_revision_above_youngest +commit_cb_failure +commit_empty_last_change +delete_revision_above_youngest +errors_from_callbacks ra-test +ra_list_has_props +ra_revision_errors +test-get-dir test-repo-* +test-run_checkout Modified: subversion/branches/addremove/subversion/tests/libsvn_ra/ra-test.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_ra/ra-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_ra/ra-test.c (original) +++ subversion/branches/addremove/subversion/tests/libsvn_ra/ra-test.c Sat May 23 14:16:56 2020 @@ -62,7 +62,8 @@ make_and_open_repos(svn_ra_session_t **s pool, pool)); SVN_ERR(svn_ra_initialize(pool)); - SVN_ERR(svn_ra_open4(session, NULL, url, NULL, cbtable, NULL, NULL, pool)); + SVN_ERR(svn_ra_open5(session, NULL, NULL, url, NULL, cbtable, NULL, NULL, + pool)); return SVN_NO_ERROR; } @@ -94,6 +95,41 @@ commit_changes(svn_ra_session_t *session return SVN_NO_ERROR; } +/* Commit two revisions: add 'B', then delete 'A' */ +static svn_error_t * +commit_two_changes(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; + void *root_baton, *dir_baton; + + /* mkdir B */ + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + revprop_table, + NULL, NULL, NULL, TRUE, pool)); + SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM, + pool, &root_baton)); + SVN_ERR(editor->add_directory("B", root_baton, NULL, SVN_INVALID_REVNUM, + pool, &dir_baton)); + SVN_ERR(editor->close_directory(dir_baton, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + + /* delete A */ + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + revprop_table, + NULL, NULL, NULL, TRUE, pool)); + SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM, + pool, &root_baton)); + SVN_ERR(editor->delete_entry("A", SVN_INVALID_REVNUM, root_baton, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + + return SVN_NO_ERROR; +} + static svn_error_t * commit_tree(svn_ra_session_t *session, apr_pool_t *pool) @@ -352,7 +388,7 @@ check_tunnel_callback_test(const svn_tes NULL, NULL, NULL, pool)); b->last_check = TRUE; - SVN_TEST_ASSERT_ERROR(svn_ra_open4(&session, NULL, + SVN_TEST_ASSERT_ERROR(svn_ra_open5(&session, NULL, NULL, "svn+foo://localhost/no-repo", NULL, cbtable, NULL, NULL, pool), SVN_ERR_RA_CANNOT_CREATE_SESSION); @@ -395,7 +431,7 @@ tunnel_callback_test(const svn_test_opts NULL, NULL, NULL, pool)); b->last_check = FALSE; - SVN_ERR(svn_ra_open4(&session, NULL, url, NULL, cbtable, NULL, NULL, + SVN_ERR(svn_ra_open5(&session, NULL, NULL, url, NULL, cbtable, NULL, NULL, scratch_pool)); SVN_TEST_ASSERT(b->last_check); SVN_TEST_ASSERT(b->open_count > 0); @@ -427,17 +463,7 @@ lock_cb(void *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->lock = svn_lock_dup(lock, b->pool); result->err = ra_err; svn_hash_sets(b->results, apr_pstrdup(b->pool, path), result); @@ -1566,7 +1592,7 @@ tunnel_run_checkout(const svn_test_opts_ b->last_check = FALSE; - SVN_ERR(svn_ra_open4(&session, NULL, url, NULL, cbtable, NULL, NULL, + SVN_ERR(svn_ra_open5(&session, NULL, NULL, url, NULL, cbtable, NULL, NULL, scratch_pool)); SVN_ERR(commit_changes(session, pool)); @@ -1636,16 +1662,16 @@ commit_empty_last_change(const svn_test_ SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, revprop_table, NULL, NULL, NULL, TRUE, tmp_pool)); - + SVN_ERR(editor->open_root(edit_baton, 1, tmp_pool, &root_baton)); SVN_ERR(editor->close_directory(root_baton, tmp_pool)); SVN_ERR(editor->close_edit(edit_baton, tmp_pool)); - + SVN_ERR(svn_ra_stat(session, "", 2+i, &dirent, tmp_pool)); - + SVN_TEST_ASSERT(dirent != NULL); SVN_TEST_STRING_ASSERT(dirent->last_author, "jrandom"); - + /* BDB used to only updates last_changed on the repos_root when there was an actual change. Now all filesystems behave in the same way */ SVN_TEST_INT_ASSERT(dirent->created_rev, 2+i); @@ -1682,6 +1708,175 @@ commit_empty_last_change(const svn_test_ return SVN_NO_ERROR; } +static svn_error_t * +commit_locked_file(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + const char *url; + svn_ra_callbacks2_t *cbtable; + svn_ra_session_t *session; + const svn_delta_editor_t *editor; + void *edit_baton; + void *root_baton; + void *file_baton; + struct lock_result_t *lock_result; + apr_hash_t *lock_tokens; + svn_txdelta_window_handler_t handler; + void *handler_baton; + svn_revnum_t fetched_rev; + apr_hash_t *fetched_props; + const svn_string_t *propval; + + SVN_ERR(svn_test__create_repos2(NULL, &url, NULL, + "test-repo-commit-locked-file-test", + opts, pool, pool)); + + SVN_ERR(svn_ra_initialize(pool)); + SVN_ERR(svn_ra_create_callbacks(&cbtable, pool)); + SVN_ERR(svn_test__init_auth_baton(&cbtable->auth_baton, pool)); + + SVN_ERR(svn_ra_open5(&session, NULL, NULL, url, NULL, cbtable, + NULL, NULL, pool)); + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + apr_hash_make(pool), + NULL, NULL, NULL, TRUE, pool)); + /* Add a file. */ + SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM, + pool, &root_baton)); + SVN_ERR(editor->add_file("file", root_baton, NULL, SVN_INVALID_REVNUM, + pool, &file_baton)); + SVN_ERR(editor->close_file(file_baton, NULL, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + + /* Acquire a lock on this file. */ + { + struct lock_baton_t baton = {0}; + svn_revnum_t rev = 1; + apr_hash_t *lock_targets; + + baton.results = apr_hash_make(pool); + baton.pool = pool; + + lock_targets = apr_hash_make(pool); + svn_hash_sets(lock_targets, "file", &rev); + SVN_ERR(svn_ra_lock(session, lock_targets, "comment", FALSE, + lock_cb, &baton, pool)); + + SVN_ERR(expect_lock("file", baton.results, session, pool)); + lock_result = svn_hash_gets(baton.results, "file"); + } + + /* Open a new session using the file parent's URL. */ + SVN_ERR(svn_ra_open5(&session, NULL, NULL, url, NULL, cbtable, + NULL, NULL, pool)); + + /* Create a new commit editor supplying our lock token. */ + lock_tokens = apr_hash_make(pool); + svn_hash_sets(lock_tokens, "file", lock_result->lock->token); + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + apr_hash_make(pool), NULL, NULL, + lock_tokens, TRUE, pool)); + /* Edit the locked file. */ + SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM, + pool, &root_baton)); + SVN_ERR(editor->open_file("file", root_baton, SVN_INVALID_REVNUM, pool, + &file_baton)); + SVN_ERR(editor->apply_textdelta(file_baton, NULL, pool, &handler, + &handler_baton)); + SVN_ERR(svn_txdelta_send_string(svn_string_create("A", pool), + handler, handler_baton, pool)); + SVN_ERR(editor->close_file(file_baton, NULL, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + + /* Check the result. */ + SVN_ERR(svn_ra_get_file(session, "file", SVN_INVALID_REVNUM, NULL, + &fetched_rev, NULL, pool)); + SVN_TEST_INT_ASSERT((int) fetched_rev, 2); + + /* Change property of the locked file. */ + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + apr_hash_make(pool), NULL, NULL, + lock_tokens, TRUE, pool)); + SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM, + pool, &root_baton)); + SVN_ERR(editor->open_file("file", root_baton, SVN_INVALID_REVNUM, pool, + &file_baton)); + SVN_ERR(editor->change_file_prop(file_baton, "propname", + svn_string_create("propval", pool), + pool)); + SVN_ERR(editor->close_file(file_baton, NULL, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + + /* Check the result. */ + SVN_ERR(svn_ra_get_file(session, "file", SVN_INVALID_REVNUM, NULL, + &fetched_rev, &fetched_props, pool)); + SVN_TEST_INT_ASSERT((int) fetched_rev, 3); + propval = svn_hash_gets(fetched_props, "propname"); + SVN_TEST_ASSERT(propval); + SVN_TEST_STRING_ASSERT(propval->data, "propval"); + + return SVN_NO_ERROR; +} + +/* Cases of 'get-deleted-rev' that should return SVN_INVALID_REVNUM. */ +static svn_error_t * +test_get_deleted_rev_no_delete(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_ra_session_t *ra_session; + svn_revnum_t revision_deleted; + + SVN_ERR(make_and_open_repos(&ra_session, + "test-repo-get-deleted-rev-no-delete", opts, + pool)); + SVN_ERR(commit_changes(ra_session, pool)); + SVN_ERR(commit_two_changes(ra_session, pool)); + + /* expect 'no deletion' in the range up to r2, when it is deleted in r3 */ + /* This was failing over RA-SVN where the 'get-deleted-rev' wire command's + prototype cannot directly represent that result. A new enough client and + server collaborate on a work-around implemented using an error code. */ + SVN_ERR(svn_ra_get_deleted_rev(ra_session, "A", 1, 2, + &revision_deleted, pool)); + SVN_TEST_INT_ASSERT(revision_deleted, SVN_INVALID_REVNUM); + + /* this connection should still be open: a simple case should still work */ + SVN_ERR(svn_ra_get_deleted_rev(ra_session, "A", 1, 3, + &revision_deleted, pool)); + SVN_TEST_INT_ASSERT(revision_deleted, 3); + + return SVN_NO_ERROR; +} + +/* Cases of 'get-deleted-rev' that should return an error. */ +static svn_error_t * +test_get_deleted_rev_errors(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_ra_session_t *ra_session; + svn_revnum_t revision_deleted; + svn_error_t *err; + + SVN_ERR(make_and_open_repos(&ra_session, + "test-repo-get-deleted-rev-errors", opts, pool)); + SVN_ERR(commit_changes(ra_session, pool)); + + /* expect an error when searching up to r3, when repository head is r1 */ + err = svn_ra_get_deleted_rev(ra_session, "A", 1, 3, &revision_deleted, pool); + + /* mod_dav_svn returns a generic error code for "500 Internal Server Error"; + * the other RA layers return the specific error code for "no such revision". + * We should make these consistent, but for now that's how it is. */ + if (opts->repos_url && strncmp(opts->repos_url, "http", 4) == 0) + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_RA_DAV_REQUEST_FAILED); + else + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_NO_SUCH_REVISION); + + return SVN_NO_ERROR; +} + /* The test table. */ @@ -1716,6 +1911,12 @@ static struct svn_test_descriptor_t test "verify checkout over a tunnel"), SVN_TEST_OPTS_PASS(commit_empty_last_change, "check how last change applies to empty commit"), + SVN_TEST_OPTS_PASS(commit_locked_file, + "check commit editor for a locked file"), + SVN_TEST_OPTS_PASS(test_get_deleted_rev_no_delete, + "test get-deleted-rev no delete"), + SVN_TEST_OPTS_PASS(test_get_deleted_rev_errors, + "test get-deleted-rev errors"), SVN_TEST_NULL }; Modified: subversion/branches/addremove/subversion/tests/libsvn_repos/authz-test.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_repos/authz-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_repos/authz-test.c (original) +++ subversion/branches/addremove/subversion/tests/libsvn_repos/authz-test.c Sat May 23 14:16:56 2020 @@ -212,7 +212,7 @@ test_authz_parse(const svn_test_opts_t * APR_READ, APR_OS_DEFAULT, pool)); groups = svn_stream_from_aprfile2(groups_file, FALSE, pool); - SVN_ERR(svn_authz__parse(&authz, rules, groups, pool, pool)); + SVN_ERR(svn_authz__parse(&authz, rules, groups, NULL, NULL, pool, pool)); printf("Access check for ('%s', '%s')\n", check_user, check_repo); @@ -277,9 +277,9 @@ test_authz_parse(const svn_test_opts_t * printf("[users]\n"); if (authz->has_anon_rights) - print_user_rights(NULL, NULL, 0, &authz->anon_rights, pool); + SVN_ERR(print_user_rights(NULL, NULL, 0, &authz->anon_rights, pool)); if (authz->has_authn_rights) - print_user_rights(NULL, NULL, 0, &authz->authn_rights, pool); + SVN_ERR(print_user_rights(NULL, NULL, 0, &authz->authn_rights, pool)); SVN_ERR(svn_iter_apr_hash(NULL, authz->user_rights, print_user_rights, NULL, pool)); printf("\n\n"); @@ -304,7 +304,7 @@ run_global_rights_tests(const char *cont svn_stringbuf_t *buffer = svn_stringbuf_create(contents, pool); svn_stream_t *stream = svn_stream_from_stringbuf(buffer, pool); - SVN_ERR(svn_repos_authz_parse(&authz, stream, NULL, pool)); + SVN_ERR(svn_repos_authz_parse2(&authz, stream, NULL, NULL, NULL, pool, pool)); for (; test_cases->repos; ++test_cases) { @@ -444,6 +444,73 @@ test_global_rights(apr_pool_t *pool) return SVN_NO_ERROR; } +static svn_error_t * +issue_4741_groups(apr_pool_t *pool) +{ + const char rules[] = + "[groups]" NL + "g1 = userA" NL + "g2 = userB" NL + "g = @g1, @g2" NL + "" NL + "[/]" NL + "* =" NL + "@g = rw" NL + ; + + svn_stringbuf_t *buf = svn_stringbuf_create(rules, pool); + svn_stream_t *stream = svn_stream_from_stringbuf(buf, pool); + svn_authz_t *authz; + svn_boolean_t access_granted; + + SVN_ERR(svn_repos_authz_parse2(&authz, stream, NULL, NULL, NULL, pool, pool)); + + SVN_ERR(svn_repos_authz_check_access(authz, "repo", "/", "userA", + svn_authz_write, &access_granted, + pool)); + SVN_TEST_ASSERT(access_granted == TRUE); + + SVN_ERR(svn_repos_authz_check_access(authz, "repo", "/", "userB", + svn_authz_write, &access_granted, + pool)); + SVN_TEST_ASSERT(access_granted == TRUE); + + return SVN_NO_ERROR; +} + +static svn_error_t * +reposful_reposless_stanzas_inherit(apr_pool_t *pool) +{ + const char rules[] = + "[groups]" NL + "company = user1, user2, user3" NL + "customer = customer1, customer2" NL + "" NL + "# company can read-write on everything" NL + "[/]" NL + "@company = rw" NL + "" NL + "[project1:/]" NL + "@customer = r" NL + "" NL + "[project2:/]" NL; + + svn_stringbuf_t *buf = svn_stringbuf_create(rules, pool); + svn_stream_t *stream = svn_stream_from_stringbuf(buf, pool); + svn_authz_t *authz; + svn_boolean_t access_granted; + + SVN_ERR(svn_repos_authz_parse2(&authz, stream, NULL, NULL, NULL, pool, pool)); + + SVN_ERR(svn_repos_authz_check_access(authz, "project1", "/foo", "user1", + svn_authz_write | svn_authz_recursive, + &access_granted, + pool)); + SVN_TEST_ASSERT(access_granted == TRUE); + + return SVN_NO_ERROR; +} + static int max_threads = 4; static struct svn_test_descriptor_t test_funcs[] = @@ -453,6 +520,10 @@ static struct svn_test_descriptor_t test "test svn_authz__parse"), SVN_TEST_PASS2(test_global_rights, "test svn_authz__get_global_rights"), + SVN_TEST_PASS2(issue_4741_groups, + "issue 4741 groups"), + SVN_TEST_XFAIL2(reposful_reposless_stanzas_inherit, + "[foo:/] inherits [/]"), SVN_TEST_NULL }; Modified: subversion/branches/addremove/subversion/tests/libsvn_repos/dump-load-test.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_repos/dump-load-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_repos/dump-load-test.c (original) +++ subversion/branches/addremove/subversion/tests/libsvn_repos/dump-load-test.c Sat May 23 14:16:56 2020 @@ -81,7 +81,7 @@ test_dump_bad_props(svn_stringbuf_t **du notify_func, notify_baton, NULL, NULL, NULL, NULL, pool)); - svn_stream_close(stream); + SVN_ERR(svn_stream_close(stream)); /* Check that the property appears in the dump data */ expected_str = apr_psprintf(pool, "K %d\n%s\n" @@ -131,7 +131,7 @@ test_load_bad_props(svn_stringbuf_t *dum notify_func, notify_baton, NULL, NULL, /*cancellation*/ pool)); - svn_stream_close(stream); + SVN_ERR(svn_stream_close(stream)); /* Check the loaded property */ fs = svn_repos_fs(repos); Modified: subversion/branches/addremove/subversion/tests/libsvn_repos/repos-test.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_repos/repos-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_repos/repos-test.c (original) +++ subversion/branches/addremove/subversion/tests/libsvn_repos/repos-test.c Sat May 23 14:16:56 2020 @@ -4154,7 +4154,7 @@ mkdir_delete_copy(svn_repos_t *repos, svn_fs_root_t *txn_root, *rev_root; SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool)); - + SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, pool)); SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); SVN_ERR(svn_fs_make_dir(txn_root, "A/T", pool)); @@ -4461,7 +4461,7 @@ test_list(const svn_test_opts_t *opts, SVN_ERR(svn_repos_list(rev_root, "/A", patterns, svn_depth_infinity, FALSE, NULL, NULL, list_callback, &counter, NULL, NULL, pool)); - SVN_TEST_ASSERT(counter == 6); + SVN_TEST_ASSERT(counter == 7); return SVN_NO_ERROR; } Propchange: subversion/branches/addremove/subversion/tests/libsvn_subr/ ------------------------------------------------------------------------------ --- svn:ignore (original) +++ svn:ignore Sat May 23 14:16:56 2020 @@ -56,3 +56,7 @@ sqlite-test-* x509-test xml-test test_apr_trunc_workaround +save-cleartext +test_stream_readline_file_crlf +test_stream_readline_file_lf +test_stream_readline_file_nul Modified: subversion/branches/addremove/subversion/tests/libsvn_subr/config-test.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_subr/config-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_subr/config-test.c (original) +++ subversion/branches/addremove/subversion/tests/libsvn_subr/config-test.c Sat May 23 14:16:56 2020 @@ -70,12 +70,12 @@ get_config_file_path(const char **cfg_fi } static const char *config_keys[] = { "foo", "a", "b", "c", "d", "e", "f", "g", - "h", "i", NULL }; + "h", "i", "m", NULL }; static const char *config_values[] = { "bar", "Aa", "100", "bar", "a %(bogus)s oyster bar", "%(bogus)s shmoo %(", "%Aa", "lyrical bard", "%(unterminated", - "Aa 100", NULL }; + "Aa 100", "foo bar baz", NULL }; static svn_error_t * test_text_retrieval(const svn_test_opts_t *opts, Modified: subversion/branches/addremove/subversion/tests/libsvn_subr/config-test.cfg URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_subr/config-test.cfg?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_subr/config-test.cfg (original) +++ subversion/branches/addremove/subversion/tests/libsvn_subr/config-test.cfg Sat May 23 14:16:56 2020 @@ -45,6 +45,10 @@ j=some %(k)scle k=c%(j)sy # Depends on a cyclic definition l=depends on a %(j)scycle! +# line continuation +m = foo + bar + baz [UpperCaseSection] a=Aa Modified: subversion/branches/addremove/subversion/tests/libsvn_subr/dirent_uri-test.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_subr/dirent_uri-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_subr/dirent_uri-test.c (original) +++ subversion/branches/addremove/subversion/tests/libsvn_subr/dirent_uri-test.c Sat May 23 14:16:56 2020 @@ -36,6 +36,7 @@ #include "svn_pools.h" #include "svn_dirent_uri.h" +#include "private/svn_dirent_uri_private.h" #include "private/svn_fspath.h" #include "private/svn_cert.h" @@ -2331,11 +2332,12 @@ test_relpath_internal_style(apr_pool_t * for (i = 0; i < COUNT_OF(tests); i++) { - const char *internal = svn_relpath__internal_style(tests[i].path, pool); + const char *internal; + SVN_ERR(svn_relpath__make_internal(&internal, tests[i].path, pool, pool)); if (strcmp(internal, tests[i].result)) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, - "svn_relpath__internal_style(\"%s\") returned " + "svn_relpath__make_internal(\"%s\") returned " "\"%s\" expected \"%s\"", tests[i].path, internal, tests[i].result); } Modified: subversion/branches/addremove/subversion/tests/libsvn_subr/io-test.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_subr/io-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_subr/io-test.c (original) +++ subversion/branches/addremove/subversion/tests/libsvn_subr/io-test.c Sat May 23 14:16:56 2020 @@ -211,6 +211,38 @@ create_comparison_candidates(struct test return err; } +/* Create an on-disk tree with optional read-only attributes on some + files and/or directories. */ +static svn_error_t * +create_dir_tree(const char **dir_path, + const char *testname, + svn_boolean_t dir_readonly, + svn_boolean_t file_readonly, + apr_pool_t *pool) +{ + const char *test_dir_path; + const char *sub_dir_path; + const char *file_path; + + SVN_ERR(svn_test_make_sandbox_dir(&test_dir_path, testname, pool)); + + sub_dir_path = svn_dirent_join(test_dir_path, "dir", pool); + SVN_ERR(svn_io_dir_make(sub_dir_path, APR_OS_DEFAULT, pool)); + + file_path = svn_dirent_join(sub_dir_path, "file", pool); + SVN_ERR(svn_io_file_create_empty(file_path, pool)); + + if (file_readonly) + SVN_ERR(svn_io_set_file_read_only(file_path, FALSE, pool)); + + if (dir_readonly) + SVN_ERR(svn_io_set_file_read_only(sub_dir_path, FALSE, pool)); + + *dir_path = sub_dir_path; + return SVN_NO_ERROR; +} + + /* Functions to check the 2-way and 3-way file comparison functions. */ @@ -1144,9 +1176,59 @@ test_apr_trunc_workaround(apr_pool_t *po SVN_ERR(svn_io_file_seek(f, APR_CUR, &offset, pool)); SVN_TEST_ASSERT(offset == (int)len); - return SVN_NO_ERROR; + return SVN_NO_ERROR; +} + + +/* Issue #4806 */ +static svn_error_t * +test_rmtree_all_writable(apr_pool_t *pool) +{ + const char *dir_path = NULL; + + SVN_ERR(create_dir_tree(&dir_path, "test_rmtree_all_writable", + FALSE, FALSE, pool)); + SVN_ERR(svn_io_remove_dir2(dir_path, FALSE, NULL, NULL, pool)); + return SVN_NO_ERROR; +} + +/* Issue #4806 */ +static svn_error_t * +test_rmtree_file_readonly(apr_pool_t *pool) +{ + const char *dir_path = NULL; + + SVN_ERR(create_dir_tree(&dir_path, "test_rmtree_file_readonly", + FALSE, TRUE, pool)); + SVN_ERR(svn_io_remove_dir2(dir_path, FALSE, NULL, NULL, pool)); + return SVN_NO_ERROR; +} + +/* Issue #4806 */ +static svn_error_t * +test_rmtree_dir_readonly(apr_pool_t *pool) +{ + const char *dir_path = NULL; + + SVN_ERR(create_dir_tree(&dir_path, "test_rmtree_dir_readonly", + TRUE, FALSE, pool)); + SVN_ERR(svn_io_remove_dir2(dir_path, FALSE, NULL, NULL, pool)); + return SVN_NO_ERROR; +} + +/* Issue #4806 */ +static svn_error_t * +test_rmtree_all_readonly(apr_pool_t *pool) +{ + const char *dir_path = NULL; + + SVN_ERR(create_dir_tree(&dir_path, "test_rmtree_all_readonly", + TRUE, TRUE, pool)); + SVN_ERR(svn_io_remove_dir2(dir_path, FALSE, NULL, NULL, pool)); + return SVN_NO_ERROR; } + /* The test table. */ static int max_threads = 3; @@ -1184,6 +1266,14 @@ static struct svn_test_descriptor_t test "test svn_io_open_uniquely_named()"), SVN_TEST_PASS2(test_apr_trunc_workaround, "test workaround for APR in svn_io_file_trunc"), + SVN_TEST_PASS2(test_rmtree_all_writable, + "test svn_io_remove_dir2() with writable tree"), + SVN_TEST_PASS2(test_rmtree_file_readonly, + "test svn_io_remove_dir2() with read-only file"), + SVN_TEST_PASS2(test_rmtree_dir_readonly, + "test svn_io_remove_dir2() with read-only directory"), + SVN_TEST_PASS2(test_rmtree_all_readonly, + "test svn_io_remove_dir2() with read-only tree"), SVN_TEST_NULL }; Modified: subversion/branches/addremove/subversion/tests/libsvn_subr/mergeinfo-test.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_subr/mergeinfo-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_subr/mergeinfo-test.c (original) +++ subversion/branches/addremove/subversion/tests/libsvn_subr/mergeinfo-test.c Sat May 23 14:16:56 2020 @@ -33,6 +33,8 @@ #include "svn_types.h" #include "svn_mergeinfo.h" #include "private/svn_mergeinfo_private.h" +#include "private/svn_sorts_private.h" +#include "private/svn_error_private.h" #include "../svn_test.h" /* A quick way to create error messages. */ @@ -1673,100 +1675,27 @@ test_remove_prefix_from_catalog(apr_pool static svn_error_t * test_rangelist_merge_overlap(apr_pool_t *pool) { - svn_rangelist_t * changes; - /* 15014-19472,19473-19612*,19613-19614,19615-19630*,19631-19634,19635-20055* */ - svn_rangelist_t * rangelist = apr_array_make(pool, 1, sizeof(svn_merge_range_t *)); - svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange)); - - /* This range is optional for reproducing issue #4686 */ - mrange->start = 15013; - mrange->end = 19472; - mrange->inheritable = TRUE; - APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange; - - mrange = apr_pcalloc(pool, sizeof(*mrange)); - mrange->start = 19472; - mrange->end = 19612; - mrange->inheritable = FALSE; - APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange; - - /* This range is optional for reproducing issue #4686 */ - mrange = apr_pcalloc(pool, sizeof(*mrange)); - mrange->start = 19612; - mrange->end = 19614; - mrange->inheritable = TRUE; - APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange; - - mrange = apr_pcalloc(pool, sizeof(*mrange)); - mrange->start = 19614; - mrange->end = 19630; - mrange->inheritable = FALSE; - APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange; - - mrange = apr_pcalloc(pool, sizeof(*mrange)); - mrange->start = 19630; - mrange->end = 19634; - mrange->inheritable = TRUE; - APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange; - - /* This range is optional for reproducing issue #4686 */ - mrange = apr_pcalloc(pool, sizeof(*mrange)); - mrange->start = 19634; - mrange->end = 20055; - mrange->inheritable = FALSE; - APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange; - - /* 15014-20515* */ - changes = apr_array_make(pool, 1, sizeof(svn_merge_range_t *)); - mrange = apr_pcalloc(pool, sizeof(*mrange)); - mrange->start = 15013; - mrange->end = 20515; - mrange->inheritable = FALSE; - APR_ARRAY_PUSH(changes, svn_merge_range_t *) = mrange; -#if 0 - { - svn_string_t * tmpString; - - svn_rangelist_to_string(&tmpString, rangelist, pool); - printf("rangelist %s\n", tmpString->data); - } - { - svn_string_t * tmpString; - - svn_rangelist_to_string(&tmpString, changes, pool); - printf("changes %s\n", tmpString->data); - } -#endif - + const char *rangelist_str = "19473-19612*,19615-19630*,19631-19634"; + const char *changes_str = "15014-20515*"; + const char *expected_str = "15014-19630*,19631-19634,19635-20515*"; + /* wrong result: "15014-19630*,19634-19631*,19631-19634,19635-20515*" */ + svn_rangelist_t *rangelist, *changes; + svn_string_t *result_string; + + /* prepare the inputs */ + SVN_ERR(svn_rangelist__parse(&rangelist, rangelist_str, pool)); + SVN_ERR(svn_rangelist__parse(&changes, changes_str, pool)); SVN_TEST_ASSERT(svn_rangelist__is_canonical(rangelist)); SVN_TEST_ASSERT(svn_rangelist__is_canonical(changes)); + /* perform the merge */ SVN_ERR(svn_rangelist_merge2(rangelist, changes, pool, pool)); + /* check the output */ SVN_TEST_ASSERT(svn_rangelist__is_canonical(rangelist)); + SVN_ERR(svn_rangelist_to_string(&result_string, rangelist, pool)); + SVN_TEST_STRING_ASSERT(result_string->data, expected_str); -#if 0 - { - svn_string_t * tmpString; - - svn_rangelist_to_string(&tmpString, rangelist, pool); - printf("result %s\n", tmpString->data); - } -#endif - - /* wrong result - result 15014-19472,19473-19612*,19613-19614,19615-19630*,19634-19631*,19631-19634,19635-20515* - */ - - { - svn_string_t * tmp_string; - svn_rangelist_t *range_list; - - svn_rangelist_to_string(&tmp_string, rangelist, pool); - - SVN_ERR(svn_rangelist__parse(&range_list, tmp_string->data, pool)); - } - return SVN_NO_ERROR; } @@ -1856,6 +1785,701 @@ test_rangelist_loop(apr_pool_t *pool) return SVN_NO_ERROR; } + +/* A specific case where result was non-canonical, around svn 1.10 ~ 1.13. */ +static svn_error_t * +test_rangelist_merge_canonical_result(apr_pool_t *pool) +{ + const char *rangelist_str = "8-10"; + const char *changes_str = "5-10*,11-24"; + const char *expected_str = "5-7*,8-24"; + /* wrong result: "5-7*,8-10,11-24" */ + svn_rangelist_t *rangelist, *changes; + svn_string_t *result_string; + + /* prepare the inputs */ + SVN_ERR(svn_rangelist__parse(&rangelist, rangelist_str, pool)); + SVN_ERR(svn_rangelist__parse(&changes, changes_str, pool)); + SVN_TEST_ASSERT(svn_rangelist__is_canonical(rangelist)); + SVN_TEST_ASSERT(svn_rangelist__is_canonical(changes)); + + /* perform the merge */ + SVN_ERR(svn_rangelist_merge2(rangelist, changes, pool, pool)); + + /* check the output */ + SVN_TEST_ASSERT(svn_rangelist__is_canonical(rangelist)); + SVN_ERR(svn_rangelist_to_string(&result_string, rangelist, pool)); + SVN_TEST_STRING_ASSERT(result_string->data, expected_str); + + return SVN_NO_ERROR; +} + +/* Test svn_rangelist_merge2() with specific inputs derived from an + * occurrence of issue #4840 "in the wild", that triggered a hard + * assertion failure (abort) in a 1.10.6 release-mode build. + */ +static svn_error_t * +test_rangelist_merge_array_insert_failure(apr_pool_t *pool) +{ + svn_rangelist_t *rx, *ry; + svn_string_t *rxs; + + /* Simplified case with same failure mode as reported case: error + * "E200004: svn_sort__array_insert2: + * Attempted insert at index 4 in array length 3" */ + SVN_ERR(svn_rangelist__parse(&rx, "2*,4*,6*,8", pool)); + SVN_ERR(svn_rangelist__parse(&ry, "1-9*,11", pool)); + SVN_ERR(svn_rangelist_merge2(rx, ry, pool, pool)); + SVN_ERR(svn_rangelist_to_string(&rxs, rx, pool)); + SVN_TEST_STRING_ASSERT(rxs->data, "1-7*,8,9*,11"); + + /* Actual reported case: in v1.10.6, aborted; after r1872118, error + * "E200004: svn_sort__array_insert2: + * Attempted insert at index 57 in array length 55". The actual "index" + * and "array length" numbers vary with changes such as r1823728. */ + SVN_ERR(svn_rangelist__parse(&rx, "997347-997597*,997884-1000223*,1000542-1000551*,1001389-1001516,1002139-1002268*,1002896-1003064*,1003320-1003468,1005939-1006089*,1006443-1006630*,1006631-1006857,1007028-1007116*,1009467-1009629,1009630-1010007*,1010774-1010860,1011036-1011502,1011672-1014004*,1014023-1014197,1014484-1014542*,1015077-1015568,1016219-1016365,1016698-1016845,1017331-1018616,1027032-1027180,1027855-1028051,1028261-1028395,1028553-1028663,1028674-1028708,1028773-1028891*,1029223-1030557,1032239-1032284*,1032801-1032959,1032960-1033074*,1033745-1033810,1034990-1035104,1035435-1036108*,1036109-1036395,1036396-1036865*,1036866-1036951,1036952-1037647*,1037648-1037750,1037751-1038548*,1038549-1038700,1038701-1042103*,1042104-1042305,1042306-1046626*,1046627-1046910,1046911-1047676*,1047677-1047818,1047819-1047914*,1047915-1048025,1048026-1048616*,1048617-1048993,1048994-1050066*,1054605-1054739,1054854-1055021", pool)); + SVN_ERR(svn_rangelist__parse(&ry, "1035435-1050066*,1052459-1054617", pool)); + SVN_ERR(svn_rangelist_merge2(rx, ry, pool, pool)); + /* Here we don't care to check the result; just that it returns "success". */ + return SVN_NO_ERROR; +} + + +/* Random testing parameters and coverage + * + * The parameters for testing random inputs, in conjunction with the + * specific test case generation code, were adjusted so as to observe the + * tests generating each of the known failure modes. The aim is also to + * have sufficient coverage of inputs to discover other failure modes in + * future if the code is changed. + * + * There are neither theoretic nor empirical guarantees on the coverage. + */ + +/* Randomize revision numbers over this small range. + * (With a larger range, we would find edge cases more rarely.) + * See comment "Random testing parameters and coverage" */ +#define RANGELIST_TESTS_MAX_REV 15 + +/* A representation of svn_rangelist_t in which + * root[R] := (revision R is in the rangelist) + * inherit[R] := (revision R is in the rangelist and inheritable) + * + * Assuming all forward ranges. + */ +typedef struct rl_array_t { + svn_boolean_t root[RANGELIST_TESTS_MAX_REV + 1]; + svn_boolean_t inherit[RANGELIST_TESTS_MAX_REV + 1]; +} rl_array_t; + +static void +rangelist_to_array(rl_array_t *a, + const svn_rangelist_t *rl) +{ + int i; + + memset(a, 0, sizeof(*a)); + for (i = 0; i < rl->nelts; i++) + { + svn_merge_range_t *range = APR_ARRAY_IDX(rl, i, svn_merge_range_t *); + svn_revnum_t r; + + for (r = range->start + 1; r <= range->end; r++) + { + a->root[r] = TRUE; + a->inherit[r] = range->inheritable; + } + } +} + +/* Compute the union of two rangelists arrays. + * Let MA := union(BA, CA) + */ +static void +rangelist_array_union(rl_array_t *ma, + const rl_array_t *ba, + const rl_array_t *ca) +{ + svn_revnum_t r; + + for (r = 0; r <= RANGELIST_TESTS_MAX_REV; r++) + { + ma->root[r] = ba->root[r] || ca->root[r]; + ma->inherit[r] = ba->inherit[r] || ca->inherit[r]; + } +} + +/* Return TRUE iff two rangelist arrays are equal. + */ +static svn_boolean_t +rangelist_array_equal(const rl_array_t *ba, + const rl_array_t *ca) +{ + svn_revnum_t r; + + for (r = 0; r <= RANGELIST_TESTS_MAX_REV; r++) + { + if (ba->root[r] != ca->root[r] + || ba->inherit[r] != ca->inherit[r]) + { + return FALSE; + } + } + return TRUE; +} + +#define IS_VALID_FORWARD_RANGE(range) \ + (SVN_IS_VALID_REVNUM((range)->start) && ((range)->start < (range)->end)) + +/* Check rangelist is sorted and contains forward ranges. */ +static svn_boolean_t +rangelist_is_sorted(const svn_rangelist_t *rangelist) +{ + int i; + + for (i = 0; i < rangelist->nelts; i++) + { + const svn_merge_range_t *thisrange + = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); + + if (!IS_VALID_FORWARD_RANGE(thisrange)) + return FALSE; + } + for (i = 1; i < rangelist->nelts; i++) + { + const svn_merge_range_t *lastrange + = APR_ARRAY_IDX(rangelist, i-1, svn_merge_range_t *); + const svn_merge_range_t *thisrange + = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); + + if (svn_sort_compare_ranges(&lastrange, &thisrange) > 0) + return FALSE; + } + return TRUE; +} + +/* Return a random number R, where 0 <= R < N. + */ +static int rand_less_than(int n, apr_uint32_t *seed) +{ + apr_uint32_t next = svn_test_rand(seed); + return ((apr_uint64_t)next * n) >> 32; +} + +/* Return a random integer in a triangular (centre-weighted) distribution in + * the inclusive interval [MIN, MAX]. */ +static int +rand_interval_triangular(int min, int max, apr_uint32_t *seed) +{ + int span = max - min + 1; + + return min + rand_less_than(span/2 + 1, seed) + + rand_less_than((span+1)/2, seed); +} + +/* Generate a rangelist with a random number of random ranges. + * Choose from 0 to NON_V_MAX_RANGES ranges, biased towards the middle. + */ +#define NON_V_MAX_RANGES 4 /* See "Random testing parameters and coverage" */ +static void +rangelist_random_non_validated(svn_rangelist_t **rl, + apr_uint32_t *seed, + apr_pool_t *pool) +{ + svn_rangelist_t *r = apr_array_make(pool, NON_V_MAX_RANGES, + sizeof(svn_merge_range_t *)); + int n_ranges = rand_interval_triangular(0, NON_V_MAX_RANGES, seed); + int i; + + for (i = 0; i < n_ranges; i++) + { + svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange)); + + mrange->start = rand_less_than(RANGELIST_TESTS_MAX_REV + 1, seed); + mrange->end = rand_less_than(RANGELIST_TESTS_MAX_REV + 1, seed); + mrange->inheritable = rand_less_than(2, seed); + APR_ARRAY_PUSH(r, svn_merge_range_t *) = mrange; + } + *rl = r; +} + +/* Compare two integers pointed to by A_P and B_P, for use with qsort(). */ +static int +int_compare(const void *a_p, const void *b_p) +{ + const int *a = a_p, *b = b_p; + + return (*a < *b) ? -1 : (*a > *b) ? 1 : 0; +} + +/* Fill an ARRAY[ARRAY_LENGTH] with values each in the inclusive range + * [0, MAX]. The values are in ascending order, possibly with the same + * value repeated any number of times. + */ +static void +ascending_values(int *array, + int array_length, + int max, + apr_uint32_t *seed) +{ + int i; + + for (i = 0; i < array_length; i++) + array[i] = rand_less_than(max + 1, seed); + /* Sort them. (Some values will be repeated.) */ + qsort(array, array_length, sizeof(*array), int_compare); +} + +/* Generate a random rangelist that is not necessarily canonical + * but is at least sorted according to svn_sort_compare_ranges() + * and on which svn_rangelist__canonicalize() would succeed. + * Choose from 0 to SEMI_C_MAX_RANGES ranges, biased towards the middle. + */ +#define SEMI_C_MAX_RANGES 8 +static void +rangelist_random_semi_canonical(svn_rangelist_t **rl, + apr_uint32_t *seed, + apr_pool_t *pool) +{ + svn_rangelist_t *r = apr_array_make(pool, 4, sizeof(svn_merge_range_t *)); + int n_ranges = rand_interval_triangular(0, SEMI_C_MAX_RANGES, seed); + int start_and_end_revs[SEMI_C_MAX_RANGES * 2]; + int i; + + /* Choose start and end revs of the ranges. To end up with ranges evenly + * distributed up to RANGELIST_TESTS_MAX_REV, we start with them evenly + * distributed up to RANGELIST_TESTS_MAX_REV - N_RANGES, before spreading. */ + ascending_values(start_and_end_revs, n_ranges * 2, + RANGELIST_TESTS_MAX_REV - n_ranges, + seed); + /* Some values will be repeated. Within one range, that is not allowed, + * so add 1 to the length of each range, spreading the ranges out so they + * still don't overlap: + * [(6,6), (6,8)] becomes [(6,7), (7, 10)] */ + for (i = 0; i < n_ranges; i++) + { + start_and_end_revs[i*2] += i; + start_and_end_revs[i*2 + 1] += i+1; + } + + for (i = 0; i < n_ranges; i++) + { + svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange)); + + mrange->start = start_and_end_revs[i * 2]; + mrange->end = start_and_end_revs[i * 2 + 1]; + mrange->inheritable = rand_less_than(2, seed); + APR_ARRAY_PUSH(r, svn_merge_range_t *) = mrange; + } + *rl = r; + + /* check postconditions */ + { + svn_rangelist_t *dup; + svn_error_t *err; + + SVN_ERR_ASSERT_NO_RETURN(rangelist_is_sorted(*rl)); + dup = svn_rangelist_dup(*rl, pool); + err = svn_rangelist__canonicalize(dup, pool); + SVN_ERR_ASSERT_NO_RETURN(!err); + } +} + +/* Generate a random rangelist that satisfies svn_rangelist__is_canonical(). + */ +static void +rangelist_random_canonical(svn_rangelist_t **rl, + apr_uint32_t *seed, + apr_pool_t *pool) +{ + svn_rangelist_t *r; + int i; + + rangelist_random_semi_canonical(&r, seed, pool); + for (i = 1; i < r->nelts; i++) + { + svn_merge_range_t *prev_mrange = APR_ARRAY_IDX(r, i-1, svn_merge_range_t *); + svn_merge_range_t *mrange = APR_ARRAY_IDX(r, i, svn_merge_range_t *); + + /* to be canonical: adjacent ranges need differing inheritability */ + if (mrange->start == prev_mrange->end) + { + mrange->inheritable = !prev_mrange->inheritable; + } + } + *rl = r; + + /* check postconditions */ + SVN_ERR_ASSERT_NO_RETURN(svn_rangelist__is_canonical(*rl)); +} + +static const char * +rangelist_to_string(const svn_rangelist_t *rl, + apr_pool_t *pool) +{ + svn_error_t *err; + svn_string_t *ss; + + err = svn_rangelist_to_string(&ss, rl, pool); + if (err) + { + const char *s + = apr_psprintf(pool, "<rangelist[%d ranges]: %s>", + rl->nelts, svn_error_purge_tracing(err)->message); + svn_error_clear(err); + return s; + } + return ss->data; +} + +/* Try svn_rangelist_merge2(rlx, rly) and check errors and result */ +static svn_error_t * +rangelist_merge_random_inputs(svn_rangelist_t *rlx, + svn_rangelist_t *rly, + apr_pool_t *pool) +{ + rl_array_t ax, ay, a_expected, a_actual; + svn_rangelist_t *rlm; + + rangelist_to_array(&ax, rlx); + rangelist_to_array(&ay, rly); + + rlm = svn_rangelist_dup(rlx, pool); + /*printf("testcase: %s / %s\n", rangelist_to_string(rlx, pool), rangelist_to_string(rly, pool));*/ + SVN_ERR(svn_rangelist_merge2(rlm, rly, pool, pool)); + + if (!svn_rangelist__is_canonical(rlm)) + { + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "non-canonical result %s", + rangelist_to_string(rlm, pool)); + } + + /*SVN_TEST_ASSERT(rangelist_equal(rlm, ...));*/ + rangelist_array_union(&a_expected, &ax, &ay); + rangelist_to_array(&a_actual, rlm); + if (!rangelist_array_equal(&a_actual, &a_expected)) + { + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "wrong result: (c? %d / %d) -> %s", + svn_rangelist__is_canonical(rlx), + svn_rangelist__is_canonical(rly), + rangelist_to_string(rlm, pool)); + } + + return SVN_NO_ERROR; +} + +/* Insert a failure mode (ERR_CHAIN) into RESPONSES, keyed by a message + * representing its failure mode. The failure mode message is the lowest + * level error message in ERR_CHAIN, with some case-specific details + * removed to aid de-duplication. If it is new failure mode (not already in + * RESPONSES), store the error and return the message (hash key), else + * clear the error and return NULL. + */ +static const char * +add_failure_mode(svn_error_t *err_chain, + apr_hash_t *failure_modes) +{ + svn_error_t *err = err_chain; + char buf[100]; + const char *message; + + if (!err_chain) + return NULL; + + while (err->child) + err = err->child; + message = svn_err_best_message(err, buf, sizeof(buf)); + + /* For deduplication, ignore case-specific data in certain messages. */ + if (strstr(message, "Unable to parse overlapping revision ranges '")) + message = "Unable to parse overlapping revision ranges '..."; + if (strstr(message, "wrong result: (c?")) + message = "wrong result: (c?..."; + if (strstr(message, "svn_sort__array_insert2: Attempted insert at index ")) + message = "svn_sort__array_insert2: Attempted insert at index ..."; + + if (!svn_hash_gets(failure_modes, message)) + { + svn_hash_sets(failure_modes, message, err); + return message; + } + else + { + svn_error_clear(err_chain); + return NULL; + } +} + +static void +clear_failure_mode_errors(apr_hash_t *failure_modes, apr_pool_t *scratch_pool) +{ + apr_hash_index_t *hi; + + for (hi = apr_hash_first(scratch_pool, failure_modes); + hi; + hi = apr_hash_next(hi)) + { + svn_error_t *err = apr_hash_this_val(hi); + svn_error_clear(err); + } +} + +static svn_error_t * +test_rangelist_merge_random_canonical_inputs(apr_pool_t *pool) +{ + static apr_uint32_t seed = 0; + apr_pool_t *iterpool = svn_pool_create(pool); + apr_hash_t *failure_modes = apr_hash_make(pool); + svn_boolean_t pass = TRUE; + int ix, iy; + + /* "300": See comment "Random testing parameters and coverage" */ + for (ix = 0; ix < 300; ix++) + { + svn_rangelist_t *rlx; + + rangelist_random_canonical(&rlx, &seed, pool); + + for (iy = 0; iy < 300; iy++) + { + svn_rangelist_t *rly; + svn_error_t *err; + const char *failure_mode; + + svn_pool_clear(iterpool); + + rangelist_random_canonical(&rly, &seed, iterpool); + + err = svn_error_trace(rangelist_merge_random_inputs(rlx, rly, iterpool)); + failure_mode = add_failure_mode(err, failure_modes); + if (failure_mode) + { + printf("first example of a failure mode: %s / %s\n" + " %s\n", + rangelist_to_string(rlx, iterpool), + rangelist_to_string(rly, iterpool), + failure_mode); + /*svn_handle_error(err, stdout, FALSE);*/ + pass = FALSE; + } + } + } + + clear_failure_mode_errors(failure_modes, pool); + + if (!pass) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Test failed: %d failure modes", + apr_hash_count(failure_modes)); + return SVN_NO_ERROR; +} + +/* Test svn_rangelist_merge2() with inputs that confirm to its doc-string. + * Fail if any errors are produced. + */ +static svn_error_t * +test_rangelist_merge_random_semi_c_inputs(apr_pool_t *pool) +{ + static apr_uint32_t seed = 0; + apr_pool_t *iterpool = svn_pool_create(pool); + apr_hash_t *failure_modes = apr_hash_make(pool); + svn_boolean_t pass = TRUE; + int ix, iy; + + /* "300": See comment "Random testing parameters and coverage" */ + for (ix = 0; ix < 300; ix++) + { + svn_rangelist_t *rlx; + + rangelist_random_semi_canonical(&rlx, &seed, pool); + + for (iy = 0; iy < 300; iy++) + { + svn_rangelist_t *rly; + svn_error_t *err; + const char *failure_mode; + + svn_pool_clear(iterpool); + + rangelist_random_semi_canonical(&rly, &seed, iterpool); + + err = svn_error_trace(rangelist_merge_random_inputs(rlx, rly, iterpool)); + failure_mode = add_failure_mode(err, failure_modes); + if (failure_mode) + { + printf("first example of a failure mode: %s / %s\n" + " %s\n", + rangelist_to_string(rlx, iterpool), + rangelist_to_string(rly, iterpool), + failure_mode); + /*svn_handle_error(err, stdout, FALSE);*/ + pass = FALSE; + } + } + } + + clear_failure_mode_errors(failure_modes, pool); + + if (!pass) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Test failed: %d failure modes", + apr_hash_count(failure_modes)); + return SVN_NO_ERROR; +} + +/* Test svn_rangelist_merge2() with random non-validated inputs. + * + * Unlike the tests with valid inputs, this test expects many assertion + * failures. We don't care about those. All we care about is that it does + * not crash. */ +static svn_error_t * +test_rangelist_merge_random_non_validated_inputs(apr_pool_t *pool) +{ + static apr_uint32_t seed = 0; + apr_pool_t *iterpool = svn_pool_create(pool); + apr_hash_t *failure_modes = apr_hash_make(pool); + int ix, iy; + + /* "300": See comment "Random testing parameters and coverage" */ + for (ix = 0; ix < 300; ix++) + { + svn_rangelist_t *rlx; + + rangelist_random_non_validated(&rlx, &seed, pool); + + for (iy = 0; iy < 300; iy++) + { + svn_rangelist_t *rly; + svn_error_t *err; + + svn_pool_clear(iterpool); + + rangelist_random_non_validated(&rly, &seed, iterpool); + + err = svn_error_trace(rangelist_merge_random_inputs(rlx, rly, iterpool)); + add_failure_mode(err, failure_modes); + } + } + + clear_failure_mode_errors(failure_modes, pool); + + return SVN_NO_ERROR; +} + +/* Generate random mergeinfo, in which the paths and rangelists are not + * necessarily valid. */ +static svn_error_t * +mergeinfo_random_non_validated(svn_mergeinfo_t *mp, + apr_uint32_t *seed, + apr_pool_t *pool) +{ + svn_mergeinfo_t m = apr_hash_make(pool); + int n_paths = 3; /* See comment "Random testing parameters and coverage" */ + int i; + + for (i = 0; i < n_paths; i++) + { + const char *path; + svn_rangelist_t *rl; + + /* A manually chosen distribution of valid and invalid paths: + See comment "Random testing parameters and coverage" */ + switch (rand_less_than(8, seed)) + { + case 0: case 1: case 2: case 3: + path = apr_psprintf(pool, "/path%d", i); break; + case 4: + path = apr_psprintf(pool, "path%d", i); break; + case 5: + path = apr_psprintf(pool, "//path%d", i); break; + case 6: + path = "/"; break; + case 7: + path = ""; break; + } + rangelist_random_non_validated(&rl, seed, pool); + svn_hash_sets(m, path, rl); + } + *mp = m; + return SVN_NO_ERROR; +} + +#if 0 +static const char * +mergeinfo_to_string_debug(svn_mergeinfo_t m, + apr_pool_t *pool) +{ + svn_string_t *s; + svn_error_t *err; + + err = svn_mergeinfo_to_string(&s, m, pool); + if (err) + { + const char *s2 = err->message; + svn_error_clear(err); + return s2; + } + return s->data; +} +#endif + +/* Try a mergeinfo merge. This does not check the result. */ +static svn_error_t * +mergeinfo_merge_random_inputs(const svn_mergeinfo_t mx, + const svn_mergeinfo_t my, + apr_pool_t *pool) +{ + svn_mergeinfo_t mm = svn_mergeinfo_dup(mx, pool); + + SVN_ERR(svn_mergeinfo_merge2(mm, my, pool, pool)); + return SVN_NO_ERROR; +} + +/* Test svn_mergeinfo_merge2() with random non-validated inputs. + * + * Unlike the tests with valid inputs, this test expects many assertion + * failures. We don't care about those. All we care about is that it does + * not crash. */ +static svn_error_t * +test_mergeinfo_merge_random_non_validated_inputs(apr_pool_t *pool) +{ + static apr_uint32_t seed = 0; + apr_pool_t *iterpool = svn_pool_create(pool); + int ix, iy; + + for (ix = 0; ix < 300; ix++) + { + svn_mergeinfo_t mx; + + SVN_ERR(mergeinfo_random_non_validated(&mx, &seed, pool)); + + for (iy = 0; iy < 300; iy++) + { + svn_mergeinfo_t my; + svn_error_t *err; + + svn_pool_clear(iterpool); + + SVN_ERR(mergeinfo_random_non_validated(&my, &seed, iterpool)); + + err = mergeinfo_merge_random_inputs(mx, my, iterpool); + if (err) + { + /* + printf("testcase FAIL: %s / %s\n", + mergeinfo_to_string_debug(mx, iterpool), + mergeinfo_to_string_debug(my, iterpool)); + svn_handle_error(err, stdout, FALSE); + */ + svn_error_clear(err); + } + } + } + + return SVN_NO_ERROR; +} /* The test table. */ @@ -1900,10 +2524,22 @@ static struct svn_test_descriptor_t test "diff of rangelists"), SVN_TEST_PASS2(test_remove_prefix_from_catalog, "removal of prefix paths from catalog keys"), - SVN_TEST_XFAIL2(test_rangelist_merge_overlap, + SVN_TEST_PASS2(test_rangelist_merge_overlap, "merge of rangelists with overlaps (issue 4686)"), - SVN_TEST_XFAIL2(test_rangelist_loop, + SVN_TEST_PASS2(test_rangelist_loop, "test rangelist edgecases via loop"), + SVN_TEST_PASS2(test_rangelist_merge_canonical_result, + "test rangelist merge canonical result (#4840)"), + SVN_TEST_PASS2(test_rangelist_merge_array_insert_failure, + "test rangelist merge array insert failure (#4840)"), + SVN_TEST_PASS2(test_rangelist_merge_random_canonical_inputs, + "test rangelist merge random canonical inputs"), + SVN_TEST_PASS2(test_rangelist_merge_random_semi_c_inputs, + "test rangelist merge random semi-c inputs"), + SVN_TEST_PASS2(test_rangelist_merge_random_non_validated_inputs, + "test rangelist merge random non-validated inputs"), + SVN_TEST_PASS2(test_mergeinfo_merge_random_non_validated_inputs, + "test mergeinfo merge random non-validated inputs"), SVN_TEST_NULL }; Modified: subversion/branches/addremove/subversion/tests/libsvn_subr/priority-queue-test.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_subr/priority-queue-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_subr/priority-queue-test.c (original) +++ subversion/branches/addremove/subversion/tests/libsvn_subr/priority-queue-test.c Sat May 23 14:16:56 2020 @@ -125,7 +125,7 @@ verify_queue_order(svn_priority_queue__t } /* the queue should now be empty */ - verify_empty_queue(queue); + SVN_ERR(verify_empty_queue(queue)); return SVN_NO_ERROR; } @@ -154,7 +154,7 @@ test_empty_queue(apr_pool_t *pool) svn_priority_queue__t *queue = svn_priority_queue__create(elements, compare_func); - verify_empty_queue(queue); + SVN_ERR(verify_empty_queue(queue)); return SVN_NO_ERROR; } @@ -214,7 +214,7 @@ test_update(apr_pool_t *pool) } /* the queue should now be empty */ - verify_empty_queue(queue); + SVN_ERR(verify_empty_queue(queue)); return SVN_NO_ERROR; } Modified: subversion/branches/addremove/subversion/tests/libsvn_subr/stream-test.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_subr/stream-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_subr/stream-test.c (original) +++ subversion/branches/addremove/subversion/tests/libsvn_subr/stream-test.c Sat May 23 14:16:56 2020 @@ -1000,6 +1000,71 @@ test_stream_readline_file_crlf(apr_pool_ return SVN_NO_ERROR; } +static svn_error_t * +test_stream_readline_file_nul(apr_pool_t *pool) +{ + /* Test is written based on the problem report in + https://lists.apache.org/thread.html/c96eb5618ac0bf6e083345e0fdcdcf834e30913f26eabe6ada7bab62@%3Cusers.subversion.apache.org%3E + where the user had an OOM when calling `svndumpfilter` with + a (most likely) invalid dump containing nul bytes. + */ + const char data[] = + { + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n' + }; + const char *tmp_dir; + const char *tmp_file; + svn_stream_t *stream; + + SVN_ERR(svn_dirent_get_absolute(&tmp_dir, "test_stream_readline_file_nul", pool)); + SVN_ERR(svn_io_remove_dir2(tmp_dir, TRUE, NULL, NULL, pool)); + SVN_ERR(svn_io_make_dir_recursively(tmp_dir, pool)); + svn_test_add_dir_cleanup(tmp_dir); + + tmp_file = svn_dirent_join(tmp_dir, "file", pool); + SVN_ERR(svn_io_file_create_bytes(tmp_file, data, sizeof(data), pool)); + SVN_ERR(svn_stream_open_readonly(&stream, tmp_file, pool, pool)); + + while (1) + { + svn_boolean_t eof; + svn_stringbuf_t *line; + + SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, pool)); + if (eof) + break; + + /* Don't check the contents of the `line` here, at least for now. + + In other words, we just test that this case does not crash or cause + unexpected errors. The reason is that currently our `readline_fn` + implementations have inconsistent behavior when dealing with \0 bytes, + handling those differently, either in strchr() or memchr() styles. + + Once we make them consistent (or even decide to bail out with an error + for \0), this part of the test should start properly checking `data`; + maybe also for non-file streams. + */ + } + + SVN_ERR(svn_stream_close(stream)); + + return SVN_NO_ERROR; +} + /* The test table. */ static int max_threads = 1; @@ -1037,6 +1102,8 @@ static struct svn_test_descriptor_t test "test reading LF-terminated lines from file"), SVN_TEST_PASS2(test_stream_readline_file_crlf, "test reading CRLF-terminated lines from file"), + SVN_TEST_PASS2(test_stream_readline_file_nul, + "test reading line from file with nul bytes"), SVN_TEST_NULL }; Modified: subversion/branches/addremove/subversion/tests/libsvn_subr/subst_translate-test.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_subr/subst_translate-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_subr/subst_translate-test.c (original) +++ subversion/branches/addremove/subversion/tests/libsvn_subr/subst_translate-test.c Sat May 23 14:16:56 2020 @@ -158,7 +158,7 @@ test_svn_subst_translate_string2_null_en }; const char **other_locale; - strncpy(orig_lc_all, setlocale(LC_ALL, NULL), sizeof (orig_lc_all)); + strncpy(orig_lc_all, setlocale(LC_ALL, NULL), sizeof (orig_lc_all) - 1); for (other_locale = other_locales; *other_locale != NULL; ++other_locale) { Modified: subversion/branches/addremove/subversion/tests/libsvn_subr/utf-test.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_subr/utf-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/tests/libsvn_subr/utf-test.c (original) +++ subversion/branches/addremove/subversion/tests/libsvn_subr/utf-test.c Sat May 23 14:16:56 2020 @@ -752,8 +752,10 @@ test_utf_conversions(apr_pool_t *pool) { svn_boolean_t sixteenbit; svn_boolean_t bigendian; + apr_size_t sourcelen; const char *source; const char *result; + svn_boolean_t counted; } tests[] = { #define UTF_32_LE FALSE, FALSE @@ -762,32 +764,36 @@ test_utf_conversions(apr_pool_t *pool) #define UTF_16_BE TRUE, TRUE /* Normal character conversion */ - { UTF_32_LE, "t\0\0\0" "e\0\0\0" "s\0\0\0" "t\0\0\0" "\0\0\0\0", "test" }, - { UTF_32_BE, "\0\0\0t" "\0\0\0e" "\0\0\0s" "\0\0\0t" "\0\0\0\0", "test" }, - { UTF_16_LE, "t\0" "e\0" "s\0" "t\0" "\0\0", "test" }, - { UTF_16_BE, "\0t" "\0e" "\0s" "\0t" "\0\0", "test" }, + { UTF_32_LE, 4, "t\0\0\0" "e\0\0\0" "s\0\0\0" "t\0\0\0" "\0\0\0\0", "test", FALSE }, + { UTF_32_BE, 4, "\0\0\0t" "\0\0\0e" "\0\0\0s" "\0\0\0t" "\0\0\0\0", "test", FALSE }, + { UTF_16_LE, 4, "t\0" "e\0" "s\0" "t\0" "\0\0", "test", FALSE }, + { UTF_16_BE, 4, "\0t" "\0e" "\0s" "\0t" "\0\0", "test", FALSE }, /* Valid surrogate pairs */ - { UTF_16_LE, "\x00\xD8" "\x00\xDC" "\0\0", "\xf0\x90\x80\x80" }, /* U+010000 */ - { UTF_16_LE, "\x34\xD8" "\x1E\xDD" "\0\0", "\xf0\x9d\x84\x9e" }, /* U+01D11E */ - { UTF_16_LE, "\xFF\xDB" "\xFD\xDF" "\0\0", "\xf4\x8f\xbf\xbd" }, /* U+10FFFD */ - - { UTF_16_BE, "\xD8\x00" "\xDC\x00" "\0\0", "\xf0\x90\x80\x80" }, /* U+010000 */ - { UTF_16_BE, "\xD8\x34" "\xDD\x1E" "\0\0", "\xf0\x9d\x84\x9e" }, /* U+01D11E */ - { UTF_16_BE, "\xDB\xFF" "\xDF\xFD" "\0\0", "\xf4\x8f\xbf\xbd" }, /* U+10FFFD */ + { UTF_16_LE, 2, "\x00\xD8" "\x00\xDC" "\0\0", "\xf0\x90\x80\x80", FALSE }, /* U+010000 */ + { UTF_16_LE, 2, "\x34\xD8" "\x1E\xDD" "\0\0", "\xf0\x9d\x84\x9e", FALSE }, /* U+01D11E */ + { UTF_16_LE, 2, "\xFF\xDB" "\xFD\xDF" "\0\0", "\xf4\x8f\xbf\xbd", FALSE }, /* U+10FFFD */ + + { UTF_16_BE, 2, "\xD8\x00" "\xDC\x00" "\0\0", "\xf0\x90\x80\x80", FALSE }, /* U+010000 */ + { UTF_16_BE, 2, "\xD8\x34" "\xDD\x1E" "\0\0", "\xf0\x9d\x84\x9e", FALSE }, /* U+01D11E */ + { UTF_16_BE, 2, "\xDB\xFF" "\xDF\xFD" "\0\0", "\xf4\x8f\xbf\xbd", FALSE }, /* U+10FFFD */ /* Swapped, single and trailing surrogate pairs */ - { UTF_16_LE, "*\0" "\x00\xDC" "\x00\xD8" "*\0\0\0", "*\xed\xb0\x80" "\xed\xa0\x80*" }, - { UTF_16_LE, "*\0" "\x1E\xDD" "*\0\0\0", "*\xed\xb4\x9e*" }, - { UTF_16_LE, "*\0" "\xFF\xDB" "*\0\0\0", "*\xed\xaf\xbf*" }, - { UTF_16_LE, "\x1E\xDD" "\0\0", "\xed\xb4\x9e" }, - { UTF_16_LE, "\xFF\xDB" "\0\0", "\xed\xaf\xbf" }, - - { UTF_16_BE, "\0*" "\xDC\x00" "\xD8\x00" "\0*\0\0", "*\xed\xb0\x80" "\xed\xa0\x80*" }, - { UTF_16_BE, "\0*" "\xDD\x1E" "\0*\0\0", "*\xed\xb4\x9e*" }, - { UTF_16_BE, "\0*" "\xDB\xFF" "\0*\0\0", "*\xed\xaf\xbf*" }, - { UTF_16_BE, "\xDD\x1E" "\0\0", "\xed\xb4\x9e" }, - { UTF_16_BE, "\xDB\xFF" "\0\0", "\xed\xaf\xbf" }, + { UTF_16_LE, 4, "*\0" "\x00\xDC" "\x00\xD8" "*\0\0\0", "*\xed\xb0\x80" "\xed\xa0\x80*", FALSE }, + { UTF_16_LE, 3, "*\0" "\x1E\xDD" "*\0\0\0", "*\xed\xb4\x9e*", FALSE }, + { UTF_16_LE, 3, "*\0" "\xFF\xDB" "*\0\0\0", "*\xed\xaf\xbf*", FALSE }, + { UTF_16_LE, 1, "\x1E\xDD" "\0\0", "\xed\xb4\x9e", FALSE }, + { UTF_16_LE, 1, "\xFF\xDB" "\0\0", "\xed\xaf\xbf", FALSE }, + + { UTF_16_BE, 4, "\0*" "\xDC\x00" "\xD8\x00" "\0*\0\0", "*\xed\xb0\x80" "\xed\xa0\x80*", FALSE }, + { UTF_16_BE, 3, "\0*" "\xDD\x1E" "\0*\0\0", "*\xed\xb4\x9e*", FALSE }, + { UTF_16_BE, 3, "\0*" "\xDB\xFF" "\0*\0\0", "*\xed\xaf\xbf*", FALSE }, + { UTF_16_BE, 1, "\xDD\x1E" "\0\0", "\xed\xb4\x9e", FALSE }, + { UTF_16_BE, 1, "\xDB\xFF" "\0\0", "\xed\xaf\xbf", FALSE }, + + /* Counted strings with NUL characters */ + { UTF_16_LE, 3, "x\0" "\0\0" "y\0" "*\0", "x\0y", TRUE }, + { UTF_32_BE, 3, "\0\0\0x" "\0\0\0\0" "\0\0\0y" "\0\0\0*", "x\0y", TRUE }, #undef UTF_32_LE #undef UTF_32_BE @@ -799,33 +805,46 @@ test_utf_conversions(apr_pool_t *pool) const struct cvt_test_t *tc; const svn_string_t *result; - int i; + apr_size_t maxlen = 0; - for (i = 1, tc = tests; tc->source; ++tc, ++i) + /* To assure proper alignment of the source string, it needs to be copied + into an array of the appropriate type before calling + svn_utf__utf{16,32}_to_utf8. */ + apr_uint16_t *source16; + apr_int32_t *source32; + + for (tc = tests; tc->source; ++tc) + if (tc->sourcelen > maxlen) + maxlen = tc->sourcelen; + maxlen++; + + source16 = apr_pcalloc(pool, maxlen * sizeof(*source16)); + source32 = apr_pcalloc(pool, maxlen * sizeof(*source32)); + + for (tc = tests; tc->source; ++tc) { if (tc->sixteenbit) - SVN_ERR(svn_utf__utf16_to_utf8(&result, (const void*)tc->source, - SVN_UTF__UNKNOWN_LENGTH, - tc->bigendian, pool, pool)); + { + memset(source16, 0, maxlen * sizeof(*source16)); + memcpy(source16, tc->source, (tc->sourcelen + 1) * sizeof(*source16)); + SVN_ERR(svn_utf__utf16_to_utf8(&result, source16, + tc->counted ? tc->sourcelen : SVN_UTF__UNKNOWN_LENGTH, + tc->bigendian, pool, pool)); + } else - SVN_ERR(svn_utf__utf32_to_utf8(&result, (const void*)tc->source, - SVN_UTF__UNKNOWN_LENGTH, - tc->bigendian, pool, pool)); - SVN_ERR_ASSERT(0 == strcmp(result->data, tc->result)); + { + memset(source32, 0, maxlen * sizeof(*source32)); + memcpy(source32, tc->source, (tc->sourcelen + 1) * sizeof(*source32)); + SVN_ERR(svn_utf__utf32_to_utf8(&result, source32, + tc->counted ? tc->sourcelen : SVN_UTF__UNKNOWN_LENGTH, + tc->bigendian, pool, pool)); + } + if (tc->counted) + SVN_ERR_ASSERT(0 == memcmp(result->data, tc->result, tc->sourcelen)); + else + SVN_ERR_ASSERT(0 == strcmp(result->data, tc->result)); } - /* Test counted strings with NUL characters */ - SVN_ERR(svn_utf__utf16_to_utf8( - &result, (void*)("x\0" "\0\0" "y\0" "*\0"), 3, - FALSE, pool, pool)); - SVN_ERR_ASSERT(0 == memcmp(result->data, "x\0y", 3)); - - SVN_ERR(svn_utf__utf32_to_utf8( - &result, - (void*)("\0\0\0x" "\0\0\0\0" "\0\0\0y" "\0\0\0*"), 3, - TRUE, pool, pool)); - SVN_ERR_ASSERT(0 == memcmp(result->data, "x\0y", 3)); - return SVN_NO_ERROR; }