GnuCash 2.4.99
test-recurrence.c
00001 /* Copyright (C) 2005, Chris Shoemaker <c.shoemaker@cox.net>
00002  *
00003  * This program is free software; you can redistribute it and/or
00004  * modify it under the terms of the GNU General Public License as
00005  * published by the Free Software Foundation; either version 2 of
00006  * the License, or (at your option) any later version.
00007  *
00008  * This program is distributed in the hope that it will be useful,
00009  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  * GNU General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU General Public License
00014  * along with this program; if not, contact:
00015  *
00016  * Free Software Foundation           Voice:  +1-617-542-5942
00017  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
00018  * Boston, MA  02110-1301,  USA       gnu@gnu.org
00019  */
00020 
00021 #include "config.h"
00022 
00023 #include <stdio.h>
00024 #include <stdlib.h>
00025 #include <glib.h>
00026 
00027 #include "test-stuff.h"
00028 #include "Recurrence.h"
00029 #include "gnc-engine.h"
00030 
00031 static QofBook *book;
00032 
00033 static void check_valid(GDate *next, GDate *ref, GDate *start,
00034                         guint16 mult, PeriodType pt, WeekendAdjust wadj)
00035 {
00036     gboolean valid;
00037     gint startToNext;
00038     /* FIXME: The WeekendAdjust argument is completely ignored for
00039        now. */
00040 
00041     valid = g_date_valid(next);
00042     if (pt == PERIOD_ONCE && g_date_compare(start, ref) <= 0)
00043         do_test(!valid, "incorrectly valid");
00044     else
00045         do_test(valid, "incorrectly invalid");
00046 
00047     if (!valid) return;
00048 
00049     do_test(g_date_compare(ref, next) < 0,
00050             "next date not strictly later than ref date");
00051     startToNext = g_date_get_julian(next) - g_date_get_julian(start);
00052 
00053     // Phase test
00054     switch (pt)
00055     {
00056     case PERIOD_YEAR:
00057         do_test((g_date_get_year(next) - g_date_get_year(start)) % mult == 0,
00058                 "year period phase wrong"); // redundant
00059         mult *= 12;
00060         // fall through
00061     case PERIOD_END_OF_MONTH:
00062         if (pt == PERIOD_END_OF_MONTH)
00063             do_test(g_date_is_last_of_month(next), "end of month phase wrong");
00064         // fall through
00065     case PERIOD_LAST_WEEKDAY:
00066     case PERIOD_NTH_WEEKDAY:
00067     case PERIOD_MONTH:
00068     {
00069         gint monthdiff;
00070         GDateDay day_start, day_next;
00071 
00072         monthdiff = (g_date_get_month(next) - g_date_get_month(start)) +
00073                     12 * (g_date_get_year(next) - g_date_get_year(start));
00074         do_test(monthdiff % mult == 0, "month or year phase wrong");
00075 
00076         if (pt == PERIOD_NTH_WEEKDAY || pt == PERIOD_LAST_WEEKDAY)
00077         {
00078             guint sweek, nweek;
00079 
00080             do_test(g_date_get_weekday(next) == g_date_get_weekday(start),
00081                     "weekday phase wrong");
00082             sweek = (g_date_get_day(start) - 1) / 7;
00083             nweek = (g_date_get_day(next) - 1) / 7;
00084 
00085             /* 3 cases: either the weeks agree, OR 'next' didn't have
00086                5 of the weekday that 'start' did, so it's only the
00087                4th, OR 'start' didn't have 5 of the weekday that
00088                'next' does and we want the LAST weekday, so it's the
00089                5th of that weekday */
00090             do_test(sweek == nweek ||
00091                     (sweek == 4 && nweek == 3 && (g_date_get_day(next) + 7) >
00092                      g_date_get_days_in_month(
00093                          g_date_get_month(next), g_date_get_year(next))) ||
00094                     (sweek == 3 && nweek == 4 && (pt == PERIOD_LAST_WEEKDAY)),
00095                     "week of month phase wrong");
00096 
00097         }
00098         else
00099         {
00100             day_start = g_date_get_day(start);
00101             day_next = g_date_get_day(next);
00102             if (day_start < 28)
00103                 do_test(day_start == day_next, "dom don't match");
00104             else if (pt != PERIOD_END_OF_MONTH)
00105             {
00106                 // the end of month case was already checked above.  near
00107                 // the end of the month, the days should still agree,
00108                 // unless they can't because of a short month.
00109                 do_test(day_start == day_next || g_date_is_last_of_month(next),
00110                         "dom don't match and next is not eom");
00111             }
00112         }
00113     }
00114     break;
00115     case PERIOD_WEEK:
00116         mult *= 7;
00117         // fall through
00118     case PERIOD_DAY:
00119         do_test((startToNext % mult) == 0, "week or day period phase wrong");
00120         break;
00121     case PERIOD_ONCE:
00122         do_test(startToNext == 0, "period once not on start date");
00123         break;
00124     default:
00125         do_test(FALSE, "invalid PeriodType");
00126         break;
00127     }
00128 
00129 }
00130 
00131 #define NUM_DATES_TO_TEST 300
00132 #define NUM_DATES_TO_TEST_REF 300
00133 #define NUM_MULT_TO_TEST 10
00134 #define JULIAN_START 2003*365     // years have to be < 10000
00135 
00136 /* Mult of zero is usually not valid, but it gets regularized to 1, so
00137    the effect is just that we end up testing mult of 1 twice, plus the
00138    regularization. */
00139 static void test_all()
00140 {
00141     Recurrence r;
00142     GDate d_start, d_start_reg;
00143     GDate d_ref, d_next;
00144     guint16 mult, mult_reg;
00145     PeriodType pt, pt_reg;
00146     WeekendAdjust wadj, wadj_reg;
00147     gint32 j1, j2;
00148     gint i_ref;
00149 
00150     for (pt = PERIOD_ONCE; pt < NUM_PERIOD_TYPES; pt++)
00151     {
00152         for (wadj = WEEKEND_ADJ_NONE; wadj < NUM_WEEKEND_ADJS; wadj++)
00153         {
00154             for (j1 = JULIAN_START; j1 < JULIAN_START + NUM_DATES_TO_TEST; j1++)
00155             {
00156                 g_date_set_julian(&d_start, j1);
00157                 for (i_ref = 0; i_ref < NUM_DATES_TO_TEST_REF; i_ref++)
00158                 {
00159                     j2 = (guint32) get_random_int_in_range(1, 1 << 19);
00160                     g_date_set_julian(&d_ref, j2);
00161 
00162                     for (mult = 0; mult < NUM_MULT_TO_TEST; mult++)
00163                     {
00164                         recurrenceSet(&r, mult, pt, &d_start, wadj);
00165                         pt_reg = recurrenceGetPeriodType(&r);
00166                         d_start_reg = recurrenceGetDate(&r);
00167                         mult_reg = recurrenceGetMultiplier(&r);
00168                         wadj_reg = recurrenceGetWeekendAdjust(&r);
00169 
00170                         recurrenceNextInstance(&r, &d_ref, &d_next);
00171                         check_valid(&d_next, &d_ref, &d_start_reg,
00172                                     mult_reg, pt_reg, wadj_reg);
00173 
00174                     }
00175                 }
00176             }
00177         }
00178     }
00179 }
00180 
00181 static gboolean test_equal(GDate *d1, GDate *d2)
00182 {
00183     if (!do_test(g_date_compare(d1, d2) == 0, "dates don't match"))
00184     {
00185         gchar s1[21];
00186         gchar s2[21];
00187         g_date_strftime(s1, 20, "%x", d1);
00188         g_date_strftime(s2, 20, "%x", d2);
00189 
00190         printf("%s != %s\n", s1, s2);
00191         return FALSE;
00192     }
00193     return TRUE;
00194 }
00195 
00196 
00197 static void test_specific(PeriodType pt, guint16 mult,
00198                           GDateMonth sm, GDateDay sd, GDateYear sy,
00199                           GDateMonth rm, GDateDay rd, GDateYear ry,
00200                           GDateMonth nm, GDateDay nd, GDateYear ny)
00201 {
00202     GDate start;
00203     GDate ref, next, true_next;
00204     Recurrence r;
00205 
00206     g_date_set_dmy(&start, sd, sm, sy);
00207     g_date_set_dmy(&ref, rd, rm, ry);
00208     g_date_set_dmy(&true_next, nd, nm, ny);
00209 
00210 
00211     recurrenceSet(&r, mult, pt, &start, WEEKEND_ADJ_NONE);
00212     recurrenceNextInstance(&r, &ref, &next);
00213 
00214     check_valid(&next, &ref, &start, mult, pt, WEEKEND_ADJ_NONE);
00215     if (!test_equal(&next, &true_next))
00216     {
00217         gchar s1[21], s2[21], s3[21];
00218         g_date_strftime(s1, 20, "%x", &start);
00219         g_date_strftime(s2, 20, "%x", &ref);
00220         g_date_strftime(s3, 20, "%x", &true_next);
00221         printf("pt = %d; mult = %d; start = %s; ref = %s; true_next = %s\n",
00222                pt, mult, s1, s2, s3);
00223     }
00224 }
00225 
00226 #if 0
00227 static void test_nth(GDateMonth sm, GDateDay sd, GDateYear sy,
00228                      GDateMonth nm, GDateDay nd, GDateYear ny,
00229                      gint diff, PeriodType pt)
00230 {
00231     GDate start, next;
00232     gint d;
00233 
00234     g_date_set_dmy(&start, sd, sm, sy);
00235     g_date_set_dmy(&next, nd, nm, ny);
00236 
00237     d = nth_weekday_compare(&start, &next, pt);
00238     do_test(d == diff, "nth");
00239 }
00240 
00241 static void test_nth_compare()
00242 {
00243     test_nth(4, 1, 2005,   4, 2, 2005, -1, PERIOD_NTH_WEEKDAY);
00244     test_nth(4, 1, 2005,   4, 4, 2005, -3, PERIOD_NTH_WEEKDAY);
00245     test_nth(4, 1, 2005,   4, 7, 2005, -6, PERIOD_NTH_WEEKDAY);
00246     test_nth(4, 1, 2005,   4, 8, 2005, -7, PERIOD_NTH_WEEKDAY);
00247     test_nth(4, 1, 2005,   4, 14, 2005, -13, PERIOD_NTH_WEEKDAY);
00248     test_nth(4, 1, 2005,   4, 30, 2005, -29, PERIOD_NTH_WEEKDAY);
00249     test_nth(4, 1, 2005,   5, 1, 2005, 5, PERIOD_NTH_WEEKDAY);
00250     test_nth(4, 1, 2005,   5, 5, 2005, 1, PERIOD_NTH_WEEKDAY);
00251     test_nth(4, 1, 2005,   5, 6, 2005, 0, PERIOD_NTH_WEEKDAY);
00252     test_nth(4, 1, 2005,   5, 7, 2005, -1, PERIOD_NTH_WEEKDAY);
00253     test_nth(4, 1, 2005,   5, 8, 2005, -2, PERIOD_NTH_WEEKDAY);
00254     test_nth(4, 1, 2005,   5, 21, 2005, -15, PERIOD_NTH_WEEKDAY);
00255 
00256 
00257     test_nth(4, 6, 2005,   4, 1, 2005, 5, PERIOD_NTH_WEEKDAY);
00258     test_nth(4, 6, 2005,   4, 4, 2005, 2, PERIOD_NTH_WEEKDAY);
00259     test_nth(4, 6, 2005,   4, 6, 2005, 0, PERIOD_NTH_WEEKDAY);
00260     test_nth(4, 6, 2005,   4, 9, 2005, -3, PERIOD_NTH_WEEKDAY);
00261     test_nth(4, 6, 2005,   4, 11, 2005, -5, PERIOD_NTH_WEEKDAY);
00262     test_nth(4, 6, 2005,   4, 13, 2005, -7, PERIOD_NTH_WEEKDAY);
00263     test_nth(4, 6, 2005,   4, 14, 2005, -8, PERIOD_NTH_WEEKDAY);
00264     test_nth(4, 6, 2005,   4, 29, 2005, -23, PERIOD_NTH_WEEKDAY);
00265 
00266     test_nth(4, 12, 2005,   4, 1, 2005, 11, PERIOD_NTH_WEEKDAY);
00267     test_nth(4, 12, 2005,   4, 4, 2005, 8, PERIOD_NTH_WEEKDAY);
00268     test_nth(4, 12, 2005,   4, 11, 2005, 1, PERIOD_NTH_WEEKDAY);
00269     test_nth(4, 12, 2005,   4, 12, 2005, 0, PERIOD_NTH_WEEKDAY);
00270     test_nth(4, 12, 2005,   4, 13, 2005, -1, PERIOD_NTH_WEEKDAY);
00271     test_nth(4, 12, 2005,   4, 17, 2005, -5, PERIOD_NTH_WEEKDAY);
00272     test_nth(4, 12, 2005,   4, 19, 2005, -7, PERIOD_NTH_WEEKDAY);
00273     test_nth(4, 12, 2005,   4, 28, 2005, -16, PERIOD_NTH_WEEKDAY);
00274 
00275     test_nth(4, 29, 2005,   4, 30, 2005, -1, PERIOD_LAST_WEEKDAY);
00276     test_nth(4, 29, 2005,   5, 1, 2005, 26, PERIOD_LAST_WEEKDAY);
00277     test_nth(4, 29, 2005,   7, 9, 2005, 20, PERIOD_LAST_WEEKDAY);
00278     test_nth(4, 29, 2005,   7, 31, 2005, -2, PERIOD_LAST_WEEKDAY);
00279 
00280     test_nth(4, 28, 2005,   4, 30, 2005, -2, PERIOD_LAST_WEEKDAY);
00281     test_nth(4, 28, 2005,   5, 1, 2005, 25, PERIOD_LAST_WEEKDAY);
00282     test_nth(4, 28, 2005,   7, 9, 2005, 19, PERIOD_LAST_WEEKDAY);
00283     test_nth(4, 28, 2005,   7, 31, 2005, -3, PERIOD_LAST_WEEKDAY);
00284     test_nth(4, 28, 2005,   9, 21, 2005, 8, PERIOD_LAST_WEEKDAY);
00285 
00286 }
00287 #endif
00288 static void test_some()
00289 {
00290     test_specific(PERIOD_NTH_WEEKDAY, 1, 4, 1, 2005,    4, 2, 2005,  5, 6, 2005);
00291     test_specific(PERIOD_NTH_WEEKDAY, 1, 7, 14, 2005,   11, 15, 2005,  12, 8, 2005);
00292     test_specific(PERIOD_NTH_WEEKDAY, 1, 7, 14, 2005,   11, 5, 2005,  11, 10, 2005);
00293     test_specific(PERIOD_NTH_WEEKDAY, 1, 4, 1, 2005,    4, 2, 2005,  5, 6, 2005);
00294     test_specific(PERIOD_NTH_WEEKDAY, 1, 4, 1, 2005,    4, 2, 2005,  5, 6, 2005);
00295 
00296     test_specific(PERIOD_LAST_WEEKDAY, 1, 4, 29, 2005,    4, 30, 2005,  5, 27, 2005);
00297     test_specific(PERIOD_LAST_WEEKDAY, 1, 4, 29, 2005,    5, 1, 2005,  5, 27, 2005);
00298     test_specific(PERIOD_LAST_WEEKDAY, 1, 4, 29, 2005,    7, 9, 2005,  7, 29, 2005);
00299     test_specific(PERIOD_LAST_WEEKDAY, 1, 4, 29, 2005,    6, 30, 2005,  7, 29, 2005);
00300     test_specific(PERIOD_LAST_WEEKDAY, 1, 4, 29, 2005,    7, 31, 2005,  8, 26, 2005);
00301 
00302     test_specific(PERIOD_NTH_WEEKDAY, 2, 4, 27, 2005,    4, 27, 2005,  6, 22, 2005);
00303     //exit(1);
00304     //return;
00305     test_specific(PERIOD_YEAR,          3,   9, 8, 838,    6, 30, 1094,  9, 8, 1096);
00306     test_specific(PERIOD_YEAR,          2,   9, 8, 838,    6, 30, 1094,  9, 8, 1094);
00307     test_specific(PERIOD_YEAR,          1,   1, 10, 1000,  1, 5, 1002,  1, 10, 1002);
00308     //return;
00309     test_specific(PERIOD_MONTH, 1,     1, 12, 1,    2, 6, 1,    2, 12, 1);
00310 
00311     test_specific(PERIOD_MONTH, 1,     1, 12, 1,    2, 12, 1,   3, 12, 1);
00312     test_specific(PERIOD_MONTH, 1,     1, 12, 1,    2, 20, 1,   3, 12, 1);
00313     test_specific(PERIOD_MONTH, 1,     1, 30, 1,    2, 28, 1,   3, 30, 1);
00314     test_specific(PERIOD_MONTH, 1,     1, 30, 1,    2, 27, 1,   2, 28, 1);
00315     test_specific(PERIOD_MONTH, 1,     2, 28, 1,    3, 30, 1,   4, 28, 1);
00316 
00317     test_specific(PERIOD_END_OF_MONTH, 1,   2, 28, 1,    3, 30, 1,   3, 31, 1);
00318     test_specific(PERIOD_END_OF_MONTH, 5,   4, 30, 1,    4, 21, 1,  4, 30, 1);
00319     test_specific(PERIOD_END_OF_MONTH, 5,   2, 28, 1,    5, 21, 1,  7, 31, 1);
00320 
00321     test_specific(PERIOD_YEAR,          7,   6, 8, 199,    9, 10, 1338,  6, 8, 1340);
00322     test_specific(PERIOD_YEAR,          2,   9, 8, 838,    6, 30, 1094,  9, 8, 1094);
00323 
00324     test_specific(PERIOD_YEAR, 1,    5, 2, 13, 1, 11, 101,   5, 2, 101);
00325     test_specific(PERIOD_DAY, 7,    4, 1, 2000,    4, 8, 2000,  4, 15, 2000);
00326 }
00327 
00328 static void test_use()
00329 {
00330     Recurrence *r;
00331 
00332     r = g_new(Recurrence, 1);
00333     do_test(r != NULL, "allocation");
00334     g_free(r);
00335 }
00336 
00337 static void test_main()
00338 {
00339 
00340     book = qof_book_new ();
00341 
00342     test_use();
00343 
00344     test_some();
00345 
00346     test_all();
00347 
00348     qof_book_destroy (book);
00349 }
00350 
00351 
00352 int
00353 main (int argc, char **argv)
00354 {
00355     qof_init();
00356 
00357     g_log_set_always_fatal( G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING );
00358 
00359 #if 0
00360     set_success_print(TRUE);
00361 #endif
00362 
00363     test_main();
00364 
00365     print_test_results();
00366     return get_rv();
00367 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines