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