/*------------------------------------------------------------------------- * * pg_regress_ecpg --- regression test driver for ecpg * * This is a C implementation of the previous shell script for running * the regression tests, and should be mostly compatible with it. * Initial author of C translation: Magnus Hagander * * This code is released under the terms of the PostgreSQL License. * * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * src/interfaces/ecpg/test/pg_regress_ecpg.c * *------------------------------------------------------------------------- */ #include "postgres_fe.h" #include "pg_regress.h" #include "common/string.h" #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_source(const char *sourcefile, const char *outfile) { FILE *s, *t; StringInfoData linebuf; s = fopen(sourcefile, "r"); if (!s) { fprintf(stderr, "Could not open file %s for reading\n", sourcefile); exit(2); } t = fopen(outfile, "w"); if (!t) { fprintf(stderr, "Could not open file %s for writing\n", outfile); exit(2); } initStringInfo(&linebuf); while (pg_get_line_buf(s, &linebuf)) { /* check for "#line " in the beginning */ if (strstr(linebuf.data, "#line ") == linebuf.data) { char *p = strchr(linebuf.data, '"'); int plen = 1; while (*p && (*(p + plen) == '.' || strchr(p + plen, '/') != NULL)) { plen++; } /* plen is one more than the number of . and / characters */ if (plen > 1) { memmove(p + 1, p + plen, strlen(p + plen) + 1); /* we don't bother to fix up linebuf.len */ } } fputs(linebuf.data, t); } pfree(linebuf.data); fclose(s); fclose(t); } /* * Remove the details of connection failure 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, "connection to server "); if (p1) { char *p2 = strstr(p1, "failed: "); if (p2) { memmove(p1 + 21, 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 */ static PID_TYPE ecpg_start_test(const char *testname, _stringlist **resultfiles, _stringlist **expectfiles, _stringlist **tags) { PID_TYPE pid; char inprg[MAXPGPATH]; char insource[MAXPGPATH]; StringInfoData testname_dash; char outfile_stdout[MAXPGPATH], expectfile_stdout[MAXPGPATH]; char outfile_stderr[MAXPGPATH], expectfile_stderr[MAXPGPATH]; char outfile_source[MAXPGPATH], expectfile_source[MAXPGPATH]; char cmd[MAXPGPATH * 3]; char *appnameenv; snprintf(inprg, sizeof(inprg), "%s/%s", inputdir, testname); snprintf(insource, sizeof(insource), "%s/%s.c", inputdir, testname); /* make a version of the test name that has dashes in place of slashes */ initStringInfo(&testname_dash); appendStringInfoString(&testname_dash, testname); for (char *c = testname_dash.data; *c != '\0'; c++) { if (*c == '/') *c = '-'; } snprintf(expectfile_stdout, sizeof(expectfile_stdout), "%s/expected/%s.stdout", expecteddir, testname_dash.data); snprintf(expectfile_stderr, sizeof(expectfile_stderr), "%s/expected/%s.stderr", expecteddir, testname_dash.data); snprintf(expectfile_source, sizeof(expectfile_source), "%s/expected/%s.c", expecteddir, testname_dash.data); snprintf(outfile_stdout, sizeof(outfile_stdout), "%s/results/%s.stdout", outputdir, testname_dash.data); snprintf(outfile_stderr, sizeof(outfile_stderr), "%s/results/%s.stderr", outputdir, testname_dash.data); snprintf(outfile_source, sizeof(outfile_source), "%s/results/%s.c", outputdir, testname_dash.data); add_stringlist_item(resultfiles, outfile_stdout); add_stringlist_item(expectfiles, expectfile_stdout); add_stringlist_item(tags, "stdout"); add_stringlist_item(resultfiles, outfile_stderr); add_stringlist_item(expectfiles, expectfile_stderr); add_stringlist_item(tags, "stderr"); add_stringlist_item(resultfiles, outfile_source); add_stringlist_item(expectfiles, expectfile_source); add_stringlist_item(tags, "source"); ecpg_filter_source(insource, outfile_source); snprintf(cmd, sizeof(cmd), "\"%s\" >\"%s\" 2>\"%s\"", inprg, outfile_stdout, outfile_stderr); appnameenv = psprintf("ecpg/%s", testname_dash.data); setenv("PGAPPNAME", appnameenv, 1); free(appnameenv); pid = spawn_process(cmd); if (pid == INVALID_PID) { fprintf(stderr, _("could not start process for test %s\n"), testname); exit(2); } unsetenv("PGAPPNAME"); free(testname_dash.data); 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[]) { /* nothing to do here at the moment */ } int main(int argc, char *argv[]) { return regression_main(argc, argv, ecpg_init, ecpg_start_test, ecpg_postprocess_result); }