1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; either version 2 of the License, or
5  *  (at your option) any later version.
6  *
7  *  This program is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *  GNU General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA  02110-1301,  USA
15  */
16 
17 /**
18  * @file test-engine-stuff.c
19  * @brief tools to set up random test data.
20  *
21  * Created by Linux Developers Group, 2001
22  * Updates Linas Vepstas July 2004
23 \todo replace with g_test routines from 2.16
24  */
25 
26 #include <sys/types.h>
27 #include <dirent.h>
28 #include <fcntl.h>
29 #include <glib.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include "config.h"
36 #include "qof.h"
37 #include "test-engine-stuff.h"
38 #include "test-stuff.h"
39 
40 static gboolean glist_strings_only = FALSE;
41 
42 static GHashTable *exclude_kvp_types = NULL;
43 static gint kvp_max_depth = 5;
44 static gint kvp_frame_max_elements = 10;
45 
46 gboolean gnc_engine_debug_random = FALSE;
47 
48 /* ========================================================== */
49 /* Set control parameters governing the run. */
50 
51 void
set_max_kvp_depth(gint max_kvp_depth)52 set_max_kvp_depth (gint max_kvp_depth)
53 {
54 	kvp_max_depth = MAX (max_kvp_depth, 1);
55 }
56 
57 void
set_max_kvp_frame_elements(gint max_kvp_frame_elements)58 set_max_kvp_frame_elements (gint max_kvp_frame_elements)
59 {
60 	kvp_frame_max_elements = MAX (max_kvp_frame_elements, 1);
61 }
62 
63 void
kvp_exclude_type(KvpValueType kvp_type)64 kvp_exclude_type (KvpValueType kvp_type)
65 {
66 	gint *key;
67 
68 	if (!exclude_kvp_types)
69 		exclude_kvp_types = g_hash_table_new (g_int_hash, g_int_equal);
70 
71 	key = g_new (gint, 1);
72 	*key = kvp_type;
73 
74 	g_hash_table_insert (exclude_kvp_types, key, exclude_kvp_types);
75 }
76 
77 static gboolean
kvp_type_excluded(KvpValueType kvp_type)78 kvp_type_excluded (KvpValueType kvp_type)
79 {
80 	gint key = kvp_type;
81 
82 	if (!exclude_kvp_types)
83 		return FALSE;
84 
85 	if (g_hash_table_lookup (exclude_kvp_types, &key))
86 		return TRUE;
87 
88 	return FALSE;
89 }
90 
91 void
random_glist_strings_only(gboolean strings_only)92 random_glist_strings_only (gboolean strings_only)
93 {
94 	glist_strings_only = strings_only;
95 }
96 
97 
98 /* ========================================================== */
99 
100 static gint borked = 80;
101 
102 static inline gboolean
do_bork(void)103 do_bork (void)
104 {
105 	if (1 == get_random_int_in_range (0, borked))
106 	{
107 		return TRUE;
108 	}
109 	return FALSE;
110 }
111 
112 /* ========================================================== */
113 /* GList stuff */
114 /*
115 static gpointer
116 get_random_list_element (GList *list)
117 {
118   g_return_val_if_fail (list, NULL);
119 
120   return g_list_nth_data (list,
121                           get_random_int_in_range (0,
122                                                    g_list_length (list) - 1));
123 }
124 */
125 static KvpValue *get_random_kvp_value_depth (int type, gint depth);
126 
127 static GList *
get_random_glist_depth(gint depth)128 get_random_glist_depth (gint depth)
129 {
130 	GList *ret = NULL;
131 	int count = get_random_int_in_range (1, 5);
132 	int i;
133 
134 	if (depth >= kvp_max_depth)
135 		return NULL;
136 
137 	for (i = 0; i < count; i++)
138 	{
139 		KvpValueType kvpt;
140 		KvpValue *value;
141 
142 		kvpt = glist_strings_only ? KVP_TYPE_STRING : -2;
143 
144 		do
145 		{
146 			value = get_random_kvp_value_depth (kvpt, depth + 1);
147 		}
148 		while (!value);
149 
150 		ret = g_list_prepend (ret, value);
151 	}
152 
153 	return ret;
154 }
155 
156 GList *
get_random_glist(void)157 get_random_glist (void)
158 {
159 	return get_random_glist_depth (0);
160 }
161 
162 /* ========================================================== */
163 /* Time/Date, GUID, binary data stuff */
164 
165 GUID *
get_random_guid(void)166 get_random_guid (void)
167 {
168 	GUID *ret;
169 
170 	ret = g_new (GUID, 1);
171 	guid_new (ret);
172 
173 	return ret;
174 }
175 
176 bin_data *
get_random_binary_data(void)177 get_random_binary_data (void)
178 {
179 	int len;
180 	bin_data *ret;
181 
182 	len = get_random_int_in_range (20, 100);
183 	ret = g_new (bin_data, 1);
184 	ret->data = g_new (guchar, len);
185 	ret->len = len;
186 
187 	for (len--; len >= 0; len--)
188 	{
189 		ret->data[len] = (guchar) get_random_int_in_range (0, 255);
190 	}
191 
192 	return ret;
193 }
194 
195 /* ========================================================== */
196 /* KVP stuff */
197 
198 static KvpFrame *get_random_kvp_frame_depth (gint depth);
199 
200 static KvpValue *
get_random_kvp_value_depth(int type,gint depth)201 get_random_kvp_value_depth (int type, gint depth)
202 {
203 	int datype = type;
204 	KvpValue *ret;
205 
206 	if (datype == -1)
207 	{
208 		datype = get_random_int_in_range (KVP_TYPE_GINT64, KVP_TYPE_FRAME);
209 	}
210 
211 	if (datype == -2)
212 	{
213 		datype =
214 			get_random_int_in_range (KVP_TYPE_GINT64, KVP_TYPE_FRAME - 1);
215 	}
216 
217 	if (datype == KVP_TYPE_FRAME && depth >= kvp_max_depth)
218 		return NULL;
219 
220 	if (datype == KVP_TYPE_GLIST && depth >= kvp_max_depth)
221 		return NULL;
222 
223 	if (kvp_type_excluded (datype))
224 		return NULL;
225 
226 	switch (datype)
227 	{
228 	case KVP_TYPE_GINT64:
229 		ret = kvp_value_new_gint64 (get_random_gint64 ());
230 		break;
231 
232 	case KVP_TYPE_DOUBLE:
233 		ret = NULL;
234 		break;
235 
236 	case KVP_TYPE_NUMERIC:
237 		ret = kvp_value_new_numeric (get_random_qof_numeric ());
238 		break;
239 
240 	case KVP_TYPE_STRING:
241 		{
242 			gchar *tmp_str;
243 			tmp_str = get_random_string ();
244 			if (!tmp_str)
245 				return NULL;
246 
247 			ret = kvp_value_new_string (tmp_str);
248 			g_free (tmp_str);
249 		}
250 		break;
251 
252 	case KVP_TYPE_GUID:
253 		{
254 			GUID *tmp_guid;
255 			tmp_guid = get_random_guid ();
256 			ret = kvp_value_new_guid (tmp_guid);
257 			g_free (tmp_guid);
258 		}
259 		break;
260 	case KVP_TYPE_BINARY:
261 		{
262 			bin_data *tmp_data;
263 			tmp_data = get_random_binary_data ();
264 			ret = kvp_value_new_binary (tmp_data->data, tmp_data->len);
265 			g_free (tmp_data->data);
266 			g_free (tmp_data);
267 		}
268 		break;
269 
270 	case KVP_TYPE_GLIST:
271 		ret = kvp_value_new_glist_nc (get_random_glist_depth (depth + 1));
272 		break;
273 
274 	case KVP_TYPE_FRAME:
275 		{
276 			KvpFrame *tmp_frame;
277 			tmp_frame = get_random_kvp_frame_depth (depth + 1);
278 			ret = kvp_value_new_frame (tmp_frame);
279 			kvp_frame_delete (tmp_frame);
280 		}
281 		break;
282 
283 	default:
284 		ret = NULL;
285 		break;
286 	}
287 	return ret;
288 }
289 
290 static KvpFrame *
get_random_kvp_frame_depth(gint depth)291 get_random_kvp_frame_depth (gint depth)
292 {
293 	KvpFrame *ret;
294 	int vals_to_add;
295 	gboolean val_added;
296 
297 	if (depth >= kvp_max_depth)
298 		return NULL;
299 
300 	ret = kvp_frame_new ();
301 
302 	vals_to_add = get_random_int_in_range (1, kvp_frame_max_elements);
303 	val_added = FALSE;
304 
305 	for (; vals_to_add > 0; vals_to_add--)
306 	{
307 		gchar *key;
308 		KvpValue *val;
309 
310 		key = NULL;
311 		while (key == NULL)
312 		{
313 			key = get_random_string_without ("/");
314 			if (*key == '\0')
315 			{
316 				g_free (key);
317 				key = NULL;
318 			}
319 		}
320 
321 		val = get_random_kvp_value_depth (-1, depth + 1);
322 		if (!val)
323 		{
324 			g_free (key);
325 			if (!val_added)
326 				vals_to_add++;
327 			continue;
328 		}
329 
330 		val_added = TRUE;
331 
332 		kvp_frame_set_slot_nc (ret, key, val);
333 
334 		g_free (key);
335 	}
336 
337 	return ret;
338 }
339 
340 KvpFrame *
get_random_kvp_frame(void)341 get_random_kvp_frame (void)
342 {
343 	return get_random_kvp_frame_depth (0);
344 }
345 
346 KvpValue *
get_random_kvp_value(int type)347 get_random_kvp_value (int type)
348 {
349 	return get_random_kvp_value_depth (type, 0);
350 }
351 
352 /* ================================================================= */
353 /* Numeric stuff */
354 
355 #define RAND_IN_RANGE(X) (((X)*((gint64) (rand()+1)))/RAND_MAX)
356 
357 QofNumeric
get_random_qof_numeric(void)358 get_random_qof_numeric (void)
359 {
360 	gint64 numer;
361 	gint64 deno;
362 
363 	if (RAND_MAX / 8 > rand ())
364 	{
365 		/* Random number between 1 and 6000 */
366 		deno = RAND_IN_RANGE (6000ULL);
367 	}
368 	else
369 	{
370 		gint64 norm = RAND_IN_RANGE (10ULL);
371 
372 		/* multiple of 10, between 1 and 10 000 million */
373 		deno = 1;
374 		while (norm)
375 		{
376 			deno *= 10;
377 			norm--;
378 		}
379 	}
380 
381 	/* Arbitrary random numbers can cause pointless overflow
382 	 * during calculations.  Limit dynamic range in hopes
383 	 * of avoiding overflow. */
384 	numer = get_random_gint64 () / 100000;
385 	if (0 == numer)
386 		numer = 1;
387 	return qof_numeric_create (numer, deno);
388 }
389 
390 /*
391 static GList *
392 get_random_guids(int max)
393 {
394   GList *guids = NULL;
395   int num_guids;
396 
397   if (max < 1) return NULL;
398 
399   num_guids = get_random_int_in_range (1, max);
400 
401   while (num_guids-- > 0)
402     g_list_prepend (guids, get_random_guid ());
403 
404   return guids;
405   }
406 *//*
407      static void
408      free_random_guids(GList *guids)
409      {
410      GList *node;
411 
412      for (node = guids; node; node = node->next)
413      g_free (node->data);
414 
415      g_list_free (guids);
416      }
417    *//*
418      static QofQueryOp
419      get_random_queryop(void)
420      {
421      QofQueryOp op = get_random_int_in_range (1, QOF_QUERY_XOR);
422      if (gnc_engine_debug_random) printf ("op = %d, ", op);
423      return op;
424      }
425    *//*
426      static GSList *
427      get_random_kvp_path (void)
428      {
429      GSList *path;
430      gint len;
431 
432      path = NULL;
433      len = get_random_int_in_range (1, kvp_max_depth);
434 
435      while (len--)
436      path = g_slist_prepend (path, get_random_string ());
437 
438      return g_slist_reverse (path);
439      }
440    *//*
441      static void
442      free_random_kvp_path (GSList *path)
443      {
444      GSList *node;
445 
446      for (node = path; node; node = node->next)
447      g_free (node->data);
448 
449      g_slist_free (path);
450      }
451    */
452 typedef enum
453 {
454 	BY_STANDARD = 1,
455 	BY_DATE,
456 	BY_DATE_ENTERED,
457 	BY_DATE_RECONCILED,
458 	BY_NUM,
459 	BY_AMOUNT,
460 	BY_MEMO,
461 	BY_DESC,
462 	BY_NONE
463 } sort_type_t;
464 
465 typedef struct
466 {
467 	QofIdType where;
468 	GSList *path;
469 	QofQuery *q;
470 } KVPQueryData;
471 
472 TestQueryTypes
get_random_query_type(void)473 get_random_query_type (void)
474 {
475 	switch (get_random_int_in_range (0, 4))
476 	{
477 	case 0:
478 		return SIMPLE_QT;
479 	case 4:
480 		return GUID_QT;
481 	default:
482 		return SIMPLE_QT;
483 	}
484 }
485