summaryrefslogtreecommitdiff
path: root/xdelta1/test/xdeltatest.c
diff options
context:
space:
mode:
Diffstat (limited to 'xdelta1/test/xdeltatest.c')
-rw-r--r--xdelta1/test/xdeltatest.c681
1 files changed, 0 insertions, 681 deletions
diff --git a/xdelta1/test/xdeltatest.c b/xdelta1/test/xdeltatest.c
deleted file mode 100644
index 773e26b..0000000
--- a/xdelta1/test/xdeltatest.c
+++ /dev/null
@@ -1,681 +0,0 @@
1/* -*- Mode: C;-*-
2 *
3 * This file is part of XDelta - A binary delta generator.
4 *
5 * Copyright (C) 1997, 1998, 2001 Josh MacDonald
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 *
21 * Author: Josh MacDonald <jmacd@CS.Berkeley.EDU>
22 *
23 * $Id: xdeltatest.c 1.7.1.1 Sat, 27 Jan 2007 17:53:47 -0800 jmacd $
24 */
25
26#include <sys/param.h>
27#include <sys/types.h>
28#include <unistd.h>
29#include "config.h"
30
31#if TIME_WITH_SYS_TIME
32# include <sys/time.h>
33# include <time.h>
34#else
35# if HAVE_SYS_TIME_H
36# include <sys/time.h>
37# else
38# include <time.h>
39# endif
40#endif
41
42#include <fcntl.h>
43#include <sys/stat.h>
44#include <sys/wait.h>
45#include <sys/resource.h>
46#include <stdio.h>
47#include <zlib.h>
48
49#include "xdelta.h"
50
51//////////////////////////////////////////////////////////////////////
52// Configure these parameters
53// @@@ Of course, a real test wouldn't require this configuration,
54// fix that!
55//////////////////////////////////////////////////////////////////////
56
57const char* cmd_data_source = "/tmp/big.1";
58guint cmd_size = 1<<20;
59
60guint cmd_warmups = 2;
61guint cmd_reps = 20;
62
63guint cmd_changes = 1000;
64guint cmd_deletion_length = 30;
65guint cmd_insertion_length = 15;
66
67//////////////////////////////////////////////////////////////////////
68//
69//////////////////////////////////////////////////////////////////////
70
71#define ARRAY_LENGTH(x) (sizeof(x)/sizeof(x[0]))
72
73#define TEST_PREFIX "/tmp/xdeltatest"
74
75#define TEST_IS_GZIP 1
76#define TEST_IS_XDELTA 2
77
78typedef struct _File File;
79typedef struct _Patch Patch;
80typedef struct _TestProfile TestProfile;
81typedef struct _Instruction Instruction;
82
83struct _Instruction {
84 guint32 offset;
85 guint32 length;
86 Instruction* next;
87};
88
89struct _File
90{
91 char name[MAXPATHLEN];
92};
93
94struct _Patch
95{
96 guint8 *data;
97 guint32 length;
98};
99
100struct _TestProfile
101{
102 const char *name;
103 const char *progname;
104 int flags;
105};
106
107#undef BUFSIZ
108#define BUFSIZ (1<<24)
109
110guint8 __tmp_buffer[BUFSIZ];
111
112// Note: Of course you should be able to set all this up on the
113// command line.
114
115TestProfile cmd_profiles[] =
116{
117 { "xdelta -qn ", "../xdelta", TEST_IS_XDELTA },
118 { "diff --rcs -a", "/usr/bin/diff", 0 },
119 { "gzip ", "/bin/gzip", TEST_IS_GZIP },
120};
121
122int cmd_slevels[] = { 16, 32, 64 };
123int cmd_zlevels[] = { 0, 1, 3, 6, 9 };
124guint16 cmd_seed[3] = { 47384, 8594, 27489 };
125
126FILE* data_source_handle;
127guint data_source_length;
128File* data_source_file;
129
130long current_to_size;
131long current_from_size;
132
133double __total_time;
134off_t __dsize;
135
136void
137reset_stats ()
138{
139 __total_time = 0;
140 __dsize = -1;
141}
142
143void add_tv (GTimer *timer)
144{
145 __total_time += g_timer_elapsed (timer, NULL);
146}
147
148void
149report (TestProfile *tp, int zlevel, int slevel)
150{
151 static gboolean once = TRUE;
152
153 double t = __total_time / (double) cmd_reps;
154 double s;
155 const char *u;
156 char slevel_str[16];
157
158 if (tp->flags & TEST_IS_XDELTA)
159 {
160 sprintf (slevel_str, "%d", slevel);
161 }
162 else
163 {
164 slevel_str[0] = 0;
165 }
166
167 if (tp->flags & TEST_IS_GZIP)
168 {
169 s = __dsize / (double) current_to_size;
170 u = "total size";
171 }
172 else
173 {
174 s = __dsize / (double) (cmd_changes * cmd_insertion_length);
175 u = "insertion size";
176 }
177
178 if (once)
179 {
180 once = FALSE;
181
182 g_print ("Program\t\tzlevel\tslevel\tSeconds\tNormalized\n\t\t\t\t\tcompression\n");
183 }
184
185 g_print ("%s\t%d\t%s\t%0.3f\t%0.3f (of %s)\n", tp->name, zlevel, slevel_str, t, s, u);
186}
187
188guint32
189random_offset (guint len)
190{
191 return lrand48 () % (data_source_length - len);
192}
193
194void
195fail ()
196{
197 g_warning ("FAILURE\n");
198 abort ();
199}
200
201gboolean starts_with (const char* s, const char *start)
202{
203 return strncmp (s, start, strlen (start)) == 0;
204}
205
206Patch*
207read_patch (File *file, struct stat *sbuf)
208{
209 Patch *p = g_new0 (Patch,1);
210 FILE *f = fopen (file->name, "r");
211
212 p->length = (int)sbuf->st_size;
213
214 p->data = g_malloc (p->length);
215
216 if (! f || fread (p->data, 1, p->length, f) != p->length)
217 {
218 perror ("fread");
219 fail ();
220 }
221
222 fclose (f);
223
224 return p;
225}
226
227File*
228empty_file ()
229{
230 static gint count = 0;
231 File *file = g_new0 (File, 1);
232
233 sprintf (file->name, "%s.%d.%d", TEST_PREFIX, getpid (), count++);
234
235 return file;
236}
237
238void
239compare_files (File* fromfile, File* tofile)
240{
241 gint pos = 0;
242
243 FILE* toh;
244 FILE* fromh;
245
246 guint8 buf1[1024], buf2[1024];
247
248 toh = fopen (tofile->name, "r");
249 fromh = fopen (fromfile->name, "r");
250
251 if (!toh || !fromh)
252 fail ();
253
254 for (;;)
255 {
256 gint readf = fread (buf1, 1, 1024, fromh);
257 gint readt = fread (buf2, 1, 1024, toh);
258 gint i, m = MIN(readf, readt);
259
260 if (readf < 0 || readt < 0)
261 fail ();
262
263 for (i = 0; i < m; i += 1, pos += 1)
264 {
265 if (buf1[i] != buf2[i])
266 fail ();
267 }
268
269 if (m != 1024)
270 {
271 if (readt == readf)
272 {
273 fclose (toh);
274 fclose (fromh);
275 return;
276 }
277
278 fail ();
279 }
280 }
281}
282
283int
284write_file (File* file, Instruction* inst)
285{
286 FILE* h;
287 int ret;
288 int size = 0;
289
290 if (! (h = fopen (file->name, "w"))) {
291 perror (file->name);
292 fail ();
293 }
294
295 for (; inst; inst = inst->next)
296 {
297 g_assert (inst->length <= BUFSIZ);
298
299 if ((ret = fseek (data_source_handle, inst->offset, SEEK_SET))) {
300 perror ("fseek");
301 fail ();
302 }
303
304 if ((ret = fread (__tmp_buffer, 1, inst->length, data_source_handle)) != inst->length) {
305 perror ("fread");
306 fail ();
307 }
308
309 if ((ret = fwrite (__tmp_buffer, 1, inst->length, h)) != inst->length) {
310 perror ("fwrite");
311 fail ();
312 }
313
314 size += inst->length;
315 }
316
317 if ((ret = fclose (h))) {
318 perror ("fclose");
319 fail ();
320 }
321
322 return size;
323}
324
325Instruction*
326perform_change_rec (Instruction* inst, guint32 change_off, guint* total_len)
327{
328 if (change_off < inst->length)
329 {
330 guint32 to_delete = cmd_deletion_length;
331 guint32 avail = inst->length;
332 guint32 this_delete = MIN (to_delete, avail);
333 Instruction* new_inst;
334
335 // One delete
336 inst->length -= this_delete;
337 to_delete -= this_delete;
338
339 while (to_delete > 0 && inst->next->length < to_delete)
340 {
341 to_delete -= inst->next->length;
342 inst->next = inst->next->next;
343 }
344
345 if (to_delete > 0)
346 inst->next->offset += to_delete;
347
348 // One insert
349 new_inst = g_new0 (Instruction, 1);
350
351 new_inst->offset = random_offset (cmd_insertion_length);
352 new_inst->length = cmd_insertion_length;
353 new_inst->next = inst->next;
354 inst->next = new_inst;
355
356 (* total_len) += cmd_insertion_length - cmd_deletion_length;
357
358 return inst;
359 }
360 else
361 {
362 inst->next = perform_change_rec (inst->next, change_off - inst->length, total_len);
363 return inst;
364 }
365}
366
367Instruction*
368perform_change (Instruction* inst, guint* len)
369{
370 return perform_change_rec (inst, lrand48() % ((* len) - cmd_deletion_length), len);
371}
372
373gboolean
374xdelta_verify (TestProfile *tp, int zlevel, int slevel, File* from, File* to, File* out, File *re)
375{
376 int pid, status;
377
378 if ((pid = vfork()) < 0)
379 return FALSE;
380
381 if (pid == 0)
382 {
383 execl (tp->progname,
384 tp->progname,
385 "patch",
386 out->name,
387 from->name,
388 re->name,
389 NULL);
390 perror ("execl failed");
391 _exit (127);
392 }
393
394 if (waitpid (pid, &status, 0) != pid)
395 {
396 perror ("wait failed");
397 fail ();
398 }
399
400 // Note: program is expected to return 0, 1 meaning diff or no diff,
401 // > 1 indicates failure
402 if (! WIFEXITED (status) || WEXITSTATUS (status) > 1)
403 {
404 g_warning ("patch command failed\n");
405 fail ();
406 }
407
408 compare_files (to, re);
409
410 return TRUE;
411}
412
413gboolean
414run_command (TestProfile *tp, int zlevel, int slevel, File* from, File* to, File* out, File *re, gboolean accounting)
415{
416 int pid, status, outfd;
417 struct stat sbuf;
418 char xdelta_args[16];
419 char xdelta_args2[16];
420 char diff_gzargs[16];
421 char gzip_args[16];
422
423 GTimer *timer = g_timer_new ();
424
425 sprintf (xdelta_args, "-qn%d", zlevel);
426 sprintf (xdelta_args2, "-s%d", slevel);
427 sprintf (diff_gzargs, "wb%d", zlevel);
428 sprintf (gzip_args, "-c%d", zlevel);
429
430 unlink (out->name);
431
432 g_timer_start (timer);
433
434 if ((pid = vfork()) < 0)
435 return FALSE;
436
437 if (pid == 0)
438 {
439 if (starts_with (tp->name, "xdelta"))
440 {
441 execl (tp->progname,
442 tp->progname,
443 "delta",
444 xdelta_args,
445 xdelta_args2,
446 from->name,
447 to->name,
448 out->name,
449 NULL);
450 }
451 else
452 {
453 outfd = open (out->name, O_CREAT | O_TRUNC | O_WRONLY, 0777);
454
455 if (outfd < 0)
456 {
457 perror ("open");
458 fail ();
459 }
460
461 dup2(outfd, STDOUT_FILENO);
462
463 if (close (outfd))
464 {
465 perror ("close");
466 fail ();
467 }
468
469 if (starts_with (tp->name, "diff"))
470 execl (tp->progname,
471 tp->progname,
472 "--rcs",
473 "-a",
474 from->name,
475 to->name,
476 NULL);
477 else if (starts_with (tp->name, "gzip"))
478 execl (tp->progname,
479 tp->progname,
480 gzip_args,
481 to->name,
482 NULL);
483 else
484 abort ();
485 }
486
487 perror ("execl failed");
488 _exit (127);
489 }
490
491 if (waitpid (pid, &status, 0) != pid)
492 {
493 perror ("wait failed");
494 fail ();
495 }
496
497 // Note: program is expected to return 0, 1 meaning diff or no diff,
498 // > 1 indicates failure
499 if (! WIFEXITED (status) || WEXITSTATUS (status) > 1)
500 {
501 g_warning ("delta command failed\n");
502 fail ();
503 }
504
505 if (stat (out->name, & sbuf))
506 {
507 perror ("stat");
508 fail ();
509 }
510
511 // Do zlib compression on behalf of diff
512 if (zlevel > 0 && starts_with (tp->name, "diff"))
513 {
514 Patch *patch = read_patch (out, & sbuf);
515 gzFile *rewrite = gzopen (out->name, diff_gzargs);
516
517 if (! rewrite) fail ();
518
519 if (gzwrite (rewrite, patch->data, patch->length) == 0) { perror ("gzwrite"); fail (); }
520 if (gzclose (rewrite) != Z_OK) { perror ("gzclose"); fail (); }
521 if (stat (out->name, & sbuf)) { perror ("stat"); fail (); }
522 }
523
524 g_timer_stop (timer);
525
526 if (accounting)
527 {
528 add_tv (timer);
529 }
530
531 if (__dsize < 0)
532 {
533 __dsize = sbuf.st_size;
534
535 // Verify only once
536
537 if (starts_with (tp->name, "xdelta"))
538 {
539 if (! xdelta_verify (tp, zlevel, slevel, from, to, out, re))
540 {
541 g_warning ("verify failed");
542 fail ();
543 }
544 }
545 }
546 else
547 {
548 if (__dsize != sbuf.st_size)
549 {
550 g_warning ("%s -%d: delta command produced different size deltas: %d and %d",
551 tp->progname, zlevel, (int)__dsize, (int)sbuf.st_size);
552 fail ();
553 }
554 }
555
556 g_timer_destroy (timer);
557
558 return TRUE;
559}
560
561void
562test1 (TestProfile *test_profile,
563 File *from_file,
564 File *to_file,
565 File *out_file,
566 File *re_file)
567{
568 int ret;
569 guint i, change, current_size = cmd_size;
570 guint end_size = (cmd_changes * cmd_insertion_length) + cmd_size;
571 Instruction* inst;
572 struct stat sbuf;
573 int zlevel_i, slevel_i;
574
575 seed48 (cmd_seed);
576
577 if ((ret = stat (cmd_data_source, & sbuf)))
578 {
579 perror (cmd_data_source);
580 fail ();
581 }
582
583 if (! (data_source_handle = fopen (cmd_data_source, "r")))
584 {
585 perror (cmd_data_source);
586 fail ();
587 }
588
589 data_source_length = sbuf.st_size;
590
591 /* arbitrary checks */
592 if (data_source_length < (1.5 * end_size))
593 g_warning ("data source should be longer\n");
594
595 if ((cmd_changes * cmd_deletion_length) > cmd_size)
596 {
597 g_warning ("no copies are expected\n");
598 fail ();
599 }
600
601 inst = g_new0 (Instruction, 1);
602
603 inst->offset = random_offset (cmd_size);
604 inst->length = cmd_size;
605
606 current_from_size = write_file (from_file, inst);
607
608 for (change = 0; change < cmd_changes; change += 1)
609 inst = perform_change (inst, & current_size);
610
611 current_to_size = write_file (to_file, inst);
612
613 for (slevel_i = 0; slevel_i < ARRAY_LENGTH(cmd_slevels); slevel_i += 1)
614 {
615 int slevel = cmd_slevels[slevel_i];
616
617 if ((test_profile->flags & TEST_IS_XDELTA) == 0 && slevel_i != 0)
618 {
619 continue;
620 }
621
622 for (zlevel_i = 0; zlevel_i < ARRAY_LENGTH(cmd_zlevels); zlevel_i += 1)
623 {
624 int zlevel = cmd_zlevels[zlevel_i];
625
626 if (test_profile->flags & TEST_IS_GZIP)
627 {
628 if (zlevel != 1 && zlevel != 9)
629 continue;
630 }
631
632 reset_stats ();
633
634 for (i = 0; i < cmd_warmups + cmd_reps; i += 1)
635 {
636 if (! run_command (test_profile,
637 zlevel,
638 slevel,
639 from_file,
640 to_file,
641 out_file,
642 re_file,
643 (i >= cmd_warmups) /* true => accounting */))
644 {
645 fail ();
646 }
647 }
648
649 report (test_profile, zlevel, slevel);
650 }
651
652 g_print ("\n");
653 }
654}
655
656int
657main (gint argc, gchar** argv)
658{
659 int profile_i;
660
661 File *from_file;
662 File *to_file;
663 File *out_file;
664 File *re_file;
665
666 system ("rm -rf " TEST_PREFIX "*");
667
668 from_file = empty_file ();
669 to_file = empty_file ();
670 out_file = empty_file ();
671 re_file = empty_file ();
672
673 for (profile_i = 0; profile_i < ARRAY_LENGTH(cmd_profiles); profile_i += 1)
674 {
675 test1 (& cmd_profiles[profile_i], from_file, to_file, out_file, re_file);
676
677 system ("rm -rf " TEST_PREFIX "*");
678 }
679
680 return 0;
681}