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.
This commit is contained in:
Andre Heinecke 2018-11-14 11:47:59 +01:00
parent c4aa4af50f
commit 0c31837766
No known key found for this signature in database
GPG Key ID: 2978E9D40CBABA5C
5 changed files with 369 additions and 1 deletions

View 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

View 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

4
tests/json/t-config.in Normal file
View File

@ -0,0 +1,4 @@
{
"op": "config",
"component": "gpg"
}

19
tests/json/t-config.out Normal file
View File

@ -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
}]
}]
}

344
tests/json/t-json.c Normal file
View File

@ -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;
}