mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 15:06:14 +01:00
gtester.c: adapted to become a rudimentary test binary launcher.
* gtester.c: increased read buffer size to match common unix pipe buffer size. added argument parsing and usage. changed io handling to capture and replicate stdout. fixed io handlers to be cleaned up when the child process exits (catch G_IO_ERR | G_IO_HUP). we now use pending/iteration instead of a main loop structure, to keep running until the child process exits and all io has been processed. launch the test binaries given on the command line. don't quit when a child couldn't be launched but --keep-going was specified. svn path=/trunk/; revision=5895
This commit is contained in:
parent
39cf400cdf
commit
d0709d04cb
322
glib/gtester.c
322
glib/gtester.c
@ -18,45 +18,61 @@
|
|||||||
* Boston, MA 02111-1307, USA.
|
* Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
/* the read buffer size in bytes */
|
/* the read buffer size in bytes */
|
||||||
#define READ_BUFFER_SIZE 1024
|
#define READ_BUFFER_SIZE 4096
|
||||||
|
|
||||||
|
/* --- prototypes --- */
|
||||||
|
static void parse_args (gint *argc_p,
|
||||||
|
gchar ***argv_p);
|
||||||
|
|
||||||
/* --- variables --- */
|
/* --- variables --- */
|
||||||
static GIOChannel* out = NULL;
|
static GIOChannel *ioc_report = NULL;
|
||||||
|
static gboolean subtest_running = FALSE;
|
||||||
|
static gboolean subtest_io_pending = FALSE;
|
||||||
|
static gboolean gtester_quiet = FALSE;
|
||||||
|
static gboolean gtester_verbose = FALSE;
|
||||||
|
static gboolean gtester_list_tests = FALSE;
|
||||||
|
static gboolean subtest_mode_fatal = FALSE;
|
||||||
|
static gboolean subtest_mode_perf = FALSE;
|
||||||
|
static gboolean subtest_mode_quick = TRUE;
|
||||||
|
static const gchar *subtest_seedstr = NULL;
|
||||||
|
static GSList *subtest_paths = NULL;
|
||||||
|
static const gchar *outpu_filename = NULL;
|
||||||
|
|
||||||
/* --- functions --- */
|
/* --- functions --- */
|
||||||
static gboolean
|
static gboolean
|
||||||
child_out_cb (GIOChannel *source,
|
child_report_cb (GIOChannel *source,
|
||||||
GIOCondition condition,
|
GIOCondition condition,
|
||||||
gpointer data)
|
gpointer data)
|
||||||
{
|
{
|
||||||
GError *error = NULL;
|
|
||||||
gsize length = 0;
|
|
||||||
gchar buffer[READ_BUFFER_SIZE];
|
|
||||||
GIOStatus status = G_IO_STATUS_NORMAL;
|
GIOStatus status = G_IO_STATUS_NORMAL;
|
||||||
|
|
||||||
while (status == G_IO_STATUS_NORMAL)
|
while (status == G_IO_STATUS_NORMAL)
|
||||||
{
|
{
|
||||||
|
gchar buffer[READ_BUFFER_SIZE];
|
||||||
|
gsize length = 0;
|
||||||
|
GError *error = NULL;
|
||||||
status = g_io_channel_read_chars (source, buffer, sizeof (buffer), &length, &error);
|
status = g_io_channel_read_chars (source, buffer, sizeof (buffer), &length, &error);
|
||||||
|
|
||||||
switch (status)
|
switch (status)
|
||||||
{
|
{
|
||||||
case G_IO_STATUS_NORMAL:
|
case G_IO_STATUS_NORMAL:
|
||||||
// FIXME: this is where the parsing happens
|
write (2, buffer, length); /* passthrough child's stdout */
|
||||||
g_print ("read output bytes: %d\n", length);
|
|
||||||
break;
|
break;
|
||||||
case G_IO_STATUS_AGAIN:
|
case G_IO_STATUS_AGAIN:
|
||||||
/* retry later */
|
/* retry later */
|
||||||
break;
|
break;
|
||||||
case G_IO_STATUS_ERROR:
|
case G_IO_STATUS_ERROR:
|
||||||
|
/* ignore, child closed fd or similar g_warning ("Error while reading data: %s", error->message); */
|
||||||
/* fall through into EOF */
|
/* fall through into EOF */
|
||||||
g_warning ("Error while reading data: %s",
|
|
||||||
error->message);
|
|
||||||
g_error_free (error);
|
|
||||||
case G_IO_STATUS_EOF:
|
case G_IO_STATUS_EOF:
|
||||||
|
subtest_io_pending = FALSE;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
g_clear_error (&error);
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -66,57 +82,259 @@ child_watch_cb (GPid pid,
|
|||||||
gint status,
|
gint status,
|
||||||
gpointer data)
|
gpointer data)
|
||||||
{
|
{
|
||||||
GMainLoop *loop = data;
|
|
||||||
|
|
||||||
g_spawn_close_pid (pid);
|
g_spawn_close_pid (pid);
|
||||||
|
subtest_running = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/* read the remaining data - also stops the io watch from being polled */
|
static gchar*
|
||||||
child_out_cb (out, G_IO_IN, data);
|
queue_gfree (GSList **slistp,
|
||||||
g_main_loop_quit (loop);
|
gchar *string)
|
||||||
|
{
|
||||||
|
*slistp = g_slist_prepend (*slistp, string);
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
launch_test (const char *binary)
|
||||||
|
{
|
||||||
|
GSList *slist, *free_list = NULL;
|
||||||
|
GError *error = NULL;
|
||||||
|
const gchar *argv[20 + g_slist_length (subtest_paths)];
|
||||||
|
GPid pid = 0;
|
||||||
|
gint i = 0, child_report = -1;
|
||||||
|
|
||||||
|
/* setup argv */
|
||||||
|
argv[i++] = binary;
|
||||||
|
// argv[i++] = "--quiet";
|
||||||
|
if (!subtest_mode_fatal)
|
||||||
|
argv[i++] = "--keep-going";
|
||||||
|
if (subtest_mode_quick)
|
||||||
|
argv[i++] = "-m=quick";
|
||||||
|
else
|
||||||
|
argv[i++] = "-m=slow";
|
||||||
|
if (subtest_mode_perf)
|
||||||
|
argv[i++] = "-m=perf";
|
||||||
|
if (subtest_seedstr)
|
||||||
|
argv[i++] = queue_gfree (&free_list, g_strdup_printf ("--seed=%s", subtest_seedstr));
|
||||||
|
for (slist = subtest_paths; slist; slist = slist->next)
|
||||||
|
argv[i++] = queue_gfree (&free_list, g_strdup_printf ("-p=%s", (gchar*) slist->data));
|
||||||
|
argv[i++] = NULL;
|
||||||
|
|
||||||
|
/* child_report will be used to capture logging information from the
|
||||||
|
* child binary. for the moment, we just use it to replicate stdout.
|
||||||
|
*/
|
||||||
|
|
||||||
|
g_spawn_async_with_pipes (NULL, /* g_get_current_dir() */
|
||||||
|
(gchar**) argv,
|
||||||
|
NULL, /* envp */
|
||||||
|
G_SPAWN_DO_NOT_REAP_CHILD, /* G_SPAWN_SEARCH_PATH */
|
||||||
|
NULL, NULL, /* child_setup, user_data */
|
||||||
|
&pid,
|
||||||
|
NULL, /* standard_input */
|
||||||
|
&child_report, /* standard_output */
|
||||||
|
NULL, /* standard_error */
|
||||||
|
&error);
|
||||||
|
g_slist_foreach (free_list, (void(*)(void*,void*)) g_free, NULL);
|
||||||
|
g_slist_free (free_list);
|
||||||
|
free_list = NULL;
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
if (subtest_mode_fatal)
|
||||||
|
g_error ("Failed to execute test binary: %s: %s", argv[0], error->message);
|
||||||
|
else
|
||||||
|
g_warning ("Failed to execute test binary: %s: %s", argv[0], error->message);
|
||||||
|
g_clear_error (&error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
subtest_running = TRUE;
|
||||||
|
subtest_io_pending = TRUE;
|
||||||
|
|
||||||
|
if (child_report >= 0)
|
||||||
|
{
|
||||||
|
ioc_report = g_io_channel_unix_new (child_report);
|
||||||
|
g_io_channel_set_flags (ioc_report, G_IO_FLAG_NONBLOCK, NULL);
|
||||||
|
g_io_add_watch_full (ioc_report, G_PRIORITY_DEFAULT - 1, G_IO_IN | G_IO_ERR | G_IO_HUP, child_report_cb, NULL, NULL);
|
||||||
|
g_io_channel_unref (ioc_report);
|
||||||
|
}
|
||||||
|
g_child_watch_add_full (G_PRIORITY_DEFAULT + 1, pid, child_watch_cb, NULL, NULL);
|
||||||
|
|
||||||
|
while (subtest_running || /* FALSE once child exits */
|
||||||
|
subtest_io_pending || /* FALSE once ioc_report closes */
|
||||||
|
g_main_context_pending (NULL)) /* TRUE while idler, etc are running */
|
||||||
|
g_main_context_iteration (NULL, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usage (gboolean just_version)
|
||||||
|
{
|
||||||
|
if (just_version)
|
||||||
|
{
|
||||||
|
g_print ("gtester version %d.%d.%d\n", GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_print ("Usage: gtester [OPTIONS] testprogram...\n");
|
||||||
|
/* 12345678901234567890123456789012345678901234567890123456789012345678901234567890 */
|
||||||
|
g_print ("Options:\n");
|
||||||
|
g_print (" -h, --help show this help message\n");
|
||||||
|
g_print (" -v, --version print version informations\n");
|
||||||
|
g_print (" --g-fatal-warnings make warnings fatal (abort)\n");
|
||||||
|
g_print (" -k, --keep-going continue running after tests failed\n");
|
||||||
|
g_print (" -l list paths of available test cases\n");
|
||||||
|
g_print (" -m=perf, -m=slow, -m=quick run test cases in mode perf, slow or quick (default)\n");
|
||||||
|
g_print (" -p=TESTPATH only start test cases matching TESTPATH\n");
|
||||||
|
g_print (" --seed=SEEDSTRING start all tests with random number seed SEEDSTRING\n");
|
||||||
|
g_print (" -o=LOGFILE write the test log to LOGFILE\n");
|
||||||
|
g_print (" -q, --quiet suppress unnecessary output\n");
|
||||||
|
g_print (" --verbose produce additional output\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
parse_args (gint *argc_p,
|
||||||
|
gchar ***argv_p)
|
||||||
|
{
|
||||||
|
guint argc = *argc_p;
|
||||||
|
gchar **argv = *argv_p;
|
||||||
|
guint i, e;
|
||||||
|
/* parse known args */
|
||||||
|
for (i = 1; i < argc; i++)
|
||||||
|
{
|
||||||
|
if (strcmp (argv[i], "--g-fatal-warnings") == 0)
|
||||||
|
{
|
||||||
|
GLogLevelFlags fatal_mask = (GLogLevelFlags) g_log_set_always_fatal ((GLogLevelFlags) G_LOG_FATAL_MASK);
|
||||||
|
fatal_mask = (GLogLevelFlags) (fatal_mask | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL);
|
||||||
|
g_log_set_always_fatal (fatal_mask);
|
||||||
|
argv[i] = NULL;
|
||||||
|
}
|
||||||
|
else if (strcmp (argv[i], "-h") == 0 || strcmp (argv[i], "--help") == 0)
|
||||||
|
{
|
||||||
|
usage (FALSE);
|
||||||
|
exit (0);
|
||||||
|
argv[i] = NULL;
|
||||||
|
}
|
||||||
|
else if (strcmp (argv[i], "-v") == 0 || strcmp (argv[i], "--version") == 0)
|
||||||
|
{
|
||||||
|
usage (TRUE);
|
||||||
|
exit (0);
|
||||||
|
argv[i] = NULL;
|
||||||
|
}
|
||||||
|
else if (strcmp (argv[i], "--keep-going") == 0 ||
|
||||||
|
strcmp (argv[i], "-k") == 0)
|
||||||
|
{
|
||||||
|
subtest_mode_fatal = FALSE;
|
||||||
|
argv[i] = NULL;
|
||||||
|
}
|
||||||
|
else if (strcmp ("-p", argv[i]) == 0 || strncmp ("-p=", argv[i], 3) == 0)
|
||||||
|
{
|
||||||
|
gchar *equal = argv[i] + 2;
|
||||||
|
if (*equal == '=')
|
||||||
|
subtest_paths = g_slist_prepend (subtest_paths, equal + 1);
|
||||||
|
else if (i + 1 < argc)
|
||||||
|
{
|
||||||
|
argv[i++] = NULL;
|
||||||
|
subtest_paths = g_slist_prepend (subtest_paths, argv[i]);
|
||||||
|
}
|
||||||
|
argv[i] = NULL;
|
||||||
|
}
|
||||||
|
else if (strcmp ("-o", argv[i]) == 0 || strncmp ("-o=", argv[i], 3) == 0)
|
||||||
|
{
|
||||||
|
gchar *equal = argv[i] + 2;
|
||||||
|
if (*equal == '=')
|
||||||
|
outpu_filename = equal + 1;
|
||||||
|
else if (i + 1 < argc)
|
||||||
|
{
|
||||||
|
argv[i++] = NULL;
|
||||||
|
outpu_filename = argv[i];
|
||||||
|
}
|
||||||
|
argv[i] = NULL;
|
||||||
|
}
|
||||||
|
else if (strcmp ("-m", argv[i]) == 0 || strncmp ("-m=", argv[i], 3) == 0)
|
||||||
|
{
|
||||||
|
gchar *equal = argv[i] + 2;
|
||||||
|
const gchar *mode = "";
|
||||||
|
if (*equal == '=')
|
||||||
|
mode = equal + 1;
|
||||||
|
else if (i + 1 < argc)
|
||||||
|
{
|
||||||
|
argv[i++] = NULL;
|
||||||
|
mode = argv[i];
|
||||||
|
}
|
||||||
|
if (strcmp (mode, "perf") == 0)
|
||||||
|
subtest_mode_perf = TRUE;
|
||||||
|
else if (strcmp (mode, "slow") == 0)
|
||||||
|
subtest_mode_quick = FALSE;
|
||||||
|
else if (strcmp (mode, "quick") == 0)
|
||||||
|
{
|
||||||
|
subtest_mode_quick = TRUE;
|
||||||
|
subtest_mode_perf = FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
g_error ("unknown test mode: -m %s", mode);
|
||||||
|
argv[i] = NULL;
|
||||||
|
}
|
||||||
|
else if (strcmp ("-q", argv[i]) == 0 || strcmp ("--quiet", argv[i]) == 0)
|
||||||
|
{
|
||||||
|
gtester_quiet = TRUE;
|
||||||
|
gtester_verbose = FALSE;
|
||||||
|
argv[i] = NULL;
|
||||||
|
}
|
||||||
|
else if (strcmp ("--verbose", argv[i]) == 0)
|
||||||
|
{
|
||||||
|
gtester_quiet = FALSE;
|
||||||
|
gtester_verbose = TRUE;
|
||||||
|
argv[i] = NULL;
|
||||||
|
}
|
||||||
|
else if (strcmp ("-l", argv[i]) == 0)
|
||||||
|
{
|
||||||
|
gtester_list_tests = TRUE;
|
||||||
|
argv[i] = NULL;
|
||||||
|
}
|
||||||
|
else if (strcmp ("--seed", argv[i]) == 0 || strncmp ("--seed=", argv[i], 7) == 0)
|
||||||
|
{
|
||||||
|
gchar *equal = argv[i] + 6;
|
||||||
|
if (*equal == '=')
|
||||||
|
subtest_seedstr = equal + 1;
|
||||||
|
else if (i + 1 < argc)
|
||||||
|
{
|
||||||
|
argv[i++] = NULL;
|
||||||
|
subtest_seedstr = argv[i];
|
||||||
|
}
|
||||||
|
argv[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* collapse argv */
|
||||||
|
e = 1;
|
||||||
|
for (i = 1; i < argc; i++)
|
||||||
|
if (argv[i])
|
||||||
|
{
|
||||||
|
argv[e++] = argv[i];
|
||||||
|
if (i >= e)
|
||||||
|
argv[i] = NULL;
|
||||||
|
}
|
||||||
|
*argc_p = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main (int argc,
|
main (int argc,
|
||||||
char **argv)
|
char **argv)
|
||||||
{
|
{
|
||||||
GMainLoop *loop;
|
guint ui;
|
||||||
GError *error = NULL;
|
|
||||||
GPid pid = 0;
|
|
||||||
gchar *working_folder;
|
|
||||||
gchar *child_argv[] = {
|
|
||||||
"/bin/ls",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
gint child_out;
|
|
||||||
|
|
||||||
working_folder = g_strdup ("."); // g_get_current_dir ();
|
g_set_prgname (argv[0]);
|
||||||
g_spawn_async_with_pipes (working_folder,
|
parse_args (&argc, &argv);
|
||||||
child_argv, NULL /* envp */,
|
|
||||||
G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
|
|
||||||
NULL, NULL,
|
|
||||||
&pid,
|
|
||||||
NULL,
|
|
||||||
&child_out,
|
|
||||||
NULL,
|
|
||||||
&error);
|
|
||||||
g_free (working_folder);
|
|
||||||
|
|
||||||
if (error)
|
if (argc <= 1)
|
||||||
{
|
{
|
||||||
g_error ("Couldn't execute child: %s", error->message);
|
usage (FALSE);
|
||||||
/* doesn't return */
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
loop = g_main_loop_new (NULL, FALSE);
|
for (ui = 1; ui < argc; ui++)
|
||||||
|
{
|
||||||
|
const char *binary = argv[ui];
|
||||||
|
launch_test (binary);
|
||||||
|
}
|
||||||
|
|
||||||
g_child_watch_add (pid, child_watch_cb, loop);
|
/* we only get here on success or if !subtest_mode_fatal */
|
||||||
|
|
||||||
out = g_io_channel_unix_new (child_out);
|
|
||||||
g_io_channel_set_flags (out, G_IO_FLAG_NONBLOCK, NULL); // FIXME: GError
|
|
||||||
g_io_add_watch (out, G_IO_IN, child_out_cb, loop);
|
|
||||||
|
|
||||||
g_main_loop_run (loop);
|
|
||||||
g_main_loop_unref (loop);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user