gnucash/libgnucash/backend/xml/test/test-xml-transaction.cpp
Geert Janssens 1238b9d8cd Prevent gcc from searching config.h in the current directory
This will avoid a ninja-build from picking up a config.h generated by the autotools build
(in the root build directory). Picking up the wrong config.h may lead to all kinds of
subtle issues if the autotools run was done with different options than the cmake run.
2017-10-26 14:05:17 +02:00

546 lines
16 KiB
C++

/***************************************************************************
* test-xml-transaction.c
*
* Fri Oct 7 21:26:59 2005
* Copyright 2005 Neil Williams
* linux@codehelp.co.uk
****************************************************************************/
/*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
extern "C"
{
#include <config.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <gnc-engine.h>
#include <cashobjects.h>
#include <TransLog.h>
#include <test-stuff.h>
#include <test-engine-stuff.h>
#include <unittest-support.h>
#include <AccountP.h>
#include <Transaction.h>
#include <TransactionP.h>
}
#include "../gnc-xml-helper.h"
#include "../gnc-xml.h"
#include "../sixtp-parsers.h"
#include "../sixtp-dom-parsers.h"
#include "../io-gncxml-gen.h"
#include "test-file-stuff.h"
static QofBook* book;
extern gboolean gnc_transaction_xml_v2_testing;
static xmlNodePtr
find_appropriate_node (xmlNodePtr node, Split* spl)
{
xmlNodePtr mark;
for (mark = node->xmlChildrenNode; mark; mark = mark->next)
{
gboolean account_guid_good = FALSE;
gboolean amount_good = FALSE;
xmlNodePtr mark2;
for (mark2 = mark->xmlChildrenNode; mark2; mark2 = mark2->next)
{
if (g_strcmp0 ((char*)mark2->name, "split:value") == 0)
{
gnc_numeric* num = dom_tree_to_gnc_numeric (mark2);
if (gnc_numeric_equal (*num, xaccSplitGetValue (spl)))
{
amount_good = TRUE;
}
g_free (num);
}
else if (g_strcmp0 ((char*)mark2->name, "split:account") == 0)
{
GncGUID* accid = dom_tree_to_guid (mark2);
Account* account = xaccSplitGetAccount (spl);
if (guid_equal (accid, xaccAccountGetGUID (account)))
{
account_guid_good = TRUE;
}
g_free (accid);
}
if (account_guid_good && amount_good)
{
return mark;
}
}
}
return NULL;
}
static const char*
equals_node_val_vs_split_internal (xmlNodePtr node, Split* spl)
{
xmlNodePtr mark;
for (mark = node->children; mark != NULL; mark = mark->next)
{
if (g_strcmp0 ((char*)mark->name, "split:id") == 0)
{
GncGUID* id = dom_tree_to_guid (mark);
if (!guid_equal (id, xaccSplitGetGUID (spl)))
{
g_free (id);
return "ids differ";
}
g_free (id);
}
else if (g_strcmp0 ((char*)mark->name, "split:memo") == 0)
{
char* memo = dom_tree_to_text (mark);
if (g_strcmp0 (memo, xaccSplitGetMemo (spl)) != 0)
{
g_free (memo);
return "memos differ";
}
g_free (memo);
}
else if (g_strcmp0 ((char*)mark->name, "split:reconciled-state") == 0)
{
char* rs = dom_tree_to_text (mark);
if (rs[0] != xaccSplitGetReconcile (spl))
{
g_free (rs);
return "states differ";
}
g_free (rs);
}
else if (g_strcmp0 ((char*)mark->name, "split:value") == 0)
{
gnc_numeric* num = dom_tree_to_gnc_numeric (mark);
gnc_numeric val = xaccSplitGetValue (spl);
if (!gnc_numeric_equal (*num, val))
{
g_free (num);
return g_strdup_printf ("values differ: %" G_GINT64_FORMAT "/%"
G_GINT64_FORMAT " v %" G_GINT64_FORMAT
"/%" G_GINT64_FORMAT,
(*num).num, (*num).denom,
val.num, val.denom);
}
g_free (num);
}
else if (g_strcmp0 ((char*)mark->name, "split:quantity") == 0)
{
gnc_numeric* num = dom_tree_to_gnc_numeric (mark);
gnc_numeric val = xaccSplitGetAmount (spl);
if (!gnc_numeric_equal (*num, val))
{
return g_strdup_printf ("quantities differ under _equal: %"
G_GINT64_FORMAT "/%" G_GINT64_FORMAT
" v %" G_GINT64_FORMAT "/%"
G_GINT64_FORMAT,
(*num).num, (*num).denom,
val.num, val.denom);
}
if (!gnc_numeric_equal (*num, val))
{
g_free (num);
return g_strdup_printf ("quantities differ: %" G_GINT64_FORMAT
"/%" G_GINT64_FORMAT " v %"
G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
(*num).num, (*num).denom,
val.num, val.denom);
}
g_free (num);
}
else if (g_strcmp0 ((char*)mark->name, "split:account") == 0)
{
GncGUID* id = dom_tree_to_guid (mark);
Account* account = xaccSplitGetAccount (spl);
if (!guid_equal (id, xaccAccountGetGUID (account)))
{
g_free (id);
return "accounts differ";
}
g_free (id);
}
}
return NULL;
}
static const char*
equals_node_val_vs_splits (xmlNodePtr node, const Transaction* trn)
{
xmlNodePtr spl_node;
Split* spl_mark;
int i;
g_return_val_if_fail (node, FALSE);
g_return_val_if_fail (node->xmlChildrenNode, FALSE);
for (i = 0, spl_mark = xaccTransGetSplit ((Transaction*)trn, i);
spl_mark;
i++, spl_mark = xaccTransGetSplit ((Transaction*)trn, i))
{
spl_node = find_appropriate_node (node, spl_mark);
if (!spl_node)
{
gchar guidstr[GUID_ENCODING_LENGTH + 1];
guid_to_string_buff (xaccSplitGetGUID (spl_mark), guidstr);
g_print ("Split GUID %s", guidstr);
return "no matching split found";
}
const char* msg = equals_node_val_vs_split_internal (spl_node, spl_mark);
if (msg != NULL)
{
return msg;
}
}
return NULL;
}
static const char*
node_and_transaction_equal (xmlNodePtr node, Transaction* trn)
{
xmlNodePtr mark;
while (g_strcmp0 ((char*)node->name, "text") == 0)
node = node->next;
if (!check_dom_tree_version (node, "2.0.0"))
{
return "version wrong. Not 2.0.0 or not there";
}
if (!node->name || g_strcmp0 ((char*)node->name, "gnc:transaction"))
{
return "Name of toplevel node is bad";
}
for (mark = node->xmlChildrenNode; mark; mark = mark->next)
{
if (g_strcmp0 ((char*)mark->name, "text") == 0)
{
}
else if (g_strcmp0 ((char*)mark->name, "trn:id") == 0)
{
if (!equals_node_val_vs_guid (mark, xaccTransGetGUID (trn)))
{
return "ids differ";
}
}
/* This test will fail for many splits where the transaction has
* splits in different commodities -- eg, buying or selling a
* stock. jralls 2010-11-02 */
else if (g_strcmp0 ((char*)mark->name, "trn:currency") == 0)
{
#if 0
if (!equals_node_val_vs_commodity (
mark, xaccTransGetCurrency (trn), xaccTransGetBook (trn)))
{
return g_strdup ("currencies differ");
}
#endif
}
else if (g_strcmp0 ((char*)mark->name, "trn:num") == 0)
{
if (!equals_node_val_vs_string (mark, xaccTransGetNum (trn)))
{
return "nums differ";
}
}
else if (g_strcmp0 ((char*)mark->name, "trn:date-posted") == 0)
{
if (!equals_node_val_vs_date (mark, xaccTransRetDatePostedTS (trn)))
{
return "posted dates differ";
}
}
else if (g_strcmp0 ((char*)mark->name, "trn:date-entered") == 0)
{
if (!equals_node_val_vs_date (mark, xaccTransRetDateEnteredTS (trn)))
{
return "entered dates differ";
}
}
else if (g_strcmp0 ((char*)mark->name, "trn:description") == 0)
{
if (!equals_node_val_vs_string (mark, xaccTransGetDescription (trn)))
{
return "descriptions differ";
}
}
else if (g_strcmp0 ((char*)mark->name, "trn:slots") == 0)
{
if (!equals_node_val_vs_kvp_frame (mark,
qof_instance_get_slots (QOF_INSTANCE (trn))))
{
return "slots differ";
}
}
else if (g_strcmp0 ((char*)mark->name, "trn:splits") == 0)
{
const char* msg = equals_node_val_vs_splits (mark, trn);
if (msg != NULL)
{
return msg;
}
}
else
{
return "unknown node";
}
}
return NULL;
}
static void
really_get_rid_of_transaction (Transaction* trn)
{
xaccTransBeginEdit (trn);
xaccTransDestroy (trn);
xaccTransCommitEdit (trn);
}
struct tran_data_struct
{
Transaction* trn;
Transaction* new_trn;
gnc_commodity* com;
int value;
};
typedef struct tran_data_struct tran_data;
static gboolean
test_add_transaction (const char* tag, gpointer globaldata, gpointer data)
{
Transaction* trans = static_cast<decltype (trans)> (data);
tran_data* gdata = static_cast<decltype (gdata)> (globaldata);
gboolean retval = TRUE;
xaccTransBeginEdit (trans);
xaccTransSetCurrency (trans, gdata->com);
xaccTransCommitEdit (trans);
if (!do_test_args (xaccTransEqual (gdata->trn, trans, TRUE, TRUE, TRUE, FALSE),
"gnc_transaction_sixtp_parser_create",
__FILE__, __LINE__,
"%d", gdata->value))
retval = FALSE;
gdata->new_trn = trans;
return retval;
}
static void
test_transaction (void)
{
int i;
for (i = 0; i < 50; i++)
{
Transaction* ran_trn;
xmlNodePtr test_node;
gnc_commodity* com, *new_com;
gchar* filename1;
int fd;
/* The next line exists for its side effect of creating the
* account tree. */
get_random_account_tree (book);
ran_trn = get_random_transaction (book);
new_com = get_random_commodity (book);
if (!ran_trn)
{
failure_args ("transaction_xml", __FILE__, __LINE__,
"get_random_transaction returned NULL");
return;
}
{
/* xaccAccountInsertSplit can reorder the splits. */
GList* list = g_list_copy (xaccTransGetSplitList (ran_trn));
GList* node = list;
for (; node; node = node->next)
{
Split* s = static_cast<decltype (s)> (node->data);
Account* a = xaccMallocAccount (book);
xaccAccountBeginEdit (a);
xaccAccountSetCommodity (a, new_com);
xaccAccountSetCommoditySCU (a, xaccSplitGetAmount (s).denom);
xaccAccountInsertSplit (a, s);
xaccAccountCommitEdit (a);
}
g_list_free (list);
}
com = xaccTransGetCurrency (ran_trn);
test_node = gnc_transaction_dom_tree_create (ran_trn);
if (!test_node)
{
failure_args ("transaction_xml", __FILE__, __LINE__,
"gnc_transaction_dom_tree_create returned NULL");
really_get_rid_of_transaction (ran_trn);
continue;
}
auto compare_msg = node_and_transaction_equal (test_node, ran_trn);
if (compare_msg != nullptr)
{
failure_args ("transaction_xml", __FILE__, __LINE__,
"node and transaction were not equal: %s",
compare_msg);
xmlElemDump (stdout, NULL, test_node);
printf ("\n");
fflush (stdout);
xmlFreeNode (test_node);
really_get_rid_of_transaction (ran_trn);
continue;
}
else
{
success_args ("transaction_xml", __FILE__, __LINE__, "%d", i);
}
filename1 = g_strdup_printf ("test_file_XXXXXX");
fd = g_mkstemp (filename1);
write_dom_node_to_file (test_node, fd);
close (fd);
{
GList* node = xaccTransGetSplitList (ran_trn);
for (; node; node = node->next)
{
Split* s = static_cast<decltype (s)> (node->data);
Account* a1 = xaccSplitGetAccount (s);
Account* a2 = xaccMallocAccount (book);
xaccAccountBeginEdit (a2);
xaccAccountSetCommoditySCU (a2, xaccAccountGetCommoditySCU (a1));
xaccAccountSetGUID (a2, xaccAccountGetGUID (a1));
xaccAccountCommitEdit (a2);
}
}
{
sixtp* parser;
tran_data data;
const char* msg =
"[xaccAccountScrubCommodity()] Account \"\" does not have a commodity!";
const char* logdomain = "gnc.engine.scrub";
GLogLevelFlags loglevel = static_cast<decltype (loglevel)>
(G_LOG_LEVEL_CRITICAL);
TestErrorStruct check = { loglevel, const_cast<char*> (logdomain),
const_cast<char*> (msg)
};
g_log_set_handler (logdomain, loglevel,
(GLogFunc)test_checked_handler, &check);
data.trn = ran_trn;
data.com = com;
data.value = i;
parser = gnc_transaction_sixtp_parser_create ();
if (!gnc_xml_parse_file (parser, filename1, test_add_transaction,
(gpointer)&data, book))
{
failure_args ("gnc_xml_parse_file returned FALSE",
__FILE__, __LINE__, "%d", i);
}
else
really_get_rid_of_transaction (data.new_trn);
}
/* no handling of circular data structures. We'll do that later */
/* sixtp_destroy(parser); */
g_unlink (filename1);
g_free (filename1);
really_get_rid_of_transaction (ran_trn);
xmlFreeNode (test_node);
}
}
static gboolean
test_real_transaction (const char* tag, gpointer global_data, gpointer data)
{
const char* msg;
msg = node_and_transaction_equal ((xmlNodePtr)global_data,
(Transaction*)data);
do_test_args (msg == NULL, "test_real_transaction",
__FILE__, __LINE__, msg);
really_get_rid_of_transaction ((Transaction*)data);
return TRUE;
}
int
main (int argc, char** argv)
{
qof_init ();
cashobjects_register ();
xaccLogDisable ();
gnc_transaction_xml_v2_testing = TRUE;
book = qof_book_new ();
if (argc > 1)
{
test_files_in_dir (argc, argv, test_real_transaction,
gnc_transaction_sixtp_parser_create (),
"gnc:transaction", book);
}
else
{
test_transaction ();
}
print_test_results ();
qof_close ();
exit (get_rv ());
}