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