~ubuntu-branches/debian/sid/subversion/sid

« back to all changes in this revision

Viewing changes to subversion/tests/svn_test_main.c

  • Committer: Package Import Robot
  • Author(s): James McCoy
  • Date: 2015-08-07 21:32:47 UTC
  • mfrom: (0.2.15) (4.1.7 experimental)
  • Revision ID: package-import@ubuntu.com-20150807213247-ozyewtmgsr6tkewl
Tags: 1.9.0-1
* Upload to unstable
* New upstream release.
  + Security fixes
    - CVE-2015-3184: Mixed anonymous/authenticated path-based authz with
      httpd 2.4
    - CVE-2015-3187: svn_repos_trace_node_locations() reveals paths hidden
      by authz
* Add >= 2.7 requirement for python-all-dev Build-Depends, needed to run
  tests.
* Remove Build-Conflicts against ruby-test-unit.  (Closes: #791844)
* Remove patches/apache_module_dependency in favor of expressing the
  dependencies in authz_svn.load/dav_svn.load.
* Build-Depend on apache2-dev (>= 2.4.16) to ensure ap_some_authn_required()
  is available when building mod_authz_svn and Depend on apache2-bin (>=
  2.4.16) for runtime support.

Show diffs side-by-side

added added

removed removed

Lines of Context:
45
45
#include "svn_path.h"
46
46
#include "svn_ctype.h"
47
47
#include "svn_utf.h"
 
48
#include "svn_version.h"
48
49
 
49
50
#include "private/svn_cmdline_private.h"
 
51
#include "private/svn_atomic.h"
 
52
#include "private/svn_mutex.h"
 
53
#include "private/svn_sqlite.h"
50
54
 
51
55
#include "svn_private_config.h"
52
56
 
 
57
#if APR_HAS_THREADS
 
58
#  include <apr_thread_proc.h>
 
59
#endif
 
60
 
53
61
/* Some Subversion test programs may want to parse options in the
54
62
   argument list, so we remember it here. */
 
63
extern int test_argc;
 
64
extern const char **test_argv;
55
65
int test_argc;
56
66
const char **test_argv;
57
67
 
 
68
/* Many tests write to disk. Instead of writing to the current
 
69
   directory, they should use this path as the root of the test data
 
70
   area. */
 
71
static const char *data_path;
58
72
 
59
73
/* Test option: Print more output */
60
74
static svn_boolean_t verbose_mode = FALSE;
70
84
 
71
85
/* Test option: Limit testing to a given mode (i.e. XFail, Skip,
72
86
   Pass, All). */
73
 
enum svn_test_mode_t mode_filter = svn_test_all;
 
87
static enum svn_test_mode_t mode_filter = svn_test_all;
 
88
 
 
89
/* Test option: Allow concurrent execution of tests */
 
90
static svn_boolean_t parallel = FALSE;
74
91
 
75
92
/* Option parsing enums and structures */
76
 
enum {
 
93
enum test_options_e {
77
94
  help_opt = SVN_OPT_FIRST_LONGOPT_ID,
78
95
  cleanup_opt,
79
96
  fstype_opt,
84
101
  server_minor_version_opt,
85
102
  allow_segfault_opt,
86
103
  srcdir_opt,
87
 
  mode_filter_opt
 
104
  reposdir_opt,
 
105
  reposurl_opt,
 
106
  repostemplate_opt,
 
107
  mode_filter_opt,
 
108
  sqlite_log_opt,
 
109
  parallel_opt,
 
110
  fsfs_version_opt
88
111
};
89
112
 
90
113
static const apr_getopt_option_t cl_options[] =
97
120
                    N_("specify test config file ARG")},
98
121
  {"fs-type",       fstype_opt, 1,
99
122
                    N_("specify a filesystem backend type ARG")},
 
123
  {"fsfs-version",  fsfs_version_opt, 1,
 
124
                    N_("specify the FSFS version ARG")},
100
125
  {"list",          list_opt, 0,
101
126
                    N_("lists all the tests with their short description")},
102
127
  {"mode-filter",   mode_filter_opt, 1,
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")},
116
151
  {0,               0, 0, 0}
117
152
};
118
153
 
123
158
/* When non-zero, don't remove test directories */
124
159
static svn_boolean_t skip_cleanup = FALSE;
125
160
 
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. */
 
163
#if APR_HAS_THREADS
 
164
/* The thread-local data key for the cleanup pool. */
 
165
static apr_threadkey_t *cleanup_pool_key = NULL;
 
166
 
 
167
/* No-op destructor for apr_threadkey_private_create(). */
 
168
static void null_threadkey_dtor(void *stuff) {}
 
169
 
 
170
/* Set the thread-specific cleanup pool. */
 
171
static void set_cleanup_pool(apr_pool_t *pool)
 
172
{
 
173
  apr_status_t status = apr_threadkey_private_set(pool, cleanup_pool_key);
 
174
  if (status)
 
175
    {
 
176
      printf("apr_threadkey_private_set() failed with code %ld.\n",
 
177
             (long)status);
 
178
      exit(1);
 
179
    }
 
180
}
 
181
 
 
182
/* Get the thread-specific cleanup pool. */
 
183
static apr_pool_t *get_cleanup_pool()
 
184
{
 
185
  void *data;
 
186
  apr_status_t status = apr_threadkey_private_get(&data, cleanup_pool_key);
 
187
  if (status)
 
188
    {
 
189
      printf("apr_threadkey_private_get() failed with code %ld.\n",
 
190
             (long)status);
 
191
      exit(1);
 
192
    }
 
193
  return data;
 
194
}
 
195
 
 
196
#  define cleanup_pool (get_cleanup_pool())
 
197
#  define HAVE_PER_THREAD_CLEANUP
 
198
#else
127
199
static apr_pool_t *cleanup_pool = NULL;
 
200
#  define set_cleanup_pool(p) (cleanup_pool = (p))
 
201
#endif
 
202
 
 
203
/* Used by test_thread to serialize access to stdout. */
 
204
static svn_mutex__t *log_mutex = NULL;
128
205
 
129
206
static apr_status_t
130
207
cleanup_rmtree(void *data)
150
227
}
151
228
 
