ROS2講座21 colcon testを実行する

# 環境

この記事は以下の環境で動いています。

| 項目 |	値 |
|:--|:--|
| CPU | Core i5-8250U |
| Ubuntu | 22.04 |
| ROS2 | Humble |
| Qt | 5.15.3 |
| SQLite | 3.37.2 |

# 概要

ROS2には`colcon test`という強力なテストツールがあります。このツールでコードの不備を指摘することが出来ます。なかなか全機能OKの状態を維持するのは大変ですが、実行することでコードをクリーンな状態で保てます。

## コードスタイルチェッカー
`colcon test`では以下の8項目に対応しています。

| pkg名 | 対象 | 説明 |
|:-:|:-:|:-:|
| [ament_cmake_copyright](https://github.com/ament/ament_lint/blob/master/ament_cmake_copyright/doc/index.rst) | c++/python/cmake | ライセンスなどの記載の確認 |
| [ament_cmake_cppcheck](https://github.com/ament/ament_lint/blob/master/ament_cmake_cppcheck/doc/index.rst) | c++ | 論理エラー（バッファオーバーフロー等）の検知 |
| [ament_cmake_cpplint](https://github.com/ament/ament_lint/blob/master/ament_cmake_cpplint/doc/index.rst) | c++ | コードスタイルチェッカー |
| [ament_cmake_flake8](https://github.com/ament/ament_lint/blob/master/ament_cmake_flake8/doc/index.rst) | python | コードスタイルチェッカー |
| [ament_cmake_lint_cmake](https://github.com/ament/ament_lint/blob/master/ament_cmake_lint_cmake/doc/index.rst) | cmake | コードスタイルチェッカー |
| [ament_cmake_pep257](https://github.com/ament/ament_lint/blob/master/ament_cmake_pep257/doc/index.rst) | python | pep257スタイルチェッカー |
| [ament_cmake_uncrustify](https://github.com/ament/ament_lint/blob/master/ament_cmake_uncrustify/doc/index.rst) | c++ | uncrustifyスタイルチェッカー |
| [ament_cmake_xmllint](https://github.com/ament/ament_lint/blob/master/ament_cmake_xmllint/doc/index.rst) | xml | コードスタイルチェッカー |

## unit_test
`colcon test`ではgtestを使ったunit_testの実行に対応しています。

# ソースコード

## c++

今回は単純な足し算をするライブラリを含むpackageを作成します。これにたいしてコードスタイルチェッカーの適用ととgtestの記載をします。
コードチェッカーをパスするためにライセンスの記載なども行っています。

```c++:test_lecture/include/test_lecture/calculator.hpp
// Copyright 2024 project-srs.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

#pragma once

namespace test_lecture
{
int add(const int a, const int b);
}
```

```c++:test_lecture/src/calculator.cpp
// Copyright 2024 project-srs.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

#include <test_lecture/calculator.hpp>

int test_lecture::add(const int a, const int b)
{
  return a + b;
}
```

## package.xml
テスト依存を記載します。ここでは今回のコードチェックで使う内容とgtestの依存を書きます。

```xml:test_lecture/package.xmlの一部
<package format="3">
  <buildtool_depend>ament_cmake</buildtool_depend>

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>
  <test_depend>ament_cmake_copyright</test_depend>
  <test_depend>ament_cmake_cppcheck</test_depend>
  <test_depend>ament_cmake_cpplint</test_depend>
  <test_depend>ament_cmake_lint_cmake</test_depend>
  <test_depend>ament_cmake_uncrustify</test_depend>
  <test_depend>ament_cmake_xmllint</test_depend>
  <test_depend>ament_cmake_gtest</test_depend>
  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>
```

## CmakeLists

ライブラリのビルドに関連する部分です。

```cmake:test_lecture/CMakeLists.txtの一部（ライブラリビルド関連部）
include_directories(include)
add_library(${PROJECT_NAME} SHARED
  src/calculator.cpp
)
ament_target_dependencies(${PROJECT_NAME}
  rclcpp
)

install(
  DIRECTORY include/
  DESTINATION include
)
ament_export_dependencies(rclcpp)
ament_export_libraries(${PROJECT_NAME})
ament_export_include_directories(include)
ament_export_targets(export_${PROJECT_NAME} HAS_LIBRARY_TARGET)

install(
  TARGETS ${PROJECT_NAME}
  EXPORT export_${PROJECT_NAME}
  ARCHIVE DESTINATION lib
  LIBRARY DESTINATION lib
  RUNTIME DESTINATION bin
  INCLUDES DESTINATION include
)
```

```cmake:test_lecture/CMakeLists.txtの一部（テスト関連部）
if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  ament_lint_auto_find_test_dependencies()

  find_package(ament_cmake_gtest REQUIRED)
  ament_add_gtest(${PROJECT_NAME}_test
    test/calculator_test.cpp
  )
  target_include_directories(${PROJECT_NAME}_test PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
  )
  target_link_libraries(${PROJECT_NAME}_test
    ${PROJECT_NAME}
  )
endif()
```

* `BUILD_TESTING`は通常の`colcon build`では`true`になっています。
* ` find_package(ament_lint_auto REQUIRED)`で`package.xml`に記載したtest関連の依存をロードします。`ament_lint_auto_find_test_dependencies()`を記載することで`colcon test`時にコードチェック系の実行をします。
    * cmakeのテンプレートである`set(ament_cmake_copyright_FOUND TRUE)`は`ament_cmake_copyright`での、`set(ament_cmake_cpplint_FOUND TRUE)`は`ament_cmake_cpplint`でのコピーライトのチェックを無効にするオプションです。コピーライトのチェックの判定が厳しいので無効にすることが多いと思われます。
* `ament_add_gtest()`でgtestの実行の追加が出来ます。`colcon build`ではgtestのビルドはされますが、テストの実行はされません。

# ビルド

```shell:ビルド
source /opt/ros/humble/setup.bash
cd ros2_ws
colcon build
```

# テストの実行

```shell:実行
source /opt/ros/humble/setup.bash
cd ros2_ws
colcon test
```

* `colcon test --packages-select test_lecture`の様にpackageを指定してテスト実行もできます。
* テストfailがあるpackageは出力で表示されます。
* failの内容は`ros2_ws/log/test_{日時}/{package名}/stdout.txt`や`ros2_ws/build/{package名}/Testing/{日時}/Test.xml`で確認できます。

# 補足
* version2.7の`cppcheck`は実行が遅いという問題があるようで、デフォルトではcppcheckがスキップするようになっています。`export AMENT_CPPCHECK_ALLOW_SLOW_VERSIONS=1`の環境変数をセットするとスキップせずに実行します。
* `cppcheck`は拡張子がh/cファイルはc言語として、hpp/cppファイルはc++言語として解釈されます。

# コードフォーマットのかけ方

## ament_cmake_flake8
`pip install black`でインストール、`black ./`でカレントディレクトリ以下の対象ファイルでフォーマットを適用します。

## ament_cmake_uncrustify
`ament_uncrustify --reformat`を実行することで、カレントディレクトリ以下の対象ファイルでフォーマットを適用します。

# 参考

* [ament_cmake解説和文翻訳](https://www.moriken254.com/entry/2019/05/11/142516)
* [ament_cmake document](https://docs.ros.org/en/humble/How-To-Guides/Ament-CMake-Documentation.html)

# 目次ページへのリンク
[ROS2講座の目次へのリンク](https://qiita.com/srs/items/5f0d765343ca78f524ed)