]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
[Bug 3116] unit tests for NTP time stamp expansion
authorJuergen Perlinger <perlinger@ntp.org>
Thu, 22 Sep 2016 18:42:39 +0000 (20:42 +0200)
committerJuergen Perlinger <perlinger@ntp.org>
Thu, 22 Sep 2016 18:42:39 +0000 (20:42 +0200)
bk: 57e4261fIZWFN6sN9Wisl5lgZp4Ftw

ChangeLog
libntp/ntp_calendar.c
tests/libntp/calendar.c
tests/libntp/run-calendar.c

index 0805467dc6b9b1ce7768a039f6a2d87af37546b9..db857f94260062a0ef7ad091f000d54af79396a0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,6 @@
+---
+* [Bug 3116] unit tests for NTP time stamp expansion. <perlinger@ntp.org>
+
 ---
 (4.2.8p8) 2016/06/02 Released by Harlan Stenn <stenn@ntp.org>
 
index ff6ead364e5ae13a8fa0f57cd33f279914f8860c..4bfb0e723c01c0bc762dc43c1b58c19ac045a2f0 100644 (file)
@@ -91,7 +91,7 @@
 /*
  *---------------------------------------------------------------------
  * replacing the 'time()' function
- * --------------------------------------------------------------------
+ *---------------------------------------------------------------------
  */
 
 static systime_func_ptr systime_func = &time;
