diff options
-rw-r--r-- | doc/src/sgml/regress.sgml | 8 | ||||
-rw-r--r-- | meson.build | 1 | ||||
-rw-r--r-- | src/Makefile.global.in | 14 | ||||
-rw-r--r-- | src/test/regress/pg_regress.c | 564 |
4 files changed, 325 insertions, 262 deletions
diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index 8a0b384dec6..f721694230f 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -30,6 +30,8 @@ parallel method starts up multiple server processes to run groups of tests in parallel. Parallel testing adds confidence that interprocess communication and locking are working correctly. + Some tests may run sequentially even in the <quote>parallel</quote> + mode in case this is required by the test. </para> <sect2 id="regress-run-temp-inst"> @@ -43,12 +45,12 @@ make check </screen> in the top-level directory. (Or you can change to <filename>src/test/regress</filename> and run the command there.) + Tests which are run in parallel are prefixed with <quote>+</quote>, and + tests which run sequentially are prefixed with <quote>-</quote>. At the end you should see something like: <screen> <computeroutput> -======================= - All 193 tests passed. -======================= +# All 213 tests passed. </computeroutput> </screen> or otherwise a note about which tests failed. See <xref diff --git a/meson.build b/meson.build index 5e708d90450..84b60c8933c 100644 --- a/meson.build +++ b/meson.build @@ -3118,6 +3118,7 @@ foreach test_dir : tests env.prepend('PATH', temp_install_bindir, test_dir['bd']) test_kwargs = { + 'protocol': 'tap', 'priority': 10, 'timeout': 1000, 'depends': test_deps + t.get('deps', []), diff --git a/src/Makefile.global.in b/src/Makefile.global.in index fb3e197fc06..736dd1ed5ed 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -444,7 +444,7 @@ ifeq ($(enable_tap_tests),yes) ifndef PGXS define prove_installcheck -echo "+++ tap install-check in $(subdir) +++" && \ +echo "# +++ tap install-check in $(subdir) +++" && \ rm -rf '$(CURDIR)'/tmp_check && \ $(MKDIR_P) '$(CURDIR)'/tmp_check && \ cd $(srcdir) && \ @@ -457,7 +457,7 @@ cd $(srcdir) && \ endef else # PGXS case define prove_installcheck -echo "+++ tap install-check in $(subdir) +++" && \ +echo "# +++ tap install-check in $(subdir) +++" && \ rm -rf '$(CURDIR)'/tmp_check && \ $(MKDIR_P) '$(CURDIR)'/tmp_check && \ cd $(srcdir) && \ @@ -471,7 +471,7 @@ endef endif # PGXS define prove_check -echo "+++ tap check in $(subdir) +++" && \ +echo "# +++ tap check in $(subdir) +++" && \ rm -rf '$(CURDIR)'/tmp_check && \ $(MKDIR_P) '$(CURDIR)'/tmp_check && \ cd $(srcdir) && \ @@ -665,7 +665,7 @@ pg_regress_locale_flags = $(if $(ENCODING),--encoding=$(ENCODING)) $(NOLOCALE) pg_regress_clean_files = results/ regression.diffs regression.out tmp_check/ tmp_check_iso/ log/ output_iso/ pg_regress_check = \ - echo "+++ regress check in $(subdir) +++" && \ + echo "\# +++ regress check in $(subdir) +++" && \ $(with_temp_install) \ $(top_builddir)/src/test/regress/pg_regress \ --temp-instance=./tmp_check \ @@ -674,14 +674,14 @@ pg_regress_check = \ $(TEMP_CONF) \ $(pg_regress_locale_flags) $(EXTRA_REGRESS_OPTS) pg_regress_installcheck = \ - echo "+++ regress install-check in $(subdir) +++" && \ + echo "\# +++ regress install-check in $(subdir) +++" && \ $(top_builddir)/src/test/regress/pg_regress \ --inputdir=$(srcdir) \ --bindir='$(bindir)' \ $(pg_regress_locale_flags) $(EXTRA_REGRESS_OPTS) pg_isolation_regress_check = \ - echo "+++ isolation check in $(subdir) +++" && \ + echo "\# +++ isolation check in $(subdir) +++" && \ $(with_temp_install) \ $(top_builddir)/src/test/isolation/pg_isolation_regress \ --temp-instance=./tmp_check_iso \ @@ -690,7 +690,7 @@ pg_isolation_regress_check = \ $(TEMP_CONF) \ $(pg_regress_locale_flags) $(EXTRA_REGRESS_OPTS) pg_isolation_regress_installcheck = \ - echo "+++ isolation install-check in $(subdir) +++" && \ + echo "\# +++ isolation install-check in $(subdir) +++" && \ $(top_builddir)/src/test/isolation/pg_isolation_regress \ --inputdir=$(srcdir) --outputdir=output_iso \ --bindir='$(bindir)' \ diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c index 7b23cc80dcd..f7bcb30ca4f 100644 --- a/src/test/regress/pg_regress.c +++ b/src/test/regress/pg_regress.c @@ -68,6 +68,25 @@ const char *basic_diff_opts = "-w"; const char *pretty_diff_opts = "-w -U3"; #endif +/* + * The width of the testname field when printing to ensure vertical alignment + * of test runtimes. This number is somewhat arbitrarily chosen to match the + * older pre-TAP output format. + */ +#define TESTNAME_WIDTH 36 + +typedef enum TAPtype +{ + DIAG = 0, + BAIL, + NOTE, + NOTE_DETAIL, + NOTE_END, + TEST_STATUS, + PLAN, + NONE +} TAPtype; + /* options settable from command line */ _stringlist *dblist = NULL; bool debug = false; @@ -103,6 +122,8 @@ static const char *sockdir; static const char *temp_sockdir; static char sockself[MAXPGPATH]; static char socklock[MAXPGPATH]; +static StringInfo failed_tests = NULL; +static bool in_note = false; static _resultmap *resultmap = NULL; @@ -115,13 +136,30 @@ static int fail_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); -static void status(const char *fmt,...) pg_attribute_printf(1, 2); +static void test_status_print(bool ok, const char *testname, double runtime, bool parallel); +static void test_status_ok(const char *testname, double runtime, bool parallel); +static void test_status_failed(const char *testname, double runtime, bool parallel); +static void bail_out(bool noatexit, const char *fmt,...) pg_attribute_printf(2, 3); +static void emit_tap_output(TAPtype type, const char *fmt,...) pg_attribute_printf(2, 3); +static void emit_tap_output_v(TAPtype type, const char *fmt, va_list argp) pg_attribute_printf(2, 0); + 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); /* + * Convenience macros for printing TAP output with a more shorthand syntax + * aimed at making the code more readable. + */ +#define plan(x) emit_tap_output(PLAN, "1..%i", (x)) +#define note(...) emit_tap_output(NOTE, __VA_ARGS__) +#define note_detail(...) emit_tap_output(NOTE_DETAIL, __VA_ARGS__) +#define diag(...) emit_tap_output(DIAG, __VA_ARGS__) +#define note_end() emit_tap_output(NOTE_END, "\n"); +#define bail_noatexit(...) bail_out(true, __VA_ARGS__) +#define bail(...) bail_out(false, __VA_ARGS__) + +/* * allow core files if possible. */ #if defined(HAVE_GETRLIMIT) @@ -133,9 +171,7 @@ unlimit_core_size(void) getrlimit(RLIMIT_CORE, &lim); if (lim.rlim_max == 0) { - fprintf(stderr, - _("%s: could not set core size: disallowed by hard limit\n"), - progname); + diag("could not set core size: disallowed by hard limit"); return; } else if (lim.rlim_max == RLIM_INFINITY || lim.rlim_cur < lim.rlim_max) @@ -201,53 +237,172 @@ split_to_stringlist(const char *s, const char *delim, _stringlist **listhead) } /* - * Print a progress banner on stdout. + * Bailing out is for unrecoverable errors which prevents further testing to + * occur and after which the test run should be aborted. By passing noatexit + * as true the process will terminate with _exit(2) and skipping registered + * exit handlers, thus avoid any risk of bottomless recursion calls to exit. */ static void -header(const char *fmt,...) +bail_out(bool noatexit, const char *fmt,...) { - char tmp[64]; va_list ap; va_start(ap, fmt); - vsnprintf(tmp, sizeof(tmp), fmt, ap); + emit_tap_output_v(BAIL, fmt, ap); va_end(ap); - fprintf(stdout, "============== %-38s ==============\n", tmp); - fflush(stdout); + if (noatexit) + _exit(2); + + exit(2); } /* - * Print "doing something ..." --- supplied text should not end with newline + * Print the result of a test run and associated metadata like runtime. Care + * is taken to align testnames and runtimes vertically to ensure the output + * is human readable while still TAP compliant. Tests run in parallel are + * prefixed with a '+' and sequential tests with a '-'. This distinction was + * previously indicated by 'test' prefixing sequential tests while parallel + * tests were indented by four leading spaces. The meson TAP parser consumes + * leading space however, so a non-whitespace prefix of the same length is + * required for both. */ static void -status(const char *fmt,...) +test_status_print(bool ok, const char *testname, double runtime, bool parallel) { - va_list ap; + int testnumber = fail_count + success_count; - va_start(ap, fmt); - vfprintf(stdout, fmt, ap); - fflush(stdout); - va_end(ap); + /* + * Testnumbers are padded to 5 characters to ensure that testnames align + * vertically (assuming at most 9999 tests). Testnames are prefixed with + * a leading character to indicate being run in parallel or not. A leading + * '+' indicates a parellel test, '-' indicates a single test. + */ + emit_tap_output(TEST_STATUS, "%sok %-5i%*s %c %-*s %8.0f ms", + (ok ? "" : "not "), + testnumber, + /* If ok, indent with four spaces matching "not " */ + (ok ? (int) strlen("not ") : 0), "", + /* Prefix a parallel test '+' and a single test with '-' */ + (parallel ? '+' : '-'), + /* Testnames are padded to align runtimes */ + TESTNAME_WIDTH, testname, + runtime); +} - if (logfile) - { - va_start(ap, fmt); - vfprintf(logfile, fmt, ap); - va_end(ap); - } +static void +test_status_ok(const char *testname, double runtime, bool parallel) +{ + success_count++; + + test_status_print(true, testname, runtime, parallel); } -/* - * Done "doing something ..." - */ static void -status_end(void) +test_status_failed(const char *testname, double runtime, bool parallel) +{ + /* + * Save failed tests in a buffer such that we can print a summary at the + * end with diag() to ensure it's shown even under test harnesses. + */ + if (!failed_tests) + failed_tests = makeStringInfo(); + else + appendStringInfoChar(failed_tests, ','); + + appendStringInfo(failed_tests, " %s", testname); + + fail_count++; + + test_status_print(false, testname, runtime, parallel); +} + + +static void +emit_tap_output(TAPtype type, const char *fmt,...) { - fprintf(stdout, "\n"); - fflush(stdout); + va_list argp; + + va_start(argp, fmt); + emit_tap_output_v(type, fmt, argp); + va_end(argp); +} + +static void +emit_tap_output_v(TAPtype type, const char *fmt, va_list argp) +{ + va_list argp_logfile; + FILE *fp; + + /* + * Diagnostic output will be hidden by prove unless printed to stderr. The + * Bail message is also printed to stderr to aid debugging under a harness + * which might otherwise not emit such an important message. + */ + if (type == DIAG || type == BAIL) + fp = stderr; + else + fp = stdout; + + /* + * If we are ending a note_detail line we can avoid further processing and + * immediately return following a newline. + */ + if (type == NOTE_END) + { + in_note = false; + fprintf(fp, "\n"); + if (logfile) + fprintf(logfile, "\n"); + return; + } + + /* Make a copy of the va args for printing to the logfile */ + va_copy(argp_logfile, argp); + + /* + * Non-protocol output such as diagnostics or notes must be prefixed by a + * '#' character. We print the Bail message like this too. + */ + if ((type == NOTE || type == DIAG || type == BAIL) + || (type == NOTE_DETAIL && !in_note)) + { + fprintf(fp, "# "); + if (logfile) + fprintf(logfile, "# "); + } + vfprintf(fp, fmt, argp); if (logfile) - fprintf(logfile, "\n"); + vfprintf(logfile, fmt, argp_logfile); + + /* + * If we are entering into a note with more details to follow, register + * that the leading '#' has been printed such that subsequent details + * aren't prefixed as well. + */ + if (type == NOTE_DETAIL) + in_note = true; + + /* + * If this was a Bail message, the bail protocol message must go to stdout + * separately. + */ + if (type == BAIL) + { + fprintf(stdout, "Bail out!"); + if (logfile) + fprintf(logfile, "Bail out!"); + } + + va_end(argp_logfile); + + if (type != NOTE_DETAIL) + { + fprintf(fp, "\n"); + if (logfile) + fprintf(logfile, "\n"); + } + fflush(NULL); } /* @@ -271,9 +426,8 @@ stop_postmaster(void) r = system(buf); if (r != 0) { - fprintf(stderr, _("\n%s: could not stop postmaster: exit code was %d\n"), - progname, r); - _exit(2); /* not exit(), that could be recursive */ + /* Not using the normal bail() as we want _exit */ + bail_noatexit(_("could not stop postmaster: exit code was %d"), r); } postmaster_running = false; @@ -331,9 +485,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. */ @@ -455,9 +608,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)) @@ -476,26 +628,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'; @@ -741,13 +887,13 @@ initialize_environment(void) } if (pghost && pgport) - printf(_("(using postmaster on %s, port %s)\n"), pghost, pgport); + note("using postmaster on %s, port %s", pghost, pgport); if (pghost && !pgport) - printf(_("(using postmaster on %s, default port)\n"), pghost); + note("using postmaster on %s, default port", pghost); if (!pghost && pgport) - printf(_("(using postmaster on Unix socket, port %s)\n"), pgport); + note("using postmaster on Unix socket, port %s", pgport); if (!pghost && !pgport) - printf(_("(using postmaster on Unix socket, default port)\n")); + note("using postmaster on Unix socket, default port"); } load_resultmap(); @@ -796,35 +942,26 @@ 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()); - exit(2); + bail("could not open process token: error code %lu", GetLastError()); } 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()); - exit(2); + bail("could not get token information buffer size: error code %lu", + GetLastError()); } 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()); - exit(2); + bail("could not get token information: error code %lu", + GetLastError()); } 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()); - exit(2); + bail("could not look up account SID: error code %lu", + GetLastError()); } free(tokenuser); @@ -870,8 +1007,7 @@ config_sspi_auth(const char *pgdata, const char *superuser_name) superuser_name = get_user_name(&errstr); if (superuser_name == NULL) { - fprintf(stderr, "%s: %s\n", progname, errstr); - exit(2); + bail("%s", errstr); } } @@ -902,9 +1038,8 @@ config_sspi_auth(const char *pgdata, const char *superuser_name) do { \ if (!(cond)) \ { \ - fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"), \ - progname, fname, strerror(errno)); \ - exit(2); \ + bail("could not write to file \"%s\": %s", \ + fname, strerror(errno)); \ } \ } while (0) @@ -915,15 +1050,13 @@ config_sspi_auth(const char *pgdata, const char *superuser_name) * Truncating this name is a fatal error, because we must not fail to * overwrite an original trust-authentication pg_hba.conf. */ - fprintf(stderr, _("%s: directory name too long\n"), progname); - exit(2); + bail("directory name too long"); } hba = fopen(fname, "w"); if (hba == NULL) { - fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"), - progname, fname, strerror(errno)); - exit(2); + bail("could not open file \"%s\" for writing: %s", + fname, strerror(errno)); } CW(fputs("# Configuration written by config_sspi_auth()\n", hba) >= 0); CW(fputs("host all all 127.0.0.1/32 sspi include_realm=1 map=regress\n", @@ -937,9 +1070,8 @@ config_sspi_auth(const char *pgdata, const char *superuser_name) ident = fopen(fname, "w"); if (ident == NULL) { - fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"), - progname, fname, strerror(errno)); - exit(2); + bail("could not open file \"%s\" for writing: %s", + fname, strerror(errno)); } CW(fputs("# Configuration written by config_sspi_auth()\n", ident) >= 0); @@ -973,7 +1105,7 @@ psql_start_command(void) StringInfo buf = makeStringInfo(); appendStringInfo(buf, - "\"%s%spsql\" -X", + "\"%s%spsql\" -X -q", bindir ? bindir : "", bindir ? "/" : ""); return buf; @@ -1029,8 +1161,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 */ @@ -1071,9 +1202,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) { @@ -1088,9 +1217,8 @@ 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)); - _exit(1); /* not exit() here... */ + /* Not using the normal bail() here as we want _exit */ + bail_noatexit("could not exec \"%s\": %s", shellprog, strerror(errno)); } /* in parent */ return pid; @@ -1128,8 +1256,8 @@ file_size(const char *file) if (!f) { - fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), - progname, file, strerror(errno)); + diag("could not open file \"%s\" for reading: %s", + file, strerror(errno)); return -1; } fseek(f, 0, SEEK_END); @@ -1150,8 +1278,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)); + diag("could not open file \"%s\" for reading: %s", + file, strerror(errno)); return -1; } while ((c = fgetc(f)) != EOF) @@ -1192,9 +1320,7 @@ 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)); } } @@ -1244,8 +1370,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 @@ -1255,8 +1380,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 @@ -1327,9 +1451,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)) @@ -1444,9 +1567,7 @@ 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; @@ -1455,9 +1576,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 */ @@ -1476,7 +1596,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]); + note_detail(" %s", names[i]); tests_left--; break; } @@ -1495,21 +1615,20 @@ static void log_child_failure(int exitstatus) { if (WIFEXITED(exitstatus)) - status(_(" (test process exited with exit code %d)"), - WEXITSTATUS(exitstatus)); + diag("(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)"), - WTERMSIG(exitstatus)); + diag("(test process was terminated by exception 0x%X)", + WTERMSIG(exitstatus)); #else - status(_(" (test process was terminated by signal %d: %s)"), - WTERMSIG(exitstatus), pg_strsignal(WTERMSIG(exitstatus))); + diag("(test process was terminated by signal %d: %s)", + WTERMSIG(exitstatus), pg_strsignal(WTERMSIG(exitstatus))); #endif } else - status(_(" (test process exited with unrecognized status %d)"), - exitstatus); + diag("(test process exited with unrecognized status %d)", exitstatus); } /* @@ -1540,9 +1659,8 @@ run_schedule(const char *schedule, test_start_function startfunc, 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)) @@ -1566,9 +1684,8 @@ run_schedule(const char *schedule, test_start_function startfunc, test = scbuf + 6; 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", + schedule, line_num, scbuf); } num_tests = 0; @@ -1584,9 +1701,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", + MAX_PARALLEL_TESTS, schedule, line_num, scbuf); } sav = *c; *c = '\0'; @@ -1608,14 +1724,12 @@ 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", + schedule, line_num, scbuf); } if (num_tests == 1) { - status(_("test %-28s ... "), 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); @@ -1623,16 +1737,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", + 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); + note_detail("parallel group (%d tests, in groups of %d): ", + num_tests, max_connections); for (i = 0; i < num_tests; i++) { if (i - oldest >= max_connections) @@ -1648,18 +1761,18 @@ run_schedule(const char *schedule, test_start_function startfunc, wait_for_tests(pids + oldest, statuses + oldest, stoptimes + oldest, tests + oldest, i - oldest); - status_end(); + note_end(); } else { - status(_("parallel group (%d tests): "), num_tests); + note_detail("parallel group (%d tests): ", num_tests); for (i = 0; i < num_tests; 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); - status_end(); + note_end(); } /* Check results for all tests */ @@ -1670,8 +1783,7 @@ run_schedule(const char *schedule, test_start_function startfunc, *tl; bool differ = false; - if (num_tests > 1) - status(_(" %-28s ... "), tests[i]); + INSTR_TIME_SUBTRACT(stoptimes[i], starttimes[i]); /* * Advance over all three lists simultaneously. @@ -1692,36 +1804,27 @@ 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); + diag("tag: %s", tl->str); } differ |= newdiff; } if (statuses[i] != 0) { - status(_("FAILED")); + test_status_failed(tests[i], INSTR_TIME_GET_MILLISEC(stoptimes[i]), (num_tests > 1)); log_child_failure(statuses[i]); - fail_count++; } else { - if (differ) { - status(_("FAILED")); - fail_count++; + test_status_failed(tests[i], INSTR_TIME_GET_MILLISEC(stoptimes[i]), (num_tests > 1)); } else { - status(_("ok ")); /* align with FAILED */ - success_count++; + test_status_ok(tests[i], INSTR_TIME_GET_MILLISEC(stoptimes[i]), (num_tests > 1)); } } - - INSTR_TIME_SUBTRACT(stoptimes[i], starttimes[i]); - status(_(" %8.0f ms"), INSTR_TIME_GET_MILLISEC(stoptimes[i])); - - status_end(); } for (i = 0; i < num_tests; i++) @@ -1756,7 +1859,6 @@ run_single_test(const char *test, test_start_function startfunc, *tl; bool differ = false; - status(_("test %-28s ... "), test); pid = (startfunc) (test, &resultfiles, &expectfiles, &tags); INSTR_TIME_SET_CURRENT(starttime); wait_for_tests(&pid, &exit_status, &stoptime, NULL, 1); @@ -1780,35 +1882,29 @@ 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); + diag("tag: %s", tl->str); } differ |= newdiff; } + INSTR_TIME_SUBTRACT(stoptime, starttime); + if (exit_status != 0) { - status(_("FAILED")); - fail_count++; + test_status_failed(test, false, INSTR_TIME_GET_MILLISEC(stoptime)); log_child_failure(exit_status); } else { if (differ) { - status(_("FAILED")); - fail_count++; + test_status_failed(test, false, INSTR_TIME_GET_MILLISEC(stoptime)); } else { - status(_("ok ")); /* align with FAILED */ - success_count++; + test_status_ok(test, INSTR_TIME_GET_MILLISEC(stoptime), false); } } - - INSTR_TIME_SUBTRACT(stoptime, starttime); - status(_(" %8.0f ms"), INSTR_TIME_GET_MILLISEC(stoptime)); - - status_end(); } /* @@ -1830,9 +1926,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("could not open file \"%s\" for writing: %s", + logfilename, strerror(errno)); } /* create the diffs file as empty */ @@ -1841,9 +1936,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("could not open file \"%s\" for writing: %s", + difffilename, strerror(errno)); } /* we don't keep the diffs file open continuously */ fclose(difffile); @@ -1859,7 +1953,6 @@ drop_database_if_exists(const char *dbname) { StringInfo buf = psql_start_command(); - header(_("dropping database \"%s\""), dbname); /* Set warning level so we don't see chatter about nonexistent DB */ psql_add_command(buf, "SET client_min_messages = warning"); psql_add_command(buf, "DROP DATABASE IF EXISTS \"%s\"", dbname); @@ -1876,7 +1969,6 @@ create_database(const char *dbname) * We use template0 so that any installation-local cruft in template1 will * not mess up the tests. */ - header(_("creating database \"%s\""), dbname); if (encoding) psql_add_command(buf, "CREATE DATABASE \"%s\" TEMPLATE=template0 ENCODING='%s'%s", dbname, encoding, (nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : ""); @@ -1898,10 +1990,7 @@ create_database(const char *dbname) * this will work whether or not the extension is preinstalled. */ for (sl = loadextension; sl != NULL; sl = sl->next) - { - header(_("installing %s"), sl->str); psql_command(dbname, "CREATE EXTENSION IF NOT EXISTS \"%s\"", sl->str); - } } static void @@ -1909,7 +1998,6 @@ drop_role_if_exists(const char *rolename) { StringInfo buf = psql_start_command(); - header(_("dropping role \"%s\""), rolename); /* Set warning level so we don't see chatter about nonexistent role */ psql_add_command(buf, "SET client_min_messages = warning"); psql_add_command(buf, "DROP ROLE IF EXISTS \"%s\"", rolename); @@ -1921,7 +2009,6 @@ create_role(const char *rolename, const _stringlist *granted_dbs) { StringInfo buf = psql_start_command(); - header(_("creating role \"%s\""), rolename); psql_add_command(buf, "CREATE ROLE \"%s\" WITH LOGIN", rolename); for (; granted_dbs != NULL; granted_dbs = granted_dbs->next) { @@ -2143,8 +2230,8 @@ regression_main(int argc, char *argv[], break; default: /* getopt_long already emitted a complaint */ - fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"), - progname); + pg_log_error_hint("Try \"%s --help\" for more information.", + progname); exit(2); } } @@ -2164,9 +2251,7 @@ regression_main(int argc, char *argv[], */ if (!(dblist && dblist->str && dblist->str[0])) { - fprintf(stderr, _("%s: no database name was specified\n"), - progname); - exit(2); + bail("no database name was specified"); } if (config_auth_datadir) @@ -2217,17 +2302,12 @@ regression_main(int argc, char *argv[], if (directory_exists(temp_instance)) { - 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("could not remove temp instance \"%s\"", temp_instance); } } - header(_("creating temporary instance")); - /* make the temp instance top directory */ make_directory(temp_instance); @@ -2237,7 +2317,6 @@ regression_main(int argc, char *argv[], make_directory(buf); /* initdb */ - header(_("initializing database system")); snprintf(buf, sizeof(buf), "\"%s%sinitdb\" -D \"%s/data\" --no-clean --no-sync%s%s > \"%s/log/initdb.log\" 2>&1", bindir ? bindir : "", @@ -2249,8 +2328,10 @@ regression_main(int argc, char *argv[], fflush(NULL); 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("initdb failed\n" + "# Examine \"%s/log/initdb.log\" for the reason.\n" + "# Command was: %s", + outputdir, buf); } /* @@ -2265,8 +2346,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("could not open \"%s\" for adding extra config: %s", + buf, strerror(errno)); } fputs("\n# Configuration added by pg_regress\n\n", pg_conf); fputs("log_autovacuum_min_duration = 0\n", pg_conf); @@ -2285,8 +2366,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("could not open \"%s\" to read extra config: %s", + temp_config, strerror(errno)); } while (fgets(line_buf, sizeof(line_buf), extra_conf) != NULL) fputs(line_buf, pg_conf); @@ -2325,14 +2406,13 @@ regression_main(int argc, char *argv[], if (port_specified_by_user || i == 15) { - fprintf(stderr, _("port %d apparently in use\n"), port); + note("port %d apparently in use", 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); + note("could not determine an available port"); + 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); + note("port %d apparently in use, trying %d", port, port + 1); port++; sprintf(s, "%d", port); setenv("PGPORT", s, 1); @@ -2344,7 +2424,6 @@ regression_main(int argc, char *argv[], /* * Start the temp postmaster */ - header(_("starting postmaster")); snprintf(buf, sizeof(buf), "\"%s%spostgres\" -D \"%s/data\" -F%s " "-c \"listen_addresses=%s\" -k \"%s\" " @@ -2356,11 +2435,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", strerror(errno)); /* * Wait till postmaster is able to accept connections; normally this @@ -2395,16 +2470,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, examine \"%s/log/postmaster.log\" for the reason", + 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); + diag("postmaster did not respond within %d seconds, examine \"%s/log/postmaster.log\" for the reason", + wait_seconds, outputdir); /* * If we get here, the postmaster is probably wedged somewhere in @@ -2413,17 +2488,14 @@ 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); + bail("postmaster failed"); } postmaster_running = true; @@ -2434,8 +2506,8 @@ regression_main(int argc, char *argv[], #else #define ULONGPID(x) (unsigned long) (x) #endif - printf(_("running on port %d with PID %lu\n"), - port, ULONGPID(postmaster_pid)); + note("using temp instance on port %d with PID %lu", + port, ULONGPID(postmaster_pid)); } else { @@ -2466,8 +2538,6 @@ regression_main(int argc, char *argv[], /* * Ready to run the tests */ - header(_("running regression test queries")); - for (sl = schedulelist; sl != NULL; sl = sl->next) { run_schedule(sl->str, startfunc, postfunc); @@ -2483,7 +2553,6 @@ regression_main(int argc, char *argv[], */ if (temp_instance) { - header(_("shutting down postmaster")); stop_postmaster(); } @@ -2494,42 +2563,30 @@ regression_main(int argc, char *argv[], */ if (temp_instance && fail_count == 0) { - header(_("removing temporary instance")); if (!rmtree(temp_instance, true)) - fprintf(stderr, _("\n%s: could not remove temp instance \"%s\"\n"), - progname, temp_instance); + diag("could not remove temp instance \"%s\"", + temp_instance); } - fclose(logfile); + /* + * Emit a TAP compliant Plan + */ + plan(fail_count + success_count); /* * Emit nice-looking summary message */ if (fail_count == 0) - snprintf(buf, sizeof(buf), - _(" All %d tests passed. "), - success_count); + note("All %d tests passed.", success_count); else - snprintf(buf, sizeof(buf), - _(" %d of %d tests failed. "), - fail_count, - success_count + fail_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'); + diag("%d of %d tests failed.", fail_count, success_count + fail_count); 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); + diag("The differences that caused some tests to fail can be viewed in the file \"%s\".", + difffilename); + diag("A copy of the test summary that you see above is saved in the file \"%s\".", + logfilename); } else { @@ -2537,6 +2594,9 @@ regression_main(int argc, char *argv[], unlink(logfilename); } + fclose(logfile); + logfile = NULL; + if (fail_count != 0) exit(1); |