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
27 #include "../svn_test.h"
28
29 #include "svn_pools.h"
30 #include "svn_time.h"
31 #include "svn_string.h"
32 #include "svn_fs.h"
33
34 #include "../svn_test_fs.h"
35
36 #include "../../libsvn_fs_base/id.h"
37 #include "../../libsvn_fs_base/trail.h"
38 #include "../../libsvn_fs_base/bdb/txn-table.h"
39 #include "../../libsvn_fs_base/bdb/nodes-table.h"
40 #include "../../libsvn_fs_base/key-gen.h"
41
42 #include "private/svn_fs_util.h"
43 #include "../../libsvn_delta/delta.h"
44
45 #define SET_STR(ps, s) ((ps)->data = (s), (ps)->len = strlen(s))
46
47
48 /*-----------------------------------------------------------------*/
49
50 /** The actual fs-tests called by `make check` **/
51
52 /* Create a filesystem. */
53 static svn_error_t *
create_berkeley_filesystem(const svn_test_opts_t * opts,apr_pool_t * pool)54 create_berkeley_filesystem(const svn_test_opts_t *opts,
55 apr_pool_t *pool)
56 {
57 svn_fs_t *fs;
58
59 /* Create and close a repository. */
60 SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-create-berkeley", opts,
61 pool));
62
63 return SVN_NO_ERROR;
64 }
65
66
67 /* Generic Berkeley DB error handler function. */
68 static void
berkeley_error_handler(const char * errpfx,char * msg)69 berkeley_error_handler(const char *errpfx, char *msg)
70 {
71 fprintf(stderr, "%s%s\n", errpfx ? errpfx : "", msg);
72 }
73
74
75 /* Open an existing filesystem. */
76 static svn_error_t *
open_berkeley_filesystem(const svn_test_opts_t * opts,apr_pool_t * pool)77 open_berkeley_filesystem(const svn_test_opts_t *opts,
78 apr_pool_t *pool)
79 {
80 svn_fs_t *fs, *fs2;
81
82 /* Create and close a repository (using fs). */
83 SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-open-berkeley", opts,
84 pool));
85
86 /* Create a different fs object, and use it to re-open the
87 repository again. */
88 SVN_ERR(svn_test__fs_new(&fs2, pool));
89 SVN_ERR(svn_fs_open_berkeley(fs2, "test-repo-open-berkeley"));
90
91 /* Provide a handler for Berkeley DB error messages. */
92 SVN_ERR(svn_fs_set_berkeley_errcall(fs2, berkeley_error_handler));
93
94 return SVN_NO_ERROR;
95 }
96
97
98 /* Set *PRESENT to true if entry NAME is present in directory PATH
99 under ROOT, else set *PRESENT to false. */
100 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)101 check_entry(svn_fs_root_t *root,
102 const char *path,
103 const char *name,
104 svn_boolean_t *present,
105 apr_pool_t *pool)
106 {
107 apr_hash_t *entries;
108 svn_fs_dirent_t *ent;
109
110 SVN_ERR(svn_fs_dir_entries(&entries, root, path, pool));
111 ent = apr_hash_get(entries, name, APR_HASH_KEY_STRING);
112
113 if (ent)
114 *present = TRUE;
115 else
116 *present = FALSE;
117
118 return SVN_NO_ERROR;
119 }
120
121
122 /* Return an error if entry NAME is absent in directory PATH under ROOT. */
123 static svn_error_t *
check_entry_present(svn_fs_root_t * root,const char * path,const char * name,apr_pool_t * pool)124 check_entry_present(svn_fs_root_t *root, const char *path,
125 const char *name, apr_pool_t *pool)
126 {
127 svn_boolean_t present = FALSE;
128 SVN_ERR(check_entry(root, path, name, &present, pool));
129
130 if (! present)
131 return svn_error_createf
132 (SVN_ERR_FS_GENERAL, NULL,
133 "entry \"%s\" absent when it should be present", name);
134
135 return SVN_NO_ERROR;
136 }
137
138
139 /* Return an error if entry NAME is present in directory PATH under ROOT. */
140 static svn_error_t *
check_entry_absent(svn_fs_root_t * root,const char * path,const char * name,apr_pool_t * pool)141 check_entry_absent(svn_fs_root_t *root, const char *path,
142 const char *name, apr_pool_t *pool)
143 {
144 svn_boolean_t present = TRUE;
145 SVN_ERR(check_entry(root, path, name, &present, pool));
146
147 if (present)
148 return svn_error_createf
149 (SVN_ERR_FS_GENERAL, NULL,
150 "entry \"%s\" present when it should be absent", name);
151
152 return SVN_NO_ERROR;
153 }
154
155
156 struct check_id_args
157 {
158 svn_fs_t *fs;
159 const svn_fs_id_t *id;
160 svn_boolean_t present;
161 };
162
163
164 static svn_error_t *
txn_body_check_id(void * baton,trail_t * trail)165 txn_body_check_id(void *baton, trail_t *trail)
166 {
167 struct check_id_args *args = baton;
168 node_revision_t *noderev;
169 svn_error_t *err;
170
171 err = svn_fs_bdb__get_node_revision(&noderev, args->fs, args->id,
172 trail, trail->pool);
173
174 if (err && (err->apr_err == SVN_ERR_FS_ID_NOT_FOUND))
175 args->present = FALSE;
176 else if (! err)
177 args->present = TRUE;
178 else
179 {
180 svn_string_t *id_str = svn_fs_unparse_id(args->id, trail->pool);
181 return svn_error_createf
182 (SVN_ERR_FS_GENERAL, err,
183 "error looking for node revision id \"%s\"", id_str->data);
184 }
185 svn_error_clear(err);
186
187 return SVN_NO_ERROR;
188 }
189
190
191 /* Set *PRESENT to true if node revision ID is present in filesystem
192 FS, else set *PRESENT to false. */
193 static svn_error_t *
check_id(svn_fs_t * fs,const svn_fs_id_t * id,svn_boolean_t * present,apr_pool_t * pool)194 check_id(svn_fs_t *fs, const svn_fs_id_t *id, svn_boolean_t *present,
195 apr_pool_t *pool)
196 {
197 struct check_id_args args;
198
199 args.id = id;
200 args.fs = fs;
201 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_check_id, &args, TRUE, pool));
202
203 if (args.present)
204 *present = TRUE;
205 else
206 *present = FALSE;
207
208 return SVN_NO_ERROR;
209 }
210
211
212 /* Return error if node revision ID is not present in FS. */
213 static svn_error_t *
check_id_present(svn_fs_t * fs,const svn_fs_id_t * id,apr_pool_t * pool)214 check_id_present(svn_fs_t *fs, const svn_fs_id_t *id, apr_pool_t *pool)
215 {
216 svn_boolean_t present = FALSE;
217 SVN_ERR(check_id(fs, id, &present, pool));
218
219 if (! present)
220 {
221 svn_string_t *id_str = svn_fs_unparse_id(id, pool);
222 return svn_error_createf
223 (SVN_ERR_FS_GENERAL, NULL,
224 "node revision id \"%s\" absent when should be present",
225 id_str->data);
226 }
227
228 return SVN_NO_ERROR;
229 }
230
231
232 /* Return error if node revision ID is present in FS. */
233 static svn_error_t *
check_id_absent(svn_fs_t * fs,const svn_fs_id_t * id,apr_pool_t * pool)234 check_id_absent(svn_fs_t *fs, const svn_fs_id_t *id, apr_pool_t *pool)
235 {
236 svn_boolean_t present = TRUE;
237 SVN_ERR(check_id(fs, id, &present, pool));
238
239 if (present)
240 {
241 svn_string_t *id_str = svn_fs_unparse_id(id, pool);
242 return svn_error_createf
243 (SVN_ERR_FS_GENERAL, NULL,
244 "node revision id \"%s\" present when should be absent",
245 id_str->data);
246 }
247
248 return SVN_NO_ERROR;
249 }
250
251
252 /* Test that aborting a Subversion transaction works.
253
254 NOTE: This function tests internal filesystem interfaces, not just
255 the public filesystem interface. */
256 static svn_error_t *
abort_txn(const svn_test_opts_t * opts,apr_pool_t * pool)257 abort_txn(const svn_test_opts_t *opts,
258 apr_pool_t *pool)
259 {
260 svn_fs_t *fs;
261 svn_fs_txn_t *txn1, *txn2;
262 svn_fs_root_t *txn1_root, *txn2_root;
263 const char *txn1_name, *txn2_name;
264
265 /* Prepare two txns to receive the Greek tree. */
266 SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-abort-txn", opts,
267 pool));
268 SVN_ERR(svn_fs_begin_txn(&txn1, fs, 0, pool));
269 SVN_ERR(svn_fs_begin_txn(&txn2, fs, 0, pool));
270 SVN_ERR(svn_fs_txn_root(&txn1_root, txn1, pool));
271 SVN_ERR(svn_fs_txn_root(&txn2_root, txn2, pool));
272
273 /* Save their names for later. */
274 SVN_ERR(svn_fs_txn_name(&txn1_name, txn1, pool));
275 SVN_ERR(svn_fs_txn_name(&txn2_name, txn2, pool));
276
277 /* Create greek trees in them. */
278 SVN_ERR(svn_test__create_greek_tree(txn1_root, pool));
279 SVN_ERR(svn_test__create_greek_tree(txn2_root, pool));
280
281 /* The test is to abort txn2, while leaving txn1.
282 *
283 * After we abort txn2, we make sure that a) all of its nodes
284 * disappeared from the database, and b) none of txn1's nodes
285 * disappeared.
286 *
287 * Finally, we create a third txn, and check that the name it got is
288 * different from the names of txn1 and txn2.
289 */
290
291 {
292 /* Yes, I really am this paranoid. */
293
294 /* IDs for every file in the standard Greek Tree. */
295 const svn_fs_id_t
296 *t1_root_id, *t2_root_id,
297 *t1_iota_id, *t2_iota_id,
298 *t1_A_id, *t2_A_id,
299 *t1_mu_id, *t2_mu_id,
300 *t1_B_id, *t2_B_id,
301 *t1_lambda_id, *t2_lambda_id,
302 *t1_E_id, *t2_E_id,
303 *t1_alpha_id, *t2_alpha_id,
304 *t1_beta_id, *t2_beta_id,
305 *t1_F_id, *t2_F_id,
306 *t1_C_id, *t2_C_id,
307 *t1_D_id, *t2_D_id,
308 *t1_gamma_id, *t2_gamma_id,
309 *t1_H_id, *t2_H_id,
310 *t1_chi_id, *t2_chi_id,
311 *t1_psi_id, *t2_psi_id,
312 *t1_omega_id, *t2_omega_id,
313 *t1_G_id, *t2_G_id,
314 *t1_pi_id, *t2_pi_id,
315 *t1_rho_id, *t2_rho_id,
316 *t1_tau_id, *t2_tau_id;
317
318 SVN_ERR(svn_fs_node_id(&t1_root_id, txn1_root, "", pool));
319 SVN_ERR(svn_fs_node_id(&t2_root_id, txn2_root, "", pool));
320 SVN_ERR(svn_fs_node_id(&t1_iota_id, txn1_root, "iota", pool));
321 SVN_ERR(svn_fs_node_id(&t2_iota_id, txn2_root, "iota", pool));
322 SVN_ERR(svn_fs_node_id(&t1_A_id, txn1_root, "/A", pool));
323 SVN_ERR(svn_fs_node_id(&t2_A_id, txn2_root, "/A", pool));
324 SVN_ERR(svn_fs_node_id(&t1_mu_id, txn1_root, "/A/mu", pool));
325 SVN_ERR(svn_fs_node_id(&t2_mu_id, txn2_root, "/A/mu", pool));
326 SVN_ERR(svn_fs_node_id(&t1_B_id, txn1_root, "/A/B", pool));
327 SVN_ERR(svn_fs_node_id(&t2_B_id, txn2_root, "/A/B", pool));
328 SVN_ERR(svn_fs_node_id(&t1_lambda_id, txn1_root, "/A/B/lambda", pool));
329 SVN_ERR(svn_fs_node_id(&t2_lambda_id, txn2_root, "/A/B/lambda", pool));
330 SVN_ERR(svn_fs_node_id(&t1_E_id, txn1_root, "/A/B/E", pool));
331 SVN_ERR(svn_fs_node_id(&t2_E_id, txn2_root, "/A/B/E", pool));
332 SVN_ERR(svn_fs_node_id(&t1_alpha_id, txn1_root, "/A/B/E/alpha", pool));
333 SVN_ERR(svn_fs_node_id(&t2_alpha_id, txn2_root, "/A/B/E/alpha", pool));
334 SVN_ERR(svn_fs_node_id(&t1_beta_id, txn1_root, "/A/B/E/beta", pool));
335 SVN_ERR(svn_fs_node_id(&t2_beta_id, txn2_root, "/A/B/E/beta", pool));
336 SVN_ERR(svn_fs_node_id(&t1_F_id, txn1_root, "/A/B/F", pool));
337 SVN_ERR(svn_fs_node_id(&t2_F_id, txn2_root, "/A/B/F", pool));
338 SVN_ERR(svn_fs_node_id(&t1_C_id, txn1_root, "/A/C", pool));
339 SVN_ERR(svn_fs_node_id(&t2_C_id, txn2_root, "/A/C", pool));
340 SVN_ERR(svn_fs_node_id(&t1_D_id, txn1_root, "/A/D", pool));
341 SVN_ERR(svn_fs_node_id(&t2_D_id, txn2_root, "/A/D", pool));
342 SVN_ERR(svn_fs_node_id(&t1_gamma_id, txn1_root, "/A/D/gamma", pool));
343 SVN_ERR(svn_fs_node_id(&t2_gamma_id, txn2_root, "/A/D/gamma", pool));
344 SVN_ERR(svn_fs_node_id(&t1_H_id, txn1_root, "/A/D/H", pool));
345 SVN_ERR(svn_fs_node_id(&t2_H_id, txn2_root, "/A/D/H", pool));
346 SVN_ERR(svn_fs_node_id(&t1_chi_id, txn1_root, "/A/D/H/chi", pool));
347 SVN_ERR(svn_fs_node_id(&t2_chi_id, txn2_root, "/A/D/H/chi", pool));
348 SVN_ERR(svn_fs_node_id(&t1_psi_id, txn1_root, "/A/D/H/psi", pool));
349 SVN_ERR(svn_fs_node_id(&t2_psi_id, txn2_root, "/A/D/H/psi", pool));
350 SVN_ERR(svn_fs_node_id(&t1_omega_id, txn1_root, "/A/D/H/omega", pool));
351 SVN_ERR(svn_fs_node_id(&t2_omega_id, txn2_root, "/A/D/H/omega", pool));
352 SVN_ERR(svn_fs_node_id(&t1_G_id, txn1_root, "/A/D/G", pool));
353 SVN_ERR(svn_fs_node_id(&t2_G_id, txn2_root, "/A/D/G", pool));
354 SVN_ERR(svn_fs_node_id(&t1_pi_id, txn1_root, "/A/D/G/pi", pool));
355 SVN_ERR(svn_fs_node_id(&t2_pi_id, txn2_root, "/A/D/G/pi", pool));
356 SVN_ERR(svn_fs_node_id(&t1_rho_id, txn1_root, "/A/D/G/rho", pool));
357 SVN_ERR(svn_fs_node_id(&t2_rho_id, txn2_root, "/A/D/G/rho", pool));
358 SVN_ERR(svn_fs_node_id(&t1_tau_id, txn1_root, "/A/D/G/tau", pool));
359 SVN_ERR(svn_fs_node_id(&t2_tau_id, txn2_root, "/A/D/G/tau", pool));
360
361 /* Abort just txn2. */
362 SVN_ERR(svn_fs_abort_txn(txn2, pool));
363
364 /* Now test that all the nodes in txn2 at the time of the abort
365 * are gone, but all of the ones in txn1 are still there.
366 */
367
368 /* Check that every node rev in t2 has vanished from the fs. */
369 SVN_ERR(check_id_absent(fs, t2_root_id, pool));
370 SVN_ERR(check_id_absent(fs, t2_iota_id, pool));
371 SVN_ERR(check_id_absent(fs, t2_A_id, pool));
372 SVN_ERR(check_id_absent(fs, t2_mu_id, pool));
373 SVN_ERR(check_id_absent(fs, t2_B_id, pool));
374 SVN_ERR(check_id_absent(fs, t2_lambda_id, pool));
375 SVN_ERR(check_id_absent(fs, t2_E_id, pool));
376 SVN_ERR(check_id_absent(fs, t2_alpha_id, pool));
377 SVN_ERR(check_id_absent(fs, t2_beta_id, pool));
378 SVN_ERR(check_id_absent(fs, t2_F_id, pool));
379 SVN_ERR(check_id_absent(fs, t2_C_id, pool));
380 SVN_ERR(check_id_absent(fs, t2_D_id, pool));
381 SVN_ERR(check_id_absent(fs, t2_gamma_id, pool));
382 SVN_ERR(check_id_absent(fs, t2_H_id, pool));
383 SVN_ERR(check_id_absent(fs, t2_chi_id, pool));
384 SVN_ERR(check_id_absent(fs, t2_psi_id, pool));
385 SVN_ERR(check_id_absent(fs, t2_omega_id, pool));
386 SVN_ERR(check_id_absent(fs, t2_G_id, pool));
387 SVN_ERR(check_id_absent(fs, t2_pi_id, pool));
388 SVN_ERR(check_id_absent(fs, t2_rho_id, pool));
389 SVN_ERR(check_id_absent(fs, t2_tau_id, pool));
390
391 /* Check that every node rev in t1 is still in the fs. */
392 SVN_ERR(check_id_present(fs, t1_root_id, pool));
393 SVN_ERR(check_id_present(fs, t1_iota_id, pool));
394 SVN_ERR(check_id_present(fs, t1_A_id, pool));
395 SVN_ERR(check_id_present(fs, t1_mu_id, pool));
396 SVN_ERR(check_id_present(fs, t1_B_id, pool));
397 SVN_ERR(check_id_present(fs, t1_lambda_id, pool));
398 SVN_ERR(check_id_present(fs, t1_E_id, pool));
399 SVN_ERR(check_id_present(fs, t1_alpha_id, pool));
400 SVN_ERR(check_id_present(fs, t1_beta_id, pool));
401 SVN_ERR(check_id_present(fs, t1_F_id, pool));
402 SVN_ERR(check_id_present(fs, t1_C_id, pool));
403 SVN_ERR(check_id_present(fs, t1_D_id, pool));
404 SVN_ERR(check_id_present(fs, t1_gamma_id, pool));
405 SVN_ERR(check_id_present(fs, t1_H_id, pool));
406 SVN_ERR(check_id_present(fs, t1_chi_id, pool));
407 SVN_ERR(check_id_present(fs, t1_psi_id, pool));
408 SVN_ERR(check_id_present(fs, t1_omega_id, pool));
409 SVN_ERR(check_id_present(fs, t1_G_id, pool));
410 SVN_ERR(check_id_present(fs, t1_pi_id, pool));
411 SVN_ERR(check_id_present(fs, t1_rho_id, pool));
412 SVN_ERR(check_id_present(fs, t1_tau_id, pool));
413 }
414
415 /* Test that txn2 itself is gone, by trying to open it. */
416 {
417 svn_fs_txn_t *txn2_again;
418 svn_error_t *err;
419
420 err = svn_fs_open_txn(&txn2_again, fs, txn2_name, pool);
421 if (err && (err->apr_err != SVN_ERR_FS_NO_SUCH_TRANSACTION))
422 {
423 return svn_error_create
424 (SVN_ERR_FS_GENERAL, err,
425 "opening non-existent txn got wrong error");
426 }
427 else if (! err)
428 {
429 return svn_error_create
430 (SVN_ERR_FS_GENERAL, NULL,
431 "opening non-existent txn failed to get error");
432 }
433 svn_error_clear(err);
434 }
435
436 /* Test that txn names are not recycled, by opening a new txn. */
437 {
438 svn_fs_txn_t *txn3;
439 const char *txn3_name;
440
441 SVN_ERR(svn_fs_begin_txn(&txn3, fs, 0, pool));
442 SVN_ERR(svn_fs_txn_name(&txn3_name, txn3, pool));
443
444 if ((strcmp(txn3_name, txn2_name) == 0)
445 || (strcmp(txn3_name, txn1_name) == 0))
446 {
447 return svn_error_createf
448 (SVN_ERR_FS_GENERAL, NULL,
449 "txn name \"%s\" was recycled", txn3_name);
450 }
451 }
452
453 /* Test that aborting a txn that's already committed fails. */
454 {
455 svn_fs_txn_t *txn4;
456 const char *txn4_name;
457 svn_revnum_t new_rev;
458 const char *conflict;
459 svn_error_t *err;
460
461 SVN_ERR(svn_fs_begin_txn(&txn4, fs, 0, pool));
462 SVN_ERR(svn_fs_txn_name(&txn4_name, txn4, pool));
463 SVN_ERR(svn_fs_commit_txn(&conflict, &new_rev, txn4, pool));
464 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(new_rev));
465 err = svn_fs_abort_txn(txn4, pool);
466 if (! err)
467 return svn_error_create
468 (SVN_ERR_FS_GENERAL, NULL,
469 "expected error trying to abort a committed txn; got none");
470 else if (err->apr_err != SVN_ERR_FS_TRANSACTION_NOT_MUTABLE)
471 return svn_error_create
472 (SVN_ERR_FS_GENERAL, err,
473 "got an unexpected error trying to abort a committed txn");
474 else
475 svn_error_clear(err);
476 }
477 return SVN_NO_ERROR;
478 }
479
480
481 /* This tests deleting of mutable nodes. We build a tree in a
482 * transaction, then try to delete various items in the tree. We
483 * never commit the tree, so every entry being deleted points to a
484 * mutable node.
485 *
486 * ### todo: this test was written before commits worked. It might
487 * now be worthwhile to combine it with delete().
488 */
489 static svn_error_t *
delete_mutables(const svn_test_opts_t * opts,apr_pool_t * pool)490 delete_mutables(const svn_test_opts_t *opts,
491 apr_pool_t *pool)
492 {
493 svn_fs_t *fs;
494 svn_fs_txn_t *txn;
495 svn_fs_root_t *txn_root;
496 svn_error_t *err;
497
498 /* Prepare a txn to receive the greek tree. */
499 SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-del-from-dir", opts,
500 pool));
501 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
502 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
503
504 /* Create the greek tree. */
505 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
506
507 /* Baby, it's time to test like you've never tested before. We do
508 * the following, in this order:
509 *
510 * 1. Delete a single file somewhere, succeed.
511 * 2. Delete two files of three, then make sure the third remains.
512 * 3. Delete the third and last file.
513 * 4. Try again to delete the dir, succeed.
514 * 5. Delete one of the natively empty dirs, succeed.
515 * 6. Try to delete root, fail.
516 * 7. Try to delete a top-level file, succeed.
517 *
518 * Specifically, that's:
519 *
520 * 1. Delete A/D/gamma.
521 * 2. Delete A/D/G/pi, A/D/G/rho.
522 * 3. Delete A/D/G/tau.
523 * 4. Try again to delete A/D/G, succeed.
524 * 5. Delete A/C.
525 * 6. Try to delete /, fail.
526 * 7. Try to delete iota, succeed.
527 *
528 * Before and after each deletion or attempted deletion, we probe
529 * the affected directory, to make sure everything is as it should
530 * be.
531 */
532
533 /* 1 */
534 {
535 const svn_fs_id_t *gamma_id;
536 SVN_ERR(svn_fs_node_id(&gamma_id, txn_root, "A/D/gamma", pool));
537
538 SVN_ERR(check_entry_present(txn_root, "A/D", "gamma", pool));
539 SVN_ERR(check_id_present(fs, gamma_id, pool));
540
541 SVN_ERR(svn_fs_delete(txn_root, "A/D/gamma", pool));
542
543 SVN_ERR(check_entry_absent(txn_root, "A/D", "gamma", pool));
544 SVN_ERR(check_id_absent(fs, gamma_id, pool));
545 }
546
547 /* 2 */
548 {
549 const svn_fs_id_t *pi_id, *rho_id, *tau_id;
550 SVN_ERR(svn_fs_node_id(&pi_id, txn_root, "A/D/G/pi", pool));
551 SVN_ERR(svn_fs_node_id(&rho_id, txn_root, "A/D/G/rho", pool));
552 SVN_ERR(svn_fs_node_id(&tau_id, txn_root, "A/D/G/tau", pool));
553
554 SVN_ERR(check_entry_present(txn_root, "A/D/G", "pi", pool));
555 SVN_ERR(check_entry_present(txn_root, "A/D/G", "rho", pool));
556 SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool));
557 SVN_ERR(check_id_present(fs, pi_id, pool));
558 SVN_ERR(check_id_present(fs, rho_id, pool));
559 SVN_ERR(check_id_present(fs, tau_id, pool));
560
561 SVN_ERR(svn_fs_delete(txn_root, "A/D/G/pi", pool));
562
563 SVN_ERR(check_entry_absent(txn_root, "A/D/G", "pi", pool));
564 SVN_ERR(check_entry_present(txn_root, "A/D/G", "rho", pool));
565 SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool));
566 SVN_ERR(check_id_absent(fs, pi_id, pool));
567 SVN_ERR(check_id_present(fs, rho_id, pool));
568 SVN_ERR(check_id_present(fs, tau_id, pool));
569
570 SVN_ERR(svn_fs_delete(txn_root, "A/D/G/rho", pool));
571
572 SVN_ERR(check_entry_absent(txn_root, "A/D/G", "pi", pool));
573 SVN_ERR(check_entry_absent(txn_root, "A/D/G", "rho", pool));
574 SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool));
575 SVN_ERR(check_id_absent(fs, pi_id, pool));
576 SVN_ERR(check_id_absent(fs, rho_id, pool));
577 SVN_ERR(check_id_present(fs, tau_id, pool));
578 }
579
580 /* 3 */
581 {
582 const svn_fs_id_t *tau_id;
583 SVN_ERR(svn_fs_node_id(&tau_id, txn_root, "A/D/G/tau", pool));
584
585 SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool));
586 SVN_ERR(check_id_present(fs, tau_id, pool));
587
588 SVN_ERR(svn_fs_delete(txn_root, "A/D/G/tau", pool));
589
590 SVN_ERR(check_entry_absent(txn_root, "A/D/G", "tau", pool));
591 SVN_ERR(check_id_absent(fs, tau_id, pool));
592 }
593
594 /* 4 */
595 {
596 const svn_fs_id_t *G_id;
597 SVN_ERR(svn_fs_node_id(&G_id, txn_root, "A/D/G", pool));
598
599 SVN_ERR(check_entry_present(txn_root, "A/D", "G", pool));
600 SVN_ERR(check_id_present(fs, G_id, pool));
601
602 SVN_ERR(svn_fs_delete(txn_root, "A/D/G", pool)); /* succeed */
603
604 SVN_ERR(check_entry_absent(txn_root, "A/D", "G", pool));
605 SVN_ERR(check_id_absent(fs, G_id, pool));
606 }
607
608 /* 5 */
609 {
610 const svn_fs_id_t *C_id;
611 SVN_ERR(svn_fs_node_id(&C_id, txn_root, "A/C", pool));
612
613 SVN_ERR(check_entry_present(txn_root, "A", "C", pool));
614 SVN_ERR(check_id_present(fs, C_id, pool));
615
616 SVN_ERR(svn_fs_delete(txn_root, "A/C", pool));
617
618 SVN_ERR(check_entry_absent(txn_root, "A", "C", pool));
619 SVN_ERR(check_id_absent(fs, C_id, pool));
620 }
621
622 /* 6 */
623 {
624 const svn_fs_id_t *root_id;
625 SVN_ERR(svn_fs_node_id(&root_id, txn_root, "", pool));
626
627 err = svn_fs_delete(txn_root, "", pool);
628
629 if (err && (err->apr_err != SVN_ERR_FS_ROOT_DIR))
630 {
631 return svn_error_createf
632 (SVN_ERR_FS_GENERAL, err,
633 "deleting root directory got wrong error");
634 }
635 else if (! err)
636 {
637 return svn_error_createf
638 (SVN_ERR_FS_GENERAL, NULL,
639 "deleting root directory failed to get error");
640 }
641 svn_error_clear(err);
642
643 SVN_ERR(check_id_present(fs, root_id, pool));
644 }
645
646 /* 7 */
647 {
648 const svn_fs_id_t *iota_id;
649 SVN_ERR(svn_fs_node_id(&iota_id, txn_root, "iota", pool));
650
651 SVN_ERR(check_entry_present(txn_root, "", "iota", pool));
652 SVN_ERR(check_id_present(fs, iota_id, pool));
653
654 SVN_ERR(svn_fs_delete(txn_root, "iota", pool));
655
656 SVN_ERR(check_entry_absent(txn_root, "", "iota", pool));
657 SVN_ERR(check_id_absent(fs, iota_id, pool));
658 }
659
660 return SVN_NO_ERROR;
661 }
662
663
664 /* This tests deleting in general.
665 *
666 * ### todo: this test was written after (and independently of)
667 * delete_mutables(). It might be worthwhile to combine them.
668 */
669 static svn_error_t *
delete(const svn_test_opts_t * opts,apr_pool_t * pool)670 delete(const svn_test_opts_t *opts,
671 apr_pool_t *pool)
672 {
673 svn_fs_t *fs;
674 svn_fs_txn_t *txn;
675 svn_fs_root_t *txn_root;
676 svn_revnum_t new_rev;
677
678 /* This function tests 5 cases:
679 *
680 * 1. Delete mutable file.
681 * 2. Delete mutable directory.
682 * 3. Delete mutable directory with immutable nodes.
683 * 4. Delete immutable file.
684 * 5. Delete immutable directory.
685 */
686
687 /* Prepare a txn to receive the greek tree. */
688 SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-del-tree", opts,
689 pool));
690 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
691 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
692
693 /* Create the greek tree. */
694 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
695
696 /* 1. Delete mutable file. */
697 {
698 const svn_fs_id_t *iota_id, *gamma_id;
699 static svn_test__tree_entry_t expected_entries[] = {
700 /* path, contents (0 = dir) */
701 { "A", 0 },
702 { "A/mu", "This is the file 'mu'.\n" },
703 { "A/B", 0 },
704 { "A/B/lambda", "This is the file 'lambda'.\n" },
705 { "A/B/E", 0 },
706 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
707 { "A/B/E/beta", "This is the file 'beta'.\n" },
708 { "A/C", 0 },
709 { "A/B/F", 0 },
710 { "A/D", 0 },
711 { "A/D/G", 0 },
712 { "A/D/G/pi", "This is the file 'pi'.\n" },
713 { "A/D/G/rho", "This is the file 'rho'.\n" },
714 { "A/D/G/tau", "This is the file 'tau'.\n" },
715 { "A/D/H", 0 },
716 { "A/D/H/chi", "This is the file 'chi'.\n" },
717 { "A/D/H/psi", "This is the file 'psi'.\n" },
718 { "A/D/H/omega", "This is the file 'omega'.\n" }
719 };
720
721 /* Check nodes revision ID is gone. */
722 SVN_ERR(svn_fs_node_id(&iota_id, txn_root, "iota", pool));
723 SVN_ERR(svn_fs_node_id(&gamma_id, txn_root, "A/D/gamma", pool));
724
725 SVN_ERR(check_entry_present(txn_root, "", "iota", pool));
726 SVN_ERR(check_id_present(fs, iota_id, pool));
727 SVN_ERR(check_id_present(fs, gamma_id, pool));
728
729 /* Try deleting mutable files. */
730 SVN_ERR(svn_fs_delete(txn_root, "iota", pool));
731 SVN_ERR(svn_fs_delete(txn_root, "A/D/gamma", pool));
732 SVN_ERR(check_entry_absent(txn_root, "", "iota", pool));
733 SVN_ERR(check_entry_absent(txn_root, "A/D", "gamma", pool));
734 SVN_ERR(check_id_absent(fs, iota_id, pool));
735 SVN_ERR(check_id_absent(fs, gamma_id, pool));
736
737 /* Validate the tree. */
738 SVN_ERR(svn_test__validate_tree(txn_root, expected_entries, 18, pool));
739 }
740 /* Abort transaction. */
741 SVN_ERR(svn_fs_abort_txn(txn, pool));
742
743 /* 2. Delete mutable directory. */
744
745 /* Prepare a txn to receive the greek tree. */
746 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
747 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
748
749 /* Create the greek tree. */
750 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
751
752 {
753 const svn_fs_id_t *A_id, *mu_id, *B_id, *lambda_id, *E_id, *alpha_id,
754 *beta_id, *F_id, *C_id, *D_id, *gamma_id, *H_id, *chi_id,
755 *psi_id, *omega_id, *G_id, *pi_id, *rho_id, *tau_id;
756
757 /* Check nodes revision ID is gone. */
758 SVN_ERR(svn_fs_node_id(&A_id, txn_root, "/A", pool));
759 SVN_ERR(check_entry_present(txn_root, "", "A", pool));
760 SVN_ERR(svn_fs_node_id(&mu_id, txn_root, "/A/mu", pool));
761 SVN_ERR(check_entry_present(txn_root, "A", "mu", pool));
762 SVN_ERR(svn_fs_node_id(&B_id, txn_root, "/A/B", pool));
763 SVN_ERR(check_entry_present(txn_root, "A", "B", pool));
764 SVN_ERR(svn_fs_node_id(&lambda_id, txn_root, "/A/B/lambda", pool));
765 SVN_ERR(check_entry_present(txn_root, "A/B", "lambda", pool));
766 SVN_ERR(svn_fs_node_id(&E_id, txn_root, "/A/B/E", pool));
767 SVN_ERR(check_entry_present(txn_root, "A/B", "E", pool));
768 SVN_ERR(svn_fs_node_id(&alpha_id, txn_root, "/A/B/E/alpha", pool));
769 SVN_ERR(check_entry_present(txn_root, "A/B/E", "alpha", pool));
770 SVN_ERR(svn_fs_node_id(&beta_id, txn_root, "/A/B/E/beta", pool));
771 SVN_ERR(check_entry_present(txn_root, "A/B/E", "beta", pool));
772 SVN_ERR(svn_fs_node_id(&F_id, txn_root, "/A/B/F", pool));
773 SVN_ERR(check_entry_present(txn_root, "A/B", "F", pool));
774 SVN_ERR(svn_fs_node_id(&C_id, txn_root, "/A/C", pool));
775 SVN_ERR(check_entry_present(txn_root, "A", "C", pool));
776 SVN_ERR(svn_fs_node_id(&D_id, txn_root, "/A/D", pool));
777 SVN_ERR(check_entry_present(txn_root, "A", "D", pool));
778 SVN_ERR(svn_fs_node_id(&gamma_id, txn_root, "/A/D/gamma", pool));
779 SVN_ERR(check_entry_present(txn_root, "A/D", "gamma", pool));
780 SVN_ERR(svn_fs_node_id(&H_id, txn_root, "/A/D/H", pool));
781 SVN_ERR(check_entry_present(txn_root, "A/D", "H", pool));
782 SVN_ERR(svn_fs_node_id(&chi_id, txn_root, "/A/D/H/chi", pool));
783 SVN_ERR(check_entry_present(txn_root, "A/D/H", "chi", pool));
784 SVN_ERR(svn_fs_node_id(&psi_id, txn_root, "/A/D/H/psi", pool));
785 SVN_ERR(check_entry_present(txn_root, "A/D/H", "psi", pool));
786 SVN_ERR(svn_fs_node_id(&omega_id, txn_root, "/A/D/H/omega", pool));
787 SVN_ERR(check_entry_present(txn_root, "A/D/H", "omega", pool));
788 SVN_ERR(svn_fs_node_id(&G_id, txn_root, "/A/D/G", pool));
789 SVN_ERR(check_entry_present(txn_root, "A/D", "G", pool));
790 SVN_ERR(svn_fs_node_id(&pi_id, txn_root, "/A/D/G/pi", pool));
791 SVN_ERR(check_entry_present(txn_root, "A/D/G", "pi", pool));
792 SVN_ERR(svn_fs_node_id(&rho_id, txn_root, "/A/D/G/rho", pool));
793 SVN_ERR(check_entry_present(txn_root, "A/D/G", "rho", pool));
794 SVN_ERR(svn_fs_node_id(&tau_id, txn_root, "/A/D/G/tau", pool));
795 SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool));
796
797 /* Try deleting a mutable empty dir. */
798 SVN_ERR(svn_fs_delete(txn_root, "A/C", pool));
799 SVN_ERR(svn_fs_delete(txn_root, "A/B/F", pool));
800 SVN_ERR(check_entry_absent(txn_root, "A", "C", pool));
801 SVN_ERR(check_entry_absent(txn_root, "A/B", "F", pool));
802 SVN_ERR(check_id_absent(fs, C_id, pool));
803 SVN_ERR(check_id_absent(fs, F_id, pool));
804
805 /* Now delete a mutable non-empty dir. */
806 SVN_ERR(svn_fs_delete(txn_root, "A", pool));
807 SVN_ERR(check_entry_absent(txn_root, "", "A", pool));
808 SVN_ERR(check_id_absent(fs, A_id, pool));
809 SVN_ERR(check_id_absent(fs, mu_id, pool));
810 SVN_ERR(check_id_absent(fs, B_id, pool));
811 SVN_ERR(check_id_absent(fs, lambda_id, pool));
812 SVN_ERR(check_id_absent(fs, E_id, pool));
813 SVN_ERR(check_id_absent(fs, alpha_id, pool));
814 SVN_ERR(check_id_absent(fs, beta_id, pool));
815 SVN_ERR(check_id_absent(fs, D_id, pool));
816 SVN_ERR(check_id_absent(fs, gamma_id, pool));
817 SVN_ERR(check_id_absent(fs, H_id, pool));
818 SVN_ERR(check_id_absent(fs, chi_id, pool));
819 SVN_ERR(check_id_absent(fs, psi_id, pool));
820 SVN_ERR(check_id_absent(fs, omega_id, pool));
821 SVN_ERR(check_id_absent(fs, G_id, pool));
822 SVN_ERR(check_id_absent(fs, pi_id, pool));
823 SVN_ERR(check_id_absent(fs, rho_id, pool));
824 SVN_ERR(check_id_absent(fs, tau_id, pool));
825
826 /* Validate the tree. */
827 {
828 static svn_test__tree_entry_t expected_entries[] = {
829 /* path, contents (0 = dir) */
830 { "iota", "This is the file 'iota'.\n" } };
831 SVN_ERR(svn_test__validate_tree(txn_root, expected_entries, 1, pool));
832 }
833 }
834
835 /* Abort transaction. */
836 SVN_ERR(svn_fs_abort_txn(txn, pool));
837
838 /* 3. Delete mutable directory with immutable nodes. */
839
840 /* Prepare a txn to receive the greek tree. */
841 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
842 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
843
844 /* Create the greek tree. */
845 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
846
847 /* Commit the greek tree. */
848 SVN_ERR(svn_fs_commit_txn(NULL, &new_rev, txn, pool));
849 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(new_rev));
850
851 /* Create new transaction. */
852 SVN_ERR(svn_fs_begin_txn(&txn, fs, new_rev, pool));
853 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
854
855 {
856 const svn_fs_id_t *A_id, *mu_id, *B_id, *lambda_id, *E_id, *alpha_id,
857 *beta_id, *F_id, *C_id, *D_id, *gamma_id, *H_id, *chi_id,
858 *psi_id, *omega_id, *G_id, *pi_id, *rho_id, *tau_id, *sigma_id;
859
860 /* Create A/D/G/sigma. This makes all components of A/D/G
861 mutable. */
862 SVN_ERR(svn_fs_make_file(txn_root, "A/D/G/sigma", pool));
863 SVN_ERR(svn_test__set_file_contents(txn_root, "A/D/G/sigma",
864 "This is another file 'sigma'.\n", pool));
865
866 /* Check that mutable node-revision-IDs are removed and immutable
867 ones still exist. */
868 SVN_ERR(svn_fs_node_id(&A_id, txn_root, "/A", pool));
869 SVN_ERR(check_entry_present(txn_root, "", "A", pool));
870 SVN_ERR(svn_fs_node_id(&mu_id, txn_root, "/A/mu", pool));
871 SVN_ERR(check_entry_present(txn_root, "A", "mu", pool));
872 SVN_ERR(svn_fs_node_id(&B_id, txn_root, "/A/B", pool));
873 SVN_ERR(check_entry_present(txn_root, "A", "B", pool));
874 SVN_ERR(svn_fs_node_id(&lambda_id, txn_root, "/A/B/lambda", pool));
875 SVN_ERR(check_entry_present(txn_root, "A/B", "lambda", pool));
876 SVN_ERR(svn_fs_node_id(&E_id, txn_root, "/A/B/E", pool));
877 SVN_ERR(check_entry_present(txn_root, "A/B", "E", pool));
878 SVN_ERR(svn_fs_node_id(&alpha_id, txn_root, "/A/B/E/alpha", pool));
879 SVN_ERR(check_entry_present(txn_root, "A/B/E", "alpha", pool));
880 SVN_ERR(svn_fs_node_id(&beta_id, txn_root, "/A/B/E/beta", pool));
881 SVN_ERR(check_entry_present(txn_root, "A/B/E", "beta", pool));
882 SVN_ERR(svn_fs_node_id(&F_id, txn_root, "/A/B/F", pool));
883 SVN_ERR(check_entry_present(txn_root, "A/B", "F", pool));
884 SVN_ERR(svn_fs_node_id(&C_id, txn_root, "/A/C", pool));
885 SVN_ERR(check_entry_present(txn_root, "A", "C", pool));
886 SVN_ERR(svn_fs_node_id(&D_id, txn_root, "/A/D", pool));
887 SVN_ERR(check_entry_present(txn_root, "A", "D", pool));
888 SVN_ERR(svn_fs_node_id(&gamma_id, txn_root, "/A/D/gamma", pool));
889 SVN_ERR(check_entry_present(txn_root, "A/D", "gamma", pool));
890 SVN_ERR(svn_fs_node_id(&H_id, txn_root, "/A/D/H", pool));
891 SVN_ERR(check_entry_present(txn_root, "A/D", "H", pool));
892 SVN_ERR(svn_fs_node_id(&chi_id, txn_root, "/A/D/H/chi", pool));
893 SVN_ERR(check_entry_present(txn_root, "A/D/H", "chi", pool));
894 SVN_ERR(svn_fs_node_id(&psi_id, txn_root, "/A/D/H/psi", pool));
895 SVN_ERR(check_entry_present(txn_root, "A/D/H", "psi", pool));
896 SVN_ERR(svn_fs_node_id(&omega_id, txn_root, "/A/D/H/omega", pool));
897 SVN_ERR(check_entry_present(txn_root, "A/D/H", "omega", pool));
898 SVN_ERR(svn_fs_node_id(&G_id, txn_root, "/A/D/G", pool));
899 SVN_ERR(check_entry_present(txn_root, "A/D", "G", pool));
900 SVN_ERR(svn_fs_node_id(&pi_id, txn_root, "/A/D/G/pi", pool));
901 SVN_ERR(check_entry_present(txn_root, "A/D/G", "pi", pool));
902 SVN_ERR(svn_fs_node_id(&rho_id, txn_root, "/A/D/G/rho", pool));
903 SVN_ERR(check_entry_present(txn_root, "A/D/G", "rho", pool));
904 SVN_ERR(svn_fs_node_id(&tau_id, txn_root, "/A/D/G/tau", pool));
905 SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool));
906 SVN_ERR(svn_fs_node_id(&sigma_id, txn_root, "/A/D/G/sigma", pool));
907 SVN_ERR(check_entry_present(txn_root, "A/D/G", "sigma", pool));
908
909 /* Delete "A" */
910 SVN_ERR(svn_fs_delete(txn_root, "A", pool));
911 SVN_ERR(check_entry_absent(txn_root, "", "A", pool));
912 SVN_ERR(check_id_absent(fs, A_id, pool));
913 SVN_ERR(check_id_present(fs, mu_id, pool));
914 SVN_ERR(check_id_present(fs, B_id, pool));
915 SVN_ERR(check_id_present(fs, lambda_id, pool));
916 SVN_ERR(check_id_present(fs, E_id, pool));
917 SVN_ERR(check_id_present(fs, alpha_id, pool));
918 SVN_ERR(check_id_present(fs, beta_id, pool));
919 SVN_ERR(check_id_present(fs, F_id, pool));
920 SVN_ERR(check_id_present(fs, C_id, pool));
921 SVN_ERR(check_id_absent(fs, D_id, pool));
922 SVN_ERR(check_id_present(fs, gamma_id, pool));
923 SVN_ERR(check_id_present(fs, H_id, pool));
924 SVN_ERR(check_id_present(fs, chi_id, pool));
925 SVN_ERR(check_id_present(fs, psi_id, pool));
926 SVN_ERR(check_id_present(fs, omega_id, pool));
927 SVN_ERR(check_id_absent(fs, G_id, pool));
928 SVN_ERR(check_id_present(fs, pi_id, pool));
929 SVN_ERR(check_id_present(fs, rho_id, pool));
930 SVN_ERR(check_id_present(fs, tau_id, pool));
931 SVN_ERR(check_id_absent(fs, sigma_id, pool));
932
933 /* Validate the tree. */
934 {
935 static svn_test__tree_entry_t expected_entries[] = {
936 /* path, contents (0 = dir) */
937 { "iota", "This is the file 'iota'.\n" }
938 };
939
940 SVN_ERR(svn_test__validate_tree(txn_root, expected_entries, 1, pool));
941 }
942 }
943
944 /* Abort transaction. */
945 SVN_ERR(svn_fs_abort_txn(txn, pool));
946
947 /* 4. Delete immutable file. */
948
949 /* Create new transaction. */
950 SVN_ERR(svn_fs_begin_txn(&txn, fs, new_rev, pool));
951 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
952
953 {
954 const svn_fs_id_t *iota_id, *gamma_id;
955
956 /* Check nodes revision ID is present. */
957 SVN_ERR(svn_fs_node_id(&iota_id, txn_root, "iota", pool));
958 SVN_ERR(svn_fs_node_id(&gamma_id, txn_root, "A/D/gamma", pool));
959 SVN_ERR(check_entry_present(txn_root, "", "iota", pool));
960 SVN_ERR(check_entry_present(txn_root, "A/D", "gamma", pool));
961 SVN_ERR(check_id_present(fs, iota_id, pool));
962 SVN_ERR(check_id_present(fs, gamma_id, pool));
963
964 /* Delete some files. */
965 SVN_ERR(svn_fs_delete(txn_root, "iota", pool));
966 SVN_ERR(svn_fs_delete(txn_root, "A/D/gamma", pool));
967 SVN_ERR(check_entry_absent(txn_root, "", "iota", pool));
968 SVN_ERR(check_entry_absent(txn_root, "A/D", "iota", pool));
969 SVN_ERR(check_id_present(fs, iota_id, pool));
970 SVN_ERR(check_id_present(fs, gamma_id, pool));
971
972 /* Validate the tree. */
973 {
974 static svn_test__tree_entry_t expected_entries[] = {
975 /* path, contents (0 = dir) */
976 { "A", 0 },
977 { "A/mu", "This is the file 'mu'.\n" },
978 { "A/B", 0 },
979 { "A/B/lambda", "This is the file 'lambda'.\n" },
980 { "A/B/E", 0 },
981 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
982 { "A/B/E/beta", "This is the file 'beta'.\n" },
983 { "A/B/F", 0 },
984 { "A/C", 0 },
985 { "A/D", 0 },
986 { "A/D/G", 0 },
987 { "A/D/G/pi", "This is the file 'pi'.\n" },
988 { "A/D/G/rho", "This is the file 'rho'.\n" },
989 { "A/D/G/tau", "This is the file 'tau'.\n" },
990 { "A/D/H", 0 },
991 { "A/D/H/chi", "This is the file 'chi'.\n" },
992 { "A/D/H/psi", "This is the file 'psi'.\n" },
993 { "A/D/H/omega", "This is the file 'omega'.\n" }
994 };
995 SVN_ERR(svn_test__validate_tree(txn_root, expected_entries, 18, pool));
996 }
997 }
998
999 /* Abort transaction. */
1000 SVN_ERR(svn_fs_abort_txn(txn, pool));
1001
1002 /* 5. Delete immutable directory. */
1003
1004 /* Create new transaction. */
1005 SVN_ERR(svn_fs_begin_txn(&txn, fs, new_rev, pool));
1006 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1007
1008 {
1009 const svn_fs_id_t *A_id, *mu_id, *B_id, *lambda_id, *E_id, *alpha_id,
1010 *beta_id, *F_id, *C_id, *D_id, *gamma_id, *H_id, *chi_id,
1011 *psi_id, *omega_id, *G_id, *pi_id, *rho_id, *tau_id;
1012
1013 /* Check nodes revision ID is present. */
1014 SVN_ERR(svn_fs_node_id(&A_id, txn_root, "/A", pool));
1015 SVN_ERR(check_entry_present(txn_root, "", "A", pool));
1016 SVN_ERR(svn_fs_node_id(&mu_id, txn_root, "/A/mu", pool));
1017 SVN_ERR(check_entry_present(txn_root, "A", "mu", pool));
1018 SVN_ERR(svn_fs_node_id(&B_id, txn_root, "/A/B", pool));
1019 SVN_ERR(check_entry_present(txn_root, "A", "B", pool));
1020 SVN_ERR(svn_fs_node_id(&lambda_id, txn_root, "/A/B/lambda", pool));
1021 SVN_ERR(check_entry_present(txn_root, "A/B", "lambda", pool));
1022 SVN_ERR(svn_fs_node_id(&E_id, txn_root, "/A/B/E", pool));
1023 SVN_ERR(check_entry_present(txn_root, "A/B", "E", pool));
1024 SVN_ERR(svn_fs_node_id(&alpha_id, txn_root, "/A/B/E/alpha", pool));
1025 SVN_ERR(check_entry_present(txn_root, "A/B/E", "alpha", pool));
1026 SVN_ERR(svn_fs_node_id(&beta_id, txn_root, "/A/B/E/beta", pool));
1027 SVN_ERR(check_entry_present(txn_root, "A/B/E", "beta", pool));
1028 SVN_ERR(svn_fs_node_id(&F_id, txn_root, "/A/B/F", pool));
1029 SVN_ERR(check_entry_present(txn_root, "A/B", "F", pool));
1030 SVN_ERR(svn_fs_node_id(&C_id, txn_root, "/A/C", pool));
1031 SVN_ERR(check_entry_present(txn_root, "A", "C", pool));
1032 SVN_ERR(svn_fs_node_id(&D_id, txn_root, "/A/D", pool));
1033 SVN_ERR(check_entry_present(txn_root, "A", "D", pool));
1034 SVN_ERR(svn_fs_node_id(&gamma_id, txn_root, "/A/D/gamma", pool));
1035 SVN_ERR(check_entry_present(txn_root, "A/D", "gamma", pool));
1036 SVN_ERR(svn_fs_node_id(&H_id, txn_root, "/A/D/H", pool));
1037 SVN_ERR(check_entry_present(txn_root, "A/D", "H", pool));
1038 SVN_ERR(svn_fs_node_id(&chi_id, txn_root, "/A/D/H/chi", pool));
1039 SVN_ERR(check_entry_present(txn_root, "A/D/H", "chi", pool));
1040 SVN_ERR(svn_fs_node_id(&psi_id, txn_root, "/A/D/H/psi", pool));
1041 SVN_ERR(check_entry_present(txn_root, "A/D/H", "psi", pool));
1042 SVN_ERR(svn_fs_node_id(&omega_id, txn_root, "/A/D/H/omega", pool));
1043 SVN_ERR(check_entry_present(txn_root, "A/D/H", "omega", pool));
1044 SVN_ERR(svn_fs_node_id(&G_id, txn_root, "/A/D/G", pool));
1045 SVN_ERR(check_entry_present(txn_root, "A/D", "G", pool));
1046 SVN_ERR(svn_fs_node_id(&pi_id, txn_root, "/A/D/G/pi", pool));
1047 SVN_ERR(check_entry_present(txn_root, "A/D/G", "pi", pool));
1048 SVN_ERR(svn_fs_node_id(&rho_id, txn_root, "/A/D/G/rho", pool));
1049 SVN_ERR(check_entry_present(txn_root, "A/D/G", "rho", pool));
1050 SVN_ERR(svn_fs_node_id(&tau_id, txn_root, "/A/D/G/tau", pool));
1051 SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool));
1052
1053 /* Delete "A" */
1054 SVN_ERR(svn_fs_delete(txn_root, "A", pool));
1055 SVN_ERR(check_entry_absent(txn_root, "", "A", pool));
1056 SVN_ERR(check_id_present(fs, A_id, pool));
1057 SVN_ERR(check_id_present(fs, mu_id, pool));
1058 SVN_ERR(check_id_present(fs, B_id, pool));
1059 SVN_ERR(check_id_present(fs, lambda_id, pool));
1060 SVN_ERR(check_id_present(fs, E_id, pool));
1061 SVN_ERR(check_id_present(fs, alpha_id, pool));
1062 SVN_ERR(check_id_present(fs, beta_id, pool));
1063 SVN_ERR(check_id_present(fs, F_id, pool));
1064 SVN_ERR(check_id_present(fs, C_id, pool));
1065 SVN_ERR(check_id_present(fs, D_id, pool));
1066 SVN_ERR(check_id_present(fs, gamma_id, pool));
1067 SVN_ERR(check_id_present(fs, H_id, pool));
1068 SVN_ERR(check_id_present(fs, chi_id, pool));
1069 SVN_ERR(check_id_present(fs, psi_id, pool));
1070 SVN_ERR(check_id_present(fs, omega_id, pool));
1071 SVN_ERR(check_id_present(fs, G_id, pool));
1072 SVN_ERR(check_id_present(fs, pi_id, pool));
1073 SVN_ERR(check_id_present(fs, rho_id, pool));
1074 SVN_ERR(check_id_present(fs, tau_id, pool));
1075
1076 /* Validate the tree. */
1077 {
1078 static svn_test__tree_entry_t expected_entries[] = {
1079 /* path, contents (0 = dir) */
1080 { "iota", "This is the file 'iota'.\n" }
1081 };
1082 SVN_ERR(svn_test__validate_tree(txn_root, expected_entries, 1, pool));
1083 }
1084 }
1085
1086 return SVN_NO_ERROR;
1087 }
1088
1089
1090 static svn_error_t *
canonicalize_abspath(const svn_test_opts_t * opts,apr_pool_t * pool)1091 canonicalize_abspath(const svn_test_opts_t *opts,
1092 apr_pool_t *pool)
1093 {
1094 apr_size_t i;
1095 const char *paths[21][2] =
1096 /* in out */
1097 { { NULL, NULL },
1098 { "", "/" },
1099 { "/", "/" },
1100 { "//", "/" },
1101 { "///", "/" },
1102 { "foo", "/foo" },
1103 { "foo/", "/foo" },
1104 { "foo//", "/foo" },
1105 { "/foo", "/foo" },
1106 { "/foo/", "/foo" },
1107 { "/foo//", "/foo" },
1108 { "//foo//", "/foo" },
1109 { "foo/bar", "/foo/bar" },
1110 { "foo/bar/", "/foo/bar" },
1111 { "foo/bar//", "/foo/bar" },
1112 { "foo//bar", "/foo/bar" },
1113 { "foo//bar/", "/foo/bar" },
1114 { "foo//bar//", "/foo/bar" },
1115 { "/foo//bar//", "/foo/bar" },
1116 { "//foo//bar//", "/foo/bar" },
1117 { "///foo///bar///baz///", "/foo/bar/baz" },
1118 };
1119
1120 for (i = 0; i < (sizeof(paths) / 2 / sizeof(const char *)); i++)
1121 {
1122 const char *input = paths[i][0];
1123 const char *output = paths[i][1];
1124 const char *actual = svn_fs__canonicalize_abspath(input, pool);
1125
1126 if ((! output) && (! actual))
1127 continue;
1128 if ((! output) && actual)
1129 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
1130 "expected NULL path; got '%s'", actual);
1131 if (output && (! actual))
1132 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
1133 "expected '%s' path; got NULL", output);
1134 if (strcmp(output, actual))
1135 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
1136 "expected '%s' path; got '%s'",
1137 output, actual);
1138 }
1139 return SVN_NO_ERROR;
1140 }
1141
1142
1143 static svn_error_t *
create_within_copy(const svn_test_opts_t * opts,apr_pool_t * pool)1144 create_within_copy(const svn_test_opts_t *opts,
1145 apr_pool_t *pool)
1146 {
1147 apr_pool_t *spool = svn_pool_create(pool);
1148 svn_fs_t *fs;
1149 svn_fs_txn_t *txn;
1150 svn_fs_root_t *txn_root, *rev_root;
1151 svn_revnum_t youngest_rev = 0;
1152
1153 /* Create a filesystem and repository. */
1154 SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-create-within-copy", opts,
1155 pool));
1156
1157 /*** Revision 1: Create the greek tree in revision. ***/
1158 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool));
1159 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
1160 SVN_ERR(svn_test__create_greek_tree(txn_root, spool));
1161 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, spool));
1162 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
1163 svn_pool_clear(spool);
1164
1165 /*** Revision 2: Copy A/D to A/D3 ***/
1166 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool));
1167 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
1168 SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, spool));
1169 SVN_ERR(svn_fs_copy(rev_root, "A/D", txn_root, "A/D3", spool));
1170 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, spool));
1171 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
1172 svn_pool_clear(spool);
1173
1174 /*** Revision 3: Copy A/D/G to A/D/G2 ***/
1175 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool));
1176 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
1177 SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, spool));
1178 SVN_ERR(svn_fs_copy(rev_root, "A/D/G", txn_root, "A/D/G2", spool));
1179 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, spool));
1180 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
1181 svn_pool_clear(spool);
1182
1183 /*** Revision 4: Copy A/D to A/D2 and create up and I in the existing
1184 A/D/G2, in the new A/D2, and in the nested, new A/D2/G2 ***/
1185 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool));
1186 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
1187 SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, spool));
1188 SVN_ERR(svn_fs_copy(rev_root, "A/D", txn_root, "A/D2", spool));
1189 SVN_ERR(svn_fs_make_dir(txn_root, "A/D/G2/I", spool));
1190 SVN_ERR(svn_fs_make_file(txn_root, "A/D/G2/up", spool));
1191 SVN_ERR(svn_fs_make_dir(txn_root, "A/D2/I", spool));
1192 SVN_ERR(svn_fs_make_file(txn_root, "A/D2/up", spool));
1193 SVN_ERR(svn_fs_make_dir(txn_root, "A/D2/G2/I", spool));
1194 SVN_ERR(svn_fs_make_file(txn_root, "A/D2/G2/up", spool));
1195 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, spool));
1196 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
1197 svn_pool_clear(spool);
1198
1199 /*** Revision 5: Create A/D3/down and A/D3/J ***/
1200 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool));
1201 SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
1202 SVN_ERR(svn_fs_make_file(txn_root, "A/D3/down", spool));
1203 SVN_ERR(svn_fs_make_dir(txn_root, "A/D3/J", spool));
1204 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, spool));
1205 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
1206 svn_pool_clear(spool);
1207
1208 {
1209 /* New items should have same CopyID as their parent */
1210 const char *pathgroup[4][3] =
1211 {
1212 { "A/D/G2",
1213 "A/D/G2/I",
1214 "A/D/G2/up" },
1215 { "A/D2",
1216 "A/D2/I",
1217 "A/D2/up" },
1218 { "A/D2/G2",
1219 "A/D2/G2/I",
1220 "A/D2/G2/up" },
1221 { "A/D3",
1222 "A/D3/down",
1223 "A/D3/J" }
1224 };
1225 int i;
1226
1227 SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, spool));
1228
1229 for (i = 0; i < 4; i++)
1230 {
1231 const svn_fs_id_t *lead_id;
1232 const char *lead_copy_id;
1233 int j;
1234
1235 /* Get the FSIdentifier for the first path in each group... */
1236 SVN_ERR(svn_fs_node_id(&lead_id, rev_root, pathgroup[i][0], spool));
1237 lead_copy_id = svn_fs_base__id_copy_id(lead_id);
1238
1239 for (j = 1; j < 3; j++)
1240 {
1241 const svn_fs_id_t *id;
1242 const char *copy_id;
1243
1244 /* ... and make sure the other members of the group have
1245 the same copy_id component as the 'lead' member. */
1246
1247 SVN_ERR(svn_fs_node_id(&id, rev_root, pathgroup[i][j], spool));
1248 copy_id = svn_fs_base__id_copy_id(id);
1249
1250 if (strcmp(copy_id, lead_copy_id) != 0)
1251 return svn_error_createf
1252 (SVN_ERR_TEST_FAILED, NULL,
1253 "'%s' id: expected copy_id '%s'; got copy_id '%s'",
1254 pathgroup[i][j], lead_copy_id, copy_id);
1255 }
1256 }
1257 svn_pool_clear(spool);
1258 }
1259
1260 svn_pool_destroy(spool);
1261 return SVN_NO_ERROR;
1262 }
1263
1264
1265 /* Test the skip delta support by commiting so many changes to a file
1266 * that some of its older revisions become reachable by skip deltas,
1267 * then try retrieving those revisions.
1268 */
1269 static svn_error_t *
skip_deltas(const svn_test_opts_t * opts,apr_pool_t * pool)1270 skip_deltas(const svn_test_opts_t *opts,
1271 apr_pool_t *pool)
1272 {
1273 svn_fs_t *fs;
1274 svn_fs_txn_t *txn;
1275 svn_fs_root_t *txn_root, *rev_root;
1276 apr_pool_t *subpool = svn_pool_create(pool);
1277 svn_revnum_t youngest_rev = 0;
1278 const char *one_line = "This is a line in file 'f'.\n";
1279 svn_stringbuf_t *f = svn_stringbuf_create(one_line, pool);
1280
1281 /* Create a filesystem and repository. */
1282 SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-skip-deltas", opts,
1283 pool));
1284
1285 /* Create the file. */
1286 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
1287 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
1288 SVN_ERR(svn_fs_make_file(txn_root, "f", subpool));
1289 SVN_ERR(svn_test__set_file_contents(txn_root, "f", f->data, subpool));
1290 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
1291 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
1292 SVN_ERR(svn_fs_deltify_revision(fs, youngest_rev, subpool));
1293 svn_pool_clear(subpool);
1294
1295 /* Now, commit changes to the file 128 times. */
1296 while (youngest_rev <= 128)
1297 {
1298 /* Append another line to the ever-growing file contents. */
1299 svn_stringbuf_appendcstr(f, one_line);
1300
1301 /* Commit the new contents. */
1302 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
1303 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
1304 SVN_ERR(svn_test__set_file_contents(txn_root, "f", f->data, subpool));
1305 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
1306 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
1307 SVN_ERR(svn_fs_deltify_revision(fs, youngest_rev, subpool));
1308 svn_pool_clear(subpool);
1309 }
1310
1311 /* Now go back and check revision 1. */
1312 SVN_ERR(svn_fs_revision_root(&rev_root, fs, 1, pool));
1313 SVN_ERR(svn_test__get_file_contents(rev_root, "f", &f, pool));
1314 if (strcmp(one_line, f->data) != 0)
1315 return svn_error_createf
1316 (SVN_ERR_TEST_FAILED, NULL,
1317 "Wrong contents. Expected:\n '%s'\nGot:\n '%s'\n",
1318 one_line, f->data);
1319
1320 svn_pool_destroy(subpool);
1321 return SVN_NO_ERROR;
1322 }
1323
1324
1325 /* Trail-ish helpers for redundant_copy(). */
1326 struct get_txn_args
1327 {
1328 transaction_t **txn;
1329 const char *txn_name;
1330 svn_fs_t *fs;
1331 };
1332
1333 static svn_error_t *
txn_body_get_txn(void * baton,trail_t * trail)1334 txn_body_get_txn(void *baton, trail_t *trail)
1335 {
1336 struct get_txn_args *args = baton;
1337 return svn_fs_bdb__get_txn(args->txn, args->fs, args->txn_name,
1338 trail, trail->pool);
1339 }
1340
1341
1342 static svn_error_t *
redundant_copy(const svn_test_opts_t * opts,apr_pool_t * pool)1343 redundant_copy(const svn_test_opts_t *opts,
1344 apr_pool_t *pool)
1345 {
1346 svn_fs_t *fs;
1347 svn_fs_txn_t *txn;
1348 const char *txn_name;
1349 transaction_t *transaction;
1350 svn_fs_root_t *txn_root, *rev_root;
1351 const svn_fs_id_t *old_D_id, *new_D_id;
1352 svn_revnum_t youngest_rev = 0;
1353 struct get_txn_args args;
1354
1355 /* Create a filesystem and repository. */
1356 SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-redundant-copy", opts,
1357 pool));
1358
1359 /* Create the greek tree in revision 1. */
1360 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, pool));
1361 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1362 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
1363 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, pool));
1364 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
1365
1366 /* In a transaction, copy A to Z. */
1367 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, pool));
1368 SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool));
1369 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
1370 SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, pool));
1371 SVN_ERR(svn_fs_copy(rev_root, "A", txn_root, "Z", pool));
1372
1373 /* Now, examine the transaction. There should have been only one
1374 copy there. */
1375 args.fs = fs;
1376 args.txn_name = txn_name;
1377 args.txn = &transaction;
1378 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_txn, &args, FALSE, pool));
1379 if (transaction->copies->nelts != 1)
1380 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
1381 "Expected 1 copy; got %d",
1382 transaction->copies->nelts);
1383
1384 /* Get the node-rev-id for A/D (the reason will be clear a little later). */
1385 SVN_ERR(svn_fs_node_id(&old_D_id, txn_root, "A/D", pool));
1386
1387 /* Now copy A/D/G Z/D/G. */
1388 SVN_ERR(svn_fs_copy(rev_root, "A/D/G", txn_root, "Z/D/G", pool));
1389
1390 /* Now, examine the transaction. There should still only have been
1391 one copy operation that "took". */
1392 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_txn, &args, FALSE, pool));
1393 if (transaction->copies->nelts != 1)
1394 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
1395 "Expected only 1 copy; got %d",
1396 transaction->copies->nelts);
1397
1398 /* Finally, check the node-rev-id for "Z/D" -- it should never have
1399 been made mutable (since the second copy should not have taken
1400 place). */
1401 SVN_ERR(svn_fs_node_id(&new_D_id, txn_root, "A/D", pool));
1402 if (! svn_string_compare(svn_fs_unparse_id(old_D_id, pool),
1403 svn_fs_unparse_id(new_D_id, pool)))
1404 return svn_error_create
1405 (SVN_ERR_TEST_FAILED, NULL,
1406 "Expected equivalent node-rev-ids; got differing ones");
1407
1408 return SVN_NO_ERROR;
1409 }
1410
1411
1412 static svn_error_t *
orphaned_textmod_change(const svn_test_opts_t * opts,apr_pool_t * pool)1413 orphaned_textmod_change(const svn_test_opts_t *opts,
1414 apr_pool_t *pool)
1415 {
1416 apr_pool_t *subpool = svn_pool_create(pool);
1417 svn_fs_t *fs;
1418 svn_fs_txn_t *txn;
1419 svn_fs_root_t *txn_root, *root;
1420 svn_revnum_t youngest_rev = 0;
1421 svn_txdelta_window_handler_t wh_func;
1422 void *wh_baton;
1423 apr_hash_t *changed_paths;
1424
1425 /* Create a filesystem and repository. */
1426 SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-orphaned-changes", opts,
1427 pool));
1428
1429 /* Revision 1: Create and commit the greek tree. */
1430 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
1431 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
1432 SVN_ERR(svn_test__create_greek_tree(txn_root, subpool));
1433 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
1434 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
1435 svn_pool_clear(subpool);
1436
1437 /* Revision 2: Start to change "iota", but don't complete the work. */
1438 SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
1439 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
1440 SVN_ERR(svn_fs_apply_textdelta
1441 (&wh_func, &wh_baton, txn_root, "iota", NULL, NULL, subpool));
1442
1443 /* Don't send any delta windows, but do commit the transaction.
1444 According to the FS API docs, this is not a legal codepath. But
1445 this requirement on the API was added *after* its BDB
1446 implementation, and the BDB backend can't enforce compliance with
1447 the additional API rules in this case. So we are really just
1448 testing that misbehaving callers don't introduce more damage to
1449 the repository than they have to. */
1450 SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
1451 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
1452 svn_pool_clear(subpool);
1453
1454 /* Fetch changed paths for the youngest revision. We should find none. */
1455 SVN_ERR(svn_fs_revision_root(&root, fs, youngest_rev, subpool));
1456 SVN_ERR(svn_fs_paths_changed(&changed_paths, root, subpool));
1457 if (apr_hash_count(changed_paths) != 0)
1458 {
1459 svn_fs_path_change_t *change = apr_hash_get(changed_paths, "/iota",
1460 APR_HASH_KEY_STRING);
1461 if (change && change->text_mod)
1462 return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
1463 "Got unexpected textmods changed path "
1464 "for 'iota'");
1465 else
1466 return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
1467 "Got non-empty changed paths hash where empty "
1468 "one expected");
1469 }
1470
1471 return SVN_NO_ERROR;
1472 }
1473
1474 static svn_error_t *
key_test(apr_pool_t * pool)1475 key_test(apr_pool_t *pool)
1476 {
1477 int i;
1478 const char *keys[9][2] = {
1479 { "0", "1" },
1480 { "9", "a" },
1481 { "zzzzz", "100000" },
1482 { "z000000zzzzzz", "z000001000000" },
1483 { "97hnq33jx2a", "97hnq33jx2b" },
1484 { "97hnq33jx2z", "97hnq33jx30" },
1485 { "999", "99a" },
1486 { "a9z", "aa0" },
1487 { "z", "10" }
1488 };
1489
1490 for (i = 0; i < 9; i++)
1491 {
1492 char gen_key[MAX_KEY_SIZE];
1493 const char *orig_key = keys[i][0];
1494 const char *next_key = keys[i][1];
1495 apr_size_t len, olen;
1496
1497 len = strlen(orig_key);
1498 olen = len;
1499
1500 svn_fs_base__next_key(orig_key, &len, gen_key);
1501 if (! (((len == olen) || (len == (olen + 1)))
1502 && (strlen(next_key) == len)
1503 && (strcmp(next_key, gen_key) == 0)))
1504 {
1505 return svn_error_createf
1506 (SVN_ERR_FS_GENERAL, NULL,
1507 "failed to increment key \"%s\" correctly\n"
1508 " expected: %s\n"
1509 " actual: %s",
1510 orig_key, next_key, gen_key);
1511 }
1512 }
1513
1514 return SVN_NO_ERROR;
1515 }
1516
1517
1518 /* ------------------------------------------------------------------------ */
1519
1520 /* The test table. */
1521
1522 static int max_threads = 2;
1523
1524 static struct svn_test_descriptor_t test_funcs[] =
1525 {
1526 SVN_TEST_NULL,
1527 SVN_TEST_OPTS_PASS(create_berkeley_filesystem,
1528 "svn_fs_create_berkeley"),
1529 SVN_TEST_OPTS_PASS(open_berkeley_filesystem,
1530 "open an existing Berkeley DB filesystem"),
1531 SVN_TEST_OPTS_PASS(delete_mutables,
1532 "delete mutable nodes from directories"),
1533 SVN_TEST_OPTS_PASS(delete,
1534 "delete nodes tree"),
1535 SVN_TEST_OPTS_PASS(abort_txn,
1536 "abort a transaction"),
1537 SVN_TEST_OPTS_PASS(create_within_copy,
1538 "create new items within a copied directory"),
1539 SVN_TEST_OPTS_PASS(canonicalize_abspath,
1540 "test svn_fs__canonicalize_abspath"),
1541 SVN_TEST_OPTS_PASS(skip_deltas,
1542 "test skip deltas"),
1543 SVN_TEST_OPTS_PASS(redundant_copy,
1544 "ensure no-op for redundant copies"),
1545 SVN_TEST_OPTS_PASS(orphaned_textmod_change,
1546 "test for orphaned textmod changed paths"),
1547 SVN_TEST_PASS2(key_test,
1548 "testing sequential alphanumeric key generation"),
1549 SVN_TEST_NULL
1550 };
1551
1552 SVN_TEST_MAIN
1553