152
229
 
 
230
 
153
231
void
154
232
svn_test_add_dir_cleanup(const char *path)
155
233
{
156
234
  if (cleanup_mode)
157
235
    {
158
236
      const char *abspath;
159
 
      svn_error_t *err = svn_path_get_absolute(&abspath, path, cleanup_pool);
 
237
      svn_error_t *err;
 
238
 
 
239
      /* All cleanup functions use the *same* pool (not subpools of it).
 
240
         Thus, we need to synchronize. */
 
241
      err = svn_mutex__lock(log_mutex);
 
242
      if (err)
 
243
        {
 
244
          if (verbose_mode)
 
245
            printf("FAILED svn_mutex__lock in svn_test_add_dir_cleanup.\n");
 
246
          svn_error_clear(err);
 
247
          return;
 
248
        }
 
249
 
 
250
      err = svn_path_get_absolute(&abspath, path, cleanup_pool);
160
251
      svn_error_clear(err);
161
252
      if (!err)
162
253
        apr_pool_cleanup_register(cleanup_pool, abspath, cleanup_rmtree,
163
254
                                  apr_pool_cleanup_null);
164
255
      else if (verbose_mode)
165
256
        printf("FAILED ABSPATH: %s\n", path);
 
257
 
 
258
      err = svn_mutex__unlock(log_mutex, NULL);
 
259
      if (err)
 
260
        {
 
261
          if (verbose_mode)
 
262
            printf("FAILED svn_mutex__unlock in svn_test_add_dir_cleanup.\n");
 
263
          svn_error_clear(err);
 
264
        }
166
265
    }
167
266
}
168
267
 
183
282
 
184
283
/* Determine the array size of test_funcs[], the inelegant way.  :)  */
185
284
static int
186
 
get_array_size(void)
 
285
get_array_size(struct svn_test_descriptor_t *test_funcs)
187
286
{
188
287
  int i;
189
288
 
205
304
  longjmp(jump_buffer, 1);
206
305
}
207
306
 
 
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.
 
310
 
 
311
   The parameters are basically the internal state of do_test_num() and
 
312
   test_thread(). */
 
313
/*  */
 
314
static svn_boolean_t
 
