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

288 views
Skip to first unread message

Josh Quigley

unread,
Jan 13, 2013, 6:13:55 PM1/13/13
to boost users'

(For those interested this question is also at http://stackoverflow.com/questions/14191855/how-do-you-fake-the-time-for-boost-timers)

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

unread,
Jan 14, 2013, 5:40:23 PM1/14/13
to boost...@lists.boost.org
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;
}


_______________________________________________
Boost-users mailing list
Boost...@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/boost-users

Josh

unread,
Jan 19, 2013, 11:24:50 PM1/19/13
to boost...@lists.boost.org

> 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).
Reply all
Reply to author
Forward
0 new messages