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