315
log_results(const char *progname,
 
316
            int test_num,
 
317
            svn_boolean_t msg_only,
 
318
            svn_boolean_t run_this_test,
 
319
            svn_boolean_t skip,
 
320
            svn_boolean_t xfail,
 
321
            svn_boolean_t wimp,
 
322
            svn_error_t *err,
 
323
            const char *msg,
 
324
            const struct svn_test_descriptor_t *desc)
 
325
{
 
326
  svn_boolean_t test_failed;
 
327
 
 
328
  if (err && err->apr_err == SVN_ERR_TEST_SKIPPED)
 
329
    {
 
330
      svn_error_clear(err);
 
331
      err = SVN_NO_ERROR;
 
332
      skip = TRUE;
 
333
      xfail = FALSE; /* Or all XFail tests reporting SKIP would be failing */
 
334
    }
 
335
 
 
336
  /* Failure means unexpected results -- FAIL or XPASS. */
 
337
  test_failed = (!wimp && ((err != SVN_NO_ERROR) != (xfail != 0)));
 
338
 
 
339
  /* If we got an error, print it out.  */
 
340
  if (err)
 
341
    {
 
342
      svn_handle_error2(err, stdout, FALSE, "svn_tests: ");
 
343
      svn_error_clear(err);
 
344
    }
 
345
 
 
346
  if (msg_only)
 
347
    {
 
348
      const svn_boolean_t otoh = !!desc->predicate.description;
 
349
 
 
350
      if (run_this_test)
 
351
        printf(" %3d    %-5s  %s%s%s%s%s%s\n",
 
352
               test_num,
 
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) ? "]]" : "",
 
358
               (otoh ? " / " : ""),
 
359
               (otoh ? desc->predicate.description : ""));
 
360
    }
 
361
  else if (run_this_test && ((! quiet_mode) || test_failed))
 
362
    {
 
363
      printf("%s %s %d: %s%s%s%s\n",
 
364
             (err
 
365
              ? (xfail ? "XFAIL:" : "FAIL: ")
 
366
              : (xfail ? "XPASS:" : (skip ? "SKIP: " : "PASS: "))),
 
367
             progname,
 
368
             test_num,
 
369
             msg ? msg : "(test did not provide name)",
 
370
             wimp ? " [[WIMP: " : "",
 
371
             wimp ? desc->wip : "",
 
372
             wimp ? "]]" : "");
 
373
    }
 
374
 
 
375
  if (msg)
 
376
    {
 
377
      size_t len = strlen(msg);
 
378
      if (len > 50)
 
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");
 
384
    }
 
385
  if (desc->msg == NULL)
 
386
    printf("WARNING: New-style test descriptor is missing a docstring.\n");
 
387
 
 
388
  fflush(stdout);
 
389
 
 
390
  return test_failed;
 
