|
GnuCash 2.4.99
|
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 }
1.7.4