summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjosh.macdonald <jmacd@users.noreply.github.com>2008-06-20 04:06:06 +0000
committerjosh.macdonald <jmacd@users.noreply.github.com>2008-06-20 04:06:06 +0000
commitc31f63a124fc6221ee82e9563bb7aeeb238c92e8 (patch)
treee2a5e621fe228db5c972223c3891c49bf7f06241
parent9cbfc47a5aa5bd5d2f61a5dc4a969336d27485fa (diff)
Add a new test file, add a Mersenne Twister implementation, and
begin work on a new merge command. The new "file_spec" model will allow working with randomly-generated inputs without having to write the entire file to disk, allowing better streaming tests: next!
-rw-r--r--xdelta3/Makefile1
-rw-r--r--xdelta3/Makefile.mingw1
-rw-r--r--xdelta3/xdelta3-test.h140
-rw-r--r--xdelta3/xdelta3-test2.h188
4 files changed, 302 insertions, 28 deletions
diff --git a/xdelta3/Makefile b/xdelta3/Makefile
index aa35a4a..83fcbd6 100644
--- a/xdelta3/Makefile
+++ b/xdelta3/Makefile
@@ -25,6 +25,7 @@ SOURCES = xdelta3-cfgs.h \
25 xdelta3-python.h \ 25 xdelta3-python.h \
26 xdelta3-second.h \ 26 xdelta3-second.h \
27 xdelta3-test.h \ 27 xdelta3-test.h \
28 xdelta3-test2.h \
28 xdelta3.c \ 29 xdelta3.c \
29 xdelta3.h 30 xdelta3.h
30 31
diff --git a/xdelta3/Makefile.mingw b/xdelta3/Makefile.mingw
index 4e84527..5a19a1a 100644
--- a/xdelta3/Makefile.mingw
+++ b/xdelta3/Makefile.mingw
@@ -26,6 +26,7 @@ SOURCES = xdelta3-cfgs.h \
26 xdelta3-python.h \ 26 xdelta3-python.h \
27 xdelta3-second.h \ 27 xdelta3-second.h \
28 xdelta3-test.h \ 28 xdelta3-test.h \
29 xdelta3-test2.h \
29 xdelta3.c \ 30 xdelta3.c \
30 xdelta3.h 31 xdelta3.h
31 32
diff --git a/xdelta3/xdelta3-test.h b/xdelta3/xdelta3-test.h
index 5389d57..4a94a2f 100644
--- a/xdelta3/xdelta3-test.h
+++ b/xdelta3/xdelta3-test.h
@@ -16,6 +16,74 @@
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */ 17 */
18 18
19/* The code in this file is being gradually migrated to
20 * xdelta3-test2.h, which has a more solid foundation. That file is
21 * included immediatly after this random number generator code, which
22 * is all that is currently shared by the two files. */
23
24/* This is public-domain Mersenne Twister code,
25 * attributed to Michael Brundage. Thanks!
26 * http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation.html
27 */
28#define MT_LEN 624
29#define MT_IA 397
30#define MT_IB (MT_LEN - MT_IA)
31#define UPPER_MASK 0x80000000
32#define LOWER_MASK 0x7FFFFFFF
33#define MATRIX_A 0x9908B0DF
34#define TWIST(b,i,j) ((b)[i] & UPPER_MASK) | ((b)[j] & LOWER_MASK)
35#define MAGIC(s) (((s)&1)*MATRIX_A)
36
37struct mtrand_ {
38 int mt_index;
39 usize_t mt_buffer[MT_LEN];
40};
41
42typedef struct mtrand_ mtrand;
43
44static void mt_init (mtrand *mt, int seed) {
45 int i;
46 srand (seed);
47 for (i = 0; i < MT_LEN; i++)
48 {
49 mt->mt_buffer[i] = rand ();
50 }
51 mt->mt_index = 0;
52}
53
54static usize_t mt_random (mtrand *mt) {
55 usize_t * b = mt->mt_buffer;
56 int idx = mt->mt_index;
57 usize_t s;
58 int i;
59
60 if (idx == MT_LEN*sizeof(usize_t))
61 {
62 idx = 0;
63 i = 0;
64 for (; i < MT_IB; i++) {
65 s = TWIST(b, i, i+1);
66 b[i] = b[i + MT_IA] ^ (s >> 1) ^ MAGIC(s);
67 }
68 for (; i < MT_LEN-1; i++) {
69 s = TWIST(b, i, i+1);
70 b[i] = b[i - MT_IB] ^ (s >> 1) ^ MAGIC(s);
71 }
72
73 s = TWIST(b, MT_LEN-1, 0);
74 b[MT_LEN-1] = b[MT_IA-1] ^ (s >> 1) ^ MAGIC(s);
75 }
76 mt->mt_index = idx + sizeof(usize_t);
77 return *(usize_t *)((unsigned char *)b + idx);
78}
79
80static mtrand static_mtrand;
81
82#include "xdelta3-test2.h"
83
84/* Everything below this line is old! The tests are still good, but
85 * better tests are being written in xdelta3-test2.h. */
86
19#include <math.h> 87#include <math.h>
20 88
21#ifndef WIN32 89#ifndef WIN32
@@ -90,13 +158,14 @@ static int do_fail (xd3_stream *stream, const char *buf)
90} 158}
91 159
92static int 160static int
93test_exponential_dist (usize_t mean, usize_t max) 161test_exponential_dist (usize_t mean, usize_t max_value)
94{ 162{
95 double mean_d = mean; 163 double mean_d = mean;
96 double erand = log (1.0 / (rand () / (double)RAND_MAX)); 164 double erand = log (1.0 / (mt_random (&static_mtrand) /
165 (double)USIZE_T_MAX));
97 usize_t x = (usize_t) (mean_d * erand + 0.5); 166 usize_t x = (usize_t) (mean_d * erand + 0.5);
98 167
99 return min (x, max); 168 return min (x, max_value);
100} 169}
101 170
102/* Test that the exponential distribution actually produces its mean. */ 171/* Test that the exponential distribution actually produces its mean. */
@@ -108,7 +177,9 @@ test_random_numbers (xd3_stream *stream, int ignore)
108 usize_t mean = 50; 177 usize_t mean = 50;
109 usize_t n_rounds = 10000; 178 usize_t n_rounds = 10000;
110 double average, error; 179 double average, error;
111 double allowed_error = 1.0; 180 double allowed_error = 2.0;
181
182 mt_init (& static_mtrand, 0x9f73f7fc);
112 183
113 for (i = 0; i < n_rounds; i += 1) 184 for (i = 0; i < n_rounds; i += 1)
114 { 185 {
@@ -120,10 +191,10 @@ test_random_numbers (xd3_stream *stream, int ignore)
120 191
121 if (error < allowed_error && error > -allowed_error) 192 if (error < allowed_error && error > -allowed_error)
122 { 193 {
123 /*DP(RINT "error is %f\n", error);*/
124 return 0; 194 return 0;
125 } 195 }
126 196
197 DP(RINT "error is %f\n", error);
127 stream->msg = "random distribution looks broken"; 198 stream->msg = "random distribution looks broken";
128 return XD3_INTERNAL; 199 return XD3_INTERNAL;
129} 200}
@@ -135,9 +206,9 @@ test_unlink (char* file)
135 while (unlink (file) != 0) 206 while (unlink (file) != 0)
136 { 207 {
137 if (errno == ENOENT) 208 if (errno == ENOENT)
138 { 209 {
139 break; 210 break;
140 } 211 }
141 sprintf (buf, "rm -f %s", file); 212 sprintf (buf, "rm -f %s", file);
142 system (buf); 213 system (buf);
143 } 214 }
@@ -174,8 +245,8 @@ test_setup (void)
174static int 245static int
175test_make_inputs (xd3_stream *stream, xoff_t *ss_out, xoff_t *ts_out) 246test_make_inputs (xd3_stream *stream, xoff_t *ss_out, xoff_t *ts_out)
176{ 247{
177 usize_t ts = (rand () % TEST_FILE_MEAN) + TEST_FILE_MEAN / 2; 248 usize_t ts = (mt_random (&static_mtrand) % TEST_FILE_MEAN) + TEST_FILE_MEAN / 2;
178 usize_t ss = (rand () % TEST_FILE_MEAN) + TEST_FILE_MEAN / 2; 249 usize_t ss = (mt_random (&static_mtrand) % TEST_FILE_MEAN) + TEST_FILE_MEAN / 2;
179 uint8_t *buf = malloc (ts + ss), *sbuf = buf, *tbuf = buf + ss; 250 uint8_t *buf = malloc (ts + ss), *sbuf = buf, *tbuf = buf + ss;
180 usize_t sadd = 0, sadd_max = ss * TEST_ADD_RATIO; 251 usize_t sadd = 0, sadd_max = ss * TEST_ADD_RATIO;
181 FILE *tf = NULL, *sf = NULL; 252 FILE *tf = NULL, *sf = NULL;
@@ -196,7 +267,7 @@ test_make_inputs (xd3_stream *stream, xoff_t *ss_out, xoff_t *ts_out)
196 { 267 {
197 for (i = 0; i < ss; ) 268 for (i = 0; i < ss; )
198 { 269 {
199 sbuf[i++] = rand (); 270 sbuf[i++] = mt_random (&static_mtrand);
200 } 271 }
201 } 272 }
202 273
@@ -215,7 +286,7 @@ test_make_inputs (xd3_stream *stream, xoff_t *ss_out, xoff_t *ts_out)
215 int do_copy; 286 int do_copy;
216 287
217 next = min (left, next); 288 next = min (left, next);
218 do_copy = (next > add_left || (rand() / (double)RAND_MAX) >= add_prob); 289 do_copy = (next > add_left || (mt_random (&static_mtrand) / (double)USIZE_T_MAX) >= add_prob);
219 290
220 if (ss_out == NULL) 291 if (ss_out == NULL)
221 { 292 {
@@ -229,7 +300,7 @@ test_make_inputs (xd3_stream *stream, xoff_t *ss_out, xoff_t *ts_out)
229 if (do_copy) 300 if (do_copy)
230 { 301 {
231 /* Copy */ 302 /* Copy */
232 size_t offset = rand () % ((ss_out == NULL) ? i : (ss - next)); 303 size_t offset = mt_random (&static_mtrand) % ((ss_out == NULL) ? i : (ss - next));
233 /* DP(RINT "[%u] copy %u at %u ", i, next, offset); */ 304 /* DP(RINT "[%u] copy %u at %u ", i, next, offset); */
234 305
235 for (j = 0; j < next; j += 1) 306 for (j = 0; j < next; j += 1)
@@ -246,7 +317,7 @@ test_make_inputs (xd3_stream *stream, xoff_t *ss_out, xoff_t *ts_out)
246 /* DP(RINT "[%u] add %u ", i, next); */ 317 /* DP(RINT "[%u] add %u ", i, next); */
247 for (j = 0; j < next; j += 1) 318 for (j = 0; j < next; j += 1)
248 { 319 {
249 char c = rand (); 320 char c = mt_random (&static_mtrand);
250 /* DP(RINT "%x%x", (c >> 4) & 0xf, c & 0xf); */ 321 /* DP(RINT "%x%x", (c >> 4) & 0xf, c & 0xf); */
251 tbuf[i++] = c; 322 tbuf[i++] = c;
252 } 323 }
@@ -625,7 +696,7 @@ test_address_cache (xd3_stream *stream, int unused)
625 696
626 addrs[0] = 0; 697 addrs[0] = 0;
627 698
628 srand (0x9f73f7fc); 699 mt_init (& static_mtrand, 0x9f73f7fc);
629 700
630 /* First pass: encode addresses */ 701 /* First pass: encode addresses */
631 xd3_init_cache (& stream->acache); 702 xd3_init_cache (& stream->acache);
@@ -637,9 +708,9 @@ test_address_cache (xd3_stream *stream, int unused)
637 usize_t prev_i; 708 usize_t prev_i;
638 usize_t nearby; 709 usize_t nearby;
639 710
640 p = (rand () / (double)RAND_MAX); 711 p = (mt_random (&static_mtrand) / (double)USIZE_T_MAX);
641 prev_i = rand () % offset; 712 prev_i = mt_random (&static_mtrand) % offset;
642 nearby = (rand () % 256) % offset, 1; 713 nearby = (mt_random (&static_mtrand) % 256) % offset, 1;
643 nearby = max (1U, nearby); 714 nearby = max (1U, nearby);
644 715
645 if (p < 0.1) { addr = addrs[offset-nearby]; } 716 if (p < 0.1) { addr = addrs[offset-nearby]; }
@@ -1081,7 +1152,7 @@ sec_dist_func6 (xd3_stream *stream, xd3_output *data)
1081 int i, ret, x; 1152 int i, ret, x;
1082 for (i = 0; i < ALPHABET_SIZE*20; i += 1) 1153 for (i = 0; i < ALPHABET_SIZE*20; i += 1)
1083 { 1154 {
1084 x = rand () % (ALPHABET_SIZE/2); 1155 x = mt_random (&static_mtrand) % (ALPHABET_SIZE/2);
1085 if ((ret = xd3_emit_byte (stream, & data, x))) { return ret; } 1156 if ((ret = xd3_emit_byte (stream, & data, x))) { return ret; }
1086 } 1157 }
1087 return 0; 1158 return 0;
@@ -1094,7 +1165,7 @@ sec_dist_func7 (xd3_stream *stream, xd3_output *data)
1094 int i, ret, x; 1165 int i, ret, x;
1095 for (i = 0; i < ALPHABET_SIZE*20; i += 1) 1166 for (i = 0; i < ALPHABET_SIZE*20; i += 1)
1096 { 1167 {
1097 x = rand () % ALPHABET_SIZE; 1168 x = mt_random (&static_mtrand) % ALPHABET_SIZE;
1098 if ((ret = xd3_emit_byte (stream, & data, x))) { return ret; } 1169 if ((ret = xd3_emit_byte (stream, & data, x))) { return ret; }
1099 } 1170 }
1100 return 0; 1171 return 0;
@@ -1272,7 +1343,7 @@ test_secondary (xd3_stream *stream, const xd3_sec_type *sec, int groups)
1272 DP(RINT "\n..."); 1343 DP(RINT "\n...");
1273 for (test_i = 0; test_i < SIZEOF_ARRAY (sec_dists); test_i += 1) 1344 for (test_i = 0; test_i < SIZEOF_ARRAY (sec_dists); test_i += 1)
1274 { 1345 {
1275 srand (0x84687674); 1346 mt_init (& static_mtrand, 0x9f73f7fc);
1276 1347
1277 in_head = xd3_alloc_output (stream, NULL); 1348 in_head = xd3_alloc_output (stream, NULL);
1278 out_head = xd3_alloc_output (stream, NULL); 1349 out_head = xd3_alloc_output (stream, NULL);
@@ -1631,7 +1702,7 @@ test_command_line_arguments (xd3_stream *stream, int ignore)
1631 xoff_t dsize; 1702 xoff_t dsize;
1632 double ratio; 1703 double ratio;
1633 1704
1634 srand (0x89162337); 1705 mt_init (& static_mtrand, 0x9f73f7fc);
1635 1706
1636 for (i = 0; i < pairs; i += 1) 1707 for (i = 0; i < pairs; i += 1)
1637 { 1708 {
@@ -2019,7 +2090,7 @@ test_externally_compressed_io (xd3_stream *stream, int ignore)
2019 int i, ret; 2090 int i, ret;
2020 char buf[TESTBUFSIZE]; 2091 char buf[TESTBUFSIZE];
2021 2092
2022 srand (0x91723913); 2093 mt_init (& static_mtrand, 0x9f73f7fc);
2023 2094
2024 if ((ret = test_make_inputs (stream, NULL, NULL))) { return ret; } 2095 if ((ret = test_make_inputs (stream, NULL, NULL))) { return ret; }
2025 2096
@@ -2069,7 +2140,7 @@ test_source_decompression (xd3_stream *stream, int ignore)
2069 char buf[TESTBUFSIZE]; 2140 char buf[TESTBUFSIZE];
2070 const main_extcomp *ext; 2141 const main_extcomp *ext;
2071 2142
2072 srand (0x9ff56acb); 2143 mt_init (& static_mtrand, 0x9f73f7fc);
2073 2144
2074 test_setup (); 2145 test_setup ();
2075 if ((ret = test_make_inputs (stream, NULL, NULL))) { return ret; } 2146 if ((ret = test_make_inputs (stream, NULL, NULL))) { return ret; }
@@ -2265,7 +2336,7 @@ test_identical_behavior (xd3_stream *stream, int ignore)
2265 usize_t delpos = 0, recsize; 2336 usize_t delpos = 0, recsize;
2266 xd3_config config; 2337 xd3_config config;
2267 2338
2268 for (i = 0; i < IDB_TGTSZ; i += 1) { buf[i] = rand (); } 2339 for (i = 0; i < IDB_TGTSZ; i += 1) { buf[i] = mt_random (&static_mtrand); }
2269 2340
2270 stream->winsize = IDB_WINSZ; 2341 stream->winsize = IDB_WINSZ;
2271 2342
@@ -2669,18 +2740,29 @@ xd3_selftest (void)
2669 xd3_stream stream; \ 2740 xd3_stream stream; \
2670 xd3_config config; \ 2741 xd3_config config; \
2671 xd3_init_config (& config, flags); \ 2742 xd3_init_config (& config, flags); \
2672 DP(RINT "xdelta3: testing " #fn "%s...", \ 2743 DP(RINT "xdelta3: testing " #fn "%s...", \
2673 flags ? (" (" #flags ")") : ""); \ 2744 flags ? (" (" #flags ")") : ""); \
2674 if ((ret = xd3_config_stream (& stream, & config) == 0) && \ 2745 if ((ret = xd3_config_stream (& stream, & config) == 0) && \
2675 (ret = test_ ## fn (& stream, arg)) == 0) { \ 2746 (ret = test_ ## fn (& stream, arg)) == 0) { \
2676 DP(RINT " success\n"); \ 2747 DP(RINT " success\n"); \
2677 } else { \ 2748 } else { \
2678 DP(RINT " failed: %s: %s\n", xd3_errstring (& stream), \ 2749 DP(RINT " failed: %s: %s\n", xd3_errstring (& stream), \
2679 xd3_mainerror (ret)); } \ 2750 xd3_mainerror (ret)); } \
2680 xd3_free_stream (& stream); \ 2751 xd3_free_stream (& stream); \
2681 if (ret != 0) { goto failure; } \ 2752 if (ret != 0) { goto failure; } \
2682 } while (0) 2753 } while (0)
2683 2754
2755#define DO_TEST2(fn) \
2756 do { \
2757 DP(RINT "xdelta3: testing " #fn "%s..."); \
2758 if ((ret = test_ ## fn ()) == 0) { \
2759 DP(RINT " success\n"); \
2760 } else { \
2761 DP(RINT " failed!\n"); \
2762 } \
2763 if (ret != 0) { goto failure; } \
2764 } while (0)
2765
2684 int ret; 2766 int ret;
2685 2767
2686#ifndef WIN32 2768#ifndef WIN32
@@ -2714,6 +2796,8 @@ xd3_selftest (void)
2714 DO_TEST (iopt_flush_instructions, 0, 0); 2796 DO_TEST (iopt_flush_instructions, 0, 0);
2715 DO_TEST (source_cksum_offset, 0, 0); 2797 DO_TEST (source_cksum_offset, 0, 0);
2716 2798
2799 DO_TEST2 (merge_command);
2800
2717 DO_TEST (decompress_single_bit_error, 0, 3); 2801 DO_TEST (decompress_single_bit_error, 0, 3);
2718 DO_TEST (decompress_single_bit_error, XD3_ADLER32, 3); 2802 DO_TEST (decompress_single_bit_error, XD3_ADLER32, 3);
2719 2803
diff --git a/xdelta3/xdelta3-test2.h b/xdelta3/xdelta3-test2.h
new file mode 100644
index 0000000..d2ce1ef
--- /dev/null
+++ b/xdelta3/xdelta3-test2.h
@@ -0,0 +1,188 @@
1/* xdelta 3 - delta compression tools and library
2 * Copyright (C) 2001, 2003, 2004, 2005, 2006, 2007. Joshua P. MacDonald
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19/* This file was started because xdelta3-test.h is large enough as-is,
20 * and has lots of old cruft. This is the shiny new test I always wanted. */
21
22static const int TESTS_PER_PARAMETER = 100;
23
24struct random_file_spec_ {
25 int size;
26 char *tmp_copy;
27 char *tmp_delta;
28 mtrand mt;
29};
30
31struct random_parameters_ {
32 int file_size;
33 int window_size;
34 int add_size;
35 int del_size;
36};
37
38typedef struct random_file_spec_ random_file_spec;
39typedef struct random_parameters_ random_parameters;
40
41static const random_parameters test_parameters[] = {
42 { 16384, 4096, 128, 0 },
43 { 16384, 4096, 0, 128 },
44 { 16384, 4096, 128, 128 },
45 { 16384, 4096, 128, 128 },
46};
47
48static random_parameters current_parameters;
49static int test2_malloc_count;
50
51void
52set_test_parameters (const random_parameters *params)
53{
54 current_parameters = *params;
55}
56
57void
58set_random_parameters ()
59{
60 // TODO(jmacd)
61 current_parameters = test_parameters[0];
62}
63
64void
65test2_free (void *ptr)
66{
67 test2_malloc_count--;
68 free (ptr);
69 XD3_ASSERT (test2_malloc_count >= 0);
70}
71
72void
73random_file_spec_clear (random_file_spec *spec)
74{
75 if (spec->tmp_copy)
76 {
77 unlink (spec->tmp_copy); /* TODO(jmacd): make portable */
78 test2_free (spec->tmp_copy);
79 }
80
81 if (spec->tmp_delta)
82 {
83 unlink (spec->tmp_delta);
84 test2_free (spec->tmp_delta);
85 }
86}
87
88void
89random_file_spec_swap (random_file_spec *a,
90 random_file_spec *b)
91{
92 random_file_spec t = *a;
93 *a = *b;
94 *b = t;
95}
96
97int
98random_file_spec_generate (random_file_spec *spec)
99{
100 return 0;
101}
102
103int
104random_file_spec_write (random_file_spec *spec)
105{
106 return 0;
107}
108
109int
110random_file_spec_mutate (random_file_spec *from, random_file_spec *to)
111{
112 return 0;
113}
114
115int
116random_file_spec_delta (random_file_spec *from, random_file_spec *to)
117{
118 return 0;
119}
120
121int
122test_merge_chain (random_file_spec *specs, int number)
123{
124 /* "number" is from 1 (a single delta) between specs[0] and
125 * specs[1], to N, an (N-1) chain from specs[0] to specs[N]. */
126 return 0;
127}
128
129static int
130test_merge_command ()
131{
132 /* Repeat random-input testing for a number of iterations.
133 * Test 2, 3, and 4-file scenarios (i.e., 1, 2, and 3-delta merges). */
134 int ret;
135 int iter = 0, param = 0;
136 random_file_spec spec[4];
137
138 memset (spec, 0, sizeof (spec));
139
140 /* Repeat this loop for TESTS_PER_PARAMETER * #parameters * 2. The
141 * first #parameters repeats are for the provided values, the second
142 * set of repeats use random parameters. */
143 for (; param < (2 * SIZEOF_ARRAY(test_parameters)); iter++)
144 {
145 if (iter % TESTS_PER_PARAMETER == 0)
146 {
147 if (param < SIZEOF_ARRAY(test_parameters))
148 {
149 set_test_parameters (&test_parameters[param]);
150 }
151 else
152 {
153 set_random_parameters ();
154 }
155
156 param++;
157
158 if ((ret = random_file_spec_generate (&spec[0]))) { return ret; }
159 if ((ret = random_file_spec_write (&spec[0]))) { return ret; }
160
161 if ((ret = random_file_spec_mutate (&spec[0], &spec[1]))) { return ret; }
162 if ((ret = random_file_spec_write (&spec[1]))) { return ret; }
163 if ((ret = random_file_spec_delta (&spec[0], &spec[1]))) { return ret; }
164
165 if ((ret = random_file_spec_mutate (&spec[1], &spec[2]))) { return ret; }
166 if ((ret = random_file_spec_write (&spec[2]))) { return ret; }
167 if ((ret = random_file_spec_delta (&spec[1], &spec[2]))) { return ret; }
168 }
169
170 /* Each iteration creates a new mutation. */
171 if ((ret = random_file_spec_mutate (&spec[2], &spec[3]))) { return ret; }
172 if ((ret = random_file_spec_write (&spec[3]))) { return ret; }
173 if ((ret = random_file_spec_delta (&spec[2], &spec[3]))) { return ret; }
174
175 /* Test 1, 2, and 3 */
176 if ((ret = test_merge_chain (spec, 1))) { return ret; }
177 if ((ret = test_merge_chain (spec, 2))) { return ret; }
178 if ((ret = test_merge_chain (spec, 3))) { return ret; }
179
180 /* Clear 1st input, shift inputs */
181 random_file_spec_clear (&spec[0]);
182 random_file_spec_swap (&spec[0], &spec[1]);
183 random_file_spec_swap (&spec[1], &spec[2]);
184 random_file_spec_swap (&spec[2], &spec[3]);
185 }
186
187 return 0;
188}