diff --git a/demos/ota_nav2_sensor_fix/ota_update_plugin/CMakeLists.txt b/demos/ota_nav2_sensor_fix/ota_update_plugin/CMakeLists.txt index 050ca09..ba7618a 100644 --- a/demos/ota_nav2_sensor_fix/ota_update_plugin/CMakeLists.txt +++ b/demos/ota_nav2_sensor_fix/ota_update_plugin/CMakeLists.txt @@ -74,6 +74,7 @@ install(DIRECTORY include/ DESTINATION include) if(BUILD_TESTING) find_package(ament_cmake_gtest REQUIRED) + include(ROS2MedkitTestDomain) ament_add_gtest(test_ota_update_plugin test/test_operation_dispatcher.cpp test/test_catalog_client.cpp @@ -81,6 +82,17 @@ if(BUILD_TESTING) ) target_link_libraries(test_ota_update_plugin ota_update_plugin_core) target_include_directories(test_ota_update_plugin PRIVATE src) + # The suite creates no ROS entity: the catalog client, the operation + # dispatcher and the plugin are exercised against doubles, and the one test + # that uses the real ProcessRunner spawns a path that does not exist. Nothing + # here touches DDS, so the test needs no domain of its own. + # + # Guarded, because ament_add_gtest registers nothing when the executable was + # not created, and declaring a property on a test that does not exist is a + # hard configure error rather than the skip ament intended. + if(TARGET test_ota_update_plugin) + medkit_test_needs_no_domain(test_ota_update_plugin) + endif() endif() ament_package() diff --git a/demos/ota_nav2_sensor_fix/ros2_packages/ota_nav2_sensor_fix_demo/config/nav2_params.yaml b/demos/ota_nav2_sensor_fix/ros2_packages/ota_nav2_sensor_fix_demo/config/nav2_params.yaml index ad5e816..721d9b7 100644 --- a/demos/ota_nav2_sensor_fix/ros2_packages/ota_nav2_sensor_fix_demo/config/nav2_params.yaml +++ b/demos/ota_nav2_sensor_fix/ros2_packages/ota_nav2_sensor_fix_demo/config/nav2_params.yaml @@ -247,7 +247,19 @@ global_costmap: raytrace_max_range: 3.0 raytrace_min_range: 0.0 obstacle_max_range: 2.5 - obstacle_min_range: 0.0 + # Global planning ignores anything closer than this, and that decides + # WHICH Nav2 node fails when the lidar regresses. The phantom sector + # broken_lidar overlays is fixed to the robot and reports a constant + # range (phantom_range_m, 0.22 m at the laser), so without this floor + # the global costmap paints it around the robot's own position and + # navfn aborts with "failed to create plan" before the controller + # runs out of trajectories - and the log bridge watches + # controller_server, not planner_server, so the supporting fault + # would go missing. The floor must therefore stay above + # phantom_range_m and below the distance at which real obstacles + # matter to a global plan; anything that close is the local + # costmap's problem, and it reads the same scan with no floor. + obstacle_min_range: 0.35 static_layer: plugin: "nav2_costmap_2d::StaticLayer" map_subscribe_transient_local: True diff --git a/tests/smoke_test_demo_narrative.sh b/tests/smoke_test_demo_narrative.sh index e42700a..1167b28 100755 --- a/tests/smoke_test_demo_narrative.sh +++ b/tests/smoke_test_demo_narrative.sh @@ -29,7 +29,8 @@ # scan_sensor_node is running broken_lidar_node; fixed_lidar_3_0_1 is # NOT yet registered (boot catalog holds only the bad update). # 2. send-goal.sh -> ACTION_NAVIGATE_TO_POSE_ABORTED reaches CONFIRMED on -# bt-navigator, and controller-server picks up a supporting LOG_* fault. +# bt-navigator, and controller-server picks up a supporting LOG_* fault +# whose message is the controller's own stall, not just any error. # 3. Fault detail (bt-navigator) has environment_data.snapshots >= 1, and # the rosbag bulk-data download returns a non-empty MCAP body. # 4. publish-fix.sh -> fixed_lidar_3_0_1 appears in /updates (SOVD @@ -68,6 +69,17 @@ CONTROLLER_ENTITY="apps/controller-server" # controller-server's LOG_CONTROLLER_SERVER_* code is content-hashed (derived # from the log message), so it is never matched by exact code - only by # "does this entity have any fault at all" (see fault_present with code=""). +# +# The message is matched instead, because "any fault on controller-server" is +# too weak for the one assertion that says WHICH Nav2 node failed. The log +# bridge promotes every controller_server ERROR at or above its severity floor, +# so a TF error or a lifecycle error would satisfy a bare count check. These two +# messages are the controller saying it cannot move: the progress checker +# (movement_time_allowance) and the controller patience (failure_tolerance). +# If the global costmap ever starts marking the phantom again, planner_server +# aborts the goal first, controller_server logs neither of these, and this is +# the assertion that goes red. +CONTROLLER_STALL_MSG="Failed to make progress|Controller patience exceeded" # --- Helpers built on top of smoke_lib.sh's api_get/poll_until ------------- @@ -231,12 +243,14 @@ else "fault never reached CONFIRMED within 60s - either nav2 didn't accept the goal or the action-status bridge is broken" fi -echo " Waiting for a supporting LOG_* fault on ${CONTROLLER_ENTITY} (max 60s)..." -if poll_until "/${CONTROLLER_ENTITY}/faults" '.items | length > 0' 60; then - pass "supporting LOG_* fault present on ${CONTROLLER_ENTITY}" +echo " Waiting for a supporting LOG_* stall fault on ${CONTROLLER_ENTITY} (max 60s)..." +if poll_until "/${CONTROLLER_ENTITY}/faults" \ + ".items[] | select(.description | test(\"${CONTROLLER_STALL_MSG}\"))" \ + 60; then + pass "supporting LOG_* fault present on ${CONTROLLER_ENTITY} and reports the controller stall" else - fail "supporting LOG_* fault present on ${CONTROLLER_ENTITY}" \ - "no fault appeared within 60s - either nav2 didn't stall or the log bridge is broken" + fail "supporting LOG_* fault present on ${CONTROLLER_ENTITY} and reports the controller stall" \ + "no fault matching '${CONTROLLER_STALL_MSG}' within 60s - either nav2 aborted somewhere other than the controller, or the log bridge is broken" fi # ---------------------------------------------------------------------