1
$\begingroup$

I am running the tests for my ROS C++ node using rostest/gtest.

Currently I have a problem where I have a class method that I need/want to test that out of date data based on the value of ros::Time::now();. Since the data that I am using for this test has a predefined date I need/want to mock the value returned by ros::Time::now();. I have seen that I should be able to achieve this by setting /use_sim_time to true for example in the launch file and then publishing in the /clock topic.

However when doing this the test execution does not return and simply times out, even when nothing is really being tested. The test execution runs fine without this.

I am executing my tests with catkin run_tests.

In this scenario:

  • Setting <param name="/use_sim_time" value="true"/> in the launch file.
  • On the main of my unittests node, I am publishing a message to /clock with msg.clock = ros::Time(1678456559, 0);.

Then I get the following executing the test.

[ WARN] [1709049828.959657287]: Main ros::Time::now() = 0.000000
[ WARN] [1709049828.960573171, 1678456559.000000000]: Test class Constructor ros::Time::now() = 1678456559.000000
[ WARN] [1709049828.960620129, 1678456559.000000000]: Test class SetUp ros::Time::now() = 1678456559.000000

It's weird that the first log has ros::Time::now() has a value of 0.0 even though it is after the message has been published.

    ros::NodeHandle nh;

    rosgraph_msgs::Clock msg;
    msg.clock = ros::Time(1678456559, 0);

    ros::Publisher clock_pub = nh.advertise<rosgraph_msgs::Clock>("/clock", 1);

    clock_pub.publish(msg);

    ros::spinOnce();

    ROS_WARN("Main ros::Time::now() = %f", ros::Time::now().toSec());

Then it's also weird that there are two timestamps in the log messages from then on. The values are correct however, but the code hangs when I create the unique_ptr to my class.

test_pr = std::make_unique<MyClassTest>();

What am I doing wrong/missing?

Thank you all in advance

Edit: After a bit more debugging it seems that what I did initially was correct. However I found that one the classes that I'm testing, initializes an object that has a small sleep in the constructor. Since with use_sim_time the time is "stopped" this sleep would never complete and it would time out.

I added a new thread that published to the clock topic to overcome this and it seems to work. Now I can set the initial time and make sure it doesn't stop.

$\endgroup$
1
  • $\begingroup$ Thanks for coming back to share the answer. Could you answer your own question below and mark is as solved instead of doing it in an edit so others can know that you've found the solution. $\endgroup$
    – Tully
    Commented Nov 24 at 14:46

2 Answers 2

1
$\begingroup$

I was missing:

void clock(ros::NodeHandle& nh, const uint32_t start_time) {
rosgraph_msgs::Clock msg;
msg.clock = ros::Time(start_time, 0);

ros::Publisher clock_pub_ = nh.advertise<rosgraph_msgs::Clock>("/clock", 1);

while (ros::ok()) {
    clock_pub_.publish(msg);
    ros::WallDuration(0.2).sleep();
    msg.clock = ros::Time(ros::Time::now().toSec() + 1);
}

And then in main

    // Start clock thread
std::thread clock_thread([&]() { clock(nh, 1678456559); });
$\endgroup$
0
$\begingroup$

First, I think there's an answer here that addresses your issue:

As noted in the rospy Time overview, one workaround is to loop the assignment until it returns a non-zero value:

prevTime = 0
while not prevTime:
    prevTime = rospy.Time.now()

but you can also mock easier if you create and code against and interface; interfaces let you swap implementations easily. Something like:

class IGetTime
{
  public:
    virtual ros::Time getTime() = 0;
}
class GetTimeRos : public IGetTime
{
  public:
    ros::Time getCurrentTime() final { return ros::Time::now(); }
}
class GetTimeMock : public IGetTime
{
  public:
    ros::Time getCurrentTime() final { ros::Time(1678456559, 0); }
}

then in your code you can do something like:

IGetTime time_getter = GetTimeRos();
my_timed_method.doStuff(time_getter.getCurrentTime());

and in your test you can:

IGetTime mock_time_getter = GetTimeMock();
my_timed_method.doStuff(mock_time_getter.getCurrentTime());
$\endgroup$
1
  • $\begingroup$ First of all thanks for the reply. I debugged a bit more and I realized that my tests where hanging because one of the constructed objects that is part of my class has a small ros sleep in the constructor. Since when we use sim time, the time is always constant this method never wakes up. My method is fairly simple in terms of time usage, I just need the value of now so I would like to avoid any big changes in that regard. In the end I created a thread in my test node that simulates the clock. So I can set the initial time and then increase it gradually such that it doesn't hang. $\endgroup$ Commented Feb 28 at 8:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.