root/xdelta30r/xdelta3-test.h

Revision 31, 69.7 kB (checked in by nlawren2, 1 year ago)

Moved xdelta30r

  • Property svn:executable set to *
Line 
1 /* xdelta 3 - delta compression tools and library
2  * Copyright (C) 2001, 2003, 2004, 2005, 2006.  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 #include <math.h>
20
21 #ifndef WIN32
22 #include <sys/wait.h>
23 #endif
24
25 #define MSG_IS(x) (stream->msg != NULL && strcmp ((x), stream->msg) == 0)
26
27 static const usize_t TWO_MEGS_AND_DELTA = (2 << 20) + (1 << 10);
28 static const usize_t ADDR_CACHE_ROUNDS = 10000;
29
30 static const usize_t TEST_FILE_MEAN    = 16384;
31 static const double TEST_ADD_MEAN     = 16;
32 static const double TEST_ADD_MAX      = 256;
33 static const double TEST_ADD_RATIO    = 0.1;
34 static const double TEST_EPSILON      = 0.55;
35
36 #define TESTBUFSIZE (1024 * 16)
37
38 #define TESTFILESIZE (1024)
39
40 static char   TEST_TARGET_FILE[TESTFILESIZE];
41 static char   TEST_SOURCE_FILE[TESTFILESIZE];
42 static char   TEST_DELTA_FILE[TESTFILESIZE];
43 static char   TEST_RECON_FILE[TESTFILESIZE];
44 static char   TEST_RECON2_FILE[TESTFILESIZE];
45 static char   TEST_COPY_FILE[TESTFILESIZE];
46 static char   TEST_NOPERM_FILE[TESTFILESIZE];
47
48 static int test_exponential_dist (usize_t mean, usize_t max);
49
50 #define CHECK(cond) if (!(cond)) { DP(RINT "check failure: " #cond); abort(); }
51
52 /* Use a fixed soft config so that test values are fixed.  See also test_compress_text(). */
53 static const char* test_softcfg_str = "-C64,64,4,128,16,8,128";
54
55 /******************************************************************************************
56  TEST HELPERS
57  ******************************************************************************************/
58
59 static void DOT (void) { DP(RINT "."); }
60 static int do_cmd (xd3_stream *stream, const char *buf)
61 {
62   int ret;
63   if ((ret = system (buf)) != 0)
64     {
65       if (WIFEXITED (ret))
66         {
67           stream->msg = "command exited non-zero";
68         }
69       else
70         {
71           stream->msg = "abnormal command termination";
72         }
73       return XD3_INTERNAL;
74     }
75   DOT ();
76   return 0;
77 }
78 static int do_fail (xd3_stream *stream, const char *buf)
79 {
80   int ret;
81   ret = system (buf);
82   if (! WIFEXITED (ret) || WEXITSTATUS (ret) != 1)
83     {
84       stream->msg = "command should have not succeeded";
85       DP(RINT "command was %s", buf);
86       return XD3_INTERNAL;
87     }
88   DOT ();
89   return 0;
90 }
91
92 static int
93 test_exponential_dist (usize_t mean, usize_t max)
94 {
95   double mean_d = mean;
96   double erand  = log (1.0 / (rand () / (double)RAND_MAX));
97   usize_t x = (usize_t) (mean_d * erand + 0.5);
98
99   return min (x, max);
100 }
101
102 /* Test that the exponential distribution actually produces its mean. */
103 static int
104 test_random_numbers (xd3_stream *stream, int ignore)
105 {
106   int i;
107   usize_t sum = 0;
108   usize_t mean = 50;
109   usize_t n_rounds = 10000;
110   double average, error;
111   double allowed_error = 1.0;
112
113   for (i = 0; i < n_rounds; i += 1)
114     {
115       sum += test_exponential_dist (mean, USIZE_T_MAX);
116     }
117
118   average = (double) sum / (double) n_rounds;
119   error   = average - (double) mean;
120
121   if (error < allowed_error && error > -allowed_error)
122     {
123       /*DP(RINT "error is %f\n", error);*/
124       return 0;
125     }
126
127   stream->msg = "random distribution looks broken";
128   return XD3_INTERNAL;
129 }
130
131 static int
132 test_setup (void)
133 {
134   static int x = 0;
135   x++;
136   //DP(RINT "test setup: %d", x);
137   sprintf (TEST_TARGET_FILE, "/tmp/xdtest.target.%d", x);
138   sprintf (TEST_SOURCE_FILE, "/tmp/xdtest.source.%d", x);
139   sprintf (TEST_DELTA_FILE, "/tmp/xdtest.delta.%d", x);
140   sprintf (TEST_RECON_FILE, "/tmp/xdtest.recon.%d", x);
141   sprintf (TEST_RECON2_FILE, "/tmp/xdtest.recon2.%d", x);
142   sprintf (TEST_COPY_FILE, "/tmp/xdtest.copy.%d", x);
143   sprintf (TEST_NOPERM_FILE, "/tmp/xdtest.noperm.%d", x);
144   return 0;
145 }
146
147 static void
148 test_unlink (char* file)
149 {
150   char buf[TESTBUFSIZE];
151   while (unlink (file) != 0)
152     {
153       if (errno == ENOENT)
154             {
155               break;
156             }
157       sprintf (buf, "rm -f %s", file);
158       system (buf);
159     }
160 }
161
162 static void
163 test_cleanup (void)
164 {
165   static int x = 0;
166   x++;
167   //DP(RINT "test cleanup: %d", x); 
168   test_unlink (TEST_TARGET_FILE);
169   test_unlink (TEST_SOURCE_FILE);
170   test_unlink (TEST_DELTA_FILE);
171   test_unlink (TEST_RECON_FILE);
172   test_unlink (TEST_RECON2_FILE);
173   test_unlink (TEST_COPY_FILE);
174   test_unlink (TEST_NOPERM_FILE);
175 }
176
177 static int
178 test_make_inputs (xd3_stream *stream, xoff_t *ss_out, xoff_t *ts_out)
179 {
180   usize_t ts = (rand () % TEST_FILE_MEAN) + TEST_FILE_MEAN;
181   usize_t ss = (rand () % TEST_FILE_MEAN) + TEST_FILE_MEAN;
182   uint8_t *buf = malloc (ts + ss), *sbuf = buf /*, *tbuf = buf + ss*/;
183   usize_t sadd = 0, sadd_max = ss * TEST_ADD_RATIO;
184   FILE  *tf /*, *sf*/;
185   usize_t i, j;
186   int ret;
187
188   if (buf == NULL) { return ENOMEM; }
189
190   if ((tf = fopen (TEST_TARGET_FILE, "w")) == NULL)
191     {
192       stream->msg = "write failed";
193       ret = get_errno ();
194       goto failure;
195     }
196
197   /* Then modify the data to produce copies, everything not copied is an add.  The
198    * following logic produces the TEST_ADD_RATIO.  The variable SADD contains the number
199    * of adds so far, which should not exceed SADD_MAX. */
200   for (i = 0; i < ss; )
201     {
202       usize_t left = ss - i;
203       usize_t next = test_exponential_dist (TEST_ADD_MEAN, TEST_ADD_MAX);
204       usize_t add_left = sadd_max - sadd;
205       double add_prob = (left == 0) ? 0 : (add_left / left);
206
207       next = min (left, next);
208
209       if (i > 0 && (next > add_left || (rand() / (double)RAND_MAX) >= add_prob))
210         {
211           /* Copy */
212           usize_t offset = rand () % i;
213
214           for (j = 0; j < next; j += 1)
215             {
216               sbuf[i++] = sbuf[offset + j];
217             }
218         }
219       else
220         {
221           /* Add */
222           for (j = 0; j < next; j += 1)
223             {
224               sbuf[i++] = rand ();
225             }
226         }
227     }
228
229   if ((fwrite (sbuf, 1, ss, tf) != ss))
230     {
231       stream->msg = "write failed";
232       ret = get_errno ();
233       goto failure;
234     }
235
236   if ((ret = fclose (tf)) /* || (ret = fclose (sf))*/)
237     {
238       stream->msg = "close failed";
239       ret = get_errno ();
240       goto failure;
241     }
242
243   if (ts_out) { (*ts_out) = ts; }
244   if (ss_out) { (*ss_out) = ss; }
245
246  failure:
247   free (buf);
248   return ret;
249 }
250
251 static int
252 compare_files (xd3_stream *stream, const char* tgt, const char *rec)
253 {
254   FILE *orig, *recons;
255   static uint8_t obuf[TESTBUFSIZE], rbuf[TESTBUFSIZE];
256   int offset = 0;
257   int i;
258   int oc, rc;
259
260   if ((orig = fopen (tgt, "r")) == NULL)
261     {
262       DP(RINT "open %s failed", tgt);
263       stream->msg = "open failed";
264       return get_errno ();
265     }
266
267     if ((recons = fopen (rec, "r")) == NULL)
268       {
269         DP(RINT "open %s failed", rec);
270         stream->msg = "open failed";
271         return get_errno ();
272       }
273
274   for (;;)
275     {
276       oc = fread (obuf, 1, TESTBUFSIZE, orig);
277       rc = fread (rbuf, 1, TESTBUFSIZE, recons);
278
279       if (oc < 0 || rc < 0)
280         {
281           stream->msg = "read failed";
282           return get_errno ();
283         }
284
285         if (oc != rc)
286           {
287             stream->msg = "compare files: different length";
288             return XD3_INTERNAL;
289           }
290
291         if (oc == 0)
292           {
293             break;
294           }
295
296         for (i = 0; i < oc; i += 1)
297           {
298             if (obuf[i] != rbuf[i])
299               {
300                 stream->msg = "compare files: different values";
301                 return XD3_INTERNAL;
302               }
303           }
304
305         offset += oc;
306     }
307
308     fclose (orig);
309     fclose (recons);
310     return 0;
311 }
312
313 static int
314 test_save_copy (const char *origname)
315 {
316   char buf[TESTBUFSIZE];
317   int ret;
318
319   sprintf (buf, "cp -f %s %s", origname, TEST_COPY_FILE);
320
321   if ((ret = system (buf)) != 0)
322     {
323       return XD3_INTERNAL;
324     }
325
326   return 0;
327 }
328
329 static int
330 test_file_size (const char* file, xoff_t *size)
331 {
332   struct stat sbuf;
333   int ret;
334   (*size) = 0;
335
336   if (stat (file, & sbuf) < 0)
337     {
338       ret = get_errno ();
339       DP(RINT "xdelta3: stat failed: %s: %s\n", file, strerror (ret));
340       return ret;
341     }
342
343   if (! S_ISREG (sbuf.st_mode))
344     {
345       ret = XD3_INTERNAL;
346       DP(RINT "xdelta3: not a regular file: %s: %s\n", file, strerror (ret));
347       return ret;
348     }
349
350   (*size) = sbuf.st_size;
351   return 0;
352 }
353
354 /******************************************************************************************
355  READ OFFSET
356  ******************************************************************************************/
357
358 /* Common test for read_integer errors: encodes a 64-bit value and then attempts to read
359  * as a 32-bit value.  If TRUNC is non-zero, attempts to get errors by shortening the
360  * input, otherwise it should overflow.  Expects XD3_INTERNAL and MSG. */
361 static int
362 test_read_integer_error (xd3_stream *stream, int trunto, const char *msg)
363 {
364   uint64_t eval = 1ULL << 34;
365   uint32_t rval;
366   xd3_output *buf = NULL;
367   const uint8_t *max;
368   const uint8_t *inp;
369   int ret;
370
371   buf = xd3_alloc_output (stream, buf);
372
373   if ((ret = xd3_emit_uint64_t (stream, & buf, eval)))
374     {
375       goto fail;
376     }
377
378  again:
379
380   inp = buf->base;
381   max = buf->base + buf->next - trunto;
382
383   if ((ret = xd3_read_uint32_t (stream, & inp, max, & rval)) != XD3_INVALID_INPUT ||
384       !MSG_IS (msg))
385     {
386       ret = XD3_INTERNAL;
387     }
388   else if (trunto && trunto < buf->next)
389     {
390       trunto += 1;
391       goto again;
392     }
393   else
394     {
395       ret = 0;
396     }
397
398  fail:
399   xd3_free_output (stream, buf);
400   return ret;
401 }
402
403 /* Test integer overflow using the above routine. */
404 static int
405 test_decode_integer_overflow (xd3_stream *stream, int unused)
406 {
407   return test_read_integer_error (stream, 0, "overflow in read_intger");
408 }
409
410 /* Test integer EOI using the above routine. */
411 static int
412 test_decode_integer_end_of_input (xd3_stream *stream, int unused)
413 {
414   return test_read_integer_error (stream, 1, "end-of-input in read_integer");
415 }
416
417 /* Test that emit_integer/decode_integer/sizeof_integer/read_integer work on correct
418  * inputs.  Tests powers of (2^7), plus or minus, up to the maximum value. */
419 #define TEST_ENCODE_DECODE_INTEGER(TYPE,ONE,MAX)                                \
420   xd3_output *rbuf = NULL;                                                      \
421   xd3_output *dbuf = NULL;                                                      \
422   TYPE values[64];                                                              \
423   int nvalues = 0;                                                              \
424   int i, ret = 0;                                                               \
425                                                                                 \
426   for (i = 0; i < (sizeof (TYPE) * 8); i += 7)                                  \
427     {                                                                           \
428       values[nvalues++] = (ONE << i) - ONE;                                     \
429       values[nvalues++] = (ONE << i);                                           \
430       values[nvalues++] = (ONE << i) + ONE;                                     \
431     }                                                                           \
432                                                                                 \
433   values[nvalues++] = MAX-ONE;                                                  \
434   values[nvalues++] = MAX;                                                      \
435                                                                                 \
436   rbuf = xd3_alloc_output (stream, rbuf);                                       \
437   dbuf = xd3_alloc_output (stream, dbuf);                                       \
438                                                                                 \
439   for (i = 0; i < nvalues; i += 1)                                              \
440     {                                                                           \
441       const uint8_t *max;                                                       \
442       const uint8_t *inp;                                                       \
443       TYPE val;                                                                 \
444                                                                                 \
445       DOT ();                                                                   \
446       rbuf->next = 0;                                                           \
447                                                                                 \
448       if ((ret = xd3_emit_ ## TYPE (stream, & rbuf, values[i])) ||              \
449           (ret = xd3_emit_ ## TYPE (stream, & dbuf, values[i])))                \
450         {                                                                       \
451           goto fail;                                                            \
452         }                                                                       \
453                                                                                 \
454       inp = rbuf->base;                                                         \
455       max = rbuf->base + rbuf->next;                                            \
456                                                                                 \
457       if (rbuf->next != xd3_sizeof_ ## TYPE (values[i]))                        \
458         {                                                                       \
459           ret = XD3_INTERNAL;                                                         \
460           goto fail;                                                            \
461         }                                                                       \
462                                                                                 \
463       if ((ret = xd3_read_ ## TYPE (stream, & inp, max, & val)))                \
464         {                                                                       \
465           goto fail;                                                            \
466         }                                                                       \
467                                                                                 \
468       if (val != values[i])                                                     \
469         {                                                                       \
470           ret = XD3_INTERNAL;                                                         \
471           goto fail;                                                            \
472         }                                                                       \
473                                                                                 \
474       DOT ();                                                                   \
475     }                                                                           \
476                                                                                 \
477   stream->next_in  = dbuf->base;                                                \
478   stream->avail_in = dbuf->next;                                                \
479                                                                                 \
480   for (i = 0; i < nvalues; i += 1)                                              \
481     {                                                                           \
482       TYPE val;                                                                 \
483                                                                                 \
484       if ((ret = xd3_decode_ ## TYPE (stream, & val)))                          \
485         {                                                                       \
486           goto fail;                                                            \
487         }                                                                       \
488                                                                                 \
489       if (val != values[i])                                                     \
490         {                                                                       \
491           ret = XD3_INTERNAL;                                                         \
492           goto fail;                                                            \
493         }                                                                       \
494     }                                                                           \
495                                                                                 \
496   if (stream->avail_in != 0)                                                    \
497     {                                                                           \
498       ret = XD3_INTERNAL;                                                             \
499       goto fail;                                                                \
500     }                                                                           \
501                                                                                 \
502  fail:                                                                          \
503   xd3_free_output (stream, rbuf);                                               \
504   xd3_free_output (stream, dbuf);                                               \
505                                                                                 \
506   return ret
507
508 static int
509 test_encode_decode_uint32_t (xd3_stream *stream, int unused)
510 {
511   TEST_ENCODE_DECODE_INTEGER(uint32_t,1U,UINT32_MAX);
512 }
513
514 static int
515 test_encode_decode_uint64_t (xd3_stream *stream, int unused)
516 {
517   TEST_ENCODE_DECODE_INTEGER(uint64_t,1ULL,UINT64_MAX);
518 }
519
520 static int
521 test_usize_t_overflow (xd3_stream *stream, int unused)
522 {
523   if (USIZE_T_OVERFLOW (0, 0)) { goto fail; }
524   if (USIZE_T_OVERFLOW (USIZE_T_MAX, 0)) { goto fail; }
525   if (USIZE_T_OVERFLOW (0, USIZE_T_MAX)) { goto fail; }
526   if (USIZE_T_OVERFLOW (USIZE_T_MAX / 2, 0)) { goto fail; }
527   if (USIZE_T_OVERFLOW (