> ## Documentation Index
> Fetch the complete documentation index at: https://docs.roboticks.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Writing Tests in C++

> Use ROBOTICKS_CONFIRMS with gtest and the roboticks_cpp ament_cmake package to write traceable C++ tests for rclcpp nodes.

# Writing tests in C++

For C++ packages, Roboticks ships [`roboticks_cpp`](https://github.com/roboticks-io/roboticks-sdk/tree/main/cpp): a header-only `ament_cmake` `INTERFACE` library that adds a single macro on top of gtest, plus a thread-safe registry that records which requirements each test confirms.

<Info>
  **v0.1 is registry-only.** The C++ side records confirms IDs; a small downstream stitcher merges them into the JUnit XML that gtest emits. Phase 11 will add a native JUnit reporter so the macro writes properties directly. The wire shape on the platform side is identical either way.
</Info>

## Install the package

In your `package.xml`:

```xml theme={null}
<test_depend>roboticks_cpp</test_depend>
<test_depend>ament_cmake_gtest</test_depend>
```

In your `CMakeLists.txt`:

```cmake theme={null}
if(BUILD_TESTING)
  find_package(ament_cmake_gtest REQUIRED)
  find_package(roboticks_cpp REQUIRED)

  ament_add_gtest(test_estop
    test/test_estop.cpp
  )
  target_link_libraries(test_estop
    roboticks_cpp::roboticks_cpp
    ${rclcpp_LIBRARIES}
  )
endif()
```

`roboticks_cpp::roboticks_cpp` is an `INTERFACE` target — no objects to compile, just headers and one inline registry definition.

## The `ROBOTICKS_CONFIRMS` macro

The macro takes a `SuiteName_TestName` token and a comma-separated list of requirement IDs. Place it at the **top** of the test body so the registration happens before any assertion fires:

```cpp theme={null}
#include <gtest/gtest.h>
#include <rclcpp/rclcpp.hpp>
#include <roboticks_cpp/confirms.hpp>

TEST(EmergencyStop, HaltsWithin100ms) {
  ROBOTICKS_CONFIRMS(EmergencyStop_HaltsWithin100ms, "REQ-001", "REQ-014");

  rclcpp::init(0, nullptr);
  auto node = std::make_shared<MyEStopNode>();

  const auto t0 = std::chrono::steady_clock::now();
  node->trigger_estop();
  spin_until(node, [&]{ return node->is_stopped(); }, std::chrono::milliseconds(150));
  const auto elapsed = std::chrono::steady_clock::now() - t0;

  EXPECT_LT(elapsed, std::chrono::milliseconds(100));
  rclcpp::shutdown();
}
```

The token mirrors `SuiteName_TestName` so the stitcher can join macro entries to gtest's JUnit `<testcase name="..." classname="...">` rows.

## The `ConfirmsRegistry`

`ROBOTICKS_CONFIRMS` calls `ConfirmsRegistry::instance().add(token, req_ids)`. The registry:

* Lives in a single translation unit (defined in the `INTERFACE` library's one `.cpp` companion).
* Is **thread-safe** — guarded by a `std::mutex` so parallel test executors don't race.
* Exposes `add(token, ids)`, `dump(path)` to write JSON, and `clear()` for in-process re-use.

A typical gtest `main` dumps the registry on exit:

```cpp theme={null}
int main(int argc, char ** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  const int rc = RUN_ALL_TESTS();
  roboticks_cpp::ConfirmsRegistry::instance().dump("test_results/confirms.json");
  return rc;
}
```

`ament_cmake_gtest` provides this `main` for you when you don't override it; the registry hook lives in the bundled `roboticks_cpp_gtest_main` target if you'd rather link that instead:

```cmake theme={null}
target_link_libraries(test_estop roboticks_cpp::gtest_main)
```

See the [C++ reference](/sdk/cpp-reference) for the full API.

## Stitching confirms into JUnit

`ament_cmake_gtest` writes `test_results/<package>/<test>.gtest.xml`. The stitcher (shipped as `roboticks-stitch-cpp-junit`, installed by `pip install roboticks`) merges `confirms.json` into the XML:

```bash theme={null}
roboticks-stitch-cpp-junit \
  --gtest-xml test_results/my_pkg/test_estop.gtest.xml \
  --confirms test_results/confirms.json \
  --out test_results/my_pkg/test_estop.junit.xml
```

The output matches the [wire contract](/testing/wire-contract). When you upload via `rbtk test cloud` or the GitHub App, the platform reads the stitched XML directly.

<Tip>
  The stitcher is wired into `colcon test --event-handlers roboticks+` automatically when both `roboticks` and `roboticks_cpp` are on `PATH` / `AMENT_PREFIX_PATH`. You almost never run it by hand.
</Tip>

## Gtest assertion helpers

`roboticks_cpp` ships rclcpp-aware helpers that mirror the Python ones:

```cpp theme={null}
#include <roboticks_cpp/assertions.hpp>

TEST(NavStack, PublishesCmdVel) {
  ROBOTICKS_CONFIRMS(NavStack_PublishesCmdVel, "REQ-007");
  auto node = rclcpp::Node::make_shared("test_observer");
  auto msg = roboticks_cpp::assert_topic_published<geometry_msgs::msg::Twist>(
    node, "/cmd_vel", std::chrono::seconds(3));
  EXPECT_GT(msg.linear.x, 0.1);
}
```

Helpers raise gtest `FAIL()` macros on timeout so failures slot into the standard gtest summary.

## A full example

```cpp theme={null}
// test/test_collision_check.cpp
#include <gtest/gtest.h>
#include <rclcpp/rclcpp.hpp>
#include <roboticks_cpp/confirms.hpp>
#include <roboticks_cpp/assertions.hpp>
#include <my_pkg/collision_checker.hpp>

class CollisionCheckerFixture : public ::testing::Test {
 protected:
  void SetUp() override {
    rclcpp::init(0, nullptr);
    node_ = std::make_shared<my_pkg::CollisionChecker>();
  }
  void TearDown() override {
    node_.reset();
    rclcpp::shutdown();
  }
  std::shared_ptr<my_pkg::CollisionChecker> node_;
};

TEST_F(CollisionCheckerFixture, DetectsObstacleWithinRange) {
  ROBOTICKS_CONFIRMS(CollisionCheckerFixture_DetectsObstacleWithinRange,
                     "REQ-051", "REQ-052");
  node_->inject_point_cloud(make_obstacle_at(1.5));
  auto verdict = roboticks_cpp::assert_topic_published<std_msgs::msg::Bool>(
    node_, "/obstacle_detected", std::chrono::seconds(2));
  EXPECT_TRUE(verdict.data);
}
```

## Next

<CardGroup cols={2}>
  <Card title="C++ reference" icon="book" href="/sdk/cpp-reference">
    Full `ConfirmsRegistry` API, macro semantics, threading guarantees.
  </Card>

  <Card title="Wire contract" icon="file-code" href="/testing/wire-contract">
    The JUnit-with-confirms schema both stitchers target.
  </Card>

  <Card title="Pytest tests" icon="python" href="/testing/writing-tests-pytest">
    The Python-side equivalent, for mixed-language packages.
  </Card>

  <Card title="Coverage" icon="percent" href="/testing/coverage">
    Wire gcov/lcov so coverage shows up next to the matrix.
  </Card>
</CardGroup>