@@ -395,7 +395,7 @@ ntpcal_get_build_date(
 /*
  *---------------------------------------------------------------------
  * basic calendar stuff
- * --------------------------------------------------------------------
+ *---------------------------------------------------------------------
  */
 
 /* month table for a year starting with March,1st */
@@ -443,11 +443,11 @@ static const uint16_t real_month_table[2][13] = {
  */
 
 /*
- * ==================================================================
+ * ====================================================================
  *
  * General algorithmic stuff
  *
- * ==================================================================
+ * ====================================================================
  */
 
 /*
@@ -495,7 +495,7 @@ static const uint16_t real_month_table[2][13] = {
  * 32/16bit divisions and is still performant is a bit more
  * difficult. Since most usecases can be coded in a way that does only
  * require the 32-bit version a 64bit version is NOT provided here.
- * ---------------------------------------------------------------------
+ *---------------------------------------------------------------------
  */
 int32_t
 ntpcal_periodic_extend(
@@ -542,8 +542,35 @@ ntpcal_periodic_extend(
        return pivot;
 }
 
+/*---------------------------------------------------------------------
+ * Note to the casual reader
+ *
+ * In the next two functions you will find (or would have found...)
+ * the expression
+ *
+ *   res.Q_s -= 0x80000000;
+ *
+ * There was some ruckus about a possible programming error due to
+ * integer overflow and sign propagation.
+ *
+ * This assumption is based on a lack of understanding of the C
+ * standard. (Though this is admittedly not one of the most 'natural'
+ * aspects of the 'C' language and easily to get wrong.)
+ *
+ * see 
+ *     http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
+ *     "ISO/IEC 9899:201x Committee Draft — April 12, 2011"
+ *     6.4.4.1 Integer constants, clause 5
+ *
+ * why there is no sign extension/overflow problem here.
+ *
+ * But to ease the minds of the doubtful, I added back the 'u' qualifiers
+ * that somehow got lost over the last years. 
+ */
+
+
 /*
- *-------------------------------------------------------------------
+ *---------------------------------------------------------------------
  * Convert a timestamp in NTP scale to a 64bit seconds value in the UN*X
  * scale with proper epoch unfolding around a given pivot or the current
  * system time. This function happily accepts negative pivot values as
@@ -553,7 +580,7 @@ ntpcal_periodic_extend(
  * This is also a periodic extension, but since the cycle is 2^32 and
  * the shift is 2^31, we can do some *very* fast math without explicit
  * divisions.
- *-------------------------------------------------------------------
+ *---------------------------------------------------------------------
  */
 vint64
 ntpcal_ntp_to_time(
@@ -568,7 +595,7 @@ ntpcal_ntp_to_time(
        res.q_s = (pivot != NULL)
                      ? *pivot
                      : now();
-       res.Q_s -= 0x80000000         /* unshift of half range */
+       res.Q_s -= 0x80000000u;         /* unshift of half range */
        ntp     -= (uint32_t)JAN_1970;  /* warp into UN*X domain */
        ntp     -= res.D_s.lo;          /* cycle difference      */
        res.Q_s += (uint64_t)ntp;       /* get expanded time     */
@@ -581,7 +608,7 @@ ntpcal_ntp_to_time(
                  ? *pivot
                  : now();
        res = time_to_vint64(&tmp);
-       M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000);
+       M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000u);
        ntp -= (uint32_t)JAN_1970;      /* warp into UN*X domain */
        ntp -= res.D_s.lo;              /* cycle difference      */
        M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp);
@@ -592,7 +619,7 @@ ntpcal_ntp_to_time(
 }
 
 /*
- *-------------------------------------------------------------------
+ *---------------------------------------------------------------------
  * Convert a timestamp in NTP scale to a 64bit seconds value in the NTP
  * scale with proper epoch unfolding around a given pivot or the current
  * system time.
@@ -602,7 +629,7 @@ ntpcal_ntp_to_time(
  * This is also a periodic extension, but since the cycle is 2^32 and
  * the shift is 2^31, we can do some *very* fast math without explicit
  * divisions.
- *-------------------------------------------------------------------
+ *---------------------------------------------------------------------
  */
 vint64
 ntpcal_ntp_to_ntp(
@@ -617,7 +644,7 @@ ntpcal_ntp_to_ntp(
        res.q_s = (pivot)
                      ? *pivot
                      : now();
-       res.Q_s -= 0x80000000         /* unshift of half range */
+       res.Q_s -= 0x80000000u;         /* unshift of half range */
        res.Q_s += (uint32_t)JAN_1970;  /* warp into NTP domain  */
        ntp     -= res.D_s.lo;          /* cycle difference      */
        res.Q_s += (uint64_t)ntp;       /* get expanded time     */
@@ -642,20 +669,20 @@ ntpcal_ntp_to_ntp(
 
 
 /*
- * ==================================================================
+ * ====================================================================
  *
  * Splitting values to composite entities
  *
- * ==================================================================
+ * ====================================================================
  */
 
 /*
- *-------------------------------------------------------------------
+ *---------------------------------------------------------------------
  * Split a 64bit seconds value into elapsed days in 'res.hi' and
  * elapsed seconds since midnight in 'res.lo' using explicit floor
  * division. This function happily accepts negative time values as
  * timestamps before the respective epoch start.
- * -------------------------------------------------------------------
+ *---------------------------------------------------------------------
  */
 ntpcal_split
 ntpcal_daysplit(
@@ -736,11 +763,11 @@ ntpcal_daysplit(
 }
 
 /*
- *-------------------------------------------------------------------
+ *---------------------------------------------------------------------
  * Split a 32bit seconds value into h/m/s and excessive days.  This
  * function happily accepts negative time values as timestamps before
  * midnight.
- * -------------------------------------------------------------------
+ *---------------------------------------------------------------------
  */
 static int32_t
 priv_timesplit(
@@ -773,7 +800,7 @@ priv_timesplit(
 }
 
 /*
- * ---------------------------------------------------------------------
+ *---------------------------------------------------------------------
  * Given the number of elapsed days in the calendar era, split this
  * number into the number of elapsed years in 'res.hi' and the number
  * of elapsed days of that year in 'res.lo'.
@@ -1053,11 +1080,11 @@ ntpcal_time_to_date(
 
 
 /*
- * ==================================================================
+ * ====================================================================
  *
  * merging composite entities
  *
- * ==================================================================
+ * ====================================================================
  */
 
 /*
@@ -1251,8 +1278,8 @@ ntpcal_edate_to_eradays(
  * Convert ELAPSED years/months/days of gregorian calendar to elapsed
  * days in year.
  *
- * Note: This will give the true difference to the start of the given year,
- * even if months & days are off-scale.
+ * Note: This will give the true difference to the start of the given
+ * year, even if months & days are off-scale.
  *---------------------------------------------------------------------
  */
 int32_t
@@ -1434,11 +1461,11 @@ ntpcal_date_to_time(
 
 
 /*
- * ==================================================================
+ * ====================================================================
  *
  * extended and unchecked variants of caljulian/caltontp
  *
- * ==================================================================
+ * ====================================================================
  */
 int
 ntpcal_ntp64_to_date(
@@ -1500,11 +1527,11 @@ ntpcal_date_to_ntp(
 
 
 /*
- * ==================================================================
+ * ====================================================================
  *
  * day-of-week calculations
  *
- * ==================================================================
+ * ====================================================================
  */
 /*
  * Given a RataDie and a day-of-week, calculate a RDN that is reater-than,
@@ -1557,7 +1584,7 @@ ntpcal_weekday_lt(
 }
 
 /*
- * ==================================================================
+ * ====================================================================
  *
  * ISO week-calendar conversions
  *
@@ -1601,7 +1628,7 @@ ntpcal_weekday_lt(
  * smallest possible powers of two, so the division can be implemented
  * as shifts if the optimiser chooses to do so.
  *
- * ==================================================================
+ * ====================================================================
  */
 
 /*
index 9d25c41b1688ebe75cf2a63d503811f542c62e2e..b631565c0c8fb17b176b9b8b33e1fdfd11d134bc 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "ntp_stdlib.h" /* test fail without this include, for some reason */
 #include "ntp_calendar.h"
+#include "ntp_unixtime.h"
 #include "unity.h"
 
 #include <string.h>
@@ -19,6 +20,7 @@ char *        DateFromCalToString(const struct calendar *cal);
 char * DateFromIsoToString(const struct isodate *iso);
 int    IsEqualDateCal(const struct calendar *expected, const struct calendar *actual);
 int    IsEqualDateIso(const struct isodate *expected, const struct isodate *actual);
+
 void   test_DaySplitMerge(void);
 void   test_SplitYearDays1(void);
 void   test_SplitYearDays2(void);
@@ -35,6 +37,8 @@ void  test_IsoCalWeeksToYearStart(void);
 void   test_IsoCalWeeksToYearEnd(void);
 void   test_DaySecToDate(void);
 
+void   test_NtpToNtp(void);
+void   test_NtpToTime(void);
 
 void
 setUp(void)
@@ -608,3 +612,126 @@ test_DaySecToDate(void)
 
        return;
 }
+
+/* --------------------------------------------------------------------
+ * unfolding of (truncated) NTP time stamps to full 64bit values.
+ *
+ * Note: These tests need a 64bit time_t to be useful.
+ */
+
+void
+test_NtpToNtp(void)
+{
+#   if SIZEOF_TIME_T <= 4
+       
+       TEST_IGNORE_MESSAGE("test only useful for sizeof(time_t) > 4, skipped");
+
+#   else
+       
+       static const uint32_t ntp_vals[6] = {
+               UINT32_C(0x00000000),
+               UINT32_C(0x00000001),
+               UINT32_C(0x7FFFFFFF),
+               UINT32_C(0x80000000),
+               UINT32_C(0x80000001),
+               UINT32_C(0xFFFFFFFF)
+       };
+
+       static char     lbuf[128];
+       vint64          hold;
+       time_t          pivot, texp, diff;
+       int             loops, iloop;
+       
+       pivot = 0;
+       for (loops = 0; loops < 16; ++loops) {
+               for (iloop = 0; iloop < 6; ++iloop) {
+                       hold = ntpcal_ntp_to_ntp(
+                               ntp_vals[iloop], &pivot);
+                       texp = vint64_to_time(&hold);
+
+                       /* constraint 1: texp must be in the
+                        * (right-open) intervall [p-(2^31), p+(2^31)[,
+                        * but the pivot 'p' must be taken in full NTP
+                        * time scale!
+                        */
+                       diff = texp - (pivot + JAN_1970);
+                       snprintf(lbuf, sizeof(lbuf),
+                                "bounds check: piv=%lld exp=%lld dif=%lld",
+                                (long long)pivot,
+                                (long long)texp,
+                                (long long)diff);
+                       TEST_ASSERT_MESSAGE((diff >= INT32_MIN) && (diff <= INT32_MAX),
+                                           lbuf);
+
+                       /* constraint 2: low word must be equal to
+                        * input
+                        */
+                       snprintf(lbuf, sizeof(lbuf),
+                                "low check: ntp(in)=$%08lu ntp(out[0:31])=$%08lu",
+                                (unsigned long)ntp_vals[iloop],
+                                (unsigned long)hold.D_s.lo);
+                       TEST_ASSERT_EQUAL_MESSAGE(ntp_vals[iloop], hold.D_s.lo, lbuf);
+               }
+               pivot += 0x20000000;
+       }
+#   endif
+}
+
+void
+test_NtpToTime(void)
+{
+#   if SIZEOF_TIME_T <= 4
+       
+       TEST_IGNORE_MESSAGE("test only useful for sizeof(time_t) > 4, skipped");
+       
+#   else
+       
+       static const uint32_t ntp_vals[6] = {
+               UINT32_C(0x00000000),
+               UINT32_C(0x00000001),
+               UINT32_C(0x7FFFFFFF),
+               UINT32_C(0x80000000),
+               UINT32_C(0x80000001),
+               UINT32_C(0xFFFFFFFF)
+       };
+
+       static char     lbuf[128];
+       vint64          hold;
+       time_t          pivot, texp, diff;
+       uint32_t        back;
+       int             loops, iloop;
+       
+       pivot = 0;
+       for (loops = 0; loops < 16; ++loops) {
+               for (iloop = 0; iloop < 6; ++iloop) {
+                       hold = ntpcal_ntp_to_time(
+                               ntp_vals[iloop], &pivot);
+                       texp = vint64_to_time(&hold);
+
+                       /* constraint 1: texp must be in the
+                        * (right-open) intervall [p-(2^31), p+(2^31)[
+                        */
+                       diff = texp - pivot;
+                       snprintf(lbuf, sizeof(lbuf),
+                                "bounds check: piv=%lld exp=%lld dif=%lld",
+                                (long long)pivot,
+                                (long long)texp,
+                                (long long)diff);
+                       TEST_ASSERT_MESSAGE((diff >= INT32_MIN) && (diff <= INT32_MAX),
+                                           lbuf);
+
+                       /* constraint 2: conversion from full time back
+                        * to truncated NTP time must yield same result
+                        * as input.
+                       */
+                       back = (uint32_t)texp + JAN_1970;
+                       snprintf(lbuf, sizeof(lbuf),
+                                "modulo check: ntp(in)=$%08lu ntp(out)=$%08lu",
+                                (unsigned long)ntp_vals[iloop],
+                                (unsigned long)back);
+                       TEST_ASSERT_EQUAL_MESSAGE(ntp_vals[iloop], back, lbuf);
+               }
+               pivot += 0x20000000;
+       }
+#   endif
+}
index 555f3ba6d67cdfd1d5184a31aa0cb3fcf0c319c2..82309bd3b8f7161d6376a846999e9213156526c8 100644 (file)
@@ -25,6 +25,7 @@
 #include "config.h"
 #include "ntp_stdlib.h"
 #include "ntp_calendar.h"
+#include "ntp_unixtime.h"
 #include <string.h>
 
 //=======External Functions This Runner Calls=====
@@ -45,6 +46,8 @@ extern void test_IsoCalYearsToWeeks(void);
 extern void test_IsoCalWeeksToYearStart(void);
 extern void test_IsoCalWeeksToYearEnd(void);
 extern void test_DaySecToDate(void);
+extern void test_NtpToNtp(void);
+extern void test_NtpToTime(void);
 
 
 //=======Test Reset Option=====
@@ -63,21 +66,23 @@ int main(int argc, char *argv[])
 {
   progname = argv[0];
   UnityBegin("calendar.c");
-  RUN_TEST(test_DaySplitMerge, 22);
-  RUN_TEST(test_SplitYearDays1, 23);
-  RUN_TEST(test_SplitYearDays2, 24);
-  RUN_TEST(test_RataDie1, 25);
-  RUN_TEST(test_LeapYears1, 26);
-  RUN_TEST(test_LeapYears2, 27);
-  RUN_TEST(test_RoundTripDate, 28);
-  RUN_TEST(test_RoundTripYearStart, 29);
-  RUN_TEST(test_RoundTripMonthStart, 30);
-  RUN_TEST(test_RoundTripWeekStart, 31);
-  RUN_TEST(test_RoundTripDayStart, 32);
-  RUN_TEST(test_IsoCalYearsToWeeks, 33);
-  RUN_TEST(test_IsoCalWeeksToYearStart, 34);
-  RUN_TEST(test_IsoCalWeeksToYearEnd, 35);
-  RUN_TEST(test_DaySecToDate, 36);
+  RUN_TEST(test_DaySplitMerge, 24);
+  RUN_TEST(test_SplitYearDays1, 25);
+  RUN_TEST(test_SplitYearDays2, 26);
+  RUN_TEST(test_RataDie1, 27);
+  RUN_TEST(test_LeapYears1, 28);
+  RUN_TEST(test_LeapYears2, 29);
+  RUN_TEST(test_RoundTripDate, 30);
+  RUN_TEST(test_RoundTripYearStart, 31);
+  RUN_TEST(test_RoundTripMonthStart, 32);
+  RUN_TEST(test_RoundTripWeekStart, 33);
+  RUN_TEST(test_RoundTripDayStart, 34);
+  RUN_TEST(test_IsoCalYearsToWeeks, 35);
+  RUN_TEST(test_IsoCalWeeksToYearStart, 36);
+  RUN_TEST(test_IsoCalWeeksToYearEnd, 37);
+  RUN_TEST(test_DaySecToDate, 38);
+  RUN_TEST(test_NtpToNtp, 40);
+  RUN_TEST(test_NtpToTime, 41);
 
   return (UnityEnd());
 }