QOF  0.7.5
test-book-merge.c
1 /*********************************************************************
2  * test-book-merge.c -- test implementation api for QoFBook merge *
3  * Copyright (C) 2004-2005 Neil Williams <linux@codehelp.co.uk> *
4  * *
5  * This program is free software; you can redistribute it and/or *
6  * modify it under the terms of the GNU General Public License as *
7  * published by the Free Software Foundation; either version 2 of *
8  * the License, or (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, contact: *
17  * *
18  * Free Software Foundation Voice: +1-617-542-5942 *
19  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20  * Boston, MA 02110-1301, USA gnu@gnu.org *
21  * *
22  ********************************************************************/
23  /* Test the qof_book_merge infrastructure. */
24 
25 #include <glib.h>
26 #include "qof.h"
27 #include "qofinstance-p.h"
28 #include "qofevent-p.h"
29 #include "test-stuff.h"
30 
31 #define TEST_MODULE_NAME "book-merge-test"
32 #define TEST_MODULE_DESC "Test Book Merge"
33 #define OBJ_NAME "somename"
34 #define OBJ_AMOUNT "anamount"
35 #define OBJ_DATE "nottoday"
36 #define OBJ_GUID "unique"
37 #define OBJ_DISCOUNT "hefty"
38 #define OBJ_VERSION "early"
39 #define OBJ_MINOR "tiny"
40 #define OBJ_ACTIVE "ofcourse"
41 #define OBJ_FLAG "tiny_flag"
42 
43 static void test_rule_loop (QofBookMergeData *, QofBookMergeRule *,
44  guint);
45 static void test_merge (void);
46 gboolean myobjRegister (void);
47 #ifdef TEST_DEBUG
48 static QofLogModule log_module = QOF_MOD_MERGE;
49 #endif
50 
51 /* simple object structure */
52 typedef struct obj_s
53 {
54  QofInstance inst;
55  gchar *Name;
56  gchar flag;
57  QofNumeric Amount;
58  const GUID *obj_guid;
59  QofTime *date;
60  gdouble discount; /* cheap pun, I know. */
61  gboolean active;
62  gint32 version;
63  gint64 minor;
64 } myobj;
65 
66 static void
67 obj_setGUID (myobj * g, const GUID * h)
68 {
69  if (!g)
70  return;
71  g->obj_guid = h;
72 }
73 
74 static myobj *
75 obj_create (QofBook * book)
76 {
77  myobj *g;
78  g_return_val_if_fail (book, NULL);
79  g = g_new (myobj, 1);
80  qof_instance_init (&g->inst, TEST_MODULE_NAME, book);
81  obj_setGUID (g, qof_instance_get_guid (&g->inst));
82  g->discount = 0;
83  g->active = TRUE;
84  g->version = 1;
85  g->minor = 1;
86  g->flag = 'n';
87  qof_event_gen (&g->inst.entity, QOF_EVENT_CREATE, NULL);
88  return g;
89 }
90 
91 static void
92 obj_setFlag (myobj * g, char f)
93 {
94  g_return_if_fail (g);
95  g->flag = f;
96 }
97 
98 static gchar
99 obj_getFlag (myobj * g)
100 {
101  g_return_val_if_fail (g, 'n');
102  return g->flag;
103 }
104 
105 static void
106 obj_setMinor (myobj * g, gint64 h)
107 {
108  g_return_if_fail (g != NULL);
109  g->minor = h;
110 }
111 
112 static gint64
113 obj_getMinor (myobj * g)
114 {
115  g_return_val_if_fail ((g != NULL), 0);
116  return g->minor;
117 }
118 
119 static void
120 obj_setVersion (myobj * g, gint32 h)
121 {
122  g_return_if_fail (g != NULL);
123  g->version = h;
124 }
125 
126 static gint32
127 obj_getVersion (myobj * g)
128 {
129  if (!g)
130  return 0;
131  return g->version;
132 }
133 
134 static void
135 obj_setActive (myobj * g, gboolean h)
136 {
137  if (!g)
138  return;
139  g->active = h;
140 }
141 
142 static gboolean
143 obj_getActive (myobj * g)
144 {
145  if (!g)
146  return FALSE;
147  return g->active;
148 }
149 
150 static void
151 obj_setDiscount (myobj * g, gdouble h)
152 {
153  if (!g)
154  return;
155  g->discount = h;
156 }
157 
158 static gdouble
159 obj_getDiscount (myobj * g)
160 {
161  if (!g)
162  return 0;
163  return g->discount;
164 }
165 
166 static void
167 obj_setDate (myobj * g, QofTime *h)
168 {
169  if (!g)
170  return;
171  do_test ((h != NULL), "passed a NULL time");
172  do_test ((qof_time_is_valid (h) == TRUE),
173  "passed an invalid time");
174  g->date = h;
175 }
176 
177 static QofTime *
178 obj_getDate (myobj * g)
179 {
180  if (!g)
181  return NULL;
182  do_test ((g->date != NULL), "stored time is NULL");
183  do_test ((qof_time_is_valid (g->date) == TRUE),
184  "stored time is invalid");
185  return g->date;
186 }
187 
188 static const GUID *
189 obj_getGUID (myobj * g)
190 {
191  if (!g)
192  return NULL;
193  return g->obj_guid;
194 }
195 
196 static void
197 obj_setName (myobj * g, char *h)
198 {
199  if (!g || !h)
200  return;
201  g->Name = strdup (h);
202 }
203 
204 static gchar *
205 obj_getName (myobj * g)
206 {
207  if (!g)
208  return NULL;
209  return g->Name;
210 }
211 
212 static void
213 obj_setAmount (myobj * g, QofNumeric h)
214 {
215  if (!g)
216  return;
217  g->Amount = h;
218 }
219 
220 static QofNumeric
221 obj_getAmount (myobj * g)
222 {
223  if (!g)
224  return qof_numeric_zero ();
225  return g->Amount;
226 }
227 
228 static QofObject obj_object_def = {
229  .interface_version = QOF_OBJECT_VERSION,
230  .e_type = TEST_MODULE_NAME,
231  .type_label = TEST_MODULE_DESC,
232  .create = (gpointer) obj_create,
233  .book_begin = NULL,
234  .book_end = NULL,
235  .is_dirty = NULL,
236  .mark_clean = NULL,
237  .foreach = qof_collection_foreach,
238  .printable = NULL,
239  .version_cmp = (gint (*)(gpointer, gpointer))
241 };
242 
243 gboolean
244 myobjRegister (void)
245 {
246  static QofParam params[] = {
247  {OBJ_NAME, QOF_TYPE_STRING, (QofAccessFunc) obj_getName,
248  (QofSetterFunc) obj_setName, NULL},
249  {OBJ_AMOUNT, QOF_TYPE_NUMERIC, (QofAccessFunc) obj_getAmount,
250  (QofSetterFunc) obj_setAmount, NULL},
251  {OBJ_GUID, QOF_TYPE_GUID, (QofAccessFunc) obj_getGUID,
252  (QofSetterFunc) obj_setGUID, NULL},
253  {OBJ_DATE, QOF_TYPE_TIME, (QofAccessFunc) obj_getDate,
254  (QofSetterFunc) obj_setDate, NULL},
255  {OBJ_DISCOUNT, QOF_TYPE_DOUBLE, (QofAccessFunc) obj_getDiscount,
256  (QofSetterFunc) obj_setDiscount, NULL},
257  {OBJ_ACTIVE, QOF_TYPE_BOOLEAN, (QofAccessFunc) obj_getActive,
258  (QofSetterFunc) obj_setActive, NULL},
259  {OBJ_VERSION, QOF_TYPE_INT32, (QofAccessFunc) obj_getVersion,
260  (QofSetterFunc) obj_setVersion, NULL},
261  {OBJ_MINOR, QOF_TYPE_INT64, (QofAccessFunc) obj_getMinor,
262  (QofSetterFunc) obj_setMinor, NULL},
263  {OBJ_FLAG, QOF_TYPE_CHAR, (QofAccessFunc) obj_getFlag,
264  (QofSetterFunc) obj_setFlag, NULL},
266  NULL, NULL},
267  {QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc) qof_instance_get_guid,
268  NULL, NULL},
269  {NULL, NULL, NULL, NULL, NULL},
270  };
271 
272  qof_class_register (TEST_MODULE_NAME, NULL, params);
273 
274  return qof_object_register (&obj_object_def);
275 }
276 
277 static void
278 test_merge (void)
279 {
280  QofBook *target, *import;
281  gdouble init_value, discount;
282  myobj *import_obj, *target_obj, *new_obj;
283  gint result;
284  QofTime *base_time, *temp_time;
285  gboolean active;
286  gint32 version;
287  gint64 minor;
288  gchar *import_init, *target_init;
289  gchar flag, flag_check;
290  QofNumeric obj_amount;
291  QofBookMergeData *mergeData;
292 
293  target = qof_book_new ();
294  import = qof_book_new ();
295  init_value = 1.00;
296  result = 0;
297  flag = get_random_character ();
298  discount = 0.175;
299  active = TRUE;
300  version = get_random_int_in_range (0, 10000);
301  minor = get_random_int_in_range (1000001, 2000000);
302  import_init = "test";
303  target_init = "testing";
304  base_time = qof_time_set (1153309194, 568714241);
305  do_test ((TRUE == qof_time_is_valid (base_time)),
306  "invalid init time");
307  {
308  gchar *str;
309  QofDate *qd;
310 
311  qd = qof_date_from_qtime (base_time);
313  do_test ((0 == safe_strcmp (
314  "2006-07-19 11:39:54.568714241 +0000", str)),
315  "failed to compare base_time correctly.");
316  g_free (str);
317  qof_date_free (qd);
318  }
319 
320  do_test ((NULL != target), "#1 target book is NULL");
321  do_test ((NULL != import), "#2 import book is NULL");
322 
323  /* import book objects - tests used */
324  import_obj = g_new (myobj, 1);
325  do_test ((NULL != import_obj), "#3 new object create");
326  qof_instance_init (&import_obj->inst, TEST_MODULE_NAME, import);
327  do_test ((NULL != &import_obj->inst), "#4 instance init");
328  obj_setGUID (import_obj, qof_instance_get_guid (&import_obj->inst));
329  do_test ((NULL != &import_obj->obj_guid), "#5 guid set");
330  qof_event_gen (&import_obj->inst.entity, QOF_EVENT_CREATE, NULL);
331  do_test ((NULL != &import_obj->inst.entity), "#6 gnc event create");
332  obj_setName (import_obj, import_init);
333  do_test ((NULL != &import_obj->Name), "#7 string set");
334  obj_amount = qof_numeric_from_double (init_value, 1, QOF_HOW_DENOM_EXACT);
335  obj_setAmount (import_obj, obj_amount);
336  do_test ((qof_numeric_check (obj_getAmount (import_obj)) == QOF_ERROR_OK),
337  "#8 gnc_numeric set");
338  obj_setActive (import_obj, active);
339  do_test ((FALSE != &import_obj->active), "#9 gboolean set");
340  obj_setDiscount (import_obj, discount);
341  obj_setVersion (import_obj, version);
342  do_test ((version == import_obj->version), "#11 gint32 set");
343  obj_setMinor (import_obj, minor);
344  do_test ((minor == import_obj->minor), "#12 gint64 set");
345  do_test ((TRUE == qof_time_is_valid (base_time)),
346  "invalid import time ts");
347  {
348  gchar *str;
349  QofDate *qd;
350 
351  qd = qof_date_from_qtime (base_time);
353  do_test ((0 == safe_strcmp (
354  "2006-07-19 11:39:54.568714241 +0000", str)),
355  "failed to compare base_time correctly.");
356  g_free (str);
357  qof_date_free (qd);
358  }
359  obj_setDate (import_obj, base_time);
360  do_test ((TRUE == qof_time_is_valid (import_obj->date)),
361  "invalid import time");
362  do_test ((qof_time_cmp (base_time, import_obj->date) == 0),
363  "test #13 date set");
364  obj_setFlag (import_obj, flag);
365  do_test ((flag == obj_getFlag (import_obj)), "#14 flag set");
366 
367  obj_amount =
368  qof_numeric_add (obj_amount, obj_amount, 1, QOF_HOW_DENOM_EXACT);
369  discount = get_random_double ();
370  version = 2;
371  minor = 3;
372 
373  /* second import object - test results would be the same, so not tested. */
374  new_obj = g_new (myobj, 1);
375  qof_instance_init (&new_obj->inst, TEST_MODULE_NAME, import);
376  obj_setGUID (new_obj, qof_instance_get_guid (&new_obj->inst));
377  qof_event_gen (&new_obj->inst.entity, QOF_EVENT_CREATE, NULL);
378  obj_setName (new_obj, import_init);
379  obj_setAmount (new_obj, obj_amount);
380  obj_setActive (new_obj, active);
381  obj_setDiscount (new_obj, discount);
382  obj_setVersion (new_obj, version);
383  obj_setMinor (new_obj, minor);
384  do_test ((TRUE == qof_time_is_valid (base_time)),
385  "second import time invalid");
386  {
387  gchar *str;
388  QofDate *qd;
389 
390  qd = qof_date_from_qtime (base_time);
392  do_test ((0 == safe_strcmp (
393  "2006-07-19 11:39:54.568714241 +0000", str)),
394  "failed to compare base_time correctly.");
395  g_free (str);
396  qof_date_free (qd);
397  }
398  obj_setDate (new_obj, base_time);
399  obj_setFlag (new_obj, flag);
400 
401  obj_amount =
402  qof_numeric_add (obj_amount, obj_amount, 1, QOF_HOW_DENOM_EXACT);
403  discount = get_random_double ();
404  version = 2;
405  minor = 3;
406  flag = 'z';
407 
408  /* target object - test results would be the same, so not tested. */
409  target_obj = g_new (myobj, 1);
410  qof_instance_init (&target_obj->inst, TEST_MODULE_NAME, target);
411  obj_setGUID (target_obj, qof_instance_get_guid (&target_obj->inst));
412  qof_event_gen (&target_obj->inst.entity, QOF_EVENT_CREATE, NULL);
413  obj_setName (target_obj, target_init);
414  obj_setAmount (target_obj, obj_amount);
415  obj_setActive (target_obj, active);
416  obj_setDiscount (target_obj, discount);
417  obj_setVersion (target_obj, version);
418  obj_setMinor (target_obj, minor);
419  {
420  gchar *str;
421  QofDate *qd;
422 
423  qd = qof_date_from_qtime (base_time);
425  do_test ((0 == safe_strcmp (
426  "2006-07-19 11:39:54.568714241 +0000", str)),
427  "failed to compare base_time correctly.");
428  g_free (str);
429  qof_date_free (qd);
430  }
431  temp_time = qof_time_add_secs_copy (base_time, 65);
432  do_test ((TRUE == qof_time_is_valid (temp_time)),
433  "time add secs returned invalid");
434  obj_setDate (target_obj, temp_time);
435  obj_setFlag (target_obj, flag);
436  do_test ((flag == obj_getFlag (target_obj)), "#15 flag set");
437 
438  mergeData = qof_book_merge_init (import, target);
439  do_test (mergeData != NULL,
440  "FATAL: Merge could not be initialised!\t aborting . . ");
441  g_return_if_fail (mergeData != NULL);
442  qof_book_merge_rule_foreach (mergeData, test_rule_loop, MERGE_REPORT);
443  qof_book_merge_rule_foreach (mergeData, test_rule_loop, MERGE_UPDATE);
444  qof_book_merge_rule_foreach (mergeData, test_rule_loop, MERGE_NEW);
445  /* reserved calls - test only */
446  qof_book_merge_rule_foreach (mergeData, test_rule_loop, MERGE_ABSOLUTE);
447  qof_book_merge_rule_foreach (mergeData, test_rule_loop, MERGE_DUPLICATE);
448 
449  /* import should not be in the target - pass if import_init fails match with target */
450  do_test (((safe_strcmp (obj_getName (import_obj),
451  obj_getName (target_obj))) != 0),
452  "Init value test #1");
453 
454  /* a good commit returns zero */
455  do_test (qof_book_merge_commit (mergeData) == 0, "Commit failed");
456 
457  /* import should be in the target - pass if import_init matches target */
458  do_test (((safe_strcmp (import_init, obj_getName (target_obj))) == 0),
459  "Merged value test #1");
460 
461  /* import should be the same as target - pass if values are the same */
462  do_test (((safe_strcmp
463  (obj_getName (target_obj), obj_getName (import_obj))) == 0),
464  "Merged value test #2");
465 
466  /* check that the Amount really is a gnc_numeric */
467  do_test ((qof_numeric_check (obj_getAmount (import_obj)) == QOF_ERROR_OK),
468  "import gnc_numeric check");
469  do_test ((qof_numeric_check (obj_getAmount (target_obj)) == QOF_ERROR_OK),
470  "target gnc_numeric check");
471 
472  /* obj_amount was changed after the import object was set, so expect a difference. */
473  do_test ((qof_numeric_compare (obj_getAmount (import_obj), obj_amount) !=
474  QOF_ERROR_OK), "gnc_numeric value check #1");
475 
476  /* obj_amount is in the target object with the import value, expect a difference/ */
477  do_test ((qof_numeric_compare (obj_getAmount (target_obj), obj_amount) !=
478  QOF_ERROR_OK), "gnc_numeric value check #2");
479 
480  /* target had a different date, so import date should now be set */
481  qof_time_free (temp_time);
482  temp_time = target_obj->date;
483  {
484  gchar *str;
485  QofDate *qd;
486 
487  qd = qof_date_from_qtime (base_time);
489  do_test ((0 == safe_strcmp (
490  "2006-07-19 11:39:54.568714241 +0000", str)),
491  "failed to compare base_time after merge.");
492  g_free (str);
493  qof_date_free (qd);
494  }
495  {
496  gchar *str;
497  QofDate *qd;
498 
499  qd = qof_date_from_qtime (temp_time);
501  do_test ((0 == safe_strcmp (
502  "2006-07-19 11:39:54.568714241 +0000", str)),
503  "failed to compare target time after merge.");
504  g_free (str);
505  qof_date_free (qd);
506  }
507  do_test ((qof_time_cmp (base_time, temp_time) == 0),
508  "date value check: 1");
509 #ifdef TEST_DEBUG
510  DEBUG (" import<->target=%d\n",
511  (qof_time_cmp (base_time, target_obj->date)));
512  {
513  QofDate *qd;
514  gchar *check;
515 
516  qd = qof_date_from_qtime (base_time);
517  DEBUG (" base_time=%" G_GINT64_FORMAT
518  " nsecs=%ld", qof_time_get_secs (base_time),
519  qof_time_get_nanosecs (base_time));
520  DEBUG (" import:\nyear=%" G_GINT64_FORMAT
521  " month=%ld day=%ld hour=%ld min=%ld sec=%"
522  G_GINT64_FORMAT "nsecs=%ld\n",
523  qd->qd_year, qd->qd_mon, qd->qd_mday, qd->qd_hour,
524  qd->qd_min, qd->qd_sec, qd->qd_nanosecs);
526  DEBUG (" import=%s\n", check);
527  qof_date_free (qd);
528  qd = qof_date_from_qtime (target_obj->date);
529  if (!qd)
530  PERR ("qd failed");
531  DEBUG (" target:\nyear=%" G_GINT64_FORMAT
532  " month=%ld day=%ld hour=%ld min=%ld sec=%"
533  G_GINT64_FORMAT "nsecs=%ld\n",
534  qd->qd_year, qd->qd_mon, qd->qd_mday, qd->qd_hour,
535  qd->qd_min, qd->qd_sec, qd->qd_nanosecs);
537  DEBUG (" target=%s\n", check);
538  g_free (check);
539  qof_date_free (qd);
540  }
541 #endif
542  qof_time_free (base_time);
543  /* import should be the same as target - pass if values are the same */
544  flag_check = obj_getFlag (target_obj);
545  do_test ((flag_check == obj_getFlag (import_obj)), "flag value check: 1");
546  do_test ((obj_getFlag (import_obj) == obj_getFlag (target_obj)),
547  "flag value check: 2");
548 }
549 
550 static void
551 test_rule_loop (QofBookMergeData * mergeData, QofBookMergeRule * rule,
552  guint remainder)
553 {
554  GSList *testing;
555  QofParam *eachParam;
556  gchar *importstring;
557  gchar *targetstring;
558  gboolean skip_target;
559 
560  importstring = NULL;
561  targetstring = NULL;
562  skip_target = FALSE;
563  mergeData->currentRule = rule;
564  do_test ((rule != NULL), "loop:#1 Rule is NULL");
565  do_test (remainder > 0, "loop:#2 remainder error.");
566  do_test ((safe_strcmp (NULL, rule->mergeLabel) != 0),
567  "loop:#3 object label\n");
568  do_test ((rule->importEnt != NULL),
569  "loop:#4 empty import entity");
570  /* targetEnt is always NULL at this stage if MERGE_NEW is set */
571  if (rule->targetEnt == NULL)
572  {
573  skip_target = TRUE;
574  }
575  if (!skip_target)
576  {
577  do_test ((safe_strcmp
578  (rule->importEnt->e_type, rule->targetEnt->e_type) == 0),
579  "loop: entity type mismatch");
580  }
581  do_test ((rule->mergeParam != NULL),
582  "loop: empty parameter list");
583  testing = rule->mergeParam;
584 
585  while (testing != NULL)
586  { // start of param loop
587  eachParam = testing->data;
588  do_test ((eachParam != NULL), "loop:#8 no QofParam data");
589  do_test ((eachParam->param_name != NULL),
590  "loop:#9 no parameter name");
591  do_test ((eachParam->param_getfcn != NULL),
592  "loop:#10 no get function");
593  do_test ((eachParam->param_setfcn != NULL),
594  "loop:#11 no set function");
595  /* non-generic - test routines only! */
596  if (safe_strcmp (eachParam->param_type, QOF_TYPE_STRING) == 0)
597  {
598  importstring =
599  g_strdup (eachParam->
600  param_getfcn (rule->importEnt, eachParam));
601  do_test ((importstring != NULL),
602  "loop:#12 direct get_fcn import");
603  do_test ((safe_strcmp (importstring, "test") == 0),
604  "loop:#13 direct import comparison");
605  if (!skip_target)
606  {
607  targetstring =
608  eachParam->param_getfcn (rule->targetEnt, eachParam);
609  do_test ((targetstring != NULL),
610  "loop:#14 direct get_fcn target");
611  do_test ((safe_strcmp (targetstring, "testing") == 0),
612  "loop:#15 direct target comparison");
613  }
614  }
615  /* param_as_string does the conversion for display purposes only */
616  /* do NOT use as_string for calculations or set_fcn */
617  importstring =
618  qof_book_merge_param_as_string (eachParam, rule->importEnt);
619  do_test ((importstring != NULL),
620  "loop:#16 import param_as_string is null");
621  if (!skip_target)
622  {
623  targetstring =
624  qof_book_merge_param_as_string (eachParam, rule->targetEnt);
625  do_test ((targetstring != NULL),
626  "loop:#17 target param_as_string is null");
627  }
628  testing = g_slist_next (testing);
629  } // end param loop
630  /* set each rule dependent on the user involvement response above. */
631  /* test routine just sets all MERGE_REPORT to MERGE_UPDATE */
632  mergeData = qof_book_merge_update_result (mergeData, MERGE_UPDATE);
633  do_test ((rule->mergeResult != MERGE_REPORT),
634  "update result fail");
635 }
636 
637 int
638 main (void)
639 {
640  qof_init ();
641  myobjRegister ();
642  test_merge ();
643  print_test_results ();
644  qof_close ();
645  return get_rv();
646 }