[gnome-desktop-testing] Allow recursive subdirs, add -l option



commit d8fe906b5b061efea583117e597211ed89997fc5
Author: Colin Walters <walters verbum org>
Date:   Thu May 16 18:14:49 2013 -0400

    Allow recursive subdirs, add -l option
    
    Allow for arbitrary nesting of test directories, like:
    
    /usr/share/installed-tests/gtk+/gdk/displaytests/foo.test
    
    Also, add a -l option to just list all the tests, similar to gtester.

 src/gnome-desktop-testing-runner.c |  348 +++++++++++++++++++++---------------
 1 files changed, 201 insertions(+), 147 deletions(-)
---
diff --git a/src/gnome-desktop-testing-runner.c b/src/gnome-desktop-testing-runner.c
index 60f9c55..a39f245 100755
--- a/src/gnome-desktop-testing-runner.c
+++ b/src/gnome-desktop-testing-runner.c
@@ -23,6 +23,8 @@
 #include "libgsystem.h"
 #include "gsystem-local-alloc.h"
 
+#include <string.h>
+
 #define TEST_SKIP_ECODE 77
 #define TESTS_FAILED_MSGID     "0eee66bf98514369bef9868327a43cf1"
 #define TESTS_SUCCESS_MSGID    "4d013788dd704743b826436c951e551d"
@@ -32,21 +34,79 @@ static int ntests = 0;
 static int n_skipped_tests = 0;
 static int n_failed_tests = 0;
 
+static gboolean opt_list;
+
+static GOptionEntry options[] = {
+  { "list", 'l', 0, G_OPTION_ARG_NONE, &opt_list, "List matching tests", NULL },
+  { NULL }
+};
+
 static gboolean
