diff options
Diffstat (limited to 'subversion/tests/libsvn_subr')
24 files changed, 4348 insertions, 632 deletions
diff --git a/subversion/tests/libsvn_subr/auth-test.c b/subversion/tests/libsvn_subr/auth-test.c index 6c3862d..9d7d866 100644 --- a/subversion/tests/libsvn_subr/auth-test.c +++ b/subversion/tests/libsvn_subr/auth-test.c @@ -22,9 +22,11 @@ */ #include "svn_auth.h" +#include "svn_dirent_uri.h" #include "svn_private_config.h" #include "../svn_test.h" +#include "private/svn_auth_private.h" static svn_error_t * test_platform_specific_auth_providers(apr_pool_t *pool) @@ -54,6 +56,9 @@ test_platform_specific_auth_providers(apr_pool_t *pool) #ifdef SVN_HAVE_KWALLET number_of_providers += 2; #endif +#ifdef SVN_HAVE_GPG_AGENT + number_of_providers += 1; +#endif #ifdef SVN_HAVE_KEYCHAIN_SERVICES number_of_providers += 2; #endif @@ -140,8 +145,8 @@ test_platform_specific_auth_providers(apr_pool_t *pool) /* Test GNOME Keyring auth providers */ #ifdef SVN_HAVE_GNOME_KEYRING - svn_auth_get_platform_specific_provider(&provider, "gnome_keyring", - "simple", pool); + SVN_ERR(svn_auth_get_platform_specific_provider(&provider, "gnome_keyring", + "simple", pool)); if (!provider) return svn_error_createf @@ -149,8 +154,8 @@ test_platform_specific_auth_providers(apr_pool_t *pool) "svn_auth_get_platform_specific_provider('gnome_keyring', 'simple') " "should not return NULL"); - svn_auth_get_platform_specific_provider(&provider, "gnome_keyring", - "ssl_client_cert_pw", pool); + SVN_ERR(svn_auth_get_platform_specific_provider(&provider, "gnome_keyring", + "ssl_client_cert_pw", pool)); if (!provider) return svn_error_createf @@ -159,8 +164,8 @@ test_platform_specific_auth_providers(apr_pool_t *pool) "'ssl_client_cert_pw') should not return NULL"); /* Make sure you do not get a Windows auth provider */ - svn_auth_get_platform_specific_provider(&provider, "windows", - "simple", pool); + SVN_ERR(svn_auth_get_platform_specific_provider(&provider, "windows", + "simple", pool)); if (provider) return svn_error_createf @@ -203,6 +208,109 @@ test_platform_specific_auth_providers(apr_pool_t *pool) return SVN_NO_ERROR; } +/* Helper for test_auth_clear(). Implements svn_config_auth_walk_func_t */ +static svn_error_t * +cleanup_callback(svn_boolean_t *delete_cred, + void *walk_baton, + const char *cred_kind, + const char *realmstring, + apr_hash_t *cred_hash, + apr_pool_t *scratch_pool) +{ + svn_auth_baton_t *b = walk_baton; + + SVN_TEST_ASSERT(strcmp(cred_kind, SVN_AUTH_CRED_SIMPLE) == 0); + SVN_TEST_ASSERT(strcmp(realmstring, "<http://my.host> My realm") == 0); + + SVN_ERR(svn_auth_forget_credentials(b, cred_kind, realmstring, scratch_pool)); + + *delete_cred = TRUE; + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_auth_clear(apr_pool_t *pool) +{ + const char *auth_dir; + svn_auth_provider_object_t *provider; + svn_auth_baton_t *baton; + apr_array_header_t *providers; + void *credentials; + svn_auth_cred_simple_t *creds; + svn_auth_iterstate_t *state; + + SVN_ERR(svn_dirent_get_absolute(&auth_dir, "", pool)); + auth_dir = svn_dirent_join(auth_dir, "auth-clear", pool); + + svn_test_add_dir_cleanup(auth_dir); + + SVN_ERR(svn_io_remove_dir2(auth_dir, TRUE, NULL, NULL, pool)); + SVN_ERR(svn_io_dir_make(auth_dir, APR_OS_DEFAULT, pool)); + + svn_auth_get_simple_provider2(&provider, NULL, NULL, pool); + + providers = apr_array_make(pool, 1, sizeof(svn_auth_provider_object_t *)); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + + svn_auth_open(&baton, providers, pool); + + svn_auth_set_parameter(baton, SVN_AUTH_PARAM_DEFAULT_USERNAME, "jrandom"); + svn_auth_set_parameter(baton, SVN_AUTH_PARAM_DEFAULT_PASSWORD, "rayjandom"); + svn_auth_set_parameter(baton, SVN_AUTH_PARAM_CONFIG_DIR, auth_dir); + + /* Create the auth subdirs. Without these we can't store passwords */ + SVN_ERR(svn_config_ensure(auth_dir, pool)); + + /* Obtain the default credentials just passed */ + SVN_ERR(svn_auth_first_credentials(&credentials, + &state, + SVN_AUTH_CRED_SIMPLE, + "<http://my.host> My realm", + baton, + pool)); + + creds = credentials; + SVN_TEST_ASSERT(strcmp(creds->username, "jrandom") == 0); + SVN_TEST_ASSERT(creds->may_save); + + /* And tell that they are ok and can be saved */ + SVN_ERR(svn_auth_save_credentials(state, pool)); + + /* Ok, and now we try to remove the credentials */ + svn_auth_set_parameter(baton, SVN_AUTH_PARAM_DEFAULT_USERNAME, NULL); + svn_auth_set_parameter(baton, SVN_AUTH_PARAM_DEFAULT_PASSWORD, NULL); + + /* Are they still in the baton? */ + SVN_ERR(svn_auth_first_credentials(&credentials, + &state, + SVN_AUTH_CRED_SIMPLE, + "<http://my.host> My realm", + baton, + pool)); + + SVN_TEST_ASSERT(credentials); + creds = credentials; + SVN_TEST_ASSERT(strcmp(creds->username, "jrandom") == 0); + SVN_TEST_ASSERT(creds->may_save); + + /* Use our walker function to delete credentials (and forget them + from the auth baton). */ + SVN_ERR(svn_config_walk_auth_data(auth_dir, cleanup_callback, baton, pool)); + + /* Finally, they should be gone! */ + SVN_ERR(svn_auth_first_credentials(&credentials, + &state, + SVN_AUTH_CRED_SIMPLE, + "<http://my.host> My realm", + baton, + pool)); + + SVN_TEST_ASSERT(! credentials); + + return SVN_NO_ERROR; +} + /* The test table. */ @@ -211,5 +319,13 @@ struct svn_test_descriptor_t test_funcs[] = SVN_TEST_NULL, SVN_TEST_PASS2(test_platform_specific_auth_providers, "test retrieving platform-specific auth providers"), +#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE + SVN_TEST_PASS2(test_auth_clear, + "test svn_auth_clear()"), +#else + SVN_TEST_WIMP(test_auth_clear, + "test svn_auth_clear()", + "Needs testing with SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE"), +#endif SVN_TEST_NULL }; diff --git a/subversion/tests/libsvn_subr/cache-test.c b/subversion/tests/libsvn_subr/cache-test.c index 9920296..9e7a7b3 100644 --- a/subversion/tests/libsvn_subr/cache-test.c +++ b/subversion/tests/libsvn_subr/cache-test.c @@ -36,7 +36,7 @@ /* Implements svn_cache__serialize_func_t */ static svn_error_t * -serialize_revnum(char **data, +serialize_revnum(void **data, apr_size_t *data_len, void *in, apr_pool_t *pool) @@ -51,7 +51,7 @@ serialize_revnum(char **data, /* Implements svn_cache__deserialize_func_t */ static svn_error_t * deserialize_revnum(void **out, - char *data, + void *data, apr_size_t data_len, apr_pool_t *pool) { @@ -155,8 +155,8 @@ test_memcache_basic(const svn_test_opts_t *opts, if (opts->config_file) { - SVN_ERR(svn_config_read2(&config, opts->config_file, - TRUE, FALSE, pool)); + SVN_ERR(svn_config_read3(&config, opts->config_file, + TRUE, FALSE, FALSE, pool)); SVN_ERR(svn_cache__make_memcache_from_config(&memcache, config, pool)); } @@ -182,16 +182,9 @@ test_membuffer_cache_basic(apr_pool_t *pool) { svn_cache__t *cache; svn_membuffer_t *membuffer; - svn_boolean_t thread_safe; -#if APR_HAS_THREADS - thread_safe = TRUE; -#else - thread_safe = FALSE; -#endif - - SVN_ERR(svn_cache__membuffer_cache_create(&membuffer, 10*1024, 1, - thread_safe, pool)); + SVN_ERR(svn_cache__membuffer_cache_create(&membuffer, 10*1024, 1, 0, + TRUE, TRUE, pool)); /* Create a cache with just one entry. */ SVN_ERR(svn_cache__create_membuffer_cache(&cache, @@ -200,11 +193,73 @@ test_membuffer_cache_basic(apr_pool_t *pool) deserialize_revnum, APR_HASH_KEY_STRING, "cache:", + FALSE, pool)); return basic_cache_test(cache, FALSE, pool); } +/* Implements svn_cache__deserialize_func_t */ +static svn_error_t * +raise_error_deserialize_func(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + return svn_error_create(APR_EGENERAL, NULL, NULL); +} + +/* Implements svn_cache__partial_getter_func_t */ +static svn_error_t * +raise_error_partial_getter_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + return svn_error_create(APR_EGENERAL, NULL, NULL); +} + +static svn_error_t * +test_membuffer_serializer_error_handling(apr_pool_t *pool) +{ + svn_cache__t *cache; + svn_membuffer_t *membuffer; + svn_revnum_t twenty = 20; + svn_boolean_t found; + void *val; + + SVN_ERR(svn_cache__membuffer_cache_create(&membuffer, 10*1024, 1, 0, + TRUE, TRUE, pool)); + + /* Create a cache with just one entry. */ + SVN_ERR(svn_cache__create_membuffer_cache(&cache, + membuffer, + serialize_revnum, + raise_error_deserialize_func, + APR_HASH_KEY_STRING, + "cache:", + FALSE, + pool)); + + SVN_ERR(svn_cache__set(cache, "twenty", &twenty, pool)); + + /* Test retrieving data from cache using full getter that + always raises an error. */ + SVN_TEST_ASSERT_ERROR( + svn_cache__get(&val, &found, cache, "twenty", pool), + APR_EGENERAL); + + /* Test retrieving data from cache using partial getter that + always raises an error. */ + SVN_TEST_ASSERT_ERROR( + svn_cache__get_partial(&val, &found, cache, "twenty", + raise_error_partial_getter_func, + NULL, pool), + APR_EGENERAL); + + return SVN_NO_ERROR; +} static svn_error_t * test_memcache_long_key(const svn_test_opts_t *opts, @@ -229,8 +284,8 @@ test_memcache_long_key(const svn_test_opts_t *opts, if (opts->config_file) { - SVN_ERR(svn_config_read2(&config, opts->config_file, - TRUE, FALSE, pool)); + SVN_ERR(svn_config_read3(&config, opts->config_file, + TRUE, FALSE, FALSE, pool)); SVN_ERR(svn_cache__make_memcache_from_config(&memcache, config, pool)); } @@ -275,5 +330,7 @@ struct svn_test_descriptor_t test_funcs[] = "memcache svn_cache with very long keys"), SVN_TEST_PASS2(test_membuffer_cache_basic, "basic membuffer svn_cache test"), + SVN_TEST_PASS2(test_membuffer_serializer_error_handling, + "test for error handling in membuffer svn_cache"), SVN_TEST_NULL }; diff --git a/subversion/tests/libsvn_subr/checksum-test.c b/subversion/tests/libsvn_subr/checksum-test.c index 8e957a6..c5e2409 100644 --- a/subversion/tests/libsvn_subr/checksum-test.c +++ b/subversion/tests/libsvn_subr/checksum-test.c @@ -23,7 +23,11 @@ #include <apr_pools.h> +#include <zlib.h> + #include "svn_error.h" +#include "svn_io.h" +#include "private/svn_pseudo_md5.h" #include "../svn_test.h" @@ -59,11 +63,195 @@ test_checksum_parse(apr_pool_t *pool) return SVN_NO_ERROR; } +static svn_error_t * +test_checksum_empty(apr_pool_t *pool) +{ + svn_checksum_t *checksum; + char data = '\0'; + + checksum = svn_checksum_empty_checksum(svn_checksum_md5, pool); + SVN_TEST_ASSERT(svn_checksum_is_empty_checksum(checksum)); + + checksum = svn_checksum_empty_checksum(svn_checksum_sha1, pool); + SVN_TEST_ASSERT(svn_checksum_is_empty_checksum(checksum)); + + SVN_ERR(svn_checksum(&checksum, svn_checksum_md5, &data, 0, pool)); + SVN_TEST_ASSERT(svn_checksum_is_empty_checksum(checksum)); + + SVN_ERR(svn_checksum(&checksum, svn_checksum_sha1, &data, 0, pool)); + SVN_TEST_ASSERT(svn_checksum_is_empty_checksum(checksum)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_pseudo_md5(apr_pool_t *pool) +{ + apr_uint32_t input[16] = { 0 }; + apr_uint32_t digest_15[4] = { 0 }; + apr_uint32_t digest_31[4] = { 0 }; + apr_uint32_t digest_63[4] = { 0 }; + svn_checksum_t *checksum; + + /* input is all 0s but the hash shall be different + (due to different input sizes)*/ + svn__pseudo_md5_15(digest_15, input); + svn__pseudo_md5_31(digest_31, input); + svn__pseudo_md5_63(digest_63, input); + + SVN_TEST_ASSERT(memcmp(digest_15, digest_31, sizeof(digest_15))); + SVN_TEST_ASSERT(memcmp(digest_15, digest_63, sizeof(digest_15))); + SVN_TEST_ASSERT(memcmp(digest_31, digest_63, sizeof(digest_15))); + + /* the checksums shall also be different from "proper" MD5 */ + SVN_ERR(svn_checksum(&checksum, svn_checksum_md5, input, 15, pool)); + SVN_TEST_ASSERT(memcmp(digest_15, checksum->digest, sizeof(digest_15))); + + SVN_ERR(svn_checksum(&checksum, svn_checksum_md5, input, 31, pool)); + SVN_TEST_ASSERT(memcmp(digest_31, checksum->digest, sizeof(digest_15))); + + SVN_ERR(svn_checksum(&checksum, svn_checksum_md5, input, 63, pool)); + SVN_TEST_ASSERT(memcmp(digest_63, checksum->digest, sizeof(digest_15))); + + return SVN_NO_ERROR; +} + +static svn_error_t * +zero_match(apr_pool_t *pool) +{ + svn_checksum_t *zero_md5; + svn_checksum_t *zero_sha1; + svn_checksum_t *A_md5; + svn_checksum_t *B_md5; + svn_checksum_t *A_sha1; + svn_checksum_t *B_sha1; + + + zero_md5 = svn_checksum_create(svn_checksum_md5, pool); + SVN_ERR(svn_checksum_clear(zero_md5)); + SVN_ERR(svn_checksum(&A_md5, svn_checksum_md5, "A", 1, pool)); + SVN_ERR(svn_checksum(&B_md5, svn_checksum_md5, "B", 1, pool)); + + zero_sha1 = svn_checksum_create(svn_checksum_sha1, pool); + SVN_ERR(svn_checksum_clear(zero_sha1)); + SVN_ERR(svn_checksum(&A_sha1, svn_checksum_sha1, "A", 1, pool)); + SVN_ERR(svn_checksum(&B_sha1, svn_checksum_sha1, "B", 1, pool)); + + /* Different non-zero don't match. */ + SVN_TEST_ASSERT(!svn_checksum_match(A_md5, B_md5)); + SVN_TEST_ASSERT(!svn_checksum_match(A_sha1, B_sha1)); + SVN_TEST_ASSERT(!svn_checksum_match(A_md5, A_sha1)); + SVN_TEST_ASSERT(!svn_checksum_match(A_md5, B_sha1)); + + /* Zero matches anything of the same kind. */ + SVN_TEST_ASSERT(svn_checksum_match(A_md5, zero_md5)); + SVN_TEST_ASSERT(svn_checksum_match(zero_md5, B_md5)); + SVN_TEST_ASSERT(svn_checksum_match(A_sha1, zero_sha1)); + SVN_TEST_ASSERT(svn_checksum_match(zero_sha1, B_sha1)); + + /* Zero doesn't match anything of a different kind... */ + SVN_TEST_ASSERT(!svn_checksum_match(zero_md5, A_sha1)); + SVN_TEST_ASSERT(!svn_checksum_match(zero_sha1, A_md5)); + /* ...even another zero. */ + SVN_TEST_ASSERT(!svn_checksum_match(zero_md5, zero_sha1)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +zlib_expansion_test(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + const char *data_path; + svn_stringbuf_t *deflated; + Byte dst_buffer[256 * 1024]; + Byte *src_buffer; + apr_size_t sz; + + data_path = svn_dirent_join(opts->srcdir, "zlib.deflated", pool); + + SVN_ERR(svn_stringbuf_from_file2(&deflated, data_path, pool)); + src_buffer = (Byte*)deflated->data; + + /* Try to decompress the same data with different blocksizes */ + for (sz = 1; sz < 256; sz++) + { + z_stream stream; + memset(&stream, 0, sizeof(stream)); + inflateInit2(&stream, -15 /* DEFLATE_WINDOW_SIZE */); + + stream.avail_in = sz; + stream.next_in = src_buffer; + stream.avail_out = sizeof(dst_buffer); + stream.next_out = dst_buffer; + + do + { + int zr = inflate(&stream, Z_NO_FLUSH); + + if (zr != Z_OK && zr != Z_STREAM_END) + { + return svn_error_createf( + SVN_ERR_TEST_FAILED, NULL, + "Failure decompressing with blocksize %d", (int)sz); + } + stream.avail_in += sz; + } while (stream.next_in + stream.avail_in < src_buffer + deflated->len); + + stream.avail_in = (src_buffer + deflated->len) - stream.next_in; + + { + int zr = inflate(&stream, Z_NO_FLUSH); + + if (zr != Z_STREAM_END) + { + return svn_error_createf( + SVN_ERR_TEST_FAILED, NULL, + "Final flush failed with blocksize %d", (int)sz); + } + + zr = inflateEnd(&stream); + + if (zr != Z_OK) + { + return svn_error_createf( + SVN_ERR_TEST_FAILED, NULL, + "End of stream handling failed with blocksize %d", + (int)sz); + } + } + + { + apr_uint32_t crc = crc32(0, dst_buffer, stream.total_out); + + if (stream.total_out != 242014 || crc != 0x8f03d934) + { + return svn_error_createf( + SVN_ERR_TEST_FAILED, NULL, + "Decompressed data doesn't match expected size or crc with " + "blocksize %d: Found crc32=0x%08x, size=%d.\n" + "Verify your ZLib installation, as this should never happen", + (int)sz, (unsigned)crc, (int)stream.total_out); + } + } + } + + return SVN_NO_ERROR; +} + /* An array of all test functions */ struct svn_test_descriptor_t test_funcs[] = { SVN_TEST_NULL, SVN_TEST_PASS2(test_checksum_parse, "checksum parse"), + SVN_TEST_PASS2(test_checksum_empty, + "checksum emptiness"), + SVN_TEST_PASS2(test_pseudo_md5, + "pseudo-md5 compatibility"), + SVN_TEST_PASS2(zero_match, + "zero checksum matching"), + SVN_TEST_OPTS_PASS(zlib_expansion_test, + "zlib expansion test (zlib regression)"), SVN_TEST_NULL }; diff --git a/subversion/tests/libsvn_subr/compat-test.c b/subversion/tests/libsvn_subr/compat-test.c index 5ce3f08..9ff8099 100644 --- a/subversion/tests/libsvn_subr/compat-test.c +++ b/subversion/tests/libsvn_subr/compat-test.c @@ -24,10 +24,12 @@ #include <apr_pools.h> #include "svn_error.h" +#include "svn_pools.h" #include "svn_version.h" #include "../svn_test.h" #include "svn_private_config.h" +#include "private/svn_subr_private.h" #ifndef SVN_DISABLE_FULL_VERSION_MATCH #define FALSE_IF_FULL FALSE @@ -100,11 +102,121 @@ test_version_compatibility(apr_pool_t *pool) return SVN_NO_ERROR; } + +static svn_error_t * +test_version_parsing(apr_pool_t *pool) +{ + unsigned int i; + apr_pool_t *iterpool; + + struct version_pair { + const char *str; + svn_boolean_t malformed; + svn_version_t version; + } versions[] = { + /* str malformed version */ + { "1.8", FALSE, { 1, 8, 0, ""} }, + { "1.8-dev", TRUE, { 0, 0, 0, ""} }, + { "1.1.0", FALSE, { 1, 1, 0, ""} }, + { "1.1.3", FALSE, { 1, 1, 3, ""} }, + { "2.10.0", FALSE, { 2, 10, 0, ""} }, + { "1.8.0-dev", FALSE, { 1, 8, 0, "dev"} }, + { "1.7.0-beta1", FALSE, { 1, 7, 0, "beta1"} }, + { "1a.8.0", TRUE, { 0, 0, 0, ""} }, + { "1a.8.0", TRUE, { 0, 0, 0, ""} }, + { "1.a8.0", TRUE, { 0, 0, 0, ""} }, + { "1.8.0a", TRUE, { 0, 0, 0, ""} }, + { "1.8.0.1", TRUE, { 0, 0, 0, ""} }, + }; + + iterpool = svn_pool_create(pool); + for (i = 0; i < sizeof(versions)/sizeof(versions[0]); ++i) + { + svn_version_t *version; + svn_error_t *err; + + svn_pool_clear(iterpool); + + err = svn_version__parse_version_string(&version, versions[i].str, + iterpool); + if (err && (err->apr_err != SVN_ERR_MALFORMED_VERSION_STRING)) + return svn_error_create(SVN_ERR_TEST_FAILED, err, + "Unexpected error code"); + if (err) + { + if (! versions[i].malformed) + return svn_error_create(SVN_ERR_TEST_FAILED, err, + "Unexpected parsing error returned"); + else + svn_error_clear(err); + } + else + { + if (versions[i].malformed) + return svn_error_create(SVN_ERR_TEST_FAILED, NULL, + "Parsing error expected; none returned"); + if (! svn_ver_equal(version, &(versions[i].version))) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Parsed version of '%s' doesn't match " + "expected", versions[i].str); + } + } + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_version_at_least(apr_pool_t *pool) +{ + unsigned int i; + + struct version_pair { + svn_version_t version; + int major; + int minor; + int patch; + svn_boolean_t at_least; + } versions[] = { + /* maj min pat version at_least */ + { { 1, 3, 3, ""}, 1, 3, 3, TRUE }, + { { 1, 3, 3, ""}, 1, 3, 4, FALSE }, + { { 1, 3, 3, ""}, 1, 4, 3, FALSE }, + { { 1, 3, 3, ""}, 0, 4, 3, TRUE }, + { { 1, 3, 3, ""}, 2, 0, 0, FALSE }, + { { 1, 3, 3, ""}, 1, 3, 2, TRUE }, + { { 1, 3, 3, ""}, 1, 2, 4, TRUE }, + { { 1, 3, 3, "dev"}, 1, 3, 2, TRUE }, + { { 1, 3, 3, "dev"}, 1, 3, 3, FALSE }, + { { 1, 3, 3, ""}, 0, 4, 3, TRUE }, + }; + + for (i = 0; i < sizeof(versions)/sizeof(versions[0]); ++i) + { + svn_boolean_t at_least = svn_version__at_least(&(versions[i].version), + versions[i].major, + versions[i].minor, + versions[i].patch); + if (at_least && (! versions[i].at_least)) + return svn_error_create(SVN_ERR_TEST_FAILED, NULL, + "Expected at-least to be FALSE; got TRUE"); + if ((! at_least) && versions[i].at_least) + return svn_error_create(SVN_ERR_TEST_FAILED, NULL, + "Expected at-least to be TRUE; got FALSE"); + } + + return SVN_NO_ERROR; +} + /* An array of all test functions */ struct svn_test_descriptor_t test_funcs[] = { SVN_TEST_NULL, SVN_TEST_PASS2(test_version_compatibility, "svn_ver_compatible"), + SVN_TEST_PASS2(test_version_parsing, + "svn_version__parse_version_string"), + SVN_TEST_PASS2(test_version_at_least, + "svn_version__at_least"), SVN_TEST_NULL }; diff --git a/subversion/tests/libsvn_subr/config-test.c b/subversion/tests/libsvn_subr/config-test.c index aee56f8..8938457 100644 --- a/subversion/tests/libsvn_subr/config-test.c +++ b/subversion/tests/libsvn_subr/config-test.c @@ -109,7 +109,7 @@ test_text_retrieval(apr_pool_t *pool) SVN_ERR(init_params(pool)); cfg_file = apr_pstrcat(pool, srcdir, "/", "config-test.cfg", (char *)NULL); - SVN_ERR(svn_config_read2(&cfg, cfg_file, TRUE, FALSE, pool)); + SVN_ERR(svn_config_read3(&cfg, cfg_file, TRUE, FALSE, FALSE, pool)); /* Test values retrieved from our ConfigParser instance against values retrieved using svn_config. */ @@ -160,7 +160,7 @@ test_boolean_retrieval(apr_pool_t *pool) SVN_ERR(init_params(pool)); cfg_file = apr_pstrcat(pool, srcdir, "/", "config-test.cfg", (char *)NULL); - SVN_ERR(svn_config_read2(&cfg, cfg_file, TRUE, FALSE, pool)); + SVN_ERR(svn_config_read3(&cfg, cfg_file, TRUE, FALSE, FALSE, pool)); for (i = 0; true_keys[i] != NULL; i++) { @@ -211,7 +211,7 @@ test_boolean_retrieval(apr_pool_t *pool) } static svn_error_t * -test_has_section(apr_pool_t *pool) +test_has_section_case_insensitive(apr_pool_t *pool) { svn_config_t *cfg; const char *cfg_file; @@ -220,17 +220,164 @@ test_has_section(apr_pool_t *pool) SVN_ERR(init_params(pool)); cfg_file = apr_pstrcat(pool, srcdir, "/", "config-test.cfg", (char *)NULL); - SVN_ERR(svn_config_read2(&cfg, cfg_file, TRUE, FALSE, pool)); + SVN_ERR(svn_config_read3(&cfg, cfg_file, TRUE, FALSE, FALSE, pool)); if (! svn_config_has_section(cfg, "section1")) return fail(pool, "Failed to find section1"); + if (! svn_config_has_section(cfg, "SECTION1")) + return fail(pool, "Failed to find SECTION1"); + + if (! svn_config_has_section(cfg, "UpperCaseSection")) + return fail(pool, "Failed to find UpperCaseSection"); + + if (! svn_config_has_section(cfg, "uppercasesection")) + return fail(pool, "Failed to find UpperCaseSection"); + + if (svn_config_has_section(cfg, "notthere")) + return fail(pool, "Returned true on missing section"); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_has_section_case_sensitive(apr_pool_t *pool) +{ + svn_config_t *cfg; + const char *cfg_file; + + if (!srcdir) + SVN_ERR(init_params(pool)); + + cfg_file = apr_pstrcat(pool, srcdir, "/", "config-test.cfg", (char *)NULL); + SVN_ERR(svn_config_read3(&cfg, cfg_file, TRUE, TRUE, FALSE, pool)); + + if (! svn_config_has_section(cfg, "section1")) + return fail(pool, "Failed to find section1"); + + if (svn_config_has_section(cfg, "SECTION1")) + return fail(pool, "Returned true on missing section"); + + if (! svn_config_has_section(cfg, "UpperCaseSection")) + return fail(pool, "Failed to find UpperCaseSection"); + + if (svn_config_has_section(cfg, "uppercasesection")) + return fail(pool, "Returned true on missing section"); + if (svn_config_has_section(cfg, "notthere")) return fail(pool, "Returned true on missing section"); return SVN_NO_ERROR; } +static svn_error_t * +test_has_option_case_sensitive(apr_pool_t *pool) +{ + svn_config_t *cfg; + const char *cfg_file; + apr_int64_t value; + int i; + + static struct test_dataset { + const char *option; + apr_int64_t value; + } const test_data[] = { + { "a", 1 }, + { "A", 2 }, + { "B", 3 }, + { "b", 4 } + }; + static const int test_data_size = sizeof(test_data)/sizeof(*test_data); + + if (!srcdir) + SVN_ERR(init_params(pool)); + + cfg_file = apr_pstrcat(pool, srcdir, "/", "config-test.cfg", (char *)NULL); + SVN_ERR(svn_config_read3(&cfg, cfg_file, TRUE, TRUE, TRUE, pool)); + + for (i = 0; i < test_data_size; ++i) + { + SVN_ERR(svn_config_get_int64(cfg, &value, "case-sensitive-option", + test_data[i].option, -1)); + if (test_data[i].value != value) + return fail(pool, + apr_psprintf(pool, + "case-sensitive-option.%s != %" + APR_INT64_T_FMT" but %"APR_INT64_T_FMT, + test_data[i].option, + test_data[i].value, + value)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_stream_interface(apr_pool_t *pool) +{ + svn_config_t *cfg; + const char *cfg_file; + svn_stream_t *stream; + + if (!srcdir) + SVN_ERR(init_params(pool)); + + cfg_file = apr_pstrcat(pool, srcdir, "/", "config-test.cfg", (char *)NULL); + SVN_ERR(svn_stream_open_readonly(&stream, cfg_file, pool, pool)); + + SVN_ERR(svn_config_parse(&cfg, stream, TRUE, TRUE, pool)); + + /* nominal test to make sure cfg is populated with something since + * svn_config_parse will happily return an empty cfg if the stream is + * empty. */ + if (! svn_config_has_section(cfg, "section1")) + return fail(pool, "Failed to find section1"); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_ignore_bom(apr_pool_t *pool) +{ + svn_config_t *cfg; + svn_string_t *cfg_string = svn_string_create("\xEF\xBB\xBF[s1]\nfoo=bar\n", + pool); + svn_stream_t *stream = svn_stream_from_string(cfg_string, pool); + + SVN_ERR(svn_config_parse(&cfg, stream, TRUE, TRUE, pool)); + + if (! svn_config_has_section(cfg, "s1")) + return fail(pool, "failed to find section s1"); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_expand(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_config_t *cfg; + const char *cfg_file, *val; + + if (!srcdir) + SVN_ERR(init_params(pool)); + + cfg_file = apr_pstrcat(pool, srcdir, "/", "config-test.cfg", (char *)NULL); + SVN_ERR(svn_config_read3(&cfg, cfg_file, TRUE, TRUE, FALSE, pool)); + + /* Get expanded "g" which requires expanding "c". */ + svn_config_get(cfg, &val, "section1", "g", NULL); + + /* Get expanded "c". */ + svn_config_get(cfg, &val, "section1", "c", NULL); + + /* With pool debugging enabled this ensures that the expanded value + of "c" was not created in a temporary pool when expanding "g". */ + SVN_TEST_STRING_ASSERT(val, "bar"); + + return SVN_NO_ERROR; +} + /* ==================================================================== If you add a new test to this file, update this array. @@ -246,7 +393,16 @@ struct svn_test_descriptor_t test_funcs[] = "test svn_config"), SVN_TEST_PASS2(test_boolean_retrieval, "test svn_config boolean conversion"), - SVN_TEST_PASS2(test_has_section, - "test svn_config_has_section"), + SVN_TEST_PASS2(test_has_section_case_insensitive, + "test svn_config_has_section (case insensitive)"), + SVN_TEST_PASS2(test_has_section_case_sensitive, + "test svn_config_has_section (case sensitive)"), + SVN_TEST_PASS2(test_has_option_case_sensitive, + "test case-sensitive option name lookup"), + SVN_TEST_PASS2(test_stream_interface, + "test svn_config_parse"), + SVN_TEST_PASS2(test_ignore_bom, "test parsing config file with BOM"), + SVN_TEST_OPTS_PASS(test_expand, + "test variable expansion"), SVN_TEST_NULL }; diff --git a/subversion/tests/libsvn_subr/config-test.cfg b/subversion/tests/libsvn_subr/config-test.cfg index 214889b..8d79a4b 100644 --- a/subversion/tests/libsvn_subr/config-test.cfg +++ b/subversion/tests/libsvn_subr/config-test.cfg @@ -41,6 +41,9 @@ h= %(unterminated # Multiple expansions i=%(a)s %(b)s +[UpperCaseSection] +a=Aa + [booleans] true1 = true true2 = Yes @@ -52,3 +55,9 @@ false2 = no false3 = oFf false4 = 0 bad_false = nyet! + +[case-sensitive-option] +a = 1 +A = 2 +B = 3 +b = 4 diff --git a/subversion/tests/libsvn_subr/crypto-test.c b/subversion/tests/libsvn_subr/crypto-test.c new file mode 100644 index 0000000..0c52804 --- /dev/null +++ b/subversion/tests/libsvn_subr/crypto-test.c @@ -0,0 +1,188 @@ +/* + * crypto-test.c -- test cryptographic routines + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + +#include <stdio.h> +#include <string.h> + +#include "svn_pools.h" + +#include "../svn_test.h" +#include "../../libsvn_subr/crypto.h" + + +/*** Helper functions ***/ + +/* Encrypt PASSWORD within CTX using MASTER, then + decrypt those results and ensure the original PASSWORD comes out + the other end. */ +static svn_error_t * +encrypt_decrypt(svn_crypto__ctx_t *ctx, + const svn_string_t *master, + const char *password, + apr_pool_t *pool) +{ + const svn_string_t *ciphertext, *iv, *salt; + const char *password_again; + + SVN_ERR(svn_crypto__encrypt_password(&ciphertext, &iv, &salt, ctx, + password, master, pool, pool)); + if (! ciphertext) + return svn_error_create(SVN_ERR_TEST_FAILED, NULL, + "Encryption failed to return ciphertext"); + if (! salt) + return svn_error_create(SVN_ERR_TEST_FAILED, NULL, + "Encryption failed to return salt"); + if (! iv) + return svn_error_create(SVN_ERR_TEST_FAILED, NULL, + "Encryption failed to return initialization " + "vector"); + + SVN_ERR(svn_crypto__decrypt_password(&password_again, ctx, ciphertext, iv, + salt, master, pool, pool)); + + if (! password_again) + return svn_error_create(SVN_ERR_TEST_FAILED, NULL, + "Decryption failed to generate results"); + + if (strcmp(password, password_again) != 0) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Encrypt/decrypt cycle failed to produce " + "original result\n" + " orig (%s)\n" + " new (%s)\n", + password, password_again); + + return SVN_NO_ERROR; +} + + + +/*** Test functions ***/ + +static svn_error_t * +test_encrypt_decrypt_password(apr_pool_t *pool) +{ + svn_crypto__ctx_t *ctx; + const svn_string_t *master = svn_string_create("Pastor Massword", pool); + int i; + apr_pool_t *iterpool; + const char *passwords[] = { + "3ncryptm!3", /* fits in one block */ + "this is a particularly long password", /* spans blocks */ + "mypassphrase", /* with 4-byte padding, should align on block boundary */ + }; + + /* Skip this test if the crypto subsystem is unavailable. */ + if (! svn_crypto__is_available()) + return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL, NULL); + + SVN_ERR(svn_crypto__context_create(&ctx, pool)); + + iterpool = svn_pool_create(pool); + for (i = 0; i < (sizeof(passwords) / sizeof(const char *)); i++) + { + svn_pool_clear(iterpool); + SVN_ERR(encrypt_decrypt(ctx, master, passwords[i], iterpool)); + } + + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; +} + + +static svn_error_t * +test_passphrase_check(apr_pool_t *pool) +{ + svn_crypto__ctx_t *ctx; + int i; + apr_pool_t *iterpool; + const char *passwords[] = { + "3ncryptm!3", /* fits in one block */ + "this is a particularly long password", /* spans blocks */ + "mypassphrase", /* with 4-byte padding, should align on block boundary */ + }; + const svn_string_t *ciphertext, *iv, *salt, *secret; + const char *checktext; + svn_boolean_t is_valid; + int num_passwords = sizeof(passwords) / sizeof(const char *); + + /* Skip this test if the crypto subsystem is unavailable. */ + if (! svn_crypto__is_available()) + return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL, NULL); + + SVN_ERR(svn_crypto__context_create(&ctx, pool)); + + iterpool = svn_pool_create(pool); + for (i = 0; i < num_passwords; i++) + { + svn_pool_clear(iterpool); + secret = svn_string_create(passwords[i], iterpool); + SVN_ERR(svn_crypto__generate_secret_checktext(&ciphertext, &iv, &salt, + &checktext, ctx, secret, + iterpool, iterpool)); + SVN_ERR(svn_crypto__verify_secret(&is_valid, ctx, secret, ciphertext, + iv, salt, checktext, iterpool)); + if (! is_valid) + return svn_error_create(SVN_ERR_TEST_FAILED, NULL, + "Error validating secret against checktext"); + } + + /* Now check that a bogus secret causes the validation to fail. We + try to verify each secret against the checktext generated by the + previous one. */ + for (i = 0; i < num_passwords; i++) + { + int test_secret_index = (i + 1) % num_passwords; + + svn_pool_clear(iterpool); + secret = svn_string_create(passwords[i], iterpool); + SVN_ERR(svn_crypto__generate_secret_checktext(&ciphertext, &iv, &salt, + &checktext, ctx, secret, + iterpool, iterpool)); + secret = svn_string_create(passwords[test_secret_index], iterpool); + SVN_ERR(svn_crypto__verify_secret(&is_valid, ctx, secret, ciphertext, + iv, salt, checktext, iterpool)); + if (is_valid) + return svn_error_create(SVN_ERR_TEST_FAILED, NULL, + "Expected secret validation failure; " + "got success"); + } + + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; +} + + + + +/* The test table. */ + +struct svn_test_descriptor_t test_funcs[] = + { + SVN_TEST_NULL, + SVN_TEST_PASS2(test_encrypt_decrypt_password, + "basic password encryption/decryption test"), + SVN_TEST_PASS2(test_passphrase_check, + "password checktext generation/validation"), + SVN_TEST_NULL + }; diff --git a/subversion/tests/libsvn_subr/dirent_uri-test.c b/subversion/tests/libsvn_subr/dirent_uri-test.c index f10ab2e..992d288 100644 --- a/subversion/tests/libsvn_subr/dirent_uri-test.c +++ b/subversion/tests/libsvn_subr/dirent_uri-test.c @@ -37,6 +37,7 @@ #include "svn_pools.h" #include "svn_dirent_uri.h" #include "private/svn_fspath.h" +#include "private/svn_cert.h" #include "../svn_test.h" @@ -618,6 +619,11 @@ test_uri_dirname(apr_pool_t *pool) { "http://server", "http://server" }, { "file:///a/b", "file:///a" }, { "file:///a", "file://" }, + { "file://", "file://" }, +#ifdef WIN32 + { "file:///A:/dir", "file:///A:" }, + { "file:///A:", "file://" }, +#endif }; for (i = 0; i < COUNT_OF(tests); i++) @@ -670,6 +676,7 @@ test_dirent_canonicalize(apr_pool_t *pool) { "foo/../", "foo/.." }, { "foo/../.", "foo/.." }, { "foo//.//bar", "foo/bar" }, + { "//foo", "/foo" }, { "///foo", "/foo" }, { "/.//./.foo", "/.foo" }, { ".///.foo", ".foo" }, @@ -743,6 +750,7 @@ test_relpath_canonicalize(apr_pool_t *pool) { "foo/../", "foo/.." }, { "foo/../.", "foo/.." }, { "foo//.//bar", "foo/bar" }, + { "//foo", "foo" }, { "///foo", "foo" }, { "/.//./.foo", ".foo" }, { ".///.foo", ".foo" }, @@ -882,15 +890,31 @@ static const testcase_canonicalize_t uri_canonical_tests[] = { "https://SERVER:80/", "https://server:80" }, { "svn://server:80", "svn://server:80" }, { "svn://SERVER:443/", "svn://server:443" }, + { "file:///C%7C/temp/REPOS", "file:///C%7C/temp/REPOS" }, + { "file:///C|/temp/REPOS", "file:///C%7C/temp/REPOS" }, + { "file:///C:/", "file:///C:" }, + { "http://[::1]/", "http://[::1]" }, + { "http://[::1]:80/", "http://[::1]" }, + { "https://[::1]:443", "https://[::1]" }, + { "http://[::1]/", "http://[::1]" }, + { "http://[::1]:80/", "http://[::1]" }, + { "https://[::1]:443", "https://[::1]" }, + { "http://[FACE:B00C::]/s","http://[face:b00c::]/s" }, + { "svn+ssh://b@[1:2::3]/s","svn+ssh://b@[1:2::3]/s" }, #ifdef SVN_USE_DOS_PATHS { "file:///c:/temp/repos", "file:///C:/temp/repos" }, { "file:///c:/temp/REPOS", "file:///C:/temp/REPOS" }, { "file:///C:/temp/REPOS", "file:///C:/temp/REPOS" }, + { "file:///c:/", "file:///C:" }, #else /* !SVN_USE_DOS_PATHS */ { "file:///c:/temp/repos", "file:///c:/temp/repos" }, { "file:///c:/temp/REPOS", "file:///c:/temp/REPOS" }, { "file:///C:/temp/REPOS", "file:///C:/temp/REPOS" }, + { "file:///c:/", "file:///c:" }, #endif /* SVN_USE_DOS_PATHS */ + /* Hostnames that look like non-canonical paths */ + { "file://./foo", "file://./foo" }, + { "http://./foo", "http://./foo" }, /* svn_uri_is_canonical() was a private function in the 1.6 API, and has since taken a MAJOR change of direction, namely that only absolute URLs are considered canonical uris now. */ @@ -917,6 +941,7 @@ static const testcase_canonicalize_t uri_canonical_tests[] = { "foo/../", NULL }, { "foo/../.", NULL }, { "foo//.//bar", NULL }, + { "//foo", NULL }, { "///foo", NULL }, { "/.//./.foo", NULL }, { ".///.foo", NULL }, @@ -1004,6 +1029,7 @@ test_dirent_is_canonical(apr_pool_t *pool) { "foo/../", FALSE }, { "foo/../.", FALSE }, { "foo//.//bar", FALSE }, + { "//foo", FALSE }, { "///foo", FALSE }, { "/.//./.foo", FALSE }, { ".///.foo", FALSE }, @@ -1104,6 +1130,7 @@ test_relpath_is_canonical(apr_pool_t *pool) { "foo/../", FALSE }, { "foo/../.", FALSE }, { "foo//.//bar", FALSE }, + { "//foo", FALSE }, { "///foo", FALSE }, { "/.//./.foo", FALSE }, { ".///.foo", FALSE }, @@ -1357,6 +1384,9 @@ static const testcase_ancestor_t dirent_ancestor_tests[] = { "foo.", "foo./.bar", ".bar" }, { "X:foo", "X:bar", NULL }, { "../foo", "..", NULL }, + { "/foo/bar/zig", "/foo", NULL }, + { "/foo/bar/zig", "/foo/ba", NULL }, + { "/foo/bar/zig", "/foo/bar/zi", NULL }, #ifdef SVN_USE_DOS_PATHS { "", "C:", NULL }, { "", "C:foo", NULL }, @@ -1377,6 +1407,9 @@ static const testcase_ancestor_t dirent_ancestor_tests[] = { "X:/foo", "X:/", NULL }, { "A:/foo", "A:/foo/bar", "bar" }, { "A:/foo", "A:/foot", NULL }, + { "A:/foo/bar/zig", "A:/foo", NULL }, + { "A:/foo/bar/zig", "A:/foo/ba", NULL }, + { "A:/foo/bar/zig", "A:/foo/bar/zi", NULL }, { "//srv", "//srv/share", NULL }, { "//srv", "//srv/shr/fld", NULL }, { "//srv/shr", "//srv", NULL }, @@ -1386,6 +1419,7 @@ static const testcase_ancestor_t dirent_ancestor_tests[] = { "//srv/s r", "//srv/s r/fld", "fld" }, { "//srv/shr/fld", "//srv/shr", NULL }, { "//srv/shr/fld", "//srv2/shr/fld", NULL }, + { "//srv/shr/fld", "//srv/shr/f", NULL }, { "/", "//srv/share", NULL }, #else /* !SVN_USE_DOS_PATHS */ { "", "C:", "C:" }, @@ -1441,28 +1475,6 @@ static const testcase_ancestor_t relpath_ancestor_tests[] = { "X:foo", "X:bar", NULL }, }; -static svn_error_t * -test_relpath_is_ancestor(apr_pool_t *pool) -{ - const testcase_ancestor_t *t; - - for (t = relpath_ancestor_tests; - t < relpath_ancestor_tests + COUNT_OF(relpath_ancestor_tests); - t++) - { - svn_boolean_t retval; - - retval = svn_relpath__is_ancestor(t->path1, t->path2); - if (!!t->result != retval) - return svn_error_createf - (SVN_ERR_TEST_FAILED, NULL, - "svn_relpath_is_ancestor (%s, %s) returned %s instead of %s", - t->path1, t->path2, retval ? "TRUE" : "FALSE", - t->result ? "TRUE" : "FALSE"); - } - return SVN_NO_ERROR; -} - static const testcase_ancestor_t uri_ancestor_tests[] = { { "http://test", "http://test", "" }, @@ -1473,6 +1485,8 @@ static const testcase_ancestor_t uri_ancestor_tests[] = { "http://", "http://test", NULL }, { "http://server", "http://server/q", "q" }, { "svn://server", "http://server/q", NULL }, + { "http://foo/bar", "http://foo", NULL }, + { "http://foo/bar", "http://foo/ba", NULL }, }; static svn_error_t * @@ -1860,133 +1874,6 @@ test_dirent_is_child(apr_pool_t *pool) } static svn_error_t * -test_relpath_is_child(apr_pool_t *pool) -{ - int i, j; - - static const char * const paths[] = { - "", - "foo", - "foo/bar", - "foo/bars", - "foo/baz", - "foo/bar/baz", - "flu/blar/blaz", - "foo/bar/baz/bing/boom", - ".foo", - ":", - "foo2", - "food", - "bar", - "baz", - "ba", - "bad" - }; - - /* Maximum number of path[] items for all platforms */ -#define MAX_PATHS 32 - - static const char * const - remainders[COUNT_OF(paths)][MAX_PATHS] = { - { 0, "foo", "foo/bar", "foo/bars", "foo/baz", "foo/bar/baz", - "flu/blar/blaz", "foo/bar/baz/bing/boom", ".foo", ":", "foo2", "food", - "bar", "baz", "ba", "bad" }, - { 0, 0, "bar", "bars", "baz", "bar/baz", 0, "bar/baz/bing/boom", 0, 0, 0, - 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, "baz", 0, "baz/bing/boom", 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, "bing/boom", 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - }; - - for (i = 0; i < COUNT_OF(paths); i++) - { - for (j = 0; j < COUNT_OF(paths); j++) - { - const char *remainder; - - remainder = svn_dirent_is_child(paths[i], paths[j], pool); - - if (strcmp(paths[j], "foodbar") == 0) - SVN_ERR_MALFUNCTION(); - - if (((remainder) && (! remainders[i][j])) - || ((! remainder) && (remainders[i][j])) - || (remainder && strcmp(remainder, remainders[i][j]))) - return svn_error_createf - (SVN_ERR_TEST_FAILED, NULL, - "svn_relpath_is_child(%s, %s) returned '%s' instead of '%s'", - paths[i], paths[j], - remainder ? remainder : "(null)", - remainders[i][j] ? remainders[i][j] : "(null)" ); - } - } - -#undef NUM_TEST_PATHS - return SVN_NO_ERROR; -} - - -static svn_error_t * -test_uri_is_child(apr_pool_t *pool) -{ - int i, j; - - static const char * const paths[] = { - "http://foo/bar", - "http://foo/bar%20bell", - "http://foo/baz", - "http://foo", - "http://f", - "file://foo/bar", - "file://foo/bar/baz%20bol", - }; - - static const char * const - remainders[COUNT_OF(paths)][COUNT_OF(paths)] = { - { 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0 }, - { "bar", "bar bell", "baz", 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, "baz bol" }, - { 0, 0, 0, 0, 0, 0, 0 }, - }; - - for (i = 0; i < COUNT_OF(paths); i++) - { - for (j = 0; j < COUNT_OF(paths); j++) - { - const char *remainder; - - remainder = svn_uri__is_child(paths[i], paths[j], pool); - - if (((remainder) && (! remainders[i][j])) - || ((! remainder) && (remainders[i][j])) - || (remainder && strcmp(remainder, remainders[i][j]))) - return svn_error_createf - (SVN_ERR_TEST_FAILED, NULL, - "svn_uri_is_child (%s, %s) [%d,%d] " - "returned '%s' instead of '%s'", - paths[i], paths[j], i, j, - remainder ? remainder : "(null)", - remainders[i][j] ? remainders[i][j] : "(null)" ); - } - } - - return SVN_NO_ERROR; -} - -static svn_error_t * test_dirent_get_absolute(apr_pool_t *pool) { int i; @@ -2166,15 +2053,34 @@ test_dirent_condense_targets(apr_pool_t *pool) const char* common; apr_array_header_t *hdr = apr_array_make(pool, 8, sizeof(const char*)); apr_array_header_t *condensed; + svn_boolean_t skip = FALSE; for (j = 0; j < COUNT_OF(tests[i].paths); j++) { if (tests[i].paths[j] != NULL) - APR_ARRAY_PUSH(hdr, const char*) = tests[i].paths[j]; + { + APR_ARRAY_PUSH(hdr, const char*) = tests[i].paths[j]; +#ifdef SVN_USE_DOS_PATHS + /* For tests that are referencing a D: drive, specifically test + if such a drive exists on the system. If not, skip the test + (svn_dirent_condense_targets will fail, because + apr_filepath_merge will produce an APR_EBADPATH error). */ + if (strncmp(tests[i].paths[j], "D:", 2) == 0 + && GetDriveType("D:\\") == DRIVE_NO_ROOT_DIR) + { + /* There is no D: drive, skip this. */ + skip = TRUE; + break; + } +#endif + } else break; } + if (skip) + continue; + SVN_ERR(svn_dirent_condense_targets(&common, &condensed, hdr, FALSE, pool, pool)); @@ -2483,7 +2389,7 @@ test_file_url_from_dirent(apr_pool_t *pool) } tests[] = { #ifdef SVN_USE_DOS_PATHS { "C:/file", "file:///C:/file" }, - { "C:/", "file:///C:/" }, + { "C:/", "file:///C:" }, { "C:/File#$", "file:///C:/File%23$" }, /* We can't check these as svn_dirent_get_absolute() won't work on shares that don't exist */ @@ -2510,6 +2416,8 @@ test_file_url_from_dirent(apr_pool_t *pool) "svn_uri_get_file_url_from_dirent(\"%s\") " "returned \"%s\" expected \"%s\"", tests[i].dirent, result, tests[i].result); + + SVN_TEST_ASSERT(svn_uri_is_canonical(result, pool)); } return SVN_NO_ERROR; @@ -2686,7 +2594,7 @@ test_fspath_join(apr_pool_t *pool) } static svn_error_t * -test_fspath_is_child(apr_pool_t *pool) +test_fspath_skip_ancestor(apr_pool_t *pool) { int i, j; @@ -2701,12 +2609,12 @@ test_fspath_is_child(apr_pool_t *pool) static const char * const remainders[COUNT_OF(paths)][COUNT_OF(paths)] = { - { 0, "f", "foo", "foo/bar", "foo/bars", "foo/bar/baz" }, - { 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, "bar", "bars", "bar/baz" }, - { 0, 0, 0, 0, 0, "baz" }, - { 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0 }, + { "", "f", "foo", "foo/bar", "foo/bars", "foo/bar/baz" }, + { 0, "", 0, 0, 0, 0 }, + { 0, 0, "", "bar", "bars", "bar/baz" }, + { 0, 0, 0, "", 0, "baz" }, + { 0, 0, 0, 0, "", 0 }, + { 0, 0, 0, 0, 0, "" }, }; for (i = 0; i < COUNT_OF(paths); i++) @@ -2714,7 +2622,7 @@ test_fspath_is_child(apr_pool_t *pool) for (j = 0; j < COUNT_OF(paths); j++) { const char *remainder - = svn_fspath__is_child(paths[i], paths[j], pool); + = svn_fspath__skip_ancestor(paths[i], paths[j]); SVN_TEST_STRING_ASSERT(remainder, remainders[i][j]); } @@ -2807,6 +2715,145 @@ test_fspath_get_longest_ancestor(apr_pool_t *pool) return SVN_NO_ERROR; } +struct cert_match_dns_test { + const char *pattern; + const char *hostname; + svn_boolean_t expected; +}; + +static svn_error_t * +run_cert_match_dns_tests(struct cert_match_dns_test *tests, apr_pool_t *pool) +{ + struct cert_match_dns_test *ct; + apr_pool_t *iterpool = svn_pool_create(pool); + + for (ct = tests; ct->pattern; ct++) + { + svn_boolean_t result; + svn_string_t *pattern, *hostname; + + svn_pool_clear(iterpool); + + pattern = svn_string_create(ct->pattern, iterpool); + hostname = svn_string_create(ct->hostname, iterpool); + + result = svn_cert__match_dns_identity(pattern, hostname); + if (result != ct->expected) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Expected %s but got %s for pattern '%s' on " + "hostname '%s'", + ct->expected ? "match" : "no match", + result ? "match" : "no match", + pattern->data, hostname->data); + + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +static struct cert_match_dns_test cert_match_dns_tests[] = { + { "foo.example.com", "foo.example.com", TRUE }, /* exact match */ + { "foo.example.com", "FOO.EXAMPLE.COM", TRUE }, /* case differences */ + { "FOO.EXAMPLE.COM", "foo.example.com", TRUE }, + { "*.example.com", "FoO.ExAmPlE.CoM", TRUE }, + { "*.ExAmPlE.CoM", "foo.example.com", TRUE }, + { "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz", TRUE }, + { "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", TRUE }, + { "foo.example.com", "bar.example.com", FALSE }, /* difference at start */ + { "foo.example.com", "foo.example.net", FALSE }, /* difference at end */ + { "foo.example.com", "foo.example.commercial", FALSE }, /* hostname longer */ + { "foo.example.commercial", "foo.example.com", FALSE }, /* pattern longer */ + { "foo.example.comcom", "foo.example.com", FALSE }, /* repeated suffix */ + { "foo.example.com", "foo.example.comcom", FALSE }, + { "foo.example.com.com", "foo.example.com", FALSE }, + { "foo.example.com", "foo.example.com.com", FALSE }, + { "foofoo.example.com", "foo.example.com", FALSE }, /* repeated prefix */ + { "foo.example.com", "foofoo.example.com", FALSE }, + { "foo.foo.example.com", "foo.example.com", FALSE }, + { "foo.example.com", "foo.foo.example.com", FALSE }, + { "foo.*.example.com", "foo.bar.example.com", FALSE }, /* RFC 6125 s. 6.4.3 + Rule 1 */ + { "*.example.com", "foo.example.com", TRUE }, /* RFC 6125 s. 6.4.3 Rule 2 */ + { "*.example.com", "bar.foo.example.com", FALSE }, /* Rule 2 */ + { "*.example.com", "example.com", FALSE }, /* Rule 2 */ + { "*.example.com", ".example.com", FALSE }, /* RFC doesn't say what to do + here and a leading period on + a hostname doesn't make sense + so we'll just reject this. */ + { "*", "foo.example.com", FALSE }, /* wildcard must be left-most label, + implies that there must be more than + one label. */ + { "*", "example.com", FALSE }, + { "*", "com", FALSE }, + { "*.example.com", "foo.example.net", FALSE }, /* difference in literal text + with a wildcard. */ + { "*.com", "example.com", TRUE }, /* See Errata ID 3090 for RFC 6125, + probably shouldn't allow this but + we do for now. */ + { "*.", "example.com", FALSE }, /* test some dubious 2 character wildcard + patterns */ + { "*.", "example.", TRUE }, /* This one feels questionable */ + { "*.", "example", FALSE }, + { "*.", ".", FALSE }, + { "a", "a", TRUE }, /* check that single letter exact matches work */ + { "a", "b", FALSE }, /* and single letter not matches shouldn't */ + { "*.*.com", "foo.example.com", FALSE }, /* unsupported wildcards */ + { "*.*.com", "example.com", FALSE }, + { "**.example.com", "foo.example.com", FALSE }, + { "**.example.com", "example.com", FALSE }, + { "f*.example.com", "foo.example.com", FALSE }, + { "f*.example.com", "bar.example.com", FALSE }, + { "*o.example.com", "foo.example.com", FALSE }, + { "*o.example.com", "bar.example.com", FALSE }, + { "f*o.example.com", "foo.example.com", FALSE }, + { "f*o.example.com", "bar.example.com", FALSE }, + { "foo.e*.com", "foo.example.com", FALSE }, + { "foo.*e.com", "foo.example.com", FALSE }, + { "foo.e*e.com", "foo.example.com", FALSE }, + { "foo.example.com", "foo.example.com.", TRUE }, /* trailing dot */ + { "*.example.com", "foo.example.com.", TRUE }, + { "foo", "foo.", TRUE }, + { "foo.example.com.", "foo.example.com", FALSE }, + { "*.example.com.", "foo.example.com", FALSE }, + { "foo.", "foo", FALSE }, + { "foo.example.com", "foo.example.com..", FALSE }, + { "*.example.com", "foo.example.com..", FALSE }, + { "foo", "foo..", FALSE }, + { "foo.example.com..", "foo.example.com", FALSE }, + { "*.example.com..", "foo.example.com", FALSE }, + { "foo..", "foo", FALSE }, + { NULL } +}; + +static svn_error_t * +test_cert_match_dns_identity(apr_pool_t *pool) +{ + return run_cert_match_dns_tests(cert_match_dns_tests, pool); +} + +/* This test table implements results that should happen if we supported + * RFC 6125 s. 6.4.3 Rule 3. We don't so it's expected to fail for now. */ +static struct cert_match_dns_test rule3_tests[] = { + { "baz*.example.net", "baz1.example.net", TRUE }, + { "*baz.example.net", "foobaz.example.net", TRUE }, + { "b*z.example.net", "buuz.example.net", TRUE }, + { "b*z.example.net", "bz.example.net", FALSE }, /* presume wildcard can't + match nothing */ + { "baz*.example.net", "baz.example.net", FALSE }, + { "*baz.example.net", "baz.example.net", FALSE }, + { "b*z.example.net", "buuzuuz.example.net", TRUE }, /* presume wildcard + should be greedy */ + { NULL } +}; + +static svn_error_t * +test_rule3(apr_pool_t *pool) +{ + return run_cert_match_dns_tests(rule3_tests, pool); +} + /* The test table. */ @@ -2861,14 +2908,8 @@ struct svn_test_descriptor_t test_funcs[] = "test svn_uri_get_longest_ancestor"), SVN_TEST_PASS2(test_dirent_is_child, "test svn_dirent_is_child"), - SVN_TEST_PASS2(test_relpath_is_child, - "test svn_relpath_is_child"), - SVN_TEST_PASS2(test_uri_is_child, - "test svn_uri_is_child"), SVN_TEST_PASS2(test_dirent_is_ancestor, "test svn_dirent_is_ancestor"), - SVN_TEST_PASS2(test_relpath_is_ancestor, - "test svn_relpath_is_ancestor"), SVN_TEST_PASS2(test_uri_is_ancestor, "test svn_uri_is_ancestor"), SVN_TEST_PASS2(test_dirent_skip_ancestor, @@ -2905,11 +2946,15 @@ struct svn_test_descriptor_t test_funcs[] = "test svn_fspath__is_canonical"), SVN_TEST_PASS2(test_fspath_join, "test svn_fspath__join"), - SVN_TEST_PASS2(test_fspath_is_child, - "test svn_fspath__is_child"), + SVN_TEST_PASS2(test_fspath_skip_ancestor, + "test svn_fspath__skip_ancestor"), SVN_TEST_PASS2(test_fspath_dirname_basename_split, "test svn_fspath__dirname/basename/split"), SVN_TEST_PASS2(test_fspath_get_longest_ancestor, "test svn_fspath__get_longest_ancestor"), + SVN_TEST_PASS2(test_cert_match_dns_identity, + "test svn_cert__match_dns_identity"), + SVN_TEST_XFAIL2(test_rule3, + "test match with RFC 6125 s. 6.4.3 Rule 3"), SVN_TEST_NULL }; diff --git a/subversion/tests/libsvn_subr/error-code-test.c b/subversion/tests/libsvn_subr/error-code-test.c new file mode 100644 index 0000000..e996616 --- /dev/null +++ b/subversion/tests/libsvn_subr/error-code-test.c @@ -0,0 +1,83 @@ +/* + * error-code-test.c -- tests for error codes + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + +#include <stdio.h> +#include <string.h> +#include <apr_general.h> + +#include "svn_error.h" + +/* Duplicate of the same typedef in libsvn_subr/error.c */ +typedef struct err_defn { + svn_errno_t errcode; /* 160004 */ + const char *errname; /* SVN_ERR_FS_CORRUPT */ + const char *errdesc; /* default message */ +} err_defn; + +/* To understand what is going on here, read svn_error_codes.h. */ +#define SVN_ERROR_BUILD_ARRAY +#include "svn_error_codes.h" + +#include "../svn_test.h" + +#define NUM_ERRORS (sizeof(error_table)/sizeof(error_table[0])) + +static svn_error_t * +check_error_codes_unique(apr_pool_t *pool) +{ + int i; + struct err_defn e = error_table[0]; + + /* Ensure error codes are strictly monotonically increasing. */ + for (i = 1; i < NUM_ERRORS; i++) + { + struct err_defn e2 = error_table[i]; + + /* Don't fail the test if there is an odd number of errors. + * The error array's sentinel has an error code of zero. */ + if (i == NUM_ERRORS - 1 && e2.errcode == 0) + break; + + /* SVN_ERR_WC_NOT_DIRECTORY is an alias for SVN_ERR_WC_NOT_WORKING_COPY + * and shares the same error code. */ + if (e.errcode != SVN_ERR_WC_NOT_DIRECTORY && + e.errcode >= e2.errcode) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Error 0x%x (%s) is not < 0x%x (%s)\n", + e.errcode, e.errdesc, e2.errcode, e2.errdesc); + e = e2; + } + + return SVN_NO_ERROR; +} + + +/* The test table. */ + +struct svn_test_descriptor_t test_funcs[] = + { + SVN_TEST_NULL, + SVN_TEST_PASS2(check_error_codes_unique, + "check that error codes are unique"), + SVN_TEST_NULL + }; diff --git a/subversion/tests/libsvn_subr/error-test.c b/subversion/tests/libsvn_subr/error-test.c index a19af72..18dacde 100644 --- a/subversion/tests/libsvn_subr/error-test.c +++ b/subversion/tests/libsvn_subr/error-test.c @@ -125,8 +125,9 @@ test_error_purge_tracing(apr_pool_t *pool) svn_error_malfunction_handler_t orig_handler; /* For this test, use a random error status. */ - err = svn_error_create(SVN_ERR_BAD_UUID, NULL, SVN_ERR__TRACED); + err = svn_error_create(SVN_ERR_BAD_UUID, NULL, ""); err = svn_error_trace(err); + err->child->message = err->message; /* Register a malfunction handler that doesn't call abort() to check that a new error chain with an assertion error is @@ -181,6 +182,46 @@ test_error_purge_tracing(apr_pool_t *pool) return SVN_NO_ERROR; } +static svn_error_t * +test_error_symbolic_name(apr_pool_t *pool) +{ + struct { + svn_errno_t errcode; + const char *errname; + } errors[] = { + { SVN_ERR_BAD_CONTAINING_POOL, "SVN_ERR_BAD_CONTAINING_POOL" }, + { SVN_ERR_BAD_FILENAME, "SVN_ERR_BAD_FILENAME" }, + { SVN_ERR_XML_ATTRIB_NOT_FOUND, "SVN_ERR_XML_ATTRIB_NOT_FOUND" }, + { SVN_ERR_ENTRY_NOT_FOUND, "SVN_ERR_ENTRY_NOT_FOUND" }, + { SVN_ERR_ENTRY_CATEGORY_START + 1, NULL }, /* unused slot */ + { SVN_ERR_ENTRY_EXISTS, "SVN_ERR_ENTRY_EXISTS" }, + { SVN_ERR_ASSERTION_ONLY_TRACING_LINKS, "SVN_ERR_ASSERTION_ONLY_TRACING_LINKS" }, + { SVN_ERR_FS_CORRUPT, "SVN_ERR_FS_CORRUPT" }, + /* The following two error codes can return either of their names + as the string. For simplicity, test what the current implementation + returns; but if it starts returning "SVN_ERR_WC_NOT_DIRECTORY", + that's fine (and permitted by the API contract). */ + { SVN_ERR_WC_NOT_DIRECTORY, "SVN_ERR_WC_NOT_WORKING_COPY" }, + { SVN_ERR_WC_NOT_WORKING_COPY, "SVN_ERR_WC_NOT_WORKING_COPY" }, + /* Test an implementation detail. */ + { SVN_ERR_BAD_CATEGORY_START, "SVN_ERR_BAD_CONTAINING_POOL" }, + /* Test non-errors. */ + { 1, NULL }, + { SVN_ERR_WC_CATEGORY_START - 1, NULL }, + /* Whitebox-test exceptional cases. */ + { SVN_WARNING, "SVN_WARNING" }, + { 0, "SVN_NO_ERROR" } + /* No sentinel. */ + }; + int i; + + for (i = 0; i < sizeof(errors) / sizeof(errors[0]); i++) + SVN_TEST_STRING_ASSERT(svn_error_symbolic_name(errors[i].errcode), + errors[i].errname); + + return SVN_NO_ERROR; +} + /* The test table. */ @@ -191,5 +232,7 @@ struct svn_test_descriptor_t test_funcs[] = "test svn_error_root_cause"), SVN_TEST_PASS2(test_error_purge_tracing, "test svn_error_purge_tracing"), + SVN_TEST_PASS2(test_error_symbolic_name, + "test svn_error_symbolic_name"), SVN_TEST_NULL }; diff --git a/subversion/tests/libsvn_subr/io-test.c b/subversion/tests/libsvn_subr/io-test.c new file mode 100644 index 0000000..82e8630 --- /dev/null +++ b/subversion/tests/libsvn_subr/io-test.c @@ -0,0 +1,527 @@ +/* io-test.c --- tests for some i/o functions + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <stdio.h> + +#include <apr.h> + +#include "svn_pools.h" +#include "svn_string.h" +#include "private/svn_skel.h" + +#include "../svn_test.h" +#include "../svn_test_fs.h" + + +/* Helpers to create the test data directory. */ + +#define TEST_DIR "io-test-temp" + +/* The definition for the test data files. */ +struct test_file_definition_t + { + /* The name of the test data file. */ + const char* const name; + + /* The string needs to contain up to 5 bytes, they + * are interpreded as: + * - first byte + * - filler between first and medium byte + * - medium byte (the byte in the middle of the file) + * - filler between medium and last byte + * - last byte. + * If the string is shorter than the file length, + * the test will fail. */ + const char* const data; + + /* The size of the file actually to create. */ + const apr_off_t size; + + /* The created path of the file. Will be filled in + * by create_test_file() */ + char* created_path; + }; + +struct test_file_definition_t test_file_definitions[] = + { + {"empty", "", 0}, + {"single_a", "a", 1}, + {"single_b", "b", 1}, + {"hundred_a", "aaaaa", 100}, + {"hundred_b", "bbbbb", 100}, + {"hundred_b1", "baaaa", 100}, + {"hundred_b2", "abaaa", 100}, + {"hundred_b3", "aabaa", 100}, + {"hundred_b4", "aaaba", 100}, + {"hundred_b5", "aaaab", 100}, + {"chunk_minus_one_a", "aaaaa", SVN__STREAM_CHUNK_SIZE - 1}, + {"chunk_minus_one_b1", "baaaa", SVN__STREAM_CHUNK_SIZE - 1}, + {"chunk_minus_one_b2", "abaaa", SVN__STREAM_CHUNK_SIZE - 1}, + {"chunk_minus_one_b3", "aabaa", SVN__STREAM_CHUNK_SIZE - 1}, + {"chunk_minus_one_b4", "aaaba", SVN__STREAM_CHUNK_SIZE - 1}, + {"chunk_minus_one_b5", "aaaab", SVN__STREAM_CHUNK_SIZE - 1}, + {"chunk_a", "aaaaa", SVN__STREAM_CHUNK_SIZE}, + {"chunk_b1", "baaaa", SVN__STREAM_CHUNK_SIZE}, + {"chunk_b2", "abaaa", SVN__STREAM_CHUNK_SIZE}, + {"chunk_b3", "aabaa", SVN__STREAM_CHUNK_SIZE}, + {"chunk_b4", "aaaba", SVN__STREAM_CHUNK_SIZE}, + {"chunk_b5", "aaaab", SVN__STREAM_CHUNK_SIZE}, + {"chunk_plus_one_a", "aaaaa", SVN__STREAM_CHUNK_SIZE + 1}, + {"chunk_plus_one_b1", "baaaa", SVN__STREAM_CHUNK_SIZE + 1}, + {"chunk_plus_one_b2", "abaaa", SVN__STREAM_CHUNK_SIZE + 1}, + {"chunk_plus_one_b3", "aabaa", SVN__STREAM_CHUNK_SIZE + 1}, + {"chunk_plus_one_b4", "aaaba", SVN__STREAM_CHUNK_SIZE + 1}, + {"chunk_plus_one_b5", "aaaab", SVN__STREAM_CHUNK_SIZE + 1}, + {"twochunk_minus_one_a", "aaaaa", SVN__STREAM_CHUNK_SIZE*2 - 1}, + {"twochunk_minus_one_b1", "baaaa", SVN__STREAM_CHUNK_SIZE*2 - 1}, + {"twochunk_minus_one_b2", "abaaa", SVN__STREAM_CHUNK_SIZE*2 - 1}, + {"twochunk_minus_one_b3", "aabaa", SVN__STREAM_CHUNK_SIZE*2 - 1}, + {"twochunk_minus_one_b4", "aaaba", SVN__STREAM_CHUNK_SIZE*2 - 1}, + {"twochunk_minus_one_b5", "aaaab", SVN__STREAM_CHUNK_SIZE*2 - 1}, + {"twochunk_a", "aaaaa", SVN__STREAM_CHUNK_SIZE*2}, + {"twochunk_b1", "baaaa", SVN__STREAM_CHUNK_SIZE*2}, + {"twochunk_b2", "abaaa", SVN__STREAM_CHUNK_SIZE*2}, + {"twochunk_b3", "aabaa", SVN__STREAM_CHUNK_SIZE*2}, + {"twochunk_b4", "aaaba", SVN__STREAM_CHUNK_SIZE*2}, + {"twochunk_b5", "aaaab", SVN__STREAM_CHUNK_SIZE*2}, + {"twochunk_plus_one_a", "aaaaa", SVN__STREAM_CHUNK_SIZE*2 + 1}, + {"twochunk_plus_one_b1", "baaaa", SVN__STREAM_CHUNK_SIZE*2 + 1}, + {"twochunk_plus_one_b2", "abaaa", SVN__STREAM_CHUNK_SIZE*2 + 1}, + {"twochunk_plus_one_b3", "aabaa", SVN__STREAM_CHUNK_SIZE*2 + 1}, + {"twochunk_plus_one_b4", "aaaba", SVN__STREAM_CHUNK_SIZE*2 + 1}, + {"twochunk_plus_one_b5", "aaaab", SVN__STREAM_CHUNK_SIZE*2 + 1}, + {0}, + }; + +/* Function to prepare a single test file */ + +static svn_error_t * +create_test_file(struct test_file_definition_t* definition, + apr_pool_t *pool, + apr_pool_t *scratch_pool) +{ + apr_status_t status = 0; + apr_file_t *file_h; + apr_off_t midpos = definition->size / 2; + svn_error_t *err = NULL; + int i; + + if (definition->size < 5) + SVN_ERR_ASSERT(strlen(definition->data) >= (apr_size_t)definition->size); + else + SVN_ERR_ASSERT(strlen(definition->data) >= 5); + + + definition->created_path = svn_dirent_join(TEST_DIR, + definition->name, + pool); + + SVN_ERR(svn_io_file_open(&file_h, + definition->created_path, + (APR_WRITE | APR_CREATE | APR_EXCL | APR_BUFFERED), + APR_OS_DEFAULT, + scratch_pool)); + + for (i=1; i <= definition->size; i += 1) + { + char c; + if (i == 1) + c = definition->data[0]; + else if (i < midpos) + c = definition->data[1]; + else if (i == midpos) + c = definition->data[2]; + else if (i < definition->size) + c = definition->data[3]; + else + c = definition->data[4]; + + status = apr_file_putc(c, file_h); + + if (status) + break; + } + + if (status) + err = svn_error_wrap_apr(status, "Can't write to file '%s'", + definition->name); + + return svn_error_compose_create(err, + svn_io_file_close(file_h, scratch_pool)); +} + +/* Function to prepare the whole set of on-disk files to be compared. */ +static svn_error_t * +create_comparison_candidates(apr_pool_t *scratch_pool) +{ + svn_node_kind_t kind; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + struct test_file_definition_t *candidate; + svn_error_t *err = SVN_NO_ERROR; + + /* If there's already a directory named io-test-temp, delete it. + Doing things this way means that repositories stick around after + a failure for postmortem analysis, but also that tests can be + re-run without cleaning out the repositories created by prior + runs. */ + SVN_ERR(svn_io_check_path(TEST_DIR, &kind, scratch_pool)); + + if (kind == svn_node_dir) + SVN_ERR(svn_io_remove_dir2(TEST_DIR, TRUE, NULL, NULL, scratch_pool)); + else if (kind != svn_node_none) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "There is already a file named '%s'", + TEST_DIR); + + SVN_ERR(svn_io_dir_make(TEST_DIR, APR_OS_DEFAULT, scratch_pool)); + + svn_test_add_dir_cleanup(TEST_DIR); + + for (candidate = test_file_definitions; + candidate->name != NULL; + candidate += 1) + { + svn_pool_clear(iterpool); + err = create_test_file(candidate, scratch_pool, iterpool); + if (err) + break; + } + + svn_pool_destroy(iterpool); + + return err; +} + + +/* Functions to check the 2-way and 3-way file comparison functions. */ + +/* Test 2-way file size checking */ +static svn_error_t * +test_two_file_size_comparison(apr_pool_t *scratch_pool) +{ + struct test_file_definition_t *inner, *outer; + svn_boolean_t actual; + svn_boolean_t expected; + svn_error_t *err = SVN_NO_ERROR; + svn_error_t *cmp_err; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + SVN_ERR(create_comparison_candidates(scratch_pool)); + + for (outer = test_file_definitions; outer->name != NULL; outer += 1) + { +#ifdef SVN_IO_TEST_ALL_PERMUTATIONS + inner = test_file_definitions; +#else + inner = outer; +#endif + for (; inner->name != NULL; inner += 1) + { + svn_pool_clear(iterpool); + + expected = inner->size != outer->size; + + cmp_err = svn_io_filesizes_different_p(&actual, + inner->created_path, + outer->created_path, + iterpool); + + if (cmp_err) + { + err = svn_error_compose_create(err, cmp_err); + } + else if (expected != actual) + { + err = svn_error_compose_create(err, + svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "size comparison problem: '%s' and '%s'", + inner->created_path, + outer->created_path)); + } + } + } + + svn_pool_destroy(iterpool); + return err; +} + + +/* Test 2-way file content checking */ +static svn_error_t * +test_two_file_content_comparison(apr_pool_t *scratch_pool) +{ + struct test_file_definition_t *inner, *outer; + svn_boolean_t actual; + svn_boolean_t expected; + svn_error_t *err = SVN_NO_ERROR; + svn_error_t *cmp_err; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + SVN_ERR(create_comparison_candidates(scratch_pool)); + + for (outer = test_file_definitions; outer->name != NULL; outer += 1) + { +#ifdef SVN_IO_TEST_ALL_PERMUTATIONS + inner = test_file_definitions; +#else + inner = outer; +#endif + for (; inner->name != NULL; inner += 1) + { + svn_pool_clear(iterpool); + + expected = inner->size == outer->size + && strcmp(inner->data, outer->data) == 0; + + cmp_err = svn_io_files_contents_same_p(&actual, + inner->created_path, + outer->created_path, + iterpool); + + if (cmp_err) + { + err = svn_error_compose_create(err, cmp_err); + } + else + { + if (expected != actual) + err = svn_error_compose_create(err, + svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "content comparison problem: '%s' and '%s'", + inner->created_path, + outer->created_path)); + } + } + } + + svn_pool_destroy(iterpool); + return err; +} + + +/* Test 3-way file size checking */ +static svn_error_t * +test_three_file_size_comparison(apr_pool_t *scratch_pool) +{ + struct test_file_definition_t *inner, *middle, *outer; + svn_boolean_t actual12, actual23, actual13; + svn_boolean_t expected12, expected23, expected13; + svn_error_t *err = SVN_NO_ERROR; + svn_error_t *cmp_err; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + SVN_ERR(create_comparison_candidates(scratch_pool)); + + for (outer = test_file_definitions; outer->name != NULL; outer += 1) + { +#ifdef SVN_IO_TEST_ALL_PERMUTATIONS + middle = test_file_definitions; +#else + middle = outer; +#endif + for (; middle->name != NULL; middle += 1) + { +#ifdef SVN_IO_TEST_ALL_PERMUTATIONS + inner = test_file_definitions; +#else + inner = middle; +#endif + for (; inner->name != NULL; inner += 1) + { + svn_pool_clear(iterpool); + + expected12 = inner->size != middle->size; + expected23 = middle->size != outer->size; + expected13 = inner->size != outer->size; + + cmp_err = svn_io_filesizes_three_different_p(&actual12, + &actual23, + &actual13, + inner->created_path, + middle->created_path, + outer->created_path, + iterpool); + + if (cmp_err) + { + err = svn_error_compose_create(err, cmp_err); + } + else + { + if (expected12 != actual12) + err = svn_error_compose_create(err, + svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "size comparison problem: '%s' and '%s'", + inner->created_path, + middle->created_path)); + + if (expected23 != actual23) + err = svn_error_compose_create(err, + svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "size comparison problem: '%s' and '%s'", + middle->created_path, + outer->created_path)); + + if (expected13 != actual13) + err = svn_error_compose_create(err, + svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "size comparison problem: '%s' and '%s'", + inner->created_path, + outer->created_path)); + } + } + } + } + + svn_pool_destroy(iterpool); + + return err; +} + + +/* Test 3-way file content checking */ +static svn_error_t * +test_three_file_content_comparison(apr_pool_t *scratch_pool) +{ + struct test_file_definition_t *inner, *middle, *outer; + svn_boolean_t actual12, actual23, actual13; + svn_boolean_t expected12, expected23, expected13; + svn_error_t *err = SVN_NO_ERROR; + svn_error_t *cmp_err; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + SVN_ERR(create_comparison_candidates(scratch_pool)); + + for (outer = test_file_definitions; outer->name != NULL; outer += 1) + { +#ifdef SVN_IO_TEST_ALL_PERMUTATIONS + middle = test_file_definitions; +#else + middle = outer; +#endif + for (; middle->name != NULL; middle += 1) + { +#ifdef SVN_IO_TEST_ALL_PERMUTATIONS + inner = test_file_definitions; +#else + inner = middle; +#endif + for (; inner->name != NULL; inner += 1) + { + svn_pool_clear(iterpool); + + expected12 = outer->size == middle->size + && strcmp(outer->data, middle->data) == 0; + expected23 = middle->size == inner->size + && strcmp(middle->data, inner->data) == 0; + expected13 = outer->size == inner->size + && strcmp(outer->data, inner->data) == 0; + + cmp_err = svn_io_files_contents_three_same_p(&actual12, + &actual23, + &actual13, + outer->created_path, + middle->created_path, + inner->created_path, + iterpool); + + if (cmp_err) + { + err = svn_error_compose_create(err, cmp_err); + } + else + { + if (expected12 != actual12) + err = svn_error_compose_create(err, + svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "size comparison problem: '%s' and '%s'", + inner->created_path, + middle->created_path)); + + if (expected23 != actual23) + err = svn_error_compose_create(err, + svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "size comparison problem: '%s' and '%s'", + middle->created_path, + outer->created_path)); + + if (expected13 != actual13) + err = svn_error_compose_create(err, + svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "size comparison problem: '%s' and '%s'", + inner->created_path, + outer->created_path)); + } + } + } + } + + return err; +} + +static svn_error_t * +read_length_line_shouldnt_loop(apr_pool_t *pool) +{ + const char *tmp_dir; + const char *tmp_file; + char buffer[4]; + apr_size_t buffer_limit = sizeof(buffer); + apr_file_t *f; + + SVN_ERR(svn_dirent_get_absolute(&tmp_dir, "read_length_tmp", 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); + + SVN_ERR(svn_io_write_unique(&tmp_file, tmp_dir, "1234\r\n", 6, + svn_io_file_del_on_pool_cleanup, pool)); + + SVN_ERR(svn_io_file_open(&f, tmp_file, APR_READ, APR_OS_DEFAULT, pool)); + + SVN_TEST_ASSERT_ERROR(svn_io_read_length_line(f, buffer, &buffer_limit, + pool), SVN_ERR_MALFORMED_FILE); + SVN_TEST_ASSERT(buffer_limit == 4); + + return SVN_NO_ERROR; +} + + +/* The test table. */ + +struct svn_test_descriptor_t test_funcs[] = + { + SVN_TEST_NULL, + SVN_TEST_PASS2(test_two_file_size_comparison, + "two file size comparison"), + SVN_TEST_PASS2(test_two_file_content_comparison, + "two file content comparison"), + SVN_TEST_PASS2(test_three_file_size_comparison, + "three file size comparison"), + SVN_TEST_PASS2(test_three_file_content_comparison, + "three file content comparison"), + SVN_TEST_PASS2(read_length_line_shouldnt_loop, + "svn_io_read_length_line() shouldn't loop"), + SVN_TEST_NULL + }; diff --git a/subversion/tests/libsvn_subr/mergeinfo-test.c b/subversion/tests/libsvn_subr/mergeinfo-test.c index 9da9a27..ecfcd02 100644 --- a/subversion/tests/libsvn_subr/mergeinfo-test.c +++ b/subversion/tests/libsvn_subr/mergeinfo-test.c @@ -28,6 +28,7 @@ #define SVN_DEPRECATED +#include "svn_hash.h" #include "svn_pools.h" #include "svn_types.h" #include "svn_mergeinfo.h" @@ -78,7 +79,7 @@ verify_mergeinfo_parse(const char *input, { const void *path; void *val; - apr_array_header_t *ranges; + svn_rangelist_t *ranges; svn_merge_range_t *range; int j; @@ -236,7 +237,7 @@ test_mergeinfo_dup(apr_pool_t *pool) { apr_hash_t *orig_mergeinfo, *copied_mergeinfo; apr_pool_t *subpool; - apr_array_header_t *rangelist; + svn_rangelist_t *rangelist; /* Assure that copies which should be empty turn out that way. */ subpool = svn_pool_create(pool); @@ -399,7 +400,7 @@ range_to_string(svn_merge_range_t *range, verified (e.g. "svn_rangelist_intersect"), while TYPE is a word describing what the ranges being examined represent. */ static svn_error_t * -verify_ranges_match(const apr_array_header_t *actual_rangelist, +verify_ranges_match(const svn_rangelist_t *actual_rangelist, svn_merge_range_t *expected_ranges, int nbr_expected, const char *func_verified, const char *type, apr_pool_t *pool) @@ -436,7 +437,7 @@ verify_mergeinfo_deltas(apr_hash_t *deltas, svn_merge_range_t *expected_deltas, const char *func_verified, const char *type, apr_pool_t *pool) { - apr_array_header_t *rangelist; + svn_rangelist_t *rangelist; if (apr_hash_count(deltas) != 1) /* Deltas on "/trunk" expected. */ @@ -479,12 +480,11 @@ test_diff_mergeinfo(apr_pool_t *pool) static svn_error_t * test_rangelist_reverse(apr_pool_t *pool) { - apr_array_header_t *rangelist; + svn_rangelist_t *rangelist; svn_merge_range_t expected_rangelist[3] = { {10, 9, TRUE}, {7, 4, TRUE}, {3, 2, TRUE} }; - SVN_ERR(svn_mergeinfo_parse(&info1, "/trunk: 3,5-7,10", pool)); - rangelist = apr_hash_get(info1, "/trunk", APR_HASH_KEY_STRING); + SVN_ERR(svn_rangelist__parse(&rangelist, "3,5-7,10", pool)); SVN_ERR(svn_rangelist_reverse(rangelist, pool)); @@ -495,7 +495,7 @@ test_rangelist_reverse(apr_pool_t *pool) static svn_error_t * test_rangelist_intersect(apr_pool_t *pool) { - apr_array_header_t *rangelist1, *rangelist2, *intersection; + svn_rangelist_t *rangelist1, *rangelist2, *intersection; /* Expected intersection when considering inheritance. */ svn_merge_range_t intersection_consider_inheritance[] = @@ -506,11 +506,9 @@ test_rangelist_intersect(apr_pool_t *pool) { {0, 1, TRUE}, {2, 4, TRUE}, {11, 12, TRUE}, {30, 32, FALSE}, {39, 42, TRUE} }; - SVN_ERR(svn_mergeinfo_parse(&info1, "/trunk: 1-6,12-16,30-32*,40-42", pool)); - SVN_ERR(svn_mergeinfo_parse(&info2, "/trunk: 1,3-4*,7,9,11-12,31-34*,38-44", - pool)); - rangelist1 = apr_hash_get(info1, "/trunk", APR_HASH_KEY_STRING); - rangelist2 = apr_hash_get(info2, "/trunk", APR_HASH_KEY_STRING); + SVN_ERR(svn_rangelist__parse(&rangelist1, "1-6,12-16,30-32*,40-42", pool)); + SVN_ERR(svn_rangelist__parse(&rangelist2, "1,3-4*,7,9,11-12,31-34*,38-44", + pool)); /* Check the intersection while considering inheritance twice, reversing the order of the rangelist arguments on the second call to @@ -565,7 +563,7 @@ test_mergeinfo_intersect(apr_pool_t *pool) { svn_merge_range_t expected_intersection[3] = { {0, 1, TRUE}, {2, 4, TRUE}, {11, 12, TRUE} }; - apr_array_header_t *rangelist; + svn_rangelist_t *rangelist; apr_hash_t *intersection; SVN_ERR(svn_mergeinfo_parse(&info1, "/trunk: 1-6,12-16\n/foo: 31", pool)); @@ -605,6 +603,7 @@ test_merge_mergeinfo(apr_pool_t *pool) struct mergeinfo_merge_path_range { const char *path; + int expected_n; svn_merge_range_t expected_rngs[MAX_NBR_MERGEINFO_RANGES]; }; @@ -627,75 +626,75 @@ test_merge_mergeinfo(apr_pool_t *pool) /* One path, intersecting inheritable ranges */ { "/trunk: 5-10", "/trunk: 6", 1, - { {"/trunk", { {4, 10, TRUE} } } } }, + { {"/trunk", 1, { {4, 10, TRUE} } } } }, /* One path, intersecting non-inheritable ranges */ { "/trunk: 5-10*", "/trunk: 6*", 1, - { {"/trunk", { {4, 10, FALSE} } } } }, + { {"/trunk", 1, { {4, 10, FALSE} } } } }, /* One path, intersecting ranges with different inheritability */ { "/trunk: 5-10", "/trunk: 6*", 1, - { {"/trunk", { {4, 10, TRUE} } } } }, + { {"/trunk", 1, { {4, 10, TRUE} } } } }, /* One path, intersecting ranges with different inheritability */ { "/trunk: 5-10*", "/trunk: 6", 1, - { {"/trunk", { {4, 5, FALSE}, {5, 6, TRUE}, {6, 10, FALSE} } } } }, + { {"/trunk", 3, { {4, 5, FALSE}, {5, 6, TRUE}, {6, 10, FALSE} } } } }, /* Adjacent ranges all inheritable ranges */ { "/trunk: 1,3,5-11,13", "/trunk: 2,4,12,14-22", 1, - { {"/trunk", { {0, 22, TRUE} } } } }, + { {"/trunk", 1, { {0, 22, TRUE} } } } }, /* Adjacent ranges all non-inheritable ranges */ { "/trunk: 1*,3*,5-11*,13*", "/trunk: 2*,4*,12*,14-22*", 1, - { {"/trunk", { {0, 22, FALSE} } } } }, + { {"/trunk", 1, { {0, 22, FALSE} } } } }, /* Adjacent ranges differing inheritability */ { "/trunk: 1*,3*,5-11*,13*", "/trunk: 2,4,12,14-22", 1, - { {"/trunk", { { 0, 1, FALSE}, { 1, 2, TRUE}, - { 2, 3, FALSE}, { 3, 4, TRUE}, - { 4, 11, FALSE}, {11, 12, TRUE}, - {12, 13, FALSE}, {13, 22, TRUE} } } } }, + { {"/trunk", 8, { { 0, 1, FALSE}, { 1, 2, TRUE}, + { 2, 3, FALSE}, { 3, 4, TRUE}, + { 4, 11, FALSE}, {11, 12, TRUE}, + {12, 13, FALSE}, {13, 22, TRUE} } } } }, /* Adjacent ranges differing inheritability */ { "/trunk: 1,3,5-11,13", "/trunk: 2*,4*,12*,14-22*", 1, - { {"/trunk", { { 0, 1, TRUE}, { 1, 2, FALSE}, - { 2, 3, TRUE}, { 3, 4, FALSE}, - { 4, 11, TRUE}, {11, 12, FALSE}, - {12, 13, TRUE}, {13, 22, FALSE} } } } }, + { {"/trunk", 8, { { 0, 1, TRUE}, { 1, 2, FALSE}, + { 2, 3, TRUE}, { 3, 4, FALSE}, + { 4, 11, TRUE}, {11, 12, FALSE}, + {12, 13, TRUE}, {13, 22, FALSE} } } } }, /* Two paths all inheritable ranges */ { "/trunk::1: 3,5,7-9,10,11,13,14\n/fred:8-10", "/trunk::1: 1-4,6\n/fred:9-12", 2, - { {"/trunk::1", { {0, 11, TRUE}, {12, 14, TRUE} } }, - {"/fred", { {7, 12, TRUE} } } } }, + { {"/trunk::1", 2, { {0, 11, TRUE}, {12, 14, TRUE} } }, + {"/fred", 1, { {7, 12, TRUE} } } } }, /* Two paths all non-inheritable ranges */ { "/trunk: 3*,5*,7-9*,10*,11*,13*,14*\n/fred:8-10*", "/trunk: 1-4*,6*\n/fred:9-12*", 2, - { {"/trunk", { {0, 11, FALSE}, {12, 14, FALSE} } }, - {"/fred", { {7, 12, FALSE} } } } }, + { {"/trunk", 2, { {0, 11, FALSE}, {12, 14, FALSE} } }, + {"/fred", 1, { {7, 12, FALSE} } } } }, /* Two paths mixed inheritability */ { "/trunk: 3,5*,7-9,10,11*,13,14\n/fred:8-10", "/trunk: 1-4,6\n/fred:9-12*", 2, - { {"/trunk", { { 0, 4, TRUE }, { 4, 5, FALSE}, {5, 10, TRUE}, - {10, 11, FALSE}, {12, 14, TRUE } } }, - {"/fred", { { 7, 10, TRUE }, {10, 12, FALSE} } } } }, + { {"/trunk", 5, { { 0, 4, TRUE }, { 4, 5, FALSE}, {5, 10, TRUE}, + {10, 11, FALSE}, {12, 14, TRUE } } }, + {"/fred", 2, { { 7, 10, TRUE }, {10, 12, FALSE} } } } }, /* A slew of different paths but no ranges to be merged */ { "/trunk: 3,5-9*\n/betty: 2-4", "/fred: 1-18\n/:barney: 1,3-43", 4, - { {"/trunk", { {2, 3, TRUE}, {4, 9, FALSE} } }, - {"/betty", { {1, 4, TRUE} } }, - {"/:barney", { {0, 1, TRUE}, {2, 43, TRUE} } }, - {"/fred", { {0, 18, TRUE} } } } } + { {"/trunk", 2, { {2, 3, TRUE}, {4, 9, FALSE} } }, + {"/betty", 1, { {1, 4, TRUE} } }, + {"/:barney", 2, { {0, 1, TRUE}, {2, 43, TRUE} } }, + {"/fred", 1, { {0, 18, TRUE} } } } } }; for (i = 0; i < NBR_MERGEINFO_MERGES; i++) @@ -714,7 +713,7 @@ test_merge_mergeinfo(apr_pool_t *pool) SVN_ERR(svn_mergeinfo_to_string(&info2_starting, info2, pool)); SVN_ERR(svn_mergeinfo_merge(info1, info2, pool)); - if (mergeinfo[i].expected_paths != apr_hash_count(info1)) + if (mergeinfo[i].expected_paths != (int)apr_hash_count(info1)) return fail(pool, "Wrong number of paths in merged mergeinfo"); /* Check that info2 remained unchanged. */ @@ -730,36 +729,18 @@ test_merge_mergeinfo(apr_pool_t *pool) for (j = 0; j < mergeinfo[i].expected_paths; j++) { - int k; - apr_array_header_t *rangelist = + svn_rangelist_t *rangelist = apr_hash_get(info1, mergeinfo[i].path_rngs[j].path, APR_HASH_KEY_STRING); if (!rangelist) return fail(pool, "Missing path '%s' in merged mergeinfo", - mergeinfo[i].path_rngs->path); - for (k = 0; k < rangelist->nelts; k++) - { - svn_merge_range_t *ranges = - APR_ARRAY_IDX(rangelist, k, svn_merge_range_t *); - if (ranges->start - != mergeinfo[i].path_rngs[j].expected_rngs[k].start - || ranges->end - != mergeinfo[i].path_rngs[j].expected_rngs[k].end - || ranges->inheritable - != mergeinfo[i].path_rngs[j].expected_rngs[k].inheritable) - return fail( - pool, - "Range'%i-%i%s' not found in merged mergeinfo", - mergeinfo[i].path_rngs->expected_rngs[k].start, - mergeinfo[i].path_rngs->expected_rngs[k].end, - mergeinfo[i].path_rngs->expected_rngs[k].inheritable - ? "" : "*"); - } - /* Were more ranges expected? */ - if (k < MAX_NBR_MERGEINFO_RANGES - && mergeinfo[i].path_rngs[j].expected_rngs[k].start != 0) - return fail(pool, - "Not all expected ranges found in merged mergeinfo"); + mergeinfo[i].path_rngs[j].path); + SVN_ERR(verify_ranges_match( + rangelist, + mergeinfo[i].path_rngs[j].expected_rngs, + mergeinfo[i].path_rngs[j].expected_n, + apr_psprintf(pool, "svn_rangelist_merge case %i:%i", i, j), + "merge", pool)); } } @@ -771,7 +752,7 @@ test_remove_rangelist(apr_pool_t *pool) { int i, j; svn_error_t *err, *child_err; - apr_array_header_t *output, *eraser, *whiteboard; + svn_rangelist_t *output, *eraser, *whiteboard; /* Struct for svn_rangelist_remove test data. Parse WHITEBOARD and ERASER to hashes and then get the rangelist for @@ -800,39 +781,39 @@ test_remove_rangelist(apr_pool_t *pool) struct rangelist_remove_test_data test_data[SIZE_OF_RANGE_REMOVE_TEST_ARRAY] = { /* Eraser is a proper subset of whiteboard */ - {"/A: 1-44", "/A: 5", 2, { {0, 4, TRUE }, {5, 44, TRUE }}, - 2, { {0, 4, TRUE }, {5, 44, TRUE }}}, - {"/A: 1-44*", "/A: 5", 1, { {0, 44, FALSE} }, - 2, { {0, 4, FALSE}, {5, 44, FALSE}}}, - {"/A: 1-44", "/A: 5*", 1, { {0, 44, TRUE } }, - 2, { {0, 4, TRUE }, {5, 44, TRUE }}}, - {"/A: 1-44*", "/A: 5*", 2, { {0, 4, FALSE}, {5, 44, FALSE}}, - 2, { {0, 4, FALSE}, {5, 44, FALSE}}}, + {"1-44", "5", 2, { {0, 4, TRUE }, {5, 44, TRUE }}, + 2, { {0, 4, TRUE }, {5, 44, TRUE }}}, + {"1-44*", "5", 1, { {0, 44, FALSE} }, + 2, { {0, 4, FALSE}, {5, 44, FALSE}}}, + {"1-44", "5*", 1, { {0, 44, TRUE } }, + 2, { {0, 4, TRUE }, {5, 44, TRUE }}}, + {"1-44*", "5*", 2, { {0, 4, FALSE}, {5, 44, FALSE}}, + 2, { {0, 4, FALSE}, {5, 44, FALSE}}}, /* Non-intersecting ranges...nothing is removed */ - {"/A: 2-9,14-19", "/A: 12", 2, { {1, 9, TRUE }, {13, 19, TRUE }}, - 2, { {1, 9, TRUE }, {13, 19, TRUE }}}, - {"/A: 2-9*,14-19*", "/A: 12", 2, { {1, 9, FALSE}, {13, 19, FALSE}}, - 2, { {1, 9, FALSE}, {13, 19, FALSE}}}, - {"/A: 2-9,14-19", "/A: 12*", 2, { {1, 9, TRUE }, {13, 19, TRUE }}, - 2, { {1, 9, TRUE }, {13, 19, TRUE }}}, - {"/A: 2-9*,14-19*", "/A: 12*", 2, { {1, 9, FALSE}, {13, 19, FALSE}}, - 2, { {1, 9, FALSE}, {13, 19, FALSE}}}, + {"2-9,14-19", "12", 2, { {1, 9, TRUE }, {13, 19, TRUE }}, + 2, { {1, 9, TRUE }, {13, 19, TRUE }}}, + {"2-9*,14-19*", "12", 2, { {1, 9, FALSE}, {13, 19, FALSE}}, + 2, { {1, 9, FALSE}, {13, 19, FALSE}}}, + {"2-9,14-19", "12*", 2, { {1, 9, TRUE }, {13, 19, TRUE }}, + 2, { {1, 9, TRUE }, {13, 19, TRUE }}}, + {"2-9*,14-19*", "12*", 2, { {1, 9, FALSE}, {13, 19, FALSE}}, + 2, { {1, 9, FALSE}, {13, 19, FALSE}}}, /* Eraser overlaps whiteboard */ - {"/A: 1,9-17", "/A: 12-20", 2, { {0, 1, TRUE }, {8, 11, TRUE }}, - 2, { {0, 1, TRUE }, {8, 11, TRUE }}}, - {"/A: 1,9-17*", "/A: 12-20", 2, { {0, 1, TRUE }, {8, 17, FALSE}}, - 2, { {0, 1, TRUE }, {8, 11, FALSE}}}, - {"/A: 1,9-17", "/A: 12-20*", 2, { {0, 1, TRUE }, {8, 17, TRUE }}, - 2, { {0, 1, TRUE }, {8, 11, TRUE }}}, - {"/A: 1,9-17*", "/A: 12-20*", 2, { {0, 1, TRUE }, {8, 11, FALSE}}, - 2, { {0, 1, TRUE }, {8, 11, FALSE}}}, - /* Empty mergeinfo (i.e. empty rangelist) */ - {"", "", 0, { {0, 0, FALSE}}, - 0, { {0, 0, FALSE}}}, - {"", "/A: 5-8,10-100", 0, { {0, 0, FALSE}}, - 0, { {0, 0, FALSE}}}, - {"/A: 5-8,10-100", "", 2, { {4, 8, TRUE }, {9, 100, TRUE }}, - 2, { {4, 8, TRUE }, {9, 100, TRUE }}} + {"1,9-17", "12-20", 2, { {0, 1, TRUE }, {8, 11, TRUE }}, + 2, { {0, 1, TRUE }, {8, 11, TRUE }}}, + {"1,9-17*", "12-20", 2, { {0, 1, TRUE }, {8, 17, FALSE}}, + 2, { {0, 1, TRUE }, {8, 11, FALSE}}}, + {"1,9-17", "12-20*", 2, { {0, 1, TRUE }, {8, 17, TRUE }}, + 2, { {0, 1, TRUE }, {8, 11, TRUE }}}, + {"1,9-17*", "12-20*", 2, { {0, 1, TRUE }, {8, 11, FALSE}}, + 2, { {0, 1, TRUE }, {8, 11, FALSE}}}, + /* Empty rangelist */ + {"", "", 0, { {0, 0, FALSE}}, + 0, { {0, 0, FALSE}}}, + {"", "5-8,10-100", 0, { {0, 0, FALSE}}, + 0, { {0, 0, FALSE}}}, + {"5-8,10-100", "", 2, { {4, 8, TRUE }, {9, 100, TRUE }}, + 2, { {4, 8, TRUE }, {9, 100, TRUE }}} }; err = child_err = SVN_NO_ERROR; @@ -847,10 +828,8 @@ test_remove_rangelist(apr_pool_t *pool) svn_string_t *whiteboard_starting; svn_string_t *whiteboard_ending; - SVN_ERR(svn_mergeinfo_parse(&info1, (test_data[i]).eraser, pool)); - SVN_ERR(svn_mergeinfo_parse(&info2, (test_data[i]).whiteboard, pool)); - eraser = apr_hash_get(info1, "/A", APR_HASH_KEY_STRING); - whiteboard = apr_hash_get(info2, "/A", APR_HASH_KEY_STRING); + SVN_ERR(svn_rangelist__parse(&eraser, test_data[i].eraser, pool)); + SVN_ERR(svn_rangelist__parse(&whiteboard, test_data[i].whiteboard, pool)); /* Represent empty mergeinfo with an empty rangelist. */ if (eraser == NULL) @@ -960,7 +939,7 @@ randomly_fill_rev_array(svn_boolean_t *revs) /* Set *RANGELIST to a rangelist representing the revisions that are marked * with TRUE in the array REVS[RANDOM_REV_ARRAY_LENGTH]. */ static svn_error_t * -rev_array_to_rangelist(apr_array_header_t **rangelist, +rev_array_to_rangelist(svn_rangelist_t **rangelist, svn_boolean_t *revs, apr_pool_t *pool) { @@ -1002,7 +981,7 @@ test_rangelist_remove_randomly(apr_pool_t *pool) svn_boolean_t first_revs[RANDOM_REV_ARRAY_LENGTH], second_revs[RANDOM_REV_ARRAY_LENGTH], expected_revs[RANDOM_REV_ARRAY_LENGTH]; - apr_array_header_t *first_rangelist, *second_rangelist, + svn_rangelist_t *first_rangelist, *second_rangelist, *expected_rangelist, *actual_rangelist; /* There will be at most RANDOM_REV_ARRAY_LENGTH ranges in expected_rangelist. */ @@ -1060,7 +1039,7 @@ test_rangelist_intersect_randomly(apr_pool_t *pool) svn_boolean_t first_revs[RANDOM_REV_ARRAY_LENGTH], second_revs[RANDOM_REV_ARRAY_LENGTH], expected_revs[RANDOM_REV_ARRAY_LENGTH]; - apr_array_header_t *first_rangelist, *second_rangelist, + svn_rangelist_t *first_rangelist, *second_rangelist, *expected_rangelist, *actual_rangelist; /* There will be at most RANDOM_REV_ARRAY_LENGTH ranges in expected_rangelist. */ @@ -1127,7 +1106,7 @@ test_remove_mergeinfo(apr_pool_t *pool) static svn_error_t * test_rangelist_to_string(apr_pool_t *pool) { - apr_array_header_t *result; + svn_rangelist_t *result; svn_string_t *output; svn_string_t *expected = svn_string_create("3,5,7-11,13-14", pool); @@ -1139,7 +1118,7 @@ test_rangelist_to_string(apr_pool_t *pool) SVN_ERR(svn_rangelist_to_string(&output, result, pool)); - if (svn_string_compare(expected, output) != TRUE) + if (!svn_string_compare(expected, output)) return fail(pool, "Rangelist string not what we expected"); return SVN_NO_ERROR; @@ -1156,7 +1135,7 @@ test_mergeinfo_to_string(apr_pool_t *pool) SVN_ERR(svn_mergeinfo_to_string(&output, info1, pool)); - if (svn_string_compare(expected, output) != TRUE) + if (!svn_string_compare(expected, output)) return fail(pool, "Mergeinfo string not what we expected"); /* Manually construct some mergeinfo with relative path @@ -1172,7 +1151,7 @@ test_mergeinfo_to_string(apr_pool_t *pool) apr_hash_get(info1, "/trunk", APR_HASH_KEY_STRING)); SVN_ERR(svn_mergeinfo_to_string(&output, info2, pool)); - if (svn_string_compare(expected, output) != TRUE) + if (!svn_string_compare(expected, output)) return fail(pool, "Mergeinfo string not what we expected"); return SVN_NO_ERROR; @@ -1184,7 +1163,7 @@ test_rangelist_merge(apr_pool_t *pool) { int i; svn_error_t *err, *child_err; - apr_array_header_t *rangelist1, *rangelist2; + svn_rangelist_t *rangelist1, *rangelist2; /* Struct for svn_rangelist_merge test data. Similar to mergeinfo_merge_test_data struct in svn_mergeinfo_merge() test. */ @@ -1201,118 +1180,118 @@ test_rangelist_merge(apr_pool_t *pool) struct rangelist_merge_test_data test_data[SIZE_OF_RANGE_MERGE_TEST_ARRAY] = { /* Non-intersecting ranges */ - {"/A: 1-44", "/A: 70-101", 2, {{ 0, 44, TRUE }, {69, 101, TRUE }}}, - {"/A: 1-44*", "/A: 70-101", 2, {{ 0, 44, FALSE}, {69, 101, TRUE }}}, - {"/A: 1-44", "/A: 70-101*", 2, {{ 0, 44, TRUE }, {69, 101, FALSE}}}, - {"/A: 1-44*", "/A: 70-101*", 2, {{ 0, 44, FALSE}, {69, 101, FALSE}}}, - {"/A: 70-101", "/A: 1-44", 2, {{ 0, 44, TRUE }, {69, 101, TRUE }}}, - {"/A: 70-101*", "/A: 1-44", 2, {{ 0, 44, TRUE }, {69, 101, FALSE}}}, - {"/A: 70-101", "/A: 1-44*", 2, {{ 0, 44, FALSE}, {69, 101, TRUE }}}, - {"/A: 70-101*", "/A: 1-44*", 2, {{ 0, 44, FALSE}, {69, 101, FALSE}}}, + {"1-44", "70-101", 2, {{ 0, 44, TRUE }, {69, 101, TRUE }}}, + {"1-44*", "70-101", 2, {{ 0, 44, FALSE}, {69, 101, TRUE }}}, + {"1-44", "70-101*", 2, {{ 0, 44, TRUE }, {69, 101, FALSE}}}, + {"1-44*", "70-101*", 2, {{ 0, 44, FALSE}, {69, 101, FALSE}}}, + {"70-101", "1-44", 2, {{ 0, 44, TRUE }, {69, 101, TRUE }}}, + {"70-101*", "1-44", 2, {{ 0, 44, TRUE }, {69, 101, FALSE}}}, + {"70-101", "1-44*", 2, {{ 0, 44, FALSE}, {69, 101, TRUE }}}, + {"70-101*", "1-44*", 2, {{ 0, 44, FALSE}, {69, 101, FALSE}}}, /* Intersecting ranges with same starting and ending revisions */ - {"/A: 4-20", "/A: 4-20", 1, {{3, 20, TRUE }}}, - {"/A: 4-20*", "/A: 4-20", 1, {{3, 20, TRUE }}}, - {"/A: 4-20", "/A: 4-20*", 1, {{3, 20, TRUE }}}, - {"/A: 4-20*", "/A: 4-20*", 1, {{3, 20, FALSE}}}, + {"4-20", "4-20", 1, {{3, 20, TRUE }}}, + {"4-20*", "4-20", 1, {{3, 20, TRUE }}}, + {"4-20", "4-20*", 1, {{3, 20, TRUE }}}, + {"4-20*", "4-20*", 1, {{3, 20, FALSE}}}, /* Intersecting ranges with same starting revision */ - {"/A: 6-17", "/A: 6-12", 1, {{5, 17, TRUE}}}, - {"/A: 6-17*", "/A: 6-12", 2, {{5, 12, TRUE }, {12, 17, FALSE}}}, - {"/A: 6-17", "/A: 6-12*", 1, {{5, 17, TRUE }}}, - {"/A: 6-17*", "/A: 6-12*", 1, {{5, 17, FALSE}}}, - {"/A: 6-12", "/A: 6-17", 1, {{5, 17, TRUE }}}, - {"/A: 6-12*", "/A: 6-17", 1, {{5, 17, TRUE }}}, - {"/A: 6-12", "/A: 6-17*", 2, {{5, 12, TRUE }, {12, 17, FALSE}}}, - {"/A: 6-12*", "/A: 6-17*", 1, {{5, 17, FALSE}}}, + {"6-17", "6-12", 1, {{5, 17, TRUE}}}, + {"6-17*", "6-12", 2, {{5, 12, TRUE }, {12, 17, FALSE}}}, + {"6-17", "6-12*", 1, {{5, 17, TRUE }}}, + {"6-17*", "6-12*", 1, {{5, 17, FALSE}}}, + {"6-12", "6-17", 1, {{5, 17, TRUE }}}, + {"6-12*", "6-17", 1, {{5, 17, TRUE }}}, + {"6-12", "6-17*", 2, {{5, 12, TRUE }, {12, 17, FALSE}}}, + {"6-12*", "6-17*", 1, {{5, 17, FALSE}}}, /* Intersecting ranges with same ending revision */ - {"/A: 5-77", "/A: 44-77", 1, {{4, 77, TRUE }}}, - {"/A: 5-77*", "/A: 44-77", 2, {{4, 43, FALSE}, {43, 77, TRUE}}}, - {"/A: 5-77", "/A: 44-77*", 1, {{4, 77, TRUE }}}, - {"/A: 5-77*", "/A: 44-77*", 1, {{4, 77, FALSE}}}, - {"/A: 44-77", "/A: 5-77", 1, {{4, 77, TRUE }}}, - {"/A: 44-77*", "/A: 5-77", 1, {{4, 77, TRUE }}}, - {"/A: 44-77", "/A: 5-77*", 2, {{4, 43, FALSE}, {43, 77, TRUE}}}, - {"/A: 44-77*", "/A: 5-77*", 1, {{4, 77, FALSE}}}, + {"5-77", "44-77", 1, {{4, 77, TRUE }}}, + {"5-77*", "44-77", 2, {{4, 43, FALSE}, {43, 77, TRUE}}}, + {"5-77", "44-77*", 1, {{4, 77, TRUE }}}, + {"5-77*", "44-77*", 1, {{4, 77, FALSE}}}, + {"44-77", "5-77", 1, {{4, 77, TRUE }}}, + {"44-77*", "5-77", 1, {{4, 77, TRUE }}}, + {"44-77", "5-77*", 2, {{4, 43, FALSE}, {43, 77, TRUE}}}, + {"44-77*", "5-77*", 1, {{4, 77, FALSE}}}, /* Intersecting ranges with different starting and ending revision where one range is a proper subset of the other. */ - {"/A: 12-24", "/A: 20-23", 1, {{11, 24, TRUE }}}, - {"/A: 12-24*", "/A: 20-23", 3, {{11, 19, FALSE}, {19, 23, TRUE }, - {23, 24, FALSE}}}, - {"/A: 12-24", "/A: 20-23*", 1, {{11, 24, TRUE }}}, - {"/A: 12-24*", "/A: 20-23*", 1, {{11, 24, FALSE}}}, - {"/A: 20-23", "/A: 12-24", 1, {{11, 24, TRUE }}}, - {"/A: 20-23*", "/A: 12-24", 1, {{11, 24, TRUE }}}, - {"/A: 20-23", "/A: 12-24*", 3, {{11, 19, FALSE}, {19, 23, TRUE }, - {23, 24, FALSE}}}, - {"/A: 20-23*", "/A: 12-24*", 1, {{11, 24, FALSE}}}, + {"12-24", "20-23", 1, {{11, 24, TRUE }}}, + {"12-24*", "20-23", 3, {{11, 19, FALSE}, {19, 23, TRUE }, + {23, 24, FALSE}}}, + {"12-24", "20-23*", 1, {{11, 24, TRUE }}}, + {"12-24*", "20-23*", 1, {{11, 24, FALSE}}}, + {"20-23", "12-24", 1, {{11, 24, TRUE }}}, + {"20-23*", "12-24", 1, {{11, 24, TRUE }}}, + {"20-23", "12-24*", 3, {{11, 19, FALSE}, {19, 23, TRUE }, + {23, 24, FALSE}}}, + {"20-23*", "12-24*", 1, {{11, 24, FALSE}}}, /* Intersecting ranges with different starting and ending revision where neither range is a proper subset of the other. */ - {"/A: 50-73", "/A: 60-99", 1, {{49, 99, TRUE }}}, - {"/A: 50-73*", "/A: 60-99", 2, {{49, 59, FALSE}, {59, 99, TRUE }}}, - {"/A: 50-73", "/A: 60-99*", 2, {{49, 73, TRUE }, {73, 99, FALSE}}}, - {"/A: 50-73*", "/A: 60-99*", 1, {{49, 99, FALSE}}}, - {"/A: 60-99", "/A: 50-73", 1, {{49, 99, TRUE }}}, - {"/A: 60-99*", "/A: 50-73", 2, {{49, 73, TRUE }, {73, 99, FALSE}}}, - {"/A: 60-99", "/A: 50-73*", 2, {{49, 59, FALSE}, {59, 99, TRUE }}}, - {"/A: 60-99*", "/A: 50-73*", 1, {{49, 99, FALSE}}}, + {"50-73", "60-99", 1, {{49, 99, TRUE }}}, + {"50-73*", "60-99", 2, {{49, 59, FALSE}, {59, 99, TRUE }}}, + {"50-73", "60-99*", 2, {{49, 73, TRUE }, {73, 99, FALSE}}}, + {"50-73*", "60-99*", 1, {{49, 99, FALSE}}}, + {"60-99", "50-73", 1, {{49, 99, TRUE }}}, + {"60-99*", "50-73", 2, {{49, 73, TRUE }, {73, 99, FALSE}}}, + {"60-99", "50-73*", 2, {{49, 59, FALSE}, {59, 99, TRUE }}}, + {"60-99*", "50-73*", 1, {{49, 99, FALSE}}}, /* Multiple ranges. */ - {"/A: 1-5,7,12-13", "/A: 2-17", 1, {{0, 17, TRUE }}}, - {"/A: 1-5*,7*,12-13*", "/A: 2-17*", 1, {{0, 17, FALSE}}}, + {"1-5,7,12-13", "2-17", 1, {{0, 17, TRUE }}}, + {"1-5*,7*,12-13*", "2-17*", 1, {{0, 17, FALSE}}}, - {"/A: 1-5,7,12-13", "/A: 2-17*", 6, + {"1-5,7,12-13", "2-17*", 6, {{0, 5, TRUE }, { 5, 6, FALSE}, { 6, 7, TRUE }, {7, 11, FALSE}, {11, 13, TRUE }, {13, 17, FALSE}}}, - {"/A: 1-5*,7*,12-13*", "/A: 2-17", 2, + {"1-5*,7*,12-13*", "2-17", 2, {{0, 1, FALSE}, {1, 17, TRUE }}}, - {"/A: 2-17", "/A: 1-5,7,12-13", 1, {{0, 17, TRUE }}}, - {"/A: 2-17*", "/A: 1-5*,7*,12-13*", 1, {{0, 17, FALSE}}}, + {"2-17", "1-5,7,12-13", 1, {{0, 17, TRUE }}}, + {"2-17*", "1-5*,7*,12-13*", 1, {{0, 17, FALSE}}}, - {"/A: 2-17*", "/A: 1-5,7,12-13", 6, + {"2-17*", "1-5,7,12-13", 6, {{0, 5, TRUE }, { 5, 6, FALSE}, { 6, 7, TRUE }, {7, 11, FALSE}, {11, 13, TRUE }, {13, 17, FALSE}}}, - {"/A: 2-17", "/A: 1-5*,7*,12-13*", 2, + {"2-17", "1-5*,7*,12-13*", 2, {{0, 1, FALSE}, {1, 17, TRUE}}}, - {"/A: 3-4*,10-15,20", "/A: 5-60*", 5, + {"3-4*,10-15,20", "5-60*", 5, {{2, 9, FALSE}, {9, 15, TRUE}, {15, 19, FALSE},{19, 20, TRUE}, {20, 60, FALSE}}}, - {"/A: 5-60*", "/A: 3-4*,10-15,20", 5, + {"5-60*", "3-4*,10-15,20", 5, {{2, 9, FALSE}, {9, 15, TRUE}, {15, 19, FALSE},{19, 20, TRUE}, {20, 60, FALSE}}}, - {"/A: 3-4*,50-100*", "/A: 5-60*", 1, {{2, 100, FALSE}}}, + {"3-4*,50-100*", "5-60*", 1, {{2, 100, FALSE}}}, - {"/A: 5-60*", "/A: 3-4*,50-100*", 1, {{2, 100, FALSE}}}, + {"5-60*", "3-4*,50-100*", 1, {{2, 100, FALSE}}}, - {"/A: 3-4*,50-100", "/A: 5-60*", 2, {{2, 49, FALSE}, {49, 100, TRUE}}}, + {"3-4*,50-100", "5-60*", 2, {{2, 49, FALSE}, {49, 100, TRUE}}}, - {"/A: 5-60*", "/A: 3-4*,50-100", 2, {{2, 49, FALSE}, {49, 100, TRUE}}}, + {"5-60*", "3-4*,50-100", 2, {{2, 49, FALSE}, {49, 100, TRUE}}}, - {"/A: 3-4,50-100*", "/A: 5-60", 2, {{2, 60, TRUE}, {60, 100, FALSE}}}, + {"3-4,50-100*", "5-60", 2, {{2, 60, TRUE}, {60, 100, FALSE}}}, - {"/A: 5-60", "/A: 3-4,50-100*", 2, {{2, 60, TRUE}, {60, 100, FALSE}}}, + {"5-60", "3-4,50-100*", 2, {{2, 60, TRUE}, {60, 100, FALSE}}}, - {"/A: 5,9,11-15,17,200-300,999", "/A: 7-50", 4, + {"5,9,11-15,17,200-300,999", "7-50", 4, {{4, 5, TRUE}, {6, 50, TRUE}, {199, 300, TRUE}, {998, 999, TRUE}}}, /* A rangelist merged with an empty rangelist should equal the non-empty rangelist but in compacted form. */ - {"/A: 1-44,45,46,47-50", "", 1, {{ 0, 50, TRUE }}}, - {"/A: 1,2,3,4,5,6,7,8", "", 1, {{ 0, 8, TRUE }}}, - {"/A: 6-10,12-13,14,15,16-22", "", 2, + {"1-44,45,46,47-50", "", 1, {{ 0, 50, TRUE }}}, + {"1,2,3,4,5,6,7,8", "", 1, {{ 0, 8, TRUE }}}, + {"6-10,12-13,14,15,16-22", "", 2, {{ 5, 10, TRUE }, { 11, 22, TRUE }}}, - {"", "/A: 1-44,45,46,47-50", 1, {{ 0, 50, TRUE }}}, - {"", "/A: 1,2,3,4,5,6,7,8", 1, {{ 0, 8, TRUE }}}, - {"", "/A: 6-10,12-13,14,15,16-22", 2, + {"", "1-44,45,46,47-50", 1, {{ 0, 50, TRUE }}}, + {"", "1,2,3,4,5,6,7,8", 1, {{ 0, 8, TRUE }}}, + {"", "6-10,12-13,14,15,16-22", 2, {{ 5, 10, TRUE }, { 11, 22, TRUE }}}, /* An empty rangelist merged with an empty rangelist is, drum-roll @@ -1325,10 +1304,8 @@ test_rangelist_merge(apr_pool_t *pool) { svn_string_t *rangelist2_starting, *rangelist2_ending; - SVN_ERR(svn_mergeinfo_parse(&info1, (test_data[i]).mergeinfo1, pool)); - SVN_ERR(svn_mergeinfo_parse(&info2, (test_data[i]).mergeinfo2, pool)); - rangelist1 = apr_hash_get(info1, "/A", APR_HASH_KEY_STRING); - rangelist2 = apr_hash_get(info2, "/A", APR_HASH_KEY_STRING); + SVN_ERR(svn_rangelist__parse(&rangelist1, test_data[i].mergeinfo1, pool)); + SVN_ERR(svn_rangelist__parse(&rangelist2, test_data[i].mergeinfo2, pool)); /* Create empty rangelists if necessary. */ if (rangelist1 == NULL) @@ -1383,7 +1360,7 @@ test_rangelist_diff(apr_pool_t *pool) { int i; svn_error_t *err, *child_err; - apr_array_header_t *from, *to, *added, *deleted; + svn_rangelist_t *from, *to, *added, *deleted; /* Structure containing two ranges to diff and the expected output of the diff both when considering and ignoring range inheritance. */ @@ -1412,7 +1389,7 @@ test_rangelist_diff(apr_pool_t *pool) #define SIZE_OF_RANGE_DIFF_TEST_ARRAY 16 /* The actual test data array. - 'from' --> {"/A: 1,5-8", "/A: 1,6,10-12", <-- 'to' + 'from' --> {"1,5-8", "1,6,10-12", <-- 'to' Number of adds when --> 1, { { 9, 12, TRUE } }, considering inheritance @@ -1430,97 +1407,97 @@ test_rangelist_diff(apr_pool_t *pool) struct rangelist_diff_test_data test_data[SIZE_OF_RANGE_DIFF_TEST_ARRAY] = { /* Add and Delete */ - {"/A: 1", "/A: 3", + {"1", "3", 1, { { 2, 3, TRUE } }, 1, { { 0, 1, TRUE } }, 1, { { 2, 3, TRUE } }, 1, { { 0, 1, TRUE } } }, /* Add only */ - {"/A: 1", "/A: 1,3", + {"1", "1,3", 1, { { 2, 3, TRUE } }, 0, { { 0, 0, FALSE } }, 1, { { 2, 3, TRUE } }, 0, { { 0, 0, FALSE } } }, /* Delete only */ - {"/A: 1,3", "/A: 1", + {"1,3", "1", 0, { { 0, 0, FALSE } }, 1, { { 2, 3, TRUE } }, 0, { { 0, 0, FALSE } }, 1, { { 2, 3, TRUE } } }, /* No diff */ - {"/A: 1,3", "/A: 1,3", + {"1,3", "1,3", 0, { { 0, 0, FALSE } }, 0, { { 0, 0, FALSE } }, 0, { { 0, 0, FALSE } }, 0, { { 0, 0, FALSE } } }, - {"/A: 1,3*", "/A: 1,3*", + {"1,3*", "1,3*", 0, { { 0, 0, FALSE } }, 0, { { 0, 0, FALSE } }, 0, { { 0, 0, FALSE } }, 0, { { 0, 0, FALSE } } }, /* Adds and Deletes */ - {"/A: 1,5-8", "/A: 1,6,10-12", + {"1,5-8", "1,6,10-12", 1, { { 9, 12, TRUE } }, 2, { { 4, 5, TRUE }, { 6, 8, TRUE } }, 1, { { 9, 12, TRUE } }, 2, { { 4, 5, TRUE }, { 6, 8, TRUE } } }, - {"/A: 6*", "/A: 6", + {"6*", "6", 1, { { 5, 6, TRUE } }, 1, { { 5, 6, FALSE } }, 0, { { 0, 0, FALSE } }, 0, { { 0, 0, FALSE } } }, /* Intersecting range with different inheritability */ - {"/A: 6", "/A: 6*", + {"6", "6*", 1, { { 5, 6, FALSE } }, 1, { { 5, 6, TRUE } }, 0, { { 0, 0, FALSE } }, 0, { { 0, 0, FALSE } } }, - {"/A: 6*", "/A: 6", + {"6*", "6", 1, { { 5, 6, TRUE } }, 1, { { 5, 6, FALSE } }, 0, { { 0, 0, FALSE } }, 0, { { 0, 0, FALSE } } }, - {"/A: 1,5-8", "/A: 1,6*,10-12", + {"1,5-8", "1,6*,10-12", 2, { { 5, 6, FALSE }, { 9, 12, TRUE } }, 1, { { 4, 8, TRUE } }, 1, { { 9, 12, TRUE } }, 2, { { 4, 5, TRUE }, { 6, 8, TRUE } } }, - {"/A: 1,5-8*", "/A: 1,6,10-12", + {"1,5-8*", "1,6,10-12", 2, { { 5, 6, TRUE }, { 9, 12, TRUE } }, 1, { { 4, 8, FALSE } }, 1, { { 9, 12, TRUE } }, 2, { { 4, 5, FALSE }, { 6, 8, FALSE } } }, /* Empty range diffs */ - {"/A: 3-9", "", + {"3-9", "", 0, { { 0, 0, FALSE } }, 1, { { 2, 9, TRUE } }, 0, { { 0, 0, FALSE } }, 1, { { 2, 9, TRUE } } }, - {"/A: 3-9*", "", + {"3-9*", "", 0, { { 0, 0, FALSE } }, 1, { { 2, 9, FALSE } }, 0, { { 0, 0, FALSE } }, 1, { { 2, 9, FALSE } } }, - {"", "/A: 3-9", + {"", "3-9", 1, { { 2, 9, TRUE } }, 0, { { 0, 0, FALSE } }, 1, { { 2, 9, TRUE } }, 0, { { 0, 0, FALSE } } }, - {"", "/A: 3-9*", + {"", "3-9*", 1, { { 2, 9, FALSE } }, 0, { { 0, 0, FALSE } }, 1, { { 2, 9, FALSE } }, @@ -1537,10 +1514,8 @@ test_rangelist_diff(apr_pool_t *pool) err = child_err = SVN_NO_ERROR; for (i = 0; i < SIZE_OF_RANGE_DIFF_TEST_ARRAY; i++) { - SVN_ERR(svn_mergeinfo_parse(&info1, (test_data[i]).to, pool)); - SVN_ERR(svn_mergeinfo_parse(&info2, (test_data[i]).from, pool)); - to = apr_hash_get(info1, "/A", APR_HASH_KEY_STRING); - from = apr_hash_get(info2, "/A", APR_HASH_KEY_STRING); + SVN_ERR(svn_rangelist__parse(&to, test_data[i].to, pool)); + SVN_ERR(svn_rangelist__parse(&from, test_data[i].from, pool)); /* Represent empty mergeinfo with an empty rangelist. */ if (to == NULL) @@ -1721,7 +1696,7 @@ struct svn_test_descriptor_t test_funcs[] = SVN_TEST_PASS2(test_diff_mergeinfo, "diff of mergeinfo"), SVN_TEST_PASS2(test_merge_mergeinfo, - "merging of mergeinfo hashs"), + "merging of mergeinfo hashes"), SVN_TEST_PASS2(test_mergeinfo_intersect, "intersection of mergeinfo"), SVN_TEST_PASS2(test_rangelist_to_string, diff --git a/subversion/tests/libsvn_subr/named_atomic-test-common.h b/subversion/tests/libsvn_subr/named_atomic-test-common.h new file mode 100644 index 0000000..2ada4ee --- /dev/null +++ b/subversion/tests/libsvn_subr/named_atomic-test-common.h @@ -0,0 +1,245 @@ +/* + * named_atomic-test-common.h: shared function implementations for + * named_atomic-test + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + + + +#include "../svn_test.h" +#include "svn_pools.h" +#include "private/svn_named_atomic.h" + +/* Some constants that we will use in our tests */ + +/* All our atomics start with that name */ +#define ATOMIC_NAME "MyTestAtomic" + +/* Factor used to create non-trivial 64 bit numbers */ +#define HUGE_VALUE 1234567890123456ll + +/* to separate this code from any production environment */ +const char *name_namespace = NULL; +const char *name_namespace1 = NULL; +const char *name_namespace2 = NULL; + +/* data structure containing all information we need to check for + * a) passing some deadline + * b) reaching the maximum iteration number + */ +typedef struct watchdog_t +{ + apr_time_t deadline; + svn_named_atomic__t *atomic_counter; + int iterations; + int call_count; /* don't call apr_time_now() too often '*/ +} watchdog_t; + +/* init the WATCHDOG data structure for checking ATOMIC_COUNTER to reach + * ITERATIONS and for the system time to pass a deadline MAX_DURATION + * microsecs in the future. + */ +static void +init_watchdog(watchdog_t *watchdog, + svn_named_atomic__t *atomic_counter, + int iterations, + apr_time_t max_duration) +{ + watchdog->deadline = apr_time_now() + max_duration; + watchdog->atomic_counter = atomic_counter; + watchdog->iterations = iterations; + watchdog->call_count = 0; +} + +/* test for watchdog conditions */ +static svn_error_t * +check_watchdog(watchdog_t *watchdog, svn_boolean_t *done) +{ + apr_int64_t counter = 0; + + /* check for normal end of loop. + * We are a watchdog, so don't check for errors. */ + *done = FALSE; + svn_error_clear(svn_named_atomic__read(&counter, + watchdog->atomic_counter)); + if (counter >= watchdog->iterations) + { + *done = TRUE; + return SVN_NO_ERROR; + } + + /* Check the system time and indicate when deadline has passed */ + if (++watchdog->call_count > 100) + { + watchdog->call_count = 100; + if (apr_time_now() > watchdog->deadline) + return svn_error_createf(SVN_ERR_TEST_FAILED, + 0, + "Deadline has passed at iteration %d/%d", + (int)counter, watchdog->iterations); + } + + /* no problem so far */ + return SVN_NO_ERROR; +} + +/* "pipeline" test: initialization code executed by the worker with ID 0. + * Pushes COUNT tokens into ATOMIC_OUT and checks for ATOMIC_COUNTER not to + * exceed ITERATIONS (early termination). + */ +static svn_error_t * +test_pipeline_prepare(svn_named_atomic__t *atomic_out, + int count, + watchdog_t *watchdog) +{ + apr_int64_t value = 0; + int i; + svn_boolean_t done = FALSE; + + /* Initialize values in thread 0, pass them along in other threads */ + + for (i = 1; i <= count; ++i) + do + { + /* Generate new token (once the old one has been removed)*/ + SVN_ERR(svn_named_atomic__cmpxchg(&value, + i, + 0, + atomic_out)); + SVN_ERR(check_watchdog(watchdog, &done)); + if (done) return SVN_NO_ERROR; + } + while (value != 0); + + return SVN_NO_ERROR; +} + +/* "pipeline" test: the main loop. Each one of the COUNT workers receives + * data in its ATOMIC_IN and passes it on to ATOMIC_OUT until ATOMIC_COUNTER + * exceeds ITERATIONS. + */ +static svn_error_t * +test_pipeline_loop(svn_named_atomic__t *atomic_in, + svn_named_atomic__t *atomic_out, + svn_named_atomic__t *atomic_counter, + int count, + int iterations, + watchdog_t *watchdog) +{ + apr_int64_t value = 0, old_value, last_value = 0; + apr_int64_t counter; + svn_boolean_t done = FALSE; + + /* Pass the tokens along */ + + do + { + /* Wait for and consume incoming token. */ + do + { + SVN_ERR(svn_named_atomic__write(&value, 0, atomic_in)); + SVN_ERR(check_watchdog(watchdog, &done)); + if (done) return SVN_NO_ERROR; + } + while (value == 0); + + /* All tokes must come in in the same order */ + SVN_TEST_ASSERT((last_value % count) == (value - 1)); + last_value = value; + + /* Wait for the target atomic to become vacant and write the token */ + do + { + SVN_ERR(svn_named_atomic__cmpxchg(&old_value, + value, + 0, + atomic_out)); + SVN_ERR(check_watchdog(watchdog, &done)); + if (done) return SVN_NO_ERROR; + } + while (old_value != 0); + + /* Count the number of operations */ + SVN_ERR(svn_named_atomic__add(&counter, 1, atomic_counter)); + } + while (counter < iterations); + + /* done */ + + return SVN_NO_ERROR; +} + +/* "pipeline" test: worker with ID 0 initializes the data; all workers + * (COUNT in total) have one input and one output bucket that form a ring + * spanning all workers. Each worker passes the value along ITERATIONS times. + */ +static svn_error_t * +test_pipeline(int id, int count, int iterations, apr_pool_t *pool) +{ + svn_atomic_namespace__t *ns; + svn_named_atomic__t *atomic_in; + svn_named_atomic__t *atomic_out; + svn_named_atomic__t *atomic_counter; + svn_error_t *err = SVN_NO_ERROR; + watchdog_t watchdog; + + /* get the two I/O atomics for this thread */ + SVN_ERR(svn_atomic_namespace__create(&ns, name_namespace, pool)); + SVN_ERR(svn_named_atomic__get(&atomic_in, + ns, + apr_pstrcat(pool, + ATOMIC_NAME, + apr_itoa(pool, + id), + NULL), + FALSE)); + SVN_ERR(svn_named_atomic__get(&atomic_out, + ns, + apr_pstrcat(pool, + ATOMIC_NAME, + apr_itoa(pool, + (id + 1) % count), + NULL), + FALSE)); + + /* our iteration counter */ + SVN_ERR(svn_named_atomic__get(&atomic_counter, ns, "counter", FALSE)); + + /* safeguard our execution time. Limit it to 20s */ + init_watchdog(&watchdog, atomic_counter, iterations, 20000000); + + /* fill pipeline */ + if (id == 0) + err = test_pipeline_prepare(atomic_out, count, &watchdog); + + /* Pass the tokens along */ + if (!err) + err = test_pipeline_loop(atomic_in, atomic_out, atomic_counter, + count, iterations, &watchdog); + + /* if we experienced an error, cause everybody to exit */ + if (err) + svn_error_clear(svn_named_atomic__write(NULL, iterations, atomic_counter)); + + /* done */ + + return err; +} diff --git a/subversion/tests/libsvn_subr/named_atomic-test-proc.c b/subversion/tests/libsvn_subr/named_atomic-test-proc.c new file mode 100644 index 0000000..534247c --- /dev/null +++ b/subversion/tests/libsvn_subr/named_atomic-test-proc.c @@ -0,0 +1,86 @@ +/* + * named_atomic-test-proc.c: a collection of svn_named_atomic__t tests + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + +/* ==================================================================== + To add tests, look toward the bottom of this file. +*/ + + +#include <stdio.h> + +/* shared test implementation */ +#include "named_atomic-test-common.h" + +/* Very simple process frame around the actual test code */ +int +main(int argc, const char *argv[]) +{ + svn_boolean_t got_error = FALSE; + apr_pool_t *pool; + svn_error_t *err; + + int id = 0; + int count = 0; + int iterations = 0; + + /* Initialize APR (Apache pools) */ + if (apr_initialize() != APR_SUCCESS) + { + printf("apr_initialize() failed.\n"); + exit(1); + } + + pool = svn_pool_create(NULL); + + /* lean & mean parameter parsing */ + if (argc != 5) + { + if (argc == 1) /* used to test that this executable can be started */ + exit(0); + + printf("Usage: named_atomic-proc-test ID COUNT ITERATIONS NS.\n"); + exit(1); + } + + id = (int)apr_atoi64(argv[1]); + count = (int)apr_atoi64(argv[2]); + iterations = (int)apr_atoi64(argv[3]); + name_namespace = argv[4]; + + /* run test routine */ + + err = test_pipeline(id, count, iterations, pool); + if (err) + { + const char *prefix = apr_psprintf(pool, "Process %d: ", id); + got_error = TRUE; + svn_handle_error2(err, stdout, FALSE, prefix); + svn_error_clear(err); + } + + /* Clean up APR */ + svn_pool_destroy(pool); + apr_terminate(); + + return got_error; +} diff --git a/subversion/tests/libsvn_subr/named_atomic-test.c b/subversion/tests/libsvn_subr/named_atomic-test.c new file mode 100644 index 0000000..05604d2 --- /dev/null +++ b/subversion/tests/libsvn_subr/named_atomic-test.c @@ -0,0 +1,761 @@ +/* + * named_atomic-test.c: a collection of svn_named_atomic__t tests + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + +/* ==================================================================== + To add tests, look toward the bottom of this file. +*/ + + +#include <stdio.h> +#include <apr_file_io.h> + +#include "svn_io.h" + +/* shared test implementation */ +#include "named_atomic-test-common.h" + +/* Name of the worker process executable */ +#define TEST_PROC "named_atomic-proc-test" + +/* number of hardware threads (logical cores) that we may use. + * Will be set to at least 2 - even on unicore machines. */ +static int hw_thread_count = 0; + +/* number of iterations that we should perform on concurrency tests + * (will be calibrated to about 1s runtime)*/ +static int suggested_iterations = 0; + +/* If possible, translate PROC to a global path and set DIRECTORY to + * the current directory. + */ +static svn_error_t * +adjust_proc_path(const char **proc, const char **directory, apr_pool_t *pool) +{ +#ifdef WIN32 + /* Under Windows, the test will not be in the current directory + * and neither will be PROC. Therefore, determine its full path */ + char path [MAX_PATH] = { 0 }; + GetModuleFileNameA(NULL, path, sizeof(path)); + *(strrchr(path, '\\') + 1) = 0; + *proc = apr_pstrcat(pool, path, *proc, ".exe", NULL); + + /* And we need to set the working dir to our working dir to make + * our sub-processes find all DLLs. */ + GetCurrentDirectoryA(sizeof(path), path); + *directory = apr_pstrdup(pool, path); +#endif + + return SVN_NO_ERROR; +} + +/* Returns true if PROC can be found and executed. + */ +static svn_boolean_t +proc_found(const char *proc, apr_pool_t *pool) +{ + static svn_tristate_t result = svn_tristate_unknown; + + if (result == svn_tristate_unknown) + { + svn_error_t *error = SVN_NO_ERROR; + const char * directory = NULL; + + /* all processes and their I/O data */ + apr_proc_t process; + const char * args[2]; + + args[0] = proc; + args[1] = NULL; + svn_error_clear(adjust_proc_path(&args[0], &directory, pool)); + + /* try to start the process */ + error = svn_io_start_cmd3(&process, + directory, /* working directory */ + args[0], + args, + NULL, /* environment */ + FALSE, /* no handle inheritance */ + FALSE, /* no STDIN pipe */ + NULL, + FALSE, /* no STDOUT pipe */ + NULL, + FALSE, /* no STDERR pipe */ + NULL, + pool); + if (!error) + error = svn_io_wait_for_cmd(&process, proc, NULL, NULL, pool); + + result = error ? svn_tristate_false : svn_tristate_true; + svn_error_clear(error); + } + + return result == svn_tristate_true; +} + +/* Remove temporary files from disk. + */ +static apr_status_t +cleanup_test_shm(void *arg) +{ + apr_pool_t *pool = arg; + + svn_error_clear(svn_atomic_namespace__cleanup(name_namespace, pool)); + svn_error_clear(svn_atomic_namespace__cleanup(name_namespace1, pool)); + svn_error_clear(svn_atomic_namespace__cleanup(name_namespace2, pool)); + + return 0; +} + +/* Bring shared memory to a defined state. This is very useful in case of + * lingering problems from previous tests or test runs. + */ +static svn_error_t * +init_test_shm(apr_pool_t *pool) +{ + svn_atomic_namespace__t *ns; + svn_named_atomic__t *atomic; + apr_pool_t *scratch = svn_pool_create(pool); + + if (name_namespace == NULL) + { + apr_pool_t *global_pool = svn_pool_create(NULL); + SVN_ERR(svn_io_open_unique_file3(NULL, + &name_namespace, + NULL, + svn_io_file_del_on_pool_cleanup, + global_pool, + pool)); + SVN_ERR(svn_io_open_unique_file3(NULL, + &name_namespace1, + NULL, + svn_io_file_del_on_pool_cleanup, + global_pool, + pool)); + SVN_ERR(svn_io_open_unique_file3(NULL, + &name_namespace2, + NULL, + svn_io_file_del_on_pool_cleanup, + global_pool, + pool)); + } + + /* skip tests if the current user does not have the required privileges */ + if (!svn_named_atomic__is_supported()) + return svn_error_wrap_apr(SVN_ERR_TEST_SKIPPED, + "user has insufficient privileges"); + + /* destroy temp files after usage */ + + apr_pool_cleanup_register(pool, pool, + cleanup_test_shm, apr_pool_cleanup_null); + + /* get the two I/O atomics for this thread */ + SVN_ERR(svn_atomic_namespace__create(&ns, name_namespace, scratch)); + SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME, TRUE)); + SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); + SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME "1", TRUE)); + SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); + SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME "2", TRUE)); + SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); + + svn_pool_clear(scratch); + + SVN_ERR(svn_atomic_namespace__create(&ns, name_namespace1, scratch)); + SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME, TRUE)); + SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); + svn_pool_clear(scratch); + + SVN_ERR(svn_atomic_namespace__create(&ns, name_namespace2, scratch)); + SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME, TRUE)); + SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); + svn_pool_clear(scratch); + + /* done */ + + return SVN_NO_ERROR; +} + +/* Prepare the shared memory for a run with COUNT workers. + */ +static svn_error_t * +init_concurrency_test_shm(apr_pool_t *pool, int count) +{ + svn_atomic_namespace__t *ns; + svn_named_atomic__t *atomic; + int i; + + /* get the two I/O atomics for this thread */ + SVN_ERR(svn_atomic_namespace__create(&ns, name_namespace, pool)); + + /* reset the I/O atomics for all threads */ + for (i = 0; i < count; ++i) + { + SVN_ERR(svn_named_atomic__get(&atomic, + ns, + apr_pstrcat(pool, + ATOMIC_NAME, + apr_itoa(pool, i), + NULL), + TRUE)); + SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); + } + + SVN_ERR(svn_named_atomic__get(&atomic, ns, "counter", TRUE)); + SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); + + return SVN_NO_ERROR; +} + +#if APR_HAS_THREADS + +/* our thread function type + */ +typedef svn_error_t *(*thread_func_t)(int, int, int, apr_pool_t *); + +/* Per-thread input and output data. + */ +struct thread_baton +{ + int thread_count; + int thread_no; + int iterations; + svn_error_t *result; + thread_func_t func; +}; + +/* APR thread function implementation: A wrapper around baton->func that + * handles the svn_error_t return value. + */ +static void * +APR_THREAD_FUNC test_thread(apr_thread_t *thread, void *baton) +{ + struct thread_baton *params = baton; + apr_pool_t *pool = svn_pool_create_ex(NULL, NULL); + + params->result = (*params->func)(params->thread_no, + params->thread_count, + params->iterations, + pool); + svn_pool_destroy(pool); + apr_thread_exit(thread, APR_SUCCESS); + + return NULL; +} + +/* Runs FUNC in COUNT concurrent threads ITERATION times and combines the + * results. + */ +static svn_error_t * +run_threads(apr_pool_t *pool, int count, int iterations, thread_func_t func) +{ + apr_status_t status; + int i; + svn_error_t *error = SVN_NO_ERROR; + + /* all threads and their I/O data */ + apr_thread_t **threads = apr_palloc(pool, count * sizeof(*threads)); + struct thread_baton *batons = apr_palloc(pool, count * sizeof(*batons)); + + /* start threads */ + for (i = 0; i < count; ++i) + { + batons[i].thread_count = count; + batons[i].thread_no = i; + batons[i].iterations = iterations; + batons[i].func = func; + + status = apr_thread_create(&threads[i], + NULL, + test_thread, + &batons[i], + pool); + if (status != APR_SUCCESS) + SVN_ERR(svn_error_wrap_apr(status, "could not create a thread")); + } + + /* Wait for threads to finish and return result. */ + for (i = 0; i < count; ++i) + { + apr_status_t retval; + status = apr_thread_join(&retval, threads[i]); + if (status != APR_SUCCESS) + SVN_ERR(svn_error_wrap_apr(status, "waiting for thread's end failed")); + + if (batons[i].result) + error = svn_error_compose_create (error, svn_error_quick_wrap + (batons[i].result, apr_psprintf(pool, "Thread %d failed", i))); + } + + return error; +} +#endif + +/* Runs PROC in COUNT concurrent worker processes and check the results. + */ +static svn_error_t * +run_procs(apr_pool_t *pool, const char *proc, int count, int iterations) +{ + int i, k; + svn_error_t *error = SVN_NO_ERROR; + const char * directory = NULL; + + /* all processes and their I/O data */ + apr_proc_t *process = apr_palloc(pool, count * sizeof(*process)); + apr_file_t *common_stdout = NULL; + apr_file_open_stdout(&common_stdout, pool); + + SVN_ERR(adjust_proc_path(&proc, &directory, pool)); + + /* start sub-processes */ + for (i = 0; i < count; ++i) + { + const char * args[6]; + + args[0] = proc; + args[1] = apr_itoa(pool, i); + args[2] = apr_itoa(pool, count); + args[3] = apr_itoa(pool, iterations); + args[4] = name_namespace; + args[5] = NULL; + + error = svn_io_start_cmd3(&process[i], + directory, /* working directory */ + args[0], + args, + NULL, /* environment */ + FALSE, /* no handle inheritance */ + FALSE, /* no STDIN pipe */ + NULL, + FALSE, /* consolidate into 1 STDOUT */ + common_stdout, + FALSE, /* no STDERR pipe */ + NULL, + pool); + if (error) + { + /* dump program name and parameters */ + for (k = 0; k < sizeof(args) / sizeof(args[0]); ++k) + if (args[k]) + printf(k == 0 ? "%s\n" : " %s\n", args[k]); + + if (directory) + printf("working folder %s:\n", directory); + + return error; + } + } + + /* Wait for sub-processes to finish and return result. */ + for (i = 0; i < count; ++i) + { + const char *cmd = apr_psprintf(pool, + "named_atomic-test-proc %d %d %d", + i, count, iterations); + error = svn_error_compose_create(error, + svn_io_wait_for_cmd(&process[i], + cmd, NULL, NULL, + pool)); + } + + return error; +} + +/* Set SUGGESTED_ITERATIONS to a value that COUNT workers will take + * about 1 second to execute. + */ +static svn_error_t * +calibrate_iterations(apr_pool_t *pool, int count) +{ + apr_time_t start; + int calib_iterations; + double taken = 0.0; + + /* increase iterations until we pass the 100ms mark */ + + for (calib_iterations = 10; taken < 100000.0; calib_iterations *= 2) + { + apr_pool_t *scratch = svn_pool_create(pool); + SVN_ERR(init_concurrency_test_shm(scratch, count)); + + start = apr_time_now(); + SVN_ERR(run_procs(pool, TEST_PROC, count, calib_iterations)); + + taken = (double)(apr_time_now() - start); + svn_pool_destroy(scratch); + } + + /* scale that to 1s */ + + suggested_iterations = (int)(1000000.0 / taken * calib_iterations); + + return SVN_NO_ERROR; +} + +/* Find out how far the system will scale, i.e. how many workers can be + * run concurrently without experiencing significant slowdowns. + * Sets HW_THREAD_COUNT to a value of 2 .. 32 (limit the system impact in + * case our heuristics fail) and determines the number of iterations. + * Can be called multiple times but will skip the calculations after the + * first successful run. + */ +static svn_error_t * +calibrate_concurrency(apr_pool_t *pool) +{ + if (hw_thread_count == 0) + { + /* these parameters should be ok even on very slow machines */ + hw_thread_count = 2; + suggested_iterations = 100; + + /* if we've got a proper machine and OS setup, let's prepare for + * some real testing */ + if (svn_named_atomic__is_efficient() && proc_found(TEST_PROC, pool)) + { + SVN_ERR(calibrate_iterations(pool, 2)); + for (; hw_thread_count < 32; hw_thread_count *= 2) + { + int saved_suggestion = suggested_iterations; + + /* run with an additional core to spare + * (even low CPU usage might cause heavy context switching) */ + SVN_ERR(calibrate_iterations(pool, hw_thread_count * 2 + 1)); + if (suggested_iterations < 100000) + { + /* Machines with only a small number of cores are prone + * to inconsistent performance due context switching. + * Reduce the number of iterations on those machines. */ + suggested_iterations = hw_thread_count > 2 + ? saved_suggestion + : saved_suggestion / 2; + break; + } + } + } + + printf("using %d cores for %d iterations\n", hw_thread_count, + suggested_iterations); + } + + return SVN_NO_ERROR; +} + +/* The individual tests */ + +static svn_error_t * +test_basics(apr_pool_t *pool) +{ + svn_atomic_namespace__t *ns; + svn_named_atomic__t *atomic; + apr_int64_t value; + + SVN_ERR(init_test_shm(pool)); + + /* Use a separate namespace for our tests isolate them from production */ + SVN_ERR(svn_atomic_namespace__create(&ns, name_namespace, pool)); + + /* Test a non-existing atomic */ + SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME "x", FALSE)); + SVN_TEST_ASSERT(atomic == NULL); + + /* Now, we auto-create it */ + SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME, TRUE)); + SVN_TEST_ASSERT(atomic != NULL); + + /* The default value should be 0 */ + SVN_TEST_ASSERT_ERROR(svn_named_atomic__read(&value, NULL), + SVN_ERR_BAD_ATOMIC); + value = 1; + SVN_ERR(svn_named_atomic__read(&value, atomic)); + SVN_TEST_ASSERT(value == 0); + + /* Write should return the previous value. */ + SVN_TEST_ASSERT_ERROR(svn_named_atomic__write(&value, 0, NULL), + SVN_ERR_BAD_ATOMIC); + value = 1; + SVN_ERR(svn_named_atomic__write(&value, 21, atomic)); + SVN_TEST_ASSERT(value == 0); + SVN_ERR(svn_named_atomic__read(&value, atomic)); + SVN_TEST_ASSERT(value == 21); + + SVN_ERR(svn_named_atomic__write(&value, 42, atomic)); + SVN_TEST_ASSERT(value == 21); + SVN_ERR(svn_named_atomic__read(&value, atomic)); + SVN_TEST_ASSERT(value == 42); + + SVN_ERR(svn_named_atomic__write(NULL, 17, atomic)); + SVN_ERR(svn_named_atomic__read(&value, atomic)); + SVN_TEST_ASSERT(value == 17); + + /* Adding & subtracting values */ + SVN_TEST_ASSERT_ERROR(svn_named_atomic__add(&value, 0, NULL), + SVN_ERR_BAD_ATOMIC); + SVN_ERR(svn_named_atomic__add(&value, 25, atomic)); + SVN_TEST_ASSERT(value == 42); + SVN_ERR(svn_named_atomic__add(NULL, 47, atomic)); + SVN_ERR(svn_named_atomic__read(&value, atomic)); + SVN_TEST_ASSERT(value == 89); + + SVN_ERR(svn_named_atomic__add(&value, -25, atomic)); + SVN_TEST_ASSERT(value == 64); + SVN_ERR(svn_named_atomic__add(NULL, -22, atomic)); + SVN_ERR(svn_named_atomic__read(&value, atomic)); + SVN_TEST_ASSERT(value == 42); + + /* Compare-and-exchange */ + SVN_TEST_ASSERT_ERROR(svn_named_atomic__cmpxchg(&value, 0, 0, NULL), + SVN_ERR_BAD_ATOMIC); + value = 1; + SVN_ERR(svn_named_atomic__cmpxchg(&value, 99, 41, atomic)); + SVN_TEST_ASSERT(value == 42); + + value = 1; + SVN_ERR(svn_named_atomic__cmpxchg(&value, 98, 42, atomic)); + SVN_TEST_ASSERT(value == 42); + SVN_ERR(svn_named_atomic__cmpxchg(&value, 67, 98, atomic)); + SVN_TEST_ASSERT(value == 98); + + SVN_ERR(svn_named_atomic__cmpxchg(NULL, 42, 67, atomic)); + SVN_ERR(svn_named_atomic__read(&value, atomic)); + SVN_TEST_ASSERT(value == 42); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_bignums(apr_pool_t *pool) +{ + svn_atomic_namespace__t *ns; + svn_named_atomic__t *atomic; + apr_int64_t value; + + SVN_ERR(init_test_shm(pool)); + + /* Use a separate namespace for our tests isolate them from production */ + SVN_ERR(svn_atomic_namespace__create(&ns, name_namespace, pool)); + + /* Auto-create our atomic variable */ + SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME, TRUE)); + SVN_TEST_ASSERT(atomic != NULL); + + /* Write should return the previous value. */ + + SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); + value = 1; + SVN_ERR(svn_named_atomic__write(&value, 21 * HUGE_VALUE, atomic)); + SVN_TEST_ASSERT(value == 0 * HUGE_VALUE); + SVN_ERR(svn_named_atomic__read(&value, atomic)); + SVN_TEST_ASSERT(value == 21 * HUGE_VALUE); + + SVN_ERR(svn_named_atomic__write(&value, 17 * HUGE_VALUE, atomic)); + SVN_TEST_ASSERT(value == 21 * HUGE_VALUE); + + /* Adding & subtracting values */ + SVN_ERR(svn_named_atomic__add(&value, 25 * HUGE_VALUE, atomic)); + SVN_TEST_ASSERT(value == 42 * HUGE_VALUE); + SVN_ERR(svn_named_atomic__add(&value, -25 * HUGE_VALUE, atomic)); + SVN_TEST_ASSERT(value == 17 * HUGE_VALUE); + + /* Compare-and-exchange */ + value = 1; + SVN_ERR(svn_named_atomic__cmpxchg(&value, 99 * HUGE_VALUE, 41 * HUGE_VALUE, atomic)); + SVN_TEST_ASSERT(value == 17 * HUGE_VALUE); + + value = 1; + SVN_ERR(svn_named_atomic__cmpxchg(&value, 98 * HUGE_VALUE, 17 * HUGE_VALUE, atomic)); + SVN_TEST_ASSERT(value == 17 * HUGE_VALUE); + SVN_ERR(svn_named_atomic__read(&value, atomic)); + SVN_TEST_ASSERT(value == 98 * HUGE_VALUE); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_multiple_atomics(apr_pool_t *pool) +{ + svn_atomic_namespace__t *ns; + svn_named_atomic__t *atomic1; + svn_named_atomic__t *atomic2; + svn_named_atomic__t *atomic1_alias; + svn_named_atomic__t *atomic2_alias; + apr_int64_t value1; + apr_int64_t value2; + + SVN_ERR(init_test_shm(pool)); + + /* Use a separate namespace for our tests isolate them from production */ + SVN_ERR(svn_atomic_namespace__create(&ns, name_namespace, pool)); + + /* Create two atomics */ + SVN_ERR(svn_named_atomic__get(&atomic1, ns, ATOMIC_NAME "1", TRUE)); + SVN_ERR(svn_named_atomic__get(&atomic2, ns, ATOMIC_NAME "2", TRUE)); + SVN_TEST_ASSERT(atomic1 != NULL); + SVN_TEST_ASSERT(atomic2 != NULL); + SVN_TEST_ASSERT(atomic1 != atomic2); + + /* Get aliases to those */ + SVN_ERR(svn_named_atomic__get(&atomic1_alias, ns, ATOMIC_NAME "1", TRUE)); + SVN_ERR(svn_named_atomic__get(&atomic2_alias, ns, ATOMIC_NAME "2", TRUE)); + SVN_TEST_ASSERT(atomic1 == atomic1_alias); + SVN_TEST_ASSERT(atomic2 == atomic2_alias); + + /* The atomics shall not overlap, i.e. changes to one do not affect the other */ + SVN_ERR(svn_named_atomic__write(NULL, 0, atomic1)); + SVN_ERR(svn_named_atomic__write(NULL, 0, atomic2)); + SVN_ERR(svn_named_atomic__write(&value1, 21 * HUGE_VALUE, atomic1)); + SVN_ERR(svn_named_atomic__write(&value2, 42 * HUGE_VALUE, atomic2)); + SVN_TEST_ASSERT(value1 == 0); + SVN_TEST_ASSERT(value2 == 0); + + SVN_ERR(svn_named_atomic__read(&value1, atomic1)); + SVN_ERR(svn_named_atomic__read(&value2, atomic2)); + SVN_TEST_ASSERT(value1 == 21 * HUGE_VALUE); + SVN_TEST_ASSERT(value2 == 42 * HUGE_VALUE); + + SVN_ERR(svn_named_atomic__add(&value1, 25 * HUGE_VALUE, atomic1)); + SVN_ERR(svn_named_atomic__add(&value2, -25 * HUGE_VALUE, atomic2)); + SVN_TEST_ASSERT(value1 == 46 * HUGE_VALUE); + SVN_TEST_ASSERT(value2 == 17 * HUGE_VALUE); + + value1 = 1; + value2 = 1; + SVN_ERR(svn_named_atomic__cmpxchg(&value1, 4 * HUGE_VALUE, 46 * HUGE_VALUE, atomic1)); + SVN_ERR(svn_named_atomic__cmpxchg(&value2, 98 * HUGE_VALUE, 17 * HUGE_VALUE, atomic2)); + SVN_TEST_ASSERT(value1 == 46 * HUGE_VALUE); + SVN_TEST_ASSERT(value2 == 17 * HUGE_VALUE); + + SVN_ERR(svn_named_atomic__read(&value1, atomic1)); + SVN_ERR(svn_named_atomic__read(&value2, atomic2)); + SVN_TEST_ASSERT(value1 == 4 * HUGE_VALUE); + SVN_TEST_ASSERT(value2 == 98 * HUGE_VALUE); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_namespaces(apr_pool_t *pool) +{ + svn_atomic_namespace__t *test_namespace1; + svn_atomic_namespace__t *test_namespace1_alias; + svn_atomic_namespace__t *test_namespace2; + svn_atomic_namespace__t *test_namespace2_alias; + svn_named_atomic__t *atomic1; + svn_named_atomic__t *atomic2; + svn_named_atomic__t *atomic1_alias; + svn_named_atomic__t *atomic2_alias; + apr_int64_t value; + + SVN_ERR(init_test_shm(pool)); + + /* Use a separate namespace for our tests isolate them from production */ + SVN_ERR(svn_atomic_namespace__create(&test_namespace1, name_namespace1, pool)); + SVN_ERR(svn_atomic_namespace__create(&test_namespace1_alias, name_namespace1, pool)); + SVN_ERR(svn_atomic_namespace__create(&test_namespace2, name_namespace2, pool)); + SVN_ERR(svn_atomic_namespace__create(&test_namespace2_alias, name_namespace2, pool)); + + /* Create two atomics with the same name in different namespaces */ + SVN_ERR(svn_named_atomic__get(&atomic1, test_namespace1, ATOMIC_NAME, TRUE)); + SVN_ERR(svn_named_atomic__get(&atomic1_alias, test_namespace1_alias, ATOMIC_NAME, FALSE)); + SVN_ERR(svn_named_atomic__get(&atomic2, test_namespace2, ATOMIC_NAME, TRUE)); + SVN_ERR(svn_named_atomic__get(&atomic2_alias, test_namespace2_alias, ATOMIC_NAME, FALSE)); + SVN_TEST_ASSERT(atomic1 != atomic1_alias); + SVN_TEST_ASSERT(atomic1_alias != NULL); + SVN_TEST_ASSERT(atomic2 != atomic2_alias); + SVN_TEST_ASSERT(atomic2_alias != NULL); + + /* Write data to our atomics */ + SVN_ERR(svn_named_atomic__write(NULL, 21 * HUGE_VALUE, atomic1)); + SVN_ERR(svn_named_atomic__write(NULL, 42 * HUGE_VALUE, atomic2)); + + /* Now check who sees which value */ + SVN_ERR(svn_named_atomic__read(&value, atomic1)); + SVN_TEST_ASSERT(value == 21 * HUGE_VALUE); + SVN_ERR(svn_named_atomic__read(&value, atomic2)); + SVN_TEST_ASSERT(value == 42 * HUGE_VALUE); + + SVN_ERR(svn_named_atomic__read(&value, atomic1_alias)); + SVN_TEST_ASSERT(value == 21 * HUGE_VALUE); + SVN_ERR(svn_named_atomic__read(&value, atomic2_alias)); + SVN_TEST_ASSERT(value == 42 * HUGE_VALUE); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_multithreaded(apr_pool_t *pool) +{ +#if APR_HAS_THREADS + SVN_ERR(init_test_shm(pool)); + + SVN_ERR(calibrate_concurrency(pool)); + + SVN_ERR(init_concurrency_test_shm(pool, hw_thread_count)); + SVN_ERR(run_threads(pool, hw_thread_count, suggested_iterations, test_pipeline)); + + return SVN_NO_ERROR; +#else + return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL, NULL); +#endif +} + +static svn_error_t * +test_multiprocess(apr_pool_t *pool) +{ + if (!proc_found(TEST_PROC, pool)) + return svn_error_wrap_apr(SVN_ERR_TEST_SKIPPED, + "executable '%s' not found", TEST_PROC); + + SVN_ERR(init_test_shm(pool)); + + SVN_ERR(calibrate_concurrency(pool)); + + SVN_ERR(init_concurrency_test_shm(pool, hw_thread_count)); + SVN_ERR(run_procs(pool, TEST_PROC, hw_thread_count, suggested_iterations)); + + return SVN_NO_ERROR; +} + +/* + ==================================================================== + If you add a new test to this file, update this array. + + (These globals are required by our included main()) +*/ + +/* An array of all test functions */ +struct svn_test_descriptor_t test_funcs[] = + { + SVN_TEST_NULL, + SVN_TEST_PASS2(test_basics, + "basic r/w access to a single atomic"), + SVN_TEST_PASS2(test_bignums, + "atomics must be 64 bits"), + SVN_TEST_PASS2(test_multiple_atomics, + "basic r/w access to multiple atomics"), + SVN_TEST_PASS2(test_namespaces, + "use different namespaces"), + SVN_TEST_PASS2(test_multithreaded, + "multithreaded access to atomics"), + SVN_TEST_PASS2(test_multiprocess, + "multi-process access to atomics"), + SVN_TEST_NULL + }; diff --git a/subversion/tests/libsvn_subr/path-test.c b/subversion/tests/libsvn_subr/path-test.c index f9d36ff..ec35176 100644 --- a/subversion/tests/libsvn_subr/path-test.c +++ b/subversion/tests/libsvn_subr/path-test.c @@ -21,6 +21,13 @@ * ==================================================================== */ +#ifdef _MSC_VER +#include <direct.h> +#define getcwd _getcwd +#else +#include <unistd.h> /* for getcwd() */ +#endif + #include <stdio.h> #include <string.h> #include <apr_general.h> @@ -871,7 +878,7 @@ test_path_remove_component(apr_pool_t *pool) int i; svn_stringbuf_t *buf; - buf = svn_stringbuf_create("", pool); + buf = svn_stringbuf_create_empty(pool); i = 0; while (tests[i].path) @@ -1195,7 +1202,6 @@ test_path_splitext(apr_pool_t *pool) const char *path; const char *path_root; const char *path_ext; - svn_boolean_t result; } tests[] = { { "no-ext", "no-ext", "" }, { "test-file.py", "test-file.", "py" }, @@ -1465,6 +1471,225 @@ test_path_internal_style(apr_pool_t *pool) } +/* The type of a function to be tested by condense_targets_tests_helper(). + * Matches svn_path_condense_targets(). + */ +typedef svn_error_t *(*condense_targets_func_t) + (const char **pcommon, + apr_array_header_t **pcondensed_targets, + const apr_array_header_t *targets, + svn_boolean_t remove_redundancies, + apr_pool_t *pool); + +/** Executes function CONDENSE_TARGETS twice - with and without requesting the + * condensed targets list - on TEST_TARGETS (comma sep. string) and compares + * the results with EXP_COMMON and EXP_TARGETS (comma sep. string). + * + * @note: a '%' character at the beginning of EXP_COMMON or EXP_TARGETS will + * be replaced by the current working directory. + * + * Returns an error if any of the comparisons fail. + */ +static svn_error_t * +condense_targets_tests_helper(const char* title, + const char* test_targets, + const char* exp_common, + const char* exp_targets, + const char* func_name, + condense_targets_func_t condense_targets, + apr_pool_t *pool) +{ + apr_array_header_t *targets; + apr_array_header_t *condensed_targets; + const char *common_path, *common_path2, *curdir; + char *token, *iter; + const char *exp_common_abs = exp_common; + int i; + char buf[8192]; + + if (! getcwd(buf, sizeof(buf))) + return svn_error_create(SVN_ERR_BASE, NULL, "getcwd() failed"); + curdir = svn_path_internal_style(buf, pool); + + /* Create the target array */ + targets = apr_array_make(pool, sizeof(test_targets), sizeof(const char *)); + token = apr_strtok(apr_pstrdup(pool, test_targets), ",", &iter); + while (token) + { + APR_ARRAY_PUSH(targets, const char *) = + svn_path_internal_style(token, pool); + token = apr_strtok(NULL, ",", &iter); + }; + + /* Call the function */ + SVN_ERR(condense_targets(&common_path, &condensed_targets, targets, + TRUE, pool)); + + /* Verify the common part with the expected (prefix with cwd). */ + if (*exp_common == '%') + exp_common_abs = apr_pstrcat(pool, curdir, exp_common + 1, (char *)NULL); + + if (strcmp(common_path, exp_common_abs) != 0) + { + return svn_error_createf + (SVN_ERR_TEST_FAILED, NULL, + "%s (test %s) returned %s instead of %s", + func_name, title, + common_path, exp_common_abs); + } + + /* Verify the condensed targets */ + token = apr_strtok(apr_pstrdup(pool, exp_targets), ",", &iter); + for (i = 0; i < condensed_targets->nelts; i++) + { + const char * target = APR_ARRAY_IDX(condensed_targets, i, const char*); + if (token && (*token == '%')) + token = apr_pstrcat(pool, curdir, token + 1, (char *)NULL); + if (! token || + (target && (strcmp(target, token) != 0))) + { + return svn_error_createf + (SVN_ERR_TEST_FAILED, NULL, + "%s (test %s) couldn't find %s in expected targets list", + func_name, title, + target); + } + token = apr_strtok(NULL, ",", &iter); + } + + /* Now ensure it works without the pbasename */ + SVN_ERR(condense_targets(&common_path2, NULL, targets, TRUE, pool)); + + /* Verify the common part again */ + if (strcmp(common_path, common_path2) != 0) + { + return svn_error_createf + (SVN_ERR_TEST_FAILED, NULL, + "%s (test %s): Common path without getting targets %s does not match" \ + "common path with targets %s", + func_name, title, + common_path2, common_path); + } + + return SVN_NO_ERROR; +} + + +static svn_error_t * +test_path_condense_targets(apr_pool_t *pool) +{ + int i; + struct { + const char* title; + const char* targets; + const char* exp_common; + const char* exp_targets; + } tests[] = { + { "normal use", "z/A/B,z/A,z/A/C,z/D/E,z/D/F,z/D,z/G,z/G/H,z/G/I", + "%/z", "A,D,G" }, + {"identical dirs", "z/A,z/A,z/A,z/A", + "%/z/A", "" }, + {"identical files", "z/A/file,z/A/file,z/A/file,z/A/file", + "%/z/A/file", "" }, + {"single dir", "z/A", + "%/z/A", "" }, + {"single file", "z/A/file", + "%/z/A/file", "" }, + {"URLs", "http://host/A/C,http://host/A/C/D,http://host/A/B/D", + "http://host/A", "C,B/D" }, + {"URLs with no common prefix", + "http://host1/A/C,http://host2/A/C/D,http://host3/A/B/D", + "", "http://host1/A/C,http://host2/A/C/D,http://host3/A/B/D" }, + {"file URLs with no common prefix", "file:///A/C,file:///B/D", + "", "file:///A/C,file:///B/D" }, + {"URLs with mixed protocols", + "http://host/A/C,file:///B/D,gopher://host/A", + "", "http://host/A/C,file:///B/D,gopher://host/A" }, + {"mixed paths and URLs", + "z/A/B,z/A,http://host/A/C/D,http://host/A/C", + "", "%/z/A,http://host/A/C" }, + }; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) + { + SVN_ERR(condense_targets_tests_helper(tests[i].title, + tests[i].targets, + tests[i].exp_common, + tests[i].exp_targets, + "svn_path_condense_targets", + svn_path_condense_targets, + pool)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_path_is_repos_relative_url(apr_pool_t *pool) +{ + int i; + struct { + const char* path; + svn_boolean_t result; + } tests[] = { + { "^/A", TRUE }, + { "http://host/A", FALSE }, + { "/A/B", FALSE }, + }; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) + { + svn_boolean_t result = svn_path_is_repos_relative_url(tests[i].path); + + if (tests[i].result != result) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "svn_path_is_repos_relative_url(\"%s\")" + " returned \"%s\" expected \"%s\"", + tests[i].path, + result ? "TRUE" : "FALSE", + tests[i].result ? "TRUE" : "FALSE"); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_path_resolve_repos_relative_url(apr_pool_t *pool) +{ + int i; + struct { + const char *relative_url; + const char *repos_root_url; + const char *absolute_url; + } tests[] = { + { "^/A", "file:///Z/X", "file:///Z/X/A" }, + { "^/A", "file:///Z/X/", "file:///Z/X//A" }, /* doesn't canonicalize */ + { "^/A@2", "file:///Z/X", "file:///Z/X/A@2" }, /* peg rev */ + { "^/A", "/Z/X", "/Z/X/A" }, /* doesn't verify repos_root is URL */ + }; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) + { + const char *result; + + SVN_ERR(svn_path_resolve_repos_relative_url(&result, + tests[i].relative_url, + tests[i].repos_root_url, + pool)); + + if (strcmp(tests[i].absolute_url,result)) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "svn_path_resolve_repos_relative_url(\"%s\"," + "\"%s\") returned \"%s\" expected \"%s\"", + tests[i].relative_url, + tests[i].repos_root_url, + result, tests[i].absolute_url); + } + + return SVN_NO_ERROR; +} + + /* local define to support XFail-ing tests on Windows/Cygwin only */ #ifdef SVN_USE_DOS_PATHS #define WINDOWS_OR_CYGWIN TRUE @@ -1526,5 +1751,11 @@ struct svn_test_descriptor_t test_funcs[] = "test svn_path_local_style"), SVN_TEST_PASS2(test_path_internal_style, "test svn_path_internal_style"), + SVN_TEST_PASS2(test_path_condense_targets, + "test svn_path_condense_targets"), + SVN_TEST_PASS2(test_path_is_repos_relative_url, + "test svn_path_is_repos_relative_url"), + SVN_TEST_PASS2(test_path_resolve_repos_relative_url, + "test svn_path_resolve_repos_relative_url"), SVN_TEST_NULL }; diff --git a/subversion/tests/libsvn_subr/skel-test.c b/subversion/tests/libsvn_subr/skel-test.c index e38aa73..49fe1a3 100644 --- a/subversion/tests/libsvn_subr/skel-test.c +++ b/subversion/tests/libsvn_subr/skel-test.c @@ -216,7 +216,7 @@ gen_implicit_length_all_chars(apr_size_t *len_p) for (i = 0; i < 256; i++) if (! skel_is_space( (apr_byte_t)i) && ! skel_is_paren( (apr_byte_t)i)) - name[pos++] = i; + name[pos++] = (char)i; *len_p = pos; return name; @@ -375,7 +375,7 @@ parse_explicit_length(apr_pool_t *pool) { char buf[1]; - buf[0] = i; + buf[0] = (char)i; SVN_ERR(try_explicit_length(buf, 1, 1, pool)); } } @@ -386,7 +386,7 @@ parse_explicit_length(apr_pool_t *pool) char data[256]; for (i = 0; i < 256; i++) - data[i] = i; + data[i] = (char)i; SVN_ERR(try_explicit_length(data, 256, 256, pool)); } @@ -592,7 +592,7 @@ parse_list(apr_pool_t *pool) svn_skel_t *child; char buf[1]; - buf[0] = atom_byte; + buf[0] = (char)atom_byte; put_list_start(str, (apr_byte_t)sep, sep_count); for (i = 0; i < list_len; i++) @@ -619,7 +619,7 @@ parse_list(apr_pool_t *pool) char data[256]; for (i = 0; i < 256; i++) - data[i] = i; + data[i] = (char)i; put_list_start(str, (apr_byte_t)sep, sep_count); for (i = 0; i < list_len; i++) @@ -797,7 +797,7 @@ unparse_list(apr_pool_t *pool) for (byte = 0; byte < 256; byte++) if (skel_is_name( (apr_byte_t)byte)) { - char buf = byte; + char buf = (char)byte; add(build_atom(1, &buf, pool), list); } @@ -858,7 +858,7 @@ unparse_list(apr_pool_t *pool) val = i * 10 + j; for (k = 0; k < sizeof(buf); k++) { - buf[k] = val; + buf[k] = (char)val; val += j; } @@ -873,6 +873,11 @@ unparse_list(apr_pool_t *pool) if (! skel_equal(top, reparsed)) return fail(pool, "failed to reparse list of lists"); + + reparsed = svn_skel__dup(reparsed, TRUE, pool); + + if (! skel_equal(top, reparsed)) + return fail(pool, "failed to dup list of lists"); } return SVN_NO_ERROR; diff --git a/subversion/tests/libsvn_subr/spillbuf-test.c b/subversion/tests/libsvn_subr/spillbuf-test.c new file mode 100644 index 0000000..c928dc3 --- /dev/null +++ b/subversion/tests/libsvn_subr/spillbuf-test.c @@ -0,0 +1,433 @@ +/* + * spillbuf-test.c : test the spill buffer code + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + +#include "svn_types.h" + +#include "private/svn_subr_private.h" + +#include "../svn_test.h" + + +static const char basic_data[] = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789"; + + +/* Validate that BUF is STARTING_SIZE in length. Then read some data from + the buffer, which should match EXPECTED. The EXPECTED value must be + NUL-terminated, but the NUL is not part of the expected/verified value. */ +#define CHECK_READ(b, s, e, p) SVN_ERR(check_read(b, s, e, p)) +static svn_error_t * +check_read(svn_spillbuf_t *buf, + svn_filesize_t starting_size, + const char *expected, + apr_pool_t *scratch_pool) +{ + apr_size_t expected_len = strlen(expected); + const char *readptr; + apr_size_t readlen; + + SVN_TEST_ASSERT(svn_spillbuf__get_size(buf) == starting_size); + SVN_ERR(svn_spillbuf__read(&readptr, &readlen, buf, scratch_pool)); + SVN_TEST_ASSERT(readptr != NULL + && readlen == expected_len + && memcmp(readptr, expected, expected_len) == 0); + return SVN_NO_ERROR; +} + + +static svn_error_t * +test_spillbuf_basic(apr_pool_t *pool) +{ + apr_size_t len = strlen(basic_data); /* Don't include basic_data's NUL */ + svn_spillbuf_t *buf = svn_spillbuf__create(len, 10 * len, pool); + int i; + const char *readptr; + apr_size_t readlen; + + /* It starts empty. */ + SVN_TEST_ASSERT(svn_spillbuf__get_size(buf) == 0); + + /* Place enough data into the buffer to cause a spill to disk. */ + for (i = 20; i--; ) + SVN_ERR(svn_spillbuf__write(buf, basic_data, len, pool)); + + /* And now has content. */ + SVN_TEST_ASSERT(svn_spillbuf__get_size(buf) > 0); + + /* Verify that we can read 20 copies of basic_data from the buffer. */ + for (i = 20; i--; ) + CHECK_READ(buf, (i + 1) * len, basic_data, pool); + + /* And after precisely 20 reads, it should be empty. */ + SVN_ERR(svn_spillbuf__read(&readptr, &readlen, buf, pool)); + SVN_TEST_ASSERT(readptr == NULL); + SVN_TEST_ASSERT(svn_spillbuf__get_size(buf) == 0); + + return SVN_NO_ERROR; +} + + +static svn_error_t * +read_callback(svn_boolean_t *stop, + void *baton, + const char *data, + apr_size_t len, + apr_pool_t *scratch_pool) +{ + int *counter = baton; + + SVN_TEST_ASSERT(len == sizeof(basic_data)); + SVN_TEST_ASSERT(memcmp(data, basic_data, len) == 0); + + *stop = (++*counter == 10); + + return SVN_NO_ERROR; +} + + +static svn_error_t * +test_spillbuf_callback(apr_pool_t *pool) +{ + svn_spillbuf_t *buf = svn_spillbuf__create( + sizeof(basic_data) /* blocksize */, + 10 * sizeof(basic_data) /* maxsize */, + pool); + int i; + int counter; + svn_boolean_t exhausted; + + /* Place enough data into the buffer to cause a spill to disk. */ + for (i = 20; i--; ) + SVN_ERR(svn_spillbuf__write(buf, basic_data, sizeof(basic_data), pool)); + + counter = 0; + SVN_ERR(svn_spillbuf__process(&exhausted, buf, read_callback, &counter, + pool)); + SVN_TEST_ASSERT(!exhausted); + + SVN_ERR(svn_spillbuf__process(&exhausted, buf, read_callback, &counter, + pool)); + SVN_TEST_ASSERT(exhausted); + + return SVN_NO_ERROR; +} + + +static svn_error_t * +test_spillbuf_file(apr_pool_t *pool) +{ + apr_size_t altsize = sizeof(basic_data) + 2; + svn_spillbuf_t *buf = svn_spillbuf__create( + altsize /* blocksize */, + 2 * sizeof(basic_data) /* maxsize */, + pool); + int i; + const char *readptr; + apr_size_t readlen; + apr_size_t cur_index; + + /* Place enough data into the buffer to cause a spill to disk. Note that + we are writing data that is *smaller* than the blocksize. */ + for (i = 7; i--; ) + SVN_ERR(svn_spillbuf__write(buf, basic_data, sizeof(basic_data), pool)); + + /* The first two reads will be in-memory blocks (the third write causes + the spill to disk). The spillbuf will pack the content into BLOCKSIZE + blocks. The second/last memory block will (thus) be a bit smaller. */ + SVN_ERR(svn_spillbuf__read(&readptr, &readlen, buf, pool)); + SVN_TEST_ASSERT(readptr != NULL); + SVN_TEST_ASSERT(readlen == altsize); + SVN_ERR(svn_spillbuf__read(&readptr, &readlen, buf, pool)); + SVN_TEST_ASSERT(readptr != NULL); + /* The second write put sizeof(basic_data) into the buffer. A small + portion was stored at the end of the memblock holding the first write. + Thus, the size of this read will be the written data, minus that + slice written to the first block. */ + SVN_TEST_ASSERT(readlen + == sizeof(basic_data) - (altsize - sizeof(basic_data))); + + /* Current index into basic_data[] that we compare against. */ + cur_index = 0; + + while (TRUE) + { + /* This will read more bytes (from the spill file into a temporary + in-memory block) than the blocks of data that we wrote. This makes + it trickier to verify that the right data is being returned. */ + SVN_ERR(svn_spillbuf__read(&readptr, &readlen, buf, pool)); + if (readptr == NULL) + break; + + while (TRUE) + { + apr_size_t amt; + + /* Compute the slice of basic_data that we will compare against, + given the readlen and cur_index. */ + if (cur_index + readlen >= sizeof(basic_data)) + amt = sizeof(basic_data) - cur_index; + else + amt = readlen; + SVN_TEST_ASSERT(memcmp(readptr, &basic_data[cur_index], amt) == 0); + if ((cur_index += amt) == sizeof(basic_data)) + cur_index = 0; + if ((readlen -= amt) == 0) + break; + readptr += amt; + } + } + + SVN_TEST_ASSERT(svn_spillbuf__get_size(buf) == 0); + + return SVN_NO_ERROR; +} + + +static svn_error_t * +test_spillbuf_interleaving(apr_pool_t *pool) +{ + svn_spillbuf_t *buf = svn_spillbuf__create(8 /* blocksize */, + 15 /* maxsize */, + pool); + + SVN_ERR(svn_spillbuf__write(buf, "abcdef", 6, pool)); + SVN_ERR(svn_spillbuf__write(buf, "ghijkl", 6, pool)); + /* now: two blocks: 8 and 4 bytes */ + + CHECK_READ(buf, 12, "abcdefgh", pool); + /* now: one block: 4 bytes */ + + SVN_ERR(svn_spillbuf__write(buf, "mnopqr", 6, pool)); + /* now: two blocks: 8 and 2 bytes */ + + CHECK_READ(buf, 10, "ijklmnop", pool); + /* now: one block: 2 bytes */ + + SVN_ERR(svn_spillbuf__write(buf, "stuvwx", 6, pool)); + SVN_ERR(svn_spillbuf__write(buf, "ABCDEF", 6, pool)); + SVN_ERR(svn_spillbuf__write(buf, "GHIJKL", 6, pool)); + /* now: two blocks: 8 and 6 bytes, and 6 bytes spilled to a file */ + + CHECK_READ(buf, 20, "qrstuvwx", pool); + CHECK_READ(buf, 12, "ABCDEF", pool); + CHECK_READ(buf, 6, "GHIJKL", pool); + + SVN_TEST_ASSERT(svn_spillbuf__get_size(buf) == 0); + + return SVN_NO_ERROR; +} + + +static svn_error_t * +test_spillbuf_reader(apr_pool_t *pool) +{ + svn_spillbuf_reader_t *sbr; + apr_size_t amt; + char buf[10]; + + sbr = svn_spillbuf__reader_create(4 /* blocksize */, + 100 /* maxsize */, + pool); + + SVN_ERR(svn_spillbuf__reader_write(sbr, "abcdef", 6, pool)); + + /* Get a buffer from the underlying reader, and grab a couple bytes. */ + SVN_ERR(svn_spillbuf__reader_read(&amt, sbr, buf, 2, pool)); + SVN_TEST_ASSERT(amt == 2 && memcmp(buf, "ab", 2) == 0); + + /* Trigger the internal "save" feature of the SBR. */ + SVN_ERR(svn_spillbuf__reader_write(sbr, "ghijkl", 6, pool)); + + /* Read from the save buffer, and from the internal blocks. */ + SVN_ERR(svn_spillbuf__reader_read(&amt, sbr, buf, 10, pool)); + SVN_TEST_ASSERT(amt == 10 && memcmp(buf, "cdefghijkl", 10) == 0); + + /* Should be done. */ + SVN_ERR(svn_spillbuf__reader_read(&amt, sbr, buf, 10, pool)); + SVN_TEST_ASSERT(amt == 0); + + return SVN_NO_ERROR; +} + + +static svn_error_t * +test_spillbuf_stream(apr_pool_t *pool) +{ + svn_stream_t *stream = svn_stream__from_spillbuf(8 /* blocksize */, + 15 /* maxsize */, + pool); + char readbuf[256]; + apr_size_t readlen; + apr_size_t writelen; + + writelen = 6; + SVN_ERR(svn_stream_write(stream, "abcdef", &writelen)); + SVN_ERR(svn_stream_write(stream, "ghijkl", &writelen)); + /* now: two blocks: 8 and 4 bytes */ + + readlen = 8; + SVN_ERR(svn_stream_read(stream, readbuf, &readlen)); + SVN_TEST_ASSERT(readlen == 8 + && memcmp(readbuf, "abcdefgh", 8) == 0); + /* now: one block: 4 bytes */ + + SVN_ERR(svn_stream_write(stream, "mnopqr", &writelen)); + /* now: two blocks: 8 and 2 bytes */ + + SVN_ERR(svn_stream_read(stream, readbuf, &readlen)); + SVN_TEST_ASSERT(readlen == 8 + && memcmp(readbuf, "ijklmnop", 8) == 0); + /* now: one block: 2 bytes */ + + SVN_ERR(svn_stream_write(stream, "stuvwx", &writelen)); + SVN_ERR(svn_stream_write(stream, "ABCDEF", &writelen)); + SVN_ERR(svn_stream_write(stream, "GHIJKL", &writelen)); + /* now: two blocks: 8 and 6 bytes, and 6 bytes spilled to a file */ + + SVN_ERR(svn_stream_read(stream, readbuf, &readlen)); + SVN_TEST_ASSERT(readlen == 8 + && memcmp(readbuf, "qrstuvwx", 8) == 0); + readlen = 6; + SVN_ERR(svn_stream_read(stream, readbuf, &readlen)); + SVN_TEST_ASSERT(readlen == 6 + && memcmp(readbuf, "ABCDEF", 6) == 0); + SVN_ERR(svn_stream_read(stream, readbuf, &readlen)); + SVN_TEST_ASSERT(readlen == 6 + && memcmp(readbuf, "GHIJKL", 6) == 0); + + return SVN_NO_ERROR; +} + + +static svn_error_t * +test_spillbuf_rwfile(apr_pool_t *pool) +{ + svn_spillbuf_t *buf = svn_spillbuf__create(4 /* blocksize */, + 10 /* maxsize */, + pool); + + SVN_ERR(svn_spillbuf__write(buf, "abcdef", 6, pool)); + SVN_ERR(svn_spillbuf__write(buf, "ghijkl", 6, pool)); + SVN_ERR(svn_spillbuf__write(buf, "mnopqr", 6, pool)); + /* now: two blocks: 4 and 2 bytes, and 12 bytes in spill file. */ + + CHECK_READ(buf, 18, "abcd", pool); + /* now: one block: 2 bytes, and 12 bytes in spill file. */ + + CHECK_READ(buf, 14, "ef", pool); + /* now: no blocks, and 12 bytes in spill file. */ + + CHECK_READ(buf, 12, "ghij", pool); + /* now: no blocks, and 8 bytes in spill file. */ + + /* Write more data. It should be appended to the spill file. */ + SVN_ERR(svn_spillbuf__write(buf, "stuvwx", 6, pool)); + /* now: no blocks, and 14 bytes in spill file. */ + + CHECK_READ(buf, 14, "klmn", pool); + /* now: no blocks, and 10 bytes in spill file. */ + + CHECK_READ(buf, 10, "opqr", pool); + /* now: no blocks, and 6 bytes in spill file. */ + + CHECK_READ(buf, 6, "stuv", pool); + /* now: no blocks, and 2 bytes in spill file. */ + + CHECK_READ(buf, 2, "wx", pool); + /* now: no blocks, and no spill file. */ + + return SVN_NO_ERROR; +} + + +static svn_error_t * +test_spillbuf_eof(apr_pool_t *pool) +{ + svn_spillbuf_t *buf = svn_spillbuf__create(4 /* blocksize */, + 10 /* maxsize */, + pool); + + SVN_ERR(svn_spillbuf__write(buf, "abcdef", 6, pool)); + SVN_ERR(svn_spillbuf__write(buf, "ghijkl", 6, pool)); + /* now: two blocks: 4 and 2 bytes, and 6 bytes in spill file. */ + + CHECK_READ(buf, 12, "abcd", pool); + CHECK_READ(buf, 8, "ef", pool); + CHECK_READ(buf, 6, "ghij", pool); + CHECK_READ(buf, 2, "kl", pool); + /* The spill file should have been emptied and forgotten. */ + + /* Assuming the spill file has been forgotten, this should result in + precisely the same behavior. Specifically: the initial write should + create two blocks, and the second write should be spilled. If there + *was* a spill file, then this written data would go into the file. */ + SVN_ERR(svn_spillbuf__write(buf, "abcdef", 6, pool)); + SVN_ERR(svn_spillbuf__write(buf, "ghijkl", 6, pool)); + CHECK_READ(buf, 12, "abcd", pool); + CHECK_READ(buf, 8, "ef", pool); + CHECK_READ(buf, 6, "ghij", pool); + CHECK_READ(buf, 2, "kl", pool); + /* The spill file should have been emptied and forgotten. */ + + /* Now, let's do a sequence where we arrange to hit EOF precisely on + a block-sized read. Note: the second write must be more than 4 bytes, + or it will not cause a spill. We use 8 to get the right boundary. */ + SVN_ERR(svn_spillbuf__write(buf, "abcdef", 6, pool)); + SVN_ERR(svn_spillbuf__write(buf, "ghijklmn", 8, pool)); + CHECK_READ(buf, 14, "abcd", pool); + CHECK_READ(buf, 10, "ef", pool); + CHECK_READ(buf, 8, "ghij", pool); + CHECK_READ(buf, 4, "klmn", pool); + /* We discard the spill file when we know it has no data, rather than + upon hitting EOF (upon a read attempt). Thus, the spill file should + be gone. */ + + /* Verify the forgotten spill file. */ + SVN_ERR(svn_spillbuf__write(buf, "abcdef", 6, pool)); + SVN_ERR(svn_spillbuf__write(buf, "ghijkl", 6, pool)); + CHECK_READ(buf, 12, "abcd", pool); + CHECK_READ(buf, 8, "ef", pool); + CHECK_READ(buf, 6, "ghij", pool); + /* Two unread bytes remaining in the spill file. */ + SVN_TEST_ASSERT(svn_spillbuf__get_size(buf) == 2); + + return SVN_NO_ERROR; +} + + +/* The test table. */ +struct svn_test_descriptor_t test_funcs[] = + { + SVN_TEST_NULL, + SVN_TEST_PASS2(test_spillbuf_basic, "basic spill buffer test"), + SVN_TEST_PASS2(test_spillbuf_callback, "spill buffer read callback"), + SVN_TEST_PASS2(test_spillbuf_file, "spill buffer file test"), + SVN_TEST_PASS2(test_spillbuf_interleaving, + "interleaving reads and writes"), + SVN_TEST_PASS2(test_spillbuf_reader, "spill buffer reader test"), + SVN_TEST_PASS2(test_spillbuf_stream, "spill buffer stream test"), + SVN_TEST_PASS2(test_spillbuf_rwfile, "read/write spill file"), + SVN_TEST_PASS2(test_spillbuf_eof, "validate reaching EOF of spill file"), + SVN_TEST_NULL + }; diff --git a/subversion/tests/libsvn_subr/stream-test.c b/subversion/tests/libsvn_subr/stream-test.c index 536e01c..c8dba13 100644 --- a/subversion/tests/libsvn_subr/stream-test.c +++ b/subversion/tests/libsvn_subr/stream-test.c @@ -67,7 +67,7 @@ test_stream_from_string(apr_pool_t *pool) apr_size_t len; inbuf = svn_stringbuf_create(strings[i], subpool); - outbuf = svn_stringbuf_create("", subpool); + outbuf = svn_stringbuf_create_empty(subpool); stream = svn_stream_from_stringbuf(inbuf, subpool); len = TEST_BUF_SIZE; while (len == TEST_BUF_SIZE) @@ -94,7 +94,7 @@ test_stream_from_string(apr_pool_t *pool) apr_size_t amt_read, len; inbuf = svn_stringbuf_create(strings[i], subpool); - outbuf = svn_stringbuf_create("", subpool); + outbuf = svn_stringbuf_create_empty(subpool); stream = svn_stream_from_stringbuf(outbuf, subpool); amt_read = 0; while (amt_read < inbuf->len) @@ -125,7 +125,7 @@ test_stream_from_string(apr_pool_t *pool) static svn_stringbuf_t * generate_test_bytes(int num_bytes, apr_pool_t *pool) { - svn_stringbuf_t *buffer = svn_stringbuf_create("", pool); + svn_stringbuf_t *buffer = svn_stringbuf_create_empty(pool); int total, repeat, repeat_iter; char c; @@ -138,7 +138,7 @@ generate_test_bytes(int num_bytes, apr_pool_t *pool) { if (c == 127) repeat++; - c = (c + 1) % 127; + c = (char)((c + 1) % 127); repeat_iter = repeat; } } @@ -189,8 +189,8 @@ test_stream_compressed(apr_pool_t *pool) apr_size_t len; origbuf = bufs[i]; - inbuf = svn_stringbuf_create("", subpool); - outbuf = svn_stringbuf_create("", subpool); + inbuf = svn_stringbuf_create_empty(subpool); + outbuf = svn_stringbuf_create_empty(subpool); stream = svn_stream_compressed(svn_stream_from_stringbuf(outbuf, subpool), @@ -232,8 +232,8 @@ static svn_error_t * test_stream_tee(apr_pool_t *pool) { svn_stringbuf_t *test_bytes = generate_test_bytes(100, pool); - svn_stringbuf_t *output_buf1 = svn_stringbuf_create("", pool); - svn_stringbuf_t *output_buf2 = svn_stringbuf_create("", pool); + svn_stringbuf_t *output_buf1 = svn_stringbuf_create_empty(pool); + svn_stringbuf_t *output_buf2 = svn_stringbuf_create_empty(pool); svn_stream_t *source_stream = svn_stream_from_stringbuf(test_bytes, pool); svn_stream_t *output_stream1 = svn_stream_from_stringbuf(output_buf1, pool); svn_stream_t *output_stream2 = svn_stream_from_stringbuf(output_buf2, pool); @@ -490,7 +490,7 @@ test_readonly(apr_pool_t *pool) /* File should be writable */ SVN_ERR(svn_io_stat(&finfo, path, wanted, pool)); SVN_ERR(svn_io__is_finfo_read_only(&read_only, &finfo, pool)); - SVN_TEST_ASSERT(read_only == FALSE); + SVN_TEST_ASSERT(!read_only); /* Set read only */ SVN_ERR(svn_io_set_file_read_only(path, FALSE, pool)); @@ -506,7 +506,7 @@ test_readonly(apr_pool_t *pool) /* File should be writable */ SVN_ERR(svn_io_stat(&finfo, path, wanted, pool)); SVN_ERR(svn_io__is_finfo_read_only(&read_only, &finfo, pool)); - SVN_TEST_ASSERT(read_only == FALSE); + SVN_TEST_ASSERT(!read_only); return SVN_NO_ERROR; } @@ -538,8 +538,8 @@ static svn_error_t * test_stream_base64(apr_pool_t *pool) { svn_stream_t *stream; - svn_stringbuf_t *actual = svn_stringbuf_create("", pool); - svn_stringbuf_t *expected = svn_stringbuf_create("", pool); + svn_stringbuf_t *actual = svn_stringbuf_create_empty(pool); + svn_stringbuf_t *expected = svn_stringbuf_create_empty(pool); int i; static const char *strings[] = { "fairly boring test data... blah blah", @@ -568,6 +568,165 @@ test_stream_base64(apr_pool_t *pool) return SVN_NO_ERROR; } +/* This test doesn't test much unless run under valgrind when it + triggers the problem reported here: + + http://mail-archives.apache.org/mod_mbox/subversion-dev/201202.mbox/%3C87sjik3m8q.fsf@stat.home.lan%3E + + The two data writes caused the base 64 code to allocate a buffer + that was a byte short but exactly matched a stringbuf blocksize. + That meant the stringbuf didn't overallocate and a write beyond + the end of the buffer occurred. + */ +static svn_error_t * +test_stream_base64_2(apr_pool_t *pool) +{ + const struct data_t { + const char *encoded1; + const char *encoded2; + } data[] = { + { + "MTI", + "123456789A123456789B123456789C123456789D123456789E" + "223456789A123456789B123456789C123456789D123456789E" + "323456789A123456789B123456789C123456789D123456789E" + "423456789A123456789B123456789C123456789D123456789E" + "523456789A123456789B123456789C123456789D123456789E" + "623456789A123456789B123456789C123456789D123456789E" + "723456789A123456789B123456789C123456789D123456789E" + "823456789A123456789B123456789C123456789D123456789E" + "923456789A123456789B123456789C123456789D123456789E" + "A23456789A123456789B123456789C123456789D123456789E" + "123456789A123456789B123456789C123456789D123456789E" + "223456789A123456789B123456789C123456789D123456789E" + "323456789A123456789B123456789C123456789D123456789E" + "423456789A123456789B123456789C123456789D123456789E" + "523456789A123456789B123456789C123456789D123456789E" + "623456789A123456789B123456789C123456789D123456789E" + "723456789A123456789B123456789C123456789D123456789E" + "823456789A123456789B123456789C123456789D123456789E" + "923456789A123456789B123456789C123456789D123456789E" + "B23456789A123456789B123456789C123456789D123456789E" + "123456789A123456789B123456789C123456789D123456789E" + "223456789A123456789B123456789C123456789D123456789E" + "323456789A123456789B123456789C123456789D123456789E" + "423456789A123456789B123456789C123456789D123456789E" + "523456789A123456789B123456789C123456789D123456789E" + "623456789A123456789B123456789C123456789D123456789E" + "723456789A123456789B123456789C123456789D123456789E" + "823456789A123456789B123456789C123456789D123456789E" + "923456789A123456789B123456789C123456789D123456789E" + "C23456789A123456789B123456789C123456789D123456789E" + "123456789A123456789B123456789C123456789D123456789E" + "223456789A123456789B123456789C123456789D123456789E" + "323456789A123456789B123456789C123456789D123456789E" + "423456789A123456789B123456789C123456789D123456789E" + "523456789A123456789B123456789C123456789D123456789E" + "623456789A123456789B123456789C123456789D123456789E" + "723456789A123456789B123456789C123456789D123456789E" + "823456789A123456789B123456789C123456789D123456789E" + "923456789A123456789B123456789C123456789D123456789E" + "D23456789A123456789B123456789C123456789D123456789E" + "123456789A123456789B123456789C123456789D123456789E" + "223456789A123456789B123456789C123456789D123456789E" + "323456789A123456789B123456789C123456789D123456789E" + "423456789A123456789B123456789C123456789D123456789E" + "523456789A123456789B123456789C123456789D123456789E" + "623456789A123456789B123456789C123456789D123456789E" + "723456789A123456789B123456789C123456789D123456789E" + "823456789A123456789B123456789C123456789D123456789E" + "923456789A123456789B123456789C123456789D123456789E" + "E23456789A123456789B123456789C123456789D123456789E" + "123456789A123456789B123456789C123456789D123456789E" + "223456789A123456789B123456789C123456789D123456789E" + "323456789A123456789B123456789C123456789D123456789E" + "423456789A123456789B123456789C123456789D123456789E" + "523456789A123456789B123456789C123456789D123456789E" + "623456789A123456789B123456789C123456789D123456789E" + "723456789A123456789B123456789C123456789D123456789E" + "823456789A123456789B123456789C123456789D123456789E" + "923456789A123456789B123456789C123456789D123456789E" + "F23456789A123456789B123456789C123456789D123456789E" + "123456789A123456789B123456789C123456789D123456789E" + "223456789A123456789B123456789C123456789D123456789E" + "323456789A123456789B123456789C123456789D123456789E" + "423456789A123456789B123456789C123456789D123456789E" + "523456789A123456789B123456789C123456789D123456789E" + "623456789A123456789B123456789C123456789D123456789E" + "723456789A123456789B123456789C123456789D123456789E" + "823456789A123456789B123456789C123456789D123456789E" + "923456789A123456789B123456789C123456789D123456789E" + "G23456789A123456789B123456789C123456789D123456789E" + "123456789A123456789B123456789C123456789D123456789E" + "223456789A123456789B123456789C123456789D123456789E" + "323456789A123456789B123456789C123456789D123456789E" + "423456789A123456789B123456789C123456789D123456789E" + "523456789A123456789B123456789C123456789D123456789E" + "623456789A123456789B123456789C123456789D123456789E" + "723456789A123456789B123456789C123456789D123456789E" + "823456789A123456789B123456789C123456789D123456789E" + "923456789A123456789B123456789C123456789D123456789E" + "H23456789A123456789B123456789C123456789D123456789E" + "123456789A123456789B123456789C123456789D123456789E" + "223456789A123456789B123456789C123456789D123456789E" + "323456789A123456789B123456789C123456789D123456789E" + "423456789A123456789B123456789C123456789D123456789E" + "523456789A123456789B123456789C123456789D123456789E" + "623456789A123456789B123456789C123456789D123456789E" + "723456789A123456789B123456789C123456789D123456789E" + "823456789A123456789B123456789C123456789D123456789E" + "923456789A123456789B123456789C123456789D123456789E" + "I23456789A123456789B123456789C123456789D123456789E" + "123456789A123456789B123456789C123456789D123456789E" + "223456789A123456789B123456789C123456789D123456789E" + "323456789A123456789B123456789C123456789D123456789E" + "423456789A123456789B123456789C123456789D123456789E" + "523456789A123456789B123456789C123456789D123456789E" + "623456789A123456789B123456789C123456789D123456789E" + "723456789A123456789B123456789C123456789D123456789E" + "823456789A123456789B123456789C123456789D123456789E" + "923456789A123456789B123456789C123456789D123456789E" + "J23456789A123456789B123456789C123456789D123456789E" + "123456789A123456789B123456789C123456789D123456789E" + "223456789A123456789B123456789C123456789D123456789E" + "323456789A123456789B123456789C123456789D123456789E" + "423456789A123456789B123456789C123456789D123456789E" + "523456789A123456789B123456789C123456789D12345" + }, + { + NULL, + NULL, + }, + }; + int i; + + for (i = 0; data[i].encoded1; i++) + { + apr_size_t len1 = strlen(data[i].encoded1); + + svn_stringbuf_t *actual = svn_stringbuf_create_empty(pool); + svn_stringbuf_t *expected = svn_stringbuf_create_empty(pool); + svn_stream_t *stream = svn_stream_from_stringbuf(actual, pool); + + stream = svn_base64_encode(stream, pool); + stream = svn_base64_decode(stream, pool); + + SVN_ERR(svn_stream_write(stream, data[i].encoded1, &len1)); + svn_stringbuf_appendbytes(expected, data[i].encoded1, len1); + + if (data[i].encoded2) + { + apr_size_t len2 = strlen(data[i].encoded2); + SVN_ERR(svn_stream_write(stream, data[i].encoded2, &len2)); + svn_stringbuf_appendbytes(expected, data[i].encoded2, len2); + } + + SVN_ERR(svn_stream_close(stream)); + } + + return SVN_NO_ERROR; +} + /* The test table. */ struct svn_test_descriptor_t test_funcs[] = @@ -591,5 +750,7 @@ struct svn_test_descriptor_t test_funcs[] = "test compressed streams with empty files"), SVN_TEST_PASS2(test_stream_base64, "test base64 encoding/decoding streams"), + SVN_TEST_PASS2(test_stream_base64_2, + "base64 decoding allocation problem"), SVN_TEST_NULL }; diff --git a/subversion/tests/libsvn_subr/string-test.c b/subversion/tests/libsvn_subr/string-test.c index e6c285a..735db18 100644 --- a/subversion/tests/libsvn_subr/string-test.c +++ b/subversion/tests/libsvn_subr/string-test.c @@ -39,7 +39,7 @@ #include "svn_io.h" #include "svn_error.h" #include "svn_string.h" /* This includes <apr_*.h> */ - +#include "private/svn_string_private.h" /* A quick way to create error messages. */ static svn_error_t * @@ -419,7 +419,7 @@ test15(apr_pool_t *pool) static svn_error_t * test16(apr_pool_t *pool) { - a = svn_stringbuf_create("", pool); + a = svn_stringbuf_create_empty(pool); return test_find_char_backward(a->data, a->len, ',', 0, pool); } @@ -479,7 +479,7 @@ test21(apr_pool_t *pool) svn_stringbuf_strip_whitespace(a); - if (svn_stringbuf_compare(a, b) == TRUE) + if (svn_stringbuf_compare(a, b)) return SVN_NO_ERROR; else return fail(pool, "test failed"); @@ -511,6 +511,234 @@ test23(apr_pool_t *pool) return test_stringbuf_unequal("abc", "abb", pool); } +static svn_error_t * +test24(apr_pool_t *pool) +{ + char buffer[SVN_INT64_BUFFER_SIZE]; + apr_size_t length; + + length = svn__i64toa(buffer, 0); + SVN_TEST_ASSERT(length == 1); + SVN_TEST_STRING_ASSERT(buffer, "0"); + + length = svn__i64toa(buffer, 0x8000000000000000ll); + SVN_TEST_ASSERT(length == 20); + SVN_TEST_STRING_ASSERT(buffer, "-9223372036854775808"); + + length = svn__i64toa(buffer, 0x7fffffffffffffffll); + SVN_TEST_ASSERT(length == 19); + SVN_TEST_STRING_ASSERT(buffer, "9223372036854775807"); + + length = svn__ui64toa(buffer, 0ull); + SVN_TEST_ASSERT(length == 1); + SVN_TEST_STRING_ASSERT(buffer, "0"); + + length = svn__ui64toa(buffer, 0xffffffffffffffffull); + SVN_TEST_ASSERT(length == 20); + SVN_TEST_STRING_ASSERT(buffer, "18446744073709551615"); + + return test_stringbuf_unequal("abc", "abb", pool); +} + +static svn_error_t * +expect_stringbuf_equal(const svn_stringbuf_t* str1, + const char* str2, + apr_pool_t *pool) +{ + if (svn_stringbuf_compare(str1, svn_stringbuf_create(str2, pool))) + return SVN_NO_ERROR; + else + return fail(pool, "test failed"); +} + +static svn_error_t * +test_stringbuf_insert(apr_pool_t *pool) +{ + a = svn_stringbuf_create("st , ", pool); + + svn_stringbuf_insert(a, 0, "teflon", 2); + SVN_TEST_STRING_ASSERT(a->data, "test , "); + + svn_stringbuf_insert(a, 5, "hllo", 4); + SVN_TEST_STRING_ASSERT(a->data, "test hllo, "); + + svn_stringbuf_insert(a, 6, a->data + 1, 1); + SVN_TEST_STRING_ASSERT(a->data, "test hello, "); + + svn_stringbuf_insert(a, 12, "world class", 5); + SVN_TEST_STRING_ASSERT(a->data, "test hello, world"); + + svn_stringbuf_insert(a, 1200, "!", 1); + SVN_TEST_STRING_ASSERT(a->data, "test hello, world!"); + + svn_stringbuf_insert(a, 4, "\0-\0", 3); + SVN_TEST_ASSERT(svn_stringbuf_compare(a, + svn_stringbuf_ncreate("test\0-\0 hello, world!", + 21, pool))); + + svn_stringbuf_insert(a, 14, a->data + 4, 3); + SVN_TEST_ASSERT(svn_stringbuf_compare(a, + svn_stringbuf_ncreate("test\0-\0 hello,\0-\0 world!", + 24, pool))); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_stringbuf_remove(apr_pool_t *pool) +{ + a = svn_stringbuf_create("test hello, world!", pool); + + svn_stringbuf_remove(a, 0, 2); + SVN_TEST_STRING_ASSERT(a->data, "st hello, world!"); + + svn_stringbuf_remove(a, 2, 2); + SVN_TEST_STRING_ASSERT(a->data, "stello, world!"); + + svn_stringbuf_remove(a, 5, 200); + SVN_TEST_STRING_ASSERT(a->data, "stell"); + + svn_stringbuf_remove(a, 1200, 393); + return expect_stringbuf_equal(a, "stell", pool); +} + +static svn_error_t * +test_stringbuf_replace(apr_pool_t *pool) +{ + a = svn_stringbuf_create("odd with some world?", pool); + + svn_stringbuf_replace(a, 0, 3, "tester", 4); + SVN_TEST_STRING_ASSERT(a->data, "test with some world?"); + + svn_stringbuf_replace(a, 5, 10, "hllo, coder", 6); + SVN_TEST_STRING_ASSERT(a->data, "test hllo, world?"); + + svn_stringbuf_replace(a, 6, 0, a->data + 1, 1); + SVN_TEST_STRING_ASSERT(a->data, "test hello, world?"); + + svn_stringbuf_replace(a, 17, 10, "!", 1); + SVN_TEST_STRING_ASSERT(a->data, "test hello, world!"); + + svn_stringbuf_replace(a, 1200, 199, "!!", 2); + SVN_TEST_STRING_ASSERT(a->data, "test hello, world!!!"); + + svn_stringbuf_replace(a, 10, 2, "\0-\0", 3); + SVN_TEST_ASSERT(svn_stringbuf_compare(a, + svn_stringbuf_ncreate("test hello\0-\0world!!!", + 21, pool))); + + svn_stringbuf_replace(a, 10, 3, a->data + 10, 3); + SVN_TEST_ASSERT(svn_stringbuf_compare(a, + svn_stringbuf_ncreate("test hello\0-\0world!!!", + 21, pool))); + + svn_stringbuf_replace(a, 19, 1, a->data + 10, 3); + SVN_TEST_ASSERT(svn_stringbuf_compare(a, + svn_stringbuf_ncreate("test hello\0-\0world!\0-\0!", + 23, pool))); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_string_similarity(apr_pool_t *pool) +{ + const struct sim_score_test_t + { + const char *stra; + const char *strb; + apr_size_t lcs; + int score; + } tests[] = + { +#define SCORE(lcs, len) ((2000 * (lcs) + (len)/2) / (len)) + + /* Equality */ + {"", "", 0, 1000}, + {"quoth", "quoth", 5, SCORE(5, 5+5)}, + + /* Deletion at start */ + {"quoth", "uoth", 4, SCORE(4, 5+4)}, + {"uoth", "quoth", 4, SCORE(4, 4+5)}, + + /* Deletion at end */ + {"quoth", "quot", 4, SCORE(4, 5+4)}, + {"quot", "quoth", 4, SCORE(4, 4+5)}, + + /* Insertion at start */ + {"quoth", "Xquoth", 5, SCORE(5, 5+6)}, + {"Xquoth", "quoth", 5, SCORE(5, 6+5)}, + + /* Insertion at end */ + {"quoth", "quothX", 5, SCORE(5, 5+6)}, + {"quothX", "quoth", 5, SCORE(5, 6+5)}, + + /* Insertion in middle */ + {"quoth", "quoXth", 5, SCORE(5, 5+6)}, + {"quoXth", "quoth", 5, SCORE(5, 6+5)}, + + /* Transposition at start */ + {"quoth", "uqoth", 4, SCORE(4, 5+5)}, + {"uqoth", "quoth", 4, SCORE(4, 5+5)}, + + /* Transposition at end */ + {"quoth", "quoht", 4, SCORE(4, 5+5)}, + {"quoht", "quoth", 4, SCORE(4, 5+5)}, + + /* Transposition in middle */ + {"quoth", "qutoh", 4, SCORE(4, 5+5)}, + {"qutoh", "quoth", 4, SCORE(4, 5+5)}, + + /* Difference */ + {"quoth", "raven", 0, SCORE(0, 5+5)}, + {"raven", "quoth", 0, SCORE(0, 5+5)}, + {"x", "", 0, SCORE(0, 1+0)}, + {"", "x", 0, SCORE(0, 0+1)}, + {"", "quoth", 0, SCORE(0, 0+5)}, + {"quoth", "", 0, SCORE(0, 5+0)}, + {"quoth", "the raven", 2, SCORE(2, 5+9)}, + {"the raven", "quoth", 2, SCORE(2, 5+9)}, + {NULL, NULL} + }; + + const struct sim_score_test_t *t; + svn_membuf_t buffer; + + svn_membuf__create(&buffer, 0, pool); + for (t = tests; t->stra; ++t) + { + apr_size_t lcs; + const unsigned int score = + svn_cstring__similarity(t->stra, t->strb, &buffer, &lcs); + /* + fprintf(stderr, + "lcs %s ~ %s score %.3f (%"APR_SIZE_T_FMT + ") expected %.3f (%"APR_SIZE_T_FMT"))\n", + t->stra, t->strb, score/1000.0, lcs, t->score/1000.0, t->lcs); + */ + if (score != t->score) + return fail(pool, "%s ~ %s score %.3f <> expected %.3f", + t->stra, t->strb, score/1000.0, t->score/1000.0); + + if (lcs != t->lcs) + return fail(pool, + "%s ~ %s lcs %"APR_SIZE_T_FMT + " <> expected %"APR_SIZE_T_FMT, + t->stra, t->strb, lcs, t->lcs); + } + + /* Test partial similarity */ + { + const svn_string_t foo = {"svn:foo", 4}; + const svn_string_t bar = {"svn:bar", 4}; + if (1000 != svn_string__similarity(&foo, &bar, &buffer, NULL)) + return fail(pool, "'%s'[:4] ~ '%s'[:4] found different", + foo.data, bar.data); + } + + return SVN_NO_ERROR; +} + /* ==================================================================== If you add a new test to this file, update this array. @@ -568,5 +796,15 @@ struct svn_test_descriptor_t test_funcs[] = "compare stringbufs; different lengths"), SVN_TEST_PASS2(test23, "compare stringbufs; same length, different content"), + SVN_TEST_PASS2(test24, + "verify i64toa"), + SVN_TEST_PASS2(test_stringbuf_insert, + "check inserting into svn_stringbuf_t"), + SVN_TEST_PASS2(test_stringbuf_remove, + "check deletion from svn_stringbuf_t"), + SVN_TEST_PASS2(test_stringbuf_replace, + "check replacement in svn_stringbuf_t"), + SVN_TEST_PASS2(test_string_similarity, + "test string similarity scores"), SVN_TEST_NULL }; diff --git a/subversion/tests/libsvn_subr/subst_translate-test.c b/subversion/tests/libsvn_subr/subst_translate-test.c index 537c9b3..1e555f2 100644 --- a/subversion/tests/libsvn_subr/subst_translate-test.c +++ b/subversion/tests/libsvn_subr/subst_translate-test.c @@ -23,12 +23,14 @@ #include <locale.h> #include <string.h> +#include <apr_time.h> #include "../svn_test.h" #include "svn_types.h" #include "svn_string.h" #include "svn_subst.h" +#include "svn_hash.h" #define ARRAY_LEN(ary) ((sizeof (ary)) / (sizeof ((ary)[0]))) @@ -96,9 +98,7 @@ test_svn_subst_translate_string2(apr_pool_t *pool) source_string, "ISO-8859-1", FALSE, pool, pool); - SVN_TEST_ASSERT(err != SVN_NO_ERROR); - SVN_TEST_ASSERT(err->apr_err == SVN_ERR_IO_INCONSISTENT_EOL); - svn_error_clear(err); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_IO_INCONSISTENT_EOL); } return SVN_NO_ERROR; @@ -115,7 +115,7 @@ test_svn_subst_translate_string2_null_encoding_helper(apr_pool_t *pool) svn_string_t *new_value = NULL; svn_boolean_t translated_to_utf8 = FALSE; svn_boolean_t translated_line_endings = TRUE; - /* 'Æ', which is 0xc6 in both ISO-8859-1 and Windows-1252 */ + /* 'Æ', which is 0xc6 in both ISO-8859-1 and Windows-1252 */ svn_string_t *source_string = svn_string_create("\xc6", pool); SVN_ERR(svn_subst_translate_string2(&new_value, &translated_to_utf8, @@ -123,8 +123,8 @@ test_svn_subst_translate_string2_null_encoding_helper(apr_pool_t *pool) source_string, NULL, FALSE, pool, pool)); SVN_TEST_STRING_ASSERT(new_value->data, "\xc3\x86"); - SVN_TEST_ASSERT(translated_to_utf8 == TRUE); - SVN_TEST_ASSERT(translated_line_endings == FALSE); + SVN_TEST_ASSERT(translated_to_utf8); + SVN_TEST_ASSERT(!translated_line_endings); } return SVN_NO_ERROR; @@ -248,6 +248,258 @@ test_svn_subst_translate_cstring2(apr_pool_t *pool) return SVN_NO_ERROR; } +static svn_error_t * +test_svn_subst_build_keywords3(apr_pool_t *pool) +{ + /* Test expansion of custom keywords. */ + struct keywords_tests_data + { + const char *keyword_name; + const char *keywords_string; + const char *expanded_keyword; + const char *rev; + const char *url; + const char *repos_root_url; + /* Can't test date since expanded value depends on local clock. */ + const char *author; + } + tests[] = + { + {"FOO", "FOO=%P%_%a%_%b%_%%", + "trunk/foo.txt stsp foo.txt %", + "1234", "http://svn.example.com/repos/trunk/foo.txt", + "http://svn.example.com/repos", "stsp"}, + {"FOO", "FOO=author%_=%_%a", + "author = stsp", + "1234", "http://svn.example.com/repos/trunk/foo.txt", + "http://svn.example.com/repos", "stsp"}, + {"MyKeyword", "MyKeyword=%r%_%u%_%_%a", + "4567 http://svn.example.com/svn/branches/myfile jrandom", + "4567", "http://svn.example.com/svn/branches/myfile", + "http://svn.example.com/svn", "jrandom"}, + {"FreeBSD", "FreeBSD=%H", + "head/README 222812 joel", /* date is not expanded in this test */ + "222812", "http://svn.freebsd.org/base/head/README", + "http://svn.freebsd.org/base", "joel"}, + {"FreeBSD", "FreeBSD=%I", + "README 222812 joel", /* date is not expanded in this test */ + "222812", "http://svn.freebsd.org/base/head/README", + "http://svn.freebsd.org/base", "joel"}, + { NULL, NULL, NULL, NULL, NULL, NULL, NULL} + }; + + const struct keywords_tests_data *t; + + for (t = tests; t->keyword_name != NULL; t++) + { + apr_hash_t *kw; + svn_string_t *expanded_keyword; + + SVN_ERR(svn_subst_build_keywords3(&kw, t->keywords_string, + t->rev, t->url, t->repos_root_url, + 0 /* date */, t->author, pool)); + expanded_keyword = svn_hash_gets(kw, t->keyword_name); + SVN_TEST_ASSERT(expanded_keyword != NULL); + SVN_TEST_STRING_ASSERT(expanded_keyword->data, t->expanded_keyword); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_svn_subst_truncated_keywords(apr_pool_t *pool) +{ + svn_string_t *src_string + = svn_string_create("$Qq: " + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "012345678901234567890123456789012345678901234567" + " $", pool); + svn_stream_t *src_stream = svn_stream_from_string(src_string, pool); + svn_stringbuf_t *dst_stringbuf = svn_stringbuf_create_empty(pool); + svn_stream_t *dst_stream = svn_stream_from_stringbuf(dst_stringbuf, pool); + apr_hash_t *keywords = apr_hash_make(pool); + svn_string_t *expanded + = svn_string_create("01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "012345678901234567890123456789012345678901234567" + "xxxxxxxxxx", + pool); + + /* The source is already at the maximum length. */ + SVN_TEST_ASSERT(src_string->len == SVN_KEYWORD_MAX_LEN); + + svn_hash_sets(keywords, "Qq", expanded); + dst_stream = svn_subst_stream_translated(dst_stream, NULL, FALSE, keywords, + TRUE, pool); + SVN_ERR(svn_stream_copy3(src_stream, dst_stream, NULL, NULL, pool)); + + /* The expanded value would make the keyword longer than the maximum + allowed so it must be truncated; the remaining part of the + expanded value is the same as the source. */ + SVN_TEST_STRING_ASSERT(dst_stringbuf->data, src_string->data); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_one_long_keyword(const char *keyword, + const char *expected, + apr_pool_t *pool) +{ + svn_string_t *src_string; + svn_stream_t *src_stream, *dst_stream; + svn_stringbuf_t *dst_stringbuf, *src_stringbuf; + apr_hash_t *keywords = apr_hash_make(pool); + svn_string_t *expanded = svn_string_create("abcdefg", pool); + + svn_hash_sets(keywords, keyword, expanded); + + /* Expand */ + src_string = svn_string_createf(pool, "$%s$", keyword); + src_stream = svn_stream_from_string(src_string, pool); + dst_stringbuf = svn_stringbuf_create_empty(pool); + dst_stream = svn_stream_from_stringbuf(dst_stringbuf, pool); + dst_stream = svn_subst_stream_translated(dst_stream, NULL, FALSE, keywords, + TRUE, pool); + SVN_ERR(svn_stream_copy3(src_stream, dst_stream, NULL, NULL, pool)); + + SVN_TEST_STRING_ASSERT(dst_stringbuf->data, expected); + + /* Unexpand */ + src_stringbuf = dst_stringbuf; + src_stream = svn_stream_from_stringbuf(src_stringbuf, pool); + dst_stringbuf = svn_stringbuf_create_empty(pool); + dst_stream = svn_stream_from_stringbuf(dst_stringbuf, pool); + dst_stream = svn_subst_stream_translated(dst_stream, NULL, FALSE, keywords, + FALSE, pool); + SVN_ERR(svn_stream_copy3(src_stream, dst_stream, NULL, NULL, pool)); + + SVN_TEST_STRING_ASSERT(dst_stringbuf->data, src_string->data); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_svn_subst_long_keywords(apr_pool_t *pool) +{ + /* The longest keyword that can be expanded to a value: there is + space for one character in the expanded value. */ + const char keyword_p1[] + = "Q" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "012345678901234567890123456789012345678901234567"; + + /* The longest keyword that can be expanded: the value is empty. */ + const char keyword_z[] + = "Q" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "0123456789012345678901234567890123456789012345678"; + + /* One more than the longest keyword that can be expanded. */ + const char keyword_m1[] + = "Q" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789"; + + /* Two more than the longest keyword that can be expanded. */ + const char keyword_m2[] + = "Q" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "0"; + + /* Three more than the longest keyword that can be expanded. */ + const char keyword_m3[] + = "Q" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01"; + + /* Four more than the longest keyword that can be expanded. */ + const char keyword_m4[] + = "Q" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "012"; + + /* Five more than the longest keyword that can be expanded. */ + const char keyword_m5[] + = "Q" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "0123"; + + /* Six more than the longest keyword that can be expanded. */ + const char keyword_m6[] + = "Q" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234"; + + SVN_ERR(test_one_long_keyword(keyword_p1, + apr_psprintf(pool, "$%s: a $", keyword_p1), + pool)); + + SVN_ERR(test_one_long_keyword(keyword_z, + apr_psprintf(pool, "$%s: $", keyword_z), + pool)); + + SVN_ERR(test_one_long_keyword(keyword_m1, + apr_psprintf(pool, "$%s$", keyword_m1), + pool)); + + SVN_ERR(test_one_long_keyword(keyword_m2, + apr_psprintf(pool, "$%s$", keyword_m2), + pool)); + + SVN_ERR(test_one_long_keyword(keyword_m3, + apr_psprintf(pool, "$%s$", keyword_m3), + pool)); + + SVN_ERR(test_one_long_keyword(keyword_m4, + apr_psprintf(pool, "$%s$", keyword_m4), + pool)); + + SVN_ERR(test_one_long_keyword(keyword_m5, + apr_psprintf(pool, "$%s$", keyword_m5), + pool)); + + SVN_ERR(test_one_long_keyword(keyword_m6, + apr_psprintf(pool, "$%s$", keyword_m6), + pool)); + + return SVN_NO_ERROR; +} + struct svn_test_descriptor_t test_funcs[] = { SVN_TEST_NULL, @@ -259,5 +511,11 @@ struct svn_test_descriptor_t test_funcs[] = "test repairing svn_subst_translate_string2()"), SVN_TEST_PASS2(test_svn_subst_translate_cstring2, "test svn_subst_translate_cstring2()"), + SVN_TEST_PASS2(test_svn_subst_build_keywords3, + "test svn_subst_build_keywords3()"), + SVN_TEST_PASS2(test_svn_subst_truncated_keywords, + "test truncated keywords (issue 4349)"), + SVN_TEST_PASS2(test_svn_subst_long_keywords, + "test long keywords (issue 4350)"), SVN_TEST_NULL }; diff --git a/subversion/tests/libsvn_subr/target-test.c b/subversion/tests/libsvn_subr/target-test.c deleted file mode 100644 index 9ab42c1..0000000 --- a/subversion/tests/libsvn_subr/target-test.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * target-test.c: test the condense_targets functions - * - * ==================================================================== - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * ==================================================================== - */ - -#include <apr_general.h> - - -#ifdef _MSC_VER -#include <direct.h> -#define getcwd _getcwd -#else -#include <unistd.h> /* for getcwd() */ -#endif - -#define SVN_DEPRECATED - -#include "svn_pools.h" -#include "svn_path.h" - -#include "../svn_test.h" - -typedef svn_error_t *(*condense_targets_func_t) - (const char **pcommon, - apr_array_header_t **pcondensed_targets, - const apr_array_header_t *targets, - svn_boolean_t remove_redundancies, - apr_pool_t *pool); - -/** Executes function CONDENSE_TARGETS twice - with and without requesting the - * condensed targets list - on TEST_TARGETS (comma sep. string) and compares - * the results with EXP_COMMON and EXP_TARGETS (comma sep. string). - * - * @note: a '%' character at the beginning of EXP_COMMON or EXP_TARGETS will - * be replaced by the current working directory. - * - * Returns an error if any of the comparisons fail. - */ -static svn_error_t * -condense_targets_tests_helper(const char* title, - const char* test_targets, - const char* exp_common, - const char* exp_targets, - const char* func_name, - condense_targets_func_t condense_targets, - apr_pool_t *pool) -{ - apr_array_header_t *targets; - apr_array_header_t *condensed_targets; - const char *common_path, *common_path2, *curdir; - char *token, *iter; - const char *exp_common_abs = exp_common; - int i; - char buf[8192]; - - if (! getcwd(buf, sizeof(buf))) - return svn_error_create(SVN_ERR_BASE, NULL, "getcwd() failed"); - curdir = svn_path_internal_style(buf, pool); - - /* Create the target array */ - targets = apr_array_make(pool, sizeof(test_targets), sizeof(const char *)); - token = apr_strtok(apr_pstrdup(pool, test_targets), ",", &iter); - while (token) - { - APR_ARRAY_PUSH(targets, const char *) = - svn_path_internal_style(token, pool); - token = apr_strtok(NULL, ",", &iter); - }; - - /* Call the function */ - SVN_ERR(condense_targets(&common_path, &condensed_targets, targets, - TRUE, pool)); - - /* Verify the common part with the expected (prefix with cwd). */ - if (*exp_common == '%') - exp_common_abs = apr_pstrcat(pool, curdir, exp_common + 1, (char *)NULL); - - if (strcmp(common_path, exp_common_abs) != 0) - { - return svn_error_createf - (SVN_ERR_TEST_FAILED, NULL, - "%s (test %s) returned %s instead of %s", - func_name, title, - common_path, exp_common_abs); - } - - /* Verify the condensed targets */ - token = apr_strtok(apr_pstrdup(pool, exp_targets), ",", &iter); - for (i = 0; i < condensed_targets->nelts; i++) - { - const char * target = APR_ARRAY_IDX(condensed_targets, i, const char*); - if (token && (*token == '%')) - token = apr_pstrcat(pool, curdir, token + 1, (char *)NULL); - if (! token || - (target && (strcmp(target, token) != 0))) - { - return svn_error_createf - (SVN_ERR_TEST_FAILED, NULL, - "%s (test %s) couldn't find %s in expected targets list", - func_name, title, - target); - } - token = apr_strtok(NULL, ",", &iter); - } - - /* Now ensure it works without the pbasename */ - SVN_ERR(condense_targets(&common_path2, NULL, targets, TRUE, pool)); - - /* Verify the common part again */ - if (strcmp(common_path, common_path2) != 0) - { - return svn_error_createf - (SVN_ERR_TEST_FAILED, NULL, - "%s (test %s): Common path without getting targets %s does not match" \ - "common path with targets %s", - func_name, title, - common_path2, common_path); - } - - return SVN_NO_ERROR; -} - - -static svn_error_t * -test_path_condense_targets(apr_pool_t *pool) -{ - int i; - struct { - const char* title; - const char* targets; - const char* exp_common; - const char* exp_targets; - } tests[] = { - { "normal use", "z/A/B,z/A,z/A/C,z/D/E,z/D/F,z/D,z/G,z/G/H,z/G/I", - "%/z", "A,D,G" }, - {"identical dirs", "z/A,z/A,z/A,z/A", - "%/z/A", "" }, - {"identical files", "z/A/file,z/A/file,z/A/file,z/A/file", - "%/z/A/file", "" }, - {"single dir", "z/A", - "%/z/A", "" }, - {"single file", "z/A/file", - "%/z/A/file", "" }, - {"URLs", "http://host/A/C,http://host/A/C/D,http://host/A/B/D", - "http://host/A", "C,B/D" }, - {"URLs with no common prefix", - "http://host1/A/C,http://host2/A/C/D,http://host3/A/B/D", - "", "http://host1/A/C,http://host2/A/C/D,http://host3/A/B/D" }, - {"file URLs with no common prefix", "file:///A/C,file:///B/D", - "", "file:///A/C,file:///B/D" }, - {"URLs with mixed protocols", - "http://host/A/C,file:///B/D,gopher://host/A", - "", "http://host/A/C,file:///B/D,gopher://host/A" }, - {"mixed paths and URLs", - "z/A/B,z/A,http://host/A/C/D,http://host/A/C", - "", "%/z/A,http://host/A/C" }, - }; - - for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) - { - SVN_ERR(condense_targets_tests_helper(tests[i].title, - tests[i].targets, - tests[i].exp_common, - tests[i].exp_targets, - "svn_path_condense_targets", - svn_path_condense_targets, - pool)); - } - - return SVN_NO_ERROR; -} - - - -/* The test table. */ - -struct svn_test_descriptor_t test_funcs[] = - { - SVN_TEST_NULL, - SVN_TEST_PASS2(test_path_condense_targets, - "test svn_path_condense_targets"), - SVN_TEST_NULL - }; diff --git a/subversion/tests/libsvn_subr/utf-test.c b/subversion/tests/libsvn_subr/utf-test.c index 474e308..2028e14 100644 --- a/subversion/tests/libsvn_subr/utf-test.c +++ b/subversion/tests/libsvn_subr/utf-test.c @@ -197,7 +197,7 @@ utf_validate2(apr_pool_t *pool) /* A random string; experiment shows that it's occasionally (less than 1%) valid but usually invalid. */ for (j = 0; j < sizeof(str) - 1; ++j) - str[j] = range_rand(0, 255); + str[j] = (char)range_rand(0, 255); str[sizeof(str) - 1] = 0; len = strlen(str); diff --git a/subversion/tests/libsvn_subr/zlib.deflated b/subversion/tests/libsvn_subr/zlib.deflated Binary files differnew file mode 100644 index 0000000..73dc6ae --- /dev/null +++ b/subversion/tests/libsvn_subr/zlib.deflated |