GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
test-xml-transaction.c
1 /***************************************************************************
2  * test-xml-transaction.c
3  *
4  * Fri Oct 7 21:26:59 2005
5  * Copyright 2005 Neil Williams
7  ****************************************************************************/
8 /*
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301, USA.
23  */
24 
25 #include "config.h"
26 
27 #include <glib.h>
28 #include <glib/gstdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 
32 #include <sys/types.h>
33 #include <dirent.h>
34 #include <sys/stat.h>
35 
36 #include "../gnc-xml-helper.h"
37 #include "../gnc-xml.h"
38 #include <gnc-engine.h>
39 #include <cashobjects.h>
40 #include "../sixtp-parsers.h"
41 
42 #include "../sixtp-dom-parsers.h"
43 #include <TransLog.h>
44 #include "../io-gncxml-gen.h"
45 
46 #include <test-stuff.h>
47 #include <test-engine-stuff.h>
48 #include <test-file-stuff.h>
49 #include <unittest-support.h>
50 
51 #include <AccountP.h>
52 #include <Transaction.h>
53 #include <TransactionP.h>
54 
55 static QofBook *book;
56 
57 extern gboolean gnc_transaction_xml_v2_testing;
58 
59 static xmlNodePtr
60 find_appropriate_node(xmlNodePtr node, Split *spl)
61 {
62  xmlNodePtr mark;
63 
64  for (mark = node->xmlChildrenNode; mark; mark = mark->next)
65  {
66  gboolean account_guid_good = FALSE;
67  gboolean amount_good = FALSE;
68  xmlNodePtr mark2;
69 
70  for (mark2 = mark->xmlChildrenNode; mark2; mark2 = mark2->next)
71  {
72  if (g_strcmp0((char*)mark2->name, "split:value") == 0)
73  {
74  gnc_numeric *num = dom_tree_to_gnc_numeric(mark2);
75 
76  if (gnc_numeric_equal(*num, xaccSplitGetValue(spl)))
77  {
78  amount_good = TRUE;
79  }
80 
81  g_free(num);
82  }
83  else if (g_strcmp0((char*)mark2->name, "split:account") == 0)
84  {
85  GncGUID *accid = dom_tree_to_guid(mark2);
86  Account *account = xaccSplitGetAccount (spl);
87 
88  if (guid_equal(accid, xaccAccountGetGUID(account)))
89  {
90  account_guid_good = TRUE;
91  }
92  g_free(accid);
93  }
94 
95  if (account_guid_good && amount_good)
96  {
97  return mark;
98  }
99  }
100  }
101 
102  return NULL;
103 }
104 
105 static char *
106 equals_node_val_vs_split_internal(xmlNodePtr node, Split* spl)
107 {
108  xmlNodePtr mark;
109 
110  for (mark = node->children; mark != NULL; mark = mark->next)
111  {
112  if (g_strcmp0((char*)mark->name, "split:id") == 0)
113  {
114  GncGUID *id = dom_tree_to_guid(mark);
115 
116  if (!guid_equal(id, xaccSplitGetGUID(spl)))
117  {
118  g_free(id);
119  return "ids differ";
120  }
121  g_free(id);
122  }
123  else if (g_strcmp0((char*)mark->name, "split:memo") == 0)
124  {
125  char *memo = dom_tree_to_text(mark);
126 
127  if (g_strcmp0(memo, xaccSplitGetMemo(spl)) != 0)
128  {
129  g_free(memo);
130  return "memos differ";
131  }
132  g_free(memo);
133  }
134  else if (g_strcmp0((char*)mark->name, "split:reconciled-state") == 0)
135  {
136  char *rs = dom_tree_to_text(mark);
137 
138  if (rs[0] != xaccSplitGetReconcile(spl))
139  {
140  g_free(rs);
141  return "states differ";
142  }
143  g_free(rs);
144  }
145  else if (g_strcmp0((char*)mark->name, "split:value") == 0)
146  {
147  gnc_numeric *num = dom_tree_to_gnc_numeric(mark);
148  gnc_numeric val = xaccSplitGetValue(spl);
149 
150  if (!gnc_numeric_equal(*num, val))
151  {
152  g_free(num);
153  return g_strdup_printf ("values differ: %" G_GINT64_FORMAT "/%"
154  G_GINT64_FORMAT " v %" G_GINT64_FORMAT
155  "/%" G_GINT64_FORMAT,
156  (*num).num, (*num).denom,
157  val.num, val.denom);
158  }
159  g_free(num);
160  }
161  else if (g_strcmp0((char*)mark->name, "split:quantity") == 0)
162  {
163  gnc_numeric *num = dom_tree_to_gnc_numeric(mark);
164  gnc_numeric val = xaccSplitGetAmount(spl);
165 
166  if (!gnc_numeric_equal(*num, val))
167  {
168  return g_strdup_printf( "quantities differ under _equal: %"
169  G_GINT64_FORMAT "/%" G_GINT64_FORMAT
170  " v %" G_GINT64_FORMAT "/%"
171  G_GINT64_FORMAT,
172  (*num).num, (*num).denom,
173  val.num, val.denom );
174  }
175  if (!gnc_numeric_equal(*num, val))
176  {
177  g_free(num);
178  return g_strdup_printf ("quantities differ: %" G_GINT64_FORMAT
179  "/%" G_GINT64_FORMAT " v %"
180  G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
181  (*num).num, (*num).denom,
182  val.num, val.denom);
183  }
184  g_free(num);
185  }
186  else if (g_strcmp0((char*)mark->name, "split:account") == 0)
187  {
188  GncGUID *id = dom_tree_to_guid(mark);
189  Account *account = xaccSplitGetAccount (spl);
190 
191  if (!guid_equal(id, xaccAccountGetGUID(account)))
192  {
193  g_free(id);
194  return "accounts differ";
195  }
196  g_free(id);
197  }
198  }
199  return NULL;
200 }
201 
202 static char *
203 equals_node_val_vs_splits(xmlNodePtr node, const Transaction *trn)
204 {
205  xmlNodePtr spl_node;
206  Split *spl_mark;
207  char *msg;
208  int i;
209 
210  g_return_val_if_fail(node, FALSE);
211  g_return_val_if_fail(node->xmlChildrenNode, FALSE);
212 
213  for (i = 0, spl_mark = xaccTransGetSplit((Transaction*)trn, i);
214  spl_mark;
215  i++, spl_mark = xaccTransGetSplit((Transaction*)trn, i))
216  {
217  spl_node = find_appropriate_node(node, spl_mark);
218 
219  if (!spl_node)
220  {
221  gchar guidstr[GUID_ENCODING_LENGTH+1];
222  guid_to_string_buff(xaccSplitGetGUID(spl_mark),guidstr);
223  g_print( "Split GUID %s", guidstr );
224  return "no matching split found";
225  }
226 
227  msg = equals_node_val_vs_split_internal(spl_node, spl_mark);
228  if (msg != NULL)
229  {
230  return msg;
231  }
232  }
233 
234  return NULL;
235 }
236 
237 static gchar*
238 node_and_transaction_equal(xmlNodePtr node, Transaction *trn)
239 {
240  xmlNodePtr mark;
241 
242  while (g_strcmp0 ((char*)node->name, "text") == 0)
243  node = node->next;
244 
245  if (!check_dom_tree_version(node, "2.0.0"))
246  {
247  return "version wrong. Not 2.0.0 or not there";
248  }
249 
250  if (!node->name || g_strcmp0((char*)node->name, "gnc:transaction"))
251  {
252  return "Name of toplevel node is bad";
253  }
254 
255  for (mark = node->xmlChildrenNode; mark; mark = mark->next)
256  {
257  if (g_strcmp0((char*)mark->name, "text") == 0)
258  {
259  }
260  else if (g_strcmp0((char*)mark->name, "trn:id") == 0)
261  {
262  if (!equals_node_val_vs_guid(mark, xaccTransGetGUID(trn)))
263  {
264  return "ids differ";
265  }
266  }
267 
268  /* This test will fail for many splits where the transaction has
269  * splits in different commodities -- eg, buying or selling a
270  * stock. jralls 2010-11-02 */
271  else if (g_strcmp0((char*)mark->name, "trn:currency") == 0)
272  {
273 #if 0
274  if (!equals_node_val_vs_commodity(
275  mark, xaccTransGetCurrency(trn), xaccTransGetBook(trn)))
276  {
277  return g_strdup("currencies differ");
278  }
279 #endif
280  }
281  else if (g_strcmp0((char*)mark->name, "trn:num") == 0)
282  {
283  if (!equals_node_val_vs_string(mark, xaccTransGetNum(trn)))
284  {
285  return "nums differ";
286  }
287  }
288  else if (g_strcmp0((char*)mark->name, "trn:date-posted") == 0)
289  {
290  if (!equals_node_val_vs_date(mark, xaccTransRetDatePostedTS(trn)))
291  {
292  return "posted dates differ";
293  }
294  }
295  else if (g_strcmp0((char*)mark->name, "trn:date-entered") == 0)
296  {
297  if (!equals_node_val_vs_date(mark, xaccTransRetDateEnteredTS(trn)))
298  {
299  return "entered dates differ";
300  }
301  }
302  else if (g_strcmp0((char*)mark->name, "trn:description") == 0)
303  {
304  if (!equals_node_val_vs_string(mark, xaccTransGetDescription(trn)))
305  {
306  return "descriptions differ";
307  }
308  }
309  else if (g_strcmp0((char*)mark->name, "trn:slots") == 0)
310  {
311  if (!equals_node_val_vs_kvp_frame(mark, qof_instance_get_slots (QOF_INSTANCE (trn))))
312  {
313  return "slots differ";
314  }
315  }
316  else if (g_strcmp0((char*)mark->name, "trn:splits") == 0)
317  {
318  char *msg = equals_node_val_vs_splits (mark, trn);
319  if (msg != NULL)
320  {
321  return msg;
322  }
323  }
324  else
325  {
326  return "unknown node";
327  }
328  }
329 
330  return NULL;
331 }
332 
333 static void
334 really_get_rid_of_transaction(Transaction *trn)
335 {
336  xaccTransBeginEdit(trn);
337  xaccTransDestroy(trn);
338  xaccTransCommitEdit(trn);
339 }
340 
342 {
343  Transaction *trn;
344  Transaction *new_trn;
345  gnc_commodity *com;
346  int value;
347 };
348 typedef struct tran_data_struct tran_data;
349 
350 static gboolean
351 test_add_transaction(const char *tag, gpointer globaldata, gpointer data)
352 {
353  Transaction *trans = data;
354  tran_data *gdata = (tran_data*)globaldata;
355  gboolean retval = TRUE;
356 
357  xaccTransBeginEdit (trans);
358  xaccTransSetCurrency (trans, gdata->com);
359  xaccTransCommitEdit (trans);
360 
361  if (!do_test_args(xaccTransEqual(gdata->trn, trans, TRUE, TRUE, TRUE, FALSE),
362  "gnc_transaction_sixtp_parser_create",
363  __FILE__, __LINE__,
364  "%d", gdata->value))
365  retval = FALSE;
366 
367  gdata->new_trn = trans;
368 
369  return retval;
370 }
371 
372 static void
373 test_transaction(void)
374 {
375  int i;
376 
377  for (i = 0; i < 50; i++)
378  {
379  Transaction *ran_trn;
380  xmlNodePtr test_node;
381  gnc_commodity *com, *new_com;
382  gchar *compare_msg;
383  gchar *filename1;
384  int fd;
385 
386  /* The next line exists for its side effect of creating the
387  * account tree. */
388  get_random_account_tree(book);
389  ran_trn = get_random_transaction(book);
390  new_com = get_random_commodity( book );
391  if (!ran_trn)
392  {
393  failure_args("transaction_xml", __FILE__, __LINE__,
394  "get_random_transaction returned NULL");
395  return;
396  }
397 
398  {
399  /* xaccAccountInsertSplit can reorder the splits. */
400  GList * list = g_list_copy(xaccTransGetSplitList (ran_trn));
401  GList * node = list;
402  for ( ; node; node = node->next)
403  {
404  Split * s = node->data;
405  Account * a = xaccMallocAccount(book);
406 
408  xaccAccountSetCommodity( a, new_com );
410  xaccAccountInsertSplit (a, s);
412  }
413  g_list_free(list);
414  }
415 
416  com = xaccTransGetCurrency (ran_trn);
417 
418  test_node = gnc_transaction_dom_tree_create(ran_trn);
419  if (!test_node)
420  {
421  failure_args("transaction_xml", __FILE__, __LINE__,
422  "gnc_transaction_dom_tree_create returned NULL");
423  really_get_rid_of_transaction(ran_trn);
424  continue;
425  }
426 
427  if ((compare_msg = node_and_transaction_equal(test_node, ran_trn)) !=
428  NULL)
429  {
430  failure_args("transaction_xml", __FILE__, __LINE__,
431  "node and transaction were not equal: %s",
432  compare_msg);
433  xmlElemDump(stdout, NULL, test_node);
434  printf("\n");
435  fflush(stdout);
436  xmlFreeNode(test_node);
437  really_get_rid_of_transaction(ran_trn);
438  continue;
439  }
440  else
441  {
442  success_args("transaction_xml", __FILE__, __LINE__, "%d", i );
443  }
444 
445  filename1 = g_strdup_printf("test_file_XXXXXX");
446 
447  fd = g_mkstemp(filename1);
448 
449  write_dom_node_to_file(test_node, fd);
450 
451  close(fd);
452 
453  {
454  GList * node = xaccTransGetSplitList (ran_trn);
455  for ( ; node; node = node->next)
456  {
457  Split * s = node->data;
458  Account * a1 = xaccSplitGetAccount(s);
459  Account * a2 = xaccMallocAccount(book);
460 
463  xaccAccountSetGUID (a2, xaccAccountGetGUID (a1));
465  }
466  }
467 
468  {
469  sixtp *parser;
470  tran_data data;
471 
472  gchar *msg = "[xaccAccountScrubCommodity()] Account \"\" does not have a commodity!";
473  gchar *logdomain = "gnc.engine.scrub";
474  guint loglevel = G_LOG_LEVEL_CRITICAL;
475  TestErrorStruct check = { loglevel, logdomain, msg };
476  g_log_set_handler (logdomain, loglevel,
477  (GLogFunc)test_checked_handler, &check);
478  data.trn = ran_trn;
479  data.com = com;
480  data.value = i;
481  parser = gnc_transaction_sixtp_parser_create();
482 
483  if (!gnc_xml_parse_file(parser, filename1, test_add_transaction,
484  (gpointer)&data, book))
485  {
486  failure_args("gnc_xml_parse_file returned FALSE",
487  __FILE__, __LINE__, "%d", i);
488  }
489  else
490  really_get_rid_of_transaction (data.new_trn);
491 
492  /* no handling of circular data structures. We'll do that later */
493  /* sixtp_destroy(parser); */
494  }
495 
496  g_unlink(filename1);
497  g_free(filename1);
498  really_get_rid_of_transaction(ran_trn);
499  xmlFreeNode(test_node);
500  }
501 }
502 
503 static gboolean
504 test_real_transaction(const char *tag, gpointer global_data, gpointer data)
505 {
506  const char *msg;
507 
508  msg = node_and_transaction_equal((xmlNodePtr)global_data,
509  (Transaction*)data);
510  do_test_args(msg == NULL, "test_real_transaction",
511  __FILE__, __LINE__, msg);
512  really_get_rid_of_transaction((Transaction*)data);
513  return TRUE;
514 }
515 
516 int
517 main (int argc, char ** argv)
518 {
519  qof_init();
520  cashobjects_register();
521  xaccLogDisable();
522 
523  gnc_transaction_xml_v2_testing = TRUE;
524 
525  book = qof_book_new ();
526 
527  if (argc > 1)
528  {
529  test_files_in_dir(argc, argv, test_real_transaction,
530  gnc_transaction_sixtp_parser_create(),
531  "gnc:transaction", book);
532  }
533  else
534  {
535  test_transaction();
536  }
537 
538  print_test_results();
539  qof_close();
540  exit(get_rv());
541 }
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Definition: sixtp.h:93
Split * xaccTransGetSplit(const Transaction *trans, int i)
Definition: Transaction.c:2144
int xaccAccountGetCommoditySCU(const Account *acc)
Definition: Account.c:2458
void xaccLogDisable(void)
Definition: TransLog.c:93
char xaccSplitGetReconcile(const Split *split)
Definition: Split.c:1980
QofBook * qof_book_new(void)
gchar * guid_to_string_buff(const GncGUID *guid, gchar *buff)
const char * xaccTransGetNum(const Transaction *trans)
Definition: Transaction.c:2178
Definition: guid.h:65
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Definition: Transaction.c:1354
void xaccTransDestroy(Transaction *trans)
Definition: Transaction.c:1402
#define xaccAccountGetGUID(X)
Definition: Account.h:239
gboolean xaccTransEqual(const Transaction *ta, const Transaction *tb, gboolean check_guids, gboolean check_splits, gboolean check_balances, gboolean assume_ordered)
Definition: Transaction.c:857
gboolean guid_equal(const GncGUID *guid_1, const GncGUID *guid_2)
#define GUID_ENCODING_LENGTH
Definition: guid.h:74
Timespec xaccTransRetDateEnteredTS(const Transaction *trans)
Definition: Transaction.c:2273
const char * xaccTransGetDescription(const Transaction *trans)
Definition: Transaction.c:2184
#define xaccTransGetBook(X)
Definition: Transaction.h:753
void xaccTransCommitEdit(Transaction *trans)
Definition: Transaction.c:1579
#define xaccSplitGetGUID(X)
Definition: Split.h:521
void xaccTransBeginEdit(Transaction *trans)
Definition: Transaction.c:1380
All type declarations for the whole Gnucash engine.
void xaccAccountSetCommoditySCU(Account *acc, int scu)
Definition: Account.c:2435
#define xaccTransGetGUID(X)
Definition: Transaction.h:755
API for the transaction logger.
void qof_close(void)
Safely close down the Query Object Framework.
Definition: SplitP.h:71
gnc_numeric xaccSplitGetValue(const Split *split)
Definition: Split.c:1993
void xaccAccountBeginEdit(Account *acc)
Definition: Account.c:1280
Account * xaccSplitGetAccount(const Split *s)
Definition: Split.c:968
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Definition: Transaction.c:1348
#define xaccAccountInsertSplit(acc, s)
Definition: Account.h:972
Timespec xaccTransRetDatePostedTS(const Transaction *trans)
Definition: Transaction.c:2243
Account * xaccMallocAccount(QofBook *book)
Definition: Account.c:1083
const char * xaccSplitGetMemo(const Split *split)
Definition: Split.c:1968
void qof_init(void)
Initialise the Query Object Framework.
API for Transactions and Splits (journal entries)
void xaccAccountCommitEdit(Account *acc)
Definition: Account.c:1321
SplitList * xaccTransGetSplitList(const Transaction *trans)
Definition: Transaction.c:2164
void xaccAccountSetCommodity(Account *acc, gnc_commodity *com)
Definition: Account.c:2389
gnc_numeric xaccSplitGetAmount(const Split *split)
Definition: Split.c:1987