📄 xdelta3-test.h
字号:
/* xdelta 3 - delta compression tools and library * Copyright (C) 2001, 2003, 2004, 2005, 2006. Joshua P. MacDonald * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <math.h>#ifndef WIN32#include <sys/wait.h>#endif#define MSG_IS(x) (stream->msg != NULL && strcmp ((x), stream->msg) == 0)static const usize_t TWO_MEGS_AND_DELTA = (2 << 20) + (1 << 10);static const usize_t ADDR_CACHE_ROUNDS = 10000;static const usize_t TEST_FILE_MEAN = 16384;static const double TEST_ADD_MEAN = 16;static const double TEST_ADD_MAX = 256;static const double TEST_ADD_RATIO = 0.1;static const double TEST_EPSILON = 0.55;#define TESTBUFSIZE (1024 * 16)#define TESTFILESIZE (1024)static char TEST_TARGET_FILE[TESTFILESIZE];static char TEST_SOURCE_FILE[TESTFILESIZE];static char TEST_DELTA_FILE[TESTFILESIZE];static char TEST_RECON_FILE[TESTFILESIZE];static char TEST_RECON2_FILE[TESTFILESIZE];static char TEST_COPY_FILE[TESTFILESIZE];static char TEST_NOPERM_FILE[TESTFILESIZE];static int test_exponential_dist (usize_t mean, usize_t max);#define CHECK(cond) if (!(cond)) { DP(RINT "check failure: " #cond); abort(); }/* Use a fixed soft config so that test values are fixed. See also test_compress_text(). */static const char* test_softcfg_str = "-C64,64,4,128,16,8,128";/****************************************************************************************** TEST HELPERS ******************************************************************************************/static void DOT (void) { DP(RINT "."); }static int do_cmd (xd3_stream *stream, const char *buf){ int ret; if ((ret = system (buf)) != 0) { if (WIFEXITED (ret)) { stream->msg = "command exited non-zero"; } else { stream->msg = "abnormal command termination"; } return XD3_INTERNAL; } DOT (); return 0;}static int do_fail (xd3_stream *stream, const char *buf){ int ret; ret = system (buf); if (! WIFEXITED (ret) || WEXITSTATUS (ret) != 1) { stream->msg = "command should have not succeeded"; DP(RINT "command was %s", buf); return XD3_INTERNAL; } DOT (); return 0;}static inttest_exponential_dist (usize_t mean, usize_t max){ double mean_d = mean; double erand = log (1.0 / (rand () / (double)RAND_MAX)); usize_t x = (usize_t) (mean_d * erand + 0.5); return min (x, max);}/* Test that the exponential distribution actually produces its mean. */static inttest_random_numbers (xd3_stream *stream, int ignore){ int i; usize_t sum = 0; usize_t mean = 50; usize_t n_rounds = 10000; double average, error; double allowed_error = 1.0; for (i = 0; i < n_rounds; i += 1) { sum += test_exponential_dist (mean, USIZE_T_MAX); } average = (double) sum / (double) n_rounds; error = average - (double) mean; if (error < allowed_error && error > -allowed_error) { /*DP(RINT "error is %f\n", error);*/ return 0; } stream->msg = "random distribution looks broken"; return XD3_INTERNAL;}static inttest_setup (void){ static int x = 0; x++; //DP(RINT "test setup: %d", x); sprintf (TEST_TARGET_FILE, "/tmp/xdtest.target.%d", x); sprintf (TEST_SOURCE_FILE, "/tmp/xdtest.source.%d", x); sprintf (TEST_DELTA_FILE, "/tmp/xdtest.delta.%d", x); sprintf (TEST_RECON_FILE, "/tmp/xdtest.recon.%d", x); sprintf (TEST_RECON2_FILE, "/tmp/xdtest.recon2.%d", x); sprintf (TEST_COPY_FILE, "/tmp/xdtest.copy.%d", x); sprintf (TEST_NOPERM_FILE, "/tmp/xdtest.noperm.%d", x); return 0;}static voidtest_unlink (char* file){ char buf[TESTBUFSIZE]; while (unlink (file) != 0) { if (errno == ENOENT) { break; } sprintf (buf, "rm -f %s", file); system (buf); }}static voidtest_cleanup (void){ static int x = 0; x++; //DP(RINT "test cleanup: %d", x); test_unlink (TEST_TARGET_FILE); test_unlink (TEST_SOURCE_FILE); test_unlink (TEST_DELTA_FILE); test_unlink (TEST_RECON_FILE); test_unlink (TEST_RECON2_FILE); test_unlink (TEST_COPY_FILE); test_unlink (TEST_NOPERM_FILE);}static inttest_make_inputs (xd3_stream *stream, xoff_t *ss_out, xoff_t *ts_out){ usize_t ts = (rand () % TEST_FILE_MEAN) + TEST_FILE_MEAN; usize_t ss = (rand () % TEST_FILE_MEAN) + TEST_FILE_MEAN; uint8_t *buf = malloc (ts + ss), *sbuf = buf /*, *tbuf = buf + ss*/; usize_t sadd = 0, sadd_max = ss * TEST_ADD_RATIO; FILE *tf /*, *sf*/; usize_t i, j; int ret; if (buf == NULL) { return ENOMEM; } if ((tf = fopen (TEST_TARGET_FILE, "w")) == NULL) { stream->msg = "write failed"; ret = get_errno (); goto failure; } /* Then modify the data to produce copies, everything not copied is an add. The * following logic produces the TEST_ADD_RATIO. The variable SADD contains the number * of adds so far, which should not exceed SADD_MAX. */ for (i = 0; i < ss; ) { usize_t left = ss - i; usize_t next = test_exponential_dist (TEST_ADD_MEAN, TEST_ADD_MAX); usize_t add_left = sadd_max - sadd; double add_prob = (left == 0) ? 0 : (add_left / left); next = min (left, next); if (i > 0 && (next > add_left || (rand() / (double)RAND_MAX) >= add_prob)) { /* Copy */ usize_t offset = rand () % i; for (j = 0; j < next; j += 1) { sbuf[i++] = sbuf[offset + j]; } } else { /* Add */ for (j = 0; j < next; j += 1) { sbuf[i++] = rand (); } } } if ((fwrite (sbuf, 1, ss, tf) != ss)) { stream->msg = "write failed"; ret = get_errno (); goto failure; } if ((ret = fclose (tf)) /* || (ret = fclose (sf))*/) { stream->msg = "close failed"; ret = get_errno (); goto failure; } if (ts_out) { (*ts_out) = ts; } if (ss_out) { (*ss_out) = ss; } failure: free (buf); return ret;}static intcompare_files (xd3_stream *stream, const char* tgt, const char *rec){ FILE *orig, *recons; static uint8_t obuf[TESTBUFSIZE], rbuf[TESTBUFSIZE]; int offset = 0; int i; int oc, rc; if ((orig = fopen (tgt, "r")) == NULL) { DP(RINT "open %s failed", tgt); stream->msg = "open failed"; return get_errno (); } if ((recons = fopen (rec, "r")) == NULL) { DP(RINT "open %s failed", rec); stream->msg = "open failed"; return get_errno (); } for (;;) { oc = fread (obuf, 1, TESTBUFSIZE, orig); rc = fread (rbuf, 1, TESTBUFSIZE, recons); if (oc < 0 || rc < 0) { stream->msg = "read failed"; return get_errno (); } if (oc != rc) { stream->msg = "compare files: different length"; return XD3_INTERNAL; } if (oc == 0) { break; } for (i = 0; i < oc; i += 1) { if (obuf[i] != rbuf[i]) { stream->msg = "compare files: different values"; return XD3_INTERNAL; } } offset += oc; } fclose (orig); fclose (recons); return 0;}static inttest_save_copy (const char *origname){ char buf[TESTBUFSIZE]; int ret; sprintf (buf, "cp -f %s %s", origname, TEST_COPY_FILE); if ((ret = system (buf)) != 0) { return XD3_INTERNAL; } return 0;}static inttest_file_size (const char* file, xoff_t *size){ struct stat sbuf; int ret; (*size) = 0; if (stat (file, & sbuf) < 0) { ret = get_errno (); DP(RINT "xdelta3: stat failed: %s: %s\n", file, strerror (ret)); return ret; } if (! S_ISREG (sbuf.st_mode)) { ret = XD3_INTERNAL; DP(RINT "xdelta3: not a regular file: %s: %s\n", file, strerror (ret)); return ret; } (*size) = sbuf.st_size; return 0;}/****************************************************************************************** READ OFFSET ******************************************************************************************//* Common test for read_integer errors: encodes a 64-bit value and then attempts to read * as a 32-bit value. If TRUNC is non-zero, attempts to get errors by shortening the * input, otherwise it should overflow. Expects XD3_INTERNAL and MSG. */static inttest_read_integer_error (xd3_stream *stream, int trunto, const char *msg){ uint64_t eval = 1ULL << 34; uint32_t rval; xd3_output *buf = NULL; const uint8_t *max; const uint8_t *inp; int ret; buf = xd3_alloc_output (stream, buf); if ((ret = xd3_emit_uint64_t (stream, & buf, eval))) { goto fail; } again: inp = buf->base; max = buf->base + buf->next - trunto; if ((ret = xd3_read_uint32_t (stream, & inp, max, & rval)) != XD3_INVALID_INPUT || !MSG_IS (msg)) { ret = XD3_INTERNAL; } else if (trunto && trunto < buf->next) { trunto += 1; goto again; } else { ret = 0; } fail: xd3_free_output (stream, buf); return ret;}/* Test integer overflow using the above routine. */static inttest_decode_integer_overflow (xd3_stream *stream, int unused){ return test_read_integer_error (stream, 0, "overflow in read_intger");}/* Test integer EOI using the above routine. */static inttest_decode_integer_end_of_input (xd3_stream *stream, int unused){ return test_read_integer_error (stream, 1, "end-of-input in read_integer");}/* Test that emit_integer/decode_integer/sizeof_integer/read_integer work on correct * inputs. Tests powers of (2^7), plus or minus, up to the maximum value. */#define TEST_ENCODE_DECODE_INTEGER(TYPE,ONE,MAX) \ xd3_output *rbuf = NULL; \ xd3_output *dbuf = NULL; \ TYPE values[64]; \ int nvalues = 0; \ int i, ret = 0; \ \ for (i = 0; i < (sizeof (TYPE) * 8); i += 7) \ { \ values[nvalues++] = (ONE << i) - ONE; \ values[nvalues++] = (ONE << i); \ values[nvalues++] = (ONE << i) + ONE; \ } \
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -