From 0c31837766e016227b3c8dfd44c476949cd4741e Mon Sep 17 00:00:00 2001 From: Andre Heinecke Date: Wed, 14 Nov 2018 11:47:59 +0100 Subject: [PATCH] tests: Add json testrunner * 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 | 1 + tests/Makefile.am | 2 +- tests/json/t-config.in | 4 + tests/json/t-config.out | 19 +++ tests/json/t-json.c | 344 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 369 insertions(+), 1 deletion(-) create mode 100644 tests/json/t-config.in create mode 100644 tests/json/t-config.out create mode 100644 tests/json/t-json.c diff --git a/configure.ac b/configure.ac index b733a9c2..4976b5b2 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/tests/Makefile.am b/tests/Makefile.am index b5825d20..29e0c424 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 index 00000000..22b37ee5 --- /dev/null +++ b/tests/json/t-config.in @@ -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 index 00000000..fce36a0f --- /dev/null +++ b/tests/json/t-config.out @@ -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 index 00000000..ec294f72 --- /dev/null +++ b/tests/json/t-json.c @@ -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 +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#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; +}