1 /***************************************************************************
2 * test-xml-transaction.c
3 *
4 * Fri Oct 7 21:26:59 2005
5 * Copyright 2005 Neil Williams
6 * linux@codehelp.co.uk
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 #include <glib.h>
25 #include <glib/gstdio.h>
26
27 extern "C"
28 {
29 #include <config.h>
30
31 #include <stdlib.h>
32 #include <unistd.h>
33
34 #include <sys/types.h>
35 #include <dirent.h>
36 #include <sys/stat.h>
37
38 #include <gnc-engine.h>
39 #include <cashobjects.h>
40 #include <TransLog.h>
41
42 #include <test-engine-stuff.h>
43 #include <unittest-support.h>
44
45 #include <AccountP.h>
46 #include <Transaction.h>
47 #include <TransactionP.h>
48 }
49
50 #include "../gnc-xml-helper.h"
51 #include "../gnc-xml.h"
52 #include "../sixtp-parsers.h"
53 #include "../sixtp-dom-parsers.h"
54 #include "../io-gncxml-gen.h"
55 #include "test-file-stuff.h"
56 #include <test-stuff.h>
57 static QofBook* book;
58
59 extern gboolean gnc_transaction_xml_v2_testing;
60
61 static xmlNodePtr
find_appropriate_node(xmlNodePtr node,Split * spl)62 find_appropriate_node (xmlNodePtr node, Split* spl)
63 {
64 xmlNodePtr mark;
65
66 for (mark = node->xmlChildrenNode; mark; mark = mark->next)
67 {
68 gboolean account_guid_good = FALSE;
69 gboolean amount_good = FALSE;
70 xmlNodePtr mark2;
71
72 for (mark2 = mark->xmlChildrenNode; mark2; mark2 = mark2->next)
73 {
74 if (g_strcmp0 ((char*)mark2->name, "split:value") == 0)
75 {
76 gnc_numeric* num = dom_tree_to_gnc_numeric (mark2);
77
78 if (gnc_numeric_equal (*num, xaccSplitGetValue (spl)))
79 {
80 amount_good = TRUE;
81 }
82
83 g_free (num);
84 }
85 else if (g_strcmp0 ((char*)mark2->name, "split:account") == 0)
86 {
87 GncGUID* accid = dom_tree_to_guid (mark2);
88 Account* account = xaccSplitGetAccount (spl);
89
90 if (guid_equal (accid, xaccAccountGetGUID (account)))
91 {
92 account_guid_good = TRUE;
93 }
94 guid_free (accid);
95 }
96
97 if (account_guid_good && amount_good)
98 {
99 return mark;
100 }
101 }
102 }
103
104 return NULL;
105 }
106
107 static const char*
equals_node_val_vs_split_internal(xmlNodePtr node,Split * spl)108 equals_node_val_vs_split_internal (xmlNodePtr node, Split* spl)
109 {
110 xmlNodePtr mark;
111
112 for (mark = node->children; mark != NULL; mark = mark->next)
113 {
114 if (g_strcmp0 ((char*)mark->name, "split:id") == 0)
115 {
116 GncGUID* id = dom_tree_to_guid (mark);
117
118 if (!guid_equal (id, xaccSplitGetGUID (spl)))
119 {
120 guid_free (id);
121 return "ids differ";
122 }
123 guid_free (id);
124 }
125 else if (g_strcmp0 ((char*)mark->name, "split:memo") == 0)
126 {
127 char* memo = dom_tree_to_text (mark);
128
129 if (g_strcmp0 (memo, xaccSplitGetMemo (spl)) != 0)
130 {
131 g_free (memo);
132 return "memos differ";
133 }
134 g_free (memo);
135 }
136 else if (g_strcmp0 ((char*)mark->name, "split:reconciled-state") == 0)
137 {
138 char* rs = dom_tree_to_text (mark);
139
140 if (rs[0] != xaccSplitGetReconcile (spl))
141 {
142 g_free (rs);
143 return "states differ";
144 }
145 g_free (rs);
146 }
147 else if (g_strcmp0 ((char*)mark->name, "split:value") == 0)
148 {
149 gnc_numeric* num = dom_tree_to_gnc_numeric (mark);
150 gnc_numeric val = xaccSplitGetValue (spl);
151
152 if (!gnc_numeric_equal (*num, val))
153 {
154 g_free (num);
155 return g_strdup_printf ("values differ: %" G_GINT64_FORMAT "/%"
156 G_GINT64_FORMAT " v %" G_GINT64_FORMAT
157 "/%" G_GINT64_FORMAT,
158 (*num).num, (*num).denom,
159 val.num, val.denom);
160 }
161 g_free (num);
162 }
163 else if (g_strcmp0 ((char*)mark->name, "split:quantity") == 0)
164 {
165 gnc_numeric* num = dom_tree_to_gnc_numeric (mark);
166 gnc_numeric val = xaccSplitGetAmount (spl);
167
168 if (!gnc_numeric_equal (*num, val))
169 {
170 return g_strdup_printf ("quantities differ under _equal: %"
171 G_GINT64_FORMAT "/%" G_GINT64_FORMAT
172 " v %" G_GINT64_FORMAT "/%"
173 G_GINT64_FORMAT,
174 (*num).num, (*num).denom,
175 val.num, val.denom);
176 }
177 if (!gnc_numeric_equal (*num, val))
178 {
179 g_free (num);
180 return g_strdup_printf ("quantities differ: %" G_GINT64_FORMAT
181 "/%" G_GINT64_FORMAT " v %"
182 G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
183 (*num).num, (*num).denom,
184 val.num, val.denom);
185 }
186 g_free (num);
187 }
188 else if (g_strcmp0 ((char*)mark->name, "split:account") == 0)
189 {
190 GncGUID* id = dom_tree_to_guid (mark);
191 Account* account = xaccSplitGetAccount (spl);
192
193 if (!guid_equal (id, xaccAccountGetGUID (account)))
194 {
195 guid_free (id);
196 return "accounts differ";
197 }
198 guid_free (id);
199 }
200 }
201 return NULL;
202 }
203
204 static const char*
equals_node_val_vs_splits(xmlNodePtr node,const Transaction * trn)205 equals_node_val_vs_splits (xmlNodePtr node, const Transaction* trn)
206 {
207 xmlNodePtr spl_node;
208 Split* spl_mark;
209 int i;
210
211 g_return_val_if_fail (node, FALSE);
212 g_return_val_if_fail (node->xmlChildrenNode, FALSE);
213
214 for (i = 0, spl_mark = xaccTransGetSplit ((Transaction*)trn, i);
215 spl_mark;
216 i++, spl_mark = xaccTransGetSplit ((Transaction*)trn, i))
217 {
218 spl_node = find_appropriate_node (node, spl_mark);
219
220 if (!spl_node)
221 {
222 gchar guidstr[GUID_ENCODING_LENGTH + 1];
223 guid_to_string_buff (xaccSplitGetGUID (spl_mark), guidstr);
224 g_print ("Split GUID %s", guidstr);
225 return "no matching split found";
226 }
227
228 const char* msg = equals_node_val_vs_split_internal (spl_node, spl_mark);
229 if (msg != NULL)
230 {
231 return msg;
232 }
233 }
234
235 return NULL;
236 }
237
238 static const char*
node_and_transaction_equal(xmlNodePtr node,Transaction * trn)239 node_and_transaction_equal (xmlNodePtr node, Transaction* trn)
240 {
241 xmlNodePtr mark;
242
243 while (g_strcmp0 ((char*)node->name, "text") == 0)
244 node = node->next;
245
246 if (!check_dom_tree_version (node, "2.0.0"))
247 {
248 return "version wrong. Not 2.0.0 or not there";
249 }
250
251 if (!node->name || g_strcmp0 ((char*)node->name, "gnc:transaction"))
252 {
253 return "Name of toplevel node is bad";
254 }
255
256 for (mark = node->xmlChildrenNode; mark; mark = mark->next)
257 {
258 if (g_strcmp0 ((char*)mark->name, "text") == 0)
259 {
260 }
261 else if (g_strcmp0 ((char*)mark->name, "trn:id") == 0)
262 {
263 if (!equals_node_val_vs_guid (mark, xaccTransGetGUID (trn)))
264 {
265 return "ids differ";
266 }
267 }
268
269 /* This test will fail for many splits where the transaction has
270 * splits in different commodities -- eg, buying or selling a
271 * stock. jralls 2010-11-02 */
272 else if (g_strcmp0 ((char*)mark->name, "trn:currency") == 0)
273 {
274 #if 0
275 if (!equals_node_val_vs_commodity (
276 mark, xaccTransGetCurrency (trn), xaccTransGetBook (trn)))
277 {
278 return g_strdup ("currencies differ");
279 }
280 #endif
281 }
282 else if (g_strcmp0 ((char*)mark->name, "trn:num") == 0)
283 {
284 if (!equals_node_val_vs_string (mark, xaccTransGetNum (trn)))
285 {
286 return "nums differ";
287 }
288 }
289 else if (g_strcmp0 ((char*)mark->name, "trn:date-posted") == 0)
290 {
291 if (!equals_node_val_vs_date (mark, xaccTransRetDatePosted (trn)))
292 {
293 return "posted dates differ";
294 }
295 }
296 else if (g_strcmp0 ((char*)mark->name, "trn:date-entered") == 0)
297 {
298 if (!equals_node_val_vs_date (mark, xaccTransRetDateEntered (trn)))
299 {
300 return "entered dates differ";
301 }
302 }
303 else if (g_strcmp0 ((char*)mark->name, "trn:description") == 0)
304 {
305 if (!equals_node_val_vs_string (mark, xaccTransGetDescription (trn)))
306 {
307 return "descriptions differ";
308 }
309 }
310 else if (g_strcmp0 ((char*)mark->name, "trn:slots") == 0)
311 {
312 if (!equals_node_val_vs_kvp_frame (mark,
313 qof_instance_get_slots (QOF_INSTANCE (trn))))
314 {
315 return "slots differ";
316 }
317 }
318 else if (g_strcmp0 ((char*)mark->name, "trn:splits") == 0)
319 {
320 const char* msg = equals_node_val_vs_splits (mark, trn);
321 if (msg != NULL)
322 {
323 return msg;
324 }
325 }
326 else
327 {
328 return "unknown node";
329 }
330 }
331
332 return NULL;
333 }
334
335 static void
really_get_rid_of_transaction(Transaction * trn)336 really_get_rid_of_transaction (Transaction* trn)
337 {
338 xaccTransBeginEdit (trn);
339 xaccTransDestroy (trn);
340 xaccTransCommitEdit (trn);
341 }
342
343 struct tran_data_struct
344 {
345 Transaction* trn;
346 Transaction* new_trn;
347 gnc_commodity* com;
348 int value;
349 };
350 typedef struct tran_data_struct tran_data;
351
352 static gboolean
test_add_transaction(const char * tag,gpointer globaldata,gpointer data)353 test_add_transaction (const char* tag, gpointer globaldata, gpointer data)
354 {
355 Transaction* trans = static_cast<decltype (trans)> (data);
356 tran_data* gdata = static_cast<decltype (gdata)> (globaldata);
357 gboolean retval = TRUE;
358
359 xaccTransBeginEdit (trans);
360 xaccTransSetCurrency (trans, gdata->com);
361 xaccTransCommitEdit (trans);
362
363 if (!do_test_args (xaccTransEqual (gdata->trn, trans, TRUE, TRUE, TRUE, FALSE),
364 "gnc_transaction_sixtp_parser_create",
365 __FILE__, __LINE__,
366 "%d", gdata->value))
367 retval = FALSE;
368
369 gdata->new_trn = trans;
370
371 return retval;
372 }
373
374 static void
test_transaction(void)375 test_transaction (void)
376 {
377 int i;
378
379 for (i = 0; i < 50; i++)
380 {
381 Transaction* ran_trn;
382 xmlNodePtr test_node;
383 gnc_commodity* com, *new_com;
384 gchar* filename1;
385 int fd;
386
387 /* The next line exists for its side effect of creating the
388 * account tree. */
389 get_random_account_tree (book);
390 ran_trn = get_random_transaction (book);
391 new_com = get_random_commodity (book);
392 if (!ran_trn)
393 {
394 failure_args ("transaction_xml", __FILE__, __LINE__,
395 "get_random_transaction returned NULL");
396 return;
397 }
398
399 {
400 /* xaccAccountInsertSplit can reorder the splits. */
401 GList* list = g_list_copy (xaccTransGetSplitList (ran_trn));
402 GList* node = list;
403 for (; node; node = node->next)
404 {
405 Split* s = static_cast<decltype (s)> (node->data);
406 Account* a = xaccMallocAccount (book);
407
408 xaccAccountBeginEdit (a);
409 xaccAccountSetCommodity (a, new_com);
410 xaccAccountSetCommoditySCU (a, xaccSplitGetAmount (s).denom);
411 xaccAccountInsertSplit (a, s);
412 xaccAccountCommitEdit (a);
413 }
414 g_list_free (list);
415 }
416
417 com = xaccTransGetCurrency (ran_trn);
418
419 test_node = gnc_transaction_dom_tree_create (ran_trn);
420 if (!test_node)
421 {
422 failure_args ("transaction_xml", __FILE__, __LINE__,
423 "gnc_transaction_dom_tree_create returned NULL");
424 really_get_rid_of_transaction (ran_trn);
425 continue;
426 }
427 auto compare_msg = node_and_transaction_equal (test_node, ran_trn);
428 if (compare_msg != nullptr)
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 = static_cast<decltype (s)> (node->data);
458 Account* a1 = xaccSplitGetAccount (s);
459 Account* a2 = xaccMallocAccount (book);
460
461 xaccAccountBeginEdit (a2);
462 xaccAccountSetCommoditySCU (a2, xaccAccountGetCommoditySCU (a1));
463 xaccAccountSetGUID (a2, xaccAccountGetGUID (a1));
464 xaccAccountCommitEdit (a2);
465 }
466 }
467
468 {
469 sixtp* parser;
470 tran_data data;
471
472 const char* msg =
473 "[xaccAccountScrubCommodity()] Account \"\" does not have a commodity!";
474 const char* logdomain = "gnc.engine.scrub";
475 GLogLevelFlags loglevel = static_cast<decltype (loglevel)>
476 (G_LOG_LEVEL_CRITICAL);
477 TestErrorStruct check = { loglevel, const_cast<char*> (logdomain),
478 const_cast<char*> (msg)
479 };
480 g_log_set_handler (logdomain, loglevel,
481 (GLogFunc)test_checked_handler, &check);
482 data.trn = ran_trn;
483 data.com = com;
484 data.value = i;
485 parser = gnc_transaction_sixtp_parser_create ();
486
487 if (!gnc_xml_parse_file (parser, filename1, test_add_transaction,
488 (gpointer)&data, book))
489 {
490 failure_args ("gnc_xml_parse_file returned FALSE",
491 __FILE__, __LINE__, "%d", i);
492 }
493 else
494 really_get_rid_of_transaction (data.new_trn);
495 }
496 /* no handling of circular data structures. We'll do that later */
497 /* sixtp_destroy(parser); */
498
499
500 g_unlink (filename1);
501 g_free (filename1);
502 really_get_rid_of_transaction (ran_trn);
503 xmlFreeNode (test_node);
504 }
505 }
506
507 static gboolean
test_real_transaction(const char * tag,gpointer global_data,gpointer data)508 test_real_transaction (const char* tag, gpointer global_data, gpointer data)
509 {
510 const char* msg;
511
512 msg = node_and_transaction_equal ((xmlNodePtr)global_data,
513 (Transaction*)data);
514 do_test_args (msg == NULL, "test_real_transaction",
515 __FILE__, __LINE__, msg);
516 really_get_rid_of_transaction ((Transaction*)data);
517 return TRUE;
518 }
519
520 int
main(int argc,char ** argv)521 main (int argc, char** argv)
522 {
523 qof_init ();
524 cashobjects_register ();
525 xaccLogDisable ();
526
527 gnc_transaction_xml_v2_testing = TRUE;
528
529 book = qof_book_new ();
530
531 if (argc > 1)
532 {
533 test_files_in_dir (argc, argv, test_real_transaction,
534 gnc_transaction_sixtp_parser_create (),
535 "gnc:transaction", book);
536 }
537 else
538 {
539 test_transaction ();
540 }
541
542 print_test_results ();
543 qof_close ();
544 exit (get_rv ());
545 }
546