summaryrefslogtreecommitdiff
path: root/subversion/tests/svn_test_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/tests/svn_test_main.c')
-rw-r--r--subversion/tests/svn_test_main.c660
1 files changed, 570 insertions, 90 deletions
diff --git a/subversion/tests/svn_test_main.c b/subversion/tests/svn_test_main.c
index 15d460c..46c0c45 100644
--- a/subversion/tests/svn_test_main.c
+++ b/subversion/tests/svn_test_main.c
@@ -45,16 +45,30 @@
#include "svn_path.h"
#include "svn_ctype.h"
#include "svn_utf.h"
+#include "svn_version.h"
#include "private/svn_cmdline_private.h"
+#include "private/svn_atomic.h"
+#include "private/svn_mutex.h"
+#include "private/svn_sqlite.h"
#include "svn_private_config.h"
+#if APR_HAS_THREADS
+# include <apr_thread_proc.h>
+#endif
+
/* Some Subversion test programs may want to parse options in the
argument list, so we remember it here. */
+extern int test_argc;
+extern const char **test_argv;
int test_argc;
const char **test_argv;
+/* Many tests write to disk. Instead of writing to the current
+ directory, they should use this path as the root of the test data
+ area. */
+static const char *data_path;
/* Test option: Print more output */
static svn_boolean_t verbose_mode = FALSE;
@@ -70,10 +84,13 @@ static svn_boolean_t allow_segfaults = FALSE;
/* Test option: Limit testing to a given mode (i.e. XFail, Skip,
Pass, All). */
-enum svn_test_mode_t mode_filter = svn_test_all;
+static enum svn_test_mode_t mode_filter = svn_test_all;
+
+/* Test option: Allow concurrent execution of tests */
+static svn_boolean_t parallel = FALSE;
/* Option parsing enums and structures */
-enum {
+enum test_options_e {
help_opt = SVN_OPT_FIRST_LONGOPT_ID,
cleanup_opt,
fstype_opt,
@@ -84,7 +101,13 @@ enum {
server_minor_version_opt,
allow_segfault_opt,
srcdir_opt,
- mode_filter_opt
+ reposdir_opt,
+ reposurl_opt,
+ repostemplate_opt,
+ mode_filter_opt,
+ sqlite_log_opt,
+ parallel_opt,
+ fsfs_version_opt
};
static const apr_getopt_option_t cl_options[] =
@@ -97,6 +120,8 @@ static const apr_getopt_option_t cl_options[] =
N_("specify test config file ARG")},
{"fs-type", fstype_opt, 1,
N_("specify a filesystem backend type ARG")},
+ {"fsfs-version", fsfs_version_opt, 1,
+ N_("specify the FSFS version ARG")},
{"list", list_opt, 0,
N_("lists all the tests with their short description")},
{"mode-filter", mode_filter_opt, 1,
@@ -112,7 +137,17 @@ static const apr_getopt_option_t cl_options[] =
{"allow-segfaults", allow_segfault_opt, 0,
N_("don't trap seg faults (useful for debugging)")},
{"srcdir", srcdir_opt, 1,
- N_("source directory")},
+ N_("directory which contains test's C source files")},
+ {"repos-dir", reposdir_opt, 1,
+ N_("directory to create repositories in")},
+ {"repos-url", reposurl_opt, 1,
+ N_("the url to access reposdir as")},
+ {"repos-template",repostemplate_opt, 1,
+ N_("the repository to use as template")},
+ {"sqlite-logging", sqlite_log_opt, 0,
+ N_("enable SQLite logging")},
+ {"parallel", parallel_opt, 0,
+ N_("allow concurrent execution of tests")},
{0, 0, 0, 0}
};
@@ -123,8 +158,50 @@ static const apr_getopt_option_t cl_options[] =
/* When non-zero, don't remove test directories */
static svn_boolean_t skip_cleanup = FALSE;
-/* All cleanup actions are registered as cleanups on this pool. */
+/* All cleanup actions are registered as cleanups on the cleanup_pool,
+ * which may be thread-specific. */
+#if APR_HAS_THREADS
+/* The thread-local data key for the cleanup pool. */
+static apr_threadkey_t *cleanup_pool_key = NULL;
+
+/* No-op destructor for apr_threadkey_private_create(). */
+static void null_threadkey_dtor(void *stuff) {}
+
+/* Set the thread-specific cleanup pool. */
+static void set_cleanup_pool(apr_pool_t *pool)
+{
+ apr_status_t status = apr_threadkey_private_set(pool, cleanup_pool_key);
+ if (status)
+ {
+ printf("apr_threadkey_private_set() failed with code %ld.\n",
+ (long)status);
+ exit(1);
+ }
+}
+
+/* Get the thread-specific cleanup pool. */
+static apr_pool_t *get_cleanup_pool()
+{
+ void *data;
+ apr_status_t status = apr_threadkey_private_get(&data, cleanup_pool_key);
+ if (status)
+ {
+ printf("apr_threadkey_private_get() failed with code %ld.\n",
+ (long)status);
+ exit(1);
+ }
+ return data;
+}
+
+# define cleanup_pool (get_cleanup_pool())
+# define HAVE_PER_THREAD_CLEANUP
+#else
static apr_pool_t *cleanup_pool = NULL;
+# define set_cleanup_pool(p) (cleanup_pool = (p))
+#endif
+
+/* Used by test_thread to serialize access to stdout. */
+static svn_mutex__t *log_mutex = NULL;
static apr_status_t
cleanup_rmtree(void *data)
@@ -150,19 +227,41 @@ cleanup_rmtree(void *data)
}
+
void
svn_test_add_dir_cleanup(const char *path)
{
if (cleanup_mode)
{
const char *abspath;
- svn_error_t *err = svn_path_get_absolute(&abspath, path, cleanup_pool);
+ svn_error_t *err;
+
+ /* All cleanup functions use the *same* pool (not subpools of it).
+ Thus, we need to synchronize. */
+ err = svn_mutex__lock(log_mutex);
+ if (err)
+ {
+ if (verbose_mode)
+ printf("FAILED svn_mutex__lock in svn_test_add_dir_cleanup.\n");
+ svn_error_clear(err);
+ return;
+ }
+
+ err = svn_path_get_absolute(&abspath, path, cleanup_pool);
svn_error_clear(err);
if (!err)
apr_pool_cleanup_register(cleanup_pool, abspath, cleanup_rmtree,
apr_pool_cleanup_null);
else if (verbose_mode)
printf("FAILED ABSPATH: %s\n", path);
+
+ err = svn_mutex__unlock(log_mutex, NULL);
+ if (err)
+ {
+ if (verbose_mode)
+ printf("FAILED svn_mutex__unlock in svn_test_add_dir_cleanup.\n");
+ svn_error_clear(err);
+ }
}
}
@@ -183,7 +282,7 @@ svn_test_rand(apr_uint32_t *seed)
/* Determine the array size of test_funcs[], the inelegant way. :) */
static int
-get_array_size(void)
+get_array_size(struct svn_test_descriptor_t *test_funcs)
{
int i;
@@ -205,6 +304,91 @@ crash_handler(int signum)
longjmp(jump_buffer, 1);
}
+/* Write the result of test number TEST_NUM to stdout. Pretty-print test
+ name and dots according to our test-suite spec, and return TRUE if there
+ has been a test failure.
+
+ The parameters are basically the internal state of do_test_num() and
+ test_thread(). */
+/* */
+static svn_boolean_t
+log_results(const char *progname,
+ int test_num,
+ svn_boolean_t msg_only,
+ svn_boolean_t run_this_test,
+ svn_boolean_t skip,
+ svn_boolean_t xfail,
+ svn_boolean_t wimp,
+ svn_error_t *err,
+ const char *msg,
+ const struct svn_test_descriptor_t *desc)
+{
+ svn_boolean_t test_failed;
+
+ if (err && err->apr_err == SVN_ERR_TEST_SKIPPED)
+ {
+ svn_error_clear(err);
+ err = SVN_NO_ERROR;
+ skip = TRUE;
+ xfail = FALSE; /* Or all XFail tests reporting SKIP would be failing */
+ }
+
+ /* Failure means unexpected results -- FAIL or XPASS. */
+ test_failed = (!wimp && ((err != SVN_NO_ERROR) != (xfail != 0)));
+
+ /* If we got an error, print it out. */
+ if (err)
+ {
+ svn_handle_error2(err, stdout, FALSE, "svn_tests: ");
+ svn_error_clear(err);
+ }
+
+ if (msg_only)
+ {
+ const svn_boolean_t otoh = !!desc->predicate.description;
+
+ if (run_this_test)
+ printf(" %3d %-5s %s%s%s%s%s%s\n",
+ test_num,
+ (xfail ? "XFAIL" : (skip ? "SKIP" : "")),
+ msg ? msg : "(test did not provide name)",
+ (wimp && verbose_mode) ? " [[" : "",
+ (wimp && verbose_mode) ? desc->wip : "",
+ (wimp && verbose_mode) ? "]]" : "",
+ (otoh ? " / " : ""),
+ (otoh ? desc->predicate.description : ""));
+ }
+ else if (run_this_test && ((! quiet_mode) || test_failed))
+ {
+ printf("%s %s %d: %s%s%s%s\n",
+ (err
+ ? (xfail ? "XFAIL:" : "FAIL: ")
+ : (xfail ? "XPASS:" : (skip ? "SKIP: " : "PASS: "))),
+ progname,
+ test_num,
+ msg ? msg : "(test did not provide name)",
+ wimp ? " [[WIMP: " : "",
+ wimp ? desc->wip : "",
+ wimp ? "]]" : "");
+ }
+
+ if (msg)
+ {
+ size_t len = strlen(msg);
+ if (len > 50)
+ printf("WARNING: Test docstring exceeds 50 characters\n");
+ if (msg[len - 1] == '.')
+ printf("WARNING: Test docstring ends in a period (.)\n");
+ if (svn_ctype_isupper(msg[0]))
+ printf("WARNING: Test docstring is capitalized\n");
+ }
+ if (desc->msg == NULL)
+ printf("WARNING: New-style test descriptor is missing a docstring.\n");
+
+ fflush(stdout);
+
+ return test_failed;
+}
/* Execute a test number TEST_NUM. Pretty-print test name and dots
according to our test-suite spec, and return the result code.
@@ -213,6 +397,7 @@ crash_handler(int signum)
static svn_boolean_t
do_test_num(const char *progname,
int test_num,
+ struct svn_test_descriptor_t *test_funcs,
svn_boolean_t msg_only,
svn_test_opts_t *opts,
const char **header_msg,
@@ -220,11 +405,11 @@ do_test_num(const char *progname,
{
svn_boolean_t skip, xfail, wimp;
svn_error_t *err = NULL;
- svn_boolean_t test_failed;
const char *msg = NULL; /* the message this individual test prints out */
const struct svn_test_descriptor_t *desc;
- const int array_size = get_array_size();
+ const int array_size = get_array_size(test_funcs);
svn_boolean_t run_this_test; /* This test's mode matches DESC->MODE. */
+ enum svn_test_mode_t test_mode;
/* Check our array bounds! */
if (test_num < 0)
@@ -239,11 +424,18 @@ do_test_num(const char *progname,
}
desc = &test_funcs[test_num];
- skip = desc->mode == svn_test_skip;
- xfail = desc->mode == svn_test_xfail;
+ /* Check the test predicate. */
+ if (desc->predicate.func
+ && desc->predicate.func(opts, desc->predicate.value, pool))
+ test_mode = desc->predicate.alternate_mode;
+ else
+ test_mode = desc->mode;
+
+ skip = test_mode == svn_test_skip;
+ xfail = test_mode == svn_test_xfail;
wimp = xfail && desc->wip;
msg = desc->msg;
- run_this_test = mode_filter == svn_test_all || mode_filter == desc->mode;
+ run_this_test = mode_filter == svn_test_all || mode_filter == test_mode;
if (run_this_test && header_msg && *header_msg)
{
@@ -272,13 +464,6 @@ do_test_num(const char *progname,
err = (*desc->func2)(pool);
else
err = (*desc->func_opts)(opts, pool);
-
- if (err && err->apr_err == SVN_ERR_TEST_SKIPPED)
- {
- svn_error_clear(err);
- err = SVN_NO_ERROR;
- skip = TRUE;
- }
}
else
err = svn_error_create(SVN_ERR_TEST_FAILED, NULL,
@@ -292,60 +477,171 @@ do_test_num(const char *progname,
}
/* Failure means unexpected results -- FAIL or XPASS. */
- test_failed = (!wimp && ((err != SVN_NO_ERROR) != (xfail != 0)));
+ skip_cleanup = log_results(progname, test_num, msg_only, run_this_test,
+ skip, xfail, wimp, err, msg, desc);
- /* If we got an error, print it out. */
- if (err)
- {
- svn_handle_error2(err, stdout, FALSE, "svn_tests: ");
- svn_error_clear(err);
- }
+ return skip_cleanup;
+}
- if (msg_only)
+#if APR_HAS_THREADS
+
+/* Per-test parameters used by test_thread */
+typedef struct test_params_t
+{
+ /* Name of the application */
+ const char *progname;
+
+ /* Total number of tests to execute */
+ svn_atomic_t test_count;
+
+ /* Global test options as provided by main() */
+ svn_test_opts_t *opts;
+
+ /* Reference to the global failure flag. Set this if any test failed. */
+ svn_atomic_t got_error;
+
+ /* Test to execute next. */
+ svn_atomic_t test_num;
+
+ /* Test functions array. */
+ struct svn_test_descriptor_t *test_funcs;
+} test_params_t;
+
+/* Thread function similar to do_test_num() but with fewer options. We do
+ catch segfaults. All parameters are given as a test_params_t in DATA.
+ */
+static void * APR_THREAD_FUNC
+test_thread(apr_thread_t *thread, void *data)
+{
+ svn_boolean_t skip, xfail, wimp;
+ svn_error_t *err;
+ const struct svn_test_descriptor_t *desc;
+ svn_boolean_t run_this_test; /* This test's mode matches DESC->MODE. */
+ enum svn_test_mode_t test_mode;
+ test_params_t *params = data;
+ svn_atomic_t test_num;
+ apr_pool_t *pool;
+ apr_pool_t *thread_root
+ = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
+
+#ifdef HAVE_PER_THREAD_CLEANUP
+ set_cleanup_pool(svn_pool_create(thread_root));
+#endif
+
+ pool = svn_pool_create(thread_root);
+
+ for (test_num = svn_atomic_inc(&params->test_num);
+ test_num <= params->test_count;
+ test_num = svn_atomic_inc(&params->test_num))
{
- if (run_this_test)
- printf(" %3d %-5s %s%s%s%s\n",
- test_num,
- (xfail ? "XFAIL" : (skip ? "SKIP" : "")),
- msg ? msg : "(test did not provide name)",
- (wimp && verbose_mode) ? " [[" : "",
- (wimp && verbose_mode) ? desc->wip : "",
- (wimp && verbose_mode) ? "]]" : "");
+ svn_pool_clear(pool);
+#ifdef HAVE_PER_THREAD_CLEANUP
+ svn_pool_clear(cleanup_pool); /* after clearing pool*/
+#endif
+
+ desc = &params->test_funcs[test_num];
+ /* Check the test predicate. */
+ if (desc->predicate.func
+ && desc->predicate.func(params->opts, desc->predicate.value, pool))
+ test_mode = desc->predicate.alternate_mode;
+ else
+ test_mode = desc->mode;
+
+ skip = test_mode == svn_test_skip;
+ xfail = test_mode == svn_test_xfail;
+ wimp = xfail && desc->wip;
+ run_this_test = mode_filter == svn_test_all
+ || mode_filter == test_mode;
+
+ /* Do test */
+ if (skip || !run_this_test)
+ err = NULL; /* pass */
+ else if (desc->func2)
+ err = (*desc->func2)(pool);
+ else
+ err = (*desc->func_opts)(params->opts, pool);
+
+ /* Write results to console */
+ svn_error_clear(svn_mutex__lock(log_mutex));
+ if (log_results(params->progname, test_num, FALSE, run_this_test,
+ skip, xfail, wimp, err, desc->msg, desc))
+ svn_atomic_set(&params->got_error, TRUE);
+ svn_error_clear(svn_mutex__unlock(log_mutex, NULL));
}
- else if (run_this_test && ((! quiet_mode) || test_failed))
+
+ svn_pool_clear(pool); /* Make sure this is cleared before cleanup_pool*/
+
+ /* Release all test memory. Possibly includes cleanup_pool */
+ svn_pool_destroy(thread_root);
+
+ /* End thread explicitly to prevent APR_INCOMPLETE return codes in
+ apr_thread_join(). */
+ apr_thread_exit(thread, 0);
+ return NULL;
+}
+
+/* Log an error with message MSG if the APR status of EXPR is not 0.
+ */
+#define CHECK_STATUS(expr,msg) \
+ do { \
+ apr_status_t rv = (expr); \
+ if (rv) \
+ { \
+ svn_error_t *svn_err__temp = svn_error_wrap_apr(rv, msg); \
+ svn_handle_error2(svn_err__temp, stdout, FALSE, "svn_tests: "); \
+ svn_error_clear(svn_err__temp); \
+ } \
+ } while (0);
+
+/* Execute all ARRAY_SIZE tests concurrently using MAX_THREADS threads.
+ Pass PROGNAME and OPTS to the individual tests. Return TRUE if at least
+ one of the tests failed. Allocate all data in POOL.
+
+ Note that cleanups are delayed until all tests have been completed.
+ */
+static svn_boolean_t
+do_tests_concurrently(const char *progname,
+ struct svn_test_descriptor_t *test_funcs,
+ int array_size,
+ int max_threads,
+ svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ int i;
+ apr_thread_t **threads;
+
+ /* Prepare thread parameters. */
+ test_params_t params;
+ params.got_error = FALSE;
+ params.opts = opts;
+ params.progname = progname;
+ params.test_num = 1;
+ params.test_funcs = test_funcs;
+ params.test_count = array_size;
+
+ /* Start all threads. */
+ threads = apr_pcalloc(pool, max_threads * sizeof(*threads));
+ for (i = 0; i < max_threads; ++i)
{
- printf("%s %s %d: %s%s%s%s\n",
- (err
- ? (xfail ? "XFAIL:" : "FAIL: ")
- : (xfail ? "XPASS:" : (skip ? "SKIP: " : "PASS: "))),
- progname,
- test_num,
- msg ? msg : "(test did not provide name)",
- wimp ? " [[WIMP: " : "",
- wimp ? desc->wip : "",
- wimp ? "]]" : "");
+ CHECK_STATUS(apr_thread_create(&threads[i], NULL, test_thread, &params,
+ pool),
+ "creating test thread failed.\n");
}
- if (msg)
+ /* Wait for all tasks (tests) to complete. */
+ for (i = 0; i < max_threads; ++i)
{
- size_t len = strlen(msg);
- if (len > 50)
- printf("WARNING: Test docstring exceeds 50 characters\n");
- if (msg[len - 1] == '.')
- printf("WARNING: Test docstring ends in a period (.)\n");
- if (svn_ctype_isupper(msg[0]))
- printf("WARNING: Test docstring is capitalized\n");
+ apr_status_t result = 0;
+ CHECK_STATUS(apr_thread_join(&result, threads[i]),
+ "Waiting for test thread to finish failed.");
+ CHECK_STATUS(result,
+ "Test thread returned an error.");
}
- if (desc->msg == NULL)
- printf("WARNING: New-style test descriptor is missing a docstring.\n");
- fflush(stdout);
-
- skip_cleanup = test_failed;
-
- return test_failed;
+ return params.got_error != FALSE;
}
+#endif
static void help(const char *progname, apr_pool_t *pool)
{
@@ -366,12 +662,106 @@ static void help(const char *progname, apr_pool_t *pool)
svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
}
+static svn_error_t *init_test_data(const char *argv0, apr_pool_t *pool)
+{
+ const char *temp_path;
+ const char *base_name;
+
+ /* Convert the program path to an absolute path. */
+ SVN_ERR(svn_utf_cstring_to_utf8(&temp_path, argv0, pool));
+ temp_path = svn_dirent_internal_style(temp_path, pool);
+ SVN_ERR(svn_dirent_get_absolute(&temp_path, temp_path, pool));
+ SVN_ERR_ASSERT(!svn_dirent_is_root(temp_path, strlen(temp_path)));
+
+ /* Extract the interesting bits of the path. */
+ temp_path = svn_dirent_dirname(temp_path, pool);
+ base_name = svn_dirent_basename(temp_path, pool);
+ if (0 == strcmp(base_name, ".libs"))
+ {
+ /* This is a libtoolized binary, skip the .libs directory. */
+ temp_path = svn_dirent_dirname(temp_path, pool);
+ base_name = svn_dirent_basename(temp_path, pool);
+ }
+ temp_path = svn_dirent_dirname(temp_path, pool);
+
+ /* temp_path should now point to the root of the test
+ builddir. Construct the path to the transient dir. Note that we
+ put the path insinde the cmdline/svn-test-work area. This is
+ because trying to get the cmdline tests to use a different work
+ area is unprintable; so we put the C test transient dir in the
+ cmdline tests area, as the lesser of evils ... */
+ temp_path = svn_dirent_join_many(pool, temp_path,
+ "cmdline", "svn-test-work",
+ base_name, SVN_VA_NULL);
+
+ /* Finally, create the transient directory. */
+ SVN_ERR(svn_io_make_dir_recursively(temp_path, pool));
+
+ data_path = temp_path;
+ return SVN_NO_ERROR;
+}
+
+const char *
+svn_test_data_path(const char *base_name, apr_pool_t *result_pool)
+{
+ return svn_dirent_join(data_path, base_name, result_pool);
+}
+
+svn_error_t *
+svn_test_get_srcdir(const char **srcdir,
+ const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ const char *cwd;
+
+ if (opts->srcdir)
+ {
+ *srcdir = opts->srcdir;
+ return SVN_NO_ERROR;
+ }
+
+ fprintf(stderr, "WARNING: missing '--srcdir' option");
+ SVN_ERR(svn_dirent_get_absolute(&cwd, ".", pool));
+ fprintf(stderr, ", assuming '%s'\n", cwd);
+ *srcdir = cwd;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_test__init_auth_baton(svn_auth_baton_t **ab,
+ apr_pool_t *result_pool)
+{
+ svn_config_t *cfg_config;
+
+ SVN_ERR(svn_config_create2(&cfg_config, FALSE, FALSE, result_pool));
+
+ /* Disable the crypto backends that might not be entirely
+ threadsafe and/or compatible with running headless.
+
+ The windows system is just our own files, but then with user-key
+ encrypted data inside. */
+ svn_config_set(cfg_config,
+ SVN_CONFIG_SECTION_AUTH,
+ SVN_CONFIG_OPTION_PASSWORD_STORES,
+ "windows-cryptoapi");
+
+ SVN_ERR(svn_cmdline_create_auth_baton(ab,
+ TRUE /* non_interactive */,
+ "jrandom", "rayjandom",
+ NULL,
+ TRUE /* no_auth_cache */,
+ FALSE /* trust_server_cert */,
+ cfg_config, NULL, NULL, result_pool));
+
+ return SVN_NO_ERROR;
+}
/* Standard svn test program */
int
-main(int argc, const char *argv[])
+svn_test_main(int argc, const char *argv[], int max_threads,
+ struct svn_test_descriptor_t *test_funcs)
{
- const char *prog_name;
int i;
svn_boolean_t got_error = FALSE;
apr_pool_t *pool, *test_pool;
@@ -383,7 +773,7 @@ main(int argc, const char *argv[])
svn_error_t *err;
char errmsg[200];
/* How many tests are there? */
- int array_size = get_array_size();
+ int array_size = get_array_size(test_funcs);
svn_test_opts_t opts = { NULL };
@@ -400,31 +790,61 @@ main(int argc, const char *argv[])
* usage but make it thread-safe to allow for multi-threaded tests.
*/
pool = apr_allocator_owner_get(svn_pool_create_allocator(TRUE));
+ err = svn_mutex__init(&log_mutex, TRUE, pool);
+ if (err)
+ {
+ svn_handle_error2(err, stderr, TRUE, "svn_tests: ");
+ svn_error_clear(err);
+ }
+
+ /* Set up the thread-local storage key for the cleanup pool. */
+#ifdef HAVE_PER_THREAD_CLEANUP
+ apr_err = apr_threadkey_private_create(&cleanup_pool_key,
+ null_threadkey_dtor,
+ pool);
+ if (apr_err)
+ {
+ printf("apr_threadkey_private_create() failed with code %ld.\n",
+ (long)apr_err);
+ exit(1);
+ }
+#endif /* HAVE_PER_THREAD_CLEANUP */
/* Remember the command line */
test_argc = argc;
test_argv = argv;
+ err = init_test_data(argv[0], pool);
+ if (err)
+ {
+ svn_handle_error2(err, stderr, TRUE, "svn_tests: ");
+ svn_error_clear(err);
+ }
+
err = svn_cmdline__getopt_init(&os, argc, argv, pool);
+ if (err)
+ {
+ svn_handle_error2(err, stderr, TRUE, "svn_tests: ");
+ svn_error_clear(err);
+ }
+
os->interleave = TRUE; /* Let options and arguments be interleaved */
/* Strip off any leading path components from the program name. */
- prog_name = strrchr(argv[0], '/');
- if (prog_name)
- prog_name++;
- else
- {
- /* Just check if this is that weird platform that uses \ instead
- of / for the path separator. */
- prog_name = strrchr(argv[0], '\\');
- if (prog_name)
- prog_name++;
- else
- prog_name = argv[0];
- }
+ opts.prog_name = svn_dirent_internal_style(argv[0], pool);
+ opts.prog_name = svn_dirent_basename(opts.prog_name, NULL);
#ifdef WIN32
+ /* Abuse cast in strstr() to remove .exe extension.
+ Value is allocated in pool by svn_dirent_internal_style() */
+ {
+ char *exe_ext = strstr(opts.prog_name, ".exe");
+
+ if (exe_ext)
+ *exe_ext = '\0';
+ }
+
#if _MSC_VER >= 1400
/* ### This should work for VC++ 2002 (=1300) and later */
/* Show the abort message on STDERR instead of a dialog to allow
@@ -446,7 +866,7 @@ main(int argc, const char *argv[])
#endif
if (err)
- return svn_cmdline_handle_exit_error(err, pool, prog_name);
+ return svn_cmdline_handle_exit_error(err, pool, opts.prog_name);
while (1)
{
const char *opt_arg;
@@ -457,7 +877,7 @@ main(int argc, const char *argv[])
break;
else if (apr_err && (apr_err != APR_BADCH))
{
- /* Ignore invalid option error to allow passing arbitary options */
+ /* Ignore invalid option error to allow passing arbitrary options */
fprintf(stderr, "apr_getopt_long failed : [%d] %s\n",
apr_err, apr_strerror(apr_err, errmsg, sizeof(errmsg)));
exit(1);
@@ -465,7 +885,7 @@ main(int argc, const char *argv[])
switch (opt_id) {
case help_opt:
- help(prog_name, pool);
+ help(opts.prog_name, pool);
exit(0);
case cleanup_opt:
cleanup_mode = TRUE;
@@ -480,6 +900,20 @@ main(int argc, const char *argv[])
SVN_INT_ERR(svn_utf_cstring_to_utf8(&opts.srcdir, opt_arg, pool));
opts.srcdir = svn_dirent_internal_style(opts.srcdir, pool);
break;
+ case reposdir_opt:
+ SVN_INT_ERR(svn_utf_cstring_to_utf8(&opts.repos_dir, opt_arg, pool));
+ opts.repos_dir = svn_dirent_internal_style(opts.repos_dir, pool);
+ break;
+ case reposurl_opt:
+ SVN_INT_ERR(svn_utf_cstring_to_utf8(&opts.repos_url, opt_arg, pool));
+ opts.repos_url = svn_uri_canonicalize(opts.repos_url, pool);
+ break;
+ case repostemplate_opt:
+ SVN_INT_ERR(svn_utf_cstring_to_utf8(&opts.repos_template, opt_arg,
+ pool));
+ opts.repos_template = svn_dirent_internal_style(opts.repos_template,
+ pool);
+ break;
case list_opt:
list_mode = TRUE;
break;
@@ -518,14 +952,24 @@ main(int argc, const char *argv[])
exit(1);
}
if ((opts.server_minor_version < 3)
- || (opts.server_minor_version > 6))
+ || (opts.server_minor_version > SVN_VER_MINOR))
{
fprintf(stderr, "FAIL: Invalid minor version given\n");
exit(1);
}
+ break;
}
+ case sqlite_log_opt:
+ svn_sqlite__dbg_enable_errorlog();
+ break;
+#if APR_HAS_THREADS
+ case parallel_opt:
+ parallel = TRUE;
+ break;
+#endif
}
}
+ opts.verbose = verbose_mode;
/* Disable sleeping for timestamps, to speed up the tests. */
apr_env_set(
@@ -540,7 +984,7 @@ main(int argc, const char *argv[])
}
/* Create an iteration pool for the tests */
- cleanup_pool = svn_pool_create(pool);
+ set_cleanup_pool(svn_pool_create(pool));
test_pool = svn_pool_create(pool);
if (!allow_segfaults)
@@ -558,8 +1002,8 @@ main(int argc, const char *argv[])
"------ ----- ----------------\n";
for (i = 1; i <= array_size; i++)
{
- if (do_test_num(prog_name, i, TRUE, &opts, &header_msg,
- test_pool))
+ if (do_test_num(opts.prog_name, i, test_funcs,
+ TRUE, &opts, &header_msg, test_pool))
got_error = TRUE;
/* Clear the per-function pool */
@@ -579,8 +1023,8 @@ main(int argc, const char *argv[])
continue;
ran_a_test = TRUE;
- if (do_test_num(prog_name, test_num, FALSE, &opts, NULL,
- test_pool))
+ if (do_test_num(opts.prog_name, test_num, test_funcs,
+ FALSE, &opts, NULL, test_pool))
got_error = TRUE;
/* Clear the per-function pool */
@@ -594,15 +1038,34 @@ main(int argc, const char *argv[])
if (! ran_a_test)
{
/* just run all tests */
- for (i = 1; i <= array_size; i++)
+ if (max_threads < 1)
+ max_threads = array_size;
+
+ if (max_threads == 1 || !parallel)
{
- if (do_test_num(prog_name, i, FALSE, &opts, NULL, test_pool))
- got_error = TRUE;
+ for (i = 1; i <= array_size; i++)
+ {
+ if (do_test_num(opts.prog_name, i, test_funcs,
+ FALSE, &opts, NULL, test_pool))
+ got_error = TRUE;
- /* Clear the per-function pool */
+ /* Clear the per-function pool */
+ svn_pool_clear(test_pool);
+ svn_pool_clear(cleanup_pool);
+ }
+ }
+#if APR_HAS_THREADS
+ else
+ {
+ got_error = do_tests_concurrently(opts.prog_name, test_funcs,
+ array_size, max_threads,
+ &opts, test_pool);
+
+ /* Execute all cleanups */
svn_pool_clear(test_pool);
svn_pool_clear(cleanup_pool);
}
+#endif
}
/* Clean up APR */
@@ -611,3 +1074,20 @@ main(int argc, const char *argv[])
return got_error;
}
+
+
+svn_boolean_t
+svn_test__fs_type_is(const svn_test_opts_t *opts,
+ const char *predicate_value,
+ apr_pool_t *pool)
+{
+ return (0 == strcmp(predicate_value, opts->fs_type));
+}
+
+svn_boolean_t
+svn_test__fs_type_not(const svn_test_opts_t *opts,
+ const char *predicate_value,
+ apr_pool_t *pool)
+{
+ return (0 != strcmp(predicate_value, opts->fs_type));
+}