391
}
208
392
 
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.
213
397
static svn_boolean_t
214
398
do_test_num(const char *progname,
215
399
            int test_num,
 
400
            struct svn_test_descriptor_t *test_funcs,
216
401
            svn_boolean_t msg_only,
217
402
            svn_test_opts_t *opts,
218
403
            const char **header_msg,
220
405
{
221
406
  svn_boolean_t skip, xfail, wimp;
222
407
  svn_error_t *err = NULL;
223
 
  svn_boolean_t test_failed;
224
408
  const char *msg = NULL;  /* the message this individual test prints out */
225
409
  const struct svn_test_descriptor_t *desc;
226
 
  const int array_size = get_array_size();
 
410
  const int array_size = get_array_size(test_funcs);
227
411
  svn_boolean_t run_this_test; /* This test's mode matches DESC->MODE. */
 
412
  enum svn_test_mode_t test_mode;
228
413
 
229
414
  /* Check our array bounds! */
230
415
  if (test_num < 0)
239
424
    }
240
425
 
241
426
  desc = &test_funcs[test_num];
242
 
  skip = desc->mode == svn_test_skip;
243
 
  xfail = desc->mode == svn_test_xfail;
 
427
  /* Check the test predicate. */
 
428
  if (desc->predicate.func
 
429
      && desc->predicate.func(opts, desc->predicate.value, pool))
 
430
    test_mode = desc->predicate.alternate_mode;
 
431
  else
 
432
    test_mode = desc->mode;
 
433
 
 
434
  skip = test_mode == svn_test_skip;
 
435
  xfail = test_mode == svn_test_xfail;
244
436
  wimp = xfail && desc->wip;
245
437
  msg = desc->msg;
246
 
  run_this_test = mode_filter == svn_test_all || mode_filter == desc->mode;
 
438
  run_this_test = mode_filter == svn_test_all || mode_filter == test_mode;
247
439
 
248
440
  if (run_this_test && header_msg && *header_msg)
249
441
    {
272
464
        err = (*desc->func2)(pool);
273
465
      else
274
466
        err = (*desc->func_opts)(opts, pool);
275
 
 
276
 
      if (err && err->apr_err == SVN_ERR_TEST_SKIPPED)
277
 
        {
278
 
          svn_error_clear(err);
279
 
          err = SVN_NO_ERROR;
280
 
          skip = TRUE;
281
 
        }
282
467
    }
283
468
  else
284
469
    err = svn_error_create(SVN_ERR_TEST_FAILED, NULL,
292
477
    }
293
478
 
294
479
  /* Failure means unexpected results -- FAIL or XPASS. */
295
 
  test_failed = (!wimp && ((err != SVN_NO_ERROR) != (xfail != 0)));
296
 
 
297
 
  /* If we got an error, print it out.  */
298
 
  if (err)
299
 
    {
300
 
      svn_handle_error2(err, stdout, FALSE, "svn_tests: ");
301
 
      svn_error_clear(err);
302
 
    }
303
 
 
304
 
  if (msg_only)
305
 
    {
306
 
      if (run_this_test)
307
 
        printf(" %3d    %-5s  %s%s%s%s\n",
308
 
               test_num,
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) ? "]]" : "");
314
 
    }
315
 
  else if (run_this_test && ((! quiet_mode) || test_failed))
316
 
    {
317
 
      printf("%s %s %d: %s%s%s%s\n",
318
 
             (err
319
 
              ? (xfail ? "XFAIL:" : "FAIL: ")
320
 
              : (xfail ? "XPASS:" : (skip ? "SKIP: " : "PASS: "))),
321
 
             progname,
322
 
             test_num,
323
 
             msg ? msg : "(test did not provide name)",
324
 
             wimp ? " [[WIMP: " : "",
325
 
             wimp ? desc->wip : "",
326
 
             wimp ? "]]" : "");
327
 
    }
328
 
 
329
 
  if (msg)
330
 
    {
331
 
      size_t len = strlen(msg);
332
 
      if (len > 50)
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");
338
 
    }
339
 
  if (desc->msg == NULL)
340
 
    printf("WARNING: New-style test descriptor is missing a docstring.\n");
341
 
 
342
 
  fflush(stdout);
343
 
 
344
 
  skip_cleanup = test_failed;
345
 
 
346
 
  return test_failed;
347
 
}
348
 
 
 
480
  skip_cleanup = log_results(progname, test_num, msg_only, run_this_test,
 
481
                             skip, xfail, wimp, err, msg, desc);
 
482
 
 
483
  return skip_cleanup;
 
484
}
 
485
 
 
486
#if APR_HAS_THREADS
 
487
 
 
488
/* Per-test parameters used by test_thread */
 
489
typedef struct test_params_t
 
490
{
 
491
  /* Name of the application */
 
492
  const char *progname;
 
493
 
 
494
  /* Total number of tests to execute */
 
495
  svn_atomic_t test_count;
 
496
 
 
497
  /* Global test options as provided by main() */
 
498
  svn_test_opts_t *opts;
 
499
 
 
500
  /* Reference to the global failure flag.  Set this if any test failed. */
 
501
  svn_atomic_t got_error;
 
502
 
 
503
  /* Test to execute next. */
 
504
  svn_atomic_t test_num;
 
505
 
 
506
  /* Test functions array. */
 
507
  struct svn_test_descriptor_t *test_funcs;
 
508
} test_params_t;
 
509
 
 
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.
 
512
 */
 
513
static void * APR_THREAD_FUNC
 
514
test_thread(apr_thread_t *thread, void *data)
 
