GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
test-recurrence.c
1 /* Copyright (C) 2005, Chris Shoemaker <[email protected]>
2  *
3  * This program is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU General Public License as
5  * published by the Free Software Foundation; either version 2 of
6  * the License, or (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, contact:
15  *
16  * Free Software Foundation Voice: +1-617-542-5942
17  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
18  * Boston, MA 02110-1301, USA [email protected]
19  */
20 
21 #include "config.h"
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <glib.h>
26 
27 #include "test-stuff.h"
28 #include "Recurrence.h"
29 #include "gnc-engine.h"
30 
31 static QofBook *book;
32 
33 static void check_valid(GDate *next, GDate *ref, GDate *start,
34  guint16 mult, PeriodType pt, WeekendAdjust wadj)
35 {
36  gboolean valid;
37  gint startToNext;
38  /* FIXME: The WeekendAdjust argument is completely ignored for
39  now. */
40 
41  valid = g_date_valid(next);
42  if (pt == PERIOD_ONCE && g_date_compare(start, ref) <= 0)
43  do_test(!valid, "incorrectly valid");
44  else
45  do_test(valid, "incorrectly invalid");
46 
47  if (!valid) return;
48 
49  do_test(g_date_compare(ref, next) < 0,
50  "next date not strictly later than ref date");
51  startToNext = g_date_get_julian(next) - g_date_get_julian(start);
52 
53  // Phase test
54  switch (pt)
55  {
56  case PERIOD_YEAR:
57  do_test((g_date_get_year(next) - g_date_get_year(start)) % mult == 0,
58  "year period phase wrong"); // redundant
59  mult *= 12;
60  // fall through
61  case PERIOD_END_OF_MONTH:
62  if (pt == PERIOD_END_OF_MONTH)
63  do_test(g_date_is_last_of_month(next), "end of month phase wrong");
64  // fall through
65  case PERIOD_LAST_WEEKDAY:
66  case PERIOD_NTH_WEEKDAY:
67  case PERIOD_MONTH:
68  {
69  gint monthdiff;
70  GDateDay day_start, day_next;
71 
72  monthdiff = (g_date_get_month(next) - g_date_get_month(start)) +
73  12 * (g_date_get_year(next) - g_date_get_year(start));
74  do_test(monthdiff % mult == 0, "month or year phase wrong");
75 
76  if (pt == PERIOD_NTH_WEEKDAY || pt == PERIOD_LAST_WEEKDAY)
77  {
78  guint sweek, nweek;
79 
80  do_test(g_date_get_weekday(next) == g_date_get_weekday(start),
81  "weekday phase wrong");
82  sweek = (g_date_get_day(start) - 1) / 7;
83  nweek = (g_date_get_day(next) - 1) / 7;
84 
85  /* 3 cases: either the weeks agree, OR 'next' didn't have
86  5 of the weekday that 'start' did, so it's only the
87  4th, OR 'start' didn't have 5 of the weekday that
88  'next' does and we want the LAST weekday, so it's the
89  5th of that weekday */
90  do_test(sweek == nweek ||
91  (sweek == 4 && nweek == 3 && (g_date_get_day(next) + 7) >
92  g_date_get_days_in_month(
93  g_date_get_month(next), g_date_get_year(next))) ||
94  (sweek == 3 && nweek == 4 && (pt == PERIOD_LAST_WEEKDAY)),
95  "week of month phase wrong");
96 
97  }
98  else
99  {
100  day_start = g_date_get_day(start);
101  day_next = g_date_get_day(next);
102  if (day_start < 28)
103  do_test(day_start == day_next, "dom don't match");
104  else if (pt != PERIOD_END_OF_MONTH)
105  {
106  // the end of month case was already checked above. near
107  // the end of the month, the days should still agree,
108  // unless they can't because of a short month.
109  do_test(day_start == day_next || g_date_is_last_of_month(next),
110  "dom don't match and next is not eom");
111  }
112  }
113  }
114  break;
115  case PERIOD_WEEK:
116  mult *= 7;
117  // fall through
118  case PERIOD_DAY:
119  do_test((startToNext % mult) == 0, "week or day period phase wrong");
120  break;
121  case PERIOD_ONCE:
122  do_test(startToNext == 0, "period once not on start date");
123  break;
124  default:
125  do_test(FALSE, "invalid PeriodType");
126  break;
127  }
128 
129 }
130 
131 #define NUM_DATES_TO_TEST 300
132 #define NUM_DATES_TO_TEST_REF 300
133 #define NUM_MULT_TO_TEST 10
134 #define JULIAN_START 2003*365 // years have to be < 10000
135 
136 /* Mult of zero is usually not valid, but it gets regularized to 1, so
137  the effect is just that we end up testing mult of 1 twice, plus the
138  regularization. */
139 static void test_all()
140 {
141  Recurrence r;
142  GDate d_start, d_start_reg;
143  GDate d_ref, d_next;
144  guint16 mult, mult_reg;
145  PeriodType pt, pt_reg;
146  WeekendAdjust wadj, wadj_reg;
147  gint32 j1, j2;
148  gint i_ref;
149 
150  for (pt = PERIOD_ONCE; pt < NUM_PERIOD_TYPES; pt++)
151  {
152  for (wadj = WEEKEND_ADJ_NONE; wadj < NUM_WEEKEND_ADJS; wadj++)
153  {
154  for (j1 = JULIAN_START; j1 < JULIAN_START + NUM_DATES_TO_TEST; j1++)
155  {
156  g_date_set_julian(&d_start, j1);
157  for (i_ref = 0; i_ref < NUM_DATES_TO_TEST_REF; i_ref++)
158  {
159  j2 = (guint32) get_random_int_in_range(1, 1 << 19);
160  g_date_set_julian(&d_ref, j2);
161 
162  for (mult = 0; mult < NUM_MULT_TO_TEST; mult++)
163  {
164  recurrenceSet(&r, mult, pt, &d_start, wadj);
165  pt_reg = recurrenceGetPeriodType(&r);
166  d_start_reg = recurrenceGetDate(&r);
167  mult_reg = recurrenceGetMultiplier(&r);
168  wadj_reg = recurrenceGetWeekendAdjust(&r);
169 
170  recurrenceNextInstance(&r, &d_ref, &d_next);
171  check_valid(&d_next, &d_ref, &d_start_reg,
172  mult_reg, pt_reg, wadj_reg);
173 
174  }
175  }
176  }
177  }
178  }
179 }
180 
181 static gboolean test_equal(GDate *d1, GDate *d2)
182 {
183  if (!do_test(g_date_compare(d1, d2) == 0, "dates don't match"))
184  {
185  gchar s1[21];
186  gchar s2[21];
187  g_date_strftime(s1, 20, "%x", d1);
188  g_date_strftime(s2, 20, "%x", d2);
189 
190  printf("%s != %s\n", s1, s2);
191  return FALSE;
192  }
193  return TRUE;
194 }
195 
196 
197 static void test_specific(PeriodType pt, guint16 mult,
198  GDateMonth sm, GDateDay sd, GDateYear sy,
199  GDateMonth rm, GDateDay rd, GDateYear ry,
200  GDateMonth nm, GDateDay nd, GDateYear ny)
201 {
202  GDate start;
203  GDate ref, next, true_next;
204  Recurrence r;
205 
206  g_date_set_dmy(&start, sd, sm, sy);
207  g_date_set_dmy(&ref, rd, rm, ry);
208  g_date_set_dmy(&true_next, nd, nm, ny);
209 
210 
211  recurrenceSet(&r, mult, pt, &start, WEEKEND_ADJ_NONE);
212  recurrenceNextInstance(&r, &ref, &next);
213 
214  check_valid(&next, &ref, &start, mult, pt, WEEKEND_ADJ_NONE);
215  if (!test_equal(&next, &true_next))
216  {
217  gchar s1[21], s2[21], s3[21];
218  g_date_strftime(s1, 20, "%x", &start);
219  g_date_strftime(s2, 20, "%x", &ref);
220  g_date_strftime(s3, 20, "%x", &true_next);
221  printf("pt = %d; mult = %d; start = %s; ref = %s; true_next = %s\n",
222  pt, mult, s1, s2, s3);
223  }
224 }
225 
226 #if 0
227 static void test_nth(GDateMonth sm, GDateDay sd, GDateYear sy,
228  GDateMonth nm, GDateDay nd, GDateYear ny,
229  gint diff, PeriodType pt)
230 {
231  GDate start, next;
232  gint d;
233 
234  g_date_set_dmy(&start, sd, sm, sy);
235  g_date_set_dmy(&next, nd, nm, ny);
236 
237  d = nth_weekday_compare(&start, &next, pt);
238  do_test(d == diff, "nth");
239 }
240 
241 static void test_nth_compare()
242 {
243  test_nth(4, 1, 2005, 4, 2, 2005, -1, PERIOD_NTH_WEEKDAY);
244  test_nth(4, 1, 2005, 4, 4, 2005, -3, PERIOD_NTH_WEEKDAY);
245  test_nth(4, 1, 2005, 4, 7, 2005, -6, PERIOD_NTH_WEEKDAY);
246  test_nth(4, 1, 2005, 4, 8, 2005, -7, PERIOD_NTH_WEEKDAY);
247  test_nth(4, 1, 2005, 4, 14, 2005, -13, PERIOD_NTH_WEEKDAY);
248  test_nth(4, 1, 2005, 4, 30, 2005, -29, PERIOD_NTH_WEEKDAY);
249  test_nth(4, 1, 2005, 5, 1, 2005, 5, PERIOD_NTH_WEEKDAY);
250  test_nth(4, 1, 2005, 5, 5, 2005, 1, PERIOD_NTH_WEEKDAY);
251  test_nth(4, 1, 2005, 5, 6, 2005, 0, PERIOD_NTH_WEEKDAY);
252  test_nth(4, 1, 2005, 5, 7, 2005, -1, PERIOD_NTH_WEEKDAY);
253  test_nth(4, 1, 2005, 5, 8, 2005, -2, PERIOD_NTH_WEEKDAY);
254  test_nth(4, 1, 2005, 5, 21, 2005, -15, PERIOD_NTH_WEEKDAY);
255 
256 
257  test_nth(4, 6, 2005, 4, 1, 2005, 5, PERIOD_NTH_WEEKDAY);
258  test_nth(4, 6, 2005, 4, 4, 2005, 2, PERIOD_NTH_WEEKDAY);
259  test_nth(4, 6, 2005, 4, 6, 2005, 0, PERIOD_NTH_WEEKDAY);
260  test_nth(4, 6, 2005, 4, 9, 2005, -3, PERIOD_NTH_WEEKDAY);
261  test_nth(4, 6, 2005, 4, 11, 2005, -5, PERIOD_NTH_WEEKDAY);
262  test_nth(4, 6, 2005, 4, 13, 2005, -7, PERIOD_NTH_WEEKDAY);
263  test_nth(4, 6, 2005, 4, 14, 2005, -8, PERIOD_NTH_WEEKDAY);
264  test_nth(4, 6, 2005, 4, 29, 2005, -23, PERIOD_NTH_WEEKDAY);
265 
266  test_nth(4, 12, 2005, 4, 1, 2005, 11, PERIOD_NTH_WEEKDAY);
267  test_nth(4, 12, 2005, 4, 4, 2005, 8, PERIOD_NTH_WEEKDAY);
268  test_nth(4, 12, 2005, 4, 11, 2005, 1, PERIOD_NTH_WEEKDAY);
269  test_nth(4, 12, 2005, 4, 12, 2005, 0, PERIOD_NTH_WEEKDAY);
270  test_nth(4, 12, 2005, 4, 13, 2005, -1, PERIOD_NTH_WEEKDAY);
271  test_nth(4, 12, 2005, 4, 17, 2005, -5, PERIOD_NTH_WEEKDAY);
272  test_nth(4, 12, 2005, 4, 19, 2005, -7, PERIOD_NTH_WEEKDAY);
273  test_nth(4, 12, 2005, 4, 28, 2005, -16, PERIOD_NTH_WEEKDAY);
274 
275  test_nth(4, 29, 2005, 4, 30, 2005, -1, PERIOD_LAST_WEEKDAY);
276  test_nth(4, 29, 2005, 5, 1, 2005, 26, PERIOD_LAST_WEEKDAY);
277  test_nth(4, 29, 2005, 7, 9, 2005, 20, PERIOD_LAST_WEEKDAY);
278  test_nth(4, 29, 2005, 7, 31, 2005, -2, PERIOD_LAST_WEEKDAY);
279 
280  test_nth(4, 28, 2005, 4, 30, 2005, -2, PERIOD_LAST_WEEKDAY);
281  test_nth(4, 28, 2005, 5, 1, 2005, 25, PERIOD_LAST_WEEKDAY);
282  test_nth(4, 28, 2005, 7, 9, 2005, 19, PERIOD_LAST_WEEKDAY);
283  test_nth(4, 28, 2005, 7, 31, 2005, -3, PERIOD_LAST_WEEKDAY);
284  test_nth(4, 28, 2005, 9, 21, 2005, 8, PERIOD_LAST_WEEKDAY);
285 
286 }
287 #endif
288 static void test_some()
289 {
290  test_specific(PERIOD_NTH_WEEKDAY, 1, 4, 1, 2005, 4, 2, 2005, 5, 6, 2005);
291  test_specific(PERIOD_NTH_WEEKDAY, 1, 7, 14, 2005, 11, 15, 2005, 12, 8, 2005);
292  test_specific(PERIOD_NTH_WEEKDAY, 1, 7, 14, 2005, 11, 5, 2005, 11, 10, 2005);
293  test_specific(PERIOD_NTH_WEEKDAY, 1, 4, 1, 2005, 4, 2, 2005, 5, 6, 2005);
294  test_specific(PERIOD_NTH_WEEKDAY, 1, 4, 1, 2005, 4, 2, 2005, 5, 6, 2005);
295 
296  test_specific(PERIOD_LAST_WEEKDAY, 1, 4, 29, 2005, 4, 30, 2005, 5, 27, 2005);
297  test_specific(PERIOD_LAST_WEEKDAY, 1, 4, 29, 2005, 5, 1, 2005, 5, 27, 2005);
298  test_specific(PERIOD_LAST_WEEKDAY, 1, 4, 29, 2005, 7, 9, 2005, 7, 29, 2005);
299  test_specific(PERIOD_LAST_WEEKDAY, 1, 4, 29, 2005, 6, 30, 2005, 7, 29, 2005);
300  test_specific(PERIOD_LAST_WEEKDAY, 1, 4, 29, 2005, 7, 31, 2005, 8, 26, 2005);
301 
302  test_specific(PERIOD_NTH_WEEKDAY, 2, 4, 27, 2005, 4, 27, 2005, 6, 22, 2005);
303  //exit(1);
304  //return;
305  test_specific(PERIOD_YEAR, 3, 9, 8, 838, 6, 30, 1094, 9, 8, 1096);
306  test_specific(PERIOD_YEAR, 2, 9, 8, 838, 6, 30, 1094, 9, 8, 1094);
307  test_specific(PERIOD_YEAR, 1, 1, 10, 1000, 1, 5, 1002, 1, 10, 1002);
308  //return;
309  test_specific(PERIOD_MONTH, 1, 1, 12, 1, 2, 6, 1, 2, 12, 1);
310 
311  test_specific(PERIOD_MONTH, 1, 1, 12, 1, 2, 12, 1, 3, 12, 1);
312  test_specific(PERIOD_MONTH, 1, 1, 12, 1, 2, 20, 1, 3, 12, 1);
313  test_specific(PERIOD_MONTH, 1, 1, 30, 1, 2, 28, 1, 3, 30, 1);
314  test_specific(PERIOD_MONTH, 1, 1, 30, 1, 2, 27, 1, 2, 28, 1);
315  test_specific(PERIOD_MONTH, 1, 2, 28, 1, 3, 30, 1, 4, 28, 1);
316 
317  test_specific(PERIOD_END_OF_MONTH, 1, 2, 28, 1, 3, 30, 1, 3, 31, 1);
318  test_specific(PERIOD_END_OF_MONTH, 5, 4, 30, 1, 4, 21, 1, 4, 30, 1);
319  test_specific(PERIOD_END_OF_MONTH, 5, 2, 28, 1, 5, 21, 1, 7, 31, 1);
320 
321  test_specific(PERIOD_YEAR, 7, 6, 8, 199, 9, 10, 1338, 6, 8, 1340);
322  test_specific(PERIOD_YEAR, 2, 9, 8, 838, 6, 30, 1094, 9, 8, 1094);
323 
324  test_specific(PERIOD_YEAR, 1, 5, 2, 13, 1, 11, 101, 5, 2, 101);
325  test_specific(PERIOD_DAY, 7, 4, 1, 2000, 4, 8, 2000, 4, 15, 2000);
326 }
327 
328 static void test_use()
329 {
330  Recurrence *r;
331 
332  r = g_new(Recurrence, 1);
333  do_test(r != NULL, "allocation");
334  g_free(r);
335 }
336 
337 static void test_main()
338 {
339 
340  book = qof_book_new ();
341 
342  test_use();
343 
344  test_some();
345 
346  test_all();
347 
348  qof_book_destroy (book);
349 }
350 
351 
352 int
353 main (int argc, char **argv)
354 {
355  qof_init();
356 
357  g_log_set_always_fatal( G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING );
358 
359 #if 0
360  set_success_print(TRUE);
361 #endif
362 
363  test_main();
364 
365  print_test_results();
366  return get_rv();
367 }
QofBook * qof_book_new(void)
All type declarations for the whole Gnucash engine.
void qof_init(void)
Initialise the Query Object Framework.
void qof_book_destroy(QofBook *book)