Failure 반환 조건 테스트 (Failure Return Condition Testing)

Failure 반환 조건 테스트 (Failure Return Condition Testing)

1. 개요

Failure 반환 조건 테스트는 조건 노드가 조건 미충족 시 FAILURE 상태를 올바르게 반환하는지를 검증하는 단위 테스트이다. 이 테스트는 조건이 거짓으로 평가되어야 하는 상황뿐만 아니라, 데이터 부재, 유효하지 않은 입력, 타임아웃 등의 이상 상황에서도 안전하게 FAILURE를 반환하는지를 확인한다. 특히 안전 관련 조건 노드에서는 실패 안전(fail-safe) 원칙의 준수를 검증하는 데 핵심적이다.

2. 테스트 케이스 분류

2.1 정상 FAILURE 케이스

조건이 논리적으로 거짓인 경우의 FAILURE 반환을 검증한다.

케이스입력기대 결과
임계값 미만< 임계값FAILURE
범위 외값이 지정 범위 외FAILURE
불일치\neq 기대값FAILURE

2.2 이상 상황 FAILURE 케이스

데이터가 비정상적인 경우에도 안전하게 FAILURE를 반환하는지를 검증한다.

케이스입력기대 결과
메시지 미수신토픽 메시지 없음FAILURE
NaN 값센서 값 = NaNFAILURE
Inf 값센서 값 = InfFAILURE
빈 배열ranges[] 비어 있음FAILURE
배터리 미장착present = falseFAILURE
GPS 미수신status = STATUS_NO_FIXFAILURE
타임아웃지정 시간 초과FAILURE

3. 구현 예시

3.1 조건 미충족 FAILURE 테스트

TEST_F(BatteryConditionTest, ReturnsFailureBelowThreshold)
{
    sensor_msgs::msg::BatteryState msg;
    msg.present = true;
    msg.percentage = 0.10;  // 10%, 임계값 20% 미만
    publishAndSpin(msg);

    auto status = tree_.tickOnce();
    EXPECT_EQ(status, BT::NodeStatus::FAILURE);
}

TEST_F(BatteryConditionTest, ReturnsFailureJustBelowThreshold)
{
    sensor_msgs::msg::BatteryState msg;
    msg.present = true;
    msg.percentage = 0.199;  // 19.9%, 임계값 20% 미만
    publishAndSpin(msg);

    auto status = tree_.tickOnce();
    EXPECT_EQ(status, BT::NodeStatus::FAILURE);
}

3.2 데이터 부재 FAILURE 테스트

TEST_F(TopicConditionTest, ReturnsFailureWhenNoMessageReceived)
{
    // 메시지를 발행하지 않고 tick
    auto status = tree_.tickOnce();
    EXPECT_EQ(status, BT::NodeStatus::FAILURE);
}

TEST_F(TopicConditionTest, ReturnsFailureWhenMessageIsNull)
{
    // RosTopicSubNode의 onTick에 nullptr이 전달되는 경우
    auto status = tree_.tickOnce();
    EXPECT_EQ(status, BT::NodeStatus::FAILURE);
}

3.3 무효 데이터 FAILURE 테스트

TEST_F(RangeConditionTest, ReturnsFailureForNanRange)
{
    sensor_msgs::msg::LaserScan msg;
    msg.range_min = 0.1;
    msg.range_max = 30.0;
    msg.ranges = {std::numeric_limits<float>::quiet_NaN()};
    publishAndSpin(msg);

    auto status = tree_.tickOnce();
    EXPECT_EQ(status, BT::NodeStatus::FAILURE);
}

TEST_F(RangeConditionTest, ReturnsFailureForInfRange)
{
    sensor_msgs::msg::LaserScan msg;
    msg.range_min = 0.1;
    msg.range_max = 30.0;
    msg.ranges = {std::numeric_limits<float>::infinity()};
    publishAndSpin(msg);

    auto status = tree_.tickOnce();
    // Inf가 장애물 없음으로 해석되는지, 오류로 해석되는지는 구현에 따라 다름
    // 구현 의도에 맞는 결과를 검증
}

TEST_F(RangeConditionTest, ReturnsFailureForEmptyRanges)
{
    sensor_msgs::msg::LaserScan msg;
    msg.ranges.clear();
    publishAndSpin(msg);

    auto status = tree_.tickOnce();
    EXPECT_EQ(status, BT::NodeStatus::FAILURE);
}

3.4 센서 비활성 상태 테스트

TEST_F(BatteryConditionTest, ReturnsFailureWhenNotPresent)
{
    sensor_msgs::msg::BatteryState msg;
    msg.present = false;  // 배터리 미장착
    msg.percentage = 1.0;  // 값은 있으나 무의미
    publishAndSpin(msg);

    auto status = tree_.tickOnce();
    EXPECT_EQ(status, BT::NodeStatus::FAILURE);
}

TEST_F(GpsConditionTest, ReturnsFailureForNoFix)
{
    sensor_msgs::msg::NavSatFix msg;
    msg.status.status =
        sensor_msgs::msg::NavSatStatus::STATUS_NO_FIX;
    publishAndSpin(msg);

    auto status = tree_.tickOnce();
    EXPECT_EQ(status, BT::NodeStatus::FAILURE);
}

3.5 타임아웃 FAILURE 테스트

TEST_F(FreshnessConditionTest, ReturnsFailureForStaleMessage)
{
    // 오래된 타임스탬프를 가진 메시지 발행
    sensor_msgs::msg::LaserScan msg;
    msg.header.stamp = node_->get_clock()->now() -
                       rclcpp::Duration(10, 0);  // 10초 전
    msg.ranges = {5.0};
    publishAndSpin(msg);

    auto status = tree_.tickOnce();
    EXPECT_EQ(status, BT::NodeStatus::FAILURE);
}

4. RUNNING 미반환 검증

TEST_F(ConditionNodeTest, NeverReturnsRunningOnFailure)
{
    // 의도적으로 FAILURE를 유발하는 입력
    publishInvalidMessage();

    for (int i = 0; i < 100; ++i)
    {
        auto status = tree_.tickOnce();
        EXPECT_TRUE(status == BT::NodeStatus::SUCCESS ||
                    status == BT::NodeStatus::FAILURE)
            << "Tick " << i << " returned RUNNING";
    }
}

5. 설계 시 고려 사항

5.1 FAILURE의 의미론적 일관성

동일한 유형의 이상 상황에 대해 모든 조건 노드가 일관된 FAILURE 반환 정책을 따라야 한다. 예를 들어, “메시지 미수신 = FAILURE” 정책은 모든 토픽 기반 조건 노드에 동일하게 적용되어야 한다.

5.2 실패 원인의 구분

테스트에서 FAILURE가 반환되었을 때, 그것이 조건의 논리적 거짓에 의한 것인지 이상 상황에 의한 것인지를 구분할 수 있어야 한다. 조건 노드의 로깅 기능을 통해 실패 원인을 추적할 수 있도록 한다.

5.3 회귀 테스트

버그 수정이나 리팩토링 후, FAILURE 반환 테스트를 회귀 테스트(regression test)로 실행하여 기존 동작이 변경되지 않았는지를 확인한다.

6. 참고 문헌

  • Colledanchise, M., & Ogren, P. (2018). Behavior Trees in Robotics and AI: An Introduction. CRC Press.
  • Google Test 문서. https://google.github.io/googletest/
  • BehaviorTree.CPP 공식 문서. https://www.behaviortree.dev/

버전날짜변경 사항
v0.12026-04-04초안 작성