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