1/*
2 * Copyright (c) 2015, Vsevolod Stakhov
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *	 * Redistributions of source code must retain the above copyright
8 *	   notice, this list of conditions and the following disclaimer.
9 *	 * Redistributions in binary form must reproduce the above copyright
10 *	   notice, this list of conditions and the following disclaimer in the
11 *	   documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "ucl.h"
26#include "ucl_internal.h"
27#include <ctype.h>
28
29static const int niter = 20;
30static const int ntests = 10;
31static const int nelt = 20;
32
33static int recursion = 0;
34
35typedef ucl_object_t* (*ucl_msgpack_test)(void);
36
37static ucl_object_t* ucl_test_integer (void);
38static ucl_object_t* ucl_test_string (void);
39static ucl_object_t* ucl_test_boolean (void);
40static ucl_object_t* ucl_test_map (void);
41static ucl_object_t* ucl_test_array (void);
42static ucl_object_t* ucl_test_large_map (void);
43static ucl_object_t* ucl_test_large_array (void);
44static ucl_object_t* ucl_test_large_string (void);
45static ucl_object_t* ucl_test_null (void);
46
47ucl_msgpack_test tests[] = {
48		ucl_test_integer,
49		ucl_test_string,
50		ucl_test_boolean,
51		ucl_test_map,
52		ucl_test_array,
53		ucl_test_null
54};
55
56#define NTESTS (sizeof(tests) / sizeof(tests[0]))
57
58typedef struct
59{
60	uint64_t state;
61	uint64_t inc;
62} pcg32_random_t;
63
64pcg32_random_t rng;
65
66/*
67 * From http://www.pcg-random.org/
68 */
69static uint32_t
70pcg32_random (void)
71{
72	uint64_t oldstate = rng.state;
73
74	rng.state = oldstate * 6364136223846793005ULL + (rng.inc | 1);
75	uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
76	uint32_t rot = oldstate >> 59u;
77	return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
78}
79
80static const char *
81random_key (size_t *lenptr)
82{
83	static char keybuf[512];
84	int keylen, i;
85	char c;
86
87	keylen = pcg32_random () % (sizeof (keybuf) - 1) + 1;
88
89	for (i = 0; i < keylen; i ++) {
90		do {
91			c = pcg32_random () & 0xFF;
92		} while (!isgraph (c));
93
94		keybuf[i] = c;
95	}
96
97	*lenptr = keylen;
98	return keybuf;
99}
100
101int
102main (int argc, char **argv)
103{
104	int fd, i, j;
105	uint32_t sel;
106	ucl_object_t *obj, *elt;
107	struct ucl_parser *parser;
108	size_t klen, elen, elen2;
109	const char *key;
110	unsigned char *emitted, *emitted2;
111	FILE *out;
112	const char *fname_out = NULL;
113
114	switch (argc) {
115	case 2:
116		fname_out = argv[1];
117		break;
118	}
119
120	/* Seed prng */
121	fd = open ("/dev/urandom", O_RDONLY);
122	assert (fd != -1);
123	assert (read (fd, &rng, sizeof (rng)) == sizeof (rng));
124	close (fd);
125
126	for (i = 0; i < niter; i ++) {
127		if (fname_out != NULL) {
128			out = fopen (fname_out, "w");
129			if (out == NULL) {
130				exit (-errno);
131			}
132		}
133		else {
134			out = NULL;
135		}
136
137		/* Generate phase */
138		obj = ucl_object_typed_new (UCL_OBJECT);
139
140		for (j = 0; j < ntests; j ++) {
141			sel = pcg32_random () % NTESTS;
142
143			key = random_key (&klen);
144			recursion = 0;
145			elt = tests[sel]();
146			assert (elt != NULL);
147			assert (klen != 0);
148
149			ucl_object_insert_key (obj, elt, key, klen, true);
150		}
151
152		key = random_key (&klen);
153		elt = ucl_test_large_array ();
154		ucl_object_insert_key (obj, elt, key, klen, true);
155
156		key = random_key (&klen);
157		elt = ucl_test_large_map ();
158		ucl_object_insert_key (obj, elt, key, klen, true);
159
160		key = random_key (&klen);
161		elt = ucl_test_large_string ();
162		ucl_object_insert_key (obj, elt, key, klen, true);
163
164		emitted = ucl_object_emit_len (obj, UCL_EMIT_MSGPACK, &elen);
165
166		assert (emitted != NULL);
167
168		if (out) {
169			fprintf (out, "%*.s\n", (int)elen, emitted);
170
171			fclose (out);
172		}
173		ucl_object_unref (obj);
174
175		parser = ucl_parser_new (0);
176
177		if (!ucl_parser_add_chunk_full (parser, emitted, elen, 0,
178				UCL_DUPLICATE_APPEND, UCL_PARSE_MSGPACK)) {
179			fprintf (stderr, "error parsing input: %s",
180					ucl_parser_get_error (parser));
181			assert (0);
182		}
183
184		obj = ucl_parser_get_object (parser);
185		assert (obj != NULL);
186
187		emitted2 = ucl_object_emit_len (obj, UCL_EMIT_MSGPACK, &elen2);
188
189		assert (emitted2 != NULL);
190		assert (elen2 == elen);
191		assert (memcmp (emitted, emitted2, elen) == 0);
192
193		ucl_parser_free (parser);
194		ucl_object_unref (obj);
195		free (emitted);
196		free (emitted2);
197	}
198
199	return 0;
200}
201
202
203static ucl_object_t*
204ucl_test_integer (void)
205{
206	ucl_object_t *res;
207	int count, i;
208	uint64_t cur;
209	double curf;
210
211	res = ucl_object_typed_new (UCL_ARRAY);
212	count = pcg32_random () % nelt;
213
214	for (i = 0; i < count; i ++) {
215		cur = ((uint64_t)pcg32_random ()) << 32 | pcg32_random ();
216		ucl_array_append (res, ucl_object_fromint (cur % 128));
217		ucl_array_append (res, ucl_object_fromint (-(cur % 128)));
218		cur = ((uint64_t)pcg32_random ()) << 32 | pcg32_random ();
219		ucl_array_append (res, ucl_object_fromint (cur % UINT16_MAX));
220		ucl_array_append (res, ucl_object_fromint (-(cur % INT16_MAX)));
221		cur = ((uint64_t)pcg32_random ()) << 32 | pcg32_random ();
222		ucl_array_append (res, ucl_object_fromint (cur % UINT32_MAX));
223		ucl_array_append (res, ucl_object_fromint (-(cur % INT32_MAX)));
224		cur = ((uint64_t)pcg32_random ()) << 32 | pcg32_random ();
225		ucl_array_append (res, ucl_object_fromint (cur));
226		ucl_array_append (res, ucl_object_fromint (-cur));
227		/* Double version */
228		cur = ((uint64_t)pcg32_random ()) << 32 | pcg32_random ();
229		curf = (cur % 128) / 19 * 16;
230		ucl_array_append (res, ucl_object_fromdouble (curf));
231		cur = ((uint64_t)pcg32_random ()) << 32 | pcg32_random ();
232		curf = -(cur % 128) / 19 * 16;
233		ucl_array_append (res, ucl_object_fromdouble (curf));
234		cur = ((uint64_t)pcg32_random ()) << 32 | pcg32_random ();
235		curf = (cur % 65536) / 19 * 16;
236		ucl_array_append (res, ucl_object_fromdouble (curf));
237		ucl_array_append (res, ucl_object_fromdouble (-curf));
238		cur = ((uint64_t)pcg32_random ()) << 32 | pcg32_random ();
239		curf = (cur % INT32_MAX) / 19 * 16;
240		ucl_array_append (res, ucl_object_fromdouble (curf));
241		cur = ((uint64_t)pcg32_random ()) << 32 | pcg32_random ();
242		memcpy (&curf, &cur, sizeof (curf));
243		ucl_array_append (res, ucl_object_fromint (cur));
244	}
245
246	return res;
247}
248
249static ucl_object_t*
250ucl_test_string (void)
251{
252	ucl_object_t *res, *elt;
253	int count, i;
254	uint32_t cur_len;
255	char *str;
256
257	res = ucl_object_typed_new (UCL_ARRAY);
258	count = pcg32_random () % nelt;
259
260	for (i = 0; i < count; i ++) {
261		while ((cur_len = pcg32_random ()) % 128 == 0);
262
263		str = malloc (cur_len % 128);
264		ucl_array_append (res, ucl_object_fromstring_common (str, cur_len % 128,
265				UCL_STRING_RAW));
266		free (str);
267
268		while ((cur_len = pcg32_random ()) % 512 == 0);
269		str = malloc (cur_len % 512);
270		ucl_array_append (res, ucl_object_fromstring_common (str, cur_len % 512,
271				UCL_STRING_RAW));
272		free (str);
273
274		while ((cur_len = pcg32_random ()) % 128 == 0);
275		str = malloc (cur_len % 128);
276		elt = ucl_object_fromstring_common (str, cur_len % 128,
277				UCL_STRING_RAW);
278		elt->flags |= UCL_OBJECT_BINARY;
279		ucl_array_append (res, elt);
280		free (str);
281
282		while ((cur_len = pcg32_random ()) % 512 == 0);
283		str = malloc (cur_len % 512);
284		elt = ucl_object_fromstring_common (str, cur_len % 512,
285				UCL_STRING_RAW);
286		elt->flags |= UCL_OBJECT_BINARY;
287		ucl_array_append (res, elt);
288		free (str);
289	}
290
291	/* One large string */
292	str = malloc (65537);
293	elt = ucl_object_fromstring_common (str, 65537,
294			UCL_STRING_RAW);
295	elt->flags |= UCL_OBJECT_BINARY;
296	ucl_array_append (res, elt);
297	free (str);
298
299	return res;
300}
301
302static ucl_object_t*
303ucl_test_boolean (void)
304{
305	ucl_object_t *res;
306	int count, i;
307
308	res = ucl_object_typed_new (UCL_ARRAY);
309	count = pcg32_random () % nelt;
310
311	for (i = 0; i < count; i ++) {
312		ucl_array_append (res, ucl_object_frombool (pcg32_random () % 2));
313	}
314
315	return res;
316}
317
318static ucl_object_t*
319ucl_test_map (void)
320{
321	ucl_object_t *res, *cur;
322	int count, i;
323	uint32_t cur_len, sel;
324	size_t klen;
325	const char *key;
326
327	res = ucl_object_typed_new (UCL_OBJECT);
328	count = pcg32_random () % nelt;
329
330	recursion ++;
331
332	for (i = 0; i < count; i ++) {
333
334		if (recursion > 10) {
335			for (;;) {
336				sel = pcg32_random () % NTESTS;
337				if (tests[sel] != ucl_test_map &&
338						tests[sel] != ucl_test_array) {
339					break;
340				}
341			}
342		}
343		else {
344			sel = pcg32_random () % NTESTS;
345		}
346
347		key = random_key (&klen);
348		cur = tests[sel]();
349		assert (cur != NULL);
350		assert (klen != 0);
351
352		ucl_object_insert_key (res, cur, key, klen, true);
353
354		/* Multi value key */
355		cur = tests[sel]();
356		assert (cur != NULL);
357
358		ucl_object_insert_key (res, cur, key, klen, true);
359	}
360
361	return res;
362}
363
364static ucl_object_t*
365ucl_test_large_map (void)
366{
367	ucl_object_t *res, *cur;
368	int count, i;
369	uint32_t cur_len;
370	size_t klen;
371	const char *key;
372
373	res = ucl_object_typed_new (UCL_OBJECT);
374	count = 65537;
375
376	recursion ++;
377
378	for (i = 0; i < count; i ++) {
379		key = random_key (&klen);
380		cur = ucl_test_boolean ();
381		assert (cur != NULL);
382		assert (klen != 0);
383
384		ucl_object_insert_key (res, cur, key, klen, true);
385	}
386
387	return res;
388}
389
390static ucl_object_t*
391ucl_test_array (void)
392{
393	ucl_object_t *res, *cur;
394	int count, i;
395	uint32_t cur_len, sel;
396
397	res = ucl_object_typed_new (UCL_ARRAY);
398	count = pcg32_random () % nelt;
399
400	recursion ++;
401
402	for (i = 0; i < count; i ++) {
403		if (recursion > 10) {
404			for (;;) {
405				sel = pcg32_random () % NTESTS;
406				if (tests[sel] != ucl_test_map &&
407						tests[sel] != ucl_test_array) {
408					break;
409				}
410			}
411		}
412		else {
413			sel = pcg32_random () % NTESTS;
414		}
415
416		cur = tests[sel]();
417		assert (cur != NULL);
418
419		ucl_array_append (res, cur);
420	}
421
422	return res;
423}
424
425static ucl_object_t*
426ucl_test_large_array (void)
427{
428	ucl_object_t *res, *cur;
429	int count, i;
430	uint32_t cur_len;
431
432	res = ucl_object_typed_new (UCL_ARRAY);
433	count = 65537;
434
435	recursion ++;
436
437	for (i = 0; i < count; i ++) {
438		cur = ucl_test_boolean ();
439		assert (cur != NULL);
440
441		ucl_array_append (res, cur);
442	}
443
444	return res;
445}
446
447static ucl_object_t*
448ucl_test_large_string (void)
449{
450	ucl_object_t *res;
451	char *str;
452	uint32_t cur_len;
453
454	while ((cur_len = pcg32_random ()) % 100000 == 0);
455	str = malloc (cur_len % 100000);
456	res = ucl_object_fromstring_common (str, cur_len % 100000,
457				UCL_STRING_RAW);
458	res->flags |= UCL_OBJECT_BINARY;
459	free (str);
460
461	return res;
462}
463
464static ucl_object_t*
465ucl_test_null (void)
466{
467	return ucl_object_typed_new (UCL_NULL);
468}
469