ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
13

data files for catkin gtest

asked 2013-04-02 06:07:51 -0500

updated 2013-04-03 02:07:13 -0500

I am creating a Unit-Test program for my catkin package, using gtest. I have set up my CMakeLists.txt to compile the unit-test program and link against my package library. In one of the tests, I would like to read in an external data file to use in the test routine. This data file is in the same directory as my utest.cpp file: <package>/test/test.dat.

In rosbuild, I run tests using make test, and the working directory is the package root. So, I can reference the test data file using a relative path: test/test.dat.

In catkin, I run make run_tests and the working directory is in the catkin_ws/devel tree. The build process does not copy my data file to this tree, so it is unclear how I should specify the relative path to my data file.

What is the best-practice for dealing with test data files under catkin?
NOTE: I don't want the test executable or data-file to be installed in the final package. These are for unit-test purposes only.

In case it's helpful, here's the relevant section of my CMakeLists.txt:

catkin_add_gtest(test_myPackage test/utest.cpp)
target_link_libraries(test_myPackage myPackage_lib)

EDIT :
NOTE: William has updated his example code, so the following bugs have been fixed. I'm leaving in this discussion for historical reasons, but the updated code should work as-is

I tried running the example code provided by @William below. When I rename test.dat to test1.dat, both tests still pass. I think the issue is with how the test code loops over the data file lines. No tests are run if the file is not found, but neither is an error thrown. Adding the following line helps to catch this case:

in.open("data/test.dat", std::fstream::in);
ASSERT_FALSE(in.fail());    // this is the new line
int op1, op2, expected;
while (in >> op1 >> op2 >> expected) {
  EXPECT_EQ(expected, test_with_data::add(op1, op2));
}

Once that line is added, both tests fail with the mis-named test1.dat file. However, once the file is restored to the correct name, the second method still fails. If I add a print of the current working directory to test_with_data2.cpp, it reports: ws/build/test_with_data/test. It looks like the WORKING_DIRECTORY argument is relative to the build directory. However, if you use an absolute path, the 2nd test method works as expected:

catkin_add_gtest(${PROJECT_NAME}2-test test/test_with_data2.cpp WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test)
edit retag flag offensive close merge delete

2 Answers

Sort by ยป oldest newest most voted
19

answered 2013-04-02 12:20:24 -0500

William gravatar image

updated 2013-04-02 20:56:22 -0500

This can be solved using CMake or using catkin.

CMake Option:

The CMake way to solve this would be to create a template file which would have the location of the test data injected at build time.

Lets say you had a layout like this:

my_pkg/
--include/
--src/
--test/
----data/
------test.dat
----test_with_data.cpp
----test_with_data_settings.h.in

The data file might look like this:

1 2 3
3 4 7
100 -16 84

The test might look like this:

#include <fstream>
#include <string.h>

#include "gtest/gtest.h"

#include "test_with_data/add.h"

#include "test_with_data_settings.h"

namespace {

class SomeTests : public ::testing::Test {};

TEST_F(SomeTests, test_add) {
  std::fstream in;
  in.open((std::string(TEST_DIR) + std::string("/test.dat")).c_str(), std::fstream::in);
  ASSERT_FALSE(in.fail());
  int op1, op2, expected;
  while (in >> op1 >> op2 >> expected) {
    EXPECT_EQ(expected, test_with_data::add(op1, op2));
  }
  in.close();
}

}

int main(int argc, char **argv) {
  try {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
  } catch (std::exception &e) {
    std::cerr << "Unhandled Exception: " << e.what() << std::endl;
  }
  return 1;
}

The header file might look like:

#define TEST_DIR "@TEST_WITH_DATA_TEST_DIR@"

And the relavent CMake code:

get_filename_component(TEST_WITH_DATA_TEST_DIR "test/data" ABSOLUTE)
configure_file(test/test_with_data_settings.h.in test/test_with_data_settings.h)
include_directories(${CMAKE_CURRENT_BINARY_DIR}/test)
catkin_add_gtest(${PROJECT_NAME}-test test/test_with_data.cpp)
if(TARGET ${PROJECT_NAME}-test)
  target_link_libraries(${PROJECT_NAME}-test add)
endif()

catkin Option:

The catkin way to solve this would be to use the WORKING_DIRECTORY option of catkin_add_gtest inorder to have your tests run in a place relative to the data location.

So you would drop the settings header and your test would look like this:

#include <fstream>
#include <string.h>

#include "gtest/gtest.h"

#include "test_with_data/add.h"

namespace {

class SomeTests : public ::testing::Test {};

TEST_F(SomeTests, test_add) {
  std::fstream in;
  in.open("data/test.dat", std::fstream::in);
  ASSERT_FALSE(in.fail());
  int op1, op2, expected;
  while (in >> op1 >> op2 >> expected) {
    EXPECT_EQ(expected, test_with_data::add(op1, op2));
  }
  in.close();
}

}

int main(int argc, char **argv) {
  try {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
  } catch (std::exception &e) {
    std::cerr << "Unhandled Exception: " << e.what() << std::endl;
  }
  return 1;
}

With this CMake code:

catkin_add_gtest(${PROJECT_NAME}2-test test/test_with_data2.cpp WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test)
if(TARGET ${PROJECT_NAME}2-test)
  target_link_libraries(${PROJECT_NAME}2-test add)
endif()

I have setup a demo package that demonstrates both methods here:

https://github.com/wjwwood/catkin_demos/tree/master/test_with_data

You can run them by making a workspace, and invoking catkin_make run_tests:

$ mkdir test_ws
$ cd test_ws
$ mkdir src
$ cd src
$ ln -s /path/to/catkin_demos/test_with_data ./
$ source /opt/ros/groovy/setup.bash  # OR ln -s /path/to/catkin ./
$ cd ..
$ catkin_make run_tests              # OR ./src/catkin/bin/catkin_make run_tests
edit flag offensive delete link more

Comments

Thanks for the very thorough answer. I was able to download & run your test code successfully. My first try at porting Option #2 to my code resulted in an error: "No such file or directory: 'test'", but I'll look more into it tomorrow. I should be able to figure it out from your example.

Jeremy Zoss gravatar image Jeremy Zoss  ( 2013-04-02 12:45:58 -0500 )edit

I got a chance to test out this demo code more tonight, and think I found some "bugs". See my edits to the original question above.

Jeremy Zoss gravatar image Jeremy Zoss  ( 2013-04-02 18:24:44 -0500 )edit

@Jeremy Zoss Thanks for catching those bugs! I'll update the example and the answer.

William gravatar image William  ( 2013-04-02 20:53:54 -0500 )edit
2

Nice work, guys! We need a catkin doc page describing how to build unit tests. This would make a valuable start.

joq gravatar image joq  ( 2013-04-03 04:57:44 -0500 )edit
0

answered 2013-04-02 06:36:53 -0500

joq gravatar image

Good question. I don't know the answer, but can suggest an alternative.

Catkin does provide a download_test_data(url, filename, md5sum) macro to replace the old rosbuild_download_test_data(url, filename, md5sum) command.

You can use that if you store the test data on a server (a good thing for large data files).

edit flag offensive delete link more

Comments

Good suggestion. But in this case, it's just a small URDF. I debated hardcoding it as a string, but it feels cleaner to have the URDF as a standalone file. I'll look into whether I can use the download_test_data command to copy from a local file URI instead of a remote server.

Jeremy Zoss gravatar image Jeremy Zoss  ( 2013-04-02 06:56:47 -0500 )edit

Unfortunately, it looks like download_test_data copies files to the "build" space, not the "devel" space. I've tried several tricks, but can't get it to download to the "devel" space. Which means I'm stuck hardcoding a relative path that assumes a catkin-ws layout, unless anyone's got a better idea

Jeremy Zoss gravatar image Jeremy Zoss  ( 2013-04-02 07:27:36 -0500 )edit

@JeremyZoss: According to this documentation, the command catkin_download_test_data has an option to specify the destination of the data.

luator gravatar image luator  ( 2015-06-01 04:45:41 -0500 )edit

Question Tools

Stats

Asked: 2013-04-02 06:07:51 -0500

Seen: 3,916 times

Last updated: Apr 03 '13