515
{
 
516
  svn_boolean_t skip, xfail, wimp;
 
517
  svn_error_t *err;
 
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;
 
523
  apr_pool_t *pool;
 
524
  apr_pool_t *thread_root
 
525
    = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
 
526
 
 
527
#ifdef HAVE_PER_THREAD_CLEANUP
 
528
  set_cleanup_pool(svn_pool_create(thread_root));
 
529
#endif
 
530
 
 
531
  pool = svn_pool_create(thread_root);
 
532
 
 
533
  for (test_num = svn_atomic_inc(&params->test_num);
 
534
       test_num <= params->test_count;
 
535
       test_num = svn_atomic_inc(&params->test_num))
 
536
    {
 
537
      svn_pool_clear(pool);
 
538
#ifdef HAVE_PER_THREAD_CLEANUP
 
539
      svn_pool_clear(cleanup_pool); /* after clearing pool*/
 
540
#endif
 
541
 
 
542
      desc = &params->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;
 
547
      else
 
548
        test_mode = desc->mode;
 
549
 
 
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;
 
555
 
 
556
      /* Do test */
 
557
      if (skip || !run_this_test)
 
558
        err = NULL; /* pass */
 
559
      else if (desc->func2)
 
560
        err = (*desc->func2)(pool);
 
561
      else
 
562
        err = (*desc->func_opts)(params->opts, pool);
 
563
 
 
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(&params->got_error, TRUE);
 
569
      svn_error_clear(svn_mutex__unlock(log_mutex, NULL));
 
570
    }
 
571
 
 
572
  svn_pool_clear(pool); /* Make sure this is cleared before cleanup_pool*/
 
573
 
 
574
  /* Release all test memory. Possibly includes cleanup_pool */
 
575
  svn_pool_destroy(thread_root);
 
576
 
 
577
  /* End thread explicitly to prevent APR_INCOMPLETE return codes in
 
578
     apr_thread_join(). */
 
579
  apr_thread_exit(thread, 0);
 
580
  return NULL;
 
581
}
 
582
 
 
583
/* Log an error with message MSG if the APR status of EXPR is not 0.
 
584
 */
 
585
#define CHECK_STATUS(expr,msg) \
 
586
  do { \
 
587
    apr_status_t rv = (expr); \
 
588
    if (rv) \
 
589
      { \
 
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); \
 
593
      } \
 
594
  } while (0);
 
595
 
 
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.
 
599
 
 
600
   Note that cleanups are delayed until all tests have been completed.
 
601
 */
 
602
static svn_boolean_t
 
603
do_tests_concurrently(const char *progname,
 
604
                      struct svn_test_descriptor_t *test_funcs,
 
605
                      int array_size,
 
606
                      int max_threads,
 
607
                      svn_test_opts_t *opts,
 
608
                      apr_pool_t *pool)
 
609
{
 
610
  int i;
 
611
  apr_thread_t **threads;
 
612
 
 
613
  /* Prepare thread parameters. */
 
614
  test_params_t params;
 
615
  params.got_error = FALSE;
 
616
  params.opts = opts;
 
617
  params.progname = progname;
 
618
  params.test_num = 1;
 
619
  params.test_funcs = test_funcs;
 
620
  params.test_count = array_size;
 
621
 
 
622
  /* Start all threads. */
 
623
  threads = apr_pcalloc(pool, max_threads * sizeof(*threads));
 
624
  for (i = 0; i < max_threads; ++i)
 
625
    {
 
626
      CHECK_STATUS(apr_thread_create(&threads[i], NULL, test_thread, &params,
 
627
                                     pool),
 
628
                   "creating test thread failed.\n");
 
629
    }
 
630
 
 
631
  /* Wait for all tasks (tests) to complete. */
 
632
  for (i = 0; i < max_threads; ++i)
 
633
    {
 
634
      apr_status_t result = 0;
 
635
      CHECK_STATUS(apr_thread_join(&result, threads[i]),
 
636
                   "Waiting for test thread to finish failed.");
 
637
      CHECK_STATUS(result,
 
638
                   "Test thread returned an error.");
 
639
    }
 
640
 
 
641
  return params.got_error != FALSE;
 
642
}
 
643
 
 
644
#endif
349
645
 
350
646
static void help(const char *progname, apr_pool_t *pool)
351
647
{
366
662
  svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
367
663
}
368
664
 
 
665
static svn_error_t *init_test_data(const char *argv0, apr_pool_t *pool)
 
666
{
 
667
  const char *temp_path;
 
668
  const char *base_name;
 
669
 
 
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)));
 
