time: Add ability to specialize system and steady clocks

Default implementation is "optimized" to use boost::chrono clocks.  When
specialization is used, now() calls are redirected to the virtual
methods of the specified specialization.

This commit also includes an implementation of UnitTestClock that
can be used in unit tests to eliminate dependency on the wall clock.

Change-Id: I7ced774634d90ce32714a71e2a66129cba91b9ae
Refs: #2158
diff --git a/src/util/monotonic_deadline_timer.hpp b/src/util/monotonic_deadline_timer.hpp
index 3328a86..6572ca4 100644
--- a/src/util/monotonic_deadline_timer.hpp
+++ b/src/util/monotonic_deadline_timer.hpp
@@ -32,11 +32,11 @@
 namespace boost {
 namespace asio {
 
-template <>
-struct time_traits<ndn::time::steady_clock::TimePoint::clock>
+template<>
+struct time_traits<ndn::time::steady_clock>
 {
   typedef ndn::time::steady_clock::TimePoint time_type;
-  typedef ndn::time::steady_clock::TimePoint::clock::duration duration_type;
+  typedef ndn::time::steady_clock::Duration  duration_type;
 
   static time_type
   now()
@@ -65,15 +65,7 @@
   static boost::posix_time::time_duration
   to_posix_duration(const duration_type& duration)
   {
-    return
-#ifdef BOOST_DATE_TIME_HAS_NANOSECONDS
-      boost::posix_time::nanoseconds(
-        ndn::time::duration_cast<ndn::time::nanoseconds>(duration).count())
-#else
-      boost::posix_time::microseconds(
-        ndn::time::duration_cast<ndn::time::microseconds>(duration).count())
-#endif
-      ;
+    return ndn::time::steady_clock::to_posix_duration(duration);
   }
 };
 
@@ -82,7 +74,7 @@
 
 namespace ndn {
 
-typedef boost::asio::basic_deadline_timer<time::steady_clock::TimePoint::clock> monotonic_deadline_timer;
+typedef boost::asio::basic_deadline_timer<time::steady_clock> monotonic_deadline_timer;
 
 } // namespace ndn
 
diff --git a/src/util/time-custom-clock.hpp b/src/util/time-custom-clock.hpp
new file mode 100644
index 0000000..066d3e9
--- /dev/null
+++ b/src/util/time-custom-clock.hpp
@@ -0,0 +1,70 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2014 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_TIME_CUSTOM_CLOCK_HPP
+#define NDN_TIME_CUSTOM_CLOCK_HPP
+
+#include "time.hpp"
+
+namespace ndn {
+namespace time {
+
+/**
+ * \brief Class implementing custom system or steady clock behavior
+ *
+ * Instance of specialization of this class may be passed to setCustomClocks() free function
+ * in order to change global behavior of system or steady clock.
+ */
+template<typename BaseClock>
+class CustomClock
+{
+public:
+  virtual ~CustomClock()
+  {
+  }
+
+  virtual typename BaseClock::time_point
+  getNow() const = 0;
+
+  virtual std::string
+  getSince() const = 0;
+
+  virtual boost::posix_time::time_duration
+  toPosixDuration(const typename BaseClock::duration& duration) const = 0;
+};
+
+typedef CustomClock<system_clock> CustomSystemClock;
+typedef CustomClock<steady_clock> CustomSteadyClock;
+
+/**
+ * \brief Set custom system and steady clocks
+ *
+ * When \p steadyClock or \p systemClock set to nullptr, the default implementation
+ * of the corresponding clock will be used
+ */
+void
+setCustomClocks(shared_ptr<CustomSteadyClock> steadyClock = nullptr,
+                shared_ptr<CustomSystemClock> systemClock = nullptr);
+
+} // namespace time
+} // namespace ndn
+
+#endif // NDN_TIME_CUSTOM_CLOCK_HPP
diff --git a/src/util/time-unit-test-clock.cpp b/src/util/time-unit-test-clock.cpp
new file mode 100644
index 0000000..73c30f7
--- /dev/null
+++ b/src/util/time-unit-test-clock.cpp
@@ -0,0 +1,82 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2014 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "time-unit-test-clock.hpp"
+
+namespace ndn {
+namespace time {
+
+template<class BaseClock>
+UnitTestClock<BaseClock>::UnitTestClock(const nanoseconds& startTime)
+  : m_currentTime(startTime)
+{
+}
+
+template<class BaseClock>
+std::string
+UnitTestClock<BaseClock>::getSince() const
+{
+  return " since unit test clock advancements";
+}
+
+template<class BaseClock>
+typename BaseClock::time_point
+UnitTestClock<BaseClock>::getNow() const
+{
+  return typename BaseClock::time_point(duration_cast<typename BaseClock::duration>(m_currentTime));
+}
+
+template<class BaseClock>
+boost::posix_time::time_duration
+UnitTestClock<BaseClock>::toPosixDuration(const typename BaseClock::duration& duration) const
+{
+  return
+#ifdef BOOST_DATE_TIME_HAS_NANOSECONDS
+    boost::posix_time::nanoseconds(1)
+#else
+    boost::posix_time::microseconds(1)
+#endif
+    ;
+}
+
+
+template<class BaseClock>
+void
+UnitTestClock<BaseClock>::advance(const nanoseconds& duration)
+{
+  m_currentTime += duration;
+}
+
+template<class BaseClock>
+void
+UnitTestClock<BaseClock>::setNow(const nanoseconds& timeSinceEpoch)
+{
+  m_currentTime = timeSinceEpoch;
+}
+
+template
+class UnitTestClock<system_clock>;
+
+template
+class UnitTestClock<steady_clock>;
+
+} // namespace time
+} // namespace ndn
diff --git a/src/util/time-unit-test-clock.hpp b/src/util/time-unit-test-clock.hpp
new file mode 100644
index 0000000..d1bbe7d
--- /dev/null
+++ b/src/util/time-unit-test-clock.hpp
@@ -0,0 +1,114 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2014 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_TIME_UNIT_TEST_CLOCK_HPP
+#define NDN_TIME_UNIT_TEST_CLOCK_HPP
+
+#include "time-custom-clock.hpp"
+
+namespace ndn {
+namespace time {
+
+
+/**
+ * @brief Traits for UnitTestClock, defining default behavior for different clocks
+ *
+ * The only behavior that is currently controlled by the traits is default start
+ * time.  The generic implementation assumes start time to be zero.
+ */
+template<class BaseClock>
+class UnitTestClockTraits
+{
+public:
+  static nanoseconds
+  getDefaultStartTime()
+  {
+    return nanoseconds::zero();
+  };
+};
+
+/**
+ * @brief Specialization of UnitTestClockTraits for system_clock
+ *
+ * This specialization sets the default start time to 1415684132 seconds
+ * (equivalent to Tue, 11 Nov 2014 05:35:32 UTC if unix epoch is assumed).
+ */
+template<>
+class UnitTestClockTraits<system_clock>
+{
+public:
+  static nanoseconds
+  getDefaultStartTime()
+  {
+    return seconds(1415684132);
+  };
+};
+
+/**
+ * @brief Clock that can be used in unit tests for time-dependent tests independent of wall clock
+ *
+ * This clock should be explicitly advanced with UnitTestClock<BaseClock>::advance() or set
+ * with UnitTestClock<BaseClock>::setNow() methods.
+ *
+ * @note Default start time is determined by UnitTestClockTraits
+ */
+template<class BaseClock>
+class UnitTestClock : public CustomClock<BaseClock>
+{
+public:
+  explicit
+  UnitTestClock(const nanoseconds& startTime =
+                UnitTestClockTraits<BaseClock>::getDefaultStartTime());
+
+  /**
+   * @brief Advance unit test clock by @p duration
+   */
+  void
+  advance(const nanoseconds& duration);
+
+  /**
+   * @brief Explicitly set clock to @p timeSinceEpoch
+   */
+  void
+  setNow(const nanoseconds& timeSinceEpoch);
+
+public: // CustomClock<BaseClock>
+
+  virtual std::string
+  getSince() const;
+
+  virtual typename BaseClock::time_point
+  getNow() const;
+
+  virtual boost::posix_time::time_duration
+  toPosixDuration(const typename BaseClock::duration& duration) const;
+
+private:
+  nanoseconds m_currentTime;
+};
+
+typedef UnitTestClock<system_clock> UnitTestSystemClock;
+typedef UnitTestClock<steady_clock> UnitTestSteadyClock;
+
+} // namespace time
+} // namespace ndn
+
+#endif // NDN_TIME_UNIT_TEST_CLOCK_HPP
diff --git a/src/util/time.cpp b/src/util/time.cpp
index 15ac925..b652a7b 100644
--- a/src/util/time.cpp
+++ b/src/util/time.cpp
@@ -20,11 +20,112 @@
  */
 
 #include "time.hpp"
+#include "time-custom-clock.hpp"
+
 #include <boost/date_time/posix_time/posix_time.hpp>
 
 namespace ndn {
 namespace time {
 
+static shared_ptr<CustomSystemClock> g_systemClock;
+static shared_ptr<CustomSteadyClock> g_steadyClock;
+
+// this method is defined in time-custom-clock.hpp
+void
+setCustomClocks(shared_ptr<CustomSteadyClock> steadyClock,
+                shared_ptr<CustomSystemClock> systemClock)
+{
+  g_systemClock = systemClock;
+  g_steadyClock = steadyClock;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+system_clock::time_point
+system_clock::now() noexcept
+{
+  if (g_systemClock == nullptr) {
+    // optimized default version
+    return time_point(boost::chrono::system_clock::now().time_since_epoch());
+  }
+  else {
+    return g_systemClock->getNow();
+  }
+}
+
+std::time_t
+system_clock::to_time_t(const time_point& t) noexcept
+{
+  return duration_cast<seconds>(t.time_since_epoch()).count();
+}
+
+system_clock::time_point
+system_clock::from_time_t(std::time_t t) noexcept
+{
+  return time_point(seconds(t));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef __APPLE__
+  // Note that on OS X platform boost::steady_clock is not truly monotonic, so we use
+  // system_clock instead.  Refer to https://svn.boost.org/trac/boost/ticket/7719)
+  typedef boost::chrono::system_clock base_steady_clock;
+#else
+  typedef boost::chrono::steady_clock base_steady_clock;
+#endif
+
+steady_clock::time_point
+steady_clock::now() noexcept
+{
+  if (g_steadyClock == nullptr) {
+    // optimized default version
+    return time_point(base_steady_clock::now().time_since_epoch());
+  }
+  else {
+    return g_steadyClock->getNow();
+  }
+}
+
+boost::posix_time::time_duration
+steady_clock::to_posix_duration(const duration& duration)
+{
+  if (g_steadyClock == nullptr) {
+    // optimized default version
+    return
+#ifdef BOOST_DATE_TIME_HAS_NANOSECONDS
+      boost::posix_time::nanoseconds(duration_cast<nanoseconds>(duration).count())
+#else
+      boost::posix_time::microseconds(duration_cast<microseconds>(duration).count())
+#endif
+      ;
+  }
+  else {
+    return g_steadyClock->toPosixDuration(duration);
+  }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+const system_clock::TimePoint&
+getUnixEpoch()
+{
+  static system_clock::TimePoint epoch = system_clock::from_time_t(0);
+  return epoch;
+}
+
+milliseconds
+toUnixTimestamp(const system_clock::TimePoint& point)
+{
+  return duration_cast<milliseconds>(point - getUnixEpoch());
+}
+
+system_clock::TimePoint
+fromUnixTimestamp(const milliseconds& duration)
+{
+  return getUnixEpoch() + duration;
+}
+
 std::string
 toIsoString(const system_clock::TimePoint& timePoint)
 {
@@ -100,3 +201,45 @@
 
 } // namespace time
 } // namespace ndn
+
+namespace boost {
+namespace chrono {
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+template<class CharT>
+std::basic_string<CharT>
+clock_string<ndn::time::system_clock, CharT>::since()
+{
+  if (ndn::time::g_systemClock == nullptr) {
+    // optimized default version
+    return clock_string<system_clock, CharT>::since();
+  }
+  else {
+    return ndn::time::g_systemClock->getSince();
+  }
+}
+
+template
+struct clock_string<ndn::time::system_clock, char>;
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+template<class CharT>
+std::basic_string<CharT>
+clock_string<ndn::time::steady_clock, CharT>::since()
+{
+  if (ndn::time::g_steadyClock == nullptr) {
+    // optimized default version
+    return clock_string<ndn::time::base_steady_clock, CharT>::since();
+  }
+  else {
+    return ndn::time::g_steadyClock->getSince();
+  }
+}
+
+template
+struct clock_string<ndn::time::steady_clock, char>;
+
+} // namespace chrono
+} // namespace boost
diff --git a/src/util/time.hpp b/src/util/time.hpp
index 92cedce..e0f64c5 100644
--- a/src/util/time.hpp
+++ b/src/util/time.hpp
@@ -24,6 +24,8 @@
 
 #include "../common.hpp"
 #include <boost/chrono.hpp>
+#include <boost/asio/time_traits.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
 
 namespace ndn {
 namespace time {
@@ -61,18 +63,29 @@
  * <code>
  *     system_clock::TimePoint time = ...;
  *     uint64_t timestampInMilliseconds = toUnixTimestamp(time).count();
- *     system_clock::TimePoint time2 = fromUnixTimestamp(time::milliseconds(timestampInMilliseconds));
+ *     system_clock::TimePoint time2 = fromUnixTimestamp(milliseconds(timestampInMilliseconds));
  * </code>
  */
-class system_clock : public boost::chrono::system_clock
+class system_clock
 {
 public:
+  typedef BOOST_SYSTEM_CLOCK_DURATION      duration;
+  typedef duration::rep                    rep;
+  typedef duration::period                 period;
+  typedef boost::chrono::time_point<system_clock> time_point;
+  static constexpr bool is_steady = false;
+
   typedef time_point TimePoint;
   typedef duration Duration;
 
-  // /// \brief Get current TimePoint
-  // TimePoint
-  // now();
+  static time_point
+  now() noexcept;
+
+  static std::time_t
+  to_time_t(const time_point& t) noexcept;
+
+  static time_point
+  from_time_t(std::time_t t) noexcept;
 }; // class system_clock
 
 /**
@@ -82,56 +95,55 @@
  * clock cannot decrease as physical time moves forward. This clock is
  * not related to wall clock time, and is best suitable for measuring
  * intervals.
- *
- * Note that on OS X platform this defaults to system clock and is not
- * truly monotonic. Refer to https://svn.boost.org/trac/boost/ticket/7719)
  */
-class steady_clock : public
-#ifdef __APPLE__
-// steady_clock may go backwards on OS X platforms, so use system_clock
-// instead
-    boost::chrono::system_clock
-#else
-    boost::chrono::steady_clock
-#endif
+class steady_clock
 {
 public:
+  typedef nanoseconds      duration;
+  typedef duration::rep    rep;
+  typedef duration::period period;
+  typedef boost::chrono::time_point<steady_clock> time_point;
+  static constexpr bool is_steady = true;
+
   typedef time_point TimePoint;
   typedef duration Duration;
 
-  // /// \brief Get current TimePoint
-  // TimePoint
-  // now();
+  static time_point
+  now() noexcept;
+
+private:
+  /**
+   * \brief Method to be used in deadline timer to select proper waiting
+   *
+   * Mock time implementations should return minimum value to ensure Boost.Asio
+   * is not enabling any waiting on mock timers.
+   *
+   * @sa http://stackoverflow.com/questions/14191855/how-do-you-mock-the-time-for-boost-timers
+   */
+  static boost::posix_time::time_duration
+  to_posix_duration(const duration& duration);
+
+  friend struct boost::asio::time_traits<steady_clock>;
 }; // class steady_clock
 
 
 /**
  * \brief Get system_clock::TimePoint representing UNIX time epoch (00:00:00 on Jan 1, 1970)
  */
-inline const system_clock::TimePoint&
-getUnixEpoch()
-{
-  static system_clock::TimePoint epoch = system_clock::from_time_t(0);
-  return epoch;
-}
+const system_clock::TimePoint&
+getUnixEpoch();
 
 /**
  * \brief Convert system_clock::TimePoint to UNIX timestamp
  */
-inline milliseconds
-toUnixTimestamp(const system_clock::TimePoint& point)
-{
-  return duration_cast<milliseconds>(point - getUnixEpoch());
-}
+milliseconds
+toUnixTimestamp(const system_clock::TimePoint& point);
 
 /**
  * \brief Convert UNIX timestamp to system_clock::TimePoint
  */
-inline system_clock::TimePoint
-fromUnixTimestamp(const milliseconds& duration)
-{
-  return getUnixEpoch() + duration;
-}
+system_clock::TimePoint
+fromUnixTimestamp(const milliseconds& duration);
 
 /**
  * \brief Convert to the ISO string representation of the time (YYYYMMDDTHHMMSS,fffffffff)
@@ -200,7 +212,30 @@
            const std::string& format = "%Y-%m-%d %H:%M:%S",
            const std::locale& locale = std::locale("C"));
 
+
+////////////////////////////////////////////////////////////////////////////////
+
 } // namespace time
 } // namespace ndn
 
+namespace boost {
+namespace chrono {
+
+template<class CharT>
+struct clock_string<ndn::time::system_clock, CharT>
+{
+  static std::basic_string<CharT>
+  since();
+};
+
+template<class CharT>
+struct clock_string<ndn::time::steady_clock, CharT>
+{
+  static std::basic_string<CharT>
+  since();
+};
+
+} // namespace chrono
+} // namespace boost
+
 #endif // NDN_TIME_HPP