diff options
-rw-r--r-- | usr.bin/tar/test/Makefile | 61 | ||||
-rw-r--r-- | usr.bin/tar/test/main.c | 1012 | ||||
-rw-r--r-- | usr.bin/tar/test/test.h | 151 | ||||
-rw-r--r-- | usr.bin/tar/test/test_0.c | 62 | ||||
-rw-r--r-- | usr.bin/tar/test/test_basic.c | 158 | ||||
-rw-r--r-- | usr.bin/tar/test/test_copy.c | 300 | ||||
-rw-r--r-- | usr.bin/tar/test/test_getdate.c | 38 | ||||
-rw-r--r-- | usr.bin/tar/test/test_help.c | 81 | ||||
-rw-r--r-- | usr.bin/tar/test/test_option_T.c | 123 | ||||
-rw-r--r-- | usr.bin/tar/test/test_stdio.c | 124 | ||||
-rw-r--r-- | usr.bin/tar/test/test_version.c | 93 |
11 files changed, 2203 insertions, 0 deletions
diff --git a/usr.bin/tar/test/Makefile b/usr.bin/tar/test/Makefile new file mode 100644 index 0000000..485669a --- /dev/null +++ b/usr.bin/tar/test/Makefile @@ -0,0 +1,61 @@ +# $FreeBSD$ + +# Where to find the tar sources (for the internal unit tests) +TAR_SRCDIR=${.CURDIR}/.. +.PATH: ${TAR_SRCDIR} + +# Some tar sources are pulled in for white-box tests +TAR_SRCS= \ + ../getdate.y + +TESTS= \ + test_0.c \ + test_basic.c \ + test_copy.c \ + test_getdate.c \ + test_help.c \ + test_option_T.c \ + test_stdio.c \ + test_version.c + +# Build the test program +SRCS= ${TAR_SRCS} \ + ${TESTS} \ + list.h \ + main.c + +CLEANFILES+= list.h + +NO_MAN=yes + +PROG=bsdtar_test +DPADD=${LIBARCHIVE} ${LIBBZ2} ${LIBZ} +CFLAGS+= -DPLATFORM_CONFIG_H=\"config_freebsd.h\" +CFLAGS+= -I.. +LDADD= -larchive -lz -lbz2 +CFLAGS+= -static -g -O2 -Wall +CFLAGS+= -I${.OBJDIR} +CFLAGS+= -I${TAR_SRCDIR} + +# Uncomment to link against dmalloc +#LDADD+= -L/usr/local/lib -ldmalloc +#CFLAGS+= -I/usr/local/include -DUSE_DMALLOC +WARNS=6 + +check test: bsdtar_test + ./bsdtar_test -p ${.OBJDIR}/../bsdtar -r ${.CURDIR} + +list.h: ${TESTS} Makefile + (cd ${.CURDIR}; cat ${TESTS}) | grep DEFINE_TEST > list.h + +clean: + rm -f *.out + rm -f *.o + rm -f *.core + rm -f *~ + rm -f list.h + rm -f archive.h ../archive.h + -chmod -R +w /tmp/bsdtar_test.* + rm -rf /tmp/bsdtar_test.* + +.include <bsd.prog.mk> diff --git a/usr.bin/tar/test/main.c b/usr.bin/tar/test/main.c new file mode 100644 index 0000000..3856f3a --- /dev/null +++ b/usr.bin/tar/test/main.c @@ -0,0 +1,1012 @@ +/* + * Copyright (c) 2003-2007 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Various utility routines useful for test programs. + * Each test program is linked against this file. + */ +#include <errno.h> +#include <locale.h> +#include <stdarg.h> +#include <time.h> + +#include "test.h" + +/* + * This same file is used pretty much verbatim for all test harnesses. + * + * The next few lines are the only differences. + */ +#define PROGRAM "bsdtar" /* Name of program being tested. */ +#define ENVBASE "BSDTAR" /* Prefix for environment variables. */ +#undef EXTRA_DUMP /* How to dump extra data */ +/* How to generate extra version info. */ +#define EXTRA_VERSION (systemf("%s --version", testprog) ? "" : "") +__FBSDID("$FreeBSD$"); + +/* + * "list.h" is simply created by "grep DEFINE_TEST"; it has + * a line like + * DEFINE_TEST(test_function) + * for each test. + * Include it here with a suitable DEFINE_TEST to declare all of the + * test functions. + */ +#undef DEFINE_TEST +#define DEFINE_TEST(name) void name(void); +#include "list.h" + +/* Interix doesn't define these in a standard header. */ +#if __INTERIX__ +extern char *optarg; +extern int optind; +#endif + +/* Enable core dump on failure. */ +static int dump_on_failure = 0; +/* Default is to remove temp dirs for successful tests. */ +static int keep_temp_files = 0; +/* Default is to print some basic information about each test. */ +static int quiet_flag = 0; +/* Default is to summarize repeated failures. */ +static int verbose = 0; +/* Cumulative count of component failures. */ +static int failures = 0; +/* Cumulative count of skipped component tests. */ +static int skips = 0; +/* Cumulative count of assertions. */ +static int assertions = 0; + +/* Directory where uuencoded reference files can be found. */ +static char *refdir; + +/* + * My own implementation of the standard assert() macro emits the + * message in the same format as GCC (file:line: message). + * It also includes some additional useful information. + * This makes it a lot easier to skim through test failures in + * Emacs. ;-) + * + * It also supports a few special features specifically to simplify + * test harnesses: + * failure(fmt, args) -- Stores a text string that gets + * printed if the following assertion fails, good for + * explaining subtle tests. + */ +static char msg[4096]; + +/* + * For each test source file, we remember how many times each + * failure was reported. + */ +static const char *failed_filename = NULL; +static struct line { + int line; + int count; +} failed_lines[1000]; + +/* + * Count this failure; return the number of previous failures. + */ +static int +previous_failures(const char *filename, int line) +{ + unsigned int i; + int count; + + if (failed_filename == NULL || strcmp(failed_filename, filename) != 0) + memset(failed_lines, 0, sizeof(failed_lines)); + failed_filename = filename; + + for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { + if (failed_lines[i].line == line) { + count = failed_lines[i].count; + failed_lines[i].count++; + return (count); + } + if (failed_lines[i].line == 0) { + failed_lines[i].line = line; + failed_lines[i].count = 1; + return (0); + } + } + return (0); +} + +/* + * Copy arguments into file-local variables. + */ +static const char *test_filename; +static int test_line; +static void *test_extra; +void test_setup(const char *filename, int line) +{ + test_filename = filename; + test_line = line; +} + +/* + * Inform user that we're skipping a test. + */ +void +test_skipping(const char *fmt, ...) +{ + va_list ap; + + if (previous_failures(test_filename, test_line)) + return; + + va_start(ap, fmt); + fprintf(stderr, " *** SKIPPING: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + ++skips; +} + +/* Common handling of failed tests. */ +static void +report_failure(void *extra) +{ + if (msg[0] != '\0') { + fprintf(stderr, " Description: %s\n", msg); + msg[0] = '\0'; + } + +#ifdef EXTRA_DUMP + if (extra != NULL) + fprintf(stderr, " detail: %s\n", EXTRA_DUMP(extra)); +#else + (void)extra; /* UNUSED */ +#endif + + if (dump_on_failure) { + fprintf(stderr, + " *** forcing core dump so failure can be debugged ***\n"); + *(char *)(NULL) = 0; + exit(1); + } +} + +/* + * Summarize repeated failures in the just-completed test file. + * The reports above suppress multiple failures from the same source + * line; this reports on any tests that did fail multiple times. + */ +static int +summarize_comparator(const void *a0, const void *b0) +{ + const struct line *a = a0, *b = b0; + if (a->line == 0 && b->line == 0) + return (0); + if (a->line == 0) + return (1); + if (b->line == 0) + return (-1); + return (a->line - b->line); +} + +static void +summarize(void) +{ + unsigned int i; + + qsort(failed_lines, sizeof(failed_lines)/sizeof(failed_lines[0]), + sizeof(failed_lines[0]), summarize_comparator); + for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { + if (failed_lines[i].line == 0) + break; + if (failed_lines[i].count > 1) + fprintf(stderr, "%s:%d: Failed %d times\n", + failed_filename, failed_lines[i].line, + failed_lines[i].count); + } + /* Clear the failure history for the next file. */ + memset(failed_lines, 0, sizeof(failed_lines)); +} + +/* Set up a message to display only after a test fails. */ +void +failure(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsprintf(msg, fmt, ap); + va_end(ap); +} + +/* Generic assert() just displays the failed condition. */ +int +test_assert(const char *file, int line, int value, const char *condition, void *extra) +{ + ++assertions; + if (value) { + msg[0] = '\0'; + return (value); + } + failures ++; + if (!verbose && previous_failures(file, line)) + return (value); + fprintf(stderr, "%s:%d: Assertion failed\n", file, line); + fprintf(stderr, " Condition: %s\n", condition); + report_failure(extra); + return (value); +} + +/* assertEqualInt() displays the values of the two integers. */ +int +test_assert_equal_int(const char *file, int line, + int v1, const char *e1, int v2, const char *e2, void *extra) +{ + ++assertions; + if (v1 == v2) { + msg[0] = '\0'; + return (1); + } + failures ++; + if (!verbose && previous_failures(file, line)) + return (0); + fprintf(stderr, "%s:%d: Assertion failed: Ints not equal\n", + file, line); + fprintf(stderr, " %s=%d\n", e1, v1); + fprintf(stderr, " %s=%d\n", e2, v2); + report_failure(extra); + return (0); +} + +static void strdump(const char *p) +{ + if (p == NULL) { + fprintf(stderr, "(null)"); + return; + } + fprintf(stderr, "\""); + while (*p != '\0') { + unsigned int c = 0xff & *p++; + switch (c) { + case '\a': fprintf(stderr, "\a"); break; + case '\b': fprintf(stderr, "\b"); break; + case '\n': fprintf(stderr, "\n"); break; + case '\r': fprintf(stderr, "\r"); break; + default: + if (c >= 32 && c < 127) + fprintf(stderr, "%c", c); + else + fprintf(stderr, "\\x%02X", c); + } + } + fprintf(stderr, "\""); +} + +/* assertEqualString() displays the values of the two strings. */ +int +test_assert_equal_string(const char *file, int line, + const char *v1, const char *e1, + const char *v2, const char *e2, + void *extra) +{ + ++assertions; + if (v1 == NULL || v2 == NULL) { + if (v1 == v2) { + msg[0] = '\0'; + return (1); + } + } else if (strcmp(v1, v2) == 0) { + msg[0] = '\0'; + return (1); + } + failures ++; + if (!verbose && previous_failures(file, line)) + return (0); + fprintf(stderr, "%s:%d: Assertion failed: Strings not equal\n", + file, line); + fprintf(stderr, " %s = ", e1); + strdump(v1); + fprintf(stderr, " (length %d)\n", v1 == NULL ? 0 : strlen(v1)); + fprintf(stderr, " %s = ", e2); + strdump(v2); + fprintf(stderr, " (length %d)\n", v2 == NULL ? 0 : strlen(v2)); + report_failure(extra); + return (0); +} + +static void wcsdump(const wchar_t *w) +{ + if (w == NULL) { + fprintf(stderr, "(null)"); + return; + } + fprintf(stderr, "\""); + while (*w != L'\0') { + unsigned int c = *w++; + if (c >= 32 && c < 127) + fprintf(stderr, "%c", c); + else if (c < 256) + fprintf(stderr, "\\x%02X", c); + else if (c < 0x10000) + fprintf(stderr, "\\u%04X", c); + else + fprintf(stderr, "\\U%08X", c); + } + fprintf(stderr, "\""); +} + +/* assertEqualWString() displays the values of the two strings. */ +int +test_assert_equal_wstring(const char *file, int line, + const wchar_t *v1, const char *e1, + const wchar_t *v2, const char *e2, + void *extra) +{ + ++assertions; + if (v1 == NULL) { + if (v2 == NULL) { + msg[0] = '\0'; + return (1); + } + } else if (v2 == NULL) { + if (v1 == NULL) { + msg[0] = '\0'; + return (1); + } + } else if (wcscmp(v1, v2) == 0) { + msg[0] = '\0'; + return (1); + } + failures ++; + if (!verbose && previous_failures(file, line)) + return (0); + fprintf(stderr, "%s:%d: Assertion failed: Unicode strings not equal\n", + file, line); + fprintf(stderr, " %s = ", e1); + wcsdump(v1); + fprintf(stderr, "\n"); + fprintf(stderr, " %s = ", e2); + wcsdump(v2); + fprintf(stderr, "\n"); + report_failure(extra); + return (0); +} + +/* + * Pretty standard hexdump routine. As a bonus, if ref != NULL, then + * any bytes in p that differ from ref will be highlighted with '_' + * before and after the hex value. + */ +static void +hexdump(const char *p, const char *ref, size_t l, size_t offset) +{ + size_t i, j; + char sep; + + for(i=0; i < l; i+=16) { + fprintf(stderr, "%04x", i + offset); + sep = ' '; + for (j = 0; j < 16 && i + j < l; j++) { + if (ref != NULL && p[i + j] != ref[i + j]) + sep = '_'; + fprintf(stderr, "%c%02x", sep, 0xff & (int)p[i+j]); + if (ref != NULL && p[i + j] == ref[i + j]) + sep = ' '; + } + for (; j < 16; j++) { + fprintf(stderr, "%c ", sep); + sep = ' '; + } + fprintf(stderr, "%c", sep); + for (j=0; j < 16 && i + j < l; j++) { + int c = p[i + j]; + if (c >= ' ' && c <= 126) + fprintf(stderr, "%c", c); + else + fprintf(stderr, "."); + } + fprintf(stderr, "\n"); + } +} + +/* assertEqualMem() displays the values of the two memory blocks. */ +/* TODO: For long blocks, hexdump the first bytes that actually differ. */ +int +test_assert_equal_mem(const char *file, int line, + const char *v1, const char *e1, + const char *v2, const char *e2, + size_t l, const char *ld, void *extra) +{ + ++assertions; + if (v1 == NULL || v2 == NULL) { + if (v1 == v2) { + msg[0] = '\0'; + return (1); + } + } else if (memcmp(v1, v2, l) == 0) { + msg[0] = '\0'; + return (1); + } + failures ++; + if (!verbose && previous_failures(file, line)) + return (0); + fprintf(stderr, "%s:%d: Assertion failed: memory not equal\n", + file, line); + fprintf(stderr, " size %s = %d\n", ld, (int)l); + fprintf(stderr, " Dump of %s\n", e1); + hexdump(v1, v2, l < 32 ? l : 32, 0); + fprintf(stderr, " Dump of %s\n", e2); + hexdump(v2, v1, l < 32 ? l : 32, 0); + fprintf(stderr, "\n"); + report_failure(extra); + return (0); +} + +int +test_assert_empty_file(const char *f1fmt, ...) +{ + char buff[1024]; + char f1[1024]; + struct stat st; + va_list ap; + ssize_t s; + int fd; + + + va_start(ap, f1fmt); + vsprintf(f1, f1fmt, ap); + va_end(ap); + + if (stat(f1, &st) != 0) { + fprintf(stderr, "%s:%d: Could not stat: %s\n", test_filename, test_line, f1); + report_failure(NULL); + return (0); + } + if (st.st_size == 0) + return (1); + + failures ++; + if (!verbose && previous_failures(test_filename, test_line)) + return (0); + + fprintf(stderr, "%s:%d: File not empty: %s\n", test_filename, test_line, f1); + fprintf(stderr, " File size: %d\n", (int)st.st_size); + fprintf(stderr, " Contents:\n"); + fd = open(f1, O_RDONLY); + if (fd < 0) { + fprintf(stderr, " Unable to open %s\n", f1); + } else { + s = sizeof(buff) < st.st_size ? sizeof(buff) : st.st_size; + s = read(fd, buff, s); + hexdump(buff, NULL, s, 0); + } + report_failure(NULL); + return (0); +} + +/* assertEqualFile() asserts that two files have the same contents. */ +/* TODO: hexdump the first bytes that actually differ. */ +int +test_assert_equal_file(const char *f1, const char *f2pattern, ...) +{ + char f2[1024]; + va_list ap; + char buff1[1024]; + char buff2[1024]; + int fd1, fd2; + int n1, n2; + + va_start(ap, f2pattern); + vsprintf(f2, f2pattern, ap); + va_end(ap); + + fd1 = open(f1, O_RDONLY); + fd2 = open(f2, O_RDONLY); + for (;;) { + n1 = read(fd1, buff1, sizeof(buff1)); + n2 = read(fd2, buff2, sizeof(buff2)); + if (n1 != n2) + break; + if (n1 == 0 && n2 == 0) + return (1); + if (memcmp(buff1, buff2, n1) != 0) + break; + } + failures ++; + if (!verbose && previous_failures(test_filename, test_line)) + return (0); + fprintf(stderr, "%s:%d: Files are not identical\n", + test_filename, test_line); + fprintf(stderr, " file1=\"%s\"\n", f1); + fprintf(stderr, " file2=\"%s\"\n", f2); + report_failure(test_extra); + return (0); +} + +int +test_assert_file_exists(const char *fpattern, ...) +{ + char f[1024]; + va_list ap; + + va_start(ap, fpattern); + vsprintf(f, fpattern, ap); + va_end(ap); + + if (!access(f, F_OK)) + return (1); + if (!previous_failures(test_filename, test_line)) { + fprintf(stderr, "%s:%d: File doesn't exist\n", + test_filename, test_line); + fprintf(stderr, " file=\"%s\"\n", f); + report_failure(test_extra); + } + return (0); +} + +int +test_assert_file_not_exists(const char *fpattern, ...) +{ + char f[1024]; + va_list ap; + + va_start(ap, fpattern); + vsprintf(f, fpattern, ap); + va_end(ap); + + if (access(f, F_OK)) + return (1); + if (!previous_failures(test_filename, test_line)) { + fprintf(stderr, "%s:%d: File exists and shouldn't\n", + test_filename, test_line); + fprintf(stderr, " file=\"%s\"\n", f); + report_failure(test_extra); + } + return (0); +} + +/* assertFileContents() asserts the contents of a file. */ +int +test_assert_file_contents(const void *buff, int s, const char *fpattern, ...) +{ + char f[1024]; + va_list ap; + char *contents; + int fd; + int n; + + va_start(ap, fpattern); + vsprintf(f, fpattern, ap); + va_end(ap); + + fd = open(f, O_RDONLY); + contents = malloc(s * 2); + n = read(fd, contents, s * 2); + if (n == s && memcmp(buff, contents, s) == 0) { + free(contents); + return (1); + } + failures ++; + if (!previous_failures(test_filename, test_line)) { + fprintf(stderr, "%s:%d: File contents don't match\n", + test_filename, test_line); + fprintf(stderr, " file=\"%s\"\n", f); + if (n > 0) + hexdump(contents, buff, n, 0); + else { + fprintf(stderr, " File empty, contents should be:\n"); + hexdump(buff, NULL, s, 0); + } + report_failure(test_extra); + } + free(contents); + return (0); +} + +/* + * Call standard system() call, but build up the command line using + * sprintf() conventions. + */ +int +systemf(const char *fmt, ...) +{ + char buff[8192]; + va_list ap; + int r; + + va_start(ap, fmt); + vsprintf(buff, fmt, ap); + r = system(buff); + va_end(ap); + return (r); +} + +/* + * Slurp a file into memory for ease of comparison and testing. + * Returns size of file in 'sizep' if non-NULL, null-terminates + * data in memory for ease of use. + */ +char * +slurpfile(size_t * sizep, const char *fmt, ...) +{ + char filename[8192]; + struct stat st; + va_list ap; + char *p; + ssize_t bytes_read; + int fd; + int r; + + va_start(ap, fmt); + vsprintf(filename, fmt, ap); + va_end(ap); + + fd = open(filename, O_RDONLY); + if (fd < 0) { + /* Note: No error; non-existent file is okay here. */ + return (NULL); + } + r = fstat(fd, &st); + if (r != 0) { + fprintf(stderr, "Can't stat file %s\n", filename); + close(fd); + return (NULL); + } + p = malloc(st.st_size + 1); + if (p == NULL) { + fprintf(stderr, "Can't allocate %ld bytes of memory to read file %s\n", (long int)st.st_size, filename); + close(fd); + return (NULL); + } + bytes_read = read(fd, p, st.st_size); + if (bytes_read < st.st_size) { + fprintf(stderr, "Can't read file %s\n", filename); + close(fd); + free(p); + return (NULL); + } + p[st.st_size] = '\0'; + if (sizep != NULL) + *sizep = (size_t)st.st_size; + close(fd); + return (p); +} + +/* + * "list.h" is automatically generated; it just has a lot of lines like: + * DEFINE_TEST(function_name) + * It's used above to declare all of the test functions. + * We reuse it here to define a list of all tests (functions and names). + */ +#undef DEFINE_TEST +#define DEFINE_TEST(n) { n, #n }, +struct { void (*func)(void); const char *name; } tests[] = { + #include "list.h" +}; + +/* + * Each test is run in a private work dir. Those work dirs + * do have consistent and predictable names, in case a group + * of tests need to collaborate. However, there is no provision + * for requiring that tests run in a certain order. + */ +static int test_run(int i, const char *tmpdir) +{ + int failures_before = failures; + + if (!quiet_flag) + printf("%d: %s\n", i, tests[i].name); + /* + * Always explicitly chdir() in case the last test moved us to + * a strange place. + */ + if (chdir(tmpdir)) { + fprintf(stderr, + "ERROR: Couldn't chdir to temp dir %s\n", + tmpdir); + exit(1); + } + /* Create a temp directory for this specific test. */ + if (mkdir(tests[i].name, 0755)) { + fprintf(stderr, + "ERROR: Couldn't create temp dir ``%s''\n", + tests[i].name); + exit(1); + } + /* Chdir() to that work directory. */ + if (chdir(tests[i].name)) { + fprintf(stderr, + "ERROR: Couldn't chdir to temp dir ``%s''\n", + tests[i].name); + exit(1); + } + /* Explicitly reset the locale before each test. */ + setlocale(LC_ALL, "C"); + /* Run the actual test. */ + (*tests[i].func)(); + /* Summarize the results of this test. */ + summarize(); + /* If there were no failures, we can remove the work dir. */ + if (failures == failures_before) { + if (!keep_temp_files && chdir(tmpdir) == 0) { + systemf("rm -rf %s", tests[i].name); + } + } + /* Return appropriate status. */ + return (failures == failures_before ? 0 : 1); +} + +static void usage(const char *program) +{ + static const int limit = sizeof(tests) / sizeof(tests[0]); + int i; + + printf("Usage: %s [options] <test> <test> ...\n", program); + printf("Default is to run all tests.\n"); + printf("Otherwise, specify the numbers of the tests you wish to run.\n"); + printf("Options:\n"); + printf(" -d Dump core after any failure, for debugging.\n"); + printf(" -k Keep all temp files.\n"); + printf(" Default: temp files for successful tests deleted.\n"); +#ifdef PROGRAM + printf(" -p <path> Path to executable to be tested.\n"); + printf(" Default: path taken from " ENVBASE " environment variable.\n"); +#endif + printf(" -q Quiet.\n"); + printf(" -r <dir> Path to dir containing reference files.\n"); + printf(" Default: Current directory.\n"); + printf(" -v Verbose.\n"); + printf("Available tests:\n"); + for (i = 0; i < limit; i++) + printf(" %d: %s\n", i, tests[i].name); + exit(1); +} + +#define UUDECODE(c) (((c) - 0x20) & 0x3f) + +void +extract_reference_file(const char *name) +{ + char buff[1024]; + FILE *in, *out; + + sprintf(buff, "%s/%s.uu", refdir, name); + in = fopen(buff, "r"); + failure("Couldn't open reference file %s", buff); + assert(in != NULL); + if (in == NULL) + return; + /* Read up to and including the 'begin' line. */ + for (;;) { + if (fgets(buff, sizeof(buff), in) == NULL) { + /* TODO: This is a failure. */ + return; + } + if (memcmp(buff, "begin ", 6) == 0) + break; + } + /* Now, decode the rest and write it. */ + /* Not a lot of error checking here; the input better be right. */ + out = fopen(name, "w"); + while (fgets(buff, sizeof(buff), in) != NULL) { + char *p = buff; + int bytes; + + if (memcmp(buff, "end", 3) == 0) + break; + + bytes = UUDECODE(*p++); + while (bytes > 0) { + int n = 0; + /* Write out 1-3 bytes from that. */ + if (bytes > 0) { + n = UUDECODE(*p++) << 18; + n |= UUDECODE(*p++) << 12; + fputc(n >> 16, out); + --bytes; + } + if (bytes > 0) { + n |= UUDECODE(*p++) << 6; + fputc((n >> 8) & 0xFF, out); + --bytes; + } + if (bytes > 0) { + n |= UUDECODE(*p++); + fputc(n & 0xFF, out); + --bytes; + } + } + } + fclose(out); + fclose(in); +} + + +int main(int argc, char **argv) +{ + static const int limit = sizeof(tests) / sizeof(tests[0]); + int i, tests_run = 0, tests_failed = 0, opt; + time_t now; + char *refdir_alloc = NULL; + char *progname, *p; + char tmpdir[256]; + char tmpdir_timestamp[256]; + + /* + * Name of this program, used to build root of our temp directory + * tree. + */ + progname = p = argv[0]; + while (*p != '\0') { + if (*p == '/') + progname = p + 1; + ++p; + } + +#ifdef PROGRAM + /* Get the target program from environment, if available. */ + testprog = getenv(ENVBASE); +#endif + + /* Allow -d to be controlled through the environment. */ + if (getenv(ENVBASE "_DEBUG") != NULL) + dump_on_failure = 1; + + /* Get the directory holding test files from environment. */ + refdir = getenv(ENVBASE "_TEST_FILES"); + + /* + * Parse options. + */ + while ((opt = getopt(argc, argv, "dkp:qr:v")) != -1) { + switch (opt) { + case 'd': + dump_on_failure = 1; + break; + case 'k': + keep_temp_files = 1; + break; + case 'p': +#ifdef PROGRAM + testprog = optarg; +#else + usage(progname); +#endif + break; + case 'q': + quiet_flag++; + break; + case 'r': + refdir = optarg; + break; + case 'v': + verbose = 1; + break; + case '?': + default: + usage(progname); + } + } + argc -= optind; + argv += optind; + + /* + * Sanity-check that our options make sense. + */ +#ifdef PROGRAM + if (testprog == NULL) + usage(progname); +#endif + + /* + * Create a temp directory for the following tests. + * Include the time the tests started as part of the name, + * to make it easier to track the results of multiple tests. + */ + now = time(NULL); + for (i = 0; i < 1000; i++) { + strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp), + "%Y-%m-%dT%H.%M.%S", + localtime(&now)); + sprintf(tmpdir, "/tmp/%s.%s-%03d", progname, tmpdir_timestamp, i); + if (mkdir(tmpdir,0755) == 0) + break; + if (errno == EEXIST) + continue; + fprintf(stderr, "ERROR: Unable to create temp directory %s\n", + tmpdir); + exit(1); + } + + /* + * If the user didn't specify a directory for locating + * reference files, use the current directory for that. + */ + if (refdir == NULL) { + systemf("/bin/pwd > %s/refdir", tmpdir); + refdir = refdir_alloc = slurpfile(NULL, "%s/refdir", tmpdir); + p = refdir + strlen(refdir); + while (p[-1] == '\n') { + --p; + *p = '\0'; + } + systemf("rm %s/refdir", tmpdir); + } + + /* + * Banner with basic information. + */ + if (!quiet_flag) { + printf("Running tests in: %s\n", tmpdir); + printf("Reference files will be read from: %s\n", refdir); +#ifdef PROGRAM + printf("Running tests on: %s\n", testprog); +#endif + printf("Exercising: "); + fflush(stdout); + printf("%s\n", EXTRA_VERSION); + } + + /* + * Run some or all of the individual tests. + */ + if (argc == 0) { + /* Default: Run all tests. */ + for (i = 0; i < limit; i++) { + if (test_run(i, tmpdir)) + tests_failed++; + tests_run++; + } + } else { + while (*(argv) != NULL) { + i = atoi(*argv); + if (**argv < '0' || **argv > '9' || i < 0 || i >= limit) { + printf("*** INVALID Test %s\n", *argv); + usage(progname); + } else { + if (test_run(i, tmpdir)) + tests_failed++; + tests_run++; + } + argv++; + } + } + + /* + * Report summary statistics. + */ + if (!quiet_flag) { + printf("\n"); + printf("%d of %d tests reported failures\n", + tests_failed, tests_run); + printf(" Total of %d assertions checked.\n", assertions); + printf(" Total of %d assertions failed.\n", failures); + printf(" Total of %d assertions skipped.\n", skips); + } + + free(refdir_alloc); + + /* If the final tmpdir is empty, we can remove it. */ + /* This should be the usual case when all tests succeed. */ + rmdir(tmpdir); + + return (tests_failed); +} diff --git a/usr.bin/tar/test/test.h b/usr.bin/tar/test/test.h new file mode 100644 index 0000000..416f0dd --- /dev/null +++ b/usr.bin/tar/test/test.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2003-2006 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* Every test program should #include "test.h" as the first thing. */ + +/* + * The goal of this file (and the matching test.c) is to + * simplify the very repetitive test-*.c test programs. + */ +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#ifndef _WIN32 +#include <unistd.h> +#endif +#include <wchar.h> + +#ifdef USE_DMALLOC +#include <dmalloc.h> +#endif + +#if defined(HAVE_CONFIG_H) +/* Most POSIX platforms use the 'configure' script to build config.h */ +#include "../../config.h" +#elif defined(__FreeBSD__) +/* Building as part of FreeBSD system requires a pre-built config.h. */ +#include "../config_freebsd.h" +#elif defined(_WIN32) +/* Win32 can't run the 'configure' script. */ +#include "../config_windows.h" +#else +/* Warn if the library hasn't been (automatically or manually) configured. */ +#error Oops: No config.h and no pre-built configuration in test.h. +#endif + +/* No non-FreeBSD platform will have __FBSDID, so just define it here. */ +#ifdef __FreeBSD__ +#include <sys/cdefs.h> /* For __FBSDID */ +#else +#define __FBSDID(a) /* null */ +#endif + +/* + * Redefine DEFINE_TEST for use in defining the test functions. + */ +#undef DEFINE_TEST +#define DEFINE_TEST(name) void name(void); void name(void) + +/* An implementation of the standard assert() macro */ +#define assert(e) test_assert(__FILE__, __LINE__, (e), #e, NULL) + +/* Assert two integers are the same. Reports value of each one if not. */ +#define assertEqualInt(v1,v2) \ + test_assert_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) + +/* Assert two strings are the same. Reports value of each one if not. */ +#define assertEqualString(v1,v2) \ + test_assert_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) +/* As above, but v1 and v2 are wchar_t * */ +#define assertEqualWString(v1,v2) \ + test_assert_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) +/* As above, but raw blocks of bytes. */ +#define assertEqualMem(v1, v2, l) \ + test_assert_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL) +/* Assert two files are the same; allow printf-style expansion of second name. + * See below for comments about variable arguments here... + */ +#define assertEqualFile \ + test_setup(__FILE__, __LINE__);test_assert_equal_file +/* Assert that a file is empty; supports printf-style arguments. */ +#define assertEmptyFile \ + test_setup(__FILE__, __LINE__);test_assert_empty_file +/* Assert that a file exists; supports printf-style arguments. */ +#define assertFileExists \ + test_setup(__FILE__, __LINE__);test_assert_file_exists +/* Assert that a file exists; supports printf-style arguments. */ +#define assertFileNotExists \ + test_setup(__FILE__, __LINE__);test_assert_file_not_exists + +/* + * This would be simple with C99 variadic macros, but I don't want to + * require that. Instead, I insert a function call before each + * skipping() call to pass the file and line information down. Crude, + * but effective. + */ +#define skipping \ + test_setup(__FILE__, __LINE__);test_skipping + +/* Function declarations. These are defined in test_utility.c. */ +void failure(const char *fmt, ...); +void test_setup(const char *, int); +void test_skipping(const char *fmt, ...); +int test_assert(const char *, int, int, const char *, void *); +int test_assert_empty_file(const char *, ...); +int test_assert_equal_file(const char *, const char *, ...); +int test_assert_equal_int(const char *, int, int, const char *, int, const char *, void *); +int test_assert_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *); +int test_assert_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *); +int test_assert_equal_mem(const char *, int, const char *, const char *, const char *, const char *, size_t, const char *, void *); +int test_assert_file_contents(const void *, int, const char *, ...); +int test_assert_file_exists(const char *, ...); +int test_assert_file_not_exists(const char *, ...); + +/* Like sprintf, then system() */ +int systemf(const char * fmt, ...); + +/* Suck file into string allocated via malloc(). Call free() when done. */ +/* Supports printf-style args: slurpfile(NULL, "%s/myfile", refdir); */ +char *slurpfile(size_t *, const char *fmt, ...); + +/* Extracts named reference file to the current directory. */ +void extract_reference_file(const char *); + +/* + * Special interfaces for program test harness. + */ + +/* Pathname of exe to be tested. */ +char *testprog; diff --git a/usr.bin/tar/test/test_0.c b/usr.bin/tar/test/test_0.c new file mode 100644 index 0000000..7a72af1 --- /dev/null +++ b/usr.bin/tar/test/test_0.c @@ -0,0 +1,62 @@ +/*- + * Copyright (c) 2003-2007 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +/* + * This first test does basic sanity checks on the environment. For + * most of these, we just exit on failure. + */ + +DEFINE_TEST(test_0) +{ + struct stat st; + + failure("File %s does not exist?!", testprog); + if (!assertEqualInt(0, stat(testprog, &st))) + exit(1); + + failure("%s is not executable?!", testprog); + if (!assert((st.st_mode & 0111) != 0)) + exit(1); + + /* + * Try to succesfully run the program; this requires that + * we know some option that will succeed. + */ + if (0 == systemf("%s --version >/dev/null", testprog)) { + /* This worked. */ + } else if (0 == systemf("%s -W version >/dev/null", testprog)) { + /* This worked. */ + } else { + failure("Unable to successfully run any of the following:\n" + " * %s --version\n" + " * %s -W version\n", + testprog, testprog); + assert(0); + } + + /* TODO: Ensure that our reference files are available. */ +} diff --git a/usr.bin/tar/test/test_basic.c b/usr.bin/tar/test/test_basic.c new file mode 100644 index 0000000..50be289 --- /dev/null +++ b/usr.bin/tar/test/test_basic.c @@ -0,0 +1,158 @@ +/*- + * Copyright (c) 2003-2007 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + + +static void +basic_tar(const char *target, const char *pack_options, const char *unpack_options) +{ + struct stat st, st2; + char buff[128]; + int r; + + assertEqualInt(0, mkdir(target, 0775)); + + /* Use the tar program to create an archive. */ + r = systemf("%s cf - %s `cat filelist` >%s/archive 2>%s/pack.err", testprog, pack_options, target, target); + failure("Error invoking %s cf -", testprog, pack_options); + assertEqualInt(r, 0); + + chdir(target); + + /* Verify that nothing went to stderr. */ + assertEmptyFile("pack.err"); + + /* + * Use tar to unpack the archive into another directory. + */ + r = systemf("%s xf archive %s >unpack.out 2>unpack.err", testprog, unpack_options); + failure("Error invoking %s xf archive %s", testprog, unpack_options); + assertEqualInt(r, 0); + + /* Verify that nothing went to stderr. */ + assertEmptyFile("unpack.err"); + + /* + * Verify unpacked files. + */ + + /* Regular file with 2 links. */ + r = lstat("file", &st); + failure("Failed to stat file %s/file, errno=%d", target, errno); + assertEqualInt(r, 0); + if (r == 0) { + assert(S_ISREG(st.st_mode)); + assertEqualInt(0644, st.st_mode & 0777); + assertEqualInt(10, st.st_size); + failure("file %s/file", target); + assertEqualInt(2, st.st_nlink); + } + + /* Another name for the same file. */ + r = lstat("linkfile", &st2); + failure("Failed to stat file %s/linkfile, errno=%d", target, errno); + assertEqualInt(r, 0); + if (r == 0) { + assert(S_ISREG(st2.st_mode)); + assertEqualInt(0644, st2.st_mode & 0777); + assertEqualInt(10, st2.st_size); + failure("file %s/linkfile", target); + assertEqualInt(2, st2.st_nlink); + /* Verify that the two are really hardlinked. */ + assertEqualInt(st.st_dev, st2.st_dev); + failure("%s/linkfile and %s/file aren't really hardlinks", target, target); + assertEqualInt(st.st_ino, st2.st_ino); + } + + /* Symlink */ + r = lstat("symlink", &st); + failure("Failed to stat file %s/symlink, errno=%d", target, errno); + assertEqualInt(r, 0); + if (r == 0) { + failure("symlink should be a symlink; actual mode is %o", + st.st_mode); + assert(S_ISLNK(st.st_mode)); + if (S_ISLNK(st.st_mode)) { + r = readlink("symlink", buff, sizeof(buff)); + assertEqualInt(r, 4); + buff[r] = '\0'; + assertEqualString(buff, "file"); + } + } + + /* dir */ + r = lstat("dir", &st); + if (r == 0) { + assertEqualInt(r, 0); + assert(S_ISDIR(st.st_mode)); + assertEqualInt(0775, st.st_mode & 0777); + } + + chdir(".."); +} + +DEFINE_TEST(test_basic) +{ + int fd; + int filelist; + int oldumask; + + oldumask = umask(0); + + /* + * Create an assortment of files on disk. + */ + filelist = open("filelist", O_CREAT | O_WRONLY, 0644); + + /* File with 10 bytes content. */ + fd = open("file", O_CREAT | O_WRONLY, 0644); + assert(fd >= 0); + assertEqualInt(10, write(fd, "123456789", 10)); + close(fd); + write(filelist, "file\n", 5); + + /* hardlink to above file. */ + assertEqualInt(0, link("file", "linkfile")); + write(filelist, "linkfile\n", 9); + + /* Symlink to above file. */ + assertEqualInt(0, symlink("file", "symlink")); + write(filelist, "symlink\n", 8); + + /* Directory. */ + assertEqualInt(0, mkdir("dir", 0775)); + write(filelist, "dir\n", 4); + /* All done. */ + close(filelist); + + /* Archive/dearchive with a variety of options. */ + basic_tar("copy", "", ""); + /* tar doesn't handle cpio symlinks correctly */ + /* basic_tar("copy_odc", "--format=odc", ""); */ + basic_tar("copy_ustar", "--format=ustar", ""); + + umask(oldumask); +} diff --git a/usr.bin/tar/test/test_copy.c b/usr.bin/tar/test/test_copy.c new file mode 100644 index 0000000..e0bbb06 --- /dev/null +++ b/usr.bin/tar/test/test_copy.c @@ -0,0 +1,300 @@ +/*- + * Copyright (c) 2003-2007 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +static void +create_tree(void) +{ + char buff[260]; + char buff2[260]; + int i; + int fd; + + assertEqualInt(0, mkdir("original", 0775)); + chdir("original"); + + buff[0] = 'f'; + buff[1] = '_'; + for (i = 0; i < 200; i++) { + /* Create a file named "f_abcdef..." */ + buff[i + 2] = 'a' + (i % 26); + buff[i + 3] = '\0'; + fd = open(buff, O_CREAT | O_WRONLY, 0644); + assert(fd >= 0); + assertEqualInt(i + 3, write(fd, buff, strlen(buff))); + close(fd); + + /* Create a link named "l_abcdef..." to the above. */ + strcpy(buff2, buff); + buff2[0] = 'l'; + assertEqualInt(0, link(buff, buff2)); + + /* Create a link named "m_abcdef..." to the above. */ + strcpy(buff2, buff); + buff2[0] = 'm'; + assertEqualInt(0, link(buff, buff2)); + + /* Create a symlink named "s_abcdef..." to the above. */ + buff2[0] = 's'; + assertEqualInt(0, symlink(buff, buff2)); + + /* Create a dir named "d_abcdef...". */ + buff2[0] = 'd'; + assertEqualInt(0, mkdir(buff2, 0775)); + } + + chdir(".."); +} + +#define LIMIT_NONE 0 +#define LIMIT_USTAR 1 + +static void +verify_tree(int limit) +{ + struct stat st, st2; + char filename[260]; + char contents[260]; + int i, r; + int fd; + int len; + const char *p; + DIR *d; + struct dirent *de; + + /* Generate the names we know should be there and verify them. */ + for (i = 0; i < 200; i++) { + /* Verify a file named "f_abcdef..." */ + filename[0] = 'f'; + filename[1] = '_'; + filename[i + 2] = 'a' + (i % 26); + filename[i + 3] = '\0'; + if (limit != LIMIT_USTAR || strlen(filename) <= 100) { + fd = open(filename, O_RDONLY); + failure("Couldn't open \"%s\": %s", + filename, strerror(errno)); + if (assert(fd >= 0)) { + len = read(fd, contents, i + 10); + close(fd); + assertEqualInt(len, i + 3); + /* Verify contents of 'contents' */ + contents[len] = '\0'; + failure("Each test file contains its own name"); + assertEqualString(filename, contents); + /* stat() file and get dev/ino for next check */ + assertEqualInt(0, lstat(filename, &st)); + } + } + + /* + * ustar allows 100 chars for links, and we have + * "original/" as part of the name, so the link + * names here can't exceed 91 chars. + */ + if (limit != LIMIT_USTAR || strlen(filename) <= 91) { + /* Verify hardlink "l_abcdef..." */ + filename[0] = 'l'; + assertEqualInt(0, (r = lstat(filename, &st2))); + if (r == 0) { + assertEqualInt(st2.st_dev, st.st_dev); + assertEqualInt(st2.st_ino, st.st_ino); + } + + /* Verify hardlink "m_abcdef..." */ + filename[0] = 'm'; + assertEqualInt(0, (r = lstat(filename, &st2))); + if (r == 0) { + assertEqualInt(st2.st_dev, st.st_dev); + assertEqualInt(st2.st_ino, st.st_ino); + } + } + + /* + * Symlink text doesn't include the 'original/' prefix, + * so the limit here is 100 characters. + */ + /* Verify symlink "s_abcdef..." */ + filename[0] = 's'; + if (limit != LIMIT_USTAR || strlen(filename) <= 100) { + /* This is a symlink. */ + failure("Couldn't stat %s (length %d)", + filename, strlen(filename)); + if (assertEqualInt(0, lstat(filename, &st2))) { + assert(S_ISLNK(st2.st_mode)); + /* This is a symlink to the file above. */ + failure("Couldn't stat %s", filename); + if (assertEqualInt(0, stat(filename, &st2))) { + assertEqualInt(st2.st_dev, st.st_dev); + assertEqualInt(st2.st_ino, st.st_ino); + } + } + } + + /* Verify dir "d_abcdef...". */ + filename[0] = 'd'; + if (limit != LIMIT_USTAR || strlen(filename) < 100) { + /* This is a dir. */ + failure("Couldn't stat %s (length %d)", + filename, strlen(filename)); + if (assertEqualInt(0, lstat(filename, &st2))) { + if (assert(S_ISDIR(st2.st_mode))) { + /* TODO: opendir/readdir this + * directory and make sure + * it's empty. + */ + } + } + } + } + + /* Now make sure nothing is there that shouldn't be. */ + d = opendir("."); + while ((de = readdir(d)) != NULL) { + p = de->d_name; + switch(p[0]) { + case 'l': case 'm': + if (limit == LIMIT_USTAR) { + failure("strlen(p) = %d", strlen(p)); + assert(strlen(p) < 92); + } + case 'd': + if (limit == LIMIT_USTAR) { + failure("strlen(p)=%d", strlen(p)); + assert(strlen(p) < 100); + } + case 'f': case 's': + if (limit == LIMIT_USTAR) { + failure("strlen(p)=%d", strlen(p)); + assert(strlen(p) < 101); + } + /* Our files have very particular filename patterns. */ + assert(p[1] == '_' && p[2] == 'a'); + assert(p[2] == 'a'); + p += 2; + for (i = 0; p[i] != '\0' && i < 200; i++) + assert(p[i] == 'a' + (i % 26)); + assert(p[i] == '\0'); + break; + case '.': + assert(p[1] == '\0' || (p[1] == '.' && p[2] == '\0')); + break; + default: + failure("File %s shouldn't be here", p); + assert(0); + } + + } + closedir(d); +} + +static void +copy_basic(void) +{ + int r; + + assertEqualInt(0, mkdir("plain", 0775)); + chdir("plain"); + + /* + * Use the tar program to create an archive. + */ + r = systemf("%s cf archive -C .. original >pack.out 2>pack.err", + testprog); + failure("Error invoking \"%s cf\"", testprog); + assertEqualInt(r, 0); + + /* Verify that nothing went to stdout or stderr. */ + assertEmptyFile("pack.err"); + assertEmptyFile("pack.out"); + + /* + * Use tar to unpack the archive into another directory. + */ + r = systemf("%s xf archive >unpack.out 2>unpack.err", testprog); + failure("Error invoking %s xf archive", testprog); + assertEqualInt(r, 0); + + /* Verify that nothing went to stdout or stderr. */ + assertEmptyFile("unpack.err"); + assertEmptyFile("unpack.out"); + + chdir("original"); + verify_tree(LIMIT_NONE); + chdir("../.."); +} + +static void +copy_ustar(void) +{ + const char *target = "ustar"; + int r; + + assertEqualInt(0, mkdir(target, 0775)); + chdir(target); + + /* + * Use the tar program to create an archive. + */ + r = systemf("%s cf archive --format=ustar -C .. original >pack.out 2>pack.err", + testprog); + failure("Error invoking \"%s cf archive --format=ustar\"", testprog); + assertEqualInt(r, 0); + + /* Verify that nothing went to stdout. */ + assertEmptyFile("pack.out"); + + /* + * Use tar to unpack the archive into another directory. + */ + r = systemf("%s xf archive >unpack.out 2>unpack.err", testprog); + failure("Error invoking %s xf archive", testprog); + assertEqualInt(r, 0); + + /* Verify that nothing went to stdout or stderr. */ + assertEmptyFile("unpack.err"); + assertEmptyFile("unpack.out"); + + chdir("original"); + verify_tree(LIMIT_USTAR); + chdir("../.."); +} + +DEFINE_TEST(test_copy) +{ + int oldumask; + + oldumask = umask(0); + + create_tree(); /* Create sample files in "original" dir. */ + + /* Test simple "tar -c | tar -x" pipeline copy. */ + copy_basic(); + + /* Same, but constrain to ustar format. */ + copy_ustar(); + + umask(oldumask); +} diff --git a/usr.bin/tar/test/test_getdate.c b/usr.bin/tar/test/test_getdate.c new file mode 100644 index 0000000..7ed447b --- /dev/null +++ b/usr.bin/tar/test/test_getdate.c @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2003-2007 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +/* + * Verify that the getdate() function works. + */ + +time_t get_date(const char *); + +DEFINE_TEST(test_getdate) +{ + assertEqualInt(0, get_date("Jan 1, 1970 UTC")); + /* TODO: Lots more tests here. */ +} diff --git a/usr.bin/tar/test/test_help.c b/usr.bin/tar/test/test_help.c new file mode 100644 index 0000000..c547dbc --- /dev/null +++ b/usr.bin/tar/test/test_help.c @@ -0,0 +1,81 @@ +/*- + * Copyright (c) 2003-2007 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +/* + * Test that "--help", "-h", and "-W help" options all work and + * generate reasonable output. + */ + +static int +in_first_line(const char *p, const char *substring) +{ + size_t l = strlen(substring); + + while (*p != '\0' && *p != '\n') { + if (memcmp(p, substring, l) == 0) + return (1); + ++p; + } + return (0); +} + +DEFINE_TEST(test_help) +{ + int r; + char *p; + size_t plen; + + /* Exercise --help option. */ + r = systemf("%s --help >help.stdout 2>help.stderr", testprog); + failure("--help should generate nothing to stderr."); + assertEmptyFile("help.stderr"); + /* Help message should start with name of program. */ + p = slurpfile(&plen, "help.stdout"); + failure("Help output should be long enough."); + assert(plen >= 6); + failure("First line of help output should contain 'bsdtar': %s", p); + assert(in_first_line(p, "bsdtar")); + /* + * TODO: Extend this check to further verify that --help output + * looks approximately right. + */ + free(p); + + /* -h option should generate the same output. */ + r = systemf("%s -h >h.stdout 2>h.stderr", testprog); + failure("-h should generate nothing to stderr."); + assertEmptyFile("h.stderr"); + failure("stdout should be same for -h and --help"); + assertEqualFile("h.stdout", "help.stdout"); + + /* -W help should be another synonym. */ + r = systemf("%s -W help >Whelp.stdout 2>Whelp.stderr", testprog); + failure("-W help should generate nothing to stderr."); + assertEmptyFile("Whelp.stderr"); + failure("stdout should be same for -W help and --help"); + assertEqualFile("Whelp.stdout", "help.stdout"); +} diff --git a/usr.bin/tar/test/test_option_T.c b/usr.bin/tar/test/test_option_T.c new file mode 100644 index 0000000..7b57908 --- /dev/null +++ b/usr.bin/tar/test/test_option_T.c @@ -0,0 +1,123 @@ +/*- + * Copyright (c) 2003-2008 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +static int +touch(const char *fn) +{ + int fd = open(fn, O_RDWR | O_CREAT); + failure("Couldn't create file '%s', fd=%d, errno=%d (%s)\n", + fn, fd, errno, strerror(errno)); + if (!assert(fd > 0)) + return (0); /* Failure. */ + close(fd); + return (1); /* Success */ +} + +DEFINE_TEST(test_option_T) +{ + FILE *f; + + /* Create a simple dir heirarchy; bail if anything fails. */ + if (!assertEqualInt(0, mkdir("d1", 0755))) return; + if (!assertEqualInt(0, mkdir("d1/d2", 0755))) return; + if (!touch("d1/f1")) return; + if (!touch("d1/f2")) return; + if (!touch("d1/d2/f3")) return; + if (!touch("d1/d2/f4")) return; + if (!touch("d1/d2/f5")) return; + + /* Populate a file list */ + f = fopen("filelist", "w+"); + if (!assert(f != NULL)) + return; + fprintf(f, "d1/f1\n"); + fprintf(f, "d1/d2/f4\n"); + fclose(f); + + /* Populate a second file list */ + f = fopen("filelist2", "w+"); + if (!assert(f != NULL)) + return; + fprintf(f, "d1/d2/f3\n"); + fprintf(f, "d1/d2/f5\n"); + fclose(f); + + /* Use -c -T to archive up the files. */ + systemf("%s -c -f test1.tar -T filelist > test1.out 2> test1.err", + testprog); + assertEmptyFile("test1.out"); + assertEmptyFile("test1.err"); + + /* Use -x -T to dearchive the files */ + if (!assertEqualInt(0, mkdir("test1", 0755))) return; + systemf("%s -x -f test1.tar -T filelist -C test1" + " > test1b.out 2> test1b.err", testprog); + assertEmptyFile("test1b.out"); + assertEmptyFile("test1b.err"); + + /* Verify the files were extracted. */ + assertFileExists("test1/d1/f1"); + assertFileNotExists("test1/d1/f2"); + assertFileNotExists("test1/d1/d2/f3"); + assertFileExists("test1/d1/d2/f4"); + assertFileNotExists("test1/d1/d2/f5"); + + /* Use -r -T to add more files to the archive. */ + systemf("%s -r -f test1.tar -T filelist2 > test2.out 2> test2.err", + testprog); + assertEmptyFile("test2.out"); + assertEmptyFile("test2.err"); + + /* Use -x without -T to dearchive the files (ensure -r worked) */ + if (!assertEqualInt(0, mkdir("test3", 0755))) return; + systemf("%s -x -f test1.tar -C test3" + " > test3.out 2> test3.err", testprog); + assertEmptyFile("test3.out"); + assertEmptyFile("test3.err"); + /* Verify the files were extracted.*/ + assertFileExists("test3/d1/f1"); + assertFileNotExists("test3/d1/f2"); + assertFileExists("test3/d1/d2/f3"); + assertFileExists("test3/d1/d2/f4"); + assertFileExists("test3/d1/d2/f5"); + + /* Use -x -T to dearchive the files (verify -x -T together) */ + if (!assertEqualInt(0, mkdir("test2", 0755))) return; + systemf("%s -x -f test1.tar -T filelist -C test2" + " > test2b.out 2> test2b.err", testprog); + assertEmptyFile("test2b.out"); + assertEmptyFile("test2b.err"); + /* Verify the files were extracted.*/ + assertFileExists("test2/d1/f1"); + assertFileNotExists("test2/d1/f2"); + assertFileNotExists("test2/d1/d2/f3"); + assertFileExists("test2/d1/d2/f4"); + assertFileNotExists("test2/d1/d2/f5"); + + /* TODO: Include some use of -C directory-changing within the filelist. */ + /* I'm pretty sure -C within the filelist is broken on extract. */ +} diff --git a/usr.bin/tar/test/test_stdio.c b/usr.bin/tar/test/test_stdio.c new file mode 100644 index 0000000..2d24ae3 --- /dev/null +++ b/usr.bin/tar/test/test_stdio.c @@ -0,0 +1,124 @@ +/*- + * Copyright (c) 2003-2007 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +DEFINE_TEST(test_stdio) +{ + int fd; + int filelist; + int oldumask; + int r; + + oldumask = umask(0); + + /* + * Create a couple of files on disk. + */ + filelist = open("filelist", O_CREAT | O_WRONLY, 0644); + /* File */ + fd = open("f", O_CREAT | O_WRONLY, 0644); + assert(fd >= 0); + write(fd, "f\n", 2); + close(fd); + write(filelist, "f\n", 2); + /* Link to above file. */ + assertEqualInt(0, link("f", "l")); + write(filelist, "l\n", 2); + close(filelist); + + /* + * Archive/dearchive with a variety of options, verifying + * stdio paths. + */ + + /* 'cf' should generate no output unless there's an error. */ + r = systemf("%s cf archive f l >cf.out 2>cf.err", testprog); + assertEqualInt(r, 0); + assertEmptyFile("cf.out"); + assertEmptyFile("cf.err"); + + /* 'cvf' should generate file list on stderr, empty stdout. */ + r = systemf("%s cvf archive f l >cvf.out 2>cvf.err", testprog); + assertEqualInt(r, 0); + failure("'cv' writes filenames to stderr, nothing to stdout (SUSv2)\n" + "Note that GNU tar writes the file list to stdout by default."); + assertEmptyFile("cvf.out"); + /* TODO: Verify cvf.err has file list in SUSv2-prescribed format. */ + + /* 'cvf -' should generate file list on stderr, archive on stdout. */ + r = systemf("%s cvf - f l >cvf-.out 2>cvf-.err", testprog); + assertEqualInt(r, 0); + failure("cvf - should write archive to stdout"); + /* TODO: Verify cvf-.out has archive. */ + failure("cvf - should write file list to stderr (SUSv2)"); + /* TODO: Verify cvf-.err has verbose file list. */ + + /* 'tf' should generate file list on stdout, empty stderr. */ + r = systemf("%s tf archive >tf.out 2>tf.err", testprog); + assertEqualInt(r, 0); + assertEmptyFile("tf.err"); + failure("'t' mode should write results to stdout"); + /* TODO: Verify tf.out has file list. */ + + /* 'tvf' should generate file list on stdout, empty stderr. */ + r = systemf("%s tvf archive >tvf.out 2>tvf.err", testprog); + assertEqualInt(r, 0); + assertEmptyFile("tvf.err"); + failure("'tv' mode should write results to stdout"); + /* TODO: Verify tvf.out has file list. */ + + /* 'tvf -' uses stdin, file list on stdout, empty stderr. */ + r = systemf("%s tvf - < archive >tvf-.out 2>tvf-.err", testprog); + assertEqualInt(r, 0); + assertEmptyFile("tvf-.err"); + /* TODO: Verify tvf-.out has file list. */ + + /* Basic 'xf' should generate no output on stdout or stderr. */ + r = systemf("%s xf archive >xf.out 2>xf.err", testprog); + assertEqualInt(r, 0); + assertEmptyFile("xf.err"); + assertEmptyFile("xf.out"); + + /* 'xvf' should generate list on stderr, empty stdout. */ + r = systemf("%s xvf archive >xvf.out 2>xvf.err", testprog); + assertEqualInt(r, 0); + assertEmptyFile("xvf.out"); + /* TODO: Verify xvf.err */ + + /* 'xvOf' should generate list on stderr, file contents on stdout. */ + r = systemf("%s xvOf archive >xvOf.out 2>xvOf.err", testprog); + assertEqualInt(r, 0); + /* TODO: Verify xvOf.out */ + /* TODO: Verify xvf.err */ + + /* 'xvf -' should generate list on stderr, empty stdout. */ + r = systemf("%s xvf - < archive >xvf-.out 2>xvf-.err", testprog); + assertEqualInt(r, 0); + assertEmptyFile("xvf-.out"); + /* TODO: Verify xvf-.err */ + + umask(oldumask); +} diff --git a/usr.bin/tar/test/test_version.c b/usr.bin/tar/test/test_version.c new file mode 100644 index 0000000..6f2f6a0 --- /dev/null +++ b/usr.bin/tar/test/test_version.c @@ -0,0 +1,93 @@ +/*- + * Copyright (c) 2003-2007 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +/* + * Test that --version option works and generates reasonable output. + */ + +DEFINE_TEST(test_version) +{ + int r; + char *p, *q; + size_t s; + + + r = systemf("%s --version >version.stdout 2>version.stderr", testprog); + if (r != 0) + r = systemf("%s -W version >version.stdout 2>version.stderr", + testprog); + failure("Unable to run either %s --version or %s -W version", + testprog, testprog); + if (!assert(r == 0)) + return; + + /* --version should generate nothing to stdout. */ + assertEmptyFile("version.stderr"); + /* Verify format of version message. */ + q = p = slurpfile(&s, "version.stdout"); + /* Version message should start with name of program, then space. */ + assert(s > 6); + failure("Version: %s", p); + assertEqualMem(q, "bsdtar ", 7); + q += 7; s -= 7; + /* Version number is a series of digits and periods. */ + while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) { + ++q; + --s; + } + /* Version number terminated by space. */ + failure("Version: %s", p); + assert(s > 1); + /* Skip a single trailing a,b,c, or d. */ + if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd') + ++q; + failure("Version: %s", p); + assert(*q == ' '); + ++q; --s; + /* Separator. */ + failure("Version: %s", p); + assertEqualMem(q, "- ", 2); + q += 2; s -= 2; + /* libarchive name and version number */ + failure("Version: %s", p); + assert(s > 11); + failure("Version: %s", p); + assertEqualMem(q, "libarchive ", 11); + q += 11; s -= 11; + /* Version number is a series of digits and periods. */ + while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) { + ++q; + --s; + } + /* Skip a single trailing a,b,c, or d. */ + if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd') + ++q; + /* All terminated by a newline. */ + assert(s >= 1); + assertEqualMem(q, "\n", 1); + free(p); +} |