Parcourir la source

[trac61] updated timeToText32() to handle time after y2106.
Also added more detailed tests.

JINMEI Tatuya il y a 14 ans
Parent
commit
3a9752bd26
3 fichiers modifiés avec 119 ajouts et 6 suppressions
  1. 42 2
      src/lib/dns/dnssectime.cc
  2. 1 1
      src/lib/dns/dnssectime.h
  3. 76 3
      src/lib/dns/tests/dnssectime_unittest.cc

+ 42 - 2
src/lib/dns/dnssectime.cc

@@ -14,6 +14,8 @@
 
 
 #include <stdint.h>
 #include <stdint.h>
 
 
+#include <sys/time.h>
+
 #include <string>
 #include <string>
 #include <iomanip>
 #include <iomanip>
 #include <iostream>
 #include <iostream>
@@ -100,9 +102,47 @@ timeToText64(uint64_t t) {
     return (oss.str());
     return (oss.str());
 }
 }
 
 
+// timeToText32() below uses the current system time.  To test it with
+// unusual current time values we introduce the following function pointer;
+// when it's non NULL, we call it to get the (normally faked) current time.
+// Otherwise we use the standard gettimeofday(2).  This hook is specifically
+// intended for testing purposes, so, even if it's visible outside of this
+// library, it's not even declared in a header file.
+namespace dnssectime {
+namespace detail {
+int64_t (*gettimeFunction)() = NULL;
+}
+}
+
+namespace {
+int64_t
+gettimeofdayWrapper() {
+    using namespace dnssectime::detail;
+    if (gettimeFunction != NULL) {
+        return (gettimeFunction());
+    }
+
+    struct timeval now;
+    gettimeofday(&now, NULL);
+
+    return (static_cast<int64_t>(now.tv_sec));
+}
+}
+
 string
 string
