@@ -386,6 +386,59 @@ TEST(tf2_time, To_From_Duration)
386386 }
387387}
388388
389+ TEST (tf2_canTransform, ExtrapolationErrorReportsCorrectLatestTime)
390+ {
391+ // this test verifies if correct time difference between transforms
392+ // are reported when transform from a to b fails in both source-to-root
393+ // and root-to-source walks.
394+ tf2::BufferCore tfc;
395+ geometry_msgs::msg::TransformStamped st;
396+ std::string error_msg;
397+
398+ // 1. publish map -> odom (stale by 10s, latest is 90.01)
399+ st.header .frame_id = " map" ;
400+ st.child_frame_id = " odom" ;
401+ st.transform .rotation .w = 1.0 ;
402+
403+ st.header .stamp .sec = 90 ;
404+ st.header .stamp .nanosec = 0 ;
405+ tfc.setTransform (st, " authority1" );
406+
407+ st.header .stamp .sec = 90 ;
408+ // 90.01s
409+ st.header .stamp .nanosec = 10000000 ;
410+ tfc.setTransform (st, " authority1" );
411+
412+ // 2. publish odom -> base_link (stale by 0.1s, latest is 99.91)
413+ st.header .frame_id = " odom" ;
414+ st.child_frame_id = " base_link" ;
415+
416+ st.header .stamp .sec = 99 ;
417+ // 99.90s
418+ st.header .stamp .nanosec = 900000000 ;
419+ tfc.setTransform (st, " authority1" );
420+
421+ st.header .stamp .sec = 99 ;
422+ // 99.91s
423+ st.header .stamp .nanosec = 910000000 ;
424+ tfc.setTransform (st, " authority1" );
425+
426+ // 3. query odom -> base_link at T=100.0s
427+ bool can_transform = tfc.canTransform (
428+ " odom" , " base_link" ,
429+ tf2::timeFromSec (100.0 ),
430+ &error_msg);
431+
432+ EXPECT_FALSE (can_transform);
433+
434+ // 4. verify the error message:
435+ // the message should report the latest data for odom->base_link (99.91),
436+ // not map->odom (90.01).
437+ EXPECT_NE (error_msg.find (" 99.91" ), std::string::npos);
438+
439+ EXPECT_EQ (error_msg.find (" 90.01" ), std::string::npos);
440+ }
441+
389442TEST (tf2_convert, Covariance_RowMajor_To_Nested)
390443{
391444 // test verifies the correct conversion of the flat covariance array to a
0 commit comments