GnuCash 2.4.99
test-engine-stuff.c
Go to the documentation of this file.
00001 
00018 #include "config.h"
00019 
00020 #include <sys/types.h>
00021 #include <dirent.h>
00022 #include <fcntl.h>
00023 #include <glib.h>
00024 #include <stdio.h>
00025 #include <stdlib.h>
00026 #include <string.h>
00027 #include <sys/stat.h>
00028 #include <unistd.h>
00029 
00030 #include "Account.h"
00031 #include "AccountP.h"
00032 #include "gnc-engine.h"
00033 #include "gnc-session.h"
00034 #include "Transaction.h"
00035 #include "TransactionP.h"
00036 #include "Recurrence.h"
00037 #include "SchedXaction.h"
00038 #include "SX-book.h"
00039 
00040 #include "test-engine-stuff.h"
00041 #include "test-stuff.h"
00042 #include "test-engine-strings.h"
00043 
00044 static gboolean glist_strings_only = FALSE;
00045 
00046 static GHashTable *exclude_kvp_types = NULL;
00047 static gint kvp_max_depth = 5;
00048 static gint kvp_frame_max_elements = 10;
00049 
00050 static gint max_tree_depth = 1;
00051 static gint max_level_accounts = 3;
00052 static gint max_total_accounts = 10;
00053 static gint max_trans_num = 1000;
00054 static gint total_num_accounts = 0;
00055 /* SCU == smallest currency unit -- the value of the denominator */
00056 static gint max_scu = 100; //6000;
00057 static gint min_scu = 100; //1;
00058 
00059 
00060 /* The inverse fraction of split/transaction data that should
00061  * contain invalid/inconsistent fields/values.  Thus,
00062  * if borked==1000, then one in 1000 fields will have bad data.
00063  * This is used to test the data integrity scrubbers, which are
00064  * supposed to clean up any crud they find.
00065  */
00066 static gint borked = 80;
00067 
00068 gboolean gnc_engine_debug_random = FALSE;
00069 
00070 /* ========================================================== */
00071 /* Set control parameters governing the run. */
00072 
00073 void
00074 set_max_account_tree_depth (gint max_tree_depth_in)
00075 {
00076     max_tree_depth = MAX (max_tree_depth_in, 1);
00077 }
00078 
00079 void
00080 set_max_accounts_per_level (gint max_level_accounts_in)
00081 {
00082     max_level_accounts = MAX (max_level_accounts_in, 1);
00083 }
00084 
00085 void
00086 set_max_kvp_depth (gint max_kvp_depth)
00087 {
00088     kvp_max_depth = MAX (max_kvp_depth, 1);
00089 }
00090 
00091 void
00092 set_max_kvp_frame_elements (gint max_kvp_frame_elements)
00093 {
00094     kvp_frame_max_elements = MAX (max_kvp_frame_elements, 1);
00095 }
00096 
00097 void
00098 kvp_exclude_type (KvpValueType kvp_type)
00099 {
00100     gint *key;
00101 
00102     if (!exclude_kvp_types)
00103         exclude_kvp_types = g_hash_table_new (g_int_hash, g_int_equal);
00104 
00105     key = g_new (gint, 1);
00106     *key = kvp_type;
00107 
00108     g_hash_table_insert (exclude_kvp_types, key, exclude_kvp_types);
00109 }
00110 
00111 static gboolean
00112 kvp_type_excluded (KvpValueType kvp_type)
00113 {
00114     gint key = kvp_type;
00115 
00116     if (!exclude_kvp_types)
00117         return FALSE;
00118 
00119     if (g_hash_table_lookup (exclude_kvp_types, &key))
00120         return TRUE;
00121 
00122     return FALSE;
00123 }
00124 
00125 void
00126 random_glist_strings_only (gboolean strings_only)
00127 {
00128     glist_strings_only = strings_only;
00129 }
00130 
00131 static gboolean zero_nsec = FALSE;
00132 
00133 void
00134 random_timespec_zero_nsec (gboolean zero_nsec_in)
00135 {
00136     zero_nsec = zero_nsec_in;
00137 }
00138 
00139 static gboolean usec_resolution = FALSE;
00140 
00141 void
00142 random_timespec_usec_resolution (gboolean usec_resolution_in)
00143 {
00144     usec_resolution = usec_resolution_in;
00145 }
00146 
00147 /* ========================================================== */
00148 
00149 static inline gboolean
00150 do_bork (void)
00151 {
00152     if (1 == get_random_int_in_range (0, borked))
00153     {
00154         return TRUE;
00155     }
00156     return FALSE;
00157 }
00158 
00159 /* ========================================================== */
00160 /* GList stuff */
00161 
00162 static gpointer
00163 get_random_list_element (GList *list)
00164 {
00165     g_return_val_if_fail (list, NULL);
00166 
00167     return g_list_nth_data (list,
00168                             get_random_int_in_range (0,
00169                                     g_list_length (list) - 1));
00170 }
00171 
00172 static kvp_value* get_random_kvp_value_depth (int type, gint depth);
00173 
00174 static GList*
00175 get_random_glist_depth (gint depth)
00176 {
00177     GList *ret = NULL;
00178     int count = get_random_int_in_range(1, 5);
00179     int i;
00180 
00181     if (depth >= kvp_max_depth)
00182         return NULL;
00183 
00184     for (i = 0; i < count; i++)
00185     {
00186         KvpValueType kvpt;
00187         KvpValue *value;
00188 
00189         kvpt = glist_strings_only ? KVP_TYPE_STRING : -2;
00190 
00191         do
00192         {
00193             value = get_random_kvp_value_depth (kvpt, depth + 1);
00194         }
00195         while (!value);
00196 
00197         ret = g_list_prepend(ret, value);
00198     }
00199 
00200     return ret;
00201 }
00202 
00203 GList*
00204 get_random_glist(void)
00205 {
00206     return get_random_glist_depth (0);
00207 }
00208 
00209 /* ========================================================== */
00210 /* Time/Date, GncGUID, binary data stuff */
00211 
00212 Timespec*
00213 get_random_timespec(void)
00214 {
00215     Timespec *ret;
00216 
00217     ret = g_new0(Timespec, 1);
00218 
00219     while (ret->tv_sec <= 0)
00220         ret->tv_sec = rand();
00221 
00222     if (zero_nsec)
00223         ret->tv_nsec = 0;
00224     else
00225     {
00226         ret->tv_nsec = rand();
00227 
00228         if (usec_resolution)
00229         {
00230             ret->tv_nsec = MIN (ret->tv_nsec, 999999999);
00231             ret->tv_nsec /= 1000;
00232             ret->tv_nsec *= 1000;
00233         }
00234     }
00235 
00236     return ret;
00237 }
00238 
00239 GncGUID*
00240 get_random_guid(void)
00241 {
00242     GncGUID *ret;
00243 
00244     ret = g_new(GncGUID, 1);
00245     guid_new(ret);
00246 
00247     return ret;
00248 }
00249 
00250 bin_data*
00251 get_random_binary_data(void)
00252 {
00253     int len;
00254     bin_data *ret;
00255 
00256     len = get_random_int_in_range(20, 100);
00257     ret = g_new(bin_data, 1);
00258     ret->data = g_new(guchar, len);
00259     ret->len = len;
00260 
00261     for (len--; len >= 0; len--)
00262     {
00263         ret->data[len] = (guchar)get_random_int_in_range(0, 255);
00264     }
00265 
00266     return ret;
00267 }
00268 
00269 /* ========================================================== */
00270 /* KVP stuff */
00271 
00272 static KvpFrame* get_random_kvp_frame_depth (gint depth);
00273 
00274 static KvpValue*
00275 get_random_kvp_value_depth (int type, gint depth)
00276 {
00277     int datype = type;
00278     KvpValue *ret;
00279 
00280     if (datype == -1)
00281     {
00282         datype = get_random_int_in_range(KVP_TYPE_GINT64, KVP_TYPE_FRAME);
00283     }
00284 
00285     if (datype == -2)
00286     {
00287         datype = get_random_int_in_range(KVP_TYPE_GINT64, KVP_TYPE_FRAME - 1);
00288     }
00289 
00290     if (datype == KVP_TYPE_FRAME && depth >= kvp_max_depth)
00291         return NULL;
00292 
00293     if (datype == KVP_TYPE_GLIST && depth >= kvp_max_depth)
00294         return NULL;
00295 
00296     if (kvp_type_excluded (datype))
00297         return NULL;
00298 
00299     switch (datype)
00300     {
00301     case KVP_TYPE_GINT64:
00302         ret = kvp_value_new_gint64(get_random_gint64());
00303         break;
00304 
00305     case KVP_TYPE_DOUBLE:
00306         ret = NULL;
00307         break;
00308 
00309     case KVP_TYPE_NUMERIC:
00310         ret = kvp_value_new_gnc_numeric(get_random_gnc_numeric());
00311         break;
00312 
00313     case KVP_TYPE_STRING:
00314     {
00315         gchar *tmp_str;
00316         tmp_str = get_random_string();
00317         if (!tmp_str)
00318             return NULL;
00319 
00320         ret = kvp_value_new_string(tmp_str);
00321         g_free(tmp_str);
00322     }
00323     break;
00324 
00325     case KVP_TYPE_GUID:
00326     {
00327         GncGUID *tmp_guid;
00328         tmp_guid = get_random_guid();
00329         ret = kvp_value_new_guid(tmp_guid);
00330         g_free(tmp_guid);
00331     }
00332     break;
00333 
00334     case KVP_TYPE_TIMESPEC:
00335     {
00336         Timespec *ts = get_random_timespec();
00337         ret = kvp_value_new_timespec (*ts);
00338         g_free(ts);
00339     }
00340     break;
00341 
00342     case KVP_TYPE_BINARY:
00343     {
00344         bin_data *tmp_data;
00345         tmp_data = get_random_binary_data();
00346         ret = kvp_value_new_binary(tmp_data->data, tmp_data->len);
00347         g_free(tmp_data->data);
00348         g_free(tmp_data);
00349     }
00350     break;
00351 
00352     case KVP_TYPE_GLIST:
00353         ret = kvp_value_new_glist_nc(get_random_glist_depth (depth + 1));
00354         break;
00355 
00356     case KVP_TYPE_FRAME:
00357     {
00358         KvpFrame *tmp_frame;
00359         tmp_frame = get_random_kvp_frame_depth(depth + 1);
00360         ret = kvp_value_new_frame(tmp_frame);
00361         kvp_frame_delete(tmp_frame);
00362     }
00363     break;
00364 
00365     default:
00366         ret = NULL;
00367         break;
00368     }
00369     return ret;
00370 }
00371 
00372 static KvpFrame*
00373 get_random_kvp_frame_depth (gint depth)
00374 {
00375     KvpFrame *ret;
00376     int vals_to_add;
00377     gboolean val_added;
00378 
00379     if (depth >= kvp_max_depth)
00380         return NULL;
00381 
00382     ret = kvp_frame_new();
00383 
00384     vals_to_add = get_random_int_in_range(1, kvp_frame_max_elements);
00385     val_added = FALSE;
00386 
00387     for (; vals_to_add > 0; vals_to_add--)
00388     {
00389         gchar *key;
00390         KvpValue *val;
00391 
00392         key = NULL;
00393         while (key == NULL)
00394         {
00395             key = get_random_string_without("/");
00396             if (*key == '\0')
00397             {
00398                 g_free(key);
00399                 key = NULL;
00400             }
00401         }
00402 
00403         val = get_random_kvp_value_depth (-1, depth + 1);
00404         if (!val)
00405         {
00406             g_free(key);
00407             if (!val_added)
00408                 vals_to_add++;
00409             continue;
00410         }
00411 
00412         val_added = TRUE;
00413 
00414         kvp_frame_set_slot_nc(ret, key, val);
00415 
00416         g_free(key);
00417     }
00418 
00419     return ret;
00420 }
00421 
00422 KvpFrame *
00423 get_random_kvp_frame (void)
00424 {
00425     return get_random_kvp_frame_depth (0);
00426 }
00427 
00428 KvpValue *
00429 get_random_kvp_value(int type)
00430 {
00431     return get_random_kvp_value_depth (type, 0);
00432 }
00433 
00434 /* ================================================================= */
00435 /* Numeric stuff */
00436 
00437 #define RAND_IN_RANGE(X) (((X)*((gint64) (rand()+1)))/RAND_MAX)
00438 
00439 gnc_numeric
00440 get_random_gnc_numeric(void)
00441 {
00442     gint64 numer;
00443     gint64 deno;
00444 
00445     if (RAND_MAX / 8 > rand())
00446     {
00447         /* Random number between 1 and 6000 */
00448         deno = RAND_IN_RANGE(6000ULL);
00449     }
00450     else
00451     {
00452         gint64 norm = RAND_IN_RANGE (7ULL);
00453 
00454         /* multiple of 10, between 1 and 1 million */
00455         deno = 1;
00456         while (norm)
00457         {
00458             deno *= 10;
00459             norm --;
00460         }
00461     }
00462 
00463     /* Arbitrary random numbers can cause pointless overflow
00464      * during calculations.  Limit dynamic range in hopes
00465      * of avoiding overflow. Right now limit it to approx 2^48.
00466      * The initial division is to help us down towards the range.
00467      * The loop is to "make sure" we get there.  We might
00468      * want to make this dependent on "deno" in the future.
00469      */
00470     do
00471     {
00472         numer = get_random_gint64() / 1000000;
00473     }
00474     while ((numer >> 31) > 0x1FFFF);
00475     if (0 == numer) numer = 1;
00476     /* Make sure we have a non-zero denominator */
00477     if (0 == deno) deno = 1;
00478     return gnc_numeric_create(numer, deno);
00479 }
00480 
00481 /* ================================================================= */
00482 /* Commodity stuff */
00483 
00484 const char *types[] =
00485 {
00486     "NASDAQ",
00487     "NYSE",
00488     "EUREX",
00489     "FUND",
00490     "AMEX",
00491     NULL
00492 };
00493 
00494 const char*
00495 get_random_commodity_namespace(void)
00496 {
00497     return get_random_string_in_array(types);
00498 }
00499 
00500 static gnc_commodity *
00501 get_random_commodity_from_table (gnc_commodity_table *table)
00502 {
00503     GList *namespaces;
00504     gnc_commodity *com = NULL;
00505 
00506     g_return_val_if_fail (table, NULL);
00507 
00508     namespaces = gnc_commodity_table_get_namespaces (table);
00509 
00510     do
00511     {
00512         GList *commodities;
00513         char *namespace;
00514 
00515         namespace = get_random_list_element (namespaces);
00516 
00517         commodities = gnc_commodity_table_get_commodities (table, namespace);
00518         if (!commodities)
00519             continue;
00520 
00521         com = get_random_list_element (commodities);
00522 
00523         g_list_free (commodities);
00524 
00525     }
00526     while (!com);
00527 
00528 
00529     g_list_free (namespaces);
00530 
00531     return com;
00532 }
00533 
00534 gnc_commodity*
00535 get_random_commodity (QofBook *book)
00536 {
00537     gnc_commodity *ret;
00538     gchar *name;
00539     const gchar *space;
00540     gchar *mn;
00541     gchar *cusip;
00542     int ran_int;
00543     gnc_commodity_table *table;
00544 
00545     table = gnc_commodity_table_get_table (book);
00546 
00547 #if 0
00548     if (table &&
00549             (gnc_commodity_table_get_size (table) > 0) &&
00550             get_random_int_in_range (1, 5) < 5)
00551         return get_random_commodity_from_table (table);
00552 #endif
00553 
00554     mn = get_random_string_length_in_range(1, 3);
00555     space = get_random_commodity_namespace();
00556 
00557     if (table)
00558     {
00559         ret = gnc_commodity_table_lookup (table, space, mn);
00560 
00561         if (ret)
00562         {
00563             g_free (mn);
00564             return ret;
00565         }
00566     }
00567 
00568     name = get_random_string();
00569     cusip = get_random_string();
00570 
00571     ran_int = get_random_int_in_range(min_scu, max_scu);
00572 
00573     ret = gnc_commodity_new(book, name, space, mn, cusip, ran_int);
00574 
00575     g_free(mn);
00576     g_free(name);
00577     g_free(cusip);
00578 
00579     if (table)
00580         ret = gnc_commodity_table_insert (table, ret);
00581 
00582     return ret;
00583 }
00584 
00585 void
00586 make_random_changes_to_commodity (gnc_commodity *com)
00587 {
00588     char *str;
00589 
00590     g_return_if_fail (com);
00591 
00592     str = get_random_string ();
00593     gnc_commodity_set_namespace (com, str);
00594     g_free (str);
00595 
00596     str = get_random_string ();
00597     gnc_commodity_set_mnemonic (com, str);
00598     g_free (str);
00599 
00600     str = get_random_string ();
00601     gnc_commodity_set_fullname (com, str);
00602     g_free (str);
00603 
00604     str = get_random_string ();
00605     gnc_commodity_set_cusip (com, str);
00606     g_free (str);
00607 
00608     gnc_commodity_set_fraction (com, get_random_int_in_range (1, 100000));
00609 }
00610 
00611 void
00612 make_random_changes_to_commodity_table (gnc_commodity_table *table)
00613 {
00614     GList *namespaces;
00615     GList *node;
00616 
00617     g_return_if_fail (table);
00618 
00619     namespaces = gnc_commodity_table_get_namespaces (table);
00620 
00621     for (node = namespaces; node; node = node->next)
00622     {
00623         const char *ns = node->data;
00624         GList *commodities;
00625         GList *com_node;
00626 
00627         if (gnc_commodity_namespace_is_iso (ns))
00628             continue;
00629 
00630         commodities = gnc_commodity_table_get_commodities (table, ns);
00631 
00632         for (com_node = commodities; com_node; com_node = com_node->next)
00633         {
00634             gnc_commodity *com = com_node->data;
00635 
00636             gnc_commodity_table_remove (table, com);
00637             make_random_changes_to_commodity (com);
00638             gnc_commodity_table_insert (table, com);
00639         }
00640 
00641         g_list_free (commodities);
00642     }
00643 
00644     g_list_free (namespaces);
00645 }
00646 /* ================================================================= */
00647 /* Price stuff */
00648 
00649 void
00650 make_random_changes_to_price (QofBook *book, GNCPrice *p)
00651 {
00652     Timespec *ts;
00653     char *string;
00654     gnc_commodity *c;
00655 
00656     g_return_if_fail (book && p);
00657 
00658     gnc_price_begin_edit (p);
00659 
00660     c = get_random_commodity (book);
00661     gnc_price_set_commodity (p, c);
00662 
00663     c = get_random_commodity (book);
00664     gnc_price_set_currency (p, c);
00665 
00666     ts = get_random_timespec ();
00667     gnc_price_set_time (p, *ts);
00668     g_free (ts);
00669 
00670     string = get_random_string ();
00671     gnc_price_set_source (p, string);
00672     g_free (string);
00673 
00674     string = get_random_string ();
00675     gnc_price_set_typestr (p, string);
00676     g_free (string);
00677 
00678     gnc_price_set_value (p, get_random_gnc_numeric ());
00679 
00680     gnc_price_commit_edit (p);
00681 }
00682 
00683 GNCPrice *
00684 get_random_price(QofBook *book)
00685 {
00686     GNCPrice *p;
00687 
00688     p = gnc_price_create (book);
00689     if (!p)
00690     {
00691         failure_args("engine-stuff", __FILE__, __LINE__,
00692                      "get_random_price failed");
00693         return NULL;
00694     }
00695 
00696     make_random_changes_to_price (book, p);
00697     if (!p)
00698     {
00699         failure_args("engine-stuff", __FILE__, __LINE__,
00700                      "make_random_changes_to_price failed");
00701         return NULL;
00702     }
00703 
00704     return p;
00705 }
00706 
00707 gboolean
00708 make_random_pricedb (QofBook *book, GNCPriceDB *db)
00709 {
00710     int num_prices;
00711     gboolean check;
00712 
00713     num_prices = get_random_int_in_range (1, 41);
00714     if (num_prices < 1) /* should be impossible */
00715     {
00716         failure_args("engine-stuff", __FILE__, __LINE__,
00717                      "get_random_int_in_range failed");
00718         return FALSE;
00719     }
00720 
00721     while (num_prices-- > 0)
00722     {
00723         GNCPrice *p;
00724 
00725         p = get_random_price (book);
00726         if (!p)
00727         {
00728             failure_args("engine-stuff", __FILE__, __LINE__,
00729                          "get_random_price failed");
00730             return FALSE;
00731         }
00732 
00733         check = gnc_pricedb_add_price (db, p);
00734         if (!check)
00735         {
00736             return check;
00737         }
00738 
00739         gnc_price_unref (p);
00740     }
00741     return TRUE;
00742 }
00743 
00744 GNCPriceDB *
00745 get_random_pricedb(QofBook *book)
00746 {
00747     GNCPriceDB *db;
00748 
00749     db = gnc_pricedb_get_db (book);
00750     if (!db)
00751     {
00752         failure_args("engine-stuff", __FILE__, __LINE__,
00753                      "gnc_pricedb_get_db failed");
00754         return NULL;
00755     }
00756     if (!make_random_pricedb (book, db))
00757     {
00758         return NULL;
00759     }
00760 
00761     return db;
00762 }
00763 
00764 static gboolean
00765 price_accumulator (GNCPrice *p, gpointer data)
00766 {
00767     GList **list = data;
00768 
00769     *list = g_list_prepend (*list, p);
00770 
00771     return TRUE;
00772 }
00773 
00774 void
00775 make_random_changes_to_pricedb (QofBook *book, GNCPriceDB *pdb)
00776 {
00777     GList *list = NULL;
00778     GList *node;
00779 
00780     g_return_if_fail (pdb);
00781 
00782     gnc_pricedb_foreach_price (pdb, price_accumulator, &list, FALSE);
00783 
00784     for (node = list; node; node = node->next)
00785     {
00786         GNCPrice *p = node->data;
00787 
00788         switch (get_random_int_in_range (0, 5))
00789         {
00790         case 0: /* Delete */
00791             gnc_pricedb_remove_price (pdb, p);
00792             break;
00793 
00794         case 1:
00795         case 2: /* Change */
00796             make_random_changes_to_price (book, p);
00797             break;
00798 
00799         default: /* nothing */
00800             break;
00801         }
00802     }
00803 
00804     g_list_free (list);
00805 
00806     /* Add a few new ones */
00807     {
00808         int i = get_random_int_in_range (1, 5);
00809 
00810         while (i--)
00811         {
00812             GNCPrice *p = get_random_price (book);
00813 
00814             gnc_pricedb_add_price (pdb, p);
00815 
00816             gnc_price_unref (p);
00817         }
00818     }
00819 }
00820 
00821 /* ================================================================= */
00822 /* Account stuff */
00823 
00824 static void
00825 set_account_random_string(Account* act,
00826                           void(*func)(Account *act, const gchar*str))
00827 {
00828     gchar *tmp_str = get_random_string();
00829     if (tmp_str)
00830     {
00831         (func)(act, tmp_str);
00832         g_free(tmp_str);
00833     }
00834 }
00835 
00836 static void
00837 set_account_random_string_from_array(
00838     Account* act, void(*func)(Account *act, const gchar*str),
00839     const gchar *list[])
00840 {
00841     const gchar *tmp_str = get_random_string_in_array(list);
00842     if (tmp_str)
00843         (func)(act, tmp_str);
00844 
00845 }
00846 
00847 static void
00848 account_add_subaccounts (QofBook *book, Account *account, int depth)
00849 {
00850     int num_accounts;
00851 
00852     if (depth == 0)
00853         return;
00854 
00855     num_accounts = get_random_int_in_range (1, 10);
00856     while (num_accounts-- > 0)
00857     {
00858         Account *sub = get_random_account (book);
00859 
00860         gnc_account_append_child (account, sub);
00861 
00862         total_num_accounts ++;
00863         if (total_num_accounts > max_total_accounts) return;
00864 
00865         account_add_subaccounts (book, sub, depth - 1);
00866     }
00867 }
00868 
00869 static void
00870 make_random_account_tree (QofBook *book, Account *root)
00871 {
00872     int depth;
00873 
00874     g_return_if_fail (book);
00875     g_return_if_fail (root);
00876 
00877     total_num_accounts = 0;
00878     depth = get_random_int_in_range (1, max_tree_depth);
00879 
00880     account_add_subaccounts (book, root, depth);
00881 
00882     /* Make sure we have at least two accounts! */
00883     if (total_num_accounts <= 1)
00884         account_add_subaccounts (book, root, 1);
00885 }
00886 
00887 Account *
00888 get_random_account_tree (QofBook *book)
00889 {
00890     Account * root;
00891 
00892     g_return_val_if_fail (book, NULL);
00893 
00894     root = gnc_book_get_root_account (book);
00895     if (!root)
00896     {
00897         root = xaccMallocAccount (book);
00898         gnc_book_set_root_account (book, root);
00899     }
00900 
00901     make_random_account_tree (book, root);
00902 
00903     return root;
00904 }
00905 
00906 /* ================================================================= */
00907 /* transaction stuff */
00908 
00914 static void
00915 add_random_splits(QofBook *book, Transaction *trn, GList *account_list)
00916 {
00917     Account *acc, *bcc;
00918     Split *s;
00919     gnc_numeric val, amt;
00920 
00921     /* Gotta have at least two different accounts */
00922     if (1 >= g_list_length (account_list)) return;
00923 
00924     acc = get_random_list_element (account_list);
00925     xaccTransBeginEdit(trn);
00926     s = get_random_split(book, acc, trn);
00927 
00928     bcc = get_random_list_element (account_list);
00929     if ((bcc == acc) && (!do_bork()))
00930     {
00931         /* Make sure that each side of the transaction is in
00932          * a different account; otherwise get weirdness in lot
00933          * calculcations.  ... Hmm maybe should fix lots in
00934          * this case? */
00935         while (bcc == acc)
00936         {
00937             bcc = get_random_list_element (account_list);
00938         }
00939     }
00940 
00941     /* Set up two splits whose values really are opposites. */
00942     val = xaccSplitGetValue(s);
00943     s = get_random_split(book, bcc, trn);
00944 
00945     /* Other split should have equal and opposite value */
00946     if (do_bork())
00947     {
00948         val = get_random_gnc_numeric();
00949     }
00950     val = gnc_numeric_neg(val);
00951     xaccSplitSetValue(s, val);
00952 
00953     if (gnc_commodity_equal (xaccTransGetCurrency(trn),
00954                              xaccAccountGetCommodity(bcc)) &&
00955             (!do_bork()))
00956     {
00957         amt = val;
00958     }
00959     else
00960     {
00961         gnc_numeric amt2 = xaccSplitGetAmount(s);
00962         if (gnc_numeric_positive_p(amt2) ^ gnc_numeric_positive_p(val))
00963             amt = gnc_numeric_neg(amt2);
00964     }
00965 
00966     if (gnc_numeric_zero_p(val))
00967         amt = val;
00968 
00969     xaccSplitSetAmount(s, val);
00970     xaccTransCommitEdit(trn);
00971 }
00972 
00973 typedef struct
00974 {
00975     GncGUID guid;
00976 } TransInfo;
00977 
00978 void
00979 make_random_changes_to_transaction_and_splits (QofBook *book,
00980         Transaction *trans,
00981         GList *accounts)
00982 {
00983     GList *splits;
00984     GList *node;
00985     Split *split;
00986 
00987     g_return_if_fail (book);
00988     g_return_if_fail (trans);
00989     g_return_if_fail (accounts);
00990 
00991     xaccTransBeginEdit (trans);
00992 
00993     make_random_changes_to_transaction (book, trans);
00994 
00995     switch (get_random_int_in_range (0, 3))
00996     {
00997     case 0: /* delete some splits, add some more */
00998         if (xaccTransGetVoidStatus (trans))
00999             break;
01000 
01001         do
01002         {
01003             split = xaccTransGetSplit (trans, 0);
01004 
01005             xaccSplitDestroy (split);
01006         }
01007         while (split);
01008 
01009         add_random_splits (book, trans, accounts);
01010 
01011         /* fall through */
01012 
01013     case 1: /* move the splits around */
01014         if (xaccTransGetVoidStatus (trans))
01015             break;
01016 
01017         splits = xaccTransGetSplitList (trans);
01018         for (node = splits; node; node = node->next)
01019         {
01020             Split *split = node->data;
01021             Account *account;
01022 
01023             account = get_random_list_element (accounts);
01024 
01025             xaccAccountInsertSplit (account, split);
01026         }
01027         break;
01028 
01029     case 2: /* destroy the transaction */
01030         xaccTransDestroy (trans);
01031         xaccTransCommitEdit (trans);
01032         return;
01033 
01034     default: /* do nothing */
01035         break;
01036     }
01037 
01038     if (xaccTransGetVoidStatus (trans))
01039     {
01040         xaccTransCommitEdit (trans);
01041         return;
01042     }
01043 
01044     /* mess with the splits */
01045     splits = xaccTransGetSplitList (trans);
01046     for (node = splits; node; node = node->next)
01047     {
01048         Split *split = node->data;
01049 
01050         if (get_random_boolean ())
01051             make_random_changes_to_split (split);
01052     }
01053 
01054     if (get_random_boolean ())
01055         xaccTransCommitEdit (trans);
01056     else
01057         xaccTransRollbackEdit (trans);
01058 }
01059 
01060 static int
01061 add_trans_helper (Transaction *trans, gpointer data)
01062 {
01063     TransInfo *ti;
01064     GList **list = data;
01065 
01066     ti = g_new (TransInfo, 1);
01067 
01068     ti->guid = *xaccTransGetGUID (trans);
01069 
01070     *list = g_list_prepend (*list, ti);
01071     return 0;
01072 }
01073 
01074 void
01075 make_random_changes_to_level (QofBook *book, Account *parent)
01076 {
01077     Account *new_account;
01078     Account *account;
01079     GList *accounts;
01080     GList *transes;
01081     GList *splits;
01082     GList *node;
01083 
01084     g_return_if_fail (parent && book);
01085 
01086     accounts = gnc_account_get_descendants (parent);
01087 
01088     /* Add a new account */
01089     new_account = get_random_account (book);
01090 
01091     if (get_random_boolean () || !accounts)
01092         gnc_account_append_child (parent, new_account);
01093     else
01094     {
01095         account = get_random_list_element (accounts);
01096 
01097         gnc_account_append_child (account, new_account);
01098     }
01099 
01100     g_list_free (accounts);
01101     accounts = gnc_account_get_descendants (parent);
01102 
01103     /* Add some new transactions */
01104     add_random_transactions_to_book (book, get_random_int_in_range (1, 6));
01105 
01106     /* Mess with the accounts */
01107     for (node = accounts; node; node = node->next)
01108     {
01109         Account *account = node->data;
01110 
01111         if (get_random_boolean ())
01112             make_random_changes_to_account (book, account);
01113     }
01114 
01115     /* Mess with the transactions & splits */
01116     transes = NULL;
01117     xaccAccountTreeForEachTransaction (parent, add_trans_helper, &transes);
01118 
01119     for (node = transes; node; node = node->next)
01120     {
01121         TransInfo *ti = node->data;
01122         Transaction *trans = xaccTransLookup (&ti->guid, book);
01123 
01124         if (!trans)
01125             continue;
01126 
01127         make_random_changes_to_transaction_and_splits (book, trans, accounts);
01128     }
01129 
01130     for (node = transes; node; node = node->next)
01131     {
01132         TransInfo *ti = node->data;
01133 
01134         g_free (ti);
01135     }
01136     g_list_free (transes);
01137     transes = NULL;
01138 
01139     /* delete an account */
01140     account = get_random_list_element (accounts);
01141 
01142     splits = xaccAccountGetSplitList (account);
01143     splits = g_list_copy (splits);
01144 
01145     for (node = splits; node; node = node->next)
01146     {
01147         Split *split = node->data;
01148 
01149         do
01150         {
01151             new_account = get_random_list_element (accounts);
01152         }
01153         while (new_account == account);
01154 
01155         xaccAccountInsertSplit (new_account, split);
01156     }
01157 
01158     xaccAccountBeginEdit (account);
01159     xaccAccountDestroy (account);
01160 
01161     g_list_free (splits);
01162     g_list_free (accounts);
01163 
01164     accounts = gnc_account_get_descendants (parent);
01165 
01166     /* move some accounts around */
01167     if (accounts && (g_list_length (accounts) > 1))
01168     {
01169         int i = get_random_int_in_range (1, 4);
01170 
01171         while (i--)
01172         {
01173             Account *a1, *a2;
01174 
01175             a1 = get_random_list_element (accounts);
01176 
01177             if (get_random_boolean ())
01178                 a2 = get_random_list_element (accounts);
01179             else
01180                 a2 = NULL;
01181 
01182             if (!a2)
01183             {
01184                 gnc_account_append_child (parent, a1);
01185                 continue;
01186             }
01187 
01188             if (a1 == a2 ||
01189                     xaccAccountHasAncestor (a1, a2) ||
01190                     xaccAccountHasAncestor (a2, a1))
01191             {
01192                 i++;
01193                 continue;
01194             }
01195 
01196             gnc_account_append_child (a2, a1);
01197         }
01198     }
01199 
01200     g_list_free (accounts);
01201 }
01202 
01203 Account*
01204 get_random_account(QofBook *book)
01205 {
01206     Account *root, *ret;
01207     int tmp_int;
01208 
01209     ret = xaccMallocAccount(book);
01210 
01211     xaccAccountBeginEdit(ret);
01212 
01213     set_account_random_string_from_array(ret, xaccAccountSetName,
01214                                          sane_account_names);
01215 
01216     tmp_int = get_random_int_in_range(ACCT_TYPE_BANK, NUM_ACCOUNT_TYPES - 1);
01217     xaccAccountSetType(ret, tmp_int);
01218 
01219     set_account_random_string(ret, xaccAccountSetCode);
01220     set_account_random_string(ret, xaccAccountSetDescription);
01221 
01222     xaccAccountSetCommodity(ret, get_random_commodity(book));
01223 
01224     qof_instance_set_slots(QOF_INSTANCE(ret), get_random_kvp_frame());
01225 
01226     root = gnc_book_get_root_account (book);
01227     if (!root)
01228     {
01229         root = xaccMallocAccount (book);
01230         gnc_book_set_root_account (book, root);
01231     }
01232     gnc_account_append_child (root, ret);
01233     xaccAccountCommitEdit(ret);
01234 
01235     return ret;
01236 }
01237 
01238 void
01239 make_random_changes_to_account (QofBook *book, Account *account)
01240 {
01241     int tmp_int;
01242 
01243     g_return_if_fail (account);
01244 
01245     xaccAccountBeginEdit (account);
01246 
01247     set_account_random_string (account, xaccAccountSetName);
01248 
01249     tmp_int = get_random_int_in_range (ACCT_TYPE_BANK, NUM_ACCOUNT_TYPES - 1);
01250     xaccAccountSetType (account, tmp_int);
01251 
01252     set_account_random_string (account, xaccAccountSetCode);
01253     set_account_random_string (account, xaccAccountSetDescription);
01254 
01255     xaccAccountSetCommodity (account, get_random_commodity(book));
01256 
01257     qof_instance_set_slots(QOF_INSTANCE(account), get_random_kvp_frame());
01258 
01259     xaccAccountCommitEdit (account);
01260 }
01261 
01262 static void
01263 set_split_random_string(Split *spl,
01264                         void(*func)(Split *act, const gchar*str))
01265 {
01266     gchar *tmp_str = get_random_string();
01267     if (tmp_str)
01268     {
01269         (func)(spl, tmp_str);
01270         g_free(tmp_str);
01271     }
01272 }
01273 
01274 /* Don't do voiding here, it should be done by xaccTransVoid */
01275 static char possible_chars[] = { NREC, CREC, YREC, FREC };
01276 
01277 Split*
01278 get_random_split(QofBook *book, Account *acct, Transaction *trn)
01279 {
01280     Split *ret;
01281     gnc_numeric amt, val, rate;
01282     const gchar *str;
01283     gnc_commodity *com;
01284     int scu, denom;
01285     Timespec *ts;
01286 
01287     com = xaccTransGetCurrency (trn);
01288     scu = gnc_commodity_get_fraction(com);
01289 
01290     ret = xaccMallocSplit(book);
01291 
01292     str = get_random_string_in_array(sane_descriptions);
01293     xaccSplitSetMemo(ret, str);
01294     str = get_random_string_in_array(sane_actions);
01295     xaccSplitSetAction(ret, str);
01296 
01297     xaccSplitSetReconcile(ret, possible_chars[get_random_int_in_range(0, 3)]);
01298 
01299     ts = get_random_timespec();
01300     xaccSplitSetDateReconciledTS(ret, ts);
01301     g_free(ts);
01302 
01303     /* Split must be in an account before we can set an amount */
01304     /* and in a transaction before it can be added to an account. */
01305     xaccTransBeginEdit(trn);
01306     xaccSplitSetParent(ret, trn);
01307     xaccSplitSetAccount(ret, acct);
01308 
01309     do
01310     {
01311         val = get_random_gnc_numeric ();
01312         if (val.num == 0)
01313             fprintf(stderr, "get_random_split: Created split with zero value: %p\n", ret);
01314 
01315         if (!do_bork())
01316             val.denom = scu;
01317     }
01318     while (gnc_numeric_check(val) != GNC_ERROR_OK);
01319     xaccSplitSetValue(ret, val);
01320 
01321     /* If the currencies are the same, the split amount should equal
01322      * the split value (unless we bork it on purpose) */
01323     if (gnc_commodity_equal (xaccTransGetCurrency(trn),
01324                              xaccAccountGetCommodity(acct)) &&
01325             (!do_bork()))
01326     {
01327         amt = val;
01328     }
01329     else
01330     {
01331         denom = gnc_commodity_get_fraction(xaccAccountGetCommodity(
01332                                                xaccSplitGetAccount(ret)));
01333         do
01334         {
01335             rate = gnc_numeric_abs(get_random_gnc_numeric());
01336             amt = gnc_numeric_mul(val, rate,
01337                                   GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
01338             amt = gnc_numeric_convert(amt, denom, GNC_HOW_RND_ROUND_HALF_UP);
01339         }
01340         while (gnc_numeric_check(amt) != GNC_ERROR_OK);
01341     }
01342     xaccSplitSetAmount(ret, amt);
01343 
01344     /* Make sure val and amt have the same sign. Note that amt is
01345        also allowed to be zero, because that is caused by a small
01346        rate. */
01347     if (gnc_numeric_positive_p(val))
01348         g_assert(!gnc_numeric_negative_p(amt)); /* non-negative amt */
01349     else
01350         g_assert(!gnc_numeric_positive_p(amt)); /* non-positive amt */
01351 
01352     xaccSplitSetSlots_nc(ret, get_random_kvp_frame());
01353     xaccTransCommitEdit(trn);
01354 
01355     return ret;
01356 }
01357 
01358 void
01359 make_random_changes_to_split (Split *split)
01360 {
01361     Transaction *trans;
01362     Timespec *ts;
01363 
01364     g_return_if_fail (split);
01365 
01366     trans = xaccSplitGetParent (split);
01367 
01368     xaccTransBeginEdit (trans);
01369 
01370     set_split_random_string (split, xaccSplitSetMemo);
01371     set_split_random_string (split, xaccSplitSetAction);
01372 
01373     xaccSplitSetReconcile (split, possible_chars[get_random_int_in_range(0, 3)]);
01374 
01375     ts = get_random_timespec();
01376     xaccSplitSetDateReconciledTS (split, ts);
01377     g_free(ts);
01378 
01379     xaccSplitSetSlots_nc (split, get_random_kvp_frame());
01380 
01381     /* Don't change share values/prices here, since that would
01382      * throw transactions out of balance. Do that in the corresponding
01383      * change transaction function. */
01384 
01385     xaccTransCommitEdit (trans);
01386 }
01387 
01388 static void
01389 set_tran_random_string(Transaction* trn,
01390                        void(*func)(Transaction *act, const gchar*str))
01391 {
01392     gchar *tmp_str = get_random_string();
01393     if (!trn || !(&trn->inst))
01394     {
01395         return;
01396     }
01397     if (tmp_str)
01398     {
01399         xaccTransBeginEdit(trn);
01400         (func)(trn, tmp_str);
01401         g_free(tmp_str);
01402         xaccTransCommitEdit(trn);
01403     }
01404 }
01405 
01406 static void
01407 set_tran_random_string_from_array(
01408     Transaction* trn, void(*func)(Transaction *trn, const gchar*str),
01409     const gchar *list[])
01410 {
01411     const gchar *tmp_str = get_random_string_in_array(list);
01412     if (tmp_str)
01413         (func)(trn, tmp_str);
01414 }
01415 
01416 
01417 static void
01418 trn_add_ran_timespec(Transaction *trn, void (*func)(Transaction*,
01419                      const Timespec*))
01420 {
01421     Timespec *to_set;
01422 
01423     to_set = get_random_timespec();
01424     func(trn, to_set);
01425     g_free(to_set);
01426 }
01427 
01428 
01429 Transaction *
01430 get_random_transaction_with_currency(QofBook *book,
01431                                      gnc_commodity *currency,
01432                                      GList *account_list)
01433 {
01434     Transaction* trans;
01435     KvpFrame *f;
01436     gint num;
01437     gchar *numstr;
01438 
01439     if (!account_list)
01440     {
01441         account_list = gnc_account_get_descendants (gnc_book_get_root_account (book));
01442     }
01443 
01444     /* Gotta have at least two different accounts */
01445     if (1 >= g_list_length (account_list))
01446     {
01447         failure_args("engine-stuff", __FILE__, __LINE__,
01448                      "get_random_transaction_with_currency: account_list too short");
01449         return NULL;
01450     }
01451 
01452     numstr = g_new0(gchar, 10);
01453 
01454     trans = xaccMallocTransaction(book);
01455 
01456     xaccTransBeginEdit(trans);
01457 
01458     xaccTransSetCurrency (trans,
01459                           currency ? currency :
01460                           get_random_commodity (book));
01461 
01462     num = get_random_int_in_range (1, max_trans_num);
01463     g_snprintf(numstr, 10, "%d", num);
01464     xaccTransSetNum(trans, numstr);
01465     set_tran_random_string_from_array(trans, xaccTransSetDescription,
01466                                       sane_descriptions);
01467     trn_add_ran_timespec(trans, xaccTransSetDatePostedTS);
01468     trn_add_ran_timespec(trans, xaccTransSetDateEnteredTS);
01469 
01470     f = get_random_kvp_frame();
01471     xaccTransSetSlots_nc(trans, f);
01472 
01473     add_random_splits(book, trans, account_list);
01474 
01475     if (get_random_int_in_range (1, 10) == 1)
01476     {
01477         char *reason = get_random_string ();
01478         xaccTransVoid (trans, reason);
01479         g_free (reason);
01480     }
01481 
01482     xaccTransCommitEdit(trans);
01483     if (!trans)
01484     {
01485         failure_args("engine-stuff", __FILE__, __LINE__,
01486                      "get_random_transaction_with_currency failed");
01487         return NULL;
01488     }
01489 
01490     return trans;
01491 }
01492 
01493 Transaction*
01494 get_random_transaction (QofBook *book)
01495 {
01496     Transaction *ret;
01497 
01498     g_return_val_if_fail(book, NULL);
01499     ret = get_random_transaction_with_currency (book, NULL, NULL);
01500     if (!ret)
01501     {
01502         failure_args("engine-stuff", __FILE__, __LINE__,
01503                      "get_random_transaction failed");
01504         return NULL;
01505     }
01506     return ret;
01507 }
01508 
01509 void
01510 make_random_changes_to_transaction (QofBook *book, Transaction *trans)
01511 {
01512     g_return_if_fail (trans && book);
01513 
01514     if (xaccTransGetVoidStatus (trans))
01515     {
01516         if (get_random_int_in_range (1, 2) == 1)
01517             xaccTransUnvoid (trans);
01518         return;
01519     }
01520 
01521     xaccTransBeginEdit (trans);
01522 
01523     xaccTransSetCurrency (trans, get_random_commodity (book));
01524 
01525     set_tran_random_string (trans, xaccTransSetNum);
01526 
01527     trn_add_ran_timespec (trans, xaccTransSetDatePostedTS);
01528     trn_add_ran_timespec (trans, xaccTransSetDateEnteredTS);
01529 
01530     set_tran_random_string (trans, xaccTransSetDescription);
01531 
01532     xaccTransSetSlots_nc (trans, get_random_kvp_frame());
01533 
01534     /* Do split manipulations in higher-level functions */
01535 
01536     xaccTransCommitEdit (trans);
01537 }
01538 
01539 
01540 static GList *
01541 get_random_guids(int max)
01542 {
01543     GList *guids = NULL;
01544     int num_guids;
01545 
01546     if (max < 1) return NULL;
01547 
01548     num_guids = get_random_int_in_range (1, max);
01549 
01550     while (num_guids-- > 0)
01551         guids = g_list_prepend (guids, get_random_guid ());
01552 
01553     return guids;
01554 }
01555 
01556 static void
01557 free_random_guids(GList *guids)
01558 {
01559     GList *node;
01560 
01561     for (node = guids; node; node = node->next)
01562         g_free (node->data);
01563 
01564     g_list_free (guids);
01565 }
01566 
01567 static QofQueryOp
01568 get_random_queryop(void)
01569 {
01570     int op_num = get_random_int_in_range(1, 11);
01571     QofQueryOp op = QOF_QUERY_AND;
01572     /* = get_random_int_in_range (1, QOF_QUERY_XOR); */
01573 
01574     /* Let's make it MUCH more likely to get AND and OR */
01575     switch (op_num)
01576     {
01577     case 1:
01578     case 2:
01579     case 3:
01580     case 4:
01581         op = QOF_QUERY_AND;
01582         break;
01583     case 5:
01584     case 6:
01585     case 7:
01586     case 8:
01587         op = QOF_QUERY_OR;
01588         break;
01589     case 9:
01590         op = QOF_QUERY_NAND;
01591         break;
01592     case 10:
01593         op = QOF_QUERY_NOR;
01594         break;
01595     case 11:
01596         op = QOF_QUERY_XOR;
01597         break;
01598     default:
01599         g_assert_not_reached();
01600     };
01601     if (gnc_engine_debug_random) printf ("op = %d (int was %d), ", op, op_num);
01602     return op;
01603 }
01604 
01605 static GSList *
01606 get_random_kvp_path (void)
01607 {
01608     GSList *path;
01609     gint len;
01610 
01611     path = NULL;
01612     len = get_random_int_in_range (1, kvp_max_depth);
01613 
01614     while (len--)
01615         path = g_slist_prepend (path, get_random_string ());
01616 
01617     return g_slist_reverse (path);
01618 }
01619 
01620 static void
01621 free_random_kvp_path (GSList *path)
01622 {
01623     GSList *node;
01624 
01625     for (node = path; node; node = node->next)
01626         g_free (node->data);
01627 
01628     g_slist_free (path);
01629 }
01630 
01631 static QofIdType
01632 get_random_id_type (void)
01633 {
01634     switch (get_random_int_in_range (1, 3))
01635     {
01636     case 1:
01637         return GNC_ID_SPLIT;
01638     case 2:
01639         return GNC_ID_TRANS;
01640     case 3:
01641         return GNC_ID_ACCOUNT;
01642     default:
01643         return get_random_string ();
01644     }
01645 }
01646 
01647 typedef enum
01648 {
01649     BY_STANDARD = 1,
01650     BY_DATE,
01651     BY_DATE_ENTERED,
01652     BY_DATE_RECONCILED,
01653     BY_NUM,
01654     BY_AMOUNT,
01655     BY_MEMO,
01656     BY_DESC,
01657     BY_NONE
01658 } sort_type_t;
01659 
01660 static void
01661 set_query_sort (QofQuery *q, sort_type_t sort_code)
01662 {
01663     GSList *p1 = NULL, *p2 = NULL, *p3 = NULL, *standard;
01664 
01665     standard = g_slist_prepend (NULL, QUERY_DEFAULT_SORT);
01666 
01667     switch (sort_code)
01668     {
01669     case BY_STANDARD:
01670         p1 = standard;
01671         break;
01672     case BY_DATE:
01673         p1 = g_slist_prepend (p1, TRANS_DATE_POSTED);
01674         p1 = g_slist_prepend (p1, SPLIT_TRANS);
01675         p2 = standard;
01676         break;
01677     case BY_DATE_ENTERED:
01678         p1 = g_slist_prepend (p1, TRANS_DATE_ENTERED);
01679         p1 = g_slist_prepend (p1, SPLIT_TRANS);
01680         p2 = standard;
01681         break;
01682     case BY_DATE_RECONCILED:
01683         p1 = g_slist_prepend (p1, SPLIT_RECONCILE);
01684         p2 = g_slist_prepend (p2, SPLIT_DATE_RECONCILED);
01685         p3 = standard;
01686         break;
01687     case BY_NUM:
01688         p1 = g_slist_prepend (p1, TRANS_NUM);
01689         p1 = g_slist_prepend (p1, SPLIT_TRANS);
01690         p2 = standard;
01691         break;
01692     case BY_AMOUNT:
01693         p1 = g_slist_prepend (p1, SPLIT_VALUE);
01694         p2 = standard;
01695         break;
01696     case BY_MEMO:
01697         p1 = g_slist_prepend (p1, SPLIT_MEMO);
01698         p2 = standard;
01699         break;
01700     case BY_DESC:
01701         p1 = g_slist_prepend (p1, TRANS_DESCRIPTION);
01702         p1 = g_slist_prepend (p1, SPLIT_TRANS);
01703         p2 = standard;
01704         break;
01705     case BY_NONE:
01706         g_slist_free (standard);
01707         break;
01708     default:
01709         g_slist_free (standard);
01710         g_return_if_fail (FALSE);
01711     }
01712 
01713     qof_query_set_sort_order (q, p1, p2, p3);
01714 }
01715 
01716 QofQuery *
01717 get_random_query(void)
01718 {
01719     QofQuery *q;
01720     int num_terms;
01721 
01722     num_terms = get_random_int_in_range (1, 3);
01723     if (gnc_engine_debug_random) printf("num_terms = %d", num_terms);
01724 
01725     q = qof_query_create_for(GNC_ID_SPLIT);
01726 
01727     while (num_terms-- > 0)
01728     {
01729         gint pr_type;
01730         KvpValue *value;
01731         Timespec *start;
01732         Timespec *end;
01733         GList *guids;
01734         GSList *path;
01735         char *string;
01736         GncGUID *guid;
01737 
01738         pr_type = get_random_int_in_range (1, 20);
01739         if (gnc_engine_debug_random) printf("\n pr_type = %d ", pr_type);
01740 
01741         switch (pr_type)
01742         {
01743         case 1: /*PR_ACCOUNT */
01744             guids = get_random_guids (10);
01745             xaccQueryAddAccountGUIDMatch
01746             (q,
01747              guids,
01748              get_random_int_in_range (1, QOF_GUID_MATCH_NONE),
01749              get_random_queryop ());
01750             free_random_guids (guids);
01751             break;
01752 
01753         case 2: /*PR_ACTION */
01754             string = get_random_string ();
01755             xaccQueryAddActionMatch (q,
01756                                      string,
01757                                      get_random_boolean (),
01758                                      get_random_boolean (),
01759                                      get_random_queryop ());
01760             g_free (string);
01761             break;
01762 
01763         case 3: /* PR_BALANCE */
01764             xaccQueryAddBalanceMatch
01765             (q,
01766              get_random_boolean (),
01767              get_random_queryop ());
01768             break;
01769 
01770         case 4: /* PR_CLEARED */
01771             xaccQueryAddClearedMatch
01772             (q,
01773              get_random_int_in_range (1,
01774                                       CLEARED_NO |
01775                                       CLEARED_CLEARED |
01776                                       CLEARED_RECONCILED |
01777                                       CLEARED_FROZEN |
01778                                       CLEARED_VOIDED),
01779              get_random_queryop ());
01780             break;
01781 
01782         case 5: /* PR_DATE */
01783             start = get_random_timespec ();
01784             end = get_random_timespec ();
01785             xaccQueryAddDateMatchTS (q,
01786                                      get_random_boolean (),
01787                                      *start,
01788                                      get_random_boolean (),
01789                                      *end,
01790                                      get_random_queryop ());
01791             g_free (start);
01792             g_free (end);
01793             break;
01794 
01795         case 6: /* PR_DESC */
01796             string = get_random_string ();
01797             xaccQueryAddDescriptionMatch (q,
01798                                           string,
01799                                           get_random_boolean (),
01800                                           get_random_boolean (),
01801                                           get_random_queryop ());
01802             g_free (string);
01803             break;
01804 
01805         case 7: /* PR_GUID */
01806             guid = get_random_guid ();
01807             xaccQueryAddGUIDMatch (q,
01808                                    guid,
01809                                    get_random_id_type (),
01810                                    get_random_queryop ());
01811             g_free (guid);
01812             break;
01813 
01814         case 8: /* PR_KVP */
01815             path = get_random_kvp_path ();
01816             do
01817             {
01818                 value = get_random_kvp_value_depth (-2, kvp_max_depth);
01819             }
01820             while (!value);
01821             xaccQueryAddKVPMatch (q,
01822                                   path,
01823                                   value,
01824                                   get_random_int_in_range (1, QOF_COMPARE_NEQ),
01825                                   get_random_id_type (),
01826                                   get_random_queryop ());
01827             kvp_value_delete (value);
01828             free_random_kvp_path (path);
01829             break;
01830 
01831         case 9: /* PR_MEMO */
01832             string = get_random_string ();
01833             xaccQueryAddMemoMatch (q,
01834                                    string,
01835                                    get_random_boolean (),
01836                                    get_random_boolean (),
01837                                    get_random_queryop ());
01838             g_free (string);
01839             break;
01840 
01841         case 10: /* PR_NUM */
01842             string = get_random_string ();
01843             xaccQueryAddNumberMatch (q,
01844                                      string,
01845                                      get_random_boolean (),
01846                                      get_random_boolean (),
01847                                      get_random_queryop ());
01848             g_free (string);
01849             break;
01850 
01851         case 11: /*  PR_PRICE */
01852             xaccQueryAddSharePriceMatch
01853             (q,
01854              get_random_gnc_numeric (),
01855              get_random_int_in_range (1, QOF_COMPARE_NEQ),
01856              get_random_queryop ());
01857             break;
01858 
01859         case 12: /* PR_SHRS */
01860             xaccQueryAddSharesMatch
01861             (q,
01862              get_random_gnc_numeric (),
01863              get_random_int_in_range (1, QOF_COMPARE_NEQ),
01864              get_random_queryop ());
01865             break;
01866 
01867         case 13: /* PR_VALUE */
01868             xaccQueryAddValueMatch
01869             (q,
01870              get_random_gnc_numeric (),
01871              get_random_int_in_range (1, QOF_NUMERIC_MATCH_ANY),
01872              get_random_int_in_range (1, QOF_COMPARE_NEQ),
01873              get_random_queryop ());
01874             break;
01875 
01876         default:
01877             if (gnc_engine_debug_random) printf("ignored..");
01878             num_terms++;
01879             break;
01880         }
01881     }
01882 
01883     if (gnc_engine_debug_random) printf ("\n");
01884     set_query_sort (q, get_random_int_in_range (1, BY_NONE));
01885 
01886     qof_query_set_sort_increasing (q,
01887                                    get_random_boolean (),
01888                                    get_random_boolean (),
01889                                    get_random_boolean ());
01890 
01891     qof_query_set_max_results (q, get_random_int_in_range (-50000, 50000));
01892 
01893     return q;
01894 }
01895 
01896 QofBook *
01897 get_random_book (void)
01898 {
01899     QofBook *book;
01900 
01901     book = qof_book_new ();
01902 
01903     get_random_account_tree (book);
01904     get_random_pricedb (book);
01905 
01906     return book;
01907 }
01908 
01909 QofSession *
01910 get_random_session (void)
01911 {
01912     QofSession *session;
01913     QofBook *book;
01914 
01915     session = qof_session_new ();
01916 
01917     book = qof_session_get_book (session);
01918 
01919     get_random_account_tree (book);
01920     get_random_pricedb (book);
01921 
01922     return session;
01923 }
01924 
01925 void
01926 add_random_transactions_to_book (QofBook *book, gint num_transactions)
01927 {
01928     gnc_commodity_table *table;
01929     GList *accounts;
01930 
01931     if (num_transactions <= 0) return;
01932 
01933     g_return_if_fail (book);
01934 
01935     accounts = gnc_account_get_descendants (gnc_book_get_root_account (book));
01936     g_return_if_fail (accounts);
01937 
01938     table = gnc_commodity_table_get_table (book);
01939 
01940     while (num_transactions--)
01941     {
01942         gnc_commodity *com;
01943         Transaction *trans;
01944 
01945         com = get_random_commodity_from_table (table);
01946         trans = get_random_transaction_with_currency (book, com, accounts);
01947     }
01948     g_list_free (accounts);
01949 }
01950 
01951 void
01952 make_random_changes_to_book (QofBook *book)
01953 {
01954     g_return_if_fail (book);
01955 
01956     make_random_changes_to_level (book, gnc_book_get_root_account (book));
01957     make_random_changes_to_pricedb (book, gnc_pricedb_get_db (book));
01958 
01959 #if 0
01960     make_random_changes_to_commodity_table (gnc_commodity_table_get_table (book));
01961 #endif
01962 }
01963 
01964 void
01965 make_random_changes_to_session (QofSession *session)
01966 {
01967     g_return_if_fail (session);
01968 
01969     make_random_changes_to_book (qof_session_get_book (session));
01970 }
01971 
01972 typedef struct
01973 {
01974     QofIdType where;
01975     GSList *path;
01976     QofQuery *q;
01977 } KVPQueryData;
01978 
01979 static void
01980 add_kvp_value_query (const char *key, KvpValue *value, gpointer data)
01981 {
01982     KVPQueryData *kqd = data;
01983     GSList *node;
01984 
01985     kqd->path = g_slist_append (kqd->path, (gpointer) key);
01986 
01987     if (kvp_value_get_type (value) == KVP_TYPE_FRAME)
01988         kvp_frame_for_each_slot (kvp_value_get_frame (value),
01989                                  add_kvp_value_query, data);
01990     else
01991         xaccQueryAddKVPMatch (kqd->q, kqd->path, value,
01992                               QOF_COMPARE_EQUAL, kqd->where,
01993                               QOF_QUERY_AND);
01994 
01995     node = g_slist_last (kqd->path);
01996     kqd->path = g_slist_remove_link (kqd->path, node);
01997     g_slist_free_1 (node);
01998 }
01999 
02000 static void
02001 add_kvp_query (QofQuery *q, KvpFrame *frame, QofIdType where)
02002 {
02003     KVPQueryData kqd;
02004 
02005     kqd.where = where;
02006     kqd.path = NULL;
02007     kqd.q = q;
02008 
02009     kvp_frame_for_each_slot (frame, add_kvp_value_query, &kqd);
02010 }
02011 
02012 static gboolean include_price = TRUE;
02013 
02014 void
02015 trans_query_include_price (gboolean include_price_in)
02016 {
02017     include_price = include_price_in;
02018 }
02019 
02020 TestQueryTypes
02021 get_random_query_type (void)
02022 {
02023     switch (get_random_int_in_range (0, 4))
02024     {
02025     case 0:
02026         return SIMPLE_QT;
02027     case 1:
02028         return SPLIT_KVP_QT;
02029     case 2:
02030         return TRANS_KVP_QT;
02031     case 3:
02032         return ACCOUNT_KVP_QT;
02033     case 4:
02034         return GUID_QT;
02035     default:
02036         return SIMPLE_QT;
02037     }
02038 }
02039 
02040 QofQuery *
02041 make_trans_query (Transaction *trans, TestQueryTypes query_types)
02042 {
02043     Account *a;
02044     gnc_numeric n;
02045     QofQuery *q;
02046     Split *s;
02047 
02048     if (query_types == RANDOM_QT)
02049         query_types = get_random_query_type ();
02050 
02051     q = qof_query_create_for(GNC_ID_SPLIT);
02052 
02053     s = xaccTransGetSplit (trans, 0);
02054     a = xaccSplitGetAccount (s);
02055 
02056     if (query_types & SIMPLE_QT)
02057     {
02058         xaccQueryAddSingleAccountMatch (q, xaccSplitGetAccount (s), QOF_QUERY_AND);
02059 
02060         if (xaccTransGetDescription(trans) && *xaccTransGetDescription(trans) != '\0')
02061         {
02062             xaccQueryAddDescriptionMatch (q, xaccTransGetDescription (trans),
02063                                           TRUE, FALSE, QOF_QUERY_AND);
02064         }
02065 
02066         if (xaccTransGetNum(trans) && *xaccTransGetNum(trans) != '\0')
02067         {
02068             xaccQueryAddNumberMatch (q, xaccTransGetNum (trans),
02069                                      TRUE, FALSE, QOF_QUERY_AND);
02070         }
02071 
02072         if (xaccSplitGetAction(s) && *xaccSplitGetAction(s) != '\0')
02073         {
02074             xaccQueryAddActionMatch (q, xaccSplitGetAction (s),
02075                                      TRUE, FALSE, QOF_QUERY_AND);
02076         }
02077 
02078         n = xaccSplitGetValue (s);
02079         xaccQueryAddValueMatch (q, n, QOF_NUMERIC_MATCH_ANY,
02080                                 QOF_COMPARE_EQUAL, QOF_QUERY_AND);
02081 
02082         n = xaccSplitGetAmount (s);
02083         xaccQueryAddSharesMatch (q, n, QOF_COMPARE_EQUAL, QOF_QUERY_AND);
02084 
02085         if (include_price)
02086         {
02087             n = xaccSplitGetSharePrice (s);
02088             xaccQueryAddSharePriceMatch (q, n, QOF_COMPARE_EQUAL, QOF_QUERY_AND);
02089         }
02090 
02091         {
02092             Timespec ts;
02093 
02094             xaccTransGetDatePostedTS (trans, &ts);
02095             xaccQueryAddDateMatchTS (q, TRUE, ts, TRUE, ts, QOF_QUERY_AND);
02096         }
02097 
02098         if (xaccSplitGetMemo(s) && *xaccSplitGetMemo(s) != '\0')
02099         {
02100             xaccQueryAddMemoMatch (q, xaccSplitGetMemo (s), TRUE, FALSE, QOF_QUERY_AND);
02101         }
02102 
02103         {
02104             cleared_match_t how;
02105 
02106             switch (xaccSplitGetReconcile (s))
02107             {
02108             case NREC:
02109                 how = CLEARED_NO;
02110                 break;
02111             case CREC:
02112                 how = CLEARED_CLEARED;
02113                 break;
02114             case YREC:
02115                 how = CLEARED_RECONCILED;
02116                 break;
02117             case FREC:
02118                 how = CLEARED_FROZEN;
02119                 break;
02120             case VREC:
02121                 how = CLEARED_VOIDED;
02122                 break;
02123             default:
02124                 failure ("bad reconcile flag");
02125                 qof_query_destroy (q);
02126                 return NULL;
02127             }
02128 
02129             xaccQueryAddClearedMatch (q, how, QOF_QUERY_AND);
02130         }
02131     }
02132 
02133     if (query_types & ACCOUNT_QT)
02134     {
02135         GList * list;
02136         GList * node;
02137 
02138         /* QOF_GUID_MATCH_ALL */
02139         list = NULL;
02140         for (node = xaccTransGetSplitList (trans); node; node = node->next)
02141         {
02142             Split * split = node->data;
02143             list = g_list_prepend (list, xaccSplitGetAccount (split));
02144         }
02145         xaccQueryAddAccountMatch (q, list, QOF_GUID_MATCH_ALL, QOF_QUERY_AND);
02146         g_list_free (list);
02147 
02148         /* QOF_GUID_MATCH_NONE */
02149         list = NULL;
02150         list = g_list_prepend (list, get_random_guid ());
02151         list = g_list_prepend (list, get_random_guid ());
02152         list = g_list_prepend (list, get_random_guid ());
02153         xaccQueryAddAccountGUIDMatch (q, list, QOF_GUID_MATCH_NONE, QOF_QUERY_AND);
02154 
02155         /* QOF_GUID_MATCH_ANY */
02156         {
02157             GncGUID * guid = get_random_guid ();
02158             *guid = *xaccAccountGetGUID (a);
02159             list = g_list_prepend (list, guid);
02160         }
02161         xaccQueryAddAccountGUIDMatch (q, list, QOF_GUID_MATCH_ANY, QOF_QUERY_AND);
02162 
02163         for (node = list; node; node = node->next)
02164             g_free (node->data);
02165         g_list_free (list);
02166     }
02167 
02168     if (query_types & GUID_QT)
02169     {
02170         xaccQueryAddGUIDMatch (q, xaccSplitGetGUID (s),
02171                                GNC_ID_SPLIT, QOF_QUERY_AND);
02172 
02173         xaccQueryAddGUIDMatch (q, xaccTransGetGUID (trans),
02174                                GNC_ID_TRANS, QOF_QUERY_AND);
02175 
02176         xaccQueryAddGUIDMatch (q, xaccAccountGetGUID (a),
02177                                GNC_ID_ACCOUNT, QOF_QUERY_AND);
02178     }
02179 
02180     if (query_types & SPLIT_KVP_QT)
02181         add_kvp_query (q, xaccSplitGetSlots (s), GNC_ID_SPLIT);
02182 
02183     if (query_types & TRANS_KVP_QT)
02184         add_kvp_query (q, xaccTransGetSlots (trans), GNC_ID_TRANS);
02185 
02186     if (query_types & ACCOUNT_KVP_QT)
02187         add_kvp_query (q, xaccAccountGetSlots (a), GNC_ID_ACCOUNT);
02188 
02189     return q;
02190 }
02191 
02192 static Recurrence*
02193 daily_freq(const GDate* start, int multiplier)
02194 {
02195     Recurrence *r = g_new0(Recurrence, 1);
02196     recurrenceSet(r, multiplier, PERIOD_DAY, start, WEEKEND_ADJ_NONE);
02197     return r;
02198 }
02199 
02200 static Recurrence*
02201 once_freq(const GDate *when)
02202 {
02203     Recurrence *r = g_new0(Recurrence, 1);
02204     recurrenceSet(r, 1, PERIOD_ONCE, when, WEEKEND_ADJ_NONE);
02205     return r;
02206 }
02207 
02208 static SchedXaction*
02209 add_sx(gchar *name, const GDate *start, const GDate *end, const GDate *last_occur, Recurrence *r)
02210 {
02211     QofBook *book = qof_session_get_book(gnc_get_current_session());
02212     SchedXaction *sx = xaccSchedXactionMalloc(book);
02213     xaccSchedXactionSetName(sx, name);
02214     xaccSchedXactionSetStartDate(sx, start);
02215     if (end != NULL)
02216         xaccSchedXactionSetEndDate(sx, end);
02217     if (last_occur != NULL)
02218         xaccSchedXactionSetLastOccurDate(sx, last_occur);
02219     {
02220         GList *recurrences = NULL;
02221         recurrences = g_list_append(recurrences, r);
02222         gnc_sx_set_schedule(sx, recurrences);
02223     }
02224 
02225     gnc_sxes_add_sx(gnc_book_get_schedxactions(book), sx);
02226 
02227     return sx;
02228 }
02229 
02230 SchedXaction*
02231 add_daily_sx(gchar *name, const GDate *start, const GDate *end, const GDate *last_occur)
02232 {
02233     return add_sx(name, start, end, last_occur, daily_freq(start, 1));
02234 }
02235 
02236 SchedXaction*
02237 add_once_sx(gchar *name, const GDate *when)
02238 {
02239     return add_sx(name, when, NULL, NULL, once_freq(when));
02240 }
02241 
02242 void
02243 remove_sx(SchedXaction *sx)
02244 {
02245     QofBook *book = qof_session_get_book(gnc_get_current_session());
02246     SchedXactions *sxes = gnc_book_get_schedxactions(book);
02247     gnc_sxes_del_sx(sxes, sx);
02248 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines