[gnome-desktop-testing] Parse test files before execution, clean up internals
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-desktop-testing] Parse test files before execution, clean up internals
- Date: Wed, 29 May 2013 23:48:51 +0000 (UTC)
commit 281b859564440cb5cf6212ae436c0d9731d33a65
Author: Colin Walters <walters verbum org>
Date: Wed May 29 18:41:33 2013 -0400
Parse test files before execution, clean up internals
This paves the way for a future commit which will respect e.g.
Type=session-exclusive to ensure we don't execute tests in parallel.
Also we should ideally handle the Description key.
src/gnome-desktop-testing-runner.c | 167 ++++++++++++++++++++++++------------
1 files changed, 113 insertions(+), 54 deletions(-)
---
diff --git a/src/gnome-desktop-testing-runner.c b/src/gnome-desktop-testing-runner.c
index 74055b6..ecacd42 100755
--- a/src/gnome-desktop-testing-runner.c
+++ b/src/gnome-desktop-testing-runner.c
@@ -40,16 +40,27 @@ typedef struct {
int parallel;
int test_index;
-
- int ntests;
- int n_skipped_tests;
- int n_failed_tests;
} TestRunnerApp;
+typedef enum {
+ TEST_STATE_UNLOADED,
+ TEST_STATE_LOADED,
+ TEST_STATE_EXECUTING,
+ TEST_STATE_COMPLETE_SUCCESS,
+ TEST_STATE_COMPLETE_SKIPPED,
+ TEST_STATE_COMPLETE_FAILED,
+} TestState;
+
typedef struct {
volatile gint refcount;
GFile *prefix_root;
GFile *path;
+ GFile *tmpdir;
+
+ char *name;
+ char **argv;
+
+ TestState state;
} Test;
static void
@@ -57,17 +68,63 @@ test_unref (Test *test)
{
if (!g_atomic_int_dec_and_test (&test->refcount))
return;
+ g_strfreev (test->argv);
+ g_clear_object (&test->tmpdir);
g_object_unref (test->prefix_root);
g_object_unref (test->path);
}
-static Test *
-test_new (GFile *prefix_root, GFile *path)
+static Test*
+test_ref (Test *test)
+{
+ g_atomic_int_inc (&test->refcount);
+ return test;
+}
+
+static gboolean
+load_test (GFile *prefix_root,
+ GFile *path,
+ Test **out_test,
+ GCancellable *cancellable,
+ GError **error)
{
+ gboolean ret = FALSE;
+ GKeyFile *keyfile = NULL;
Test *test = g_new0 (Test, 1);
+ int test_argc;
+ gs_free char *exec_key = NULL;
+ const char *test_path;
+
+ g_assert (test->state == TEST_STATE_UNLOADED);
+
test->prefix_root = g_object_ref (prefix_root);
test->path = g_object_ref (path);
- return test;
+
+ test->name = g_file_get_relative_path (test->prefix_root, test->path);
+
+ test_path = gs_file_get_path_cached (test->path);
+
+ keyfile = g_key_file_new ();
+ if (!g_key_file_load_from_file (keyfile, test_path, 0, error))
+ goto out;
+
+ exec_key = g_key_file_get_string (keyfile, "Test", "Exec", error);
+ if (exec_key == NULL)
+ goto out;
+
+ if (!g_shell_parse_argv (exec_key, &test_argc, &test->argv, error))
+ goto out;
+
+ test->state = TEST_STATE_LOADED;
+
+ ret = TRUE;
+ out:
+ g_clear_pointer (&keyfile, g_key_file_free);
+ if (!ret)
+ test_unref (test);
+ else
+ *out_test = test;
+ return ret;
}
static TestRunnerApp *app;
@@ -125,7 +182,10 @@ gather_all_tests_recurse (GFile *prefix_root,
if (type == G_FILE_TYPE_REGULAR && g_str_has_suffix (name, ".test"))
{
- g_ptr_array_add (tests, test_new (prefix_root, child));
+ Test *test;
+ if (!load_test (prefix_root, child, &test, cancellable, error))
+ goto out;
+ g_ptr_array_add (tests, test);
}
else if (type == G_FILE_TYPE_DIRECTORY)
{
@@ -158,14 +218,13 @@ on_test_exited (GObject *obj,
GSSubprocess *proc = GS_SUBPROCESS (obj);
GTask *task = G_TASK (user_data);
GCancellable *cancellable = g_task_get_cancellable (task);
- GFile *test_path;
+ Test *test;
GFile *test_tmpdir_f;
- const char *testname;
gboolean failed = FALSE;
- testname = g_object_get_data ((GObject*)task, "gdtr-test-name");
- test_path = g_object_get_data ((GObject*)task, "gdtr-test");
- test_tmpdir_f = g_object_get_data ((GObject*)task, "gdtr-test-tmpdir");
+ test = g_object_get_data ((GObject*)task, "gdtr-test");
+
+ g_assert (test->state == TEST_STATE_EXECUTING);
if (!gs_subprocess_wait_finish (proc, result, &estatus, error))
goto out;
@@ -173,15 +232,15 @@ on_test_exited (GObject *obj,
{
if (g_error_matches (tmp_error, G_SPAWN_EXIT_ERROR, 77))
{
+ test->state = TEST_STATE_COMPLETE_SKIPPED;
gs_log_structured_print_id_v (ONE_TEST_SKIPPED_MSGID,
- "Test %s skipped (exit code 77)", testname);
- app->n_skipped_tests++;
+ "Test %s skipped (exit code 77)", test->name);
}
else
{
+ test->state = TEST_STATE_COMPLETE_FAILED;
gs_log_structured_print_id_v (ONE_TEST_FAILED_MSGID,
- "Test %s failed: %s", testname, tmp_error->message);
- app->n_failed_tests++;
+ "Test %s failed: %s", test->name, tmp_error->message);
failed = TRUE;
}
/* Individual test failures don't count as failure of the whole process */
@@ -189,14 +248,14 @@ on_test_exited (GObject *obj,
}
else
{
- gs_log_structured_print_id_v (ONE_TEST_SUCCESS_MSGID, "PASS: %s", testname);
- app->ntests += 1;
+ gs_log_structured_print_id_v (ONE_TEST_SUCCESS_MSGID, "PASS: %s", test->name);
+ test->state = TEST_STATE_COMPLETE_SUCCESS;
}
/* Keep around temporaries from failed tests */
if (!(failed && opt_report_directory))
{
- if (!gs_shutil_rm_rf (test_tmpdir_f, cancellable, error))
+ if (!gs_shutil_rm_rf (test->tmpdir, cancellable, error))
goto out;
}
@@ -218,7 +277,6 @@ run_test_async (Test *test,
GError *local_error = NULL;
GError **error = &local_error;
- GKeyFile *keyfile = NULL;
gs_free char *testname = NULL;
gs_free char *exec_key = NULL;
gs_free char *test_tmpdir = NULL;
@@ -236,6 +294,8 @@ run_test_async (Test *test,
int estatus;
GTask *task;
+ g_assert (test->state == TEST_STATE_LOADED);
+
if (g_once_init_enter (&initialized))
{
slash_regex = g_regex_new ("/", 0, 0, NULL);
@@ -245,23 +305,9 @@ run_test_async (Test *test,
task = g_task_new (test->path, cancellable, callback, user_data);
- testname = g_file_get_relative_path (test->prefix_root, test->path);
- test_path = gs_file_get_path_cached (test->path);
-
- keyfile = g_key_file_new ();
- if (!g_key_file_load_from_file (keyfile, test_path, 0, error))
- goto out;
-
- exec_key = g_key_file_get_string (keyfile, "Test", "Exec", error);
- if (exec_key == NULL)
- goto out;
-
- if (!g_shell_parse_argv (exec_key, &test_argc, &test_argv, error))
- goto out;
-
- g_print ("Running test: %s\n", test_path);
+ g_print ("Running test: %s\n", test->name);
- test_squashed_name = g_regex_replace_literal (slash_regex, testname, -1,
+ test_squashed_name = g_regex_replace_literal (slash_regex, test->name, -1,
0, "_", 0, NULL);
if (!opt_report_directory)
{
@@ -269,37 +315,33 @@ run_test_async (Test *test,
test_tmpdir = g_dir_make_tmp (test_tmpname, error);
if (!test_tmpdir)
goto out;
- test_tmpdir_f = g_file_new_for_path (test_tmpdir);
+ test->tmpdir = g_file_new_for_path (test_tmpdir);
}
else
{
test_tmpdir = g_build_filename (opt_report_directory, test_squashed_name, NULL);
- test_tmpdir_f = g_file_new_for_path (test_tmpdir);
+ test->tmpdir = g_file_new_for_path (test_tmpdir);
if (!gs_shutil_rm_rf (test_tmpdir_f, cancellable, error))
goto out;
if (!gs_file_ensure_directory (test_tmpdir_f, TRUE, cancellable, error))
goto out;
}
- proc_context = gs_subprocess_context_new (test_argv);
+ proc_context = gs_subprocess_context_new (test->argv);
gs_subprocess_context_set_cwd (proc_context, test_tmpdir);
proc = gs_subprocess_new (proc_context, cancellable, error);
if (!proc)
goto out;
- g_object_set_data_full ((GObject*)task, "gdtr-test-name",
- g_strdup (testname), (GDestroyNotify)g_free);
+ test->state = TEST_STATE_EXECUTING;
+
g_object_set_data_full ((GObject*)task, "gdtr-test",
- g_object_ref (test->path), (GDestroyNotify)g_object_unref);
- g_object_set_data_full ((GObject*)task, "gdtr-test-tmpdir",
- g_object_ref (test_tmpdir_f), (GDestroyNotify)g_object_unref);
+ test_ref (test), (GDestroyNotify)test_unref);
gs_subprocess_wait (proc, cancellable, on_test_exited, task);
out:
- g_clear_pointer (&keyfile, g_key_file_free);
- g_clear_pointer (&test_argv, g_strfreev);
if (local_error)
{
g_task_report_error (test->path, callback, user_data, run_test_async, local_error);
@@ -381,6 +423,7 @@ main (int argc, char **argv)
TestRunnerApp appstruct;
GPtrArray *test_paths;
const char *const *datadirs_iter;
+ int n_passed, n_skipped, n_failed;
memset (&appstruct, 0, sizeof (appstruct));
app = &appstruct;
@@ -426,11 +469,10 @@ main (int argc, char **argv)
{
gboolean matches = FALSE;
Test *test = app->tests->pdata[j];
- gs_free char *test_relname = g_file_get_relative_path (test->prefix_root, test->path);
for (i = 1; i < argc; i++)
{
const char *prefix = argv[i];
- if (g_str_has_prefix (test_relname, prefix))
+ if (g_str_has_prefix (test->name, prefix))
{
matches = TRUE;
break;
@@ -450,9 +492,7 @@ main (int argc, char **argv)
for (i = 0; i < app->tests->len; i++)
{
Test *test = app->tests->pdata[i];
- const char *path = gs_file_get_path_cached (test->path);
- gs_free char *test_relname = g_file_get_relative_path (test->prefix_root, test->path);
- g_print ("%s (%s)\n", test_relname, gs_file_get_path_cached (test->prefix_root));
+ g_print ("%s (%s)\n", test->name, gs_file_get_path_cached (test->prefix_root));
}
}
else
@@ -468,13 +508,32 @@ main (int argc, char **argv)
ret = TRUE;
out:
- g_clear_pointer (&app->tests, g_ptr_array_unref);
if (!opt_list)
{
+ n_passed = n_skipped = n_failed = 0;
+ for (i = 0; i < app->tests->len; i++)
+ {
+ Test *test = app->tests->pdata[i];
+ switch (test->state)
+ {
+ case TEST_STATE_COMPLETE_SUCCESS:
+ n_passed++;
+ break;
+ case TEST_STATE_COMPLETE_SKIPPED:
+ n_skipped++;
+ break;
+ case TEST_STATE_COMPLETE_FAILED:
+ n_failed++;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
gs_log_structured_print_id_v (TESTS_COMPLETE_MSGID,
"SUMMARY: total: %u passed: %d skipped: %d failed: %d",
- total_tests, app->ntests, app->n_skipped_tests, app->n_failed_tests);
+ total_tests, n_passed, n_skipped, n_failed);
}
+ g_clear_pointer (&app->tests, g_ptr_array_unref);
if (!ret)
{
g_assert (local_error);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]