catkin_make run_tests と catkin_make test の違い

# はじめに
ROSコードのユニットテストの枠組みとして，`rostest`と言うものがあります．

詳細は[ROS Wiki - rostest](http://wiki.ros.org/rostest)あたりを覗くとなんとなく分かった気になれます．

この`rostest`の実行方法が2つあることに疑問を感じ，調べたことを共有することとします．

# `rostest`の2つの実行方法とは？
下記2つのコマンドを指します．

- `catkin_make run_tests`
- `catkin_make test`

出力されるログが異なるのですが，どっちでもテストはできちゃいます．

# ドキュメントにはなんて書いてある？
[ROS Wiki - rostest](http://wiki.ros.org/rostest)を見てみると，`catkin`のタブを選択すると`run tests`の説明が出てきて，`rosbuild`のタブを選択すると`test`の説明が出てきます．

「ビルドシステムでコマンドが違うだけ，ちゃんちゃん」という話なら良かったのですが，いかんせん`catkin`でも`catkin_make test`が出来てしまいます．エイリアスとして残してくれているとかそういう位置づけなのかもしれませんが，じゃあ何が違うのかが余計気になってきます．

と思って調べてみても，これら2つのコマンドの違いをまとめた記述が全然出てこない笑．

# 結局，利用者側から見た`run_tests`と`test`の違いは？
色々試したところ，各々下記のような特長があることが分かりました．

## `catkin_make runtests`
### Pros
- cppのテスト(`gtest`)を使う場合，テストコードを初回ビルドするのにこちらが必須．
- テストノードが非同期でどんどん実行されるのでテスト実行時間自体は短い．
- 詳細なエラーログを吐く上，テスト別の実行もできるので，デバッグ時はこちら便利．
  * `run_tests_` でTabを押すと補完候補が出るので，特定のテストノードの実行が可能．

### Cons
- テストがたくさんある場合，どのテストが`Fail`したのかがパッと見てわかりづらい．
- 常に0(success)を返すので，これ`run_tests`単体ではテストが`Fail`したかを判別できない．
   - 判定には`catkin_test_results`というコマンドを使う．直前のテストで1つでも`Fail`があれば0ではない値を返す．

## `catkin_make test`
### Pros
- テスト全体の視認性が非常に良い．
- 一度ビルドしたあとなら，普段使いのチェック用にするのが良さげ．
- こちらでエラーを吐いたテストは，`catkin_make run_tests`側で詳細にデバッグ．
- 直前のテストで1つでも`Fail`があれば0ではない値を返すので，`test`単体でテストと判定が可能．

### Cons
- cppのテスト(`gtest`)を使う場合，こちらではビルドしない．
  * 必ず `catkin_make runtests` のあとに，`catkin_make test`を行う必要がある．
- `Fail`時にログを出してくれない．ただ`Fail`したとしか言ってくれない．
- シーケンシャルに実行するのでテスト実行時間が長い．

# どう使い分けるか？
というわけで，私は勝手に下記のように使い分けることにしています．

- 初回はビルドのため必ず`catkin_make run_tests`を実行する．
  * でもどれが`Fail`したのか良くわからない．困った．
- そこで，ビルドが終わったのを確認して別のターミナルで`catkin_make test`で`Fail`するテストを確認する．
  * でも，`Fail`の理由は分からない．困った．
- そこで，`Fail`したテストを確認したら，`catkin_make run_tests_hogehoge`で，特定のテストだけ実行して詳細なデバッグログを確認する．

これにて一応一見落着．何か，行ったり来たりで，本当にこれで正しいのかがよく分かりません ^^;

# 上記仕様を手軽に確かめる方法

## 参考リポジトリ
下記リポジトリがシンプルととても分かり易いです．

- [felixduvallet/ros-travis-integration](https://github.com/felixduvallet/ros-travis-integration)

## ディレクトリ構成
テスト部分だけ抜粋したディレクトリ構成です．

```
ros-travis-integration/
│
└ ros_pkg_with_tests/
     │
     ├ scripts/
     │     │
     │     ├ test/
     │     │     │
     │     │     └ test_double.py
     │     │          ↑ python用 テストコード．
     │     └ double.py
     │          ↑ python コード実体．
     ├ src/
     │     │
     │     ├ test/
     │     │     │
     │     │     └ test_square.cpp
     │     │          ↑ cpp用 テストコード．
     │     ├ square.cpp
     │     │     ↕ cpp コード実体
     │     └ square.h
     │
     ├ package.xml
     │
     └ CMakeLists.txt
```

## 挙動の確認
今回はこのコードの中身は追わず，単に`run_tests`と`test`の挙動を確認することとします．

`<your_ws>/src/`に，上記リポジトリをクローンしておきます．

```bash
$ cd <your_ws>
$ catkin_make
$ source devel/setup.bash
```

### テストコードビルド前に`catkin_make test`を試す
一回，`test`を試してみます．ビルドされていないテストコードは実行できないはずです．

```bash
$ cd <your_ws>
$ catkin_make test
Base path: /<your_ws>
Source space: /<your_ws>/src
Build space: /<your_ws>/build
Devel space: /<your_ws>/devel
Install space: /<your_ws>/install
####
#### Running command: "make cmake_check_build_system" in "/<your_ws>/build"
####
####
#### Running command: "make test -j8 -l8" in "/<your_ws>/build"
####
Running tests...
Test project /<your_ws>/build
    Start 1: _ctest_ros_pkg_with_tests_gtest_ros_pkg_with_tests-test
1/2 Test #1: _ctest_ros_pkg_with_tests_gtest_ros_pkg_with_tests-test ...***Failed    0.05 sec
    Start 2: _ctest_ros_pkg_with_tests_nosetests_scripts.test
2/2 Test #2: _ctest_ros_pkg_with_tests_nosetests_scripts.test ..........   Passed    0.47 sec

50% tests passed, 1 tests failed out of 2

Total Test time (real) =   0.53 sec

The following tests FAILED:
	  1 - _ctest_ros_pkg_with_tests_gtest_ros_pkg_with_tests-test (Failed)
Errors while running CTest
make: *** [test] エラー 8
```

### `catkin_make run_tests`を試す
```bash
$ cd <your_ws>
$ catkin_make run_tests
Base path: /<your_ws>
Source space: /<your_ws>/src
Build space: /<your_ws>/build
Devel space: /<your_ws>/devel
Install space: /<your_ws>/install
####
#### Running command: "make cmake_check_build_system" in "/<your_ws>/build"
####
####
#### Running command: "make run_tests -j8 -l8" in "/<your_ws>/build"
####
Scanning dependencies of target clean_test_results_ros_pkg_with_tests
Scanning dependencies of target gtest
[ 25%] Built target square
[ 50%] Removing test result files from '/<your_ws>/build/test_results/ros_pkg_with_tests'
- removing '/<your_ws>/build/test_results/ros_pkg_with_tests/MISSING-gtest-ros_pkg_with_tests-test.xml'
- removing '/<your_ws>/build/test_results/ros_pkg_with_tests/nosetests-scripts.test.xml'
Building CXX object gtest/CMakeFiles/gtest.dir/src/gtest-all.cc.o
[ 50%] Built target clean_test_results_ros_pkg_with_tests
Linking CXX shared library libgtest.so
[ 50%] Built target gtest
Scanning dependencies of target gtest_main
[ 75%] Building CXX object gtest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.o
Linking CXX shared library libgtest_main.so
[ 75%] Built target gtest_main
Scanning dependencies of target ros_pkg_with_tests-test
[100%] Building CXX object ros-travis-integration/ros_pkg_with_tests/CMakeFiles/ros_pkg_with_tests-test.dir/src/test/test_square.cpp.o
Linking CXX executable /<your_ws>/devel/lib/ros_pkg_with_tests/ros_pkg_with_tests-test
[100%] Built target ros_pkg_with_tests-test
Scanning dependencies of target tests
[100%] Built target tests
Scanning dependencies of target _run_tests_ros_pkg_with_tests_nosetests_scripts.test
Scanning dependencies of target _run_tests_ros_pkg_with_tests_gtest_ros_pkg_with_tests-test
-- run_tests.py: execute commands
-- run_tests.py: execute commands
  /usr/bin/cmake -E make_directory /<your_ws>/build/test_results/ros_pkg_with_tests
  /<your_ws>/devel/lib/ros_pkg_with_tests/ros_pkg_with_tests-test --gtest_output=xml:/<your_ws>/build/test_results/ros_pkg_with_tests/gtest-ros_pkg_with_tests-test.xml
  /usr/bin/nosetests-2.7 -P --process-timeout=60 --where=/<your_ws>/src/ros-travis-integration/ros_pkg_with_tests/scripts/test --with-xunit --xunit-file=/<your_ws>/build/test_results/ros_pkg_with_tests/nosetests-scripts.test.xml
[==========] Running 2 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 2 tests from TestSuite
[ RUN      ] TestSuite.squareTwo
[       OK ] TestSuite.squareTwo (0 ms)
[ RUN      ] TestSuite.squareFour
[       OK ] TestSuite.squareFour (0 ms)
[----------] 2 tests from TestSuite (0 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test case ran. (0 ms total)
[  PASSED  ] 2 tests.
-- run_tests.py: verify result "/<your_ws>/build/test_results/ros_pkg_with_tests/gtest-ros_pkg_with_tests-test.xml"
[100%] Built target _run_tests_ros_pkg_with_tests_gtest_ros_pkg_with_tests-test
Scanning dependencies of target _run_tests_ros_pkg_with_tests_gtest
[100%] Built target _run_tests_ros_pkg_with_tests_gtest
....
----------------------------------------------------------------------
Ran 4 tests in 0.001s

OK
-- run_tests.py: verify result "/<your_ws>/build/test_results/ros_pkg_with_tests/nosetests-scripts.test.xml"
[100%] Built target _run_tests_ros_pkg_with_tests_nosetests_scripts.test
Scanning dependencies of target _run_tests_ros_pkg_with_tests_nosetests
[100%] Built target _run_tests_ros_pkg_with_tests_nosetests
Scanning dependencies of target _run_tests_ros_pkg_with_tests
[100%] Built target _run_tests_ros_pkg_with_tests
Scanning dependencies of target run_tests
[100%] Built target run_tests
```

ビルドがされています．
ログが詳細なのは良いのですが，少しごちゃごちゃしています ^^;

このサンプルではテストの数が少ないのでまだいいほうですが，テスト数がウン10個とかになると，もはやパット見ただけではわけが分からなくなります笑．こっちとしては，まずは単純にテストが通ったかどうかだけを知りたいのです．

### また`catkin_make test`を試す
というわけで，晴れてビルドされたコードでこちらを試してみます．

```bash
$ cd <your_ws>
$ catkin_make test
Base path: /<your_ws>
Source space: /<your_ws>/src
Build space: /<your_ws>/build
Devel space: /<your_ws>/devel
Install space: /<your_ws>/install
####
#### Running command: "make cmake_check_build_system" in "/<your_ws>/build"
####
####
#### Running command: "make test -j8 -l8" in "/<your_ws>/build"
####
Running tests...
Test project /<your_ws>/build
    Start 1: _ctest_ros_pkg_with_tests_gtest_ros_pkg_with_tests-test
1/2 Test #1: _ctest_ros_pkg_with_tests_gtest_ros_pkg_with_tests-test ...   Passed    0.04 sec
    Start 2: _ctest_ros_pkg_with_tests_nosetests_scripts.test
2/2 Test #2: _ctest_ros_pkg_with_tests_nosetests_scripts.test ..........   Passed    0.34 sec

100% tests passed, 0 tests failed out of 2

Total Test time (real) =   0.39 sec
```

なんと分かりやすいこと！

### `catkin_make run_tests_ros_pkg_with_tests_nosetests_scripts.test`を試す
仮に`_ctest_ros_pkg_with_tests_nosetests_scripts.test`が`Fail`したとして，このテストだけを実行してみます．

```bash
$ cd <your_ws>
$ catkin_make run_tests_ros_pkg_with_tests_nosetests_scripts.test
Base path: /<your_ws>
Source space: /<your_ws>/src
Build space: /<your_ws>/build
Devel space: /<your_ws>/devel
Install space: /<your_ws>/install
####
#### Running command: "make cmake_check_build_system" in "/<your_ws>/build"
####
####
#### Running command: "make run_tests -j8 -l8" in "/<your_ws>/build"
####
Scanning dependencies of target run_tests_ros_pkg_with_tests_nosetests_scripts.test
-- run_tests.py: execute commands
  /usr/bin/cmake -E make_directory /<your_ws>/build/test_results/ros_pkg_with_tests
  /usr/bin/nosetests-2.7 -P --process-timeout=60 --where=/<your_ws>/src/ros-travis-integration/ros_pkg_with_tests/scripts/test --with-xunit --xunit-file=/<your_ws>/build/test_results/ros_pkg_with_tests/nosetests-scripts.test.xml
....
----------------------------------------------------------------------
Ran 4 tests in 0.001s

OK
-- run_tests.py: verify result "/<your_ws>/build/test_results/ros_pkg_with_tests/nosetests-scripts.test.xml"
Built target run_tests_ros_pkg_with_tests_nosetests_scripts.test
```

単一のテストコードのログだけなので，先ほど`catkin_make run_tests`したときよりはスッキリです．

なんとなく，提案した使い分けルールの雰囲気を感じてもらえたなら，幸いです．

# おわりに
`run_tests`と`test`の違いについてまとめてみました．

使い分けについては，一般的な方法かどうかは分からないので，悪しからず… ^^;
（`run_tests`側のオプションでうまいことならないのかと感じているのですが，それっぽいオプションが見当たりませんでした…．）