-run_tests_in_directory (GFile         *file,
-                       GCancellable  *cancellable,
-                       GError       **error)
+gather_all_tests_recurse (GFile         *dir,
+                          const char    *prefix,
+                          GPtrArray     *tests,
+                          GCancellable  *cancellable,
+                          GError       **error)
 {
   gboolean ret = FALSE;
-  gs_free char *suite_name = NULL;
   gs_unref_object GFileEnumerator *dir_enum = NULL;
   gs_unref_object GFileInfo *info = NULL;
+  gs_free char *suite_name = NULL;
+  gs_free char *suite_prefix = NULL;
+  GError *tmp_error = NULL;
+
+  suite_name = g_file_get_basename (dir);
+  suite_prefix = g_strconcat (prefix, suite_name, "/", NULL);
+  
+  dir_enum = g_file_enumerate_children (dir, "standard::name,standard::type", 0,
+                                        cancellable, error);
+  if (!dir_enum)
+    goto out;
+  while ((info = g_file_enumerator_next_file (dir_enum, cancellable, &tmp_error)) != NULL)
+    {
+      GFileType type = g_file_info_get_file_type (info);
+      const char *name = g_file_info_get_name (info);
+      gs_unref_object GFile *child = g_file_get_child (dir, name);
+
+      if (type == G_FILE_TYPE_REGULAR && g_str_has_suffix (name, ".test"))
+        {
+          g_ptr_array_add (tests, g_object_ref (child));
+        }
+      else if (type == G_FILE_TYPE_DIRECTORY)
+        {
+          if (!gather_all_tests_recurse (child, suite_prefix, tests,
+                                         cancellable, error))
+            goto out;
+        }
+      g_clear_object (&info);
+    }
+  if (tmp_error != NULL)
+    {
+      g_propagate_error (error, tmp_error);
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+run_test (GFile         *testbase,
+          GFile         *test,
+          GCancellable  *cancellable,
+          GError       **error)
+{
+  static gsize initialized;
+  static GRegex *slash_regex;
+
+  gboolean ret = FALSE;
   GKeyFile *keyfile = NULL;
   gs_free char *testname = NULL;
   gs_free char *exec_key = NULL;
   gs_free char *test_tmpdir = NULL;
-  gs_unref_object GFile *child = NULL;
   gs_unref_object GFile *test_tmpdir_f = NULL;
+  gs_free char *test_squashed_name = NULL;
   gs_free char *test_tmpname = NULL;
   gs_unref_object GSSubprocessContext *proc_context = NULL;
   gs_unref_object GSSubprocess *proc = NULL;
@@ -54,122 +114,105 @@ run_tests_in_directory (GFile         *file,
   int test_argc;
   char **test_argv = NULL;
   gboolean test_success = TRUE;
+  const char *test_path;
+  int estatus;
+  gboolean skipped = FALSE;
+  gboolean failed = FALSE;
 
-  suite_name = g_file_get_basename (file);
-  
-  dir_enum = g_file_enumerate_children (file, "standard::name,standard::type",
-                                       G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                       cancellable, error);
-  if (!dir_enum)
-    goto out;
-  while ((info = g_file_enumerator_next_file (dir_enum, cancellable, &tmp_error)) != NULL)
+  if (g_once_init_enter (&initialized))
     {
-      const char *name = g_file_info_get_name (info);
-      const char *child_path;
-      int estatus;
-      gboolean skipped = FALSE;
-      gboolean failed = FALSE;
-      GError *tmp_error = NULL;
-
-      if (!g_str_has_suffix (name, ".test"))
-       continue;
-      
-      g_clear_pointer (&testname, g_free);
-      testname = g_strdup_printf ("%s/%s", suite_name, name);
-
-      g_clear_object (&child);
-      child = g_file_get_child (file, name);
-      child_path = gs_file_get_path_cached (child);
-
-      g_clear_pointer (&keyfile, g_key_file_free);
-      keyfile = g_key_file_new ();
-      if (!g_key_file_load_from_file (keyfile, child_path, 0, error))
-       goto out;
-
-      g_clear_pointer (&exec_key, g_free);
-      exec_key = g_key_file_get_string (keyfile, "Test", "Exec", error);
-      if (exec_key == NULL)
-       goto out;
-
-      g_clear_pointer (&test_argv, g_strfreev);
-      if (!g_shell_parse_argv (exec_key, &test_argc, &test_argv, error))
-       goto out;
-      
-      g_print ("Running test: %s\n", child_path);
-
-      g_clear_pointer (&test_tmpname, g_free);
-      test_tmpname = g_strconcat ("test-tmp-", name, "-XXXXXX", NULL);
-      g_clear_pointer (&test_tmpdir, g_free);
-      test_tmpdir = g_dir_make_tmp (test_tmpname, error);
-      if (!test_tmpdir)
-       goto out;
-
-      g_clear_object (&proc_context);
-      proc_context = gs_subprocess_context_new (test_argv);
-      gs_subprocess_context_set_cwd (proc_context, test_tmpdir);
-      g_clear_object (&proc);
-
-      proc = gs_subprocess_new (proc_context, cancellable, error);
-      if (!proc)
-       goto out;
-      if (!gs_subprocess_wait_sync (proc, &estatus, cancellable, error))
-       goto out;
-      if (!g_spawn_check_exit_status (estatus, &tmp_error))
-       {
-         if (g_error_matches (tmp_error, G_SPAWN_EXIT_ERROR, 77))
-           {
-             g_print("Skipping test %s\n", child_path);
-             n_skipped_tests++;
-             skipped = TRUE;
-           }
-         else
-           {
-             char *keys[] = { "MESSAGE_ID=" TESTS_FAILED_MSGID, NULL };
-             gs_free char *msg = g_strdup_printf ("Test %s failed: %s", name, tmp_error->message);
-             gs_log_structured_print (msg, (const char* const*)keys);
-             failed = TRUE;
-           }
-         g_clear_error (&tmp_error);
-       }
-      else
-       {
-         ntests += 1;
-       }
+      slash_regex = g_regex_new ("/", 0, 0, NULL);
+      g_assert (slash_regex != NULL);
+      g_once_init_leave (&initialized, 1);
+    }
 
-      g_clear_object (&test_tmpdir_f);
-      test_tmpdir_f = g_file_new_for_path (test_tmpdir);
-      if (!gs_shutil_rm_rf (test_tmpdir_f, cancellable, error))
-       goto out;
+  testname = g_file_get_relative_path (testbase, test);
 
-      g_clear_object (&info);
+  test_path = gs_file_get_path_cached (test);
 
-      /* For now, just exit on the first failing test */
-      if (failed)
-       {
-         n_failed_tests++;
-         break;
-       }
+  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);
+
+  test_squashed_name = g_regex_replace_literal (slash_regex, testname, -1,
+                                                0, "_", 0, NULL);
+  test_tmpname = g_strconcat ("test-tmp-", test_squashed_name, "-XXXXXX", NULL);
+  test_tmpdir = g_dir_make_tmp (test_tmpname, error);
+  if (!test_tmpdir)
+    goto out;
+
+  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;
+  if (!gs_subprocess_wait_sync (proc, &estatus, cancellable, error))
+    goto out;
+  if (!g_spawn_check_exit_status (estatus, &tmp_error))
+    {
+      if (g_error_matches (tmp_error, G_SPAWN_EXIT_ERROR, 77))
+        {
+          g_print("Skipping test %s\n", test_path);
+          n_skipped_tests++;
+          skipped = TRUE;
+        }
       else
-       {
-         char *keys[] = { "MESSAGE_ID=" ONE_TEST_SUCCESS_MSGID, NULL };
-         gs_free char *msg = g_strdup_printf ("PASS: %s", testname);
-         gs_log_structured_print (msg, (const char* const*)keys);
-       }
+        {
+          char *keys[] = { "MESSAGE_ID=" TESTS_FAILED_MSGID, NULL };
+          gs_free char *msg = g_strdup_printf ("Test %s failed: %s", testname, tmp_error->message);
+          gs_log_structured_print (msg, (const char* const*)keys);
+          failed = TRUE;
+        }
+      g_clear_error (&tmp_error);
     }
-  if (tmp_error != NULL)
+  else
     {
-      g_propagate_error (error, tmp_error);
-      goto out;
+      ntests += 1;
+    }
+  
+  test_tmpdir_f = g_file_new_for_path (test_tmpdir);
+  if (!gs_shutil_rm_rf (test_tmpdir_f, cancellable, error))
+    goto out;
+
+  /* For now, just exit on the first failing test */
+  if (failed)
+    {
+      n_failed_tests++;
+    }
+  else
+    {
+      char *keys[] = { "MESSAGE_ID=" ONE_TEST_SUCCESS_MSGID, NULL };
+      gs_free char *msg = g_strdup_printf ("PASS: %s", testname);
+      gs_log_structured_print (msg, (const char* const*)keys);
     }
   
   ret = TRUE;
  out:
   g_clear_pointer (&keyfile, g_key_file_free);
-  g_clear_pointer (&exec_key, g_free);
   g_clear_pointer (&test_argv, g_strfreev);
   return ret;
 }
 
+static gint
+cmp_tests (gconstpointer a,
+           gconstpointer b)
+{
+  const char *apath = gs_file_get_path_cached (*((GFile**)a));
+  const char *bpath = gs_file_get_path_cached (*((GFile**)b));
+
+  return strcmp (apath, bpath);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -177,64 +220,75 @@ main (int argc, char **argv)
   GCancellable *cancellable = NULL;
   GError *local_error = NULL;
   GError **error = &local_error;
-  int i;
+  int i, j;
   gs_unref_object GFile *prefix_root = NULL;
-  GSList *testdirs = NULL;
-  GSList *iter;
+  gs_unref_ptrarray GPtrArray *tests = NULL;
+  GOptionContext *context;
+
+  context = g_option_context_new ("[PREFIX...] - Run installed tests");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
 
   prefix_root = g_file_new_for_path (DATADIR "/installed-tests");
 
-  if (argc <= 1)
+  tests = g_ptr_array_new_with_free_func (g_object_unref);
+
+  if (!gather_all_tests_recurse (prefix_root, "", tests,
+                                 cancellable, error))
+    goto out;
+
+  g_ptr_array_sort (tests, cmp_tests);
+
+  if (argc > 1)
     {
-      gs_unref_object GFileEnumerator *dir_enum = NULL;
-      gs_unref_object GFileInfo *info = NULL;
-      GError *tmp_error = NULL;
-
-      dir_enum = g_file_enumerate_children (prefix_root, "standard::name,standard::type",
-                                           G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                           cancellable, error);
-      if (!dir_enum)
-       goto out;
-
-      while ((info = g_file_enumerator_next_file (dir_enum, cancellable, &tmp_error)) != NULL)
-       {
-         if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
-           testdirs = g_slist_prepend (testdirs, g_file_get_child (prefix_root, g_file_info_get_name 
(info)));
-       }
-      if (tmp_error != NULL)
-       {
-         g_propagate_error (error, tmp_error);
-         goto out;
-       }
+      for (j = 0; j < tests->len; j++)
+        {
+          gboolean matches = FALSE;
+          GFile *test = tests->pdata[j];
+          gs_free char *test_relname = g_file_get_relative_path (prefix_root, test);
+          for (i = 1; i < argc; i++)
+            {
+              const char *prefix = argv[i];
+              if (g_str_has_prefix (test_relname, prefix))
+                {
+                  matches = TRUE;
+                  break;
+                }
+            }
+          if (matches)
+            {
+              if (opt_list)
+                g_print ("%s\n", test_relname);
+              else if (!run_test (prefix_root, test, cancellable, error))
+                goto out;
+            }
+        }
     }
   else
     {
-      for (i = 1; i < argc; i++)
-       {
-         testdirs = g_slist_prepend (testdirs, g_file_get_child (prefix_root, argv[i]));
-       }
+      for (i = 0; i < tests->len; i++)
+        {
+          GFile *test = tests->pdata[i];
+          gs_free char *test_relname = g_file_get_relative_path (prefix_root, test);
+          if (opt_list)
+            g_print ("%s\n", test_relname);
+          else if (!run_test (prefix_root, test, cancellable, error))
+            goto out;
+        }
     }
-  
-  testdirs = g_slist_reverse (testdirs);
 
-  for (iter = testdirs; iter; iter = iter->next)
+  if (!opt_list)
     {
-      GFile *path = iter->data;
-      if (!run_tests_in_directory (path, cancellable, error))
-       goto out;
+      char *keys[] = { "MESSAGE_ID=" TESTS_SUCCESS_MSGID, NULL };
+      gs_free char *msg = g_strdup_printf ("SUMMARY: total: %u passed: %d skipped: %d failed: %d",
+                                           tests->len, ntests, n_skipped_tests, n_failed_tests);
+      gs_log_structured_print (msg, (const char *const*)keys);
     }
-
-  {
-    char *keys[] = { "MESSAGE_ID=" TESTS_SUCCESS_MSGID, NULL };
-    gs_free char *msg = g_strdup_printf ("SUMMARY: passed: %d skipped: %d failed: %d",
-                                        ntests, n_skipped_tests, n_failed_tests);
-    gs_log_structured_print (msg, (const char *const*)keys);
-  }
   
   ret = TRUE;
  out:
-  g_slist_foreach (testdirs, (GFunc)g_object_unref, NULL);
-  g_slist_free (testdirs);
   if (!ret)
     {
       char *keys[] = { "MESSAGE_ID=" TESTS_FAILED_MSGID, NULL };


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]