112
137
{"allow-segfaults", allow_segfault_opt, 0,
113
138
N_("don't trap seg faults (useful for debugging)")},
114
139
{"srcdir", srcdir_opt, 1,
115
N_("source directory")},
140
N_("directory which contains test's C source files")},
141
{"repos-dir", reposdir_opt, 1,
142
N_("directory to create repositories in")},
143
{"repos-url", reposurl_opt, 1,
144
N_("the url to access reposdir as")},
145
{"repos-template",repostemplate_opt, 1,
146
N_("the repository to use as template")},
147
{"sqlite-logging", sqlite_log_opt, 0,
148
N_("enable SQLite logging")},
149
{"parallel", parallel_opt, 0,
150
N_("allow concurrent execution of tests")},
123
158
/* When non-zero, don't remove test directories */
124
159
static svn_boolean_t skip_cleanup = FALSE;
126
/* All cleanup actions are registered as cleanups on this pool. */
161
/* All cleanup actions are registered as cleanups on the cleanup_pool,
162
* which may be thread-specific. */
164
/* The thread-local data key for the cleanup pool. */
165
static apr_threadkey_t *cleanup_pool_key = NULL;
167
/* No-op destructor for apr_threadkey_private_create(). */
168
static void null_threadkey_dtor(void *stuff) {}
170
/* Set the thread-specific cleanup pool. */
171
static void set_cleanup_pool(apr_pool_t *pool)
173
apr_status_t status = apr_threadkey_private_set(pool, cleanup_pool_key);
176
printf("apr_threadkey_private_set() failed with code %ld.\n",
182
/* Get the thread-specific cleanup pool. */
183
static apr_pool_t *get_cleanup_pool()
186
apr_status_t status = apr_threadkey_private_get(&data, cleanup_pool_key);
189
printf("apr_threadkey_private_get() failed with code %ld.\n",
196
# define cleanup_pool (get_cleanup_pool())
197
# define HAVE_PER_THREAD_CLEANUP
127
199
static apr_pool_t *cleanup_pool = NULL;
200
# define set_cleanup_pool(p) (cleanup_pool = (p))
203
/* Used by test_thread to serialize access to stdout. */
204
static svn_mutex__t *log_mutex = NULL;
129
206
static apr_status_t
130
207
cleanup_rmtree(void *data)
205
304
longjmp(jump_buffer, 1);
307
/* Write the result of test number TEST_NUM to stdout. Pretty-print test
308
name and dots according to our test-suite spec, and return TRUE if there
309
has been a test failure.
311
The parameters are basically the internal state of do_test_num() and
315
log_results(const char *progname,
317
svn_boolean_t msg_only,
318
svn_boolean_t run_this_test,
324
const struct svn_test_descriptor_t *desc)
326
svn_boolean_t test_failed;
328
if (err && err->apr_err == SVN_ERR_TEST_SKIPPED)
330
svn_error_clear(err);
333
xfail = FALSE; /* Or all XFail tests reporting SKIP would be failing */
336
/* Failure means unexpected results -- FAIL or XPASS. */
337
test_failed = (!wimp && ((err != SVN_NO_ERROR) != (xfail != 0)));
339
/* If we got an error, print it out. */
342
svn_handle_error2(err, stdout, FALSE, "svn_tests: ");
343
svn_error_clear(err);
348
const svn_boolean_t otoh = !!desc->predicate.description;
351
printf(" %3d %-5s %s%s%s%s%s%s\n",
353
(xfail ? "XFAIL" : (skip ? "SKIP" : "")),
354
msg ? msg : "(test did not provide name)",
355
(wimp && verbose_mode) ? " [[" : "",
356
(wimp && verbose_mode) ? desc->wip : "",
357
(wimp && verbose_mode) ? "]]" : "",
359
(otoh ? desc->predicate.description : ""));
361
else if (run_this_test && ((! quiet_mode) || test_failed))
363
printf("%s %s %d: %s%s%s%s\n",
365
? (xfail ? "XFAIL:" : "FAIL: ")
366
: (xfail ? "XPASS:" : (skip ? "SKIP: " : "PASS: "))),
369
msg ? msg : "(test did not provide name)",
370
wimp ? " [[WIMP: " : "",
371
wimp ? desc->wip : "",
377
size_t len = strlen(msg);
379
printf("WARNING: Test docstring exceeds 50 characters\n");
380
if (msg[len - 1] == '.')
381
printf("WARNING: Test docstring ends in a period (.)\n");
382
if (svn_ctype_isupper(msg[0]))
383
printf("WARNING: Test docstring is capitalized\n");
385
if (desc->msg == NULL)
386
printf("WARNING: New-style test descriptor is missing a docstring.\n");
209
393
/* Execute a test number TEST_NUM. Pretty-print test name and dots
210
394
according to our test-suite spec, and return the result code.
294
479
/* Failure means unexpected results -- FAIL or XPASS. */
295
test_failed = (!wimp && ((err != SVN_NO_ERROR) != (xfail != 0)));
297
/* If we got an error, print it out. */
300
svn_handle_error2(err, stdout, FALSE, "svn_tests: ");
301
svn_error_clear(err);
307
printf(" %3d %-5s %s%s%s%s\n",
309
(xfail ? "XFAIL" : (skip ? "SKIP" : "")),
310
msg ? msg : "(test did not provide name)",
311
(wimp && verbose_mode) ? " [[" : "",
312
(wimp && verbose_mode) ? desc->wip : "",
313
(wimp && verbose_mode) ? "]]" : "");
315
else if (run_this_test && ((! quiet_mode) || test_failed))
317
printf("%s %s %d: %s%s%s%s\n",
319
? (xfail ? "XFAIL:" : "FAIL: ")
320
: (xfail ? "XPASS:" : (skip ? "SKIP: " : "PASS: "))),
323
msg ? msg : "(test did not provide name)",
324
wimp ? " [[WIMP: " : "",
325
wimp ? desc->wip : "",
331
size_t len = strlen(msg);
333
printf("WARNING: Test docstring exceeds 50 characters\n");
334
if (msg[len - 1] == '.')
335
printf("WARNING: Test docstring ends in a period (.)\n");
336
if (svn_ctype_isupper(msg[0]))
337
printf("WARNING: Test docstring is capitalized\n");
339
if (desc->msg == NULL)
340
printf("WARNING: New-style test descriptor is missing a docstring.\n");
344
skip_cleanup = test_failed;
480
skip_cleanup = log_results(progname, test_num, msg_only, run_this_test,
481
skip, xfail, wimp, err, msg, desc);
488
/* Per-test parameters used by test_thread */
489
typedef struct test_params_t
491
/* Name of the application */
492
const char *progname;
494
/* Total number of tests to execute */
495
svn_atomic_t test_count;
497
/* Global test options as provided by main() */
498
svn_test_opts_t *opts;
500
/* Reference to the global failure flag. Set this if any test failed. */
501
svn_atomic_t got_error;
503
/* Test to execute next. */
504
svn_atomic_t test_num;
506
/* Test functions array. */
507
struct svn_test_descriptor_t *test_funcs;
510
/* Thread function similar to do_test_num() but with fewer options. We do
511
catch segfaults. All parameters are given as a test_params_t in DATA.
513
static void * APR_THREAD_FUNC
514
test_thread(apr_thread_t *thread, void *data)
516
svn_boolean_t skip, xfail, wimp;
518
const struct svn_test_descriptor_t *desc;
519
svn_boolean_t run_this_test; /* This test's mode matches DESC->MODE. */
520
enum svn_test_mode_t test_mode;
521
test_params_t *params = data;
522
svn_atomic_t test_num;
524
apr_pool_t *thread_root
525
= apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
527
#ifdef HAVE_PER_THREAD_CLEANUP
528
set_cleanup_pool(svn_pool_create(thread_root));
531
pool = svn_pool_create(thread_root);
533
for (test_num = svn_atomic_inc(¶ms->test_num);
534
test_num <= params->test_count;
535
test_num = svn_atomic_inc(¶ms->test_num))
537
svn_pool_clear(pool);
538
#ifdef HAVE_PER_THREAD_CLEANUP
539
svn_pool_clear(cleanup_pool); /* after clearing pool*/
542
desc = ¶ms->test_funcs[test_num];
543
/* Check the test predicate. */
544
if (desc->predicate.func
545
&& desc->predicate.func(params->opts, desc->predicate.value, pool))
546
test_mode = desc->predicate.alternate_mode;
548
test_mode = desc->mode;
550
skip = test_mode == svn_test_skip;
551
xfail = test_mode == svn_test_xfail;
552
wimp = xfail && desc->wip;
553
run_this_test = mode_filter == svn_test_all
554
|| mode_filter == test_mode;
557
if (skip || !run_this_test)
558
err = NULL; /* pass */
559
else if (desc->func2)
560
err = (*desc->func2)(pool);
562
err = (*desc->func_opts)(params->opts, pool);
564
/* Write results to console */
565
svn_error_clear(svn_mutex__lock(log_mutex));
566
if (log_results(params->progname, test_num, FALSE, run_this_test,
567
skip, xfail, wimp, err, desc->msg, desc))
568
svn_atomic_set(¶ms->got_error, TRUE);
569
svn_error_clear(svn_mutex__unlock(log_mutex, NULL));
572
svn_pool_clear(pool); /* Make sure this is cleared before cleanup_pool*/
574
/* Release all test memory. Possibly includes cleanup_pool */
575
svn_pool_destroy(thread_root);
577
/* End thread explicitly to prevent APR_INCOMPLETE return codes in
578
apr_thread_join(). */
579
apr_thread_exit(thread, 0);
583
/* Log an error with message MSG if the APR status of EXPR is not 0.
585
#define CHECK_STATUS(expr,msg) \
587
apr_status_t rv = (expr); \
590
svn_error_t *svn_err__temp = svn_error_wrap_apr(rv, msg); \
591
svn_handle_error2(svn_err__temp, stdout, FALSE, "svn_tests: "); \
592
svn_error_clear(svn_err__temp); \
596
/* Execute all ARRAY_SIZE tests concurrently using MAX_THREADS threads.
597
Pass PROGNAME and OPTS to the individual tests. Return TRUE if at least
598
one of the tests failed. Allocate all data in POOL.
600
Note that cleanups are delayed until all tests have been completed.
603
do_tests_concurrently(const char *progname,
604
struct svn_test_descriptor_t *test_funcs,
607
svn_test_opts_t *opts,
611
apr_thread_t **threads;
613
/* Prepare thread parameters. */
614
test_params_t params;
615
params.got_error = FALSE;
617
params.progname = progname;
619
params.test_funcs = test_funcs;
620
params.test_count = array_size;
622
/* Start all threads. */
623
threads = apr_pcalloc(pool, max_threads * sizeof(*threads));
624
for (i = 0; i < max_threads; ++i)
626
CHECK_STATUS(apr_thread_create(&threads[i], NULL, test_thread, ¶ms,
628
"creating test thread failed.\n");
631
/* Wait for all tasks (tests) to complete. */
632
for (i = 0; i < max_threads; ++i)
634
apr_status_t result = 0;
635
CHECK_STATUS(apr_thread_join(&result, threads[i]),
636
"Waiting for test thread to finish failed.");
638
"Test thread returned an error.");
641
return params.got_error != FALSE;
350
646
static void help(const char *progname, apr_pool_t *pool)
366
662
svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
665
static svn_error_t *init_test_data(const char *argv0, apr_pool_t *pool)
667
const char *temp_path;
668
const char *base_name;
670
/* Convert the program path to an absolute path. */
671
SVN_ERR(svn_utf_cstring_to_utf8(&temp_path, argv0, pool));
672
temp_path = svn_dirent_internal_style(temp_path, pool);
673
SVN_ERR(svn_dirent_get_absolute(&temp_path, temp_path, pool));
674
SVN_ERR_ASSERT(!svn_dirent_is_root(temp_path, strlen(temp_path)));
676
/* Extract the interesting bits of the path. */
677
temp_path = svn_dirent_dirname(temp_path, pool);
678
base_name = svn_dirent_basename(temp_path, pool);
679
if (0 == strcmp(base_name, ".libs"))
681
/* This is a libtoolized binary, skip the .libs directory. */
682
temp_path = svn_dirent_dirname(temp_path, pool);
683
base_name = svn_dirent_basename(temp_path, pool);
685
temp_path = svn_dirent_dirname(temp_path, pool);
687
/* temp_path should now point to the root of the test
688
builddir. Construct the path to the transient dir. Note that we
689
put the path insinde the cmdline/svn-test-work area. This is
690
because trying to get the cmdline tests to use a different work
691
area is unprintable; so we put the C test transient dir in the
692
cmdline tests area, as the lesser of evils ... */
693
temp_path = svn_dirent_join_many(pool, temp_path,
694
"cmdline", "svn-test-work",
695
base_name, SVN_VA_NULL);
697
/* Finally, create the transient directory. */
698
SVN_ERR(svn_io_make_dir_recursively(temp_path, pool));
700
data_path = temp_path;
705
svn_test_data_path(const char *base_name, apr_pool_t *result_pool)
707
return svn_dirent_join(data_path, base_name, result_pool);
711
svn_test_get_srcdir(const char **srcdir,
712
const svn_test_opts_t *opts,
719
*srcdir = opts->srcdir;
723
fprintf(stderr, "WARNING: missing '--srcdir' option");
724
SVN_ERR(svn_dirent_get_absolute(&cwd, ".", pool));
725
fprintf(stderr, ", assuming '%s'\n", cwd);
732
svn_test__init_auth_baton(svn_auth_baton_t **ab,
733
apr_pool_t *result_pool)
735
svn_config_t *cfg_config;
737
SVN_ERR(svn_config_create2(&cfg_config, FALSE, FALSE, result_pool));
739
/* Disable the crypto backends that might not be entirely
740
threadsafe and/or compatible with running headless.
742
The windows system is just our own files, but then with user-key
743
encrypted data inside. */
744
svn_config_set(cfg_config,
745
SVN_CONFIG_SECTION_AUTH,
746
SVN_CONFIG_OPTION_PASSWORD_STORES,
747
"windows-cryptoapi");
749
SVN_ERR(svn_cmdline_create_auth_baton(ab,
750
TRUE /* non_interactive */,
751
"jrandom", "rayjandom",
753
TRUE /* no_auth_cache */,
754
FALSE /* trust_server_cert */,
755
cfg_config, NULL, NULL, result_pool));
370
760
/* Standard svn test program */
372
main(int argc, const char *argv[])
762
svn_test_main(int argc, const char *argv[], int max_threads,
763
struct svn_test_descriptor_t *test_funcs)
374
const char *prog_name;
376
766
svn_boolean_t got_error = FALSE;
377
767
apr_pool_t *pool, *test_pool;
400
790
* usage but make it thread-safe to allow for multi-threaded tests.
402
792
pool = apr_allocator_owner_get(svn_pool_create_allocator(TRUE));
793
err = svn_mutex__init(&log_mutex, TRUE, pool);
796
svn_handle_error2(err, stderr, TRUE, "svn_tests: ");
797
svn_error_clear(err);
800
/* Set up the thread-local storage key for the cleanup pool. */
801
#ifdef HAVE_PER_THREAD_CLEANUP
802
apr_err = apr_threadkey_private_create(&cleanup_pool_key,
807
printf("apr_threadkey_private_create() failed with code %ld.\n",
811
#endif /* HAVE_PER_THREAD_CLEANUP */
404
813
/* Remember the command line */
405
814
test_argc = argc;
406
815
test_argv = argv;
817
err = init_test_data(argv[0], pool);
820
svn_handle_error2(err, stderr, TRUE, "svn_tests: ");
821
svn_error_clear(err);
408
824
err = svn_cmdline__getopt_init(&os, argc, argv, pool);
827
svn_handle_error2(err, stderr, TRUE, "svn_tests: ");
828
svn_error_clear(err);
410
832
os->interleave = TRUE; /* Let options and arguments be interleaved */
412
834
/* Strip off any leading path components from the program name. */
413
prog_name = strrchr(argv[0], '/');
418
/* Just check if this is that weird platform that uses \ instead
419
of / for the path separator. */
420
prog_name = strrchr(argv[0], '\\');
835
opts.prog_name = svn_dirent_internal_style(argv[0], pool);
836
opts.prog_name = svn_dirent_basename(opts.prog_name, NULL);
839
/* Abuse cast in strstr() to remove .exe extension.
840
Value is allocated in pool by svn_dirent_internal_style() */
842
char *exe_ext = strstr(opts.prog_name, ".exe");
428
848
#if _MSC_VER >= 1400
429
849
/* ### This should work for VC++ 2002 (=1300) and later */
430
850
/* Show the abort message on STDERR instead of a dialog to allow