[asio] How do you fake the time for boost timers?

(For those interested this question is also at http://stackoverflow.com/questions/14191855/how-do-you-fake-the-time-for-boo... ) Hi all, If possible, how do you fake the time for the purpose of triggering boost timers in a unit test? For example, is it possible to achieve something like the following: #include <iostream>#include <boost/asio.hpp>#include <boost/date_time/posix_time/posix_time.hpp> void print(const boost::system::error_code& /*e*/){ std::cout << "Hello, world!\n";} int main(){ boost::asio::io_service io; // Possibly another class needed here, or a way of setting the clock to be fake boost::asio::deadline_timer t(io, boost::posix_time::hours(24)); t.async_wait(&print); io.poll(); // Nothing should happen - no handlers ready // PSEUDO-CODE below of what I'd like to happen, jump ahead 24 hours io.set_time(io.get_time() + boost::posix_time::hours(24)); io.poll(); // The timer should go off return 0;} I'm aware I could wrap the io_service and deadline_timer in a wrapper and write my own test implementation, thus providing the real and test version to the production code I'm trying to test. What I'm hoping is the boost framework allows a way to create an io service that is related to a fake clock rather than the system time, meaning I don't need to create my own wrappers. I should also point out that the docs indicate providing something like my own WaitableTimerService orTimerService may be the way to go, but I can't see which one (if any) is responsible for providing the clock. Any tips appreciated! Josh.

Josh Quigley <0zeroth <at> gmail.com> writes: Apologies about the formatting snafu. The code should have read: For example, is it possible to achieve something like the following: #include <iostream> #include <boost/asio.hpp> #include <boost/date_time/posix_time/posix_time.hpp> void print(const boost::system::error_code& /*e*/) { std::cout << "Hello, world!\n"; } int main() { boost::asio::io_service io; // Possibly another class needed here, or a way of setting the clock to be fake boost::asio::deadline_timer t(io, boost::posix_time::hours(24)); t.async_wait(&print); io.poll(); // Nothing should happen - no handlers ready // PSEUDO-CODE below of what I'd like to happen, jump ahead 24 hours io.set_time(io.get_time() + boost::posix_time::hours(24)); io.poll(); // The timer should go off return 0; }

If possible, how do you fake the time for the purpose of triggering boost timers in a unit test?
A minimal example of achieving the requirements, based on a blog post by Chris (author of boost.asio) Thanks to the SO community for the providing hints. #include <boost/asio.hpp> #include <boost/optional.hpp> class mock_time_traits { typedef boost::asio::deadline_timer::traits_type source_traits; public: typedef source_traits::time_type time_type; typedef source_traits::duration_type duration_type; // Note this implemenation requires set_now(...) to be called before now() static time_type now() { return *now_; } // After modifying the clock, we need to sleep the thread to give the io_service // the opportunity to poll and notice the change in clock time static void set_now(time_type t) { now_ = t; boost::this_thread::sleep_for(boost::chrono::milliseconds(2)); } static time_type add(time_type t, duration_type d) { return source_traits::add(t, d); } static duration_type subtract(time_type t1, time_type t2) { return source_traits::subtract(t1, t2); } static bool less_than(time_type t1, time_type t2) { return source_traits::less_than(t1, t2); } // This function is called by asio to determine how often to check // if the timer is ready to fire. By manipulating this function, we // can make sure asio detects changes to now_ in a timely fashion. static boost::posix_time::time_duration to_posix_duration(duration_type d) { return d < boost::posix_time::milliseconds(1) ? d : boost::posix_time::milliseconds(1); } private: static boost::optional<time_type> now_; }; boost::optional<mock_time_traits::time_type> mock_time_traits::now_; typedef boost::asio::basic_deadline_timer< boost::posix_time::ptime, mock_time_traits> mock_deadline_timer; void handler(const boost::system::error_code &ec) { std::cout << "Handler!" << std::endl; } int main() { mock_time_traits::set_now(boost::posix_time::time_from_string("2013-01-20 1:44:01.000")); boost::asio::io_service io_service; mock_deadline_timer timer(io_service, boost::posix_time::seconds(5)); timer.async_wait(handler); std::cout << "Poll 1" << std::endl; io_service.poll(); mock_time_traits::set_now(mock_time_traits::now() + boost::posix_time::seconds(6)); std::cout << "Poll 2" << std::endl; io_service.poll(); std::cout << "Poll 3" << std::endl; io_service.poll(); return 0; } // Output Poll 1 Poll 2 Handler! Poll 3 The above is slightly modified (and I believe slightly improved) on the blog post. By not using an offset on the system clock, you gain complete control over the timers: they will not fire until you explicitly set time forward past the timer. The solution could be improved by making the this_thread::sleep part configurable. Note that the to_posix_duration hack described in [1] needs to use a smaller duration than the sleep. To me this approach still seems a bit magic, since the time_traits are not well documented, and in particular the hack of to_posix_duration has a whiff of voodoo about it. I guess it just comes down to intimate knowledge of the deadline_timer implementation (which I don't have).
participants (2)
-
Josh
-
Josh Quigley