1 /* fs-test.c --- tests for the filesystem
2 *
3 * ====================================================================
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 * ====================================================================
21 */
22
23 #include <stdlib.h>
24 #include <string.h>
25 #include <apr_pools.h>
26 #include <apr_thread_proc.h>
27 #include <apr_poll.h>
28 #include <assert.h>
29
30 #include "../svn_test.h"
31
32 #include "svn_hash.h"
33 #include "svn_pools.h"
34 #include "svn_time.h"
35 #include "svn_string.h"
36 #include "svn_fs.h"
37 #include "svn_checksum.h"
38 #include "svn_mergeinfo.h"
39 #include "svn_props.h"
40 #include "svn_version.h"
41
42 #include "svn_private_config.h"
43 #include "private/svn_cache.h"
44 #include "private/svn_fs_util.h"
45 #include "private/svn_fs_private.h"
46 #include "private/svn_fspath.h"
47 #include "private/svn_sqlite.h"
48
49 #include "../svn_test_fs.h"
50
51 #include "../../libsvn_delta/delta.h"
52 #include "../../libsvn_fs/fs-loader.h"
53
54 #define SET_STR(ps, s) ((ps)->data = (s), (ps)->len = strlen(s))
55
56
57 /*-----------------------------------------------------------------*/
58
59 /** The actual fs-tests called by `make check` **/
60
61 /* Helper: commit TXN, expecting either success or failure:
62 *
63 * If EXPECTED_CONFLICT is null, then the commit is expected to
64 * succeed. If it does succeed, set *NEW_REV to the new revision;
65 * else return error.
66 *
67 * If EXPECTED_CONFLICT is non-null, it is either the empty string or
68 * the expected path of the conflict. If it is the empty string, any
69 * conflict is acceptable. If it is a non-empty string, the commit
70 * must fail due to conflict, and the conflict path must match
71 * EXPECTED_CONFLICT. If they don't match, return error.
72 *
73 * If a conflict is expected but the commit succeeds anyway, return
74 * error. If the commit fails but does not provide an error, return
75 * error.
76 */
77 static svn_error_t *
test_commit_txn(svn_revnum_t * new_rev,svn_fs_txn_t * txn,const char * expected_conflict,apr_pool_t * pool)78 test_commit_txn(svn_revnum_t *new_rev,
79 svn_fs_txn_t *txn,
80 const char *expected_conflict,
81 apr_pool_t *pool)
82 {
83 const char *conflict;
84 svn_error_t *err;
85
86 err = svn_fs_commit_txn(&conflict, new_rev, txn, pool);
87
88 if (err && (err->apr_err == SVN_ERR_FS_CONFLICT))
89 {
90 svn_error_clear(err);
91 if (! expected_conflict)
92 {
93 return svn_error_createf
94 (SVN_ERR_FS_CONFLICT, NULL,
95 "commit conflicted at '%s', but no conflict expected",
96 conflict ? conflict : "(missing conflict info!)");
97 }
98 else if (conflict == NULL)
99 {
100 return svn_error_createf
101 (SVN_ERR_FS_CONFLICT, NULL,
102 "commit conflicted as expected, "
103 "but no conflict path was returned ('%s' expected)",
104 expected_conflict);
105 }
106 else if ((strcmp(expected_conflict, "") != 0)
107 && (strcmp(conflict, expected_conflict) != 0))
108 {
109 return svn_error_createf
110 (SVN_ERR_FS_CONFLICT, NULL,
111 "commit conflicted at '%s', but expected conflict at '%s')",
112 conflict, expected_conflict);
113 }
114
115 /* The svn_fs_commit_txn() API promises to set *NEW_REV to an
116 invalid revision number in the case of a conflict. */
117 if (SVN_IS_VALID_REVNUM(*new_rev))
118 {
119 return svn_error_createf
120 (SVN_ERR_FS_GENERAL, NULL,
121 "conflicting commit returned valid new revision");
122 }
123 }
124 else if (err) /* commit may have succeeded, but always report an error */
125 {
126 if (SVN_IS_VALID_REVNUM(*new_rev))
127 return svn_error_quick_wrap
128 (err, "commit succeeded but something else failed");
129 else
130 return svn_error_quick_wrap
131 (err, "commit failed due to something other than a conflict");
132 }
133 else /* err == NULL, commit should have succeeded */
134 {
135 if (! SVN_IS_VALID_REVNUM(*new_rev))
136 {
137 return svn_error_create
138 (SVN_ERR_FS_GENERAL, NULL,
139 "commit failed but no error was returned");
140 }
141
142 if (expected_conflict)
143 {
144 return svn_error_createf
145 (SVN_ERR_FS_GENERAL, NULL,
146 "commit succeeded that was expected to fail at '%s'",
147 expected_conflict);
148 }
149 }
150
151 return SVN_NO_ERROR;
152 }
153
154
155
156 /* Begin a txn, check its name, then close it */
157 static svn_error_t *
trivial_transaction(const svn_test_opts_t * opts,apr_pool_t * pool)158 trivial_transaction(const svn_test_opts_t *opts,
159 apr_pool_t *pool)
160 {
161 svn_fs_t *fs;
162 svn_fs_txn_t *txn;
163 const char *txn_name;
164 int is_invalid_char[256];
165 int i;
166 const char *p;
167
168 SVN_ERR(svn_test__create_fs(&fs, "test-repo-trivial-txn",
169 opts, pool));
170
171 /* Begin a new transaction that is based on revision 0. */
172 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
173
174 /* Test that the txn name is non-null. */
175 SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool));
176
177 if (! txn_name)
178 return svn_error_create(SVN_ERR_FS_GENERAL, NULL,
179 "Got a NULL txn name.");
180
181 /* Test that the txn name contains only valid characters. See
182 svn_fs.h for the list of valid characters. */
183 for (i = 0; i < sizeof(is_invalid_char)/sizeof(*is_invalid_char); ++i)
184 is_invalid_char[i] = 1;
185 for (i = '0'; i <= '9'; ++i)
186 is_invalid_char[i] = 0;
187 for (i = 'a'; i <= 'z'; ++i)
188 is_invalid_char[i] = 0;
189 for (i = 'A'; i <= 'Z'; ++i)
190 is_invalid_char[i] = 0;
191 for (p = "-."; *p; ++p)
192 is_invalid_char[(unsigned char) *p] = 0;
193
194 for (p = txn_name; *p; ++p)
195 {
196 if (is_invalid_char[(unsigned char) *p])
197 return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
198 "The txn name '%s' contains an illegal '%c' "
199 "character", txn_name, *p);
200 }
201
202 return SVN_NO_ERROR;
203 }
204
205
206
207 /* Open an existing transaction by name. */
208 static svn_error_t *
reopen_trivial_transaction(const svn_test_opts_t * opts,apr_pool_t * pool)209 reopen_trivial_transaction(const svn_test_opts_t *opts,
210 apr_pool_t *pool)
211 {
212 svn_fs_t *fs;
213 svn_fs_txn_t *txn;
214 svn_fs_root_t *root;
215 const char *txn_name;
216 apr_pool_t *subpool = svn_pool_create(pool);
217
218 SVN_ERR(svn_test__create_fs(&fs, "test-repo-reopen-trivial-txn",
219 opts, pool));
220
221 /* Create a first transaction - we don't want that one to reopen. */
222 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
223
224 /* Begin a second transaction that is based on revision 0. */
225 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
226
227 /* Don't use the subpool, txn_name must persist beyond the current txn */
228 SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool));
229
230 SVN_TEST_ASSERT(svn_fs_txn_base_revision(txn) == 0);
231
232 /* Create a third transaction - we don't want that one to reopen. */
233 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
234
235 /* Close the transaction. */
236 svn_pool_clear(subpool);
237
238 /* Reopen the transaction by name */
239 SVN_ERR(svn_fs_open_txn(&txn, fs, txn_name, subpool));
240
241 /* Does it have the same name? */
242 SVN_ERR(svn_fs_txn_root(&root, txn, subpool));
243 SVN_TEST_STRING_ASSERT(svn_fs_txn_root_name(root, subpool), txn_name);
244
245 SVN_TEST_ASSERT(svn_fs_txn_base_revision(txn) == 0);
246
247 {
248 const char *conflict;
249 svn_revnum_t new_rev;
250 SVN_ERR(svn_fs_commit_txn(&conflict, &new_rev, txn, subpool));
251 SVN_TEST_STRING_ASSERT(conflict, NULL);
252 SVN_TEST_ASSERT(new_rev == 1);
253 }
254
255 /* Close the transaction ... again. */
256 svn_pool_clear(subpool);
257
258 /* Begin another transaction that is based on revision 1. */
259 SVN_ERR(svn_fs_begin_txn(&txn, fs, 1, subpool));
260
261 /* Don't use the subpool, txn_name must persist beyond the current txn */
262 SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool));
263
264 SVN_TEST_ASSERT(svn_fs_txn_base_revision(txn) == 1);
265
266 /* Keep the txn name in pool */
267 SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool));
268
269 /* Close the transaction ... again. */
270 svn_pool_clear(subpool);
271
272 /* Reopen the transaction by name ... again */
273 SVN_ERR(svn_fs_open_txn(&txn, fs, txn_name, subpool));
274
275 /* Does it have the same name? ... */
276 SVN_ERR(svn_fs_txn_root(&root, txn, subpool));
277 SVN_TEST_STRING_ASSERT(svn_fs_txn_root_name(root, subpool), txn_name);
278
279 /* And the same base revision? */
280 SVN_TEST_ASSERT(svn_fs_txn_base_revision(txn) == 1);
281
282 svn_pool_destroy(subpool);
283
284 return SVN_NO_ERROR;
285 }
286
287
288
289 /* Create a file! */
290 static svn_error_t *
create_file_transaction(const svn_test_opts_t * opts,apr_pool_t * pool)291 create_file_transaction(const svn_test_opts_t *opts,
292 apr_pool_t *pool)
293 {
294 svn_fs_t *fs;
295 svn_fs_txn_t *txn;
296 svn_fs_root_t *txn_root;
297
298 SVN_ERR(svn_test__create_fs(&fs, "test-repo-create-file-txn",
299 opts, pool));
300
301 /* Begin a new transaction that is based on revision 0. */
302 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
303
304 /* Get the txn root */
305 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
306
307 /* Create a new file in the root directory. */
308 SVN_ERR(svn_fs_make_file(txn_root, "beer.txt", pool));
309
310 return SVN_NO_ERROR;
311 }
312
313
314 /* Make sure we get txn lists correctly. */
315 static svn_error_t *
verify_txn_list(const svn_test_opts_t * opts,apr_pool_t * pool)316 verify_txn_list(const svn_test_opts_t *opts,
317 apr_pool_t *pool)
318 {
319 svn_fs_t *fs;
320 apr_pool_t *subpool;
321 svn_fs_txn_t *txn1, *txn2;
322 const char *name1, *name2;
323 apr_array_header_t *txn_list;
324
325 SVN_ERR(svn_test__create_fs(&fs, "test-repo-verify-txn-list",
326 opts, pool));
327
328 /* Begin a new transaction, get its name (in the top pool), close it. */
329 subpool = svn_pool_create(pool);
330 SVN_ERR(svn_fs_begin_txn(&txn1, fs, 0, subpool));
331 SVN_ERR(svn_fs_txn_name(&name1, txn1, pool));
332 svn_pool_destroy(subpool);
333
334 /* Begin *another* transaction, get its name (in the top pool), close it. */
335 subpool = svn_pool_create(pool);
336 SVN_ERR(svn_fs_begin_txn(&txn2, fs, 0, subpool));
337 SVN_ERR(svn_fs_txn_name(&name2, txn2, pool));
338 svn_pool_destroy(subpool);
339
340 /* Get the list of active transactions from the fs. */
341 SVN_ERR(svn_fs_list_transactions(&txn_list, fs, pool));
342
343 /* Check the list. It should have *exactly* two entries. */
344 if (txn_list->nelts != 2)
345 goto all_bad;
346
347 /* We should be able to find our 2 txn names in the list, in some
348 order. */
349 if ((! strcmp(name1, APR_ARRAY_IDX(txn_list, 0, const char *)))
350 && (! strcmp(name2, APR_ARRAY_IDX(txn_list, 1, const char *))))
351 goto all_good;
352
353 else if ((! strcmp(name2, APR_ARRAY_IDX(txn_list, 0, const char *)))
354 && (! strcmp(name1, APR_ARRAY_IDX(txn_list, 1, const char *))))
355 goto all_good;
356
357 all_bad:
358
359 return svn_error_create(SVN_ERR_FS_GENERAL, NULL,
360 "Got a bogus txn list.");
361 all_good:
362
363 return SVN_NO_ERROR;
364 }
365
366
367 /* Generate N consecutive transactions, then abort them all. Return
368 the list of transaction names. */
369 static svn_error_t *
txn_names_are_not_reused_helper1(apr_hash_t ** txn_names,svn_fs_t * fs,apr_pool_t * pool)370 txn_names_are_not_reused_helper1(apr_hash_t **txn_names,
371 svn_fs_t *fs,
372 apr_pool_t *pool)
373 {
374 apr_hash_index_t *hi;
375 const int N = 10;
376 int i;
377 apr_pool_t *subpool = svn_pool_create(pool);
378
379 *txn_names = apr_hash_make(pool);
380
381 /* Create the transactions and store in a hash table the transaction
382 name as the key and the svn_fs_txn_t * as the value. */
383 for (i = 0; i < N; ++i)
384 {
385 svn_fs_txn_t *txn;
386 const char *name;
387
388 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
389 SVN_ERR(svn_fs_txn_name(&name, txn, pool));
390 if (apr_hash_get(*txn_names, name, APR_HASH_KEY_STRING) != NULL)
391 return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
392 "beginning a new transaction used an "
393 "existing transaction name '%s'",
394 name);
395 apr_hash_set(*txn_names, name, APR_HASH_KEY_STRING, txn);
396 }
397
398 i = 0;
399 for (hi = apr_hash_first(pool, *txn_names); hi; hi = apr_hash_next(hi))
400 {
401 void *val;
402 apr_hash_this(hi, NULL, NULL, &val);
403 SVN_ERR(svn_fs_abort_txn((svn_fs_txn_t *)val, pool));
404 ++i;
405 }
406
407 if (i != N)
408 return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
409 "created %d transactions, but only aborted %d",
410 N, i);
411
412 svn_pool_destroy(subpool);
413 return SVN_NO_ERROR;
414 }
415
416 /* Compare two hash tables and ensure that no keys in the first hash
417 table appear in the second hash table. */
418 static svn_error_t *
txn_names_are_not_reused_helper2(apr_hash_t * ht1,apr_hash_t * ht2,apr_pool_t * pool)419 txn_names_are_not_reused_helper2(apr_hash_t *ht1,
420 apr_hash_t *ht2,
421 apr_pool_t *pool)
422 {
423 apr_hash_index_t *hi;
424
425 for (hi = apr_hash_first(pool, ht1); hi; hi = apr_hash_next(hi))
426 {
427 const void *key;
428 const char *key_string;
429 apr_hash_this(hi, &key, NULL, NULL);
430 key_string = key;
431 if (apr_hash_get(ht2, key, APR_HASH_KEY_STRING) != NULL)
432 return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
433 "the transaction name '%s' was reused",
434 key_string);
435 }
436
437 return SVN_NO_ERROR;
438 }
439
440 /* Make sure that transaction names are not reused. */
441 static svn_error_t *
txn_names_are_not_reused(const svn_test_opts_t * opts,apr_pool_t * pool)442 txn_names_are_not_reused(const svn_test_opts_t *opts,
443 apr_pool_t *pool)
444 {
445 svn_fs_t *fs;
446 apr_pool_t *subpool;
447 apr_hash_t *txn_names1, *txn_names2;
448
449 /* Bail (with success) on known-untestable scenarios */
450 if ((strcmp(opts->fs_type, "fsfs") == 0)
451 && (opts->server_minor_version && (opts->server_minor_version < 5)))
452 return SVN_NO_ERROR;
453
454 SVN_ERR(svn_test__create_fs(&fs, "test-repo-txn-names-are-not-reused",
455 opts, pool));
456
457 subpool = svn_pool_create(pool);
458
459 /* Create N transactions, abort them all, and collect the generated
460 transaction names. Do this twice. */
461 SVN_ERR(txn_names_are_not_reused_helper1(&txn_names1, fs, subpool));
462 SVN_ERR(txn_names_are_not_reused_helper1(&txn_names2, fs, subpool));
463
464 /* Check that no transaction names appear in both hash tables. */
465 SVN_ERR(txn_names_are_not_reused_helper2(txn_names1, txn_names2, subpool));
466 SVN_ERR(txn_names_are_not_reused_helper2(txn_names2, txn_names1, subpool));
467
468 svn_pool_destroy(subpool);
469
470 return SVN_NO_ERROR;
471 }
472
473
474
475 /* Test writing & reading a file's contents. */
476 static svn_error_t *
write_and_read_file(const svn_test_opts_t * opts,apr_pool_t * pool)477 write_and_read_file(const svn_test_opts_t *opts,
478 apr_pool_t *pool)
479 {
480 svn_fs_t *fs;
481 svn_fs_txn_t *txn;
482 svn_fs_root_t *txn_root;
483 svn_stream_t *rstream;
484 svn_stringbuf_t *rstring;
485 svn_stringbuf_t *wstring;
486
487 wstring = svn_stringbuf_create("Wicki wild, wicki wicki wild.", pool);
488 SVN_ERR(svn_test__create_fs(&fs, "test-repo-read-and-write-file",
489 opts, pool));
490 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
491 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
492
493 /* Add an empty file. */
494 SVN_ERR(svn_fs_make_file(txn_root, "beer.txt", pool));
495
496 /* And write some data into this file. */
497 SVN_ERR(svn_test__set_file_contents(txn_root, "beer.txt",
498 wstring->data, pool));
499
500 /* Now let's read the data back from the file. */
501 SVN_ERR(svn_fs_file_contents(&rstream, txn_root, "beer.txt", pool));
502 SVN_ERR(svn_test__stream_to_string(&rstring, rstream, pool));
503
504 /* Compare what was read to what was written. */
505 if (! svn_stringbuf_compare(rstring, wstring))
506 return svn_error_create(SVN_ERR_FS_GENERAL, NULL,
507 "data read != data written.");
508
509 return SVN_NO_ERROR;
510 }
511
512
513
514 /* Create a file, a directory, and a file in that directory! */
515 static svn_error_t *
create_mini_tree_transaction(const svn_test_opts_t * opts,apr_pool_t * pool)516 create_mini_tree_transaction(const svn_test_opts_t *opts,
517 apr_pool_t *pool)
518 {
519 svn_fs_t *fs;
520 svn_fs_txn_t *txn;
521 svn_fs_root_t *txn_root;
522
523 SVN_ERR(svn_test__create_fs(&fs, "test-repo-create-mini-tree-txn",
524 opts, pool));
525
526 /* Begin a new transaction that is based on revision 0. */
527 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
528
529 /* Get the txn root */
530 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
531
532 /* Create a new file in the root directory. */
533 SVN_ERR(svn_fs_make_file(txn_root, "wine.txt", pool));
534
535 /* Create a new directory in the root directory. */
536 SVN_ERR(svn_fs_make_dir(txn_root, "keg", pool));
537
538 /* Now, create a file in our new directory. */
539 SVN_ERR(svn_fs_make_file(txn_root, "keg/beer.txt", pool));
540
541 return SVN_NO_ERROR;
542 }
543
544
545 /* Create a file, a directory, and a file in that directory! */
546 static svn_error_t *
create_greek_tree_transaction(const svn_test_opts_t * opts,apr_pool_t * pool)547 create_greek_tree_transaction(const svn_test_opts_t *opts,
548 apr_pool_t *pool)
549 {
550 svn_fs_t *fs;
551 svn_fs_txn_t *txn;
552 svn_fs_root_t *txn_root;
553
554 /* Prepare a txn to receive the greek tree. */
555 SVN_ERR(svn_test__create_fs(&fs, "test-repo-create-greek-tree-txn",
556 opts, pool));
557 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
558 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
559
560 /* Create and verify the greek tree. */
561 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
562
563 return SVN_NO_ERROR;
564 }
565
566
567 /* Verify that entry KEY is present in ENTRIES, and that its value is
568 an svn_fs_dirent_t whose name and id are not null. */
569 static svn_error_t *
verify_entry(apr_hash_t * entries,const char * key)570 verify_entry(apr_hash_t *entries, const char *key)
571 {
572 svn_fs_dirent_t *ent = apr_hash_get(entries, key,
573 APR_HASH_KEY_STRING);
574
575 if (ent == NULL)
576 return svn_error_createf
577 (SVN_ERR_FS_GENERAL, NULL,
578 "didn't find dir entry for \"%s\"", key);
579
580 if ((ent->name == NULL) && (ent->id == NULL))
581 return svn_error_createf
582 (SVN_ERR_FS_GENERAL, NULL,
583 "dir entry for \"%s\" has null name and null id", key);
584
585 if (ent->name == NULL)
586 return svn_error_createf
587 (SVN_ERR_FS_GENERAL, NULL,
588 "dir entry for \"%s\" has null name", key);
589
590 if (ent->id == NULL)
591 return svn_error_createf
592 (SVN_ERR_FS_GENERAL, NULL,
593 "dir entry for \"%s\" has null id", key);
594
595 if (strcmp(ent->name, key) != 0)
596 return svn_error_createf
597 (SVN_ERR_FS_GENERAL, NULL,
598 "dir entry for \"%s\" contains wrong name (\"%s\")", key, ent->name);
599
600 return SVN_NO_ERROR;
601 }
602
603
604 static svn_error_t *
list_directory(const svn_test_opts_t * opts,apr_pool_t * pool)605 list_directory(const svn_test_opts_t *opts,
606 apr_pool_t *pool)
607 {
608 svn_fs_t *fs;
609 svn_fs_txn_t *txn;
610 svn_fs_root_t *txn_root;
611 apr_hash_t *entries;
612
613 SVN_ERR(svn_test__create_fs(&fs, "test-repo-list-dir",
614 opts, pool));
615 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
616 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
617
618 /* We create this tree
619 *
620 * /q
621 * /A/x
622 * /A/y
623 * /A/z
624 * /B/m
625 * /B/n
626 * /B/o
627 *
628 * then list dir A. It should have 3 files: "x", "y", and "z", no
629 * more, no less.
630 */
631
632 /* Create the tree. */
633 SVN_ERR(svn_fs_make_file(txn_root, "q", pool));
634 SVN_ERR(svn_fs_make_dir(txn_root, "A", pool));
635 SVN_ERR(svn_fs_make_file(txn_root, "A/x", pool));
636 SVN_ERR(svn_fs_make_file(txn_root, "A/y", pool));
637 SVN_ERR(svn_fs_make_file(txn_root, "A/z", pool));
638 SVN_ERR(svn_fs_make_dir(txn_root, "B", pool));
639 SVN_ERR(svn_fs_make_file(txn_root, "B/m", pool));
640 SVN_ERR(svn_fs_make_file(txn_root, "B/n", pool));
641 SVN_ERR(svn_fs_make_file(txn_root, "B/o", pool));
642
643 /* Get A's entries. */
644 SVN_ERR(svn_fs_dir_entries(&entries, txn_root, "A", pool));
645
646 /* Make sure exactly the right set of entries is present. */
647 if (apr_hash_count(entries) != 3)
648 {
649 return svn_error_create(SVN_ERR_FS_GENERAL, NULL,
650 "unexpected number of entries in dir");
651 }
652 else
653 {
654 SVN_ERR(verify_entry(entries, "x"));
655 SVN_ERR(verify_entry(entries, "y"));
656 SVN_ERR(verify_entry(entries, "z"));
657 }
658
659 return SVN_NO_ERROR;
660 }
661
662
663 /* If EXPR raises SVN_ERR_FS_PROP_BASEVALUE_MISMATCH, continue; else, fail
664 * the test. */
665 #define FAILS_WITH_BOV(expr) \
666 do { \
667 svn_error_t *__err = (expr); \
668 if (!__err || __err->apr_err != SVN_ERR_FS_PROP_BASEVALUE_MISMATCH) \
669 return svn_error_create(SVN_ERR_TEST_FAILED, __err, \
670 "svn_fs_change_rev_prop2() failed to " \
671 "detect unexpected old value"); \
672 else \
673 svn_error_clear(__err); \
674 } while (0)
675
676 static svn_error_t *
revision_props(const svn_test_opts_t * opts,apr_pool_t * pool)677 revision_props(const svn_test_opts_t *opts,
678 apr_pool_t *pool)
679 {
680 svn_fs_t *fs;
681 apr_hash_t *proplist;
682 svn_string_t *value;
683 int i;
684 svn_string_t s1;
685
686 const char *initial_props[4][2] = {
687 { "color", "red" },
688 { "size", "XXL" },
689 { "favorite saturday morning cartoon", "looney tunes" },
690 { "auto", "Green 1997 Saturn SL1" }
691 };
692
693 const char *final_props[4][2] = {
694 { "color", "violet" },
695 { "flower", "violet" },
696 { "favorite saturday morning cartoon", "looney tunes" },
697 { "auto", "Red 2000 Chevrolet Blazer" }
698 };
699
700 /* Open the fs */
701 SVN_ERR(svn_test__create_fs(&fs, "test-repo-rev-props",
702 opts, pool));
703
704 /* Set some properties on the revision. */
705 for (i = 0; i < 4; i++)
706 {
707 SET_STR(&s1, initial_props[i][1]);
708 SVN_ERR(svn_fs_change_rev_prop(fs, 0, initial_props[i][0], &s1, pool));
709 }
710
711 /* Change some of the above properties. */
712 SET_STR(&s1, "violet");
713 SVN_ERR(svn_fs_change_rev_prop(fs, 0, "color", &s1, pool));
714
715 SET_STR(&s1, "Red 2000 Chevrolet Blazer");
716 SVN_ERR(svn_fs_change_rev_prop(fs, 0, "auto", &s1, pool));
717
718 /* Remove a property altogether */
719 SVN_ERR(svn_fs_change_rev_prop(fs, 0, "size", NULL, pool));
720
721 /* Copy a property's value into a new property. */
722 SVN_ERR(svn_fs_revision_prop(&value, fs, 0, "color", pool));
723 SVN_TEST_ASSERT(value);
724
725 s1.data = value->data;
726 s1.len = value->len;
727 SVN_ERR(svn_fs_change_rev_prop(fs, 0, "flower", &s1, pool));
728
729 /* Test svn_fs_change_rev_prop2(). If the whole block goes through, then
730 * it is a no-op (it undoes all changes it makes). */
731 {
732 const svn_string_t s2 = { "wrong value", 11 };
733 const svn_string_t *s2_p = &s2;
734 const svn_string_t *s1_p = &s1;
735 const svn_string_t *unset = NULL;
736 const svn_string_t *s1_dup;
737
738 /* Value of "flower" is 's1'. */
739
740 FAILS_WITH_BOV(svn_fs_change_rev_prop2(fs, 0, "flower", &s2_p, s1_p, pool));
741 s1_dup = svn_string_dup(&s1, pool);
742 SVN_ERR(svn_fs_change_rev_prop2(fs, 0, "flower", &s1_dup, s2_p, pool));
743
744 /* Value of "flower" is 's2'. */
745
746 FAILS_WITH_BOV(svn_fs_change_rev_prop2(fs, 0, "flower", &s1_p, NULL, pool));
747 SVN_ERR(svn_fs_change_rev_prop2(fs, 0, "flower", &s2_p, NULL, pool));
748
749 /* Value of "flower" is <not set>. */
750
751 FAILS_WITH_BOV(svn_fs_change_rev_prop2(fs, 0, "flower", &s2_p, s1_p, pool));
752 SVN_ERR(svn_fs_change_rev_prop2(fs, 0, "flower", &unset, s1_p, pool));
753
754 /* Value of "flower" is 's1'. */
755 }
756
757 /* Obtain a list of all current properties, and make sure it matches
758 the expected values. */
759 SVN_ERR(svn_fs_revision_proplist(&proplist, fs, 0, pool));
760 SVN_TEST_ASSERT(proplist);
761 {
762 svn_string_t *prop_value;
763
764 if (apr_hash_count(proplist) < 4 )
765 return svn_error_createf
766 (SVN_ERR_FS_GENERAL, NULL,
767 "too few revision properties found");
768
769 /* Loop through our list of expected revision property name/value
770 pairs. */
771 for (i = 0; i < 4; i++)
772 {
773 /* For each expected property: */
774
775 /* Step 1. Find it by name in the hash of all rev. props
776 returned to us by svn_fs_revision_proplist. If it can't be
777 found, return an error. */
778 prop_value = apr_hash_get(proplist,
779 final_props[i][0],
780 APR_HASH_KEY_STRING);
781 if (! prop_value)
782 return svn_error_createf
783 (SVN_ERR_FS_GENERAL, NULL,
784 "unable to find expected revision property");
785
786 /* Step 2. Make sure the value associated with it is the same
787 as what was expected, else return an error. */
788 if (strcmp(prop_value->data, final_props[i][1]))
789 return svn_error_createf
790 (SVN_ERR_FS_GENERAL, NULL,
791 "revision property had an unexpected value");
792 }
793 }
794
795 return SVN_NO_ERROR;
796 }
797
798
799 static svn_error_t *
transaction_props(const svn_test_opts_t * opts,apr_pool_t * pool)800 transaction_props(const svn_test_opts_t *opts,
801 apr_pool_t *pool)
802 {
803 svn_fs_t *fs;
804 svn_fs_txn_t *txn;
805 apr_hash_t *proplist;
806 svn_string_t *value;
807 svn_revnum_t after_rev;
808 int i;
809 svn_string_t s1;
810
811 const char *initial_props[4][2] = {
812 { "color", "red" },
813 { "size", "XXL" },
814 { "favorite saturday morning cartoon", "looney tunes" },
815 { "auto", "Green 1997 Saturn SL1" }
816 };
817
818 const char *final_props[5][2] = {
819 { "color", "violet" },
820 { "flower", "violet" },
821 { "favorite saturday morning cartoon", "looney tunes" },
822 { "auto", "Red 2000 Chevrolet Blazer" },
823 { SVN_PROP_REVISION_DATE, "<some datestamp value>" }
824 };
825
826 /* Open the fs */
827 SVN_ERR(svn_test__create_fs(&fs, "test-repo-txn-props",
828 opts, pool));
829 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
830
831 /* Set some properties on the revision. */
832 for (i = 0; i < 4; i++)
833 {
834 SET_STR(&s1, initial_props[i][1]);
835 SVN_ERR(svn_fs_change_txn_prop(txn, initial_props[i][0], &s1, pool));
836 }
837
838 /* Change some of the above properties. */
839 SET_STR(&s1, "violet");
840 SVN_ERR(svn_fs_change_txn_prop(txn, "color", &s1, pool));
841
842 SET_STR(&s1, "Red 2000 Chevrolet Blazer");
843 SVN_ERR(svn_fs_change_txn_prop(txn, "auto", &s1, pool));
844
845 /* Remove a property altogether */
846 SVN_ERR(svn_fs_change_txn_prop(txn, "size", NULL, pool));
847
848 /* Copy a property's value into a new property. */
849 SVN_ERR(svn_fs_txn_prop(&value, txn, "color", pool));
850
851 s1.data = value->data;
852 s1.len = value->len;
853 SVN_ERR(svn_fs_change_txn_prop(txn, "flower", &s1, pool));
854
855 /* Obtain a list of all current properties, and make sure it matches
856 the expected values. */
857 SVN_ERR(svn_fs_txn_proplist(&proplist, txn, pool));
858 {
859 svn_string_t *prop_value;
860
861 /* All transactions get a datestamp property at their inception,
862 so we expect *5*, not 4 properties. */
863 if (apr_hash_count(proplist) != 5 )
864 return svn_error_createf
865 (SVN_ERR_FS_GENERAL, NULL,
866 "unexpected number of transaction properties were found");
867
868 /* Loop through our list of expected revision property name/value
869 pairs. */
870 for (i = 0; i < 5; i++)
871 {
872 /* For each expected property: */
873
874 /* Step 1. Find it by name in the hash of all rev. props
875 returned to us by svn_fs_revision_proplist. If it can't be
876 found, return an error. */
877 prop_value = apr_hash_get(proplist,
878 final_props[i][0],
879 APR_HASH_KEY_STRING);
880 if (! prop_value)
881 return svn_error_createf
882 (SVN_ERR_FS_GENERAL, NULL,
883 "unable to find expected transaction property");
884
885 /* Step 2. Make sure the value associated with it is the same
886 as what was expected, else return an error. */
887 if (strcmp(final_props[i][0], SVN_PROP_REVISION_DATE))
888 if (strcmp(prop_value->data, final_props[i][1]))
889 return svn_error_createf
890 (SVN_ERR_FS_GENERAL, NULL,
891 "transaction property had an unexpected value");
892 }
893 }
894
895 /* Commit the transaction. */
896 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
897 if (after_rev != 1)
898 return svn_error_createf
899 (SVN_ERR_FS_GENERAL, NULL,
900 "committed transaction got wrong revision number");
901
902 /* Obtain a list of all properties on the new revision, and make
903 sure it matches the expected values. If you're wondering, the
904 expected values should be the exact same set of properties that
905 existed on the transaction just prior to its being committed. */
906 SVN_ERR(svn_fs_revision_proplist(&proplist, fs, after_rev, pool));
907 {
908 svn_string_t *prop_value;
909
910 if (apr_hash_count(proplist) < 5 )
911 return svn_error_createf
912 (SVN_ERR_FS_GENERAL, NULL,
913 "unexpected number of revision properties were found");
914
915 /* Loop through our list of expected revision property name/value
916 pairs. */
917 for (i = 0; i < 5; i++)
918 {
919 /* For each expected property: */
920
921 /* Step 1. Find it by name in the hash of all rev. props
922 returned to us by svn_fs_revision_proplist. If it can't be
923 found, return an error. */
924 prop_value = apr_hash_get(proplist,
925 final_props[i][0],
926 APR_HASH_KEY_STRING);
927 if (! prop_value)
928 return svn_error_createf
929 (SVN_ERR_FS_GENERAL, NULL,
930 "unable to find expected revision property");
931
932 /* Step 2. Make sure the value associated with it is the same
933 as what was expected, else return an error. */
934 if (strcmp(final_props[i][0], SVN_PROP_REVISION_DATE))
935 if (strcmp(prop_value->data, final_props[i][1]))
936 return svn_error_createf
937 (SVN_ERR_FS_GENERAL, NULL,
938 "revision property had an unexpected value");
939 }
940 }
941
942 return SVN_NO_ERROR;
943 }
944
945
946 static svn_error_t *
node_props(const svn_test_opts_t * opts,apr_pool_t * pool)947 node_props(const svn_test_opts_t *opts,
948 apr_pool_t *pool)
949 {
950 svn_fs_t *fs;
951 svn_fs_txn_t *txn;
952 svn_fs_root_t *txn_root;
953 apr_hash_t *proplist;
954 svn_string_t *value;
955 int i;
956 svn_string_t s1;
957
958 const char *initial_props[4][2] = {
959 { "Best Rock Artist", "Creed" },
960 { "Best Rap Artist", "Eminem" },
961 { "Best Country Artist", "(null)" },
962 { "Best Sound Designer", "Pluessman" }
963 };
964
965 const char *final_props[4][2] = {
966 { "Best Rock Artist", "P.O.D." },
967 { "Best Rap Artist", "Busta Rhymes" },
968 { "Best Sound Designer", "Pluessman" },
969 { "Biggest Cakewalk Fanatic", "Pluessman" }
970 };
971
972 /* Open the fs and transaction */
973 SVN_ERR(svn_test__create_fs(&fs, "test-repo-node-props",
974 opts, pool));
975 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
976 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
977
978 /* Make a node to put some properties into */
979 SVN_ERR(svn_fs_make_file(txn_root, "music.txt", pool));
980
981 /* Set some properties on the nodes. */
982 for (i = 0; i < 4; i++)
983 {
984 SET_STR(&s1, initial_props[i][1]);
985 SVN_ERR(svn_fs_change_node_prop
986 (txn_root, "music.txt", initial_props[i][0], &s1, pool));
987 }
988
989 /* Change some of the above properties. */
990 SET_STR(&s1, "P.O.D.");
991 SVN_ERR(svn_fs_change_node_prop(txn_root, "music.txt", "Best Rock Artist",
992 &s1, pool));
993
994 SET_STR(&s1, "Busta Rhymes");
995 SVN_ERR(svn_fs_change_node_prop(txn_root, "music.txt", "Best Rap Artist",
996 &s1, pool));
997
998 /* Remove a property altogether */
999 SVN_ERR(svn_fs_change_node_prop(txn_root, "music.txt",
1000 "Best Country Artist", NULL, pool));
1001
1002 /* Copy a property's value into a new property. */
1003 SVN_ERR(svn_fs_node_prop(&value, txn_root, "music.txt",
1004 "Best Sound Designer", pool));
1005
1006 s1.data = value->data;
1007 s1.len = value->len;
1008 SVN_ERR(svn_fs_change_node_prop(txn_root, "music.txt",
1009 "Biggest Cakewalk Fanatic", &s1, pool));
1010
1011 /* Obtain a list of all current properties, and make sure it matches
1012 the expected values. */
1013 SVN_ERR(svn_fs_node_proplist(&proplist, txn_root, "music.txt", pool));
1014 {
1015 svn_string_t *prop_value;
1016
1017 if (apr_hash_count(proplist) != 4 )
1018 return svn_error_createf
1019 (SVN_ERR_FS_GENERAL, NULL,
1020 "unexpected number of node properties were found");
1021
1022 /* Loop through our list of expected node property name/value
1023 pairs. */
1024 for (i = 0; i < 4; i++)
1025 {
1026 /* For each expected property: */
1027
1028 /* Step 1. Find it by name in the hash of all node props
1029 returned to us by svn_fs_node_proplist. If it can't be
1030 found, return an error. */
1031 prop_value = apr_hash_get(proplist,
1032 final_props[i][0],
1033 APR_HASH_KEY_STRING);
1034 if (! prop_value)
1035 return svn_error_createf
1036 (SVN_ERR_FS_GENERAL, NULL,
1037 "unable to find expected node property");
1038
1039 /* Step 2. Make sure the value associated with it is the same
1040 as what was expected, else return an error. */
1041 if (strcmp(prop_value->data, final_props[i][1]))
1042 return svn_error_createf
1043 (SVN_ERR_FS_GENERAL, NULL,
1044 "node property had an unexpected value");
1045 }
1046 }
1047
1048 return SVN_NO_ERROR;
1049 }
1050
1051
1052
1053 /* Set *PRESENT to true if entry NAME is present in directory PATH
1054 under ROOT, else set *PRESENT to false. */
1055 static svn_error_t *
check_entry(svn_fs_root_t * root,const char * path,const char * name,svn_boolean_t * present,apr_pool_t * pool)1056 check_entry(svn_fs_root_t *root,
1057 const char *path,
1058 const char *name,
1059 svn_boolean_t *present,
1060 apr_pool_t *pool)
1061 {
1062 apr_hash_t *entries;
1063 svn_fs_dirent_t *ent;
1064 apr_pool_t *subpool = svn_pool_create(pool);
1065
1066 SVN_ERR(svn_fs_dir_entries(&entries, root, path, subpool));
1067 ent = apr_hash_get(entries, name, APR_HASH_KEY_STRING);
1068
1069 if (ent)
1070 *present = TRUE;
1071 else
1072 *present = FALSE;
1073
1074 svn_pool_destroy(subpool);
1075 return SVN_NO_ERROR;
1076 }
1077
1078
1079 /* Return an error if entry NAME is absent in directory PATH under ROOT. */
1080 static svn_error_t *
check_entry_present(svn_fs_root_t * root,const char * path,const char * name,apr_pool_t * pool)1081 check_entry_present(svn_fs_root_t *root, const char *path,
1082 const char *name, apr_pool_t *pool)
1083 {
1084 svn_boolean_t present = FALSE;
1085 SVN_ERR(check_entry(root, path, name, &present, pool));
1086
1087 if (! present)
1088 return svn_error_createf
1089 (SVN_ERR_FS_GENERAL, NULL,
1090 "entry \"%s\" absent when it should be present", name);
1091
1092 return SVN_NO_ERROR;
1093 }
1094
1095
1096 /* Return an error if entry NAME is present in directory PATH under ROOT. */
1097 static svn_error_t *
check_entry_absent(svn_fs_root_t * root,const char * path,const char * name,apr_pool_t * pool)1098 check_entry_absent(svn_fs_root_t *root, const char *path,
1099 const char *name, apr_pool_t *pool)
1100 {
1101 svn_boolean_t present = TRUE;
1102 SVN_ERR(check_entry(root, path, name, &present, pool));
1103
1104 if (present)
1105 return svn_error_createf
1106 (SVN_ERR_FS_GENERAL, NULL,
1107 "entry \"%s\" present when it should be absent", name);
1108
1109 return SVN_NO_ERROR;
1110 }
1111
1112
1113 /* Fetch the youngest revision from a repos. */
1114 static svn_error_t *
fetch_youngest_rev(const svn_test_opts_t * opts,apr_pool_t * pool)1115 fetch_youngest_rev(const svn_test_opts_t *opts,
1116 apr_pool_t *pool)
1117 {
1118 svn_fs_t *fs;
1119 svn_fs_txn_t *txn;
1120 svn_fs_root_t *txn_root;
1121 svn_revnum_t new_rev;
1122 svn_revnum_t youngest_rev, new_youngest_rev;
1123
1124 SVN_ERR(svn_test__create_fs(&fs, "test-repo-youngest-rev",
1125 opts, pool));
1126
1127 /* Get youngest revision of brand spankin' new filesystem. */
1128 SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool));
1129
1130 /* Prepare a txn to receive the greek tree. */
1131 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
1132 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1133
1134 /* Create the greek tree. */
1135 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
1136
1137 /* Commit it. */
1138 SVN_ERR(test_commit_txn(&new_rev, txn, NULL, pool));
1139
1140 /* Get the new youngest revision. */
1141 SVN_ERR(svn_fs_youngest_rev(&new_youngest_rev, fs, pool));
1142
1143 if (youngest_rev == new_rev)
1144 return svn_error_create(SVN_ERR_FS_GENERAL, NULL,
1145 "commit didn't bump up revision number");
1146
1147 if (new_youngest_rev != new_rev)
1148 return svn_error_create(SVN_ERR_FS_GENERAL, NULL,
1149 "couldn't fetch youngest revision");
1150
1151 return SVN_NO_ERROR;
1152 }
1153
1154
1155 /* Test committing against an empty repository.
1156 todo: also test committing against youngest? */
1157 static svn_error_t *
basic_commit(const svn_test_opts_t * opts,apr_pool_t * pool)1158 basic_commit(const svn_test_opts_t *opts,
1159 apr_pool_t *pool)
1160 {
1161 svn_fs_t *fs;
1162 svn_fs_txn_t *txn;
1163 svn_fs_root_t *txn_root, *revision_root;
1164 svn_revnum_t before_rev, after_rev;
1165 const char *conflict;
1166
1167 /* Prepare a filesystem. */
1168 SVN_ERR(svn_test__create_fs(&fs, "test-repo-basic-commit",
1169 opts, pool));
1170
1171 /* Save the current youngest revision. */
1172 SVN_ERR(svn_fs_youngest_rev(&before_rev, fs, pool));
1173
1174 /* Prepare a txn to receive the greek tree. */
1175 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
1176 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1177
1178 /* Paranoidly check that the current youngest rev is unchanged. */
1179 SVN_ERR(svn_fs_youngest_rev(&after_rev, fs, pool));
1180 if (after_rev != before_rev)
1181 return svn_error_create
1182 (SVN_ERR_FS_GENERAL, NULL,
1183 "youngest revision changed unexpectedly");
1184
1185 /* Create the greek tree. */
1186 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
1187 SVN_TEST_ASSERT(svn_fs_is_txn_root(txn_root));
1188 SVN_TEST_ASSERT(!svn_fs_is_revision_root(txn_root));
1189
1190 /* Commit it. */
1191 SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, pool));
1192 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev));
1193
1194 /* Make sure it's a different revision than before. */
1195 if (after_rev == before_rev)
1196 return svn_error_create
1197 (SVN_ERR_FS_GENERAL, NULL,
1198 "youngest revision failed to change");
1199
1200 /* Get root of the revision */
1201 SVN_ERR(svn_fs_revision_root(&revision_root, fs, after_rev, pool));
1202 SVN_TEST_ASSERT(!svn_fs_is_txn_root(revision_root));
1203 SVN_TEST_ASSERT(svn_fs_is_revision_root(revision_root));
1204
1205 /* Check the tree. */
1206 SVN_ERR(svn_test__check_greek_tree(revision_root, pool));
1207
1208 return SVN_NO_ERROR;
1209 }
1210
1211
1212
1213 static svn_error_t *
test_tree_node_validation(const svn_test_opts_t * opts,apr_pool_t * pool)1214 test_tree_node_validation(const svn_test_opts_t *opts,
1215 apr_pool_t *pool)
1216 {
1217 svn_fs_t *fs;
1218 svn_fs_txn_t *txn;
1219 svn_fs_root_t *txn_root, *revision_root;
1220 svn_revnum_t after_rev;
1221 const char *conflict;
1222 apr_pool_t *subpool;
1223
1224 /* Prepare a filesystem. */
1225 SVN_ERR(svn_test__create_fs(&fs, "test-repo-validate-tree-entries",
1226 opts, pool));
1227
1228 /* In a txn, create the greek tree. */
1229 subpool = svn_pool_create(pool);
1230 {
1231 static svn_test__tree_entry_t expected_entries[] = {
1232 /* path, contents (0 = dir) */
1233 { "iota", "This is the file 'iota'.\n" },
1234 { "A", 0 },
1235 { "A/mu", "This is the file 'mu'.\n" },
1236 { "A/B", 0 },
1237 { "A/B/lambda", "This is the file 'lambda'.\n" },
1238 { "A/B/E", 0 },
1239 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1240 { "A/B/E/beta", "This is the file 'beta'.\n" },
1241 { "A/B/F", 0 },
1242 { "A/C", 0 },
1243 { "A/D", 0 },
1244 { "A/D/gamma", "This is the file 'gamma'.\n" },
1245 { "A/D/G", 0 },
1246 { "A/D/G/pi", "This is the file 'pi'.\n" },
1247 { "A/D/G/rho", "This is the file 'rho'.\n" },
1248 { "A/D/G/tau", "This is the file 'tau'.\n" },
1249 { "A/D/H", 0 },
1250 { "A/D/H/chi", "This is the file 'chi'.\n" },
1251 { "A/D/H/psi", "This is the file 'psi'.\n" },
1252 { "A/D/H/omega", "This is the file 'omega'.\n" }
1253 };
1254 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
1255 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
1256 SVN_ERR(svn_test__create_greek_tree(txn_root, subpool));
1257
1258 /* Carefully validate that tree in the transaction. */
1259 SVN_ERR(svn_test__validate_tree(txn_root, expected_entries, 20,
1260 subpool));
1261
1262 /* Go ahead and commit the tree, and destroy the txn object. */
1263 SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, subpool));
1264 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev));
1265
1266 /* Carefully validate that tree in the new revision, now. */
1267 SVN_ERR(svn_fs_revision_root(&revision_root, fs, after_rev, subpool));
1268 SVN_ERR(svn_test__validate_tree(revision_root, expected_entries, 20,
1269 subpool));
1270 }
1271 svn_pool_destroy(subpool);
1272
1273 /* In a new txn, modify the greek tree. */
1274 subpool = svn_pool_create(pool);
1275 {
1276 static svn_test__tree_entry_t expected_entries[] = {
1277 /* path, contents (0 = dir) */
1278 { "iota", "This is a new version of 'iota'.\n" },
1279 { "A", 0 },
1280 { "A/B", 0 },
1281 { "A/B/lambda", "This is the file 'lambda'.\n" },
1282 { "A/B/E", 0 },
1283 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1284 { "A/B/E/beta", "This is the file 'beta'.\n" },
1285 { "A/B/F", 0 },
1286 { "A/C", 0 },
1287 { "A/C/kappa", "This is the file 'kappa'.\n" },
1288 { "A/D", 0 },
1289 { "A/D/gamma", "This is the file 'gamma'.\n" },
1290 { "A/D/H", 0 },
1291 { "A/D/H/chi", "This is the file 'chi'.\n" },
1292 { "A/D/H/psi", "This is the file 'psi'.\n" },
1293 { "A/D/H/omega", "This is the file 'omega'.\n" },
1294 { "A/D/I", 0 },
1295 { "A/D/I/delta", "This is the file 'delta'.\n" },
1296 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1297 };
1298
1299 SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, subpool));
1300 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
1301 SVN_ERR(svn_test__set_file_contents
1302 (txn_root, "iota", "This is a new version of 'iota'.\n",
1303 subpool));
1304 SVN_ERR(svn_fs_delete(txn_root, "A/mu", subpool));
1305 SVN_ERR(svn_fs_delete(txn_root, "A/D/G", subpool));
1306 SVN_ERR(svn_fs_make_dir(txn_root, "A/D/I", subpool));
1307 SVN_ERR(svn_fs_make_file(txn_root, "A/D/I/delta", subpool));
1308 SVN_ERR(svn_test__set_file_contents
1309 (txn_root, "A/D/I/delta", "This is the file 'delta'.\n",
1310 subpool));
1311 SVN_ERR(svn_fs_make_file(txn_root, "A/D/I/epsilon", subpool));
1312 SVN_ERR(svn_test__set_file_contents
1313 (txn_root, "A/D/I/epsilon", "This is the file 'epsilon'.\n",
1314 subpool));
1315 SVN_ERR(svn_fs_make_file(txn_root, "A/C/kappa", subpool));
1316 SVN_ERR(svn_test__set_file_contents
1317 (txn_root, "A/C/kappa", "This is the file 'kappa'.\n",
1318 subpool));
1319
1320 /* Carefully validate that tree in the transaction. */
1321 SVN_ERR(svn_test__validate_tree(txn_root, expected_entries, 19,
1322 subpool));
1323
1324 /* Go ahead and commit the tree, and destroy the txn object. */
1325 SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, subpool));
1326 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev));
1327
1328 /* Carefully validate that tree in the new revision, now. */
1329 SVN_ERR(svn_fs_revision_root(&revision_root, fs, after_rev, subpool));
1330 SVN_ERR(svn_test__validate_tree(revision_root, expected_entries,
1331 19, subpool));
1332 }
1333 svn_pool_destroy(subpool);
1334
1335 return SVN_NO_ERROR;
1336 }
1337
1338
1339 /* Commit with merging (committing against non-youngest). */
1340 static svn_error_t *
merging_commit(const svn_test_opts_t * opts,apr_pool_t * pool)1341 merging_commit(const svn_test_opts_t *opts,
1342 apr_pool_t *pool)
1343 {
1344 svn_fs_t *fs;
1345 svn_fs_txn_t *txn;
1346 svn_fs_root_t *txn_root, *revision_root;
1347 svn_revnum_t after_rev;
1348 svn_revnum_t revisions[24];
1349 apr_size_t i;
1350 svn_revnum_t revision_count;
1351
1352 /* Prepare a filesystem. */
1353 SVN_ERR(svn_test__create_fs(&fs, "test-repo-merging-commit",
1354 opts, pool));
1355
1356 /* Initialize our revision number stuffs. */
1357 for (i = 0;
1358 i < ((sizeof(revisions)) / (sizeof(svn_revnum_t)));
1359 i++)
1360 revisions[i] = SVN_INVALID_REVNUM;
1361 revision_count = 0;
1362 revisions[revision_count++] = 0; /* the brand spankin' new revision */
1363
1364 /***********************************************************************/
1365 /* REVISION 0 */
1366 /***********************************************************************/
1367
1368 /* In one txn, create and commit the greek tree. */
1369 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
1370 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1371 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
1372 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
1373
1374 /***********************************************************************/
1375 /* REVISION 1 */
1376 /***********************************************************************/
1377 {
1378 static svn_test__tree_entry_t expected_entries[] = {
1379 /* path, contents (0 = dir) */
1380 { "iota", "This is the file 'iota'.\n" },
1381 { "A", 0 },
1382 { "A/mu", "This is the file 'mu'.\n" },
1383 { "A/B", 0 },
1384 { "A/B/lambda", "This is the file 'lambda'.\n" },
1385 { "A/B/E", 0 },
1386 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1387 { "A/B/E/beta", "This is the file 'beta'.\n" },
1388 { "A/B/F", 0 },
1389 { "A/C", 0 },
1390 { "A/D", 0 },
1391 { "A/D/gamma", "This is the file 'gamma'.\n" },
1392 { "A/D/G", 0 },
1393 { "A/D/G/pi", "This is the file 'pi'.\n" },
1394 { "A/D/G/rho", "This is the file 'rho'.\n" },
1395 { "A/D/G/tau", "This is the file 'tau'.\n" },
1396 { "A/D/H", 0 },
1397 { "A/D/H/chi", "This is the file 'chi'.\n" },
1398 { "A/D/H/psi", "This is the file 'psi'.\n" },
1399 { "A/D/H/omega", "This is the file 'omega'.\n" }
1400 };
1401 SVN_ERR(svn_fs_revision_root(&revision_root, fs, after_rev, pool));
1402 SVN_ERR(svn_test__validate_tree(revision_root, expected_entries,
1403 20, pool));
1404 }
1405 revisions[revision_count++] = after_rev;
1406
1407 /* Let's add a directory and some files to the tree, and delete
1408 'iota' */
1409 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[revision_count-1], pool));
1410 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1411 SVN_ERR(svn_fs_make_dir(txn_root, "A/D/I", pool));
1412 SVN_ERR(svn_fs_make_file(txn_root, "A/D/I/delta", pool));
1413 SVN_ERR(svn_test__set_file_contents
1414 (txn_root, "A/D/I/delta", "This is the file 'delta'.\n", pool));
1415 SVN_ERR(svn_fs_make_file(txn_root, "A/D/I/epsilon", pool));
1416 SVN_ERR(svn_test__set_file_contents
1417 (txn_root, "A/D/I/epsilon", "This is the file 'epsilon'.\n", pool));
1418 SVN_ERR(svn_fs_make_file(txn_root, "A/C/kappa", pool));
1419 SVN_ERR(svn_test__set_file_contents
1420 (txn_root, "A/C/kappa", "This is the file 'kappa'.\n", pool));
1421 SVN_ERR(svn_fs_delete(txn_root, "iota", pool));
1422 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
1423
1424 /***********************************************************************/
1425 /* REVISION 2 */
1426 /***********************************************************************/
1427 {
1428 static svn_test__tree_entry_t expected_entries[] = {
1429 /* path, contents (0 = dir) */
1430 { "A", 0 },
1431 { "A/mu", "This is the file 'mu'.\n" },
1432 { "A/B", 0 },
1433 { "A/B/lambda", "This is the file 'lambda'.\n" },
1434 { "A/B/E", 0 },
1435 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1436 { "A/B/E/beta", "This is the file 'beta'.\n" },
1437 { "A/B/F", 0 },
1438 { "A/C", 0 },
1439 { "A/C/kappa", "This is the file 'kappa'.\n" },
1440 { "A/D", 0 },
1441 { "A/D/gamma", "This is the file 'gamma'.\n" },
1442 { "A/D/G", 0 },
1443 { "A/D/G/pi", "This is the file 'pi'.\n" },
1444 { "A/D/G/rho", "This is the file 'rho'.\n" },
1445 { "A/D/G/tau", "This is the file 'tau'.\n" },
1446 { "A/D/H", 0 },
1447 { "A/D/H/chi", "This is the file 'chi'.\n" },
1448 { "A/D/H/psi", "This is the file 'psi'.\n" },
1449 { "A/D/H/omega", "This is the file 'omega'.\n" },
1450 { "A/D/I", 0 },
1451 { "A/D/I/delta", "This is the file 'delta'.\n" },
1452 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1453 };
1454 SVN_ERR(svn_fs_revision_root(&revision_root, fs, after_rev, pool));
1455 SVN_ERR(svn_test__validate_tree(revision_root, expected_entries,
1456 23, pool));
1457 }
1458 revisions[revision_count++] = after_rev;
1459
1460 /* We don't think the A/D/H directory is pulling its weight...let's
1461 knock it off. Oh, and let's re-add iota, too. */
1462 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[revision_count-1], pool));
1463 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1464 SVN_ERR(svn_fs_delete(txn_root, "A/D/H", pool));
1465 SVN_ERR(svn_fs_make_file(txn_root, "iota", pool));
1466 SVN_ERR(svn_test__set_file_contents
1467 (txn_root, "iota", "This is the new file 'iota'.\n", pool));
1468 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
1469
1470 /***********************************************************************/
1471 /* REVISION 3 */
1472 /***********************************************************************/
1473 {
1474 static svn_test__tree_entry_t expected_entries[] = {
1475 /* path, contents (0 = dir) */
1476 { "iota", "This is the new file 'iota'.\n" },
1477 { "A", 0 },
1478 { "A/mu", "This is the file 'mu'.\n" },
1479 { "A/B", 0 },
1480 { "A/B/lambda", "This is the file 'lambda'.\n" },
1481 { "A/B/E", 0 },
1482 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1483 { "A/B/E/beta", "This is the file 'beta'.\n" },
1484 { "A/B/F", 0 },
1485 { "A/C", 0 },
1486 { "A/C/kappa", "This is the file 'kappa'.\n" },
1487 { "A/D", 0 },
1488 { "A/D/gamma", "This is the file 'gamma'.\n" },
1489 { "A/D/G", 0 },
1490 { "A/D/G/pi", "This is the file 'pi'.\n" },
1491 { "A/D/G/rho", "This is the file 'rho'.\n" },
1492 { "A/D/G/tau", "This is the file 'tau'.\n" },
1493 { "A/D/I", 0 },
1494 { "A/D/I/delta", "This is the file 'delta'.\n" },
1495 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1496 };
1497 SVN_ERR(svn_fs_revision_root(&revision_root, fs, after_rev, pool));
1498 SVN_ERR(svn_test__validate_tree(revision_root, expected_entries,
1499 20, pool));
1500 }
1501 revisions[revision_count++] = after_rev;
1502
1503 /* Delete iota (yet again). */
1504 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[revision_count-1], pool));
1505 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1506 SVN_ERR(svn_fs_delete(txn_root, "iota", pool));
1507 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
1508
1509 /***********************************************************************/
1510 /* REVISION 4 */
1511 /***********************************************************************/
1512 {
1513 static svn_test__tree_entry_t expected_entries[] = {
1514 /* path, contents (0 = dir) */
1515 { "A", 0 },
1516 { "A/mu", "This is the file 'mu'.\n" },
1517 { "A/B", 0 },
1518 { "A/B/lambda", "This is the file 'lambda'.\n" },
1519 { "A/B/E", 0 },
1520 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1521 { "A/B/E/beta", "This is the file 'beta'.\n" },
1522 { "A/B/F", 0 },
1523 { "A/C", 0 },
1524 { "A/C/kappa", "This is the file 'kappa'.\n" },
1525 { "A/D", 0 },
1526 { "A/D/gamma", "This is the file 'gamma'.\n" },
1527 { "A/D/G", 0 },
1528 { "A/D/G/pi", "This is the file 'pi'.\n" },
1529 { "A/D/G/rho", "This is the file 'rho'.\n" },
1530 { "A/D/G/tau", "This is the file 'tau'.\n" },
1531 { "A/D/I", 0 },
1532 { "A/D/I/delta", "This is the file 'delta'.\n" },
1533 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1534 };
1535 SVN_ERR(svn_fs_revision_root(&revision_root, fs, after_rev, pool));
1536 SVN_ERR(svn_test__validate_tree(revision_root, expected_entries,
1537 19, pool));
1538 }
1539 revisions[revision_count++] = after_rev;
1540
1541 /***********************************************************************/
1542 /* GIVEN: A and B, with common ancestor ANCESTOR, where A and B
1543 directories, and E, an entry in either A, B, or ANCESTOR.
1544
1545 For every E, the following cases exist:
1546 - E exists in neither ANCESTOR nor A.
1547 - E doesn't exist in ANCESTOR, and has been added to A.
1548 - E exists in ANCESTOR, but has been deleted from A.
1549 - E exists in both ANCESTOR and A ...
1550 - but refers to different node revisions.
1551 - and refers to the same node revision.
1552
1553 The same set of possible relationships with ANCESTOR holds for B,
1554 so there are thirty-six combinations. The matrix is symmetrical
1555 with A and B reversed, so we only have to describe one triangular
1556 half, including the diagonal --- 21 combinations.
1557
1558 Our goal here is to test all the possible scenarios that can
1559 occur given the above boolean logic table, and to make sure that
1560 the results we get are as expected.
1561
1562 The test cases below have the following features:
1563
1564 - They run straight through the scenarios as described in the
1565 `structure' document at this time.
1566
1567 - In each case, a txn is begun based on some revision (ANCESTOR),
1568 is modified into a new tree (B), and then is attempted to be
1569 committed (which happens against the head of the tree, A).
1570
1571 - If the commit is successful (and is *expected* to be such),
1572 that new revision (which exists now as a result of the
1573 successful commit) is thoroughly tested for accuracy of tree
1574 entries, and in the case of files, for their contents. It is
1575 important to realize that these successful commits are
1576 advancing the head of the tree, and each one effective becomes
1577 the new `A' described in further test cases.
1578 */
1579 /***********************************************************************/
1580
1581 /* (6) E exists in neither ANCESTOR nor A. */
1582 {
1583 /* (1) E exists in neither ANCESTOR nor B. Can't occur, by
1584 assumption that E exists in either A, B, or ancestor. */
1585
1586 /* (1) E has been added to B. Add E in the merged result. */
1587 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[0], pool));
1588 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1589 SVN_ERR(svn_fs_make_file(txn_root, "theta", pool));
1590 SVN_ERR(svn_test__set_file_contents
1591 (txn_root, "theta", "This is the file 'theta'.\n", pool));
1592 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
1593
1594 /*********************************************************************/
1595 /* REVISION 5 */
1596 /*********************************************************************/
1597 {
1598 static svn_test__tree_entry_t expected_entries[] = {
1599 /* path, contents (0 = dir) */
1600 { "theta", "This is the file 'theta'.\n" },
1601 { "A", 0 },
1602 { "A/mu", "This is the file 'mu'.\n" },
1603 { "A/B", 0 },
1604 { "A/B/lambda", "This is the file 'lambda'.\n" },
1605 { "A/B/E", 0 },
1606 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1607 { "A/B/E/beta", "This is the file 'beta'.\n" },
1608 { "A/B/F", 0 },
1609 { "A/C", 0 },
1610 { "A/C/kappa", "This is the file 'kappa'.\n" },
1611 { "A/D", 0 },
1612 { "A/D/gamma", "This is the file 'gamma'.\n" },
1613 { "A/D/G", 0 },
1614 { "A/D/G/pi", "This is the file 'pi'.\n" },
1615 { "A/D/G/rho", "This is the file 'rho'.\n" },
1616 { "A/D/G/tau", "This is the file 'tau'.\n" },
1617 { "A/D/I", 0 },
1618 { "A/D/I/delta", "This is the file 'delta'.\n" },
1619 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1620 };
1621 SVN_ERR(svn_fs_revision_root(&revision_root, fs, after_rev, pool));
1622 SVN_ERR(svn_test__validate_tree(revision_root,
1623 expected_entries,
1624 20, pool));
1625 }
1626 revisions[revision_count++] = after_rev;
1627
1628 /* (1) E has been deleted from B. Can't occur, by assumption that
1629 E doesn't exist in ANCESTOR. */
1630
1631 /* (3) E exists in both ANCESTOR and B. Can't occur, by
1632 assumption that E doesn't exist in ancestor. */
1633 }
1634
1635 /* (5) E doesn't exist in ANCESTOR, and has been added to A. */
1636 {
1637 svn_revnum_t failed_rev;
1638 /* (1) E doesn't exist in ANCESTOR, and has been added to B.
1639 Conflict. */
1640 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[4], pool));
1641 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1642 SVN_ERR(svn_fs_make_file(txn_root, "theta", pool));
1643 SVN_ERR(svn_test__set_file_contents
1644 (txn_root, "theta", "This is another file 'theta'.\n", pool));
1645
1646 /* TXN must actually be based upon revisions[4] (instead of HEAD). */
1647 SVN_TEST_ASSERT(svn_fs_txn_base_revision(txn) == revisions[4]);
1648
1649 SVN_ERR(test_commit_txn(&failed_rev, txn, "/theta", pool));
1650 SVN_ERR(svn_fs_abort_txn(txn, pool));
1651
1652 /* (1) E exists in ANCESTOR, but has been deleted from B. Can't
1653 occur, by assumption that E doesn't exist in ANCESTOR. */
1654
1655 /* (3) E exists in both ANCESTOR and B. Can't occur, by assumption
1656 that E doesn't exist in ANCESTOR. */
1657
1658 SVN_TEST_ASSERT(failed_rev == SVN_INVALID_REVNUM);
1659 }
1660
1661 /* (4) E exists in ANCESTOR, but has been deleted from A */
1662 {
1663 /* (1) E exists in ANCESTOR, but has been deleted from B. If
1664 neither delete was a result of a rename, then omit E from the
1665 merged tree. Otherwise, conflict. */
1666 /* ### cmpilato todo: the rename case isn't actually handled by
1667 merge yet, so we know we won't get a conflict here. */
1668 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[1], pool));
1669 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1670 SVN_ERR(svn_fs_delete(txn_root, "A/D/H", pool));
1671
1672 /* TXN must actually be based upon revisions[1] (instead of HEAD). */
1673 SVN_TEST_ASSERT(svn_fs_txn_base_revision(txn) == revisions[1]);
1674
1675 /* We used to create the revision like this before fixing issue
1676 #2751 -- Directory prop mods reverted in overlapping commits scenario.
1677
1678 But we now expect that to fail as out of date */
1679 {
1680 svn_revnum_t failed_rev;
1681 SVN_ERR(test_commit_txn(&failed_rev, txn, "/A/D/H", pool));
1682
1683 SVN_TEST_ASSERT(failed_rev == SVN_INVALID_REVNUM);
1684 }
1685 /*********************************************************************/
1686 /* REVISION 6 */
1687 /*********************************************************************/
1688 {
1689 static svn_test__tree_entry_t expected_entries[] = {
1690 /* path, contents (0 = dir) */
1691 { "theta", "This is the file 'theta'.\n" },
1692 { "A", 0 },
1693 { "A/mu", "This is the file 'mu'.\n" },
1694 { "A/B", 0 },
1695 { "A/B/lambda", "This is the file 'lambda'.\n" },
1696 { "A/B/E", 0 },
1697 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1698 { "A/B/E/beta", "This is the file 'beta'.\n" },
1699 { "A/B/F", 0 },
1700 { "A/C", 0 },
1701 { "A/C/kappa", "This is the file 'kappa'.\n" },
1702 { "A/D", 0 },
1703 { "A/D/gamma", "This is the file 'gamma'.\n" },
1704 { "A/D/G", 0 },
1705 { "A/D/G/pi", "This is the file 'pi'.\n" },
1706 { "A/D/G/rho", "This is the file 'rho'.\n" },
1707 { "A/D/G/tau", "This is the file 'tau'.\n" },
1708 { "A/D/I", 0 },
1709 { "A/D/I/delta", "This is the file 'delta'.\n" },
1710 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1711 };
1712 SVN_ERR(svn_fs_revision_root(&revision_root, fs, after_rev, pool));
1713 SVN_ERR(svn_test__validate_tree(revision_root,
1714 expected_entries,
1715 20, pool));
1716 }
1717 revisions[revision_count++] = after_rev;
1718
1719 /* Try deleting a file F inside a subtree S where S does not exist
1720 in the most recent revision, but does exist in the ancestor
1721 tree. This should conflict. */
1722 {
1723 svn_revnum_t failed_rev;
1724 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[1], pool));
1725 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1726 SVN_ERR(svn_fs_delete(txn_root, "A/D/H/omega", pool));
1727 SVN_ERR(test_commit_txn(&failed_rev, txn, "/A/D/H", pool));
1728 SVN_ERR(svn_fs_abort_txn(txn, pool));
1729
1730 SVN_TEST_ASSERT(failed_rev == SVN_INVALID_REVNUM);
1731 }
1732
1733 /* E exists in both ANCESTOR and B ... */
1734 {
1735 /* (1) but refers to different nodes. Conflict. */
1736 SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, pool));
1737 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1738 SVN_ERR(svn_fs_make_dir(txn_root, "A/D/H", pool));
1739 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
1740 revisions[revision_count++] = after_rev;
1741
1742 /*********************************************************************/
1743 /* REVISION 7 */
1744 /*********************************************************************/
1745
1746 /* Re-remove A/D/H because future tests expect it to be absent. */
1747 {
1748 SVN_ERR(svn_fs_begin_txn
1749 (&txn, fs, revisions[revision_count - 1], pool));
1750 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1751 SVN_ERR(svn_fs_delete(txn_root, "A/D/H", pool));
1752 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
1753 revisions[revision_count++] = after_rev;
1754 }
1755
1756 /*********************************************************************/
1757 /* REVISION 8 (looks exactly like revision 6, we hope) */
1758 /*********************************************************************/
1759
1760 /* (1) but refers to different revisions of the same node.
1761 Conflict. */
1762 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[1], pool));
1763 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1764 SVN_ERR(svn_fs_make_file(txn_root, "A/D/H/zeta", pool));
1765 SVN_ERR(test_commit_txn(&after_rev, txn, "/A/D/H", pool));
1766 SVN_ERR(svn_fs_abort_txn(txn, pool));
1767
1768 /* (1) and refers to the same node revision. Omit E from the
1769 merged tree. This is already tested in Merge-Test 3
1770 (A/D/H/chi, A/D/H/psi, e.g.), but we'll test it here again
1771 anyway. A little paranoia never hurt anyone. */
1772 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[1], pool));
1773 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1774 SVN_ERR(svn_fs_delete(txn_root, "A/mu", pool)); /* unrelated change */
1775 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
1776
1777 /*********************************************************************/
1778 /* REVISION 9 */
1779 /*********************************************************************/
1780 {
1781 static svn_test__tree_entry_t expected_entries[] = {
1782 /* path, contents (0 = dir) */
1783 { "theta", "This is the file 'theta'.\n" },
1784 { "A", 0 },
1785 { "A/B", 0 },
1786 { "A/B/lambda", "This is the file 'lambda'.\n" },
1787 { "A/B/E", 0 },
1788 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1789 { "A/B/E/beta", "This is the file 'beta'.\n" },
1790 { "A/B/F", 0 },
1791 { "A/C", 0 },
1792 { "A/C/kappa", "This is the file 'kappa'.\n" },
1793 { "A/D", 0 },
1794 { "A/D/gamma", "This is the file 'gamma'.\n" },
1795 { "A/D/G", 0 },
1796 { "A/D/G/pi", "This is the file 'pi'.\n" },
1797 { "A/D/G/rho", "This is the file 'rho'.\n" },
1798 { "A/D/G/tau", "This is the file 'tau'.\n" },
1799 { "A/D/I", 0 },
1800 { "A/D/I/delta", "This is the file 'delta'.\n" },
1801 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1802 };
1803 SVN_ERR(svn_fs_revision_root(&revision_root, fs, after_rev, pool));
1804 SVN_ERR(svn_test__validate_tree(revision_root,
1805 expected_entries,
1806 19, pool));
1807 }
1808 revisions[revision_count++] = after_rev;
1809 }
1810 }
1811
1812 /* Preparation for upcoming tests.
1813 We make a new head revision, with A/mu restored, but containing
1814 slightly different contents than its first incarnation. */
1815 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[revision_count-1], pool));
1816 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1817 SVN_ERR(svn_fs_make_file(txn_root, "A/mu", pool));
1818 SVN_ERR(svn_test__set_file_contents
1819 (txn_root, "A/mu", "A new file 'mu'.\n", pool));
1820 SVN_ERR(svn_fs_make_file(txn_root, "A/D/G/xi", pool));
1821 SVN_ERR(svn_test__set_file_contents
1822 (txn_root, "A/D/G/xi", "This is the file 'xi'.\n", pool));
1823 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
1824 /*********************************************************************/
1825 /* REVISION 10 */
1826 /*********************************************************************/
1827 {
1828 static svn_test__tree_entry_t expected_entries[] = {
1829 /* path, contents (0 = dir) */
1830 { "theta", "This is the file 'theta'.\n" },
1831 { "A", 0 },
1832 { "A/mu", "A new file 'mu'.\n" },
1833 { "A/B", 0 },
1834 { "A/B/lambda", "This is the file 'lambda'.\n" },
1835 { "A/B/E", 0 },
1836 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1837 { "A/B/E/beta", "This is the file 'beta'.\n" },
1838 { "A/B/F", 0 },
1839 { "A/C", 0 },
1840 { "A/C/kappa", "This is the file 'kappa'.\n" },
1841 { "A/D", 0 },
1842 { "A/D/gamma", "This is the file 'gamma'.\n" },
1843 { "A/D/G", 0 },
1844 { "A/D/G/pi", "This is the file 'pi'.\n" },
1845 { "A/D/G/rho", "This is the file 'rho'.\n" },
1846 { "A/D/G/tau", "This is the file 'tau'.\n" },
1847 { "A/D/G/xi", "This is the file 'xi'.\n" },
1848 { "A/D/I", 0 },
1849 { "A/D/I/delta", "This is the file 'delta'.\n" },
1850 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1851 };
1852 SVN_ERR(svn_fs_revision_root(&revision_root, fs, after_rev, pool));
1853 SVN_ERR(svn_test__validate_tree(revision_root, expected_entries,
1854 21, pool));
1855 }
1856 revisions[revision_count++] = after_rev;
1857
1858 /* (3) E exists in both ANCESTOR and A, but refers to different
1859 nodes. */
1860 {
1861 /* (1) E exists in both ANCESTOR and B, but refers to different
1862 nodes, and not all nodes are directories. Conflict. */
1863
1864 /* ### kff todo: A/mu's contents will be exactly the same.
1865 If the fs ever starts optimizing this case, these tests may
1866 start to fail. */
1867 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[1], pool));
1868 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1869 SVN_ERR(svn_fs_delete(txn_root, "A/mu", pool));
1870 SVN_ERR(svn_fs_make_file(txn_root, "A/mu", pool));
1871 SVN_ERR(svn_test__set_file_contents
1872 (txn_root, "A/mu", "This is the file 'mu'.\n", pool));
1873 SVN_ERR(test_commit_txn(&after_rev, txn, "/A/mu", pool));
1874 SVN_ERR(svn_fs_abort_txn(txn, pool));
1875
1876 /* (1) E exists in both ANCESTOR and B, but refers to different
1877 revisions of the same node. Conflict. */
1878 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[1], pool));
1879 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1880 SVN_ERR(svn_test__set_file_contents
1881 (txn_root, "A/mu", "A change to file 'mu'.\n", pool));
1882 SVN_ERR(test_commit_txn(&after_rev, txn, "/A/mu", pool));
1883 SVN_ERR(svn_fs_abort_txn(txn, pool));
1884
1885 /* (1) E exists in both ANCESTOR and B, and refers to the same
1886 node revision. Replace E with A's node revision. */
1887 {
1888 svn_stringbuf_t *old_mu_contents;
1889 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[1], pool));
1890 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1891 SVN_ERR(svn_test__get_file_contents
1892 (txn_root, "A/mu", &old_mu_contents, pool));
1893 if ((! old_mu_contents) || (strcmp(old_mu_contents->data,
1894 "This is the file 'mu'.\n") != 0))
1895 {
1896 return svn_error_create
1897 (SVN_ERR_FS_GENERAL, NULL,
1898 "got wrong contents from an old revision tree");
1899 }
1900 SVN_ERR(svn_fs_make_file(txn_root, "A/sigma", pool));
1901 SVN_ERR(svn_test__set_file_contents /* unrelated change */
1902 (txn_root, "A/sigma", "This is the file 'sigma'.\n", pool));
1903 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
1904 /*********************************************************************/
1905 /* REVISION 11 */
1906 /*********************************************************************/
1907 {
1908 static svn_test__tree_entry_t expected_entries[] = {
1909 /* path, contents (0 = dir) */
1910 { "theta", "This is the file 'theta'.\n" },
1911 { "A", 0 },
1912 { "A/mu", "A new file 'mu'.\n" },
1913 { "A/sigma", "This is the file 'sigma'.\n" },
1914 { "A/B", 0 },
1915 { "A/B/lambda", "This is the file 'lambda'.\n" },
1916 { "A/B/E", 0 },
1917 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1918 { "A/B/E/beta", "This is the file 'beta'.\n" },
1919 { "A/B/F", 0 },
1920 { "A/C", 0 },
1921 { "A/C/kappa", "This is the file 'kappa'.\n" },
1922 { "A/D", 0 },
1923 { "A/D/gamma", "This is the file 'gamma'.\n" },
1924 { "A/D/G", 0 },
1925 { "A/D/G/pi", "This is the file 'pi'.\n" },
1926 { "A/D/G/rho", "This is the file 'rho'.\n" },
1927 { "A/D/G/tau", "This is the file 'tau'.\n" },
1928 { "A/D/G/xi", "This is the file 'xi'.\n" },
1929 { "A/D/I", 0 },
1930 { "A/D/I/delta", "This is the file 'delta'.\n" },
1931 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1932 };
1933 SVN_ERR(svn_fs_revision_root(&revision_root, fs, after_rev, pool));
1934 SVN_ERR(svn_test__validate_tree(revision_root,
1935 expected_entries,
1936 22, pool));
1937 }
1938 revisions[revision_count++] = after_rev;
1939 }
1940 }
1941
1942 /* Preparation for upcoming tests.
1943 We make a new head revision. There are two changes in the new
1944 revision: A/B/lambda has been modified. We will also use the
1945 recent addition of A/D/G/xi, treated as a modification to
1946 A/D/G. */
1947 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[revision_count-1], pool));
1948 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1949 SVN_ERR(svn_test__set_file_contents
1950 (txn_root, "A/B/lambda", "Change to file 'lambda'.\n", pool));
1951 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
1952 /*********************************************************************/
1953 /* REVISION 12 */
1954 /*********************************************************************/
1955 {
1956 static svn_test__tree_entry_t expected_entries[] = {
1957 /* path, contents (0 = dir) */
1958 { "theta", "This is the file 'theta'.\n" },
1959 { "A", 0 },
1960 { "A/mu", "A new file 'mu'.\n" },
1961 { "A/sigma", "This is the file 'sigma'.\n" },
1962 { "A/B", 0 },
1963 { "A/B/lambda", "Change to file 'lambda'.\n" },
1964 { "A/B/E", 0 },
1965 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1966 { "A/B/E/beta", "This is the file 'beta'.\n" },
1967 { "A/B/F", 0 },
1968 { "A/C", 0 },
1969 { "A/C/kappa", "This is the file 'kappa'.\n" },
1970 { "A/D", 0 },
1971 { "A/D/gamma", "This is the file 'gamma'.\n" },
1972 { "A/D/G", 0 },
1973 { "A/D/G/pi", "This is the file 'pi'.\n" },
1974 { "A/D/G/rho", "This is the file 'rho'.\n" },
1975 { "A/D/G/tau", "This is the file 'tau'.\n" },
1976 { "A/D/G/xi", "This is the file 'xi'.\n" },
1977 { "A/D/I", 0 },
1978 { "A/D/I/delta", "This is the file 'delta'.\n" },
1979 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1980 };
1981 SVN_ERR(svn_fs_revision_root(&revision_root, fs, after_rev, pool));
1982 SVN_ERR(svn_test__validate_tree(revision_root, expected_entries,
1983 22, pool));
1984 }
1985 revisions[revision_count++] = after_rev;
1986
1987 /* (2) E exists in both ANCESTOR and A, but refers to different
1988 revisions of the same node. */
1989 {
1990 /* (1a) E exists in both ANCESTOR and B, but refers to different
1991 revisions of the same file node. Conflict. */
1992 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[1], pool));
1993 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1994 SVN_ERR(svn_test__set_file_contents
1995 (txn_root, "A/B/lambda", "A different change to 'lambda'.\n",
1996 pool));
1997 SVN_ERR(test_commit_txn(&after_rev, txn, "/A/B/lambda", pool));
1998 SVN_ERR(svn_fs_abort_txn(txn, pool));
1999
2000 /* (1b) E exists in both ANCESTOR and B, but refers to different
2001 revisions of the same directory node. Merge A/E and B/E,
2002 recursively. Succeed, because no conflict beneath E. */
2003 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[1], pool));
2004 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
2005 SVN_ERR(svn_fs_make_file(txn_root, "A/D/G/nu", pool));
2006 SVN_ERR(svn_test__set_file_contents
2007 (txn_root, "A/D/G/nu", "This is the file 'nu'.\n", pool));
2008 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
2009 /*********************************************************************/
2010 /* REVISION 13 */
2011 /*********************************************************************/
2012 {
2013 static svn_test__tree_entry_t expected_entries[] = {
2014 /* path, contents (0 = dir) */
2015 { "theta", "This is the file 'theta'.\n" },
2016 { "A", 0 },
2017 { "A/mu", "A new file 'mu'.\n" },
2018 { "A/sigma", "This is the file 'sigma'.\n" },
2019 { "A/B", 0 },
2020 { "A/B/lambda", "Change to file 'lambda'.\n" },
2021 { "A/B/E", 0 },
2022 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
2023 { "A/B/E/beta", "This is the file 'beta'.\n" },
2024 { "A/B/F", 0 },
2025 { "A/C", 0 },
2026 { "A/C/kappa", "This is the file 'kappa'.\n" },
2027 { "A/D", 0 },
2028 { "A/D/gamma", "This is the file 'gamma'.\n" },
2029 { "A/D/G", 0 },
2030 { "A/D/G/pi", "This is the file 'pi'.\n" },
2031 { "A/D/G/rho", "This is the file 'rho'.\n" },
2032 { "A/D/G/tau", "This is the file 'tau'.\n" },
2033 { "A/D/G/xi", "This is the file 'xi'.\n" },
2034 { "A/D/G/nu", "This is the file 'nu'.\n" },
2035 { "A/D/I", 0 },
2036 { "A/D/I/delta", "This is the file 'delta'.\n" },
2037 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
2038 };
2039 SVN_ERR(svn_fs_revision_root(&revision_root, fs, after_rev, pool));
2040 SVN_ERR(svn_test__validate_tree(revision_root,
2041 expected_entries,
2042 23, pool));
2043 }
2044 revisions[revision_count++] = after_rev;
2045
2046 /* (1c) E exists in both ANCESTOR and B, but refers to different
2047 revisions of the same directory node. Merge A/E and B/E,
2048 recursively. Fail, because conflict beneath E. */
2049 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[1], pool));
2050 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
2051 SVN_ERR(svn_fs_make_file(txn_root, "A/D/G/xi", pool));
2052 SVN_ERR(svn_test__set_file_contents
2053 (txn_root, "A/D/G/xi", "This is a different file 'xi'.\n", pool));
2054 SVN_ERR(test_commit_txn(&after_rev, txn, "/A/D/G/xi", pool));
2055 SVN_ERR(svn_fs_abort_txn(txn, pool));
2056
2057 /* (1) E exists in both ANCESTOR and B, and refers to the same node
2058 revision. Replace E with A's node revision. */
2059 {
2060 svn_stringbuf_t *old_lambda_ctnts;
2061 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[1], pool));
2062 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
2063 SVN_ERR(svn_test__get_file_contents
2064 (txn_root, "A/B/lambda", &old_lambda_ctnts, pool));
2065 if ((! old_lambda_ctnts)
2066 || (strcmp(old_lambda_ctnts->data,
2067 "This is the file 'lambda'.\n") != 0))
2068 {
2069 return svn_error_create
2070 (SVN_ERR_FS_GENERAL, NULL,
2071 "got wrong contents from an old revision tree");
2072 }
2073 SVN_ERR(svn_test__set_file_contents
2074 (txn_root, "A/D/G/rho",
2075 "This is an irrelevant change to 'rho'.\n", pool));
2076 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
2077 /*********************************************************************/
2078 /* REVISION 14 */
2079 /*********************************************************************/
2080 {
2081 static svn_test__tree_entry_t expected_entries[] = {
2082 /* path, contents (0 = dir) */
2083 { "theta", "This is the file 'theta'.\n" },
2084 { "A", 0 },
2085 { "A/mu", "A new file 'mu'.\n" },
2086 { "A/sigma", "This is the file 'sigma'.\n" },
2087 { "A/B", 0 },
2088 { "A/B/lambda", "Change to file 'lambda'.\n" },
2089 { "A/B/E", 0 },
2090 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
2091 { "A/B/E/beta", "This is the file 'beta'.\n" },
2092 { "A/B/F", 0 },
2093 { "A/C", 0 },
2094 { "A/C/kappa", "This is the file 'kappa'.\n" },
2095 { "A/D", 0 },
2096 { "A/D/gamma", "This is the file 'gamma'.\n" },
2097 { "A/D/G", 0 },
2098 { "A/D/G/pi", "This is the file 'pi'.\n" },
2099 { "A/D/G/rho", "This is an irrelevant change to 'rho'.\n" },
2100 { "A/D/G/tau", "This is the file 'tau'.\n" },
2101 { "A/D/G/xi", "This is the file 'xi'.\n" },
2102 { "A/D/G/nu", "This is the file 'nu'.\n"},
2103 { "A/D/I", 0 },
2104 { "A/D/I/delta", "This is the file 'delta'.\n" },
2105 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
2106 };
2107 SVN_ERR(svn_fs_revision_root(&revision_root, fs, after_rev, pool));
2108 SVN_ERR(svn_test__validate_tree(revision_root,
2109 expected_entries,
2110 23, pool));
2111 }
2112 revisions[revision_count++] = after_rev;
2113 }
2114 }
2115
2116 /* (1) E exists in both ANCESTOR and A, and refers to the same node
2117 revision. */
2118 {
2119 /* (1) E exists in both ANCESTOR and B, and refers to the same
2120 node revision. Nothing has happened to ANCESTOR/E, so no
2121 change is necessary. */
2122
2123 /* This has now been tested about fifty-four trillion times. We
2124 don't need to test it again here. */
2125 }
2126
2127 /* E exists in ANCESTOR, but has been deleted from A. E exists in
2128 both ANCESTOR and B but refers to different revisions of the same
2129 node. Conflict. */
2130 SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[1], pool));
2131 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
2132 SVN_ERR(svn_test__set_file_contents
2133 (txn_root, "iota", "New contents for 'iota'.\n", pool));
2134 SVN_ERR(test_commit_txn(&after_rev, txn, "/iota", pool));
2135 SVN_ERR(svn_fs_abort_txn(txn, pool));
2136
2137 return SVN_NO_ERROR;
2138 }
2139
2140
2141 static svn_error_t *
copy_test(const svn_test_opts_t * opts,apr_pool_t * pool)2142 copy_test(const svn_test_opts_t *opts,
2143 apr_pool_t *pool)
2144 {
2145 svn_fs_t *fs;
2146 svn_fs_txn_t *txn;
2147 svn_fs_root_t *txn_root, *rev_root;
2148 svn_revnum_t after_rev;
2149
2150 /* Prepare a filesystem. */
2151 SVN_ERR(svn_test__create_fs(&fs, "test-repo-copy",
2152 opts, pool));
2153
2154 /* In first txn, create and commit the greek tree. */
2155 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
2156 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
2157 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
2158 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
2159
2160 /* In second txn, copy the file A/D/G/pi into the subtree A/D/H as
2161 pi2. Change that file's contents to state its new name. Along
2162 the way, test that the copy history was preserved both during the
2163 transaction and after the commit. */
2164
2165 SVN_ERR(svn_fs_revision_root(&rev_root, fs, after_rev, pool));
2166 SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, pool));
2167 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
2168 SVN_ERR(svn_fs_copy(rev_root, "A/D/G/pi",
2169 txn_root, "A/D/H/pi2",
2170 pool));
2171 { /* Check that copy history was preserved. */
2172 svn_revnum_t rev;
2173 const char *path;
2174
2175 SVN_ERR(svn_fs_copied_from(&rev, &path, txn_root,
2176 "A/D/H/pi2", pool));
2177
2178 if (rev != after_rev)
2179 return svn_error_create
2180 (SVN_ERR_FS_GENERAL, NULL,
2181 "pre-commit copy history not preserved (rev lost) for A/D/H/pi2");
2182
2183 if (strcmp(path, "/A/D/G/pi") != 0)
2184 return svn_error_create
2185 (SVN_ERR_FS_GENERAL, NULL,
2186 "pre-commit copy history not preserved (path lost) for A/D/H/pi2");
2187 }
2188 SVN_ERR(svn_test__set_file_contents
2189 (txn_root, "A/D/H/pi2", "This is the file 'pi2'.\n", pool));
2190 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
2191
2192 { /* Check that copy history is still preserved _after_ the commit. */
2193 svn_fs_root_t *root;
2194 svn_revnum_t rev;
2195 const char *path;
2196
2197 SVN_ERR(svn_fs_revision_root(&root, fs, after_rev, pool));
2198 SVN_ERR(svn_fs_copied_from(&rev, &path, root, "A/D/H/pi2", pool));
2199
2200 if (rev != (after_rev - 1))
2201 return svn_error_create
2202 (SVN_ERR_FS_GENERAL, NULL,
2203 "post-commit copy history wrong (rev) for A/D/H/pi2");
2204
2205 if (strcmp(path, "/A/D/G/pi") != 0)
2206 return svn_error_create
2207 (SVN_ERR_FS_GENERAL, NULL,
2208 "post-commit copy history wrong (path) for A/D/H/pi2");
2209 }
2210
2211 /* Let's copy the copy we just made, to make sure copy history gets
2212 chained correctly. */
2213 SVN_ERR(svn_fs_revision_root(&rev_root, fs, after_rev, pool));
2214 SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, pool));
2215 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
2216 SVN_ERR(svn_fs_copy(rev_root, "A/D/H/pi2", txn_root, "A/D/H/pi3", pool));
2217 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
2218 { /* Check the copy history. */
2219 svn_fs_root_t *root;
2220 svn_revnum_t rev;
2221 const char *path;
2222
2223 /* Check that the original copy still has its old history. */
2224 SVN_ERR(svn_fs_revision_root(&root, fs, (after_rev - 1), pool));
2225 SVN_ERR(svn_fs_copied_from(&rev, &path, root, "A/D/H/pi2", pool));
2226
2227 if (rev != (after_rev - 2))
2228 return svn_error_create
2229 (SVN_ERR_FS_GENERAL, NULL,
2230 "first copy history wrong (rev) for A/D/H/pi2");
2231
2232 if (strcmp(path, "/A/D/G/pi") != 0)
2233 return svn_error_create
2234 (SVN_ERR_FS_GENERAL, NULL,
2235 "first copy history wrong (path) for A/D/H/pi2");
2236
2237 /* Check that the copy of the copy has the right history. */
2238 SVN_ERR(svn_fs_revision_root(&root, fs, after_rev, pool));
2239 SVN_ERR(svn_fs_copied_from(&rev, &path, root, "A/D/H/pi3", pool));
2240
2241 if (rev != (after_rev - 1))
2242 return svn_error_create
2243 (SVN_ERR_FS_GENERAL, NULL,
2244 "second copy history wrong (rev) for A/D/H/pi3");
2245
2246 if (strcmp(path, "/A/D/H/pi2") != 0)
2247 return svn_error_create
2248 (SVN_ERR_FS_GENERAL, NULL,
2249 "second copy history wrong (path) for A/D/H/pi3");
2250 }
2251
2252 /* Commit a regular change to a copy, make sure the copy history
2253 isn't inherited. */
2254 SVN_ERR(svn_fs_revision_root(&rev_root, fs, after_rev, pool));
2255 SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, pool));
2256 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
2257 SVN_ERR(svn_test__set_file_contents
2258 (txn_root, "A/D/H/pi3", "This is the file 'pi3'.\n", pool));
2259 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
2260 { /* Check the copy history. */
2261 svn_fs_root_t *root;
2262 svn_revnum_t rev;
2263 const char *path;
2264
2265 /* Check that the copy still has its history. */
2266 SVN_ERR(svn_fs_revision_root(&root, fs, (after_rev - 1), pool));
2267 SVN_ERR(svn_fs_copied_from(&rev, &path, root, "A/D/H/pi3", pool));
2268
2269 if (rev != (after_rev - 2))
2270 return svn_error_create
2271 (SVN_ERR_FS_GENERAL, NULL,
2272 "copy history wrong (rev) for A/D/H/pi3");
2273
2274 if (strcmp(path, "/A/D/H/pi2") != 0)
2275 return svn_error_create
2276 (SVN_ERR_FS_GENERAL, NULL,
2277 "copy history wrong (path) for A/D/H/pi3");
2278
2279 /* Check that the next revision after the copy has no copy history. */
2280 SVN_ERR(svn_fs_revision_root(&root, fs, after_rev, pool));
2281 SVN_ERR(svn_fs_copied_from(&rev, &path, root, "A/D/H/pi3", pool));
2282
2283 if (rev != SVN_INVALID_REVNUM)
2284 return svn_error_create
2285 (SVN_ERR_FS_GENERAL, NULL,
2286 "copy history wrong (rev) for A/D/H/pi3");
2287
2288 if (path != NULL)
2289 return svn_error_create
2290 (SVN_ERR_FS_GENERAL, NULL,
2291 "copy history wrong (path) for A/D/H/pi3");
2292 }
2293
2294 /* Then, as if that wasn't fun enough, copy the whole subtree A/D/H
2295 into the root directory as H2! */
2296 SVN_ERR(svn_fs_revision_root(&rev_root, fs, after_rev, pool));
2297 SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, pool));
2298 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
2299 SVN_ERR(svn_fs_copy(rev_root, "A/D/H", txn_root, "H2", pool));
2300 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
2301 { /* Check the copy history. */
2302 svn_fs_root_t *root;
2303 svn_revnum_t rev;
2304 const char *path;
2305
2306 /* Check that the top of the copy has history. */
2307 SVN_ERR(svn_fs_revision_root(&root, fs, after_rev, pool));
2308 SVN_ERR(svn_fs_copied_from(&rev, &path, root, "H2", pool));
2309
2310 if (rev != (after_rev - 1))
2311 return svn_error_create
2312 (SVN_ERR_FS_GENERAL, NULL,
2313 "copy history wrong (rev) for H2");
2314
2315 if (strcmp(path, "/A/D/H") != 0)
2316 return svn_error_create
2317 (SVN_ERR_FS_GENERAL, NULL,
2318 "copy history wrong (path) for H2");
2319
2320 /* Check that a random file under H2 reports no copy history. */
2321 SVN_ERR(svn_fs_copied_from(&rev, &path, root, "H2/omega", pool));
2322
2323 if (rev != SVN_INVALID_REVNUM)
2324 return svn_error_create
2325 (SVN_ERR_FS_GENERAL, NULL,
2326 "copy history wrong (rev) for H2/omega");
2327
2328 if (path != NULL)
2329 return svn_error_create
2330 (SVN_ERR_FS_GENERAL, NULL,
2331 "copy history wrong (path) for H2/omega");
2332
2333 /* Note that H2/pi2 still has copy history, though. See the doc
2334 string for svn_fs_copied_from() for more on this. */
2335 }
2336
2337 /* Let's live dangerously. What happens if we copy a path into one
2338 of its own children. Looping filesystem? Cyclic ancestry?
2339 Another West Virginia family tree with no branches? We certainly
2340 hope that's not the case. */
2341 SVN_ERR(svn_fs_revision_root(&rev_root, fs, after_rev, pool));
2342 SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, pool));
2343 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
2344 SVN_ERR(svn_fs_copy(rev_root, "A/B", txn_root, "A/B/E/B", pool));
2345 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
2346 { /* Check the copy history. */
2347 svn_fs_root_t *root;
2348 svn_revnum_t rev;
2349 const char *path;
2350
2351 /* Check that the copy has history. */
2352 SVN_ERR(svn_fs_revision_root(&root, fs, after_rev, pool));
2353 SVN_ERR(svn_fs_copied_from(&rev, &path, root, "A/B/E/B", pool));
2354
2355 if (rev != (after_rev - 1))
2356 return svn_error_create
2357 (SVN_ERR_FS_GENERAL, NULL,
2358 "copy history wrong (rev) for A/B/E/B");
2359
2360 if (strcmp(path, "/A/B") != 0)
2361 return svn_error_create
2362 (SVN_ERR_FS_GENERAL, NULL,
2363 "copy history wrong (path) for A/B/E/B");
2364
2365 /* Check that the original does not have copy history. */
2366 SVN_ERR(svn_fs_revision_root(&root, fs, after_rev, pool));
2367 SVN_ERR(svn_fs_copied_from(&rev, &path, root, "A/B", pool));
2368
2369 if (rev != SVN_INVALID_REVNUM)
2370 return svn_error_create
2371 (SVN_ERR_FS_GENERAL, NULL,
2372 "copy history wrong (rev) for A/B");
2373
2374 if (path != NULL)
2375 return svn_error_create
2376 (SVN_ERR_FS_GENERAL, NULL,
2377 "copy history wrong (path) for A/B");
2378 }
2379
2380 /* After all these changes, let's see if the filesystem looks as we
2381 would expect it to. */
2382 {
2383 static svn_test__tree_entry_t expected_entries[] = {
2384 /* path, contents (0 = dir) */
2385 { "iota", "This is the file 'iota'.\n" },
2386 { "H2", 0 },
2387 { "H2/chi", "This is the file 'chi'.\n" },
2388 { "H2/pi2", "This is the file 'pi2'.\n" },
2389 { "H2/pi3", "This is the file 'pi3'.\n" },
2390 { "H2/psi", "This is the file 'psi'.\n" },
2391 { "H2/omega", "This is the file 'omega'.\n" },
2392 { "A", 0 },
2393 { "A/mu", "This is the file 'mu'.\n" },
2394 { "A/B", 0 },
2395 { "A/B/lambda", "This is the file 'lambda'.\n" },
2396 { "A/B/E", 0 },
2397 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
2398 { "A/B/E/beta", "This is the file 'beta'.\n" },
2399 { "A/B/E/B", 0 },
2400 { "A/B/E/B/lambda", "This is the file 'lambda'.\n" },
2401 { "A/B/E/B/E", 0 },
2402 { "A/B/E/B/E/alpha", "This is the file 'alpha'.\n" },
2403 { "A/B/E/B/E/beta", "This is the file 'beta'.\n" },
2404 { "A/B/E/B/F", 0 },
2405 { "A/B/F", 0 },
2406 { "A/C", 0 },
2407 { "A/D", 0 },
2408 { "A/D/gamma", "This is the file 'gamma'.\n" },
2409 { "A/D/G", 0 },
2410 { "A/D/G/pi", "This is the file 'pi'.\n" },
2411 { "A/D/G/rho", "This is the file 'rho'.\n" },
2412 { "A/D/G/tau", "This is the file 'tau'.\n" },
2413 { "A/D/H", 0 },
2414 { "A/D/H/chi", "This is the file 'chi'.\n" },
2415 { "A/D/H/pi2", "This is the file 'pi2'.\n" },
2416 { "A/D/H/pi3", "This is the file 'pi3'.\n" },
2417 { "A/D/H/psi", "This is the file 'psi'.\n" },
2418 { "A/D/H/omega", "This is the file 'omega'.\n" }
2419 };
2420 SVN_ERR(svn_fs_revision_root(&rev_root, fs, after_rev, pool));
2421 SVN_ERR(svn_test__validate_tree(rev_root, expected_entries,
2422 34, pool));
2423 }
2424
2425 return SVN_NO_ERROR;
2426 }
2427
2428
2429 /* This tests deleting of mutable nodes. We build a tree in a
2430 * transaction, then try to delete various items in the tree. We
2431 * never commit the tree, so every entry being deleted points to a
2432 * mutable node.
2433 *
2434 * ### todo: this test was written before commits worked. It might
2435 * now be worthwhile to combine it with delete().
2436 */
2437 static svn_error_t *
delete_mutables(const svn_test_opts_t * opts,apr_pool_t * pool)2438 delete_mutables(const svn_test_opts_t *opts,
2439 apr_pool_t *pool)
2440 {
2441 svn_fs_t *fs;
2442 svn_fs_txn_t *txn;
2443 svn_fs_root_t *txn_root;
2444 svn_error_t *err;
2445
2446 /* Prepare a txn to receive the greek tree. */
2447 SVN_ERR(svn_test__create_fs(&fs, "test-repo-del-from-dir",
2448 opts, pool));
2449 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
2450 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
2451
2452 /* Create the greek tree. */
2453 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
2454
2455 /* Baby, it's time to test like you've never tested before. We do
2456 * the following, in this order:
2457 *
2458 * 1. Delete a single file somewhere, succeed.
2459 * 2. Delete two files of three, then make sure the third remains.
2460 * 3. Delete the third and last file.
2461 * 4. Try again to delete the dir, succeed.
2462 * 5. Delete one of the natively empty dirs, succeed.
2463 * 6. Try to delete root, fail.
2464 * 7. Try to delete a top-level file, succeed.
2465 *
2466 * Specifically, that's:
2467 *
2468 * 1. Delete A/D/gamma.
2469 * 2. Delete A/D/G/pi, A/D/G/rho.
2470 * 3. Delete A/D/G/tau.
2471 * 4. Try again to delete A/D/G, succeed.
2472 * 5. Delete A/C.
2473 * 6. Try to delete /, fail.
2474 * 7. Try to delete iota, succeed.
2475 *
2476 * Before and after each deletion or attempted deletion, we probe
2477 * the affected directory, to make sure everything is as it should
2478 * be.
2479 */
2480
2481 /* 1 */
2482 {
2483 const svn_fs_id_t *gamma_id;
2484 SVN_ERR(svn_fs_node_id(&gamma_id, txn_root, "A/D/gamma", pool));
2485 SVN_ERR(check_entry_present(txn_root, "A/D", "gamma", pool));
2486 SVN_ERR(svn_fs_delete(txn_root, "A/D/gamma", pool));
2487 SVN_ERR(check_entry_absent(txn_root, "A/D", "gamma", pool));
2488 }
2489
2490 /* 2 */
2491 {
2492 const svn_fs_id_t *pi_id, *rho_id, *tau_id;
2493 SVN_ERR(svn_fs_node_id(&pi_id, txn_root, "A/D/G/pi", pool));
2494 SVN_ERR(svn_fs_node_id(&rho_id, txn_root, "A/D/G/rho", pool));
2495 SVN_ERR(svn_fs_node_id(&tau_id, txn_root, "A/D/G/tau", pool));
2496 SVN_ERR(check_entry_present(txn_root, "A/D/G", "pi", pool));
2497 SVN_ERR(check_entry_present(txn_root, "A/D/G", "rho", pool));
2498 SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool));
2499 SVN_ERR(svn_fs_delete(txn_root, "A/D/G/pi", pool));
2500 SVN_ERR(check_entry_absent(txn_root, "A/D/G", "pi", pool));
2501 SVN_ERR(check_entry_present(txn_root, "A/D/G", "rho", pool));
2502 SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool));
2503 SVN_ERR(svn_fs_delete(txn_root, "A/D/G/rho", pool));
2504 SVN_ERR(check_entry_absent(txn_root, "A/D/G", "pi", pool));
2505 SVN_ERR(check_entry_absent(txn_root, "A/D/G", "rho", pool));
2506 SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool));
2507 }
2508
2509 /* 3 */
2510 {
2511 const svn_fs_id_t *tau_id;
2512 SVN_ERR(svn_fs_node_id(&tau_id, txn_root, "A/D/G/tau", pool));
2513 SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool));
2514 SVN_ERR(svn_fs_delete(txn_root, "A/D/G/tau", pool));
2515 SVN_ERR(check_entry_absent(txn_root, "A/D/G", "tau", pool));
2516 }
2517
2518 /* 4 */
2519 {
2520 const svn_fs_id_t *G_id;
2521 SVN_ERR(svn_fs_node_id(&G_id, txn_root, "A/D/G", pool));
2522 SVN_ERR(check_entry_present(txn_root, "A/D", "G", pool));
2523 SVN_ERR(svn_fs_delete(txn_root, "A/D/G", pool)); /* succeed */
2524 SVN_ERR(check_entry_absent(txn_root, "A/D", "G", pool));
2525 }
2526
2527 /* 5 */
2528 {
2529 const svn_fs_id_t *C_id;
2530 SVN_ERR(svn_fs_node_id(&C_id, txn_root, "A/C", pool));
2531 SVN_ERR(check_entry_present(txn_root, "A", "C", pool));
2532 SVN_ERR(svn_fs_delete(txn_root, "A/C", pool));
2533 SVN_ERR(check_entry_absent(txn_root, "A", "C", pool));
2534 }
2535
2536 /* 6 */
2537 {
2538 const svn_fs_id_t *root_id;
2539 SVN_ERR(svn_fs_node_id(&root_id, txn_root, "", pool));
2540
2541 err = svn_fs_delete(txn_root, "", pool);
2542
2543 if (err && (err->apr_err != SVN_ERR_FS_ROOT_DIR))
2544 {
2545 return svn_error_createf
2546 (SVN_ERR_FS_GENERAL, NULL,
2547 "deleting root directory got wrong error");
2548 }
2549 else if (! err)
2550 {
2551 return svn_error_createf
2552 (SVN_ERR_FS_GENERAL, NULL,
2553 "deleting root directory failed to get error");
2554 }
2555 svn_error_clear(err);
2556
2557 }
2558
2559 /* 7 */
2560 {
2561 const svn_fs_id_t *iota_id;
2562 SVN_ERR(svn_fs_node_id(&iota_id, txn_root, "iota", pool));
2563 SVN_ERR(check_entry_present(txn_root, "", "iota", pool));
2564 SVN_ERR(svn_fs_delete(txn_root, "iota", pool));
2565 SVN_ERR(check_entry_absent(txn_root, "", "iota", pool));
2566 }
2567
2568 return SVN_NO_ERROR;
2569 }
2570
2571
2572 /* This tests deleting in general.
2573 *
2574 * ### todo: this test was written after (and independently of)
2575 * delete_mutables(). It might be worthwhile to combine them.
2576 */
2577 static svn_error_t *
delete(const svn_test_opts_t * opts,apr_pool_t * pool)2578 delete(const svn_test_opts_t *opts,
2579 apr_pool_t *pool)
2580 {
2581 svn_fs_t *fs;
2582 svn_fs_txn_t *txn;
2583 svn_fs_root_t *txn_root;
2584 svn_revnum_t new_rev;
2585
2586 /* This function tests 5 cases:
2587 *
2588 * 1. Delete mutable file.
2589 * 2. Delete mutable directory.
2590 * 3. Delete mutable directory with immutable nodes.
2591 * 4. Delete immutable file.
2592 * 5. Delete immutable directory.
2593 */
2594
2595 /* Prepare a txn to receive the greek tree. */
2596 SVN_ERR(svn_test__create_fs(&fs, "test-repo-del-tree",
2597 opts, pool));
2598 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
2599 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
2600
2601 /* Create the greek tree. */
2602 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
2603
2604 /* 1. Delete mutable file. */
2605 {
2606 const svn_fs_id_t *iota_id, *gamma_id;
2607 static svn_test__tree_entry_t expected_entries[] = {
2608 /* path, contents (0 = dir) */
2609 { "A", 0 },
2610 { "A/mu", "This is the file 'mu'.\n" },
2611 { "A/B", 0 },
2612 { "A/B/lambda", "This is the file 'lambda'.\n" },
2613 { "A/B/E", 0 },
2614 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
2615 { "A/B/E/beta", "This is the file 'beta'.\n" },
2616 { "A/C", 0 },
2617 { "A/B/F", 0 },
2618 { "A/D", 0 },
2619 { "A/D/G", 0 },
2620 { "A/D/G/pi", "This is the file 'pi'.\n" },
2621 { "A/D/G/rho", "This is the file 'rho'.\n" },
2622 { "A/D/G/tau", "This is the file 'tau'.\n" },
2623 { "A/D/H", 0 },
2624 { "A/D/H/chi", "This is the file 'chi'.\n" },
2625 { "A/D/H/psi", "This is the file 'psi'.\n" },
2626 { "A/D/H/omega", "This is the file 'omega'.\n" }
2627 };
2628
2629 /* Check nodes revision ID is gone. */
2630 SVN_ERR(svn_fs_node_id(&iota_id, txn_root, "iota", pool));
2631 SVN_ERR(svn_fs_node_id(&gamma_id, txn_root, "A/D/gamma", pool));
2632
2633 SVN_ERR(check_entry_present(txn_root, "", "iota", pool));
2634
2635 /* Try deleting mutable files. */
2636 SVN_ERR(svn_fs_delete(txn_root, "iota", pool));
2637 SVN_ERR(svn_fs_delete(txn_root, "A/D/gamma", pool));
2638 SVN_ERR(check_entry_absent(txn_root, "", "iota", pool));
2639 SVN_ERR(check_entry_absent(txn_root, "A/D", "gamma", pool));
2640
2641 /* Validate the tree. */
2642 SVN_ERR(svn_test__validate_tree(txn_root, expected_entries, 18, pool));
2643 }
2644 /* Abort transaction. */
2645 SVN_ERR(svn_fs_abort_txn(txn, pool));
2646
2647 /* 2. Delete mutable directory. */
2648
2649 /* Prepare a txn to receive the greek tree. */
2650 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
2651 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
2652
2653 /* Create the greek tree. */
2654 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
2655
2656 {
2657 const svn_fs_id_t *A_id, *mu_id, *B_id, *lambda_id, *E_id, *alpha_id,
2658 *beta_id, *F_id, *C_id, *D_id, *gamma_id, *H_id, *chi_id,
2659 *psi_id, *omega_id, *G_id, *pi_id, *rho_id, *tau_id;
2660
2661 /* Check nodes revision ID is gone. */
2662 SVN_ERR(svn_fs_node_id(&A_id, txn_root, "/A", pool));
2663 SVN_ERR(check_entry_present(txn_root, "", "A", pool));
2664 SVN_ERR(svn_fs_node_id(&mu_id, txn_root, "/A/mu", pool));
2665 SVN_ERR(check_entry_present(txn_root, "A", "mu", pool));
2666 SVN_ERR(svn_fs_node_id(&B_id, txn_root, "/A/B", pool));
2667 SVN_ERR(check_entry_present(txn_root, "A", "B", pool));
2668 SVN_ERR(svn_fs_node_id(&lambda_id, txn_root, "/A/B/lambda", pool));
2669 SVN_ERR(check_entry_present(txn_root, "A/B", "lambda", pool));
2670 SVN_ERR(svn_fs_node_id(&E_id, txn_root, "/A/B/E", pool));
2671 SVN_ERR(check_entry_present(txn_root, "A/B", "E", pool));
2672 SVN_ERR(svn_fs_node_id(&alpha_id, txn_root, "/A/B/E/alpha", pool));
2673 SVN_ERR(check_entry_present(txn_root, "A/B/E", "alpha", pool));
2674 SVN_ERR(svn_fs_node_id(&beta_id, txn_root, "/A/B/E/beta", pool));
2675 SVN_ERR(check_entry_present(txn_root, "A/B/E", "beta", pool));
2676 SVN_ERR(svn_fs_node_id(&F_id, txn_root, "/A/B/F", pool));
2677 SVN_ERR(check_entry_present(txn_root, "A/B", "F", pool));
2678 SVN_ERR(svn_fs_node_id(&C_id, txn_root, "/A/C", pool));
2679 SVN_ERR(check_entry_present(txn_root, "A", "C", pool));
2680 SVN_ERR(svn_fs_node_id(&D_id, txn_root, "/A/D", pool));
2681 SVN_ERR(check_entry_present(txn_root, "A", "D", pool));
2682 SVN_ERR(svn_fs_node_id(&gamma_id, txn_root, "/A/D/gamma", pool));
2683 SVN_ERR(check_entry_present(txn_root, "A/D", "gamma", pool));
2684 SVN_ERR(svn_fs_node_id(&H_id, txn_root, "/A/D/H", pool));
2685 SVN_ERR(check_entry_present(txn_root, "A/D", "H", pool));
2686 SVN_ERR(svn_fs_node_id(&chi_id, txn_root, "/A/D/H/chi", pool));
2687 SVN_ERR(check_entry_present(txn_root, "A/D/H", "chi", pool));
2688 SVN_ERR(svn_fs_node_id(&psi_id, txn_root, "/A/D/H/psi", pool));
2689 SVN_ERR(check_entry_present(txn_root, "A/D/H", "psi", pool));
2690 SVN_ERR(svn_fs_node_id(&omega_id, txn_root, "/A/D/H/omega", pool));
2691 SVN_ERR(check_entry_present(txn_root, "A/D/H", "omega", pool));
2692 SVN_ERR(svn_fs_node_id(&G_id, txn_root, "/A/D/G", pool));
2693 SVN_ERR(check_entry_present(txn_root, "A/D", "G", pool));
2694 SVN_ERR(svn_fs_node_id(&pi_id, txn_root, "/A/D/G/pi", pool));
2695 SVN_ERR(check_entry_present(txn_root, "A/D/G", "pi", pool));
2696 SVN_ERR(svn_fs_node_id(&rho_id, txn_root, "/A/D/G/rho", pool));
2697 SVN_ERR(check_entry_present(txn_root, "A/D/G", "rho", pool));
2698 SVN_ERR(svn_fs_node_id(&tau_id, txn_root, "/A/D/G/tau", pool));
2699 SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool));
2700
2701 /* Try deleting a mutable empty dir. */
2702 SVN_ERR(svn_fs_delete(txn_root, "A/C", pool));
2703 SVN_ERR(svn_fs_delete(txn_root, "A/B/F", pool));
2704 SVN_ERR(check_entry_absent(txn_root, "A", "C", pool));
2705 SVN_ERR(check_entry_absent(txn_root, "A/B", "F", pool));
2706
2707 /* Now delete a mutable non-empty dir. */
2708 SVN_ERR(svn_fs_delete(txn_root, "A", pool));
2709 SVN_ERR(check_entry_absent(txn_root, "", "A", pool));
2710
2711 /* Validate the tree. */
2712 {
2713 static svn_test__tree_entry_t expected_entries[] = {
2714 /* path, contents (0 = dir) */
2715 { "iota", "This is the file 'iota'.\n" } };
2716 SVN_ERR(svn_test__validate_tree(txn_root, expected_entries, 1, pool));
2717 }
2718 }
2719
2720 /* Abort transaction. */
2721 SVN_ERR(svn_fs_abort_txn(txn, pool));
2722
2723 /* 3. Delete mutable directory with immutable nodes. */
2724
2725 /* Prepare a txn to receive the greek tree. */
2726 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
2727 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
2728
2729 /* Create the greek tree. */
2730 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
2731
2732 /* Commit the greek tree. */
2733 SVN_ERR(svn_fs_commit_txn(NULL, &new_rev, txn, pool));
2734 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(new_rev));
2735
2736 /* Create new transaction. */
2737 SVN_ERR(svn_fs_begin_txn(&txn, fs, new_rev, pool));
2738 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
2739
2740 {
2741 const svn_fs_id_t *A_id, *mu_id, *B_id, *lambda_id, *E_id, *alpha_id,
2742 *beta_id, *F_id, *C_id, *D_id, *gamma_id, *H_id, *chi_id,
2743 *psi_id, *omega_id, *G_id, *pi_id, *rho_id, *tau_id, *sigma_id;
2744
2745 /* Create A/D/G/sigma. This makes all components of A/D/G
2746 mutable. */
2747 SVN_ERR(svn_fs_make_file(txn_root, "A/D/G/sigma", pool));
2748 SVN_ERR(svn_test__set_file_contents(txn_root, "A/D/G/sigma",
2749 "This is another file 'sigma'.\n", pool));
2750
2751 /* Check that mutable node-revision-IDs are removed and immutable
2752 ones still exist. */
2753 SVN_ERR(svn_fs_node_id(&A_id, txn_root, "/A", pool));
2754 SVN_ERR(check_entry_present(txn_root, "", "A", pool));
2755 SVN_ERR(svn_fs_node_id(&mu_id, txn_root, "/A/mu", pool));
2756 SVN_ERR(check_entry_present(txn_root, "A", "mu", pool));
2757 SVN_ERR(svn_fs_node_id(&B_id, txn_root, "/A/B", pool));
2758 SVN_ERR(check_entry_present(txn_root, "A", "B", pool));
2759 SVN_ERR(svn_fs_node_id(&lambda_id, txn_root, "/A/B/lambda", pool));
2760 SVN_ERR(check_entry_present(txn_root, "A/B", "lambda", pool));
2761 SVN_ERR(svn_fs_node_id(&E_id, txn_root, "/A/B/E", pool));
2762 SVN_ERR(check_entry_present(txn_root, "A/B", "E", pool));
2763 SVN_ERR(svn_fs_node_id(&alpha_id, txn_root, "/A/B/E/alpha", pool));
2764 SVN_ERR(check_entry_present(txn_root, "A/B/E", "alpha", pool));
2765 SVN_ERR(svn_fs_node_id(&beta_id, txn_root, "/A/B/E/beta", pool));
2766 SVN_ERR(check_entry_present(txn_root, "A/B/E", "beta", pool));
2767 SVN_ERR(svn_fs_node_id(&F_id, txn_root, "/A/B/F", pool));
2768 SVN_ERR(check_entry_present(txn_root, "A/B", "F", pool));
2769 SVN_ERR(svn_fs_node_id(&C_id, txn_root, "/A/C", pool));
2770 SVN_ERR(check_entry_present(txn_root, "A", "C", pool));
2771 SVN_ERR(svn_fs_node_id(&D_id, txn_root, "/A/D", pool));
2772 SVN_ERR(check_entry_present(txn_root, "A", "D", pool));
2773 SVN_ERR(svn_fs_node_id(&gamma_id, txn_root, "/A/D/gamma", pool));
2774 SVN_ERR(check_entry_present(txn_root, "A/D", "gamma", pool));
2775 SVN_ERR(svn_fs_node_id(&H_id, txn_root, "/A/D/H", pool));
2776 SVN_ERR(check_entry_present(txn_root, "A/D", "H", pool));
2777 SVN_ERR(svn_fs_node_id(&chi_id, txn_root, "/A/D/H/chi", pool));
2778 SVN_ERR(check_entry_present(txn_root, "A/D/H", "chi", pool));
2779 SVN_ERR(svn_fs_node_id(&psi_id, txn_root, "/A/D/H/psi", pool));
2780 SVN_ERR(check_entry_present(txn_root, "A/D/H", "psi", pool));
2781 SVN_ERR(svn_fs_node_id(&omega_id, txn_root, "/A/D/H/omega", pool));
2782 SVN_ERR(check_entry_present(txn_root, "A/D/H", "omega", pool));
2783 SVN_ERR(svn_fs_node_id(&G_id, txn_root, "/A/D/G", pool));
2784 SVN_ERR(check_entry_present(txn_root, "A/D", "G", pool));
2785 SVN_ERR(svn_fs_node_id(&pi_id, txn_root, "/A/D/G/pi", pool));
2786 SVN_ERR(check_entry_present(txn_root, "A/D/G", "pi", pool));
2787 SVN_ERR(svn_fs_node_id(&rho_id, txn_root, "/A/D/G/rho", pool));
2788 SVN_ERR(check_entry_present(txn_root, "A/D/G", "rho", pool));
2789 SVN_ERR(svn_fs_node_id(&tau_id, txn_root, "/A/D/G/tau", pool));
2790 SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool));
2791 SVN_ERR(svn_fs_node_id(&sigma_id, txn_root, "/A/D/G/sigma", pool));
2792 SVN_ERR(check_entry_present(txn_root, "A/D/G", "sigma", pool));
2793
2794 /* Delete "A" */
2795 SVN_ERR(svn_fs_delete(txn_root, "A", pool));
2796 SVN_ERR(check_entry_absent(txn_root, "", "A", pool));
2797
2798 /* Validate the tree. */
2799 {
2800 static svn_test__tree_entry_t expected_entries[] = {
2801 /* path, contents (0 = dir) */
2802 { "iota", "This is the file 'iota'.\n" }
2803 };
2804
2805 SVN_ERR(svn_test__validate_tree(txn_root, expected_entries, 1, pool));
2806 }
2807 }
2808
2809 /* Abort transaction. */
2810 SVN_ERR(svn_fs_abort_txn(txn, pool));
2811
2812 /* 4. Delete immutable file. */
2813
2814 /* Create new transaction. */
2815 SVN_ERR(svn_fs_begin_txn(&txn, fs, new_rev, pool));
2816 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
2817
2818 {
2819 const svn_fs_id_t *iota_id, *gamma_id;
2820
2821 /* Check nodes revision ID is present. */
2822 SVN_ERR(svn_fs_node_id(&iota_id, txn_root, "iota", pool));
2823 SVN_ERR(svn_fs_node_id(&gamma_id, txn_root, "A/D/gamma", pool));
2824 SVN_ERR(check_entry_present(txn_root, "", "iota", pool));
2825 SVN_ERR(check_entry_present(txn_root, "A/D", "gamma", pool));
2826
2827 /* Delete some files. */
2828 SVN_ERR(svn_fs_delete(txn_root, "iota", pool));
2829 SVN_ERR(svn_fs_delete(txn_root, "A/D/gamma", pool));
2830 SVN_ERR(check_entry_absent(txn_root, "", "iota", pool));
2831 SVN_ERR(check_entry_absent(txn_root, "A/D", "iota", pool));
2832
2833 /* Validate the tree. */
2834 {
2835 static svn_test__tree_entry_t expected_entries[] = {
2836 /* path, contents (0 = dir) */
2837 { "A", 0 },
2838 { "A/mu", "This is the file 'mu'.\n" },
2839 { "A/B", 0 },
2840 { "A/B/lambda", "This is the file 'lambda'.\n" },
2841 { "A/B/E", 0 },
2842 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
2843 { "A/B/E/beta", "This is the file 'beta'.\n" },
2844 { "A/B/F", 0 },
2845 { "A/C", 0 },
2846 { "A/D", 0 },
2847 { "A/D/G", 0 },
2848 { "A/D/G/pi", "This is the file 'pi'.\n" },
2849 { "A/D/G/rho", "This is the file 'rho'.\n" },
2850 { "A/D/G/tau", "This is the file 'tau'.\n" },
2851 { "A/D/H", 0 },
2852 { "A/D/H/chi", "This is the file 'chi'.\n" },
2853 { "A/D/H/psi", "This is the file 'psi'.\n" },
2854 { "A/D/H/omega", "This is the file 'omega'.\n" }
2855 };
2856 SVN_ERR(svn_test__validate_tree(txn_root, expected_entries, 18, pool));
2857 }
2858 }
2859
2860 /* Abort transaction. */
2861 SVN_ERR(svn_fs_abort_txn(txn, pool));
2862
2863 /* 5. Delete immutable directory. */
2864
2865 /* Create new transaction. */
2866 SVN_ERR(svn_fs_begin_txn(&txn, fs, new_rev, pool));
2867 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
2868
2869 {
2870 const svn_fs_id_t *A_id, *mu_id, *B_id, *lambda_id, *E_id, *alpha_id,
2871 *beta_id, *F_id, *C_id, *D_id, *gamma_id, *H_id, *chi_id,
2872 *psi_id, *omega_id, *G_id, *pi_id, *rho_id, *tau_id;
2873
2874 /* Check nodes revision ID is present. */
2875 SVN_ERR(svn_fs_node_id(&A_id, txn_root, "/A", pool));
2876 SVN_ERR(check_entry_present(txn_root, "", "A", pool));
2877 SVN_ERR(svn_fs_node_id(&mu_id, txn_root, "/A/mu", pool));
2878 SVN_ERR(check_entry_present(txn_root, "A", "mu", pool));
2879 SVN_ERR(svn_fs_node_id(&B_id, txn_root, "/A/B", pool));
2880 SVN_ERR(check_entry_present(txn_root, "A", "B", pool));
2881 SVN_ERR(svn_fs_node_id(&lambda_id, txn_root, "/A/B/lambda", pool));
2882 SVN_ERR(check_entry_present(txn_root, "A/B", "lambda", pool));
2883 SVN_ERR(svn_fs_node_id(&E_id, txn_root, "/A/B/E", pool));
2884 SVN_ERR(check_entry_present(txn_root, "A/B", "E", pool));
2885 SVN_ERR(svn_fs_node_id(&alpha_id, txn_root, "/A/B/E/alpha", pool));
2886 SVN_ERR(check_entry_present(txn_root, "A/B/E", "alpha", pool));
2887 SVN_ERR(svn_fs_node_id(&beta_id, txn_root, "/A/B/E/beta", pool));
2888 SVN_ERR(check_entry_present(txn_root, "A/B/E", "beta", pool));
2889 SVN_ERR(svn_fs_node_id(&F_id, txn_root, "/A/B/F", pool));
2890 SVN_ERR(check_entry_present(txn_root, "A/B", "F", pool));
2891 SVN_ERR(svn_fs_node_id(&C_id, txn_root, "/A/C", pool));
2892 SVN_ERR(check_entry_present(txn_root, "A", "C", pool));
2893 SVN_ERR(svn_fs_node_id(&D_id, txn_root, "/A/D", pool));
2894 SVN_ERR(check_entry_present(txn_root, "A", "D", pool));
2895 SVN_ERR(svn_fs_node_id(&gamma_id, txn_root, "/A/D/gamma", pool));
2896 SVN_ERR(check_entry_present(txn_root, "A/D", "gamma", pool));
2897 SVN_ERR(svn_fs_node_id(&H_id, txn_root, "/A/D/H", pool));
2898 SVN_ERR(check_entry_present(txn_root, "A/D", "H", pool));
2899 SVN_ERR(svn_fs_node_id(&chi_id, txn_root, "/A/D/H/chi", pool));
2900 SVN_ERR(check_entry_present(txn_root, "A/D/H", "chi", pool));
2901 SVN_ERR(svn_fs_node_id(&psi_id, txn_root, "/A/D/H/psi", pool));
2902 SVN_ERR(check_entry_present(txn_root, "A/D/H", "psi", pool));
2903 SVN_ERR(svn_fs_node_id(&omega_id, txn_root, "/A/D/H/omega", pool));
2904 SVN_ERR(check_entry_present(txn_root, "A/D/H", "omega", pool));
2905 SVN_ERR(svn_fs_node_id(&G_id, txn_root, "/A/D/G", pool));
2906 SVN_ERR(check_entry_present(txn_root, "A/D", "G", pool));
2907 SVN_ERR(svn_fs_node_id(&pi_id, txn_root, "/A/D/G/pi", pool));
2908 SVN_ERR(check_entry_present(txn_root, "A/D/G", "pi", pool));
2909 SVN_ERR(svn_fs_node_id(&rho_id, txn_root, "/A/D/G/rho", pool));
2910 SVN_ERR(check_entry_present(txn_root, "A/D/G", "rho", pool));
2911 SVN_ERR(svn_fs_node_id(&tau_id, txn_root, "/A/D/G/tau", pool));
2912 SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool));
2913
2914 /* Delete "A" */
2915 SVN_ERR(svn_fs_delete(txn_root, "A", pool));
2916 SVN_ERR(check_entry_absent(txn_root, "", "A", pool));
2917
2918 /* Validate the tree. */
2919 {
2920 static svn_test__tree_entry_t expected_entries[] = {
2921 /* path, contents (0 = dir) */
2922 { "iota", "This is the file 'iota'.\n" }
2923 };
2924 SVN_ERR(svn_test__validate_tree(txn_root, expected_entries, 1, pool));
2925 }
2926 }
2927
2928 return SVN_NO_ERROR;
2929 }
2930
2931
2932
2933 /* Test the datestamps on commits. */
2934 static svn_error_t *
commit_date(const svn_test_opts_t * opts,apr_pool_t * pool)2935 commit_date(const svn_test_opts_t *opts,
2936 apr_pool_t *pool)
2937 {
2938 svn_fs_t *fs;
2939 svn_fs_txn_t *txn;
2940 svn_fs_root_t *txn_root;
2941 svn_revnum_t rev;
2942 svn_string_t *datestamp;
2943 apr_time_t before_commit, at_commit, after_commit;
2944
2945 /* Prepare a filesystem. */
2946 SVN_ERR(svn_test__create_fs(&fs, "test-repo-commit-date",
2947 opts, pool));
2948
2949 before_commit = apr_time_now();
2950
2951 /* Commit a greek tree. */
2952 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
2953 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
2954 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
2955 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, pool));
2956 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(rev));
2957
2958 after_commit = apr_time_now();
2959
2960 /* Get the datestamp of the commit. */
2961 SVN_ERR(svn_fs_revision_prop(&datestamp, fs, rev, SVN_PROP_REVISION_DATE,
2962 pool));
2963
2964 if (datestamp == NULL)
2965 return svn_error_create
2966 (SVN_ERR_FS_GENERAL, NULL,
2967 "failed to get datestamp of committed revision");
2968
2969 SVN_ERR(svn_time_from_cstring(&at_commit, datestamp->data, pool));
2970
2971 if (at_commit < before_commit)
2972 return svn_error_create
2973 (SVN_ERR_FS_GENERAL, NULL,
2974 "datestamp too early");
2975
2976 if (at_commit > after_commit)
2977 return svn_error_create
2978 (SVN_ERR_FS_GENERAL, NULL,
2979 "datestamp too late");
2980
2981 return SVN_NO_ERROR;
2982 }
2983
2984
2985 static svn_error_t *
check_old_revisions(const svn_test_opts_t * opts,apr_pool_t * pool)2986 check_old_revisions(const svn_test_opts_t *opts,
2987 apr_pool_t *pool)
2988 {
2989 svn_fs_t *fs;
2990 svn_fs_txn_t *txn;
2991 svn_fs_root_t *txn_root;
2992 svn_revnum_t rev;
2993 apr_pool_t *subpool = svn_pool_create(pool);
2994
2995 /* Prepare a filesystem. */
2996 SVN_ERR(svn_test__create_fs(&fs, "test-repo-check-old-revisions",
2997 opts, pool));
2998
2999 /* Commit a greek tree. */
3000 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
3001 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
3002 SVN_ERR(svn_test__create_greek_tree(txn_root, subpool));
3003 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, subpool));
3004 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(rev));
3005 svn_pool_clear(subpool);
3006
3007 /* Modify and commit iota a few times, then test to see if we can
3008 retrieve all the committed revisions. */
3009 {
3010 /* right-side numbers match revision numbers */
3011 #define iota_contents_1 "This is the file 'iota'.\n"
3012
3013 /* Add a char to the front. */
3014 #define iota_contents_2 "XThis is the file 'iota'.\n"
3015
3016 /* Add a char to the end. */
3017 #define iota_contents_3 "XThis is the file 'iota'.\nX"
3018
3019 /* Add a couple of chars in the middle. */
3020 #define iota_contents_4 "XThis is the X file 'iota'.\nX"
3021
3022 /* Randomly add and delete chars all over. */
3023 #define iota_contents_5 \
3024 "XTYhQis is ACK, PHHHT! no longer 'ioZZZZZta'.blarf\nbye"
3025
3026 /* Reassure iota that it will live for quite some time. */
3027 #define iota_contents_6 "Matthew 5:18 (Revised Standard Version) --\n\
3028 For truly, I say to you, till heaven and earth pass away, not an iota,\n\
3029 not a dot, will pass from the law until all is accomplished."
3030
3031 /* Revert to the original contents. */
3032 #define iota_contents_7 "This is the file 'iota'.\n"
3033
3034 /* Revision 2. */
3035 SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, subpool));
3036 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
3037 SVN_ERR(svn_test__set_file_contents
3038 (txn_root, "iota", iota_contents_2, subpool));
3039 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, subpool));
3040 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(rev));
3041 svn_pool_clear(subpool);
3042
3043 /* Revision 3. */
3044 SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, subpool));
3045 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
3046 SVN_ERR(svn_test__set_file_contents
3047 (txn_root, "iota", iota_contents_3, subpool));
3048 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, subpool));
3049 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(rev));
3050 svn_pool_clear(subpool);
3051
3052 /* Revision 4. */
3053 SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, subpool));
3054 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
3055 SVN_ERR(svn_test__set_file_contents
3056 (txn_root, "iota", iota_contents_4, subpool));
3057 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, subpool));
3058 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(rev));
3059 svn_pool_clear(subpool);
3060
3061 /* Revision 5. */
3062 SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, subpool));
3063 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
3064 SVN_ERR(svn_test__set_file_contents
3065 (txn_root, "iota", iota_contents_5, subpool));
3066 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, subpool));
3067 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(rev));
3068 svn_pool_clear(subpool);
3069
3070 /* Revision 6. */
3071 SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, subpool));
3072 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
3073 SVN_ERR(svn_test__set_file_contents
3074 (txn_root, "iota", iota_contents_6, subpool));
3075 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, subpool));
3076 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(rev));
3077 svn_pool_clear(subpool);
3078
3079 /* Revision 7. */
3080 SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, subpool));
3081 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
3082 SVN_ERR(svn_test__set_file_contents
3083 (txn_root, "iota", iota_contents_7, subpool));
3084 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, subpool));
3085 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(rev));
3086 svn_pool_clear(subpool);
3087
3088 /** Now check the full Greek Tree in all of those revisions,
3089 adjusting `iota' for each one. ***/
3090
3091 /* Validate revision 1. */
3092 {
3093 svn_fs_root_t *root;
3094 static svn_test__tree_entry_t expected_entries[] = {
3095 /* path, contents (0 = dir) */
3096 { "iota", iota_contents_1 },
3097 { "A", 0 },
3098 { "A/mu", "This is the file 'mu'.\n" },
3099 { "A/B", 0 },
3100 { "A/B/lambda", "This is the file 'lambda'.\n" },
3101 { "A/B/E", 0 },
3102 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3103 { "A/B/E/beta", "This is the file 'beta'.\n" },
3104 { "A/B/F", 0 },
3105 { "A/C", 0 },
3106 { "A/D", 0 },
3107 { "A/D/gamma", "This is the file 'gamma'.\n" },
3108 { "A/D/G", 0 },
3109 { "A/D/G/pi", "This is the file 'pi'.\n" },
3110 { "A/D/G/rho", "This is the file 'rho'.\n" },
3111 { "A/D/G/tau", "This is the file 'tau'.\n" },
3112 { "A/D/H", 0 },
3113 { "A/D/H/chi", "This is the file 'chi'.\n" },
3114 { "A/D/H/psi", "This is the file 'psi'.\n" },
3115 { "A/D/H/omega", "This is the file 'omega'.\n" }
3116 };
3117
3118 SVN_ERR(svn_fs_revision_root(&root, fs, 1, pool));
3119 SVN_ERR(svn_test__validate_tree(root, expected_entries, 20, pool));
3120 }
3121
3122 /* Validate revision 2. */
3123 {
3124 svn_fs_root_t *root;
3125 static svn_test__tree_entry_t expected_entries[] = {
3126 /* path, contents (0 = dir) */
3127 { "iota", iota_contents_2 },
3128 { "A", 0 },
3129 { "A/mu", "This is the file 'mu'.\n" },
3130 { "A/B", 0 },
3131 { "A/B/lambda", "This is the file 'lambda'.\n" },
3132 { "A/B/E", 0 },
3133 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3134 { "A/B/E/beta", "This is the file 'beta'.\n" },
3135 { "A/B/F", 0 },
3136 { "A/C", 0 },
3137 { "A/D", 0 },
3138 { "A/D/gamma", "This is the file 'gamma'.\n" },
3139 { "A/D/G", 0 },
3140 { "A/D/G/pi", "This is the file 'pi'.\n" },
3141 { "A/D/G/rho", "This is the file 'rho'.\n" },
3142 { "A/D/G/tau", "This is the file 'tau'.\n" },
3143 { "A/D/H", 0 },
3144 { "A/D/H/chi", "This is the file 'chi'.\n" },
3145 { "A/D/H/psi", "This is the file 'psi'.\n" },
3146 { "A/D/H/omega", "This is the file 'omega'.\n" }
3147 };
3148
3149 SVN_ERR(svn_fs_revision_root(&root, fs, 2, pool));
3150 SVN_ERR(svn_test__validate_tree(root, expected_entries, 20, pool));
3151 }
3152
3153 /* Validate revision 3. */
3154 {
3155 svn_fs_root_t *root;
3156 static svn_test__tree_entry_t expected_entries[] = {
3157 /* path, contents (0 = dir) */
3158 { "iota", iota_contents_3 },
3159 { "A", 0 },
3160 { "A/mu", "This is the file 'mu'.\n" },
3161 { "A/B", 0 },
3162 { "A/B/lambda", "This is the file 'lambda'.\n" },
3163 { "A/B/E", 0 },
3164 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3165 { "A/B/E/beta", "This is the file 'beta'.\n" },
3166 { "A/B/F", 0 },
3167 { "A/C", 0 },
3168 { "A/D", 0 },
3169 { "A/D/gamma", "This is the file 'gamma'.\n" },
3170 { "A/D/G", 0 },
3171 { "A/D/G/pi", "This is the file 'pi'.\n" },
3172 { "A/D/G/rho", "This is the file 'rho'.\n" },
3173 { "A/D/G/tau", "This is the file 'tau'.\n" },
3174 { "A/D/H", 0 },
3175 { "A/D/H/chi", "This is the file 'chi'.\n" },
3176 { "A/D/H/psi", "This is the file 'psi'.\n" },
3177 { "A/D/H/omega", "This is the file 'omega'.\n" }
3178 };
3179
3180 SVN_ERR(svn_fs_revision_root(&root, fs, 3, pool));
3181 SVN_ERR(svn_test__validate_tree(root, expected_entries, 20, pool));
3182 }
3183
3184 /* Validate revision 4. */
3185 {
3186 svn_fs_root_t *root;
3187 static svn_test__tree_entry_t expected_entries[] = {
3188 /* path, contents (0 = dir) */
3189 { "iota", iota_contents_4 },
3190 { "A", 0 },
3191 { "A/mu", "This is the file 'mu'.\n" },
3192 { "A/B", 0 },
3193 { "A/B/lambda", "This is the file 'lambda'.\n" },
3194 { "A/B/E", 0 },
3195 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3196 { "A/B/E/beta", "This is the file 'beta'.\n" },
3197 { "A/B/F", 0 },
3198 { "A/C", 0 },
3199 { "A/D", 0 },
3200 { "A/D/gamma", "This is the file 'gamma'.\n" },
3201 { "A/D/G", 0 },
3202 { "A/D/G/pi", "This is the file 'pi'.\n" },
3203 { "A/D/G/rho", "This is the file 'rho'.\n" },
3204 { "A/D/G/tau", "This is the file 'tau'.\n" },
3205 { "A/D/H", 0 },
3206 { "A/D/H/chi", "This is the file 'chi'.\n" },
3207 { "A/D/H/psi", "This is the file 'psi'.\n" },
3208 { "A/D/H/omega", "This is the file 'omega'.\n" }
3209 };
3210
3211 SVN_ERR(svn_fs_revision_root(&root, fs, 4, pool));
3212 SVN_ERR(svn_test__validate_tree(root, expected_entries, 20, pool));
3213 }
3214
3215 /* Validate revision 5. */
3216 {
3217 svn_fs_root_t *root;
3218 static svn_test__tree_entry_t expected_entries[] = {
3219 /* path, contents (0 = dir) */
3220 { "iota", iota_contents_5 },
3221 { "A", 0 },
3222 { "A/mu", "This is the file 'mu'.\n" },
3223 { "A/B", 0 },
3224 { "A/B/lambda", "This is the file 'lambda'.\n" },
3225 { "A/B/E", 0 },
3226 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3227 { "A/B/E/beta", "This is the file 'beta'.\n" },
3228 { "A/B/F", 0 },
3229 { "A/C", 0 },
3230 { "A/D", 0 },
3231 { "A/D/G", 0 },
3232 { "A/D/gamma", "This is the file 'gamma'.\n" },
3233 { "A/D/G/pi", "This is the file 'pi'.\n" },
3234 { "A/D/G/rho", "This is the file 'rho'.\n" },
3235 { "A/D/G/tau", "This is the file 'tau'.\n" },
3236 { "A/D/H", 0 },
3237 { "A/D/H/chi", "This is the file 'chi'.\n" },
3238 { "A/D/H/psi", "This is the file 'psi'.\n" },
3239 { "A/D/H/omega", "This is the file 'omega'.\n" }
3240 };
3241
3242 SVN_ERR(svn_fs_revision_root(&root, fs, 5, pool));
3243 SVN_ERR(svn_test__validate_tree(root, expected_entries, 20, pool));
3244 }
3245
3246 /* Validate revision 6. */
3247 {
3248 svn_fs_root_t *root;
3249 static svn_test__tree_entry_t expected_entries[] = {
3250 /* path, contents (0 = dir) */
3251 { "iota", iota_contents_6 },
3252 { "A", 0 },
3253 { "A/mu", "This is the file 'mu'.\n" },
3254 { "A/B", 0 },
3255 { "A/B/lambda", "This is the file 'lambda'.\n" },
3256 { "A/B/E", 0 },
3257 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3258 { "A/B/E/beta", "This is the file 'beta'.\n" },
3259 { "A/B/F", 0 },
3260 { "A/C", 0 },
3261 { "A/D", 0 },
3262 { "A/D/gamma", "This is the file 'gamma'.\n" },
3263 { "A/D/G", 0 },
3264 { "A/D/G/pi", "This is the file 'pi'.\n" },
3265 { "A/D/G/rho", "This is the file 'rho'.\n" },
3266 { "A/D/G/tau", "This is the file 'tau'.\n" },
3267 { "A/D/H", 0 },
3268 { "A/D/H/chi", "This is the file 'chi'.\n" },
3269 { "A/D/H/psi", "This is the file 'psi'.\n" },
3270 { "A/D/H/omega", "This is the file 'omega'.\n" }
3271 };
3272
3273 SVN_ERR(svn_fs_revision_root(&root, fs, 6, pool));
3274 SVN_ERR(svn_test__validate_tree(root, expected_entries, 20, pool));
3275 }
3276
3277 /* Validate revision 7. */
3278 {
3279 svn_fs_root_t *root;
3280 static svn_test__tree_entry_t expected_entries[] = {
3281 /* path, contents (0 = dir) */
3282 { "iota", iota_contents_7 },
3283 { "A", 0 },
3284 { "A/mu", "This is the file 'mu'.\n" },
3285 { "A/B", 0 },
3286 { "A/B/lambda", "This is the file 'lambda'.\n" },
3287 { "A/B/E", 0 },
3288 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3289 { "A/B/E/beta", "This is the file 'beta'.\n" },
3290 { "A/B/F", 0 },
3291 { "A/C", 0 },
3292 { "A/D", 0 },
3293 { "A/D/gamma", "This is the file 'gamma'.\n" },
3294 { "A/D/G", 0 },
3295 { "A/D/G/pi", "This is the file 'pi'.\n" },
3296 { "A/D/G/rho", "This is the file 'rho'.\n" },
3297 { "A/D/G/tau", "This is the file 'tau'.\n" },
3298 { "A/D/H", 0 },
3299 { "A/D/H/chi", "This is the file 'chi'.\n" },
3300 { "A/D/H/psi", "This is the file 'psi'.\n" },
3301 { "A/D/H/omega", "This is the file 'omega'.\n" }
3302 };
3303
3304 SVN_ERR(svn_fs_revision_root(&root, fs, 7, pool));
3305 SVN_ERR(svn_test__validate_tree(root, expected_entries, 20, pool));
3306 }
3307 }
3308
3309 svn_pool_destroy(subpool);
3310 return SVN_NO_ERROR;
3311 }
3312
3313
3314 /* For each revision R in FS, from 0 to MAX_REV, check that it
3315 matches the tree in EXPECTED_TREES[R]. Use POOL for any
3316 allocations. This is a helper function for check_all_revisions. */
3317 static svn_error_t *
validate_revisions(svn_fs_t * fs,svn_test__tree_t * expected_trees,svn_revnum_t max_rev,apr_pool_t * pool)3318 validate_revisions(svn_fs_t *fs,
3319 svn_test__tree_t *expected_trees,
3320 svn_revnum_t max_rev,
3321 apr_pool_t *pool)
3322 {
3323 svn_fs_root_t *revision_root;
3324 svn_revnum_t i;
3325 svn_error_t *err;
3326 apr_pool_t *subpool = svn_pool_create(pool);
3327
3328 /* Validate all revisions up to the current one. */
3329 for (i = 0; i <= max_rev; i++)
3330 {
3331 SVN_ERR(svn_fs_revision_root(&revision_root, fs,
3332 (svn_revnum_t)i, subpool));
3333 err = svn_test__validate_tree(revision_root,
3334 expected_trees[i].entries,
3335 expected_trees[i].num_entries,
3336 subpool);
3337 if (err)
3338 return svn_error_createf
3339 (SVN_ERR_FS_GENERAL, err,
3340 "Error validating revision %ld (youngest is %ld)", i, max_rev);
3341 svn_pool_clear(subpool);
3342 }
3343
3344 svn_pool_destroy(subpool);
3345 return SVN_NO_ERROR;
3346 }
3347
3348
3349 static svn_error_t *
check_all_revisions(const svn_test_opts_t * opts,apr_pool_t * pool)3350 check_all_revisions(const svn_test_opts_t *opts,
3351 apr_pool_t *pool)
3352 {
3353 svn_fs_t *fs;
3354 svn_fs_txn_t *txn;
3355 svn_fs_root_t *txn_root;
3356 svn_revnum_t youngest_rev;
3357 svn_test__tree_t expected_trees[5]; /* one tree per commit, please */
3358 svn_revnum_t revision_count = 0;
3359 apr_pool_t *subpool = svn_pool_create(pool);
3360
3361 /* Create a filesystem and repository. */
3362 SVN_ERR(svn_test__create_fs(&fs, "test-repo-check-all-revisions",
3363 opts, pool));
3364
3365 /***********************************************************************/
3366 /* REVISION 0 */
3367 /***********************************************************************/
3368 {
3369 expected_trees[revision_count].num_entries = 0;
3370 expected_trees[revision_count].entries = 0;
3371 SVN_ERR(validate_revisions(fs, expected_trees, revision_count, subpool));
3372 revision_count++;
3373 }
3374 svn_pool_clear(subpool);
3375
3376 /* Create and commit the greek tree. */
3377 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
3378 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
3379 SVN_ERR(svn_test__create_greek_tree(txn_root, subpool));
3380 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
3381 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
3382
3383 /***********************************************************************/
3384 /* REVISION 1 */
3385 /***********************************************************************/
3386 {
3387 static svn_test__tree_entry_t expected_entries[] = {
3388 /* path, contents (0 = dir) */
3389 { "iota", "This is the file 'iota'.\n" },
3390 { "A", 0 },
3391 { "A/mu", "This is the file 'mu'.\n" },
3392 { "A/B", 0 },
3393 { "A/B/lambda", "This is the file 'lambda'.\n" },
3394 { "A/B/E", 0 },
3395 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3396 { "A/B/E/beta", "This is the file 'beta'.\n" },
3397 { "A/B/F", 0 },
3398 { "A/C", 0 },
3399 { "A/D", 0 },
3400 { "A/D/gamma", "This is the file 'gamma'.\n" },
3401 { "A/D/G", 0 },
3402 { "A/D/G/pi", "This is the file 'pi'.\n" },
3403 { "A/D/G/rho", "This is the file 'rho'.\n" },
3404 { "A/D/G/tau", "This is the file 'tau'.\n" },
3405 { "A/D/H", 0 },
3406 { "A/D/H/chi", "This is the file 'chi'.\n" },
3407 { "A/D/H/psi", "This is the file 'psi'.\n" },
3408 { "A/D/H/omega", "This is the file 'omega'.\n" }
3409 };
3410 expected_trees[revision_count].entries = expected_entries;
3411 expected_trees[revision_count].num_entries = 20;
3412 SVN_ERR(validate_revisions(fs, expected_trees, revision_count, subpool));
3413 revision_count++;
3414 }
3415 svn_pool_clear(subpool);
3416
3417 /* Make a new txn based on the youngest revision, make some changes,
3418 and commit those changes (which makes a new youngest
3419 revision). */
3420 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
3421 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
3422 {
3423 static svn_test__txn_script_command_t script_entries[] = {
3424 { 'a', "A/delta", "This is the file 'delta'.\n" },
3425 { 'a', "A/epsilon", "This is the file 'epsilon'.\n" },
3426 { 'a', "A/B/Z", 0 },
3427 { 'a', "A/B/Z/zeta", "This is the file 'zeta'.\n" },
3428 { 'd', "A/C", 0 },
3429 { 'd', "A/mu", "" },
3430 { 'd', "A/D/G/tau", "" },
3431 { 'd', "A/D/H/omega", "" },
3432 { 'e', "iota", "Changed file 'iota'.\n" },
3433 { 'e', "A/D/G/rho", "Changed file 'rho'.\n" }
3434 };
3435 SVN_ERR(svn_test__txn_script_exec(txn_root, script_entries, 10,
3436 subpool));
3437 }
3438 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
3439 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
3440
3441 /***********************************************************************/
3442 /* REVISION 2 */
3443 /***********************************************************************/
3444 {
3445 static svn_test__tree_entry_t expected_entries[] = {
3446 /* path, contents (0 = dir) */
3447 { "iota", "Changed file 'iota'.\n" },
3448 { "A", 0 },
3449 { "A/delta", "This is the file 'delta'.\n" },
3450 { "A/epsilon", "This is the file 'epsilon'.\n" },
3451 { "A/B", 0 },
3452 { "A/B/lambda", "This is the file 'lambda'.\n" },
3453 { "A/B/E", 0 },
3454 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3455 { "A/B/E/beta", "This is the file 'beta'.\n" },
3456 { "A/B/F", 0 },
3457 { "A/B/Z", 0 },
3458 { "A/B/Z/zeta", "This is the file 'zeta'.\n" },
3459 { "A/D", 0 },
3460 { "A/D/gamma", "This is the file 'gamma'.\n" },
3461 { "A/D/G", 0 },
3462 { "A/D/G/pi", "This is the file 'pi'.\n" },
3463 { "A/D/G/rho", "Changed file 'rho'.\n" },
3464 { "A/D/H", 0 },
3465 { "A/D/H/chi", "This is the file 'chi'.\n" },
3466 { "A/D/H/psi", "This is the file 'psi'.\n" }
3467 };
3468 expected_trees[revision_count].entries = expected_entries;
3469 expected_trees[revision_count].num_entries = 20;
3470 SVN_ERR(validate_revisions(fs, expected_trees, revision_count, subpool));
3471 revision_count++;
3472 }
3473 svn_pool_clear(subpool);
3474
3475 /* Make a new txn based on the youngest revision, make some changes,
3476 and commit those changes (which makes a new youngest
3477 revision). */
3478 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
3479 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
3480 {
3481 static svn_test__txn_script_command_t script_entries[] = {
3482 { 'a', "A/mu", "Re-added file 'mu'.\n" },
3483 { 'a', "A/D/H/omega", 0 }, /* re-add omega as directory! */
3484 { 'd', "iota", "" },
3485 { 'e', "A/delta", "This is the file 'delta'.\nLine 2.\n" }
3486 };
3487 SVN_ERR(svn_test__txn_script_exec(txn_root, script_entries, 4,
3488 subpool));
3489 }
3490 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
3491 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
3492
3493 /***********************************************************************/
3494 /* REVISION 3 */
3495 /***********************************************************************/
3496 {
3497 static svn_test__tree_entry_t expected_entries[] = {
3498 /* path, contents (0 = dir) */
3499 { "A", 0 },
3500 { "A/delta", "This is the file 'delta'.\nLine 2.\n" },
3501 { "A/epsilon", "This is the file 'epsilon'.\n" },
3502 { "A/mu", "Re-added file 'mu'.\n" },
3503 { "A/B", 0 },
3504 { "A/B/lambda", "This is the file 'lambda'.\n" },
3505 { "A/B/E", 0 },
3506 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3507 { "A/B/E/beta", "This is the file 'beta'.\n" },
3508 { "A/B/F", 0 },
3509 { "A/B/Z", 0 },
3510 { "A/B/Z/zeta", "This is the file 'zeta'.\n" },
3511 { "A/D", 0 },
3512 { "A/D/gamma", "This is the file 'gamma'.\n" },
3513 { "A/D/G", 0 },
3514 { "A/D/G/pi", "This is the file 'pi'.\n" },
3515 { "A/D/G/rho", "Changed file 'rho'.\n" },
3516 { "A/D/H", 0 },
3517 { "A/D/H/chi", "This is the file 'chi'.\n" },
3518 { "A/D/H/psi", "This is the file 'psi'.\n" },
3519 { "A/D/H/omega", 0 }
3520 };
3521 expected_trees[revision_count].entries = expected_entries;
3522 expected_trees[revision_count].num_entries = 21;
3523 SVN_ERR(validate_revisions(fs, expected_trees, revision_count, subpool));
3524 revision_count++;
3525 }
3526 svn_pool_clear(subpool);
3527
3528 /* Make a new txn based on the youngest revision, make some changes,
3529 and commit those changes (which makes a new youngest
3530 revision). */
3531 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
3532 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
3533 {
3534 static svn_test__txn_script_command_t script_entries[] = {
3535 { 'c', "A/D/G", "A/D/G2" },
3536 { 'c', "A/epsilon", "A/B/epsilon" },
3537 };
3538 SVN_ERR(svn_test__txn_script_exec(txn_root, script_entries, 2, subpool));
3539 }
3540 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
3541 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
3542
3543 /***********************************************************************/
3544 /* REVISION 4 */
3545 /***********************************************************************/
3546 {
3547 static svn_test__tree_entry_t expected_entries[] = {
3548 /* path, contents (0 = dir) */
3549 { "A", 0 },
3550 { "A/delta", "This is the file 'delta'.\nLine 2.\n" },
3551 { "A/epsilon", "This is the file 'epsilon'.\n" },
3552 { "A/mu", "Re-added file 'mu'.\n" },
3553 { "A/B", 0 },
3554 { "A/B/epsilon", "This is the file 'epsilon'.\n" },
3555 { "A/B/lambda", "This is the file 'lambda'.\n" },
3556 { "A/B/E", 0 },
3557 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3558 { "A/B/E/beta", "This is the file 'beta'.\n" },
3559 { "A/B/F", 0 },
3560 { "A/B/Z", 0 },
3561 { "A/B/Z/zeta", "This is the file 'zeta'.\n" },
3562 { "A/D", 0 },
3563 { "A/D/gamma", "This is the file 'gamma'.\n" },
3564 { "A/D/G", 0 },
3565 { "A/D/G/pi", "This is the file 'pi'.\n" },
3566 { "A/D/G/rho", "Changed file 'rho'.\n" },
3567 { "A/D/G2", 0 },
3568 { "A/D/G2/pi", "This is the file 'pi'.\n" },
3569 { "A/D/G2/rho", "Changed file 'rho'.\n" },
3570 { "A/D/H", 0 },
3571 { "A/D/H/chi", "This is the file 'chi'.\n" },
3572 { "A/D/H/psi", "This is the file 'psi'.\n" },
3573 { "A/D/H/omega", 0 }
3574 };
3575 expected_trees[revision_count].entries = expected_entries;
3576 expected_trees[revision_count].num_entries = 25;
3577 SVN_ERR(validate_revisions(fs, expected_trees, revision_count, subpool));
3578 revision_count++;
3579 }
3580 svn_pool_destroy(subpool);
3581
3582 return SVN_NO_ERROR;
3583 }
3584
3585
3586 /* Helper function for large_file_integrity(). Given a ROOT and PATH
3587 to a file, set *CHECKSUM to the checksum of kind CHECKSUM_KIND for the
3588 contents of the file. */
3589 static svn_error_t *
get_file_checksum(svn_checksum_t ** checksum,svn_checksum_kind_t checksum_kind,svn_fs_root_t * root,const char * path,apr_pool_t * pool)3590 get_file_checksum(svn_checksum_t **checksum,
3591 svn_checksum_kind_t checksum_kind,
3592 svn_fs_root_t *root,
3593 const char *path,
3594 apr_pool_t *pool)
3595 {
3596 svn_stream_t *stream;
3597
3598 /* Get a stream for the file contents. */
3599 SVN_ERR(svn_fs_file_contents(&stream, root, path, pool));
3600 SVN_ERR(svn_stream_contents_checksum(checksum, stream, checksum_kind,
3601 pool, pool));
3602
3603 return SVN_NO_ERROR;
3604 }
3605
3606
3607 /* Return a pseudo-random number in the range [0,SCALAR) i.e. return
3608 a number N such that 0 <= N < SCALAR */
my_rand(apr_uint64_t scalar,apr_uint32_t * seed)3609 static int my_rand(apr_uint64_t scalar, apr_uint32_t *seed)
3610 {
3611 static const apr_uint32_t TEST_RAND_MAX = 0xffffffffUL;
3612 /* Assumes TEST_RAND_MAX+1 can be exactly represented in a double */
3613 apr_uint32_t r = svn_test_rand(seed);
3614 return (int)(((double)r
3615 / ((double)TEST_RAND_MAX+1.0))
3616 * (double)scalar);
3617 }
3618
3619
3620 /* Put pseudo-random bytes in buffer BUF (which is LEN bytes long).
3621 If FULL is TRUE, simply replace every byte in BUF with a
3622 pseudo-random byte, else, replace a pseudo-random collection of
3623 bytes with pseudo-random data. */
3624 static void
random_data_to_buffer(char * buf,apr_size_t buf_len,svn_boolean_t full,apr_uint32_t * seed)3625 random_data_to_buffer(char *buf,
3626 apr_size_t buf_len,
3627 svn_boolean_t full,
3628 apr_uint32_t *seed)
3629 {
3630 apr_size_t i;
3631 apr_size_t num_bytes;
3632 apr_size_t offset;
3633
3634 int ds_off = 0;
3635 const char *dataset = "0123456789";
3636 apr_size_t dataset_size = strlen(dataset);
3637
3638 if (full)
3639 {
3640 for (i = 0; i < buf_len; i++)
3641 {
3642 ds_off = my_rand(dataset_size, seed);
3643 buf[i] = dataset[ds_off];
3644 }
3645
3646 return;
3647 }
3648
3649 num_bytes = my_rand(buf_len / 100, seed) + 1;
3650 for (i = 0; i < num_bytes; i++)
3651 {
3652 offset = my_rand(buf_len - 1, seed);
3653 ds_off = my_rand(dataset_size, seed);
3654 buf[offset] = dataset[ds_off];
3655 }
3656
3657 return;
3658 }
3659
3660
3661 static svn_error_t *
file_integrity_helper(apr_size_t filesize,apr_uint32_t * seed,const svn_test_opts_t * opts,const char * fs_name,apr_pool_t * pool)3662 file_integrity_helper(apr_size_t filesize, apr_uint32_t *seed,
3663 const svn_test_opts_t *opts, const char *fs_name,
3664 apr_pool_t *pool)
3665 {
3666 svn_fs_t *fs;
3667 svn_fs_txn_t *txn;
3668 svn_fs_root_t *txn_root, *rev_root;
3669 svn_revnum_t youngest_rev = 0;
3670 apr_pool_t *subpool = svn_pool_create(pool);
3671 svn_string_t contents;
3672 char *content_buffer;
3673 svn_checksum_t *checksum;
3674 svn_checksum_kind_t checksum_kind = svn_checksum_md5;
3675 svn_checksum_t *checksum_list[100];
3676 svn_txdelta_window_handler_t wh_func;
3677 void *wh_baton;
3678 svn_revnum_t j;
3679
3680 /* Create a filesystem and repository. */
3681 SVN_ERR(svn_test__create_fs(&fs, fs_name, opts, pool));
3682
3683 /* Set up our file contents string buffer. */
3684 content_buffer = apr_palloc(pool, filesize);
3685
3686 contents.data = content_buffer;
3687 contents.len = filesize;
3688
3689 /* THE PLAN:
3690
3691 The plan here is simple. We have a very large file (FILESIZE
3692 bytes) that we initialize with pseudo-random data and commit.
3693 Then we make pseudo-random modifications to that file's contents,
3694 committing after each mod. Prior to each commit, we generate an
3695 MD5 checksum for the contents of the file, storing each of those
3696 checksums in an array. After we've made a whole bunch of edits
3697 and commits, we'll re-check that file's contents as of each
3698 revision in the repository, recalculate a checksum for those
3699 contents, and make sure the "before" and "after" checksums
3700 match. */
3701
3702 /* Create a big, ugly, pseudo-random-filled file and commit it. */
3703 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
3704 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
3705 SVN_ERR(svn_fs_make_file(txn_root, "bigfile", subpool));
3706 random_data_to_buffer(content_buffer, filesize, TRUE, seed);
3707 SVN_ERR(svn_checksum(&checksum, checksum_kind, contents.data, contents.len,
3708 pool));
3709 SVN_ERR(svn_fs_apply_textdelta
3710 (&wh_func, &wh_baton, txn_root, "bigfile", NULL, NULL, subpool));
3711 SVN_ERR(svn_txdelta_send_string(&contents, wh_func, wh_baton, subpool));
3712 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
3713 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
3714 SVN_ERR(svn_fs_deltify_revision(fs, youngest_rev, subpool));
3715 checksum_list[youngest_rev] = checksum;
3716 svn_pool_clear(subpool);
3717
3718 /* Now, let's make some edits to the beginning of our file, and
3719 commit those. */
3720 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
3721 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
3722 random_data_to_buffer(content_buffer, 20, TRUE, seed);
3723 SVN_ERR(svn_checksum(&checksum, checksum_kind, contents.data, contents.len,
3724 pool));
3725 SVN_ERR(svn_fs_apply_textdelta
3726 (&wh_func, &wh_baton, txn_root, "bigfile", NULL, NULL, subpool));
3727 SVN_ERR(svn_txdelta_send_string(&contents, wh_func, wh_baton, subpool));
3728 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
3729 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
3730 SVN_ERR(svn_fs_deltify_revision(fs, youngest_rev, subpool));
3731 checksum_list[youngest_rev] = checksum;
3732 svn_pool_clear(subpool);
3733
3734 /* Now, let's make some edits to the end of our file. */
3735 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
3736 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
3737 random_data_to_buffer(content_buffer + (filesize - 20), 20, TRUE, seed);
3738 SVN_ERR(svn_checksum(&checksum, checksum_kind, contents.data, contents.len,
3739 pool));
3740 SVN_ERR(svn_fs_apply_textdelta
3741 (&wh_func, &wh_baton, txn_root, "bigfile", NULL, NULL, subpool));
3742 SVN_ERR(svn_txdelta_send_string(&contents, wh_func, wh_baton, subpool));
3743 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
3744 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
3745 SVN_ERR(svn_fs_deltify_revision(fs, youngest_rev, subpool));
3746 checksum_list[youngest_rev] = checksum;
3747 svn_pool_clear(subpool);
3748
3749 /* How about some edits to both the beginning and the end of the
3750 file? */
3751 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
3752 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
3753 random_data_to_buffer(content_buffer, 20, TRUE, seed);
3754 random_data_to_buffer(content_buffer + (filesize - 20), 20, TRUE, seed);
3755 SVN_ERR(svn_checksum(&checksum, checksum_kind, contents.data, contents.len,
3756 pool));
3757 SVN_ERR(svn_fs_apply_textdelta
3758 (&wh_func, &wh_baton, txn_root, "bigfile", NULL, NULL, subpool));
3759 SVN_ERR(svn_txdelta_send_string(&contents, wh_func, wh_baton, subpool));
3760 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
3761 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
3762 SVN_ERR(svn_fs_deltify_revision(fs, youngest_rev, subpool));
3763 checksum_list[youngest_rev] = checksum;
3764 svn_pool_clear(subpool);
3765
3766 /* Alright, now we're just going to go crazy. Let's make many more
3767 edits -- pseudo-random numbers and offsets of bytes changed to
3768 more pseudo-random values. */
3769 for (j = youngest_rev; j < 30; j = youngest_rev)
3770 {
3771 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
3772 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
3773 random_data_to_buffer(content_buffer, filesize, FALSE, seed);
3774 SVN_ERR(svn_checksum(&checksum, checksum_kind, contents.data,
3775 contents.len, pool));
3776 SVN_ERR(svn_fs_apply_textdelta(&wh_func, &wh_baton, txn_root,
3777 "bigfile", NULL, NULL, subpool));
3778 SVN_ERR(svn_txdelta_send_string
3779 (&contents, wh_func, wh_baton, subpool));
3780 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
3781 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
3782 SVN_ERR(svn_fs_deltify_revision(fs, youngest_rev, subpool));
3783 checksum_list[youngest_rev] = checksum;
3784 svn_pool_clear(subpool);
3785 }
3786
3787 /* Now, calculate an MD5 digest for the contents of our big ugly
3788 file in each revision currently in existence, and make the sure
3789 the checksum matches the checksum of the data prior to its
3790 commit. */
3791 for (j = youngest_rev; j > 0; j--)
3792 {
3793 SVN_ERR(svn_fs_revision_root(&rev_root, fs, j, subpool));
3794 SVN_ERR(get_file_checksum(&checksum, checksum_kind, rev_root, "bigfile",
3795 subpool));
3796 if (!svn_checksum_match(checksum, checksum_list[j]))
3797 return svn_error_createf
3798 (SVN_ERR_FS_GENERAL, NULL,
3799 "verify-checksum: checksum mismatch, revision %ld:\n"
3800 " expected: %s\n"
3801 " actual: %s\n", j,
3802 svn_checksum_to_cstring(checksum_list[j], pool),
3803 svn_checksum_to_cstring(checksum, pool));
3804
3805 svn_pool_clear(subpool);
3806 }
3807
3808 svn_pool_destroy(subpool);
3809 return SVN_NO_ERROR;
3810 }
3811
3812
3813 static svn_error_t *
small_file_integrity(const svn_test_opts_t * opts,apr_pool_t * pool)3814 small_file_integrity(const svn_test_opts_t *opts,
3815 apr_pool_t *pool)
3816 {
3817 apr_uint32_t seed = (apr_uint32_t) apr_time_now();
3818
3819 /* Just use a really small file size... */
3820 return file_integrity_helper(20, &seed, opts,
3821 "test-repo-small-file-integrity", pool);
3822 }
3823
3824
3825 static svn_error_t *
almostmedium_file_integrity(const svn_test_opts_t * opts,apr_pool_t * pool)3826 almostmedium_file_integrity(const svn_test_opts_t *opts,
3827 apr_pool_t *pool)
3828 {
3829 apr_uint32_t seed = (apr_uint32_t) apr_time_now();
3830
3831 return file_integrity_helper(SVN_DELTA_WINDOW_SIZE - 1, &seed, opts,
3832 "test-repo-almostmedium-file-integrity", pool);
3833 }
3834
3835
3836 static svn_error_t *
medium_file_integrity(const svn_test_opts_t * opts,apr_pool_t * pool)3837 medium_file_integrity(const svn_test_opts_t *opts,
3838 apr_pool_t *pool)
3839 {
3840 apr_uint32_t seed = (apr_uint32_t) apr_time_now();
3841
3842 /* Being no larger than the standard delta window size affects
3843 deltification internally, so test that. */
3844 return file_integrity_helper(SVN_DELTA_WINDOW_SIZE, &seed, opts,
3845 "test-repo-medium-file-integrity", pool);
3846 }
3847
3848
3849 static svn_error_t *
large_file_integrity(const svn_test_opts_t * opts,apr_pool_t * pool)3850 large_file_integrity(const svn_test_opts_t *opts,
3851 apr_pool_t *pool)
3852 {
3853 apr_uint32_t seed = (apr_uint32_t) apr_time_now();
3854
3855 /* Being larger than the standard delta window size affects
3856 deltification internally, so test that. */
3857 return file_integrity_helper(SVN_DELTA_WINDOW_SIZE + 1, &seed, opts,
3858 "test-repo-large-file-integrity", pool);
3859 }
3860
3861
3862 static svn_error_t *
check_root_revision(const svn_test_opts_t * opts,apr_pool_t * pool)3863 check_root_revision(const svn_test_opts_t *opts,
3864 apr_pool_t *pool)
3865 {
3866 svn_fs_t *fs;
3867 svn_fs_txn_t *txn;
3868 svn_fs_root_t *txn_root, *rev_root;
3869 svn_revnum_t youngest_rev, test_rev;
3870 apr_pool_t *subpool = svn_pool_create(pool);
3871 int i;
3872
3873 /* Create a filesystem and repository. */
3874 SVN_ERR(svn_test__create_fs(&fs, "test-repo-check-root-revision",
3875 opts, pool));
3876
3877 /* Create and commit the greek tree. */
3878 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
3879 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
3880 SVN_ERR(svn_test__create_greek_tree(txn_root, subpool));
3881 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
3882 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
3883
3884 /* Root node's revision should be the same as YOUNGEST_REV. */
3885 SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, subpool));
3886 SVN_ERR(svn_fs_node_created_rev(&test_rev, rev_root, "", subpool));
3887 if (test_rev != youngest_rev)
3888 return svn_error_createf
3889 (SVN_ERR_FS_GENERAL, NULL,
3890 "Root node in revision %ld has unexpected stored revision %ld",
3891 youngest_rev, test_rev);
3892 svn_pool_clear(subpool);
3893
3894 for (i = 0; i < 10; i++)
3895 {
3896 /* Create and commit the greek tree. */
3897 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
3898 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
3899 SVN_ERR(svn_test__set_file_contents
3900 (txn_root, "iota",
3901 apr_psprintf(subpool, "iota version %d", i + 2), subpool));
3902
3903 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
3904 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
3905
3906 /* Root node's revision should be the same as YOUNGEST_REV. */
3907 SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, subpool));
3908 SVN_ERR(svn_fs_node_created_rev(&test_rev, rev_root, "", subpool));
3909 if (test_rev != youngest_rev)
3910 return svn_error_createf
3911 (SVN_ERR_FS_GENERAL, NULL,
3912 "Root node in revision %ld has unexpected stored revision %ld",
3913 youngest_rev, test_rev);
3914 svn_pool_clear(subpool);
3915 }
3916
3917 svn_pool_destroy(subpool);
3918 return SVN_NO_ERROR;
3919 }
3920
3921
3922 struct node_created_rev_args {
3923 const char *path;
3924 svn_revnum_t rev;
3925 };
3926
3927
3928 static svn_error_t *
verify_path_revs(svn_fs_root_t * root,struct node_created_rev_args * args,int num_path_revs,apr_pool_t * pool)3929 verify_path_revs(svn_fs_root_t *root,
3930 struct node_created_rev_args *args,
3931 int num_path_revs,
3932 apr_pool_t *pool)
3933 {
3934 apr_pool_t *subpool = svn_pool_create(pool);
3935 int i;
3936 svn_revnum_t rev;
3937
3938 for (i = 0; i < num_path_revs; i++)
3939 {
3940 svn_pool_clear(subpool);
3941 SVN_ERR(svn_fs_node_created_rev(&rev, root, args[i].path, subpool));
3942 if (rev != args[i].rev)
3943 return svn_error_createf
3944 (SVN_ERR_FS_GENERAL, NULL,
3945 "verify_path_revs: '%s' has created rev '%ld' "
3946 "(expected '%ld')",
3947 args[i].path, rev, args[i].rev);
3948 }
3949
3950 svn_pool_destroy(subpool);
3951 return SVN_NO_ERROR;
3952 }
3953
3954
3955 static svn_error_t *
test_node_created_rev(const svn_test_opts_t * opts,apr_pool_t * pool)3956 test_node_created_rev(const svn_test_opts_t *opts,
3957 apr_pool_t *pool)
3958 {
3959 apr_pool_t *subpool = svn_pool_create(pool);
3960 svn_fs_t *fs;
3961 svn_fs_txn_t *txn;
3962 svn_fs_root_t *txn_root, *rev_root;
3963 svn_revnum_t youngest_rev = 0;
3964 int i;
3965 struct node_created_rev_args path_revs[21];
3966 const char *greek_paths[21] = {
3967 /* 0 */ "",
3968 /* 1 */ "iota",
3969 /* 2 */ "A",
3970 /* 3 */ "A/mu",
3971 /* 4 */ "A/B",
3972 /* 5 */ "A/B/lambda",
3973 /* 6 */ "A/B/E",
3974 /* 7 */ "A/B/E/alpha",
3975 /* 8 */ "A/B/E/beta",
3976 /* 9 */ "A/B/F",
3977 /* 10 */ "A/C",
3978 /* 11 */ "A/D",
3979 /* 12 */ "A/D/gamma",
3980 /* 13 */ "A/D/G",
3981 /* 14 */ "A/D/G/pi",
3982 /* 15 */ "A/D/G/rho",
3983 /* 16 */ "A/D/G/tau",
3984 /* 17 */ "A/D/H",
3985 /* 18 */ "A/D/H/chi",
3986 /* 19 */ "A/D/H/psi",
3987 /* 20 */ "A/D/H/omega",
3988 };
3989
3990 /* Initialize the paths in our args list. */
3991 for (i = 0; i < 20; i++)
3992 path_revs[i].path = greek_paths[i];
3993
3994 /* Create a filesystem and repository. */
3995 SVN_ERR(svn_test__create_fs(&fs, "test-repo-node-created-rev",
3996 opts, pool));
3997
3998 /* Created the greek tree in revision 1. */
3999 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
4000 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
4001 SVN_ERR(svn_test__create_greek_tree(txn_root, subpool));
4002
4003 /* Now, prior to committing, all these nodes should have an invalid
4004 created rev. After all, the rev has been created yet. Verify
4005 this. */
4006 for (i = 0; i < 20; i++)
4007 path_revs[i].rev = SVN_INVALID_REVNUM;
4008 SVN_ERR(verify_path_revs(txn_root, path_revs, 20, subpool));
4009
4010 /* Now commit the transaction. */
4011 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
4012 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4013
4014 /* Now, we have a new revision, and all paths in it should have a
4015 created rev of 1. Verify this. */
4016 SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, subpool));
4017 for (i = 0; i < 20; i++)
4018 path_revs[i].rev = 1;
4019 SVN_ERR(verify_path_revs(rev_root, path_revs, 20, subpool));
4020
4021 /*** Let's make some changes/commits here and there, and make sure
4022 we can keep this whole created rev thing in good standing. The
4023 general rule here is that prior to commit, mutable things have
4024 an invalid created rev, immutable things have their original
4025 created rev. After the commit, those things which had invalid
4026 created revs in the transaction now have the youngest revision
4027 as their created rev.
4028
4029 ### NOTE: Bubble-up currently affect the created revisions for
4030 directory nodes. I'm not sure if this is the behavior we've
4031 settled on as desired.
4032 */
4033
4034 /*** clear the per-commit pool */
4035 svn_pool_clear(subpool);
4036 /* begin a new transaction */
4037 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
4038 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
4039 /* The created revs on a txn root should be the same as on the rev
4040 root it came from, if we haven't made changes yet. (See issue
4041 #2608.) */
4042 SVN_ERR(verify_path_revs(txn_root, path_revs, 20, subpool));
4043 /* make mods */
4044 SVN_ERR(svn_test__set_file_contents
4045 (txn_root, "iota", "pointless mod here", subpool));
4046 /* verify created revs */
4047 path_revs[0].rev = SVN_INVALID_REVNUM; /* (root) */
4048 path_revs[1].rev = SVN_INVALID_REVNUM; /* iota */
4049 SVN_ERR(verify_path_revs(txn_root, path_revs, 20, subpool));
4050 /* commit transaction */
4051 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
4052 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4053 /* get a revision root for the new revision */
4054 SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, subpool));
4055 /* verify created revs */
4056 path_revs[0].rev = 2; /* (root) */
4057 path_revs[1].rev = 2; /* iota */
4058 SVN_ERR(verify_path_revs(rev_root, path_revs, 20, subpool));
4059
4060 /*** clear the per-commit pool */
4061 svn_pool_clear(subpool);
4062 /* begin a new transaction */
4063 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
4064 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
4065 /* make mods */
4066 SVN_ERR(svn_test__set_file_contents
4067 (txn_root, "A/D/H/omega", "pointless mod here", subpool));
4068 /* verify created revs */
4069 path_revs[0].rev = SVN_INVALID_REVNUM; /* (root) */
4070 path_revs[2].rev = SVN_INVALID_REVNUM; /* A */
4071 path_revs[11].rev = SVN_INVALID_REVNUM; /* D */
4072 path_revs[17].rev = SVN_INVALID_REVNUM; /* H */
4073 path_revs[20].rev = SVN_INVALID_REVNUM; /* omega */
4074 SVN_ERR(verify_path_revs(txn_root, path_revs, 20, subpool));
4075 /* commit transaction */
4076 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
4077 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4078 /* get a revision root for the new revision */
4079 SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, subpool));
4080 /* verify created revs */
4081 path_revs[0].rev = 3; /* (root) */
4082 path_revs[2].rev = 3; /* A */
4083 path_revs[11].rev = 3; /* D */
4084 path_revs[17].rev = 3; /* H */
4085 path_revs[20].rev = 3; /* omega */
4086 SVN_ERR(verify_path_revs(rev_root, path_revs, 20, subpool));
4087
4088 /* Destroy the per-commit subpool. */
4089 svn_pool_destroy(subpool);
4090
4091 return SVN_NO_ERROR;
4092 }
4093
4094
4095 static svn_error_t *
check_related(const svn_test_opts_t * opts,apr_pool_t * pool)4096 check_related(const svn_test_opts_t *opts,
4097 apr_pool_t *pool)
4098 {
4099 apr_pool_t *subpool = svn_pool_create(pool);
4100 svn_fs_t *fs;
4101 svn_fs_txn_t *txn;
4102 svn_fs_root_t *txn_root, *rev_root;
4103 svn_revnum_t youngest_rev = 0;
4104
4105 /* Create a filesystem and repository. */
4106 SVN_ERR(svn_test__create_fs(&fs, "test-repo-check-related",
4107 opts, pool));
4108
4109 /*** Step I: Build up some state in our repository through a series
4110 of commits */
4111
4112 /* Using files because bubble-up complicates the testing. However,
4113 the algorithm itself is ambivalent about what type of node is
4114 being examined.
4115
4116 - New files show up in this order (through time): A,B,C,D,E,F
4117 - Number following filename is the revision.
4118 - Vertical motion shows revision history
4119 - Horizontal motion show copy history.
4120
4121 A1---------C4 E7
4122 | | |
4123 A2 C5 E8---F9
4124 | | |
4125 A3---B4 C6 F10
4126 | |
4127 A4 B5----------D6
4128 | |
4129 B6 D7
4130 */
4131 /* Revision 1 */
4132 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
4133 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
4134 SVN_ERR(svn_fs_make_file(txn_root, "A", subpool));
4135 SVN_ERR(svn_test__set_file_contents(txn_root, "A", "1", subpool));
4136 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
4137 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4138 svn_pool_clear(subpool);
4139 /* Revision 2 */
4140 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
4141 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
4142 SVN_ERR(svn_test__set_file_contents(txn_root, "A", "2", subpool));
4143 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
4144 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4145 svn_pool_clear(subpool);
4146 /* Revision 3 */
4147 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
4148 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
4149 SVN_ERR(svn_test__set_file_contents(txn_root, "A", "3", subpool));
4150 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
4151 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4152 svn_pool_clear(subpool);
4153 /* Revision 4 */
4154 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
4155 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
4156 SVN_ERR(svn_test__set_file_contents(txn_root, "A", "4", subpool));
4157 SVN_ERR(svn_fs_revision_root(&rev_root, fs, 3, subpool));
4158 SVN_ERR(svn_fs_copy(rev_root, "A", txn_root, "B", subpool));
4159 SVN_ERR(svn_test__set_file_contents(txn_root, "B", "4", subpool));
4160 SVN_ERR(svn_fs_revision_root(&rev_root, fs, 1, subpool));
4161 SVN_ERR(svn_fs_copy(rev_root, "A", txn_root, "C", subpool));
4162 SVN_ERR(svn_test__set_file_contents(txn_root, "C", "4", subpool));
4163 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
4164 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4165 svn_pool_clear(subpool);
4166 /* Revision 5 */
4167 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
4168 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
4169 SVN_ERR(svn_test__set_file_contents(txn_root, "B", "5", subpool));
4170 SVN_ERR(svn_test__set_file_contents(txn_root, "C", "5", subpool));
4171 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
4172 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4173 svn_pool_clear(subpool);
4174 /* Revision 6 */
4175 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
4176 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
4177 SVN_ERR(svn_test__set_file_contents(txn_root, "B", "6", subpool));
4178 SVN_ERR(svn_test__set_file_contents(txn_root, "C", "6", subpool));
4179 SVN_ERR(svn_fs_revision_root(&rev_root, fs, 5, subpool));
4180 SVN_ERR(svn_fs_copy(rev_root, "B", txn_root, "D", subpool));
4181 SVN_ERR(svn_test__set_file_contents(txn_root, "D", "5", subpool));
4182 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
4183 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4184 svn_pool_clear(subpool);
4185 /* Revision 7 */
4186 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
4187 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
4188 SVN_ERR(svn_test__set_file_contents(txn_root, "D", "7", subpool));
4189 SVN_ERR(svn_fs_make_file(txn_root, "E", subpool));
4190 SVN_ERR(svn_test__set_file_contents(txn_root, "E", "7", subpool));
4191 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
4192 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4193 svn_pool_clear(subpool);
4194 /* Revision 8 */
4195 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
4196 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
4197 SVN_ERR(svn_test__set_file_contents(txn_root, "E", "8", subpool));
4198 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
4199 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4200 svn_pool_clear(subpool);
4201 /* Revision 9 */
4202 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
4203 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
4204 SVN_ERR(svn_fs_revision_root(&rev_root, fs, 8, subpool));
4205 SVN_ERR(svn_fs_copy(rev_root, "E", txn_root, "F", subpool));
4206 SVN_ERR(svn_test__set_file_contents(txn_root, "F", "9", subpool));
4207 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
4208 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4209 svn_pool_clear(subpool);
4210 /* Revision 10 */
4211 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
4212 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
4213 SVN_ERR(svn_test__set_file_contents(txn_root, "F", "10", subpool));
4214 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
4215 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4216 svn_pool_clear(subpool);
4217
4218 /*** Step II: Exhaustively verify relationship between all nodes in
4219 existence. */
4220 {
4221 int i, j;
4222
4223 struct path_rev_t
4224 {
4225 const char *path;
4226 svn_revnum_t rev;
4227 };
4228
4229 /* Our 16 existing files/revisions. */
4230 struct path_rev_t path_revs[16] = {
4231 { "A", 1 }, { "A", 2 }, { "A", 3 }, { "A", 4 },
4232 { "B", 4 }, { "B", 5 }, { "B", 6 }, { "C", 4 },
4233 { "C", 5 }, { "C", 6 }, { "D", 6 }, { "D", 7 },
4234 { "E", 7 }, { "E", 8 }, { "F", 9 }, { "F", 10 }
4235 };
4236
4237 /* Latest revision that touched the respective path. */
4238 struct path_rev_t latest_changes[6] = {
4239 { "A", 4 }, { "B", 6 }, { "C", 6 },
4240 { "D", 7 }, { "E", 8 }, { "F", 10 }
4241 };
4242
4243 int related_matrix[16][16] = {
4244 /* A1 ... F10 across the top here*/
4245 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* A1 */
4246 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* A2 */
4247 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* A3 */
4248 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* A4 */
4249 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* B4 */
4250 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* B5 */
4251 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* B6 */
4252 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* C4 */
4253 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* C5 */
4254 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* C6 */
4255 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* D6 */
4256 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* D7 */
4257 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, /* E7 */
4258 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, /* E8 */
4259 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, /* F9 */
4260 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 } /* F10 */
4261 };
4262
4263 /* Here's the fun part. Running the tests. */
4264 for (i = 0; i < 16; i++)
4265 {
4266 for (j = 0; j < 16; j++)
4267 {
4268 struct path_rev_t pr1 = path_revs[i];
4269 struct path_rev_t pr2 = path_revs[j];
4270 const svn_fs_id_t *id1, *id2;
4271 int related = 0;
4272 svn_fs_node_relation_t relation;
4273 svn_fs_root_t *rev_root1, *rev_root2;
4274
4275 /* Get the ID for the first path/revision combination. */
4276 SVN_ERR(svn_fs_revision_root(&rev_root1, fs, pr1.rev, subpool));
4277 SVN_ERR(svn_fs_node_id(&id1, rev_root1, pr1.path, subpool));
4278
4279 /* Get the ID for the second path/revision combination. */
4280 SVN_ERR(svn_fs_revision_root(&rev_root2, fs, pr2.rev, subpool));
4281 SVN_ERR(svn_fs_node_id(&id2, rev_root2, pr2.path, subpool));
4282
4283 /* <exciting> Now, run the relationship check! </exciting> */
4284 related = svn_fs_check_related(id1, id2) ? 1 : 0;
4285 if (related == related_matrix[i][j])
4286 {
4287 /* xlnt! */
4288 }
4289 else if (related && (! related_matrix[i][j]))
4290 {
4291 return svn_error_createf
4292 (SVN_ERR_TEST_FAILED, NULL,
4293 "expected '%s:%d' to be related to '%s:%d'; it was not",
4294 pr1.path, (int)pr1.rev, pr2.path, (int)pr2.rev);
4295 }
4296 else if ((! related) && related_matrix[i][j])
4297 {
4298 return svn_error_createf
4299 (SVN_ERR_TEST_FAILED, NULL,
4300 "expected '%s:%d' to not be related to '%s:%d'; it was",
4301 pr1.path, (int)pr1.rev, pr2.path, (int)pr2.rev);
4302 }
4303
4304 /* Asking directly, i.e. without involving the noderev IDs as
4305 * an intermediate, should yield the same results. */
4306 SVN_ERR(svn_fs_node_relation(&relation, rev_root1, pr1.path,
4307 rev_root2, pr2.path, subpool));
4308 if (i == j)
4309 {
4310 /* Identical note. */
4311 if (!related || relation != svn_fs_node_unchanged)
4312 {
4313 return svn_error_createf
4314 (SVN_ERR_TEST_FAILED, NULL,
4315 "expected '%s:%d' to be the same as '%s:%d';"
4316 " it was not",
4317 pr1.path, (int)pr1.rev, pr2.path, (int)pr2.rev);
4318 }
4319 }
4320 else if (related && relation != svn_fs_node_common_ancestor)
4321 {
4322 return svn_error_createf
4323 (SVN_ERR_TEST_FAILED, NULL,
4324 "expected '%s:%d' to have a common ancestor with '%s:%d';"
4325 " it had not",
4326 pr1.path, (int)pr1.rev, pr2.path, (int)pr2.rev);
4327 }
4328 else if (!related && relation != svn_fs_node_unrelated)
4329 {
4330 return svn_error_createf
4331 (SVN_ERR_TEST_FAILED, NULL,
4332 "expected '%s:%d' to not be related to '%s:%d'; it was",
4333 pr1.path, (int)pr1.rev, pr2.path, (int)pr2.rev);
4334 }
4335
4336 svn_pool_clear(subpool);
4337 } /* for ... */
4338 } /* for ... */
4339
4340 /* Verify that the noderevs stay the same after their last change. */
4341 for (i = 0; i < 6; ++i)
4342 {
4343 const char *path = latest_changes[i].path;
4344 svn_revnum_t latest = latest_changes[i].rev;
4345 svn_fs_root_t *latest_root;
4346 svn_revnum_t rev;
4347 svn_fs_node_relation_t relation;
4348
4349 /* FS root of the latest change. */
4350 svn_pool_clear(subpool);
4351 SVN_ERR(svn_fs_revision_root(&latest_root, fs, latest, subpool));
4352
4353 /* All future revisions. */
4354 for (rev = latest + 1; rev <= 10; ++rev)
4355 {
4356 /* Query their noderev relationship to the latest change. */
4357 SVN_ERR(svn_fs_revision_root(&rev_root, fs, rev, subpool));
4358 SVN_ERR(svn_fs_node_relation(&relation, latest_root, path,
4359 rev_root, path, subpool));
4360
4361 /* They shall use the same noderevs */
4362 if (relation != svn_fs_node_unchanged)
4363 {
4364 return svn_error_createf
4365 (SVN_ERR_TEST_FAILED, NULL,
4366 "expected '%s:%d' to be the same as '%s:%d';"
4367 " it was not",
4368 path, (int)latest, path, (int)rev);
4369 }
4370 } /* for ... */
4371 } /* for ... */
4372 }
4373
4374 /* Destroy the subpool. */
4375 svn_pool_destroy(subpool);
4376
4377 return SVN_NO_ERROR;
4378 }
4379
4380
4381 static svn_error_t *
check_txn_related(const svn_test_opts_t * opts,apr_pool_t * pool)4382 check_txn_related(const svn_test_opts_t *opts,
4383 apr_pool_t *pool)
4384 {
4385 apr_pool_t *subpool = svn_pool_create(pool);
4386 svn_fs_t *fs;
4387 svn_fs_txn_t *txn[3];
4388 svn_fs_root_t *root[3];
4389 svn_revnum_t youngest_rev = 0;
4390
4391 /* Create a filesystem and repository. */
4392 SVN_ERR(svn_test__create_fs(&fs, "test-repo-check-txn-related",
4393 opts, pool));
4394
4395 /*** Step I: Build up some state in our repository through a series
4396 of commits */
4397
4398 /* This is the node graph we are testing. It contains one revision (r1)
4399 and two transactions, T1 and T2 - yet uncommitted.
4400
4401 A is a file that exists in r1 (A-0) and gets modified in both txns.
4402 C is a copy of A-0 made in both txns.
4403 B is a new node created in both txns
4404 D is a file that exists in r1 (D-0) and never gets modified.
4405 / is the root folder, touched in r0, r1 and both txns (root-0)
4406 R is a copy of the root-0 made in both txns.
4407
4408 The edges in the graph connect related noderevs:
4409
4410 +--A-0--+ D-0 +-root-0-+
4411 | | | |
4412 +-----+ +-----+ +------+ +------+
4413 | | | | | | | |
4414 B-1 C-1 A-1 A-2 C-2 B-2 R-1 root-1 root-2 R-2
4415 */
4416 /* Revision 1 */
4417 SVN_ERR(svn_fs_begin_txn(&txn[0], fs, youngest_rev, subpool));
4418 SVN_ERR(svn_fs_txn_root(&root[0], txn[0], subpool));
4419 SVN_ERR(svn_fs_make_file(root[0], "A", subpool));
4420 SVN_ERR(svn_test__set_file_contents(root[0], "A", "1", subpool));
4421 SVN_ERR(svn_fs_make_file(root[0], "D", subpool));
4422 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn[0], subpool));
4423 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4424 svn_pool_clear(subpool);
4425 SVN_ERR(svn_fs_revision_root(&root[0], fs, youngest_rev, pool));
4426
4427 /* Transaction 1 */
4428 SVN_ERR(svn_fs_begin_txn(&txn[1], fs, youngest_rev, pool));
4429 SVN_ERR(svn_fs_txn_root(&root[1], txn[1], pool));
4430 SVN_ERR(svn_test__set_file_contents(root[1], "A", "2", pool));
4431 SVN_ERR(svn_fs_copy(root[0], "A", root[1], "C", pool));
4432 SVN_ERR(svn_fs_copy(root[0], "", root[1], "R", pool));
4433 SVN_ERR(svn_fs_make_file(root[1], "B", pool));
4434
4435 /* Transaction 2 */
4436 SVN_ERR(svn_fs_begin_txn(&txn[2], fs, youngest_rev, pool));
4437 SVN_ERR(svn_fs_txn_root(&root[2], txn[2], pool));
4438 SVN_ERR(svn_test__set_file_contents(root[2], "A", "2", pool));
4439 SVN_ERR(svn_fs_copy(root[0], "A", root[2], "C", pool));
4440 SVN_ERR(svn_fs_copy(root[0], "", root[2], "R", pool));
4441 SVN_ERR(svn_fs_make_file(root[2], "B", pool));
4442
4443 /*** Step II: Exhaustively verify relationship between all nodes in
4444 existence. */
4445 {
4446 enum { NODE_COUNT = 13 };
4447 int i, j;
4448
4449 struct path_rev_t
4450 {
4451 const char *path;
4452 int root;
4453 };
4454
4455 /* Our 16 existing files/revisions. */
4456 struct path_rev_t path_revs[NODE_COUNT] = {
4457 { "A", 0 }, { "A", 1 }, { "A", 2 },
4458 { "B", 1 }, { "B", 2 },
4459 { "C", 1 }, { "C", 2 },
4460 { "D", 0 },
4461 { "/", 0 }, { "/", 1 }, { "/", 2 },
4462 { "R", 1 }, { "R", 2 }
4463 };
4464
4465 int related_matrix[NODE_COUNT][NODE_COUNT] = {
4466 /* A-0 ... R-2 across the top here*/
4467 { 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, /* A-0 */
4468 { 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, /* A-1 */
4469 { 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, /* A-2 */
4470 { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* B-1 */
4471 { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, /* B-2 */
4472 { 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, /* C-1 */
4473 { 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, /* C-2 */
4474 { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, /* D-0 */
4475 { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 }, /* root-0 */
4476 { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 }, /* root-1 */
4477 { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 }, /* root-2 */
4478 { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 }, /* R-1 */
4479 { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 }, /* R-2 */
4480 };
4481
4482 /* Here's the fun part. Running the tests. */
4483 for (i = 0; i < NODE_COUNT; i++)
4484 {
4485 for (j = 0; j < NODE_COUNT; j++)
4486 {
4487 struct path_rev_t pr1 = path_revs[i];
4488 struct path_rev_t pr2 = path_revs[j];
4489 const svn_fs_id_t *id1, *id2;
4490 int related = 0;
4491 svn_fs_node_relation_t relation;
4492
4493 svn_pool_clear(subpool);
4494
4495 /* Get the ID for the first path/revision combination. */
4496 SVN_ERR(svn_fs_node_id(&id1, root[pr1.root], pr1.path, subpool));
4497
4498 /* Get the ID for the second path/revision combination. */
4499 SVN_ERR(svn_fs_node_id(&id2, root[pr2.root], pr2.path, subpool));
4500
4501 /* <exciting> Now, run the relationship check! </exciting> */
4502 related = svn_fs_check_related(id1, id2) ? 1 : 0;
4503 if (related == related_matrix[i][j])
4504 {
4505 /* xlnt! */
4506 }
4507 else if ((! related) && related_matrix[i][j])
4508 {
4509 return svn_error_createf
4510 (SVN_ERR_TEST_FAILED, NULL,
4511 "expected '%s-%d' to be related to '%s-%d'; it was not",
4512 pr1.path, pr1.root, pr2.path, pr2.root);
4513 }
4514 else if (related && (! related_matrix[i][j]))
4515 {
4516 return svn_error_createf
4517 (SVN_ERR_TEST_FAILED, NULL,
4518 "expected '%s-%d' to not be related to '%s-%d'; it was",
4519 pr1.path, pr1.root, pr2.path, pr2.root);
4520 }
4521
4522 /* Asking directly, i.e. without involving the noderev IDs as
4523 * an intermediate, should yield the same results. */
4524 SVN_ERR(svn_fs_node_relation(&relation, root[pr1.root], pr1.path,
4525 root[pr2.root], pr2.path, subpool));
4526 if (i == j)
4527 {
4528 /* Identical noderev. */
4529 if (!related || relation != svn_fs_node_unchanged)
4530 {
4531 return svn_error_createf
4532 (SVN_ERR_TEST_FAILED, NULL,
4533 "expected '%s-%d' to be the same as '%s-%d';"
4534 " it was not",
4535 pr1.path, pr1.root, pr2.path, pr2.root);
4536 }
4537 }
4538 else if (related && relation != svn_fs_node_common_ancestor)
4539 {
4540 return svn_error_createf
4541 (SVN_ERR_TEST_FAILED, NULL,
4542 "expected '%s-%d' to have a common ancestor with '%s-%d';"
4543 " it had not",
4544 pr1.path, pr1.root, pr2.path, pr2.root);
4545 }
4546 else if (!related && relation != svn_fs_node_unrelated)
4547 {
4548 return svn_error_createf
4549 (SVN_ERR_TEST_FAILED, NULL,
4550 "expected '%s-%d' to not be related to '%s-%d'; it was",
4551 pr1.path, pr1.root, pr2.path, pr2.root);
4552 }
4553 } /* for ... */
4554 } /* for ... */
4555
4556 /* Verify that the noderevs stay the same after their last change.
4557 There is only D that is not changed. */
4558 for (i = 1; i <= 2; ++i)
4559 {
4560 svn_fs_node_relation_t relation;
4561 svn_pool_clear(subpool);
4562
4563 /* Query their noderev relationship to the latest change. */
4564 SVN_ERR(svn_fs_node_relation(&relation, root[i], "D",
4565 root[0], "D", subpool));
4566
4567 /* They shall use the same noderevs */
4568 if (relation != svn_fs_node_unchanged)
4569 {
4570 return svn_error_createf
4571 (SVN_ERR_TEST_FAILED, NULL,
4572 "expected 'D-%d' to be the same as 'D-0'; it was not", i);
4573 }
4574 } /* for ... */
4575 }
4576
4577 /* Destroy the subpool. */
4578 svn_pool_destroy(subpool);
4579
4580 return SVN_NO_ERROR;
4581 }
4582
4583
4584 static svn_error_t *
branch_test(const svn_test_opts_t * opts,apr_pool_t * pool)4585 branch_test(const svn_test_opts_t *opts,
4586 apr_pool_t *pool)
4587 {
4588 apr_pool_t *spool = svn_pool_create(pool);
4589 svn_fs_t *fs;
4590 svn_fs_txn_t *txn;
4591 svn_fs_root_t *txn_root, *rev_root;
4592 svn_revnum_t youngest_rev = 0;
4593
4594 /* Create a filesystem and repository. */
4595 SVN_ERR(svn_test__create_fs(&fs, "test-repo-branch",
4596 opts, pool));
4597
4598 /*** Revision 1: Create the greek tree in revision. ***/
4599 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool));
4600 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
4601 SVN_ERR(svn_test__create_greek_tree(txn_root, spool));
4602 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, spool));
4603 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4604 svn_pool_clear(spool);
4605
4606 /*** Revision 2: Copy A/D/G/rho to A/D/G/rho2. ***/
4607 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool));
4608 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
4609 SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, spool));
4610 SVN_ERR(svn_fs_copy(rev_root, "A/D/G/rho", txn_root, "A/D/G/rho2", spool));
4611 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, spool));
4612 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4613 svn_pool_clear(spool);
4614
4615 /*** Revision 3: Copy A/D/G to A/D/G2. ***/
4616 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool));
4617 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
4618 SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, spool));
4619 SVN_ERR(svn_fs_copy(rev_root, "A/D/G", txn_root, "A/D/G2", spool));
4620 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, spool));
4621 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4622 svn_pool_clear(spool);
4623
4624 /*** Revision 4: Copy A/D to A/D2. ***/
4625 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool));
4626 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
4627 SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, spool));
4628 SVN_ERR(svn_fs_copy(rev_root, "A/D", txn_root, "A/D2", spool));
4629 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, spool));
4630 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4631 svn_pool_clear(spool);
4632
4633 /*** Revision 5: Edit all the rho's! ***/
4634 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool));
4635 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
4636 SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, spool));
4637 SVN_ERR(svn_test__set_file_contents(txn_root, "A/D/G/rho",
4638 "Edited text.", spool));
4639 SVN_ERR(svn_test__set_file_contents(txn_root, "A/D/G/rho2",
4640 "Edited text.", spool));
4641 SVN_ERR(svn_test__set_file_contents(txn_root, "A/D/G2/rho",
4642 "Edited text.", spool));
4643 SVN_ERR(svn_test__set_file_contents(txn_root, "A/D/G2/rho2",
4644 "Edited text.", spool));
4645 SVN_ERR(svn_test__set_file_contents(txn_root, "A/D2/G/rho",
4646 "Edited text.", spool));
4647 SVN_ERR(svn_test__set_file_contents(txn_root, "A/D2/G/rho2",
4648 "Edited text.", spool));
4649 SVN_ERR(svn_test__set_file_contents(txn_root, "A/D2/G2/rho",
4650 "Edited text.", spool));
4651 SVN_ERR(svn_test__set_file_contents(txn_root, "A/D2/G2/rho2",
4652 "Edited text.", spool));
4653 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, spool));
4654 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
4655
4656 svn_pool_destroy(spool);
4657
4658 return SVN_NO_ERROR;
4659 }
4660
4661
4662 /* Verify that file FILENAME under ROOT has the same contents checksum
4663 * as CONTENTS when comparing the checksums of the given TYPE.
4664 * Use POOL for temporary allocations. */
4665 static svn_error_t *
verify_file_checksum(svn_stringbuf_t * contents,svn_fs_root_t * root,const char * filename,svn_checksum_kind_t type,apr_pool_t * pool)4666 verify_file_checksum(svn_stringbuf_t *contents,
4667 svn_fs_root_t *root,
4668 const char *filename,
4669 svn_checksum_kind_t type,
4670 apr_pool_t *pool)
4671 {
4672 svn_checksum_t *expected_checksum, *actual_checksum;
4673
4674 /* Write a file, compare the repository's idea of its checksum
4675 against our idea of its checksum. They should be the same. */
4676 SVN_ERR(svn_checksum(&expected_checksum, type, contents->data,
4677 contents->len, pool));
4678 SVN_ERR(svn_fs_file_checksum(&actual_checksum, type, root, filename, TRUE,
4679 pool));
4680 if (!svn_checksum_match(expected_checksum, actual_checksum))
4681 return svn_error_createf
4682 (SVN_ERR_FS_GENERAL, NULL,
4683 "verify-checksum: checksum mismatch:\n"
4684 " expected: %s\n"
4685 " actual: %s\n",
4686 svn_checksum_to_cstring(expected_checksum, pool),
4687 svn_checksum_to_cstring(actual_checksum, pool));
4688
4689 return SVN_NO_ERROR;
4690 }
4691
4692 static svn_error_t *
verify_checksum(const svn_test_opts_t * opts,apr_pool_t * pool)4693 verify_checksum(const svn_test_opts_t *opts,
4694 apr_pool_t *pool)
4695 {
4696 svn_fs_t *fs;
4697 svn_fs_txn_t *txn;
4698 svn_fs_root_t *txn_root, *rev_root;
4699 svn_stringbuf_t *str;
4700 svn_revnum_t rev;
4701
4702 /* Write a file, compare the repository's idea of its checksum
4703 against our idea of its checksum. They should be the same. */
4704 str = svn_stringbuf_create("My text editor charges me rent.", pool);
4705
4706 SVN_ERR(svn_test__create_fs(&fs, "test-repo-verify-checksum",
4707 opts, pool));
4708 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
4709 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
4710 SVN_ERR(svn_fs_make_file(txn_root, "fact", pool));
4711 SVN_ERR(svn_test__set_file_contents(txn_root, "fact", str->data, pool));
4712
4713 /* Do it for the txn. */
4714 SVN_ERR(verify_file_checksum(str, txn_root, "fact", svn_checksum_md5,
4715 pool));
4716 SVN_ERR(verify_file_checksum(str, txn_root, "fact", svn_checksum_sha1,
4717 pool));
4718
4719 /* Do it again - this time for the revision. */
4720 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, pool));
4721 SVN_ERR(svn_fs_revision_root(&rev_root, fs, rev, pool));
4722 SVN_ERR(verify_file_checksum(str, rev_root, "fact", svn_checksum_md5,
4723 pool));
4724 SVN_ERR(verify_file_checksum(str, rev_root, "fact", svn_checksum_sha1,
4725 pool));
4726
4727 return SVN_NO_ERROR;
4728 }
4729
4730
4731 /* Helper for closest_copy_test(). Verify that CLOSEST_PATH and the
4732 revision associated with CLOSEST_ROOT match the EXPECTED_PATH and
4733 EXPECTED_REVISION, respectively. */
4734 static svn_error_t *
test_closest_copy_pair(svn_fs_root_t * closest_root,const char * closest_path,svn_revnum_t expected_revision,const char * expected_path)4735 test_closest_copy_pair(svn_fs_root_t *closest_root,
4736 const char *closest_path,
4737 svn_revnum_t expected_revision,
4738 const char *expected_path)
4739 {
4740 svn_revnum_t closest_rev = SVN_INVALID_REVNUM;
4741
4742 /* Callers must pass valid -- EXPECTED_PATH and EXPECTED_REVISION
4743 come as a both-or-nothing pair. */
4744 assert(((! expected_path) && (! SVN_IS_VALID_REVNUM(expected_revision)))
4745 || (expected_path && SVN_IS_VALID_REVNUM(expected_revision)));
4746
4747 /* CLOSEST_PATH and CLOSEST_ROOT come as a both-or-nothing pair, too. */
4748 if (closest_path && (! closest_root))
4749 return svn_error_create(SVN_ERR_FS_GENERAL, NULL,
4750 "got closest path but no closest root");
4751 if ((! closest_path) && closest_root)
4752 return svn_error_create(SVN_ERR_FS_GENERAL, NULL,
4753 "got closest root but no closest path");
4754
4755 /* Now that our pairs are known sane, we can compare them. */
4756 if (closest_path && (! expected_path))
4757 return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
4758 "got closest path ('%s') when none expected",
4759 closest_path);
4760 if ((! closest_path) && expected_path)
4761 return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
4762 "got no closest path; expected '%s'",
4763 expected_path);
4764 if (closest_path && (strcmp(closest_path, expected_path) != 0))
4765 return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
4766 "got a different closest path than expected:\n"
4767 " expected: %s\n"
4768 " actual: %s",
4769 expected_path, closest_path);
4770 if (closest_root)
4771 closest_rev = svn_fs_revision_root_revision(closest_root);
4772 if (closest_rev != expected_revision)
4773 return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
4774 "got a different closest rev than expected:\n"
4775 " expected: %ld\n"
4776 " actual: %ld",
4777 expected_revision, closest_rev);
4778
4779 return SVN_NO_ERROR;
4780 }
4781
4782
4783 static svn_error_t *
closest_copy_test(const svn_test_opts_t * opts,apr_pool_t * pool)4784 closest_copy_test(const svn_test_opts_t *opts,
4785 apr_pool_t *pool)
4786 {
4787 svn_fs_t *fs;
4788 svn_fs_txn_t *txn;
4789 svn_fs_root_t *txn_root, *rev_root, *croot;
4790 svn_revnum_t after_rev;
4791 const char *cpath;
4792 apr_pool_t *spool = svn_pool_create(pool);
4793
4794 /* Prepare a filesystem. */
4795 SVN_ERR(svn_test__create_fs(&fs, "test-repo-closest-copy",
4796 opts, pool));
4797
4798 /* In first txn, create and commit the greek tree. */
4799 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, spool));
4800 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
4801 SVN_ERR(svn_test__create_greek_tree(txn_root, spool));
4802 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, spool));
4803 svn_pool_clear(spool);
4804 SVN_ERR(svn_fs_revision_root(&rev_root, fs, after_rev, spool));
4805
4806 /* Copy A to Z, and commit. */
4807 SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, spool));
4808 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
4809 SVN_ERR(svn_fs_copy(rev_root, "A", txn_root, "Z", spool));
4810 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, spool));
4811 svn_pool_clear(spool);
4812 SVN_ERR(svn_fs_revision_root(&rev_root, fs, after_rev, spool));
4813
4814 /* Anything under Z should have a closest copy pair of ("/Z", 2), so
4815 we'll pick some spots to test. Stuff under A should have no
4816 relevant closest copy. */
4817 SVN_ERR(svn_fs_closest_copy(&croot, &cpath, rev_root, "Z", spool));
4818 SVN_ERR(test_closest_copy_pair(croot, cpath, 2, "/Z"));
4819 SVN_ERR(svn_fs_closest_copy(&croot, &cpath, rev_root, "Z/D/G", spool));
4820 SVN_ERR(test_closest_copy_pair(croot, cpath, 2, "/Z"));
4821 SVN_ERR(svn_fs_closest_copy(&croot, &cpath, rev_root, "Z/mu", spool));
4822 SVN_ERR(test_closest_copy_pair(croot, cpath, 2, "/Z"));
4823 SVN_ERR(svn_fs_closest_copy(&croot, &cpath, rev_root, "Z/B/E/beta", spool));
4824 SVN_ERR(test_closest_copy_pair(croot, cpath, 2, "/Z"));
4825 SVN_ERR(svn_fs_closest_copy(&croot, &cpath, rev_root, "A", spool));
4826 SVN_ERR(test_closest_copy_pair(croot, cpath, SVN_INVALID_REVNUM, NULL));
4827 SVN_ERR(svn_fs_closest_copy(&croot, &cpath, rev_root, "A/D/G", spool));
4828 SVN_ERR(test_closest_copy_pair(croot, cpath, SVN_INVALID_REVNUM, NULL));
4829 SVN_ERR(svn_fs_closest_copy(&croot, &cpath, rev_root, "A/mu", spool));
4830 SVN_ERR(test_closest_copy_pair(croot, cpath, SVN_INVALID_REVNUM, NULL));
4831 SVN_ERR(svn_fs_closest_copy(&croot, &cpath, rev_root, "A/B/E/beta", spool));
4832 SVN_ERR(test_closest_copy_pair(croot, cpath, SVN_INVALID_REVNUM, NULL));
4833
4834 /* Okay, so let's do some more stuff. We'll edit Z/mu, copy A to
4835 Z2, copy A/D/H to Z2/D/H2, and edit Z2/D/H2/chi. We'll also make
4836 new Z/t and Z2/D/H2/t files. */
4837 SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, spool));
4838 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
4839 SVN_ERR(svn_test__set_file_contents(txn_root, "Z/mu",
4840 "Edited text.", spool));
4841 SVN_ERR(svn_fs_copy(rev_root, "A", txn_root, "Z2", spool));
4842 SVN_ERR(svn_fs_copy(rev_root, "A/D/H", txn_root, "Z2/D/H2", spool));
4843 SVN_ERR(svn_test__set_file_contents(txn_root, "Z2/D/H2/chi",
4844 "Edited text.", spool));
4845 SVN_ERR(svn_fs_make_file(txn_root, "Z/t", pool));
4846 SVN_ERR(svn_fs_make_file(txn_root, "Z2/D/H2/t", pool));
4847 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, spool));
4848 svn_pool_clear(spool);
4849 SVN_ERR(svn_fs_revision_root(&rev_root, fs, after_rev, spool));
4850
4851 /* Okay, just for kicks, let's modify Z2/D/H2/t. Shouldn't affect
4852 its closest-copy-ness, right? */
4853 SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, spool));
4854 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
4855 SVN_ERR(svn_test__set_file_contents(txn_root, "Z2/D/H2/t",
4856 "Edited text.", spool));
4857 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, spool));
4858 svn_pool_clear(spool);
4859 SVN_ERR(svn_fs_revision_root(&rev_root, fs, after_rev, spool));
4860
4861 /* Now, we expect Z2/D/H2 to have a closest copy of ("/Z2/D/H2", 3)
4862 because of the deepest path rule. We expected Z2/D to have a
4863 closest copy of ("/Z2", 3). Z/mu should still have a closest
4864 copy of ("/Z", 2). As for the two new files (Z/t and Z2/D/H2/t),
4865 neither should have a closest copy. */
4866 SVN_ERR(svn_fs_closest_copy(&croot, &cpath, rev_root, "A/mu", spool));
4867 SVN_ERR(test_closest_copy_pair(croot, cpath, SVN_INVALID_REVNUM, NULL));
4868 SVN_ERR(svn_fs_closest_copy(&croot, &cpath, rev_root, "Z/mu", spool));
4869 SVN_ERR(test_closest_copy_pair(croot, cpath, 2, "/Z"));
4870 SVN_ERR(svn_fs_closest_copy(&croot, &cpath, rev_root, "Z2/D/H2", spool));
4871 SVN_ERR(test_closest_copy_pair(croot, cpath, 3, "/Z2/D/H2"));
4872 SVN_ERR(svn_fs_closest_copy(&croot, &cpath, rev_root, "Z2/D", spool));
4873 SVN_ERR(test_closest_copy_pair(croot, cpath, 3, "/Z2"));
4874 SVN_ERR(svn_fs_closest_copy(&croot, &cpath, rev_root, "Z/t", spool));
4875 SVN_ERR(test_closest_copy_pair(croot, cpath, SVN_INVALID_REVNUM, NULL));
4876 SVN_ERR(svn_fs_closest_copy(&croot, &cpath, rev_root, "Z2/D/H2/t", spool));
4877 SVN_ERR(test_closest_copy_pair(croot, cpath, SVN_INVALID_REVNUM, NULL));
4878
4879 return SVN_NO_ERROR;
4880 }
4881
4882 static svn_error_t *
root_revisions(const svn_test_opts_t * opts,apr_pool_t * pool)4883 root_revisions(const svn_test_opts_t *opts,
4884 apr_pool_t *pool)
4885 {
4886 svn_fs_t *fs;
4887 svn_fs_txn_t *txn;
4888 svn_fs_root_t *txn_root, *rev_root;
4889 svn_revnum_t after_rev, fetched_rev;
4890 apr_pool_t *spool = svn_pool_create(pool);
4891
4892 /* Prepare a filesystem. */
4893 SVN_ERR(svn_test__create_fs(&fs, "test-repo-root-revisions",
4894 opts, pool));
4895
4896 /* In first txn, create and commit the greek tree. */
4897 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, spool));
4898 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
4899 SVN_ERR(svn_test__create_greek_tree(txn_root, spool));
4900 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, spool));
4901
4902 /* First, verify that a revision root based on our new revision
4903 reports the correct associated revision. */
4904 SVN_ERR(svn_fs_revision_root(&rev_root, fs, after_rev, spool));
4905 fetched_rev = svn_fs_revision_root_revision(rev_root);
4906 if (after_rev != fetched_rev)
4907 return svn_error_createf
4908 (SVN_ERR_TEST_FAILED, NULL,
4909 "expected revision '%d'; "
4910 "got '%d' from svn_fs_revision_root_revision(rev_root)",
4911 (int)after_rev, (int)fetched_rev);
4912
4913 /* Then verify that we can't ask about the txn-base-rev from a
4914 revision root. */
4915 fetched_rev = svn_fs_txn_root_base_revision(rev_root);
4916 if (fetched_rev != SVN_INVALID_REVNUM)
4917 return svn_error_createf
4918 (SVN_ERR_TEST_FAILED, NULL,
4919 "expected SVN_INVALID_REVNUM; "
4920 "got '%d' from svn_fs_txn_root_base_revision(rev_root)",
4921 (int)fetched_rev);
4922
4923 /* Now, create a second txn based on AFTER_REV. */
4924 SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, spool));
4925 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
4926
4927 /* Verify that it reports the right base revision. */
4928 fetched_rev = svn_fs_txn_root_base_revision(txn_root);
4929 if (after_rev != fetched_rev)
4930 return svn_error_createf
4931 (SVN_ERR_TEST_FAILED, NULL,
4932 "expected '%d'; "
4933 "got '%d' from svn_fs_txn_root_base_revision(txn_root)",
4934 (int)after_rev, (int)fetched_rev);
4935
4936 /* Then verify that we can't ask about the rev-root-rev from a
4937 txn root. */
4938 fetched_rev = svn_fs_revision_root_revision(txn_root);
4939 if (fetched_rev != SVN_INVALID_REVNUM)
4940 return svn_error_createf
4941 (SVN_ERR_TEST_FAILED, NULL,
4942 "expected SVN_INVALID_REVNUM; "
4943 "got '%d' from svn_fs_revision_root_revision(txn_root)",
4944 (int)fetched_rev);
4945
4946 return SVN_NO_ERROR;
4947 }
4948
4949
4950 static svn_error_t *
unordered_txn_dirprops(const svn_test_opts_t * opts,apr_pool_t * pool)4951 unordered_txn_dirprops(const svn_test_opts_t *opts,
4952 apr_pool_t *pool)
4953 {
4954 svn_fs_t *fs;
4955 svn_fs_txn_t *txn, *txn2;
4956 svn_fs_root_t *txn_root, *txn_root2;
4957 svn_string_t pval;
4958 svn_revnum_t new_rev, not_rev;
4959 svn_boolean_t is_bdb = strcmp(opts->fs_type, SVN_FS_TYPE_BDB) == 0;
4960
4961 /* This is a regression test for issue #2751. */
4962
4963 /* Prepare a filesystem. */
4964 SVN_ERR(svn_test__create_fs(&fs, "test-repo-unordered-txn-dirprops",
4965 opts, pool));
4966
4967 /* Create and commit the greek tree. */
4968 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
4969 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
4970 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
4971 SVN_ERR(test_commit_txn(&new_rev, txn, NULL, pool));
4972
4973 /* Open two transactions */
4974 SVN_ERR(svn_fs_begin_txn(&txn, fs, new_rev, pool));
4975 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
4976 SVN_ERR(svn_fs_begin_txn(&txn2, fs, new_rev, pool));
4977 SVN_ERR(svn_fs_txn_root(&txn_root2, txn2, pool));
4978
4979 /* Change a child file in one. */
4980 SVN_ERR(svn_test__set_file_contents(txn_root, "/A/B/E/alpha",
4981 "New contents", pool));
4982
4983 /* Change dir props in the other. (We're using svn:mergeinfo
4984 property just to make sure special handling logic for that
4985 property doesn't croak.) */
4986 SET_STR(&pval, "/A/C:1");
4987 SVN_ERR(svn_fs_change_node_prop(txn_root2, "/A/B", "svn:mergeinfo",
4988 &pval, pool));
4989
4990 /* Commit the second one first. */
4991 SVN_ERR(test_commit_txn(&new_rev, txn2, NULL, pool));
4992
4993 /* Then commit the first -- but expect a conflict due to the
4994 propchanges made by the other txn. */
4995 SVN_ERR(test_commit_txn(¬_rev, txn, "/A/B", pool));
4996 SVN_ERR(svn_fs_abort_txn(txn, pool));
4997
4998 /* Now, let's try those in reverse. Open two transactions */
4999 SVN_ERR(svn_fs_begin_txn(&txn, fs, new_rev, pool));
5000 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
5001 SVN_ERR(svn_fs_begin_txn(&txn2, fs, new_rev, pool));
5002 SVN_ERR(svn_fs_txn_root(&txn_root2, txn2, pool));
5003
5004 /* Change a child file in one. */
5005 SVN_ERR(svn_test__set_file_contents(txn_root, "/A/B/E/alpha",
5006 "New contents", pool));
5007
5008 /* Change dir props in the other. */
5009 SET_STR(&pval, "/A/C:1");
5010 SVN_ERR(svn_fs_change_node_prop(txn_root2, "/A/B", "svn:mergeinfo",
5011 &pval, pool));
5012
5013 /* Commit the first one first. */
5014 SVN_ERR(test_commit_txn(&new_rev, txn, NULL, pool));
5015
5016 /* Some backends are cleverer than others. */
5017 if (is_bdb)
5018 {
5019 /* Then commit the second -- but expect an conflict because the
5020 directory wasn't up-to-date, which is required for propchanges. */
5021 SVN_ERR(test_commit_txn(¬_rev, txn2, "/A/B", pool));
5022 SVN_ERR(svn_fs_abort_txn(txn2, pool));
5023 }
5024 else
5025 {
5026 /* Then commit the second -- there will be no conflict despite the
5027 directory being out-of-data because the properties as well as the
5028 directory structure (list of nodes) was up-to-date. */
5029 SVN_ERR(test_commit_txn(¬_rev, txn2, NULL, pool));
5030 }
5031
5032 return SVN_NO_ERROR;
5033 }
5034
5035 static svn_error_t *
set_uuid(const svn_test_opts_t * opts,apr_pool_t * pool)5036 set_uuid(const svn_test_opts_t *opts,
5037 apr_pool_t *pool)
5038 {
5039 svn_fs_t *fs;
5040 const char *fixed_uuid = svn_uuid_generate(pool);
5041 const char *fetched_uuid;
5042
5043 /* Prepare a filesystem. */
5044 SVN_ERR(svn_test__create_fs(&fs, "test-repo-set-uuid",
5045 opts, pool));
5046
5047 /* Set the repository UUID to something fixed. */
5048 SVN_ERR(svn_fs_set_uuid(fs, fixed_uuid, pool));
5049
5050 /* Make sure we get back what we set. */
5051 SVN_ERR(svn_fs_get_uuid(fs, &fetched_uuid, pool));
5052 if (strcmp(fixed_uuid, fetched_uuid) != 0)
5053 return svn_error_createf
5054 (SVN_ERR_TEST_FAILED, NULL, "expected UUID '%s'; got '%s'",
5055 fixed_uuid, fetched_uuid);
5056
5057 /* Set the repository UUID to something new (and unknown). */
5058 SVN_ERR(svn_fs_set_uuid(fs, NULL, pool));
5059
5060 /* Make sure we *don't* get back what we previously set (after all,
5061 this stuff is supposed to be universally unique!). */
5062 SVN_ERR(svn_fs_get_uuid(fs, &fetched_uuid, pool));
5063 if (strcmp(fixed_uuid, fetched_uuid) == 0)
5064 return svn_error_createf
5065 (SVN_ERR_TEST_FAILED, NULL,
5066 "expected something other than UUID '%s', but got that one",
5067 fixed_uuid);
5068
5069 return SVN_NO_ERROR;
5070 }
5071
5072 static svn_error_t *
node_origin_rev(const svn_test_opts_t * opts,apr_pool_t * pool)5073 node_origin_rev(const svn_test_opts_t *opts,
5074 apr_pool_t *pool)
5075 {
5076 apr_pool_t *subpool = svn_pool_create(pool);
5077 svn_fs_t *fs;
5078 svn_fs_txn_t *txn;
5079 svn_fs_root_t *txn_root, *root;
5080 svn_revnum_t youngest_rev = 0;
5081 int i;
5082
5083 struct path_rev_t {
5084 const char *path;
5085 svn_revnum_t rev;
5086 };
5087
5088 /* Create the repository. */
5089 SVN_ERR(svn_test__create_fs(&fs, "test-repo-node-origin-rev",
5090 opts, pool));
5091
5092 /* Revision 1: Create the Greek tree. */
5093 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
5094 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
5095 SVN_ERR(svn_test__create_greek_tree(txn_root, subpool));
5096 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
5097 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
5098 svn_pool_clear(subpool);
5099
5100 /* Revision 2: Modify A/D/H/chi and A/B/E/alpha. */
5101 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
5102 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
5103 SVN_ERR(svn_test__set_file_contents(txn_root, "A/D/H/chi", "2", subpool));
5104 SVN_ERR(svn_test__set_file_contents(txn_root, "A/B/E/alpha", "2", subpool));
5105 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
5106 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
5107 svn_pool_clear(subpool);
5108
5109 /* Revision 3: Copy A/D to A/D2, and create A/D2/floop new. */
5110 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
5111 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
5112 SVN_ERR(svn_fs_revision_root(&root, fs, youngest_rev, subpool));
5113 SVN_ERR(svn_fs_copy(root, "A/D", txn_root, "A/D2", subpool));
5114 SVN_ERR(svn_fs_make_file(txn_root, "A/D2/floop", subpool));
5115 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
5116 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
5117 svn_pool_clear(subpool);
5118
5119 /* Revision 4: Modify A/D/H/chi and A/D2/H/chi. */
5120 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
5121 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
5122 SVN_ERR(svn_test__set_file_contents(txn_root, "A/D/H/chi", "4", subpool));
5123 SVN_ERR(svn_test__set_file_contents(txn_root, "A/D2/H/chi", "4", subpool));
5124 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
5125 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
5126 svn_pool_clear(subpool);
5127
5128 /* Revision 5: Delete A/D2/G, add A/B/E/alfalfa. */
5129 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
5130 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
5131 SVN_ERR(svn_fs_delete(txn_root, "A/D2/G", subpool));
5132 SVN_ERR(svn_fs_make_file(txn_root, "A/B/E/alfalfa", subpool));
5133 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
5134 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
5135 svn_pool_clear(subpool);
5136
5137 /* Revision 6: Restore A/D2/G (from version 4). */
5138 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
5139 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
5140 SVN_ERR(svn_fs_revision_root(&root, fs, 4, subpool));
5141 SVN_ERR(svn_fs_copy(root, "A/D2/G", txn_root, "A/D2/G", subpool));
5142 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
5143 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
5144 svn_pool_clear(subpool);
5145
5146 /* Revision 7: Move A/D2 to A/D (replacing it), Add a new file A/D2,
5147 and tweak A/D/floop. */
5148 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
5149 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
5150 SVN_ERR(svn_fs_revision_root(&root, fs, youngest_rev, subpool));
5151 SVN_ERR(svn_fs_delete(txn_root, "A/D", subpool));
5152 SVN_ERR(svn_fs_copy(root, "A/D2", txn_root, "A/D", subpool));
5153 SVN_ERR(svn_fs_delete(txn_root, "A/D2", subpool));
5154 SVN_ERR(svn_fs_make_file(txn_root, "A/D2", subpool));
5155 SVN_ERR(svn_test__set_file_contents(txn_root, "A/D/floop", "7", subpool));
5156 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
5157 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
5158 svn_pool_clear(subpool);
5159
5160 /* Now test some origin revisions. */
5161 {
5162 struct path_rev_t pathrevs[5] = { { "A/D", 1 },
5163 { "A/D/floop", 3 },
5164 { "A/D2", 7 },
5165 { "iota", 1 },
5166 { "A/B/E/alfalfa", 5 } };
5167
5168 SVN_ERR(svn_fs_revision_root(&root, fs, youngest_rev, pool));
5169 for (i = 0; i < (sizeof(pathrevs) / sizeof(struct path_rev_t)); i++)
5170 {
5171 struct path_rev_t path_rev = pathrevs[i];
5172 svn_revnum_t revision;
5173 SVN_ERR(svn_fs_node_origin_rev(&revision, root, path_rev.path, pool));
5174 if (path_rev.rev != revision)
5175 return svn_error_createf
5176 (SVN_ERR_TEST_FAILED, NULL,
5177 "expected origin revision of '%ld' for '%s'; got '%ld'",
5178 path_rev.rev, path_rev.path, revision);
5179 }
5180 }
5181
5182 /* Also, we'll check a couple of queries into a transaction root. */
5183 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
5184 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
5185 SVN_ERR(svn_fs_make_file(txn_root, "bloop", subpool));
5186 SVN_ERR(svn_fs_make_dir(txn_root, "A/D/blarp", subpool));
5187
5188 {
5189 struct path_rev_t pathrevs[6] = { { "A/D", 1 },
5190 { "A/D/floop", 3 },
5191 { "bloop", -1 },
5192 { "A/D/blarp", -1 },
5193 { "iota", 1 },
5194 { "A/B/E/alfalfa", 5 } };
5195
5196 root = txn_root;
5197 for (i = 0; i < (sizeof(pathrevs) / sizeof(struct path_rev_t)); i++)
5198 {
5199 struct path_rev_t path_rev = pathrevs[i];
5200 svn_revnum_t revision;
5201 SVN_ERR(svn_fs_node_origin_rev(&revision, root, path_rev.path, pool));
5202 if (! SVN_IS_VALID_REVNUM(revision))
5203 revision = -1;
5204 if (path_rev.rev != revision)
5205 return svn_error_createf
5206 (SVN_ERR_TEST_FAILED, NULL,
5207 "expected origin revision of '%ld' for '%s'; got '%ld'",
5208 path_rev.rev, path_rev.path, revision);
5209 }
5210 }
5211
5212 return SVN_NO_ERROR;
5213 }
5214
5215
5216 /* Helper: call svn_fs_history_location() and check the results. */
5217 static svn_error_t *
check_history_location(const char * expected_path,svn_revnum_t expected_revision,svn_fs_history_t * history,apr_pool_t * pool)5218 check_history_location(const char *expected_path,
5219 svn_revnum_t expected_revision,
5220 svn_fs_history_t *history,
5221 apr_pool_t *pool)
5222 {
5223 const char *actual_path;
5224 svn_revnum_t actual_revision;
5225
5226 SVN_ERR(svn_fs_history_location(&actual_path, &actual_revision,
5227 history, pool));
5228
5229 /* Validate the location against our expectations. */
5230 if (actual_revision != expected_revision
5231 || (actual_path && expected_path && strcmp(actual_path, expected_path))
5232 || (actual_path != NULL) != (expected_path != NULL))
5233 {
5234 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
5235 "svn_fs_history_location() failed:\n"
5236 " expected '%s@%ld'\n"
5237 " found '%s@%ld",
5238 expected_path ? expected_path : "(null)",
5239 expected_revision,
5240 actual_path ? actual_path : "(null)",
5241 actual_revision);
5242 }
5243
5244 return SVN_NO_ERROR;
5245 }
5246
5247 /* Test svn_fs_history_*(). */
5248 static svn_error_t *
node_history(const svn_test_opts_t * opts,apr_pool_t * pool)5249 node_history(const svn_test_opts_t *opts,
5250 apr_pool_t *pool)
5251 {
5252 svn_fs_t *fs;
5253 svn_fs_txn_t *txn;
5254 svn_fs_root_t *txn_root;
5255 svn_revnum_t after_rev;
5256
5257 /* Prepare a txn to receive the greek tree. */
5258 SVN_ERR(svn_test__create_fs(&fs, "test-repo-node-history",
5259 opts, pool));
5260 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
5261 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
5262
5263 /* Create and verify the greek tree. */
5264 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
5265 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
5266
5267 /* Make some changes, following copy_test() above. */
5268
5269 /* r2: copy pi to pi2, with textmods. */
5270 {
5271 svn_fs_root_t *rev_root;
5272
5273 SVN_ERR(svn_fs_revision_root(&rev_root, fs, after_rev, pool));
5274 SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, pool));
5275 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
5276 SVN_ERR(svn_fs_copy(rev_root, "A/D/G/pi",
5277 txn_root, "A/D/H/pi2",
5278 pool));
5279 SVN_ERR(svn_test__set_file_contents
5280 (txn_root, "A/D/H/pi2", "This is the file 'pi2'.\n", pool));
5281 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool));
5282 }
5283
5284 /* Go back in history: pi2@r2 -> pi@r1 */
5285 {
5286 svn_fs_history_t *history;
5287 svn_fs_root_t *rev_root;
5288
5289 SVN_ERR(svn_fs_revision_root(&rev_root, fs, after_rev, pool));
5290
5291 /* Fetch a history object, and walk it until its start. */
5292
5293 SVN_ERR(svn_fs_node_history(&history, rev_root, "A/D/H/pi2", pool));
5294 SVN_ERR(check_history_location("/A/D/H/pi2", 2, history, pool));
5295
5296 SVN_ERR(svn_fs_history_prev(&history, history, TRUE, pool));
5297 SVN_ERR(check_history_location("/A/D/H/pi2", 2, history, pool));
5298
5299 SVN_ERR(svn_fs_history_prev(&history, history, TRUE, pool));
5300 SVN_ERR(check_history_location("/A/D/G/pi", 1, history, pool));
5301
5302 SVN_ERR(svn_fs_history_prev(&history, history, TRUE, pool));
5303 SVN_TEST_ASSERT(history == NULL);
5304 }
5305
5306 return SVN_NO_ERROR;
5307 }
5308
5309 /* Test svn_fs_delete_fs(). */
5310 static svn_error_t *
delete_fs(const svn_test_opts_t * opts,apr_pool_t * pool)5311 delete_fs(const svn_test_opts_t *opts,
5312 apr_pool_t *pool)
5313 {
5314 const char *path;
5315 svn_node_kind_t kind;
5316
5317 /* We have to use a subpool to close the svn_fs_t before calling
5318 svn_fs_delete_fs. See issue 4264. */
5319 {
5320 svn_fs_t *fs;
5321 apr_pool_t *subpool = svn_pool_create(pool);
5322 SVN_ERR(svn_test__create_fs(&fs, "test-repo-delete-fs", opts, subpool));
5323 path = svn_fs_path(fs, pool);
5324 svn_pool_destroy(subpool);
5325 }
5326
5327 SVN_ERR(svn_io_check_path(path, &kind, pool));
5328 SVN_TEST_ASSERT(kind != svn_node_none);
5329 SVN_ERR(svn_fs_delete_fs(path, pool));
5330 SVN_ERR(svn_io_check_path(path, &kind, pool));
5331 SVN_TEST_ASSERT(kind == svn_node_none);
5332
5333 /* Recreate dir so that test cleanup doesn't fail. */
5334 SVN_ERR(svn_io_dir_make(path, APR_OS_DEFAULT, pool));
5335
5336 return SVN_NO_ERROR;
5337 }
5338
5339 /* Issue 4340, "filenames containing \n corrupt FSFS repositories" */
5340 static svn_error_t *
filename_trailing_newline(const svn_test_opts_t * opts,apr_pool_t * pool)5341 filename_trailing_newline(const svn_test_opts_t *opts,
5342 apr_pool_t *pool)
5343 {
5344 apr_pool_t *subpool = svn_pool_create(pool);
5345 svn_fs_t *fs;
5346 svn_fs_txn_t *txn;
5347 svn_fs_root_t *txn_root, *root;
5348 svn_revnum_t youngest_rev = 0;
5349 svn_error_t *err;
5350
5351 /* The FS API wants \n to be permitted, but FSFS never implemented that.
5352 * Moreover, formats like svn:mergeinfo and svn:externals don't support
5353 * it either. So, we can't have newlines in file names in any FS.
5354 */
5355 SVN_ERR(svn_test__create_fs(&fs, "test-repo-filename-trailing-newline",
5356 opts, pool));
5357
5358 /* Revision 1: Add a directory /foo */
5359 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
5360 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
5361 SVN_ERR(svn_fs_make_dir(txn_root, "/foo", subpool));
5362 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
5363 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
5364 svn_pool_clear(subpool);
5365
5366 /* Attempt to copy /foo to "/bar\n". This should fail. */
5367 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
5368 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
5369 SVN_ERR(svn_fs_revision_root(&root, fs, youngest_rev, subpool));
5370 err = svn_fs_copy(root, "/foo", txn_root, "/bar\n", subpool);
5371 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_PATH_SYNTAX);
5372
5373 /* Attempt to create a file /foo/baz\n. This should fail. */
5374 err = svn_fs_make_file(txn_root, "/foo/baz\n", subpool);
5375 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_PATH_SYNTAX);
5376
5377 /* Attempt to create a directory /foo/bang\n. This should fail. */
5378 err = svn_fs_make_dir(txn_root, "/foo/bang\n", subpool);
5379 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_PATH_SYNTAX);
5380
5381 return SVN_NO_ERROR;
5382 }
5383
5384 static svn_error_t *
test_fs_info_format(const svn_test_opts_t * opts,apr_pool_t * pool)5385 test_fs_info_format(const svn_test_opts_t *opts,
5386 apr_pool_t *pool)
5387 {
5388 svn_fs_t *fs;
5389 int fs_format;
5390 svn_version_t *supports_version;
5391 svn_version_t v1_5_0 = {1, 5, 0, ""};
5392 svn_version_t v1_10_0 = {1, 10, 0, ""};
5393 svn_test_opts_t opts2;
5394 svn_boolean_t is_fsx = strcmp(opts->fs_type, "fsx") == 0;
5395
5396 opts2 = *opts;
5397 opts2.server_minor_version = is_fsx ? 10 : 5;
5398
5399 SVN_ERR(svn_test__create_fs(&fs, "test-repo-fs-format-info", &opts2, pool));
5400 SVN_ERR(svn_fs_info_format(&fs_format, &supports_version, fs, pool, pool));
5401
5402 if (is_fsx)
5403 {
5404 SVN_TEST_ASSERT(fs_format == 2);
5405 SVN_TEST_ASSERT(svn_ver_equal(supports_version, &v1_10_0));
5406 }
5407 else
5408 {
5409 /* happens to be the same for FSFS and BDB */
5410 SVN_TEST_ASSERT(fs_format == 3);
5411 SVN_TEST_ASSERT(svn_ver_equal(supports_version, &v1_5_0));
5412 }
5413
5414 return SVN_NO_ERROR;
5415 }
5416
5417 /* Sleeps until apr_time_now() value changes. */
sleep_for_timestamps(void)5418 static void sleep_for_timestamps(void)
5419 {
5420 apr_time_t start = apr_time_now();
5421
5422 while (start == apr_time_now())
5423 {
5424 apr_sleep(APR_USEC_PER_SEC / 1000);
5425 }
5426 }
5427
5428 static svn_error_t *
commit_timestamp(const svn_test_opts_t * opts,apr_pool_t * pool)5429 commit_timestamp(const svn_test_opts_t *opts,
5430 apr_pool_t *pool)
5431 {
5432 svn_fs_t *fs;
5433 svn_fs_txn_t *txn;
5434 svn_fs_root_t *txn_root;
5435 svn_string_t *date = svn_string_create("Yesterday", pool);
5436 svn_revnum_t rev = 0;
5437 apr_hash_t *proplist;
5438 svn_string_t *svn_date;
5439 svn_string_t *txn_svn_date;
5440
5441 SVN_ERR(svn_test__create_fs(&fs, "test-repo-fs-commit-timestamp",
5442 opts, pool));
5443
5444 /* Commit with a specified svn:date. */
5445 SVN_ERR(svn_fs_begin_txn2(&txn, fs, rev, SVN_FS_TXN_CLIENT_DATE, pool));
5446 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
5447 SVN_ERR(svn_fs_make_dir(txn_root, "/foo", pool));
5448 SVN_ERR(svn_fs_change_txn_prop(txn, SVN_PROP_REVISION_DATE, date, pool));
5449 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, pool));
5450
5451 SVN_ERR(svn_fs_revision_proplist(&proplist, fs, rev, pool));
5452 svn_date = apr_hash_get(proplist, SVN_PROP_REVISION_DATE,
5453 APR_HASH_KEY_STRING);
5454 SVN_TEST_ASSERT(svn_date && !strcmp(svn_date->data, date->data));
5455
5456 /* Commit that overwrites the specified svn:date. */
5457 SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, pool));
5458 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
5459 SVN_ERR(svn_fs_make_dir(txn_root, "/bar", pool));
5460 SVN_ERR(svn_fs_change_txn_prop(txn, SVN_PROP_REVISION_DATE, date, pool));
5461 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, pool));
5462
5463 SVN_ERR(svn_fs_revision_proplist(&proplist, fs, rev, pool));
5464 svn_date = apr_hash_get(proplist, SVN_PROP_REVISION_DATE,
5465 APR_HASH_KEY_STRING);
5466 SVN_TEST_ASSERT(svn_date && strcmp(svn_date->data, date->data));
5467
5468 /* Commit with a missing svn:date. */
5469 SVN_ERR(svn_fs_begin_txn2(&txn, fs, rev, SVN_FS_TXN_CLIENT_DATE, pool));
5470 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
5471 SVN_ERR(svn_fs_make_dir(txn_root, "/zag", pool));
5472 SVN_ERR(svn_fs_change_txn_prop(txn, SVN_PROP_REVISION_DATE, NULL, pool));
5473 SVN_ERR(svn_fs_txn_prop(&svn_date, txn, SVN_PROP_REVISION_DATE, pool));
5474 SVN_TEST_ASSERT(!svn_date);
5475 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, pool));
5476
5477 SVN_ERR(svn_fs_revision_proplist(&proplist, fs, rev, pool));
5478 svn_date = apr_hash_get(proplist, SVN_PROP_REVISION_DATE,
5479 APR_HASH_KEY_STRING);
5480 SVN_TEST_ASSERT(!svn_date);
5481
5482 /* Commit that overwites a missing svn:date. */
5483 SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, pool));
5484 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
5485 SVN_ERR(svn_fs_make_dir(txn_root, "/zig", pool));
5486 SVN_ERR(svn_fs_change_txn_prop(txn, SVN_PROP_REVISION_DATE, NULL, pool));
5487 SVN_ERR(svn_fs_txn_prop(&svn_date, txn, SVN_PROP_REVISION_DATE, pool));
5488 SVN_TEST_ASSERT(!svn_date);
5489 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, pool));
5490
5491 SVN_ERR(svn_fs_revision_proplist(&proplist, fs, rev, pool));
5492 svn_date = apr_hash_get(proplist, SVN_PROP_REVISION_DATE,
5493 APR_HASH_KEY_STRING);
5494 SVN_TEST_ASSERT(svn_date);
5495
5496 /* Commit that doesn't do anything special about svn:date. */
5497 SVN_ERR(svn_fs_begin_txn2(&txn, fs, rev, 0, pool));
5498 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
5499 SVN_ERR(svn_fs_make_dir(txn_root, "/zig/foo", pool));
5500 SVN_ERR(svn_fs_txn_prop(&txn_svn_date, txn, SVN_PROP_REVISION_DATE, pool));
5501 SVN_TEST_ASSERT(txn_svn_date);
5502 sleep_for_timestamps();
5503 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, pool));
5504
5505 SVN_ERR(svn_fs_revision_proplist(&proplist, fs, rev, pool));
5506 svn_date = apr_hash_get(proplist, SVN_PROP_REVISION_DATE,
5507 APR_HASH_KEY_STRING);
5508 SVN_TEST_ASSERT(svn_date);
5509 SVN_TEST_ASSERT(!svn_string_compare(svn_date, txn_svn_date));
5510
5511 /* Commit that instructs the backend to use a specific svn:date, but
5512 * doesn't provide one. This used to fail with BDB prior to r1663697. */
5513 SVN_ERR(svn_fs_begin_txn2(&txn, fs, rev, SVN_FS_TXN_CLIENT_DATE, pool));
5514 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
5515 SVN_ERR(svn_fs_make_dir(txn_root, "/zig/bar", pool));
5516 SVN_ERR(svn_fs_txn_prop(&txn_svn_date, txn, SVN_PROP_REVISION_DATE, pool));
5517 SVN_TEST_ASSERT(txn_svn_date);
5518 sleep_for_timestamps();
5519 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, pool));
5520
5521 SVN_ERR(svn_fs_revision_proplist(&proplist, fs, rev, pool));
5522 svn_date = apr_hash_get(proplist, SVN_PROP_REVISION_DATE,
5523 APR_HASH_KEY_STRING);
5524 SVN_TEST_ASSERT(svn_date);
5525 SVN_TEST_ASSERT(!svn_string_compare(svn_date, txn_svn_date));
5526
5527 return SVN_NO_ERROR;
5528 }
5529
5530 static svn_error_t *
test_compat_version(const svn_test_opts_t * opts,apr_pool_t * pool)5531 test_compat_version(const svn_test_opts_t *opts,
5532 apr_pool_t *pool)
5533 {
5534 svn_version_t *compatible_version;
5535 apr_hash_t *config = apr_hash_make(pool);
5536
5537 svn_version_t vcurrent = {SVN_VER_MAJOR, SVN_VER_MINOR, 0, ""};
5538 svn_version_t v1_2_0 = {1, 2, 0, ""};
5539 svn_version_t v1_3_0 = {1, 3, 0, ""};
5540 svn_version_t v1_5_0 = {1, 5, 0, ""};
5541
5542 /* no version specified -> default to the current one */
5543 SVN_ERR(svn_fs__compatible_version(&compatible_version, config, pool));
5544 SVN_TEST_ASSERT(svn_ver_equal(compatible_version, &vcurrent));
5545
5546 /* test specific compat option */
5547 svn_hash_sets(config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE, "1");
5548 SVN_ERR(svn_fs__compatible_version(&compatible_version, config, pool));
5549 SVN_TEST_ASSERT(svn_ver_equal(compatible_version, &v1_5_0));
5550
5551 /* test precedence amongst compat options */
5552 svn_hash_sets(config, SVN_FS_CONFIG_PRE_1_8_COMPATIBLE, "1");
5553 SVN_ERR(svn_fs__compatible_version(&compatible_version, config, pool));
5554 SVN_TEST_ASSERT(svn_ver_equal(compatible_version, &v1_5_0));
5555
5556 svn_hash_sets(config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE, "1");
5557 SVN_ERR(svn_fs__compatible_version(&compatible_version, config, pool));
5558 SVN_TEST_ASSERT(svn_ver_equal(compatible_version, &v1_3_0));
5559
5560 /* precedence should work with the generic option as well */
5561 svn_hash_sets(config, SVN_FS_CONFIG_COMPATIBLE_VERSION, "1.4.17-??");
5562 SVN_ERR(svn_fs__compatible_version(&compatible_version, config, pool));
5563 SVN_TEST_ASSERT(svn_ver_equal(compatible_version, &v1_3_0));
5564
5565 svn_hash_sets(config, SVN_FS_CONFIG_COMPATIBLE_VERSION, "1.2.3-no!");
5566 SVN_ERR(svn_fs__compatible_version(&compatible_version, config, pool));
5567 SVN_TEST_ASSERT(svn_ver_equal(compatible_version, &v1_2_0));
5568
5569 /* test generic option alone */
5570 config = apr_hash_make(pool);
5571 svn_hash_sets(config, SVN_FS_CONFIG_COMPATIBLE_VERSION, "1.2.3-no!");
5572 SVN_ERR(svn_fs__compatible_version(&compatible_version, config, pool));
5573 SVN_TEST_ASSERT(svn_ver_equal(compatible_version, &v1_2_0));
5574
5575 /* out of range values should be caped by the current tool version */
5576 svn_hash_sets(config, SVN_FS_CONFIG_COMPATIBLE_VERSION, "2.3.4-x");
5577 SVN_ERR(svn_fs__compatible_version(&compatible_version, config, pool));
5578 SVN_TEST_ASSERT(svn_ver_equal(compatible_version, &vcurrent));
5579
5580 return SVN_NO_ERROR;
5581 }
5582
5583 static svn_error_t *
dir_prop_merge(const svn_test_opts_t * opts,apr_pool_t * pool)5584 dir_prop_merge(const svn_test_opts_t *opts,
5585 apr_pool_t *pool)
5586 {
5587 svn_fs_t *fs;
5588 svn_revnum_t head_rev;
5589 svn_fs_root_t *root;
5590 svn_fs_txn_t *txn, *mid_txn, *top_txn, *sub_txn, *c_txn;
5591 svn_boolean_t is_bdb = strcmp(opts->fs_type, SVN_FS_TYPE_BDB) == 0;
5592
5593 /* Create test repository. */
5594 SVN_ERR(svn_test__create_fs(&fs, "test-repo-fs-dir_prop-merge", opts,
5595 pool));
5596
5597 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
5598 SVN_ERR(svn_fs_txn_root(&root, txn, pool));
5599
5600 /* Create and verify the greek tree. */
5601 SVN_ERR(svn_test__create_greek_tree(root, pool));
5602 SVN_ERR(test_commit_txn(&head_rev, txn, NULL, pool));
5603
5604 /* Start concurrent transactions */
5605
5606 /* 1st: modify a mid-level directory */
5607 SVN_ERR(svn_fs_begin_txn2(&mid_txn, fs, head_rev, 0, pool));
5608 SVN_ERR(svn_fs_txn_root(&root, mid_txn, pool));
5609 SVN_ERR(svn_fs_change_node_prop(root, "A/D", "test-prop",
5610 svn_string_create("val1", pool), pool));
5611 svn_fs_close_root(root);
5612
5613 /* 2st: modify a top-level directory */
5614 SVN_ERR(svn_fs_begin_txn2(&top_txn, fs, head_rev, 0, pool));
5615 SVN_ERR(svn_fs_txn_root(&root, top_txn, pool));
5616 SVN_ERR(svn_fs_change_node_prop(root, "A", "test-prop",
5617 svn_string_create("val2", pool), pool));
5618 svn_fs_close_root(root);
5619
5620 SVN_ERR(svn_fs_begin_txn2(&sub_txn, fs, head_rev, 0, pool));
5621 SVN_ERR(svn_fs_txn_root(&root, sub_txn, pool));
5622 SVN_ERR(svn_fs_change_node_prop(root, "A/D/G", "test-prop",
5623 svn_string_create("val3", pool), pool));
5624 svn_fs_close_root(root);
5625
5626 /* 3rd: modify a conflicting change to the mid-level directory */
5627 SVN_ERR(svn_fs_begin_txn2(&c_txn, fs, head_rev, 0, pool));
5628 SVN_ERR(svn_fs_txn_root(&root, c_txn, pool));
5629 SVN_ERR(svn_fs_change_node_prop(root, "A/D", "test-prop",
5630 svn_string_create("valX", pool), pool));
5631 svn_fs_close_root(root);
5632
5633 /* Prop changes to the same node should conflict */
5634 SVN_ERR(test_commit_txn(&head_rev, mid_txn, NULL, pool));
5635 SVN_ERR(test_commit_txn(&head_rev, c_txn, "/A/D", pool));
5636 SVN_ERR(svn_fs_abort_txn(c_txn, pool));
5637
5638 /* Changes in a sub-tree should not conflict with prop changes to some
5639 parent directory but some backends are cleverer than others. */
5640 if (is_bdb)
5641 {
5642 SVN_ERR(test_commit_txn(&head_rev, top_txn, "/A", pool));
5643 SVN_ERR(svn_fs_abort_txn(top_txn, pool));
5644 }
5645 else
5646 {
5647 SVN_ERR(test_commit_txn(&head_rev, top_txn, NULL, pool));
5648 }
5649
5650 /* The inverted case is not that trivial to handle. Hence, conflict.
5651 Depending on the checking order, the reported conflict path differs. */
5652 SVN_ERR(test_commit_txn(&head_rev, sub_txn, is_bdb ? "/A/D" : "/A", pool));
5653 SVN_ERR(svn_fs_abort_txn(sub_txn, pool));
5654
5655 return SVN_NO_ERROR;
5656 }
5657
5658 static svn_error_t *
upgrade_while_committing(const svn_test_opts_t * opts,apr_pool_t * pool)5659 upgrade_while_committing(const svn_test_opts_t *opts,
5660 apr_pool_t *pool)
5661 {
5662 svn_fs_t *fs;
5663 svn_revnum_t head_rev = 0;
5664 svn_fs_root_t *root;
5665 svn_fs_txn_t *txn1, *txn2;
5666 const char *fs_path;
5667 apr_hash_t *fs_config = apr_hash_make(pool);
5668
5669 /* Bail (with success) on known-untestable scenarios */
5670 if (strcmp(opts->fs_type, "fsfs") != 0)
5671 return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
5672 "this will test FSFS repositories only");
5673
5674 if (opts->server_minor_version && (opts->server_minor_version < 6))
5675 return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
5676 "pre-1.6 SVN doesn't support FSFS packing");
5677
5678 /* Create test repository with greek tree. */
5679 fs_path = "test-repo-upgrade-while-committing";
5680
5681 svn_hash_sets(fs_config, SVN_FS_CONFIG_COMPATIBLE_VERSION, "1.7");
5682 svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_SHARD_SIZE, "2");
5683 SVN_ERR(svn_test__create_fs2(&fs, fs_path, opts, fs_config, pool));
5684
5685 SVN_ERR(svn_fs_begin_txn(&txn1, fs, head_rev, pool));
5686 SVN_ERR(svn_fs_txn_root(&root, txn1, pool));
5687 SVN_ERR(svn_test__create_greek_tree(root, pool));
5688 SVN_ERR(test_commit_txn(&head_rev, txn1, NULL, pool));
5689
5690 /* Create txn with changes. */
5691 SVN_ERR(svn_fs_begin_txn(&txn1, fs, head_rev, pool));
5692 SVN_ERR(svn_fs_txn_root(&root, txn1, pool));
5693 SVN_ERR(svn_fs_make_dir(root, "/foo", pool));
5694
5695 /* Upgrade filesystem, but keep existing svn_fs_t object. */
5696 SVN_ERR(svn_fs_upgrade(fs_path, pool));
5697
5698 /* Creating a new txn for the old svn_fs_t should not fail. */
5699 SVN_ERR(svn_fs_begin_txn(&txn2, fs, head_rev, pool));
5700
5701 /* Committing the already existing txn should not fail. */
5702 SVN_ERR(test_commit_txn(&head_rev, txn1, NULL, pool));
5703
5704 /* Verify filesystem content. */
5705 SVN_ERR(svn_fs_verify(fs_path, NULL, 0, SVN_INVALID_REVNUM, NULL, NULL,
5706 NULL, NULL, pool));
5707
5708 return SVN_NO_ERROR;
5709 }
5710
5711 /* Utility method for test_paths_changed. Verify that REV in FS changes
5712 * exactly one path and that that change is a property change. Expect
5713 * the MERGEINFO_MOD flag of the change to have the given value.
5714 */
5715 static svn_error_t *
verify_root_prop_change(svn_fs_t * fs,svn_revnum_t rev,svn_tristate_t mergeinfo_mod,apr_pool_t * pool)5716 verify_root_prop_change(svn_fs_t *fs,
5717 svn_revnum_t rev,
5718 svn_tristate_t mergeinfo_mod,
5719 apr_pool_t *pool)
5720 {
5721 svn_fs_path_change2_t *change;
5722 svn_fs_root_t *root;
5723 apr_hash_t *changes;
5724
5725 SVN_ERR(svn_fs_revision_root(&root, fs, rev, pool));
5726 SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
5727 SVN_TEST_ASSERT(apr_hash_count(changes) == 1);
5728 change = svn_hash_gets(changes, "/");
5729
5730 SVN_TEST_ASSERT(change->node_rev_id);
5731 SVN_TEST_ASSERT(change->change_kind == svn_fs_path_change_modify);
5732 SVN_TEST_ASSERT( change->node_kind == svn_node_dir
5733 || change->node_kind == svn_node_unknown);
5734 SVN_TEST_ASSERT(change->text_mod == FALSE);
5735 SVN_TEST_ASSERT(change->prop_mod == TRUE);
5736
5737 if (change->copyfrom_known)
5738 {
5739 SVN_TEST_ASSERT(change->copyfrom_rev == SVN_INVALID_REVNUM);
5740 SVN_TEST_ASSERT(change->copyfrom_path == NULL);
5741 }
5742
5743 SVN_TEST_ASSERT(change->mergeinfo_mod == mergeinfo_mod);
5744
5745 return SVN_NO_ERROR;
5746 }
5747
5748 static svn_error_t *
test_paths_changed(const svn_test_opts_t * opts,apr_pool_t * pool)5749 test_paths_changed(const svn_test_opts_t *opts,
5750 apr_pool_t *pool)
5751 {
5752 svn_fs_t *fs;
5753 svn_revnum_t head_rev = 0;
5754 svn_fs_root_t *root;
5755 svn_fs_txn_t *txn;
5756 const char *fs_path;
5757 apr_hash_t *changes;
5758 svn_boolean_t has_mergeinfo_mod = FALSE;
5759 apr_hash_index_t *hi;
5760 int i;
5761
5762 /* The "mergeinfo_mod flag will say "unknown" until recently. */
5763 if ( strcmp(opts->fs_type, SVN_FS_TYPE_BDB) != 0
5764 && (!opts->server_minor_version || (opts->server_minor_version >= 9)))
5765 has_mergeinfo_mod = TRUE;
5766
5767 /* Create test repository with greek tree. */
5768 fs_path = "test-repo-paths-changed";
5769
5770 SVN_ERR(svn_test__create_fs2(&fs, fs_path, opts, NULL, pool));
5771
5772 SVN_ERR(svn_fs_begin_txn(&txn, fs, head_rev, pool));
5773 SVN_ERR(svn_fs_txn_root(&root, txn, pool));
5774 SVN_ERR(svn_test__create_greek_tree(root, pool));
5775 SVN_ERR(test_commit_txn(&head_rev, txn, NULL, pool));
5776
5777 /* Create txns with various prop changes. */
5778 SVN_ERR(svn_fs_begin_txn(&txn, fs, head_rev, pool));
5779 SVN_ERR(svn_fs_txn_root(&root, txn, pool));
5780 SVN_ERR(svn_fs_change_node_prop(root, "/", "propname",
5781 svn_string_create("propval", pool), pool));
5782 SVN_ERR(test_commit_txn(&head_rev, txn, NULL, pool));
5783
5784 SVN_ERR(svn_fs_begin_txn(&txn, fs, head_rev, pool));
5785 SVN_ERR(svn_fs_txn_root(&root, txn, pool));
5786 SVN_ERR(svn_fs_change_node_prop(root, "/", "svn:mergeinfo",
5787 svn_string_create("/: 1\n", pool), pool));
5788 SVN_ERR(test_commit_txn(&head_rev, txn, NULL, pool));
5789
5790 /* Verify changed path lists. */
5791
5792 /* Greek tree creation rev. */
5793 SVN_ERR(svn_fs_revision_root(&root, fs, head_rev - 2, pool));
5794 SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
5795
5796 /* Reports all paths? */
5797 for (i = 0; svn_test__greek_tree_nodes[i].path; ++i)
5798 {
5799 const char *path
5800 = svn_fspath__canonicalize(svn_test__greek_tree_nodes[i].path, pool);
5801
5802 SVN_TEST_ASSERT(svn_hash_gets(changes, path));
5803 }
5804
5805 SVN_TEST_ASSERT(apr_hash_count(changes) == i);
5806
5807 /* Verify per-path info. */
5808 for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
5809 {
5810 svn_fs_path_change2_t *change = apr_hash_this_val(hi);
5811
5812 SVN_TEST_ASSERT(change->node_rev_id);
5813 SVN_TEST_ASSERT(change->change_kind == svn_fs_path_change_add);
5814 SVN_TEST_ASSERT( change->node_kind == svn_node_file
5815 || change->node_kind == svn_node_dir
5816 || change->node_kind == svn_node_unknown);
5817
5818 if (change->node_kind != svn_node_unknown)
5819 SVN_TEST_ASSERT(change->text_mod == ( change->node_kind
5820 == svn_node_file));
5821
5822 SVN_TEST_ASSERT(change->prop_mod == FALSE);
5823
5824 if (change->copyfrom_known)
5825 {
5826 SVN_TEST_ASSERT(change->copyfrom_rev == SVN_INVALID_REVNUM);
5827 SVN_TEST_ASSERT(change->copyfrom_path == NULL);
5828 }
5829
5830 if (has_mergeinfo_mod)
5831 SVN_TEST_ASSERT(change->mergeinfo_mod == svn_tristate_false);
5832 else
5833 SVN_TEST_ASSERT(change->mergeinfo_mod == svn_tristate_unknown);
5834 }
5835
5836 /* Propset rev. */
5837 SVN_ERR(verify_root_prop_change(fs, head_rev - 1,
5838 has_mergeinfo_mod ? svn_tristate_false
5839 : svn_tristate_unknown,
5840 pool));
5841
5842 /* Mergeinfo set rev. */
5843 SVN_ERR(verify_root_prop_change(fs, head_rev,
5844 has_mergeinfo_mod ? svn_tristate_true
5845 : svn_tristate_unknown,
5846 pool));
5847
5848 return SVN_NO_ERROR;
5849 }
5850
5851 static svn_error_t *
test_delete_replaced_paths_changed(const svn_test_opts_t * opts,apr_pool_t * pool)5852 test_delete_replaced_paths_changed(const svn_test_opts_t *opts,
5853 apr_pool_t *pool)
5854 {
5855 svn_fs_t *fs;
5856 svn_revnum_t head_rev = 0;
5857 svn_fs_root_t *root;
5858 svn_fs_txn_t *txn;
5859 const char *fs_path;
5860 apr_hash_t *changes;
5861 svn_fs_path_change2_t *change;
5862 const svn_fs_id_t *file_id;
5863
5864 /* Create test repository with greek tree. */
5865 fs_path = "test-repo-delete-replace-paths-changed";
5866
5867 SVN_ERR(svn_test__create_fs2(&fs, fs_path, opts, NULL, pool));
5868
5869 SVN_ERR(svn_fs_begin_txn(&txn, fs, head_rev, pool));
5870 SVN_ERR(svn_fs_txn_root(&root, txn, pool));
5871 SVN_ERR(svn_test__create_greek_tree(root, pool));
5872 SVN_ERR(test_commit_txn(&head_rev, txn, NULL, pool));
5873
5874 /* Create that replaces a file with a folder and then deletes that
5875 * replacement. Start with the deletion. */
5876 SVN_ERR(svn_fs_begin_txn(&txn, fs, head_rev, pool));
5877 SVN_ERR(svn_fs_txn_root(&root, txn, pool));
5878 SVN_ERR(svn_fs_delete(root, "/iota", pool));
5879
5880 /* The change list should now report a deleted file. */
5881 SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
5882 change = svn_hash_gets(changes, "/iota");
5883 file_id = change->node_rev_id;
5884 SVN_TEST_ASSERT( change->node_kind == svn_node_file
5885 || change->node_kind == svn_node_unknown);
5886 SVN_TEST_ASSERT(change->change_kind == svn_fs_path_change_delete);
5887
5888 /* Add a replacement. */
5889 SVN_ERR(svn_fs_make_dir(root, "/iota", pool));
5890
5891 /* The change list now reports a replacement by a directory. */
5892 SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
5893 change = svn_hash_gets(changes, "/iota");
5894 SVN_TEST_ASSERT( change->node_kind == svn_node_dir
5895 || change->node_kind == svn_node_unknown);
5896 SVN_TEST_ASSERT(change->change_kind == svn_fs_path_change_replace);
5897 SVN_TEST_ASSERT(svn_fs_compare_ids(change->node_rev_id, file_id) != 0);
5898
5899 /* Delete the replacement again. */
5900 SVN_ERR(svn_fs_delete(root, "/iota", pool));
5901
5902 /* The change list should now be reported as a deleted file again. */
5903 SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
5904 change = svn_hash_gets(changes, "/iota");
5905 SVN_TEST_ASSERT( change->node_kind == svn_node_file
5906 || change->node_kind == svn_node_unknown);
5907 SVN_TEST_ASSERT(change->change_kind == svn_fs_path_change_delete);
5908 SVN_TEST_ASSERT(svn_fs_compare_ids(change->node_rev_id, file_id) == 0);
5909
5910 /* Finally, commit the change. */
5911 SVN_ERR(test_commit_txn(&head_rev, txn, NULL, pool));
5912
5913 /* The committed revision should still report the same change. */
5914 SVN_ERR(svn_fs_revision_root(&root, fs, head_rev, pool));
5915 SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
5916 change = svn_hash_gets(changes, "/iota");
5917 SVN_TEST_ASSERT( change->node_kind == svn_node_file
5918 || change->node_kind == svn_node_unknown);
5919 SVN_TEST_ASSERT(change->change_kind == svn_fs_path_change_delete);
5920
5921 return SVN_NO_ERROR;
5922 }
5923
5924 /* Get rid of transaction NAME in FS. This function deals with backend-
5925 * specific behavior as permitted by the API. */
5926 static svn_error_t *
cleanup_txn(svn_fs_t * fs,const char * name,apr_pool_t * scratch_pool)5927 cleanup_txn(svn_fs_t *fs,
5928 const char *name,
5929 apr_pool_t *scratch_pool)
5930 {
5931 /* Get rid of the txns one at a time. */
5932 svn_error_t *err = svn_fs_purge_txn(fs, name, scratch_pool);
5933
5934 /* Some backends (BDB) don't support purging transactions that have never
5935 * seen an abort or commit attempt. Simply abort those txns. */
5936 if (err && err->apr_err == SVN_ERR_FS_TRANSACTION_NOT_DEAD)
5937 {
5938 svn_fs_txn_t *txn;
5939 svn_error_clear(err);
5940 err = SVN_NO_ERROR;
5941
5942 SVN_ERR(svn_fs_open_txn(&txn, fs, name, scratch_pool));
5943 SVN_ERR(svn_fs_abort_txn(txn, scratch_pool));
5944
5945 /* Should be gone now ... */
5946 SVN_TEST_ASSERT_ERROR(svn_fs_open_txn(&txn, fs, name, scratch_pool),
5947 SVN_ERR_FS_NO_SUCH_TRANSACTION);
5948 }
5949
5950 return svn_error_trace(err);
5951 }
5952
5953 /* Make sure we get txn lists correctly. */
5954 static svn_error_t *
purge_txn_test(const svn_test_opts_t * opts,apr_pool_t * pool)5955 purge_txn_test(const svn_test_opts_t *opts,
5956 apr_pool_t *pool)
5957 {
5958 svn_fs_t *fs;
5959 svn_fs_txn_t *txn;
5960 const char *name1, *name2;
5961 apr_array_header_t *txn_list;
5962 apr_pool_t *subpool = svn_pool_create(pool);
5963
5964 SVN_ERR(svn_test__create_fs(&fs, "test-repo-purge-txn",
5965 opts, pool));
5966
5967 /* Begin a new transaction, get its name (in the top pool), close it. */
5968 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
5969 SVN_ERR(svn_fs_txn_name(&name1, txn, pool));
5970
5971 /* Begin *another* transaction, get its name (in the top pool), close it. */
5972 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
5973 SVN_ERR(svn_fs_txn_name(&name2, txn, pool));
5974 svn_pool_clear(subpool);
5975
5976 /* Get rid of the txns one at a time. */
5977 SVN_ERR(cleanup_txn(fs, name1, pool));
5978
5979 /* There should be exactly one left. */
5980 SVN_ERR(svn_fs_list_transactions(&txn_list, fs, pool));
5981
5982 /* Check the list. It should have *exactly* one entry. */
5983 SVN_TEST_ASSERT( txn_list->nelts == 1
5984 && !strcmp(name2, APR_ARRAY_IDX(txn_list, 0, const char *)));
5985
5986 /* Get rid of the other txn as well. */
5987 SVN_ERR(cleanup_txn(fs, name2, pool));
5988
5989 /* There should be exactly one left. */
5990 SVN_ERR(svn_fs_list_transactions(&txn_list, fs, pool));
5991
5992 /* Check the list. It should have no entries. */
5993 SVN_TEST_ASSERT(txn_list->nelts == 0);
5994
5995 return SVN_NO_ERROR;
5996 }
5997
5998 /* Test svn_fs_{contents,props}_{different,changed}().
5999 * ### This currently only tests them on revision roots, not on txn roots.
6000 */
6001 static svn_error_t *
compare_contents(const svn_test_opts_t * opts,apr_pool_t * pool)6002 compare_contents(const svn_test_opts_t *opts,
6003 apr_pool_t *pool)
6004 {
6005 svn_fs_t *fs;
6006 svn_fs_txn_t *txn;
6007 svn_fs_root_t *txn_root, *root1, *root2;
6008 const char *original = "original contents";
6009 svn_revnum_t rev;
6010 int i;
6011 apr_pool_t *iterpool = svn_pool_create(pool);
6012 svn_boolean_t changed;
6013
6014 /* Two similar but different texts that yield the same MD5 digest. */
6015 const char *evil_text1
6016 = "\xd1\x31\xdd\x02\xc5\xe6\xee\xc4\x69\x3d\x9a\x06\x98\xaf\xf9\x5c"
6017 "\x2f\xca\xb5\x87\x12\x46\x7e\xab\x40\x04\x58\x3e\xb8\xfb\x7f\x89"
6018 "\x55\xad\x34\x06\x09\xf4\xb3\x02\x83\xe4\x88\x83\x25\x71\x41\x5a"
6019 "\x08\x51\x25\xe8\xf7\xcd\xc9\x9f\xd9\x1d\xbd\xf2\x80\x37\x3c\x5b"
6020 "\xd8\x82\x3e\x31\x56\x34\x8f\x5b\xae\x6d\xac\xd4\x36\xc9\x19\xc6"
6021 "\xdd\x53\xe2\xb4\x87\xda\x03\xfd\x02\x39\x63\x06\xd2\x48\xcd\xa0"
6022 "\xe9\x9f\x33\x42\x0f\x57\x7e\xe8\xce\x54\xb6\x70\x80\xa8\x0d\x1e"
6023 "\xc6\x98\x21\xbc\xb6\xa8\x83\x93\x96\xf9\x65\x2b\x6f\xf7\x2a\x70";
6024 const char *evil_text2
6025 = "\xd1\x31\xdd\x02\xc5\xe6\xee\xc4\x69\x3d\x9a\x06\x98\xaf\xf9\x5c"
6026 "\x2f\xca\xb5\x07\x12\x46\x7e\xab\x40\x04\x58\x3e\xb8\xfb\x7f\x89"
6027 "\x55\xad\x34\x06\x09\xf4\xb3\x02\x83\xe4\x88\x83\x25\xf1\x41\x5a"
6028 "\x08\x51\x25\xe8\xf7\xcd\xc9\x9f\xd9\x1d\xbd\x72\x80\x37\x3c\x5b"
6029 "\xd8\x82\x3e\x31\x56\x34\x8f\x5b\xae\x6d\xac\xd4\x36\xc9\x19\xc6"
6030 "\xdd\x53\xe2\x34\x87\xda\x03\xfd\x02\x39\x63\x06\xd2\x48\xcd\xa0"
6031 "\xe9\x9f\x33\x42\x0f\x57\x7e\xe8\xce\x54\xb6\x70\x80\x28\x0d\x1e"
6032 "\xc6\x98\x21\xbc\xb6\xa8\x83\x93\x96\xf9\x65\xab\x6f\xf7\x2a\x70";
6033 svn_checksum_t *checksum1, *checksum2;
6034
6035 /* (path, rev) pairs to compare plus the expected API return values */
6036 struct
6037 {
6038 svn_revnum_t rev1;
6039 const char *path1;
6040 svn_revnum_t rev2;
6041 const char *path2;
6042
6043 svn_boolean_t different; /* result of svn_fs_*_different */
6044 svn_tristate_t changed; /* result of svn_fs_*_changed */
6045 } to_compare[] =
6046 {
6047 /* same representation */
6048 { 1, "foo", 2, "foo", FALSE, svn_tristate_false },
6049 { 1, "foo", 2, "bar", FALSE, svn_tristate_false },
6050 { 2, "foo", 2, "bar", FALSE, svn_tristate_false },
6051
6052 /* different content but MD5 check is not reliable */
6053 { 3, "foo", 3, "bar", TRUE, svn_tristate_true },
6054
6055 /* different contents */
6056 { 1, "foo", 3, "bar", TRUE, svn_tristate_true },
6057 { 1, "foo", 3, "foo", TRUE, svn_tristate_true },
6058 { 3, "foo", 4, "bar", TRUE, svn_tristate_true },
6059 { 3, "foo", 4, "bar", TRUE, svn_tristate_true },
6060 { 2, "bar", 3, "bar", TRUE, svn_tristate_true },
6061 { 3, "bar", 4, "bar", TRUE, svn_tristate_true },
6062
6063 /* variations on the same theme: same content, possibly different rep */
6064 { 4, "foo", 4, "bar", FALSE, svn_tristate_unknown },
6065 { 1, "foo", 4, "bar", FALSE, svn_tristate_unknown },
6066 { 2, "foo", 4, "bar", FALSE, svn_tristate_unknown },
6067 { 1, "foo", 4, "foo", FALSE, svn_tristate_unknown },
6068 { 2, "foo", 4, "foo", FALSE, svn_tristate_unknown },
6069 { 2, "bar", 4, "bar", FALSE, svn_tristate_unknown },
6070
6071 /* EOL */
6072 { 0 },
6073 };
6074
6075 /* Same same, but different.
6076 * Just checking that we actually have an MD5 collision. */
6077 SVN_ERR(svn_checksum(&checksum1, svn_checksum_md5, evil_text1,
6078 strlen(evil_text1), pool));
6079 SVN_ERR(svn_checksum(&checksum2, svn_checksum_md5, evil_text2,
6080 strlen(evil_text2), pool));
6081 SVN_TEST_ASSERT(svn_checksum_match(checksum1, checksum1));
6082 SVN_TEST_ASSERT(strcmp(evil_text1, evil_text2));
6083
6084 /* Now, build up our test repo. */
6085 SVN_ERR(svn_test__create_fs(&fs, "test-repo-compare-contents",
6086 opts, pool));
6087
6088 /* Rev 1: create a file. */
6089 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, iterpool));
6090 SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
6091 SVN_ERR(svn_fs_make_file(txn_root, "foo", iterpool));
6092 SVN_ERR(svn_test__set_file_contents(txn_root, "foo", original, iterpool));
6093 SVN_ERR(svn_fs_change_node_prop(txn_root, "foo", "prop",
6094 svn_string_create(original, iterpool),
6095 iterpool));
6096 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, iterpool));
6097 SVN_TEST_ASSERT(rev == 1);
6098 svn_pool_clear(iterpool);
6099
6100 /* Rev 2: copy that file. */
6101 SVN_ERR(svn_fs_revision_root(&root1, fs, rev, iterpool));
6102 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, iterpool));
6103 SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
6104 SVN_ERR(svn_fs_copy(root1, "foo", txn_root, "bar", iterpool));
6105 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, iterpool));
6106 SVN_TEST_ASSERT(rev == 2);
6107 svn_pool_clear(iterpool);
6108
6109 /* Rev 3: modify both files.
6110 * The new contents differs for both files but has the same length and MD5.
6111 */
6112 SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, iterpool));
6113 SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
6114 SVN_ERR(svn_test__set_file_contents(txn_root, "foo", evil_text1, iterpool));
6115 SVN_ERR(svn_test__set_file_contents(txn_root, "bar", evil_text2, iterpool));
6116 SVN_ERR(svn_fs_change_node_prop(txn_root, "foo", "prop",
6117 svn_string_create(evil_text1, iterpool),
6118 iterpool));
6119 SVN_ERR(svn_fs_change_node_prop(txn_root, "bar", "prop",
6120 svn_string_create(evil_text2, iterpool),
6121 iterpool));
6122 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, iterpool));
6123 SVN_TEST_ASSERT(rev == 3);
6124 svn_pool_clear(iterpool);
6125
6126 /* Rev 4: revert both file contents.
6127 */
6128 SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, iterpool));
6129 SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
6130 SVN_ERR(svn_test__set_file_contents(txn_root, "foo", original, iterpool));
6131 SVN_ERR(svn_test__set_file_contents(txn_root, "bar", original, iterpool));
6132 SVN_ERR(svn_fs_change_node_prop(txn_root, "foo", "prop",
6133 svn_string_create(original, iterpool),
6134 iterpool));
6135 SVN_ERR(svn_fs_change_node_prop(txn_root, "bar", "prop",
6136 svn_string_create(original, iterpool),
6137 iterpool));
6138 SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, iterpool));
6139 SVN_TEST_ASSERT(rev == 4);
6140 svn_pool_clear(iterpool);
6141
6142 /* Perform all comparisons listed in TO_COMPARE. */
6143 for (i = 0; to_compare[i].rev1 > 0; ++i)
6144 {
6145 svn_boolean_t text_different;
6146 svn_boolean_t text_changed;
6147 svn_boolean_t props_different;
6148 svn_boolean_t props_changed;
6149
6150 svn_pool_clear(iterpool);
6151 SVN_ERR(svn_fs_revision_root(&root1, fs, to_compare[i].rev1, iterpool));
6152 SVN_ERR(svn_fs_revision_root(&root2, fs, to_compare[i].rev2, iterpool));
6153
6154 /* Compare node texts. */
6155 SVN_ERR(svn_fs_contents_different(&text_different,
6156 root1, to_compare[i].path1,
6157 root2, to_compare[i].path2,
6158 iterpool));
6159 SVN_ERR(svn_fs_contents_changed(&text_changed,
6160 root1, to_compare[i].path1,
6161 root2, to_compare[i].path2,
6162 iterpool));
6163
6164 /* Compare properties. */
6165 SVN_ERR(svn_fs_props_different(&props_different,
6166 root1, to_compare[i].path1,
6167 root2, to_compare[i].path2,
6168 iterpool));
6169 SVN_ERR(svn_fs_props_changed(&props_changed,
6170 root1, to_compare[i].path1,
6171 root2, to_compare[i].path2,
6172 iterpool));
6173
6174 /* Check results. */
6175 SVN_TEST_ASSERT(text_different == to_compare[i].different);
6176 SVN_TEST_ASSERT(props_different == to_compare[i].different);
6177
6178 switch (to_compare[i].changed)
6179 {
6180 case svn_tristate_true:
6181 SVN_TEST_ASSERT(text_changed);
6182 SVN_TEST_ASSERT(props_changed);
6183 break;
6184
6185 case svn_tristate_false:
6186 SVN_TEST_ASSERT(!text_changed);
6187 SVN_TEST_ASSERT(!props_changed);
6188 break;
6189
6190 default:
6191 break;
6192 }
6193 }
6194
6195 /* Check how svn_fs_contents_different() and svn_fs_contents_changed()
6196 handles invalid path.*/
6197 SVN_ERR(svn_fs_revision_root(&root1, fs, 1, iterpool));
6198 SVN_TEST_ASSERT_ANY_ERROR(
6199 svn_fs_contents_changed(&changed, root1, "/", root1, "/", iterpool));
6200 SVN_TEST_ASSERT_ANY_ERROR(
6201 svn_fs_contents_different(&changed, root1, "/", root1, "/", iterpool));
6202
6203 SVN_TEST_ASSERT_ANY_ERROR(
6204 svn_fs_contents_changed(&changed, root1, "/non-existent", root1,
6205 "/non-existent", iterpool));
6206 SVN_TEST_ASSERT_ANY_ERROR(
6207 svn_fs_contents_different(&changed, root1, "/non-existent", root1,
6208 "/non-existent", iterpool));
6209
6210 svn_pool_destroy(iterpool);
6211
6212 return SVN_NO_ERROR;
6213 }
6214
6215 static svn_error_t *
test_path_change_create(const svn_test_opts_t * opts,apr_pool_t * pool)6216 test_path_change_create(const svn_test_opts_t *opts,
6217 apr_pool_t *pool)
6218 {
6219 svn_fs_t *fs;
6220 svn_fs_root_t *root;
6221 const svn_fs_id_t *id;
6222 svn_fs_path_change2_t *change;
6223
6224 /* Build an empty test repo ... */
6225 SVN_ERR(svn_test__create_fs(&fs, "test-repo-path-change-create",
6226 opts, pool));
6227
6228 /* ... just to give us a valid ID. */
6229 SVN_ERR(svn_fs_revision_root(&root, fs, 0, pool));
6230 SVN_ERR(svn_fs_node_id(&id, root, "", pool));
6231
6232 /* Do what we came here for. */
6233 change = svn_fs_path_change2_create(id, svn_fs_path_change_replace, pool);
6234
6235 SVN_TEST_ASSERT(change);
6236 SVN_TEST_ASSERT(change->node_rev_id == id);
6237 SVN_TEST_ASSERT(change->change_kind == svn_fs_path_change_replace);
6238
6239 /* All other fields should be "empty" / "unused". */
6240 SVN_TEST_ASSERT(change->node_kind == svn_node_none);
6241
6242 SVN_TEST_ASSERT(change->text_mod == FALSE);
6243 SVN_TEST_ASSERT(change->prop_mod == FALSE);
6244 SVN_TEST_ASSERT(change->mergeinfo_mod == svn_tristate_unknown);
6245
6246 SVN_TEST_ASSERT(change->copyfrom_known == FALSE);
6247 SVN_TEST_ASSERT(change->copyfrom_rev == SVN_INVALID_REVNUM);
6248 SVN_TEST_ASSERT(change->copyfrom_path == NULL);
6249
6250 return SVN_NO_ERROR;
6251 }
6252
6253 static svn_error_t *
test_node_created_info(const svn_test_opts_t * opts,apr_pool_t * pool)6254 test_node_created_info(const svn_test_opts_t *opts,
6255 apr_pool_t *pool)
6256 {
6257 svn_fs_t *fs;
6258 svn_fs_txn_t *txn;
6259 svn_fs_root_t *txn_root, *root;
6260 svn_revnum_t rev;
6261 int i;
6262 apr_pool_t *iterpool = svn_pool_create(pool);
6263
6264 /* Test vectors. */
6265 struct
6266 {
6267 svn_revnum_t rev;
6268 const char *path;
6269 svn_revnum_t crev;
6270 const char *cpath;
6271 } to_check[] =
6272 {
6273 /* New noderev only upon modification. */
6274 { 1, "A/B/E/beta", 1, "/A/B/E/beta" },
6275 { 2, "A/B/E/beta", 1, "/A/B/E/beta" },
6276 { 3, "A/B/E/beta", 3, "/A/B/E/beta" },
6277 { 4, "A/B/E/beta", 3, "/A/B/E/beta" },
6278
6279 /* Lazily copied node. */
6280 { 2, "Z/B/E/beta", 1, "/A/B/E/beta" },
6281 { 3, "Z/B/E/beta", 1, "/A/B/E/beta" },
6282 { 4, "Z/B/E/beta", 4, "/Z/B/E/beta" },
6283
6284 /* Bubble-up upon sub-tree change. */
6285 { 2, "Z", 2, "/Z" },
6286 { 3, "Z", 2, "/Z" },
6287 { 4, "Z", 4, "/Z" },
6288
6289 { 0 }
6290 };
6291
6292 /* Start with a new repo and the greek tree in rev 1. */
6293 SVN_ERR(svn_test__create_fs(&fs, "test-repo-node-created-path",
6294 opts, pool));
6295
6296 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, iterpool));
6297 SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
6298 SVN_ERR(svn_test__create_greek_tree(txn_root, iterpool));
6299 SVN_ERR(test_commit_txn(&rev, txn, NULL, iterpool));
6300 svn_pool_clear(iterpool);
6301
6302 /* r2: copy a subtree */
6303 SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, iterpool));
6304 SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
6305 SVN_ERR(svn_fs_revision_root(&root, fs, rev, iterpool));
6306 SVN_ERR(svn_fs_copy(root, "A", txn_root, "Z", iterpool));
6307 SVN_ERR(test_commit_txn(&rev, txn, NULL, iterpool));
6308 svn_pool_clear(iterpool);
6309
6310 /* r3: touch node in copy source */
6311 SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, iterpool));
6312 SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
6313 SVN_ERR(svn_test__set_file_contents(txn_root, "A/B/E/beta", "new", iterpool));
6314 SVN_ERR(test_commit_txn(&rev, txn, NULL, iterpool));
6315 svn_pool_clear(iterpool);
6316
6317 /* r4: touch same relative node in copy target */
6318 SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, iterpool));
6319 SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
6320 SVN_ERR(svn_test__set_file_contents(txn_root, "Z/B/E/beta", "new", iterpool));
6321 SVN_ERR(test_commit_txn(&rev, txn, NULL, iterpool));
6322 svn_pool_clear(iterpool);
6323
6324 /* Now ask for some 'node created' info. */
6325 for (i = 0; to_check[i].rev > 0; ++i)
6326 {
6327 svn_revnum_t crev;
6328 const char *cpath;
6329
6330 svn_pool_clear(iterpool);
6331
6332 /* Get created path and rev. */
6333 SVN_ERR(svn_fs_revision_root(&root, fs, to_check[i].rev, iterpool));
6334 SVN_ERR(svn_fs_node_created_path(&cpath, root, to_check[i].path,
6335 iterpool));
6336 SVN_ERR(svn_fs_node_created_rev(&crev, root, to_check[i].path,
6337 iterpool));
6338
6339 /* Compare the results with our expectations. */
6340 SVN_TEST_STRING_ASSERT(cpath, to_check[i].cpath);
6341
6342 if (crev != to_check[i].crev)
6343 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
6344 "created rev mismatch for %s@%ld:\n"
6345 " expected '%ld'\n"
6346 " found '%ld",
6347 to_check[i].path,
6348 to_check[i].rev,
6349 to_check[i].crev,
6350 crev);
6351 }
6352
6353 svn_pool_destroy(iterpool);
6354
6355 return SVN_NO_ERROR;
6356 }
6357
6358 static svn_error_t *
test_print_modules(const svn_test_opts_t * opts,apr_pool_t * pool)6359 test_print_modules(const svn_test_opts_t *opts,
6360 apr_pool_t *pool)
6361 {
6362 const char *expected, *module_name;
6363 svn_stringbuf_t *modules = svn_stringbuf_create_empty(pool);
6364
6365 /* Name of the providing module */
6366 if (strcmp(opts->fs_type, SVN_FS_TYPE_FSX) == 0)
6367 module_name = "fs_x";
6368 else if (strcmp(opts->fs_type, SVN_FS_TYPE_FSFS) == 0)
6369 module_name = "fs_fs";
6370 else if (strcmp(opts->fs_type, SVN_FS_TYPE_BDB) == 0)
6371 module_name = "fs_base";
6372 else
6373 return svn_error_createf(SVN_ERR_TEST_SKIPPED, NULL,
6374 "don't know the module name for %s",
6375 opts->fs_type);
6376
6377 SVN_ERR(svn_fs_print_modules(modules, pool));
6378
6379 /* The requested FS type must be listed amongst the available modules. */
6380 expected = apr_psprintf(pool, "* %s : ", module_name);
6381 SVN_TEST_ASSERT(strstr(modules->data, expected));
6382
6383 return SVN_NO_ERROR;
6384 }
6385
6386 /* Baton to be used with process_file_contents. */
6387 typedef struct process_file_contents_baton_t
6388 {
6389 const char *contents;
6390 svn_boolean_t processed;
6391 } process_file_contents_baton_t;
6392
6393 /* Implements svn_fs_process_contents_func_t.
6394 * We flag the BATON as "processed" and compare the CONTENTS we've got to
6395 * what we expect through the BATON.
6396 */
6397 static svn_error_t *
process_file_contents(const unsigned char * contents,apr_size_t len,void * baton,apr_pool_t * scratch_pool)6398 process_file_contents(const unsigned char *contents,
6399 apr_size_t len,
6400 void *baton,
6401 apr_pool_t *scratch_pool)
6402 {
6403 process_file_contents_baton_t *b = baton;
6404
6405 SVN_TEST_ASSERT(strlen(b->contents) == len);
6406 SVN_TEST_ASSERT(memcmp(b->contents, contents, len) == 0);
6407 b->processed = TRUE;
6408
6409 return SVN_NO_ERROR;
6410 }
6411
6412 static svn_error_t *
test_zero_copy_processsing(const svn_test_opts_t * opts,apr_pool_t * pool)6413 test_zero_copy_processsing(const svn_test_opts_t *opts,
6414 apr_pool_t *pool)
6415 {
6416 svn_fs_t *fs;
6417 svn_fs_txn_t *txn;
6418 svn_fs_root_t *txn_root, *root;
6419 svn_revnum_t rev;
6420 const struct svn_test__tree_entry_t *node;
6421 apr_pool_t *iterpool = svn_pool_create(pool);
6422
6423 /* Start with a new repo and the greek tree in rev 1. */
6424 SVN_ERR(svn_test__create_fs(&fs, "test-repo-zero-copy-processing",
6425 opts, pool));
6426
6427 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, iterpool));
6428 SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
6429 SVN_ERR(svn_test__create_greek_tree(txn_root, iterpool));
6430 SVN_ERR(test_commit_txn(&rev, txn, NULL, iterpool));
6431 svn_pool_clear(iterpool);
6432
6433 SVN_ERR(svn_fs_revision_root(&root, fs, rev, pool));
6434
6435 /* Prime the full-text cache by reading all file contents. */
6436 for (node = svn_test__greek_tree_nodes; node->path; node++)
6437 if (node->contents)
6438 {
6439 svn_stream_t *stream;
6440 svn_pool_clear(iterpool);
6441
6442 SVN_ERR(svn_fs_file_contents(&stream, root, node->path, iterpool));
6443 SVN_ERR(svn_stream_copy3(stream, svn_stream_buffered(iterpool),
6444 NULL, NULL, iterpool));
6445 }
6446
6447 /* Now, try to get the data directly from cache
6448 * (if the backend has caches). */
6449 for (node = svn_test__greek_tree_nodes; node->path; node++)
6450 if (node->contents)
6451 {
6452 svn_boolean_t success;
6453
6454 process_file_contents_baton_t baton;
6455 baton.contents = node->contents;
6456 baton.processed = FALSE;
6457
6458 svn_pool_clear(iterpool);
6459
6460 SVN_ERR(svn_fs_try_process_file_contents(&success, root, node->path,
6461 process_file_contents, &baton,
6462 iterpool));
6463 SVN_TEST_ASSERT(success == baton.processed);
6464 }
6465
6466 svn_pool_destroy(iterpool);
6467
6468 return SVN_NO_ERROR;
6469 }
6470
6471 static svn_error_t *
test_dir_optimal_order(const svn_test_opts_t * opts,apr_pool_t * pool)6472 test_dir_optimal_order(const svn_test_opts_t *opts,
6473 apr_pool_t *pool)
6474 {
6475 svn_fs_t *fs;
6476 svn_fs_txn_t *txn;
6477 svn_fs_root_t *txn_root, *root;
6478 svn_revnum_t rev;
6479 apr_hash_t *unordered;
6480 apr_array_header_t *ordered;
6481 int i;
6482 apr_hash_index_t *hi;
6483
6484 /* Create a new repo and the greek tree in rev 1. */
6485 SVN_ERR(svn_test__create_fs(&fs, "test-repo-dir-optimal-order",
6486 opts, pool));
6487
6488 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
6489 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
6490 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
6491 SVN_ERR(test_commit_txn(&rev, txn, NULL, pool));
6492
6493 SVN_ERR(svn_fs_revision_root(&root, fs, rev, pool));
6494
6495 /* Call the API function we are interested in. */
6496 SVN_ERR(svn_fs_dir_entries(&unordered, root, "A", pool));
6497 SVN_ERR(svn_fs_dir_optimal_order(&ordered, root, unordered, pool, pool));
6498
6499 /* Verify that all entries are returned. */
6500 SVN_TEST_ASSERT(ordered->nelts == apr_hash_count(unordered));
6501 for (hi = apr_hash_first(pool, unordered); hi; hi = apr_hash_next(hi))
6502 {
6503 svn_boolean_t found = FALSE;
6504 const char *name = apr_hash_this_key(hi);
6505
6506 /* Compare hash -> array because the array might contain the same
6507 * entry more than once. Since that can't happen in the hash, doing
6508 * it in this direction ensures ORDERED won't contain duplicates.
6509 */
6510 for (i = 0; !found && i < ordered->nelts; ++i)
6511 {
6512 svn_fs_dirent_t *item = APR_ARRAY_IDX(ordered, i, svn_fs_dirent_t*);
6513 if (strcmp(item->name, name) == 0)
6514 {
6515 found = TRUE;
6516 SVN_TEST_ASSERT(item == apr_hash_this_val(hi));
6517 }
6518 }
6519
6520 SVN_TEST_ASSERT(found);
6521 }
6522
6523 return SVN_NO_ERROR;
6524 }
6525
6526 static svn_error_t *
test_config_files(const svn_test_opts_t * opts,apr_pool_t * pool)6527 test_config_files(const svn_test_opts_t *opts,
6528 apr_pool_t *pool)
6529 {
6530 svn_fs_t *fs;
6531 apr_array_header_t *files;
6532 int i;
6533 const char *repo_name = "test-repo-config-files";
6534
6535 /* Create a empty and get its config files. */
6536 SVN_ERR(svn_test__create_fs(&fs, repo_name, opts, pool));
6537 SVN_ERR(svn_fs_info_config_files(&files, fs, pool, pool));
6538
6539 /* All files should exist and be below the repo. */
6540 for (i = 0; i < files->nelts; ++i)
6541 {
6542 svn_node_kind_t kind;
6543 const char *path = APR_ARRAY_IDX(files, i, const char*);
6544
6545 SVN_ERR(svn_io_check_path(path, &kind, pool));
6546
6547 SVN_TEST_ASSERT(kind == svn_node_file);
6548 SVN_TEST_ASSERT(svn_dirent_is_ancestor(repo_name, path));
6549 }
6550
6551 return SVN_NO_ERROR;
6552 }
6553
6554 static svn_error_t *
test_delta_file_stream(const svn_test_opts_t * opts,apr_pool_t * pool)6555 test_delta_file_stream(const svn_test_opts_t *opts,
6556 apr_pool_t *pool)
6557 {
6558 svn_fs_t *fs;
6559 svn_fs_txn_t *txn;
6560 svn_fs_root_t *txn_root, *root1, *root2;
6561 svn_revnum_t rev;
6562 apr_pool_t *subpool = svn_pool_create(pool);
6563
6564 const char *old_content = "some content";
6565 const char *new_content = "some more content";
6566 svn_txdelta_window_handler_t delta_handler;
6567 void *delta_baton;
6568 svn_txdelta_stream_t *delta_stream;
6569 svn_stringbuf_t *source = svn_stringbuf_create_empty(pool);
6570 svn_stringbuf_t *dest = svn_stringbuf_create_empty(pool);
6571
6572 /* Create a new repo. */
6573 SVN_ERR(svn_test__create_fs(&fs, "test-repo-delta-file-stream",
6574 opts, pool));
6575
6576 /* Revision 1: create a file. */
6577 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
6578 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
6579 SVN_ERR(svn_fs_make_file(txn_root, "foo", pool));
6580 SVN_ERR(svn_test__set_file_contents(txn_root, "foo", old_content, pool));
6581 SVN_ERR(test_commit_txn(&rev, txn, NULL, pool));
6582
6583 /* Revision 2: create a file. */
6584 SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, pool));
6585 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
6586 SVN_ERR(svn_test__set_file_contents(txn_root, "foo", new_content, pool));
6587 SVN_ERR(test_commit_txn(&rev, txn, NULL, pool));
6588
6589 SVN_ERR(svn_fs_revision_root(&root1, fs, 1, pool));
6590 SVN_ERR(svn_fs_revision_root(&root2, fs, 2, pool));
6591
6592 /* Test 1: Get delta against empty target. */
6593 SVN_ERR(svn_fs_get_file_delta_stream(&delta_stream,
6594 NULL, NULL, root1, "foo", subpool));
6595
6596 svn_stringbuf_setempty(source);
6597 svn_stringbuf_setempty(dest);
6598
6599 svn_txdelta_apply(svn_stream_from_stringbuf(source, subpool),
6600 svn_stream_from_stringbuf(dest, subpool),
6601 NULL, NULL, subpool, &delta_handler, &delta_baton);
6602 SVN_ERR(svn_txdelta_send_txstream(delta_stream,
6603 delta_handler,
6604 delta_baton,
6605 subpool));
6606 SVN_TEST_STRING_ASSERT(old_content, dest->data);
6607 svn_pool_clear(subpool);
6608
6609 /* Test 2: Get delta against previous version. */
6610 SVN_ERR(svn_fs_get_file_delta_stream(&delta_stream,
6611 root1, "foo", root2, "foo", subpool));
6612
6613 svn_stringbuf_set(source, old_content);
6614 svn_stringbuf_setempty(dest);
6615
6616 svn_txdelta_apply(svn_stream_from_stringbuf(source, subpool),
6617 svn_stream_from_stringbuf(dest, subpool),
6618 NULL, NULL, subpool, &delta_handler, &delta_baton);
6619 SVN_ERR(svn_txdelta_send_txstream(delta_stream,
6620 delta_handler,
6621 delta_baton,
6622 subpool));
6623 SVN_TEST_STRING_ASSERT(new_content, dest->data);
6624 svn_pool_clear(subpool);
6625
6626 /* Test 3: Get reverse delta. */
6627 SVN_ERR(svn_fs_get_file_delta_stream(&delta_stream,
6628 root2, "foo", root1, "foo", subpool));
6629
6630 svn_stringbuf_set(source, new_content);
6631 svn_stringbuf_setempty(dest);
6632
6633 svn_txdelta_apply(svn_stream_from_stringbuf(source, subpool),
6634 svn_stream_from_stringbuf(dest, subpool),
6635 NULL, NULL, subpool, &delta_handler, &delta_baton);
6636 SVN_ERR(svn_txdelta_send_txstream(delta_stream,
6637 delta_handler,
6638 delta_baton,
6639 subpool));
6640 SVN_TEST_STRING_ASSERT(old_content, dest->data);
6641
6642 svn_pool_destroy(subpool);
6643
6644 return SVN_NO_ERROR;
6645 }
6646
6647 static svn_error_t *
test_fs_merge(const svn_test_opts_t * opts,apr_pool_t * pool)6648 test_fs_merge(const svn_test_opts_t *opts,
6649 apr_pool_t *pool)
6650 {
6651 svn_fs_t *fs;
6652 svn_fs_txn_t *txn;
6653 svn_fs_root_t *txn_root, *root0, *root1;
6654 svn_revnum_t rev;
6655
6656 /* Very basic test for svn_fs_merge because all the other interesting
6657 * cases get tested implicitly with concurrent txn / commit tests.
6658 * This API is just a thin layer around the internal merge function
6659 * and we simply check that the plumbing between them works.
6660 */
6661
6662 /* Create a new repo. */
6663 SVN_ERR(svn_test__create_fs(&fs, "test-repo-fs-merge",
6664 opts, pool));
6665 SVN_ERR(svn_fs_revision_root(&root0, fs, 0, pool));
6666
6667 /* Revision 1: create a file. */
6668 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
6669 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
6670 SVN_ERR(svn_fs_make_file(txn_root, "foo", pool));
6671 SVN_ERR(test_commit_txn(&rev, txn, NULL, pool));
6672 SVN_ERR(svn_fs_revision_root(&root1, fs, rev, pool));
6673
6674 /* Merge-able txn: create another file. */
6675 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
6676 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
6677 SVN_ERR(svn_fs_make_file(txn_root, "bar", pool));
6678
6679 SVN_ERR(svn_fs_merge(NULL, root1, "/", txn_root, "/", root0, "/", pool));
6680
6681 /* Not merge-able: create the same file file. */
6682 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
6683 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
6684 SVN_ERR(svn_fs_make_file(txn_root, "foo", pool));
6685
6686 SVN_TEST_ASSERT_ERROR(svn_fs_merge(NULL, root1, "/", txn_root, "/", root0,
6687 "/", pool), SVN_ERR_FS_CONFLICT);
6688
6689 return SVN_NO_ERROR;
6690 }
6691
6692 static svn_error_t *
test_fsfs_config_opts(const svn_test_opts_t * opts,apr_pool_t * pool)6693 test_fsfs_config_opts(const svn_test_opts_t *opts,
6694 apr_pool_t *pool)
6695 {
6696 apr_hash_t *fs_config;
6697 svn_fs_t *fs;
6698 const svn_fs_info_placeholder_t *fs_info;
6699 const svn_fs_fsfs_info_t *fsfs_info;
6700 const char *dir_name = "test-repo-fsfs-config-opts";
6701 const char *repo_name_default = "test-repo-fsfs-config-opts/default";
6702 const char *repo_name_custom = "test-repo-fsfs-config-opts/custom";
6703
6704 /* Bail (with SKIP) on known-untestable scenarios */
6705 if (strcmp(opts->fs_type, SVN_FS_TYPE_FSFS) != 0)
6706 return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
6707 "this will test FSFS repositories only");
6708
6709 /* Remove the test directory from previous runs. */
6710 SVN_ERR(svn_io_remove_dir2(dir_name, TRUE, NULL, NULL, pool));
6711
6712 /* Create the test directory and add it to the test cleanup list. */
6713 SVN_ERR(svn_io_dir_make(dir_name, APR_OS_DEFAULT, pool));
6714 svn_test_add_dir_cleanup(dir_name);
6715
6716 /* Create an FSFS filesystem with default config.*/
6717 fs_config = apr_hash_make(pool);
6718 svn_hash_sets(fs_config, SVN_FS_CONFIG_FS_TYPE, SVN_FS_TYPE_FSFS);
6719 SVN_ERR(svn_fs_create(&fs, repo_name_default, fs_config, pool));
6720
6721 /* Re-open FS to test the data on disk. */
6722 SVN_ERR(svn_fs_open2(&fs, repo_name_default, NULL, pool, pool));
6723
6724 SVN_ERR(svn_fs_info(&fs_info, fs, pool, pool));
6725 SVN_TEST_STRING_ASSERT(fs_info->fs_type, SVN_FS_TYPE_FSFS);
6726 fsfs_info = (const void *) fs_info;
6727
6728 /* Check FSFS specific info. Don't check the SHARD_SIZE, because it depends
6729 * on a compile-time constant and may be overridden. */
6730 SVN_TEST_ASSERT(fsfs_info->log_addressing);
6731 SVN_TEST_ASSERT(fsfs_info->min_unpacked_rev == 0);
6732
6733 /* Create an FSFS filesystem with custom settings: disabled log-addressing
6734 * and custom shard size (123). */
6735 fs_config = apr_hash_make(pool);
6736 svn_hash_sets(fs_config, SVN_FS_CONFIG_FS_TYPE, SVN_FS_TYPE_FSFS);
6737 svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_LOG_ADDRESSING, "false");
6738 svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_SHARD_SIZE, "123");
6739 SVN_ERR(svn_fs_create(&fs, repo_name_custom, fs_config, pool));
6740
6741 /* Re-open FS to test the data on disk. */
6742 SVN_ERR(svn_fs_open2(&fs, repo_name_custom, NULL, pool, pool));
6743
6744 SVN_ERR(svn_fs_info(&fs_info, fs, pool, pool));
6745 SVN_TEST_STRING_ASSERT(fs_info->fs_type, SVN_FS_TYPE_FSFS);
6746 fsfs_info = (const void *) fs_info;
6747
6748 /* Check FSFS specific info, including the SHARD_SIZE. */
6749 SVN_TEST_ASSERT(fsfs_info->log_addressing == FALSE);
6750 SVN_TEST_ASSERT(fsfs_info->shard_size == 123);
6751 SVN_TEST_ASSERT(fsfs_info->min_unpacked_rev == 0);
6752
6753 return SVN_NO_ERROR;
6754 }
6755
6756 static svn_error_t *
test_txn_pool_lifetime(const svn_test_opts_t * opts,apr_pool_t * pool)6757 test_txn_pool_lifetime(const svn_test_opts_t *opts,
6758 apr_pool_t *pool)
6759 {
6760 /* Technically, the FS API makes no assumption on the lifetime of logically
6761 * dependent objects. In particular, a txn root object may get destroyed
6762 * after the FS object that it has been built upon. Actual data access is
6763 * implied to be invalid without a valid svn_fs_t.
6764 *
6765 * This test verifies that at least the destruction order of those two
6766 * objects is arbitrary.
6767 */
6768 svn_fs_t *fs;
6769 svn_fs_txn_t *txn;
6770 svn_fs_root_t *txn_root;
6771
6772 /* We will allocate FS in FS_POOL. Using a separate allocator makes
6773 * sure that we actually free the memory when destroying the pool.
6774 */
6775 apr_allocator_t *fs_allocator = svn_pool_create_allocator(FALSE);
6776 apr_pool_t *fs_pool = apr_allocator_owner_get(fs_allocator);
6777
6778 /* Create a new repo. */
6779 SVN_ERR(svn_test__create_fs(&fs, "test-repo-pool-lifetime",
6780 opts, fs_pool));
6781
6782 /* Create a TXN_ROOT referencing FS. */
6783 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
6784 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
6785
6786 /* Destroy FS. Depending on the actual allocator implementation,
6787 * these memory pages becomes inaccessible. */
6788 svn_pool_destroy(fs_pool);
6789
6790 /* Unclean implementations will try to access FS and may segfault here. */
6791 svn_fs_close_root(txn_root);
6792
6793 return SVN_NO_ERROR;
6794 }
6795
6796 static svn_error_t *
test_modify_txn_being_written(const svn_test_opts_t * opts,apr_pool_t * pool)6797 test_modify_txn_being_written(const svn_test_opts_t *opts,
6798 apr_pool_t *pool)
6799 {
6800 /* FSFS has a limitation (and check) that only one file can be
6801 * modified in TXN at time: see r861812 and svn_fs_apply_text() docstring.
6802 * This is regression test for this behavior. */
6803 svn_fs_t *fs;
6804 svn_fs_txn_t *txn;
6805 const char *txn_name;
6806 svn_fs_root_t *txn_root;
6807 svn_stream_t *foo_contents;
6808 svn_stream_t *bar_contents;
6809
6810 /* Bail (with success) on known-untestable scenarios */
6811 if (strcmp(opts->fs_type, SVN_FS_TYPE_BDB) == 0)
6812 return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
6813 "this will not test BDB repositories");
6814
6815 /* Create a new repo. */
6816 SVN_ERR(svn_test__create_fs(&fs, "test-repo-modify-txn-being-written",
6817 opts, pool));
6818
6819 /* Create a TXN_ROOT referencing FS. */
6820 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
6821 SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool));
6822 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
6823
6824 /* Make file /foo and open for writing.*/
6825 SVN_ERR(svn_fs_make_file(txn_root, "/foo", pool));
6826 SVN_ERR(svn_fs_apply_text(&foo_contents, txn_root, "/foo", NULL, pool));
6827
6828 /* Attempt to modify another file '/bar' -- FSFS doesn't allow this. */
6829 SVN_ERR(svn_fs_make_file(txn_root, "/bar", pool));
6830 SVN_TEST_ASSERT_ERROR(
6831 svn_fs_apply_text(&bar_contents, txn_root, "/bar", NULL, pool),
6832 SVN_ERR_FS_REP_BEING_WRITTEN);
6833
6834 /* *Reopen TXN. */
6835 SVN_ERR(svn_fs_open_txn(&txn, fs, txn_name, pool));
6836 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
6837
6838 /* Check that file '/bar' still cannot be modified */
6839 SVN_TEST_ASSERT_ERROR(
6840 svn_fs_apply_text(&bar_contents, txn_root, "/bar", NULL, pool),
6841 SVN_ERR_FS_REP_BEING_WRITTEN);
6842
6843 /* Close file '/foo'. */
6844 SVN_ERR(svn_stream_close(foo_contents));
6845
6846 /* Now file '/bar' can be modified. */
6847 SVN_ERR(svn_fs_apply_text(&bar_contents, txn_root, "/bar", NULL, pool));
6848
6849 return SVN_NO_ERROR;
6850 }
6851
6852 static svn_error_t *
test_prop_and_text_rep_sharing_collision(const svn_test_opts_t * opts,apr_pool_t * pool)6853 test_prop_and_text_rep_sharing_collision(const svn_test_opts_t *opts,
6854 apr_pool_t *pool)
6855 {
6856 /* Regression test for issue 4554: Wrong file length with PLAIN
6857 * representations in FSFS. */
6858 svn_fs_t *fs;
6859 svn_fs_txn_t *txn;
6860 svn_fs_root_t *txn_root;
6861 svn_fs_root_t *rev_root;
6862 svn_revnum_t new_rev;
6863 svn_filesize_t length;
6864 const char *testdir = "test-repo-prop-and-text-rep-sharing-collision";
6865
6866 /* Create a new repo. */
6867 SVN_ERR(svn_test__create_fs(&fs, testdir, opts, pool));
6868
6869 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
6870 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
6871 /* Set node property for the root. */
6872 SVN_ERR(svn_fs_change_node_prop(txn_root, "/", "prop",
6873 svn_string_create("value", pool),
6874 pool));
6875
6876 /* Commit revision r1. */
6877 SVN_ERR(test_commit_txn(&new_rev, txn, NULL, pool));
6878
6879 SVN_ERR(svn_fs_begin_txn(&txn, fs, 1, pool));
6880 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
6881
6882 /* Create file with same contents as property representation. */
6883 SVN_ERR(svn_fs_make_file(txn_root, "/foo", pool));
6884 SVN_ERR(svn_test__set_file_contents(txn_root, "/foo",
6885 "K 4\n"
6886 "prop\n"
6887 "V 5\n"
6888 "value\n"
6889 "END\n", pool));
6890
6891 /* Commit revision r2. */
6892 SVN_ERR(test_commit_txn(&new_rev, txn, NULL, pool));
6893
6894 /* Check that FS reports correct length for the file (23). */
6895 SVN_ERR(svn_fs_revision_root(&rev_root, fs, 2, pool));
6896 SVN_ERR(svn_fs_file_length(&length, rev_root, "/foo", pool));
6897
6898 SVN_TEST_ASSERT(length == 23);
6899 return SVN_NO_ERROR;
6900 }
6901
6902 static svn_error_t *
test_internal_txn_props(const svn_test_opts_t * opts,apr_pool_t * pool)6903 test_internal_txn_props(const svn_test_opts_t *opts,
6904 apr_pool_t *pool)
6905 {
6906 svn_fs_t *fs;
6907 svn_fs_txn_t *txn;
6908 svn_string_t *val;
6909 svn_prop_t prop;
6910 svn_prop_t internal_prop;
6911 apr_array_header_t *props;
6912 apr_hash_t *proplist;
6913 svn_error_t *err;
6914
6915 SVN_ERR(svn_test__create_fs(&fs, "test-repo-internal-txn-props",
6916 opts, pool));
6917 SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0,
6918 SVN_FS_TXN_CHECK_LOCKS |
6919 SVN_FS_TXN_CHECK_OOD |
6920 SVN_FS_TXN_CLIENT_DATE, pool));
6921
6922 /* Ensure that we cannot read internal transaction properties. */
6923 SVN_ERR(svn_fs_txn_prop(&val, txn, SVN_FS__PROP_TXN_CHECK_LOCKS, pool));
6924 SVN_TEST_ASSERT(!val);
6925 SVN_ERR(svn_fs_txn_prop(&val, txn, SVN_FS__PROP_TXN_CHECK_OOD, pool));
6926 SVN_TEST_ASSERT(!val);
6927 SVN_ERR(svn_fs_txn_prop(&val, txn, SVN_FS__PROP_TXN_CLIENT_DATE, pool));
6928 SVN_TEST_ASSERT(!val);
6929
6930 SVN_ERR(svn_fs_txn_proplist(&proplist, txn, pool));
6931 SVN_TEST_ASSERT(apr_hash_count(proplist) == 1);
6932 val = svn_hash_gets(proplist, SVN_PROP_REVISION_DATE);
6933 SVN_TEST_ASSERT(val);
6934
6935 /* We also cannot change or discard them. */
6936 val = svn_string_create("Ooops!", pool);
6937
6938 err = svn_fs_change_txn_prop(txn, SVN_FS__PROP_TXN_CHECK_LOCKS, val, pool);
6939 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_INCORRECT_PARAMS);
6940 err = svn_fs_change_txn_prop(txn, SVN_FS__PROP_TXN_CHECK_LOCKS, NULL, pool);
6941 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_INCORRECT_PARAMS);
6942 err = svn_fs_change_txn_prop(txn, SVN_FS__PROP_TXN_CHECK_OOD, val, pool);
6943 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_INCORRECT_PARAMS);
6944 err = svn_fs_change_txn_prop(txn, SVN_FS__PROP_TXN_CHECK_OOD, NULL, pool);
6945 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_INCORRECT_PARAMS);
6946 err = svn_fs_change_txn_prop(txn, SVN_FS__PROP_TXN_CLIENT_DATE, val, pool);
6947 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_INCORRECT_PARAMS);
6948 err = svn_fs_change_txn_prop(txn, SVN_FS__PROP_TXN_CLIENT_DATE, NULL, pool);
6949 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_INCORRECT_PARAMS);
6950
6951 prop.name = "foo";
6952 prop.value = svn_string_create("bar", pool);
6953 internal_prop.name = SVN_FS__PROP_TXN_CHECK_LOCKS;
6954 internal_prop.value = svn_string_create("Ooops!", pool);
6955
6956 props = apr_array_make(pool, 2, sizeof(svn_prop_t));
6957 APR_ARRAY_PUSH(props, svn_prop_t) = prop;
6958 APR_ARRAY_PUSH(props, svn_prop_t) = internal_prop;
6959
6960 err = svn_fs_change_txn_props(txn, props, pool);
6961 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_INCORRECT_PARAMS);
6962
6963 return SVN_NO_ERROR;
6964 }
6965
6966 /* A freeze function that expects an 'svn_error_t *' baton, and returns it. */
6967 /* This function implements svn_fs_freeze_func_t. */
6968 static svn_error_t *
freeze_func(void * baton,apr_pool_t * pool)6969 freeze_func(void *baton, apr_pool_t *pool)
6970 {
6971 return baton;
6972 }
6973
6974 static svn_error_t *
freeze_and_commit(const svn_test_opts_t * opts,apr_pool_t * pool)6975 freeze_and_commit(const svn_test_opts_t *opts,
6976 apr_pool_t *pool)
6977 {
6978 svn_fs_t *fs;
6979 svn_fs_txn_t *txn;
6980 svn_fs_root_t *txn_root;
6981 svn_revnum_t new_rev = 0;
6982 apr_pool_t *subpool = svn_pool_create(pool);
6983 const char *repo_name = "test-repo-freeze-and-commit";
6984
6985 if (!strcmp(opts->fs_type, "bdb"))
6986 return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
6987 "this will not test BDB repositories");
6988
6989 SVN_ERR(svn_test__create_fs(&fs, repo_name, opts, subpool));
6990
6991 /* This test used to FAIL with an SQLite error since svn_fs_freeze()
6992 * wouldn't unlock rep-cache.db. Therefore, part of the role of creating
6993 * the Greek tree is to create a rep-cache.db, in order to test that
6994 * svn_fs_freeze() unlocks it. */
6995
6996 /* r1: Commit the Greek tree. */
6997 SVN_ERR(svn_fs_begin_txn(&txn, fs, new_rev, subpool));
6998 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
6999 SVN_ERR(svn_test__create_greek_tree(txn_root, subpool));
7000 SVN_ERR(test_commit_txn(&new_rev, txn, NULL, subpool));
7001
7002 /* Freeze and unfreeze. */
7003 SVN_ERR(svn_fs_freeze(fs, freeze_func, SVN_NO_ERROR, pool));
7004
7005 /* Freeze again, but have freeze_func fail. */
7006 {
7007 svn_error_t *err = svn_error_create(APR_EGENERAL, NULL, NULL);
7008 SVN_TEST_ASSERT_ERROR(svn_fs_freeze(fs, freeze_func, err, pool),
7009 err->apr_err);
7010 }
7011
7012 /* Make some commit using same FS instance. */
7013 SVN_ERR(svn_fs_begin_txn(&txn, fs, new_rev, pool));
7014 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
7015 SVN_ERR(svn_fs_change_node_prop(txn_root, "", "temperature",
7016 svn_string_create("310.05", pool),
7017 pool));
7018 SVN_ERR(test_commit_txn(&new_rev, txn, NULL, pool));
7019
7020 /* Re-open FS and make another commit. */
7021 SVN_ERR(svn_fs_open(&fs, repo_name, NULL, subpool));
7022 SVN_ERR(svn_fs_begin_txn(&txn, fs, new_rev, pool));
7023 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
7024 SVN_ERR(svn_fs_change_node_prop(txn_root, "/", "temperature",
7025 svn_string_create("451", pool),
7026 pool));
7027 SVN_ERR(test_commit_txn(&new_rev, txn, NULL, pool));
7028
7029 return SVN_NO_ERROR;
7030 }
7031
7032 /* Number of changes in a revision.
7033 * Should be > 100 to span multiple blocks. */
7034 #define CHANGES_COUNT 1017
7035
7036 /* Check that REVISION in FS reports the expected changes. */
7037 static svn_error_t *
verify_added_files_list(svn_fs_t * fs,svn_revnum_t revision,apr_pool_t * scratch_pool)7038 verify_added_files_list(svn_fs_t *fs,
7039 svn_revnum_t revision,
7040 apr_pool_t *scratch_pool)
7041 {
7042 int i;
7043 svn_fs_root_t *root;
7044 apr_hash_t *changed_paths;
7045 svn_fs_path_change_iterator_t *iterator;
7046 svn_fs_path_change3_t *change;
7047 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
7048
7049 /* Collect changes and test that no path gets reported twice. */
7050 SVN_ERR(svn_fs_revision_root(&root, fs, revision, scratch_pool));
7051 SVN_ERR(svn_fs_paths_changed3(&iterator, root, scratch_pool, scratch_pool));
7052
7053 changed_paths = apr_hash_make(scratch_pool);
7054 SVN_ERR(svn_fs_path_change_get(&change, iterator));
7055 while (change)
7056 {
7057 const char *path = apr_pstrmemdup(scratch_pool, change->path.data,
7058 change->path.len);
7059 SVN_TEST_ASSERT(change->change_kind == svn_fs_path_change_add);
7060 SVN_TEST_ASSERT(!apr_hash_get(changed_paths, path, change->path.len));
7061
7062 apr_hash_set(changed_paths, path, change->path.len, path);
7063 SVN_ERR(svn_fs_path_change_get(&change, iterator));
7064 }
7065
7066 /* Verify that we've got exactly all paths that we added. */
7067 SVN_TEST_ASSERT(CHANGES_COUNT == apr_hash_count(changed_paths));
7068 for (i = 0; i < CHANGES_COUNT; ++i)
7069 {
7070 const char *file_name;
7071 svn_pool_clear(iterpool);
7072
7073 file_name = apr_psprintf(iterpool, "/file-%d", i);
7074 SVN_TEST_ASSERT(svn_hash_gets(changed_paths, file_name));
7075 }
7076
7077 return SVN_NO_ERROR;
7078 }
7079
7080 static svn_error_t *
test_large_changed_paths_list(const svn_test_opts_t * opts,apr_pool_t * pool)7081 test_large_changed_paths_list(const svn_test_opts_t *opts,
7082 apr_pool_t *pool)
7083 {
7084 svn_fs_t *fs;
7085 svn_fs_txn_t *txn;
7086 svn_fs_root_t *txn_root;
7087 int i;
7088 svn_revnum_t rev = 0;
7089 apr_pool_t *iterpool = svn_pool_create(pool);
7090 const char *repo_name = "test-repo-changed-paths-list";
7091
7092 SVN_ERR(svn_test__create_fs(&fs, repo_name, opts, pool));
7093
7094 /* r1: Add many empty files - just to amass a long list of changes. */
7095 SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, pool));
7096 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
7097
7098 for (i = 0; i < CHANGES_COUNT; ++i)
7099 {
7100 const char *file_name;
7101 svn_pool_clear(iterpool);
7102
7103 file_name = apr_psprintf(iterpool, "/file-%d", i);
7104 SVN_ERR(svn_fs_make_file(txn_root, file_name, iterpool));
7105 }
7106
7107 SVN_ERR(test_commit_txn(&rev, txn, NULL, pool));
7108
7109 /* Now, read the change list.
7110 * Do it twice to cover cached data as well. */
7111 svn_pool_clear(iterpool);
7112 SVN_ERR(verify_added_files_list(fs, rev, iterpool));
7113 svn_pool_clear(iterpool);
7114 SVN_ERR(verify_added_files_list(fs, rev, iterpool));
7115 svn_pool_destroy(iterpool);
7116
7117 return SVN_NO_ERROR;
7118 }
7119
7120 #undef CHANGES_COUNT
7121
7122 static svn_error_t *
commit_with_locked_rep_cache(const svn_test_opts_t * opts,apr_pool_t * pool)7123 commit_with_locked_rep_cache(const svn_test_opts_t *opts,
7124 apr_pool_t *pool)
7125 {
7126 svn_fs_t *fs;
7127 svn_fs_txn_t *txn;
7128 svn_fs_root_t *txn_root;
7129 svn_revnum_t new_rev;
7130 svn_sqlite__db_t *sdb;
7131 svn_error_t *err;
7132 const char *fs_path;
7133 const char *statements[] = { "SELECT MAX(revision) FROM rep_cache", NULL };
7134
7135 if (strcmp(opts->fs_type, SVN_FS_TYPE_BDB) == 0)
7136 return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
7137 "this will not test BDB repositories");
7138
7139 if (opts->server_minor_version && (opts->server_minor_version < 6))
7140 return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
7141 "pre-1.6 SVN doesn't support FSFS rep-sharing");
7142
7143 fs_path = "test-repo-commit-with-locked-rep-cache";
7144 SVN_ERR(svn_test__create_fs(&fs, fs_path, opts, pool));
7145
7146 /* r1: Add a file. */
7147 SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0, 0, pool));
7148 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
7149 SVN_ERR(svn_fs_make_file(txn_root, "/foo", pool));
7150 SVN_ERR(svn_test__set_file_contents(txn_root, "/foo", "a", pool));
7151 SVN_ERR(test_commit_txn(&new_rev, txn, NULL, pool));
7152 SVN_TEST_INT_ASSERT(new_rev, 1);
7153
7154 /* Begin a new transaction based on r1. */
7155 SVN_ERR(svn_fs_begin_txn2(&txn, fs, 1, 0, pool));
7156 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
7157 SVN_ERR(svn_test__set_file_contents(txn_root, "/foo", "b", pool));
7158
7159 /* Obtain a shared lock on the rep-cache.db by starting a new read
7160 * transaction. */
7161 SVN_ERR(svn_sqlite__open(&sdb,
7162 svn_dirent_join(fs_path, "rep-cache.db", pool),
7163 svn_sqlite__mode_readonly, statements, 0, NULL,
7164 0, pool, pool));
7165 SVN_ERR(svn_sqlite__begin_transaction(sdb));
7166 SVN_ERR(svn_sqlite__exec_statements(sdb, 0));
7167
7168 /* Attempt to commit fs transaction. This should result in a commit
7169 * post-processing error due to us still holding the shared lock on the
7170 * rep-cache.db. */
7171 err = svn_fs_commit_txn(NULL, &new_rev, txn, pool);
7172 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_SQLITE_BUSY);
7173 SVN_TEST_INT_ASSERT(new_rev, 2);
7174
7175 /* Release the shared lock. */
7176 SVN_ERR(svn_sqlite__finish_transaction(sdb, SVN_NO_ERROR));
7177 SVN_ERR(svn_sqlite__close(sdb));
7178
7179 /* Try an operation that reads from rep-cache.db.
7180 *
7181 * XFAIL: Around r1740802, this call was producing an error due to the
7182 * svn_fs_t keeping an unusable db connection (and associated file
7183 * locks) within it.
7184 */
7185 SVN_ERR(svn_fs_verify(fs_path, NULL, 0, SVN_INVALID_REVNUM, NULL, NULL,
7186 NULL, NULL, pool));
7187
7188 return SVN_NO_ERROR;
7189 }
7190
7191
7192 static svn_error_t *
test_cache_clear_during_stream(const svn_test_opts_t * opts,apr_pool_t * pool)7193 test_cache_clear_during_stream(const svn_test_opts_t *opts,
7194 apr_pool_t *pool)
7195 {
7196 svn_fs_t *fs;
7197 svn_fs_txn_t *txn;
7198 svn_fs_root_t *txn_root, *rev_root;
7199 svn_revnum_t new_rev;
7200 const char *fs_path;
7201 apr_pool_t *iterpool = svn_pool_create(pool);
7202 apr_pool_t *subpool = svn_pool_create(pool);
7203 svn_txdelta_window_handler_t consumer_func;
7204 void *consumer_baton;
7205 int i;
7206 svn_stream_t *stream;
7207 svn_stringbuf_t *buf;
7208
7209
7210 fs_path = "test-repo-cache_clear_during_stream";
7211 SVN_ERR(svn_test__create_fs(&fs, fs_path, opts, pool));
7212
7213 /* r1: Add a file. */
7214 SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0, 0, pool));
7215 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
7216 SVN_ERR(svn_fs_make_file(txn_root, "/foo", pool));
7217
7218 /* Make the file large enough to span multiple txdelta windows.
7219 * Just to be sure, make it not too uniform to keep self-txdelta at bay. */
7220 SVN_ERR(svn_fs_apply_textdelta(&consumer_func, &consumer_baton,
7221 txn_root, "/foo", NULL, NULL, subpool));
7222 stream = svn_txdelta_target_push(consumer_func, consumer_baton,
7223 svn_stream_empty(subpool), subpool);
7224 for (i = 0; i < 10000; ++ i)
7225 {
7226 svn_string_t *text;
7227
7228 svn_pool_clear(iterpool);
7229 text = svn_string_createf(iterpool, "some dummy text - %d\n", i);
7230 SVN_ERR(svn_stream_write(stream, text->data, &text->len));
7231 }
7232
7233 SVN_ERR(svn_stream_close(stream));
7234 svn_pool_destroy(subpool);
7235
7236 SVN_ERR(test_commit_txn(&new_rev, txn, NULL, pool));
7237 SVN_TEST_INT_ASSERT(new_rev, 1);
7238
7239 /* Read the file once to populate the fulltext cache. */
7240 SVN_ERR(svn_fs_revision_root(&rev_root, fs, 1, pool));
7241 SVN_ERR(svn_fs_file_contents(&stream, rev_root, "/foo", pool));
7242 SVN_ERR(svn_test__stream_to_string(&buf, stream, pool));
7243
7244 /* Start reading it again from cache, clear the cache and continue.
7245 * Make sure we read more than one txdelta window before clearing
7246 * the cache. That gives the FS backend a chance to skip windows
7247 * when continuing the read from disk. */
7248 SVN_ERR(svn_fs_file_contents(&stream, rev_root, "/foo", pool));
7249 buf->len = 2 * SVN_STREAM_CHUNK_SIZE;
7250 SVN_ERR(svn_stream_read_full(stream, buf->data, &buf->len));
7251 SVN_ERR(svn_cache__membuffer_clear(svn_cache__get_global_membuffer_cache()));
7252 SVN_ERR(svn_test__stream_to_string(&buf, stream, pool));
7253
7254 svn_pool_destroy(iterpool);
7255
7256 return SVN_NO_ERROR;
7257 }
7258
7259 static svn_error_t *
test_rep_sharing_strict_content_check(const svn_test_opts_t * opts,apr_pool_t * pool)7260 test_rep_sharing_strict_content_check(const svn_test_opts_t *opts,
7261 apr_pool_t *pool)
7262 {
7263 svn_fs_t *fs;
7264 svn_fs_txn_t *txn;
7265 svn_fs_root_t *txn_root;
7266 svn_revnum_t new_rev;
7267 const char *fs_path, *fs_path2;
7268 apr_pool_t *subpool = svn_pool_create(pool);
7269 svn_error_t *err;
7270
7271 /* Bail (with success) on known-untestable scenarios */
7272 if (strcmp(opts->fs_type, SVN_FS_TYPE_BDB) == 0)
7273 return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
7274 "BDB repositories don't support rep-sharing");
7275
7276 /* Create 2 repos with same structure & size but different contents */
7277 fs_path = "test-rep-sharing-strict-content-check1";
7278 fs_path2 = "test-rep-sharing-strict-content-check2";
7279
7280 SVN_ERR(svn_test__create_fs(&fs, fs_path, opts, subpool));
7281
7282 SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0, 0, subpool));
7283 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
7284 SVN_ERR(svn_fs_make_file(txn_root, "/foo", subpool));
7285 SVN_ERR(svn_test__set_file_contents(txn_root, "foo", "quite bad", subpool));
7286 SVN_ERR(test_commit_txn(&new_rev, txn, NULL, subpool));
7287 SVN_TEST_INT_ASSERT(new_rev, 1);
7288
7289 SVN_ERR(svn_test__create_fs(&fs, fs_path2, opts, subpool));
7290
7291 SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0, 0, subpool));
7292 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
7293 SVN_ERR(svn_fs_make_file(txn_root, "foo", subpool));
7294 SVN_ERR(svn_test__set_file_contents(txn_root, "foo", "very good", subpool));
7295 SVN_ERR(test_commit_txn(&new_rev, txn, NULL, subpool));
7296 SVN_TEST_INT_ASSERT(new_rev, 1);
7297
7298 /* Close both repositories. */
7299 svn_pool_clear(subpool);
7300
7301 /* Doctor the first repo such that it uses the wrong rep-cache. */
7302 SVN_ERR(svn_io_copy_file(svn_relpath_join(fs_path2, "rep-cache.db", pool),
7303 svn_relpath_join(fs_path, "rep-cache.db", pool),
7304 FALSE, pool));
7305
7306 /* Changing the file contents such that rep-sharing would kick in if
7307 the file contents was not properly compared. */
7308 SVN_ERR(svn_fs_open2(&fs, fs_path, NULL, subpool, subpool));
7309
7310 SVN_ERR(svn_fs_begin_txn2(&txn, fs, 1, 0, subpool));
7311 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
7312 err = svn_test__set_file_contents(txn_root, "foo", "very good", subpool);
7313 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_AMBIGUOUS_CHECKSUM_REP);
7314
7315 svn_pool_destroy(subpool);
7316
7317 return SVN_NO_ERROR;
7318 }
7319
7320 static svn_error_t *
closest_copy_test_svn_4677(const svn_test_opts_t * opts,apr_pool_t * pool)7321 closest_copy_test_svn_4677(const svn_test_opts_t *opts,
7322 apr_pool_t *pool)
7323 {
7324 svn_fs_t *fs;
7325 svn_fs_txn_t *txn;
7326 svn_fs_root_t *txn_root, *rev_root, *croot;
7327 svn_revnum_t after_rev;
7328 const char *cpath;
7329 apr_pool_t *spool = svn_pool_create(pool);
7330
7331 /* Prepare a filesystem. */
7332 SVN_ERR(svn_test__create_fs(&fs, "test-repo-svn-4677",
7333 opts, pool));
7334
7335 /* In first txn, create file A/foo. */
7336 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, spool));
7337 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
7338 SVN_ERR(svn_fs_make_dir(txn_root, "A", spool));
7339 SVN_ERR(svn_fs_make_file(txn_root, "A/foo", spool));
7340 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, spool));
7341 svn_pool_clear(spool);
7342 SVN_ERR(svn_fs_revision_root(&rev_root, fs, after_rev, spool));
7343
7344 /* Move A to B, and commit. */
7345 SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, spool));
7346 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
7347 SVN_ERR(svn_fs_copy(rev_root, "A", txn_root, "B", spool));
7348 SVN_ERR(svn_fs_delete(txn_root, "A", spool));
7349 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, spool));
7350 svn_pool_clear(spool);
7351 SVN_ERR(svn_fs_revision_root(&rev_root, fs, after_rev, spool));
7352
7353 /* Replace file B/foo with directory B/foo, add B/foo/bar, and commit. */
7354 SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, spool));
7355 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
7356 SVN_ERR(svn_fs_delete(txn_root, "B/foo", spool));
7357 SVN_ERR(svn_fs_make_dir(txn_root, "B/foo", spool));
7358 SVN_ERR(svn_fs_make_file(txn_root, "B/foo/bar", spool));
7359 SVN_ERR(test_commit_txn(&after_rev, txn, NULL, spool));
7360 svn_pool_clear(spool);
7361 SVN_ERR(svn_fs_revision_root(&rev_root, fs, after_rev, spool));
7362
7363 /* B/foo/bar has been copied.
7364 Issue 4677 was caused by returning an error in this situation. */
7365 SVN_ERR(svn_fs_closest_copy(&croot, &cpath, rev_root, "B/foo/bar", spool));
7366 SVN_TEST_ASSERT(cpath == NULL);
7367 SVN_TEST_ASSERT(croot == NULL);
7368
7369 return SVN_NO_ERROR;
7370 }
7371
7372 static svn_error_t *
test_closest_copy_file_replaced_with_dir(const svn_test_opts_t * opts,apr_pool_t * pool)7373 test_closest_copy_file_replaced_with_dir(const svn_test_opts_t *opts,
7374 apr_pool_t *pool)
7375 {
7376 svn_fs_t *fs;
7377 svn_fs_txn_t *txn;
7378 svn_fs_root_t *txn_root;
7379 svn_fs_root_t *rev_root;
7380 svn_revnum_t youngest_rev;
7381 svn_fs_root_t *copy_root;
7382 const char *copy_path;
7383
7384 /* Prepare a filesystem. */
7385 SVN_ERR(svn_test__create_fs(&fs, "test-closest-copy-file-replaced-with-dir",
7386 opts, pool));
7387
7388 youngest_rev = 0;
7389
7390 /* Modeled after the case described in the thread:
7391 "[PATCH] A test for "Can't get entries" error"
7392 https://lists.apache.org/thread.html/693a95b0da834387e78a7f08df2392b634397d32f37428c81c02f8c5@%3Cdev.subversion.apache.org%3E
7393 */
7394 /* r1: Add a directory with a file. */
7395 SVN_ERR(svn_fs_begin_txn2(&txn, fs, youngest_rev, 0, pool));
7396 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
7397 SVN_ERR(svn_fs_make_dir(txn_root, "/A", pool));
7398 SVN_ERR(svn_fs_make_file(txn_root, "/A/mu", pool));
7399 SVN_ERR(test_commit_txn(&youngest_rev, txn, NULL, pool));
7400 SVN_TEST_INT_ASSERT(youngest_rev, 1);
7401
7402 /* r2: Copy the directory. */
7403 SVN_ERR(svn_fs_begin_txn2(&txn, fs, youngest_rev, 0, pool));
7404 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
7405 SVN_ERR(svn_fs_revision_root(&rev_root, fs, 1, pool));
7406 SVN_ERR(svn_fs_copy(rev_root, "/A", txn_root, "/B", pool));
7407 SVN_ERR(test_commit_txn(&youngest_rev, txn, NULL, pool));
7408 SVN_TEST_INT_ASSERT(youngest_rev, 2);
7409
7410 /* r3: Delete the file. */
7411 SVN_ERR(svn_fs_begin_txn2(&txn, fs, youngest_rev, 0, pool));
7412 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
7413 SVN_ERR(svn_fs_delete(txn_root, "/B/mu", pool));
7414 SVN_ERR(test_commit_txn(&youngest_rev, txn, NULL, pool));
7415 SVN_TEST_INT_ASSERT(youngest_rev, 3);
7416
7417 /* r4: Replace the file with a new directory containing a file. */
7418 SVN_ERR(svn_fs_begin_txn2(&txn, fs, youngest_rev, 0, pool));
7419 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
7420 SVN_ERR(svn_fs_make_dir(txn_root, "/B/mu", pool));
7421 SVN_ERR(svn_fs_make_file(txn_root, "/B/mu/iota", pool));
7422 SVN_ERR(test_commit_txn(&youngest_rev, txn, NULL, pool));
7423 SVN_TEST_INT_ASSERT(youngest_rev, 4);
7424
7425 /* Test a couple of svn_fs_closest_copy() calls; the second call used
7426 to fail with an unexpected SVN_ERR_FS_NOT_DIRECTORY error. */
7427
7428 SVN_ERR(svn_fs_revision_root(&rev_root, fs, 2, pool));
7429 SVN_ERR(svn_fs_closest_copy(©_root, ©_path, rev_root, "/B/mu", pool));
7430
7431 SVN_TEST_ASSERT(copy_root != NULL);
7432 SVN_TEST_INT_ASSERT(svn_fs_revision_root_revision(copy_root), 2);
7433 SVN_TEST_STRING_ASSERT(copy_path, "/B");
7434
7435 SVN_ERR(svn_fs_revision_root(&rev_root, fs, 4, pool));
7436 SVN_ERR(svn_fs_closest_copy(©_root, ©_path, rev_root, "/B/mu/iota", pool));
7437
7438 SVN_TEST_ASSERT(copy_root == NULL);
7439 SVN_TEST_ASSERT(copy_path == NULL);
7440
7441 return SVN_NO_ERROR;
7442 }
7443
7444 static svn_error_t *
test_unrecognized_ioctl(const svn_test_opts_t * opts,apr_pool_t * pool)7445 test_unrecognized_ioctl(const svn_test_opts_t *opts,
7446 apr_pool_t *pool)
7447 {
7448 svn_fs_t *fs;
7449 svn_error_t *err;
7450 svn_fs_ioctl_code_t code = {0};
7451
7452 SVN_ERR(svn_test__create_fs(&fs, "test-unrecognized-ioctl", opts, pool));
7453
7454 code.fs_type = "NON-EXISTING";
7455 code.code = 98765;
7456 err = svn_fs_ioctl(fs, code, NULL, NULL, NULL, NULL, pool, pool);
7457 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE);
7458
7459 code.fs_type = "NON-EXISTING";
7460 code.code = 98765;
7461 err = svn_fs_ioctl(NULL, code, NULL, NULL, NULL, NULL, pool, pool);
7462 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_UNKNOWN_FS_TYPE);
7463
7464 code.fs_type = opts->fs_type;
7465 code.code = 98765;
7466 err = svn_fs_ioctl(NULL, code, NULL, NULL, NULL, NULL, pool, pool);
7467 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE);
7468
7469 return SVN_NO_ERROR;
7470 }
7471
7472 /* ------------------------------------------------------------------------ */
7473
7474 /* The test table. */
7475
7476 static int max_threads = 8;
7477
7478 static struct svn_test_descriptor_t test_funcs[] =
7479 {
7480 SVN_TEST_NULL,
7481 SVN_TEST_OPTS_PASS(trivial_transaction,
7482 "begin a txn, check its name, then close it"),
7483 SVN_TEST_OPTS_PASS(reopen_trivial_transaction,
7484 "open an existing transaction by name"),
7485 SVN_TEST_OPTS_PASS(create_file_transaction,
7486 "begin a txn, get the txn root, and add a file"),
7487 SVN_TEST_OPTS_PASS(verify_txn_list,
7488 "create 2 txns, list them, and verify the list"),
7489 SVN_TEST_OPTS_PASS(txn_names_are_not_reused,
7490 "check that transaction names are not reused"),
7491 SVN_TEST_OPTS_PASS(write_and_read_file,
7492 "write and read a file's contents"),
7493 SVN_TEST_OPTS_PASS(almostmedium_file_integrity,
7494 "create and modify almostmedium file"),
7495 SVN_TEST_OPTS_PASS(medium_file_integrity,
7496 "create and modify medium file"),
7497 SVN_TEST_OPTS_PASS(large_file_integrity,
7498 "create and modify large file"),
7499 SVN_TEST_OPTS_PASS(create_mini_tree_transaction,
7500 "test basic file and subdirectory creation"),
7501 SVN_TEST_OPTS_PASS(create_greek_tree_transaction,
7502 "make The Official Subversion Test Tree"),
7503 SVN_TEST_OPTS_PASS(list_directory,
7504 "fill a directory, then list it"),
7505 SVN_TEST_OPTS_PASS(revision_props,
7506 "set and get some revision properties"),
7507 SVN_TEST_OPTS_PASS(transaction_props,
7508 "set/get txn props, commit, validate new rev props"),
7509 SVN_TEST_OPTS_PASS(node_props,
7510 "set and get some node properties"),
7511 SVN_TEST_OPTS_PASS(delete_mutables,
7512 "delete mutable nodes from directories"),
7513 SVN_TEST_OPTS_PASS(delete,
7514 "delete nodes tree"),
7515 SVN_TEST_OPTS_PASS(fetch_youngest_rev,
7516 "fetch the youngest revision from a filesystem"),
7517 SVN_TEST_OPTS_PASS(basic_commit,
7518 "basic commit"),
7519 SVN_TEST_OPTS_PASS(test_tree_node_validation,
7520 "testing tree validation helper"),
7521 SVN_TEST_OPTS_PASS(merging_commit, "merging commit"),
7522 SVN_TEST_OPTS_PASS(copy_test,
7523 "copying and tracking copy history"),
7524 SVN_TEST_OPTS_PASS(commit_date,
7525 "commit datestamps"),
7526 SVN_TEST_OPTS_PASS(check_old_revisions,
7527 "check old revisions"),
7528 SVN_TEST_OPTS_PASS(check_all_revisions,
7529 "after each commit, check all revisions"),
7530 SVN_TEST_OPTS_PASS(check_root_revision,
7531 "ensure accurate storage of root node"),
7532 SVN_TEST_OPTS_PASS(test_node_created_rev,
7533 "svn_fs_node_created_rev test"),
7534 SVN_TEST_OPTS_PASS(check_related,
7535 "test svn_fs_check_related"),
7536 SVN_TEST_OPTS_PASS(branch_test,
7537 "test complex copies (branches)"),
7538 SVN_TEST_OPTS_PASS(verify_checksum,
7539 "test checksums"),
7540 SVN_TEST_OPTS_PASS(closest_copy_test,
7541 "calculating closest history-affecting copies"),
7542 SVN_TEST_OPTS_PASS(root_revisions,
7543 "svn_fs_root_t (base) revisions"),
7544 SVN_TEST_OPTS_PASS(unordered_txn_dirprops,
7545 "test dir prop preservation in unordered txns"),
7546 SVN_TEST_OPTS_PASS(set_uuid,
7547 "test svn_fs_set_uuid"),
7548 SVN_TEST_OPTS_PASS(node_origin_rev,
7549 "test svn_fs_node_origin_rev"),
7550 SVN_TEST_OPTS_PASS(small_file_integrity,
7551 "create and modify small file"),
7552 SVN_TEST_OPTS_PASS(node_history,
7553 "test svn_fs_node_history"),
7554 SVN_TEST_OPTS_PASS(delete_fs,
7555 "test svn_fs_delete_fs"),
7556 SVN_TEST_OPTS_PASS(filename_trailing_newline,
7557 "filenames with trailing \\n might be rejected"),
7558 SVN_TEST_OPTS_PASS(test_fs_info_format,
7559 "test svn_fs_info_format"),
7560 SVN_TEST_OPTS_PASS(commit_timestamp,
7561 "commit timestamp"),
7562 SVN_TEST_OPTS_PASS(test_compat_version,
7563 "test svn_fs__compatible_version"),
7564 SVN_TEST_OPTS_PASS(dir_prop_merge,
7565 "test merge directory properties"),
7566 SVN_TEST_OPTS_PASS(upgrade_while_committing,
7567 "upgrade while committing"),
7568 SVN_TEST_OPTS_PASS(test_paths_changed,
7569 "test svn_fs_paths_changed"),
7570 SVN_TEST_OPTS_PASS(test_delete_replaced_paths_changed,
7571 "test deletion after replace in changed paths list"),
7572 SVN_TEST_OPTS_PASS(purge_txn_test,
7573 "test purging transactions"),
7574 SVN_TEST_OPTS_PASS(compare_contents,
7575 "compare contents of different nodes"),
7576 SVN_TEST_OPTS_PASS(test_path_change_create,
7577 "test svn_fs_path_change2_create"),
7578 SVN_TEST_OPTS_PASS(test_node_created_info,
7579 "test FS 'node created' info"),
7580 SVN_TEST_OPTS_PASS(test_print_modules,
7581 "test FS module listing"),
7582 SVN_TEST_OPTS_PASS(test_zero_copy_processsing,
7583 "test zero copy file processing"),
7584 SVN_TEST_OPTS_PASS(test_dir_optimal_order,
7585 "test svn_fs_dir_optimal_order"),
7586 SVN_TEST_OPTS_PASS(test_config_files,
7587 "get configuration files"),
7588 SVN_TEST_OPTS_PASS(test_delta_file_stream,
7589 "get a delta stream on a file"),
7590 SVN_TEST_OPTS_PASS(test_fs_merge,
7591 "get merging txns with newer revisions"),
7592 SVN_TEST_OPTS_PASS(test_fsfs_config_opts,
7593 "test creating FSFS repository with different opts"),
7594 SVN_TEST_OPTS_PASS(test_txn_pool_lifetime,
7595 "test pool lifetime dependencies with txn roots"),
7596 SVN_TEST_OPTS_PASS(test_modify_txn_being_written,
7597 "test modify txn being written"),
7598 SVN_TEST_OPTS_PASS(test_prop_and_text_rep_sharing_collision,
7599 "test property and text rep-sharing collision"),
7600 SVN_TEST_OPTS_PASS(test_internal_txn_props,
7601 "test setting and getting internal txn props"),
7602 SVN_TEST_OPTS_PASS(check_txn_related,
7603 "test svn_fs_check_related for transactions"),
7604 SVN_TEST_OPTS_PASS(freeze_and_commit,
7605 "freeze and commit"),
7606 SVN_TEST_OPTS_PASS(test_large_changed_paths_list,
7607 "test reading a large changed paths list"),
7608 SVN_TEST_OPTS_PASS(commit_with_locked_rep_cache,
7609 "test commit with locked rep-cache"),
7610 SVN_TEST_OPTS_PASS(test_cache_clear_during_stream,
7611 "test clearing the cache while streaming a rep"),
7612 SVN_TEST_OPTS_PASS(test_rep_sharing_strict_content_check,
7613 "test rep-sharing on content rather than SHA1"),
7614 SVN_TEST_OPTS_PASS(closest_copy_test_svn_4677,
7615 "test issue SVN-4677 regression"),
7616 SVN_TEST_OPTS_PASS(test_closest_copy_file_replaced_with_dir,
7617 "svn_fs_closest_copy after replacing file with dir"),
7618 SVN_TEST_OPTS_PASS(test_unrecognized_ioctl,
7619 "test svn_fs_ioctl with unrecognized code"),
7620 SVN_TEST_NULL
7621 };
7622
7623 SVN_TEST_MAIN
7624