#include "lib/stringinfo.h"
+/*
+ * Create a filtered copy of sourcefile, removing any path
+ * appearing in #line directives; for example, replace
+ * #line x "./../bla/foo.h" with #line x "foo.h".
+ * This is needed because the path part can vary depending
+ * on compiler, platform, build options, etc.
+ */
static void
-ecpg_filter(const char *sourcefile, const char *outfile)
+ecpg_filter_source(const char *sourcefile, const char *outfile)
{
- /*
- * Create a filtered copy of sourcefile, replacing #line x
- * "./../bla/foo.h" with #line x "foo.h"
- */
FILE *s,
*t;
StringInfoData linebuf;
fclose(t);
}
+/*
+ * Remove the details of "could not connect to ...: " error messages
+ * in a test result file, since the target host/pathname and/or port
+ * can vary. Rewrite the result file in-place.
+ *
+ * At some point it might be interesting to unify this with
+ * ecpg_filter_source, but building a general pattern matcher
+ * is no fun, nor does it seem desirable to introduce a
+ * dependency on an external one.
+ */
+static void
+ecpg_filter_stderr(const char *resultfile, const char *tmpfile)
+{
+ FILE *s,
+ *t;
+ StringInfoData linebuf;
+
+ s = fopen(resultfile, "r");
+ if (!s)
+ {
+ fprintf(stderr, "Could not open file %s for reading\n", resultfile);
+ exit(2);
+ }
+ t = fopen(tmpfile, "w");
+ if (!t)
+ {
+ fprintf(stderr, "Could not open file %s for writing\n", tmpfile);
+ exit(2);
+ }
+
+ initStringInfo(&linebuf);
+
+ while (pg_get_line_buf(s, &linebuf))
+ {
+ char *p1 = strstr(linebuf.data, "could not connect to ");
+
+ if (p1)
+ {
+ char *p2 = strstr(p1, ": ");
+
+ if (p2)
+ {
+ memmove(p1 + 17, p2, strlen(p2) + 1);
+ /* we don't bother to fix up linebuf.len */
+ }
+ }
+ fputs(linebuf.data, t);
+ }
+
+ pfree(linebuf.data);
+ fclose(s);
+ fclose(t);
+ if (rename(tmpfile, resultfile) != 0)
+ {
+ fprintf(stderr, "Could not overwrite file %s with %s\n",
+ resultfile, tmpfile);
+ exit(2);
+ }
+}
+
/*
* start an ecpg test process for specified file (including redirection),
* and return process ID
add_stringlist_item(expectfiles, expectfile_source);
add_stringlist_item(tags, "source");
- ecpg_filter(insource, outfile_source);
+ ecpg_filter_source(insource, outfile_source);
snprintf(cmd, sizeof(cmd),
"\"%s\" >\"%s\" 2>\"%s\"",
return pid;
}
+static void
+ecpg_postprocess_result(const char *filename)
+{
+ int nlen = strlen(filename);
+
+ /* Only stderr files require filtering, at the moment */
+ if (nlen > 7 && strcmp(filename + nlen - 7, ".stderr") == 0)
+ {
+ char *tmpfile = psprintf("%s.tmp", filename);
+
+ ecpg_filter_stderr(filename, tmpfile);
+ pfree(tmpfile);
+ }
+}
+
static void
ecpg_init(int argc, char *argv[])
{
int
main(int argc, char *argv[])
{
- return regression_main(argc, argv, ecpg_init, ecpg_start_test);
+ return regression_main(argc, argv,
+ ecpg_init,
+ ecpg_start_test,
+ ecpg_postprocess_result);
}
int
main(int argc, char *argv[])
{
- return regression_main(argc, argv, isolation_init, isolation_start_test);
+ return regression_main(argc, argv,
+ isolation_init,
+ isolation_start_test,
+ NULL /* no postfunc needed */ );
}
initialize_environment(void)
{
/*
- * Set default application_name. (The test_function may choose to
+ * Set default application_name. (The test_start_function may choose to
* override this, but if it doesn't, we have something useful in place.)
*/
setenv("PGAPPNAME", "pg_regress", 1);
* Run all the tests specified in one schedule file
*/
static void
-run_schedule(const char *schedule, test_function tfunc)
+run_schedule(const char *schedule, test_start_function startfunc,
+ postprocess_result_function postfunc)
{
#define MAX_PARALLEL_TESTS 100
char *tests[MAX_PARALLEL_TESTS];
if (num_tests == 1)
{
status(_("test %-28s ... "), tests[0]);
- pids[0] = (tfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]);
+ pids[0] = (startfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]);
INSTR_TIME_SET_CURRENT(starttimes[0]);
wait_for_tests(pids, statuses, stoptimes, NULL, 1);
/* status line is finished below */
tests + oldest, i - oldest);
oldest = i;
}
- pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
+ pids[i] = (startfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
INSTR_TIME_SET_CURRENT(starttimes[i]);
}
wait_for_tests(pids + oldest, statuses + oldest,
status(_("parallel group (%d tests): "), num_tests);
for (i = 0; i < num_tests; i++)
{
- pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
+ pids[i] = (startfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
INSTR_TIME_SET_CURRENT(starttimes[i]);
}
wait_for_tests(pids, statuses, stoptimes, tests, num_tests);
{
bool newdiff;
+ if (postfunc)
+ (*postfunc) (rl->str);
newdiff = results_differ(tests[i], rl->str, el->str);
if (newdiff && tl)
{
* Run a single test
*/
static void
-run_single_test(const char *test, test_function tfunc)
+run_single_test(const char *test, test_start_function startfunc,
+ postprocess_result_function postfunc)
{
PID_TYPE pid;
instr_time starttime;
bool differ = false;
status(_("test %-28s ... "), test);
- pid = (tfunc) (test, &resultfiles, &expectfiles, &tags);
+ pid = (startfunc) (test, &resultfiles, &expectfiles, &tags);
INSTR_TIME_SET_CURRENT(starttime);
wait_for_tests(&pid, &exit_status, &stoptime, NULL, 1);
{
bool newdiff;
+ if (postfunc)
+ (*postfunc) (rl->str);
newdiff = results_differ(test, rl->str, el->str);
if (newdiff && tl)
{
}
int
-regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc)
+regression_main(int argc, char *argv[],
+ init_function ifunc,
+ test_start_function startfunc,
+ postprocess_result_function postfunc)
{
static struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
for (sl = schedulelist; sl != NULL; sl = sl->next)
{
- run_schedule(sl->str, tfunc);
+ run_schedule(sl->str, startfunc, postfunc);
}
for (sl = extra_tests; sl != NULL; sl = sl->next)
{
- run_single_test(sl->str, tfunc);
+ run_single_test(sl->str, startfunc, postfunc);
}
/*
struct _stringlist *next;
} _stringlist;
-typedef PID_TYPE(*test_function) (const char *,
- _stringlist **,
- _stringlist **,
- _stringlist **);
+/*
+ * Callback function signatures for test programs that use regression_main()
+ */
+
+/* Initialize at program start */
typedef void (*init_function) (int argc, char **argv);
+/* Launch one test case */
+typedef PID_TYPE(*test_start_function) (const char *testname,
+ _stringlist **resultfiles,
+ _stringlist **expectfiles,
+ _stringlist **tags);
+
+/* Postprocess one result file (optional) */
+typedef void (*postprocess_result_function) (const char *filename);
+
+
extern char *bindir;
extern char *libdir;
extern char *datadir;
extern const char *pretty_diff_opts;
int regression_main(int argc, char *argv[],
- init_function ifunc, test_function tfunc);
+ init_function ifunc,
+ test_start_function startfunc,
+ postprocess_result_function postfunc);
+
void add_stringlist_item(_stringlist **listhead, const char *str);
PID_TYPE spawn_process(const char *cmdline);
void replace_string(struct StringInfoData *string,
int
main(int argc, char *argv[])
{
- return regression_main(argc, argv, psql_init, psql_start_test);
+ return regression_main(argc, argv,
+ psql_init,
+ psql_start_test,
+ NULL /* no postfunc needed */ );
}