2006-07-19 04:37:00 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* pg_regress --- regression test driver
|
|
|
|
*
|
|
|
|
* This is a C implementation of the previous shell script for running
|
2006-07-21 02:24:04 +02:00
|
|
|
* the regression tests, and should be mostly compatible with it.
|
2006-07-19 04:37:00 +02:00
|
|
|
* Initial author of C translation: Magnus Hagander
|
|
|
|
*
|
|
|
|
* This code is released under the terms of the PostgreSQL License.
|
|
|
|
*
|
2008-01-01 20:46:01 +01:00
|
|
|
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
2006-07-19 04:37:00 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
2008-02-20 23:44:16 +01:00
|
|
|
* $PostgreSQL: pgsql/src/test/regress/pg_regress.c,v 1.42 2008/02/20 22:44:16 tgl Exp $
|
2006-07-19 04:37:00 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
#include "pg_regress.h"
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/wait.h>
|
2006-07-19 07:21:57 +02:00
|
|
|
#include <signal.h>
|
2006-07-19 04:37:00 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
|
2007-01-05 17:17:55 +01:00
|
|
|
#ifdef HAVE_SYS_RESOURCE_H
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/resource.h>
|
|
|
|
#endif
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
#include "getopt_long.h"
|
2006-07-20 03:16:57 +02:00
|
|
|
#include "pg_config_paths.h"
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* for resultmap we need a list of pairs of strings */
|
|
|
|
typedef struct _resultmap
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char *test;
|
2007-06-12 13:07:34 +02:00
|
|
|
char *type;
|
2006-10-04 02:30:14 +02:00
|
|
|
char *resultfile;
|
2006-07-19 04:37:00 +02:00
|
|
|
struct _resultmap *next;
|
2006-10-04 02:30:14 +02:00
|
|
|
} _resultmap;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/*
|
2006-07-21 02:24:04 +02:00
|
|
|
* Values obtained from pg_config_paths.h and Makefile. The PG installation
|
|
|
|
* paths are only used in temp_install mode: we use these strings to find
|
|
|
|
* out where "make install" will put stuff under the temp_install directory.
|
|
|
|
* In non-temp_install mode, the only thing we need is the location of psql,
|
|
|
|
* which we expect to find in psqldir, or in the PATH if psqldir isn't given.
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
2007-11-15 22:14:46 +01:00
|
|
|
char *bindir = PGBINDIR;
|
|
|
|
char *libdir = LIBDIR;
|
|
|
|
char *datadir = PGSHAREDIR;
|
|
|
|
char *host_platform = HOST_TUPLE;
|
|
|
|
|
2007-02-08 16:28:58 +01:00
|
|
|
#ifndef WIN32_ONLY_COMPILER
|
2006-07-19 04:37:00 +02:00
|
|
|
static char *makeprog = MAKEPROG;
|
2007-02-08 16:28:58 +01:00
|
|
|
#endif
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-07-20 04:15:17 +02:00
|
|
|
#ifndef WIN32 /* not used in WIN32 case */
|
2006-07-19 19:02:59 +02:00
|
|
|
static char *shellprog = SHELLPROG;
|
2006-07-20 04:15:17 +02:00
|
|
|
#endif
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* currently we can use the same diff switches on all platforms */
|
2007-06-12 13:07:34 +02:00
|
|
|
const char *basic_diff_opts = "-w";
|
|
|
|
const char *pretty_diff_opts = "-w -C3";
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* options settable from command line */
|
2007-06-12 13:07:34 +02:00
|
|
|
_stringlist *dblist = NULL;
|
2007-11-15 22:14:46 +01:00
|
|
|
bool debug = false;
|
|
|
|
char *inputdir = ".";
|
|
|
|
char *outputdir = ".";
|
|
|
|
char *psqldir = NULL;
|
2006-07-19 04:37:00 +02:00
|
|
|
static _stringlist *loadlanguage = NULL;
|
2006-10-04 02:30:14 +02:00
|
|
|
static int max_connections = 0;
|
2006-07-19 04:37:00 +02:00
|
|
|
static char *encoding = NULL;
|
|
|
|
static _stringlist *schedulelist = NULL;
|
|
|
|
static _stringlist *extra_tests = NULL;
|
|
|
|
static char *temp_install = NULL;
|
2007-09-09 22:40:54 +02:00
|
|
|
static char *temp_config = NULL;
|
2006-07-19 04:37:00 +02:00
|
|
|
static char *top_builddir = NULL;
|
2006-10-04 02:30:14 +02:00
|
|
|
static int temp_port = 65432;
|
2006-07-19 04:37:00 +02:00
|
|
|
static bool nolocale = false;
|
|
|
|
static char *hostname = NULL;
|
2006-10-04 02:30:14 +02:00
|
|
|
static int port = -1;
|
2006-07-19 04:37:00 +02:00
|
|
|
static char *user = NULL;
|
2007-01-19 17:42:24 +01:00
|
|
|
static char *srcdir = NULL;
|
2007-06-12 13:07:34 +02:00
|
|
|
static _stringlist *extraroles = NULL;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* internal variables */
|
|
|
|
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;
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
static int success_count = 0;
|
|
|
|
static int fail_count = 0;
|
|
|
|
static int fail_ignore_count = 0;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2008-01-19 18:43:42 +01:00
|
|
|
static bool directory_exists(const char *dir);
|
|
|
|
static void make_directory(const char *dir);
|
2007-01-19 17:42:24 +01:00
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
static void
|
|
|
|
header(const char *fmt,...)
|
|
|
|
/* This extension allows gcc to check the format string for consistency with
|
|
|
|
the supplied arguments. */
|
|
|
|
__attribute__((format(printf, 1, 2)));
|
|
|
|
static void
|
|
|
|
status(const char *fmt,...)
|
|
|
|
/* This extension allows gcc to check the format string for consistency with
|
|
|
|
the supplied arguments. */
|
|
|
|
__attribute__((format(printf, 1, 2)));
|
|
|
|
static void
|
2006-10-04 02:30:14 +02:00
|
|
|
psql_command(const char *database, const char *query,...)
|
2006-07-19 04:37:00 +02:00
|
|
|
/* This extension allows gcc to check the format string for consistency with
|
|
|
|
the supplied arguments. */
|
|
|
|
__attribute__((format(printf, 2, 3)));
|
|
|
|
|
2007-02-08 16:28:58 +01:00
|
|
|
#ifdef WIN32
|
2007-11-15 22:14:46 +01:00
|
|
|
typedef BOOL(WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE);
|
2007-02-08 20:48:28 +01:00
|
|
|
|
|
|
|
/* Windows API define missing from MingW headers */
|
|
|
|
#define DISABLE_MAX_PRIVILEGE 0x1
|
2007-02-08 16:28:58 +01:00
|
|
|
#endif
|
|
|
|
|
2007-01-05 17:17:55 +01:00
|
|
|
/*
|
|
|
|
* allow core files if possible.
|
|
|
|
*/
|
|
|
|
#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
|
2007-11-15 22:14:46 +01:00
|
|
|
static void
|
2007-01-05 17:17:55 +01:00
|
|
|
unlimit_core_size(void)
|
|
|
|
{
|
|
|
|
struct rlimit lim;
|
2007-11-15 22:14:46 +01:00
|
|
|
|
|
|
|
getrlimit(RLIMIT_CORE, &lim);
|
2007-01-05 17:17:55 +01:00
|
|
|
if (lim.rlim_max == 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
2007-11-15 22:14:46 +01:00
|
|
|
_("%s: cannot set core size,: disallowed by hard limit.\n"),
|
2007-01-05 17:17:55 +01:00
|
|
|
progname);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (lim.rlim_max == RLIM_INFINITY || lim.rlim_cur < lim.rlim_max)
|
|
|
|
{
|
|
|
|
lim.rlim_cur = lim.rlim_max;
|
2007-11-15 22:14:46 +01:00
|
|
|
setrlimit(RLIMIT_CORE, &lim);
|
|
|
|
}
|
2007-01-05 17:17:55 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Add an item at the end of a stringlist.
|
|
|
|
*/
|
2007-06-12 13:07:34 +02:00
|
|
|
void
|
2006-10-04 02:30:14 +02:00
|
|
|
add_stringlist_item(_stringlist ** listhead, const char *str)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
_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)
|
2006-10-04 02:30:14 +02:00
|
|
|
/* skip */ ;
|
2006-07-19 04:37:00 +02:00
|
|
|
oldentry->next = newentry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
/*
|
|
|
|
* Free a stringlist.
|
|
|
|
*/
|
|
|
|
static void
|
2007-11-15 22:14:46 +01:00
|
|
|
free_stringlist(_stringlist ** listhead)
|
2007-06-12 13:07:34 +02:00
|
|
|
{
|
|
|
|
if (listhead == NULL || *listhead == NULL)
|
|
|
|
return;
|
|
|
|
if ((*listhead)->next != NULL)
|
|
|
|
free_stringlist(&((*listhead)->next));
|
|
|
|
free((*listhead)->str);
|
|
|
|
free(*listhead);
|
|
|
|
*listhead = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Split a delimited string into a stringlist
|
|
|
|
*/
|
|
|
|
static void
|
2007-11-15 22:14:46 +01:00
|
|
|
split_to_stringlist(const char *s, const char *delim, _stringlist ** listhead)
|
2007-06-12 13:07:34 +02:00
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
char *sc = strdup(s);
|
|
|
|
char *token = strtok(sc, delim);
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
while (token)
|
|
|
|
{
|
|
|
|
add_stringlist_item(listhead, token);
|
|
|
|
token = strtok(NULL, delim);
|
|
|
|
}
|
|
|
|
free(sc);
|
|
|
|
}
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
|
|
|
* Print a progress banner on stdout.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
header(const char *fmt,...)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Print "doing something ..." --- supplied text should not end with newline
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
status(const char *fmt,...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vfprintf(stdout, fmt, ap);
|
|
|
|
fflush(stdout);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
if (logfile)
|
|
|
|
{
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vfprintf(logfile, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Done "doing something ..."
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
status_end(void)
|
|
|
|
{
|
|
|
|
fprintf(stdout, "\n");
|
|
|
|
fflush(stdout);
|
|
|
|
if (logfile)
|
|
|
|
fprintf(logfile, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* shut down temp postmaster
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
stop_postmaster(void)
|
|
|
|
{
|
|
|
|
if (postmaster_running)
|
|
|
|
{
|
|
|
|
/* We use pg_ctl to issue the kill and wait for stop */
|
2006-10-04 02:30:14 +02:00
|
|
|
char buf[MAXPGPATH * 2];
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2006-07-20 05:30:58 +02:00
|
|
|
/* On Windows, system() seems not to force fflush, so... */
|
|
|
|
fflush(stdout);
|
|
|
|
fflush(stderr);
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
snprintf(buf, sizeof(buf),
|
2006-07-19 06:50:57 +02:00
|
|
|
SYSTEMQUOTE "\"%s/pg_ctl\" stop -D \"%s/data\" -s -m fast" SYSTEMQUOTE,
|
2006-07-19 04:37:00 +02:00
|
|
|
bindir, temp_install);
|
|
|
|
system(buf); /* ignore exit status */
|
|
|
|
postmaster_running = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Always exit through here, not through plain exit(), to ensure we make
|
|
|
|
* an effort to shut down a temp postmaster
|
|
|
|
*/
|
2007-06-12 13:07:34 +02:00
|
|
|
void
|
2006-07-19 04:37:00 +02:00
|
|
|
exit_nicely(int code)
|
|
|
|
{
|
|
|
|
stop_postmaster();
|
|
|
|
exit(code);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check whether string matches pattern
|
|
|
|
*
|
|
|
|
* In the original shell script, this function was implemented using expr(1),
|
|
|
|
* which provides basic regular expressions restricted to match starting at
|
|
|
|
* the string start (in conventional regex terms, there's an implicit "^"
|
|
|
|
* at the start of the pattern --- but no implicit "$" at the end).
|
|
|
|
*
|
|
|
|
* For now, we only support "." and ".*" as non-literal metacharacters,
|
|
|
|
* because that's all that anyone has found use for in resultmap. This
|
|
|
|
* code could be extended if more functionality is needed.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
string_matches_pattern(const char *str, const char *pattern)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2007-01-19 17:42:24 +01:00
|
|
|
/*
|
|
|
|
* Replace all occurances of a string in a string with a different string.
|
|
|
|
* NOTE: Assumes there is enough room in the target buffer!
|
|
|
|
*/
|
2007-06-12 13:07:34 +02:00
|
|
|
void
|
2007-01-19 17:42:24 +01:00
|
|
|
replace_string(char *string, char *replace, char *replacement)
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
char *ptr;
|
2007-01-19 17:42:24 +01:00
|
|
|
|
2007-11-15 22:14:46 +01:00
|
|
|
while ((ptr = strstr(string, replace)) != NULL)
|
2007-01-19 17:42:24 +01:00
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
char *dup = strdup(string);
|
2007-01-19 17:42:24 +01:00
|
|
|
|
2007-02-07 01:52:35 +01:00
|
|
|
strlcpy(string, dup, ptr - string + 1);
|
2007-01-19 17:42:24 +01:00
|
|
|
strcat(string, replacement);
|
|
|
|
strcat(string, dup + (ptr - string) + strlen(replace));
|
|
|
|
free(dup);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert *.source found in the "source" directory, replacing certain tokens
|
|
|
|
* in the file contents with their intended values, and put the resulting files
|
|
|
|
* in the "dest" directory, replacing the ".source" prefix in their names with
|
|
|
|
* the given suffix.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
convert_sourcefiles_in(char *source, char *dest, char *suffix)
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
char abs_srcdir[MAXPGPATH];
|
|
|
|
char abs_builddir[MAXPGPATH];
|
|
|
|
char testtablespace[MAXPGPATH];
|
|
|
|
char indir[MAXPGPATH];
|
2007-11-27 20:13:30 +01:00
|
|
|
struct stat st;
|
|
|
|
int ret;
|
2007-11-15 22:14:46 +01:00
|
|
|
char **name;
|
|
|
|
char **names;
|
|
|
|
int count = 0;
|
|
|
|
|
2007-01-19 17:42:24 +01:00
|
|
|
#ifdef WIN32
|
2007-11-15 22:14:46 +01:00
|
|
|
char *c;
|
2007-01-19 17:42:24 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!getcwd(abs_builddir, sizeof(abs_builddir)))
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: could not get current directory: %s\n"),
|
2007-11-15 22:14:46 +01:00
|
|
|
progname, strerror(errno));
|
2007-01-19 17:42:24 +01:00
|
|
|
exit_nicely(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2007-11-15 22:14:46 +01:00
|
|
|
* in a VPATH build, use the provided source directory; otherwise, use the
|
|
|
|
* current directory.
|
2007-01-19 17:42:24 +01:00
|
|
|
*/
|
|
|
|
if (srcdir)
|
2007-11-27 20:13:30 +01:00
|
|
|
strlcpy(abs_srcdir, srcdir, MAXPGPATH);
|
2007-01-19 17:42:24 +01:00
|
|
|
else
|
2007-11-27 20:13:30 +01:00
|
|
|
strlcpy(abs_srcdir, abs_builddir, MAXPGPATH);
|
2007-01-19 17:42:24 +01:00
|
|
|
|
|
|
|
snprintf(indir, MAXPGPATH, "%s/%s", abs_srcdir, source);
|
2007-11-27 20:13:30 +01:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2007-01-19 17:42:24 +01:00
|
|
|
names = pgfnames(indir);
|
|
|
|
if (!names)
|
|
|
|
/* Error logged in pgfnames */
|
|
|
|
exit_nicely(2);
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
/* in Win32, replace backslashes with forward slashes */
|
|
|
|
for (c = abs_builddir; *c; c++)
|
|
|
|
if (*c == '\\')
|
|
|
|
*c = '/';
|
|
|
|
for (c = abs_srcdir; *c; c++)
|
|
|
|
if (*c == '\\')
|
|
|
|
*c = '/';
|
|
|
|
#endif
|
|
|
|
|
|
|
|
snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", abs_builddir);
|
2008-01-19 18:43:42 +01:00
|
|
|
|
|
|
|
#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.
|
|
|
|
*/
|
2007-01-19 17:42:24 +01:00
|
|
|
if (directory_exists(testtablespace))
|
|
|
|
rmtree(testtablespace, true);
|
|
|
|
make_directory(testtablespace);
|
2008-01-19 18:43:42 +01:00
|
|
|
#endif
|
2007-01-19 17:42:24 +01:00
|
|
|
|
|
|
|
/* finally loop on each file and do the replacement */
|
|
|
|
for (name = names; *name; name++)
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
char srcfile[MAXPGPATH];
|
|
|
|
char destfile[MAXPGPATH];
|
|
|
|
char prefix[MAXPGPATH];
|
|
|
|
FILE *infile,
|
|
|
|
*outfile;
|
|
|
|
char line[1024];
|
2007-01-19 17:42:24 +01:00
|
|
|
|
|
|
|
/* 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", dest, 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_nicely(2);
|
|
|
|
}
|
|
|
|
outfile = fopen(destfile, "w");
|
|
|
|
if (!outfile)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
|
2007-11-15 22:14:46 +01:00
|
|
|
progname, destfile, strerror(errno));
|
2007-01-19 17:42:24 +01:00
|
|
|
exit_nicely(2);
|
|
|
|
}
|
|
|
|
while (fgets(line, sizeof(line), infile))
|
|
|
|
{
|
|
|
|
replace_string(line, "@abs_srcdir@", abs_srcdir);
|
|
|
|
replace_string(line, "@abs_builddir@", abs_builddir);
|
|
|
|
replace_string(line, "@testtablespace@", testtablespace);
|
|
|
|
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 --srcdir argument.
|
|
|
|
*/
|
|
|
|
if (count <= 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: no *.source files found in %s\n"),
|
|
|
|
progname, indir);
|
|
|
|
exit_nicely(2);
|
|
|
|
}
|
2007-11-15 22:14:46 +01:00
|
|
|
|
|
|
|
pgfnames_cleanup(names);
|
2007-01-19 17:42:24 +01:00
|
|
|
}
|
|
|
|
|
2007-01-19 22:21:13 +01:00
|
|
|
/* Create the .sql and .out files from the .source files, if any */
|
2007-01-19 17:42:24 +01:00
|
|
|
static void
|
|
|
|
convert_sourcefiles(void)
|
|
|
|
{
|
2007-11-27 20:13:30 +01:00
|
|
|
convert_sourcefiles_in("input", "sql", "sql");
|
|
|
|
convert_sourcefiles_in("output", "expected", "out");
|
2007-01-19 17:42:24 +01:00
|
|
|
}
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
|
|
|
* Scan resultmap file to find which platform-specific expected files to use.
|
|
|
|
*
|
|
|
|
* The format of each line of the file is
|
2006-10-04 02:30:14 +02:00
|
|
|
* testname/hostplatformpattern=substitutefile
|
2006-07-19 04:37:00 +02:00
|
|
|
* where the hostplatformpattern is evaluated per the rules of expr(1),
|
|
|
|
* namely, it is a standard regular expression with an implicit ^ at the start.
|
|
|
|
* (We currently support only a very limited subset of regular expressions,
|
|
|
|
* see string_matches_pattern() above.) What hostplatformpattern will be
|
2006-10-04 02:30:14 +02:00
|
|
|
* matched against is the config.guess output. (In the shell-script version,
|
2006-07-19 04:37:00 +02:00
|
|
|
* we also provided an indication of whether gcc or another compiler was in
|
|
|
|
* use, but that facility isn't used anymore.)
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
load_resultmap(void)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char buf[MAXPGPATH];
|
|
|
|
FILE *f;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* scan the file ... */
|
|
|
|
snprintf(buf, sizeof(buf), "%s/resultmap", inputdir);
|
2006-10-04 02:30:14 +02:00
|
|
|
f = fopen(buf, "r");
|
2006-07-19 04:37:00 +02:00
|
|
|
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_nicely(2);
|
|
|
|
}
|
2006-07-19 18:23:17 +02:00
|
|
|
|
|
|
|
while (fgets(buf, sizeof(buf), f))
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char *platform;
|
2007-06-12 13:07:34 +02:00
|
|
|
char *file_type;
|
2006-10-04 02:30:14 +02:00
|
|
|
char *expected;
|
|
|
|
int i;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* strip trailing whitespace, especially the newline */
|
|
|
|
i = strlen(buf);
|
2006-10-04 02:30:14 +02:00
|
|
|
while (i > 0 && isspace((unsigned char) buf[i - 1]))
|
2006-07-19 04:37:00 +02:00
|
|
|
buf[--i] = '\0';
|
|
|
|
|
|
|
|
/* parse out the line fields */
|
2007-06-12 13:07:34 +02:00
|
|
|
file_type = strchr(buf, ':');
|
|
|
|
if (!file_type)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
|
2007-11-15 22:14:46 +01:00
|
|
|
buf);
|
2007-06-12 13:07:34 +02:00
|
|
|
exit_nicely(2);
|
|
|
|
}
|
|
|
|
*file_type++ = '\0';
|
|
|
|
|
|
|
|
platform = strchr(file_type, ':');
|
2006-07-19 04:37:00 +02:00
|
|
|
if (!platform)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
|
|
|
|
buf);
|
|
|
|
exit_nicely(2);
|
|
|
|
}
|
|
|
|
*platform++ = '\0';
|
|
|
|
expected = strchr(platform, '=');
|
|
|
|
if (!expected)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
|
|
|
|
buf);
|
|
|
|
exit_nicely(2);
|
|
|
|
}
|
|
|
|
*expected++ = '\0';
|
|
|
|
|
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* 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.
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
|
|
|
if (string_matches_pattern(host_platform, platform))
|
|
|
|
{
|
|
|
|
_resultmap *entry = malloc(sizeof(_resultmap));
|
|
|
|
|
|
|
|
entry->test = strdup(buf);
|
2007-06-12 13:07:34 +02:00
|
|
|
entry->type = strdup(file_type);
|
2006-07-19 04:37:00 +02:00
|
|
|
entry->resultfile = strdup(expected);
|
|
|
|
entry->next = resultmap;
|
|
|
|
resultmap = entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
/*
|
|
|
|
* Check in resultmap if we should be looking at a different file
|
|
|
|
*/
|
|
|
|
static
|
2007-11-15 22:14:46 +01:00
|
|
|
const char *
|
|
|
|
get_expectfile(const char *testname, const char *file)
|
2007-06-12 13:07:34 +02:00
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
char *file_type;
|
2007-06-12 13:07:34 +02:00
|
|
|
_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;
|
|
|
|
}
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
|
|
|
* Handy subroutine for setting an environment variable "var" to "val"
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
doputenv(const char *var, const char *val)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char *s = malloc(strlen(var) + strlen(val) + 2);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
sprintf(s, "%s=%s", var, val);
|
|
|
|
putenv(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the environment variable "pathname", prepending "addval" to its
|
|
|
|
* old value (if any).
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
add_to_path(const char *pathname, char separator, const char *addval)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char *oldval = getenv(pathname);
|
|
|
|
char *newval;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
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);
|
2006-10-04 02:30:14 +02:00
|
|
|
sprintf(newval, "%s=%s%c%s", pathname, addval, separator, oldval);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
putenv(newval);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Prepare environment variables for running regression tests
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
initialize_environment(void)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char *tmp;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear out any non-C locale settings
|
|
|
|
*/
|
|
|
|
unsetenv("LC_COLLATE");
|
|
|
|
unsetenv("LC_CTYPE");
|
|
|
|
unsetenv("LC_MONETARY");
|
|
|
|
unsetenv("LC_MESSAGES");
|
|
|
|
unsetenv("LC_NUMERIC");
|
|
|
|
unsetenv("LC_TIME");
|
|
|
|
unsetenv("LC_ALL");
|
|
|
|
unsetenv("LANG");
|
|
|
|
unsetenv("LANGUAGE");
|
Wording cleanup for error messages. Also change can't -> cannot.
Standard English uses "may", "can", and "might" in different ways:
may - permission, "You may borrow my rake."
can - ability, "I can lift that log."
might - possibility, "It might rain today."
Unfortunately, in conversational English, their use is often mixed, as
in, "You may use this variable to do X", when in fact, "can" is a better
choice. Similarly, "It may crash" is better stated, "It might crash".
2007-02-01 20:10:30 +01:00
|
|
|
/* On Windows the default locale cannot be English, so force it */
|
2006-07-25 03:37:42 +02:00
|
|
|
#if defined(WIN32) || defined(__CYGWIN__)
|
2006-07-19 04:37:00 +02:00
|
|
|
putenv("LANG=en");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set multibyte 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");
|
|
|
|
|
|
|
|
if (temp_install)
|
|
|
|
{
|
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* 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.
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
|
|
|
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)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char s[16];
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
sprintf(s, "%d", port);
|
|
|
|
doputenv("PGPORT", s);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2006-07-21 02:24:04 +02:00
|
|
|
/* psql will be installed into temp-install bindir */
|
|
|
|
psqldir = bindir;
|
2007-11-15 22:14:46 +01:00
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
|
|
|
* Set up shared library paths to include the temp install.
|
|
|
|
*
|
|
|
|
* LD_LIBRARY_PATH covers many platforms. DYLD_LIBRARY_PATH works on
|
2006-10-04 02:30:14 +02:00
|
|
|
* Darwin, and maybe other Mach-based systems. LIBPATH is for AIX.
|
2006-09-19 17:36:08 +02:00
|
|
|
* Windows needs shared libraries in PATH (only those linked into
|
2006-10-04 02:30:14 +02:00
|
|
|
* executables, not dlopen'ed ones). Feel free to account for others
|
|
|
|
* as well.
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
|
|
|
add_to_path("LD_LIBRARY_PATH", ':', libdir);
|
|
|
|
add_to_path("DYLD_LIBRARY_PATH", ':', libdir);
|
2006-09-19 17:36:08 +02:00
|
|
|
add_to_path("LIBPATH", ':', libdir);
|
2006-07-25 03:37:42 +02:00
|
|
|
#if defined(WIN32) || defined(__CYGWIN__)
|
2006-07-19 04:37:00 +02:00
|
|
|
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)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char s[16];
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
sprintf(s, "%d", port);
|
|
|
|
doputenv("PGPORT", s);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
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"));
|
|
|
|
}
|
|
|
|
|
2007-01-19 17:42:24 +01:00
|
|
|
convert_sourcefiles();
|
2006-07-19 04:37:00 +02:00
|
|
|
load_resultmap();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Issue a command via psql, connecting to the specified database
|
|
|
|
*
|
|
|
|
* Since we use system(), this doesn't return until the operation finishes
|
|
|
|
*/
|
|
|
|
static void
|
2006-10-04 02:30:14 +02:00
|
|
|
psql_command(const char *database, const char *query,...)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char query_formatted[1024];
|
|
|
|
char query_escaped[2048];
|
|
|
|
char psql_cmd[MAXPGPATH + 2048];
|
|
|
|
va_list args;
|
|
|
|
char *s;
|
|
|
|
char *d;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* 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),
|
2006-07-21 02:24:04 +02:00
|
|
|
SYSTEMQUOTE "\"%s%spsql\" -X -c \"%s\" \"%s\"" SYSTEMQUOTE,
|
|
|
|
psqldir ? psqldir : "",
|
|
|
|
psqldir ? "/" : "",
|
|
|
|
query_escaped,
|
|
|
|
database);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
if (system(psql_cmd) != 0)
|
|
|
|
{
|
|
|
|
/* psql probably already reported the error */
|
|
|
|
fprintf(stderr, _("command failed: %s\n"), psql_cmd);
|
|
|
|
exit_nicely(2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Spawn a process to execute the given shell command; don't wait for it
|
|
|
|
*
|
2006-09-24 19:10:18 +02:00
|
|
|
* Returns the process ID (or HANDLE) so we can wait for it later
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
2007-06-12 13:07:34 +02:00
|
|
|
PID_TYPE
|
2006-07-19 04:37:00 +02:00
|
|
|
spawn_process(const char *cmdline)
|
|
|
|
{
|
|
|
|
#ifndef WIN32
|
2006-10-04 02:30:14 +02:00
|
|
|
pid_t pid;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* Must flush I/O buffers before fork. Ideally we'd use fflush(NULL) here
|
2006-07-19 04:37:00 +02:00
|
|
|
* ... 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_nicely(2);
|
|
|
|
}
|
|
|
|
if (pid == 0)
|
|
|
|
{
|
2006-07-19 19:02:59 +02:00
|
|
|
/*
|
|
|
|
* In child
|
|
|
|
*
|
2006-10-04 02:30:14 +02:00
|
|
|
* 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.
|
2006-07-19 19:02:59 +02:00
|
|
|
*/
|
2006-10-04 02:30:14 +02:00
|
|
|
char *cmdline2 = malloc(strlen(cmdline) + 6);
|
2006-07-19 19:02:59 +02:00
|
|
|
|
|
|
|
sprintf(cmdline2, "exec %s", cmdline);
|
2007-07-18 23:19:17 +02:00
|
|
|
execl(shellprog, shellprog, "-c", cmdline2, (char *) NULL);
|
2006-07-19 19:02:59 +02:00
|
|
|
fprintf(stderr, _("%s: could not exec \"%s\": %s\n"),
|
|
|
|
progname, shellprog, strerror(errno));
|
|
|
|
exit(1); /* not exit_nicely here... */
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
/* in parent */
|
|
|
|
return pid;
|
|
|
|
#else
|
2006-10-04 02:30:14 +02:00
|
|
|
char *cmdline2;
|
2007-11-15 22:14:46 +01:00
|
|
|
BOOL b;
|
2006-07-19 04:37:00 +02:00
|
|
|
STARTUPINFO si;
|
|
|
|
PROCESS_INFORMATION pi;
|
2007-11-15 22:14:46 +01:00
|
|
|
HANDLE origToken;
|
|
|
|
HANDLE restrictedToken;
|
2007-02-08 16:28:58 +01:00
|
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
|
|
|
|
SID_AND_ATTRIBUTES dropSids[2];
|
|
|
|
__CreateRestrictedToken _CreateRestrictedToken = NULL;
|
2007-11-15 22:14:46 +01:00
|
|
|
HANDLE Advapi32Handle;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
ZeroMemory(&si, sizeof(si));
|
|
|
|
si.cb = sizeof(si);
|
2007-11-15 22:14:46 +01:00
|
|
|
|
2007-02-08 16:28:58 +01:00
|
|
|
Advapi32Handle = LoadLibrary("ADVAPI32.DLL");
|
|
|
|
if (Advapi32Handle != NULL)
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
_CreateRestrictedToken = (__CreateRestrictedToken) GetProcAddress(Advapi32Handle, "CreateRestrictedToken");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_CreateRestrictedToken == NULL)
|
|
|
|
{
|
|
|
|
if (Advapi32Handle != NULL)
|
|
|
|
FreeLibrary(Advapi32Handle);
|
|
|
|
fprintf(stderr, "ERROR: cannot create restricted tokens on this platform\n");
|
|
|
|
exit_nicely(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: %lu\n", GetLastError());
|
|
|
|
exit_nicely(2);
|
|
|
|
}
|
2007-02-08 16:28:58 +01:00
|
|
|
|
|
|
|
/* Allocate list of SIDs to remove */
|
|
|
|
ZeroMemory(&dropSids, sizeof(dropSids));
|
|
|
|
if (!AllocateAndInitializeSid(&NtAuthority, 2,
|
2007-11-15 22:14:46 +01:00
|
|
|
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: %lu\n", GetLastError());
|
|
|
|
exit_nicely(2);
|
|
|
|
}
|
|
|
|
|
2007-02-08 16:28:58 +01:00
|
|
|
b = _CreateRestrictedToken(origToken,
|
2007-11-15 22:14:46 +01:00
|
|
|
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: %lu\n", GetLastError());
|
|
|
|
exit_nicely(2);
|
|
|
|
}
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
cmdline2 = malloc(strlen(cmdline) + 8);
|
|
|
|
sprintf(cmdline2, "cmd /c %s", cmdline);
|
|
|
|
|
2007-02-08 16:28:58 +01:00
|
|
|
if (!CreateProcessAsUser(restrictedToken, NULL, cmdline2, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2006-07-19 19:02:59 +02:00
|
|
|
fprintf(stderr, _("could not start process for \"%s\": %lu\n"),
|
2006-07-19 04:37:00 +02:00
|
|
|
cmdline2, GetLastError());
|
|
|
|
exit_nicely(2);
|
|
|
|
}
|
|
|
|
free(cmdline2);
|
|
|
|
|
|
|
|
CloseHandle(pi.hThread);
|
|
|
|
return pi.hProcess;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Count bytes in file
|
|
|
|
*/
|
|
|
|
static long
|
|
|
|
file_size(const char *file)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
long r;
|
|
|
|
FILE *f = fopen(file, "r");
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
if (!f)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
|
|
|
|
progname, file, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
fseek(f, 0, SEEK_END);
|
|
|
|
r = ftell(f);
|
|
|
|
fclose(f);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Count lines in file
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
file_line_count(const char *file)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
int c;
|
|
|
|
int l = 0;
|
|
|
|
FILE *f = fopen(file, "r");
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
if (!f)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
|
|
|
|
progname, file, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
while ((c = fgetc(f)) != EOF)
|
|
|
|
{
|
|
|
|
if (c == '\n')
|
|
|
|
l++;
|
|
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
file_exists(const char *file)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
FILE *f = fopen(file, "r");
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
if (!f)
|
|
|
|
return false;
|
|
|
|
fclose(f);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
directory_exists(const char *dir)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
if (stat(dir, &st) != 0)
|
|
|
|
return false;
|
|
|
|
if (st.st_mode & S_IFDIR)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a directory */
|
|
|
|
static void
|
|
|
|
make_directory(const char *dir)
|
|
|
|
{
|
2006-07-27 17:37:19 +02:00
|
|
|
if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
|
|
|
|
progname, dir, strerror(errno));
|
|
|
|
exit_nicely(2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
/*
|
|
|
|
* In: filename.ext, Return: filename_i.ext, where 0 < i <= 9
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
get_alternative_expectfile(const char *expectfile, int i)
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
char *last_dot;
|
|
|
|
int ssize = strlen(expectfile) + 2 + 1;
|
|
|
|
char *tmp = (char *) malloc(ssize);
|
|
|
|
char *s = (char *) malloc(ssize);
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
strcpy(tmp, expectfile);
|
2007-11-15 22:14:46 +01:00
|
|
|
last_dot = strrchr(tmp, '.');
|
2007-06-12 13:07:34 +02:00
|
|
|
if (!last_dot)
|
|
|
|
return NULL;
|
|
|
|
*last_dot = '\0';
|
2007-11-15 22:14:46 +01:00
|
|
|
snprintf(s, ssize, "%s_%d.%s", tmp, i, last_dot + 1);
|
2007-06-12 13:07:34 +02:00
|
|
|
free(tmp);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:25:30 +02:00
|
|
|
/*
|
2006-07-30 03:45:21 +02:00
|
|
|
* Run a "diff" command and also check that it didn't crash
|
2006-07-20 18:25:30 +02:00
|
|
|
*/
|
2006-07-30 03:45:21 +02:00
|
|
|
static int
|
|
|
|
run_diff(const char *cmd, const char *filename)
|
2006-07-20 18:25:30 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
int r;
|
2006-07-20 18:25:30 +02:00
|
|
|
|
|
|
|
r = system(cmd);
|
|
|
|
if (!WIFEXITED(r) || WEXITSTATUS(r) > 1)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("diff command failed with status %d: %s\n"), r, cmd);
|
|
|
|
exit_nicely(2);
|
|
|
|
}
|
2006-07-30 03:45:21 +02:00
|
|
|
#ifdef WIN32
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-07-30 03:45:21 +02:00
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* On WIN32, if the 'diff' command cannot be found, system() returns 1,
|
|
|
|
* but produces nothing to stdout, so we check for that here.
|
2006-07-30 03:45:21 +02:00
|
|
|
*/
|
|
|
|
if (WEXITSTATUS(r) == 1 && file_size(filename) <= 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("diff command not found: %s\n"), cmd);
|
|
|
|
exit_nicely(2);
|
|
|
|
}
|
|
|
|
#endif
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-07-30 03:45:21 +02:00
|
|
|
return WEXITSTATUS(r);
|
2006-07-20 18:25:30 +02:00
|
|
|
}
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
|
|
|
* Check the actual result file for the given test against expected results
|
|
|
|
*
|
|
|
|
* Returns true if different (failure), false if correct match found.
|
|
|
|
* In the true case, the diff is appended to the diffs file.
|
|
|
|
*/
|
|
|
|
static bool
|
2007-06-12 13:07:34 +02:00
|
|
|
results_differ(const char *testname, const char *resultsfile, const char *default_expectfile)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
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;
|
2007-06-12 13:07:34 +02:00
|
|
|
const char *platform_expectfile;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
/*
|
2007-11-15 22:14:46 +01:00
|
|
|
* We can pass either the resultsfile or the expectfile, they should have
|
|
|
|
* the same type (filename.type) anyway.
|
2007-06-12 13:07:34 +02:00
|
|
|
*/
|
|
|
|
platform_expectfile = get_expectfile(testname, resultsfile);
|
|
|
|
|
|
|
|
strcpy(expectfile, default_expectfile);
|
2007-11-15 22:14:46 +01:00
|
|
|
if (platform_expectfile)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2007-06-12 13:07:34 +02:00
|
|
|
/*
|
|
|
|
* Replace everything afer the last slash in expectfile with what the
|
|
|
|
* platform_expectfile contains.
|
|
|
|
*/
|
2007-11-15 22:14:46 +01:00
|
|
|
char *p = strrchr(expectfile, '/');
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
if (p)
|
|
|
|
strcpy(++p, platform_expectfile);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Name to use for temporary diff file */
|
2007-06-12 13:07:34 +02:00
|
|
|
snprintf(diff, sizeof(diff), "%s.diff", resultsfile);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* OK, run the diff */
|
|
|
|
snprintf(cmd, sizeof(cmd),
|
2006-07-20 18:25:30 +02:00
|
|
|
SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE,
|
2006-07-19 04:37:00 +02:00
|
|
|
basic_diff_opts, expectfile, resultsfile, diff);
|
|
|
|
|
|
|
|
/* Is the diff file empty? */
|
2006-07-30 03:45:21 +02:00
|
|
|
if (run_diff(cmd, diff) == 0)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
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++)
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
char *alt_expectfile;
|
2007-06-12 13:07:34 +02:00
|
|
|
|
|
|
|
alt_expectfile = get_alternative_expectfile(expectfile, i);
|
|
|
|
if (!file_exists(alt_expectfile))
|
2006-07-19 04:37:00 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
snprintf(cmd, sizeof(cmd),
|
2006-07-20 18:25:30 +02:00
|
|
|
SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE,
|
2007-06-12 13:07:34 +02:00
|
|
|
basic_diff_opts, alt_expectfile, resultsfile, diff);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2006-07-30 03:45:21 +02:00
|
|
|
if (run_diff(cmd, diff) == 0)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
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;
|
2007-06-12 13:07:34 +02:00
|
|
|
strcpy(best_expect_file, alt_expectfile);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
2007-06-12 13:07:34 +02:00
|
|
|
free(alt_expectfile);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
/*
|
|
|
|
* fall back on the canonical results file if we haven't tried it yet and
|
|
|
|
* haven't found a complete match yet.
|
2006-08-01 16:56:29 +02:00
|
|
|
*/
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
if (platform_expectfile)
|
2006-08-01 16:56:29 +02:00
|
|
|
{
|
|
|
|
snprintf(cmd, sizeof(cmd),
|
|
|
|
SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE,
|
2007-06-12 13:07:34 +02:00
|
|
|
basic_diff_opts, default_expectfile, resultsfile, diff);
|
2006-08-01 16:56:29 +02:00
|
|
|
|
|
|
|
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;
|
2007-06-12 13:07:34 +02:00
|
|
|
strcpy(best_expect_file, default_expectfile);
|
2006-08-01 16:56:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* Use the best comparison file to generate the "pretty" diff, which we
|
|
|
|
* append to the diffs summary file.
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
|
|
|
snprintf(cmd, sizeof(cmd),
|
2006-07-20 18:25:30 +02:00
|
|
|
SYSTEMQUOTE "diff %s \"%s\" \"%s\" >> \"%s\"" SYSTEMQUOTE,
|
2006-07-19 04:37:00 +02:00
|
|
|
pretty_diff_opts, best_expect_file, resultsfile, difffilename);
|
2006-07-30 03:45:21 +02:00
|
|
|
run_diff(cmd, difffilename);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* And append a separator */
|
|
|
|
difffile = fopen(difffilename, "a");
|
|
|
|
if (difffile)
|
|
|
|
{
|
|
|
|
fprintf(difffile,
|
|
|
|
"\n======================================================================\n\n");
|
|
|
|
fclose(difffile);
|
|
|
|
}
|
|
|
|
|
|
|
|
unlink(diff);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Wait for specified subprocesses to finish
|
2006-07-20 04:10:00 +02:00
|
|
|
*
|
|
|
|
* If names isn't NULL, report each subprocess as it finishes
|
|
|
|
*
|
|
|
|
* Note: it's OK to scribble on the pids array, but not on the names array
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
|
|
|
static void
|
2006-10-04 02:30:14 +02:00
|
|
|
wait_for_tests(PID_TYPE * pids, char **names, int num_tests)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
int tests_left;
|
|
|
|
int i;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2006-07-20 04:10:00 +02:00
|
|
|
#ifdef WIN32
|
2006-10-04 02:30:14 +02:00
|
|
|
PID_TYPE *active_pids = malloc(num_tests * sizeof(PID_TYPE));
|
2006-07-20 04:10:00 +02:00
|
|
|
|
|
|
|
memcpy(active_pids, pids, num_tests * sizeof(PID_TYPE));
|
|
|
|
#endif
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
tests_left = num_tests;
|
|
|
|
while (tests_left > 0)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
PID_TYPE p;
|
2006-07-20 04:10:00 +02:00
|
|
|
|
|
|
|
#ifndef WIN32
|
|
|
|
p = wait(NULL);
|
|
|
|
|
|
|
|
if (p == INVALID_PID)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("failed to wait for subprocesses: %s\n"),
|
|
|
|
strerror(errno));
|
|
|
|
exit_nicely(2);
|
|
|
|
}
|
|
|
|
#else
|
2006-10-04 02:30:14 +02:00
|
|
|
int r;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2006-07-20 04:10:00 +02:00
|
|
|
r = WaitForMultipleObjects(tests_left, active_pids, FALSE, INFINITE);
|
|
|
|
if (r < WAIT_OBJECT_0 || r >= WAIT_OBJECT_0 + tests_left)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2006-07-20 04:10:00 +02:00
|
|
|
fprintf(stderr, _("failed to wait for subprocesses: %lu\n"),
|
|
|
|
GetLastError());
|
2006-07-19 04:37:00 +02:00
|
|
|
exit_nicely(2);
|
|
|
|
}
|
2006-07-20 04:10:00 +02:00
|
|
|
p = active_pids[r - WAIT_OBJECT_0];
|
|
|
|
/* compact the active_pids array */
|
|
|
|
active_pids[r - WAIT_OBJECT_0] = active_pids[tests_left - 1];
|
2006-10-04 02:30:14 +02:00
|
|
|
#endif /* WIN32 */
|
2006-07-20 04:10:00 +02:00
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
for (i = 0; i < num_tests; i++)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
if (p == pids[i])
|
|
|
|
{
|
2006-07-20 04:10:00 +02:00
|
|
|
#ifdef WIN32
|
|
|
|
CloseHandle(pids[i]);
|
|
|
|
#endif
|
|
|
|
pids[i] = INVALID_PID;
|
|
|
|
if (names)
|
|
|
|
status(" %s", names[i]);
|
2006-07-19 04:37:00 +02:00
|
|
|
tests_left--;
|
2006-07-20 04:10:00 +02:00
|
|
|
break;
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-20 04:10:00 +02:00
|
|
|
#ifdef WIN32
|
|
|
|
free(active_pids);
|
2006-07-19 04:37:00 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Run all the tests specified in one schedule file
|
|
|
|
*/
|
|
|
|
static void
|
2007-06-12 13:07:34 +02:00
|
|
|
run_schedule(const char *schedule, test_function tfunc)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
#define MAX_PARALLEL_TESTS 100
|
2006-10-04 02:30:14 +02:00
|
|
|
char *tests[MAX_PARALLEL_TESTS];
|
2007-06-12 13:07:34 +02:00
|
|
|
_stringlist *resultfiles[MAX_PARALLEL_TESTS];
|
|
|
|
_stringlist *expectfiles[MAX_PARALLEL_TESTS];
|
|
|
|
_stringlist *tags[MAX_PARALLEL_TESTS];
|
2006-10-04 02:30:14 +02:00
|
|
|
PID_TYPE pids[MAX_PARALLEL_TESTS];
|
2006-07-19 04:37:00 +02:00
|
|
|
_stringlist *ignorelist = NULL;
|
2006-10-04 02:30:14 +02:00
|
|
|
char scbuf[1024];
|
|
|
|
FILE *scf;
|
|
|
|
int line_num = 0;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2007-11-15 22:14:46 +01:00
|
|
|
memset(resultfiles, 0, sizeof(_stringlist *) * MAX_PARALLEL_TESTS);
|
|
|
|
memset(expectfiles, 0, sizeof(_stringlist *) * MAX_PARALLEL_TESTS);
|
|
|
|
memset(tags, 0, sizeof(_stringlist *) * MAX_PARALLEL_TESTS);
|
2007-06-12 13:07:34 +02:00
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
scf = fopen(schedule, "r");
|
|
|
|
if (!scf)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
|
|
|
|
progname, schedule, strerror(errno));
|
|
|
|
exit_nicely(2);
|
|
|
|
}
|
|
|
|
|
2006-07-19 18:23:17 +02:00
|
|
|
while (fgets(scbuf, sizeof(scbuf), scf))
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char *test = NULL;
|
|
|
|
char *c;
|
|
|
|
int num_tests;
|
|
|
|
bool inword;
|
|
|
|
int i;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
line_num++;
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/* strip trailing whitespace, especially the newline */
|
|
|
|
i = strlen(scbuf);
|
2006-10-04 02:30:14 +02:00
|
|
|
while (i > 0 && isspace((unsigned char) scbuf[i - 1]))
|
2006-07-19 04:37:00 +02:00
|
|
|
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);
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
|
|
|
* Note: ignore: lines do not run the test, they just say that
|
2006-10-04 02:30:14 +02:00
|
|
|
* 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.
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n"),
|
|
|
|
schedule, line_num, scbuf);
|
|
|
|
exit_nicely(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_nicely(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_nicely(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_tests == 1)
|
|
|
|
{
|
|
|
|
status(_("test %-20s ... "), tests[0]);
|
2007-11-15 22:14:46 +01:00
|
|
|
pids[0] = (tfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]);
|
2006-07-20 04:10:00 +02:00
|
|
|
wait_for_tests(pids, NULL, 1);
|
2006-07-19 04:37:00 +02:00
|
|
|
/* status line is finished below */
|
|
|
|
}
|
|
|
|
else if (max_connections > 0 && max_connections < num_tests)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
int oldest = 0;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2006-07-20 04:10:00 +02:00
|
|
|
wait_for_tests(pids + oldest, tests + oldest, i - oldest);
|
2006-07-19 04:37:00 +02:00
|
|
|
oldest = i;
|
|
|
|
}
|
2007-11-15 22:14:46 +01:00
|
|
|
pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
2006-07-20 04:10:00 +02:00
|
|
|
wait_for_tests(pids + oldest, tests + oldest, i - oldest);
|
2006-07-19 04:37:00 +02:00
|
|
|
status_end();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
status(_("parallel group (%d tests): "), num_tests);
|
|
|
|
for (i = 0; i < num_tests; i++)
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
2006-07-20 04:10:00 +02:00
|
|
|
wait_for_tests(pids, tests, num_tests);
|
2006-07-19 04:37:00 +02:00
|
|
|
status_end();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check results for all tests */
|
|
|
|
for (i = 0; i < num_tests; i++)
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
_stringlist *rl,
|
|
|
|
*el,
|
|
|
|
*tl;
|
|
|
|
bool differ = false;
|
2007-06-12 13:07:34 +02:00
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
if (num_tests > 1)
|
|
|
|
status(_(" %-20s ... "), tests[i]);
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
/*
|
|
|
|
* Advance over all three lists simultaneously.
|
|
|
|
*
|
2007-11-15 22:14:46 +01:00
|
|
|
* 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.
|
2007-06-12 13:07:34 +02:00
|
|
|
*/
|
|
|
|
for (rl = resultfiles[i], el = expectfiles[i], tl = tags[i];
|
2007-11-15 22:14:46 +01:00
|
|
|
rl != NULL; /* rl and el have the same length */
|
|
|
|
rl = rl->next, el = el->next)
|
2007-06-12 13:07:34 +02:00
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
bool newdiff;
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
if (tl)
|
2007-11-15 22:14:46 +01:00
|
|
|
tl = tl->next; /* tl has the same lengt has rl and el
|
|
|
|
* if it exists */
|
2007-06-12 13:07:34 +02:00
|
|
|
|
|
|
|
newdiff = results_differ(tests[i], rl->str, el->str);
|
2007-11-15 22:14:46 +01:00
|
|
|
if (newdiff && tl)
|
2007-06-12 13:07:34 +02:00
|
|
|
{
|
|
|
|
printf("%s ", tl->str);
|
|
|
|
}
|
|
|
|
differ |= newdiff;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (differ)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
bool ignore = false;
|
2006-07-19 04:37:00 +02:00
|
|
|
_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++;
|
|
|
|
}
|
|
|
|
|
|
|
|
status_end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(scf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Run a single test
|
|
|
|
*/
|
|
|
|
static void
|
2007-06-12 13:07:34 +02:00
|
|
|
run_single_test(const char *test, test_function tfunc)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
PID_TYPE pid;
|
2007-06-12 15:26:45 +02:00
|
|
|
_stringlist *resultfiles = NULL;
|
|
|
|
_stringlist *expectfiles = NULL;
|
|
|
|
_stringlist *tags = NULL;
|
2007-11-15 22:14:46 +01:00
|
|
|
_stringlist *rl,
|
|
|
|
*el,
|
|
|
|
*tl;
|
2007-06-12 13:07:34 +02:00
|
|
|
bool differ = false;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
status(_("test %-20s ... "), test);
|
2007-11-15 22:14:46 +01:00
|
|
|
pid = (tfunc) (test, &resultfiles, &expectfiles, &tags);
|
2006-07-20 04:10:00 +02:00
|
|
|
wait_for_tests(&pid, NULL, 1);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
/*
|
|
|
|
* Advance over all three lists simultaneously.
|
|
|
|
*
|
2007-11-15 22:14:46 +01:00
|
|
|
* 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.
|
2007-06-12 13:07:34 +02:00
|
|
|
*/
|
|
|
|
for (rl = resultfiles, el = expectfiles, tl = tags;
|
2007-11-15 22:14:46 +01:00
|
|
|
rl != NULL; /* rl and el have the same length */
|
|
|
|
rl = rl->next, el = el->next)
|
2007-06-12 13:07:34 +02:00
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
bool newdiff;
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
if (tl)
|
2007-11-15 22:14:46 +01:00
|
|
|
tl = tl->next; /* tl has the same lengt has rl and el if it
|
|
|
|
* exists */
|
2007-06-12 13:07:34 +02:00
|
|
|
|
|
|
|
newdiff = results_differ(test, rl->str, el->str);
|
2007-11-15 22:14:46 +01:00
|
|
|
if (newdiff && tl)
|
2007-06-12 13:07:34 +02:00
|
|
|
{
|
|
|
|
printf("%s ", tl->str);
|
|
|
|
}
|
|
|
|
differ |= newdiff;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (differ)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
status(_("FAILED"));
|
|
|
|
fail_count++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
status(_("ok"));
|
|
|
|
success_count++;
|
|
|
|
}
|
|
|
|
status_end();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create the summary-output files (making them empty if already existing)
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
open_result_files(void)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char file[MAXPGPATH];
|
|
|
|
FILE *difffile;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* 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_nicely(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_nicely(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);
|
|
|
|
}
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
static void
|
|
|
|
drop_database_if_exists(const char *dbname)
|
|
|
|
{
|
|
|
|
header(_("dropping database \"%s\""), dbname);
|
|
|
|
psql_command("postgres", "DROP DATABASE IF EXISTS \"%s\"", dbname);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
create_database(const char *dbname)
|
|
|
|
{
|
|
|
|
_stringlist *sl;
|
2007-11-15 22:14:46 +01:00
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
/*
|
|
|
|
* 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'", dbname, encoding);
|
|
|
|
else
|
|
|
|
psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0", dbname);
|
2007-11-15 22:14:46 +01:00
|
|
|
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);
|
2007-06-12 13:07:34 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Install any requested procedural languages
|
|
|
|
*/
|
|
|
|
for (sl = loadlanguage; sl != NULL; sl = sl->next)
|
|
|
|
{
|
|
|
|
header(_("installing %s"), sl->str);
|
|
|
|
psql_command(dbname, "CREATE LANGUAGE \"%s\"", sl->str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
drop_role_if_exists(const char *rolename)
|
|
|
|
{
|
|
|
|
header(_("dropping role \"%s\""), rolename);
|
|
|
|
psql_command("postgres", "DROP ROLE IF EXISTS \"%s\"", rolename);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2007-11-15 22:14:46 +01:00
|
|
|
create_role(const char *rolename, const _stringlist * granted_dbs)
|
2007-06-12 13:07:34 +02:00
|
|
|
{
|
|
|
|
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\"",
|
2007-11-15 22:14:46 +01:00
|
|
|
granted_dbs->str, rolename);
|
2007-06-12 13:07:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
static void
|
|
|
|
help(void)
|
|
|
|
{
|
|
|
|
printf(_("PostgreSQL regression test driver\n"));
|
|
|
|
printf(_("\n"));
|
|
|
|
printf(_("Usage: %s [options...] [extra tests...]\n"), progname);
|
|
|
|
printf(_("\n"));
|
|
|
|
printf(_("Options:\n"));
|
|
|
|
printf(_(" --dbname=DB use database DB (default \"regression\")\n"));
|
|
|
|
printf(_(" --debug turn on debug mode in programs that are run\n"));
|
|
|
|
printf(_(" --inputdir=DIR take input files from DIR (default \".\")\n"));
|
|
|
|
printf(_(" --load-language=lang load the named language before running the\n"));
|
|
|
|
printf(_(" tests; can appear multiple times\n"));
|
2007-06-12 13:07:34 +02:00
|
|
|
printf(_(" --create-role=ROLE create the specified role before testing\n"));
|
2006-07-19 04:37:00 +02:00
|
|
|
printf(_(" --max-connections=N maximum number of concurrent connections\n"));
|
|
|
|
printf(_(" (default is 0 meaning unlimited)\n"));
|
|
|
|
printf(_(" --multibyte=ENCODING use ENCODING as the multibyte encoding\n"));
|
|
|
|
printf(_(" --outputdir=DIR place output files in DIR (default \".\")\n"));
|
|
|
|
printf(_(" --schedule=FILE use test ordering schedule from FILE\n"));
|
Wording cleanup for error messages. Also change can't -> cannot.
Standard English uses "may", "can", and "might" in different ways:
may - permission, "You may borrow my rake."
can - ability, "I can lift that log."
might - possibility, "It might rain today."
Unfortunately, in conversational English, their use is often mixed, as
in, "You may use this variable to do X", when in fact, "can" is a better
choice. Similarly, "It may crash" is better stated, "It might crash".
2007-02-01 20:10:30 +01:00
|
|
|
printf(_(" (can be used multiple times to concatenate)\n"));
|
2007-01-19 17:42:24 +01:00
|
|
|
printf(_(" --srcdir=DIR absolute path to source directory (for VPATH builds)\n"));
|
2006-07-19 04:37:00 +02:00
|
|
|
printf(_(" --temp-install=DIR create a temporary installation in DIR\n"));
|
|
|
|
printf(_("\n"));
|
|
|
|
printf(_("Options for \"temp-install\" mode:\n"));
|
2007-09-09 22:40:54 +02:00
|
|
|
printf(_(" --no-locale use C locale\n"));
|
2006-07-19 04:37:00 +02:00
|
|
|
printf(_(" --top-builddir=DIR (relative) path to top level build directory\n"));
|
|
|
|
printf(_(" --temp-port=PORT port number to start temp postmaster on\n"));
|
2007-09-09 22:40:54 +02:00
|
|
|
printf(_(" --temp-config=PATH append contents of PATH to temporary config\n"));
|
2006-07-19 04:37:00 +02:00
|
|
|
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"));
|
2006-07-21 02:24:04 +02:00
|
|
|
printf(_(" --psqldir=DIR use psql in DIR (default: find in PATH)\n"));
|
2006-07-19 04:37:00 +02:00
|
|
|
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 <pgsql-bugs@postgresql.org>.\n"));
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-06-12 13:07:34 +02:00
|
|
|
regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
_stringlist *sl;
|
2006-10-04 02:30:14 +02:00
|
|
|
int c;
|
|
|
|
int i;
|
|
|
|
int option_index;
|
|
|
|
char buf[MAXPGPATH * 4];
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
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},
|
|
|
|
{"multibyte", 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},
|
|
|
|
{"temp-port", required_argument, NULL, 12},
|
|
|
|
{"host", required_argument, NULL, 13},
|
|
|
|
{"port", required_argument, NULL, 14},
|
|
|
|
{"user", required_argument, NULL, 15},
|
2006-07-21 02:24:04 +02:00
|
|
|
{"psqldir", required_argument, NULL, 16},
|
2007-01-19 17:42:24 +01:00
|
|
|
{"srcdir", required_argument, NULL, 17},
|
2007-06-12 13:07:34 +02:00
|
|
|
{"create-role", required_argument, NULL, 18},
|
2007-09-09 22:40:54 +02:00
|
|
|
{"temp-config", required_argument, NULL, 19},
|
2006-07-19 04:37:00 +02:00
|
|
|
{NULL, 0, NULL, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
progname = get_progname(argv[0]);
|
|
|
|
set_pglocale_pgservice(argv[0], "pg_regress");
|
|
|
|
|
|
|
|
#ifndef HAVE_UNIX_SOCKETS
|
|
|
|
/* no unix domain sockets available, so change default */
|
|
|
|
hostname = "localhost";
|
|
|
|
#endif
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
/*
|
|
|
|
* We call the initialization function here because that way we can set
|
|
|
|
* default parameters and let them be overwritten by the commandline.
|
|
|
|
*/
|
|
|
|
ifunc();
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
while ((c = getopt_long(argc, argv, "hV", long_options, &option_index)) != -1)
|
|
|
|
{
|
|
|
|
switch (c)
|
|
|
|
{
|
|
|
|
case 'h':
|
|
|
|
help();
|
|
|
|
exit_nicely(0);
|
|
|
|
case 'V':
|
2008-02-20 23:44:16 +01:00
|
|
|
puts("pg_regress (PostgreSQL) " PG_VERSION);
|
2006-07-19 04:37:00 +02:00
|
|
|
exit_nicely(0);
|
|
|
|
case 1:
|
2007-11-15 22:14:46 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If a default database was specified, we need to remove it
|
|
|
|
* before we add the specified one.
|
2007-06-12 15:54:58 +02:00
|
|
|
*/
|
|
|
|
free_stringlist(&dblist);
|
2007-06-12 13:07:34 +02:00
|
|
|
split_to_stringlist(strdup(optarg), ", ", &dblist);
|
2006-07-19 04:37:00 +02:00
|
|
|
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 must be absolute path */
|
|
|
|
if (is_absolute_path(optarg))
|
|
|
|
temp_install = strdup(optarg);
|
|
|
|
else
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char cwdbuf[MAXPGPATH];
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
if (!getcwd(cwdbuf, sizeof(cwdbuf)))
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("could not get current working directory: %s\n"), strerror(errno));
|
|
|
|
exit_nicely(2);
|
|
|
|
}
|
|
|
|
temp_install = malloc(strlen(cwdbuf) + strlen(optarg) + 2);
|
2006-10-04 02:30:14 +02:00
|
|
|
sprintf(temp_install, "%s/%s", cwdbuf, optarg);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
canonicalize_path(temp_install);
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
nolocale = true;
|
|
|
|
break;
|
|
|
|
case 11:
|
|
|
|
top_builddir = strdup(optarg);
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
int p = atoi(optarg);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* Since Makefile isn't very bright, check port range */
|
|
|
|
if (p >= 1024 && p <= 65535)
|
|
|
|
temp_port = p;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 13:
|
|
|
|
hostname = strdup(optarg);
|
|
|
|
break;
|
|
|
|
case 14:
|
|
|
|
port = atoi(optarg);
|
|
|
|
break;
|
|
|
|
case 15:
|
|
|
|
user = strdup(optarg);
|
|
|
|
break;
|
2006-07-21 02:24:04 +02:00
|
|
|
case 16:
|
|
|
|
/* "--psqldir=" should mean to use PATH */
|
|
|
|
if (strlen(optarg))
|
|
|
|
psqldir = strdup(optarg);
|
|
|
|
break;
|
2007-01-19 17:42:24 +01:00
|
|
|
case 17:
|
|
|
|
srcdir = strdup(optarg);
|
|
|
|
break;
|
2007-06-12 13:07:34 +02:00
|
|
|
case 18:
|
|
|
|
split_to_stringlist(strdup(optarg), ", ", &extraroles);
|
|
|
|
break;
|
2007-09-09 22:40:54 +02:00
|
|
|
case 19:
|
|
|
|
temp_config = strdup(optarg);
|
|
|
|
break;
|
2006-07-19 04:37:00 +02:00
|
|
|
default:
|
|
|
|
/* getopt_long already emitted a complaint */
|
|
|
|
fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
|
|
|
|
progname);
|
|
|
|
exit_nicely(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 = temp_port;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialization
|
|
|
|
*/
|
|
|
|
open_result_files();
|
|
|
|
|
|
|
|
initialize_environment();
|
|
|
|
|
2007-01-05 17:17:55 +01:00
|
|
|
#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
|
|
|
|
unlimit_core_size();
|
|
|
|
#endif
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
if (temp_install)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Prepare the temp installation
|
|
|
|
*/
|
|
|
|
if (!top_builddir)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("--top-builddir must be specified when using --temp-install\n"));
|
|
|
|
exit_nicely(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (directory_exists(temp_install))
|
|
|
|
{
|
|
|
|
header(_("removing existing temp installation"));
|
2006-10-04 02:30:14 +02:00
|
|
|
rmtree(temp_install, true);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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" */
|
2007-02-08 16:28:58 +01:00
|
|
|
#ifndef WIN32_ONLY_COMPILER
|
2006-07-19 04:37:00 +02:00
|
|
|
snprintf(buf, sizeof(buf),
|
2006-07-20 18:25:30 +02:00
|
|
|
SYSTEMQUOTE "\"%s\" -C \"%s\" DESTDIR=\"%s/install\" install with_perl=no with_python=no > \"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
|
2006-07-19 06:50:57 +02:00
|
|
|
makeprog, top_builddir, temp_install, outputdir);
|
2007-02-08 16:28:58 +01:00
|
|
|
#else
|
2007-11-15 22:14:46 +01:00
|
|
|
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);
|
2007-02-08 16:28:58 +01:00
|
|
|
#endif
|
2006-07-19 04:37:00 +02:00
|
|
|
if (system(buf))
|
|
|
|
{
|
2006-07-19 18:23:17 +02:00
|
|
|
fprintf(stderr, _("\n%s: installation failed\nExamine %s/log/install.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf);
|
2006-07-19 04:37:00 +02:00
|
|
|
exit_nicely(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* initdb */
|
|
|
|
header(_("initializing database system"));
|
|
|
|
snprintf(buf, sizeof(buf),
|
2006-07-20 18:25:30 +02:00
|
|
|
SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L \"%s\" --noclean%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE,
|
2006-07-19 04:37:00 +02:00
|
|
|
bindir, temp_install, datadir,
|
2006-07-20 18:25:30 +02:00
|
|
|
debug ? " --debug" : "",
|
|
|
|
nolocale ? " --no-locale" : "",
|
2006-07-19 04:37:00 +02:00
|
|
|
outputdir);
|
|
|
|
if (system(buf))
|
|
|
|
{
|
2006-07-19 18:23:17 +02:00
|
|
|
fprintf(stderr, _("\n%s: initdb failed\nExamine %s/log/initdb.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf);
|
2006-07-19 04:37:00 +02:00
|
|
|
exit_nicely(2);
|
|
|
|
}
|
|
|
|
|
2007-09-09 22:40:54 +02:00
|
|
|
/* add any extra config specified to the postgresql.conf */
|
|
|
|
if (temp_config != NULL)
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
FILE *extra_conf;
|
|
|
|
FILE *pg_conf;
|
|
|
|
char line_buf[1024];
|
2007-09-09 22:40:54 +02:00
|
|
|
|
2007-11-15 22:14:46 +01:00
|
|
|
snprintf(buf, sizeof(buf), "%s/data/postgresql.conf", temp_install);
|
|
|
|
pg_conf = fopen(buf, "a");
|
2007-09-09 22:40:54 +02:00
|
|
|
if (pg_conf == NULL)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("\n%s: could not open %s for adding extra config:\nError was %s\n"), progname, buf, strerror(errno));
|
2007-11-15 22:14:46 +01:00
|
|
|
exit_nicely(2);
|
2007-09-09 22:40:54 +02:00
|
|
|
}
|
2007-11-15 22:14:46 +01:00
|
|
|
extra_conf = fopen(temp_config, "r");
|
2007-09-09 22:40:54 +02:00
|
|
|
if (extra_conf == NULL)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("\n%s: could not open %s to read extra config:\nError was %s\n"), progname, buf, strerror(errno));
|
2007-11-15 22:14:46 +01:00
|
|
|
exit_nicely(2);
|
2007-09-09 22:40:54 +02:00
|
|
|
}
|
2007-11-15 22:14:46 +01:00
|
|
|
while (fgets(line_buf, sizeof(line_buf), extra_conf) != NULL)
|
2007-09-09 22:40:54 +02:00
|
|
|
fputs(line_buf, pg_conf);
|
|
|
|
fclose(extra_conf);
|
|
|
|
fclose(pg_conf);
|
|
|
|
}
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
|
|
|
* Start the temp postmaster
|
|
|
|
*/
|
|
|
|
header(_("starting postmaster"));
|
|
|
|
snprintf(buf, sizeof(buf),
|
2006-09-24 19:10:18 +02:00
|
|
|
SYSTEMQUOTE "\"%s/postgres\" -D \"%s/data\" -F%s -c \"listen_addresses=%s\" > \"%s/log/postmaster.log\" 2>&1" SYSTEMQUOTE,
|
2006-07-19 04:37:00 +02:00
|
|
|
bindir, temp_install,
|
2006-07-20 18:25:30 +02:00
|
|
|
debug ? " -d 5" : "",
|
2006-07-19 04:37:00 +02:00
|
|
|
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_nicely(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* 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.
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
|
|
|
snprintf(buf, sizeof(buf),
|
2006-07-19 06:50:57 +02:00
|
|
|
SYSTEMQUOTE "\"%s/psql\" -X postgres <%s 2>%s" SYSTEMQUOTE,
|
2006-07-19 04:37:00 +02:00
|
|
|
bindir, DEVNULL, DEVNULL);
|
|
|
|
for (i = 0; i < 60; i++)
|
|
|
|
{
|
|
|
|
/* Done if psql succeeds */
|
|
|
|
if (system(buf) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fail immediately if postmaster has exited
|
|
|
|
*/
|
|
|
|
#ifndef WIN32
|
|
|
|
if (kill(postmaster_pid, 0) != 0)
|
2006-09-24 19:10:18 +02:00
|
|
|
#else
|
|
|
|
if (WaitForSingleObject(postmaster_pid, 0) == WAIT_OBJECT_0)
|
|
|
|
#endif
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
fprintf(stderr, _("\n%s: postmaster failed\nExamine %s/log/postmaster.log for the reason\n"), progname, outputdir);
|
|
|
|
exit_nicely(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
pg_usleep(1000000L);
|
|
|
|
}
|
2006-08-13 22:39:07 +02:00
|
|
|
if (i >= 60)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2006-08-13 22:39:07 +02:00
|
|
|
fprintf(stderr, _("\n%s: postmaster did not respond within 60 seconds\nExamine %s/log/postmaster.log for the reason\n"), progname, outputdir);
|
|
|
|
|
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* 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
|
2006-08-13 22:39:07 +02:00
|
|
|
* 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));
|
2006-09-24 19:10:18 +02:00
|
|
|
#else
|
|
|
|
if (TerminateProcess(postmaster_pid, 255) == 0)
|
|
|
|
fprintf(stderr, _("\n%s: could not kill failed postmaster: %lu\n"),
|
|
|
|
progname, GetLastError());
|
2006-08-13 22:39:07 +02:00
|
|
|
#endif
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
exit_nicely(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
postmaster_running = true;
|
|
|
|
|
|
|
|
printf(_("running on port %d with pid %lu\n"),
|
|
|
|
temp_port, (unsigned long) postmaster_pid);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Using an existing installation, so may need to get rid of
|
2007-06-12 13:07:34 +02:00
|
|
|
* pre-existing database(s) and role(s)
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
2007-06-12 13:07:34 +02:00
|
|
|
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);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2007-06-12 13:07:34 +02:00
|
|
|
* Create the test database(s) and role(s)
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
2007-06-12 13:07:34 +02:00
|
|
|
for (sl = dblist; sl; sl = sl->next)
|
|
|
|
create_database(sl->str);
|
|
|
|
for (sl = extraroles; sl; sl = sl->next)
|
|
|
|
create_role(sl->str, dblist);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Ready to run the tests
|
|
|
|
*/
|
|
|
|
header(_("running regression test queries"));
|
|
|
|
|
|
|
|
for (sl = schedulelist; sl != NULL; sl = sl->next)
|
|
|
|
{
|
2007-06-12 13:07:34 +02:00
|
|
|
run_schedule(sl->str, tfunc);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for (sl = extra_tests; sl != NULL; sl = sl->next)
|
|
|
|
{
|
2007-06-12 13:07:34 +02:00
|
|
|
run_single_test(sl->str, tfunc);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
2006-10-04 02:30:14 +02:00
|
|
|
else if (fail_count == 0) /* fail_count=0, fail_ignore_count>0 */
|
2006-07-19 04:37:00 +02:00
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
|
_(" %d of %d tests passed, %d failed test(s) ignored. "),
|
|
|
|
success_count,
|
|
|
|
success_count + fail_ignore_count,
|
|
|
|
fail_ignore_count);
|
2006-10-04 02:30:14 +02:00
|
|
|
else if (fail_ignore_count == 0) /* fail_count>0 && fail_ignore_count=0 */
|
2006-07-19 04:37:00 +02:00
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
|
_(" %d of %d tests failed. "),
|
|
|
|
fail_count,
|
2006-10-04 02:30:14 +02:00
|
|
|
success_count + fail_count);
|
|
|
|
else
|
|
|
|
/* fail_count>0 && fail_ignore_count>0 */
|
2006-07-19 04:37:00 +02:00
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
|
_(" %d of %d tests failed, %d of these failures ignored. "),
|
2006-10-04 02:30:14 +02:00
|
|
|
fail_count + fail_ignore_count,
|
|
|
|
success_count + fail_count + fail_ignore_count,
|
2006-07-19 04:37:00 +02:00
|
|
|
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_nicely(1);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|