Make a status test pass against old servers.
[svn.git] / subversion / tests / libsvn_delta / random-test.c
blob3b05fe912785d42b2902933040bb64c9824646c4
1 /*
2 * random-test.c: Test delta generation and application using random data.
4 * ====================================================================
5 * Copyright (c) 2000-2004 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
20 #include <assert.h>
22 #define APR_WANT_STDIO
23 #define APR_WANT_STRFUNC
24 #include <apr_want.h>
25 #include <apr_general.h>
26 #include <apr_getopt.h>
27 #include <apr_file_io.h>
29 #include "svn_delta.h"
30 #include "svn_pools.h"
31 #include "svn_error.h"
33 #include "../svn_test.h"
35 #include "../../libsvn_delta/delta.h"
36 #include "delta-window-test.h"
39 #define DEFAULT_ITERATIONS 30
40 #define DEFAULT_MAXLEN (100 * 1024)
41 #define DEFAULT_DUMP_FILES 0
42 #define DEFAULT_PRINT_WINDOWS 0
43 #define SEEDS 50
44 #define MAXSEQ 100
47 /* Initialize parameters for the random tests. */
48 extern int test_argc;
49 extern const char **test_argv;
51 static void init_params(apr_uint32_t *seed,
52 apr_uint32_t *maxlen, int *iterations,
53 int *dump_files, int *print_windows,
54 const char **random_bytes,
55 apr_uint32_t *bytes_range,
56 apr_pool_t *pool)
58 apr_getopt_t *opt;
59 char optch;
60 const char *opt_arg;
61 apr_status_t status;
63 *seed = (apr_uint32_t) apr_time_now();
64 *maxlen = DEFAULT_MAXLEN;
65 *iterations = DEFAULT_ITERATIONS;
66 *dump_files = DEFAULT_DUMP_FILES;
67 *print_windows = DEFAULT_PRINT_WINDOWS;
68 *random_bytes = NULL;
69 *bytes_range = 256;
71 apr_getopt_init(&opt, pool, test_argc, test_argv);
72 while (APR_SUCCESS
73 == (status = apr_getopt(opt, "s:l:n:r:FW", &optch, &opt_arg)))
75 switch (optch)
77 case 's':
78 *seed = atol(opt_arg);
79 break;
80 case 'l':
81 *maxlen = atoi(opt_arg);
82 break;
83 case 'n':
84 *iterations = atoi(opt_arg);
85 break;
86 case 'r':
87 *random_bytes = opt_arg + 1;
88 *bytes_range = strlen(*random_bytes);
89 break;
90 case 'F':
91 *dump_files = !*dump_files;
92 break;
93 case 'W':
94 *print_windows = !*print_windows;
95 break;
101 /* Open a temporary file. */
102 static apr_file_t *
103 open_tempfile(const char *name_template, apr_pool_t *pool)
105 apr_status_t apr_err;
106 apr_file_t *fp = NULL;
107 char *templ;
109 if (!name_template)
110 templ = apr_pstrdup(pool, "tempfile_XXXXXX");
111 else
112 templ = apr_pstrdup(pool, name_template);
114 apr_err = apr_file_mktemp(&fp, templ, 0, pool);
115 assert(apr_err == 0);
116 assert(fp != NULL);
117 return fp;
120 /* Rewind the file pointer */
121 static void rewind_file(apr_file_t *fp)
123 apr_off_t offset = 0;
124 #ifndef NDEBUG
125 apr_status_t apr_err =
126 #endif
127 apr_file_seek(fp, APR_SET, &offset);
128 assert(apr_err == 0);
129 assert(offset == 0);
133 static void
134 dump_file_contents(apr_file_t *fp)
136 static char file_buffer[10240];
137 apr_size_t length = sizeof file_buffer;
138 fputs("--------\n", stdout);
141 apr_file_read_full(fp, file_buffer, sizeof file_buffer, &length);
142 fwrite(file_buffer, 1, length, stdout);
144 while (length == sizeof file_buffer);
145 putc('\n', stdout);
146 rewind_file(fp);
149 /* Generate a temporary file containing sort-of random data. Diffs
150 between files of random data tend to be pretty boring, so we try to
151 make sure there are a bunch of common substrings between two runs
152 of this function with the same seedbase. */
153 static apr_file_t *
154 generate_random_file(apr_uint32_t maxlen,
155 apr_uint32_t subseed_base,
156 apr_uint32_t *seed,
157 const char *random_bytes,
158 apr_uint32_t bytes_range,
159 int dump_files,
160 apr_pool_t *pool)
162 static char file_buffer[10240];
163 char *buf = file_buffer;
164 char *const end = buf + sizeof file_buffer;
166 apr_uint32_t len, seqlen;
167 apr_file_t *fp;
168 unsigned long r;
170 fp = open_tempfile("random_XXXXXX", pool);
171 len = svn_test_rand(seed) % maxlen; /* We might go over this by a bit. */
172 while (len > 0)
174 /* Generate a pseudo-random sequence of up to MAXSEQ bytes,
175 where the seed is in the range [seedbase..seedbase+MAXSEQ-1].
176 (Use our own pseudo-random number generator here to avoid
177 clobbering the seed of the libc random number generator.) */
179 seqlen = svn_test_rand(seed) % MAXSEQ;
180 if (seqlen > len) seqlen = len;
181 len -= seqlen;
182 r = subseed_base + svn_test_rand(seed) % SEEDS;
183 while (seqlen-- > 0)
185 const int ch = (random_bytes
186 ? (unsigned)random_bytes[r % bytes_range]
187 : r % bytes_range);
188 if (buf == end)
190 apr_size_t ignore_length;
191 apr_file_write_full(fp, file_buffer, sizeof file_buffer,
192 &ignore_length);
193 buf = file_buffer;
196 *buf++ = ch;
197 r = r * 1103515245 + 12345;
201 if (buf > file_buffer)
203 apr_size_t ignore_length;
204 apr_file_write_full(fp, file_buffer, buf - file_buffer, &ignore_length);
205 buf = file_buffer;
207 rewind_file(fp);
209 if (dump_files)
210 dump_file_contents(fp);
212 return fp;
215 /* Compare two open files. The file positions may change. */
216 static svn_error_t *
217 compare_files(apr_file_t *f1, apr_file_t *f2, int dump_files)
219 static char file_buffer_1[10240];
220 static char file_buffer_2[10240];
222 char *c1, *c2;
223 apr_off_t pos = 0;
224 apr_size_t len1, len2;
226 rewind_file(f1);
227 rewind_file(f2);
229 if (dump_files)
230 dump_file_contents(f2);
234 apr_file_read_full(f1, file_buffer_1, sizeof file_buffer_1, &len1);
235 apr_file_read_full(f2, file_buffer_2, sizeof file_buffer_2, &len2);
237 for (c1 = file_buffer_1, c2 = file_buffer_2;
238 c1 < file_buffer_1 + len1 && c2 < file_buffer_2 + len2;
239 ++c1, ++c2, ++pos)
241 if (*c1 != *c2)
242 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
243 "mismatch at position %"APR_OFF_T_FMT,
244 pos);
247 if (len1 != len2)
248 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
249 "unequal file sizes at position"
250 " %"APR_OFF_T_FMT, pos);
252 while (len1 == sizeof file_buffer_1);
253 return SVN_NO_ERROR;
257 static apr_file_t *
258 copy_tempfile(apr_file_t *fp, apr_pool_t *pool)
260 static char file_buffer[10240];
261 apr_file_t *newfp;
262 apr_size_t length1, length2;
264 newfp = open_tempfile("copy_XXXXXX", pool);
266 rewind_file(fp);
269 apr_file_read_full(fp, file_buffer, sizeof file_buffer, &length1);
270 apr_file_write_full(newfp, file_buffer, length1, &length2);
271 assert(length1 == length2);
273 while (length1 == sizeof file_buffer);
275 rewind_file(fp);
276 rewind_file(newfp);
277 return newfp;
282 /* Implements svn_test_driver_t. */
283 static svn_error_t *
284 random_test(const char **msg,
285 svn_boolean_t msg_only,
286 svn_test_opts_t *opts,
287 apr_pool_t *pool)
289 static char msg_buff[256];
291 apr_uint32_t seed, bytes_range, maxlen;
292 int i, iterations, dump_files, print_windows;
293 const char *random_bytes;
295 /* Initialize parameters and print out the seed in case we dump core
296 or something. */
297 init_params(&seed, &maxlen, &iterations, &dump_files, &print_windows,
298 &random_bytes, &bytes_range, pool);
299 sprintf(msg_buff, "random delta test, seed = %lu", (unsigned long) seed);
300 *msg = msg_buff;
302 if (msg_only)
303 return SVN_NO_ERROR;
304 else
305 printf("SEED: %s\n", msg_buff);
307 for (i = 0; i < iterations; i++)
309 /* Generate source and target for the delta and its application. */
310 apr_uint32_t subseed_base = svn_test_rand(&seed);
311 apr_file_t *source = generate_random_file(maxlen, subseed_base, &seed,
312 random_bytes, bytes_range,
313 dump_files, pool);
314 apr_file_t *target = generate_random_file(maxlen, subseed_base, &seed,
315 random_bytes, bytes_range,
316 dump_files, pool);
317 apr_file_t *source_copy = copy_tempfile(source, pool);
318 apr_file_t *target_regen = open_tempfile(NULL, pool);
320 svn_txdelta_stream_t *txdelta_stream;
321 svn_txdelta_window_handler_t handler;
322 svn_stream_t *stream;
323 void *handler_baton;
325 /* Set up a four-stage pipeline: create a delta, convert it to
326 svndiff format, parse it back into delta format, and apply it
327 to a copy of the source file to see if we get the same target
328 back. */
329 apr_pool_t *delta_pool = svn_pool_create(pool);
331 /* Make stage 4: apply the text delta. */
332 svn_txdelta_apply(svn_stream_from_aprfile(source_copy, delta_pool),
333 svn_stream_from_aprfile(target_regen, delta_pool),
334 NULL, NULL, delta_pool, &handler, &handler_baton);
336 /* Make stage 3: reparse the text delta. */
337 stream = svn_txdelta_parse_svndiff(handler, handler_baton, TRUE,
338 delta_pool);
340 /* Make stage 2: encode the text delta in svndiff format. */
341 svn_txdelta_to_svndiff2(&handler, &handler_baton, stream, 1,
342 delta_pool);
344 /* Make stage 1: create the text delta. */
345 svn_txdelta(&txdelta_stream,
346 svn_stream_from_aprfile(source, delta_pool),
347 svn_stream_from_aprfile(target, delta_pool),
348 delta_pool);
350 SVN_ERR(svn_txdelta_send_txstream(txdelta_stream,
351 handler,
352 handler_baton,
353 delta_pool));
355 svn_pool_destroy(delta_pool);
357 SVN_ERR(compare_files(target, target_regen, dump_files));
359 apr_file_close(source);
360 apr_file_close(target);
361 apr_file_close(source_copy);
362 apr_file_close(target_regen);
365 return SVN_NO_ERROR;
370 /* (Note: *LAST_SEED is an output parameter.) */
371 static svn_error_t *
372 do_random_combine_test(const char **msg,
373 svn_boolean_t msg_only,
374 apr_pool_t *pool,
375 apr_uint32_t *last_seed)
377 static char msg_buff[256];
379 apr_uint32_t seed, bytes_range, maxlen;
380 int i, iterations, dump_files, print_windows;
381 const char *random_bytes;
383 /* Initialize parameters and print out the seed in case we dump core
384 or something. */
385 init_params(&seed, &maxlen, &iterations, &dump_files, &print_windows,
386 &random_bytes, &bytes_range, pool);
387 sprintf(msg_buff,
388 "random combine delta test, seed = %lu", (unsigned long) seed);
389 *msg = msg_buff;
391 if (msg_only)
392 return SVN_NO_ERROR;
393 else
394 printf("SEED: %s\n", msg_buff);
396 for (i = 0; i < iterations; i++)
398 /* Generate source and target for the delta and its application. */
399 apr_uint32_t subseed_base = svn_test_rand((*last_seed = seed, &seed));
400 apr_file_t *source = generate_random_file(maxlen, subseed_base, &seed,
401 random_bytes, bytes_range,
402 dump_files, pool);
403 apr_file_t *middle = generate_random_file(maxlen, subseed_base, &seed,
404 random_bytes, bytes_range,
405 dump_files, pool);
406 apr_file_t *target = generate_random_file(maxlen, subseed_base, &seed,
407 random_bytes, bytes_range,
408 dump_files, pool);
409 apr_file_t *source_copy = copy_tempfile(source, pool);
410 apr_file_t *middle_copy = copy_tempfile(middle, pool);
411 apr_file_t *target_regen = open_tempfile(NULL, pool);
413 svn_txdelta_stream_t *txdelta_stream_A;
414 svn_txdelta_stream_t *txdelta_stream_B;
415 svn_txdelta_window_handler_t handler;
416 svn_stream_t *stream;
417 void *handler_baton;
419 /* Set up a four-stage pipeline: create two deltas, combine them
420 and convert the result to svndiff format, parse that back
421 into delta format, and apply it to a copy of the source file
422 to see if we get the same target back. */
423 apr_pool_t *delta_pool = svn_pool_create(pool);
425 /* Make stage 4: apply the text delta. */
426 svn_txdelta_apply(svn_stream_from_aprfile(source_copy, delta_pool),
427 svn_stream_from_aprfile(target_regen, delta_pool),
428 NULL, NULL, delta_pool, &handler, &handler_baton);
430 /* Make stage 3: reparse the text delta. */
431 stream = svn_txdelta_parse_svndiff(handler, handler_baton, TRUE,
432 delta_pool);
434 /* Make stage 2: encode the text delta in svndiff format. */
435 svn_txdelta_to_svndiff2(&handler, &handler_baton, stream, 1,
436 delta_pool);
438 /* Make stage 1: create the text deltas. */
440 svn_txdelta(&txdelta_stream_A,
441 svn_stream_from_aprfile(source, delta_pool),
442 svn_stream_from_aprfile(middle, delta_pool),
443 delta_pool);
445 svn_txdelta(&txdelta_stream_B,
446 svn_stream_from_aprfile(middle_copy, delta_pool),
447 svn_stream_from_aprfile(target, delta_pool),
448 delta_pool);
451 svn_txdelta_window_t *window_A;
452 svn_txdelta_window_t *window_B;
453 svn_txdelta_window_t *composite;
454 apr_pool_t *wpool = svn_pool_create(delta_pool);
458 SVN_ERR(svn_txdelta_next_window(&window_A, txdelta_stream_A,
459 wpool));
460 if (print_windows)
461 delta_window_print(window_A, "A ", stdout);
462 SVN_ERR(svn_txdelta_next_window(&window_B, txdelta_stream_B,
463 wpool));
464 if (print_windows)
465 delta_window_print(window_B, "B ", stdout);
466 if (!window_B)
467 break;
468 assert(window_A != NULL || window_B->src_ops == 0);
469 if (window_B->src_ops == 0)
471 composite = window_B;
472 composite->sview_len = 0;
474 else
475 composite = svn_txdelta_compose_windows(window_A, window_B,
476 wpool);
477 if (print_windows)
478 delta_window_print(composite, "AB", stdout);
480 /* The source view length should not be 0 if there are
481 source copy ops in the window. */
482 if (composite
483 && composite->sview_len == 0 && composite->src_ops > 0)
484 return svn_error_create
485 (SVN_ERR_FS_GENERAL, NULL,
486 "combined delta window is inconsistent");
488 SVN_ERR(handler(composite, handler_baton));
489 svn_pool_clear(wpool);
491 while (composite != NULL);
492 svn_pool_destroy(wpool);
495 svn_pool_destroy(delta_pool);
497 SVN_ERR(compare_files(target, target_regen, dump_files));
499 apr_file_close(source);
500 apr_file_close(middle);
501 apr_file_close(target);
502 apr_file_close(source_copy);
503 apr_file_close(middle_copy);
504 apr_file_close(target_regen);
507 return SVN_NO_ERROR;
510 /* Implements svn_test_driver_t. */
511 static svn_error_t *
512 random_combine_test(const char **msg,
513 svn_boolean_t msg_only,
514 svn_test_opts_t *opts,
515 apr_pool_t *pool)
517 apr_uint32_t seed;
518 svn_error_t *err = do_random_combine_test(msg, msg_only, pool, &seed);
519 if (!msg_only)
520 printf("SEED: Last seen = %lu\n", (unsigned long) seed);
521 return err;
525 /* Change to 1 to enable the unit test for the delta combiner's range index: */
526 #if 0
527 #include "range-index-test.h"
528 #endif
532 /* The test table. */
534 struct svn_test_descriptor_t test_funcs[] =
536 SVN_TEST_NULL,
537 SVN_TEST_PASS(random_test),
538 SVN_TEST_PASS(random_combine_test),
539 #ifdef SVN_RANGE_INDEX_TEST_H
540 SVN_TEST_PASS(random_range_index_test),
541 #endif
542 SVN_TEST_NULL