675
 
 
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"))
 
680
    {
 
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);
 
684
    }
 
685
  temp_path = svn_dirent_dirname(temp_path, pool);
 
686
 
 
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);
 
696
 
 
697
  /* Finally, create the transient directory. */
 
698
  SVN_ERR(svn_io_make_dir_recursively(temp_path, pool));
 
699
 
 
700
  data_path = temp_path;
 
701
  return SVN_NO_ERROR;
 
702
}
 
703
 
 
704
const char *
 
705
svn_test_data_path(const char *base_name, apr_pool_t *result_pool)
 
706
{
 
707
  return svn_dirent_join(data_path, base_name, result_pool);
 
708
}
 
709
 
 
710
svn_error_t *
 
711
svn_test_get_srcdir(const char **srcdir,
 
712
                    const svn_test_opts_t *opts,
 
713
                    apr_pool_t *pool)
 
714
{
 
715
  const char *cwd;
 
716
 
 
717
  if (opts->srcdir)
 
718
    {
 
719
      *srcdir = opts->srcdir;
 
720
      return SVN_NO_ERROR;
 
721
    }
 
722
 
 
723
  fprintf(stderr, "WARNING: missing '--srcdir' option");
 
724
  SVN_ERR(svn_dirent_get_absolute(&cwd, ".", pool));
 
725
  fprintf(stderr, ", assuming '%s'\n", cwd);
 
726
  *srcdir = cwd;
 
727
 
 
728
  return SVN_NO_ERROR;
 
729
}
 
730
 
 
731
svn_error_t *
 
732
svn_test__init_auth_baton(svn_auth_baton_t **ab,
 
733
                          apr_pool_t *result_pool)
 
734
{
 
735
  svn_config_t *cfg_config;
 
736
 
 
737
  SVN_ERR(svn_config_create2(&cfg_config, FALSE, FALSE, result_pool));
 
738
 
 
739
  /* Disable the crypto backends that might not be entirely
 
740
     threadsafe and/or compatible with running headless.
 
741
 
 
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");
 
748
 
 
749
  SVN_ERR(svn_cmdline_create_auth_baton(ab,
 
750
                                        TRUE  /* non_interactive */,
 
751
                                        "jrandom", "rayjandom",
 
752
                                        NULL,
 
753
                                        TRUE  /* no_auth_cache */,
 
754
                                        FALSE /* trust_server_cert */,
 
755
                                        cfg_config, NULL, NULL, result_pool));
 
756
 
 
757
  return SVN_NO_ERROR;
 
758
}
369
759
 
370
760
/* Standard svn test program */
371
761
int
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)
373
764
{
374
 
  const char *prog_name;
375
765
  int i;
376
766
  svn_boolean_t got_error = FALSE;
377
767
  apr_pool_t *pool, *test_pool;
383
773
  svn_error_t *err;
384
774
  char errmsg[200];
385
775
  /* How many tests are there? */
386
 
  int array_size = get_array_size();
 
776
  int array_size = get_array_size(test_funcs);
387
777
 
388
778
  svn_test_opts_t opts = { NULL };
389
779
 
400
790
   * usage but make it thread-safe to allow for multi-threaded tests.
401
791
   */
402
792
  pool = apr_allocator_owner_get(svn_pool_create_allocator(TRUE));
 
793
  err = svn_mutex__init(&log_mutex, TRUE, pool);
 
794
  if (err)
 
795
    {
 
796
      svn_handle_error2(err, stderr, TRUE, "svn_tests: ");
 
797
      svn_error_clear(err);
 
798
    }
 
799
 
 
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,
 
803
                                         null_threadkey_dtor,
 
804
                                         pool);
 
805
  if (apr_err)
 
806
    {
 
807
      printf("apr_threadkey_private_create() failed with code %ld.\n",
 
808
             (long)apr_err);
 
809
      exit(1);
 
810
    }
 
811
#endif /* HAVE_PER_THREAD_CLEANUP */
403
812
 
404
813
  /* Remember the command line */
405
814
  test_argc = argc;
406
815
  test_argv = argv;
407
816
 
 
817
  err = init_test_data(argv[0], pool);
 
818
  if (err)
 
