Unit Testing Modules

I am trying to write unit/functional tests for a custom module, but following the directions on the developer guide leads to linking failures during the build. To verify that it is not my code that is causing the issue, I added a barebones functional test to the logger module.

#include <logger/logger.h>
#include <gtest/gtest.h>

class loggerTest : public ::testing::Test
{
public:
	void SetUp() override
	{
		return;
	}
};

TEST_F(loggerTest, testSomeFunction)
{
	EXPECT_EQ(0, 0);
}

Adding the corresponding gtest statement to the CMakeLists.txt file:

px4_add_module(
	MODULE modules__logger
	MAIN logger
	PRIORITY "SCHED_PRIORITY_MAX-30"
	COMPILE_FLAGS
		-Wno-cast-align # TODO: fix and enable
	SRCS
		logger.cpp
		log_writer.cpp
		log_writer_file.cpp
		log_writer_mavlink.cpp
		util.cpp
		watchdog.cpp
	DEPENDS
		version
	)
px4_add_functional_gtest(SRC loggerTest.cpp LINKLIBS modules__logger)

Then building with make tests results in the following errors:

[ 93%] Building CXX object src/modules/logger/CMakeFiles/functional-logger.dir/loggerTest.cpp.o
In file included from /home/jonathan/Code/pixhawk/src/modules/logger/logger.h:42,
                 from /home/jonathan/Code/pixhawk/src/modules/logger/loggerTest.cpp:36:
/home/jonathan/Code/pixhawk/src/lib/version/version.h: In function ‘const char* px4_board_name()’:
/home/jonathan/Code/pixhawk/src/lib/version/version.h:56:9: error: ‘PX4_BOARD_NAME’ was not declared in this scope
   56 |  return PX4_BOARD_NAME;
      |         ^~~~~~~~~~~~~~
In file included from /home/jonathan/Code/pixhawk/src/platforms/px4_defines.h:42,
                 from /home/jonathan/Code/pixhawk/src/modules/logger/log_writer_file.h:36,
                 from /home/jonathan/Code/pixhawk/src/modules/logger/log_writer.h:36,
                 from /home/jonathan/Code/pixhawk/src/modules/logger/logger.h:36,
                 from /home/jonathan/Code/pixhawk/src/modules/logger/loggerTest.cpp:36:
/home/jonathan/Code/pixhawk/src/platforms/px4_module.h: In static member function ‘static int ModuleBase<T>::run_trampoline(int, char**)’:
/home/jonathan/Code/pixhawk/src/platforms/px4_log.h:232:29: error: ‘MODULE_NAME’ was not declared in this scope
  232 |   px4_log_modulename(level, MODULE_NAME, fmt, ##__VA_ARGS__); \
      |                             ^~~~~~~~~~~
/home/jonathan/Code/pixhawk/src/platforms/px4_log.h:428:27: note: in expansion of macro ‘__px4_log_modulename’
  428 | #define PX4_ERR(FMT, ...) __px4_log_modulename(_PX4_LOG_LEVEL_ERROR, FMT, ##__VA_ARGS__)
      |                           ^~~~~~~~~~~~~~~~~~~~
/home/jonathan/Code/pixhawk/src/platforms/px4_module.h:185:4: note: in expansion of macro ‘PX4_ERR’
  185 |    PX4_ERR("failed to instantiate object");
      |    ^~~~~~~
/home/jonathan/Code/pixhawk/src/platforms/px4_module.h: In static member function ‘static int ModuleBase<T>::start_command_base(int, char**)’:
/home/jonathan/Code/pixhawk/src/platforms/px4_log.h:232:29: error: ‘MODULE_NAME’ was not declared in this scope
  232 |   px4_log_modulename(level, MODULE_NAME, fmt, ##__VA_ARGS__); \
      |                             ^~~~~~~~~~~
/home/jonathan/Code/pixhawk/src/platforms/px4_log.h:428:27: note: in expansion of macro ‘__px4_log_modulename’
  428 | #define PX4_ERR(FMT, ...) __px4_log_modulename(_PX4_LOG_LEVEL_ERROR, FMT, ##__VA_ARGS__)
      |                           ^~~~~~~~~~~~~~~~~~~~
/home/jonathan/Code/pixhawk/src/platforms/px4_module.h:208:4: note: in expansion of macro ‘PX4_ERR’
  208 |    PX4_ERR("Task already running");
      |    ^~~~~~~
/home/jonathan/Code/pixhawk/src/platforms/px4_log.h:232:29: error: ‘MODULE_NAME’ was not declared in this scope
  232 |   px4_log_modulename(level, MODULE_NAME, fmt, ##__VA_ARGS__); \
      |                             ^~~~~~~~~~~
/home/jonathan/Code/pixhawk/src/platforms/px4_log.h:428:27: note: in expansion of macro ‘__px4_log_modulename’
  428 | #define PX4_ERR(FMT, ...) __px4_log_modulename(_PX4_LOG_LEVEL_ERROR, FMT, ##__VA_ARGS__)
      |                           ^~~~~~~~~~~~~~~~~~~~
/home/jonathan/Code/pixhawk/src/platforms/px4_module.h:214:5: note: in expansion of macro ‘PX4_ERR’
  214 |     PX4_ERR("Task start failed (%i)", ret);
      |     ^~~~~~~
/home/jonathan/Code/pixhawk/src/platforms/px4_module.h: In static member function ‘static int ModuleBase<T>::status_command()’:
/home/jonathan/Code/pixhawk/src/platforms/px4_log.h:232:29: error: ‘MODULE_NAME’ was not declared in this scope
  232 |   px4_log_modulename(level, MODULE_NAME, fmt, ##__VA_ARGS__); \
      |                             ^~~~~~~~~~~
/home/jonathan/Code/pixhawk/src/platforms/px4_log.h:388:29: note: in expansion of macro ‘__px4_log_modulename’
  388 | #define PX4_INFO(FMT, ...)  __px4_log_modulename(_PX4_LOG_LEVEL_INFO, FMT, ##__VA_ARGS__)
      |                             ^~~~~~~~~~~~~~~~~~~~
/home/jonathan/Code/pixhawk/src/platforms/px4_module.h:287:4: note: in expansion of macro ‘PX4_INFO’
  287 |    PX4_INFO("not running");
      |    ^~~~~~~~
/home/jonathan/Code/pixhawk/src/platforms/px4_module.h: In member function ‘virtual int ModuleBase<T>::print_status()’:
/home/jonathan/Code/pixhawk/src/platforms/px4_log.h:232:29: error: ‘MODULE_NAME’ was not declared in this scope
  232 |   px4_log_modulename(level, MODULE_NAME, fmt, ##__VA_ARGS__); \
      |                             ^~~~~~~~~~~
/home/jonathan/Code/pixhawk/src/platforms/px4_log.h:388:29: note: in expansion of macro ‘__px4_log_modulename’
  388 | #define PX4_INFO(FMT, ...)  __px4_log_modulename(_PX4_LOG_LEVEL_INFO, FMT, ##__VA_ARGS__)
      |                             ^~~~~~~~~~~~~~~~~~~~
/home/jonathan/Code/pixhawk/src/platforms/px4_module.h:301:3: note: in expansion of macro ‘PX4_INFO’
  301 |   PX4_INFO("running");
      |   ^~~~~~~~
/home/jonathan/Code/pixhawk/src/platforms/px4_module.h: In static member function ‘static int ModuleBase<T>::wait_until_running()’:
/home/jonathan/Code/pixhawk/src/platforms/px4_log.h:232:29: error: ‘MODULE_NAME’ was not declared in this scope
  232 |   px4_log_modulename(level, MODULE_NAME, fmt, ##__VA_ARGS__); \
      |                             ^~~~~~~~~~~
/home/jonathan/Code/pixhawk/src/platforms/px4_log.h:428:27: note: in expansion of macro ‘__px4_log_modulename’
  428 | #define PX4_ERR(FMT, ...) __px4_log_modulename(_PX4_LOG_LEVEL_ERROR, FMT, ##__VA_ARGS__)
      |                           ^~~~~~~~~~~~~~~~~~~~
/home/jonathan/Code/pixhawk/src/platforms/px4_module.h:376:4: note: in expansion of macro ‘PX4_ERR’
  376 |    PX4_ERR("Timed out while waiting for thread to start");

Any advice on how to fix this issue? This is likely the same issue that was referenced here, but that got no responses.

I don’t think module tests use gtest cmake macro. Take a look at the Commander module tests, for example.

Library tests do use gtest and it might work for you to push log calls to the module code and out of any library code.

Ok that’s definitely doable. So the official strategy is to write a custom testing framework for modules, especially those that depend on logging, rather than using GTest?

I’ve been pouring over the cmake files trying to find where the discrepancy between a standard build and a testing build comes from but haven’t been able to find it yet.

I don’t know if there is an official strategy. It works pretty well for me to limit global namespace access to the module class and provide indirect access via a local interface. That approach keeps the logic of the module independent and more easily verifiable via unit testing. The module class becomes a simple bridge to the outside world that doesn’t need unit testing, itself.

Check out cmake/gtest/px4_add_gtest.cmake macro to see how the recipe for gtest executables.

1 Like