tests,json: Fix and improve t-json
[gpgme.git] / tests / json / t-json.c
1 /* t-json.c - Regression test.
2    Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
3                       Software engineering by Intevation GmbH
4
5    This file is part of GPGME.
6
7    GPGME is free software; you can redistribute it and/or modify it
8    under the terms of the GNU Lesser General Public License as
9    published by the Free Software Foundation; either version 2.1 of
10    the License, or (at your option) any later version.
11
12    GPGME is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <sys/stat.h>
31
32 #include <gpgme.h>
33 #include <gpg-error.h>
34
35 #include "../gpg/t-support.h"
36 #include "../../src/cJSON.h"
37
38 /* Register tests here */
39 static const char*tests[] = { "t-config", NULL };
40
41 static int verbose = 0;
42
43 static char *
44 get_file (const char *fname)
45 {
46   gpg_error_t err;
47   gpgrt_stream_t fp;
48   struct stat st;
49   char *buf;
50   size_t buflen;
51
52   fp = gpgrt_fopen (fname, "r");
53   if (!fp)
54     {
55       err = gpg_error_from_syserror ();
56       fprintf (stderr, "Error: can't open '%s': %s\n", fname,
57                gpg_strerror (err));
58       return NULL;
59     }
60
61   if (fstat (gpgrt_fileno(fp), &st))
62     {
63       err = gpg_error_from_syserror ();
64       fprintf (stderr, "Error: can't stat '%s': %s\n", fname,
65                gpg_strerror (err));
66       gpgrt_fclose (fp);
67       return NULL;
68     }
69
70   buflen = st.st_size;
71   buf = malloc (buflen+1);
72   if (!buf)
73     {
74       fprintf (stderr, "Error: no mem\n");
75       gpgrt_fclose (fp);
76       return NULL;
77     }
78
79   if (gpgrt_fread (buf, buflen, 1, fp) != 1)
80     {
81       err = gpg_error_from_syserror ();
82       fprintf (stderr, "error reading '%s': %s\n", fname, gpg_strerror (err));
83       gpgrt_fclose (fp);
84       free (buf);
85       return NULL;
86     }
87   buf[buflen] = 0;
88   gpgrt_fclose (fp);
89
90   return buf;
91 }
92
93 /* Check that the element needle exists in hay. Returns 0 if
94    the needle was found. */
95 int
96 test_contains (cjson_t needle, cjson_t hay)
97 {
98   /*fprintf (stderr, "checking \n%s\n -------against-------- \n%s\n",
99            cJSON_Print (needle), cJSON_Print (hay)); */
100
101   /* Type check. This automatically checks bool vals and NULL */
102   if (needle->type != hay->type)
103     {
104       if (verbose)
105         fprintf (stderr, "type mismatch expected %i got %i\n", needle->type,
106                  hay->type);
107       return 1;
108     }
109
110   /* First the simple types */
111   if (cjson_is_number (needle))
112     {
113       if (needle->valueint != hay->valueint)
114         {
115           if (verbose)
116             fprintf (stderr, "Value mismatch. Expected %i got %i\n",
117                      needle->valueint, hay->valueint);
118           return 1;
119         }
120     }
121   if (cjson_is_string (needle))
122     {
123       if (strcmp (needle->valuestring, hay->valuestring))
124         {
125           if (verbose)
126             fprintf (stderr, "String mismatch Expected '%s' got '%s'\n",
127                      needle->valuestring, hay->valuestring);
128           return 1;
129         }
130     }
131
132   /* Now the complex types */
133   if (needle->child)
134     {
135       if (!hay->child)
136         {
137           fprintf (stderr, "Depth mismatch. Expected child for %s\n",
138                    nonnull (needle->string));
139         }
140       if (test_contains (needle->child, hay->child))
141         {
142           return 1;
143         }
144     }
145
146   if (needle->prev)
147     {
148       return 0;
149     }
150
151   /* Walk elements of an array */
152   for (cjson_t it = needle->next; it; it = it->next)
153     {
154       int found = 0;
155       if (!it->string && it->child)
156         {
157           /* Try out all other anonymous children on the same level */
158           cjson_t hit = hay;
159           /* Return to the beginning */
160           while (hit->prev)
161             {
162               hit = hit->prev;
163             }
164           for (; hit && hit->child; hit = hit->next)
165             {
166               found |= !test_contains (it->child, hit->child);
167               if (found)
168                 {
169                   break;
170                 }
171             }
172           if (!found)
173             {
174               return 1;
175             }
176           continue;
177         }
178
179       /* Try the children in the haystack */
180       for (cjson_t hit = hay; hit; hit = hit->next)
181         {
182           if (hit->string && it->string &&
183               !strcmp (hit->string, it->string))
184             {
185               found = 1;
186               if (test_contains (it, hit))
187                 {
188                   return 1;
189                 }
190             }
191         }
192       if (!found)
193         {
194           if (verbose)
195             fprintf (stderr, "Failed to find '%s' in list\n",
196                      nonnull (it->string));
197           return 1;
198         }
199     }
200   return 0;
201 }
202
203
204 int
205 check_response (const char *response, const char *expected)
206 {
207   cjson_t hay;
208   cjson_t needle;
209   int rc;
210   size_t erroff;
211
212   hay = cJSON_Parse (response, &erroff);
213
214   if (!hay)
215     {
216       fprintf (stderr, "Failed to parse json at %i:\n%s\n", (int) erroff,
217                response);
218       return 1;
219     }
220   needle = cJSON_Parse (expected, &erroff);
221   if (!needle)
222     {
223       fprintf (stderr, "Failed to parse json at %i:\n%s\n", (int) erroff,
224                expected);
225       cJSON_Delete (hay);
226       return 1;
227     }
228
229   rc = test_contains (needle, hay);
230
231   cJSON_Delete (needle);
232   cJSON_Delete (hay);
233   return rc;
234 }
235
236
237 int
238 run_test (const char *test, const char *gpgme_json)
239 {
240   gpgme_ctx_t ctx;
241   gpgme_data_t json_stdin = NULL;
242   gpgme_data_t json_stdout = NULL;
243   gpgme_data_t json_stderr = NULL;
244   char *test_in;
245   char *test_out;
246   const char *argv[3];
247   char *response;
248   char *expected = NULL;
249   size_t response_size;
250   int rc = 0;
251   const char *top_srcdir = getenv ("top_srcdir");
252
253   if (!top_srcdir)
254     {
255       fprintf (stderr, "Error top_srcdir environment variable not set\n");
256       exit(1);
257     }
258
259   gpgrt_asprintf (&test_in, "%s/tests/json/%s.in",
260                   top_srcdir, test);
261   gpgrt_asprintf (&test_out, "%s/tests/json/%s.out",
262                   top_srcdir, test);
263
264   printf ("Running %s...\n", test);
265
266   fail_if_err (gpgme_new (&ctx));
267
268   gpgme_set_protocol (ctx, GPGME_PROTOCOL_SPAWN);
269
270   fail_if_err (gpgme_data_new_from_file (&json_stdin, test_in, 1));
271   fail_if_err (gpgme_data_new (&json_stdout));
272   fail_if_err (gpgme_data_new (&json_stderr));
273
274   argv[0] = gpgme_json;
275   argv[1] = "-s";
276   argv[2] = NULL;
277
278   fail_if_err (gpgme_op_spawn (ctx, gpgme_json, argv,
279                                json_stdin,
280                                json_stdout,
281                                json_stderr,
282                                0));
283   response = gpgme_data_release_and_get_mem (json_stdout,
284                                              &response_size);
285   if (response_size)
286     {
287       expected = get_file (test_out);
288
289       test (expected);
290
291       rc = check_response (response, expected);
292     }
293   else
294     {
295       rc = 1;
296     }
297
298   if (!rc)
299     {
300       printf (" success\n");
301       gpgme_data_release (json_stderr);
302     }
303   else
304     {
305       char *buf;
306       size_t size;
307
308       buf = gpgme_data_release_and_get_mem (json_stderr, &size);
309       printf (" failed%s\n", response_size ? "" :
310                              ", no response from gpgme-json");
311       if (size)
312         {
313           printf ("gpgme-json stderr:\n%.*s\n", (int)size, buf);
314         }
315       free (buf);
316     }
317
318   free (test_out);
319   free (test_in);
320   free (response);
321   free (expected);
322   gpgme_data_release (json_stdin);
323   gpgme_release (ctx);
324
325   return rc;
326 }
327
328 int
329 main (int argc, char *argv[])
330 {
331   const char *gpgme_json = getenv ("gpgme_json");
332
333   if (argc == 2 && !strcmp (argv[1], "--verbose"))
334     {
335       /* Note that verbose will print out lots of mismatchs
336          because we have to try trough anonymous objects */
337       verbose = 1;
338     }
339
340
341   init_gpgme (GPGME_PROTOCOL_SPAWN);
342
343   for (const char **test = tests; *test; test++)
344     {
345       if (run_test (*test, gpgme_json))
346         {
347           exit(1);
348         }
349     }
350   return 0;
351 }