summaryrefslogtreecommitdiff
path: root/subversion/tests/libsvn_subr
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/tests/libsvn_subr')
-rw-r--r--subversion/tests/libsvn_subr/auth-test.c128
-rw-r--r--subversion/tests/libsvn_subr/cache-test.c87
-rw-r--r--subversion/tests/libsvn_subr/checksum-test.c188
-rw-r--r--subversion/tests/libsvn_subr/compat-test.c112
-rw-r--r--subversion/tests/libsvn_subr/config-test.c168
-rw-r--r--subversion/tests/libsvn_subr/config-test.cfg9
-rw-r--r--subversion/tests/libsvn_subr/crypto-test.c188
-rw-r--r--subversion/tests/libsvn_subr/dirent_uri-test.c379
-rw-r--r--subversion/tests/libsvn_subr/error-code-test.c83
-rw-r--r--subversion/tests/libsvn_subr/error-test.c45
-rw-r--r--subversion/tests/libsvn_subr/io-test.c527
-rw-r--r--subversion/tests/libsvn_subr/mergeinfo-test.c385
-rw-r--r--subversion/tests/libsvn_subr/named_atomic-test-common.h245
-rw-r--r--subversion/tests/libsvn_subr/named_atomic-test-proc.c86
-rw-r--r--subversion/tests/libsvn_subr/named_atomic-test.c761
-rw-r--r--subversion/tests/libsvn_subr/path-test.c235
-rw-r--r--subversion/tests/libsvn_subr/skel-test.c19
-rw-r--r--subversion/tests/libsvn_subr/spillbuf-test.c433
-rw-r--r--subversion/tests/libsvn_subr/stream-test.c185
-rw-r--r--subversion/tests/libsvn_subr/string-test.c244
-rw-r--r--subversion/tests/libsvn_subr/subst_translate-test.c270
-rw-r--r--subversion/tests/libsvn_subr/target-test.c201
-rw-r--r--subversion/tests/libsvn_subr/utf-test.c2
-rw-r--r--subversion/tests/libsvn_subr/zlib.deflatedbin0 -> 77174 bytes
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
new file mode 100644
index 0000000..73dc6ae
--- /dev/null
+++ b/subversion/tests/libsvn_subr/zlib.deflated
Binary files differ