#include #include #include "config/AirportBounds.h" #include "utils/Logger.h" #include #include #include // 测试用的 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(); // 设置基本测试数据 // 参考点设为原点 (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 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)) << "边界外的点应该被认为在边界外"; }