Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pg_regress --- regression test driver
4 : *
5 : * This is a C implementation of the previous shell script for running
6 : * the regression tests, and should be mostly compatible with it.
7 : * Initial author of C translation: Magnus Hagander
8 : *
9 : * This code is released under the terms of the PostgreSQL License.
10 : *
11 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
12 : * Portions Copyright (c) 1994, Regents of the University of California
13 : *
14 : * src/test/regress/pg_regress.c
15 : *
16 : *-------------------------------------------------------------------------
17 : */
18 :
19 : #include "postgres_fe.h"
20 :
21 : #include <ctype.h>
22 : #include <sys/resource.h>
23 : #include <sys/stat.h>
24 : #include <sys/time.h>
25 : #include <sys/wait.h>
26 : #include <signal.h>
27 : #include <unistd.h>
28 :
29 : #include "common/logging.h"
30 : #include "common/restricted_token.h"
31 : #include "common/string.h"
32 : #include "common/username.h"
33 : #include "getopt_long.h"
34 : #include "lib/stringinfo.h"
35 : #include "libpq-fe.h"
36 : #include "libpq/pqcomm.h" /* needed for UNIXSOCK_PATH() */
37 : #include "pg_config_paths.h"
38 : #include "pg_regress.h"
39 : #include "portability/instr_time.h"
40 :
41 : /* for resultmap we need a list of pairs of strings */
42 : typedef struct _resultmap
43 : {
44 : char *test;
45 : char *type;
46 : char *resultfile;
47 : struct _resultmap *next;
48 : } _resultmap;
49 :
50 : /*
51 : * Values obtained from Makefile.
52 : */
53 : char *host_platform = HOST_TUPLE;
54 :
55 : #ifndef WIN32 /* not used in WIN32 case */
56 : static char *shellprog = SHELLPROG;
57 : #endif
58 :
59 : /*
60 : * On Windows we use -w in diff switches to avoid problems with inconsistent
61 : * newline representation. The actual result files will generally have
62 : * Windows-style newlines, but the comparison files might or might not.
63 : */
64 : #ifndef WIN32
65 : const char *basic_diff_opts = "";
66 : const char *pretty_diff_opts = "-U3";
67 : #else
68 : const char *basic_diff_opts = "-w";
69 : const char *pretty_diff_opts = "-w -U3";
70 : #endif
71 :
72 : /*
73 : * The width of the testname field when printing to ensure vertical alignment
74 : * of test runtimes. This number is somewhat arbitrarily chosen to match the
75 : * older pre-TAP output format.
76 : */
77 : #define TESTNAME_WIDTH 36
78 :
79 : /*
80 : * The number times per second that pg_regress checks to see if the test
81 : * instance server has started and is available for connection.
82 : */
83 : #define WAIT_TICKS_PER_SECOND 20
84 :
85 : typedef enum TAPtype
86 : {
87 : DIAG = 0,
88 : BAIL,
89 : NOTE,
90 : NOTE_DETAIL,
91 : NOTE_END,
92 : TEST_STATUS,
93 : PLAN,
94 : NONE,
95 : } TAPtype;
96 :
97 : /* options settable from command line */
98 : _stringlist *dblist = NULL;
99 : bool debug = false;
100 : char *inputdir = ".";
101 : char *outputdir = ".";
102 : char *expecteddir = ".";
103 : char *bindir = PGBINDIR;
104 : char *launcher = NULL;
105 : static _stringlist *loadextension = NULL;
106 : static int max_connections = 0;
107 : static int max_concurrent_tests = 0;
108 : static char *encoding = NULL;
109 : static _stringlist *schedulelist = NULL;
110 : static _stringlist *extra_tests = NULL;
111 : static char *temp_instance = NULL;
112 : static _stringlist *temp_configs = NULL;
113 : static bool nolocale = false;
114 : static bool use_existing = false;
115 : static char *hostname = NULL;
116 : static int port = -1;
117 : static char portstr[16];
118 : static bool port_specified_by_user = false;
119 : static char *dlpath = PKGLIBDIR;
120 : static char *user = NULL;
121 : static _stringlist *extraroles = NULL;
122 : static char *config_auth_datadir = NULL;
123 :
124 : /* internal variables */
125 : static const char *progname;
126 : static char *logfilename;
127 : static FILE *logfile;
128 : static char *difffilename;
129 : static const char *sockdir;
130 : static const char *temp_sockdir;
131 : static char sockself[MAXPGPATH];
132 : static char socklock[MAXPGPATH];
133 : static StringInfo failed_tests = NULL;
134 : static bool in_note = false;
135 :
136 : static _resultmap *resultmap = NULL;
137 :
138 : static PID_TYPE postmaster_pid = INVALID_PID;
139 : static bool postmaster_running = false;
140 :
141 : static int success_count = 0;
142 : static int fail_count = 0;
143 :
144 : static bool directory_exists(const char *dir);
145 : static void make_directory(const char *dir);
146 :
147 : static void test_status_print(bool ok, const char *testname, double runtime, bool parallel);
148 : static void test_status_ok(const char *testname, double runtime, bool parallel);
149 : static void test_status_failed(const char *testname, double runtime, bool parallel);
150 : static void bail_out(bool noatexit, const char *fmt,...) pg_attribute_printf(2, 3);
151 : static void emit_tap_output(TAPtype type, const char *fmt,...) pg_attribute_printf(2, 3);
152 : static void emit_tap_output_v(TAPtype type, const char *fmt, va_list argp) pg_attribute_printf(2, 0);
153 :
154 : static StringInfo psql_start_command(void);
155 : static void psql_add_command(StringInfo buf, const char *query,...) pg_attribute_printf(2, 3);
156 : static void psql_end_command(StringInfo buf, const char *database);
157 :
158 : /*
159 : * Convenience macros for printing TAP output with a more shorthand syntax
160 : * aimed at making the code more readable.
161 : */
162 : #define plan(x) emit_tap_output(PLAN, "1..%i", (x))
163 : #define note(...) emit_tap_output(NOTE, __VA_ARGS__)
164 : #define note_detail(...) emit_tap_output(NOTE_DETAIL, __VA_ARGS__)
165 : #define diag(...) emit_tap_output(DIAG, __VA_ARGS__)
166 : #define note_end() emit_tap_output(NOTE_END, "\n");
167 : #define bail_noatexit(...) bail_out(true, __VA_ARGS__)
168 : #define bail(...) bail_out(false, __VA_ARGS__)
169 :
170 : /*
171 : * allow core files if possible.
172 : */
173 : #if defined(HAVE_GETRLIMIT)
174 : static void
175 170 : unlimit_core_size(void)
176 : {
177 : struct rlimit lim;
178 :
179 170 : getrlimit(RLIMIT_CORE, &lim);
180 170 : if (lim.rlim_max == 0)
181 : {
182 0 : diag("could not set core size: disallowed by hard limit");
183 0 : return;
184 : }
185 170 : else if (lim.rlim_max == RLIM_INFINITY || lim.rlim_cur < lim.rlim_max)
186 : {
187 170 : lim.rlim_cur = lim.rlim_max;
188 170 : setrlimit(RLIMIT_CORE, &lim);
189 : }
190 : }
191 : #endif
192 :
193 :
194 : /*
195 : * Add an item at the end of a stringlist.
196 : */
197 : void
198 8240 : add_stringlist_item(_stringlist **listhead, const char *str)
199 : {
200 8240 : _stringlist *newentry = pg_malloc(sizeof(_stringlist));
201 : _stringlist *oldentry;
202 :
203 8240 : newentry->str = pg_strdup(str);
204 8240 : newentry->next = NULL;
205 8240 : if (*listhead == NULL)
206 6210 : *listhead = newentry;
207 : else
208 : {
209 6712 : for (oldentry = *listhead; oldentry->next; oldentry = oldentry->next)
210 : /* skip */ ;
211 2030 : oldentry->next = newentry;
212 : }
213 8240 : }
214 :
215 : /*
216 : * Free a stringlist.
217 : */
218 : static void
219 7176 : free_stringlist(_stringlist **listhead)
220 : {
221 7176 : if (listhead == NULL || *listhead == NULL)
222 1574 : return;
223 5602 : if ((*listhead)->next != NULL)
224 1536 : free_stringlist(&((*listhead)->next));
225 5602 : free((*listhead)->str);
226 5602 : free(*listhead);
227 5602 : *listhead = NULL;
228 : }
229 :
230 : /*
231 : * Split a delimited string into a stringlist
232 : */
233 : static void
234 208 : split_to_stringlist(const char *s, const char *delim, _stringlist **listhead)
235 : {
236 208 : char *sc = pg_strdup(s);
237 208 : char *token = strtok(sc, delim);
238 :
239 430 : while (token)
240 : {
241 222 : add_stringlist_item(listhead, token);
242 222 : token = strtok(NULL, delim);
243 : }
244 208 : free(sc);
245 208 : }
246 :
247 : /*
248 : * Bailing out is for unrecoverable errors which prevents further testing to
249 : * occur and after which the test run should be aborted. By passing noatexit
250 : * as true the process will terminate with _exit(2) and skipping registered
251 : * exit handlers, thus avoid any risk of bottomless recursion calls to exit.
252 : */
253 : static void
254 0 : bail_out(bool noatexit, const char *fmt,...)
255 : {
256 : va_list ap;
257 :
258 0 : va_start(ap, fmt);
259 0 : emit_tap_output_v(BAIL, fmt, ap);
260 0 : va_end(ap);
261 :
262 0 : if (noatexit)
263 0 : _exit(2);
264 :
265 0 : exit(2);
266 : }
267 :
268 : /*
269 : * Print the result of a test run and associated metadata like runtime. Care
270 : * is taken to align testnames and runtimes vertically to ensure the output
271 : * is human readable while still TAP compliant. Tests run in parallel are
272 : * prefixed with a '+' and sequential tests with a '-'. This distinction was
273 : * previously indicated by 'test' prefixing sequential tests while parallel
274 : * tests were indented by four leading spaces. The meson TAP parser consumes
275 : * leading space however, so a non-whitespace prefix of the same length is
276 : * required for both.
277 : */
278 : static void
279 2432 : test_status_print(bool ok, const char *testname, double runtime, bool parallel)
280 : {
281 2432 : int testnumber = fail_count + success_count;
282 :
283 : /*
284 : * Testnumbers are padded to 5 characters to ensure that testnames align
285 : * vertically (assuming at most 9999 tests). Testnames are prefixed with
286 : * a leading character to indicate being run in parallel or not. A leading
287 : * '+' indicates a parallel test, '-' indicates a single test.
288 : */
289 2432 : emit_tap_output(TEST_STATUS, "%sok %-5i%*s %c %-*s %8.0f ms",
290 : (ok ? "" : "not "),
291 : testnumber,
292 : /* If ok, indent with four spaces matching "not " */
293 : (ok ? (int) strlen("not ") : 0), "",
294 : /* Prefix a parallel test '+' and a single test with '-' */
295 : (parallel ? '+' : '-'),
296 : /* Testnames are padded to align runtimes */
297 : TESTNAME_WIDTH, testname,
298 : runtime);
299 2432 : }
300 :
301 : static void
302 2432 : test_status_ok(const char *testname, double runtime, bool parallel)
303 : {
304 2432 : success_count++;
305 :
306 2432 : test_status_print(true, testname, runtime, parallel);
307 2432 : }
308 :
309 : static void
310 0 : test_status_failed(const char *testname, double runtime, bool parallel)
311 : {
312 : /*
313 : * Save failed tests in a buffer such that we can print a summary at the
314 : * end with diag() to ensure it's shown even under test harnesses.
315 : */
316 0 : if (!failed_tests)
317 0 : failed_tests = makeStringInfo();
318 : else
319 0 : appendStringInfoChar(failed_tests, ',');
320 :
321 0 : appendStringInfo(failed_tests, " %s", testname);
322 :
323 0 : fail_count++;
324 :
325 0 : test_status_print(false, testname, runtime, parallel);
326 0 : }
327 :
328 :
329 : static void
330 4614 : emit_tap_output(TAPtype type, const char *fmt,...)
331 : {
332 : va_list argp;
333 :
334 4614 : va_start(argp, fmt);
335 4614 : emit_tap_output_v(type, fmt, argp);
336 4614 : va_end(argp);
337 4614 : }
338 :
339 : static void
340 4614 : emit_tap_output_v(TAPtype type, const char *fmt, va_list argp)
341 : {
342 : va_list argp_logfile;
343 : FILE *fp;
344 : int save_errno;
345 :
346 : /*
347 : * The fprintf() calls used to output TAP-protocol elements might clobber
348 : * errno, so save it here and restore it before vfprintf()-ing the user's
349 : * format string, in case it contains %m placeholders.
350 : */
351 4614 : save_errno = errno;
352 :
353 : /*
354 : * Diagnostic output will be hidden by prove unless printed to stderr. The
355 : * Bail message is also printed to stderr to aid debugging under a harness
356 : * which might otherwise not emit such an important message.
357 : */
358 4614 : if (type == DIAG || type == BAIL)
359 0 : fp = stderr;
360 : else
361 4614 : fp = stdout;
362 :
363 : /*
364 : * If we are ending a note_detail line we can avoid further processing and
365 : * immediately return following a newline.
366 : */
367 4614 : if (type == NOTE_END)
368 : {
369 108 : in_note = false;
370 108 : fprintf(fp, "\n");
371 108 : if (logfile)
372 108 : fprintf(logfile, "\n");
373 108 : return;
374 : }
375 :
376 : /* Make a copy of the va args for printing to the logfile */
377 4506 : va_copy(argp_logfile, argp);
378 :
379 : /*
380 : * Non-protocol output such as diagnostics or notes must be prefixed by a
381 : * '#' character. We print the Bail message like this too.
382 : */
383 4506 : if ((type == NOTE || type == DIAG || type == BAIL)
384 4000 : || (type == NOTE_DETAIL && !in_note))
385 : {
386 614 : fprintf(fp, "# ");
387 614 : if (logfile)
388 614 : fprintf(logfile, "# ");
389 : }
390 4506 : errno = save_errno;
391 4506 : vfprintf(fp, fmt, argp);
392 4506 : if (logfile)
393 : {
394 4506 : errno = save_errno;
395 4506 : vfprintf(logfile, fmt, argp_logfile);
396 : }
397 :
398 : /*
399 : * If we are entering into a note with more details to follow, register
400 : * that the leading '#' has been printed such that subsequent details
401 : * aren't prefixed as well.
402 : */
403 4506 : if (type == NOTE_DETAIL)
404 1398 : in_note = true;
405 :
406 : /*
407 : * If this was a Bail message, the bail protocol message must go to stdout
408 : * separately.
409 : */
410 4506 : if (type == BAIL)
411 : {
412 0 : fprintf(stdout, "Bail out!");
413 0 : if (logfile)
414 0 : fprintf(logfile, "Bail out!");
415 : }
416 :
417 4506 : va_end(argp_logfile);
418 :
419 4506 : if (type != NOTE_DETAIL)
420 : {
421 3108 : fprintf(fp, "\n");
422 3108 : if (logfile)
423 3108 : fprintf(logfile, "\n");
424 : }
425 4506 : fflush(NULL);
426 : }
427 :
428 : /*
429 : * shut down temp postmaster
430 : */
431 : static void
432 846 : stop_postmaster(void)
433 : {
434 846 : if (postmaster_running)
435 : {
436 : /* We use pg_ctl to issue the kill and wait for stop */
437 : char buf[MAXPGPATH * 2];
438 : int r;
439 :
440 332 : snprintf(buf, sizeof(buf),
441 : "\"%s%spg_ctl\" stop -D \"%s/data\" -s",
442 166 : bindir ? bindir : "",
443 166 : bindir ? "/" : "",
444 : temp_instance);
445 166 : fflush(NULL);
446 166 : r = system(buf);
447 166 : if (r != 0)
448 : {
449 : /* Not using the normal bail() as we want _exit */
450 0 : bail_noatexit(_("could not stop postmaster: exit code was %d"), r);
451 : }
452 :
453 166 : postmaster_running = false;
454 : }
455 846 : }
456 :
457 : /*
458 : * Remove the socket temporary directory. pg_regress never waits for a
459 : * postmaster exit, so it is indeterminate whether the postmaster has yet to
460 : * unlink the socket and lock file. Unlink them here so we can proceed to
461 : * remove the directory. Ignore errors; leaking a temporary directory is
462 : * unimportant. This can run from a signal handler. The code is not
463 : * acceptable in a Windows signal handler (see initdb.c:trapsig()), but
464 : * on Windows, pg_regress does not use Unix sockets by default.
465 : */
466 : static void
467 164 : remove_temp(void)
468 : {
469 : Assert(temp_sockdir);
470 164 : unlink(sockself);
471 164 : unlink(socklock);
472 164 : rmdir(temp_sockdir);
473 164 : }
474 :
475 : /*
476 : * Signal handler that calls remove_temp() and reraises the signal.
477 : */
478 : static void
479 0 : signal_remove_temp(SIGNAL_ARGS)
480 : {
481 0 : remove_temp();
482 :
483 0 : pqsignal(postgres_signal_arg, SIG_DFL);
484 0 : raise(postgres_signal_arg);
485 0 : }
486 :
487 : /*
488 : * Create a temporary directory suitable for the server's Unix-domain socket.
489 : * The directory will have mode 0700 or stricter, so no other OS user can open
490 : * our socket to exploit our use of trust authentication. Most systems
491 : * constrain the length of socket paths well below _POSIX_PATH_MAX, so we
492 : * place the directory under /tmp rather than relative to the possibly-deep
493 : * current working directory.
494 : *
495 : * Compared to using the compiled-in DEFAULT_PGSOCKET_DIR, this also permits
496 : * testing to work in builds that relocate it to a directory not writable to
497 : * the build/test user.
498 : */
499 : static const char *
500 164 : make_temp_sockdir(void)
501 : {
502 164 : char *template = psprintf("%s/pg_regress-XXXXXX",
503 164 : getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp");
504 :
505 164 : temp_sockdir = mkdtemp(template);
506 164 : if (temp_sockdir == NULL)
507 0 : bail("could not create directory \"%s\": %m", template);
508 :
509 : /* Stage file names for remove_temp(). Unsafe in a signal handler. */
510 164 : UNIXSOCK_PATH(sockself, port, temp_sockdir);
511 164 : snprintf(socklock, sizeof(socklock), "%s.lock", sockself);
512 :
513 : /* Remove the directory during clean exit. */
514 164 : atexit(remove_temp);
515 :
516 : /*
517 : * Remove the directory before dying to the usual signals. Omit SIGQUIT,
518 : * preserving it as a quick, untidy exit.
519 : */
520 164 : pqsignal(SIGHUP, signal_remove_temp);
521 164 : pqsignal(SIGINT, signal_remove_temp);
522 164 : pqsignal(SIGPIPE, signal_remove_temp);
523 164 : pqsignal(SIGTERM, signal_remove_temp);
524 :
525 164 : return temp_sockdir;
526 : }
527 :
528 : /*
529 : * Check whether string matches pattern
530 : *
531 : * In the original shell script, this function was implemented using expr(1),
532 : * which provides basic regular expressions restricted to match starting at
533 : * the string start (in conventional regex terms, there's an implicit "^"
534 : * at the start of the pattern --- but no implicit "$" at the end).
535 : *
536 : * For now, we only support "." and ".*" as non-literal metacharacters,
537 : * because that's all that anyone has found use for in resultmap. This
538 : * code could be extended if more functionality is needed.
539 : */
540 : static bool
541 84 : string_matches_pattern(const char *str, const char *pattern)
542 : {
543 156 : while (*str && *pattern)
544 : {
545 156 : if (*pattern == '.' && pattern[1] == '*')
546 : {
547 48 : pattern += 2;
548 : /* Trailing .* matches everything. */
549 48 : if (*pattern == '\0')
550 0 : return true;
551 :
552 : /*
553 : * Otherwise, scan for a text position at which we can match the
554 : * rest of the pattern.
555 : */
556 564 : while (*str)
557 : {
558 : /*
559 : * Optimization to prevent most recursion: don't recurse
560 : * unless first pattern char might match this text char.
561 : */
562 516 : if (*str == *pattern || *pattern == '.')
563 : {
564 72 : if (string_matches_pattern(str, pattern))
565 0 : return true;
566 : }
567 :
568 516 : str++;
569 : }
570 :
571 : /*
572 : * End of text with no match.
573 : */
574 48 : return false;
575 : }
576 108 : else if (*pattern != '.' && *str != *pattern)
577 : {
578 : /*
579 : * Not the single-character wildcard and no explicit match? Then
580 : * time to quit...
581 : */
582 36 : return false;
583 : }
584 :
585 72 : str++;
586 72 : pattern++;
587 : }
588 :
589 0 : if (*pattern == '\0')
590 0 : return true; /* end of pattern, so declare match */
591 :
592 : /* End of input string. Do we have matching pattern remaining? */
593 0 : while (*pattern == '.' && pattern[1] == '*')
594 0 : pattern += 2;
595 0 : if (*pattern == '\0')
596 0 : return true; /* end of pattern, so declare match */
597 :
598 0 : return false;
599 : }
600 :
601 : /*
602 : * Scan resultmap file to find which platform-specific expected files to use.
603 : *
604 : * The format of each line of the file is
605 : * testname/hostplatformpattern=substitutefile
606 : * where the hostplatformpattern is evaluated per the rules of expr(1),
607 : * namely, it is a standard regular expression with an implicit ^ at the start.
608 : * (We currently support only a very limited subset of regular expressions,
609 : * see string_matches_pattern() above.) What hostplatformpattern will be
610 : * matched against is the config.guess output. (In the shell-script version,
611 : * we also provided an indication of whether gcc or another compiler was in
612 : * use, but that facility isn't used anymore.)
613 : */
614 : static void
615 170 : load_resultmap(void)
616 : {
617 : char buf[MAXPGPATH];
618 : FILE *f;
619 :
620 : /* scan the file ... */
621 170 : snprintf(buf, sizeof(buf), "%s/resultmap", inputdir);
622 170 : f = fopen(buf, "r");
623 170 : if (!f)
624 : {
625 : /* OK if it doesn't exist, else complain */
626 164 : if (errno == ENOENT)
627 164 : return;
628 0 : bail("could not open file \"%s\" for reading: %m", buf);
629 : }
630 :
631 18 : while (fgets(buf, sizeof(buf), f))
632 : {
633 : char *platform;
634 : char *file_type;
635 : char *expected;
636 : int i;
637 :
638 : /* strip trailing whitespace, especially the newline */
639 12 : i = strlen(buf);
640 24 : while (i > 0 && isspace((unsigned char) buf[i - 1]))
641 12 : buf[--i] = '\0';
642 :
643 : /* parse out the line fields */
644 12 : file_type = strchr(buf, ':');
645 12 : if (!file_type)
646 : {
647 0 : bail("incorrectly formatted resultmap entry: %s", buf);
648 : }
649 12 : *file_type++ = '\0';
650 :
651 12 : platform = strchr(file_type, ':');
652 12 : if (!platform)
653 : {
654 0 : bail("incorrectly formatted resultmap entry: %s", buf);
655 : }
656 12 : *platform++ = '\0';
657 12 : expected = strchr(platform, '=');
658 12 : if (!expected)
659 : {
660 0 : bail("incorrectly formatted resultmap entry: %s", buf);
661 : }
662 12 : *expected++ = '\0';
663 :
664 : /*
665 : * if it's for current platform, save it in resultmap list. Note: by
666 : * adding at the front of the list, we ensure that in ambiguous cases,
667 : * the last match in the resultmap file is used. This mimics the
668 : * behavior of the old shell script.
669 : */
670 12 : if (string_matches_pattern(host_platform, platform))
671 : {
672 0 : _resultmap *entry = pg_malloc(sizeof(_resultmap));
673 :
674 0 : entry->test = pg_strdup(buf);
675 0 : entry->type = pg_strdup(file_type);
676 0 : entry->resultfile = pg_strdup(expected);
677 0 : entry->next = resultmap;
678 0 : resultmap = entry;
679 : }
680 : }
681 6 : fclose(f);
682 : }
683 :
684 : /*
685 : * Check in resultmap if we should be looking at a different file
686 : */
687 : static
688 : const char *
689 2956 : get_expectfile(const char *testname, const char *file)
690 : {
691 : char *file_type;
692 : _resultmap *rm;
693 :
694 : /*
695 : * Determine the file type from the file name. This is just what is
696 : * following the last dot in the file name.
697 : */
698 2956 : if (!file || !(file_type = strrchr(file, '.')))
699 0 : return NULL;
700 :
701 2956 : file_type++;
702 :
703 2956 : for (rm = resultmap; rm != NULL; rm = rm->next)
704 : {
705 0 : if (strcmp(testname, rm->test) == 0 && strcmp(file_type, rm->type) == 0)
706 : {
707 0 : return rm->resultfile;
708 : }
709 : }
710 :
711 2956 : return NULL;
712 : }
713 :
714 : /*
715 : * Prepare environment variables for running regression tests
716 : */
717 : static void
718 170 : initialize_environment(void)
719 : {
720 : /*
721 : * Set default application_name. (The test_start_function may choose to
722 : * override this, but if it doesn't, we have something useful in place.)
723 : */
724 170 : setenv("PGAPPNAME", "pg_regress", 1);
725 :
726 : /*
727 : * Set variables that the test scripts may need to refer to.
728 : */
729 170 : setenv("PG_ABS_SRCDIR", inputdir, 1);
730 170 : setenv("PG_ABS_BUILDDIR", outputdir, 1);
731 170 : setenv("PG_LIBDIR", dlpath, 1);
732 170 : setenv("PG_DLSUFFIX", DLSUFFIX, 1);
733 :
734 170 : if (nolocale)
735 : {
736 : /*
737 : * Clear out any non-C locale settings
738 : */
739 4 : unsetenv("LC_COLLATE");
740 4 : unsetenv("LC_CTYPE");
741 4 : unsetenv("LC_MONETARY");
742 4 : unsetenv("LC_NUMERIC");
743 4 : unsetenv("LC_TIME");
744 4 : unsetenv("LANG");
745 :
746 : /*
747 : * Most platforms have adopted the POSIX locale as their
748 : * implementation-defined default locale. Exceptions include native
749 : * Windows, macOS with --enable-nls, and Cygwin with --enable-nls.
750 : * (Use of --enable-nls matters because libintl replaces setlocale().)
751 : * Also, PostgreSQL does not support macOS with locale environment
752 : * variables unset; see PostmasterMain().
753 : */
754 : #if defined(WIN32) || defined(__CYGWIN__) || defined(__darwin__)
755 : setenv("LANG", "C", 1);
756 : #endif
757 : }
758 :
759 : /*
760 : * Set translation-related settings to English; otherwise psql will
761 : * produce translated messages and produce diffs. (XXX If we ever support
762 : * translation of pg_regress, this needs to be moved elsewhere, where psql
763 : * is actually called.)
764 : */
765 170 : unsetenv("LANGUAGE");
766 170 : unsetenv("LC_ALL");
767 170 : setenv("LC_MESSAGES", "C", 1);
768 :
769 : /*
770 : * Set encoding as requested
771 : */
772 170 : if (encoding)
773 2 : setenv("PGCLIENTENCODING", encoding, 1);
774 : else
775 168 : unsetenv("PGCLIENTENCODING");
776 :
777 : /*
778 : * Set timezone and datestyle for datetime-related tests
779 : */
780 170 : setenv("PGTZ", "PST8PDT", 1);
781 170 : setenv("PGDATESTYLE", "Postgres, MDY", 1);
782 :
783 : /*
784 : * Likewise set intervalstyle to ensure consistent results. This is a bit
785 : * more painful because we must use PGOPTIONS, and we want to preserve the
786 : * user's ability to set other variables through that.
787 : */
788 : {
789 170 : const char *my_pgoptions = "-c intervalstyle=postgres_verbose";
790 170 : const char *old_pgoptions = getenv("PGOPTIONS");
791 : char *new_pgoptions;
792 :
793 170 : if (!old_pgoptions)
794 170 : old_pgoptions = "";
795 170 : new_pgoptions = psprintf("%s %s",
796 : old_pgoptions, my_pgoptions);
797 170 : setenv("PGOPTIONS", new_pgoptions, 1);
798 170 : free(new_pgoptions);
799 : }
800 :
801 170 : if (temp_instance)
802 : {
803 : /*
804 : * Clear out any environment vars that might cause psql to connect to
805 : * the wrong postmaster, or otherwise behave in nondefault ways. (Note
806 : * we also use psql's -X switch consistently, so that ~/.psqlrc files
807 : * won't mess things up.) Also, set PGPORT to the temp port, and set
808 : * PGHOST depending on whether we are using TCP or Unix sockets.
809 : *
810 : * This list should be kept in sync with PostgreSQL/Test/Utils.pm.
811 : */
812 166 : unsetenv("PGCHANNELBINDING");
813 : /* PGCLIENTENCODING, see above */
814 166 : unsetenv("PGCONNECT_TIMEOUT");
815 166 : unsetenv("PGDATA");
816 166 : unsetenv("PGDATABASE");
817 166 : unsetenv("PGGSSDELEGATION");
818 166 : unsetenv("PGGSSENCMODE");
819 166 : unsetenv("PGGSSLIB");
820 : /* PGHOSTADDR, see below */
821 166 : unsetenv("PGKRBSRVNAME");
822 166 : unsetenv("PGPASSFILE");
823 166 : unsetenv("PGPASSWORD");
824 166 : unsetenv("PGREQUIREPEER");
825 166 : unsetenv("PGREQUIRESSL");
826 166 : unsetenv("PGSERVICE");
827 166 : unsetenv("PGSERVICEFILE");
828 166 : unsetenv("PGSSLCERT");
829 166 : unsetenv("PGSSLCRL");
830 166 : unsetenv("PGSSLCRLDIR");
831 166 : unsetenv("PGSSLKEY");
832 166 : unsetenv("PGSSLMAXPROTOCOLVERSION");
833 166 : unsetenv("PGSSLMINPROTOCOLVERSION");
834 166 : unsetenv("PGSSLMODE");
835 166 : unsetenv("PGSSLROOTCERT");
836 166 : unsetenv("PGSSLSNI");
837 166 : unsetenv("PGTARGETSESSIONATTRS");
838 166 : unsetenv("PGUSER");
839 : /* PGPORT, see below */
840 : /* PGHOST, see below */
841 :
842 166 : if (hostname != NULL)
843 2 : setenv("PGHOST", hostname, 1);
844 : else
845 : {
846 164 : sockdir = getenv("PG_REGRESS_SOCK_DIR");
847 164 : if (!sockdir)
848 164 : sockdir = make_temp_sockdir();
849 164 : setenv("PGHOST", sockdir, 1);
850 : }
851 166 : unsetenv("PGHOSTADDR");
852 166 : if (port != -1)
853 : {
854 : char s[16];
855 :
856 166 : snprintf(s, sizeof(s), "%d", port);
857 166 : setenv("PGPORT", s, 1);
858 : }
859 : }
860 : else
861 : {
862 : const char *pghost;
863 : const char *pgport;
864 :
865 : /*
866 : * When testing an existing install, we honor existing environment
867 : * variables, except if they're overridden by command line options.
868 : */
869 4 : if (hostname != NULL)
870 : {
871 4 : setenv("PGHOST", hostname, 1);
872 4 : unsetenv("PGHOSTADDR");
873 : }
874 4 : if (port != -1)
875 : {
876 : char s[16];
877 :
878 4 : snprintf(s, sizeof(s), "%d", port);
879 4 : setenv("PGPORT", s, 1);
880 : }
881 4 : if (user != NULL)
882 0 : setenv("PGUSER", user, 1);
883 :
884 : /*
885 : * However, we *don't* honor PGDATABASE, since we certainly don't wish
886 : * to connect to whatever database the user might like as default.
887 : * (Most tests override PGDATABASE anyway, but there are some ECPG
888 : * test cases that don't.)
889 : */
890 4 : unsetenv("PGDATABASE");
891 :
892 : /*
893 : * Report what we're connecting to
894 : */
895 4 : pghost = getenv("PGHOST");
896 4 : pgport = getenv("PGPORT");
897 4 : if (!pghost)
898 : {
899 : /* Keep this bit in sync with libpq's default host location: */
900 0 : if (DEFAULT_PGSOCKET_DIR[0])
901 : /* do nothing, we'll print "Unix socket" below */ ;
902 : else
903 0 : pghost = "localhost"; /* DefaultHost in fe-connect.c */
904 : }
905 :
906 4 : if (pghost && pgport)
907 4 : note("using postmaster on %s, port %s", pghost, pgport);
908 4 : if (pghost && !pgport)
909 0 : note("using postmaster on %s, default port", pghost);
910 4 : if (!pghost && pgport)
911 0 : note("using postmaster on Unix socket, port %s", pgport);
912 4 : if (!pghost && !pgport)
913 0 : note("using postmaster on Unix socket, default port");
914 : }
915 :
916 170 : load_resultmap();
917 170 : }
918 :
919 : #ifdef ENABLE_SSPI
920 :
921 : /* support for config_sspi_auth() */
922 : static const char *
923 : fmtHba(const char *raw)
924 : {
925 : static char *ret;
926 : const char *rp;
927 : char *wp;
928 :
929 : wp = ret = pg_realloc(ret, 3 + strlen(raw) * 2);
930 :
931 : *wp++ = '"';
932 : for (rp = raw; *rp; rp++)
933 : {
934 : if (*rp == '"')
935 : *wp++ = '"';
936 : *wp++ = *rp;
937 : }
938 : *wp++ = '"';
939 : *wp++ = '\0';
940 :
941 : return ret;
942 : }
943 :
944 : /*
945 : * Get account and domain/realm names for the current user. This is based on
946 : * pg_SSPI_recvauth(). The returned strings use static storage.
947 : */
948 : static void
949 : current_windows_user(const char **acct, const char **dom)
950 : {
951 : static char accountname[MAXPGPATH];
952 : static char domainname[MAXPGPATH];
953 : HANDLE token;
954 : TOKEN_USER *tokenuser;
955 : DWORD retlen;
956 : DWORD accountnamesize = sizeof(accountname);
957 : DWORD domainnamesize = sizeof(domainname);
958 : SID_NAME_USE accountnameuse;
959 :
960 : if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token))
961 : {
962 : bail("could not open process token: error code %lu", GetLastError());
963 : }
964 :
965 : if (!GetTokenInformation(token, TokenUser, NULL, 0, &retlen) && GetLastError() != 122)
966 : {
967 : bail("could not get token information buffer size: error code %lu",
968 : GetLastError());
969 : }
970 : tokenuser = pg_malloc(retlen);
971 : if (!GetTokenInformation(token, TokenUser, tokenuser, retlen, &retlen))
972 : {
973 : bail("could not get token information: error code %lu",
974 : GetLastError());
975 : }
976 :
977 : if (!LookupAccountSid(NULL, tokenuser->User.Sid, accountname, &accountnamesize,
978 : domainname, &domainnamesize, &accountnameuse))
979 : {
980 : bail("could not look up account SID: error code %lu",
981 : GetLastError());
982 : }
983 :
984 : free(tokenuser);
985 :
986 : *acct = accountname;
987 : *dom = domainname;
988 : }
989 :
990 : /*
991 : * Rewrite pg_hba.conf and pg_ident.conf to use SSPI authentication. Permit
992 : * the current OS user to authenticate as the bootstrap superuser and as any
993 : * user named in a --create-role option.
994 : *
995 : * In --config-auth mode, the --user switch can be used to specify the
996 : * bootstrap superuser's name, otherwise we assume it is the default.
997 : */
998 : static void
999 : config_sspi_auth(const char *pgdata, const char *superuser_name)
1000 : {
1001 : const char *accountname,
1002 : *domainname;
1003 : char *errstr;
1004 : bool have_ipv6;
1005 : char fname[MAXPGPATH];
1006 : int res;
1007 : FILE *hba,
1008 : *ident;
1009 : _stringlist *sl;
1010 :
1011 : /* Find out the name of the current OS user */
1012 : current_windows_user(&accountname, &domainname);
1013 :
1014 : /* Determine the bootstrap superuser's name */
1015 : if (superuser_name == NULL)
1016 : {
1017 : /*
1018 : * Compute the default superuser name the same way initdb does.
1019 : *
1020 : * It's possible that this result always matches "accountname", the
1021 : * value SSPI authentication discovers. But the underlying system
1022 : * functions do not clearly guarantee that.
1023 : */
1024 : superuser_name = get_user_name(&errstr);
1025 : if (superuser_name == NULL)
1026 : {
1027 : bail("%s", errstr);
1028 : }
1029 : }
1030 :
1031 : /*
1032 : * Like initdb.c:setup_config(), determine whether the platform recognizes
1033 : * ::1 (IPv6 loopback) as a numeric host address string.
1034 : */
1035 : {
1036 : struct addrinfo *gai_result;
1037 : struct addrinfo hints;
1038 : WSADATA wsaData;
1039 :
1040 : hints.ai_flags = AI_NUMERICHOST;
1041 : hints.ai_family = AF_UNSPEC;
1042 : hints.ai_socktype = 0;
1043 : hints.ai_protocol = 0;
1044 : hints.ai_addrlen = 0;
1045 : hints.ai_canonname = NULL;
1046 : hints.ai_addr = NULL;
1047 : hints.ai_next = NULL;
1048 :
1049 : have_ipv6 = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0 &&
1050 : getaddrinfo("::1", NULL, &hints, &gai_result) == 0);
1051 : }
1052 :
1053 : /* Check a Write outcome and report any error. */
1054 : #define CW(cond) \
1055 : do { \
1056 : if (!(cond)) \
1057 : bail("could not write to file \"%s\": %m", fname); \
1058 : } while (0)
1059 :
1060 : res = snprintf(fname, sizeof(fname), "%s/pg_hba.conf", pgdata);
1061 : if (res < 0 || res >= sizeof(fname))
1062 : {
1063 : /*
1064 : * Truncating this name is a fatal error, because we must not fail to
1065 : * overwrite an original trust-authentication pg_hba.conf.
1066 : */
1067 : bail("directory name too long");
1068 : }
1069 : hba = fopen(fname, "w");
1070 : if (hba == NULL)
1071 : {
1072 : bail("could not open file \"%s\" for writing: %m", fname);
1073 : }
1074 : CW(fputs("# Configuration written by config_sspi_auth()\n", hba) >= 0);
1075 : CW(fputs("host all all 127.0.0.1/32 sspi include_realm=1 map=regress\n",
1076 : hba) >= 0);
1077 : if (have_ipv6)
1078 : CW(fputs("host all all ::1/128 sspi include_realm=1 map=regress\n",
1079 : hba) >= 0);
1080 : CW(fclose(hba) == 0);
1081 :
1082 : snprintf(fname, sizeof(fname), "%s/pg_ident.conf", pgdata);
1083 : ident = fopen(fname, "w");
1084 : if (ident == NULL)
1085 : {
1086 : bail("could not open file \"%s\" for writing: %m", fname);
1087 : }
1088 : CW(fputs("# Configuration written by config_sspi_auth()\n", ident) >= 0);
1089 :
1090 : /*
1091 : * Double-quote for the benefit of account names containing whitespace or
1092 : * '#'. Windows forbids the double-quote character itself, so don't
1093 : * bother escaping embedded double-quote characters.
1094 : */
1095 : CW(fprintf(ident, "regress \"%s@%s\" %s\n",
1096 : accountname, domainname, fmtHba(superuser_name)) >= 0);
1097 : for (sl = extraroles; sl; sl = sl->next)
1098 : CW(fprintf(ident, "regress \"%s@%s\" %s\n",
1099 : accountname, domainname, fmtHba(sl->str)) >= 0);
1100 : CW(fclose(ident) == 0);
1101 : }
1102 :
1103 : #endif /* ENABLE_SSPI */
1104 :
1105 : /*
1106 : * psql_start_command, psql_add_command, psql_end_command
1107 : *
1108 : * Issue one or more commands within one psql call.
1109 : * Set up with psql_start_command, then add commands one at a time
1110 : * with psql_add_command, and finally execute with psql_end_command.
1111 : *
1112 : * Since we use system(), this doesn't return until the operation finishes
1113 : */
1114 : static StringInfo
1115 196 : psql_start_command(void)
1116 : {
1117 196 : StringInfo buf = makeStringInfo();
1118 :
1119 392 : appendStringInfo(buf,
1120 : "\"%s%spsql\" -X -q",
1121 196 : bindir ? bindir : "",
1122 196 : bindir ? "/" : "");
1123 196 : return buf;
1124 : }
1125 :
1126 : static void
1127 390 : psql_add_command(StringInfo buf, const char *query,...)
1128 : {
1129 : StringInfoData cmdbuf;
1130 : const char *cmdptr;
1131 :
1132 : /* Add each command as a -c argument in the psql call */
1133 390 : appendStringInfoString(buf, " -c \"");
1134 :
1135 : /* Generate the query with insertion of sprintf arguments */
1136 390 : initStringInfo(&cmdbuf);
1137 : for (;;)
1138 0 : {
1139 : va_list args;
1140 : int needed;
1141 :
1142 390 : va_start(args, query);
1143 390 : needed = appendStringInfoVA(&cmdbuf, query, args);
1144 390 : va_end(args);
1145 390 : if (needed == 0)
1146 390 : break; /* success */
1147 0 : enlargeStringInfo(&cmdbuf, needed);
1148 : }
1149 :
1150 : /* Now escape any shell double-quote metacharacters */
1151 75714 : for (cmdptr = cmdbuf.data; *cmdptr; cmdptr++)
1152 : {
1153 75324 : if (strchr("\\\"$`", *cmdptr))
1154 2544 : appendStringInfoChar(buf, '\\');
1155 75324 : appendStringInfoChar(buf, *cmdptr);
1156 : }
1157 :
1158 390 : appendStringInfoChar(buf, '"');
1159 :
1160 390 : pfree(cmdbuf.data);
1161 390 : }
1162 :
1163 : static void
1164 196 : psql_end_command(StringInfo buf, const char *database)
1165 : {
1166 : /* Add the database name --- assume it needs no extra escaping */
1167 196 : appendStringInfo(buf,
1168 : " \"%s\"",
1169 : database);
1170 :
1171 : /* And now we can execute the shell command */
1172 196 : fflush(NULL);
1173 196 : if (system(buf->data) != 0)
1174 : {
1175 : /* psql probably already reported the error */
1176 0 : bail("command failed: %s", buf->data);
1177 : }
1178 :
1179 : /* Clean up */
1180 196 : destroyStringInfo(buf);
1181 196 : }
1182 :
1183 : /*
1184 : * Shorthand macro for the common case of a single command
1185 : */
1186 : #define psql_command(database, ...) \
1187 : do { \
1188 : StringInfo cmdbuf = psql_start_command(); \
1189 : psql_add_command(cmdbuf, __VA_ARGS__); \
1190 : psql_end_command(cmdbuf, database); \
1191 : } while (0)
1192 :
1193 : /*
1194 : * Spawn a process to execute the given shell command; don't wait for it
1195 : *
1196 : * Returns the process ID (or HANDLE) so we can wait for it later
1197 : */
1198 : PID_TYPE
1199 2598 : spawn_process(const char *cmdline)
1200 : {
1201 : #ifndef WIN32
1202 : pid_t pid;
1203 :
1204 : /*
1205 : * Must flush I/O buffers before fork.
1206 : */
1207 2598 : fflush(NULL);
1208 :
1209 : #ifdef EXEC_BACKEND
1210 : pg_disable_aslr();
1211 : #endif
1212 :
1213 2598 : pid = fork();
1214 5196 : if (pid == -1)
1215 : {
1216 0 : bail("could not fork: %m");
1217 : }
1218 5196 : if (pid == 0)
1219 : {
1220 : /*
1221 : * In child
1222 : *
1223 : * Instead of using system(), exec the shell directly, and tell it to
1224 : * "exec" the command too. This saves two useless processes per
1225 : * parallel test case.
1226 : */
1227 : char *cmdline2;
1228 :
1229 2598 : cmdline2 = psprintf("exec %s", cmdline);
1230 2598 : execl(shellprog, shellprog, "-c", cmdline2, (char *) NULL);
1231 : /* Not using the normal bail() here as we want _exit */
1232 2598 : bail_noatexit("could not exec \"%s\": %m", shellprog);
1233 : }
1234 : /* in parent */
1235 2598 : return pid;
1236 : #else
1237 : PROCESS_INFORMATION pi;
1238 : char *cmdline2;
1239 : const char *comspec;
1240 :
1241 : /* Find CMD.EXE location using COMSPEC, if it's set */
1242 : comspec = getenv("COMSPEC");
1243 : if (comspec == NULL)
1244 : comspec = "CMD";
1245 :
1246 : memset(&pi, 0, sizeof(pi));
1247 : cmdline2 = psprintf("\"%s\" /d /c \"%s\"", comspec, cmdline);
1248 :
1249 : if (!CreateRestrictedProcess(cmdline2, &pi))
1250 : exit(2);
1251 :
1252 : CloseHandle(pi.hThread);
1253 : return pi.hProcess;
1254 : #endif
1255 : }
1256 :
1257 : /*
1258 : * Count bytes in file
1259 : */
1260 : static long
1261 170 : file_size(const char *file)
1262 : {
1263 : long r;
1264 170 : FILE *f = fopen(file, "r");
1265 :
1266 170 : if (!f)
1267 : {
1268 0 : diag("could not open file \"%s\" for reading: %m", file);
1269 0 : return -1;
1270 : }
1271 170 : fseek(f, 0, SEEK_END);
1272 170 : r = ftell(f);
1273 170 : fclose(f);
1274 170 : return r;
1275 : }
1276 :
1277 : /*
1278 : * Count lines in file
1279 : */
1280 : static int
1281 58 : file_line_count(const char *file)
1282 : {
1283 : int c;
1284 58 : int l = 0;
1285 58 : FILE *f = fopen(file, "r");
1286 :
1287 58 : if (!f)
1288 : {
1289 0 : diag("could not open file \"%s\" for reading: %m", file);
1290 0 : return -1;
1291 : }
1292 387914 : while ((c = fgetc(f)) != EOF)
1293 : {
1294 387856 : if (c == '\n')
1295 13984 : l++;
1296 : }
1297 58 : fclose(f);
1298 58 : return l;
1299 : }
1300 :
1301 : bool
1302 4468 : file_exists(const char *file)
1303 : {
1304 4468 : FILE *f = fopen(file, "r");
1305 :
1306 4468 : if (!f)
1307 2382 : return false;
1308 2086 : fclose(f);
1309 2086 : return true;
1310 : }
1311 :
1312 : static bool
1313 672 : directory_exists(const char *dir)
1314 : {
1315 : struct stat st;
1316 :
1317 672 : if (stat(dir, &st) != 0)
1318 510 : return false;
1319 162 : if (S_ISDIR(st.st_mode))
1320 162 : return true;
1321 0 : return false;
1322 : }
1323 :
1324 : /* Create a directory */
1325 : static void
1326 510 : make_directory(const char *dir)
1327 : {
1328 510 : if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
1329 0 : bail("could not create directory \"%s\": %m", dir);
1330 510 : }
1331 :
1332 : /*
1333 : * In: filename.ext, Return: filename_i.ext, where 0 < i <= 9
1334 : */
1335 : static char *
1336 128 : get_alternative_expectfile(const char *expectfile, int i)
1337 : {
1338 : char *last_dot;
1339 128 : int ssize = strlen(expectfile) + 2 + 1;
1340 : char *tmp;
1341 : char *s;
1342 :
1343 128 : if (!(tmp = (char *) malloc(ssize)))
1344 0 : return NULL;
1345 :
1346 128 : if (!(s = (char *) malloc(ssize)))
1347 : {
1348 0 : free(tmp);
1349 0 : return NULL;
1350 : }
1351 :
1352 128 : strcpy(tmp, expectfile);
1353 128 : last_dot = strrchr(tmp, '.');
1354 128 : if (!last_dot)
1355 : {
1356 0 : free(tmp);
1357 0 : free(s);
1358 0 : return NULL;
1359 : }
1360 128 : *last_dot = '\0';
1361 128 : snprintf(s, ssize, "%s_%d.%s", tmp, i, last_dot + 1);
1362 128 : free(tmp);
1363 128 : return s;
1364 : }
1365 :
1366 : /*
1367 : * Run a "diff" command and also check that it didn't crash
1368 : */
1369 : static int
1370 3014 : run_diff(const char *cmd, const char *filename)
1371 : {
1372 : int r;
1373 :
1374 3014 : fflush(NULL);
1375 3014 : r = system(cmd);
1376 3014 : if (!WIFEXITED(r) || WEXITSTATUS(r) > 1)
1377 : {
1378 0 : bail("diff command failed with status %d: %s", r, cmd);
1379 : }
1380 : #ifdef WIN32
1381 :
1382 : /*
1383 : * On WIN32, if the 'diff' command cannot be found, system() returns 1,
1384 : * but produces nothing to stdout, so we check for that here.
1385 : */
1386 : if (WEXITSTATUS(r) == 1 && file_size(filename) <= 0)
1387 : {
1388 : bail("diff command not found: %s", cmd);
1389 : }
1390 : #endif
1391 :
1392 3014 : return WEXITSTATUS(r);
1393 : }
1394 :
1395 : /*
1396 : * Check the actual result file for the given test against expected results
1397 : *
1398 : * Returns true if different (failure), false if correct match found.
1399 : * In the true case, the diff is appended to the diffs file.
1400 : */
1401 : static bool
1402 2956 : results_differ(const char *testname, const char *resultsfile, const char *default_expectfile)
1403 : {
1404 : char expectfile[MAXPGPATH];
1405 : char diff[MAXPGPATH];
1406 : char cmd[MAXPGPATH * 3];
1407 : char best_expect_file[MAXPGPATH];
1408 : FILE *difffile;
1409 : int best_line_count;
1410 : int i;
1411 : int l;
1412 : const char *platform_expectfile;
1413 :
1414 : /*
1415 : * We can pass either the resultsfile or the expectfile, they should have
1416 : * the same type (filename.type) anyway.
1417 : */
1418 2956 : platform_expectfile = get_expectfile(testname, resultsfile);
1419 :
1420 2956 : strlcpy(expectfile, default_expectfile, sizeof(expectfile));
1421 2956 : if (platform_expectfile)
1422 : {
1423 : /*
1424 : * Replace everything after the last slash in expectfile with what the
1425 : * platform_expectfile contains.
1426 : */
1427 0 : char *p = strrchr(expectfile, '/');
1428 :
1429 0 : if (p)
1430 0 : strcpy(++p, platform_expectfile);
1431 : }
1432 :
1433 : /* Name to use for temporary diff file */
1434 2956 : snprintf(diff, sizeof(diff), "%s.diff", resultsfile);
1435 :
1436 : /* OK, run the diff */
1437 2956 : snprintf(cmd, sizeof(cmd),
1438 : "diff %s \"%s\" \"%s\" > \"%s\"",
1439 : basic_diff_opts, expectfile, resultsfile, diff);
1440 :
1441 : /* Is the diff file empty? */
1442 2956 : if (run_diff(cmd, diff) == 0)
1443 : {
1444 2906 : unlink(diff);
1445 2906 : return false;
1446 : }
1447 :
1448 : /* There may be secondary comparison files that match better */
1449 50 : best_line_count = file_line_count(diff);
1450 50 : strcpy(best_expect_file, expectfile);
1451 :
1452 128 : for (i = 0; i <= 9; i++)
1453 : {
1454 : char *alt_expectfile;
1455 :
1456 128 : alt_expectfile = get_alternative_expectfile(expectfile, i);
1457 128 : if (!alt_expectfile)
1458 0 : bail("Unable to check secondary comparison files: %m");
1459 :
1460 128 : if (!file_exists(alt_expectfile))
1461 : {
1462 70 : free(alt_expectfile);
1463 70 : continue;
1464 : }
1465 :
1466 58 : snprintf(cmd, sizeof(cmd),
1467 : "diff %s \"%s\" \"%s\" > \"%s\"",
1468 : basic_diff_opts, alt_expectfile, resultsfile, diff);
1469 :
1470 58 : if (run_diff(cmd, diff) == 0)
1471 : {
1472 50 : unlink(diff);
1473 50 : free(alt_expectfile);
1474 50 : return false;
1475 : }
1476 :
1477 8 : l = file_line_count(diff);
1478 8 : if (l < best_line_count)
1479 : {
1480 : /* This diff was a better match than the last one */
1481 8 : best_line_count = l;
1482 8 : strlcpy(best_expect_file, alt_expectfile, sizeof(best_expect_file));
1483 : }
1484 8 : free(alt_expectfile);
1485 : }
1486 :
1487 : /*
1488 : * fall back on the canonical results file if we haven't tried it yet and
1489 : * haven't found a complete match yet.
1490 : */
1491 :
1492 0 : if (platform_expectfile)
1493 : {
1494 0 : snprintf(cmd, sizeof(cmd),
1495 : "diff %s \"%s\" \"%s\" > \"%s\"",
1496 : basic_diff_opts, default_expectfile, resultsfile, diff);
1497 :
1498 0 : if (run_diff(cmd, diff) == 0)
1499 : {
1500 : /* No diff = no changes = good */
1501 0 : unlink(diff);
1502 0 : return false;
1503 : }
1504 :
1505 0 : l = file_line_count(diff);
1506 0 : if (l < best_line_count)
1507 : {
1508 : /* This diff was a better match than the last one */
1509 0 : best_line_count = l;
1510 0 : strlcpy(best_expect_file, default_expectfile, sizeof(best_expect_file));
1511 : }
1512 : }
1513 :
1514 : /*
1515 : * Use the best comparison file to generate the "pretty" diff, which we
1516 : * append to the diffs summary file.
1517 : */
1518 :
1519 : /* Write diff header */
1520 0 : difffile = fopen(difffilename, "a");
1521 0 : if (difffile)
1522 : {
1523 0 : fprintf(difffile,
1524 : "diff %s %s %s\n",
1525 : pretty_diff_opts, best_expect_file, resultsfile);
1526 0 : fclose(difffile);
1527 : }
1528 :
1529 : /* Run diff */
1530 0 : snprintf(cmd, sizeof(cmd),
1531 : "diff %s \"%s\" \"%s\" >> \"%s\"",
1532 : pretty_diff_opts, best_expect_file, resultsfile, difffilename);
1533 0 : run_diff(cmd, difffilename);
1534 :
1535 0 : unlink(diff);
1536 0 : return true;
1537 : }
1538 :
1539 : /*
1540 : * Wait for specified subprocesses to finish, and return their exit
1541 : * statuses into statuses[] and stop times into stoptimes[]
1542 : *
1543 : * If names isn't NULL, print each subprocess's name as it finishes
1544 : *
1545 : * Note: it's OK to scribble on the pids array, but not on the names array
1546 : */
1547 : static void
1548 1250 : wait_for_tests(PID_TYPE * pids, int *statuses, instr_time *stoptimes,
1549 : char **names, int num_tests)
1550 : {
1551 : int tests_left;
1552 : int i;
1553 :
1554 : #ifdef WIN32
1555 : PID_TYPE *active_pids = pg_malloc(num_tests * sizeof(PID_TYPE));
1556 :
1557 : memcpy(active_pids, pids, num_tests * sizeof(PID_TYPE));
1558 : #endif
1559 :
1560 1250 : tests_left = num_tests;
1561 3682 : while (tests_left > 0)
1562 : {
1563 : PID_TYPE p;
1564 :
1565 : #ifndef WIN32
1566 : int exit_status;
1567 :
1568 2432 : p = wait(&exit_status);
1569 :
1570 2432 : if (p == INVALID_PID)
1571 0 : bail("failed to wait for subprocesses: %m");
1572 : #else
1573 : DWORD exit_status;
1574 : int r;
1575 :
1576 : r = WaitForMultipleObjects(tests_left, active_pids, FALSE, INFINITE);
1577 : if (r < WAIT_OBJECT_0 || r >= WAIT_OBJECT_0 + tests_left)
1578 : {
1579 : bail("failed to wait for subprocesses: error code %lu",
1580 : GetLastError());
1581 : }
1582 : p = active_pids[r - WAIT_OBJECT_0];
1583 : /* compact the active_pids array */
1584 : active_pids[r - WAIT_OBJECT_0] = active_pids[tests_left - 1];
1585 : #endif /* WIN32 */
1586 :
1587 11996 : for (i = 0; i < num_tests; i++)
1588 : {
1589 11996 : if (p == pids[i])
1590 : {
1591 : #ifdef WIN32
1592 : GetExitCodeProcess(pids[i], &exit_status);
1593 : CloseHandle(pids[i]);
1594 : #endif
1595 2432 : pids[i] = INVALID_PID;
1596 2432 : statuses[i] = (int) exit_status;
1597 2432 : INSTR_TIME_SET_CURRENT(stoptimes[i]);
1598 2432 : if (names)
1599 1290 : note_detail(" %s", names[i]);
1600 2432 : tests_left--;
1601 2432 : break;
1602 : }
1603 : }
1604 : }
1605 :
1606 : #ifdef WIN32
1607 : free(active_pids);
1608 : #endif
1609 1250 : }
1610 :
1611 : /*
1612 : * report nonzero exit code from a test process
1613 : */
1614 : static void
1615 0 : log_child_failure(int exitstatus)
1616 : {
1617 0 : if (WIFEXITED(exitstatus))
1618 0 : diag("(test process exited with exit code %d)",
1619 : WEXITSTATUS(exitstatus));
1620 0 : else if (WIFSIGNALED(exitstatus))
1621 : {
1622 : #if defined(WIN32)
1623 : diag("(test process was terminated by exception 0x%X)",
1624 : WTERMSIG(exitstatus));
1625 : #else
1626 0 : diag("(test process was terminated by signal %d: %s)",
1627 : WTERMSIG(exitstatus), pg_strsignal(WTERMSIG(exitstatus)));
1628 : #endif
1629 : }
1630 : else
1631 0 : diag("(test process exited with unrecognized status %d)", exitstatus);
1632 0 : }
1633 :
1634 : /*
1635 : * Run all the tests specified in one schedule file
1636 : */
1637 : static void
1638 12 : run_schedule(const char *schedule, test_start_function startfunc,
1639 : postprocess_result_function postfunc)
1640 : {
1641 : #define MAX_PARALLEL_TESTS 100
1642 : char *tests[MAX_PARALLEL_TESTS];
1643 : _stringlist *resultfiles[MAX_PARALLEL_TESTS];
1644 : _stringlist *expectfiles[MAX_PARALLEL_TESTS];
1645 : _stringlist *tags[MAX_PARALLEL_TESTS];
1646 : PID_TYPE pids[MAX_PARALLEL_TESTS];
1647 : instr_time starttimes[MAX_PARALLEL_TESTS];
1648 : instr_time stoptimes[MAX_PARALLEL_TESTS];
1649 : int statuses[MAX_PARALLEL_TESTS];
1650 : char scbuf[1024];
1651 : FILE *scf;
1652 12 : int line_num = 0;
1653 :
1654 12 : memset(tests, 0, sizeof(tests));
1655 12 : memset(resultfiles, 0, sizeof(resultfiles));
1656 12 : memset(expectfiles, 0, sizeof(expectfiles));
1657 12 : memset(tags, 0, sizeof(tags));
1658 :
1659 12 : scf = fopen(schedule, "r");
1660 12 : if (!scf)
1661 0 : bail("could not open file \"%s\" for reading: %m", schedule);
1662 :
1663 1328 : while (fgets(scbuf, sizeof(scbuf), scf))
1664 : {
1665 1316 : char *test = NULL;
1666 : char *c;
1667 : int num_tests;
1668 : bool inword;
1669 : int i;
1670 :
1671 1316 : line_num++;
1672 :
1673 : /* strip trailing whitespace, especially the newline */
1674 1316 : i = strlen(scbuf);
1675 2632 : while (i > 0 && isspace((unsigned char) scbuf[i - 1]))
1676 1316 : scbuf[--i] = '\0';
1677 :
1678 1316 : if (scbuf[0] == '\0' || scbuf[0] == '#')
1679 672 : continue;
1680 644 : if (strncmp(scbuf, "test: ", 6) == 0)
1681 644 : test = scbuf + 6;
1682 : else
1683 : {
1684 0 : bail("syntax error in schedule file \"%s\" line %d: %s",
1685 : schedule, line_num, scbuf);
1686 : }
1687 :
1688 644 : num_tests = 0;
1689 644 : inword = false;
1690 22424 : for (c = test;; c++)
1691 : {
1692 22424 : if (*c == '\0' || isspace((unsigned char) *c))
1693 : {
1694 1826 : if (inword)
1695 : {
1696 : /* Reached end of a test name */
1697 : char sav;
1698 :
1699 1826 : if (num_tests >= MAX_PARALLEL_TESTS)
1700 : {
1701 0 : bail("too many parallel tests (more than %d) in schedule file \"%s\" line %d: %s",
1702 : MAX_PARALLEL_TESTS, schedule, line_num, scbuf);
1703 : }
1704 1826 : sav = *c;
1705 1826 : *c = '\0';
1706 1826 : tests[num_tests] = pg_strdup(test);
1707 1826 : num_tests++;
1708 1826 : *c = sav;
1709 1826 : inword = false;
1710 : }
1711 1826 : if (*c == '\0')
1712 644 : break; /* loop exit is here */
1713 : }
1714 20598 : else if (!inword)
1715 : {
1716 : /* Start of a test name */
1717 1826 : test = c;
1718 1826 : inword = true;
1719 : }
1720 : }
1721 :
1722 644 : if (num_tests == 0)
1723 : {
1724 0 : bail("syntax error in schedule file \"%s\" line %d: %s",
1725 : schedule, line_num, scbuf);
1726 : }
1727 :
1728 644 : if (num_tests == 1)
1729 : {
1730 536 : pids[0] = (startfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]);
1731 536 : INSTR_TIME_SET_CURRENT(starttimes[0]);
1732 536 : wait_for_tests(pids, statuses, stoptimes, NULL, 1);
1733 : /* status line is finished below */
1734 : }
1735 108 : else if (max_concurrent_tests > 0 && max_concurrent_tests < num_tests)
1736 : {
1737 0 : bail("too many parallel tests (more than %d) in schedule file \"%s\" line %d: %s",
1738 : max_concurrent_tests, schedule, line_num, scbuf);
1739 : }
1740 108 : else if (max_connections > 0 && max_connections < num_tests)
1741 0 : {
1742 0 : int oldest = 0;
1743 :
1744 0 : note_detail("parallel group (%d tests, in groups of %d): ",
1745 : num_tests, max_connections);
1746 0 : for (i = 0; i < num_tests; i++)
1747 : {
1748 0 : if (i - oldest >= max_connections)
1749 : {
1750 0 : wait_for_tests(pids + oldest, statuses + oldest,
1751 0 : stoptimes + oldest,
1752 0 : tests + oldest, i - oldest);
1753 0 : oldest = i;
1754 : }
1755 0 : pids[i] = (startfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
1756 0 : INSTR_TIME_SET_CURRENT(starttimes[i]);
1757 : }
1758 0 : wait_for_tests(pids + oldest, statuses + oldest,
1759 0 : stoptimes + oldest,
1760 0 : tests + oldest, i - oldest);
1761 0 : note_end();
1762 : }
1763 : else
1764 : {
1765 108 : note_detail("parallel group (%d tests): ", num_tests);
1766 1398 : for (i = 0; i < num_tests; i++)
1767 : {
1768 1290 : pids[i] = (startfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
1769 1290 : INSTR_TIME_SET_CURRENT(starttimes[i]);
1770 : }
1771 108 : wait_for_tests(pids, statuses, stoptimes, tests, num_tests);
1772 108 : note_end();
1773 : }
1774 :
1775 : /* Check results for all tests */
1776 2470 : for (i = 0; i < num_tests; i++)
1777 : {
1778 : _stringlist *rl,
1779 : *el,
1780 : *tl;
1781 1826 : bool differ = false;
1782 :
1783 1826 : INSTR_TIME_SUBTRACT(stoptimes[i], starttimes[i]);
1784 :
1785 : /*
1786 : * Advance over all three lists simultaneously.
1787 : *
1788 : * Compare resultfiles[j] with expectfiles[j] always. Tags are
1789 : * optional but if there are tags, the tag list has the same
1790 : * length as the other two lists.
1791 : */
1792 4164 : for (rl = resultfiles[i], el = expectfiles[i], tl = tags[i];
1793 : rl != NULL; /* rl and el have the same length */
1794 2338 : rl = rl->next, el = el->next,
1795 2338 : tl = tl ? tl->next : NULL)
1796 : {
1797 : bool newdiff;
1798 :
1799 2338 : if (postfunc)
1800 768 : (*postfunc) (rl->str);
1801 2338 : newdiff = results_differ(tests[i], rl->str, el->str);
1802 2338 : if (newdiff && tl)
1803 : {
1804 0 : diag("tag: %s", tl->str);
1805 : }
1806 2338 : differ |= newdiff;
1807 : }
1808 :
1809 1826 : if (statuses[i] != 0)
1810 : {
1811 0 : test_status_failed(tests[i], INSTR_TIME_GET_MILLISEC(stoptimes[i]), (num_tests > 1));
1812 0 : log_child_failure(statuses[i]);
1813 : }
1814 : else
1815 : {
1816 1826 : if (differ)
1817 : {
1818 0 : test_status_failed(tests[i], INSTR_TIME_GET_MILLISEC(stoptimes[i]), (num_tests > 1));
1819 : }
1820 : else
1821 : {
1822 1826 : test_status_ok(tests[i], INSTR_TIME_GET_MILLISEC(stoptimes[i]), (num_tests > 1));
1823 : }
1824 : }
1825 : }
1826 :
1827 2470 : for (i = 0; i < num_tests; i++)
1828 : {
1829 1826 : pg_free(tests[i]);
1830 1826 : tests[i] = NULL;
1831 1826 : free_stringlist(&resultfiles[i]);
1832 1826 : free_stringlist(&expectfiles[i]);
1833 1826 : free_stringlist(&tags[i]);
1834 : }
1835 : }
1836 :
1837 12 : fclose(scf);
1838 12 : }
1839 :
1840 : /*
1841 : * Run a single test
1842 : */
1843 : static void
1844 606 : run_single_test(const char *test, test_start_function startfunc,
1845 : postprocess_result_function postfunc)
1846 : {
1847 : PID_TYPE pid;
1848 : instr_time starttime;
1849 : instr_time stoptime;
1850 : int exit_status;
1851 606 : _stringlist *resultfiles = NULL;
1852 606 : _stringlist *expectfiles = NULL;
1853 606 : _stringlist *tags = NULL;
1854 : _stringlist *rl,
1855 : *el,
1856 : *tl;
1857 606 : bool differ = false;
1858 :
1859 606 : pid = (startfunc) (test, &resultfiles, &expectfiles, &tags);
1860 606 : INSTR_TIME_SET_CURRENT(starttime);
1861 606 : wait_for_tests(&pid, &exit_status, &stoptime, NULL, 1);
1862 :
1863 : /*
1864 : * Advance over all three lists simultaneously.
1865 : *
1866 : * Compare resultfiles[j] with expectfiles[j] always. Tags are optional
1867 : * but if there are tags, the tag list has the same length as the other
1868 : * two lists.
1869 : */
1870 1224 : for (rl = resultfiles, el = expectfiles, tl = tags;
1871 : rl != NULL; /* rl and el have the same length */
1872 618 : rl = rl->next, el = el->next,
1873 618 : tl = tl ? tl->next : NULL)
1874 : {
1875 : bool newdiff;
1876 :
1877 618 : if (postfunc)
1878 18 : (*postfunc) (rl->str);
1879 618 : newdiff = results_differ(test, rl->str, el->str);
1880 618 : if (newdiff && tl)
1881 : {
1882 0 : diag("tag: %s", tl->str);
1883 : }
1884 618 : differ |= newdiff;
1885 : }
1886 :
1887 606 : INSTR_TIME_SUBTRACT(stoptime, starttime);
1888 :
1889 606 : if (exit_status != 0)
1890 : {
1891 0 : test_status_failed(test, INSTR_TIME_GET_MILLISEC(stoptime), false);
1892 0 : log_child_failure(exit_status);
1893 : }
1894 : else
1895 : {
1896 606 : if (differ)
1897 : {
1898 0 : test_status_failed(test, INSTR_TIME_GET_MILLISEC(stoptime), false);
1899 : }
1900 : else
1901 : {
1902 606 : test_status_ok(test, INSTR_TIME_GET_MILLISEC(stoptime), false);
1903 : }
1904 : }
1905 606 : }
1906 :
1907 : /*
1908 : * Create the summary-output files (making them empty if already existing)
1909 : */
1910 : static void
1911 170 : open_result_files(void)
1912 : {
1913 : char file[MAXPGPATH];
1914 : FILE *difffile;
1915 :
1916 : /* create outputdir directory if not present */
1917 170 : if (!directory_exists(outputdir))
1918 12 : make_directory(outputdir);
1919 :
1920 : /* create the log file (copy of running status output) */
1921 170 : snprintf(file, sizeof(file), "%s/regression.out", outputdir);
1922 170 : logfilename = pg_strdup(file);
1923 170 : logfile = fopen(logfilename, "w");
1924 170 : if (!logfile)
1925 0 : bail("could not open file \"%s\" for writing: %m", logfilename);
1926 :
1927 : /* create the diffs file as empty */
1928 170 : snprintf(file, sizeof(file), "%s/regression.diffs", outputdir);
1929 170 : difffilename = pg_strdup(file);
1930 170 : difffile = fopen(difffilename, "w");
1931 170 : if (!difffile)
1932 0 : bail("could not open file \"%s\" for writing: %m", difffilename);
1933 :
1934 : /* we don't keep the diffs file open continuously */
1935 170 : fclose(difffile);
1936 :
1937 : /* also create the results directory if not present */
1938 170 : snprintf(file, sizeof(file), "%s/results", outputdir);
1939 170 : if (!directory_exists(file))
1940 168 : make_directory(file);
1941 170 : }
1942 :
1943 : static void
1944 4 : drop_database_if_exists(const char *dbname)
1945 : {
1946 4 : StringInfo buf = psql_start_command();
1947 :
1948 : /* Set warning level so we don't see chatter about nonexistent DB */
1949 4 : psql_add_command(buf, "SET client_min_messages = warning");
1950 4 : psql_add_command(buf, "DROP DATABASE IF EXISTS \"%s\"", dbname);
1951 4 : psql_end_command(buf, "postgres");
1952 4 : }
1953 :
1954 : static void
1955 174 : create_database(const char *dbname)
1956 : {
1957 174 : StringInfo buf = psql_start_command();
1958 : _stringlist *sl;
1959 :
1960 : /*
1961 : * We use template0 so that any installation-local cruft in template1 will
1962 : * not mess up the tests.
1963 : */
1964 174 : if (encoding)
1965 2 : psql_add_command(buf, "CREATE DATABASE \"%s\" TEMPLATE=template0 ENCODING='%s'%s", dbname, encoding,
1966 2 : (nolocale) ? " LOCALE='C'" : "");
1967 : else
1968 172 : psql_add_command(buf, "CREATE DATABASE \"%s\" TEMPLATE=template0%s", dbname,
1969 172 : (nolocale) ? " LOCALE='C'" : "");
1970 174 : psql_add_command(buf,
1971 : "ALTER DATABASE \"%s\" SET lc_messages TO 'C';"
1972 : "ALTER DATABASE \"%s\" SET lc_monetary TO 'C';"
1973 : "ALTER DATABASE \"%s\" SET lc_numeric TO 'C';"
1974 : "ALTER DATABASE \"%s\" SET lc_time TO 'C';"
1975 : "ALTER DATABASE \"%s\" SET bytea_output TO 'hex';"
1976 : "ALTER DATABASE \"%s\" SET timezone_abbreviations TO 'Default';",
1977 : dbname, dbname, dbname, dbname, dbname, dbname);
1978 174 : psql_end_command(buf, "postgres");
1979 :
1980 : /*
1981 : * Install any requested extensions. We use CREATE IF NOT EXISTS so that
1982 : * this will work whether or not the extension is preinstalled.
1983 : */
1984 184 : for (sl = loadextension; sl != NULL; sl = sl->next)
1985 10 : psql_command(dbname, "CREATE EXTENSION IF NOT EXISTS \"%s\"", sl->str);
1986 174 : }
1987 :
1988 : static void
1989 0 : drop_role_if_exists(const char *rolename)
1990 : {
1991 0 : StringInfo buf = psql_start_command();
1992 :
1993 : /* Set warning level so we don't see chatter about nonexistent role */
1994 0 : psql_add_command(buf, "SET client_min_messages = warning");
1995 0 : psql_add_command(buf, "DROP ROLE IF EXISTS \"%s\"", rolename);
1996 0 : psql_end_command(buf, "postgres");
1997 0 : }
1998 :
1999 : static void
2000 8 : create_role(const char *rolename, const _stringlist *granted_dbs)
2001 : {
2002 8 : StringInfo buf = psql_start_command();
2003 :
2004 8 : psql_add_command(buf, "CREATE ROLE \"%s\" WITH LOGIN", rolename);
2005 24 : for (; granted_dbs != NULL; granted_dbs = granted_dbs->next)
2006 : {
2007 16 : psql_add_command(buf, "GRANT ALL ON DATABASE \"%s\" TO \"%s\"",
2008 : granted_dbs->str, rolename);
2009 : }
2010 8 : psql_end_command(buf, "postgres");
2011 8 : }
2012 :
2013 : static void
2014 0 : help(void)
2015 : {
2016 0 : printf(_("PostgreSQL regression test driver\n"));
2017 0 : printf(_("\n"));
2018 0 : printf(_("Usage:\n %s [OPTION]... [EXTRA-TEST]...\n"), progname);
2019 0 : printf(_("\n"));
2020 0 : printf(_("Options:\n"));
2021 0 : printf(_(" --bindir=BINPATH use BINPATH for programs that are run;\n"));
2022 0 : printf(_(" if empty, use PATH from the environment\n"));
2023 0 : printf(_(" --config-auth=DATADIR update authentication settings for DATADIR\n"));
2024 0 : printf(_(" --create-role=ROLE create the specified role before testing\n"));
2025 0 : printf(_(" --dbname=DB use database DB (default \"regression\")\n"));
2026 0 : printf(_(" --debug turn on debug mode in programs that are run\n"));
2027 0 : printf(_(" --dlpath=DIR look for dynamic libraries in DIR\n"));
2028 0 : printf(_(" --encoding=ENCODING use ENCODING as the encoding\n"));
2029 0 : printf(_(" --expecteddir=DIR take expected files from DIR (default \".\")\n"));
2030 0 : printf(_(" -h, --help show this help, then exit\n"));
2031 0 : printf(_(" --inputdir=DIR take input files from DIR (default \".\")\n"));
2032 0 : printf(_(" --launcher=CMD use CMD as launcher of psql\n"));
2033 0 : printf(_(" --load-extension=EXT load the named extension before running the\n"));
2034 0 : printf(_(" tests; can appear multiple times\n"));
2035 0 : printf(_(" --max-connections=N maximum number of concurrent connections\n"));
2036 0 : printf(_(" (default is 0, meaning unlimited)\n"));
2037 0 : printf(_(" --max-concurrent-tests=N maximum number of concurrent tests in schedule\n"));
2038 0 : printf(_(" (default is 0, meaning unlimited)\n"));
2039 0 : printf(_(" --outputdir=DIR place output files in DIR (default \".\")\n"));
2040 0 : printf(_(" --schedule=FILE use test ordering schedule from FILE\n"));
2041 0 : printf(_(" (can be used multiple times to concatenate)\n"));
2042 0 : printf(_(" --temp-instance=DIR create a temporary instance in DIR\n"));
2043 0 : printf(_(" --use-existing use an existing installation\n"));
2044 0 : printf(_(" -V, --version output version information, then exit\n"));
2045 0 : printf(_("\n"));
2046 0 : printf(_("Options for \"temp-instance\" mode:\n"));
2047 0 : printf(_(" --no-locale use C locale\n"));
2048 0 : printf(_(" --port=PORT start postmaster on PORT\n"));
2049 0 : printf(_(" --temp-config=FILE append contents of FILE to temporary config\n"));
2050 0 : printf(_("\n"));
2051 0 : printf(_("Options for using an existing installation:\n"));
2052 0 : printf(_(" --host=HOST use postmaster running on HOST\n"));
2053 0 : printf(_(" --port=PORT use postmaster running at PORT\n"));
2054 0 : printf(_(" --user=USER connect as USER\n"));
2055 0 : printf(_("\n"));
2056 0 : printf(_("The exit status is 0 if all tests passed, 1 if some tests failed, and 2\n"));
2057 0 : printf(_("if the tests could not be run for some reason.\n"));
2058 0 : printf(_("\n"));
2059 0 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
2060 0 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
2061 0 : }
2062 :
2063 : int
2064 680 : regression_main(int argc, char *argv[],
2065 : init_function ifunc,
2066 : test_start_function startfunc,
2067 : postprocess_result_function postfunc)
2068 : {
2069 : static struct option long_options[] = {
2070 : {"help", no_argument, NULL, 'h'},
2071 : {"version", no_argument, NULL, 'V'},
2072 : {"dbname", required_argument, NULL, 1},
2073 : {"debug", no_argument, NULL, 2},
2074 : {"inputdir", required_argument, NULL, 3},
2075 : {"max-connections", required_argument, NULL, 5},
2076 : {"encoding", required_argument, NULL, 6},
2077 : {"outputdir", required_argument, NULL, 7},
2078 : {"schedule", required_argument, NULL, 8},
2079 : {"temp-instance", required_argument, NULL, 9},
2080 : {"no-locale", no_argument, NULL, 10},
2081 : {"host", required_argument, NULL, 13},
2082 : {"port", required_argument, NULL, 14},
2083 : {"user", required_argument, NULL, 15},
2084 : {"bindir", required_argument, NULL, 16},
2085 : {"dlpath", required_argument, NULL, 17},
2086 : {"create-role", required_argument, NULL, 18},
2087 : {"temp-config", required_argument, NULL, 19},
2088 : {"use-existing", no_argument, NULL, 20},
2089 : {"launcher", required_argument, NULL, 21},
2090 : {"load-extension", required_argument, NULL, 22},
2091 : {"config-auth", required_argument, NULL, 24},
2092 : {"max-concurrent-tests", required_argument, NULL, 25},
2093 : {"expecteddir", required_argument, NULL, 26},
2094 : {NULL, 0, NULL, 0}
2095 : };
2096 :
2097 : bool use_unix_sockets;
2098 : _stringlist *sl;
2099 : int c;
2100 : int i;
2101 : int option_index;
2102 : char buf[MAXPGPATH * 4];
2103 :
2104 680 : pg_logging_init(argv[0]);
2105 680 : progname = get_progname(argv[0]);
2106 680 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_regress"));
2107 :
2108 680 : get_restricted_token();
2109 :
2110 680 : atexit(stop_postmaster);
2111 :
2112 : #if defined(WIN32)
2113 :
2114 : /*
2115 : * We don't use Unix-domain sockets on Windows by default (see comment at
2116 : * remove_temp() for a reason). Override at your own risk.
2117 : */
2118 : use_unix_sockets = getenv("PG_TEST_USE_UNIX_SOCKETS") ? true : false;
2119 : #else
2120 680 : use_unix_sockets = true;
2121 : #endif
2122 :
2123 680 : if (!use_unix_sockets)
2124 0 : hostname = "localhost";
2125 :
2126 : /*
2127 : * We call the initialization function here because that way we can set
2128 : * default parameters and let them be overwritten by the commandline.
2129 : */
2130 680 : ifunc(argc, argv);
2131 :
2132 680 : if (getenv("PG_REGRESS_DIFF_OPTS"))
2133 0 : pretty_diff_opts = getenv("PG_REGRESS_DIFF_OPTS");
2134 :
2135 1994 : while ((c = getopt_long(argc, argv, "hV", long_options, &option_index)) != -1)
2136 : {
2137 1314 : switch (c)
2138 : {
2139 0 : case 'h':
2140 0 : help();
2141 0 : exit(0);
2142 0 : case 'V':
2143 0 : puts("pg_regress (PostgreSQL) " PG_VERSION);
2144 0 : exit(0);
2145 162 : case 1:
2146 :
2147 : /*
2148 : * If a default database was specified, we need to remove it
2149 : * before we add the specified one.
2150 : */
2151 162 : free_stringlist(&dblist);
2152 162 : split_to_stringlist(optarg, ",", &dblist);
2153 162 : break;
2154 0 : case 2:
2155 0 : debug = true;
2156 0 : break;
2157 166 : case 3:
2158 166 : inputdir = pg_strdup(optarg);
2159 166 : break;
2160 0 : case 5:
2161 0 : max_connections = atoi(optarg);
2162 0 : break;
2163 2 : case 6:
2164 2 : encoding = pg_strdup(optarg);
2165 2 : break;
2166 16 : case 7:
2167 16 : outputdir = pg_strdup(optarg);
2168 16 : break;
2169 12 : case 8:
2170 12 : add_stringlist_item(&schedulelist, optarg);
2171 12 : break;
2172 166 : case 9:
2173 166 : temp_instance = make_absolute_path(optarg);
2174 166 : break;
2175 4 : case 10:
2176 4 : nolocale = true;
2177 4 : break;
2178 6 : case 13:
2179 6 : hostname = pg_strdup(optarg);
2180 6 : break;
2181 4 : case 14:
2182 4 : port = atoi(optarg);
2183 4 : port_specified_by_user = true;
2184 4 : break;
2185 6 : case 15:
2186 6 : user = pg_strdup(optarg);
2187 6 : break;
2188 170 : case 16:
2189 : /* "--bindir=" means to use PATH */
2190 170 : if (strlen(optarg))
2191 0 : bindir = pg_strdup(optarg);
2192 : else
2193 170 : bindir = NULL;
2194 170 : break;
2195 8 : case 17:
2196 8 : dlpath = pg_strdup(optarg);
2197 8 : break;
2198 46 : case 18:
2199 46 : split_to_stringlist(optarg, ",", &extraroles);
2200 46 : break;
2201 16 : case 19:
2202 16 : add_stringlist_item(&temp_configs, optarg);
2203 16 : break;
2204 0 : case 20:
2205 0 : use_existing = true;
2206 0 : break;
2207 0 : case 21:
2208 0 : launcher = pg_strdup(optarg);
2209 0 : break;
2210 10 : case 22:
2211 10 : add_stringlist_item(&loadextension, optarg);
2212 10 : break;
2213 510 : case 24:
2214 510 : config_auth_datadir = pg_strdup(optarg);
2215 510 : break;
2216 6 : case 25:
2217 6 : max_concurrent_tests = atoi(optarg);
2218 6 : break;
2219 4 : case 26:
2220 4 : expecteddir = pg_strdup(optarg);
2221 4 : break;
2222 0 : default:
2223 : /* getopt_long already emitted a complaint */
2224 0 : pg_log_error_hint("Try \"%s --help\" for more information.",
2225 : progname);
2226 0 : exit(2);
2227 : }
2228 : }
2229 :
2230 : /*
2231 : * if we still have arguments, they are extra tests to run
2232 : */
2233 1286 : while (argc - optind >= 1)
2234 : {
2235 606 : add_stringlist_item(&extra_tests, argv[optind]);
2236 606 : optind++;
2237 : }
2238 :
2239 : /*
2240 : * We must have a database to run the tests in; either a default name, or
2241 : * one supplied by the --dbname switch.
2242 : */
2243 680 : if (!(dblist && dblist->str && dblist->str[0]))
2244 : {
2245 0 : bail("no database name was specified");
2246 : }
2247 :
2248 680 : if (config_auth_datadir)
2249 : {
2250 : #ifdef ENABLE_SSPI
2251 : if (!use_unix_sockets)
2252 : config_sspi_auth(config_auth_datadir, user);
2253 : #endif
2254 510 : exit(0);
2255 : }
2256 :
2257 170 : if (temp_instance && !port_specified_by_user)
2258 :
2259 : /*
2260 : * To reduce chances of interference with parallel installations, use
2261 : * a port number starting in the private range (49152-65535)
2262 : * calculated from the version number. This aids non-Unix socket mode
2263 : * systems; elsewhere, the use of a private socket directory already
2264 : * prevents interference.
2265 : */
2266 166 : port = 0xC000 | (PG_VERSION_NUM & 0x3FFF);
2267 :
2268 170 : inputdir = make_absolute_path(inputdir);
2269 170 : outputdir = make_absolute_path(outputdir);
2270 170 : expecteddir = make_absolute_path(expecteddir);
2271 170 : dlpath = make_absolute_path(dlpath);
2272 :
2273 : /*
2274 : * Initialization
2275 : */
2276 170 : open_result_files();
2277 :
2278 170 : initialize_environment();
2279 :
2280 : #if defined(HAVE_GETRLIMIT)
2281 170 : unlimit_core_size();
2282 : #endif
2283 :
2284 170 : if (temp_instance)
2285 : {
2286 : StringInfoData cmd;
2287 : FILE *pg_conf;
2288 : const char *env_wait;
2289 : int wait_seconds;
2290 : const char *initdb_template_dir;
2291 : const char *keywords[4];
2292 : const char *values[4];
2293 : PGPing rv;
2294 : const char *initdb_extra_opts_env;
2295 :
2296 : /*
2297 : * Prepare the temp instance
2298 : */
2299 :
2300 166 : if (directory_exists(temp_instance))
2301 : {
2302 0 : if (!rmtree(temp_instance, true))
2303 : {
2304 0 : bail("could not remove temp instance \"%s\"", temp_instance);
2305 : }
2306 : }
2307 :
2308 : /* make the temp instance top directory */
2309 166 : make_directory(temp_instance);
2310 :
2311 : /* and a directory for log files */
2312 166 : snprintf(buf, sizeof(buf), "%s/log", outputdir);
2313 166 : if (!directory_exists(buf))
2314 164 : make_directory(buf);
2315 :
2316 166 : initdb_extra_opts_env = getenv("PG_TEST_INITDB_EXTRA_OPTS");
2317 :
2318 166 : initStringInfo(&cmd);
2319 :
2320 : /*
2321 : * Create data directory.
2322 : *
2323 : * If available, use a previously initdb'd cluster as a template by
2324 : * copying it. For a lot of tests, that's substantially cheaper.
2325 : *
2326 : * There's very similar code in Cluster.pm, but we can't easily de
2327 : * duplicate it until we require perl at build time.
2328 : */
2329 166 : initdb_template_dir = getenv("INITDB_TEMPLATE");
2330 166 : if (initdb_template_dir == NULL || nolocale || debug || initdb_extra_opts_env)
2331 : {
2332 4 : note("initializing database system by running initdb");
2333 :
2334 8 : appendStringInfo(&cmd,
2335 : "\"%s%sinitdb\" -D \"%s/data\" --no-clean --no-sync",
2336 4 : bindir ? bindir : "",
2337 4 : bindir ? "/" : "",
2338 : temp_instance);
2339 4 : if (debug)
2340 0 : appendStringInfoString(&cmd, " --debug");
2341 4 : if (nolocale)
2342 4 : appendStringInfoString(&cmd, " --no-locale");
2343 4 : if (initdb_extra_opts_env)
2344 0 : appendStringInfo(&cmd, " %s", initdb_extra_opts_env);
2345 4 : appendStringInfo(&cmd, " > \"%s/log/initdb.log\" 2>&1", outputdir);
2346 4 : fflush(NULL);
2347 4 : if (system(cmd.data))
2348 : {
2349 0 : bail("initdb failed\n"
2350 : "# Examine \"%s/log/initdb.log\" for the reason.\n"
2351 : "# Command was: %s",
2352 : outputdir, cmd.data);
2353 : }
2354 : }
2355 : else
2356 : {
2357 : #ifndef WIN32
2358 162 : const char *copycmd = "cp -RPp \"%s\" \"%s/data\"";
2359 162 : int expected_exitcode = 0;
2360 : #else
2361 : const char *copycmd = "robocopy /E /NJS /NJH /NFL /NDL /NP \"%s\" \"%s/data\"";
2362 : int expected_exitcode = 1; /* 1 denotes files were copied */
2363 : #endif
2364 :
2365 162 : note("initializing database system by copying initdb template");
2366 :
2367 162 : appendStringInfo(&cmd,
2368 : copycmd,
2369 : initdb_template_dir,
2370 : temp_instance);
2371 162 : appendStringInfo(&cmd, " > \"%s/log/initdb.log\" 2>&1", outputdir);
2372 162 : fflush(NULL);
2373 162 : if (system(cmd.data) != expected_exitcode)
2374 : {
2375 0 : bail("copying of initdb template failed\n"
2376 : "# Examine \"%s/log/initdb.log\" for the reason.\n"
2377 : "# Command was: %s",
2378 : outputdir, cmd.data);
2379 : }
2380 : }
2381 :
2382 166 : pfree(cmd.data);
2383 :
2384 : /*
2385 : * Adjust the default postgresql.conf for regression testing. The user
2386 : * can specify a file to be appended; in any case we expand logging
2387 : * and set max_prepared_transactions to enable testing of prepared
2388 : * xacts. (Note: to reduce the probability of unexpected shmmax
2389 : * failures, don't set max_prepared_transactions any higher than
2390 : * actually needed by the prepared_xacts regression test.)
2391 : */
2392 166 : snprintf(buf, sizeof(buf), "%s/data/postgresql.conf", temp_instance);
2393 166 : pg_conf = fopen(buf, "a");
2394 166 : if (pg_conf == NULL)
2395 0 : bail("could not open \"%s\" for adding extra config: %m", buf);
2396 :
2397 166 : fputs("\n# Configuration added by pg_regress\n\n", pg_conf);
2398 166 : fputs("log_autovacuum_min_duration = 0\n", pg_conf);
2399 166 : fputs("log_checkpoints = on\n", pg_conf);
2400 166 : fputs("log_line_prefix = '%m %b[%p] %q%a '\n", pg_conf);
2401 166 : fputs("log_lock_waits = on\n", pg_conf);
2402 166 : fputs("log_temp_files = 128kB\n", pg_conf);
2403 166 : fputs("max_prepared_transactions = 2\n", pg_conf);
2404 :
2405 182 : for (sl = temp_configs; sl != NULL; sl = sl->next)
2406 : {
2407 16 : char *temp_config = sl->str;
2408 : FILE *extra_conf;
2409 : char line_buf[1024];
2410 :
2411 16 : extra_conf = fopen(temp_config, "r");
2412 16 : if (extra_conf == NULL)
2413 : {
2414 0 : bail("could not open \"%s\" to read extra config: %m",
2415 : temp_config);
2416 : }
2417 54 : while (fgets(line_buf, sizeof(line_buf), extra_conf) != NULL)
2418 38 : fputs(line_buf, pg_conf);
2419 16 : fclose(extra_conf);
2420 : }
2421 :
2422 166 : fclose(pg_conf);
2423 :
2424 : #ifdef ENABLE_SSPI
2425 : if (!use_unix_sockets)
2426 : {
2427 : /*
2428 : * Since we successfully used the same buffer for the much-longer
2429 : * "initdb" command, this can't truncate.
2430 : */
2431 : snprintf(buf, sizeof(buf), "%s/data", temp_instance);
2432 : config_sspi_auth(buf, NULL);
2433 : }
2434 : #endif
2435 :
2436 : /*
2437 : * Prepare the connection params for checking the state of the server
2438 : * before starting the tests.
2439 : */
2440 166 : sprintf(portstr, "%d", port);
2441 166 : keywords[0] = "dbname";
2442 166 : values[0] = "postgres";
2443 166 : keywords[1] = "port";
2444 166 : values[1] = portstr;
2445 166 : keywords[2] = "host";
2446 166 : values[2] = hostname ? hostname : sockdir;
2447 166 : keywords[3] = NULL;
2448 166 : values[3] = NULL;
2449 :
2450 : /*
2451 : * Check if there is a postmaster running already.
2452 : */
2453 166 : for (i = 0; i < 16; i++)
2454 : {
2455 166 : rv = PQpingParams(keywords, values, 1);
2456 :
2457 166 : if (rv == PQPING_OK)
2458 : {
2459 0 : if (port_specified_by_user || i == 15)
2460 : {
2461 0 : note("port %d apparently in use", port);
2462 0 : if (!port_specified_by_user)
2463 0 : note("could not determine an available port");
2464 0 : bail("Specify an unused port using the --port option or shut down any conflicting PostgreSQL servers.");
2465 : }
2466 :
2467 0 : note("port %d apparently in use, trying %d", port, port + 1);
2468 0 : port++;
2469 0 : sprintf(portstr, "%d", port);
2470 0 : setenv("PGPORT", portstr, 1);
2471 : }
2472 : else
2473 166 : break;
2474 : }
2475 :
2476 : /*
2477 : * Start the temp postmaster
2478 : */
2479 830 : snprintf(buf, sizeof(buf),
2480 : "\"%s%spostgres\" -D \"%s/data\" -F%s "
2481 : "-c \"listen_addresses=%s\" -k \"%s\" "
2482 : "> \"%s/log/postmaster.log\" 2>&1",
2483 166 : bindir ? bindir : "",
2484 166 : bindir ? "/" : "",
2485 166 : temp_instance, debug ? " -d 5" : "",
2486 332 : hostname ? hostname : "", sockdir ? sockdir : "",
2487 : outputdir);
2488 166 : postmaster_pid = spawn_process(buf);
2489 166 : if (postmaster_pid == INVALID_PID)
2490 0 : bail("could not spawn postmaster: %m");
2491 :
2492 : /*
2493 : * Wait till postmaster is able to accept connections; normally takes
2494 : * only a fraction of a second or so, but Cygwin is reportedly *much*
2495 : * slower, and test builds using Valgrind or similar tools might be
2496 : * too. Hence, allow the default timeout of 60 seconds to be
2497 : * overridden from the PGCTLTIMEOUT environment variable.
2498 : */
2499 166 : env_wait = getenv("PGCTLTIMEOUT");
2500 166 : if (env_wait != NULL)
2501 : {
2502 0 : wait_seconds = atoi(env_wait);
2503 0 : if (wait_seconds <= 0)
2504 0 : wait_seconds = 60;
2505 : }
2506 : else
2507 166 : wait_seconds = 60;
2508 :
2509 426 : for (i = 0; i < wait_seconds * WAIT_TICKS_PER_SECOND; i++)
2510 : {
2511 : /*
2512 : * It's fairly unlikely that the server is responding immediately
2513 : * so we start with sleeping before checking instead of the other
2514 : * way around.
2515 : */
2516 426 : pg_usleep(1000000L / WAIT_TICKS_PER_SECOND);
2517 :
2518 426 : rv = PQpingParams(keywords, values, 1);
2519 :
2520 : /* Done if the server is running and accepts connections */
2521 426 : if (rv == PQPING_OK)
2522 166 : break;
2523 :
2524 260 : if (rv == PQPING_NO_ATTEMPT)
2525 0 : bail("attempting to connect to postmaster failed");
2526 :
2527 : /*
2528 : * Fail immediately if postmaster has exited
2529 : */
2530 : #ifndef WIN32
2531 260 : if (waitpid(postmaster_pid, NULL, WNOHANG) == postmaster_pid)
2532 : #else
2533 : if (WaitForSingleObject(postmaster_pid, 0) == WAIT_OBJECT_0)
2534 : #endif
2535 : {
2536 0 : bail("postmaster failed, examine \"%s/log/postmaster.log\" for the reason",
2537 : outputdir);
2538 : }
2539 : }
2540 166 : if (i >= wait_seconds * WAIT_TICKS_PER_SECOND)
2541 : {
2542 0 : diag("postmaster did not respond within %d seconds, examine \"%s/log/postmaster.log\" for the reason",
2543 : wait_seconds, outputdir);
2544 :
2545 : /*
2546 : * If we get here, the postmaster is probably wedged somewhere in
2547 : * startup. Try to kill it ungracefully rather than leaving a
2548 : * stuck postmaster that might interfere with subsequent test
2549 : * attempts.
2550 : */
2551 : #ifndef WIN32
2552 0 : if (kill(postmaster_pid, SIGKILL) != 0 && errno != ESRCH)
2553 0 : bail("could not kill failed postmaster: %m");
2554 : #else
2555 : if (TerminateProcess(postmaster_pid, 255) == 0)
2556 : bail("could not kill failed postmaster: error code %lu",
2557 : GetLastError());
2558 : #endif
2559 0 : bail("postmaster failed");
2560 : }
2561 :
2562 166 : postmaster_running = true;
2563 :
2564 : #ifdef _WIN64
2565 : /* need a series of two casts to convert HANDLE without compiler warning */
2566 : #define ULONGPID(x) (unsigned long) (unsigned long long) (x)
2567 : #else
2568 : #define ULONGPID(x) (unsigned long) (x)
2569 : #endif
2570 166 : note("using temp instance on port %d with PID %lu",
2571 : port, ULONGPID(postmaster_pid));
2572 : }
2573 : else
2574 : {
2575 : /*
2576 : * Using an existing installation, so may need to get rid of
2577 : * pre-existing database(s) and role(s)
2578 : */
2579 4 : if (!use_existing)
2580 : {
2581 8 : for (sl = dblist; sl; sl = sl->next)
2582 4 : drop_database_if_exists(sl->str);
2583 4 : for (sl = extraroles; sl; sl = sl->next)
2584 0 : drop_role_if_exists(sl->str);
2585 : }
2586 : }
2587 :
2588 : /*
2589 : * Create the test database(s) and role(s)
2590 : */
2591 170 : if (!use_existing)
2592 : {
2593 344 : for (sl = dblist; sl; sl = sl->next)
2594 174 : create_database(sl->str);
2595 178 : for (sl = extraroles; sl; sl = sl->next)
2596 8 : create_role(sl->str, dblist);
2597 : }
2598 :
2599 : /*
2600 : * Ready to run the tests
2601 : */
2602 182 : for (sl = schedulelist; sl != NULL; sl = sl->next)
2603 : {
2604 12 : run_schedule(sl->str, startfunc, postfunc);
2605 : }
2606 :
2607 776 : for (sl = extra_tests; sl != NULL; sl = sl->next)
2608 : {
2609 606 : run_single_test(sl->str, startfunc, postfunc);
2610 : }
2611 :
2612 : /*
2613 : * Shut down temp installation's postmaster
2614 : */
2615 170 : if (temp_instance)
2616 : {
2617 166 : stop_postmaster();
2618 : }
2619 :
2620 : /*
2621 : * If there were no errors, remove the temp instance immediately to
2622 : * conserve disk space. (If there were errors, we leave the instance in
2623 : * place for possible manual investigation.)
2624 : */
2625 170 : if (temp_instance && fail_count == 0)
2626 : {
2627 166 : if (!rmtree(temp_instance, true))
2628 0 : diag("could not remove temp instance \"%s\"",
2629 : temp_instance);
2630 : }
2631 :
2632 : /*
2633 : * Emit a TAP compliant Plan
2634 : */
2635 170 : plan(fail_count + success_count);
2636 :
2637 : /*
2638 : * Emit nice-looking summary message
2639 : */
2640 170 : if (fail_count == 0)
2641 170 : note("All %d tests passed.", success_count);
2642 : else
2643 0 : diag("%d of %d tests failed.", fail_count, success_count + fail_count);
2644 :
2645 170 : if (file_size(difffilename) > 0)
2646 : {
2647 0 : diag("The differences that caused some tests to fail can be viewed in the file \"%s\".",
2648 : difffilename);
2649 0 : diag("A copy of the test summary that you see above is saved in the file \"%s\".",
2650 : logfilename);
2651 : }
2652 : else
2653 : {
2654 170 : unlink(difffilename);
2655 170 : unlink(logfilename);
2656 : }
2657 :
2658 170 : fclose(logfile);
2659 170 : logfile = NULL;
2660 :
2661 170 : if (fail_count != 0)
2662 0 : exit(1);
2663 :
2664 170 : return 0;
2665 : }
|