/* * 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 #include #include #ifndef WIN32 #include #endif #include #include #include #include #include #ifdef WIN32 #include #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 ...\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; }