GnuCash c935c2f+
Loading...
Searching...
No Matches
test-engine-stuff.cpp
1
18/********************************************************************\
19 * This program is free software; you can redistribute it and/or *
20 * modify it under the terms of the GNU General Public License as *
21 * published by the Free Software Foundation; either version 2 of *
22 * the License, or (at your option) any later version. *
23 * *
24 * This program is distributed in the hope that it will be useful, *
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
27 * GNU General Public License for more details. *
28 * *
29 * You should have received a copy of the GNU General Public License*
30 * along with this program; if not, contact: *
31 * *
32 * Free Software Foundation Voice: +1-617-542-5942 *
33 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
34 * Boston, MA 02110-1301, USA gnu@gnu.org *
35 * *
36\********************************************************************/
37
38#include <guid.hpp>
39#include <kvp-frame.hpp>
40
41#include <platform.h>
42#if PLATFORM(WINDOWS)
43#define __STDC_FORMAT_MACROS
44#endif
45#include <sys/types.h>
46#include <dirent.h>
47#include <fcntl.h>
48#include <glib.h>
49#include <stdio.h>
50#include <inttypes.h>
51#include <stdlib.h>
52#include <string.h>
53#include <sys/stat.h>
54
55#include <string>
56
57#include <qof.h>
58
59#include "Account.h"
60#include "AccountP.hpp"
61#include "gnc-engine.h"
62#include "gnc-session.h"
63#include "Transaction.h"
64#include "TransactionP.hpp"
65#include "Recurrence.h"
66#include "SchedXaction.h"
67#include "SX-book.h"
68
69#include "test-engine-stuff.h"
70#include "test-stuff.h"
71#include "test-engine-strings.h"
72#include <qofinstance-p.h>
73
74static GHashTable *exclude_kvp_types = NULL;
75static gint kvp_max_depth = 5;
76static gint kvp_frame_max_elements = 10;
77
78static gint max_tree_depth = 1;
79static gint max_level_accounts = 3;
80static gint max_total_accounts = 10;
81static gint max_trans_num = 1000;
82static gint total_num_accounts = 0;
83/* SCU == smallest currency unit -- the value of the denominator */
84static gint max_scu = 100; //6000;
85static gint min_scu = 100; //1;
86static const int64_t num_limit = INT64_MAX; //1E19+
87static const int64_t max_denom_mult = 1000000000LL; //1E9
88
89
90/* The inverse fraction of split/transaction data that should
91 * contain invalid/inconsistent fields/values. Thus,
92 * if borked==1000, then one in 1000 fields will have bad data.
93 * This is used to test the data integrity scrubbers, which are
94 * supposed to clean up any crud they find.
95 */
96static gint borked = 80;
97
98gboolean gnc_engine_debug_random = FALSE;
99
100/* ========================================================== */
101/* Set control parameters governing the run. */
102
103void
104set_max_account_tree_depth (gint max_tree_depth_in)
105{
106 max_tree_depth = MAX (max_tree_depth_in, 1);
107}
108
109void
110set_max_accounts_per_level (gint max_level_accounts_in)
111{
112 max_level_accounts = MAX (max_level_accounts_in, 1);
113}
114
115void
116set_max_kvp_depth (gint max_kvp_depth)
117{
118 kvp_max_depth = MAX (max_kvp_depth, 1);
119}
120
121void
122set_max_kvp_frame_elements (gint max_kvp_frame_elements)
123{
124 kvp_frame_max_elements = MAX (max_kvp_frame_elements, 1);
125}
126
127static gboolean
128kvp_type_excluded (KvpValue::Type kvp_type)
129{
130 gint key = kvp_type;
131
132 if (!exclude_kvp_types)
133 return FALSE;
134
135 if (g_hash_table_lookup (exclude_kvp_types, &key))
136 return TRUE;
137
138 return FALSE;
139}
140
141/* ========================================================== */
142
143static inline gboolean
144do_bork (void)
145{
146 if (1 == get_random_int_in_range (0, borked))
147 {
148 return TRUE;
149 }
150 return FALSE;
151}
152
153/* ========================================================== */
154/* GList stuff */
155
156static gpointer
157get_random_list_element (GList *list)
158{
159 g_return_val_if_fail (list, NULL);
160
161 return g_list_nth_data (list,
162 get_random_int_in_range (0,
163 g_list_length (list) - 1));
164}
165
166static KvpValue* get_random_kvp_value_depth (int type, gint depth);
167
168static GList*
169get_random_glist_depth (gint depth)
170{
171 GList *ret = NULL;
172 int count = get_random_int_in_range(1, 5);
173 int i;
174
175 if (depth >= kvp_max_depth)
176 return NULL;
177
178 for (i = 0; i < count; i++)
179 {
180 KvpValue *value = nullptr;
181
182 do
183 {
184 value = get_random_kvp_value_depth (-2, depth + 1);
185 }
186 while (!value);
187
188 ret = g_list_prepend(ret, value);
189 }
190
191 return ret;
192}
193
194/* ========================================================== */
195/* Time/Date, GncGUID data stuff */
196
197time64
198get_random_time (void)
199{
200 time64 ret {0};
201 while (ret <= 0)
202 ret = rand();
203 return ret;
204}
205
206GncGUID*
207get_random_guid(void)
208{
209 return guid_new();
210}
211
212/* ========================================================== */
213/* KVP stuff */
214
215static KvpFrame* get_random_kvp_frame_depth (gint depth);
216
217static KvpValue*
218get_random_kvp_value_depth (int type, gint depth)
219{
220 KvpValue::Type datype;
221 KvpValue *ret;
222
223 if (type == -1)
224 {
225 datype = static_cast<KvpValue::Type>(get_random_int_in_range(KvpValue::Type::INT64, KvpValue::Type::FRAME));
226 }
227
228 else if (type == -2)
229 {
230 datype = static_cast<KvpValue::Type>(get_random_int_in_range(KvpValue::Type::INT64, KvpValue::Type::FRAME - 1));
231 }
232 else
233 datype = static_cast<KvpValue::Type>(type);
234
235 if (datype == KvpValue::Type::FRAME && depth >= kvp_max_depth)
236 return NULL;
237
238 if (datype == KvpValue::Type::GLIST && depth >= kvp_max_depth)
239 return NULL;
240
241 if (kvp_type_excluded (datype))
242 return NULL;
243
244 switch (datype)
245 {
246 case KvpValue::Type::INT64:
247 ret = new KvpValue(get_random_gint64());
248 break;
249
250 case KvpValue::Type::DOUBLE:
251 ret = NULL;
252 break;
253
254 case KvpValue::Type::NUMERIC:
255 ret = new KvpValue(get_random_gnc_numeric(GNC_DENOM_AUTO));
256 break;
257
258 case KvpValue::Type::STRING:
259 {
260 gchar *tmp_str;
261 tmp_str = get_random_string();
262 if (!tmp_str)
263 return NULL;
264
265 ret = new KvpValue(tmp_str);
266 }
267 break;
268
269 case KvpValue::Type::GUID:
270 {
271 return new KvpValue(get_random_guid());
272 }
273 break;
274
275 case KvpValue::Type::TIME64:
276 {
277 time64 t = get_random_time();
278 ret = new KvpValue(t);
279 }
280 break;
281
282 case KvpValue::Type::GLIST:
283 ret = new KvpValue(get_random_glist_depth (depth + 1));
284 break;
285
286 case KvpValue::Type::FRAME:
287 {
288 return new KvpValue(get_random_kvp_frame_depth(depth + 1));
289 }
290 break;
291
292 default:
293 ret = NULL;
294 break;
295 }
296 return ret;
297}
298
299static KvpFrame*
300get_random_kvp_frame_depth (gint depth)
301{
302 int vals_to_add;
303 gboolean val_added;
304
305 if (depth >= kvp_max_depth)
306 return NULL;
307
308 auto ret = new KvpFrame;
309
310 vals_to_add = get_random_int_in_range(1, kvp_frame_max_elements);
311 val_added = FALSE;
312
313 for (; vals_to_add > 0; vals_to_add--)
314 {
315 gchar *key;
316 KvpValue *val;
317
318 key = NULL;
319 while (key == NULL)
320 {
321 key = get_random_string_without("/");
322 if (*key == '\0')
323 {
324 g_free(key);
325 key = NULL;
326 }
327 }
328
329 val = get_random_kvp_value_depth (-1, depth + 1);
330 if (!val)
331 {
332 g_free(key);
333 if (!val_added)
334 vals_to_add++;
335 continue;
336 }
337
338 val_added = TRUE;
339
340 ret->set_path({key}, val);
341
342 g_free(key);
343 }
344
345 return ret;
346}
347
348KvpFrame *
349get_random_kvp_frame (void)
350{
351 return get_random_kvp_frame_depth (0);
352}
353
354KvpValue *
355get_random_kvp_value(int type)
356{
357 return get_random_kvp_value_depth (type, 0);
358}
359
360/* ================================================================= */
361/* Numeric stuff */
362
363#define RAND_IN_RANGE(X) (((X)*((gint64) (rand()+1)))/RAND_MAX)
364
365gnc_numeric
366get_random_gnc_numeric(int64_t deno)
367{
368 gint64 numer;
369 int64_t limit;
370 if (deno == GNC_DENOM_AUTO)
371 {
372 if (RAND_MAX / 8 > rand())
373 {
374 /* Random number between 1 and 6000 */
375 deno = RAND_IN_RANGE(6000ULL);
376 }
377 else
378 {
379 gint64 norm = RAND_IN_RANGE (11ULL);
380
381 /* multiple of 10, between 1 and 100 billion */
382 deno = 1;
383 while (norm)
384 {
385 deno *= 10;
386 norm --;
387 }
388 }
389 }
390 /* Make sure we have a non-zero denominator */
391 if (0 == deno) deno = 1;
392
393 /* Arbitrary random numbers can cause pointless overflow during
394 * calculations, in particular the revaluing in xaccSplitSetValue where an
395 * input gnc_numeric is converted to use a new denominator. To prevent that,
396 * the numerator is clamped to the larger of num_limit / deno or num_limit /
397 * max_denom_mult.
398 */
399 limit = num_limit / (max_denom_mult / deno == 0 ? max_denom_mult : max_denom_mult / deno);
400 numer = get_random_gint64 ();
401 if (numer > limit)
402 {
403 int64_t num = numer % limit;
404 if (num)
405 numer = num;
406 else
407 numer = limit;
408 }
409 if (0 == numer) numer = 1;
410 g_log("test.engine.suff", G_LOG_LEVEL_INFO, "New GncNumeric %" PRIu64 " / %" PRIu64 " !\n", numer, deno);
411 return gnc_numeric_create(numer, deno);
412}
413
414
415/* Rate here really means price or exchange rate, this is used solely
416 * to compute an amount from a randomly-created value. */
417static gnc_numeric
418get_random_rate (void)
419{
420 /* Large rates blow up xaccSplitAssignToLot, so we clamp the rate
421 * at a smallish value */
422 gint64 numer = get_random_gint64 () % (2ULL << 24);
423 gint64 denom = 100LL;
424 return gnc_numeric_create (numer, denom);
425}
426
427/* ================================================================= */
428/* Commodity stuff */
429
430const char *types[] =
431{
432 "NASDAQ",
433 "NYSE",
434 "EUREX",
435 "FUND",
436 "AMEX",
437 NULL
438};
439
440const char*
441get_random_commodity_namespace(void)
442{
443 return get_random_string_in_array(types);
444}
445
446static gnc_commodity *
447get_random_commodity_from_table (gnc_commodity_table *table)
448{
449 GList *namespaces;
450 gnc_commodity *com = NULL;
451
452 g_return_val_if_fail (table, NULL);
453
455
456 do
457 {
458 GList *commodities;
459 char *name_space;
460
461 name_space = static_cast<char*>(get_random_list_element (namespaces));
462
463 commodities = gnc_commodity_table_get_commodities (table, name_space);
464 if (!commodities)
465 continue;
466
467 com = static_cast<gnc_commodity*>(get_random_list_element (commodities));
468
469 g_list_free (commodities);
470
471 }
472 while (!com);
473
474
475 g_list_free (namespaces);
476
477 return com;
478}
479
480gnc_commodity*
481get_random_commodity (QofBook *book)
482{
483 gnc_commodity *ret;
484 gchar *name;
485 const gchar *space;
486 gchar *mn;
487 gchar *cusip;
488 int ran_int;
489 gnc_commodity_table *table;
490
492
493#if 0
494 if (table &&
496 get_random_int_in_range (1, 5) < 5)
497 return get_random_commodity_from_table (table);
498#endif
499
500 mn = get_random_string_length_in_range(1, 3);
501 space = get_random_commodity_namespace();
502
503 if (table)
504 {
505 ret = gnc_commodity_table_lookup (table, space, mn);
506
507 if (ret)
508 {
509 g_free (mn);
510 return ret;
511 }
512 }
513
514 name = get_random_string();
515 cusip = get_random_string();
516
517 ran_int = get_random_int_in_range(min_scu, max_scu);
518
519 ret = gnc_commodity_new(book, name, space, mn, cusip, ran_int);
520
521 g_free(mn);
522 g_free(name);
523 g_free(cusip);
524
525 if (table)
527
528 return ret;
529}
530
531void
532make_random_changes_to_commodity (gnc_commodity *com)
533{
534 char *str;
535
536 g_return_if_fail (com);
537
538 str = get_random_string ();
540 g_free (str);
541
542 str = get_random_string ();
544 g_free (str);
545
546 str = get_random_string ();
548 g_free (str);
549
550 str = get_random_string ();
551 gnc_commodity_set_cusip (com, str);
552 g_free (str);
553
554 gnc_commodity_set_fraction (com, get_random_int_in_range (1, 100000));
555}
556
557void
558make_random_changes_to_commodity_table (gnc_commodity_table *table)
559{
560 GList *namespaces;
561 GList *node;
562
563 g_return_if_fail (table);
564
566
567 for (node = namespaces; node; node = node->next)
568 {
569 auto ns = static_cast<const char *>(node->data);
570 GList *commodities;
571 GList *com_node;
572
574 continue;
575
576 commodities = gnc_commodity_table_get_commodities (table, ns);
577
578 for (com_node = commodities; com_node; com_node = com_node->next)
579 {
580 auto com = static_cast<gnc_commodity *>(com_node->data);
581
583 make_random_changes_to_commodity (com);
585 }
586
587 g_list_free (commodities);
588 }
589
590 g_list_free (namespaces);
591}
592/* ================================================================= */
593/* Price stuff */
594
595void
596make_random_changes_to_price (QofBook *book, GNCPrice *p)
597{
598 time64 time;
599 PriceSource ps;
600 char *string;
601 gnc_commodity *c;
602
603 g_return_if_fail (book && p);
604
605 gnc_price_begin_edit (p);
606
607 c = get_random_commodity (book);
608 gnc_price_set_commodity (p, c);
609
610 c = get_random_commodity (book);
611 gnc_price_set_currency (p, c);
612
613 time = get_random_time ();
614 gnc_price_set_time64 (p, time);
615
616 ps = (PriceSource)get_random_int_in_range((int)PRICE_SOURCE_EDIT_DLG,
617 (int)PRICE_SOURCE_INVALID);
618 gnc_price_set_source (p, ps);
619
620 string = get_random_string ();
621 gnc_price_set_typestr (p, string);
622 g_free (string);
623
624 gnc_price_set_value (p, get_random_gnc_numeric (GNC_DENOM_AUTO));
625
626 gnc_price_commit_edit (p);
627}
628
629GNCPrice *
630get_random_price(QofBook *book)
631{
632 GNCPrice *p;
633
634 p = gnc_price_create (book);
635 if (!p)
636 {
637 failure_args("engine-stuff", __FILE__, __LINE__,
638 "get_random_price failed");
639 return NULL;
640 }
641
642 make_random_changes_to_price (book, p);
643 if (!p)
644 {
645 failure_args("engine-stuff", __FILE__, __LINE__,
646 "make_random_changes_to_price failed");
647 return NULL;
648 }
649
650 return p;
651}
652
653gboolean
654make_random_pricedb (QofBook *book, GNCPriceDB *db)
655{
656 int num_prices;
657
658 num_prices = get_random_int_in_range (1, 41);
659 if (num_prices < 1) /* should be impossible */
660 {
661 failure_args("engine-stuff", __FILE__, __LINE__,
662 "get_random_int_in_range failed");
663 return FALSE;
664 }
665
666 while (num_prices-- > 0)
667 {
668 GNCPrice *p;
669
670 p = get_random_price (book);
671 if (!p)
672 {
673 failure_args("engine-stuff", __FILE__, __LINE__,
674 "get_random_price failed");
675 return FALSE;
676 }
677
678 if (!gnc_pricedb_add_price (db, p))
679 /* probably the same date as another price, just try again. */
680 ++num_prices;
681
682 gnc_price_unref (p);
683 }
684 return TRUE;
685}
686
687GNCPriceDB *
688get_random_pricedb(QofBook *book)
689{
690 GNCPriceDB *db;
691
692 db = gnc_pricedb_get_db (book);
693 if (!db)
694 {
695 failure_args("engine-stuff", __FILE__, __LINE__,
696 "gnc_pricedb_get_db failed");
697 return NULL;
698 }
699 if (!make_random_pricedb (book, db))
700 {
701 return NULL;
702 }
703
704 return db;
705}
706
707static gboolean
708price_accumulator (GNCPrice *p, gpointer data)
709{
710 auto list = static_cast<GList**>(data);
711
712 *list = g_list_prepend (*list, p);
713
714 return TRUE;
715}
716
717void
718make_random_changes_to_pricedb (QofBook *book, GNCPriceDB *pdb)
719{
720 GList *list = NULL;
721 GList *node;
722
723 g_return_if_fail (pdb);
724
725 gnc_pricedb_foreach_price (pdb, price_accumulator, &list, FALSE);
726
727 for (node = list; node; node = node->next)
728 {
729 auto p = static_cast<GNCPrice *>(node->data);
730
731 switch (get_random_int_in_range (0, 5))
732 {
733 case 0: /* Delete */
735 break;
736
737 case 1:
738 case 2: /* Change */
739 make_random_changes_to_price (book, p);
740 break;
741
742 default: /* nothing */
743 break;
744 }
745 }
746
747 g_list_free (list);
748
749 /* Add a few new ones */
750 {
751 int i = get_random_int_in_range (1, 5);
752
753 while (i--)
754 {
755 GNCPrice *p = get_random_price (book);
756
757 gnc_pricedb_add_price (pdb, p);
758
759 gnc_price_unref (p);
760 }
761 }
762}
763
764/* ================================================================= */
765/* Account stuff */
766
767static void
768set_account_random_string(Account* act,
769 void(*func)(Account *act, const gchar*str))
770{
771 gchar *tmp_str = get_random_string();
772 if (tmp_str)
773 {
774 (func)(act, tmp_str);
775 g_free(tmp_str);
776 }
777}
778
779static void
780set_account_random_string_from_array(
781 Account* act, void(*func)(Account *act, const gchar*str),
782 const gchar *list[])
783{
784 const gchar *tmp_str = get_random_string_in_array(list);
785 if (tmp_str)
786 (func)(act, tmp_str);
787
788}
789
790static void
791account_add_subaccounts (QofBook *book, Account *account, int depth)
792{
793 int num_accounts;
794
795 if (depth == 0)
796 return;
797
798 num_accounts = get_random_int_in_range (1, 10);
799 while (num_accounts-- > 0)
800 {
801 Account *sub = get_random_account (book);
802
803 gnc_account_append_child (account, sub);
804
805 total_num_accounts ++;
806 if (total_num_accounts > max_total_accounts) return;
807
808 account_add_subaccounts (book, sub, depth - 1);
809 }
810}
811
812static void
813make_random_account_tree (QofBook *book, Account *root)
814{
815 int depth;
816
817 g_return_if_fail (book);
818 g_return_if_fail (root);
819
820 total_num_accounts = 0;
821 depth = get_random_int_in_range (1, max_tree_depth);
822
823 account_add_subaccounts (book, root, depth);
824
825 /* Make sure we have at least two accounts! */
826 if (total_num_accounts <= 1)
827 account_add_subaccounts (book, root, 1);
828}
829
830Account *
831get_random_account_tree (QofBook *book)
832{
833 Account * root;
834
835 g_return_val_if_fail (book, NULL);
836
837 root = gnc_book_get_root_account (book);
838 if (!root)
839 {
840 root = xaccMallocAccount (book);
841 gnc_book_set_root_account (book, root);
842 }
843
844 make_random_account_tree (book, root);
845
846 return root;
847}
848
849/* ================================================================= */
850/* transaction stuff */
851
857static void
858add_random_splits(QofBook *book, Transaction *trn, GList *account_list)
859{
860 Split *s1, *s2;
861 gnc_numeric val;
862 int s2_scu;
863
864 /* Gotta have at least two different accounts */
865 if (1 >= g_list_length (account_list)) return;
866
867 auto acc = static_cast<Account*>(get_random_list_element (account_list));
869 s1 = get_random_split(book, acc, trn);
870
871 auto bcc = static_cast<Account*>(get_random_list_element (account_list));
872 if ((bcc == acc) && (!do_bork()))
873 {
874 /* Make sure that each side of the transaction is in
875 * a different account; otherwise get weirdness in lot
876 * calculcations. ... Hmm maybe should fix lots in
877 * this case? */
878 while (bcc == acc)
879 {
880 bcc = static_cast<Account*>(get_random_list_element (account_list));
881 }
882 }
883
884 /* Set up two splits whose values really are opposites. */
885 val = xaccSplitGetValue(s1);
886 s2 = get_random_split(book, bcc, trn);
888
889 /* Other split should have equal and opposite value */
890 if (do_bork())
891 {
892 val = get_random_gnc_numeric(s2_scu);
893 g_log ("test.engine.suff", G_LOG_LEVEL_DEBUG, "Borking second %" PRIu64
894 " / %" PRIu64 ", scu %d\n", val.num, val.denom, s2_scu);
895 }
896 val = gnc_numeric_neg(val);
897 xaccSplitSetValue(s2, val);
898 if (val.denom != s2_scu)
899 {
900 if (val.denom > s2_scu)
901 val.num /= val.denom / s2_scu;
902 val.denom = s2_scu;
903 }
904 xaccSplitSetAmount(s2, val);
906}
907
908typedef struct
909{
910 GncGUID guid;
911} TransInfo;
912
913void
914make_random_changes_to_transaction_and_splits (QofBook *book,
915 Transaction *trans,
916 GList *accounts)
917{
918 GList *splits;
919 GList *node;
920 Split *split;
921
922 g_return_if_fail (book);
923 g_return_if_fail (trans);
924 g_return_if_fail (accounts);
925
926 xaccTransBeginEdit (trans);
927
928 make_random_changes_to_transaction (book, trans);
929
930 switch (get_random_int_in_range (0, 3))
931 {
932 case 0: /* delete some splits, add some more */
933 if (xaccTransGetVoidStatus (trans))
934 break;
935
936 do
937 {
938 split = xaccTransGetSplit (trans, 0);
939
940 xaccSplitDestroy (split);
941 }
942 while (split);
943
944 add_random_splits (book, trans, accounts);
945
946 /* fall through */
947
948 case 1: /* move the splits around */
949 if (xaccTransGetVoidStatus (trans))
950 break;
951
952 splits = xaccTransGetSplitList (trans);
953 for (node = splits; node; node = node->next)
954 {
955 auto split = static_cast<Split *>(node->data);
956 auto account = static_cast<Account*>(get_random_list_element (accounts));
957
958 xaccAccountInsertSplit (account, split);
959 }
960 break;
961
962 case 2: /* destroy the transaction */
963 xaccTransDestroy (trans);
964 xaccTransCommitEdit (trans);
965 return;
966
967 default: /* do nothing */
968 break;
969 }
970
971 if (xaccTransGetVoidStatus (trans))
972 {
973 xaccTransCommitEdit (trans);
974 return;
975 }
976
977 /* mess with the splits */
978 splits = xaccTransGetSplitList (trans);
979 for (node = splits; node; node = node->next)
980 {
981 auto split = static_cast<Split *>(node->data);
982
983 if (get_random_boolean ())
984 make_random_changes_to_split (split);
985 }
986
987 if (get_random_boolean ())
988 xaccTransCommitEdit (trans);
989 else
990 xaccTransRollbackEdit (trans);
991}
992
993static int
994add_trans_helper (Transaction *trans, gpointer data)
995{
996 TransInfo *ti;
997 auto list = static_cast<GList **>(data);
998
999 ti = g_new (TransInfo, 1);
1000
1001 ti->guid = *xaccTransGetGUID (trans);
1002
1003 *list = g_list_prepend (*list, ti);
1004 return 0;
1005}
1006
1007void
1008make_random_changes_to_level (QofBook *book, Account *parent)
1009{
1010 Account *new_account;
1011 Account *account;
1012 GList *accounts;
1013 GList *transes;
1014 GList *splits;
1015 GList *node;
1016
1017 g_return_if_fail (parent && book);
1018
1019 accounts = gnc_account_get_descendants (parent);
1020
1021 /* Add a new account */
1022 new_account = get_random_account (book);
1023
1024 if (get_random_boolean () || !accounts)
1025 gnc_account_append_child (parent, new_account);
1026 else
1027 {
1028 account = static_cast<Account*>(get_random_list_element (accounts));
1029
1030 gnc_account_append_child (account, new_account);
1031 }
1032
1033 g_list_free (accounts);
1034 accounts = gnc_account_get_descendants (parent);
1035
1036 /* Add some new transactions */
1037 add_random_transactions_to_book (book, get_random_int_in_range (1, 6));
1038
1039 /* Mess with the accounts */
1040 for (node = accounts; node; node = node->next)
1041 {
1042 auto account = static_cast<Account *>(node->data);
1043
1044 if (get_random_boolean ())
1045 make_random_changes_to_account (book, account);
1046 }
1047
1048 /* Mess with the transactions & splits */
1049 transes = NULL;
1050 xaccAccountTreeForEachTransaction (parent, add_trans_helper, &transes);
1051
1052 for (node = transes; node; node = node->next)
1053 {
1054 auto ti = static_cast<TransInfo *>(node->data);
1055 Transaction *trans = xaccTransLookup (&ti->guid, book);
1056
1057 if (!trans)
1058 continue;
1059
1060 make_random_changes_to_transaction_and_splits (book, trans, accounts);
1061 }
1062
1063 for (node = transes; node; node = node->next)
1064 {
1065 auto ti = static_cast<TransInfo *>(node->data);
1066
1067 g_free (ti);
1068 }
1069 g_list_free (transes);
1070 transes = NULL;
1071
1072 /* delete an account */
1073 account = static_cast<Account*>(get_random_list_element (accounts));
1074
1075 splits = xaccAccountGetSplitList (account);
1076
1077 for (node = splits; node; node = node->next)
1078 {
1079 auto split = static_cast<Split *>(node->data);
1080
1081 do
1082 {
1083 new_account = static_cast<Account*>(get_random_list_element (accounts));
1084 }
1085 while (new_account == account);
1086
1087 xaccAccountInsertSplit (new_account, split);
1088 }
1089
1090 xaccAccountBeginEdit (account);
1091 xaccAccountDestroy (account);
1092
1093 g_list_free (splits);
1094 g_list_free (accounts);
1095
1096 accounts = gnc_account_get_descendants (parent);
1097
1098 /* move some accounts around */
1099 if (accounts && (g_list_length (accounts) > 1))
1100 {
1101 int i = get_random_int_in_range (1, 4);
1102
1103 while (i--)
1104 {
1105 Account *a2;
1106
1107 auto a1 = static_cast<Account*>(get_random_list_element (accounts));
1108
1109 if (get_random_boolean ())
1110 a2 = static_cast<Account*>(get_random_list_element (accounts));
1111 else
1112 a2 = NULL;
1113
1114 if (!a2)
1115 {
1116 gnc_account_append_child (parent, a1);
1117 continue;
1118 }
1119
1120 if (a1 == a2 ||
1121 xaccAccountHasAncestor (a1, a2) ||
1122 xaccAccountHasAncestor (a2, a1))
1123 {
1124 i++;
1125 continue;
1126 }
1127
1128 gnc_account_append_child (a2, a1);
1129 }
1130 }
1131
1132 g_list_free (accounts);
1133}
1134
1135Account*
1136get_random_account(QofBook *book)
1137{
1138 Account *root, *ret;
1139 int tmp_int;
1140
1141 ret = xaccMallocAccount(book);
1142
1144
1145 set_account_random_string_from_array(ret, xaccAccountSetName,
1146 sane_account_names);
1147
1148 tmp_int = get_random_int_in_range(ACCT_TYPE_BANK, NUM_ACCOUNT_TYPES - 1);
1149 xaccAccountSetType(ret, static_cast<GNCAccountType>(tmp_int));
1150
1151 set_account_random_string(ret, xaccAccountSetCode);
1152 set_account_random_string(ret, xaccAccountSetDescription);
1153
1154 xaccAccountSetCommodity(ret, get_random_commodity(book));
1155
1156 qof_instance_set_slots(QOF_INSTANCE(ret), get_random_kvp_frame());
1157
1158 root = gnc_book_get_root_account (book);
1159 if (!root)
1160 {
1161 root = xaccMallocAccount (book);
1162 gnc_book_set_root_account (book, root);
1163 }
1164 gnc_account_append_child (root, ret);
1166
1167 return ret;
1168}
1169
1170void
1171make_random_changes_to_account (QofBook *book, Account *account)
1172{
1173 int tmp_int;
1174
1175 g_return_if_fail (account);
1176
1177 xaccAccountBeginEdit (account);
1178
1179 set_account_random_string (account, xaccAccountSetName);
1180
1181 tmp_int = get_random_int_in_range (ACCT_TYPE_BANK, NUM_ACCOUNT_TYPES - 1);
1182 xaccAccountSetType (account, static_cast<GNCAccountType>(tmp_int));
1183
1184 set_account_random_string (account, xaccAccountSetCode);
1185 set_account_random_string (account, xaccAccountSetDescription);
1186
1187 xaccAccountSetCommodity (account, get_random_commodity(book));
1188
1189 qof_instance_set_slots(QOF_INSTANCE(account), get_random_kvp_frame());
1190
1191 xaccAccountCommitEdit (account);
1192}
1193
1194static void
1195set_split_random_string(Split *spl,
1196 void(*func)(Split *act, const gchar*str))
1197{
1198 gchar *tmp_str = get_random_string();
1199 if (tmp_str)
1200 {
1201 (func)(spl, tmp_str);
1202 g_free(tmp_str);
1203 }
1204}
1205
1206/* Don't do voiding here, it should be done by xaccTransVoid */
1207static char possible_chars[] = { NREC, CREC, YREC, FREC };
1208
1209Split*
1210get_random_split(QofBook *book, Account *acct, Transaction *trn)
1211{
1212 Split *ret;
1213 gnc_numeric amt = {0, 1}, val = {0, 1}, rate = {0, 0};
1214 const gchar *str;
1215 gnc_commodity *com;
1216 int scu, denom;
1217 time64 time;
1218
1219 com = xaccTransGetCurrency (trn);
1220 scu = gnc_commodity_get_fraction(com);
1221
1222 ret = xaccMallocSplit(book);
1223
1224 str = get_random_string_in_array(sane_descriptions);
1225 xaccSplitSetMemo(ret, str);
1226 str = get_random_string_in_array(sane_actions);
1227 xaccSplitSetAction(ret, str);
1228
1229 xaccSplitSetReconcile(ret, possible_chars[get_random_int_in_range(0, 3)]);
1230
1231 time = get_random_time();
1233
1234 /* Split must be in an account before we can set an amount */
1235 /* and in a transaction before it can be added to an account. */
1236 xaccTransBeginEdit(trn);
1237 xaccSplitSetParent(ret, trn);
1238 xaccSplitSetAccount(ret, acct);
1239
1240 do
1241 {
1242 val = get_random_gnc_numeric (scu);
1243 if (val.num == 0)
1244 fprintf(stderr, "get_random_split: Created split with zero value: %p\n", ret);
1245
1246 if (!do_bork())
1247/* Another overflow-prevention measure. A numerator near the overflow limit can
1248 * be made too large by replacing the denominator with a smaller scu.
1249 */
1250 {
1251 if (val.denom > scu && val.num > num_limit / (max_denom_mult / scu))
1252 {
1253 int64_t new_num = val.num / (val.denom / scu);
1254 g_log("test.engine.suff", G_LOG_LEVEL_DEBUG,
1255 "Adjusting val.denom from %" PRIu64 " to %" PRIu64 "\n",
1256 val.num, new_num);
1257 val.num = new_num;
1258 }
1259 val.denom = scu;
1260 }
1261 }
1262 while (gnc_numeric_check(val) != GNC_ERROR_OK);
1263 g_log ("test.engine.suff", G_LOG_LEVEL_DEBUG,
1264 "Random split value: %" PRIu64 " / %" PRIu64 ", scu %d\n",
1265 val.num, val.denom, scu);
1266 xaccSplitSetValue(ret, val);
1267
1268 /* If the currencies are the same, the split amount should equal
1269 * the split value (unless we bork it on purpose) */
1271 xaccSplitGetAccount(ret)));
1273 xaccAccountGetCommodity(acct)) &&
1274 (!do_bork()))
1275 {
1276 amt = val;
1277 }
1278 else
1279 {
1280 do
1281 {
1282 rate = get_random_rate ();
1283 amt = gnc_numeric_div(val, rate, denom, GNC_HOW_RND_ROUND_HALF_UP);
1284 }
1285 while (gnc_numeric_check(amt) != GNC_ERROR_OK);
1286 }
1287 g_log ("test.engine.suff", G_LOG_LEVEL_DEBUG, "Random split amount: %"
1288 PRIu64 " / %" PRIu64 ", rate %" PRIu64 " / %" PRIu64 "\n",
1289 amt.num, amt.denom, rate.num, rate.denom);
1290
1291
1292 xaccSplitSetAmount(ret, amt);
1293
1294 /* Make sure val and amt have the same sign. Note that amt is
1295 also allowed to be zero, because that is caused by a small
1296 rate. */
1297 if (gnc_numeric_positive_p(val))
1298 g_assert_true(!gnc_numeric_negative_p(amt)); /* non-negative amt */
1299 else
1300 g_assert_true(!gnc_numeric_positive_p(amt)); /* non-positive amt */
1301
1302// g_assert_true(amt.num < (2LL << 56));
1303 qof_instance_set_slots(QOF_INSTANCE (ret), get_random_kvp_frame());
1305
1306 return ret;
1307}
1308
1309void
1310make_random_changes_to_split (Split *split)
1311{
1312 Transaction *trans;
1313 time64 time;
1314
1315 g_return_if_fail (split);
1316
1317 trans = xaccSplitGetParent (split);
1318
1319 xaccTransBeginEdit (trans);
1320
1321 set_split_random_string (split, xaccSplitSetMemo);
1322 set_split_random_string (split, xaccSplitSetAction);
1323
1324 xaccSplitSetReconcile (split, possible_chars[get_random_int_in_range(0, 3)]);
1325
1326 time = get_random_time();
1327 xaccSplitSetDateReconciledSecs (split, time);
1328
1329 qof_instance_set_slots (QOF_INSTANCE (split), get_random_kvp_frame());
1330
1331 /* Don't change share values/prices here, since that would
1332 * throw transactions out of balance. Do that in the corresponding
1333 * change transaction function. */
1334
1335 xaccTransCommitEdit (trans);
1336}
1337
1338static void
1339set_tran_random_string(Transaction* trn,
1340 void(*func)(Transaction *act, const gchar*str))
1341{
1342 gchar *tmp_str = get_random_string();
1343 if (!trn)
1344 {
1345 return;
1346 }
1347 if (tmp_str)
1348 {
1349 xaccTransBeginEdit(trn);
1350 (func)(trn, tmp_str);
1351 g_free(tmp_str);
1353 }
1354}
1355
1356static void
1357set_tran_random_string_from_array(
1358 Transaction* trn, void(*func)(Transaction *trn, const gchar*str),
1359 const gchar *list[])
1360{
1361 const gchar *tmp_str = get_random_string_in_array(list);
1362 if (tmp_str)
1363 (func)(trn, tmp_str);
1364}
1365
1366static void
1367trn_add_ran_time (Transaction *trn, void (*func)(Transaction*, time64))
1368{
1369 func(trn, get_random_time());
1370}
1371
1372
1373Transaction *
1374get_random_transaction_with_currency(QofBook *book,
1375 gnc_commodity *currency,
1376 GList *account_list)
1377{
1378 Transaction* trans;
1379 KvpFrame *f;
1380
1381 if (!account_list)
1382 {
1383 account_list = gnc_account_get_descendants (gnc_book_get_root_account (book));
1384 }
1385
1386 /* Gotta have at least two different accounts */
1387 if (1 >= g_list_length (account_list))
1388 {
1389 failure_args("engine-stuff", __FILE__, __LINE__,
1390 "get_random_transaction_with_currency: account_list too short");
1391 return NULL;
1392 }
1393
1394 trans = xaccMallocTransaction(book);
1395
1396 xaccTransBeginEdit(trans);
1397
1398 xaccTransSetCurrency (trans,
1399 currency ? currency :
1400 get_random_commodity (book));
1401
1402 gint num = get_random_int_in_range (1, max_trans_num);
1403 auto numstr = std::to_string(num);
1404 xaccTransSetNum(trans, numstr.c_str());
1405
1406 set_tran_random_string_from_array(trans, xaccTransSetDescription,
1407 sane_descriptions);
1408 trn_add_ran_time(trans, xaccTransSetDatePostedSecs);
1409 trn_add_ran_time(trans, xaccTransSetDateEnteredSecs);
1410
1411 f = get_random_kvp_frame();
1412 qof_instance_set_slots (QOF_INSTANCE (trans), f);
1413
1414 add_random_splits(book, trans, account_list);
1415
1416 if (get_random_int_in_range (1, 10) == 1)
1417 {
1418 char *reason = get_random_string ();
1419 xaccTransVoid (trans, reason);
1420 g_free (reason);
1421 }
1422
1423 xaccTransCommitEdit(trans);
1424 if (!trans)
1425 {
1426 failure_args("engine-stuff", __FILE__, __LINE__,
1427 "get_random_transaction_with_currency failed");
1428 return NULL;
1429 }
1430
1431 return trans;
1432}
1433
1434Transaction*
1435get_random_transaction (QofBook *book)
1436{
1437 Transaction *ret;
1438
1439 g_return_val_if_fail(book, NULL);
1440 ret = get_random_transaction_with_currency (book, NULL, NULL);
1441 if (!ret)
1442 {
1443 failure_args("engine-stuff", __FILE__, __LINE__,
1444 "get_random_transaction failed");
1445 return NULL;
1446 }
1447 return ret;
1448}
1449
1450void
1451make_random_changes_to_transaction (QofBook *book, Transaction *trans)
1452{
1453 g_return_if_fail (trans && book);
1454
1455 if (xaccTransGetVoidStatus (trans))
1456 {
1457 if (get_random_int_in_range (1, 2) == 1)
1458 xaccTransUnvoid (trans);
1459 return;
1460 }
1461
1462 xaccTransBeginEdit (trans);
1463
1464 xaccTransSetCurrency (trans, get_random_commodity (book));
1465
1466 set_tran_random_string (trans, xaccTransSetNum);
1467
1468 trn_add_ran_time (trans, xaccTransSetDatePostedSecs);
1469 trn_add_ran_time (trans, xaccTransSetDateEnteredSecs);
1470
1471 set_tran_random_string (trans, xaccTransSetDescription);
1472
1473 qof_instance_set_slots (QOF_INSTANCE (trans), get_random_kvp_frame());
1474
1475 /* Do split manipulations in higher-level functions */
1476
1477 xaccTransCommitEdit (trans);
1478}
1479
1480
1481static GList *
1482get_random_guids(int max)
1483{
1484 GList *guids = NULL;
1485 int num_guids;
1486
1487 if (max < 1) return NULL;
1488
1489 num_guids = get_random_int_in_range (1, max);
1490
1491 while (num_guids-- > 0)
1492 guids = g_list_prepend (guids, get_random_guid ());
1493
1494 return guids;
1495}
1496
1497static void
1498free_guids(GList *guids)
1499{
1500 g_list_free_full (guids, (GDestroyNotify)guid_free);
1501}
1502
1503static QofQueryOp
1504get_random_queryop(void)
1505{
1506 int op_num = get_random_int_in_range(1, 11);
1507 QofQueryOp op = QOF_QUERY_AND;
1508 /* = get_random_int_in_range (1, QOF_QUERY_XOR); */
1509
1510 /* Let's make it MUCH more likely to get AND and OR */
1511 switch (op_num)
1512 {
1513 case 1:
1514 case 2:
1515 case 3:
1516 case 4:
1517 op = QOF_QUERY_AND;
1518 break;
1519 case 5:
1520 case 6:
1521 case 7:
1522 case 8:
1523 op = QOF_QUERY_OR;
1524 break;
1525 case 9:
1526 op = QOF_QUERY_NAND;
1527 break;
1528 case 10:
1529 op = QOF_QUERY_NOR;
1530 break;
1531 case 11:
1532 op = QOF_QUERY_XOR;
1533 break;
1534 default:
1535 g_assert_not_reached();
1536 break;
1537 };
1538 if (gnc_engine_debug_random) printf ("op = %d (int was %d), ", op, op_num);
1539 return op;
1540}
1541
1542static QofIdType
1543get_random_id_type (void)
1544{
1545 switch (get_random_int_in_range (1, 3))
1546 {
1547 case 1:
1548 return GNC_ID_SPLIT;
1549 case 2:
1550 return GNC_ID_TRANS;
1551 case 3:
1552 return GNC_ID_ACCOUNT;
1553 default:
1554 return get_random_string ();
1555 }
1556}
1557
1558typedef enum
1559{
1560 BY_STANDARD = 1,
1561 BY_DATE,
1562 BY_DATE_ENTERED,
1563 BY_DATE_RECONCILED,
1564 BY_NUM,
1565 BY_AMOUNT,
1566 BY_MEMO,
1567 BY_DESC,
1568 BY_NONE
1569} sort_type_t;
1570
1571static void
1572set_query_sort (QofQuery *q, sort_type_t sort_code)
1573{
1574 GSList *p1 = NULL, *p2 = NULL, *p3 = NULL, *standard;
1575
1576 standard = g_slist_prepend (NULL, const_cast<char*>(QUERY_DEFAULT_SORT));
1577
1578 switch (sort_code)
1579 {
1580 case BY_STANDARD:
1581 p1 = standard;
1582 break;
1583 case BY_DATE:
1584 p1 = g_slist_prepend (p1, const_cast<char*>(TRANS_DATE_POSTED));
1585 p1 = g_slist_prepend (p1, const_cast<char*>(SPLIT_TRANS));
1586 p2 = standard;
1587 break;
1588 case BY_DATE_ENTERED:
1589 p1 = g_slist_prepend (p1, const_cast<char*>(TRANS_DATE_ENTERED));
1590 p1 = g_slist_prepend (p1, const_cast<char*>(SPLIT_TRANS));
1591 p2 = standard;
1592 break;
1593 case BY_DATE_RECONCILED:
1594 p1 = g_slist_prepend (p1, const_cast<char*>(SPLIT_RECONCILE));
1595 p2 = g_slist_prepend (p2, const_cast<char*>(SPLIT_DATE_RECONCILED));
1596 p3 = standard;
1597 break;
1598 case BY_NUM:
1599 p1 = g_slist_prepend (p1, const_cast<char*>(TRANS_NUM));
1600 p1 = g_slist_prepend (p1, const_cast<char*>(SPLIT_TRANS));
1601 p2 = standard;
1602 break;
1603 case BY_AMOUNT:
1604 p1 = g_slist_prepend (p1, const_cast<char*>(SPLIT_VALUE));
1605 p2 = standard;
1606 break;
1607 case BY_MEMO:
1608 p1 = g_slist_prepend (p1, const_cast<char*>(SPLIT_MEMO));
1609 p2 = standard;
1610 break;
1611 case BY_DESC:
1612 p1 = g_slist_prepend (p1, const_cast<char*>(TRANS_DESCRIPTION));
1613 p1 = g_slist_prepend (p1, const_cast<char*>(SPLIT_TRANS));
1614 p2 = standard;
1615 break;
1616 case BY_NONE:
1617 g_slist_free (standard);
1618 break;
1619 default:
1620 g_slist_free (standard);
1621 g_return_if_fail (FALSE);
1622 break;
1623 }
1624
1625 qof_query_set_sort_order (q, p1, p2, p3);
1626}
1627
1628template <typename T> inline T
1629compare_param(int max)
1630{
1631 auto ret = get_random_int_in_range (max == 1 ? 0 : 1, max);
1632 return static_cast<T>(ret);
1633}
1634
1635QofQuery *
1636get_random_query(void)
1637{
1638 QofQuery *q;
1639 int num_terms;
1640
1641 num_terms = get_random_int_in_range (1, 3);
1642 if (gnc_engine_debug_random) printf("num_terms = %d", num_terms);
1643
1644 q = qof_query_create_for(GNC_ID_SPLIT);
1645
1646 while (num_terms-- > 0)
1647 {
1648 gint pr_type;
1649 time64 start;
1650 time64 end;
1651 GList *guids;
1652 char *string;
1653 GncGUID *guid;
1654
1655 pr_type = get_random_int_in_range (1, 20);
1656 if (gnc_engine_debug_random) printf("\n pr_type = %d ", pr_type);
1657
1658 switch (pr_type)
1659 {
1660 case 1: /*PR_ACCOUNT */
1661 guids = get_random_guids (10);
1662 xaccQueryAddAccountGUIDMatch
1663 (q,
1664 guids,
1665 compare_param<QofGuidMatch>(QOF_GUID_MATCH_NONE),
1666 get_random_queryop ());
1667 free_guids (guids);
1668 break;
1669
1670 case 2: /*PR_ACTION */
1671 string = get_random_string_without ("\\");
1672 xaccQueryAddActionMatch (q,
1673 string,
1674 get_random_boolean (),
1675 get_random_boolean (),
1676 compare_param<QofQueryCompare>(QOF_COMPARE_CONTAINS),
1677 get_random_queryop ());
1678 g_free (string);
1679 break;
1680
1681 case 3: /* PR_BALANCE */
1682 xaccQueryAddBalanceMatch
1683 (q,
1684 compare_param<QofQueryCompare> (1),
1685 get_random_queryop ());
1686 break;
1687
1688 case 4: /* PR_CLEARED */
1689 xaccQueryAddClearedMatch
1690 (q,
1691 static_cast<cleared_match_t>(get_random_int_in_range (1,
1692 CLEARED_NO |
1693 CLEARED_CLEARED |
1694 CLEARED_RECONCILED |
1695 CLEARED_FROZEN |
1696 CLEARED_VOIDED)),
1697 get_random_queryop ());
1698 break;
1699
1700 case 5: /* PR_DATE */
1701 start = get_random_time ();
1702 end = get_random_time ();
1703 xaccQueryAddDateMatchTT (q,
1704 get_random_boolean (),
1705 start,
1706 get_random_boolean (),
1707 end,
1708 get_random_queryop ());
1709 break;
1710
1711 case 6: /* PR_DESC */
1712 string = get_random_string_without ("\\");
1713 xaccQueryAddDescriptionMatch (q,
1714 string,
1715 get_random_boolean (),
1716 get_random_boolean (),
1717 compare_param<QofQueryCompare>(QOF_COMPARE_CONTAINS),
1718 get_random_queryop ());
1719 g_free (string);
1720 break;
1721
1722 case 7: /* PR_GUID */
1723 guid = get_random_guid ();
1724 xaccQueryAddGUIDMatch (q,
1725 guid,
1726 get_random_id_type (),
1727 get_random_queryop ());
1728 guid_free (guid);
1729 break;
1730
1731 case 8: /* PR_KVP */
1732 break;
1733
1734 case 9: /* PR_MEMO */
1735 string = get_random_string_without ("\\");
1736 xaccQueryAddMemoMatch (q,
1737 string,
1738 get_random_boolean (),
1739 get_random_boolean (),
1740 compare_param<QofQueryCompare>(QOF_COMPARE_CONTAINS),
1741 get_random_queryop ());
1742 g_free (string);
1743 break;
1744
1745 case 10: /* PR_NUM */
1746 string = get_random_string_without ("\\");
1747 xaccQueryAddNumberMatch (q,
1748 string,
1749 get_random_boolean (),
1750 get_random_boolean (),
1751 compare_param<QofQueryCompare>(QOF_COMPARE_CONTAINS),
1752 get_random_queryop ());
1753 g_free (string);
1754 break;
1755
1756 case 11: /* PR_PRICE */
1757 xaccQueryAddSharePriceMatch
1758 (q,
1759 get_random_gnc_numeric (GNC_DENOM_AUTO),
1760 compare_param<QofQueryCompare> (QOF_COMPARE_NEQ),
1761 get_random_queryop ());
1762 break;
1763
1764 case 12: /* PR_SHRS */
1765 xaccQueryAddSharesMatch
1766 (q,
1767 get_random_gnc_numeric (GNC_DENOM_AUTO),
1768 compare_param<QofQueryCompare> (QOF_COMPARE_NEQ),
1769 get_random_queryop ());
1770 break;
1771
1772 case 13: /* PR_VALUE */
1773 xaccQueryAddValueMatch
1774 (q,
1775 get_random_gnc_numeric (GNC_DENOM_AUTO),
1776 compare_param<QofNumericMatch> (QOF_NUMERIC_MATCH_ANY),
1777 compare_param<QofQueryCompare> (QOF_COMPARE_NEQ),
1778 get_random_queryop ());
1779 break;
1780
1781 default:
1782 if (gnc_engine_debug_random) printf("ignored..");
1783 num_terms++;
1784 break;
1785 }
1786 }
1787
1788 if (gnc_engine_debug_random) printf ("\n");
1789 set_query_sort (q, compare_param<sort_type_t>(BY_NONE));
1790
1792 get_random_boolean (),
1793 get_random_boolean (),
1794 get_random_boolean ());
1795
1796 qof_query_set_max_results (q, get_random_int_in_range (-50000, 50000));
1797
1798 return q;
1799}
1800
1801QofBook *
1802get_random_book (void)
1803{
1804 QofBook *book;
1805
1806 book = qof_book_new ();
1807
1808 get_random_account_tree (book);
1809 get_random_pricedb (book);
1810
1811 return book;
1812}
1813
1814QofSession *
1815get_random_session (void)
1816{
1817 auto book = qof_book_new ();
1818 auto session = qof_session_new (book);
1819
1820 get_random_account_tree (book);
1821 get_random_pricedb (book);
1822
1823 return session;
1824}
1825
1826void
1827add_random_transactions_to_book (QofBook *book, gint num_transactions)
1828{
1829 gnc_commodity_table *table;
1830 GList *accounts;
1831
1832 if (num_transactions <= 0) return;
1833
1834 g_return_if_fail (book);
1835
1836 accounts = gnc_account_get_descendants (gnc_book_get_root_account (book));
1837 g_return_if_fail (accounts);
1838
1840
1841 while (num_transactions--)
1842 {
1843 gnc_commodity *com;
1844
1845 com = get_random_commodity_from_table (table);
1846 get_random_transaction_with_currency (book, com, accounts);
1847 }
1848 g_list_free (accounts);
1849}
1850
1851void
1852make_random_changes_to_book (QofBook *book)
1853{
1854 g_return_if_fail (book);
1855
1856 make_random_changes_to_level (book, gnc_book_get_root_account (book));
1857 make_random_changes_to_pricedb (book, gnc_pricedb_get_db (book));
1858
1859#if 0
1860 make_random_changes_to_commodity_table (gnc_commodity_table_get_table (book));
1861#endif
1862}
1863
1864void
1865make_random_changes_to_session (QofSession *session)
1866{
1867 g_return_if_fail (session);
1868
1869 make_random_changes_to_book (qof_session_get_book (session));
1870}
1871
1872typedef struct
1873{
1874 QofIdType where;
1875 GSList *path;
1876 QofQuery *q;
1877} KVPQueryData;
1878
1879static gboolean include_price = TRUE;
1880
1881void
1882trans_query_include_price (gboolean include_price_in)
1883{
1884 include_price = include_price_in;
1885}
1886
1887TestQueryTypes
1888get_random_query_type (void)
1889{
1890 switch (get_random_int_in_range (0, 1))
1891 {
1892 case 0:
1893 return SIMPLE_QT;
1894 case 1:
1895 return GUID_QT;
1896 default:
1897 return SIMPLE_QT;
1898 }
1899}
1900
1901QofQuery *
1902make_trans_query (Transaction *trans, TestQueryTypes query_types)
1903{
1904 Account *a;
1905 gnc_numeric n;
1906 QofQuery *q;
1907 Split *s;
1908
1909 if (query_types == RANDOM_QT)
1910 query_types = get_random_query_type ();
1911
1912 q = qof_query_create_for(GNC_ID_SPLIT);
1913
1914 s = xaccTransGetSplit (trans, 0);
1915 a = xaccSplitGetAccount (s);
1916
1917 if (query_types & SIMPLE_QT)
1918 {
1919 xaccQueryAddSingleAccountMatch (q, xaccSplitGetAccount (s), QOF_QUERY_AND);
1920
1921 if (xaccTransGetDescription(trans) && *xaccTransGetDescription(trans) != '\0')
1922 {
1923 xaccQueryAddDescriptionMatch (q, xaccTransGetDescription (trans),
1924 TRUE, FALSE, QOF_COMPARE_CONTAINS, QOF_QUERY_AND);
1925 }
1926
1927 if (xaccTransGetNum(trans) && *xaccTransGetNum(trans) != '\0')
1928 {
1929 xaccQueryAddNumberMatch (q, xaccTransGetNum (trans),
1930 TRUE, FALSE, QOF_COMPARE_CONTAINS, QOF_QUERY_AND);
1931 }
1932
1933 if (xaccSplitGetAction(s) && *xaccSplitGetAction(s) != '\0')
1934 {
1935 xaccQueryAddActionMatch (q, xaccSplitGetAction (s),
1936 TRUE, FALSE, QOF_COMPARE_CONTAINS, QOF_QUERY_AND);
1937 }
1938
1939 n = xaccSplitGetValue (s);
1940 xaccQueryAddValueMatch (q, n, QOF_NUMERIC_MATCH_ANY,
1941 QOF_COMPARE_EQUAL, QOF_QUERY_AND);
1942
1943 n = xaccSplitGetAmount (s);
1944 xaccQueryAddSharesMatch (q, n, QOF_COMPARE_EQUAL, QOF_QUERY_AND);
1945
1946 if (include_price)
1947 {
1948 n = xaccSplitGetSharePrice (s);
1949 xaccQueryAddSharePriceMatch (q, n, QOF_COMPARE_EQUAL, QOF_QUERY_AND);
1950 }
1951
1952 {
1953 time64 time = xaccTransRetDatePosted (trans);
1954 xaccQueryAddDateMatchTT (q, TRUE, time, TRUE, time, QOF_QUERY_AND);
1955 }
1956
1957 if (xaccSplitGetMemo(s) && *xaccSplitGetMemo(s) != '\0')
1958 {
1959 xaccQueryAddMemoMatch (q, xaccSplitGetMemo (s), TRUE, FALSE, QOF_COMPARE_CONTAINS, QOF_QUERY_AND);
1960 }
1961
1962 {
1963 cleared_match_t how;
1964
1965 switch (xaccSplitGetReconcile (s))
1966 {
1967 case NREC:
1968 how = CLEARED_NO;
1969 break;
1970 case CREC:
1971 how = CLEARED_CLEARED;
1972 break;
1973 case YREC:
1974 how = CLEARED_RECONCILED;
1975 break;
1976 case FREC:
1977 how = CLEARED_FROZEN;
1978 break;
1979 case VREC:
1980 how = CLEARED_VOIDED;
1981 break;
1982 default:
1983 failure ("bad reconcile flag");
1985 return NULL;
1986 }
1987
1988 xaccQueryAddClearedMatch (q, how, QOF_QUERY_AND);
1989 }
1990 }
1991
1992 if (query_types & ACCOUNT_QT)
1993 {
1994 GList * list;
1995 GList * node;
1996
1997 /* QOF_GUID_MATCH_ALL */
1998 list = NULL;
1999 for (node = xaccTransGetSplitList (trans); node; node = node->next)
2000 {
2001 auto split = static_cast<Split * >(node->data);
2002 list = g_list_prepend (list, xaccSplitGetAccount (split));
2003 }
2004 xaccQueryAddAccountMatch (q, list, QOF_GUID_MATCH_ALL, QOF_QUERY_AND);
2005 g_list_free (list);
2006
2007 /* QOF_GUID_MATCH_NONE */
2008 list = NULL;
2009 list = g_list_prepend (list, get_random_guid ());
2010 list = g_list_prepend (list, get_random_guid ());
2011 list = g_list_prepend (list, get_random_guid ());
2012 xaccQueryAddAccountGUIDMatch (q, list, QOF_GUID_MATCH_NONE, QOF_QUERY_AND);
2013
2014 /* QOF_GUID_MATCH_ANY */
2015 {
2016 GncGUID * guid = get_random_guid ();
2017 *guid = *xaccAccountGetGUID (a);
2018 list = g_list_prepend (list, guid);
2019 }
2020 xaccQueryAddAccountGUIDMatch (q, list, QOF_GUID_MATCH_ANY, QOF_QUERY_AND);
2021
2022 free_guids (list);
2023 }
2024
2025 if (query_types & GUID_QT)
2026 {
2027 xaccQueryAddGUIDMatch (q, xaccSplitGetGUID (s),
2028 GNC_ID_SPLIT, QOF_QUERY_AND);
2029
2030 xaccQueryAddGUIDMatch (q, xaccTransGetGUID (trans),
2031 GNC_ID_TRANS, QOF_QUERY_AND);
2032
2033 xaccQueryAddGUIDMatch (q, xaccAccountGetGUID (a),
2034 GNC_ID_ACCOUNT, QOF_QUERY_AND);
2035 }
2036
2037 return q;
2038}
2039
2040static Recurrence*
2041daily_freq(const GDate* start, int multiplier)
2042{
2043 Recurrence *r = g_new0(Recurrence, 1);
2044 recurrenceSet(r, multiplier, PERIOD_DAY, start, WEEKEND_ADJ_NONE);
2045 return r;
2046}
2047
2048static Recurrence*
2049once_freq(const GDate *when)
2050{
2051 Recurrence *r = g_new0(Recurrence, 1);
2052 recurrenceSet(r, 1, PERIOD_ONCE, when, WEEKEND_ADJ_NONE);
2053 return r;
2054}
2055
2056static SchedXaction*
2057add_sx(const gchar *name, const GDate *start, const GDate *end,
2058 const GDate *last_occur, Recurrence *r)
2059{
2060 QofBook *book = qof_session_get_book(gnc_get_current_session());
2061 SchedXaction *sx = xaccSchedXactionMalloc(book);
2062 xaccSchedXactionSetName(sx, name);
2063 xaccSchedXactionSetStartDate(sx, start);
2064 if (end != NULL)
2066 if (last_occur != NULL)
2067 xaccSchedXactionSetLastOccurDate(sx, last_occur);
2068 {
2069 GList *recurrences = NULL;
2070 recurrences = g_list_append(recurrences, r);
2071 gnc_sx_set_schedule(sx, recurrences);
2072 }
2073
2074 gnc_sxes_add_sx(gnc_book_get_schedxactions(book), sx);
2075
2076 return sx;
2077}
2078
2079SchedXaction*
2080add_daily_sx(const gchar *name, const GDate *start,
2081 const GDate *end, const GDate *last_occur)
2082{
2083 return add_sx(name, start, end, last_occur, daily_freq(start, 1));
2084}
2085
2086SchedXaction*
2087add_once_sx(const gchar *name, const GDate *when)
2088{
2089 return add_sx(name, when, NULL, NULL, once_freq(when));
2090}
2091
2092void
2093remove_sx(SchedXaction *sx)
2094{
2095 QofBook *book = qof_session_get_book(gnc_get_current_session());
2096 SchedXactions *sxes = gnc_book_get_schedxactions(book);
2097 gnc_sxes_del_sx(sxes, sx);
2098}
This is the private header for the account structure.
Account handling public routines.
Anchor Scheduled Transaction info in a book.
Scheduled Transactions public handling routines.
API for Transactions and Splits (journal entries)
All type declarations for the whole Gnucash engine.
gboolean xaccAccountHasAncestor(const Account *acc, const Account *ancestor)
Returns true if the account is 'ancestor' or has 'ancestor' as an ancestor.
Definition Account.cpp:4224
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition Account.cpp:1516
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition Account.cpp:1475
GNCAccountType
The account types are used to determine how the transaction data in the account is displayed.
Definition Account.h:103
void gnc_account_append_child(Account *new_parent, Account *child)
This function will remove from the child account any pre-existing parent relationship,...
Definition Account.cpp:2836
void xaccAccountSetName(Account *acc, const char *str)
Set the account's name.
Definition Account.cpp:2458
GList * gnc_account_get_descendants(const Account *account)
This routine returns a flat list of all of the accounts that are descendants of the specified account...
Definition Account.cpp:3044
#define xaccAccountGetGUID(X)
Definition Account.h:252
void xaccAccountSetDescription(Account *acc, const char *str)
Set the account's description.
Definition Account.cpp:2497
void xaccAccountDestroy(Account *acc)
The xaccAccountDestroy() routine can be used to get rid of an account.
Definition Account.cpp:1590
void xaccAccountSetType(Account *acc, GNCAccountType tip)
Set the account's type.
Definition Account.cpp:2437
Account * xaccMallocAccount(QofBook *book)
Constructor.
Definition Account.cpp:1270
void xaccAccountSetCode(Account *acc, const char *str)
Set the account's accounting code.
Definition Account.cpp:2478
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account's commodity
Definition Account.cpp:3408
void xaccAccountSetCommodity(Account *acc, gnc_commodity *com)
Set the account's commodity.
Definition Account.cpp:2678
@ NUM_ACCOUNT_TYPES
stop here; the following types just aren't ready for prime time
Definition Account.h:161
QofBook * qof_session_get_book(const QofSession *session)
Returns the QofBook of this session.
QofBook * qof_book_new(void)
Allocate, initialise and return a new QofBook.
Definition qofbook.cpp:290
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
guint gnc_commodity_table_get_size(const gnc_commodity_table *tbl)
Returns the number of commodities in the commodity table.
void gnc_commodity_set_cusip(gnc_commodity *cm, const char *cusip)
Set the 'exchange code' for the specified commodity.
void gnc_commodity_table_remove(gnc_commodity_table *table, gnc_commodity *comm)
Remove a commodity from the commodity table.
void gnc_commodity_set_namespace(gnc_commodity *cm, const char *name_space)
Set the namespace for the specified commodity.
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
gnc_commodity * gnc_commodity_new(QofBook *book, const char *fullname, const char *name_space, const char *mnemonic, const char *cusip, int fraction)
Create a new commodity.
void gnc_commodity_set_fullname(gnc_commodity *cm, const char *fullname)
Set the full name for the specified commodity.
gboolean gnc_commodity_namespace_is_iso(const char *name_space)
Checks to see if the specified commodity namespace is the namespace for ISO 4217 currencies.
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
Returns the commodity table associated with a book.
void gnc_commodity_set_fraction(gnc_commodity *cm, int fraction)
Set the fraction for the specified commodity.
gnc_commodity * gnc_commodity_table_insert(gnc_commodity_table *table, gnc_commodity *comm)
Add a new commodity to the commodity table.
GList * gnc_commodity_table_get_namespaces(const gnc_commodity_table *table)
Return a list of all namespaces in the commodity table.
void gnc_commodity_set_mnemonic(gnc_commodity *cm, const char *mnemonic)
Set the mnemonic for the specified commodity.
CommodityList * gnc_commodity_table_get_commodities(const gnc_commodity_table *table, const char *name_space)
Return a list of all commodities in the commodity table that are in the given namespace.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition gnc-date.h:87
void xaccTransSetDescription(Transaction *trans, const char *desc)
Sets the transaction Description.
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
gboolean xaccTransGetVoidStatus(const Transaction *trans)
Retrieve information on whether or not a transaction has been voided.
void xaccTransDestroy(Transaction *trans)
Destroys a transaction.
void xaccTransSetNum(Transaction *trans, const char *xnum)
Sets the transaction Number (or ID) field; rather than use this function directly,...
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
#define xaccTransGetGUID(X)
Transaction * xaccTransLookup(const GncGUID *guid, QofBook *book)
The xaccTransLookup() subroutine will return the transaction associated with the given id,...
Split * xaccTransGetSplit(const Transaction *trans, int i)
Return a pointer to the indexed split in this transaction's split list.
time64 xaccTransRetDatePosted(const Transaction *trans)
Retrieve the posted date of the transaction.
void xaccTransSetDateEnteredSecs(Transaction *trans, time64 secs)
Modify the date of when the transaction was entered.
void xaccTransUnvoid(Transaction *trans)
xaccTransUnvoid restores a voided transaction to its original state.
void xaccTransSetDatePostedSecs(Transaction *trans, time64 secs)
The xaccTransSetDatePostedSecs() method will modify the posted date of the transaction,...
Transaction * xaccMallocTransaction(QofBook *book)
The xaccMallocTransaction() will malloc memory and initialize it.
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Set a new currency on a transaction.
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
const char * xaccTransGetNum(const Transaction *trans)
Gets the transaction Number (or ID) field; rather than use this function directly,...
void xaccTransVoid(Transaction *trans, const char *reason)
xaccTransVoid voids a transaction.
#define xaccAccountInsertSplit(acc, s)
The xaccAccountInsertSplit() method will insert the indicated split into the indicated account.
Definition Account.h:1069
void xaccTransRollbackEdit(Transaction *trans)
The xaccTransRollbackEdit() routine rejects all edits made, and sets the transaction back to where it...
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
SplitList * xaccAccountGetSplitList(const Account *acc)
The xaccAccountGetSplitList() routine returns a pointer to a GList of the splits in the account.
Definition Account.cpp:3949
const gchar * QofIdType
QofIdType declaration.
Definition qofid.h:80
GncGUID * guid_new(void)
Allocate and construct a new GUID.
Definition guid.cpp:186
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Division.
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Check for error signal in value.
gboolean gnc_numeric_negative_p(gnc_numeric a)
Returns 1 if a < 0, otherwise returns 0.
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value.
gboolean gnc_numeric_positive_p(gnc_numeric a)
Returns 1 if a > 0, otherwise returns 0.
@ GNC_ERROR_OK
No error.
gboolean gnc_pricedb_foreach_price(GNCPriceDB *db, GncPriceForeachFunc f, gpointer user_data, gboolean stable_order)
Call a GncPriceForeachFunction once for each price in db, until the function returns FALSE.
gboolean gnc_pricedb_remove_price(GNCPriceDB *db, GNCPrice *p)
Remove a price from the pricedb and unref the price.
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Return the pricedb associated with the book.
gboolean gnc_pricedb_add_price(GNCPriceDB *db, GNCPrice *p)
Add a price to the pricedb.
PriceSource
Price source enum.
GNCPrice * gnc_price_create(QofBook *book)
gnc_price_create - returns a newly allocated and initialized price with a reference count of 1.
void gnc_price_unref(GNCPrice *p)
gnc_price_unref - indicate you're finished with a price (i.e.
#define QUERY_DEFAULT_SORT
Default sort object type.
Definition qofquery.h:105
void qof_query_set_sort_increasing(QofQuery *q, gboolean prim_inc, gboolean sec_inc, gboolean tert_inc)
When a query is run, the results are sorted before being returned.
void qof_query_set_sort_order(QofQuery *q, QofQueryParamList *params1, QofQueryParamList *params2, QofQueryParamList *params3)
When a query is run, the results are sorted before being returned.
QofQueryOp
Query Term Operators, for combining Query Terms.
Definition qofquery.h:93
void qof_query_destroy(QofQuery *query)
Frees the resources associate with a Query object.
void qof_query_set_max_results(QofQuery *q, int n)
Set the maximum number of results that should be returned.
void gnc_sx_set_schedule(SchedXaction *sx, GList *schedule)
SchedXaction * xaccSchedXactionMalloc(QofBook *book)
Creates and initializes a scheduled transaction.
void xaccSchedXactionSetName(SchedXaction *sx, const gchar *newName)
A copy of the name is made.
void xaccSchedXactionSetEndDate(SchedXaction *sx, const GDate *newEnd)
Set to an invalid GDate to turn off 'end-date' definition.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
void xaccSplitSetReconcile(Split *split, char recn)
Set the reconcile flag.
gboolean xaccSplitDestroy(Split *split)
Destructor.
Definition Split.cpp:1506
char xaccSplitGetReconcile(const Split *split)
Returns the value of the reconcile flag.
void xaccSplitSetMemo(Split *split, const char *memo)
The memo is an arbitrary string associated with a split.
#define xaccSplitGetGUID(X)
Definition Split.h:579
void xaccSplitSetValue(Split *split, gnc_numeric val)
The xaccSplitSetValue() method sets the value of this split in the transaction's commodity.
Split * xaccMallocSplit(QofBook *book)
Constructor.
void xaccSplitSetAmount(Split *split, gnc_numeric amt)
The xaccSplitSetAmount() method sets the amount in the account's commodity that the split should have...
const char * xaccSplitGetAction(const Split *split)
Returns the action string.
#define VREC
split is void
Definition Split.h:77
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction's commodity.
gnc_numeric xaccSplitGetSharePrice(const Split *split)
Returns the price of the split, that is, the value divided by the amount.
Definition Split.cpp:1995
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
void xaccSplitSetDateReconciledSecs(Split *split, time64 secs)
Set the date on which this split was reconciled by specifying the time as time64.
#define NREC
not reconciled or cleared
Definition Split.h:76
#define FREC
frozen into accounting period
Definition Split.h:75
#define CREC
The Split has been cleared
Definition Split.h:73
#define YREC
The Split has been reconciled.
Definition Split.h:74
void xaccSplitSetAction(Split *split, const char *actn)
The Action is an arbitrary user-assigned string.
Definition Split.cpp:1786
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account's commodity.
STRUCTS.
The type used to store guids in C.
Definition guid.h:75
QofBook reference.
Definition qofbook-p.hpp:47
A Query.
Definition qofquery.cpp:75
$brief This file declares testing functions for the engine.