QOF 0.8.4
test-engine-stuff.c
Go to the documentation of this file.
00001 /*
00002  *  This program is free software; you can redistribute it and/or modify
00003  *  it under the terms of the GNU General Public License as published by
00004  *  the Free Software Foundation; either version 2 of the License, or
00005  *  (at your option) any later version.
00006  *
00007  *  This program is distributed in the hope that it will be useful,
00008  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00009  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010  *  GNU General Public License for more details.
00011  *
00012  *  You should have received a copy of the GNU General Public License
00013  *  along with this program; if not, write to the Free Software
00014  *  Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA  02110-1301,  USA
00015  */
00016 
00026 #include <sys/types.h>
00027 #include <dirent.h>
00028 #include <fcntl.h>
00029 #include <glib.h>
00030 #include <stdio.h>
00031 #include <stdlib.h>
00032 #include <string.h>
00033 #include <sys/stat.h>
00034 #include <unistd.h>
00035 #include "config.h"
00036 #include "qof.h"
00037 #include "test-engine-stuff.h"
00038 #include "test-stuff.h"
00039 
00040 static gboolean glist_strings_only = FALSE;
00041 
00042 static GHashTable *exclude_kvp_types = NULL;
00043 static gint kvp_max_depth = 5;
00044 static gint kvp_frame_max_elements = 10;
00045 
00046 gboolean gnc_engine_debug_random = FALSE;
00047 
00048 /* ========================================================== */
00049 /* Set control parameters governing the run. */
00050 
00051 void
00052 set_max_kvp_depth (gint max_kvp_depth)
00053 {
00054     kvp_max_depth = MAX (max_kvp_depth, 1);
00055 }
00056 
00057 void
00058 set_max_kvp_frame_elements (gint max_kvp_frame_elements)
00059 {
00060     kvp_frame_max_elements = MAX (max_kvp_frame_elements, 1);
00061 }
00062 
00063 void
00064 kvp_exclude_type (KvpValueType kvp_type)
00065 {
00066     gint *key;
00067 
00068     if (!exclude_kvp_types)
00069         exclude_kvp_types = g_hash_table_new (g_int_hash, g_int_equal);
00070 
00071     key = g_new (gint, 1);
00072     *key = kvp_type;
00073 
00074     g_hash_table_insert (exclude_kvp_types, key, exclude_kvp_types);
00075 }
00076 
00077 static gboolean
00078 kvp_type_excluded (KvpValueType kvp_type)
00079 {
00080     gint key = kvp_type;
00081 
00082     if (!exclude_kvp_types)
00083         return FALSE;
00084 
00085     if (g_hash_table_lookup (exclude_kvp_types, &key))
00086         return TRUE;
00087 
00088     return FALSE;
00089 }
00090 
00091 void
00092 random_glist_strings_only (gboolean strings_only)
00093 {
00094     glist_strings_only = strings_only;
00095 }
00096 
00097 
00098 /* ========================================================== */
00099 
00100 static gint borked = 80;
00101 
00102 static inline gboolean
00103 do_bork (void)
00104 {
00105     if (1 == get_random_int_in_range (0, borked))
00106     {
00107         return TRUE;
00108     }
00109     return FALSE;
00110 }
00111 
00112 /* ========================================================== */
00113 /* GList stuff */
00114 /*
00115 static gpointer
00116 get_random_list_element (GList *list)
00117 {
00118   g_return_val_if_fail (list, NULL);
00119 
00120   return g_list_nth_data (list,
00121                           get_random_int_in_range (0,
00122                                                    g_list_length (list) - 1));
00123 }
00124 */
00125 static KvpValue *get_random_kvp_value_depth (int type, gint depth);
00126 
00127 static GList *
00128 get_random_glist_depth (gint depth)
00129 {
00130     GList *ret = NULL;
00131     int count = get_random_int_in_range (1, 5);
00132     int i;
00133 
00134     if (depth >= kvp_max_depth)
00135         return NULL;
00136 
00137     for (i = 0; i < count; i++)
00138     {
00139         KvpValueType kvpt;
00140         KvpValue *value;
00141 
00142         kvpt = glist_strings_only ? KVP_TYPE_STRING : -2;
00143 
00144         do
00145         {
00146             value = get_random_kvp_value_depth (kvpt, depth + 1);
00147         }
00148         while (!value);
00149 
00150         ret = g_list_prepend (ret, value);
00151     }
00152 
00153     return ret;
00154 }
00155 
00156 GList *
00157 get_random_glist (void)
00158 {
00159     return get_random_glist_depth (0);
00160 }
00161 
00162 /* ========================================================== */
00163 /* Time/Date, GUID, binary data stuff */
00164 
00165 GUID *
00166 get_random_guid (void)
00167 {
00168     GUID *ret;
00169 
00170     ret = g_new (GUID, 1);
00171     guid_new (ret);
00172 
00173     return ret;
00174 }
00175 
00176 bin_data *
00177 get_random_binary_data (void)
00178 {
00179     int len;
00180     bin_data *ret;
00181 
00182     len = get_random_int_in_range (20, 100);
00183     ret = g_new (bin_data, 1);
00184     ret->data = g_new (guchar, len);
00185     ret->len = len;
00186 
00187     for (len--; len >= 0; len--)
00188     {
00189         ret->data[len] = (guchar) get_random_int_in_range (0, 255);
00190     }
00191 
00192     return ret;
00193 }
00194 
00195 /* ========================================================== */
00196 /* KVP stuff */
00197 
00198 static KvpFrame *get_random_kvp_frame_depth (gint depth);
00199 
00200 static KvpValue *
00201 get_random_kvp_value_depth (int type, gint depth)
00202 {
00203     int datype = type;
00204     KvpValue *ret;
00205 
00206     if (datype == -1)
00207     {
00208         datype = get_random_int_in_range (KVP_TYPE_GINT64, KVP_TYPE_FRAME);
00209     }
00210 
00211     if (datype == -2)
00212     {
00213         datype =
00214             get_random_int_in_range (KVP_TYPE_GINT64, KVP_TYPE_FRAME - 1);
00215     }
00216 
00217     if (datype == KVP_TYPE_FRAME && depth >= kvp_max_depth)
00218         return NULL;
00219 
00220     if (datype == KVP_TYPE_GLIST && depth >= kvp_max_depth)
00221         return NULL;
00222 
00223     if (kvp_type_excluded (datype))
00224         return NULL;
00225 
00226     switch (datype)
00227     {
00228     case KVP_TYPE_GINT64:
00229         ret = kvp_value_new_gint64 (get_random_gint64 ());
00230         break;
00231 
00232     case KVP_TYPE_DOUBLE:
00233         ret = NULL;
00234         break;
00235 
00236     case KVP_TYPE_NUMERIC:
00237         ret = kvp_value_new_numeric (get_random_qof_numeric ());
00238         break;
00239 
00240     case KVP_TYPE_STRING:
00241         {
00242             gchar *tmp_str;
00243             tmp_str = get_random_string ();
00244             if (!tmp_str)
00245                 return NULL;
00246 
00247             ret = kvp_value_new_string (tmp_str);
00248             g_free (tmp_str);
00249         }
00250         break;
00251 
00252     case KVP_TYPE_GUID:
00253         {
00254             GUID *tmp_guid;
00255             tmp_guid = get_random_guid ();
00256             ret = kvp_value_new_guid (tmp_guid);
00257             g_free (tmp_guid);
00258         }
00259         break;
00260     case KVP_TYPE_BINARY:
00261         {
00262             bin_data *tmp_data;
00263             tmp_data = get_random_binary_data ();
00264             ret = kvp_value_new_binary (tmp_data->data, tmp_data->len);
00265             g_free (tmp_data->data);
00266             g_free (tmp_data);
00267         }
00268         break;
00269 
00270     case KVP_TYPE_GLIST:
00271         ret = kvp_value_new_glist_nc (get_random_glist_depth (depth + 1));
00272         break;
00273 
00274     case KVP_TYPE_FRAME:
00275         {
00276             KvpFrame *tmp_frame;
00277             tmp_frame = get_random_kvp_frame_depth (depth + 1);
00278             ret = kvp_value_new_frame (tmp_frame);
00279             kvp_frame_delete (tmp_frame);
00280         }
00281         break;
00282 
00283     default:
00284         ret = NULL;
00285         break;
00286     }
00287     return ret;
00288 }
00289 
00290 static KvpFrame *
00291 get_random_kvp_frame_depth (gint depth)
00292 {
00293     KvpFrame *ret;
00294     int vals_to_add;
00295     gboolean val_added;
00296 
00297     if (depth >= kvp_max_depth)
00298         return NULL;
00299 
00300     ret = kvp_frame_new ();
00301 
00302     vals_to_add = get_random_int_in_range (1, kvp_frame_max_elements);
00303     val_added = FALSE;
00304 
00305     for (; vals_to_add > 0; vals_to_add--)
00306     {
00307         gchar *key;
00308         KvpValue *val;
00309 
00310         key = NULL;
00311         while (key == NULL)
00312         {
00313             key = get_random_string_without ("/");
00314             if (*key == '\0')
00315             {
00316                 g_free (key);
00317                 key = NULL;
00318             }
00319         }
00320 
00321         val = get_random_kvp_value_depth (-1, depth + 1);
00322         if (!val)
00323         {
00324             g_free (key);
00325             if (!val_added)
00326                 vals_to_add++;
00327             continue;
00328         }
00329 
00330         val_added = TRUE;
00331 
00332         kvp_frame_set_slot_nc (ret, key, val);
00333 
00334         g_free (key);
00335     }
00336 
00337     return ret;
00338 }
00339 
00340 KvpFrame *
00341 get_random_kvp_frame (void)
00342 {
00343     return get_random_kvp_frame_depth (0);
00344 }
00345 
00346 KvpValue *
00347 get_random_kvp_value (int type)
00348 {
00349     return get_random_kvp_value_depth (type, 0);
00350 }
00351 
00352 /* ================================================================= */
00353 /* Numeric stuff */
00354 
00355 #define RAND_IN_RANGE(X) (((X)*((gint64) (rand()+1)))/RAND_MAX)
00356 
00357 QofNumeric
00358 get_random_qof_numeric (void)
00359 {
00360     gint64 numer;
00361     gint64 deno;
00362 
00363     if (RAND_MAX / 8 > rand ())
00364     {
00365         /* Random number between 1 and 6000 */
00366         deno = RAND_IN_RANGE (6000ULL);
00367     }
00368     else
00369     {
00370         gint64 norm = RAND_IN_RANGE (10ULL);
00371 
00372         /* multiple of 10, between 1 and 10 000 million */
00373         deno = 1;
00374         while (norm)
00375         {
00376             deno *= 10;
00377             norm--;
00378         }
00379     }
00380 
00381     /* Arbitrary random numbers can cause pointless overflow
00382      * during calculations.  Limit dynamic range in hopes
00383      * of avoiding overflow. */
00384     numer = get_random_gint64 () / 100000;
00385     if (0 == numer)
00386         numer = 1;
00387     return qof_numeric_create (numer, deno);
00388 }
00389 
00390 /*
00391 static GList *
00392 get_random_guids(int max)
00393 {
00394   GList *guids = NULL;
00395   int num_guids;
00396 
00397   if (max < 1) return NULL;
00398 
00399   num_guids = get_random_int_in_range (1, max);
00400 
00401   while (num_guids-- > 0)
00402     g_list_prepend (guids, get_random_guid ());
00403 
00404   return guids;
00405   }
00406 *//*
00407      static void
00408      free_random_guids(GList *guids)
00409      {
00410      GList *node;
00411 
00412      for (node = guids; node; node = node->next)
00413      g_free (node->data);
00414 
00415      g_list_free (guids);
00416      }
00417    *//*
00418      static QofQueryOp
00419      get_random_queryop(void)
00420      {
00421      QofQueryOp op = get_random_int_in_range (1, QOF_QUERY_XOR);
00422      if (gnc_engine_debug_random) printf ("op = %d, ", op);
00423      return op;
00424      }
00425    *//*
00426      static GSList *
00427      get_random_kvp_path (void)
00428      {
00429      GSList *path;
00430      gint len;
00431 
00432      path = NULL;
00433      len = get_random_int_in_range (1, kvp_max_depth);
00434 
00435      while (len--)
00436      path = g_slist_prepend (path, get_random_string ());
00437 
00438      return g_slist_reverse (path);
00439      }
00440    *//*
00441      static void
00442      free_random_kvp_path (GSList *path)
00443      {
00444      GSList *node;
00445 
00446      for (node = path; node; node = node->next)
00447      g_free (node->data);
00448 
00449      g_slist_free (path);
00450      }
00451    */
00452 typedef enum
00453 {
00454     BY_STANDARD = 1,
00455     BY_DATE,
00456     BY_DATE_ENTERED,
00457     BY_DATE_RECONCILED,
00458     BY_NUM,
00459     BY_AMOUNT,
00460     BY_MEMO,
00461     BY_DESC,
00462     BY_NONE
00463 } sort_type_t;
00464 
00465 typedef struct
00466 {
00467     QofIdType where;
00468     GSList *path;
00469     QofQuery *q;
00470 } KVPQueryData;
00471 
00472 TestQueryTypes
00473 get_random_query_type (void)
00474 {
00475     switch (get_random_int_in_range (0, 4))
00476     {
00477     case 0:
00478         return SIMPLE_QT;
00479     case 4:
00480         return GUID_QT;
00481     default:
00482         return SIMPLE_QT;
00483     }
00484 }