Mark many merge tests as skip-against-old-server.
[svn.git] / subversion / tests / svn_test_fs.c
blobc2daad339198d43f5e3d6d06b548293c94ccf71f
1 /* fs-helpers.c --- tests for the filesystem
3 * ====================================================================
4 * Copyright (c) 2000-2006 CollabNet. All rights reserved.
6 * This software is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at http://subversion.tigris.org/license-1.html.
9 * If newer versions of this license are posted there, you may use a
10 * newer version instead, at your option.
12 * This software consists of voluntary contributions made by many
13 * individuals. For exact contribution history, see the revision
14 * history and logs, available at http://subversion.tigris.org/.
15 * ====================================================================
18 #include <stdlib.h>
19 #include <string.h>
20 #include <apr_pools.h>
21 #include "svn_pools.h"
22 #include "svn_error.h"
23 #include "svn_fs.h"
24 #include "svn_path.h"
25 #include "svn_delta.h"
27 #include "svn_test.h"
28 #include "svn_test_fs.h"
31 /*-------------------------------------------------------------------*/
33 /** Helper routines. **/
36 static void
37 fs_warning_handler(void *baton, svn_error_t *err)
39 svn_handle_warning(stderr, err);
42 /* This is used only by bdb fs tests. */
43 svn_error_t *
44 svn_test__fs_new(svn_fs_t **fs_p, apr_pool_t *pool)
46 apr_hash_t *fs_config = apr_hash_make(pool);
47 apr_hash_set(fs_config, SVN_FS_CONFIG_BDB_TXN_NOSYNC,
48 APR_HASH_KEY_STRING, "1");
50 *fs_p = svn_fs_new(fs_config, pool);
51 if (! *fs_p)
52 return svn_error_create(SVN_ERR_FS_GENERAL, NULL,
53 "Couldn't alloc a new fs object.");
55 /* Provide a warning function that just dumps the message to stderr. */
56 svn_fs_set_warning_func(*fs_p, fs_warning_handler, NULL);
58 return SVN_NO_ERROR;
62 static apr_hash_t *
63 make_fs_config(const char *fs_type,
64 apr_pool_t *pool)
66 apr_hash_t *fs_config = apr_hash_make(pool);
67 apr_hash_set(fs_config, SVN_FS_CONFIG_BDB_TXN_NOSYNC,
68 APR_HASH_KEY_STRING, "1");
69 apr_hash_set(fs_config, SVN_FS_CONFIG_FS_TYPE,
70 APR_HASH_KEY_STRING,
71 fs_type);
72 return fs_config;
76 svn_error_t *
77 svn_test__create_fs(svn_fs_t **fs_p,
78 const char *name,
79 const char *fs_type,
80 apr_pool_t *pool)
82 apr_finfo_t finfo;
83 apr_hash_t *fs_config = make_fs_config(fs_type, pool);
85 /* If there's already a repository named NAME, delete it. Doing
86 things this way means that repositories stick around after a
87 failure for postmortem analysis, but also that tests can be
88 re-run without cleaning out the repositories created by prior
89 runs. */
90 if (apr_stat(&finfo, name, APR_FINFO_TYPE, pool) == APR_SUCCESS)
92 if (finfo.filetype == APR_DIR)
93 SVN_ERR(svn_fs_delete_fs(name, pool));
94 else
95 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
96 "there is already a file named '%s'", name);
99 SVN_ERR(svn_fs_create(fs_p, name, fs_config, pool));
100 if (! *fs_p)
101 return svn_error_create(SVN_ERR_FS_GENERAL, NULL,
102 "Couldn't alloc a new fs object.");
104 /* Provide a warning function that just dumps the message to stderr. */
105 svn_fs_set_warning_func(*fs_p, fs_warning_handler, NULL);
107 /* Register this fs for cleanup. */
108 svn_test_add_dir_cleanup(name);
110 return SVN_NO_ERROR;
114 svn_error_t *
115 svn_test__create_repos(svn_repos_t **repos_p,
116 const char *name,
117 const char *fs_type,
118 apr_pool_t *pool)
120 apr_finfo_t finfo;
121 apr_hash_t *fs_config = make_fs_config(fs_type, pool);
123 /* If there's already a repository named NAME, delete it. Doing
124 things this way means that repositories stick around after a
125 failure for postmortem analysis, but also that tests can be
126 re-run without cleaning out the repositories created by prior
127 runs. */
128 if (apr_stat(&finfo, name, APR_FINFO_TYPE, pool) == APR_SUCCESS)
130 if (finfo.filetype == APR_DIR)
131 SVN_ERR(svn_repos_delete(name, pool));
132 else
133 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
134 "there is already a file named '%s'", name);
137 SVN_ERR(svn_repos_create(repos_p, name, NULL, NULL, NULL,
138 fs_config, pool));
140 /* Register this repo for cleanup. */
141 svn_test_add_dir_cleanup(name);
143 return SVN_NO_ERROR;
147 svn_error_t *
148 svn_test__stream_to_string(svn_stringbuf_t **string,
149 svn_stream_t *stream,
150 apr_pool_t *pool)
152 char buf[10]; /* Making this really small because a) hey, they're
153 just tests, not the prime place to beg for
154 optimization, and b) we've had repository
155 problems in the past that only showed up when
156 reading a file into a buffer that couldn't hold the
157 file's whole contents -- the kind of thing you'd
158 like to catch while testing.
160 ### cmpilato todo: Perhaps some day this size can
161 be passed in as a parameter. Not high on my list
162 of priorities today, though. */
164 apr_size_t len;
165 svn_stringbuf_t *str = svn_stringbuf_create("", pool);
169 len = sizeof(buf);
170 SVN_ERR(svn_stream_read(stream, buf, &len));
172 /* Now copy however many bytes were *actually* read into str. */
173 svn_stringbuf_appendbytes(str, buf, len);
175 } while (len); /* Continue until we're told that no bytes were
176 read. */
178 *string = str;
179 return SVN_NO_ERROR;
182 svn_error_t *
183 svn_test__set_file_contents(svn_fs_root_t *root,
184 const char *path,
185 const char *contents,
186 apr_pool_t *pool)
188 svn_txdelta_window_handler_t consumer_func;
189 void *consumer_baton;
190 svn_string_t string;
192 SVN_ERR(svn_fs_apply_textdelta(&consumer_func, &consumer_baton,
193 root, path, NULL, NULL, pool));
195 string.data = contents;
196 string.len = strlen(contents);
197 SVN_ERR(svn_txdelta_send_string(&string, consumer_func,
198 consumer_baton, pool));
200 return SVN_NO_ERROR;
204 svn_error_t *
205 svn_test__get_file_contents(svn_fs_root_t *root,
206 const char *path,
207 svn_stringbuf_t **str,
208 apr_pool_t *pool)
210 svn_stream_t *stream;
212 SVN_ERR(svn_fs_file_contents(&stream, root, path, pool));
213 SVN_ERR(svn_test__stream_to_string(str, stream, pool));
215 return SVN_NO_ERROR;
219 /* Read all the entries in directory PATH under transaction or
220 revision root ROOT, copying their full paths into the TREE_ENTRIES
221 hash, and recursing when those entries are directories */
222 static svn_error_t *
223 get_dir_entries(apr_hash_t *tree_entries,
224 svn_fs_root_t *root,
225 const char *path,
226 apr_pool_t *pool)
228 apr_hash_t *entries;
229 apr_hash_index_t *hi;
231 SVN_ERR(svn_fs_dir_entries(&entries, root, path, pool));
233 /* Copy this list to the master list with the path prepended to the
234 names */
235 for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
237 void *val;
238 svn_fs_dirent_t *dirent;
239 const char *full_path;
241 apr_hash_this(hi, NULL, NULL, &val);
242 dirent = val;
244 /* Calculate the full path of this entry (by appending the name
245 to the path thus far) */
246 full_path = svn_path_join(path, dirent->name, pool);
248 /* Now, copy this dirent to the master hash, but this time, use
249 the full path for the key */
250 apr_hash_set(tree_entries, full_path, APR_HASH_KEY_STRING, dirent);
252 /* If this entry is a directory, recurse into the tree. */
253 if (dirent->kind == svn_node_dir)
254 SVN_ERR(get_dir_entries(tree_entries, root, full_path, pool));
257 return SVN_NO_ERROR;
261 static svn_error_t *
262 validate_tree_entry(svn_fs_root_t *root,
263 const char *path,
264 const char *contents,
265 apr_pool_t *pool)
267 svn_stream_t *rstream;
268 svn_stringbuf_t *rstring;
269 svn_boolean_t is_dir;
271 /* Verify that this is the expected type of node */
272 SVN_ERR(svn_fs_is_dir(&is_dir, root, path, pool));
273 if ((!is_dir && !contents) || (is_dir && contents))
274 return svn_error_createf
275 (SVN_ERR_FS_GENERAL, NULL,
276 "node '%s' in tree was of unexpected node type",
277 path);
279 /* Verify that the contents are as expected (files only) */
280 if (! is_dir)
282 SVN_ERR(svn_fs_file_contents(&rstream, root, path, pool));
283 SVN_ERR(svn_test__stream_to_string(&rstring, rstream, pool));
284 if (! svn_stringbuf_compare(rstring,
285 svn_stringbuf_create(contents, pool)))
286 return svn_error_createf
287 (SVN_ERR_FS_GENERAL, NULL,
288 "node '%s' in tree had unexpected contents",
289 path);
292 return SVN_NO_ERROR;
297 /* Given a transaction or revision root (ROOT), check to see if the
298 tree that grows from that root has all the path entries, and only
299 those entries, passed in the array ENTRIES (which is an array of
300 NUM_ENTRIES tree_test_entry_t's) */
301 svn_error_t *
302 svn_test__validate_tree(svn_fs_root_t *root,
303 svn_test__tree_entry_t *entries,
304 int num_entries,
305 apr_pool_t *pool)
307 apr_hash_t *tree_entries, *expected_entries;
308 apr_pool_t *subpool = svn_pool_create(pool);
309 svn_stringbuf_t *extra_entries = NULL;
310 svn_stringbuf_t *missing_entries = NULL;
311 svn_stringbuf_t *corrupt_entries = NULL;
312 apr_hash_index_t *hi;
313 int i;
315 /* Create a hash for storing our expected entries */
316 expected_entries = apr_hash_make(subpool);
318 /* Copy our array of expected entries into a hash. */
319 for (i = 0; i < num_entries; i++)
320 apr_hash_set(expected_entries, entries[i].path,
321 APR_HASH_KEY_STRING, &(entries[i]));
323 /* Create our master hash for storing the entries */
324 tree_entries = apr_hash_make(pool);
326 /* Begin the recursive directory entry dig */
327 SVN_ERR(get_dir_entries(tree_entries, root, "", subpool));
329 /* For each entry in our EXPECTED_ENTRIES hash, try to find that
330 entry in the TREE_ENTRIES hash given us by the FS. If we find
331 that object, remove it from the TREE_ENTRIES. If we don't find
332 it, there's a problem to report! */
333 for (hi = apr_hash_first(subpool, expected_entries);
335 hi = apr_hash_next(hi))
337 const void *key;
338 apr_ssize_t keylen;
339 void *val;
340 svn_test__tree_entry_t *entry;
342 apr_hash_this(hi, &key, &keylen, &val);
343 entry = val;
345 /* Verify that the entry exists in our full list of entries. */
346 val = apr_hash_get(tree_entries, key, keylen);
347 if (val)
349 svn_error_t *err;
351 if ((err = validate_tree_entry(root, entry->path,
352 entry->contents, subpool)))
354 /* If we don't have a corrupt entries string, make one. */
355 if (! corrupt_entries)
356 corrupt_entries = svn_stringbuf_create("", subpool);
358 /* Append this entry name to the list of corrupt entries. */
359 svn_stringbuf_appendcstr(corrupt_entries, " ");
360 svn_stringbuf_appendbytes(corrupt_entries, (const char *)key,
361 keylen);
362 svn_stringbuf_appendcstr(corrupt_entries, "\n");
363 svn_error_clear(err);
366 apr_hash_set(tree_entries, key, keylen, NULL);
368 else
370 /* If we don't have a missing entries string, make one. */
371 if (! missing_entries)
372 missing_entries = svn_stringbuf_create("", subpool);
374 /* Append this entry name to the list of missing entries. */
375 svn_stringbuf_appendcstr(missing_entries, " ");
376 svn_stringbuf_appendbytes(missing_entries, (const char *)key,
377 keylen);
378 svn_stringbuf_appendcstr(missing_entries, "\n");
382 /* Any entries still left in TREE_ENTRIES are extra ones that are
383 not expected to be present. Assemble a string with their names. */
384 for (hi = apr_hash_first(subpool, tree_entries);
386 hi = apr_hash_next(hi))
388 const void *key;
389 apr_ssize_t keylen;
391 apr_hash_this(hi, &key, &keylen, NULL);
393 /* If we don't have an extra entries string, make one. */
394 if (! extra_entries)
395 extra_entries = svn_stringbuf_create("", subpool);
397 /* Append this entry name to the list of missing entries. */
398 svn_stringbuf_appendcstr(extra_entries, " ");
399 svn_stringbuf_appendbytes(extra_entries, (const char *)key, keylen);
400 svn_stringbuf_appendcstr(extra_entries, "\n");
403 if (missing_entries || extra_entries || corrupt_entries)
405 return svn_error_createf
406 (SVN_ERR_FS_GENERAL, NULL,
407 "Repository tree does not look as expected.\n"
408 "Corrupt entries:\n%s"
409 "Missing entries:\n%s"
410 "Extra entries:\n%s",
411 corrupt_entries ? corrupt_entries->data : "",
412 missing_entries ? missing_entries->data : "",
413 extra_entries ? extra_entries->data : "");
416 svn_pool_destroy(subpool);
417 return SVN_NO_ERROR;
421 svn_error_t *
422 svn_test__txn_script_exec(svn_fs_root_t *txn_root,
423 svn_test__txn_script_command_t *script,
424 int num_edits,
425 apr_pool_t *pool)
427 int i;
429 /* Run through the list of edits, making the appropriate edit on
430 that entry in the TXN_ROOT. */
431 for (i = 0; i < num_edits; i++)
433 const char *path = script[i].path;
434 const char *param1 = script[i].param1;
435 int cmd = script[i].cmd;
436 svn_boolean_t is_dir = (param1 == 0);
438 switch (cmd)
440 case 'a':
441 if (is_dir)
443 SVN_ERR(svn_fs_make_dir(txn_root, path, pool));
445 else
447 SVN_ERR(svn_fs_make_file(txn_root, path, pool));
448 SVN_ERR(svn_test__set_file_contents(txn_root, path,
449 param1, pool));
451 break;
453 case 'c':
455 svn_revnum_t youngest;
456 svn_fs_root_t *rev_root;
457 svn_fs_t *fs = svn_fs_root_fs(txn_root);
459 SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool));
460 SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest, pool));
461 SVN_ERR(svn_fs_copy(rev_root, path, txn_root, param1, pool));
463 break;
465 case 'd':
466 SVN_ERR(svn_fs_delete(txn_root, path, pool));
467 break;
469 case 'e':
470 if (! is_dir)
472 SVN_ERR(svn_test__set_file_contents(txn_root, path,
473 param1, pool));
475 break;
477 default:
478 break;
482 return SVN_NO_ERROR;
486 svn_error_t *
487 svn_test__check_greek_tree(svn_fs_root_t *root,
488 apr_pool_t *pool)
490 svn_stream_t *rstream;
491 svn_stringbuf_t *rstring;
492 svn_stringbuf_t *content;
493 int i;
495 const char *file_contents[12][2] =
497 { "iota", "This is the file 'iota'.\n" },
498 { "A/mu", "This is the file 'mu'.\n" },
499 { "A/B/lambda", "This is the file 'lambda'.\n" },
500 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
501 { "A/B/E/beta", "This is the file 'beta'.\n" },
502 { "A/D/gamma", "This is the file 'gamma'.\n" },
503 { "A/D/G/pi", "This is the file 'pi'.\n" },
504 { "A/D/G/rho", "This is the file 'rho'.\n" },
505 { "A/D/G/tau", "This is the file 'tau'.\n" },
506 { "A/D/H/chi", "This is the file 'chi'.\n" },
507 { "A/D/H/psi", "This is the file 'psi'.\n" },
508 { "A/D/H/omega", "This is the file 'omega'.\n" }
511 /* Loop through the list of files, checking for matching content. */
512 for (i = 0; i < 12; i++)
514 SVN_ERR(svn_fs_file_contents(&rstream, root,
515 file_contents[i][0], pool));
516 SVN_ERR(svn_test__stream_to_string(&rstring, rstream, pool));
517 content = svn_stringbuf_create(file_contents[i][1], pool);
518 if (! svn_stringbuf_compare(rstring, content))
519 return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
520 "data read != data written in file '%s'.",
521 file_contents[i][0]);
523 return SVN_NO_ERROR;
528 svn_error_t *
529 svn_test__create_greek_tree(svn_fs_root_t *txn_root,
530 apr_pool_t *pool)
532 SVN_ERR(svn_fs_make_file(txn_root, "iota", pool));
533 SVN_ERR(svn_test__set_file_contents
534 (txn_root, "iota", "This is the file 'iota'.\n", pool));
535 SVN_ERR(svn_fs_make_dir (txn_root, "A", pool));
536 SVN_ERR(svn_fs_make_file(txn_root, "A/mu", pool));
537 SVN_ERR(svn_test__set_file_contents
538 (txn_root, "A/mu", "This is the file 'mu'.\n", pool));
539 SVN_ERR(svn_fs_make_dir (txn_root, "A/B", pool));
540 SVN_ERR(svn_fs_make_file(txn_root, "A/B/lambda", pool));
541 SVN_ERR(svn_test__set_file_contents
542 (txn_root, "A/B/lambda", "This is the file 'lambda'.\n", pool));
543 SVN_ERR(svn_fs_make_dir (txn_root, "A/B/E", pool));
544 SVN_ERR(svn_fs_make_file(txn_root, "A/B/E/alpha", pool));
545 SVN_ERR(svn_test__set_file_contents
546 (txn_root, "A/B/E/alpha", "This is the file 'alpha'.\n", pool));
547 SVN_ERR(svn_fs_make_file(txn_root, "A/B/E/beta", pool));
548 SVN_ERR(svn_test__set_file_contents
549 (txn_root, "A/B/E/beta", "This is the file 'beta'.\n", pool));
550 SVN_ERR(svn_fs_make_dir (txn_root, "A/B/F", pool));
551 SVN_ERR(svn_fs_make_dir (txn_root, "A/C", pool));
552 SVN_ERR(svn_fs_make_dir (txn_root, "A/D", pool));
553 SVN_ERR(svn_fs_make_file(txn_root, "A/D/gamma", pool));
554 SVN_ERR(svn_test__set_file_contents
555 (txn_root, "A/D/gamma", "This is the file 'gamma'.\n", pool));
556 SVN_ERR(svn_fs_make_dir (txn_root, "A/D/G", pool));
557 SVN_ERR(svn_fs_make_file(txn_root, "A/D/G/pi", pool));
558 SVN_ERR(svn_test__set_file_contents
559 (txn_root, "A/D/G/pi", "This is the file 'pi'.\n", pool));
560 SVN_ERR(svn_fs_make_file(txn_root, "A/D/G/rho", pool));
561 SVN_ERR(svn_test__set_file_contents
562 (txn_root, "A/D/G/rho", "This is the file 'rho'.\n", pool));
563 SVN_ERR(svn_fs_make_file(txn_root, "A/D/G/tau", pool));
564 SVN_ERR(svn_test__set_file_contents
565 (txn_root, "A/D/G/tau", "This is the file 'tau'.\n", pool));
566 SVN_ERR(svn_fs_make_dir (txn_root, "A/D/H", pool));
567 SVN_ERR(svn_fs_make_file(txn_root, "A/D/H/chi", pool));
568 SVN_ERR(svn_test__set_file_contents
569 (txn_root, "A/D/H/chi", "This is the file 'chi'.\n", pool));
570 SVN_ERR(svn_fs_make_file(txn_root, "A/D/H/psi", pool));
571 SVN_ERR(svn_test__set_file_contents
572 (txn_root, "A/D/H/psi", "This is the file 'psi'.\n", pool));
573 SVN_ERR(svn_fs_make_file(txn_root, "A/D/H/omega", pool));
574 SVN_ERR(svn_test__set_file_contents
575 (txn_root, "A/D/H/omega", "This is the file 'omega'.\n", pool));
576 return SVN_NO_ERROR;