v5-0001-pg_regress-TAP-output-format.patch
application/octet-stream
Filename: v5-0001-pg_regress-TAP-output-format.patch
Type: application/octet-stream
Part: 0
Message:
Re: TAP output format in pg_regress
Patch
Format: format-patch
Series: patch v5-0001
Subject: pg_regress TAP output format
| File | + | − |
|---|---|---|
| src/test/regress/pg_regress.c | 536 | 209 |
From fea1f31702bed856aa32f3e5f3cec19ace8c8bba Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Wed, 29 Jun 2022 21:44:52 +0200
Subject: [PATCH v5] pg_regress TAP output format
TAP is now a supported output format for pg_regress test runs on top
of the existing format which is called regress in this patch. Format
is selected via a new --format=STRING option to pg_regress which can
take tap and regress and parameters. Output formats are implemented
via a set of function pointers which implements an output API. Using
NULL for all functions will generate a silent mode, but thats not a
part of this commit.
This also brings a new parameter to pg_regress for deciding verbose
output, since TAP doesn't have a verbose mode (and the existing mode
was deemed overly chatty).
As all output from pg_regress had to be addressed, the frontend log
framework was also brought to use as it was already being set up but
not used.
---
src/test/regress/pg_regress.c | 745 ++++++++++++++++++++++++----------
1 file changed, 536 insertions(+), 209 deletions(-)
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 982801e029..265f9e6148 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -74,6 +74,7 @@ const char *pretty_diff_opts = "-w -U3";
/* options settable from command line */
_stringlist *dblist = NULL;
bool debug = false;
+bool verbose = false;
char *inputdir = ".";
char *outputdir = ".";
char *bindir = PGBINDIR;
@@ -95,6 +96,9 @@ static char *dlpath = PKGLIBDIR;
static char *user = NULL;
static _stringlist *extraroles = NULL;
static char *config_auth_datadir = NULL;
+static char *format = NULL;
+static char *psql_formatting = NULL;
+static bool has_status = false;
/* internal variables */
static const char *progname;
@@ -120,11 +124,96 @@ static int fail_ignore_count = 0;
static bool directory_exists(const char *dir);
static void make_directory(const char *dir);
-static void header(const char *fmt,...) pg_attribute_printf(1, 2);
+typedef enum
+{
+ COMMENT_TEST_STATUS = 0,
+ COMMENT_DIAGNOSTIC,
+ COMMENT_ERROR,
+ COMMENT_VERBOSE
+} CommentLevel;
+
+/*
+ * Each supported output format implements the below functions and supplies
+ * a filled out output_func structure. All output functions are voluntary to
+ * implement, using an output_func structure with all NULLs will generate no
+ * output except for program errors.
+ */
+struct output_func
+{
+ void (*header)(const char *line);
+ void (*footer)(const char *difffilename, const char *logfilename);
+ void (*comment)(CommentLevel level, const char *comment);
+
+ void (*test_status_preamble)(const char *testname);
+
+ void (*test_status_ok)(const char *testname);
+ void (*test_status_failed)(const char *testname, bool ignore, char *tags);
+
+ void (*test_runtime)(const char *testname, double runtime);
+ void (*bail)(const char *message);
+};
+
+/*
+ * Main output functions which in turn operate on the function pointers in the
+ * output_func which controls the format.
+ */
+static void header(const char *fmt,...);
+static void footer(const char *difffilename, const char *logfilename);
+static void comment(CommentLevel level, const char *fmt,...);
+static void test_status_preamble(const char *testname);
+static void test_status_ok(const char *testname);
+static void test_status_failed(const char *testname, bool ignore, char *tags);
+static void runtime(const char *testname, double runtime);
+static void bail(const char *fmt,...);
+
+/* Text output format */
+static void header_text(const char *line);
+static void footer_text(const char *difffilename, const char *logfilename);
+static void comment_text(CommentLevel level, const char *comment);
+static void test_status_preamble_text(const char *testname);
+static void test_status_ok_text(const char *testname);
+static void test_status_failed_text(const char *testname, bool ignore, char *tags);
+static void test_runtime_text(const char *testname, double runtime);
+static void bail_text(const char *message);
+
+struct output_func output_func_text =
+{
+ header_text,
+ footer_text,
+ comment_text,
+ test_status_preamble_text,
+ test_status_ok_text,
+ test_status_failed_text,
+ test_runtime_text,
+ bail_text
+};
+
+/* TAP output format */
+static void footer_tap(const char *difffilename, const char *logfilename);
+static void comment_tap(CommentLevel level, const char *comment);
+static void test_status_ok_tap(const char *testname);
+static void test_status_failed_tap(const char *testname, bool ignore, char *tags);
+static void bail_tap(const char *message);
+
+struct output_func output_func_tap =
+{
+ NULL,
+ footer_tap,
+ comment_tap,
+ NULL,
+ test_status_ok_tap,
+ test_status_failed_tap,
+ NULL,
+ bail_tap
+};
+
+struct output_func *output = &output_func_text;
+
static void status(const char *fmt,...) pg_attribute_printf(1, 2);
static StringInfo psql_start_command(void);
static void psql_add_command(StringInfo buf, const char *query,...) pg_attribute_printf(2, 3);
static void psql_end_command(StringInfo buf, const char *database);
+static void status_end(void);
/*
* allow core files if possible.
@@ -138,7 +227,7 @@ unlimit_core_size(void)
getrlimit(RLIMIT_CORE, &lim);
if (lim.rlim_max == 0)
{
- fprintf(stderr,
+ comment(COMMENT_VERBOSE,
_("%s: could not set core size: disallowed by hard limit\n"),
progname);
return;
@@ -208,18 +297,293 @@ split_to_stringlist(const char *s, const char *delim, _stringlist **listhead)
/*
* Print a progress banner on stdout.
*/
+static void
+header_text(const char *line)
+{
+ if (!verbose)
+ return;
+
+ fprintf(stdout, "============== %-38s ==============\n", line);
+ fflush(stdout);
+}
+
static void
header(const char *fmt,...)
{
char tmp[64];
va_list ap;
+ if (!output->header)
+ return;
+
va_start(ap, fmt);
vsnprintf(tmp, sizeof(tmp), fmt, ap);
va_end(ap);
- fprintf(stdout, "============== %-38s ==============\n", tmp);
- fflush(stdout);
+ output->header(tmp);
+}
+
+static void
+footer_text(const char *difffilename, const char *logfilename)
+{
+ char buf[256];
+
+ /*
+ * Emit nice-looking summary message
+ */
+ if (fail_count == 0 && fail_ignore_count == 0)
+ snprintf(buf, sizeof(buf),
+ _("All %d tests passed. "),
+ success_count);
+ else if (fail_count == 0) /* fail_count=0, fail_ignore_count>0 */
+ snprintf(buf, sizeof(buf),
+ _("%d of %d tests passed, %d failed test(s) ignored. "),
+ success_count,
+ success_count + fail_ignore_count,
+ fail_ignore_count);
+ else if (fail_ignore_count == 0) /* fail_count>0 && fail_ignore_count=0 */
+ snprintf(buf, sizeof(buf),
+ _("%d of %d tests failed. "),
+ fail_count,
+ success_count + fail_count);
+ else
+ /* fail_count>0 && fail_ignore_count>0 */
+ snprintf(buf, sizeof(buf),
+ _("%d of %d tests failed, %d of these failures ignored. "),
+ fail_count + fail_ignore_count,
+ success_count + fail_count + fail_ignore_count,
+ fail_ignore_count);
+
+ if (verbose)
+ {
+ putchar('\n');
+ for (int i = strlen(buf); i > 0; i--)
+ putchar('=');
+ printf("\n %s\n", buf);
+ for (int i = strlen(buf); i > 0; i--)
+ putchar('=');
+ putchar('\n');
+ putchar('\n');
+
+ if (difffilename && logfilename)
+ {
+ printf(_("The differences that caused some tests to fail can be viewed in the\n"
+ "file \"%s\". A copy of the test summary that you see\n"
+ "above is saved in the file \"%s\".\n\n"),
+ difffilename, logfilename);
+ }
+ }
+ else
+ {
+ printf("\n%s\n", buf);
+ if (difffilename && logfilename)
+ printf(_("Failing diffs: \"%s\"\nTest summary: \"%s\"\n"),
+ difffilename, logfilename);
+ }
+}
+
+static void
+footer_tap(const char *difffilename, const char *logfilename)
+{
+ status("1..%i\n", (fail_count + fail_ignore_count + success_count));
+ status_end();
+}
+
+static void
+footer(const char *difffilename, const char *logfilename)
+{
+ if (!output->footer)
+ return;
+
+ output->footer(difffilename, logfilename);
+}
+
+static void
+comment_text(CommentLevel level, const char *comment)
+{
+ if (level == COMMENT_VERBOSE && !verbose)
+ return;
+
+ if (level == COMMENT_ERROR)
+ pg_log_error("%s", comment);
+ else
+ status("%s", comment);
+}
+
+static void
+comment_tap(CommentLevel level, const char *comment)
+{
+ /* The TAP format doesn't have verbose output */
+ if (level == COMMENT_VERBOSE)
+ return;
+
+ /*
+ * Diagnostic comments are defined to start on a new line. Errors are
+ * treated as diagnostics for the current test as there isn't a generic
+ * error output defined in the TAP specification.
+ */
+ if (level == COMMENT_DIAGNOSTIC || level == COMMENT_ERROR)
+ status("\n");
+
+ status("# %s", comment);
+}
+
+static void
+comment(CommentLevel level, const char *fmt,...)
+{
+ char tmp[256];
+ va_list ap;
+
+ if (!output->comment)
+ return;
+
+ va_start(ap, fmt);
+ vsnprintf(tmp, sizeof(tmp), fmt, ap);
+ va_end(ap);
+
+ output->comment(level, tmp);
+}
+
+/*
+ * Bailing out is for unrecoverable errors which prevents further testing to
+ * occur and after which the test run should be aborted.
+ */
+static void
+bail(const char *fmt,...)
+{
+ char tmp[256];
+ va_list ap;
+
+ if (output->bail)
+ {
+ va_start(ap, fmt);
+ vsnprintf(tmp, sizeof(tmp), fmt, ap);
+ va_end(ap);
+
+ output->bail(tmp);
+ }
+
+ status_end();
+ exit(2);
+}
+
+static void
+bail_tap(const char *message)
+{
+ status("\nBail out! %s", message);
+}
+
+static void
+bail_text(const char *message)
+{
+ pg_log_error("%s", message);
+}
+
+static void
+test_status_preamble_text(const char *testname)
+{
+ status(_("test %-28s ... "), testname);
+}
+
+static void
+test_status_preamble(const char *testname)
+{
+ if (!output->test_status_preamble)
+ return;
+
+ output->test_status_preamble(testname);
+}
+
+static void
+test_status_ok_tap(const char *testname)
+{
+ /* There is no NLS translation here as "ok" is a protocol message */
+ status("ok %i - %s",
+ (fail_count + fail_ignore_count + success_count),
+ testname);
+}
+
+static void
+test_status_ok_text(const char *testname)
+{
+ (void) testname; /* unused */
+ status(_("ok ")); /* align with FAILED */
+}
+
+static void
+test_status_ok(const char *testname)
+{
+ success_count++;
+ if (!output->test_status_ok)
+ return;
+
+ output->test_status_ok(testname);
+}
+
+static void
+test_status_failed_tap(const char *testname, bool ignore, char *tags)
+{
+ /* There is no NLS translation here as "(not) ok" are protocol messages */
+ if (ignore)
+ {
+ status("ok %i - %s ",
+ (fail_count + fail_ignore_count + success_count),
+ testname);
+ comment(COMMENT_TEST_STATUS, "SKIP (ignored)");
+ }
+ else
+ status("not ok %i - %s",
+ (fail_count + fail_ignore_count + success_count),
+ testname);
+
+ if (tags && strlen(tags) > 0)
+ {
+ status("\n");
+ comment(COMMENT_DIAGNOSTIC, "tags: %s", tags);
+ }
+}
+
+
+static void
+test_status_failed_text(const char *testname, bool ignore, char *tags)
+{
+ if (tags && strlen(tags) > 0)
+ status("%s ", tags);
+
+ if (ignore)
+ status(_("failed (ignored)"));
+ else
+ status(_("FAILED"));
+}
+
+static void
+test_status_failed(const char *testname, bool ignore, char *tags)
+{
+ if (ignore)
+ fail_ignore_count++;
+ else
+ fail_count++;
+
+ if (!output->test_status_failed)
+ return;
+
+ output->test_status_failed(testname, ignore, tags);
+}
+
+static void
+test_runtime_text(const char *testname, double runtime)
+{
+ (void)testname;
+ status(_(" %8.0f ms"), runtime);
+}
+
+static void
+runtime(const char *testname, double runtime)
+{
+ if (!output->test_runtime)
+ return;
+
+ output->test_runtime(testname, runtime);
}
/*
@@ -230,6 +594,7 @@ status(const char *fmt,...)
{
va_list ap;
+ has_status = true;
va_start(ap, fmt);
vfprintf(stdout, fmt, ap);
fflush(stdout);
@@ -249,6 +614,10 @@ status(const char *fmt,...)
static void
status_end(void)
{
+ if (!has_status)
+ return;
+
+ has_status = false;
fprintf(stdout, "\n");
fflush(stdout);
if (logfile)
@@ -279,8 +648,7 @@ stop_postmaster(void)
r = system(buf);
if (r != 0)
{
- fprintf(stderr, _("\n%s: could not stop postmaster: exit code was %d\n"),
- progname, r);
+ pg_log_error("could not stop postmaster: exit code was %d", r);
_exit(2); /* not exit(), that could be recursive */
}
@@ -340,9 +708,8 @@ make_temp_sockdir(void)
temp_sockdir = mkdtemp(template);
if (temp_sockdir == NULL)
{
- fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
- progname, template, strerror(errno));
- exit(2);
+ bail(_("could not create directory \"%s\": %s"),
+ template, strerror(errno));
}
/* Stage file names for remove_temp(). Unsafe in a signal handler. */
@@ -465,9 +832,8 @@ load_resultmap(void)
/* OK if it doesn't exist, else complain */
if (errno == ENOENT)
return;
- fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
- progname, buf, strerror(errno));
- exit(2);
+ bail(_("could not open file \"%s\" for reading: %s"),
+ buf, strerror(errno));
}
while (fgets(buf, sizeof(buf), f))
@@ -486,26 +852,20 @@ load_resultmap(void)
file_type = strchr(buf, ':');
if (!file_type)
{
- fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
- buf);
- exit(2);
+ bail(_("incorrectly formatted resultmap entry: %s"), buf);
}
*file_type++ = '\0';
platform = strchr(file_type, ':');
if (!platform)
{
- fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
- buf);
- exit(2);
+ bail(_("incorrectly formatted resultmap entry: %s"), buf);
}
*platform++ = '\0';
expected = strchr(platform, '=');
if (!expected)
{
- fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
- buf);
- exit(2);
+ bail(_("incorrectly formatted resultmap entry: %s"), buf);
}
*expected++ = '\0';
@@ -758,13 +1118,13 @@ initialize_environment(void)
}
if (pghost && pgport)
- printf(_("(using postmaster on %s, port %s)\n"), pghost, pgport);
+ comment(COMMENT_VERBOSE, _("(using postmaster on %s, port %s)\n"), pghost, pgport);
if (pghost && !pgport)
- printf(_("(using postmaster on %s, default port)\n"), pghost);
+ comment(COMMENT_VERBOSE, _("(using postmaster on %s, default port)\n"), pghost);
if (!pghost && pgport)
- printf(_("(using postmaster on Unix socket, port %s)\n"), pgport);
+ comment(COMMENT_VERBOSE, _("(using postmaster on Unix socket, port %s)\n"), pgport);
if (!pghost && !pgport)
- printf(_("(using postmaster on Unix socket, default port)\n"));
+ comment(COMMENT_VERBOSE, _("(using postmaster on Unix socket, default port)\n"));
}
load_resultmap();
@@ -813,34 +1173,30 @@ current_windows_user(const char **acct, const char **dom)
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token))
{
- fprintf(stderr,
- _("%s: could not open process token: error code %lu\n"),
- progname, GetLastError());
+ pg_log_error("could not open process token: error code %lu",
+ GetLastError());
exit(2);
}
if (!GetTokenInformation(token, TokenUser, NULL, 0, &retlen) && GetLastError() != 122)
{
- fprintf(stderr,
- _("%s: could not get token information buffer size: error code %lu\n"),
- progname, GetLastError());
+ pg_log_error("could not get token information buffer size: error code %lu",
+ GetLastError());
exit(2);
}
tokenuser = pg_malloc(retlen);
if (!GetTokenInformation(token, TokenUser, tokenuser, retlen, &retlen))
{
- fprintf(stderr,
- _("%s: could not get token information: error code %lu\n"),
- progname, GetLastError());
+ pg_log_error("could not get token information: error code %lu",
+ GetLastError());
exit(2);
}
if (!LookupAccountSid(NULL, tokenuser->User.Sid, accountname, &accountnamesize,
domainname, &domainnamesize, &accountnameuse))
{
- fprintf(stderr,
- _("%s: could not look up account SID: error code %lu\n"),
- progname, GetLastError());
+ pg_log_error("could not look up account SID: error code %lu",
+ GetLastError());
exit(2);
}
@@ -990,9 +1346,10 @@ psql_start_command(void)
StringInfo buf = makeStringInfo();
appendStringInfo(buf,
- "\"%s%spsql\" -X",
+ "\"%s%spsql\" -X %s",
bindir ? bindir : "",
- bindir ? "/" : "");
+ bindir ? "/" : "",
+ psql_formatting ? psql_formatting : "");
return buf;
}
@@ -1045,8 +1402,7 @@ psql_end_command(StringInfo buf, const char *database)
if (system(buf->data) != 0)
{
/* psql probably already reported the error */
- fprintf(stderr, _("command failed: %s\n"), buf->data);
- exit(2);
+ bail(_("command failed: %s"), buf->data);
}
/* Clean up */
@@ -1091,9 +1447,7 @@ spawn_process(const char *cmdline)
pid = fork();
if (pid == -1)
{
- fprintf(stderr, _("%s: could not fork: %s\n"),
- progname, strerror(errno));
- exit(2);
+ bail(_("could not fork: %s"), strerror(errno));
}
if (pid == 0)
{
@@ -1108,8 +1462,7 @@ spawn_process(const char *cmdline)
cmdline2 = psprintf("exec %s", cmdline);
execl(shellprog, shellprog, "-c", cmdline2, (char *) NULL);
- fprintf(stderr, _("%s: could not exec \"%s\": %s\n"),
- progname, shellprog, strerror(errno));
+ pg_log_error("could not exec \"%s\": %s", shellprog, strerror(errno));
_exit(1); /* not exit() here... */
}
/* in parent */
@@ -1148,8 +1501,8 @@ file_size(const char *file)
if (!f)
{
- fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
- progname, file, strerror(errno));
+ comment(COMMENT_ERROR, _("could not open file \"%s\" for reading: %s"),
+ file, strerror(errno));
return -1;
}
fseek(f, 0, SEEK_END);
@@ -1170,8 +1523,8 @@ file_line_count(const char *file)
if (!f)
{
- fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
- progname, file, strerror(errno));
+ comment(COMMENT_ERROR, _("could not open file \"%s\" for reading: %s"),
+ file, strerror(errno));
return -1;
}
while ((c = fgetc(f)) != EOF)
@@ -1212,9 +1565,8 @@ make_directory(const char *dir)
{
if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
{
- fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
- progname, dir, strerror(errno));
- exit(2);
+ bail(_("could not create directory \"%s\": %s"),
+ dir, strerror(errno));
}
}
@@ -1263,8 +1615,7 @@ run_diff(const char *cmd, const char *filename)
r = system(cmd);
if (!WIFEXITED(r) || WEXITSTATUS(r) > 1)
{
- fprintf(stderr, _("diff command failed with status %d: %s\n"), r, cmd);
- exit(2);
+ bail(_("diff command failed with status %d: %s"), r, cmd);
}
#ifdef WIN32
@@ -1274,8 +1625,7 @@ run_diff(const char *cmd, const char *filename)
*/
if (WEXITSTATUS(r) == 1 && file_size(filename) <= 0)
{
- fprintf(stderr, _("diff command not found: %s\n"), cmd);
- exit(2);
+ bail(_("diff command not found: %s"), cmd);
}
#endif
@@ -1346,9 +1696,8 @@ results_differ(const char *testname, const char *resultsfile, const char *defaul
alt_expectfile = get_alternative_expectfile(expectfile, i);
if (!alt_expectfile)
{
- fprintf(stderr, _("Unable to check secondary comparison files: %s\n"),
- strerror(errno));
- exit(2);
+ bail(_("Unable to check secondary comparison files: %s"),
+ strerror(errno));
}
if (!file_exists(alt_expectfile))
@@ -1463,9 +1812,8 @@ wait_for_tests(PID_TYPE * pids, int *statuses, instr_time *stoptimes,
if (p == INVALID_PID)
{
- fprintf(stderr, _("failed to wait for subprocesses: %s\n"),
- strerror(errno));
- exit(2);
+ bail(_("failed to wait for subprocesses: %s"),
+ strerror(errno));
}
#else
DWORD exit_status;
@@ -1474,9 +1822,8 @@ wait_for_tests(PID_TYPE * pids, int *statuses, instr_time *stoptimes,
r = WaitForMultipleObjects(tests_left, active_pids, FALSE, INFINITE);
if (r < WAIT_OBJECT_0 || r >= WAIT_OBJECT_0 + tests_left)
{
- fprintf(stderr, _("failed to wait for subprocesses: error code %lu\n"),
- GetLastError());
- exit(2);
+ bail(_("failed to wait for subprocesses: error code %lu"),
+ GetLastError());
}
p = active_pids[r - WAIT_OBJECT_0];
/* compact the active_pids array */
@@ -1495,7 +1842,7 @@ wait_for_tests(PID_TYPE * pids, int *statuses, instr_time *stoptimes,
statuses[i] = (int) exit_status;
INSTR_TIME_SET_CURRENT(stoptimes[i]);
if (names)
- status(" %s", names[i]);
+ comment(COMMENT_VERBOSE, " %s", names[i]);
tests_left--;
break;
}
@@ -1514,20 +1861,20 @@ static void
log_child_failure(int exitstatus)
{
if (WIFEXITED(exitstatus))
- status(_(" (test process exited with exit code %d)"),
+ comment(COMMENT_DIAGNOSTIC, _(" (test process exited with exit code %d)"),
WEXITSTATUS(exitstatus));
else if (WIFSIGNALED(exitstatus))
{
#if defined(WIN32)
- status(_(" (test process was terminated by exception 0x%X)"),
+ comment(COMMENT_DIAGNOSTIC, _(" (test process was terminated by exception 0x%X)"),
WTERMSIG(exitstatus));
#else
- status(_(" (test process was terminated by signal %d: %s)"),
+ comment(COMMENT_DIAGNOSTIC, _(" (test process was terminated by signal %d: %s)"),
WTERMSIG(exitstatus), pg_strsignal(WTERMSIG(exitstatus)));
#endif
}
else
- status(_(" (test process exited with unrecognized status %d)"),
+ comment(COMMENT_DIAGNOSTIC, _(" (test process exited with unrecognized status %d)"),
exitstatus);
}
@@ -1551,18 +1898,20 @@ run_schedule(const char *schedule, test_start_function startfunc,
char scbuf[1024];
FILE *scf;
int line_num = 0;
+ StringInfoData tagbuf;
memset(tests, 0, sizeof(tests));
memset(resultfiles, 0, sizeof(resultfiles));
memset(expectfiles, 0, sizeof(expectfiles));
memset(tags, 0, sizeof(tags));
+ initStringInfo(&tagbuf);
+
scf = fopen(schedule, "r");
if (!scf)
{
- fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
- progname, schedule, strerror(errno));
- exit(2);
+ bail(_("could not open file \"%s\" for reading: %s"),
+ schedule, strerror(errno));
}
while (fgets(scbuf, sizeof(scbuf), scf))
@@ -1600,9 +1949,8 @@ run_schedule(const char *schedule, test_start_function startfunc,
}
else
{
- fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n"),
- schedule, line_num, scbuf);
- exit(2);
+ bail(_("syntax error in schedule file \"%s\" line %d: %s\n"),
+ schedule, line_num, scbuf);
}
num_tests = 0;
@@ -1618,9 +1966,8 @@ run_schedule(const char *schedule, test_start_function startfunc,
if (num_tests >= MAX_PARALLEL_TESTS)
{
- fprintf(stderr, _("too many parallel tests (more than %d) in schedule file \"%s\" line %d: %s\n"),
- MAX_PARALLEL_TESTS, schedule, line_num, scbuf);
- exit(2);
+ bail(_("too many parallel tests (more than %d) in schedule file \"%s\" line %d: %s\n"),
+ MAX_PARALLEL_TESTS, schedule, line_num, scbuf);
}
sav = *c;
*c = '\0';
@@ -1642,14 +1989,13 @@ run_schedule(const char *schedule, test_start_function startfunc,
if (num_tests == 0)
{
- fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n"),
- schedule, line_num, scbuf);
- exit(2);
+ bail(_("syntax error in schedule file \"%s\" line %d: %s\n"),
+ schedule, line_num, scbuf);
}
if (num_tests == 1)
{
- status(_("test %-28s ... "), tests[0]);
+ test_status_preamble(tests[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);
@@ -1657,16 +2003,15 @@ run_schedule(const char *schedule, test_start_function startfunc,
}
else if (max_concurrent_tests > 0 && max_concurrent_tests < num_tests)
{
- fprintf(stderr, _("too many parallel tests (more than %d) in schedule file \"%s\" line %d: %s\n"),
- max_concurrent_tests, schedule, line_num, scbuf);
- exit(2);
+ bail(_("too many parallel tests (more than %d) in schedule file \"%s\" line %d: %s\n"),
+ max_concurrent_tests, schedule, line_num, scbuf);
}
else if (max_connections > 0 && max_connections < num_tests)
{
int oldest = 0;
- status(_("parallel group (%d tests, in groups of %d): "),
- num_tests, max_connections);
+ comment(COMMENT_VERBOSE, _("parallel group (%d tests, in groups of %d): "),
+ num_tests, max_connections);
for (i = 0; i < num_tests; i++)
{
if (i - oldest >= max_connections)
@@ -1686,7 +2031,7 @@ run_schedule(const char *schedule, test_start_function startfunc,
}
else
{
- status(_("parallel group (%d tests): "), num_tests);
+ comment(COMMENT_VERBOSE, _("parallel group (%d tests): "), num_tests);
for (i = 0; i < num_tests; i++)
{
pids[i] = (startfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
@@ -1704,8 +2049,10 @@ run_schedule(const char *schedule, test_start_function startfunc,
*tl;
bool differ = false;
+ resetStringInfo(&tagbuf);
+
if (num_tests > 1)
- status(_(" %-28s ... "), tests[i]);
+ test_status_preamble(tests[i]);
/*
* Advance over all three lists simultaneously.
@@ -1726,7 +2073,7 @@ run_schedule(const char *schedule, test_start_function startfunc,
newdiff = results_differ(tests[i], rl->str, el->str);
if (newdiff && tl)
{
- printf("%s ", tl->str);
+ appendStringInfo(&tagbuf, "%s ", tl->str);
}
differ |= newdiff;
}
@@ -1744,28 +2091,17 @@ run_schedule(const char *schedule, test_start_function startfunc,
break;
}
}
- if (ignore)
- {
- status(_("failed (ignored)"));
- fail_ignore_count++;
- }
- else
- {
- status(_("FAILED"));
- fail_count++;
- }
+
+ test_status_failed(tests[i], ignore, tagbuf.data);
}
else
- {
- status(_("ok ")); /* align with FAILED */
- success_count++;
- }
+ test_status_ok(tests[i]);
if (statuses[i] != 0)
log_child_failure(statuses[i]);
INSTR_TIME_SUBTRACT(stoptimes[i], starttimes[i]);
- status(_(" %8.0f ms"), INSTR_TIME_GET_MILLISEC(stoptimes[i]));
+ runtime(tests[i], INSTR_TIME_GET_MILLISEC(stoptimes[i]));
status_end();
}
@@ -1780,6 +2116,7 @@ run_schedule(const char *schedule, test_start_function startfunc,
}
}
+ pg_free(tagbuf.data);
free_stringlist(&ignorelist);
fclose(scf);
@@ -1803,11 +2140,13 @@ run_single_test(const char *test, test_start_function startfunc,
*el,
*tl;
bool differ = false;
+ StringInfoData tagbuf;
- status(_("test %-28s ... "), test);
+ test_status_preamble(test);
pid = (startfunc) (test, &resultfiles, &expectfiles, &tags);
INSTR_TIME_SET_CURRENT(starttime);
wait_for_tests(&pid, &exit_status, &stoptime, NULL, 1);
+ initStringInfo(&tagbuf);
/*
* Advance over all three lists simultaneously.
@@ -1828,27 +2167,23 @@ run_single_test(const char *test, test_start_function startfunc,
newdiff = results_differ(test, rl->str, el->str);
if (newdiff && tl)
{
- printf("%s ", tl->str);
+ appendStringInfo(&tagbuf, "%s ", tl->str);
}
differ |= newdiff;
}
if (differ)
- {
- status(_("FAILED"));
- fail_count++;
- }
+ test_status_failed(test, false, tagbuf.data);
else
- {
- status(_("ok ")); /* align with FAILED */
- success_count++;
- }
+ test_status_ok(test);
if (exit_status != 0)
log_child_failure(exit_status);
INSTR_TIME_SUBTRACT(stoptime, starttime);
- status(_(" %8.0f ms"), INSTR_TIME_GET_MILLISEC(stoptime));
+ runtime(_(" %8.0f ms"), INSTR_TIME_GET_MILLISEC(stoptime));
+
+ pg_free(tagbuf.data);
status_end();
}
@@ -1872,9 +2207,8 @@ open_result_files(void)
logfile = fopen(logfilename, "w");
if (!logfile)
{
- fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
- progname, logfilename, strerror(errno));
- exit(2);
+ bail(_("%s: could not open file \"%s\" for writing: %s"),
+ progname, logfilename, strerror(errno));
}
/* create the diffs file as empty */
@@ -1883,9 +2217,8 @@ open_result_files(void)
difffile = fopen(difffilename, "w");
if (!difffile)
{
- fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
- progname, difffilename, strerror(errno));
- exit(2);
+ bail(_("%s: could not open file \"%s\" for writing: %s"),
+ progname, difffilename, strerror(errno));
}
/* we don't keep the diffs file open continuously */
fclose(difffile);
@@ -1989,6 +2322,7 @@ help(void)
printf(_(" --debug turn on debug mode in programs that are run\n"));
printf(_(" --dlpath=DIR look for dynamic libraries in DIR\n"));
printf(_(" --encoding=ENCODING use ENCODING as the encoding\n"));
+ printf(_(" --format=(regress|tap) output format to use\n"));
printf(_(" -h, --help show this help, then exit\n"));
printf(_(" --inputdir=DIR take input files from DIR (default \".\")\n"));
printf(_(" --launcher=CMD use CMD as launcher of psql\n"));
@@ -2003,6 +2337,7 @@ help(void)
printf(_(" (can be used multiple times to concatenate)\n"));
printf(_(" --temp-instance=DIR create a temporary instance in DIR\n"));
printf(_(" --use-existing use an existing installation\n"));
+ printf(_(" -v, --verbose verbose output, only applies to regress output format\n"));
printf(_(" -V, --version output version information, then exit\n"));
printf(_("\n"));
printf(_("Options for \"temp-instance\" mode:\n"));
@@ -2031,6 +2366,7 @@ regression_main(int argc, char *argv[],
static struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
+ {"verbose", no_argument, NULL, 'v'},
{"dbname", required_argument, NULL, 1},
{"debug", no_argument, NULL, 2},
{"inputdir", required_argument, NULL, 3},
@@ -2052,6 +2388,7 @@ regression_main(int argc, char *argv[],
{"load-extension", required_argument, NULL, 22},
{"config-auth", required_argument, NULL, 24},
{"max-concurrent-tests", required_argument, NULL, 25},
+ {"format", required_argument, NULL, 26},
{NULL, 0, NULL, 0}
};
@@ -2097,7 +2434,7 @@ regression_main(int argc, char *argv[],
if (getenv("PG_REGRESS_DIFF_OPTS"))
pretty_diff_opts = getenv("PG_REGRESS_DIFF_OPTS");
- while ((c = getopt_long(argc, argv, "hV", long_options, &option_index)) != -1)
+ while ((c = getopt_long(argc, argv, "hVv", long_options, &option_index)) != -1)
{
switch (c)
{
@@ -2107,6 +2444,9 @@ regression_main(int argc, char *argv[],
case 'V':
puts("pg_regress (PostgreSQL) " PG_VERSION);
exit(0);
+ case 'v':
+ verbose = true;
+ break;
case 1:
/*
@@ -2181,9 +2521,12 @@ regression_main(int argc, char *argv[],
case 25:
max_concurrent_tests = atoi(optarg);
break;
+ case 26:
+ format = pg_strdup(optarg);
+ break;
default:
/* getopt_long already emitted a complaint */
- fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
+ pg_log_error_hint("Try \"%s --help\" for more information.",
progname);
exit(2);
}
@@ -2207,6 +2550,29 @@ regression_main(int argc, char *argv[],
exit(0);
}
+ if (!verbose)
+ psql_formatting = pg_strdup("-q");
+
+ /*
+ * text (regress) is the default so we don't need to set any variables in
+ * that case.
+ */
+ if (format)
+ {
+ if (strcmp(format, "tap") == 0)
+ {
+ output = &output_func_tap;
+ if (!psql_formatting)
+ psql_formatting = pg_strdup("-q");
+ }
+ else if (strcmp(format, "regress") != 0)
+ {
+ pg_log_error("%s: invalid format specified: \"%s\". Supported formats are \"tap\" and \"regress\"",
+ progname, format);
+ exit(2);
+ }
+ }
+
if (temp_instance && !port_specified_by_user)
/*
@@ -2248,9 +2614,8 @@ regression_main(int argc, char *argv[],
header(_("removing existing temp instance"));
if (!rmtree(temp_instance, true))
{
- fprintf(stderr, _("\n%s: could not remove temp instance \"%s\"\n"),
- progname, temp_instance);
- exit(2);
+ bail(_("%s: could not remove temp instance \"%s\""),
+ progname, temp_instance);
}
}
@@ -2276,8 +2641,8 @@ regression_main(int argc, char *argv[],
outputdir);
if (system(buf))
{
- fprintf(stderr, _("\n%s: initdb failed\nExamine %s/log/initdb.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf);
- exit(2);
+ bail(_("%s: initdb failed\nExamine %s/log/initdb.log for the reason.\nCommand was: %s"),
+ progname, outputdir, buf);
}
/*
@@ -2292,8 +2657,8 @@ regression_main(int argc, char *argv[],
pg_conf = fopen(buf, "a");
if (pg_conf == NULL)
{
- fprintf(stderr, _("\n%s: could not open \"%s\" for adding extra config: %s\n"), progname, buf, strerror(errno));
- exit(2);
+ bail( _("%s: could not open \"%s\" for adding extra config: %s"),
+ progname, buf, strerror(errno));
}
fputs("\n# Configuration added by pg_regress\n\n", pg_conf);
fputs("log_autovacuum_min_duration = 0\n", pg_conf);
@@ -2312,8 +2677,8 @@ regression_main(int argc, char *argv[],
extra_conf = fopen(temp_config, "r");
if (extra_conf == NULL)
{
- fprintf(stderr, _("\n%s: could not open \"%s\" to read extra config: %s\n"), progname, temp_config, strerror(errno));
- exit(2);
+ bail(_("%s: could not open \"%s\" to read extra config: %s"),
+ progname, temp_config, strerror(errno));
}
while (fgets(line_buf, sizeof(line_buf), extra_conf) != NULL)
fputs(line_buf, pg_conf);
@@ -2353,14 +2718,16 @@ regression_main(int argc, char *argv[],
if (port_specified_by_user || i == 15)
{
- fprintf(stderr, _("port %d apparently in use\n"), port);
+ comment(COMMENT_VERBOSE, _("port %d apparently in use\n"),
+ port);
if (!port_specified_by_user)
- fprintf(stderr, _("%s: could not determine an available port\n"), progname);
- fprintf(stderr, _("Specify an unused port using the --port option or shut down any conflicting PostgreSQL servers.\n"));
- exit(2);
+ comment(COMMENT_VERBOSE,
+ _("could not determine an available port\n"));
+ bail(_("Specify an unused port using the --port option or shut down any conflicting PostgreSQL servers."));
}
- fprintf(stderr, _("port %d apparently in use, trying %d\n"), port, port + 1);
+ comment(COMMENT_VERBOSE, _("port %d apparently in use, trying %d\n"),
+ port, port + 1);
port++;
sprintf(s, "%d", port);
setenv("PGPORT", s, 1);
@@ -2384,11 +2751,7 @@ regression_main(int argc, char *argv[],
outputdir);
postmaster_pid = spawn_process(buf);
if (postmaster_pid == INVALID_PID)
- {
- fprintf(stderr, _("\n%s: could not spawn postmaster: %s\n"),
- progname, strerror(errno));
- exit(2);
- }
+ bail(_("could not spawn postmaster: %s\n"), strerror(errno));
/*
* Wait till postmaster is able to accept connections; normally this
@@ -2422,16 +2785,16 @@ regression_main(int argc, char *argv[],
if (WaitForSingleObject(postmaster_pid, 0) == WAIT_OBJECT_0)
#endif
{
- fprintf(stderr, _("\n%s: postmaster failed\nExamine %s/log/postmaster.log for the reason\n"), progname, outputdir);
- exit(2);
+ bail(_("postmaster failed\nExamine %s/log/postmaster.log for the reason\n"),
+ outputdir);
}
pg_usleep(1000000L);
}
if (i >= wait_seconds)
{
- fprintf(stderr, _("\n%s: postmaster did not respond within %d seconds\nExamine %s/log/postmaster.log for the reason\n"),
- progname, wait_seconds, outputdir);
+ comment(COMMENT_VERBOSE, _("postmaster did not respond within %d seconds\nExamine %s/log/postmaster.log for the reason\n"),
+ wait_seconds, outputdir);
/*
* If we get here, the postmaster is probably wedged somewhere in
@@ -2440,16 +2803,13 @@ regression_main(int argc, char *argv[],
* attempts.
*/
#ifndef WIN32
- if (kill(postmaster_pid, SIGKILL) != 0 &&
- errno != ESRCH)
- fprintf(stderr, _("\n%s: could not kill failed postmaster: %s\n"),
- progname, strerror(errno));
+ if (kill(postmaster_pid, SIGKILL) != 0 && errno != ESRCH)
+ bail(_("could not kill failed postmaster: %s"), strerror(errno));
#else
if (TerminateProcess(postmaster_pid, 255) == 0)
- fprintf(stderr, _("\n%s: could not kill failed postmaster: error code %lu\n"),
- progname, GetLastError());
+ bail(_("could not kill failed postmaster: error code %lu"),
+ GetLastError());
#endif
-
exit(2);
}
@@ -2461,7 +2821,7 @@ regression_main(int argc, char *argv[],
#else
#define ULONGPID(x) (unsigned long) (x)
#endif
- printf(_("running on port %d with PID %lu\n"),
+ comment(COMMENT_VERBOSE, _("running on port %d with PID %lu\n"),
port, ULONGPID(postmaster_pid));
}
else
@@ -2514,6 +2874,23 @@ regression_main(int argc, char *argv[],
stop_postmaster();
}
+ fclose(logfile);
+ logfile = NULL;
+
+ if (file_size(difffilename) <= 0)
+ {
+ unlink(difffilename);
+ unlink(logfilename);
+
+ free(difffilename);
+ difffilename = NULL;
+ free(logfilename);
+ logfilename = NULL;
+ }
+
+ footer(difffilename, logfilename);
+ status_end();
+
/*
* If there were no errors, remove the temp instance immediately to
* conserve disk space. (If there were errors, we leave the instance in
@@ -2523,58 +2900,8 @@ regression_main(int argc, char *argv[],
{
header(_("removing temporary instance"));
if (!rmtree(temp_instance, true))
- fprintf(stderr, _("\n%s: could not remove temp instance \"%s\"\n"),
- progname, temp_instance);
- }
-
- fclose(logfile);
-
- /*
- * Emit nice-looking summary message
- */
- if (fail_count == 0 && fail_ignore_count == 0)
- snprintf(buf, sizeof(buf),
- _(" All %d tests passed. "),
- success_count);
- else if (fail_count == 0) /* fail_count=0, fail_ignore_count>0 */
- snprintf(buf, sizeof(buf),
- _(" %d of %d tests passed, %d failed test(s) ignored. "),
- success_count,
- success_count + fail_ignore_count,
- fail_ignore_count);
- else if (fail_ignore_count == 0) /* fail_count>0 && fail_ignore_count=0 */
- snprintf(buf, sizeof(buf),
- _(" %d of %d tests failed. "),
- fail_count,
- success_count + fail_count);
- else
- /* fail_count>0 && fail_ignore_count>0 */
- snprintf(buf, sizeof(buf),
- _(" %d of %d tests failed, %d of these failures ignored. "),
- fail_count + fail_ignore_count,
- success_count + fail_count + fail_ignore_count,
- fail_ignore_count);
-
- putchar('\n');
- for (i = strlen(buf); i > 0; i--)
- putchar('=');
- printf("\n%s\n", buf);
- for (i = strlen(buf); i > 0; i--)
- putchar('=');
- putchar('\n');
- putchar('\n');
-
- if (file_size(difffilename) > 0)
- {
- printf(_("The differences that caused some tests to fail can be viewed in the\n"
- "file \"%s\". A copy of the test summary that you see\n"
- "above is saved in the file \"%s\".\n\n"),
- difffilename, logfilename);
- }
- else
- {
- unlink(difffilename);
- unlink(logfilename);
+ pg_log_error("could not remove temp instance \"%s\"",
+ temp_instance);
}
if (fail_count != 0)
--
2.32.1 (Apple Git-133)