819
    {
 
820
      svn_handle_error2(err, stderr, TRUE, "svn_tests: ");
 
821
      svn_error_clear(err);
 
822
    }
 
823
 
408
824
  err = svn_cmdline__getopt_init(&os, argc, argv, pool);
 
825
  if (err)
 
826
    {
 
827
      svn_handle_error2(err, stderr, TRUE, "svn_tests: ");
 
828
      svn_error_clear(err);
 
829
    }
 
830
 
409
831
 
410
832
  os->interleave = TRUE; /* Let options and arguments be interleaved */
411
833
 
412
834
  /* Strip off any leading path components from the program name.  */
413
 
  prog_name = strrchr(argv[0], '/');
414
 
  if (prog_name)
415
 
    prog_name++;
416
 
  else
417
 
    {
418
 
      /* Just check if this is that weird platform that uses \ instead
419
 
         of / for the path separator. */
420
 
      prog_name = strrchr(argv[0], '\\');
421
 
      if (prog_name)
422
 
        prog_name++;
423
 
      else
424
 
        prog_name = argv[0];
425
 
    }
 
835
  opts.prog_name = svn_dirent_internal_style(argv[0], pool);
 
836
  opts.prog_name = svn_dirent_basename(opts.prog_name, NULL);
426
837
 
427
838
#ifdef WIN32
 
839
  /* Abuse cast in strstr() to remove .exe extension.
 
840
     Value is allocated in pool by svn_dirent_internal_style() */
 
841
  {
 
842
    char *exe_ext = strstr(opts.prog_name, ".exe");
 
843
 
 
844
    if (exe_ext)
 
845
      *exe_ext = '\0';
 
846
  }
 
847
 
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
446
866
#endif
447
867
 
448
868
  if (err)
449
 
    return svn_cmdline_handle_exit_error(err, pool, prog_name);
 
869
    return svn_cmdline_handle_exit_error(err, pool, opts.prog_name);
450
870
  while (1)
451
871
    {
452
872
      const char *opt_arg;
457
877
        break;
458
878
      else if (apr_err && (apr_err != APR_BADCH))
459
879
        {
460
 
          /* Ignore invalid option error to allow passing arbitary options */
 
880
          /* Ignore invalid option error to allow passing arbitrary options */
461
881
          fprintf(stderr, "apr_getopt_long failed : [%d] %s\n",
462
882
                  apr_err, apr_strerror(apr_err, errmsg, sizeof(errmsg)));
463
883
          exit(1);
465
885
 
466
886
      switch (opt_id) {
467
887
        case help_opt:
468
 
          help(prog_name, pool);
 
888
          help(opts.prog_name, pool);
469
889
          exit(0);
470
890
        case cleanup_opt:
471
891
          cleanup_mode = TRUE;
480
900
          SVN_INT_ERR(svn_utf_cstring_to_utf8(&opts.srcdir, opt_arg, pool));
481
901
          opts.srcdir = svn_dirent_internal_style(opts.srcdir, pool);
482
902
          break;
 
903
        case reposdir_opt:
 
904
          SVN_INT_ERR(svn_utf_cstring_to_utf8(&opts.repos_dir, opt_arg, pool));
 
905
          opts.repos_dir = svn_dirent_internal_style(opts.repos_dir, pool);
 
906
          break;
 
907
        case reposurl_opt:
 
908
          SVN_INT_ERR(svn_utf_cstring_to_utf8(&opts.repos_url, opt_arg, pool));
 
909
          opts.repos_url = svn_uri_canonicalize(opts.repos_url, pool);
 
910
          break;
 
911
        case repostemplate_opt:
 
912
          SVN_INT_ERR(svn_utf_cstring_to_utf8(&opts.repos_template, opt_arg,
 
913
                                              pool));
 
914
          opts.repos_template = svn_dirent_internal_style(opts.repos_template,
 
915
                                                          pool);
 
916
          break;
483
917
        case list_opt:
484
918
          list_mode = TRUE;
485
919
          break;
518
952
                exit(1);
519
953
              }
520
954
            if ((opts.server_minor_version < 3)
521
 
                || (opts.server_minor_version > 6))
 
955
                || (opts.server_minor_version > SVN_VER_MINOR))
522
956
              {
523
957
                fprintf(stderr, "FAIL: Invalid minor version given\n");
524
958
                exit(1);
525
959
              }
 
960
            break;
526
961
          }
 
