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 * ====================================================================
22 #define APR_WANT_STDIO
23 #define APR_WANT_STRFUNC
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
47 /* Initialize parameters for the random tests. */
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
,
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
;
71 apr_getopt_init(&opt
, pool
, test_argc
, test_argv
);
73 == (status
= apr_getopt(opt
, "s:l:n:r:FW", &optch
, &opt_arg
)))
78 *seed
= atol(opt_arg
);
81 *maxlen
= atoi(opt_arg
);
84 *iterations
= atoi(opt_arg
);
87 *random_bytes
= opt_arg
+ 1;
88 *bytes_range
= strlen(*random_bytes
);
91 *dump_files
= !*dump_files
;
94 *print_windows
= !*print_windows
;
101 /* Open a temporary file. */
103 open_tempfile(const char *name_template
, apr_pool_t
*pool
)
105 apr_status_t apr_err
;
106 apr_file_t
*fp
= NULL
;
110 templ
= apr_pstrdup(pool
, "tempfile_XXXXXX");
112 templ
= apr_pstrdup(pool
, name_template
);
114 apr_err
= apr_file_mktemp(&fp
, templ
, 0, pool
);
115 assert(apr_err
== 0);
120 /* Rewind the file pointer */
121 static void rewind_file(apr_file_t
*fp
)
123 apr_off_t offset
= 0;
125 apr_status_t apr_err
=
127 apr_file_seek(fp
, APR_SET
, &offset
);
128 assert(apr_err
== 0);
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
);
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. */
154 generate_random_file(apr_uint32_t maxlen
,
155 apr_uint32_t subseed_base
,
157 const char *random_bytes
,
158 apr_uint32_t bytes_range
,
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
;
170 fp
= open_tempfile("random_XXXXXX", pool
);
171 len
= svn_test_rand(seed
) % maxlen
; /* We might go over this by a bit. */
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
;
182 r
= subseed_base
+ svn_test_rand(seed
) % SEEDS
;
185 const int ch
= (random_bytes
186 ? (unsigned)random_bytes
[r
% bytes_range
]
190 apr_size_t ignore_length
;
191 apr_file_write_full(fp
, file_buffer
, sizeof file_buffer
,
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
);
210 dump_file_contents(fp
);
215 /* Compare two open files. The file positions may change. */
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];
224 apr_size_t len1
, len2
;
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
;
242 return svn_error_createf(SVN_ERR_TEST_FAILED
, NULL
,
243 "mismatch at position %"APR_OFF_T_FMT
,
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
);
258 copy_tempfile(apr_file_t
*fp
, apr_pool_t
*pool
)
260 static char file_buffer
[10240];
262 apr_size_t length1
, length2
;
264 newfp
= open_tempfile("copy_XXXXXX", pool
);
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
);
282 /* Implements svn_test_driver_t. */
284 random_test(const char **msg
,
285 svn_boolean_t msg_only
,
286 svn_test_opts_t
*opts
,
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
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
);
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
,
314 apr_file_t
*target
= generate_random_file(maxlen
, subseed_base
, &seed
,
315 random_bytes
, bytes_range
,
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
;
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
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
,
340 /* Make stage 2: encode the text delta in svndiff format. */
341 svn_txdelta_to_svndiff2(&handler
, &handler_baton
, stream
, 1,
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
),
350 SVN_ERR(svn_txdelta_send_txstream(txdelta_stream
,
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
);
370 /* (Note: *LAST_SEED is an output parameter.) */
372 do_random_combine_test(const char **msg
,
373 svn_boolean_t msg_only
,
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
385 init_params(&seed
, &maxlen
, &iterations
, &dump_files
, &print_windows
,
386 &random_bytes
, &bytes_range
, pool
);
388 "random combine delta test, seed = %lu", (unsigned long) seed
);
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
,
403 apr_file_t
*middle
= generate_random_file(maxlen
, subseed_base
, &seed
,
404 random_bytes
, bytes_range
,
406 apr_file_t
*target
= generate_random_file(maxlen
, subseed_base
, &seed
,
407 random_bytes
, bytes_range
,
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
;
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
,
434 /* Make stage 2: encode the text delta in svndiff format. */
435 svn_txdelta_to_svndiff2(&handler
, &handler_baton
, stream
, 1,
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
),
445 svn_txdelta(&txdelta_stream_B
,
446 svn_stream_from_aprfile(middle_copy
, delta_pool
),
447 svn_stream_from_aprfile(target
, 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
,
461 delta_window_print(window_A
, "A ", stdout
);
462 SVN_ERR(svn_txdelta_next_window(&window_B
, txdelta_stream_B
,
465 delta_window_print(window_B
, "B ", stdout
);
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;
475 composite
= svn_txdelta_compose_windows(window_A
, window_B
,
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. */
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
);
510 /* Implements svn_test_driver_t. */
512 random_combine_test(const char **msg
,
513 svn_boolean_t msg_only
,
514 svn_test_opts_t
*opts
,
518 svn_error_t
*err
= do_random_combine_test(msg
, msg_only
, pool
, &seed
);
520 printf("SEED: Last seen = %lu\n", (unsigned long) seed
);
525 /* Change to 1 to enable the unit test for the delta combiner's range index: */
527 #include "range-index-test.h"
532 /* The test table. */
534 struct svn_test_descriptor_t test_funcs
[] =
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
),