246 lines
9.6 KiB
C++
246 lines
9.6 KiB
C++
#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))
|
||
<< "边界外的点应该被认为在边界外";
|
||
} |