962
        case sqlite_log_opt:
 
963
          svn_sqlite__dbg_enable_errorlog();
 
964
          break;
 
965
#if APR_HAS_THREADS
 
966
        case parallel_opt:
 
967
          parallel = TRUE;
 
968
          break;
 
969
#endif
527
970
      }
528
971
    }
 
972
  opts.verbose = verbose_mode;
529
973
 
530
974
  /* Disable sleeping for timestamps, to speed up the tests. */
531
975
  apr_env_set(
540
984
    }
541
985
 
542
986
  /* Create an iteration pool for the tests */
543
 
  cleanup_pool = svn_pool_create(pool);
 
987
  set_cleanup_pool(svn_pool_create(pool));
544
988
  test_pool = svn_pool_create(pool);
545
989
 
546
990
  if (!allow_segfaults)
558
1002
                       "------  -----  ----------------\n";
559
1003
          for (i = 1; i <= array_size; i++)
560
1004
            {
561
 
              if (do_test_num(prog_name, i, TRUE, &opts, &header_msg,
562
 
                              test_pool))
 
1005
              if (do_test_num(opts.prog_name, i, test_funcs,
 
1006
                              TRUE, &opts, &header_msg, test_pool))
563
1007
                got_error = TRUE;
564
1008
 
565
1009
              /* Clear the per-function pool */
579
1023
                    continue;
580
1024
 
581
1025
                  ran_a_test = TRUE;
582
 
                  if (do_test_num(prog_name, test_num, FALSE, &opts, NULL,
583
 
                                  test_pool))
 
1026
                  if (do_test_num(opts.prog_name, test_num, test_funcs,
 
1027
                                  FALSE, &opts, NULL, test_pool))
584
1028
                    got_error = TRUE;
585
1029
 
586
1030
                  /* Clear the per-function pool */
594
1038
  if (! ran_a_test)
595
1039
    {
596
1040
      /* just run all tests */
597
 
      for (i = 1; i <= array_size; i++)
598
 
        {
599
 
          if (do_test_num(prog_name, i, FALSE, &opts, NULL, test_pool))
600
 
            got_error = TRUE;
601
 
 
602
 
          /* Clear the per-function pool */
 
1041
      if (max_threads < 1)
 
1042
        max_threads = array_size;
 
1043
 
 
1044
      if (max_threads == 1 || !parallel)
 
1045
        {
 
1046
          for (i = 1; i <= array_size; i++)
 
1047
            {
 
1048
              if (do_test_num(opts.prog_name, i, test_funcs,
 
1049
                              FALSE, &opts, NULL, test_pool))
 
1050
                got_error = TRUE;
 
1051
 
 
1052
              /* Clear the per-function pool */
 
1053
              svn_pool_clear(test_pool);
 
1054
              svn_pool_clear(cleanup_pool);
 
1055
            }
 
1056
        }
 
1057
#if APR_HAS_THREADS
 
1058
      else
 
1059
        {
 
1060
          got_error = do_tests_concurrently(opts.prog_name, test_funcs,
 
1061
                                            array_size, max_threads,
 
1062
                                            &opts, test_pool);
 
1063
 
 
1064
          /* Execute all cleanups */
603
1065
          svn_pool_clear(test_pool);
604
1066
          svn_pool_clear(cleanup_pool);
605
1067
        }
 
1068
#endif
606
1069
    }
607
1070
 
608
1071
  /* Clean up APR */
611
1074
 
612
1075
  return got_error;
613
1076
}
 
1077
 
 
1078
 
 
1079
svn_boolean_t
 
1080
svn_test__fs_type_is(const svn_test_opts_t *opts,
 
1081
                     const char *predicate_value,
 
1082
                     apr_pool_t *pool)
 
1083
{
 
1084
  return (0 == strcmp(predicate_value, opts->fs_type));
 
1085
}
 
1086
 
 
1087
svn_boolean_t
 
1088
svn_test__fs_type_not(const svn_test_opts_t *opts,
 
1089
                      const char *predicate_value,
 
1090
                      apr_pool_t *pool)
 
1091
{
 
1092
  return (0 != strcmp(predicate_value, opts->fs_type));
 
1093
}