Skip to content

test(mapoi_server): test_nav2_bridge_unit が稀にタイムアウト — 毎テストのノード生成/破棄が tf2_ros の未修正 race を踏む #461

Description

@kimushun1101

現象

週次 schedule 実行の build-test (lyrical)colcon test でタイムアウト失敗した。

ログの時系列:

  • 先頭 14 テストは各 15–53 ms で完走
  • 15 個目 Nav2BridgeTestFixture.IsWithinArrivalToleranceBoundaryYaw[ RUN ] 出力後に復帰せず kill された
  • [ RUN ] 15 回 / コンストラクタ完了ログ (MapoiNav2Bridge initialized.) 15 回 / [ OK ] 14 回でカウントが一致する
    SetUp は完走しており、返っていないのは TearDown

当該テストの本体は static 純関数への EXPECT_TRUE 1 行だけで、node_ を参照しない。
よってハングはテスト本体ではなく TearDown()node_.reset() にある。

原因

Nav2BridgeTestFixture は毎テスト MapoiNav2Bridge を生成・破棄している。
コンストラクタは単引数版 (= spin_thread=true) の TransformListener を作る。

tf_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_);

単引数版は内部ノードと専用 executor スレッドを持ち、デストラクタで cancel()join() する。
ここに未修正の upstream race がある。

この test binary は 1 run で 67 回 ノードを生成・破棄している (fixture 58 + テスト本体 9)。
そのたびに race の発火機会がある。各テストが 15–23 ms と短命なことが、
cancel-before-spin の窓に正確に嵌まっている。

なお lyrical / humble の既定 RMW はどちらも rmw_fastrtps_cpp であり、distro 差ではなく確率的な発火。
CPU が逼迫する CI ランナーで顕在化しやすい。

対応方針

本番コードの挙動は変えず、テスト側のノード生成回数を削減する。

  1. Nav2BridgeTestFixture からノード生成を撤去する (rclcpp::init の保証のみ残す)。
    node_ を参照しない 45 テストはこれで足りる。
  2. node_ を必要とする 13 テストは、スイート内でノードを 1 個共有する新フィクスチャへ移す。
    共有に伴う順序依存は、各テストで前提を明示的に初期化して潰す。
  3. コンストラクタの分岐そのものを検証する 9 テストは、ノード生成が検証対象なので残す。

これで 67 → 10 に減る。

採らない案

  • ctest timeout の延長: 真のデッドロックなので延ばしても解決しない。
  • #ifdef UNIT_TEST で本番コードに分岐を足す: テストした物と出荷する物が別になる。
  • TransformListenerspin_thread=false にする: 本番の TF 更新スレッド構成が変わる。
    select_map_callback が default group を最大 ~11s 塞ぐ設計 (mapoi_nav_server の callback_group 分離: backend_status timer を blocking 呼び出しと独立させる #213) と干渉するため、
    CI flake 対策とは切り分けて別途検討する。

受入条件

  • humble / lyrical 双方で colcon buildcolcon test --ctest-args -LE launch_test が green
  • テストケース総数が変更前と一致する (取りこぼしが無いこと)
  • --gtest_shuffle を付けても green (順序依存が無いこと)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions