]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
[Bug 1771] fixed and test cases provided
authorJuergen Perlinger <perlinger@ntp.org>
Mon, 3 Jan 2011 19:19:11 +0000 (20:19 +0100)
committerJuergen Perlinger <perlinger@ntp.org>
Mon, 3 Jan 2011 19:19:11 +0000 (20:19 +0100)
bk: 4d22212fQeUPWLuU93HjEbprYmy3PQ

ChangeLog
include/ntp_calendar.h
libntp/clocktime.c
libntp/ntp_calendar.c
tests/libntp/caljulian.cpp
tests/libntp/calyearstart.cpp
tests/libntp/clocktime.cpp
tests/libntp/libntptest.cpp
tests/libntp/libntptest.h

index 955e438a5d1f5d4ec48a4a30f41a984843c990a7..acc2c313f10d171bc6be1db0e092c1ffe7aea4f6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,5 @@
+* [Bug 1771] algorithmic error in 'clocktime()' fixed;
+  Unit test extended for fixed system time
 (4.2.7p109) 2011/01/02 Released by Harlan Stenn <stenn@ntp.org>
 * Remove nearly all strcpy() and most strcat() from NTP distribution.
   One major pocket remains in ntp_crypto.c.  libopts & libisc also have
index 6804a184bb50f762dcc005631dbad2e433b690e4..012fb1be9f749608157878cfc7818dd8ffabb3c9 100644 (file)
@@ -34,6 +34,15 @@ typedef struct {
        int32 lo;
 } ntpcal_split;
 
+typedef time_t (*systime_func_ptr)(time_t*);
+
+/*
+ * set the function for getting the system time. This is mostly used for
+ * unit testing to provide a fixed / shifted time stamp. Setting the
+ * value to NULL restores the original function, that is, 'time()',
+ * which is also the automatic default.
+ */
+extern systime_func_ptr ntpcal_set_timefunc(systime_func_ptr);
 
 /*
  * days-of-week
index 2781c68008e686c6f464f25950e5659698936288..c1a3ba09c0af2c400838c2e0ed333137662304b9 100644 (file)
@@ -8,13 +8,6 @@
 #include "ntp_stdlib.h"
 #include "ntp_calendar.h"
 
-/*
- * Hacks to avoid excercising the multiplier.  I have no pride.
- */
-#define        MULBY10(x)      (((x)<<3) + ((x)<<1))
-#define        MULBY60(x)      (((x)<<6) - ((x)<<2))   /* watch overflow */
-#define        MULBY24(x)      (((x)<<4) + ((x)<<3))
-
 /*
  * We check that the time be within CLOSETIME seconds of the receive
  * time stamp. This is about 4 hours, which hopefully should be wide
  */
 #define NEARTIME       (182u * SECSPERDAY)
 
-/*
- * For a final check, we use a limit that is exactly half of a leap year
- */
-#define NEARLIMIT      (183u * SECSPERDAY)
-
 /*
  * local calendar helpers
  */
@@ -80,21 +68,20 @@ clocktime(
         * Compute the offset into the year in seconds.  Note that
         * this could come out to be a negative number.
         */
-       tmp = yday-1;
-       tmp = MULBY24(tmp) + hour + tzoff;
-       tmp = MULBY60(tmp) + (long)minute;
-       tmp = MULBY60(tmp) + (long)second;
-       
+       tmp = ((int32)second +
+              SECSPERMIN * ((int32)minute +
+                            MINSPERHR * ((int32)hour + (int32)tzoff +
+                                         HRSPERDAY * ((int32)yday - 1))));
        /*
         * Based on the cached year start, do a first attempt. Be
         * happy and return if this gets us better than NEARTIME to
         * the receive time stamp. Do this only if the cached year
-        * start is not zero, which will no happen after 1900 for the
+        * start is not zero, which will not happen after 1900 for the
         * next few thousand years.
         */
        if (*yearstart) {
                /* -- get time stamp of potential solution */
-               test[0] = (u_int32)*yearstart + tmp;
+               test[0] = (u_int32)(*yearstart) + tmp;
                /* -- calc absolute difference to receive time */
                diff[0] = test[0] - rec_ui;
                if (diff[0] >= 0x80000000u)
@@ -116,10 +103,7 @@ clocktime(
         * around the guess and select the entry with the minimum
         * absolute difference to the receive time stamp.
         */
-       y = ntp_to_year(rec_ui)
-         + (tmp / SECSPERAVGYEAR)
-         - (tmp < 0); /* get close */
-
+       y = ntp_to_year(rec_ui - tmp);
        for (idx = 0; idx < 3; idx++) {
                /* -- get year start of potential solution */
                ystt[idx] = year_to_ntp(y + idx - 1);
@@ -134,12 +118,9 @@ clocktime(
        for (min = 1, idx = 0; idx < 3; idx++)
                if (diff[idx] < diff[min])
                        min = idx;
-       if (diff[min] <= NEARLIMIT) {   
-           /* -*- store results and update year start */
-           *ts_ui     = test[min];
-           *yearstart = ystt[min];
-       } else
-           *ts_ui = rec_ui; /* emergency fallback */
+       /* -*- store results and update year start */
+       *ts_ui     = test[min];
+       *yearstart = ystt[min];
 
        /* -*- tell if we could get into CLOSETIME*/
        return diff[min] < CLOSETIME;
index 2821ff312217a35508e046b7436c7114e34ef299..ab1a18a77e192ed070457da51563be1880d33e75 100644 (file)
 #include "ntp_fp.h"
 #include "ntp_unixtime.h"
 
+/*
+ *---------------------------------------------------------------------
+ * replacing the 'time()' function
+ * --------------------------------------------------------------------
+ */
+
+static systime_func_ptr systime_func = time;
+
+systime_func_ptr
+ntpcal_set_timefunc(
+    systime_func_ptr nfunc)
+{
+    systime_func_ptr res = systime_func;
+
+    if (!nfunc)
+       nfunc = time;
+    systime_func = nfunc;
+
+    return res;
+}    
+
+
+
+static time_t
+now()
+{
+    if (systime_func)
+       return (*systime_func)(NULL);
+    else
+       return time(NULL);
+}
+
 /*
  *---------------------------------------------------------------------
  * basic calendar stuff
@@ -212,7 +244,7 @@ ntpcal_ntp_to_time(
 
 #ifdef HAVE_INT64
 
-       res.q_s = pivot ? *pivot : time(NULL);
+       res.q_s = pivot ? *pivot : now();
        
        res.Q_s -= 0x80000000u;         /* unshift of half range */
        ntp     -= (u_int32)JAN_1970;   /* warp into UN*X domain */
@@ -221,7 +253,7 @@ ntpcal_ntp_to_time(
 
 #else /* no 64bit scalars */
        
-       time_t tmp = pivot ? *pivot : time(NULL);
+       time_t tmp = pivot ? *pivot : now();
 
        /*
         * shifting negative signed quantities is compiler-dependent, so
@@ -273,7 +305,7 @@ ntpcal_ntp_to_ntp(
 
 #ifdef HAVE_INT64
 
-       res.q_s = pivot ? *pivot : time(NULL);
+       res.q_s = pivot ? *pivot : now();
 
        res.Q_s -= 0x80000000u;         /* unshift of half range */
        res.Q_s += (u_int32)JAN_1970;   /* warp into NTP domain  */
@@ -282,7 +314,7 @@ ntpcal_ntp_to_ntp(
 
 #else /* no 64bit scalars */
        
-       time_t tmp = pivot ? *pivot : time(NULL);
+       time_t tmp = pivot ? *pivot : now();
 
        /*
         * shifting negative signed quantities is compiler-dependent, so
index 2d974dd340884215c8b1dea44ce4cb6b9c6223f5..d9cc952933e7e60b23dfe7bf00c9f68b1d05c9ee 100644 (file)
@@ -9,6 +9,9 @@ extern "C" {
 
 class caljulianTest : public libntptest {
 protected:
+       virtual void SetUp();
+       virtual void TearDown();
+
        std::string CalendarToString(const calendar &cal) {
                std::ostringstream ss;
                ss << cal.year << "-" << (u_int)cal.month << "-" << (u_int)cal.monthday
@@ -34,6 +37,18 @@ protected:
        }
 };
 
+void caljulianTest::SetUp()
+{
+    ntpcal_set_timefunc(timefunc);
+    settime(1970, 1, 1, 0, 0, 0);
+}
+
+void caljulianTest::TearDown()
+{
+    ntpcal_set_timefunc(NULL);
+}
+
+
 TEST_F(caljulianTest, RegularTime) {
        u_long testDate = 3485080800UL; // 2010-06-09 14:00:00
        calendar expected = {2010,160,6,9,14,0,0};
index 59ac1f8e50db32d206f6d75acf6eaac55fb137a0..774edf3eae44c694d9b4b94feae680512ece84c6 100644 (file)
@@ -1,33 +1,43 @@
 #include "libntptest.h"
 
-/*
- * calyearstart uses a pivot time, which defaults to the current system
- * time if not given. Since this is not a good idea in an regression
- * test, we use 2020-01-01 for the pivot.
- */
-
-static const time_t hold = 1577836800; // 2020-01-01 00:00:00
-
 class calyearstartTest : public libntptest {
+protected:
+       virtual void SetUp();
+       virtual void TearDown();
 };
 
+void calyearstartTest::SetUp()
+{
+    ntpcal_set_timefunc(timefunc);
+    settime(1970, 1, 1, 0, 0, 0);
+}
+
+void calyearstartTest::TearDown()
+{
+    ntpcal_set_timefunc(NULL);
+}
+
+
 TEST_F(calyearstartTest, NoWrapInDateRange) {
        const u_int32 input = 3486372600UL; // 2010-06-24 12:50:00.
        const u_int32 expected = 3471292800UL; // 2010-01-01 00:00:00
 
-       EXPECT_EQ(expected, calyearstart(input, &hold));
+       EXPECT_EQ(expected, calyearstart(input, &nowtime));
+       EXPECT_EQ(expected, calyearstart(input, NULL));
 }
 
 TEST_F(calyearstartTest, NoWrapInDateRangeLeapYear) {
        const u_int32 input = 3549528000UL; // 2012-06-24 12:00:00
        const u_int32 expected = 3534364800UL; // 2012-01-01 00:00:00
 
-       EXPECT_EQ(expected, calyearstart(input, &hold));
+       EXPECT_EQ(expected, calyearstart(input, &nowtime));
+       EXPECT_EQ(expected, calyearstart(input, NULL));
 }
 
 TEST_F(calyearstartTest, WrapInDateRange) {
        const u_int32 input = 19904UL; // 2036-02-07 12:00:00
        const u_int32 expected = 4291747200UL; // 2036-01-01 00:00:00
 
-       EXPECT_EQ(expected, calyearstart(input, &hold));
+       EXPECT_EQ(expected, calyearstart(input, &nowtime));
+       EXPECT_EQ(expected, calyearstart(input, NULL));
 }
index 3c7aa67ccf9c8892b1d87820129814d5563fe62f..efda8c5c8e6c6998c6c80c7390133c776081122b 100644 (file)
@@ -1,18 +1,35 @@
 #include "libntptest.h"
 
-/*
- * clocktime() uses calyearstart(), which in uses caljulian().
- * caljulian() should be ideally be mocked, because it uses
- * the current system time.
- */
+// ---------------------------------------------------------------------
+// test fixture
+//
+// The clocktimeTest uses the NTP calendar feature to use a mockup
+// function for getting the current system time, so the tests are not
+// dependent on the actual system time.
 
 class clocktimeTest : public libntptest {
+       virtual void SetUp();
+       virtual void TearDown();
 };
 
+void clocktimeTest::SetUp()
+{
+    ntpcal_set_timefunc(timefunc);
+    settime(2000, 1, 1, 0, 0, 0);
+}
+
+void clocktimeTest::TearDown()
+{
+    ntpcal_set_timefunc(NULL);
+}
+
+// ---------------------------------------------------------------------
+// test cases
+
 TEST_F(clocktimeTest, CurrentYear) {
        // Timestamp: 2010-06-24 12:50:00Z
-       const u_long timestamp = 3486372600UL;
-       const u_int32 expected = timestamp; // exactly the same.
+       const u_int32 timestamp = 3486372600UL;
+       const u_int32 expected  = timestamp; // exactly the same.
 
        const int yday=175, hour=12, minute=50, second=0, tzoff=0;
 
@@ -33,8 +50,8 @@ TEST_F(clocktimeTest, CurrentYearFuzz) {
         * timestamp for the 12:00:00 time.
         */
 
-       const u_long timestamp = 3486372600UL; // 2010-06-24 12:50:00Z
-       const u_int32 expected = 3486369600UL; // 2010-06-24 12:00:00Z
+       const u_int32 timestamp = 3486372600UL; // 2010-06-24 12:50:00Z
+       const u_int32 expected  = 3486369600UL; // 2010-06-24 12:00:00Z
 
        const int yday=175, hour=12, minute=0, second=0, tzoff=0;
 
@@ -53,8 +70,8 @@ TEST_F(clocktimeTest, TimeZoneOffset) {
         *
         * Time sent into function is 04:00:00 +0800
         */
-       const u_long timestamp = 3486369600UL;
-       const u_int32 expected = timestamp;
+       const u_int32 timestamp = 3486369600UL;
+       const u_int32 expected  = timestamp;
 
        const int yday=175, hour=4, minute=0, second=0, tzoff=8;
 
@@ -72,8 +89,8 @@ TEST_F(clocktimeTest, WrongYearStart) {
         * Time sent into function is 11:00:00.
         * Yearstart sent into function is the yearstart of 2009!
         */
-       const u_long timestamp = 3471418800UL;
-       const u_int32 expected = timestamp;
+       const u_int32 timestamp = 3471418800UL;
+       const u_int32 expected  = timestamp;
 
        const int yday=2, hour=11, minute=0, second=0, tzoff=0;
 
@@ -91,8 +108,8 @@ TEST_F(clocktimeTest, PreviousYear) {
         * Time sent into function is 23:00:00
         * (which is meant to be 2009-12-31 23:00:00Z)
         */
-       const u_long timestamp = 3471296400UL;
-       const u_int32 expected = 3471289200UL;
+       const u_int32 timestamp = 3471296400UL;
+       const u_int32 expected  = 3471289200UL;
 
        const int yday=365, hour=23, minute=0, second=0, tzoff=0;
 
@@ -110,8 +127,8 @@ TEST_F(clocktimeTest, NextYear) {
         * Time sent into function is 01:00:00
         * (which is meant to be 2010-01-01 01:00:00Z)
         */
-       const u_long timestamp = 3471289200UL;
-       const u_int32 expected = 3471296400UL;
+       const u_int32 timestamp = 3471289200UL;
+       const u_int32 expected  = 3471296400UL;
 
        const int yday=1, hour=1, minute=0, second=0, tzoff=0;
        u_long yearstart = 0;
@@ -124,7 +141,7 @@ TEST_F(clocktimeTest, NextYear) {
 
 TEST_F(clocktimeTest, NoReasonableConversion) {
        /* Timestamp is: 2010-01-02 11:00:00Z */
-       const u_long timestamp = 3471418800UL;
+       const u_int32 timestamp = 3471418800UL;
        
        const int yday=100, hour=12, minute=0, second=0, tzoff=0;
        u_long yearstart = 0;
@@ -133,3 +150,26 @@ TEST_F(clocktimeTest, NoReasonableConversion) {
        ASSERT_FALSE(clocktime(yday, hour, minute, second, tzoff, timestamp,
                                                   &yearstart, &actual));
 }
+
+TEST_F(clocktimeTest, AlwaysInLimit) {
+       /* Timestamp is: 2010-01-02 11:00:00Z */
+       const u_int32 timestamp = 3471418800UL;
+       
+       int cyc, yday, hour, minute, second;
+       u_long yearstart = 0;
+       u_int32 actual, diff;
+
+       for (cyc = 0; cyc < 5; cyc++) {
+               settime(1900+cyc*65, 1, 1, 0, 0, 0);
+               for (yday = -26000; yday < 26000; yday += 17)
+                       for (hour = -204; hour < 204; hour+=2)
+                               for (minute = -60; minute < 60; minute++) {
+                                       clocktime(yday, hour, minute, 30, 0,
+                                                 timestamp, &yearstart, &actual);
+                                       diff = actual - timestamp;
+                                       if (diff >= 0x80000000UL)
+                                               diff = ~diff + 1;
+                                       ASSERT_LE(diff, (183u * SECSPERDAY));
+                               }
+       }
+}
index 21a622933b976cb5bd5d5f9306e190b07d7ff1d0..7d3afdf3d5211d212ee20eabd17af560f3cde633 100644 (file)
@@ -7,3 +7,22 @@
 u_long current_time = 4; // needed by authkeys. Used only in to calculate lifetime.
 volatile int debug = 0;
 const char *progname = "libntptest";
+
+time_t libntptest::nowtime = 0;
+
+time_t libntptest::timefunc(time_t *ptr)
+{
+    if (ptr)
+       *ptr = nowtime;
+    return nowtime;
+}
+
+void libntptest::settime(int y, int m, int d, int H, int M, int S)
+{
+
+    time_t days(ntpcal_edate_to_eradays(y-1, m-1, d-1) + 1 - DAY_UNIX_STARTS);
+    time_t secs(ntpcal_etime_to_seconds(H, M, S));
+
+    nowtime = days * SECSPERDAY + secs;
+}
+
index 5f2920cceec39daaa0b327349c7a1291ffa1e9b9..ef2daa88a268c819afc3be6925abab6d4c1e14a8 100644 (file)
@@ -2,8 +2,14 @@
 
 extern "C" {
 #include "ntp_stdlib.h"
+#include "ntp_calendar.h"
 };
 
 class libntptest : public ntptest {
 
+protected:
+       static time_t timefunc(time_t*);
+       static time_t nowtime;
+       static void   settime(int y, int m, int d, int H, int M, int S);
+
 };