tests: Add json testrunner
authorAndre Heinecke <aheinecke@intevation.de>
Wed, 14 Nov 2018 10:47:59 +0000 (11:47 +0100)
committerAndre Heinecke <aheinecke@intevation.de>
Wed, 14 Nov 2018 10:51:08 +0000 (11:51 +0100)
* configure.ac: Configure makefile.
* tests/Makefile.am: Run json tests if gpg tests are run.
* tests/json/t-json.c: New testrunner for json tests.
* tests/json/t-config.in, tests/json/t-config.out: First test.
--
The idea of this test runner is that it only looks for parts
in the output. This should allow it to write robust tests
that check for the basics in the output but don't fail when
the output is extended or slightly changed.

configure.ac
tests/Makefile.am
tests/json/t-config.in [new file with mode: 0644]
tests/json/t-config.out [new file with mode: 0644]
tests/json/t-json.c [new file with mode: 0644]

index b733a9c..4976b5b 100644 (file)
@@ -853,6 +853,7 @@ AC_CONFIG_FILES(Makefile src/Makefile
                 tests/gpg/Makefile
                 tests/gpgsm/Makefile
                 tests/opassuan/Makefile
+                tests/json/Makefile
                doc/Makefile
                 src/versioninfo.rc
                 src/gpgme.pc
index b5825d2..29e0c42 100644 (file)
@@ -38,7 +38,7 @@ noinst_PROGRAMS = $(TESTS) run-keylist run-export run-import run-sign \
 
 
 if RUN_GPG_TESTS
-gpgtests = gpg
+gpgtests = gpg json
 else
 gpgtests =
 endif
diff --git a/tests/json/t-config.in b/tests/json/t-config.in
new file mode 100644 (file)
index 0000000..22b37ee
--- /dev/null
@@ -0,0 +1,4 @@
+{
+    "op": "config",
+    "component": "gpg"
+}
diff --git a/tests/json/t-config.out b/tests/json/t-config.out
new file mode 100644 (file)
index 0000000..fce36a0
--- /dev/null
@@ -0,0 +1,19 @@
+{
+    "components":   [{
+        "name": "gpg",
+            "description":  "OpenPGP",
+            "options":      [{
+                "name": "Monitor",
+                "flags":        1,
+                "level":        0,
+                "type": 0,
+                "alt_type":     0
+            }, {
+                "name": "verbose",
+                "description":  "verbose",
+                "level":        0,
+                "type": 0,
+                "alt_type":     0
+            }]
+    }]
+}
diff --git a/tests/json/t-json.c b/tests/json/t-json.c
new file mode 100644 (file)
index 0000000..ec294f7
--- /dev/null
@@ -0,0 +1,344 @@
+/* t-json.c - Regression test.
+   Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+                      Software engineering by Intevation GmbH
+
+   This file is part of GPGME.
+
+   GPGME is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of
+   the License, or (at your option) any later version.
+
+   GPGME is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <gpgme.h>
+#include <gpg-error.h>
+
+#include "../gpg/t-support.h"
+#include "../../src/cJSON.h"
+
+/* Register tests here */
+static const char*tests[] = { "t-config", NULL };
+
+static int verbose = 0;
+
+static char *
+get_file (const char *fname)
+{
+  gpg_error_t err;
+  gpgrt_stream_t fp;
+  struct stat st;
+  char *buf;
+  size_t buflen;
+
+  fp = gpgrt_fopen (fname, "r");
+  if (!fp)
+    {
+      err = gpg_error_from_syserror ();
+      fprintf (stderr, "Error: can't open '%s': %s\n", fname,
+               gpg_strerror (err));
+      return NULL;
+    }
+
+  if (fstat (gpgrt_fileno(fp), &st))
+    {
+      err = gpg_error_from_syserror ();
+      fprintf (stderr, "Error: can't stat '%s': %s\n", fname,
+               gpg_strerror (err));
+      gpgrt_fclose (fp);
+      return NULL;
+    }
+
+  buflen = st.st_size;
+  buf = malloc (buflen+1);
+  if (!buf)
+    {
+      fprintf (stderr, "Error: no mem\n");
+      gpgrt_fclose (fp);
+      return NULL;
+    }
+
+  if (gpgrt_fread (buf, buflen, 1, fp) != 1)
+    {
+      err = gpg_error_from_syserror ();
+      fprintf (stderr, "error reading '%s': %s\n", fname, gpg_strerror (err));
+      gpgrt_fclose (fp);
+      free (buf);
+      return NULL;
+    }
+  buf[buflen] = 0;
+  gpgrt_fclose (fp);
+
+  return buf;
+}
+
+/* Check that the element needle exists in hay. Returns 0 if
+   the needle was found. */
+int
+test_contains (cjson_t needle, cjson_t hay)
+{
+  /*fprintf (stderr, "checking \n%s\n -------against-------- \n%s\n",
+           cJSON_Print (needle), cJSON_Print (hay)); */
+
+  /* Type check. This automatically checks bool vals and NULL */
+  if (needle->type != hay->type)
+    {
+      if (verbose)
+        fprintf (stderr, "type mismatch expected %i got %i\n", needle->type,
+                 hay->type);
+      return 1;
+    }
+
+  /* First the simple types */
+  if (cjson_is_number (needle))
+    {
+      if (needle->valueint != hay->valueint)
+        {
+          if (verbose)
+            fprintf (stderr, "Value mismatch. Expected %i got %i\n",
+                     needle->valueint, hay->valueint);
+          return 1;
+        }
+    }
+  if (cjson_is_string (needle))
+    {
+      if (strcmp (needle->valuestring, hay->valuestring))
+        {
+          if (verbose)
+            fprintf (stderr, "String mismatch Expected '%s' got '%s'\n",
+                     needle->valuestring, hay->valuestring);
+          return 1;
+        }
+    }
+
+  /* Now the complex types */
+  if (needle->child)
+    {
+      if (!hay->child)
+        {
+          fprintf (stderr, "Depth mismatch. Expected child for %s\n",
+                   nonnull (needle->string));
+        }
+      if (test_contains (needle->child, hay->child))
+        {
+          return 1;
+        }
+    }
+
+  if (needle->prev)
+    {
+      return 0;
+    }
+
+  /* Walk elements of an array */
+  for (cjson_t it = needle->next; it; it = it->next)
+    {
+      int found = 0;
+      if (!it->string && it->child)
+        {
+          /* Try out all other anonymous children on the same level */
+          cjson_t hit = hay;
+          /* Return to the beginning */
+          while (hit->prev)
+            {
+              hit = hit->prev;
+            }
+          for (; hit && hit->child; hit = hit->next)
+            {
+              found |= !test_contains (it->child, hit->child);
+              if (found)
+                {
+                  break;
+                }
+            }
+          if (!found)
+            {
+              return 1;
+            }
+          continue;
+        }
+
+      /* Try the children in the haystack */
+      for (cjson_t hit = hay; hit; hit = hit->next)
+        {
+          if (hit->string && it->string &&
+              !strcmp (hit->string, it->string))
+            {
+              found = 1;
+              if (test_contains (it, hit))
+                {
+                  return 1;
+                }
+            }
+        }
+      if (!found)
+        {
+          if (verbose)
+            fprintf (stderr, "Failed to find '%s' in list\n",
+                     nonnull (it->string));
+          return 1;
+        }
+    }
+  return 0;
+}
+
+
+int
+check_response (const char *response, const char *expected)
+{
+  cjson_t hay;
+  cjson_t needle;
+  int rc;
+  size_t erroff;
+
+  hay = cJSON_Parse (response, &erroff);
+
+  if (!hay)
+    {
+      fprintf (stderr, "Failed to parse json at %i:\n%s\n", (int) erroff,
+               response);
+      return 1;
+    }
+  needle = cJSON_Parse (expected, &erroff);
+  if (!needle)
+    {
+      fprintf (stderr, "Failed to parse json at %i:\n%s\n", (int) erroff,
+               expected);
+      cJSON_Delete (hay);
+      return 1;
+    }
+
+  rc = test_contains (needle, hay);
+
+  cJSON_Delete (needle);
+  cJSON_Delete (hay);
+  return rc;
+}
+
+
+int
+run_test (const char *test, const char *gpgme_json)
+{
+  gpgme_ctx_t ctx;
+  gpgme_data_t json_stdin = NULL;
+  gpgme_data_t json_stdout = NULL;
+  gpgme_data_t json_stderr = NULL;
+  char *test_in;
+  char *test_out;
+  const char *argv[2];
+  char *response;
+  char *expected;
+  size_t response_size;
+  int rc = 0;
+  const char *top_srcdir = getenv ("top_srcdir");
+
+  if (!top_srcdir)
+    {
+      fprintf (stderr, "Error top_srcdir environment variable not set\n");
+      exit(1);
+    }
+
+  gpgrt_asprintf (&test_in, "%s//tests//json//%s.in",
+                  top_srcdir, test);
+  gpgrt_asprintf (&test_out, "%s//tests//json//%s.out",
+                  top_srcdir, test);
+
+  printf ("Running %s...\n", test);
+
+  fail_if_err (gpgme_new (&ctx));
+
+  gpgme_set_protocol (ctx, GPGME_PROTOCOL_SPAWN);
+
+  fail_if_err (gpgme_data_new_from_file (&json_stdin, test_in, 1));
+  fail_if_err (gpgme_data_new (&json_stdout));
+  fail_if_err (gpgme_data_new (&json_stderr));
+
+  argv[0] = gpgme_json;
+  argv[1] = "-s";
+
+  fail_if_err (gpgme_op_spawn (ctx, gpgme_json, argv,
+                               json_stdin,
+                               json_stdout,
+                               json_stderr,
+                               0));
+  response = gpgme_data_release_and_get_mem (json_stdout,
+                                             &response_size);
+  test (response_size);
+
+  expected = get_file (test_out);
+
+  test (expected);
+
+  rc = check_response (response, expected);
+
+  if (!rc)
+    {
+      printf (" success\n");
+      gpgme_data_release (json_stderr);
+    }
+  else
+    {
+      char *buf;
+      size_t size;
+
+      buf = gpgme_data_release_and_get_mem (json_stderr, &size);
+      printf (" failed\n");
+      if (size)
+        {
+          printf ("gpgme-json stderr:\n%.*s\n", (int)size, buf);
+        }
+      free (buf);
+    }
+
+  free (test_out);
+  free (test_in);
+  free (response);
+  free (expected);
+  gpgme_data_release (json_stdin);
+  gpgme_release (ctx);
+
+  return rc;
+}
+
+int
+main (int argc, char *argv[])
+{
+  const char *gpgme_json = getenv ("gpgme_json");
+
+  if (argc == 2 && !strcmp (argv[1], "--verbose"))
+    {
+      /* Note that verbose will print out lots of mismatchs
+         because we have to try trough anonymous objects */
+      verbose = 1;
+    }
+
+
+  init_gpgme (GPGME_PROTOCOL_SPAWN);
+
+  for (const char **test = tests; *test; test++)
+    {
+      if (run_test (*test, gpgme_json))
+        {
+          exit(1);
+        }
+    }
+  return 0;
+}