1
/* repos-test.c --- tests for the filesystem
3
* ====================================================================
4
* Copyright (c) 2000-2004 CollabNet. All rights reserved.
6
* This software is licensed as described in the file COPYING, which
7
* you should have received as part of this distribution. The terms
8
* are also available at http://subversion.tigris.org/license-1.html.
9
* If newer versions of this license are posted there, you may use a
10
* newer version instead, at your option.
12
* This software consists of voluntary contributions made by many
13
* individuals. For exact contribution history, see the revision
14
* history and logs, available at http://subversion.tigris.org/.
15
* ====================================================================
20
#include <apr_pools.h>
22
#include "svn_pools.h"
23
#include "svn_error.h"
25
#include "svn_repos.h"
27
#include "svn_delta.h"
29
#include "../fs-helpers.h"
31
#include "dir-delta-editor.h"
37
dir_deltas (const char **msg,
38
svn_boolean_t msg_only,
39
svn_test_opts_t *opts,
45
svn_fs_root_t *txn_root, *revision_root;
46
svn_revnum_t youngest_rev;
48
const svn_delta_editor_t *editor;
49
svn_test__tree_t expected_trees[8];
50
int revision_count = 0;
52
apr_pool_t *subpool = svn_pool_create (pool);
54
*msg = "test svn_repos_dir_delta";
61
The filesystem function svn_repos_dir_delta exists to drive an
62
editor in such a way that given a source tree S and a target tree
63
T, that editor manipulation will transform S into T, insomuch as
64
directories and files, and their contents and properties, go.
65
The general notion of the test plan will be to create pairs of
66
trees (S, T), and an editor that edits a copy of tree S, run them
67
through svn_repos_dir_delta, and then verify that the edited copy of
68
S is identical to T when it is all said and done. */
70
/* Create a filesystem and repository. */
71
SVN_ERR (svn_test__create_repos (&repos, "test-repo-dir-deltas",
72
opts->fs_type, pool));
73
fs = svn_repos_fs (repos);
74
expected_trees[revision_count].num_entries = 0;
75
expected_trees[revision_count++].entries = 0;
77
/* Prepare a txn to receive the greek tree. */
78
SVN_ERR (svn_fs_begin_txn (&txn, fs, 0, subpool));
79
SVN_ERR (svn_fs_txn_root (&txn_root, txn, subpool));
80
SVN_ERR (svn_test__create_greek_tree (txn_root, subpool));
81
SVN_ERR (svn_repos_fs_commit_txn (NULL, repos, &youngest_rev, txn, subpool));
83
/***********************************************************************/
85
/***********************************************************************/
87
static svn_test__tree_entry_t expected_entries[] = {
88
/* path, contents (0 = dir) */
89
{ "iota", "This is the file 'iota'.\n" },
91
{ "A/mu", "This is the file 'mu'.\n" },
93
{ "A/B/lambda", "This is the file 'lambda'.\n" },
95
{ "A/B/E/alpha", "This is the file 'alpha'.\n" },
96
{ "A/B/E/beta", "This is the file 'beta'.\n" },
100
{ "A/D/gamma", "This is the file 'gamma'.\n" },
102
{ "A/D/G/pi", "This is the file 'pi'.\n" },
103
{ "A/D/G/rho", "This is the file 'rho'.\n" },
104
{ "A/D/G/tau", "This is the file 'tau'.\n" },
106
{ "A/D/H/chi", "This is the file 'chi'.\n" },
107
{ "A/D/H/psi", "This is the file 'psi'.\n" },
108
{ "A/D/H/omega", "This is the file 'omega'.\n" }
110
expected_trees[revision_count].entries = expected_entries;
111
expected_trees[revision_count].num_entries = 20;
112
SVN_ERR (svn_fs_revision_root (&revision_root, fs,
113
youngest_rev, subpool));
114
SVN_ERR (svn_test__validate_tree
115
(revision_root, expected_trees[revision_count].entries,
116
expected_trees[revision_count].num_entries, subpool));
119
svn_pool_clear (subpool);
121
/* Make a new txn based on the youngest revision, make some changes,
122
and commit those changes (which makes a new youngest
124
SVN_ERR (svn_fs_begin_txn (&txn, fs, youngest_rev, subpool));
125
SVN_ERR (svn_fs_txn_root (&txn_root, txn, subpool));
127
static svn_test__txn_script_command_t script_entries[] = {
128
{ 'a', "A/delta", "This is the file 'delta'.\n" },
129
{ 'a', "A/epsilon", "This is the file 'epsilon'.\n" },
131
{ 'a', "A/B/Z/zeta", "This is the file 'zeta'.\n" },
134
{ 'd', "A/D/G/tau", "" },
135
{ 'd', "A/D/H/omega", "" },
136
{ 'e', "iota", "Changed file 'iota'.\n" },
137
{ 'e', "A/D/G/rho", "Changed file 'rho'.\n" }
139
SVN_ERR (svn_test__txn_script_exec (txn_root, script_entries, 10,
142
SVN_ERR (svn_repos_fs_commit_txn (NULL, repos, &youngest_rev, txn, subpool));
144
/***********************************************************************/
146
/***********************************************************************/
148
static svn_test__tree_entry_t expected_entries[] = {
149
/* path, contents (0 = dir) */
150
{ "iota", "Changed file 'iota'.\n" },
152
{ "A/delta", "This is the file 'delta'.\n" },
153
{ "A/epsilon", "This is the file 'epsilon'.\n" },
155
{ "A/B/lambda", "This is the file 'lambda'.\n" },
157
{ "A/B/E/alpha", "This is the file 'alpha'.\n" },
158
{ "A/B/E/beta", "This is the file 'beta'.\n" },
161
{ "A/B/Z/zeta", "This is the file 'zeta'.\n" },
163
{ "A/D/gamma", "This is the file 'gamma'.\n" },
165
{ "A/D/G/pi", "This is the file 'pi'.\n" },
166
{ "A/D/G/rho", "Changed file 'rho'.\n" },
168
{ "A/D/H/chi", "This is the file 'chi'.\n" },
169
{ "A/D/H/psi", "This is the file 'psi'.\n" }
171
expected_trees[revision_count].entries = expected_entries;
172
expected_trees[revision_count].num_entries = 20;
173
SVN_ERR (svn_fs_revision_root (&revision_root, fs,
174
youngest_rev, subpool));
175
SVN_ERR (svn_test__validate_tree
176
(revision_root, expected_trees[revision_count].entries,
177
expected_trees[revision_count].num_entries, subpool));
180
svn_pool_clear (subpool);
182
/* Make a new txn based on the youngest revision, make some changes,
183
and commit those changes (which makes a new youngest
185
SVN_ERR (svn_fs_begin_txn (&txn, fs, youngest_rev, subpool));
186
SVN_ERR (svn_fs_txn_root (&txn_root, txn, subpool));
188
static svn_test__txn_script_command_t script_entries[] = {
189
{ 'a', "A/mu", "Re-added file 'mu'.\n" },
190
{ 'a', "A/D/H/omega", 0 }, /* re-add omega as directory! */
192
{ 'e', "A/delta", "This is the file 'delta'.\nLine 2.\n" }
194
SVN_ERR (svn_test__txn_script_exec (txn_root, script_entries, 4, subpool));
196
SVN_ERR (svn_repos_fs_commit_txn (NULL, repos, &youngest_rev, txn, subpool));
198
/***********************************************************************/
200
/***********************************************************************/
202
static svn_test__tree_entry_t expected_entries[] = {
203
/* path, contents (0 = dir) */
205
{ "A/delta", "This is the file 'delta'.\nLine 2.\n" },
206
{ "A/epsilon", "This is the file 'epsilon'.\n" },
207
{ "A/mu", "Re-added file 'mu'.\n" },
209
{ "A/B/lambda", "This is the file 'lambda'.\n" },
211
{ "A/B/E/alpha", "This is the file 'alpha'.\n" },
212
{ "A/B/E/beta", "This is the file 'beta'.\n" },
215
{ "A/B/Z/zeta", "This is the file 'zeta'.\n" },
217
{ "A/D/gamma", "This is the file 'gamma'.\n" },
219
{ "A/D/G/pi", "This is the file 'pi'.\n" },
220
{ "A/D/G/rho", "Changed file 'rho'.\n" },
222
{ "A/D/H/chi", "This is the file 'chi'.\n" },
223
{ "A/D/H/psi", "This is the file 'psi'.\n" },
226
expected_trees[revision_count].entries = expected_entries;
227
expected_trees[revision_count].num_entries = 21;
228
SVN_ERR (svn_fs_revision_root (&revision_root, fs,
229
youngest_rev, subpool));
230
SVN_ERR (svn_test__validate_tree
231
(revision_root, expected_trees[revision_count].entries,
232
expected_trees[revision_count].num_entries, subpool));
235
svn_pool_clear (subpool);
237
/* Make a new txn based on the youngest revision, make some changes,
238
and commit those changes (which makes a new youngest
240
SVN_ERR (svn_fs_begin_txn (&txn, fs, youngest_rev, subpool));
241
SVN_ERR (svn_fs_txn_root (&txn_root, txn, subpool));
242
SVN_ERR (svn_fs_revision_root (&revision_root, fs, youngest_rev, subpool));
243
SVN_ERR (svn_fs_copy (revision_root, "A/D/G",
246
SVN_ERR (svn_fs_copy (revision_root, "A/epsilon",
247
txn_root, "A/B/epsilon",
249
SVN_ERR (svn_repos_fs_commit_txn (NULL, repos, &youngest_rev, txn, subpool));
251
/***********************************************************************/
253
/***********************************************************************/
255
static svn_test__tree_entry_t expected_entries[] = {
256
/* path, contents (0 = dir) */
258
{ "A/delta", "This is the file 'delta'.\nLine 2.\n" },
259
{ "A/epsilon", "This is the file 'epsilon'.\n" },
260
{ "A/mu", "Re-added file 'mu'.\n" },
262
{ "A/B/epsilon", "This is the file 'epsilon'.\n" },
263
{ "A/B/lambda", "This is the file 'lambda'.\n" },
265
{ "A/B/E/alpha", "This is the file 'alpha'.\n" },
266
{ "A/B/E/beta", "This is the file 'beta'.\n" },
269
{ "A/B/Z/zeta", "This is the file 'zeta'.\n" },
271
{ "A/D/gamma", "This is the file 'gamma'.\n" },
273
{ "A/D/G/pi", "This is the file 'pi'.\n" },
274
{ "A/D/G/rho", "Changed file 'rho'.\n" },
276
{ "A/D/G2/pi", "This is the file 'pi'.\n" },
277
{ "A/D/G2/rho", "Changed file 'rho'.\n" },
279
{ "A/D/H/chi", "This is the file 'chi'.\n" },
280
{ "A/D/H/psi", "This is the file 'psi'.\n" },
283
expected_trees[revision_count].entries = expected_entries;
284
expected_trees[revision_count].num_entries = 25;
285
SVN_ERR (svn_fs_revision_root (&revision_root, fs,
286
youngest_rev, pool));
287
SVN_ERR (svn_test__validate_tree
288
(revision_root, expected_trees[revision_count].entries,
289
expected_trees[revision_count].num_entries, subpool));
292
svn_pool_clear (subpool);
294
/* THE BIG IDEA: Now that we have a collection of revisions, let's
295
first make sure that given any two revisions, we can get the
296
right delta between them. We'll do this by selecting our two
297
revisions, R1 and R2, basing a transaction off R1, deltafying the
298
txn with respect to R2, and then making sure our final txn looks
299
exactly like R2. This should work regardless of the
300
chronological order in which R1 and R2 were created. */
301
for (i = 0; i < revision_count; i++)
303
for (j = 0; j < revision_count; j++)
305
/* Prepare a txn that will receive the changes from
306
svn_repos_dir_delta */
307
SVN_ERR (svn_fs_begin_txn (&txn, fs, i, subpool));
308
SVN_ERR (svn_fs_txn_root (&txn_root, txn, subpool));
310
/* Get the editor that will be modifying our transaction. */
311
SVN_ERR (dir_delta_get_editor (&editor,
318
/* Here's the kicker...do the directory delta. */
319
SVN_ERR (svn_fs_revision_root (&revision_root, fs, j, subpool));
320
SVN_ERR (svn_repos_dir_delta (txn_root,
335
/* Hopefully at this point our transaction has been modified
336
to look exactly like our latest revision. We'll check
338
SVN_ERR (svn_test__validate_tree
339
(txn_root, expected_trees[j].entries,
340
expected_trees[j].num_entries, subpool));
342
/* We don't really want to do anything with this
343
transaction...so we'll abort it (good for software, bad
344
bad bad for society). */
345
svn_error_clear (svn_fs_abort_txn (txn, subpool));
346
svn_pool_clear (subpool);
350
svn_pool_destroy (subpool);
357
node_tree_delete_under_copy (const char **msg,
358
svn_boolean_t msg_only,
359
svn_test_opts_t *opts,
365
svn_fs_root_t *txn_root, *revision_root, *revision_2_root;
366
svn_revnum_t youngest_rev;
368
const svn_delta_editor_t *editor;
369
svn_repos_node_t *tree;
370
apr_pool_t *subpool = svn_pool_create (pool);
372
*msg = "test deletions under copies in node_tree code";
377
/* Create a filesystem and repository. */
378
SVN_ERR (svn_test__create_repos (&repos, "test-repo-del-under-copy",
379
opts->fs_type, pool));
380
fs = svn_repos_fs (repos);
382
/* Prepare a txn to receive the greek tree. */
383
SVN_ERR (svn_fs_begin_txn (&txn, fs, 0, pool));
384
SVN_ERR (svn_fs_txn_root (&txn_root, txn, pool));
386
/* Create and commit the greek tree. */
387
SVN_ERR (svn_test__create_greek_tree (txn_root, pool));
388
SVN_ERR (svn_repos_fs_commit_txn (NULL, repos, &youngest_rev, txn, pool));
390
/* Now, commit again, this time after copying a directory, and then
391
deleting some paths under that directory. */
392
SVN_ERR (svn_fs_revision_root (&revision_root, fs, youngest_rev, pool));
393
SVN_ERR (svn_fs_begin_txn (&txn, fs, youngest_rev, pool));
394
SVN_ERR (svn_fs_txn_root (&txn_root, txn, pool));
395
SVN_ERR (svn_fs_copy (revision_root, "A", txn_root, "Z", pool));
396
SVN_ERR (svn_fs_delete (txn_root, "Z/D/G/rho", pool));
397
SVN_ERR (svn_fs_delete (txn_root, "Z/D/H", pool));
398
SVN_ERR (svn_repos_fs_commit_txn (NULL, repos, &youngest_rev, txn, pool));
400
/* Now, we run the node_tree editor code, and see that a) it doesn't
401
bomb out, and b) that our nodes are all good. */
402
SVN_ERR (svn_fs_revision_root (&revision_2_root, fs, youngest_rev, pool));
403
SVN_ERR (svn_repos_node_editor (&editor, &edit_baton, repos,
404
revision_root, revision_2_root,
406
SVN_ERR (svn_repos_replay (revision_2_root, editor, edit_baton, subpool));
408
/* Get the root of the generated tree, and cleanup our mess. */
409
tree = svn_repos_node_from_baton (edit_baton);
410
svn_pool_destroy (subpool);
412
/* See that we got what we expected (fortunately, svn_repos_replay
413
drivers editor paths in a predictable fashion!). */
416
&& tree->child /* /Z */
417
&& tree->child->child /* /Z/D */
418
&& tree->child->child->child /* /Z/D/G */
419
&& tree->child->child->child->child /* /Z/D/G/rho */
420
&& tree->child->child->child->sibling)) /* /Z/D/H */
421
return svn_error_create (SVN_ERR_TEST_FAILED, NULL,
422
"Generated node tree is bogus.");
424
if (! ((strcmp (tree->name, "") == 0)
425
&& (strcmp (tree->child->name, "Z") == 0)
426
&& (strcmp (tree->child->child->name, "D") == 0)
427
&& (strcmp (tree->child->child->child->name, "G") == 0)
428
&& ((strcmp (tree->child->child->child->child->name, "rho") == 0)
429
&& (tree->child->child->child->child->kind == svn_node_file)
430
&& (tree->child->child->child->child->action == 'D'))
431
&& ((strcmp (tree->child->child->child->sibling->name, "H") == 0)
432
&& (tree->child->child->child->sibling->kind == svn_node_dir)
433
&& (tree->child->child->child->sibling->action == 'D'))))
434
return svn_error_create (SVN_ERR_TEST_FAILED, NULL,
435
"Generated node tree is bogus.");
441
/* Helper for revisions_changed(). */
443
print_chrevs (const apr_array_header_t *revs_got,
444
int num_revs_expected,
445
const svn_revnum_t *revs_expected,
452
outstr = apr_psprintf (pool, "Got: { ");
455
for (i = 0; i < revs_got->nelts; i++)
457
rev = ((svn_revnum_t *)revs_got->elts)[i];
458
outstr = apr_pstrcat (pool,
460
apr_psprintf (pool, "%ld ", rev),
464
outstr = apr_pstrcat (pool, outstr, "} Expected: { ", NULL);
465
for (i = 0; i < num_revs_expected; i++)
467
outstr = apr_pstrcat (pool,
469
apr_psprintf (pool, "%ld ",
473
return apr_pstrcat (pool, outstr, "}", NULL);
477
/* Implements svn_repos_history_func_t interface. Accumulate history
478
revisions the apr_array_header_t * which is the BATON. */
480
history_to_revs_array (void *baton,
482
svn_revnum_t revision,
485
apr_array_header_t *revs_array = baton;
486
APR_ARRAY_PUSH (revs_array, svn_revnum_t) = revision;
490
struct revisions_changed_results
494
svn_revnum_t revs_changed[11];
499
revisions_changed (const char **msg,
500
svn_boolean_t msg_only,
501
svn_test_opts_t *opts,
504
apr_pool_t *spool = svn_pool_create (pool);
508
svn_fs_root_t *txn_root, *rev_root;
509
svn_revnum_t youngest_rev = 0;
511
*msg = "test svn_repos_history() (partially)";
516
/* Create a filesystem and repository. */
517
SVN_ERR (svn_test__create_repos (&repos, "test-repo-revisions-changed",
518
opts->fs_type, pool));
519
fs = svn_repos_fs (repos);
521
/*** Testing Algorithm ***
523
1. Create a greek tree in revision 1.
524
2. Make a series of new revisions, changing a file here and file
526
3. Loop over each path in each revision, verifying that we get
527
the right revisions-changed array back from the filesystem.
530
/* Created the greek tree in revision 1. */
531
SVN_ERR (svn_fs_begin_txn (&txn, fs, youngest_rev, spool));
532
SVN_ERR (svn_fs_txn_root (&txn_root, txn, spool));
533
SVN_ERR (svn_test__create_greek_tree (txn_root, spool));
534
SVN_ERR (svn_fs_commit_txn (NULL, &youngest_rev, txn, spool));
535
svn_pool_clear (spool);
537
/* Revision 2 - mu, alpha, omega */
538
SVN_ERR (svn_fs_begin_txn (&txn, fs, youngest_rev, spool));
539
SVN_ERR (svn_fs_txn_root (&txn_root, txn, spool));
540
SVN_ERR (svn_test__set_file_contents (txn_root, "A/mu", "2", spool));
541
SVN_ERR (svn_test__set_file_contents (txn_root, "A/B/E/alpha", "2", spool));
542
SVN_ERR (svn_test__set_file_contents (txn_root, "A/D/H/omega", "2", spool));
543
SVN_ERR (svn_fs_commit_txn (NULL, &youngest_rev, txn, spool));
544
svn_pool_clear (spool);
546
/* Revision 3 - iota, lambda, psi, omega */
547
SVN_ERR (svn_fs_begin_txn (&txn, fs, youngest_rev, spool));
548
SVN_ERR (svn_fs_txn_root (&txn_root, txn, spool));
549
SVN_ERR (svn_test__set_file_contents (txn_root, "iota", "3", spool));
550
SVN_ERR (svn_test__set_file_contents (txn_root, "A/B/lambda", "3", spool));
551
SVN_ERR (svn_test__set_file_contents (txn_root, "A/D/H/psi", "3", spool));
552
SVN_ERR (svn_test__set_file_contents (txn_root, "A/D/H/omega", "3", spool));
553
SVN_ERR (svn_fs_commit_txn (NULL, &youngest_rev, txn, spool));
554
svn_pool_clear (spool);
556
/* Revision 4 - iota, beta, gamma, pi, rho */
557
SVN_ERR (svn_fs_begin_txn (&txn, fs, youngest_rev, spool));
558
SVN_ERR (svn_fs_txn_root (&txn_root, txn, spool));
559
SVN_ERR (svn_test__set_file_contents (txn_root, "iota", "4", spool));
560
SVN_ERR (svn_test__set_file_contents (txn_root, "A/B/E/beta", "4", spool));
561
SVN_ERR (svn_test__set_file_contents (txn_root, "A/D/gamma", "4", spool));
562
SVN_ERR (svn_test__set_file_contents (txn_root, "A/D/G/pi", "4", spool));
563
SVN_ERR (svn_test__set_file_contents (txn_root, "A/D/G/rho", "4", spool));
564
SVN_ERR (svn_fs_commit_txn (NULL, &youngest_rev, txn, spool));
565
svn_pool_clear (spool);
567
/* Revision 5 - mu, alpha, tau, chi */
568
SVN_ERR (svn_fs_begin_txn (&txn, fs, youngest_rev, spool));
569
SVN_ERR (svn_fs_txn_root (&txn_root, txn, spool));
570
SVN_ERR (svn_test__set_file_contents (txn_root, "A/mu", "5", spool));
571
SVN_ERR (svn_test__set_file_contents (txn_root, "A/B/E/alpha", "5", spool));
572
SVN_ERR (svn_test__set_file_contents (txn_root, "A/D/G/tau", "5", spool));
573
SVN_ERR (svn_test__set_file_contents (txn_root, "A/D/H/chi", "5", spool));
574
SVN_ERR (svn_fs_commit_txn (NULL, &youngest_rev, txn, spool));
575
svn_pool_clear (spool);
577
/* Revision 6 - move A/D to A/Z */
578
SVN_ERR (svn_fs_begin_txn (&txn, fs, youngest_rev, spool));
579
SVN_ERR (svn_fs_txn_root (&txn_root, txn, spool));
580
SVN_ERR (svn_fs_revision_root (&rev_root, fs, youngest_rev, spool));
581
SVN_ERR (svn_fs_copy (rev_root, "A/D", txn_root, "A/Z", spool));
582
SVN_ERR (svn_fs_delete (txn_root, "A/D", spool));
583
SVN_ERR (svn_fs_commit_txn (NULL, &youngest_rev, txn, spool));
584
svn_pool_clear (spool);
586
/* Revision 7 - edit A/Z/G/pi */
587
SVN_ERR (svn_fs_begin_txn (&txn, fs, youngest_rev, spool));
588
SVN_ERR (svn_fs_txn_root (&txn_root, txn, spool));
589
SVN_ERR (svn_test__set_file_contents (txn_root, "A/Z/G/pi", "7", spool));
590
SVN_ERR (svn_fs_commit_txn (NULL, &youngest_rev, txn, spool));
591
svn_pool_clear (spool);
593
/* Revision 8 - move A/Z back to A/D, edit iota */
594
SVN_ERR (svn_fs_begin_txn (&txn, fs, youngest_rev, spool));
595
SVN_ERR (svn_fs_txn_root (&txn_root, txn, spool));
596
SVN_ERR (svn_fs_revision_root (&rev_root, fs, youngest_rev, spool));
597
SVN_ERR (svn_fs_copy (rev_root, "A/Z", txn_root, "A/D", spool));
598
SVN_ERR (svn_fs_delete (txn_root, "A/Z", spool));
599
SVN_ERR (svn_test__set_file_contents (txn_root, "iota", "8", spool));
600
SVN_ERR (svn_fs_commit_txn (NULL, &youngest_rev, txn, spool));
601
svn_pool_clear (spool);
603
/* Revision 9 - copy A/D/G to A/D/Q */
604
SVN_ERR (svn_fs_begin_txn (&txn, fs, youngest_rev, spool));
605
SVN_ERR (svn_fs_txn_root (&txn_root, txn, spool));
606
SVN_ERR (svn_fs_revision_root (&rev_root, fs, youngest_rev, spool));
607
SVN_ERR (svn_fs_copy (rev_root, "A/D/G", txn_root, "A/D/Q", spool));
608
SVN_ERR (svn_fs_commit_txn (NULL, &youngest_rev, txn, spool));
609
svn_pool_clear (spool);
611
/* Revision 10 - edit A/D/Q/pi and A/D/Q/rho */
612
SVN_ERR (svn_fs_begin_txn (&txn, fs, youngest_rev, spool));
613
SVN_ERR (svn_fs_txn_root (&txn_root, txn, spool));
614
SVN_ERR (svn_test__set_file_contents (txn_root, "A/D/Q/pi", "10", spool));
615
SVN_ERR (svn_test__set_file_contents (txn_root, "A/D/Q/rho", "10", spool));
616
SVN_ERR (svn_fs_commit_txn (NULL, &youngest_rev, txn, spool));
617
svn_pool_clear (spool);
619
/* Now, it's time to verify our results. */
622
/* Number, and list of, changed revisions for each path. Note
623
that for now, bubble-up in directories causes the directory to
624
appear changed though no entries were added or removed, and no
625
property mods occurred. Also note that this matrix represents
626
only the final state of the paths existing in HEAD of the
629
Notice for each revision, you can glance down that revision's
630
column in this table and see all the paths modified directory or
632
static const struct revisions_changed_results test_data[25] = {
633
/* path, num, revisions changed... */
634
{ "", 11, { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 } },
635
{ "iota", 4, { 8, 4, 3, 1 } },
636
{ "A", 10, { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 } },
637
{ "A/mu", 3, { 5, 2, 1 } },
638
{ "A/B", 5, { 5, 4, 3, 2, 1 } },
639
{ "A/B/lambda", 2, { 3, 1 } },
640
{ "A/B/E", 4, { 5, 4, 2, 1 } },
641
{ "A/B/E/alpha", 3, { 5, 2, 1 } },
642
{ "A/B/E/beta", 2, { 4, 1 } },
643
{ "A/B/F", 1, { 1 } },
645
{ "A/D", 10, { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 } },
646
{ "A/D/gamma", 4, { 8, 6, 4, 1 } },
647
{ "A/D/G", 6, { 8, 7, 6, 5, 4, 1 } },
648
{ "A/D/G/pi", 5, { 8, 7, 6, 4, 1 } },
649
{ "A/D/G/rho", 4, { 8, 6, 4, 1 } },
650
{ "A/D/G/tau", 4, { 8, 6, 5, 1 } },
651
{ "A/D/Q", 8, { 10, 9, 8, 7, 6, 5, 4, 1 } },
652
{ "A/D/Q/pi", 7, { 10, 9, 8, 7, 6, 4, 1 } },
653
{ "A/D/Q/rho", 6, { 10, 9, 8, 6, 4, 1 } },
654
{ "A/D/Q/tau", 5, { 9, 8, 6, 5, 1 } },
655
{ "A/D/H", 6, { 8, 6, 5, 3, 2, 1 } },
656
{ "A/D/H/chi", 4, { 8, 6, 5, 1 } },
657
{ "A/D/H/psi", 4, { 8, 6, 3, 1 } },
658
{ "A/D/H/omega", 5, { 8, 6, 3, 2, 1 } }
661
/* Now, for each path in the revision, get its changed-revisions
662
array and compare the array to the static results above. */
663
for (j = 0; j < 25; j++)
666
const char *path = test_data[j].path;
667
int num_revs = test_data[j].num_revs;
668
const svn_revnum_t *revs_changed = test_data[j].revs_changed;
669
apr_array_header_t *revs = apr_array_make (spool, 10,
670
sizeof (svn_revnum_t));
672
SVN_ERR (svn_repos_history (fs, path, history_to_revs_array, revs,
673
0, youngest_rev, TRUE, spool));
675
/* Are we at least looking at the right number of returned
677
if ((! revs) || (revs->nelts != num_revs))
678
return svn_error_createf
679
(SVN_ERR_FS_GENERAL, NULL,
680
"Changed revisions differ from expected for '%s'\n%s",
681
path, print_chrevs (revs, num_revs, revs_changed, spool));
683
/* Do the revisions lists match up exactly? */
684
for (i = 0; i < num_revs; i++)
686
svn_revnum_t rev = ((svn_revnum_t *)revs->elts)[i];
687
if (rev != revs_changed[i])
688
return svn_error_createf
689
(SVN_ERR_FS_GENERAL, NULL,
690
"Changed revisions differ from expected for '%s'\n%s",
691
path, print_chrevs (revs, num_revs, revs_changed, spool));
694
/* Clear the per-iteration subpool. */
695
svn_pool_clear (spool);
699
/* Destroy the subpool. */
700
svn_pool_destroy (spool);
707
struct locations_info
713
/* Check that LOCATIONS contain everything in INFO and nothing more. */
715
check_locations_info (apr_hash_t *locations, const struct locations_info *info)
718
for (i = 0; info->rev != 0; ++i, ++info)
720
const char *p = apr_hash_get (locations, &info->rev, sizeof
723
return svn_error_createf (SVN_ERR_TEST_FAILED, NULL,
724
"Missing path for revision %ld", info->rev);
725
if (strcmp (p, info->path) != 0)
726
return svn_error_createf (SVN_ERR_TEST_FAILED, NULL,
727
"Pth mismatch for rev %ld", info->rev);
730
if (apr_hash_count (locations) > i)
731
return svn_error_create (SVN_ERR_TEST_FAILED, NULL,
732
"Returned locations contain too many elements.");
737
/* Check that all locations in INFO exist in REPOS for PATH and PEG_REVISION.
740
check_locations (svn_fs_t *fs, struct locations_info *info,
741
const char *path, svn_revnum_t peg_revision,
744
apr_array_header_t *a = apr_array_make (pool, 0, sizeof (svn_revnum_t));
746
struct locations_info *iter;
748
for (iter = info; iter->rev != 0; ++iter)
749
*(svn_revnum_t *) apr_array_push (a) = iter->rev;
751
SVN_ERR (svn_repos_trace_node_locations (fs, &h, path, peg_revision, a,
753
SVN_ERR (check_locations_info (h, info));
759
node_locations (const char **msg,
760
svn_boolean_t msg_only,
761
svn_test_opts_t *opts,
764
apr_pool_t *subpool = svn_pool_create (pool);
768
svn_fs_root_t *txn_root, *root;
769
svn_revnum_t youngest_rev;
771
*msg = "test svn_repos_node_locations";
775
/* Create the repository with a Greek tree. */
776
SVN_ERR (svn_test__create_repos (&repos, "test-repo-node-locations",
777
opts->fs_type, pool));
778
fs = svn_repos_fs (repos);
779
SVN_ERR (svn_fs_begin_txn (&txn, fs, 0, subpool));
780
SVN_ERR (svn_fs_txn_root (&txn_root, txn, subpool));
781
SVN_ERR (svn_test__create_greek_tree (txn_root, subpool));
782
SVN_ERR (svn_repos_fs_commit_txn (NULL, repos, &youngest_rev, txn, subpool));
783
svn_pool_clear (subpool);
785
/* Move a file. Rev 2. */
786
SVN_ERR (svn_fs_revision_root (&root, fs, youngest_rev, subpool));
787
SVN_ERR (svn_fs_begin_txn (&txn, fs, 0, subpool));
788
SVN_ERR (svn_fs_txn_root (&txn_root, txn, subpool));
789
SVN_ERR (svn_fs_copy (root, "/A/mu", txn_root, "/mu.new", subpool));
790
SVN_ERR (svn_repos_fs_commit_txn (NULL, repos, &youngest_rev, txn, subpool));
792
struct locations_info info[] =
798
SVN_ERR (check_locations (fs, info, "/mu.new", 2, pool));
800
svn_pool_clear (subpool);
807
/* Testing the reporter. */
809
/* Functions for an editor that will catch removal of defunct locks. */
811
/* The main editor baton. */
812
typedef struct rmlocks_baton_t {
817
/* The file baton. */
818
typedef struct rmlocks_file_baton_t {
819
rmlocks_baton_t *main_baton;
821
} rmlocks_file_baton_t;
823
/* An svn_delta_editor_t function. */
825
rmlocks_open_file (const char *path,
827
svn_revnum_t base_revision,
828
apr_pool_t *file_pool,
831
rmlocks_file_baton_t *fb = apr_palloc (file_pool, sizeof (*fb));
832
rmlocks_baton_t *b = parent_baton;
835
fb->path = apr_pstrdup (b->pool, path);
842
/* An svn_delta_editor_t function. */
844
rmlocks_change_prop (void *file_baton,
846
const svn_string_t *value,
849
rmlocks_file_baton_t *fb = file_baton;
851
if (strcmp (name, SVN_PROP_ENTRY_LOCK_TOKEN) == 0)
854
return svn_error_create (SVN_ERR_TEST_FAILED, NULL,
855
"Value for lock-token property not NULL");
857
/* We only want it removed once. */
858
if (apr_hash_get (fb->main_baton->removed, fb->path,
859
APR_HASH_KEY_STRING) != NULL)
860
return svn_error_createf (SVN_ERR_TEST_FAILED, NULL,
861
"Lock token for '%s' already removed",
864
/* Mark as removed. */
865
apr_hash_set (fb->main_baton->removed, fb->path, APR_HASH_KEY_STRING,
872
/* An svn_delta_editor_t function. */
874
rmlocks_open_root (void *edit_baton,
875
svn_revnum_t base_revision,
876
apr_pool_t *dir_pool,
879
*root_baton = edit_baton;
883
/* An svn_delta_editor_t function. */
885
rmlocks_open_directory (const char *path,
887
svn_revnum_t base_revision,
891
*dir_baton = parent_baton;
895
/* Create an svn_delta_editor/baton, storing them in EDITOR/EDIT_BATON,
896
that will store paths for which lock tokens were *REMOVED in REMOVED.
897
Allocate the editor and *REMOVED in POOL. */
899
create_rmlocks_editor (svn_delta_editor_t **editor,
901
apr_hash_t **removed,
904
rmlocks_baton_t *baton = apr_palloc (pool, sizeof (*baton));
906
/* Create the editor. */
907
*editor = svn_delta_default_editor (pool);
908
(*editor)->open_root = rmlocks_open_root;
909
(*editor)->open_directory = rmlocks_open_directory;
910
(*editor)->open_file = rmlocks_open_file;
911
(*editor)->change_file_prop = rmlocks_change_prop;
913
/* Initialize the baton. */
914
baton->removed = apr_hash_make (pool);
918
*removed = baton->removed;
923
/* Check that HASH contains exactly the const char * entries for all entries
924
in the NULL-terminated array SPEC. */
926
rmlocks_check (const char **spec, apr_hash_t *hash)
930
for (; *spec; ++spec, ++n)
932
if (! apr_hash_get (hash, *spec, APR_HASH_KEY_STRING))
933
return svn_error_createf
934
(SVN_ERR_TEST_FAILED, NULL,
935
"Lock token for '%s' should have been removed", *spec);
938
if (n < apr_hash_count (hash))
939
return svn_error_create (SVN_ERR_TEST_FAILED, NULL,
940
"Lock token for one or more paths unexpectedly "
945
/* Test that defunct locks are removed by the reporter. */
947
rmlocks (const char **msg,
948
svn_boolean_t msg_only,
949
svn_test_opts_t *opts,
955
svn_fs_root_t *txn_root;
956
apr_pool_t *subpool = svn_pool_create (pool);
957
svn_revnum_t youngest_rev;
958
svn_delta_editor_t *editor;
959
void *edit_baton, *report_baton;
960
svn_lock_t *l1, *l2, *l3, *l4;
961
svn_fs_access_t *fs_access;
964
*msg = "test removal of defunct locks";
969
/* Create a filesystem and repository. */
970
SVN_ERR (svn_test__create_repos (&repos, "test-repo-rmlocks",
971
opts->fs_type, pool));
972
fs = svn_repos_fs (repos);
974
/* Prepare a txn to receive the greek tree. */
975
SVN_ERR (svn_fs_begin_txn (&txn, fs, 0, subpool));
976
SVN_ERR (svn_fs_txn_root (&txn_root, txn, subpool));
977
SVN_ERR (svn_test__create_greek_tree (txn_root, subpool));
978
SVN_ERR (svn_repos_fs_commit_txn (NULL, repos, &youngest_rev, txn, subpool));
979
svn_pool_clear (subpool);
981
SVN_ERR (svn_fs_create_access (&fs_access, "user1", pool));
982
SVN_ERR (svn_fs_set_access (fs, fs_access));
984
/* Lock some files, break a lock, steal another and check that those get
987
const char *expected [] = { "A/mu", "A/D/gamma", NULL };
989
SVN_ERR (svn_fs_lock (&l1, fs, "/iota", NULL, NULL, 0, 0, youngest_rev,
991
SVN_ERR (svn_fs_lock (&l2, fs, "/A/mu", NULL, NULL, 0, 0, youngest_rev,
993
SVN_ERR (svn_fs_lock (&l3, fs, "/A/D/gamma", NULL, NULL, 0, 0, youngest_rev,
997
SVN_ERR (svn_fs_unlock (fs, "/A/mu", NULL, TRUE, subpool));
999
/* Steal l3 from ourselves. */
1000
SVN_ERR (svn_fs_lock (&l4, fs, "/A/D/gamma", NULL, NULL, 0, 0, youngest_rev,
1003
/* Create the editor. */
1004
SVN_ERR (create_rmlocks_editor (&editor, &edit_baton, &removed, subpool));
1006
/* Report what we have. */
1007
SVN_ERR (svn_repos_begin_report (&report_baton, 1, "user1", repos, "/", "",
1008
NULL, FALSE, TRUE, FALSE, editor,
1009
edit_baton, NULL, NULL, subpool));
1010
SVN_ERR (svn_repos_set_path2 (report_baton, "", 1, FALSE, NULL,
1012
SVN_ERR (svn_repos_set_path2 (report_baton, "iota", 1, FALSE, l1->token,
1014
SVN_ERR (svn_repos_set_path2 (report_baton, "A/mu", 1, FALSE, l2->token,
1016
SVN_ERR (svn_repos_set_path2 (report_baton, "A/D/gamma", 1, FALSE,
1017
l3->token, subpool));
1019
/* End the report. */
1020
SVN_ERR (svn_repos_finish_report (report_baton, pool));
1022
/* And check that the edit did what we wanted. */
1023
SVN_ERR (rmlocks_check (expected, removed));
1026
svn_pool_destroy (subpool);
1028
return SVN_NO_ERROR;
1031
/* The test table. */
1033
struct svn_test_descriptor_t test_funcs[] =
1036
SVN_TEST_PASS (dir_deltas),
1037
SVN_TEST_PASS (node_tree_delete_under_copy),
1038
SVN_TEST_PASS (revisions_changed),
1039
SVN_TEST_PASS (node_locations),
1040
SVN_TEST_PASS (rmlocks),