QDAirPortTestSystemBackend/tests/AirportBoundsTest.cpp
2026-01-27 15:24:05 +08:00

246 lines
9.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "config/AirportBounds.h"
#include "utils/Logger.h"
#include <cmath>
#include <fstream>
#include <filesystem>
// 测试用的 AirportBounds 类,暴露 protected 成员以便测试
class TestAirportBounds : public AirportBounds {
public:
using AirportBounds::AirportBounds;
using AirportBounds::toAirportCoordinate;
using AirportBounds::airportBounds_;
using AirportBounds::areaBounds_;
using AirportBounds::referencePoint_;
using AirportBounds::rotationAngle_;
// 设置测试数据
void setTestData(const Vector2D& refPoint, double angle, const Bounds& bounds) {
referencePoint_ = refPoint;
rotationAngle_ = angle * M_PI / 180.0; // 转换为弧度
airportBounds_ = bounds;
}
};
class AirportBoundsTest : public ::testing::Test {
protected:
void SetUp() override {
// 设置日志级别
Logger::setLogLevel(LogLevel::DEBUG);
// 创建测试实例
bounds_ = std::make_unique<TestAirportBounds>();
// 设置基本测试数据
// 参考点设为原点 (0, 0)
// 旋转角度设为 90 度,便于验证
bounds_->setTestData(
Vector2D{ 0, 0 }, // 参考点
90.0, // 旋转角度(度)
Bounds(-2000, -2000, 4000, 4000) // 4km x 4km 的区域
);
}
// 辅助函数:检查两个点是否足够接近(考虑浮点误差)
bool pointsAreClose(const Vector2D& p1, const Vector2D& p2, double tolerance = 0.1) {
return std::abs(p1.x - p2.x) < tolerance && std::abs(p1.y - p2.y) < tolerance;
}
std::unique_ptr<TestAirportBounds> bounds_;
};
// 测试坐标转换 - 参考点
TEST_F(AirportBoundsTest, ReferencePointTransformation) {
// 参考点应该转换为原点 (0,0)
Vector2D result = bounds_->toAirportCoordinate(Vector2D{ 0, 0 });
EXPECT_TRUE(pointsAreClose(result, Vector2D{ 0, 0 }))
<< "参考点转换结果: (" << result.x << ", " << result.y << ")";
}
// 测试坐标转换 - 基本位移90度逆时针旋转
TEST_F(AirportBoundsTest, BasicTranslation) {
// 向右移动 100 米,逆时针旋转 90 度后,应该在 y 轴正方向
Vector2D result = bounds_->toAirportCoordinate(Vector2D{ 100, 0 });
EXPECT_NEAR(result.x, 0, 0.001);
EXPECT_NEAR(result.y, 100, 0.001)
<< "向右100米的点逆时针旋转90度后应该在 (0, 100),实际在: ("
<< result.x << ", " << result.y << ")";
// 向上移动 100 米,逆时针旋转 90 度后,应该在 x 轴负方向
result = bounds_->toAirportCoordinate(Vector2D{ 0, 100 });
EXPECT_NEAR(result.x, -100, 0.001);
EXPECT_NEAR(result.y, 0, 0.001)
<< "向上100米的点逆时针旋转90度后应该在 (-100, 0),实际在: ("
<< result.x << ", " << result.y << ")";
}
// 测试坐标转换 - 复合变换90度逆时针旋转
TEST_F(AirportBoundsTest, CompositeTransformation) {
// 向右上方移动 (100, 100),逆时针旋转 90 度
Vector2D result = bounds_->toAirportCoordinate(Vector2D{ 100, 100 });
EXPECT_NEAR(result.x, -100, 0.001);
EXPECT_NEAR(result.y, 100, 0.001)
<< "点(100,100)逆时针旋转90度后应该在 (-100, 100),实际在: ("
<< result.x << ", " << result.y << ")";
}
// 测试边界检查 - 边界内的点
TEST_F(AirportBoundsTest, PointInBounds) {
// 测试参考点(一定在边界内)
EXPECT_TRUE(bounds_->isPointInBounds(Vector2D{ 0, 0 }))
<< "参考点应该在边界内";
// 测试边界内的点
EXPECT_TRUE(bounds_->isPointInBounds(Vector2D{ 1000, 1000 }))
<< "距离参考点1km的点应该在边界内";
}
// 测试边界检查 - 边界外的点
TEST_F(AirportBoundsTest, PointOutOfBounds) {
// 测试边界外的点
EXPECT_FALSE(bounds_->isPointInBounds(Vector2D{ 3000, 3000 }))
<< "距离参考点3km的点应该在边界外";
// 测试另一个边界外的点
EXPECT_FALSE(bounds_->isPointInBounds(Vector2D{ -2500, -2500 }))
<< "距离参考点2.5km的点应该在边界外";
}
// 测试不同旋转角度45度逆时针旋转
TEST_F(AirportBoundsTest, DifferentRotations) {
// 重新设置为45度逆时针旋转
bounds_->setTestData(
Vector2D{ 0, 0 }, // 参考点
45.0, // 旋转角度(度)
Bounds(-2000, -2000, 4000, 4000)
);
// 向右移动 100 米,逆时针旋转 45 度
Vector2D result = bounds_->toAirportCoordinate(Vector2D{ 100, 0 });
double sqrt2_2 = std::sqrt(2.0) / 2.0; // cos(45°) = sin(45°) = √2/2
EXPECT_NEAR(result.x, 100 * sqrt2_2, 0.001);
EXPECT_NEAR(result.y, 100 * sqrt2_2, 0.001)
<< "向右100米的点逆时针旋转45度后应该在 (70.71, 70.71),实际在: ("
<< result.x << ", " << result.y << ")";
// 向上移动 100 米,逆时针旋转 45 度
result = bounds_->toAirportCoordinate(Vector2D{ 0, 100 });
EXPECT_NEAR(result.x, -100 * sqrt2_2, 0.001);
EXPECT_NEAR(result.y, 100 * sqrt2_2, 0.001)
<< "向上100米的点逆时针旋转45度后应该在 (-70.71, 70.71),实际在: ("
<< result.x << ", " << result.y << ")";
}
// 测试旋转变换30度逆时针旋转
TEST_F(AirportBoundsTest, RotationTransformation) {
// 重新设置为30度逆时针旋转
bounds_->setTestData(
Vector2D{ 0, 0 }, // 参考点
30.0, // 旋转角度(度)
Bounds(-2000, -2000, 4000, 4000)
);
// 在世界坐标系中向右移动1000米逆时针旋转 30 度
Vector2D result = bounds_->toAirportCoordinate(Vector2D{ 1000, 0 });
double cos30 = std::cos(M_PI / 6); // cos(30°) ≈ 0.866025
double sin30 = std::sin(M_PI / 6); // sin(30°) ≈ 0.5
// 在机场坐标系中的期望结果:
// x = 1000 * cos(30°) ≈ 866.025
// y = 1000 * sin(30°) ≈ 500
EXPECT_NEAR(result.x, 1000 * cos30, 0.1);
EXPECT_NEAR(result.y, 1000 * sin30, 0.1)
<< "向右1000米的点逆时针旋转30度后应该在 (866.025, 500),实际在: ("
<< result.x << ", " << result.y << ")";
// 在世界坐标系中向上移动1000米逆时针旋转 30 度
result = bounds_->toAirportCoordinate(Vector2D{ 0, 1000 });
// 在机场坐标系中的期望结果:
// x = -1000 * sin(30°) ≈ -500
// y = 1000 * cos(30°) ≈ 866.025
EXPECT_NEAR(result.x, -1000 * sin30, 0.1);
EXPECT_NEAR(result.y, 1000 * cos30, 0.1)
<< "向上1000米的点逆时针旋转30度后应该在 (-500, 866.025),实际在: ("
<< result.x << ", " << result.y << ")";
}
// 测试区域检查
TEST_F(AirportBoundsTest, AreaCheck) {
// 重新设置为0度旋转便于测试
bounds_->setTestData(
Vector2D{ 0, 0 }, // 参考点
0.0, // 旋转角度(度)
Bounds(-2000, -2000, 4000, 4000)
);
// 设置测试区域
bounds_->areaBounds_[AreaType::RUNWAY] = Bounds(-1000, -100, 2000, 200);
bounds_->areaBounds_[AreaType::TAXIWAY] = Bounds(-800, -300, 1600, 600);
// 测试跑道上的点
Vector2D runwayPoint{ 500, 0 }; // 跑道中心偏右500米
EXPECT_TRUE(bounds_->isPointInArea(runwayPoint, AreaType::RUNWAY))
<< "跑道上的点应该在跑道区域内";
// 测试滑行道上的点
Vector2D taxiwayPoint{ 0, 200 }; // 滑行道中心向上200米
EXPECT_TRUE(bounds_->isPointInArea(taxiwayPoint, AreaType::TAXIWAY))
<< "滑行道上的点应该在滑行道区域内";
// 测试区域外的点
Vector2D outPoint{ 3000, 3000 }; // 远离机场的点
EXPECT_FALSE(bounds_->isPointInArea(outPoint, AreaType::RUNWAY))
<< "远离机场的点不应该在任何区域内";
EXPECT_FALSE(bounds_->isPointInArea(outPoint, AreaType::TAXIWAY))
<< "远离机场的点不应该在任何区域内";
}
// 测试配置文件加载
TEST_F(AirportBoundsTest, ConfigLoading) {
// 使用项目中的配置文件
AirportBounds bounds("config/airport_bounds.json");
// 验证配置是否正确加载
const auto& airportBounds = bounds.getAirportBounds();
EXPECT_TRUE(airportBounds.width > 0 && airportBounds.height > 0)
<< "机场边界的宽度和高度应该为正值";
// 验证区域配置
const auto& runwayBounds = bounds.getAreaBounds(AreaType::RUNWAY);
EXPECT_TRUE(runwayBounds.width > 0 && runwayBounds.height > 0)
<< "跑道区域的宽度和高度应该为正值";
EXPECT_TRUE(runwayBounds.width < airportBounds.width && runwayBounds.height < airportBounds.height)
<< "跑道区域应该小于机场边界";
}
// 测试配置文件错误处理
TEST_F(AirportBoundsTest, ConfigErrorHandling) {
// 测试文件不存在
EXPECT_THROW(AirportBounds("nonexistent.json"), std::runtime_error);
// 测试配置文件格式错误
EXPECT_THROW(AirportBounds("tests/data/invalid_airport_bounds.json"), std::runtime_error);
}
// 测试边界条件
TEST_F(AirportBoundsTest, EdgeCases) {
// 重新设置为0度旋转便于测试
bounds_->setTestData(
Vector2D{ 0, 0 }, // 参考点
0.0, // 旋转角度(度)
Bounds(-2000, -2000, 4000, 4000)
);
// 测试正好在边界上的点
Vector2D edge{ 2000, 2000 }; // 右上角边界点
EXPECT_TRUE(bounds_->isPointInBounds(edge))
<< "边界上的点应该被认为在边界内";
// 测试边界外的点
Vector2D outside{ 2001, 2001 }; // 刚好超出边界
EXPECT_FALSE(bounds_->isPointInBounds(outside))
<< "边界外的点应该被认为在边界外";
}