441 lines
9.3 KiB
C
441 lines
9.3 KiB
C
/*
|
|
* A test driver for the psqlodbc regression tests.
|
|
*
|
|
* This program runs one regression tests from the exe/ directory,
|
|
* and compares the output with the expected output in the expected/ directory.
|
|
* Reports success or failure in TAP compatible fashion.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#ifndef WIN32
|
|
#include <unistd.h>
|
|
#endif
|
|
#include <stdarg.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#ifdef WIN32
|
|
#include <io.h>
|
|
#define open _open
|
|
#define read _read
|
|
#define close _close
|
|
#define snprintf _snprintf
|
|
#define vsnprintf _vsnprintf
|
|
#define strdup _strdup
|
|
#endif
|
|
|
|
static int rundiff(const char *testname, const char *inputdir);
|
|
static int runtest(const char *binname, const char *testname, int testno, const char *inputdir);
|
|
|
|
static char *slurpfile(const char *filename, size_t *len);
|
|
|
|
static void
|
|
bailout(const char *fmt, ...)
|
|
{
|
|
va_list argp;
|
|
|
|
va_start(argp, fmt);
|
|
|
|
printf("Bail out! ");
|
|
vprintf(fmt, argp);
|
|
|
|
va_end(argp);
|
|
|
|
exit(1);
|
|
}
|
|
|
|
#ifdef WIN32
|
|
#define DIR_SEP '\\'
|
|
#else
|
|
#define DIR_SEP '/'
|
|
#endif
|
|
|
|
/* Given a test program's name, get the test name */
|
|
void
|
|
parse_argument(const char *in, char *testname, char *binname)
|
|
{
|
|
const char *basename;
|
|
#ifdef WIN32
|
|
const char *suffix = "-test.exe";
|
|
#else
|
|
const char *suffix = "-test";
|
|
#endif
|
|
size_t len;
|
|
|
|
/* if the input is a plain test name, construct the binary name from it */
|
|
if (strchr(in, DIR_SEP) == NULL)
|
|
{
|
|
strcpy(testname, in);
|
|
sprintf(binname, "exe%c%s-test", DIR_SEP, in);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Otherwise the input is a binary name, and we'll construct the test name
|
|
* from it.
|
|
*/
|
|
strcpy(binname, in);
|
|
|
|
/* Find the last / or \ character */
|
|
basename = strrchr(in, DIR_SEP) + 1;
|
|
|
|
/* Strip -test or -test.exe suffix */
|
|
if (strlen(basename) <= strlen(suffix))
|
|
{
|
|
strcpy(testname, basename);
|
|
return;
|
|
}
|
|
|
|
len = strlen(basename) - strlen(suffix);
|
|
if (strcmp(&basename[len], suffix) != 0)
|
|
{
|
|
strcpy(testname, basename);
|
|
return;
|
|
}
|
|
|
|
memcpy(testname, basename, len);
|
|
testname[len] = '\0';
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
char binname[1000];
|
|
char testname[100];
|
|
int numtests;
|
|
int i, j;
|
|
int failures;
|
|
const char *inputdir = ".";
|
|
int sub_count = 1;
|
|
|
|
if (argc < 2)
|
|
{
|
|
printf("Usage: runsuite <test binary> ...\n");
|
|
exit(1);
|
|
}
|
|
if (strncmp(argv[argc - 1], "--inputdir=", 11) == 0)
|
|
{
|
|
sub_count++;
|
|
inputdir = argv[argc - 1] + 11;
|
|
}
|
|
numtests = argc - sub_count;
|
|
|
|
printf("TAP version 13\n");
|
|
printf("1..%d\n", numtests);
|
|
|
|
/*
|
|
* We accept either test binary name or plain test name.
|
|
*/
|
|
failures = 0;
|
|
for (i = 1, j = 1; i <= numtests; i++, j++)
|
|
{
|
|
parse_argument(argv[j], testname, binname);
|
|
if (runtest(binname, testname, i, inputdir) != 0)
|
|
failures++;
|
|
}
|
|
|
|
exit(failures > 254 ? 254 : failures);
|
|
}
|
|
|
|
/* Return 0 on success, 1 on failure */
|
|
static int
|
|
runtest(const char *binname, const char *testname, int testno, const char *inputdir)
|
|
{
|
|
char cmdline[1024];
|
|
int rc;
|
|
int ret;
|
|
int diff;
|
|
|
|
/*
|
|
* ODBCSYSINI=. tells unixodbc where to find the driver config file,
|
|
* odbcinst.ini
|
|
*
|
|
* ODBCINSTINI=./odbcinst.ini tells the same for iodbc. iodbc also requires
|
|
* ODBCINI=./odbc.ini to tell it where to find the datasource config.
|
|
*
|
|
* We wouldn't need to iodbc stuff when building with unixodbc and vice
|
|
* versa, but it doesn't hurt either.
|
|
*/
|
|
#ifndef WIN32
|
|
snprintf(cmdline, sizeof(cmdline),
|
|
"ODBCSYSINI=. ODBCINSTINI=./odbcinst.ini ODBCINI=./odbc.ini "
|
|
"%s > results/%s.out",
|
|
binname, testname);
|
|
#else
|
|
snprintf(cmdline, sizeof(cmdline),
|
|
"%s > results\\%s.out",
|
|
binname, testname);
|
|
#endif
|
|
rc = system(cmdline);
|
|
|
|
diff = rundiff(testname, inputdir);
|
|
if (rc != 0)
|
|
{
|
|
printf("not ok %d - %s test returned %d\n", testno, testname, rc);
|
|
ret = 1;
|
|
}
|
|
else if (diff != 0)
|
|
{
|
|
printf("not ok %d - %s test output differs\n", testno, testname);
|
|
ret = 1;
|
|
}
|
|
else
|
|
{
|
|
printf("ok %d - %s\n", testno, testname);
|
|
ret = 0;
|
|
}
|
|
fflush(stdout);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef WIN32
|
|
static int diff_call = 1, first_call = 1;
|
|
#endif /* WIN32 */
|
|
|
|
static int
|
|
call_diff(const char *inputdir, const char *expected_dir, const char *testname, int outputno, const char *result_dir, const char *outspec)
|
|
{
|
|
char no_str[8];
|
|
char cmdline[1024];
|
|
int diff_rtn;
|
|
|
|
if (0 == outputno)
|
|
*no_str = '\0';
|
|
else
|
|
snprintf(no_str, sizeof(no_str), "_%d", outputno);
|
|
#ifdef WIN32
|
|
if (diff_call)
|
|
snprintf(cmdline, sizeof(cmdline),
|
|
"diff -c --strip-trailing-cr %s%s%s%s.out %s%s.out %s",
|
|
inputdir, expected_dir, testname, no_str, result_dir, testname, outspec);
|
|
else /* use fc command instead */
|
|
snprintf(cmdline, sizeof(cmdline),
|
|
"fc /N %s%s%s%s.out %s%s.out %s",
|
|
inputdir, expected_dir, testname, no_str, result_dir, testname, outspec);
|
|
#else
|
|
snprintf(cmdline, sizeof(cmdline),
|
|
"diff -c %s%s%s%s.out %s%s.out %s",
|
|
inputdir, expected_dir, testname, no_str, result_dir, testname, outspec);
|
|
#endif /* WIN32 */
|
|
if ((diff_rtn = system(cmdline)) == -1)
|
|
printf("# diff failed\n");
|
|
|
|
return diff_rtn;
|
|
}
|
|
|
|
static int
|
|
rundiff(const char *testname, const char *inputdir)
|
|
{
|
|
char filename[1024];
|
|
int outputno, no_spec;
|
|
char *result = NULL;
|
|
size_t result_len;
|
|
#ifdef WIN32
|
|
const char *expected_dir = "\\expected\\";
|
|
const char *result_dir = "results\\";
|
|
#else
|
|
const char *expected_dir = "/expected/";
|
|
const char *result_dir = "results/";
|
|
#endif
|
|
int diff_rtn;
|
|
int i, j;
|
|
const char CR = '\r', LF = '\n';
|
|
char se, sr;
|
|
|
|
snprintf(filename, sizeof(filename), "%s%s.out", result_dir, testname);
|
|
result = slurpfile(filename, &result_len);
|
|
|
|
outputno = 0;
|
|
for (;;)
|
|
{
|
|
char *expected;
|
|
size_t expected_len;
|
|
|
|
if (outputno == 0)
|
|
snprintf(filename, sizeof(filename), "%s%s%s.out", inputdir, expected_dir, testname);
|
|
else
|
|
snprintf(filename, sizeof(filename), "%s%s%s_%d.out", inputdir, expected_dir, testname, outputno);
|
|
expected = slurpfile(filename, &expected_len);
|
|
if (expected == NULL)
|
|
{
|
|
if (outputno == 0)
|
|
bailout("could not open file %s: %s\n", filename, strerror(ENOENT));
|
|
break;
|
|
}
|
|
|
|
if (expected_len == result_len &&
|
|
memcmp(expected, result, expected_len) == 0)
|
|
{
|
|
/* The files are equal. */
|
|
free(result);
|
|
free(expected);
|
|
return 0;
|
|
}
|
|
/* Ignore the difference between CR LF, LF and CR line break */
|
|
for (i = 0, j = 0, se = sr = '\0'; i < expected_len && j < result_len;
|
|
se = expected[i], sr = result[j], i++, j++)
|
|
{
|
|
if (expected[i] == result[j])
|
|
continue;
|
|
if (result[j] == LF)
|
|
{
|
|
if (expected[i] == CR)
|
|
{
|
|
i++;
|
|
if (expected[i] != LF)
|
|
i--;
|
|
continue;
|
|
}
|
|
else if (sr == CR && se == CR)
|
|
{
|
|
i--;
|
|
continue;
|
|
}
|
|
}
|
|
else if (expected[i] == LF)
|
|
{
|
|
if (result[j] == CR)
|
|
{
|
|
j++;
|
|
if (result[j] != LF)
|
|
j--;
|
|
continue;
|
|
}
|
|
else if (sr == CR && se == CR)
|
|
{
|
|
j--;
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
if (i >= expected_len && j >= result_len)
|
|
{
|
|
/* The files are equal. */
|
|
free(result);
|
|
free(expected);
|
|
return 0;
|
|
}
|
|
|
|
free(expected);
|
|
|
|
outputno++;
|
|
}
|
|
/* no matching output found */
|
|
if (NULL != result)
|
|
free(result);
|
|
|
|
/*
|
|
* Try to run diff. If this fails on Windows system, use fc command
|
|
* instead.
|
|
*/
|
|
#ifdef WIN32
|
|
if (first_call)
|
|
{
|
|
char cmdline[1024];
|
|
|
|
/*
|
|
* test diff the same file
|
|
*/
|
|
snprintf(cmdline, sizeof(cmdline),
|
|
"diff -c --strip-trailing-cr results\\%s.out results\\%s.out",
|
|
testname, testname);
|
|
first_call = 0;
|
|
/*
|
|
* If diff command exists, the system function would
|
|
* return 0.
|
|
*/
|
|
if (system(cmdline) != 0)
|
|
diff_call = 0;
|
|
}
|
|
#endif
|
|
/*
|
|
* We run the diff against all output files and print the smallest
|
|
* diff.
|
|
*/
|
|
no_spec = 0;
|
|
if (outputno > 1)
|
|
{
|
|
const char *tmpdiff = "tmpdiff.diffs";
|
|
char outfmt[32];
|
|
int fd, file_size;
|
|
struct stat stbuf;
|
|
int min_size = -1;
|
|
|
|
snprintf(outfmt, sizeof(outfmt), "> %s", tmpdiff);
|
|
for (i = 0; i < outputno; i++)
|
|
{
|
|
call_diff(inputdir, expected_dir, testname, i, result_dir, outfmt);
|
|
if ((fd = open(tmpdiff, O_RDONLY)) < 0)
|
|
break;
|
|
if (fstat(fd, &stbuf) == -1)
|
|
break;
|
|
if (file_size = stbuf.st_size, file_size == 0)
|
|
{
|
|
min_size = 0;
|
|
no_spec = i;
|
|
}
|
|
else if (min_size < 0)
|
|
min_size = file_size;
|
|
else if (file_size < min_size)
|
|
{
|
|
no_spec = i;
|
|
min_size = file_size;
|
|
}
|
|
close(fd);
|
|
}
|
|
remove(tmpdiff);
|
|
if (min_size == 0)
|
|
return 0;
|
|
}
|
|
diff_rtn = call_diff(inputdir, expected_dir, testname, no_spec, result_dir, ">> regression.diffs");
|
|
|
|
return diff_rtn;
|
|
}
|
|
|
|
|
|
/*
|
|
* Reads file to memory. The file is returned, or NULL if it doesn't exist.
|
|
* Length is returned in *len.
|
|
*/
|
|
static char *
|
|
slurpfile(const char *filename, size_t *len)
|
|
{
|
|
int fd;
|
|
struct stat stbuf;
|
|
int readlen;
|
|
off_t filelen;
|
|
char *p;
|
|
|
|
#ifdef WIN32
|
|
fd = open(filename, O_RDONLY | O_BINARY, 0);
|
|
#else
|
|
fd = open(filename, O_RDONLY, 0);
|
|
#endif
|
|
if (fd == -1)
|
|
{
|
|
if (errno == ENOENT)
|
|
return NULL;
|
|
|
|
bailout("could not open file %s: %s\n", filename, strerror(errno));
|
|
}
|
|
if (fstat(fd, &stbuf) < 0)
|
|
bailout("fstat failed on file %s: %s\n", filename, strerror(errno));
|
|
|
|
filelen = stbuf.st_size;
|
|
p = malloc(filelen + 1);
|
|
if (!p)
|
|
bailout("out of memory reading file %s\n", filename);
|
|
readlen = read(fd, p, filelen);
|
|
if (readlen != filelen)
|
|
bailout("read only %d bytes out of %d from %s\n", (int) readlen, (int) filelen, filename);
|
|
p[readlen] = '\0';
|
|
close(fd);
|
|
|
|
*len = readlen;
|
|
return p;
|
|
}
|