-timeToText32(const time_t timeval) {
-    return (timeToText64(timeval));
+timeToText32(const uint32_t timeval) {
+    // We first adjust the time to the closest epoch based on the current time.
+    // Note that the following variables must be signed in order to handle
+    // time until year 2038 correctly.
+    const int64_t start = gettimeofdayWrapper() - 0x7fffffff;
+    int64_t base = 0;
+    int64_t t;
+    while ((t = (base + timeval)) < start) {
+        base += 0x100000000LL;
+    }
+
+    // Then convert it to text.
+    return (timeToText64(t));
 }
 }
 
 
 namespace {
 namespace {

+ 1 - 1
src/lib/dns/dnssectime.h

@@ -46,7 +46,7 @@ uint64_t
 timeFromText64(const std::string& time_txt);
 timeFromText64(const std::string& time_txt);
 
 
 std::string
 std::string
-timeToText32(const time_t timeval);
+timeToText32(const uint32_t timeval);
 
 
 std::string
 std::string
 timeToText64(uint64_t t);
 timeToText64(uint64_t t);

+ 76 - 3
src/lib/dns/tests/dnssectime_unittest.cc

@@ -23,9 +23,27 @@
 using namespace std;
 using namespace std;
 using namespace isc::dns;
 using namespace isc::dns;
 
 
+// See dnssectime.cc
+namespace isc {
+namespace dns {
+namespace dnssectime {
+namespace detail {
+extern int64_t (*gettimeFunction)();
+}
+}
+}
+}
+
 namespace {
 namespace {
 
 
-TEST(DNSSECTimeTest, fromText) {
+class DNSSECTimeTest : public ::testing::Test {
+protected:
+    ~DNSSECTimeTest() {
+        dnssectime::detail::gettimeFunction = NULL;
+    }
+};
+
+TEST_F(DNSSECTimeTest, fromText) {
     // In most cases (in practice) the 32-bit and 64-bit versions should
     // In most cases (in practice) the 32-bit and 64-bit versions should
     // behave identically, so we'll mainly test the 32-bit version, which
     // behave identically, so we'll mainly test the 32-bit version, which
     // will be more commonly used in actual code (because many of the wire
     // will be more commonly used in actual code (because many of the wire
@@ -74,12 +92,67 @@ TEST(DNSSECTimeTest, fromText) {
     EXPECT_EQ(4294967306LL, timeFromText64("21060207062826"));
     EXPECT_EQ(4294967306LL, timeFromText64("21060207062826"));
 }
 }
 
 
-TEST(DNSSECTimeTest, toText) {
+// This helper templated function tells timeToText32 a faked current time.
+// The template parameter is that faked time in the form of int64_t seconds
+// since epoch.
+template <int64_t NOW>
+int64_t
+testGetTime() {
+    return (NOW);
+}
+
+TEST_F(DNSSECTimeTest, toText) {
+    // Set the current time to: Feb 18 09:04:14 UTC 2012 (an arbitrary choice
+    // in the range of the first half of uint32 since epoch).
+    dnssectime::detail::gettimeFunction = testGetTime<1329555854LL>;
+
+    // Check some basic cases
     EXPECT_EQ("19700101000000", timeToText32(0));
     EXPECT_EQ("19700101000000", timeToText32(0));
     EXPECT_EQ("20100311233000", timeToText32(1268350200));
     EXPECT_EQ("20100311233000", timeToText32(1268350200));
+
+    // Test the "year 2038" problem.
+    // Check the result of toText() for "INT_MIN" in int32_t.  It's in the
+    // 68-year range from the faked current time, so the result should be
+    // in year 2038, instead of 1901.
+    EXPECT_EQ("20380119031408", timeToText64(0x80000000L));
+    EXPECT_EQ("20380119031408", timeToText32(0x80000000L));
+
+    // A controversial case: what should we do with "-1"?  It's out of range
+    // in future, but according to RFC time before epoch doesn't seem to be
+    // considered "in-range" either.  Our toText() implementation handles
+    // this range as a special case and always treats them as future time
+    // until year 2038.  This won't be a real issue in practice, though,
+    // since such too large values won't be used in actual deployment by then.
+    EXPECT_EQ("21060207062815", timeToText32(0xffffffffL));
+
+    // After the singular point of year 2038, the first half of uint32 can
+    // point to a future time.
+    // Set the current time to: Apr 1 00:00:00 UTC 2038:
+    dnssectime::detail::gettimeFunction = testGetTime<2153692800LL>;
+    // then time "10" is Feb 7 06:28:26 UTC 2106
+    EXPECT_EQ("21060207062826", timeToText32(10));
+    // in 64-bit, it's 2^32 + 10
+    EXPECT_EQ("21060207062826", timeToText64(0x10000000aLL));
+
+    // After year 2106, the upper half of uint32 can point to past time
+    // (as it should).
+    dnssectime::detail::gettimeFunction = testGetTime<0x10000000aLL>;
+    EXPECT_EQ("21060207062815", timeToText32(0xffffffffL));
+
+    // set the current time to: Mar16 12:56:32 UTC 2242
+    dnssectime::detail::gettimeFunction = testGetTime<8589934592LL>;
+    EXPECT_EQ("22420316125632", timeToText64(8589934592LL));
+    EXPECT_EQ("22420316125632", timeToText32(0));
+
+    // Try very large time value.  Actually it's the possible farthest time
+    // that can be represented in the form of YYYYMMDDHHmmSS.
+    const uint64_t YEAR10K_EVE = 253402300799LL;
+    EXPECT_EQ("99991231235959", timeToText64(YEAR10K_EVE));
+    dnssectime::detail::gettimeFunction = testGetTime<YEAR10K_EVE - 10>;
+    EXPECT_EQ("99991231235959", timeToText32(4294197631L));
 }
 }
 
 
-TEST(DNSSECTimeTest, overflow) {
+TEST_F(DNSSECTimeTest, overflow) {
     // Jan 1, Year 10,000.
     // Jan 1, Year 10,000.
     EXPECT_THROW(timeToText64(253402300800LL), InvalidTime);
     EXPECT_THROW(timeToText64(253402300800LL), InvalidTime);
 }
 }