Files

2098 lines
53 KiB
C++
Raw Permalink Normal View History

/**
* @file test-engine-stuff.c
* @brief tools to set up random, but finanically consistent books.
* Create transactions with random values, random accounts, random
2017-01-16 23:03:50 +01:00
* account hierarchies, etc.
*
* XXX We should modify routines to create really, ugly, dirty
2010-02-18 05:31:54 +00:00
* transactions
* -- 3 or more splits (TBD)
* -- splits without parent accounts (done)
* -- splits that have accounts but aren't in a transaction (TBD)
* -- splits that share a currency with the transaction, but whose
* value doesn't equal amount (done)
2004-07-06 03:46:41 +00:00
*
* Created by Linux Developers Group, 2001
* Updates Linas Vepstas July 2004
*/
/********************************************************************\
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
* *
\********************************************************************/
2016-06-07 10:39:46 -04:00
#include <guid.hpp>
2017-08-06 17:12:16 +02:00
#include <kvp-frame.hpp>
2015-06-26 12:24:34 -07:00
2015-07-11 22:05:28 -07:00
#include <platform.h>
#if PLATFORM(WINDOWS)
#define __STDC_FORMAT_MACROS
#endif
2002-01-30 07:38:35 +00:00
#include <sys/types.h>
2001-08-18 00:13:45 +00:00
#include <dirent.h>
#include <fcntl.h>
#include <glib.h>
#include <stdio.h>
#include <inttypes.h>
2001-08-18 00:13:45 +00:00
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <string>
#include <qof.h>
2001-08-18 00:13:45 +00:00
2004-07-09 03:07:34 +00:00
#include "Account.h"
#include "AccountP.hpp"
2001-08-24 01:13:04 +00:00
#include "gnc-engine.h"
#include "gnc-session.h"
2002-01-02 20:53:29 +00:00
#include "Transaction.h"
2024-04-25 21:15:31 +08:00
#include "TransactionP.hpp"
#include "Recurrence.h"
#include "SchedXaction.h"
#include "SX-book.h"
2001-08-18 00:13:45 +00:00
#include "test-engine-stuff.h"
#include "test-stuff.h"
#include "test-engine-strings.h"
#include <qofinstance-p.h>
2001-08-18 00:13:45 +00:00
2001-09-29 10:03:38 +00:00
static GHashTable *exclude_kvp_types = NULL;
2001-10-16 00:50:01 +00:00
static gint kvp_max_depth = 5;
2001-09-29 10:03:38 +00:00
static gint kvp_frame_max_elements = 10;
static gint max_tree_depth = 1;
static gint max_level_accounts = 3;
static gint max_total_accounts = 10;
static gint max_trans_num = 1000;
static gint total_num_accounts = 0;
/* SCU == smallest currency unit -- the value of the denominator */
static gint max_scu = 100; //6000;
static gint min_scu = 100; //1;
2014-12-03 10:29:55 -08:00
static const int64_t num_limit = INT64_MAX; //1E19+
static const int64_t max_denom_mult = 1000000000LL; //1E9
2001-09-29 10:03:38 +00:00
2010-02-18 05:31:54 +00:00
/* The inverse fraction of split/transaction data that should
* contain invalid/inconsistent fields/values. Thus,
* if borked==1000, then one in 1000 fields will have bad data.
2010-02-18 05:31:54 +00:00
* This is used to test the data integrity scrubbers, which are
* supposed to clean up any crud they find.
*/
static gint borked = 80;
2001-09-29 10:03:38 +00:00
gboolean gnc_engine_debug_random = FALSE;
2001-09-29 10:03:38 +00:00
/* ========================================================== */
/* Set control parameters governing the run. */
2001-08-18 00:13:45 +00:00
void
set_max_account_tree_depth (gint max_tree_depth_in)
{
2010-02-18 05:31:54 +00:00
max_tree_depth = MAX (max_tree_depth_in, 1);
}
void
set_max_accounts_per_level (gint max_level_accounts_in)
{
2010-02-18 05:31:54 +00:00
max_level_accounts = MAX (max_level_accounts_in, 1);
}
2001-09-29 10:03:38 +00:00
void
set_max_kvp_depth (gint max_kvp_depth)
{
2010-02-18 05:31:54 +00:00
kvp_max_depth = MAX (max_kvp_depth, 1);
2001-09-29 10:03:38 +00:00
}
void
set_max_kvp_frame_elements (gint max_kvp_frame_elements)
{
2010-02-18 05:31:54 +00:00
kvp_frame_max_elements = MAX (max_kvp_frame_elements, 1);
2001-09-29 10:03:38 +00:00
}
static gboolean
2015-06-27 15:39:54 -07:00
kvp_type_excluded (KvpValue::Type kvp_type)
2001-09-29 10:03:38 +00:00
{
2010-02-18 05:31:54 +00:00
gint key = kvp_type;
2001-09-29 10:03:38 +00:00
2010-02-18 05:31:54 +00:00
if (!exclude_kvp_types)
return FALSE;
2001-09-29 10:03:38 +00:00
2010-02-18 05:31:54 +00:00
if (g_hash_table_lookup (exclude_kvp_types, &key))
return TRUE;
2001-09-29 10:03:38 +00:00
2010-02-18 05:31:54 +00:00
return FALSE;
2001-09-29 10:03:38 +00:00
}
/* ========================================================== */
static inline gboolean
do_bork (void)
{
2010-02-18 05:31:54 +00:00
if (1 == get_random_int_in_range (0, borked))
{
return TRUE;
}
return FALSE;
}
/* ========================================================== */
/* GList stuff */
static gpointer
get_random_list_element (GList *list)
{
2010-02-18 05:31:54 +00:00
g_return_val_if_fail (list, NULL);
2010-02-18 05:31:54 +00:00
return g_list_nth_data (list,
get_random_int_in_range (0,
g_list_length (list) - 1));
}
2014-09-17 17:31:16 -04:00
static KvpValue* get_random_kvp_value_depth (int type, gint depth);
static GList*
get_random_glist_depth (gint depth)
{
GList *ret = NULL;
int count = get_random_int_in_range(1, 5);
int i;
if (depth >= kvp_max_depth)
2010-02-18 05:31:54 +00:00
return NULL;
for (i = 0; i < count; i++)
{
2015-06-26 12:24:34 -07:00
KvpValue *value = nullptr;
do
{
2015-06-26 12:24:34 -07:00
value = get_random_kvp_value_depth (-2, depth + 1);
}
while (!value);
ret = g_list_prepend(ret, value);
}
return ret;
}
/* ========================================================== */
2014-09-16 10:30:47 -04:00
/* Time/Date, GncGUID data stuff */
2017-12-29 13:52:45 -05:00
time64
get_random_time (void)
{
time64 ret {0};
while (ret <= 0)
ret = rand();
return ret;
}
GncGUID*
get_random_guid(void)
{
return guid_new();
}
/* ========================================================== */
/* KVP stuff */
2004-07-09 03:07:34 +00:00
static KvpFrame* get_random_kvp_frame_depth (gint depth);
2004-07-09 03:07:34 +00:00
static KvpValue*
get_random_kvp_value_depth (int type, gint depth)
{
2015-06-27 15:39:54 -07:00
KvpValue::Type datype;
2005-11-02 03:32:36 +00:00
KvpValue *ret;
2015-06-26 12:24:34 -07:00
if (type == -1)
{
2015-06-27 15:39:54 -07:00
datype = static_cast<KvpValue::Type>(get_random_int_in_range(KvpValue::Type::INT64, KvpValue::Type::FRAME));
}
2015-06-26 12:24:34 -07:00
else if (type == -2)
{
2015-06-27 15:39:54 -07:00
datype = static_cast<KvpValue::Type>(get_random_int_in_range(KvpValue::Type::INT64, KvpValue::Type::FRAME - 1));
}
2015-06-26 12:24:34 -07:00
else
2015-06-27 15:39:54 -07:00
datype = static_cast<KvpValue::Type>(type);
if (datype == KvpValue::Type::FRAME && depth >= kvp_max_depth)
2010-02-18 05:31:54 +00:00
return NULL;
2015-06-27 15:39:54 -07:00
if (datype == KvpValue::Type::GLIST && depth >= kvp_max_depth)
2010-02-18 05:31:54 +00:00
return NULL;
if (kvp_type_excluded (datype))
2010-02-18 05:31:54 +00:00
return NULL;
2010-02-18 05:31:54 +00:00
switch (datype)
{
2015-06-27 15:39:54 -07:00
case KvpValue::Type::INT64:
ret = new KvpValue(get_random_gint64());
break;
2015-06-27 15:39:54 -07:00
case KvpValue::Type::DOUBLE:
2005-11-02 03:32:36 +00:00
ret = NULL;
break;
2015-06-27 15:39:54 -07:00
case KvpValue::Type::NUMERIC:
ret = new KvpValue(get_random_gnc_numeric(GNC_DENOM_AUTO));
break;
2015-06-27 15:39:54 -07:00
case KvpValue::Type::STRING:
{
gchar *tmp_str;
tmp_str = get_random_string();
2010-02-18 05:31:54 +00:00
if (!tmp_str)
return NULL;
2015-06-27 15:39:54 -07:00
ret = new KvpValue(tmp_str);
}
2010-02-18 05:31:54 +00:00
break;
2015-06-27 15:39:54 -07:00
case KvpValue::Type::GUID:
{
2015-06-27 15:39:54 -07:00
return new KvpValue(get_random_guid());
}
2010-02-18 05:31:54 +00:00
break;
case KvpValue::Type::TIME64:
{
time64 t = get_random_time();
ret = new KvpValue(t);
}
2010-02-18 05:31:54 +00:00
break;
2015-06-27 15:39:54 -07:00
case KvpValue::Type::GLIST:
ret = new KvpValue(get_random_glist_depth (depth + 1));
break;
2015-06-27 15:39:54 -07:00
case KvpValue::Type::FRAME:
{
2015-06-27 15:39:54 -07:00
return new KvpValue(get_random_kvp_frame_depth(depth + 1));
}
2010-02-18 05:31:54 +00:00
break;
default:
2005-11-02 03:32:36 +00:00
ret = NULL;
break;
}
2005-11-02 03:32:36 +00:00
return ret;
}
2004-07-09 03:07:34 +00:00
static KvpFrame*
get_random_kvp_frame_depth (gint depth)
{
int vals_to_add;
gboolean val_added;
if (depth >= kvp_max_depth)
2010-02-18 05:31:54 +00:00
return NULL;
2015-06-27 15:39:54 -07:00
auto ret = new KvpFrame;
2010-02-18 05:31:54 +00:00
vals_to_add = get_random_int_in_range(1, kvp_frame_max_elements);
val_added = FALSE;
2010-02-18 05:31:54 +00:00
for (; vals_to_add > 0; vals_to_add--)
{
gchar *key;
2004-07-09 03:07:34 +00:00
KvpValue *val;
2010-02-18 05:31:54 +00:00
key = NULL;
while (key == NULL)
{
key = get_random_string_without("/");
if (*key == '\0')
{
g_free(key);
key = NULL;
}
}
val = get_random_kvp_value_depth (-1, depth + 1);
if (!val)
{
2010-02-18 05:31:54 +00:00
g_free(key);
if (!val_added)
vals_to_add++;
continue;
}
val_added = TRUE;
ret->set_path({key}, val);
g_free(key);
}
return ret;
}
2004-07-09 03:07:34 +00:00
KvpFrame *
get_random_kvp_frame (void)
{
2010-02-18 05:31:54 +00:00
return get_random_kvp_frame_depth (0);
}
2004-07-09 03:07:34 +00:00
KvpValue *
get_random_kvp_value(int type)
{
2010-02-18 05:31:54 +00:00
return get_random_kvp_value_depth (type, 0);
}
/* ================================================================= */
/* Numeric stuff */
2004-07-03 01:24:01 +00:00
#define RAND_IN_RANGE(X) (((X)*((gint64) (rand()+1)))/RAND_MAX)
2001-08-18 00:13:45 +00:00
gnc_numeric
get_random_gnc_numeric(int64_t deno)
2001-08-18 00:13:45 +00:00
{
2004-07-03 01:24:01 +00:00
gint64 numer;
int64_t limit;
if (deno == GNC_DENOM_AUTO)
{
2014-12-03 10:29:55 -08:00
if (RAND_MAX / 8 > rand())
2010-02-18 05:31:54 +00:00
{
2014-12-03 10:29:55 -08:00
/* Random number between 1 and 6000 */
deno = RAND_IN_RANGE(6000ULL);
}
else
{
gint64 norm = RAND_IN_RANGE (11ULL);
/* multiple of 10, between 1 and 100 billion */
2014-12-03 10:29:55 -08:00
deno = 1;
while (norm)
{
deno *= 10;
norm --;
}
2010-02-18 05:31:54 +00:00
}
}
/* Make sure we have a non-zero denominator */
if (0 == deno) deno = 1;
2014-09-20 18:06:41 -07:00
/* Arbitrary random numbers can cause pointless overflow during
* calculations, in particular the revaluing in xaccSplitSetValue where an
* input gnc_numeric is converted to use a new denominator. To prevent that,
* the numerator is clamped to the larger of num_limit / deno or num_limit /
* max_denom_mult.
2014-09-20 18:06:41 -07:00
*/
limit = num_limit / (max_denom_mult / deno == 0 ? max_denom_mult : max_denom_mult / deno);
2014-09-20 18:06:41 -07:00
numer = get_random_gint64 ();
if (numer > limit)
2014-09-20 18:06:41 -07:00
{
int64_t num = numer % limit;
2014-09-20 18:06:41 -07:00
if (num)
numer = num;
else
numer = limit;
2014-09-20 18:06:41 -07:00
}
if (0 == numer) numer = 1;
g_log("test.engine.suff", G_LOG_LEVEL_INFO, "New GncNumeric %" PRIu64 " / %" PRIu64 " !\n", numer, deno);
return gnc_numeric_create(numer, deno);
2001-08-18 00:13:45 +00:00
}
/* Rate here really means price or exchange rate, this is used solely
* to compute an amount from a randomly-created value. */
static gnc_numeric
get_random_rate (void)
{
/* Large rates blow up xaccSplitAssignToLot, so we clamp the rate
* at a smallish value */
gint64 numer = get_random_gint64 () % (2ULL << 24);
gint64 denom = 100LL;
return gnc_numeric_create (numer, denom);
}
/* ================================================================= */
/* Commodity stuff */
2001-08-18 00:13:45 +00:00
const char *types[] =
{
"NASDAQ",
"NYSE",
"EUREX",
"FUND",
"AMEX",
NULL
};
const char*
get_random_commodity_namespace(void)
{
return get_random_string_in_array(types);
}
static gnc_commodity *
get_random_commodity_from_table (gnc_commodity_table *table)
2001-08-18 00:13:45 +00:00
{
2010-02-18 05:31:54 +00:00
GList *namespaces;
gnc_commodity *com = NULL;
2001-08-18 00:13:45 +00:00
2010-02-18 05:31:54 +00:00
g_return_val_if_fail (table, NULL);
2010-02-18 05:31:54 +00:00
namespaces = gnc_commodity_table_get_namespaces (table);
2010-02-18 05:31:54 +00:00
do
{
GList *commodities;
char *name_space;
2015-06-26 12:24:34 -07:00
name_space = static_cast<char*>(get_random_list_element (namespaces));
commodities = gnc_commodity_table_get_commodities (table, name_space);
2010-02-18 05:31:54 +00:00
if (!commodities)
continue;
2015-06-26 12:24:34 -07:00
com = static_cast<gnc_commodity*>(get_random_list_element (commodities));
2010-02-18 05:31:54 +00:00
g_list_free (commodities);
2010-02-18 05:31:54 +00:00
}
while (!com);
2010-02-18 05:31:54 +00:00
g_list_free (namespaces);
2010-02-18 05:31:54 +00:00
return com;
}
gnc_commodity*
get_random_commodity (QofBook *book)
{
gnc_commodity *ret;
gchar *name;
const gchar *space;
gchar *mn;
gchar *cusip;
int ran_int;
gnc_commodity_table *table;
2004-07-09 03:07:34 +00:00
table = gnc_commodity_table_get_table (book);
#if 0
if (table &&
2010-02-18 05:31:54 +00:00
(gnc_commodity_table_get_size (table) > 0) &&
get_random_int_in_range (1, 5) < 5)
return get_random_commodity_from_table (table);
#endif
mn = get_random_string_length_in_range(1, 3);
space = get_random_commodity_namespace();
if (table)
{
2010-02-18 05:31:54 +00:00
ret = gnc_commodity_table_lookup (table, space, mn);
2010-02-18 05:31:54 +00:00
if (ret)
{
g_free (mn);
return ret;
}
}
name = get_random_string();
cusip = get_random_string();
ran_int = get_random_int_in_range(min_scu, max_scu);
ret = gnc_commodity_new(book, name, space, mn, cusip, ran_int);
g_free(mn);
g_free(name);
g_free(cusip);
if (table)
2010-02-18 05:31:54 +00:00
ret = gnc_commodity_table_insert (table, ret);
return ret;
}
void
make_random_changes_to_commodity (gnc_commodity *com)
{
2010-02-18 05:31:54 +00:00
char *str;
2010-02-18 05:31:54 +00:00
g_return_if_fail (com);
2010-02-18 05:31:54 +00:00
str = get_random_string ();
gnc_commodity_set_namespace (com, str);
g_free (str);
2010-02-18 05:31:54 +00:00
str = get_random_string ();
gnc_commodity_set_mnemonic (com, str);
g_free (str);
2010-02-18 05:31:54 +00:00
str = get_random_string ();
gnc_commodity_set_fullname (com, str);
g_free (str);
2010-02-18 05:31:54 +00:00
str = get_random_string ();
gnc_commodity_set_cusip (com, str);
g_free (str);
2010-02-18 05:31:54 +00:00
gnc_commodity_set_fraction (com, get_random_int_in_range (1, 100000));
}
void
make_random_changes_to_commodity_table (gnc_commodity_table *table)
{
2010-02-18 05:31:54 +00:00
GList *namespaces;
GList *node;
2010-02-18 05:31:54 +00:00
g_return_if_fail (table);
2010-02-18 05:31:54 +00:00
namespaces = gnc_commodity_table_get_namespaces (table);
2010-02-18 05:31:54 +00:00
for (node = namespaces; node; node = node->next)
{
2015-06-26 12:24:34 -07:00
auto ns = static_cast<const char *>(node->data);
2010-02-18 05:31:54 +00:00
GList *commodities;
GList *com_node;
2010-02-18 05:31:54 +00:00
if (gnc_commodity_namespace_is_iso (ns))
continue;
2010-02-18 05:31:54 +00:00
commodities = gnc_commodity_table_get_commodities (table, ns);
2010-02-18 05:31:54 +00:00
for (com_node = commodities; com_node; com_node = com_node->next)
{
2015-06-26 12:24:34 -07:00
auto com = static_cast<gnc_commodity *>(com_node->data);
2010-02-18 05:31:54 +00:00
gnc_commodity_table_remove (table, com);
make_random_changes_to_commodity (com);
gnc_commodity_table_insert (table, com);
}
2010-02-18 05:31:54 +00:00
g_list_free (commodities);
}
2010-02-18 05:31:54 +00:00
g_list_free (namespaces);
}
/* ================================================================= */
/* Price stuff */
void
make_random_changes_to_price (QofBook *book, GNCPrice *p)
{
2018-08-02 15:46:27 -07:00
time64 time;
PriceSource ps;
2010-02-18 05:31:54 +00:00
char *string;
gnc_commodity *c;
2010-02-18 05:31:54 +00:00
g_return_if_fail (book && p);
2010-02-18 05:31:54 +00:00
gnc_price_begin_edit (p);
2001-08-18 00:13:45 +00:00
2010-02-18 05:31:54 +00:00
c = get_random_commodity (book);
gnc_price_set_commodity (p, c);
2001-08-24 01:13:04 +00:00
2010-02-18 05:31:54 +00:00
c = get_random_commodity (book);
gnc_price_set_currency (p, c);
2001-08-18 00:13:45 +00:00
2018-08-02 15:46:27 -07:00
time = get_random_time ();
gnc_price_set_time64 (p, time);
2001-08-18 00:13:45 +00:00
ps = (PriceSource)get_random_int_in_range((int)PRICE_SOURCE_EDIT_DLG,
(int)PRICE_SOURCE_INVALID);
gnc_price_set_source (p, ps);
2001-08-18 00:13:45 +00:00
2010-02-18 05:31:54 +00:00
string = get_random_string ();
gnc_price_set_typestr (p, string);
g_free (string);
2001-08-18 00:13:45 +00:00
gnc_price_set_value (p, get_random_gnc_numeric (GNC_DENOM_AUTO));
2001-08-18 00:13:45 +00:00
2010-02-18 05:31:54 +00:00
gnc_price_commit_edit (p);
}
GNCPrice *
get_random_price(QofBook *book)
{
2010-02-18 05:31:54 +00:00
GNCPrice *p;
2010-02-18 05:31:54 +00:00
p = gnc_price_create (book);
if (!p)
{
failure_args("engine-stuff", __FILE__, __LINE__,
"get_random_price failed");
return NULL;
}
2010-02-18 05:31:54 +00:00
make_random_changes_to_price (book, p);
if (!p)
{
failure_args("engine-stuff", __FILE__, __LINE__,
"make_random_changes_to_price failed");
return NULL;
}
2010-02-18 05:31:54 +00:00
return p;
2001-08-18 00:13:45 +00:00
}
2005-11-02 03:32:36 +00:00
gboolean
make_random_pricedb (QofBook *book, GNCPriceDB *db)
2001-08-24 01:13:04 +00:00
{
2010-02-18 05:31:54 +00:00
int num_prices;
2001-08-24 01:13:04 +00:00
2010-02-18 05:31:54 +00:00
num_prices = get_random_int_in_range (1, 41);
if (num_prices < 1) /* should be impossible */
2005-11-02 03:32:36 +00:00
{
failure_args("engine-stuff", __FILE__, __LINE__,
2010-02-18 05:31:54 +00:00
"get_random_int_in_range failed");
return FALSE;
2005-11-02 03:32:36 +00:00
}
2001-08-24 01:13:04 +00:00
2010-02-18 05:31:54 +00:00
while (num_prices-- > 0)
{
GNCPrice *p;
p = get_random_price (book);
if (!p)
{
failure_args("engine-stuff", __FILE__, __LINE__,
"get_random_price failed");
return FALSE;
}
if (!gnc_pricedb_add_price (db, p))
/* probably the same date as another price, just try again. */
++num_prices;
2001-08-24 01:13:04 +00:00
2010-02-18 05:31:54 +00:00
gnc_price_unref (p);
}
return TRUE;
2001-09-28 00:15:49 +00:00
}
GNCPriceDB *
get_random_pricedb(QofBook *book)
2001-09-28 00:15:49 +00:00
{
2010-02-18 05:31:54 +00:00
GNCPriceDB *db;
2001-09-28 00:15:49 +00:00
2010-02-18 05:31:54 +00:00
db = gnc_pricedb_get_db (book);
if (!db)
{
failure_args("engine-stuff", __FILE__, __LINE__,
"gnc_pricedb_get_db failed");
return NULL;
}
if (!make_random_pricedb (book, db))
{
return NULL;
}
2001-08-24 01:13:04 +00:00
2010-02-18 05:31:54 +00:00
return db;
2001-08-24 01:13:04 +00:00
}
static gboolean
price_accumulator (GNCPrice *p, gpointer data)
{
2015-06-26 12:24:34 -07:00
auto list = static_cast<GList**>(data);
2010-02-18 05:31:54 +00:00
*list = g_list_prepend (*list, p);
2010-02-18 05:31:54 +00:00
return TRUE;
}
void
make_random_changes_to_pricedb (QofBook *book, GNCPriceDB *pdb)
{
2010-02-18 05:31:54 +00:00
GList *list = NULL;
GList *node;
2010-02-18 05:31:54 +00:00
g_return_if_fail (pdb);
2010-02-18 05:31:54 +00:00
gnc_pricedb_foreach_price (pdb, price_accumulator, &list, FALSE);
2010-02-18 05:31:54 +00:00
for (node = list; node; node = node->next)
{
2015-06-26 12:24:34 -07:00
auto p = static_cast<GNCPrice *>(node->data);
2010-02-18 05:31:54 +00:00
switch (get_random_int_in_range (0, 5))
{
case 0: /* Delete */
gnc_pricedb_remove_price (pdb, p);
break;
2010-02-18 05:31:54 +00:00
case 1:
case 2: /* Change */
make_random_changes_to_price (book, p);
break;
2010-02-18 05:31:54 +00:00
default: /* nothing */
break;
}
}
2010-02-18 05:31:54 +00:00
g_list_free (list);
2010-02-18 05:31:54 +00:00
/* Add a few new ones */
{
2010-02-18 05:31:54 +00:00
int i = get_random_int_in_range (1, 5);
2010-02-18 05:31:54 +00:00
while (i--)
{
GNCPrice *p = get_random_price (book);
2010-02-18 05:31:54 +00:00
gnc_pricedb_add_price (pdb, p);
gnc_price_unref (p);
}
}
}
/* ================================================================= */
/* Account stuff */
2001-09-29 10:03:38 +00:00
2001-08-18 00:13:45 +00:00
static void
set_account_random_string(Account* act,
void(*func)(Account *act, const gchar*str))
{
gchar *tmp_str = get_random_string();
2010-02-18 05:31:54 +00:00
if (tmp_str)
2001-08-18 00:13:45 +00:00
{
(func)(act, tmp_str);
g_free(tmp_str);
}
}
static void
set_account_random_string_from_array(
Account* act, void(*func)(Account *act, const gchar*str),
const gchar *list[])
{
const gchar *tmp_str = get_random_string_in_array(list);
2010-02-18 05:31:54 +00:00
if (tmp_str)
(func)(act, tmp_str);
}
2001-08-20 23:25:39 +00:00
static void
account_add_subaccounts (QofBook *book, Account *account, int depth)
2001-08-20 23:25:39 +00:00
{
2010-02-18 05:31:54 +00:00
int num_accounts;
2001-08-20 23:25:39 +00:00
2010-02-18 05:31:54 +00:00
if (depth == 0)
return;
2001-08-20 23:25:39 +00:00
2010-02-18 05:31:54 +00:00
num_accounts = get_random_int_in_range (1, 10);
while (num_accounts-- > 0)
{
Account *sub = get_random_account (book);
2001-08-20 23:25:39 +00:00
2010-02-18 05:31:54 +00:00
gnc_account_append_child (account, sub);
2001-08-20 23:25:39 +00:00
2010-02-18 05:31:54 +00:00
total_num_accounts ++;
if (total_num_accounts > max_total_accounts) return;
2010-02-18 05:31:54 +00:00
account_add_subaccounts (book, sub, depth - 1);
}
2001-08-20 23:25:39 +00:00
}
2001-11-25 09:05:23 +00:00
static void
make_random_account_tree (QofBook *book, Account *root)
2001-11-25 09:05:23 +00:00
{
2010-02-18 05:31:54 +00:00
int depth;
2010-02-18 05:31:54 +00:00
g_return_if_fail (book);
g_return_if_fail (root);
2010-02-18 05:31:54 +00:00
total_num_accounts = 0;
depth = get_random_int_in_range (1, max_tree_depth);
2010-02-18 05:31:54 +00:00
account_add_subaccounts (book, root, depth);
2010-02-18 05:31:54 +00:00
/* Make sure we have at least two accounts! */
if (total_num_accounts <= 1)
account_add_subaccounts (book, root, 1);
}
Account *
get_random_account_tree (QofBook *book)
{
2010-02-18 05:31:54 +00:00
Account * root;
2010-02-18 05:31:54 +00:00
g_return_val_if_fail (book, NULL);
2010-02-18 05:31:54 +00:00
root = gnc_book_get_root_account (book);
if (!root)
{
root = xaccMallocAccount (book);
gnc_book_set_root_account (book, root);
}
2010-02-18 05:31:54 +00:00
make_random_account_tree (book, root);
2010-02-18 05:31:54 +00:00
return root;
}
/* ================================================================= */
/* transaction stuff */
/** This routine creates a random, but otherwise self-consistent,
* 'legal' transaction. It's been modified to occasionally build
2010-02-18 05:31:54 +00:00
* cruddy, inconsistent transactions, so that the engine 'scrub'
* routines get tested.
*/
static void
add_random_splits(QofBook *book, Transaction *trn, GList *account_list)
{
2014-12-03 10:29:55 -08:00
Split *s1, *s2;
2012-05-26 23:47:34 +00:00
gnc_numeric val;
2014-12-03 10:29:55 -08:00
int s2_scu;
2001-11-25 09:05:23 +00:00
/* Gotta have at least two different accounts */
if (1 >= g_list_length (account_list)) return;
2001-11-25 09:05:23 +00:00
2015-06-26 12:24:34 -07:00
auto acc = static_cast<Account*>(get_random_list_element (account_list));
xaccTransBeginEdit(trn);
2014-12-03 10:29:55 -08:00
s1 = get_random_split(book, acc, trn);
2001-08-20 23:25:39 +00:00
2015-06-26 12:24:34 -07:00
auto bcc = static_cast<Account*>(get_random_list_element (account_list));
if ((bcc == acc) && (!do_bork()))
2010-02-18 05:31:54 +00:00
{
/* Make sure that each side of the transaction is in
* a different account; otherwise get weirdness in lot
* calculcations. ... Hmm maybe should fix lots in
* this case? */
while (bcc == acc)
{
2015-06-26 12:24:34 -07:00
bcc = static_cast<Account*>(get_random_list_element (account_list));
2010-02-18 05:31:54 +00:00
}
}
2001-08-20 23:25:39 +00:00
/* Set up two splits whose values really are opposites. */
2014-12-03 10:29:55 -08:00
val = xaccSplitGetValue(s1);
s2 = get_random_split(book, bcc, trn);
s2_scu = gnc_commodity_get_fraction (xaccAccountGetCommodity(s2->acc));
/* Other split should have equal and opposite value */
2010-02-18 05:31:54 +00:00
if (do_bork())
{
val = get_random_gnc_numeric(s2_scu);
g_log ("test.engine.suff", G_LOG_LEVEL_DEBUG, "Borking second %" PRIu64
" / %" PRIu64 ", scu %d\n", val.num, val.denom, s2_scu);
}
val = gnc_numeric_neg(val);
2014-12-03 10:29:55 -08:00
xaccSplitSetValue(s2, val);
if (val.denom != s2_scu)
{
if (val.denom > s2_scu)
val.num /= val.denom / s2_scu;
val.denom = s2_scu;
}
xaccSplitSetAmount(s2, val);
xaccTransCommitEdit(trn);
2001-08-20 23:25:39 +00:00
}
2001-10-16 09:13:07 +00:00
typedef struct
{
GncGUID guid;
2001-10-16 09:13:07 +00:00
} TransInfo;
void
make_random_changes_to_transaction_and_splits (QofBook *book,
2010-02-18 05:31:54 +00:00
Transaction *trans,
GList *accounts)
2001-10-16 09:13:07 +00:00
{
2010-02-18 05:31:54 +00:00
GList *splits;
GList *node;
Split *split;
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
g_return_if_fail (book);
g_return_if_fail (trans);
g_return_if_fail (accounts);
2010-02-18 05:31:54 +00:00
xaccTransBeginEdit (trans);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
make_random_changes_to_transaction (book, trans);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
switch (get_random_int_in_range (0, 3))
{
2001-10-16 09:13:07 +00:00
case 0: /* delete some splits, add some more */
2010-02-18 05:31:54 +00:00
if (xaccTransGetVoidStatus (trans))
break;
2001-11-07 08:59:30 +00:00
2010-02-18 05:31:54 +00:00
do
{
split = xaccTransGetSplit (trans, 0);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
xaccSplitDestroy (split);
}
while (split);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
add_random_splits (book, trans, accounts);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
/* fall through */
2001-10-16 09:13:07 +00:00
case 1: /* move the splits around */
2010-02-18 05:31:54 +00:00
if (xaccTransGetVoidStatus (trans))
break;
2001-11-07 08:59:30 +00:00
2010-02-18 05:31:54 +00:00
splits = xaccTransGetSplitList (trans);
for (node = splits; node; node = node->next)
{
2015-06-26 12:24:34 -07:00
auto split = static_cast<Split *>(node->data);
auto account = static_cast<Account*>(get_random_list_element (accounts));
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
xaccAccountInsertSplit (account, split);
}
break;
2001-10-16 09:13:07 +00:00
case 2: /* destroy the transaction */
2010-02-18 05:31:54 +00:00
xaccTransDestroy (trans);
xaccTransCommitEdit (trans);
return;
2001-10-16 09:13:07 +00:00
default: /* do nothing */
2010-02-18 05:31:54 +00:00
break;
}
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
if (xaccTransGetVoidStatus (trans))
{
xaccTransCommitEdit (trans);
return;
}
2001-11-07 08:59:30 +00:00
2010-02-18 05:31:54 +00:00
/* mess with the splits */
splits = xaccTransGetSplitList (trans);
for (node = splits; node; node = node->next)
{
2015-06-26 12:24:34 -07:00
auto split = static_cast<Split *>(node->data);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
if (get_random_boolean ())
make_random_changes_to_split (split);
}
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
if (get_random_boolean ())
xaccTransCommitEdit (trans);
else
xaccTransRollbackEdit (trans);
2001-10-16 09:13:07 +00:00
}
static int
2001-10-16 09:13:07 +00:00
add_trans_helper (Transaction *trans, gpointer data)
{
2010-02-18 05:31:54 +00:00
TransInfo *ti;
2015-06-26 12:24:34 -07:00
auto list = static_cast<GList **>(data);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
ti = g_new (TransInfo, 1);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
ti->guid = *xaccTransGetGUID (trans);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
*list = g_list_prepend (*list, ti);
return 0;
2001-10-16 09:13:07 +00:00
}
void
make_random_changes_to_level (QofBook *book, Account *parent)
2001-10-16 09:13:07 +00:00
{
2010-02-18 05:31:54 +00:00
Account *new_account;
Account *account;
GList *accounts;
GList *transes;
GList *splits;
GList *node;
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
g_return_if_fail (parent && book);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
accounts = gnc_account_get_descendants (parent);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
/* Add a new account */
new_account = get_random_account (book);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
if (get_random_boolean () || !accounts)
gnc_account_append_child (parent, new_account);
else
{
2015-06-26 12:24:34 -07:00
account = static_cast<Account*>(get_random_list_element (accounts));
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
gnc_account_append_child (account, new_account);
}
2010-02-18 05:31:54 +00:00
g_list_free (accounts);
accounts = gnc_account_get_descendants (parent);
2010-02-18 05:31:54 +00:00
/* Add some new transactions */
add_random_transactions_to_book (book, get_random_int_in_range (1, 6));
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
/* Mess with the accounts */
for (node = accounts; node; node = node->next)
{
2015-06-26 12:24:34 -07:00
auto account = static_cast<Account *>(node->data);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
if (get_random_boolean ())
make_random_changes_to_account (book, account);
}
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
/* Mess with the transactions & splits */
transes = NULL;
xaccAccountTreeForEachTransaction (parent, add_trans_helper, &transes);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
for (node = transes; node; node = node->next)
{
2015-06-26 12:24:34 -07:00
auto ti = static_cast<TransInfo *>(node->data);
2010-02-18 05:31:54 +00:00
Transaction *trans = xaccTransLookup (&ti->guid, book);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
if (!trans)
continue;
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
make_random_changes_to_transaction_and_splits (book, trans, accounts);
}
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
for (node = transes; node; node = node->next)
{
2015-06-26 12:24:34 -07:00
auto ti = static_cast<TransInfo *>(node->data);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
g_free (ti);
}
g_list_free (transes);
transes = NULL;
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
/* delete an account */
2015-06-26 12:24:34 -07:00
account = static_cast<Account*>(get_random_list_element (accounts));
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
splits = xaccAccountGetSplitList (account);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
for (node = splits; node; node = node->next)
2001-10-16 09:13:07 +00:00
{
2015-06-26 12:24:34 -07:00
auto split = static_cast<Split *>(node->data);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
do
{
2015-06-26 12:24:34 -07:00
new_account = static_cast<Account*>(get_random_list_element (accounts));
2010-02-18 05:31:54 +00:00
}
while (new_account == account);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
xaccAccountInsertSplit (new_account, split);
}
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
xaccAccountBeginEdit (account);
xaccAccountDestroy (account);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
g_list_free (splits);
g_list_free (accounts);
2010-02-18 05:31:54 +00:00
accounts = gnc_account_get_descendants (parent);
2010-02-18 05:31:54 +00:00
/* move some accounts around */
if (accounts && (g_list_length (accounts) > 1))
{
2010-02-18 05:31:54 +00:00
int i = get_random_int_in_range (1, 4);
2010-02-18 05:31:54 +00:00
while (i--)
{
2015-06-26 12:24:34 -07:00
Account *a2;
2010-02-18 05:31:54 +00:00
2015-06-26 12:24:34 -07:00
auto a1 = static_cast<Account*>(get_random_list_element (accounts));
2010-02-18 05:31:54 +00:00
if (get_random_boolean ())
2015-06-26 12:24:34 -07:00
a2 = static_cast<Account*>(get_random_list_element (accounts));
2010-02-18 05:31:54 +00:00
else
a2 = NULL;
if (!a2)
{
gnc_account_append_child (parent, a1);
continue;
}
if (a1 == a2 ||
xaccAccountHasAncestor (a1, a2) ||
xaccAccountHasAncestor (a2, a1))
{
i++;
continue;
}
gnc_account_append_child (a2, a1);
}
}
2010-02-18 05:31:54 +00:00
g_list_free (accounts);
2001-10-16 09:13:07 +00:00
}
2001-08-18 00:13:45 +00:00
Account*
get_random_account(QofBook *book)
2001-08-18 00:13:45 +00:00
{
Account *root, *ret;
2001-08-18 00:13:45 +00:00
int tmp_int;
2001-11-24 12:10:42 +00:00
ret = xaccMallocAccount(book);
2001-08-18 00:13:45 +00:00
xaccAccountBeginEdit(ret);
set_account_random_string_from_array(ret, xaccAccountSetName,
sane_account_names);
2001-08-18 00:13:45 +00:00
tmp_int = get_random_int_in_range(ACCT_TYPE_BANK, NUM_ACCOUNT_TYPES - 1);
2015-06-26 12:24:34 -07:00
xaccAccountSetType(ret, static_cast<GNCAccountType>(tmp_int));
2001-08-18 00:13:45 +00:00
set_account_random_string(ret, xaccAccountSetCode);
set_account_random_string(ret, xaccAccountSetDescription);
2001-11-24 12:10:42 +00:00
xaccAccountSetCommodity(ret, get_random_commodity(book));
qof_instance_set_slots(QOF_INSTANCE(ret), get_random_kvp_frame());
root = gnc_book_get_root_account (book);
2010-02-18 05:31:54 +00:00
if (!root)
2004-07-09 03:07:34 +00:00
{
root = xaccMallocAccount (book);
gnc_book_set_root_account (book, root);
2004-07-09 03:07:34 +00:00
}
gnc_account_append_child (root, ret);
2001-08-18 00:13:45 +00:00
xaccAccountCommitEdit(ret);
2001-08-18 00:13:45 +00:00
return ret;
}
2001-10-16 09:13:07 +00:00
void
make_random_changes_to_account (QofBook *book, Account *account)
2001-10-16 09:13:07 +00:00
{
int tmp_int;
g_return_if_fail (account);
xaccAccountBeginEdit (account);
set_account_random_string (account, xaccAccountSetName);
tmp_int = get_random_int_in_range (ACCT_TYPE_BANK, NUM_ACCOUNT_TYPES - 1);
2015-06-26 12:24:34 -07:00
xaccAccountSetType (account, static_cast<GNCAccountType>(tmp_int));
2001-10-16 09:13:07 +00:00
set_account_random_string (account, xaccAccountSetCode);
set_account_random_string (account, xaccAccountSetDescription);
2001-11-24 12:10:42 +00:00
xaccAccountSetCommodity (account, get_random_commodity(book));
2001-10-16 09:13:07 +00:00
qof_instance_set_slots(QOF_INSTANCE(account), get_random_kvp_frame());
2001-10-16 09:13:07 +00:00
xaccAccountCommitEdit (account);
}
2001-08-18 00:13:45 +00:00
static void
set_split_random_string(Split *spl,
2010-02-18 05:31:54 +00:00
void(*func)(Split *act, const gchar*str))
2001-08-18 00:13:45 +00:00
{
gchar *tmp_str = get_random_string();
2010-02-18 05:31:54 +00:00
if (tmp_str)
2001-08-18 00:13:45 +00:00
{
(func)(spl, tmp_str);
g_free(tmp_str);
}
}
2001-11-07 08:59:30 +00:00
/* Don't do voiding here, it should be done by xaccTransVoid */
2001-11-07 09:56:15 +00:00
static char possible_chars[] = { NREC, CREC, YREC, FREC };
2001-08-18 00:13:45 +00:00
Split*
2005-11-02 03:32:36 +00:00
get_random_split(QofBook *book, Account *acct, Transaction *trn)
2001-08-18 00:13:45 +00:00
{
Split *ret;
2014-12-03 10:29:55 -08:00
gnc_numeric amt = {0, 1}, val = {0, 1}, rate = {0, 0};
const gchar *str;
gnc_commodity *com;
int scu, denom;
time64 time;
com = xaccTransGetCurrency (trn);
scu = gnc_commodity_get_fraction(com);
2001-10-05 08:35:04 +00:00
2001-11-24 12:10:42 +00:00
ret = xaccMallocSplit(book);
2001-08-18 00:13:45 +00:00
str = get_random_string_in_array(sane_descriptions);
xaccSplitSetMemo(ret, str);
str = get_random_string_in_array(sane_actions);
xaccSplitSetAction(ret, str);
2001-08-18 00:13:45 +00:00
2001-11-07 08:59:30 +00:00
xaccSplitSetReconcile(ret, possible_chars[get_random_int_in_range(0, 3)]);
2001-08-18 00:13:45 +00:00
time = get_random_time();
xaccSplitSetDateReconciledSecs (ret, time);
2001-08-18 00:13:45 +00:00
/* Split must be in an account before we can set an amount */
2005-11-02 03:32:36 +00:00
/* and in a transaction before it can be added to an account. */
xaccTransBeginEdit(trn);
xaccSplitSetParent(ret, trn);
xaccSplitSetAccount(ret, acct);
2001-08-18 00:13:45 +00:00
2010-02-18 05:31:54 +00:00
do
{
val = get_random_gnc_numeric (scu);
if (val.num == 0)
fprintf(stderr, "get_random_split: Created split with zero value: %p\n", ret);
2010-02-18 05:31:54 +00:00
if (!do_bork())
/* Another overflow-prevention measure. A numerator near the overflow limit can
* be made too large by replacing the denominator with a smaller scu.
*/
{
2014-12-03 10:29:55 -08:00
if (val.denom > scu && val.num > num_limit / (max_denom_mult / scu))
{
int64_t new_num = val.num / (val.denom / scu);
g_log("test.engine.suff", G_LOG_LEVEL_DEBUG,
"Adjusting val.denom from %" PRIu64 " to %" PRIu64 "\n",
val.num, new_num);
2014-12-03 10:29:55 -08:00
val.num = new_num;
}
val.denom = scu;
}
2010-02-18 05:31:54 +00:00
}
while (gnc_numeric_check(val) != GNC_ERROR_OK);
g_log ("test.engine.suff", G_LOG_LEVEL_DEBUG,
"Random split value: %" PRIu64 " / %" PRIu64 ", scu %d\n",
val.num, val.denom, scu);
xaccSplitSetValue(ret, val);
/* If the currencies are the same, the split amount should equal
* the split value (unless we bork it on purpose) */
2014-12-03 10:29:55 -08:00
denom = gnc_commodity_get_fraction(xaccAccountGetCommodity(
xaccSplitGetAccount(ret)));
2010-02-18 05:31:54 +00:00
if (gnc_commodity_equal (xaccTransGetCurrency(trn),
xaccAccountGetCommodity(acct)) &&
2010-02-18 05:31:54 +00:00
(!do_bork()))
{
amt = val;
2010-02-18 05:31:54 +00:00
}
else
{
do
{
rate = get_random_rate ();
amt = gnc_numeric_div(val, rate, denom, GNC_HOW_RND_ROUND_HALF_UP);
2010-02-18 05:31:54 +00:00
}
while (gnc_numeric_check(amt) != GNC_ERROR_OK);
}
g_log ("test.engine.suff", G_LOG_LEVEL_DEBUG, "Random split amount: %"
PRIu64 " / %" PRIu64 ", rate %" PRIu64 " / %" PRIu64 "\n",
amt.num, amt.denom, rate.num, rate.denom);
2014-12-03 10:29:55 -08:00
xaccSplitSetAmount(ret, amt);
/* Make sure val and amt have the same sign. Note that amt is
also allowed to be zero, because that is caused by a small
rate. */
if (gnc_numeric_positive_p(val))
g_assert_true(!gnc_numeric_negative_p(amt)); /* non-negative amt */
else
g_assert_true(!gnc_numeric_positive_p(amt)); /* non-positive amt */
2010-02-18 05:31:54 +00:00
// g_assert_true(amt.num < (2LL << 56));
qof_instance_set_slots(QOF_INSTANCE (ret), get_random_kvp_frame());
xaccTransCommitEdit(trn);
2001-08-18 00:13:45 +00:00
return ret;
}
2001-10-16 00:50:01 +00:00
void
make_random_changes_to_split (Split *split)
{
2010-02-18 05:31:54 +00:00
Transaction *trans;
time64 time;
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
g_return_if_fail (split);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
trans = xaccSplitGetParent (split);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
xaccTransBeginEdit (trans);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
set_split_random_string (split, xaccSplitSetMemo);
set_split_random_string (split, xaccSplitSetAction);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
xaccSplitSetReconcile (split, possible_chars[get_random_int_in_range(0, 3)]);
2001-10-16 09:13:07 +00:00
time = get_random_time();
xaccSplitSetDateReconciledSecs (split, time);
2001-10-16 09:13:07 +00:00
qof_instance_set_slots (QOF_INSTANCE (split), get_random_kvp_frame());
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
/* Don't change share values/prices here, since that would
* throw transactions out of balance. Do that in the corresponding
* change transaction function. */
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
xaccTransCommitEdit (trans);
2001-10-16 00:50:01 +00:00
}
2001-08-18 00:13:45 +00:00
static void
set_tran_random_string(Transaction* trn,
void(*func)(Transaction *act, const gchar*str))
{
gchar *tmp_str = get_random_string();
2015-06-26 12:24:34 -07:00
if (!trn)
2010-02-18 05:31:54 +00:00
{
return;
}
if (tmp_str)
2001-08-18 00:13:45 +00:00
{
2005-11-02 03:32:36 +00:00
xaccTransBeginEdit(trn);
2001-08-18 00:13:45 +00:00
(func)(trn, tmp_str);
g_free(tmp_str);
2005-11-02 03:32:36 +00:00
xaccTransCommitEdit(trn);
2001-08-18 00:13:45 +00:00
}
}
static void
set_tran_random_string_from_array(
Transaction* trn, void(*func)(Transaction *trn, const gchar*str),
const gchar *list[])
{
const gchar *tmp_str = get_random_string_in_array(list);
2010-02-18 05:31:54 +00:00
if (tmp_str)
(func)(trn, tmp_str);
}
2001-08-18 00:13:45 +00:00
static void
2017-12-31 07:37:02 -05:00
trn_add_ran_time (Transaction *trn, void (*func)(Transaction*, time64))
2001-08-18 00:13:45 +00:00
{
2017-12-31 07:37:02 -05:00
func(trn, get_random_time());
2001-08-18 00:13:45 +00:00
}
2010-02-18 05:31:54 +00:00
2001-10-13 08:09:30 +00:00
Transaction *
get_random_transaction_with_currency(QofBook *book,
gnc_commodity *currency,
GList *account_list)
2001-08-18 00:13:45 +00:00
{
Transaction* trans;
2004-09-22 18:40:10 +00:00
KvpFrame *f;
2001-08-18 00:13:45 +00:00
2010-02-18 05:31:54 +00:00
if (!account_list)
{
2010-02-18 05:31:54 +00:00
account_list = gnc_account_get_descendants (gnc_book_get_root_account (book));
}
2004-07-09 03:07:34 +00:00
/* Gotta have at least two different accounts */
2010-02-18 05:31:54 +00:00
if (1 >= g_list_length (account_list))
{
failure_args("engine-stuff", __FILE__, __LINE__,
"get_random_transaction_with_currency: account_list too short");
return NULL;
}
2004-07-09 03:07:34 +00:00
trans = xaccMallocTransaction(book);
2001-08-18 00:13:45 +00:00
xaccTransBeginEdit(trans);
2005-11-02 03:32:36 +00:00
xaccTransSetCurrency (trans,
2001-10-13 08:09:30 +00:00
currency ? currency :
2001-11-24 12:10:42 +00:00
get_random_commodity (book));
2001-08-18 00:13:45 +00:00
gint num = get_random_int_in_range (1, max_trans_num);
auto numstr = std::to_string(num);
xaccTransSetNum(trans, numstr.c_str());
set_tran_random_string_from_array(trans, xaccTransSetDescription,
sane_descriptions);
2017-12-31 07:37:02 -05:00
trn_add_ran_time(trans, xaccTransSetDatePostedSecs);
trn_add_ran_time(trans, xaccTransSetDateEnteredSecs);
2001-08-18 00:13:45 +00:00
2004-09-22 18:40:10 +00:00
f = get_random_kvp_frame();
2015-06-09 16:19:41 -07:00
qof_instance_set_slots (QOF_INSTANCE (trans), f);
2001-08-18 00:13:45 +00:00
add_random_splits(book, trans, account_list);
2001-08-18 00:13:45 +00:00
2001-11-07 08:59:30 +00:00
if (get_random_int_in_range (1, 10) == 1)
{
2010-02-18 05:31:54 +00:00
char *reason = get_random_string ();
xaccTransVoid (trans, reason);
g_free (reason);
2001-11-07 08:59:30 +00:00
}
xaccTransCommitEdit(trans);
if (!trans)
2005-11-02 03:32:36 +00:00
{
failure_args("engine-stuff", __FILE__, __LINE__,
"get_random_transaction_with_currency failed");
return NULL;
}
return trans;
2001-08-18 00:13:45 +00:00
}
2001-10-13 08:09:30 +00:00
Transaction*
get_random_transaction (QofBook *book)
2001-10-13 08:09:30 +00:00
{
2010-02-18 05:31:54 +00:00
Transaction *ret;
2005-11-02 03:32:36 +00:00
2010-02-18 05:31:54 +00:00
g_return_val_if_fail(book, NULL);
ret = get_random_transaction_with_currency (book, NULL, NULL);
if (!ret)
2005-11-02 03:32:36 +00:00
{
failure_args("engine-stuff", __FILE__, __LINE__,
"get_random_transaction failed");
return NULL;
}
2010-02-18 05:31:54 +00:00
return ret;
2001-10-13 08:09:30 +00:00
}
2001-10-16 09:13:07 +00:00
void
make_random_changes_to_transaction (QofBook *book, Transaction *trans)
2001-10-16 09:13:07 +00:00
{
2010-02-18 05:31:54 +00:00
g_return_if_fail (trans && book);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
if (xaccTransGetVoidStatus (trans))
{
if (get_random_int_in_range (1, 2) == 1)
xaccTransUnvoid (trans);
return;
}
2001-11-07 08:59:30 +00:00
2010-02-18 05:31:54 +00:00
xaccTransBeginEdit (trans);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
xaccTransSetCurrency (trans, get_random_commodity (book));
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
set_tran_random_string (trans, xaccTransSetNum);
2001-10-16 09:13:07 +00:00
2017-12-31 07:37:02 -05:00
trn_add_ran_time (trans, xaccTransSetDatePostedSecs);
trn_add_ran_time (trans, xaccTransSetDateEnteredSecs);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
set_tran_random_string (trans, xaccTransSetDescription);
2001-10-16 09:13:07 +00:00
2015-06-09 16:19:41 -07:00
qof_instance_set_slots (QOF_INSTANCE (trans), get_random_kvp_frame());
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
/* Do split manipulations in higher-level functions */
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
xaccTransCommitEdit (trans);
2001-10-16 09:13:07 +00:00
}
2001-08-18 00:13:45 +00:00
static GList *
get_random_guids(int max)
{
2010-02-18 05:31:54 +00:00
GList *guids = NULL;
int num_guids;
2001-08-18 00:13:45 +00:00
2010-02-18 05:31:54 +00:00
if (max < 1) return NULL;
2001-08-18 00:13:45 +00:00
2010-02-18 05:31:54 +00:00
num_guids = get_random_int_in_range (1, max);
2001-08-18 00:13:45 +00:00
2010-02-18 05:31:54 +00:00
while (num_guids-- > 0)
guids = g_list_prepend (guids, get_random_guid ());
2001-08-18 00:13:45 +00:00
2010-02-18 05:31:54 +00:00
return guids;
2001-08-18 00:13:45 +00:00
}
static void
free_guids(GList *guids)
2001-08-18 00:13:45 +00:00
{
g_list_free_full (guids, (GDestroyNotify)guid_free);
2001-08-18 00:13:45 +00:00
}
2003-06-25 07:38:15 +00:00
static QofQueryOp
2001-08-18 00:13:45 +00:00
get_random_queryop(void)
{
2010-02-18 05:31:54 +00:00
int op_num = get_random_int_in_range(1, 11);
QofQueryOp op = QOF_QUERY_AND;
/* = get_random_int_in_range (1, QOF_QUERY_XOR); */
2010-02-18 05:31:54 +00:00
/* Let's make it MUCH more likely to get AND and OR */
switch (op_num)
{
case 1:
case 2:
case 3:
case 4:
op = QOF_QUERY_AND;
break;
case 5:
case 6:
case 7:
case 8:
op = QOF_QUERY_OR;
break;
case 9:
op = QOF_QUERY_NAND;
break;
case 10:
op = QOF_QUERY_NOR;
break;
case 11:
op = QOF_QUERY_XOR;
break;
default:
g_assert_not_reached();
break;
2010-02-18 05:31:54 +00:00
};
if (gnc_engine_debug_random) printf ("op = %d (int was %d), ", op, op_num);
return op;
2001-08-18 00:13:45 +00:00
}
static QofIdType
2001-11-14 09:07:49 +00:00
get_random_id_type (void)
{
2010-02-18 05:31:54 +00:00
switch (get_random_int_in_range (1, 3))
{
case 1:
return GNC_ID_SPLIT;
case 2:
return GNC_ID_TRANS;
case 3:
return GNC_ID_ACCOUNT;
default:
2014-03-29 16:02:50 -07:00
return get_random_string ();
2010-02-18 05:31:54 +00:00
}
}
typedef enum
{
BY_STANDARD = 1,
BY_DATE,
BY_DATE_ENTERED,
BY_DATE_RECONCILED,
BY_NUM,
BY_AMOUNT,
BY_MEMO,
BY_DESC,
BY_NONE
} sort_type_t;
static void
set_query_sort (QofQuery *q, sort_type_t sort_code)
{
2010-02-18 05:31:54 +00:00
GSList *p1 = NULL, *p2 = NULL, *p3 = NULL, *standard;
2015-06-26 12:24:34 -07:00
standard = g_slist_prepend (NULL, const_cast<char*>(QUERY_DEFAULT_SORT));
2010-02-18 05:31:54 +00:00
switch (sort_code)
{
case BY_STANDARD:
2010-02-18 05:31:54 +00:00
p1 = standard;
break;
case BY_DATE:
2015-06-26 12:24:34 -07:00
p1 = g_slist_prepend (p1, const_cast<char*>(TRANS_DATE_POSTED));
p1 = g_slist_prepend (p1, const_cast<char*>(SPLIT_TRANS));
2010-02-18 05:31:54 +00:00
p2 = standard;
break;
case BY_DATE_ENTERED:
2015-06-26 12:24:34 -07:00
p1 = g_slist_prepend (p1, const_cast<char*>(TRANS_DATE_ENTERED));
p1 = g_slist_prepend (p1, const_cast<char*>(SPLIT_TRANS));
2010-02-18 05:31:54 +00:00
p2 = standard;
break;
case BY_DATE_RECONCILED:
2015-06-26 12:24:34 -07:00
p1 = g_slist_prepend (p1, const_cast<char*>(SPLIT_RECONCILE));
p2 = g_slist_prepend (p2, const_cast<char*>(SPLIT_DATE_RECONCILED));
2010-02-18 05:31:54 +00:00
p3 = standard;
break;
case BY_NUM:
2015-06-26 12:24:34 -07:00
p1 = g_slist_prepend (p1, const_cast<char*>(TRANS_NUM));
p1 = g_slist_prepend (p1, const_cast<char*>(SPLIT_TRANS));
2010-02-18 05:31:54 +00:00
p2 = standard;
break;
case BY_AMOUNT:
2015-06-26 12:24:34 -07:00
p1 = g_slist_prepend (p1, const_cast<char*>(SPLIT_VALUE));
2010-02-18 05:31:54 +00:00
p2 = standard;
break;
case BY_MEMO:
2015-06-26 12:24:34 -07:00
p1 = g_slist_prepend (p1, const_cast<char*>(SPLIT_MEMO));
2010-02-18 05:31:54 +00:00
p2 = standard;
break;
case BY_DESC:
2015-06-26 12:24:34 -07:00
p1 = g_slist_prepend (p1, const_cast<char*>(TRANS_DESCRIPTION));
p1 = g_slist_prepend (p1, const_cast<char*>(SPLIT_TRANS));
2010-02-18 05:31:54 +00:00
p2 = standard;
break;
case BY_NONE:
2010-02-18 05:31:54 +00:00
g_slist_free (standard);
break;
default:
2010-02-18 05:31:54 +00:00
g_slist_free (standard);
g_return_if_fail (FALSE);
break;
2010-02-18 05:31:54 +00:00
}
2010-02-18 05:31:54 +00:00
qof_query_set_sort_order (q, p1, p2, p3);
2001-11-14 09:07:49 +00:00
}
2015-06-26 12:24:34 -07:00
template <typename T> inline T
compare_param(int max)
{
auto ret = get_random_int_in_range (max == 1 ? 0 : 1, max);
return static_cast<T>(ret);
}
QofQuery *
2001-08-18 00:13:45 +00:00
get_random_query(void)
{
QofQuery *q;
2010-02-18 05:31:54 +00:00
int num_terms;
2001-08-18 00:13:45 +00:00
2010-02-18 05:31:54 +00:00
num_terms = get_random_int_in_range (1, 3);
if (gnc_engine_debug_random) printf("num_terms = %d", num_terms);
2001-08-18 00:13:45 +00:00
q = qof_query_create_for(GNC_ID_SPLIT);
2001-08-18 00:13:45 +00:00
2010-02-18 05:31:54 +00:00
while (num_terms-- > 0)
2001-08-18 00:13:45 +00:00
{
2010-02-18 05:31:54 +00:00
gint pr_type;
time64 start;
time64 end;
2010-02-18 05:31:54 +00:00
GList *guids;
char *string;
GncGUID *guid;
2001-08-18 00:13:45 +00:00
2010-02-18 05:31:54 +00:00
pr_type = get_random_int_in_range (1, 20);
if (gnc_engine_debug_random) printf("\n pr_type = %d ", pr_type);
2001-08-18 00:13:45 +00:00
2010-02-18 05:31:54 +00:00
switch (pr_type)
2001-11-06 00:49:46 +00:00
{
2010-02-18 05:31:54 +00:00
case 1: /*PR_ACCOUNT */
guids = get_random_guids (10);
xaccQueryAddAccountGUIDMatch
(q,
guids,
2015-06-26 12:24:34 -07:00
compare_param<QofGuidMatch>(QOF_GUID_MATCH_NONE),
2010-02-18 05:31:54 +00:00
get_random_queryop ());
free_guids (guids);
2010-02-18 05:31:54 +00:00
break;
case 2: /*PR_ACTION */
2014-03-29 16:02:50 -07:00
string = get_random_string_without ("\\");
2010-02-18 05:31:54 +00:00
xaccQueryAddActionMatch (q,
string,
get_random_boolean (),
get_random_boolean (),
2015-06-26 12:24:34 -07:00
compare_param<QofQueryCompare>(QOF_COMPARE_CONTAINS),
2010-02-18 05:31:54 +00:00
get_random_queryop ());
g_free (string);
break;
case 3: /* PR_BALANCE */
xaccQueryAddBalanceMatch
(q,
2015-06-26 12:24:34 -07:00
compare_param<QofQueryCompare> (1),
2010-02-18 05:31:54 +00:00
get_random_queryop ());
break;
case 4: /* PR_CLEARED */
xaccQueryAddClearedMatch
(q,
2015-06-26 12:24:34 -07:00
static_cast<cleared_match_t>(get_random_int_in_range (1,
2010-02-18 05:31:54 +00:00
CLEARED_NO |
CLEARED_CLEARED |
CLEARED_RECONCILED |
CLEARED_FROZEN |
2015-06-26 12:24:34 -07:00
CLEARED_VOIDED)),
2010-02-18 05:31:54 +00:00
get_random_queryop ());
break;
case 5: /* PR_DATE */
start = get_random_time ();
end = get_random_time ();
xaccQueryAddDateMatchTT (q,
2010-02-18 05:31:54 +00:00
get_random_boolean (),
start,
2010-02-18 05:31:54 +00:00
get_random_boolean (),
end,
2010-02-18 05:31:54 +00:00
get_random_queryop ());
break;
case 6: /* PR_DESC */
2014-03-29 16:02:50 -07:00
string = get_random_string_without ("\\");
2010-02-18 05:31:54 +00:00
xaccQueryAddDescriptionMatch (q,
string,
get_random_boolean (),
get_random_boolean (),
2015-06-26 12:24:34 -07:00
compare_param<QofQueryCompare>(QOF_COMPARE_CONTAINS),
2010-02-18 05:31:54 +00:00
get_random_queryop ());
g_free (string);
break;
case 7: /* PR_GUID */
guid = get_random_guid ();
xaccQueryAddGUIDMatch (q,
guid,
get_random_id_type (),
get_random_queryop ());
guid_free (guid);
2010-02-18 05:31:54 +00:00
break;
case 8: /* PR_KVP */
break;
case 9: /* PR_MEMO */
2014-03-29 16:02:50 -07:00
string = get_random_string_without ("\\");
2010-02-18 05:31:54 +00:00
xaccQueryAddMemoMatch (q,
string,
get_random_boolean (),
get_random_boolean (),
2015-06-26 12:24:34 -07:00
compare_param<QofQueryCompare>(QOF_COMPARE_CONTAINS),
2010-02-18 05:31:54 +00:00
get_random_queryop ());
g_free (string);
break;
case 10: /* PR_NUM */
2014-03-29 16:02:50 -07:00
string = get_random_string_without ("\\");
2010-02-18 05:31:54 +00:00
xaccQueryAddNumberMatch (q,
string,
get_random_boolean (),
get_random_boolean (),
2015-06-26 12:24:34 -07:00
compare_param<QofQueryCompare>(QOF_COMPARE_CONTAINS),
2010-02-18 05:31:54 +00:00
get_random_queryop ());
g_free (string);
break;
case 11: /* PR_PRICE */
xaccQueryAddSharePriceMatch
(q,
get_random_gnc_numeric (GNC_DENOM_AUTO),
2015-06-26 12:24:34 -07:00
compare_param<QofQueryCompare> (QOF_COMPARE_NEQ),
2010-02-18 05:31:54 +00:00
get_random_queryop ());
break;
case 12: /* PR_SHRS */
xaccQueryAddSharesMatch
(q,
get_random_gnc_numeric (GNC_DENOM_AUTO),
2015-06-26 12:24:34 -07:00
compare_param<QofQueryCompare> (QOF_COMPARE_NEQ),
2010-02-18 05:31:54 +00:00
get_random_queryop ());
break;
case 13: /* PR_VALUE */
xaccQueryAddValueMatch
(q,
get_random_gnc_numeric (GNC_DENOM_AUTO),
2015-06-26 12:24:34 -07:00
compare_param<QofNumericMatch> (QOF_NUMERIC_MATCH_ANY),
compare_param<QofQueryCompare> (QOF_COMPARE_NEQ),
2010-02-18 05:31:54 +00:00
get_random_queryop ());
break;
2001-08-18 00:13:45 +00:00
2010-02-18 05:31:54 +00:00
default:
if (gnc_engine_debug_random) printf("ignored..");
num_terms++;
break;
}
2001-08-18 00:13:45 +00:00
}
2010-02-18 05:31:54 +00:00
if (gnc_engine_debug_random) printf ("\n");
2015-06-26 12:24:34 -07:00
set_query_sort (q, compare_param<sort_type_t>(BY_NONE));
2001-08-18 00:13:45 +00:00
qof_query_set_sort_increasing (q,
2010-12-04 21:09:57 +00:00
get_random_boolean (),
get_random_boolean (),
get_random_boolean ());
2001-08-18 00:13:45 +00:00
qof_query_set_max_results (q, get_random_int_in_range (-50000, 50000));
2001-08-18 00:13:45 +00:00
2010-02-18 05:31:54 +00:00
return q;
2001-08-18 00:13:45 +00:00
}
QofBook *
2001-11-24 12:10:42 +00:00
get_random_book (void)
2001-09-28 00:15:49 +00:00
{
2010-02-18 05:31:54 +00:00
QofBook *book;
2001-09-28 00:15:49 +00:00
2010-02-18 05:31:54 +00:00
book = qof_book_new ();
2001-09-28 00:15:49 +00:00
2010-02-18 05:31:54 +00:00
get_random_account_tree (book);
get_random_pricedb (book);
2001-09-28 00:15:49 +00:00
2010-02-18 05:31:54 +00:00
return book;
2001-09-28 00:15:49 +00:00
}
2001-10-12 10:50:35 +00:00
QofSession *
2001-10-12 10:50:35 +00:00
get_random_session (void)
{
2018-06-17 16:43:33 -07:00
auto book = qof_book_new ();
auto session = qof_session_new (book);
2001-10-12 10:50:35 +00:00
2010-02-18 05:31:54 +00:00
get_random_account_tree (book);
get_random_pricedb (book);
2010-02-18 05:31:54 +00:00
return session;
2001-10-12 10:50:35 +00:00
}
2001-10-13 08:09:30 +00:00
void
add_random_transactions_to_book (QofBook *book, gint num_transactions)
2001-10-13 08:09:30 +00:00
{
2010-02-18 05:31:54 +00:00
gnc_commodity_table *table;
GList *accounts;
2001-10-13 08:09:30 +00:00
2010-02-18 05:31:54 +00:00
if (num_transactions <= 0) return;
2001-10-13 08:09:30 +00:00
2010-02-18 05:31:54 +00:00
g_return_if_fail (book);
2001-10-13 08:09:30 +00:00
2010-02-18 05:31:54 +00:00
accounts = gnc_account_get_descendants (gnc_book_get_root_account (book));
g_return_if_fail (accounts);
2001-10-13 08:09:30 +00:00
2010-02-18 05:31:54 +00:00
table = gnc_commodity_table_get_table (book);
2001-10-13 08:09:30 +00:00
2010-02-18 05:31:54 +00:00
while (num_transactions--)
{
gnc_commodity *com;
2001-10-13 08:09:30 +00:00
2010-02-18 05:31:54 +00:00
com = get_random_commodity_from_table (table);
2012-05-26 23:47:34 +00:00
get_random_transaction_with_currency (book, com, accounts);
2010-02-18 05:31:54 +00:00
}
g_list_free (accounts);
2001-10-13 08:09:30 +00:00
}
2001-10-16 09:13:07 +00:00
void
make_random_changes_to_book (QofBook *book)
2001-10-16 09:13:07 +00:00
{
2010-02-18 05:31:54 +00:00
g_return_if_fail (book);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
make_random_changes_to_level (book, gnc_book_get_root_account (book));
make_random_changes_to_pricedb (book, gnc_pricedb_get_db (book));
#if 0
2010-02-18 05:31:54 +00:00
make_random_changes_to_commodity_table (gnc_commodity_table_get_table (book));
#endif
2001-10-16 09:13:07 +00:00
}
void
make_random_changes_to_session (QofSession *session)
2001-10-16 09:13:07 +00:00
{
2010-02-18 05:31:54 +00:00
g_return_if_fail (session);
2001-10-16 09:13:07 +00:00
2010-02-18 05:31:54 +00:00
make_random_changes_to_book (qof_session_get_book (session));
2001-10-16 09:13:07 +00:00
}
typedef struct
{
2010-02-18 05:31:54 +00:00
QofIdType where;
GSList *path;
QofQuery *q;
} KVPQueryData;
static gboolean include_price = TRUE;
void
trans_query_include_price (gboolean include_price_in)
{
2010-02-18 05:31:54 +00:00
include_price = include_price_in;
}
2001-11-09 11:50:29 +00:00
TestQueryTypes
get_random_query_type (void)
{
2015-05-23 15:05:52 -07:00
switch (get_random_int_in_range (0, 1))
2010-02-18 05:31:54 +00:00
{
case 0:
return SIMPLE_QT;
case 1:
return GUID_QT;
default:
return SIMPLE_QT;
}
2001-11-09 11:50:29 +00:00
}
QofQuery *
2001-11-09 11:50:29 +00:00
make_trans_query (Transaction *trans, TestQueryTypes query_types)
{
2010-02-18 05:31:54 +00:00
Account *a;
gnc_numeric n;
QofQuery *q;
2010-02-18 05:31:54 +00:00
Split *s;
2001-11-09 11:50:29 +00:00
2010-02-18 05:31:54 +00:00
if (query_types == RANDOM_QT)
query_types = get_random_query_type ();
q = qof_query_create_for(GNC_ID_SPLIT);
2010-02-18 05:31:54 +00:00
s = xaccTransGetSplit (trans, 0);
a = xaccSplitGetAccount (s);
2010-02-18 05:31:54 +00:00
if (query_types & SIMPLE_QT)
2006-05-07 21:39:30 +00:00
{
2010-02-18 05:31:54 +00:00
xaccQueryAddSingleAccountMatch (q, xaccSplitGetAccount (s), QOF_QUERY_AND);
2010-02-18 05:31:54 +00:00
if (xaccTransGetDescription(trans) && *xaccTransGetDescription(trans) != '\0')
{
xaccQueryAddDescriptionMatch (q, xaccTransGetDescription (trans),
TRUE, FALSE, QOF_COMPARE_CONTAINS, QOF_QUERY_AND);
2010-02-18 05:31:54 +00:00
}
2010-02-18 05:31:54 +00:00
if (xaccTransGetNum(trans) && *xaccTransGetNum(trans) != '\0')
{
xaccQueryAddNumberMatch (q, xaccTransGetNum (trans),
TRUE, FALSE, QOF_COMPARE_CONTAINS, QOF_QUERY_AND);
2010-02-18 05:31:54 +00:00
}
2010-02-18 05:31:54 +00:00
if (xaccSplitGetAction(s) && *xaccSplitGetAction(s) != '\0')
{
xaccQueryAddActionMatch (q, xaccSplitGetAction (s),
TRUE, FALSE, QOF_COMPARE_CONTAINS, QOF_QUERY_AND);
2010-02-18 05:31:54 +00:00
}
2010-02-18 05:31:54 +00:00
n = xaccSplitGetValue (s);
xaccQueryAddValueMatch (q, n, QOF_NUMERIC_MATCH_ANY,
QOF_COMPARE_EQUAL, QOF_QUERY_AND);
2010-02-18 05:31:54 +00:00
n = xaccSplitGetAmount (s);
xaccQueryAddSharesMatch (q, n, QOF_COMPARE_EQUAL, QOF_QUERY_AND);
2010-02-18 05:31:54 +00:00
if (include_price)
{
n = xaccSplitGetSharePrice (s);
xaccQueryAddSharePriceMatch (q, n, QOF_COMPARE_EQUAL, QOF_QUERY_AND);
}
2010-02-18 05:31:54 +00:00
{
2017-12-31 07:37:02 -05:00
time64 time = xaccTransRetDatePosted (trans);
xaccQueryAddDateMatchTT (q, TRUE, time, TRUE, time, QOF_QUERY_AND);
2010-02-18 05:31:54 +00:00
}
2001-11-09 11:50:29 +00:00
2010-02-18 05:31:54 +00:00
if (xaccSplitGetMemo(s) && *xaccSplitGetMemo(s) != '\0')
{
xaccQueryAddMemoMatch (q, xaccSplitGetMemo (s), TRUE, FALSE, QOF_COMPARE_CONTAINS, QOF_QUERY_AND);
2010-02-18 05:31:54 +00:00
}
2001-11-09 11:50:29 +00:00
2010-02-18 05:31:54 +00:00
{
cleared_match_t how;
switch (xaccSplitGetReconcile (s))
{
case NREC:
how = CLEARED_NO;
break;
case CREC:
how = CLEARED_CLEARED;
break;
case YREC:
how = CLEARED_RECONCILED;
break;
case FREC:
how = CLEARED_FROZEN;
break;
case VREC:
how = CLEARED_VOIDED;
break;
default:
failure ("bad reconcile flag");
qof_query_destroy (q);
2010-02-18 05:31:54 +00:00
return NULL;
}
xaccQueryAddClearedMatch (q, how, QOF_QUERY_AND);
}
}
2010-02-18 05:31:54 +00:00
if (query_types & ACCOUNT_QT)
{
2010-02-18 05:31:54 +00:00
GList * list;
GList * node;
/* QOF_GUID_MATCH_ALL */
2010-02-18 05:31:54 +00:00
list = NULL;
for (node = xaccTransGetSplitList (trans); node; node = node->next)
{
2015-06-26 12:24:34 -07:00
auto split = static_cast<Split * >(node->data);
2010-02-18 05:31:54 +00:00
list = g_list_prepend (list, xaccSplitGetAccount (split));
}
xaccQueryAddAccountMatch (q, list, QOF_GUID_MATCH_ALL, QOF_QUERY_AND);
g_list_free (list);
/* QOF_GUID_MATCH_NONE */
2010-02-18 05:31:54 +00:00
list = NULL;
list = g_list_prepend (list, get_random_guid ());
list = g_list_prepend (list, get_random_guid ());
list = g_list_prepend (list, get_random_guid ());
xaccQueryAddAccountGUIDMatch (q, list, QOF_GUID_MATCH_NONE, QOF_QUERY_AND);
/* QOF_GUID_MATCH_ANY */
2010-02-18 05:31:54 +00:00
{
GncGUID * guid = get_random_guid ();
2010-02-18 05:31:54 +00:00
*guid = *xaccAccountGetGUID (a);
list = g_list_prepend (list, guid);
}
xaccQueryAddAccountGUIDMatch (q, list, QOF_GUID_MATCH_ANY, QOF_QUERY_AND);
free_guids (list);
2010-02-18 05:31:54 +00:00
}
2001-11-14 09:07:49 +00:00
2010-02-18 05:31:54 +00:00
if (query_types & GUID_QT)
{
xaccQueryAddGUIDMatch (q, xaccSplitGetGUID (s),
GNC_ID_SPLIT, QOF_QUERY_AND);
2001-11-14 09:07:49 +00:00
2010-02-18 05:31:54 +00:00
xaccQueryAddGUIDMatch (q, xaccTransGetGUID (trans),
GNC_ID_TRANS, QOF_QUERY_AND);
2010-02-18 05:31:54 +00:00
xaccQueryAddGUIDMatch (q, xaccAccountGetGUID (a),
GNC_ID_ACCOUNT, QOF_QUERY_AND);
}
return q;
}
static Recurrence*
2010-08-22 11:10:12 +00:00
daily_freq(const GDate* start, int multiplier)
{
2010-02-18 05:31:54 +00:00
Recurrence *r = g_new0(Recurrence, 1);
recurrenceSet(r, multiplier, PERIOD_DAY, start, WEEKEND_ADJ_NONE);
return r;
}
static Recurrence*
2010-08-22 11:10:12 +00:00
once_freq(const GDate *when)
{
2010-02-18 05:31:54 +00:00
Recurrence *r = g_new0(Recurrence, 1);
recurrenceSet(r, 1, PERIOD_ONCE, when, WEEKEND_ADJ_NONE);
return r;
}
static SchedXaction*
2015-09-24 11:28:07 -07:00
add_sx(const gchar *name, const GDate *start, const GDate *end,
const GDate *last_occur, Recurrence *r)
{
2010-02-18 05:31:54 +00:00
QofBook *book = qof_session_get_book(gnc_get_current_session());
SchedXaction *sx = xaccSchedXactionMalloc(book);
xaccSchedXactionSetName(sx, name);
xaccSchedXactionSetStartDate(sx, start);
if (end != NULL)
xaccSchedXactionSetEndDate(sx, end);
if (last_occur != NULL)
xaccSchedXactionSetLastOccurDate(sx, last_occur);
{
GList *recurrences = NULL;
recurrences = g_list_append(recurrences, r);
gnc_sx_set_schedule(sx, recurrences);
}
2010-02-18 05:31:54 +00:00
gnc_sxes_add_sx(gnc_book_get_schedxactions(book), sx);
2010-02-18 05:31:54 +00:00
return sx;
}
SchedXaction*
2015-09-24 11:28:07 -07:00
add_daily_sx(const gchar *name, const GDate *start,
const GDate *end, const GDate *last_occur)
{
2010-02-18 05:31:54 +00:00
return add_sx(name, start, end, last_occur, daily_freq(start, 1));
}
SchedXaction*
2015-09-24 11:28:07 -07:00
add_once_sx(const gchar *name, const GDate *when)
{
2010-02-18 05:31:54 +00:00
return add_sx(name, when, NULL, NULL, once_freq(when));
}
void
remove_sx(SchedXaction *sx)
{
2010-02-18 05:31:54 +00:00
QofBook *book = qof_session_get_book(gnc_get_current_session());
SchedXactions *sxes = gnc_book_get_schedxactions(book);
gnc_sxes_del_sx(sxes, sx);
}