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