#include "pg_regress.h"
#include <ctype.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include "getopt_long.h"
#include "pg_config_paths.h"
Go to the source code of this file.
Data Structures | |
struct | _resultmap |
Defines | |
#define | MAX_PARALLEL_TESTS 100 |
#define | ULONGPID(x) (unsigned long) (x) |
Typedefs | |
typedef struct _resultmap | _resultmap |
Functions | |
static bool | directory_exists (const char *dir) |
static void | make_directory (const char *dir) |
static void | header (const char *fmt,...) __attribute__((format(PG_PRINTF_ATTRIBUTE |
static void static void | status (const char *fmt,...) __attribute__((format(PG_PRINTF_ATTRIBUTE |
static void static void static void | psql_command (const char *database, const char *query,...) __attribute__((format(PG_PRINTF_ATTRIBUTE |
static void static void static void void | add_stringlist_item (_stringlist **listhead, const char *str) |
static void | free_stringlist (_stringlist **listhead) |
static void | split_to_stringlist (const char *s, const char *delim, _stringlist **listhead) |
static void | status_end (void) |
static void | stop_postmaster (void) |
static bool | string_matches_pattern (const char *str, const char *pattern) |
void | replace_string (char *string, char *replace, char *replacement) |
static void | convert_sourcefiles_in (char *source_subdir, char *dest_dir, char *dest_subdir, char *suffix) |
static void | convert_sourcefiles (void) |
static void | load_resultmap (void) |
static const char * | get_expectfile (const char *testname, const char *file) |
static void | doputenv (const char *var, const char *val) |
static void | add_to_path (const char *pathname, char separator, const char *addval) |
static void | initialize_environment (void) |
PID_TYPE | spawn_process (const char *cmdline) |
static long | file_size (const char *file) |
static int | file_line_count (const char *file) |
bool | file_exists (const char *file) |
static char * | get_alternative_expectfile (const char *expectfile, int i) |
static int | run_diff (const char *cmd, const char *filename) |
static bool | results_differ (const char *testname, const char *resultsfile, const char *default_expectfile) |
static void | wait_for_tests (PID_TYPE *pids, int *statuses, char **names, int num_tests) |
static void | log_child_failure (int exitstatus) |
static void | run_schedule (const char *schedule, test_function tfunc) |
static void | run_single_test (const char *test, test_function tfunc) |
static void | open_result_files (void) |
static void | drop_database_if_exists (const char *dbname) |
static void | create_database (const char *dbname) |
static void | drop_role_if_exists (const char *rolename) |
static void | create_role (const char *rolename, const _stringlist *granted_dbs) |
static char * | make_absolute_path (const char *in) |
static void | help (void) |
int | regression_main (int argc, char *argv[], init_function ifunc, test_function tfunc) |
Variables | |
char * | bindir = PGBINDIR |
char * | libdir = LIBDIR |
char * | datadir = PGSHAREDIR |
char * | host_platform = HOST_TUPLE |
static char * | makeprog = MAKEPROG |
static char * | shellprog = SHELLPROG |
const char * | basic_diff_opts = "" |
const char * | pretty_diff_opts = "-C3" |
_stringlist * | dblist = NULL |
bool | debug = false |
char * | inputdir = "." |
char * | outputdir = "." |
char * | psqldir = PGBINDIR |
char * | launcher = NULL |
static _stringlist * | loadlanguage = NULL |
static _stringlist * | loadextension = NULL |
static int | max_connections = 0 |
static char * | encoding = NULL |
static _stringlist * | schedulelist = NULL |
static _stringlist * | extra_tests = NULL |
static char * | temp_install = NULL |
static char * | temp_config = NULL |
static char * | top_builddir = NULL |
static bool | nolocale = false |
static bool | use_existing = false |
static char * | hostname = NULL |
static int | port = -1 |
static bool | port_specified_by_user = false |
static char * | dlpath = PKGLIBDIR |
static char * | user = NULL |
static _stringlist * | extraroles = NULL |
static _stringlist * | extra_install = NULL |
static const char * | progname |
static char * | logfilename |
static FILE * | logfile |
static char * | difffilename |
static _resultmap * | resultmap = NULL |
static PID_TYPE | postmaster_pid = INVALID_PID |
static bool | postmaster_running = false |
static int | success_count = 0 |
static int | fail_count = 0 |
static int | fail_ignore_count = 0 |
#define MAX_PARALLEL_TESTS 100 |
Referenced by run_schedule().
#define ULONGPID | ( | x | ) | (unsigned long) (x) |
Referenced by regression_main().
typedef struct _resultmap _resultmap |
static void static void static void void add_stringlist_item | ( | _stringlist ** | listhead, | |
const char * | str | |||
) |
Definition at line 180 of file pg_regress.c.
References malloc, _stringlist::next, NULL, and _stringlist::str.
Referenced by ecpg_start_test(), isolation_init(), isolation_start_test(), psql_init(), psql_start_test(), regression_main(), run_schedule(), and split_to_stringlist().
{ _stringlist *newentry = malloc(sizeof(_stringlist)); _stringlist *oldentry; newentry->str = strdup(str); newentry->next = NULL; if (*listhead == NULL) *listhead = newentry; else { for (oldentry = *listhead; oldentry->next; oldentry = oldentry->next) /* skip */ ; oldentry->next = newentry; } }
static void add_to_path | ( | const char * | pathname, | |
char | separator, | |||
const char * | addval | |||
) | [static] |
Definition at line 668 of file pg_regress.c.
References malloc, and putenv.
Referenced by initialize_environment().
{ char *oldval = getenv(pathname); char *newval; if (!oldval || !oldval[0]) { /* no previous value */ newval = malloc(strlen(pathname) + strlen(addval) + 2); sprintf(newval, "%s=%s", pathname, addval); } else { newval = malloc(strlen(pathname) + strlen(addval) + strlen(oldval) + 3); sprintf(newval, "%s=%s%c%s", pathname, addval, separator, oldval); } putenv(newval); }
static void convert_sourcefiles | ( | void | ) | [static] |
Definition at line 524 of file pg_regress.c.
References convert_sourcefiles_in(), inputdir, and outputdir.
Referenced by initialize_environment().
{ convert_sourcefiles_in("input", inputdir, "sql", "sql"); convert_sourcefiles_in("output", outputdir, "expected", "out"); }
static void convert_sourcefiles_in | ( | char * | source_subdir, | |
char * | dest_dir, | |||
char * | dest_subdir, | |||
char * | suffix | |||
) | [static] |
Definition at line 410 of file pg_regress.c.
References _, directory_exists(), dlpath, inputdir, make_directory(), MAXPGPATH, name, outputdir, pgfnames(), pgfnames_cleanup(), progname, replace_string(), rmtree(), snprintf(), and strerror().
Referenced by convert_sourcefiles().
{ char testtablespace[MAXPGPATH]; char indir[MAXPGPATH]; struct stat st; int ret; char **name; char **names; int count = 0; snprintf(indir, MAXPGPATH, "%s/%s", inputdir, source_subdir); /* Check that indir actually exists and is a directory */ ret = stat(indir, &st); if (ret != 0 || !S_ISDIR(st.st_mode)) { /* * No warning, to avoid noise in tests that do not have these * directories; for example, ecpg, contrib and src/pl. */ return; } names = pgfnames(indir); if (!names) /* Error logged in pgfnames */ exit(2); snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", outputdir); #ifdef WIN32 /* * On Windows only, clean out the test tablespace dir, or create it if it * doesn't exist. On other platforms we expect the Makefile to take care * of that. (We don't migrate that functionality in here because it'd be * harder to cope with platform-specific issues such as SELinux.) * * XXX it would be better if pg_regress.c had nothing at all to do with * testtablespace, and this were handled by a .BAT file or similar on * Windows. See pgsql-hackers discussion of 2008-01-18. */ if (directory_exists(testtablespace)) rmtree(testtablespace, true); make_directory(testtablespace); #endif /* finally loop on each file and do the replacement */ for (name = names; *name; name++) { char srcfile[MAXPGPATH]; char destfile[MAXPGPATH]; char prefix[MAXPGPATH]; FILE *infile, *outfile; char line[1024]; /* reject filenames not finishing in ".source" */ if (strlen(*name) < 8) continue; if (strcmp(*name + strlen(*name) - 7, ".source") != 0) continue; count++; /* build the full actual paths to open */ snprintf(prefix, strlen(*name) - 6, "%s", *name); snprintf(srcfile, MAXPGPATH, "%s/%s", indir, *name); snprintf(destfile, MAXPGPATH, "%s/%s/%s.%s", dest_dir, dest_subdir, prefix, suffix); infile = fopen(srcfile, "r"); if (!infile) { fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), progname, srcfile, strerror(errno)); exit(2); } outfile = fopen(destfile, "w"); if (!outfile) { fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"), progname, destfile, strerror(errno)); exit(2); } while (fgets(line, sizeof(line), infile)) { replace_string(line, "@abs_srcdir@", inputdir); replace_string(line, "@abs_builddir@", outputdir); replace_string(line, "@testtablespace@", testtablespace); replace_string(line, "@libdir@", dlpath); replace_string(line, "@DLSUFFIX@", DLSUFFIX); fputs(line, outfile); } fclose(infile); fclose(outfile); } /* * If we didn't process any files, complain because it probably means * somebody neglected to pass the needed --inputdir argument. */ if (count <= 0) { fprintf(stderr, _("%s: no *.source files found in \"%s\"\n"), progname, indir); exit(2); } pgfnames_cleanup(names); }
static void create_database | ( | const char * | dbname | ) | [static] |
Definition at line 1782 of file pg_regress.c.
References _, encoding, header(), _stringlist::next, nolocale, psql_command(), and _stringlist::str.
Referenced by regression_main().
{ _stringlist *sl; /* * We use template0 so that any installation-local cruft in template1 will * not mess up the tests. */ header(_("creating database \"%s\""), dbname); if (encoding) psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0 ENCODING='%s'%s", dbname, encoding, (nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : ""); else psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0%s", dbname, (nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : ""); psql_command(dbname, "ALTER DATABASE \"%s\" SET lc_messages TO 'C';" "ALTER DATABASE \"%s\" SET lc_monetary TO 'C';" "ALTER DATABASE \"%s\" SET lc_numeric TO 'C';" "ALTER DATABASE \"%s\" SET lc_time TO 'C';" "ALTER DATABASE \"%s\" SET timezone_abbreviations TO 'Default';", dbname, dbname, dbname, dbname, dbname); /* * Install any requested procedural languages. We use CREATE OR REPLACE * so that this will work whether or not the language is preinstalled. */ for (sl = loadlanguage; sl != NULL; sl = sl->next) { header(_("installing %s"), sl->str); psql_command(dbname, "CREATE OR REPLACE LANGUAGE \"%s\"", sl->str); } /* * Install any requested extensions. We use CREATE IF NOT EXISTS so that * this will work whether or not the extension is preinstalled. */ for (sl = loadextension; sl != NULL; sl = sl->next) { header(_("installing %s"), sl->str); psql_command(dbname, "CREATE EXTENSION IF NOT EXISTS \"%s\"", sl->str); } }
static void create_role | ( | const char * | rolename, | |
const _stringlist * | granted_dbs | |||
) | [static] |
Definition at line 1834 of file pg_regress.c.
References _, header(), _stringlist::next, psql_command(), and _stringlist::str.
Referenced by regression_main().
{ header(_("creating role \"%s\""), rolename); psql_command("postgres", "CREATE ROLE \"%s\" WITH LOGIN", rolename); for (; granted_dbs != NULL; granted_dbs = granted_dbs->next) { psql_command("postgres", "GRANT ALL ON DATABASE \"%s\" TO \"%s\"", granted_dbs->str, rolename); } }
static bool directory_exists | ( | const char * | dir | ) | [static] |
Definition at line 1133 of file pg_regress.c.
Referenced by convert_sourcefiles_in(), open_result_files(), and regression_main().
{ struct stat st; if (stat(dir, &st) != 0) return false; if (S_ISDIR(st.st_mode)) return true; return false; }
static void doputenv | ( | const char * | var, | |
const char * | val | |||
) | [static] |
Definition at line 655 of file pg_regress.c.
References malloc, and putenv.
Referenced by initialize_environment(), and regression_main().
static void drop_database_if_exists | ( | const char * | dbname | ) | [static] |
Definition at line 1775 of file pg_regress.c.
References _, header(), and psql_command().
Referenced by regression_main().
{ header(_("dropping database \"%s\""), dbname); psql_command("postgres", "DROP DATABASE IF EXISTS \"%s\"", dbname); }
static void drop_role_if_exists | ( | const char * | rolename | ) | [static] |
Definition at line 1827 of file pg_regress.c.
References _, header(), and psql_command().
Referenced by regression_main().
{ header(_("dropping role \"%s\""), rolename); psql_command("postgres", "DROP ROLE IF EXISTS \"%s\"", rolename); }
bool file_exists | ( | const char * | file | ) |
Definition at line 1122 of file pg_regress.c.
{ FILE *f = fopen(file, "r"); if (!f) return false; fclose(f); return true; }
static int file_line_count | ( | const char * | file | ) | [static] |
Definition at line 1100 of file pg_regress.c.
References _, progname, and strerror().
Referenced by results_differ().
static long file_size | ( | const char * | file | ) | [static] |
Definition at line 1079 of file pg_regress.c.
References _, progname, and strerror().
Referenced by regression_main(), and run_diff().
static void free_stringlist | ( | _stringlist ** | listhead | ) | [static] |
Definition at line 201 of file pg_regress.c.
Referenced by regression_main(), and run_schedule().
static char* get_alternative_expectfile | ( | const char * | expectfile, | |
int | i | |||
) | [static] |
Definition at line 1160 of file pg_regress.c.
References free, malloc, and snprintf().
Referenced by results_differ().
{ char *last_dot; int ssize = strlen(expectfile) + 2 + 1; char *tmp = (char *) malloc(ssize); char *s = (char *) malloc(ssize); strcpy(tmp, expectfile); last_dot = strrchr(tmp, '.'); if (!last_dot) { free(tmp); free(s); return NULL; } *last_dot = '\0'; snprintf(s, ssize, "%s_%d.%s", tmp, i, last_dot + 1); free(tmp); return s; }
static const char* get_expectfile | ( | const char * | testname, | |
const char * | file | |||
) | [static] |
Definition at line 626 of file pg_regress.c.
References _resultmap::next, _resultmap::resultfile, _resultmap::test, and _resultmap::type.
Referenced by results_differ().
{ char *file_type; _resultmap *rm; /* * Determine the file type from the file name. This is just what is * following the last dot in the file name. */ if (!file || !(file_type = strrchr(file, '.'))) return NULL; file_type++; for (rm = resultmap; rm != NULL; rm = rm->next) { if (strcmp(testname, rm->test) == 0 && strcmp(file_type, rm->type) == 0) { return rm->resultfile; } } return NULL; }
static void header | ( | const char * | fmt, | |
... | ||||
) | [static] |
Definition at line 233 of file pg_regress.c.
References vsnprintf().
Referenced by _tarPositionTo(), create_database(), create_role(), decodePageSplitRecord(), drop_database_if_exists(), drop_role_if_exists(), ExecHashJoinGetSavedTuple(), ReceiveTarFile(), and regression_main().
{ char tmp[64]; va_list ap; va_start(ap, fmt); vsnprintf(tmp, sizeof(tmp), fmt, ap); va_end(ap); fprintf(stdout, "============== %-38s ==============\n", tmp); fflush(stdout); }
static void help | ( | void | ) | [static] |
Definition at line 1874 of file pg_regress.c.
Referenced by regression_main().
{ printf(_("PostgreSQL regression test driver\n")); printf(_("\n")); printf(_("Usage:\n %s [OPTION]... [EXTRA-TEST]...\n"), progname); printf(_("\n")); printf(_("Options:\n")); printf(_(" --create-role=ROLE create the specified role before testing\n")); printf(_(" --dbname=DB use database DB (default \"regression\")\n")); printf(_(" --debug turn on debug mode in programs that are run\n")); printf(_(" --dlpath=DIR look for dynamic libraries in DIR\n")); printf(_(" --encoding=ENCODING use ENCODING as the encoding\n")); printf(_(" --inputdir=DIR take input files from DIR (default \".\")\n")); printf(_(" --launcher=CMD use CMD as launcher of psql\n")); printf(_(" --load-extension=EXT load the named extension before running the\n")); printf(_(" tests; can appear multiple times\n")); printf(_(" --load-language=LANG load the named language before running the\n")); printf(_(" tests; can appear multiple times\n")); printf(_(" --max-connections=N maximum number of concurrent connections\n")); printf(_(" (default is 0, meaning unlimited)\n")); printf(_(" --outputdir=DIR place output files in DIR (default \".\")\n")); printf(_(" --schedule=FILE use test ordering schedule from FILE\n")); printf(_(" (can be used multiple times to concatenate)\n")); printf(_(" --temp-install=DIR create a temporary installation in DIR\n")); printf(_(" --use-existing use an existing installation\n")); printf(_("\n")); printf(_("Options for \"temp-install\" mode:\n")); printf(_(" --extra-install=DIR additional directory to install (e.g., contrib)\n")); printf(_(" --no-locale use C locale\n")); printf(_(" --port=PORT start postmaster on PORT\n")); printf(_(" --temp-config=FILE append contents of FILE to temporary config\n")); printf(_(" --top-builddir=DIR (relative) path to top level build directory\n")); printf(_("\n")); printf(_("Options for using an existing installation:\n")); printf(_(" --host=HOST use postmaster running on HOST\n")); printf(_(" --port=PORT use postmaster running at PORT\n")); printf(_(" --user=USER connect as USER\n")); printf(_(" --psqldir=DIR use psql in DIR (default: configured bindir)\n")); printf(_("\n")); printf(_("The exit status is 0 if all tests passed, 1 if some tests failed, and 2\n")); printf(_("if the tests could not be run for some reason.\n")); printf(_("\n")); printf(_("Report bugs to <[email protected]>.\n")); }
static void initialize_environment | ( | void | ) | [static] |
Definition at line 691 of file pg_regress.c.
References _, add_to_path(), bindir, convert_sourcefiles(), datadir, doputenv(), encoding, hostname, libdir, load_resultmap(), malloc, nolocale, NULL, pghost, pgport, port, psqldir, putenv, temp_install, unsetenv, and user.
Referenced by regression_main().
{ char *tmp; putenv("PGAPPNAME=pg_regress"); if (nolocale) { /* * Clear out any non-C locale settings */ unsetenv("LC_COLLATE"); unsetenv("LC_CTYPE"); unsetenv("LC_MONETARY"); unsetenv("LC_NUMERIC"); unsetenv("LC_TIME"); unsetenv("LANG"); /* On Windows the default locale cannot be English, so force it */ #if defined(WIN32) || defined(__CYGWIN__) putenv("LANG=en"); #endif } /* * Set translation-related settings to English; otherwise psql will * produce translated messages and produce diffs. (XXX If we ever support * translation of pg_regress, this needs to be moved elsewhere, where psql * is actually called.) */ unsetenv("LANGUAGE"); unsetenv("LC_ALL"); putenv("LC_MESSAGES=C"); /* * Set encoding as requested */ if (encoding) doputenv("PGCLIENTENCODING", encoding); else unsetenv("PGCLIENTENCODING"); /* * Set timezone and datestyle for datetime-related tests */ putenv("PGTZ=PST8PDT"); putenv("PGDATESTYLE=Postgres, MDY"); /* * Likewise set intervalstyle to ensure consistent results. This is a bit * more painful because we must use PGOPTIONS, and we want to preserve the * user's ability to set other variables through that. */ { const char *my_pgoptions = "-c intervalstyle=postgres_verbose"; const char *old_pgoptions = getenv("PGOPTIONS"); char *new_pgoptions; if (!old_pgoptions) old_pgoptions = ""; new_pgoptions = malloc(strlen(old_pgoptions) + strlen(my_pgoptions) + 12); sprintf(new_pgoptions, "PGOPTIONS=%s %s", old_pgoptions, my_pgoptions); putenv(new_pgoptions); } if (temp_install) { /* * Clear out any environment vars that might cause psql to connect to * the wrong postmaster, or otherwise behave in nondefault ways. (Note * we also use psql's -X switch consistently, so that ~/.psqlrc files * won't mess things up.) Also, set PGPORT to the temp port, and set * or unset PGHOST depending on whether we are using TCP or Unix * sockets. */ unsetenv("PGDATABASE"); unsetenv("PGUSER"); unsetenv("PGSERVICE"); unsetenv("PGSSLMODE"); unsetenv("PGREQUIRESSL"); unsetenv("PGCONNECT_TIMEOUT"); unsetenv("PGDATA"); if (hostname != NULL) doputenv("PGHOST", hostname); else unsetenv("PGHOST"); unsetenv("PGHOSTADDR"); if (port != -1) { char s[16]; sprintf(s, "%d", port); doputenv("PGPORT", s); } /* * GNU make stores some flags in the MAKEFLAGS environment variable to * pass arguments to its own children. If we are invoked by make, * that causes the make invoked by us to think its part of the make * task invoking us, and so it tries to communicate with the toplevel * make. Which fails. * * Unset the variable to protect against such problems. We also reset * MAKELEVEL to be certain the child doesn't notice the make above us. */ unsetenv("MAKEFLAGS"); unsetenv("MAKELEVEL"); /* * Adjust path variables to point into the temp-install tree */ tmp = malloc(strlen(temp_install) + 32 + strlen(bindir)); sprintf(tmp, "%s/install/%s", temp_install, bindir); bindir = tmp; tmp = malloc(strlen(temp_install) + 32 + strlen(libdir)); sprintf(tmp, "%s/install/%s", temp_install, libdir); libdir = tmp; tmp = malloc(strlen(temp_install) + 32 + strlen(datadir)); sprintf(tmp, "%s/install/%s", temp_install, datadir); datadir = tmp; /* psql will be installed into temp-install bindir */ psqldir = bindir; /* * Set up shared library paths to include the temp install. * * LD_LIBRARY_PATH covers many platforms. DYLD_LIBRARY_PATH works on * Darwin, and maybe other Mach-based systems. LIBPATH is for AIX. * Windows needs shared libraries in PATH (only those linked into * executables, not dlopen'ed ones). Feel free to account for others * as well. */ add_to_path("LD_LIBRARY_PATH", ':', libdir); add_to_path("DYLD_LIBRARY_PATH", ':', libdir); add_to_path("LIBPATH", ':', libdir); #if defined(WIN32) add_to_path("PATH", ';', libdir); #elif defined(__CYGWIN__) add_to_path("PATH", ':', libdir); #endif } else { const char *pghost; const char *pgport; /* * When testing an existing install, we honor existing environment * variables, except if they're overridden by command line options. */ if (hostname != NULL) { doputenv("PGHOST", hostname); unsetenv("PGHOSTADDR"); } if (port != -1) { char s[16]; sprintf(s, "%d", port); doputenv("PGPORT", s); } if (user != NULL) doputenv("PGUSER", user); /* * Report what we're connecting to */ pghost = getenv("PGHOST"); pgport = getenv("PGPORT"); #ifndef HAVE_UNIX_SOCKETS if (!pghost) pghost = "localhost"; #endif if (pghost && pgport) printf(_("(using postmaster on %s, port %s)\n"), pghost, pgport); if (pghost && !pgport) printf(_("(using postmaster on %s, default port)\n"), pghost); if (!pghost && pgport) printf(_("(using postmaster on Unix socket, port %s)\n"), pgport); if (!pghost && !pgport) printf(_("(using postmaster on Unix socket, default port)\n")); } convert_sourcefiles(); load_resultmap(); }
static void load_resultmap | ( | void | ) | [static] |
Definition at line 544 of file pg_regress.c.
References _, buf, host_platform, i, inputdir, malloc, _resultmap::next, progname, _resultmap::resultfile, snprintf(), strerror(), string_matches_pattern(), _resultmap::test, and _resultmap::type.
Referenced by initialize_environment().
{ char buf[MAXPGPATH]; FILE *f; /* scan the file ... */ snprintf(buf, sizeof(buf), "%s/resultmap", inputdir); f = fopen(buf, "r"); if (!f) { /* OK if it doesn't exist, else complain */ if (errno == ENOENT) return; fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), progname, buf, strerror(errno)); exit(2); } while (fgets(buf, sizeof(buf), f)) { char *platform; char *file_type; char *expected; int i; /* strip trailing whitespace, especially the newline */ i = strlen(buf); while (i > 0 && isspace((unsigned char) buf[i - 1])) buf[--i] = '\0'; /* parse out the line fields */ file_type = strchr(buf, ':'); if (!file_type) { fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"), buf); exit(2); } *file_type++ = '\0'; platform = strchr(file_type, ':'); if (!platform) { fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"), buf); exit(2); } *platform++ = '\0'; expected = strchr(platform, '='); if (!expected) { fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"), buf); exit(2); } *expected++ = '\0'; /* * if it's for current platform, save it in resultmap list. Note: by * adding at the front of the list, we ensure that in ambiguous cases, * the last match in the resultmap file is used. This mimics the * behavior of the old shell script. */ if (string_matches_pattern(host_platform, platform)) { _resultmap *entry = malloc(sizeof(_resultmap)); entry->test = strdup(buf); entry->type = strdup(file_type); entry->resultfile = strdup(expected); entry->next = resultmap; resultmap = entry; } } fclose(f); }
static void log_child_failure | ( | int | exitstatus | ) | [static] |
Definition at line 1424 of file pg_regress.c.
References _, status(), WEXITSTATUS, WIFEXITED, WIFSIGNALED, and WTERMSIG.
Referenced by run_schedule(), and run_single_test().
{ if (WIFEXITED(exitstatus)) status(_(" (test process exited with exit code %d)"), WEXITSTATUS(exitstatus)); else if (WIFSIGNALED(exitstatus)) { #if defined(WIN32) status(_(" (test process was terminated by exception 0x%X)"), WTERMSIG(exitstatus)); #elif defined(HAVE_DECL_SYS_SIGLIST) && HAVE_DECL_SYS_SIGLIST status(_(" (test process was terminated by signal %d: %s)"), WTERMSIG(exitstatus), WTERMSIG(exitstatus) < NSIG ? sys_siglist[WTERMSIG(exitstatus)] : "(unknown))"); #else status(_(" (test process was terminated by signal %d)"), WTERMSIG(exitstatus)); #endif } else status(_(" (test process exited with unrecognized status %d)"), exitstatus); }
static char* make_absolute_path | ( | const char * | in | ) | [static] |
Definition at line 1846 of file pg_regress.c.
References _, canonicalize_path(), is_absolute_path, malloc, and strerror().
{ char *result; if (is_absolute_path(in)) result = strdup(in); else { static char cwdbuf[MAXPGPATH]; if (!cwdbuf[0]) { if (!getcwd(cwdbuf, sizeof(cwdbuf))) { fprintf(stderr, _("could not get current working directory: %s\n"), strerror(errno)); exit(2); } } result = malloc(strlen(cwdbuf) + strlen(in) + 2); sprintf(result, "%s/%s", cwdbuf, in); } canonicalize_path(result); return result; }
static void make_directory | ( | const char * | dir | ) | [static] |
Definition at line 1146 of file pg_regress.c.
References _, mkdir, progname, S_IRWXG, S_IRWXO, and strerror().
Referenced by convert_sourcefiles_in(), open_result_files(), and regression_main().
static void open_result_files | ( | void | ) | [static] |
Definition at line 1739 of file pg_regress.c.
References _, difffilename, directory_exists(), logfile, logfilename, make_directory(), outputdir, progname, snprintf(), and strerror().
Referenced by regression_main().
{ char file[MAXPGPATH]; FILE *difffile; /* create the log file (copy of running status output) */ snprintf(file, sizeof(file), "%s/regression.out", outputdir); logfilename = strdup(file); logfile = fopen(logfilename, "w"); if (!logfile) { fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"), progname, logfilename, strerror(errno)); exit(2); } /* create the diffs file as empty */ snprintf(file, sizeof(file), "%s/regression.diffs", outputdir); difffilename = strdup(file); difffile = fopen(difffilename, "w"); if (!difffile) { fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"), progname, difffilename, strerror(errno)); exit(2); } /* we don't keep the diffs file open continuously */ fclose(difffile); /* also create the output directory if not present */ snprintf(file, sizeof(file), "%s/results", outputdir); if (!directory_exists(file)) make_directory(file); }
static void psql_command | ( | const char * | database, | |
const char * | query, | |||
... | ||||
) | [static] |
Definition at line 888 of file pg_regress.c.
References _, MAXPGPATH, psqldir, snprintf(), system(), SYSTEMQUOTE, and vsnprintf().
Referenced by create_database(), create_role(), drop_database_if_exists(), and drop_role_if_exists().
{ char query_formatted[1024]; char query_escaped[2048]; char psql_cmd[MAXPGPATH + 2048]; va_list args; char *s; char *d; /* Generate the query with insertion of sprintf arguments */ va_start(args, query); vsnprintf(query_formatted, sizeof(query_formatted), query, args); va_end(args); /* Now escape any shell double-quote metacharacters */ d = query_escaped; for (s = query_formatted; *s; s++) { if (strchr("\\\"$`", *s)) *d++ = '\\'; *d++ = *s; } *d = '\0'; /* And now we can build and execute the shell command */ snprintf(psql_cmd, sizeof(psql_cmd), SYSTEMQUOTE "\"%s%spsql\" -X -c \"%s\" \"%s\"" SYSTEMQUOTE, psqldir ? psqldir : "", psqldir ? "/" : "", query_escaped, database); if (system(psql_cmd) != 0) { /* psql probably already reported the error */ fprintf(stderr, _("command failed: %s\n"), psql_cmd); exit(2); } }
int regression_main | ( | int | argc, | |
char * | argv[], | |||
init_function | ifunc, | |||
test_function | tfunc | |||
) |
Definition at line 1920 of file pg_regress.c.
References _, add_stringlist_item(), bindir, buf, create_database(), create_role(), datadir, debug, DEVNULL, difffilename, directory_exists(), dlpath, doputenv(), drop_database_if_exists(), drop_role_if_exists(), encoding, fail_count, fail_ignore_count, file_size(), free_stringlist(), get_progname(), getopt_long(), header(), help(), hostname, i, initialize_environment(), inputdir, INVALID_PID, launcher, logfile, logfilename, make_absolute_path(), make_directory(), makeprog, max_connections, MAXPGPATH, _stringlist::next, nolocale, NULL, open_result_files(), optarg, optind, outputdir, PG_TEXTDOMAIN, pg_usleep(), port, port_specified_by_user, postmaster_pid, postmaster_running, pretty_diff_opts, progname, psqldir, rmtree(), run_schedule(), run_single_test(), set_pglocale_pgservice(), SIGKILL, snprintf(), spawn_process(), split_to_stringlist(), stop_postmaster(), _stringlist::str, strerror(), success_count, system(), SYSTEMQUOTE, temp_config, temp_install, top_builddir, ULONGPID, unlink(), use_existing, and user.
Referenced by main().
{ static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {"dbname", required_argument, NULL, 1}, {"debug", no_argument, NULL, 2}, {"inputdir", required_argument, NULL, 3}, {"load-language", required_argument, NULL, 4}, {"max-connections", required_argument, NULL, 5}, {"encoding", required_argument, NULL, 6}, {"outputdir", required_argument, NULL, 7}, {"schedule", required_argument, NULL, 8}, {"temp-install", required_argument, NULL, 9}, {"no-locale", no_argument, NULL, 10}, {"top-builddir", required_argument, NULL, 11}, {"host", required_argument, NULL, 13}, {"port", required_argument, NULL, 14}, {"user", required_argument, NULL, 15}, {"psqldir", required_argument, NULL, 16}, {"dlpath", required_argument, NULL, 17}, {"create-role", required_argument, NULL, 18}, {"temp-config", required_argument, NULL, 19}, {"use-existing", no_argument, NULL, 20}, {"launcher", required_argument, NULL, 21}, {"load-extension", required_argument, NULL, 22}, {"extra-install", required_argument, NULL, 23}, {NULL, 0, NULL, 0} }; _stringlist *sl; int c; int i; int option_index; char buf[MAXPGPATH * 4]; char buf2[MAXPGPATH * 4]; progname = get_progname(argv[0]); set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_regress")); atexit(stop_postmaster); #ifndef HAVE_UNIX_SOCKETS /* no unix domain sockets available, so change default */ hostname = "localhost"; #endif /* * We call the initialization function here because that way we can set * default parameters and let them be overwritten by the commandline. */ ifunc(); if (getenv("PG_REGRESS_DIFF_OPTS")) pretty_diff_opts = getenv("PG_REGRESS_DIFF_OPTS"); while ((c = getopt_long(argc, argv, "hV", long_options, &option_index)) != -1) { switch (c) { case 'h': help(); exit(0); case 'V': puts("pg_regress (PostgreSQL) " PG_VERSION); exit(0); case 1: /* * If a default database was specified, we need to remove it * before we add the specified one. */ free_stringlist(&dblist); split_to_stringlist(strdup(optarg), ", ", &dblist); break; case 2: debug = true; break; case 3: inputdir = strdup(optarg); break; case 4: add_stringlist_item(&loadlanguage, optarg); break; case 5: max_connections = atoi(optarg); break; case 6: encoding = strdup(optarg); break; case 7: outputdir = strdup(optarg); break; case 8: add_stringlist_item(&schedulelist, optarg); break; case 9: temp_install = make_absolute_path(optarg); break; case 10: nolocale = true; break; case 11: top_builddir = strdup(optarg); break; case 13: hostname = strdup(optarg); break; case 14: port = atoi(optarg); port_specified_by_user = true; break; case 15: user = strdup(optarg); break; case 16: /* "--psqldir=" should mean to use PATH */ if (strlen(optarg)) psqldir = strdup(optarg); break; case 17: dlpath = strdup(optarg); break; case 18: split_to_stringlist(strdup(optarg), ", ", &extraroles); break; case 19: temp_config = strdup(optarg); break; case 20: use_existing = true; break; case 21: launcher = strdup(optarg); break; case 22: add_stringlist_item(&loadextension, optarg); break; case 23: add_stringlist_item(&extra_install, optarg); break; default: /* getopt_long already emitted a complaint */ fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"), progname); exit(2); } } /* * if we still have arguments, they are extra tests to run */ while (argc - optind >= 1) { add_stringlist_item(&extra_tests, argv[optind]); optind++; } if (temp_install && !port_specified_by_user) /* * To reduce chances of interference with parallel installations, use * a port number starting in the private range (49152-65535) * calculated from the version number. */ port = 0xC000 | (PG_VERSION_NUM & 0x3FFF); inputdir = make_absolute_path(inputdir); outputdir = make_absolute_path(outputdir); dlpath = make_absolute_path(dlpath); /* * Initialization */ open_result_files(); initialize_environment(); #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE) unlimit_core_size(); #endif if (temp_install) { FILE *pg_conf; _stringlist *sl; /* * Prepare the temp installation */ if (!top_builddir) { fprintf(stderr, _("--top-builddir must be specified when using --temp-install\n")); exit(2); } if (directory_exists(temp_install)) { header(_("removing existing temp installation")); rmtree(temp_install, true); } header(_("creating temporary installation")); /* make the temp install top directory */ make_directory(temp_install); /* and a directory for log files */ snprintf(buf, sizeof(buf), "%s/log", outputdir); if (!directory_exists(buf)) make_directory(buf); /* "make install" */ #ifndef WIN32_ONLY_COMPILER snprintf(buf, sizeof(buf), SYSTEMQUOTE "\"%s\" -C \"%s\" DESTDIR=\"%s/install\" install > \"%s/log/install.log\" 2>&1" SYSTEMQUOTE, makeprog, top_builddir, temp_install, outputdir); #else snprintf(buf, sizeof(buf), SYSTEMQUOTE "perl \"%s/src/tools/msvc/install.pl\" \"%s/install\" >\"%s/log/install.log\" 2>&1" SYSTEMQUOTE, top_builddir, temp_install, outputdir); #endif if (system(buf)) { fprintf(stderr, _("\n%s: installation failed\nExamine %s/log/install.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf); exit(2); } for (sl = extra_install; sl != NULL; sl = sl->next) { #ifndef WIN32_ONLY_COMPILER snprintf(buf, sizeof(buf), SYSTEMQUOTE "\"%s\" -C \"%s/%s\" DESTDIR=\"%s/install\" install >> \"%s/log/install.log\" 2>&1" SYSTEMQUOTE, makeprog, top_builddir, sl->str, temp_install, outputdir); #else fprintf(stderr, _("\n%s: --extra-install option not supported on this platform\n"), progname); exit(2); #endif if (system(buf)) { fprintf(stderr, _("\n%s: installation failed\nExamine %s/log/install.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf); exit(2); } } /* initdb */ header(_("initializing database system")); snprintf(buf, sizeof(buf), SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L \"%s\" --noclean --nosync%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE, bindir, temp_install, datadir, debug ? " --debug" : "", nolocale ? " --no-locale" : "", outputdir); if (system(buf)) { fprintf(stderr, _("\n%s: initdb failed\nExamine %s/log/initdb.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf); exit(2); } /* * Adjust the default postgresql.conf as needed for regression * testing. The user can specify a file to be appended; in any case we * set max_prepared_transactions to enable testing of prepared xacts. * (Note: to reduce the probability of unexpected shmmax failures, * don't set max_prepared_transactions any higher than actually needed * by the prepared_xacts regression test.) */ snprintf(buf, sizeof(buf), "%s/data/postgresql.conf", temp_install); pg_conf = fopen(buf, "a"); if (pg_conf == NULL) { fprintf(stderr, _("\n%s: could not open \"%s\" for adding extra config: %s\n"), progname, buf, strerror(errno)); exit(2); } fputs("\n# Configuration added by pg_regress\n\n", pg_conf); fputs("max_prepared_transactions = 2\n", pg_conf); if (temp_config != NULL) { FILE *extra_conf; char line_buf[1024]; extra_conf = fopen(temp_config, "r"); if (extra_conf == NULL) { fprintf(stderr, _("\n%s: could not open \"%s\" to read extra config: %s\n"), progname, temp_config, strerror(errno)); exit(2); } while (fgets(line_buf, sizeof(line_buf), extra_conf) != NULL) fputs(line_buf, pg_conf); fclose(extra_conf); } fclose(pg_conf); /* * Check if there is a postmaster running already. */ snprintf(buf2, sizeof(buf2), SYSTEMQUOTE "\"%s/psql\" -X postgres <%s 2>%s" SYSTEMQUOTE, bindir, DEVNULL, DEVNULL); for (i = 0; i < 16; i++) { if (system(buf2) == 0) { char s[16]; if (port_specified_by_user || i == 15) { fprintf(stderr, _("port %d apparently in use\n"), port); if (!port_specified_by_user) fprintf(stderr, _("%s: could not determine an available port\n"), progname); fprintf(stderr, _("Specify an unused port using the --port option or shut down any conflicting PostgreSQL servers.\n")); exit(2); } fprintf(stderr, _("port %d apparently in use, trying %d\n"), port, port + 1); port++; sprintf(s, "%d", port); doputenv("PGPORT", s); } else break; } /* * Start the temp postmaster */ header(_("starting postmaster")); snprintf(buf, sizeof(buf), SYSTEMQUOTE "\"%s/postgres\" -D \"%s/data\" -F%s -c \"listen_addresses=%s\" > \"%s/log/postmaster.log\" 2>&1" SYSTEMQUOTE, bindir, temp_install, debug ? " -d 5" : "", hostname ? hostname : "", outputdir); postmaster_pid = spawn_process(buf); if (postmaster_pid == INVALID_PID) { fprintf(stderr, _("\n%s: could not spawn postmaster: %s\n"), progname, strerror(errno)); exit(2); } /* * Wait till postmaster is able to accept connections (normally only a * second or so, but Cygwin is reportedly *much* slower). Don't wait * forever, however. */ for (i = 0; i < 60; i++) { /* Done if psql succeeds */ if (system(buf2) == 0) break; /* * Fail immediately if postmaster has exited */ #ifndef WIN32 if (kill(postmaster_pid, 0) != 0) #else if (WaitForSingleObject(postmaster_pid, 0) == WAIT_OBJECT_0) #endif { fprintf(stderr, _("\n%s: postmaster failed\nExamine %s/log/postmaster.log for the reason\n"), progname, outputdir); exit(2); } pg_usleep(1000000L); } if (i >= 60) { fprintf(stderr, _("\n%s: postmaster did not respond within 60 seconds\nExamine %s/log/postmaster.log for the reason\n"), progname, outputdir); /* * If we get here, the postmaster is probably wedged somewhere in * startup. Try to kill it ungracefully rather than leaving a * stuck postmaster that might interfere with subsequent test * attempts. */ #ifndef WIN32 if (kill(postmaster_pid, SIGKILL) != 0 && errno != ESRCH) fprintf(stderr, _("\n%s: could not kill failed postmaster: %s\n"), progname, strerror(errno)); #else if (TerminateProcess(postmaster_pid, 255) == 0) fprintf(stderr, _("\n%s: could not kill failed postmaster: error code %lu\n"), progname, GetLastError()); #endif exit(2); } postmaster_running = true; #ifdef WIN64 /* need a series of two casts to convert HANDLE without compiler warning */ #define ULONGPID(x) (unsigned long) (unsigned long long) (x) #else #define ULONGPID(x) (unsigned long) (x) #endif printf(_("running on port %d with PID %lu\n"), port, ULONGPID(postmaster_pid)); } else { /* * Using an existing installation, so may need to get rid of * pre-existing database(s) and role(s) */ if (!use_existing) { for (sl = dblist; sl; sl = sl->next) drop_database_if_exists(sl->str); for (sl = extraroles; sl; sl = sl->next) drop_role_if_exists(sl->str); } } /* * Create the test database(s) and role(s) */ if (!use_existing) { for (sl = dblist; sl; sl = sl->next) create_database(sl->str); for (sl = extraroles; sl; sl = sl->next) create_role(sl->str, dblist); } /* * Ready to run the tests */ header(_("running regression test queries")); for (sl = schedulelist; sl != NULL; sl = sl->next) { run_schedule(sl->str, tfunc); } for (sl = extra_tests; sl != NULL; sl = sl->next) { run_single_test(sl->str, tfunc); } /* * Shut down temp installation's postmaster */ if (temp_install) { header(_("shutting down postmaster")); stop_postmaster(); } fclose(logfile); /* * Emit nice-looking summary message */ if (fail_count == 0 && fail_ignore_count == 0) snprintf(buf, sizeof(buf), _(" All %d tests passed. "), success_count); else if (fail_count == 0) /* fail_count=0, fail_ignore_count>0 */ snprintf(buf, sizeof(buf), _(" %d of %d tests passed, %d failed test(s) ignored. "), success_count, success_count + fail_ignore_count, fail_ignore_count); else if (fail_ignore_count == 0) /* fail_count>0 && fail_ignore_count=0 */ snprintf(buf, sizeof(buf), _(" %d of %d tests failed. "), fail_count, success_count + fail_count); else /* fail_count>0 && fail_ignore_count>0 */ snprintf(buf, sizeof(buf), _(" %d of %d tests failed, %d of these failures ignored. "), fail_count + fail_ignore_count, success_count + fail_count + fail_ignore_count, fail_ignore_count); putchar('\n'); for (i = strlen(buf); i > 0; i--) putchar('='); printf("\n%s\n", buf); for (i = strlen(buf); i > 0; i--) putchar('='); putchar('\n'); putchar('\n'); if (file_size(difffilename) > 0) { printf(_("The differences that caused some tests to fail can be viewed in the\n" "file \"%s\". A copy of the test summary that you see\n" "above is saved in the file \"%s\".\n\n"), difffilename, logfilename); } else { unlink(difffilename); unlink(logfilename); } if (fail_count != 0) exit(1); return 0; }
void replace_string | ( | char * | string, | |
char * | replace, | |||
char * | replacement | |||
) |
Definition at line 388 of file pg_regress.c.
References free, NULL, and strlcpy().
Referenced by convert_sourcefiles_in(), ecpg_filter(), and ecpg_start_test().
static bool results_differ | ( | const char * | testname, | |
const char * | resultsfile, | |||
const char * | default_expectfile | |||
) | [static] |
Definition at line 1218 of file pg_regress.c.
References basic_diff_opts, difffilename, file_exists(), file_line_count(), free, get_alternative_expectfile(), get_expectfile(), i, MAXPGPATH, pretty_diff_opts, run_diff(), snprintf(), SYSTEMQUOTE, and unlink().
Referenced by run_schedule(), and run_single_test().
{ char expectfile[MAXPGPATH]; char diff[MAXPGPATH]; char cmd[MAXPGPATH * 3]; char best_expect_file[MAXPGPATH]; FILE *difffile; int best_line_count; int i; int l; const char *platform_expectfile; /* * We can pass either the resultsfile or the expectfile, they should have * the same type (filename.type) anyway. */ platform_expectfile = get_expectfile(testname, resultsfile); strcpy(expectfile, default_expectfile); if (platform_expectfile) { /* * Replace everything afer the last slash in expectfile with what the * platform_expectfile contains. */ char *p = strrchr(expectfile, '/'); if (p) strcpy(++p, platform_expectfile); } /* Name to use for temporary diff file */ snprintf(diff, sizeof(diff), "%s.diff", resultsfile); /* OK, run the diff */ snprintf(cmd, sizeof(cmd), SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE, basic_diff_opts, expectfile, resultsfile, diff); /* Is the diff file empty? */ if (run_diff(cmd, diff) == 0) { unlink(diff); return false; } /* There may be secondary comparison files that match better */ best_line_count = file_line_count(diff); strcpy(best_expect_file, expectfile); for (i = 0; i <= 9; i++) { char *alt_expectfile; alt_expectfile = get_alternative_expectfile(expectfile, i); if (!file_exists(alt_expectfile)) continue; snprintf(cmd, sizeof(cmd), SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE, basic_diff_opts, alt_expectfile, resultsfile, diff); if (run_diff(cmd, diff) == 0) { unlink(diff); return false; } l = file_line_count(diff); if (l < best_line_count) { /* This diff was a better match than the last one */ best_line_count = l; strcpy(best_expect_file, alt_expectfile); } free(alt_expectfile); } /* * fall back on the canonical results file if we haven't tried it yet and * haven't found a complete match yet. */ if (platform_expectfile) { snprintf(cmd, sizeof(cmd), SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE, basic_diff_opts, default_expectfile, resultsfile, diff); if (run_diff(cmd, diff) == 0) { /* No diff = no changes = good */ unlink(diff); return false; } l = file_line_count(diff); if (l < best_line_count) { /* This diff was a better match than the last one */ best_line_count = l; strcpy(best_expect_file, default_expectfile); } } /* * Use the best comparison file to generate the "pretty" diff, which we * append to the diffs summary file. */ snprintf(cmd, sizeof(cmd), SYSTEMQUOTE "diff %s \"%s\" \"%s\" >> \"%s\"" SYSTEMQUOTE, pretty_diff_opts, best_expect_file, resultsfile, difffilename); run_diff(cmd, difffilename); /* And append a separator */ difffile = fopen(difffilename, "a"); if (difffile) { fprintf(difffile, "\n======================================================================\n\n"); fclose(difffile); } unlink(diff); return true; }
static int run_diff | ( | const char * | cmd, | |
const char * | filename | |||
) | [static] |
Definition at line 1185 of file pg_regress.c.
References _, file_size(), system(), WEXITSTATUS, and WIFEXITED.
Referenced by results_differ().
{ int r; r = system(cmd); if (!WIFEXITED(r) || WEXITSTATUS(r) > 1) { fprintf(stderr, _("diff command failed with status %d: %s\n"), r, cmd); exit(2); } #ifdef WIN32 /* * On WIN32, if the 'diff' command cannot be found, system() returns 1, * but produces nothing to stdout, so we check for that here. */ if (WEXITSTATUS(r) == 1 && file_size(filename) <= 0) { fprintf(stderr, _("diff command not found: %s\n"), cmd); exit(2); } #endif return WEXITSTATUS(r); }
static void run_schedule | ( | const char * | schedule, | |
test_function | tfunc | |||
) | [static] |
Definition at line 1453 of file pg_regress.c.
References _, add_stringlist_item(), fail_count, fail_ignore_count, free_stringlist(), i, log_child_failure(), max_connections, MAX_PARALLEL_TESTS, _stringlist::next, NULL, PID_TYPE, progname, results_differ(), status(), status_end(), _stringlist::str, strerror(), success_count, test(), and wait_for_tests().
Referenced by regression_main().
{ #define MAX_PARALLEL_TESTS 100 char *tests[MAX_PARALLEL_TESTS]; _stringlist *resultfiles[MAX_PARALLEL_TESTS]; _stringlist *expectfiles[MAX_PARALLEL_TESTS]; _stringlist *tags[MAX_PARALLEL_TESTS]; PID_TYPE pids[MAX_PARALLEL_TESTS]; int statuses[MAX_PARALLEL_TESTS]; _stringlist *ignorelist = NULL; char scbuf[1024]; FILE *scf; int line_num = 0; memset(resultfiles, 0, sizeof(_stringlist *) * MAX_PARALLEL_TESTS); memset(expectfiles, 0, sizeof(_stringlist *) * MAX_PARALLEL_TESTS); memset(tags, 0, sizeof(_stringlist *) * MAX_PARALLEL_TESTS); scf = fopen(schedule, "r"); if (!scf) { fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), progname, schedule, strerror(errno)); exit(2); } while (fgets(scbuf, sizeof(scbuf), scf)) { char *test = NULL; char *c; int num_tests; bool inword; int i; line_num++; for (i = 0; i < MAX_PARALLEL_TESTS; i++) { if (resultfiles[i] == NULL) break; free_stringlist(&resultfiles[i]); free_stringlist(&expectfiles[i]); free_stringlist(&tags[i]); } /* strip trailing whitespace, especially the newline */ i = strlen(scbuf); while (i > 0 && isspace((unsigned char) scbuf[i - 1])) scbuf[--i] = '\0'; if (scbuf[0] == '\0' || scbuf[0] == '#') continue; if (strncmp(scbuf, "test: ", 6) == 0) test = scbuf + 6; else if (strncmp(scbuf, "ignore: ", 8) == 0) { c = scbuf + 8; while (*c && isspace((unsigned char) *c)) c++; add_stringlist_item(&ignorelist, c); /* * Note: ignore: lines do not run the test, they just say that * failure of this test when run later on is to be ignored. A bit * odd but that's how the shell-script version did it. */ continue; } else { fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n"), schedule, line_num, scbuf); exit(2); } num_tests = 0; inword = false; for (c = test; *c; c++) { if (isspace((unsigned char) *c)) { *c = '\0'; inword = false; } else if (!inword) { if (num_tests >= MAX_PARALLEL_TESTS) { /* can't print scbuf here, it's already been trashed */ fprintf(stderr, _("too many parallel tests in schedule file \"%s\", line %d\n"), schedule, line_num); exit(2); } tests[num_tests] = c; num_tests++; inword = true; } } if (num_tests == 0) { fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n"), schedule, line_num, scbuf); exit(2); } if (num_tests == 1) { status(_("test %-24s ... "), tests[0]); pids[0] = (tfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]); wait_for_tests(pids, statuses, NULL, 1); /* status line is finished below */ } else if (max_connections > 0 && max_connections < num_tests) { int oldest = 0; status(_("parallel group (%d tests, in groups of %d): "), num_tests, max_connections); for (i = 0; i < num_tests; i++) { if (i - oldest >= max_connections) { wait_for_tests(pids + oldest, statuses + oldest, tests + oldest, i - oldest); oldest = i; } pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]); } wait_for_tests(pids + oldest, statuses + oldest, tests + oldest, i - oldest); status_end(); } else { status(_("parallel group (%d tests): "), num_tests); for (i = 0; i < num_tests; i++) { pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]); } wait_for_tests(pids, statuses, tests, num_tests); status_end(); } /* Check results for all tests */ for (i = 0; i < num_tests; i++) { _stringlist *rl, *el, *tl; bool differ = false; if (num_tests > 1) status(_(" %-24s ... "), tests[i]); /* * Advance over all three lists simultaneously. * * Compare resultfiles[j] with expectfiles[j] always. Tags are * optional but if there are tags, the tag list has the same * length as the other two lists. */ for (rl = resultfiles[i], el = expectfiles[i], tl = tags[i]; rl != NULL; /* rl and el have the same length */ rl = rl->next, el = el->next) { bool newdiff; if (tl) tl = tl->next; /* tl has the same length as rl and el * if it exists */ newdiff = results_differ(tests[i], rl->str, el->str); if (newdiff && tl) { printf("%s ", tl->str); } differ |= newdiff; } if (differ) { bool ignore = false; _stringlist *sl; for (sl = ignorelist; sl != NULL; sl = sl->next) { if (strcmp(tests[i], sl->str) == 0) { ignore = true; break; } } if (ignore) { status(_("failed (ignored)")); fail_ignore_count++; } else { status(_("FAILED")); fail_count++; } } else { status(_("ok")); success_count++; } if (statuses[i] != 0) log_child_failure(statuses[i]); status_end(); } } fclose(scf); }
static void run_single_test | ( | const char * | test, | |
test_function | tfunc | |||
) | [static] |
Definition at line 1677 of file pg_regress.c.
References _, fail_count, log_child_failure(), _stringlist::next, NULL, PID_TYPE, results_differ(), status(), status_end(), _stringlist::str, success_count, and wait_for_tests().
Referenced by regression_main().
{ PID_TYPE pid; int exit_status; _stringlist *resultfiles = NULL; _stringlist *expectfiles = NULL; _stringlist *tags = NULL; _stringlist *rl, *el, *tl; bool differ = false; status(_("test %-24s ... "), test); pid = (tfunc) (test, &resultfiles, &expectfiles, &tags); wait_for_tests(&pid, &exit_status, NULL, 1); /* * Advance over all three lists simultaneously. * * Compare resultfiles[j] with expectfiles[j] always. Tags are optional * but if there are tags, the tag list has the same length as the other * two lists. */ for (rl = resultfiles, el = expectfiles, tl = tags; rl != NULL; /* rl and el have the same length */ rl = rl->next, el = el->next) { bool newdiff; if (tl) tl = tl->next; /* tl has the same length as rl and el if it * exists */ newdiff = results_differ(test, rl->str, el->str); if (newdiff && tl) { printf("%s ", tl->str); } differ |= newdiff; } if (differ) { status(_("FAILED")); fail_count++; } else { status(_("ok")); success_count++; } if (exit_status != 0) log_child_failure(exit_status); status_end(); }
PID_TYPE spawn_process | ( | const char * | cmdline | ) |
Definition at line 934 of file pg_regress.c.
References _, BOOL(), free, logfile, malloc, NULL, progname, shellprog, strerror(), and TRUE.
Referenced by ecpg_start_test(), isolation_start_test(), psql_start_test(), and regression_main().
{ #ifndef WIN32 pid_t pid; /* * Must flush I/O buffers before fork. Ideally we'd use fflush(NULL) here * ... does anyone still care about systems where that doesn't work? */ fflush(stdout); fflush(stderr); if (logfile) fflush(logfile); pid = fork(); if (pid == -1) { fprintf(stderr, _("%s: could not fork: %s\n"), progname, strerror(errno)); exit(2); } if (pid == 0) { /* * In child * * Instead of using system(), exec the shell directly, and tell it to * "exec" the command too. This saves two useless processes per * parallel test case. */ char *cmdline2 = malloc(strlen(cmdline) + 6); sprintf(cmdline2, "exec %s", cmdline); execl(shellprog, shellprog, "-c", cmdline2, (char *) NULL); fprintf(stderr, _("%s: could not exec \"%s\": %s\n"), progname, shellprog, strerror(errno)); _exit(1); /* not exit() here... */ } /* in parent */ return pid; #else char *cmdline2; BOOL b; STARTUPINFO si; PROCESS_INFORMATION pi; HANDLE origToken; HANDLE restrictedToken; SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; SID_AND_ATTRIBUTES dropSids[2]; __CreateRestrictedToken _CreateRestrictedToken = NULL; HANDLE Advapi32Handle; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); Advapi32Handle = LoadLibrary("ADVAPI32.DLL"); if (Advapi32Handle != NULL) { _CreateRestrictedToken = (__CreateRestrictedToken) GetProcAddress(Advapi32Handle, "CreateRestrictedToken"); } if (_CreateRestrictedToken == NULL) { if (Advapi32Handle != NULL) FreeLibrary(Advapi32Handle); fprintf(stderr, _("%s: cannot create restricted tokens on this platform\n"), progname); exit(2); } /* Open the current token to use as base for the restricted one */ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken)) { fprintf(stderr, _("could not open process token: error code %lu\n"), GetLastError()); exit(2); } /* Allocate list of SIDs to remove */ ZeroMemory(&dropSids, sizeof(dropSids)); if (!AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &dropSids[0].Sid) || !AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0, 0, &dropSids[1].Sid)) { fprintf(stderr, _("could not allocate SIDs: error code %lu\n"), GetLastError()); exit(2); } b = _CreateRestrictedToken(origToken, DISABLE_MAX_PRIVILEGE, sizeof(dropSids) / sizeof(dropSids[0]), dropSids, 0, NULL, 0, NULL, &restrictedToken); FreeSid(dropSids[1].Sid); FreeSid(dropSids[0].Sid); CloseHandle(origToken); FreeLibrary(Advapi32Handle); if (!b) { fprintf(stderr, _("could not create restricted token: error code %lu\n"), GetLastError()); exit(2); } cmdline2 = malloc(strlen(cmdline) + 8); sprintf(cmdline2, "cmd /c %s", cmdline); #ifndef __CYGWIN__ AddUserToTokenDacl(restrictedToken); #endif if (!CreateProcessAsUser(restrictedToken, NULL, cmdline2, NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) { fprintf(stderr, _("could not start process for \"%s\": error code %lu\n"), cmdline2, GetLastError()); exit(2); } free(cmdline2); ResumeThread(pi.hThread); CloseHandle(pi.hThread); return pi.hProcess; #endif }
static void split_to_stringlist | ( | const char * | s, | |
const char * | delim, | |||
_stringlist ** | listhead | |||
) | [static] |
Definition at line 216 of file pg_regress.c.
References add_stringlist_item(), free, and NULL.
Referenced by regression_main().
{ char *sc = strdup(s); char *token = strtok(sc, delim); while (token) { add_stringlist_item(listhead, token); token = strtok(NULL, delim); } free(sc); }
static void status | ( | const char * | fmt, | |
... | ||||
) | [static] |
Definition at line 250 of file pg_regress.c.
References logfile.
Referenced by _MasterEndParallelItem(), ClientAuthentication(), ecpg_execute(), ecpg_store_result(), exec_command(), GetMultiXactIdHintBits(), HandleSlashCmds(), heap_lock_tuple(), log_child_failure(), MultiXactIdGetUpdateXid(), mxid_to_string(), pgrowlocks(), pqFunctionCall2(), pqFunctionCall3(), PQresetPoll(), RecordNewMultiXact(), restore_toc_entry(), run_schedule(), run_single_test(), setQFout(), test_postmaster_connection(), and wait_for_tests().
static void status_end | ( | void | ) | [static] |
Definition at line 271 of file pg_regress.c.
References logfile.
Referenced by run_schedule(), and run_single_test().
static void stop_postmaster | ( | void | ) | [static] |
Definition at line 283 of file pg_regress.c.
References _, bindir, buf, MAXPGPATH, postmaster_running, progname, snprintf(), system(), SYSTEMQUOTE, and temp_install.
{ if (postmaster_running) { /* We use pg_ctl to issue the kill and wait for stop */ char buf[MAXPGPATH * 2]; int r; /* On Windows, system() seems not to force fflush, so... */ fflush(stdout); fflush(stderr); snprintf(buf, sizeof(buf), SYSTEMQUOTE "\"%s/pg_ctl\" stop -D \"%s/data\" -s -m fast" SYSTEMQUOTE, bindir, temp_install); r = system(buf); if (r != 0) { fprintf(stderr, _("\n%s: could not stop postmaster: exit code was %d\n"), progname, r); _exit(2); /* not exit(), that could be recursive */ } postmaster_running = false; } }
static bool string_matches_pattern | ( | const char * | str, | |
const char * | pattern | |||
) | [static] |
Definition at line 323 of file pg_regress.c.
Referenced by load_resultmap().
{ while (*str && *pattern) { if (*pattern == '.' && pattern[1] == '*') { pattern += 2; /* Trailing .* matches everything. */ if (*pattern == '\0') return true; /* * Otherwise, scan for a text position at which we can match the * rest of the pattern. */ while (*str) { /* * Optimization to prevent most recursion: don't recurse * unless first pattern char might match this text char. */ if (*str == *pattern || *pattern == '.') { if (string_matches_pattern(str, pattern)) return true; } str++; } /* * End of text with no match. */ return false; } else if (*pattern != '.' && *str != *pattern) { /* * Not the single-character wildcard and no explicit match? Then * time to quit... */ return false; } str++; pattern++; } if (*pattern == '\0') return true; /* end of pattern, so declare match */ /* End of input string. Do we have matching pattern remaining? */ while (*pattern == '.' && pattern[1] == '*') pattern += 2; if (*pattern == '\0') return true; /* end of pattern, so declare match */ return false; }
static void wait_for_tests | ( | PID_TYPE * | pids, | |
int * | statuses, | |||
char ** | names, | |||
int | num_tests | |||
) | [static] |
Definition at line 1354 of file pg_regress.c.
References _, FALSE, free, i, INVALID_PID, malloc, PID_TYPE, status(), and strerror().
Referenced by run_schedule(), and run_single_test().
{ int tests_left; int i; #ifdef WIN32 PID_TYPE *active_pids = malloc(num_tests * sizeof(PID_TYPE)); memcpy(active_pids, pids, num_tests * sizeof(PID_TYPE)); #endif tests_left = num_tests; while (tests_left > 0) { PID_TYPE p; #ifndef WIN32 int exit_status; p = wait(&exit_status); if (p == INVALID_PID) { fprintf(stderr, _("failed to wait for subprocesses: %s\n"), strerror(errno)); exit(2); } #else DWORD exit_status; int r; r = WaitForMultipleObjects(tests_left, active_pids, FALSE, INFINITE); if (r < WAIT_OBJECT_0 || r >= WAIT_OBJECT_0 + tests_left) { fprintf(stderr, _("failed to wait for subprocesses: error code %lu\n"), GetLastError()); exit(2); } p = active_pids[r - WAIT_OBJECT_0]; /* compact the active_pids array */ active_pids[r - WAIT_OBJECT_0] = active_pids[tests_left - 1]; #endif /* WIN32 */ for (i = 0; i < num_tests; i++) { if (p == pids[i]) { #ifdef WIN32 GetExitCodeProcess(pids[i], &exit_status); CloseHandle(pids[i]); #endif pids[i] = INVALID_PID; statuses[i] = (int) exit_status; if (names) status(" %s", names[i]); tests_left--; break; } } } #ifdef WIN32 free(active_pids); #endif }
const char* basic_diff_opts = "" |
Definition at line 74 of file pg_regress.c.
Referenced by results_differ().
char* bindir = PGBINDIR |
Definition at line 55 of file pg_regress.c.
Referenced by initialize_environment(), regression_main(), and stop_postmaster().
char* datadir = PGSHAREDIR |
Definition at line 57 of file pg_regress.c.
Referenced by fuzzy_open_file(), initialize_environment(), and regression_main().
_stringlist* dblist = NULL |
Definition at line 82 of file pg_regress.c.
Referenced by do_start_worker(), get_database_list(), isolation_init(), isolation_start_test(), psql_init(), psql_start_test(), and rebuild_database_list().
bool debug = false |
Definition at line 83 of file pg_regress.c.
char* difffilename [static] |
Definition at line 111 of file pg_regress.c.
Referenced by open_result_files(), regression_main(), and results_differ().
char* dlpath = PKGLIBDIR [static] |
Definition at line 102 of file pg_regress.c.
Referenced by convert_sourcefiles_in(), and regression_main().
char* encoding = NULL [static] |
Definition at line 91 of file pg_regress.c.
Referenced by create_database(), initialize_environment(), and regression_main().
_stringlist* extra_install = NULL [static] |
Definition at line 105 of file pg_regress.c.
_stringlist* extra_tests = NULL [static] |
Definition at line 93 of file pg_regress.c.
_stringlist* extraroles = NULL [static] |
Definition at line 104 of file pg_regress.c.
int fail_count = 0 [static] |
Definition at line 119 of file pg_regress.c.
Referenced by regression_main(), run_schedule(), and run_single_test().
int fail_ignore_count = 0 [static] |
Definition at line 120 of file pg_regress.c.
Referenced by regression_main(), and run_schedule().
char* host_platform = HOST_TUPLE |
Definition at line 58 of file pg_regress.c.
Referenced by load_resultmap().
char* hostname = NULL [static] |
Definition at line 99 of file pg_regress.c.
Referenced by initialize_environment(), and regression_main().
char* inputdir = "." |
Definition at line 84 of file pg_regress.c.
Referenced by convert_sourcefiles(), convert_sourcefiles_in(), ecpg_start_test(), isolation_start_test(), load_resultmap(), psql_start_test(), and regression_main().
char* launcher = NULL |
Definition at line 87 of file pg_regress.c.
Referenced by isolation_start_test(), psql_start_test(), and regression_main().
char* libdir = LIBDIR |
Definition at line 56 of file pg_regress.c.
Referenced by initialize_environment().
_stringlist* loadextension = NULL [static] |
Definition at line 89 of file pg_regress.c.
_stringlist* loadlanguage = NULL [static] |
Definition at line 88 of file pg_regress.c.
FILE* logfile [static] |
Definition at line 110 of file pg_regress.c.
Referenced by open_result_files(), regression_main(), spawn_process(), status(), status_end(), threadRun(), and write_syslogger_file().
char* logfilename [static] |
Definition at line 109 of file pg_regress.c.
Referenced by open_result_files(), and regression_main().
char* makeprog = MAKEPROG [static] |
Definition at line 61 of file pg_regress.c.
Referenced by regression_main().
int max_connections = 0 [static] |
Definition at line 90 of file pg_regress.c.
Referenced by regression_main(), and run_schedule().
Definition at line 97 of file pg_regress.c.
Referenced by create_database(), initialize_environment(), and regression_main().
char* outputdir = "." |
Definition at line 85 of file pg_regress.c.
Referenced by convert_sourcefiles(), convert_sourcefiles_in(), ecpg_start_test(), isolation_start_test(), open_result_files(), psql_start_test(), and regression_main().
int port = -1 [static] |
Definition at line 100 of file pg_regress.c.
Referenced by ECPGconnect(), inet_client_addr(), inet_client_port(), inet_server_addr(), inet_server_port(), initialize_environment(), log_disconnections(), main(), regression_main(), ServerLoop(), and ssl_client_serial().
bool port_specified_by_user = false [static] |
Definition at line 101 of file pg_regress.c.
Referenced by regression_main().
PID_TYPE postmaster_pid = INVALID_PID [static] |
Definition at line 115 of file pg_regress.c.
Referenced by regression_main().
bool postmaster_running = false [static] |
Definition at line 116 of file pg_regress.c.
Referenced by regression_main(), and stop_postmaster().
const char* pretty_diff_opts = "-C3" |
Definition at line 75 of file pg_regress.c.
Referenced by regression_main(), and results_differ().
const char* progname [static] |
Definition at line 108 of file pg_regress.c.
char* psqldir = PGBINDIR |
Definition at line 86 of file pg_regress.c.
Referenced by initialize_environment(), psql_command(), psql_start_test(), and regression_main().
_resultmap* resultmap = NULL [static] |
Definition at line 113 of file pg_regress.c.
_stringlist* schedulelist = NULL [static] |
Definition at line 92 of file pg_regress.c.
char* shellprog = SHELLPROG [static] |
Definition at line 65 of file pg_regress.c.
Referenced by spawn_process().
int success_count = 0 [static] |
Definition at line 118 of file pg_regress.c.
Referenced by regression_main(), run_schedule(), and run_single_test().
char* temp_config = NULL [static] |
Definition at line 95 of file pg_regress.c.
Referenced by regression_main().
char* temp_install = NULL [static] |
Definition at line 94 of file pg_regress.c.
Referenced by initialize_environment(), regression_main(), and stop_postmaster().
char* top_builddir = NULL [static] |
Definition at line 96 of file pg_regress.c.
Referenced by regression_main().
bool use_existing = false [static] |
Definition at line 98 of file pg_regress.c.
Referenced by regression_main().
char* user = NULL [static] |
Definition at line 103 of file pg_regress.c.
Referenced by conninfo_uri_parse_options(), exec_command(), initialize_environment(), main(), postgresAcquireSampleRowsFunc(), postgresAnalyzeForeignTable(), postgresBeginForeignModify(), postgresBeginForeignScan(), regression_main(), and usage().