refactor: tests with Catch2, consolidate test cases and improve structure
Signed-off-by: szdytom <szdytom@qq.com>
This commit is contained in:
parent
516f545cd7
commit
60e0e213a0
1
third_party/CMakeLists.txt
vendored
1
third_party/CMakeLists.txt
vendored
@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.27)
|
||||
set(ISTD_THIRD_PARTY_LIBS
|
||||
entt
|
||||
asio
|
||||
catch2
|
||||
)
|
||||
|
||||
foreach(lib IN LISTS ISTD_THIRD_PARTY_LIBS)
|
||||
|
12
third_party/catch2.cmake
vendored
Normal file
12
third_party/catch2.cmake
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
cmake_minimum_required(VERSION 3.27)
|
||||
cmake_policy(VERSION 3.27)
|
||||
include(FetchContent)
|
||||
|
||||
# Catch2 third-party library setup
|
||||
message(STATUS "Downloading Catch2...")
|
||||
FetchContent_Declare(
|
||||
Catch2
|
||||
URL "https://github.com/catchorg/Catch2/archive/refs/tags/v3.9.0.zip"
|
||||
)
|
||||
FetchContent_MakeAvailable(Catch2)
|
||||
message(STATUS "Catch2 ready")
|
@ -3,13 +3,15 @@ cmake_minimum_required(VERSION 3.27)
|
||||
include(CTest)
|
||||
enable_testing()
|
||||
|
||||
function(declare_istd_util_test name src)
|
||||
add_executable(${name} ${src})
|
||||
target_link_libraries(${name} PRIVATE istd_util)
|
||||
target_compile_features(${name} PRIVATE cxx_std_23)
|
||||
add_test(NAME ${name} COMMAND ${name})
|
||||
endfunction()
|
||||
# Create a unified test executable from multiple source files
|
||||
add_executable(istd_util_tests
|
||||
test_small_map.cpp
|
||||
test_vec2.cpp
|
||||
test_tile_geometry.cpp
|
||||
)
|
||||
|
||||
declare_istd_util_test(test_small_map small_map.cpp)
|
||||
declare_istd_util_test(test_vec2 test_vec2.cpp)
|
||||
declare_istd_util_test(test_tile_geometry test_tile_geometry.cpp)
|
||||
target_link_libraries(istd_util_tests PRIVATE istd_util Catch2::Catch2WithMain)
|
||||
target_compile_features(istd_util_tests PRIVATE cxx_std_23)
|
||||
|
||||
# Add the test to CTest
|
||||
add_test(NAME istd_util_tests COMMAND istd_util_tests)
|
||||
|
@ -1,77 +0,0 @@
|
||||
#include "istd_util/small_map.h"
|
||||
#include <cassert>
|
||||
|
||||
int main() {
|
||||
using namespace istd;
|
||||
// Test insert and size
|
||||
SmallMap<int, int> map;
|
||||
assert(map.empty());
|
||||
map.insert(1, 10);
|
||||
map.insert(2, 20);
|
||||
map.insert(3, 30);
|
||||
assert(map.size() == 3);
|
||||
assert(!map.empty());
|
||||
|
||||
// Test operator[]
|
||||
assert(map[1] == 10);
|
||||
assert(map[2] == 20);
|
||||
assert(map[3] == 30);
|
||||
|
||||
// Test erase
|
||||
map.erase(2);
|
||||
assert(map.size() == 2);
|
||||
assert(map[1] == 10);
|
||||
assert(map[3] == 30);
|
||||
|
||||
// Test clear
|
||||
map.clear();
|
||||
assert(map.empty());
|
||||
|
||||
// Test duplicate insert throws
|
||||
SmallMap<int, int> map2;
|
||||
map2.insert(5, 50);
|
||||
bool thrown = false;
|
||||
try {
|
||||
map2.insert(5, 60);
|
||||
} catch (const std::invalid_argument &) {
|
||||
thrown = true;
|
||||
}
|
||||
assert(thrown);
|
||||
|
||||
// Test out_of_range on operator[]
|
||||
thrown = false;
|
||||
try {
|
||||
(void)map2[99];
|
||||
} catch (const std::out_of_range &) {
|
||||
thrown = true;
|
||||
}
|
||||
assert(thrown);
|
||||
|
||||
// Test erase throws on missing key
|
||||
thrown = false;
|
||||
try {
|
||||
map2.erase(99);
|
||||
} catch (const std::out_of_range &) {
|
||||
thrown = true;
|
||||
}
|
||||
assert(thrown);
|
||||
|
||||
// Test iterator
|
||||
map2.insert(6, 60);
|
||||
map2.insert(7, 70);
|
||||
int sum = 0;
|
||||
for (auto it = map2.begin(); it != map2.end(); ++it) {
|
||||
sum += it->value;
|
||||
}
|
||||
assert(sum == 180); // 50 + 60 + 70
|
||||
|
||||
// Test const iterators
|
||||
const auto &cmap = map2;
|
||||
sum = 0;
|
||||
for (auto it = cmap.cbegin(); it != cmap.cend(); ++it) {
|
||||
sum += it->value;
|
||||
}
|
||||
assert(sum == 180);
|
||||
|
||||
return 0;
|
||||
}
|
103
util/test/test_small_map.cpp
Normal file
103
util/test/test_small_map.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
#include "istd_util/small_map.h"
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
using namespace istd;
|
||||
|
||||
TEST_CASE("SmallMap basic operations", "[small_map]") {
|
||||
SECTION("insert and size") {
|
||||
SmallMap<int, int> map;
|
||||
REQUIRE(map.empty());
|
||||
|
||||
map.insert(1, 10);
|
||||
map.insert(2, 20);
|
||||
map.insert(3, 30);
|
||||
|
||||
REQUIRE(map.size() == 3);
|
||||
REQUIRE_FALSE(map.empty());
|
||||
}
|
||||
|
||||
SECTION("operator[] access") {
|
||||
SmallMap<int, int> map;
|
||||
map.insert(1, 10);
|
||||
map.insert(2, 20);
|
||||
map.insert(3, 30);
|
||||
|
||||
REQUIRE(map[1] == 10);
|
||||
REQUIRE(map[2] == 20);
|
||||
REQUIRE(map[3] == 30);
|
||||
}
|
||||
|
||||
SECTION("erase operation") {
|
||||
SmallMap<int, int> map;
|
||||
map.insert(1, 10);
|
||||
map.insert(2, 20);
|
||||
map.insert(3, 30);
|
||||
|
||||
map.erase(2);
|
||||
REQUIRE(map.size() == 2);
|
||||
REQUIRE(map[1] == 10);
|
||||
REQUIRE(map[3] == 30);
|
||||
}
|
||||
|
||||
SECTION("clear operation") {
|
||||
SmallMap<int, int> map;
|
||||
map.insert(1, 10);
|
||||
map.insert(2, 20);
|
||||
|
||||
map.clear();
|
||||
REQUIRE(map.empty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("SmallMap exception handling", "[small_map]") {
|
||||
SECTION("duplicate insert throws") {
|
||||
SmallMap<int, int> map;
|
||||
map.insert(5, 50);
|
||||
|
||||
REQUIRE_THROWS_AS(map.insert(5, 60), std::invalid_argument);
|
||||
}
|
||||
|
||||
SECTION("out_of_range on operator[]") {
|
||||
SmallMap<int, int> map;
|
||||
map.insert(5, 50);
|
||||
|
||||
REQUIRE_THROWS_AS(map[99], std::out_of_range);
|
||||
}
|
||||
|
||||
SECTION("erase throws on missing key") {
|
||||
SmallMap<int, int> map;
|
||||
map.insert(5, 50);
|
||||
|
||||
REQUIRE_THROWS_AS(map.erase(99), std::out_of_range);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("SmallMap iterators", "[small_map]") {
|
||||
SECTION("iterator traversal") {
|
||||
SmallMap<int, int> map;
|
||||
map.insert(5, 50);
|
||||
map.insert(6, 60);
|
||||
map.insert(7, 70);
|
||||
|
||||
int sum = 0;
|
||||
for (auto it = map.begin(); it != map.end(); ++it) {
|
||||
sum += it->value;
|
||||
}
|
||||
REQUIRE(sum == 180); // 50 + 60 + 70
|
||||
}
|
||||
|
||||
SECTION("const iterators") {
|
||||
SmallMap<int, int> map;
|
||||
map.insert(5, 50);
|
||||
map.insert(6, 60);
|
||||
map.insert(7, 70);
|
||||
|
||||
const auto &cmap = map;
|
||||
int sum = 0;
|
||||
for (auto it = cmap.cbegin(); it != cmap.cend(); ++it) {
|
||||
sum += it->value;
|
||||
}
|
||||
REQUIRE(sum == 180);
|
||||
}
|
||||
}
|
@ -1,98 +1,112 @@
|
||||
#include "istd_util/tile_geometry.h"
|
||||
#include <cassert>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
using namespace istd;
|
||||
using Catch::Approx;
|
||||
|
||||
int main() {
|
||||
// Test a simple horizontal segment
|
||||
Vec2 p1(0.5f, 1.2f);
|
||||
Vec2 p2(0.5f, 4.8f);
|
||||
std::vector<std::tuple<int, int>> result;
|
||||
for (auto [i, j] : tiles_on_segment(p1, p2)) {
|
||||
result.emplace_back(i, j);
|
||||
}
|
||||
// Should traverse columns 1 to 4, row 0
|
||||
assert(result.size() == 4);
|
||||
assert(result[0] == std::make_tuple(0, 1));
|
||||
assert(result[1] == std::make_tuple(0, 2));
|
||||
assert(result[2] == std::make_tuple(0, 3));
|
||||
assert(result[3] == std::make_tuple(0, 4));
|
||||
TEST_CASE("tiles_on_segment function", "[tile_geometry]") {
|
||||
SECTION("horizontal segment") {
|
||||
Vec2 p1(0.5f, 1.2f);
|
||||
Vec2 p2(0.5f, 4.8f);
|
||||
std::vector<std::tuple<int, int>> result;
|
||||
for (auto [i, j] : tiles_on_segment(p1, p2)) {
|
||||
result.emplace_back(i, j);
|
||||
}
|
||||
|
||||
// Test a diagonal segment
|
||||
p1 = Vec2(1.1f, 1.1f);
|
||||
p2 = Vec2(3.9f, 3.9f);
|
||||
result.clear();
|
||||
for (auto [i, j] : tiles_on_segment(p1, p2)) {
|
||||
result.emplace_back(i, j);
|
||||
}
|
||||
// Should traverse (1,1), (2,2), (3,3)
|
||||
assert(result.size() == 3);
|
||||
assert(result[0] == std::make_tuple(1, 1));
|
||||
assert(result[1] == std::make_tuple(2, 2));
|
||||
assert(result[2] == std::make_tuple(3, 3));
|
||||
|
||||
// Test vertical segment
|
||||
p1 = Vec2(2.2f, 0.5f);
|
||||
p2 = Vec2(5.7f, 0.5f);
|
||||
result.clear();
|
||||
for (auto [i, j] : tiles_on_segment(p1, p2)) {
|
||||
result.emplace_back(i, j);
|
||||
}
|
||||
// Should traverse rows 2 to 5, column 0
|
||||
assert(result.size() == 4);
|
||||
assert(result[0] == std::make_tuple(2, 0));
|
||||
assert(result[1] == std::make_tuple(3, 0));
|
||||
assert(result[2] == std::make_tuple(4, 0));
|
||||
assert(result[3] == std::make_tuple(5, 0));
|
||||
|
||||
// Test single tile
|
||||
p1 = Vec2(7.3f, 8.9f);
|
||||
p2 = Vec2(7.7f, 8.1f);
|
||||
result.clear();
|
||||
for (auto [i, j] : tiles_on_segment(p1, p2)) {
|
||||
result.emplace_back(i, j);
|
||||
}
|
||||
assert(result.size() == 1);
|
||||
assert(result[0] == std::make_tuple(7, 8));
|
||||
|
||||
// Test tile_segment_intersection: horizontal segment
|
||||
p1 = Vec2(0.5f, 1.2f);
|
||||
p2 = Vec2(0.5f, 4.8f);
|
||||
{
|
||||
Vec2 inter = tile_segment_intersection(p1, p2, {0, 2});
|
||||
assert(inter.is_valid());
|
||||
assert(std::abs(inter.x - 0.5f) < 1e-6);
|
||||
assert(inter.y >= 2.0f && inter.y <= 3.0f);
|
||||
// Should traverse columns 1 to 4, row 0
|
||||
REQUIRE(result.size() == 4);
|
||||
REQUIRE(result[0] == std::make_tuple(0, 1));
|
||||
REQUIRE(result[1] == std::make_tuple(0, 2));
|
||||
REQUIRE(result[2] == std::make_tuple(0, 3));
|
||||
REQUIRE(result[3] == std::make_tuple(0, 4));
|
||||
}
|
||||
|
||||
// Test tile_segment_intersection: diagonal segment
|
||||
p1 = Vec2(1.1f, 1.1f);
|
||||
p2 = Vec2(3.9f, 3.9f);
|
||||
{
|
||||
Vec2 inter = tile_segment_intersection(p1, p2, {2, 2});
|
||||
assert(inter.is_valid());
|
||||
assert(inter.x >= 2.0f && inter.x <= 3.0f);
|
||||
assert(inter.y >= 2.0f && inter.y <= 3.0f);
|
||||
SECTION("diagonal segment") {
|
||||
Vec2 p1(1.1f, 1.1f);
|
||||
Vec2 p2(3.9f, 3.9f);
|
||||
std::vector<std::tuple<int, int>> result;
|
||||
for (auto [i, j] : tiles_on_segment(p1, p2)) {
|
||||
result.emplace_back(i, j);
|
||||
}
|
||||
|
||||
// Should traverse (1,1), (2,2), (3,3)
|
||||
REQUIRE(result.size() == 3);
|
||||
REQUIRE(result[0] == std::make_tuple(1, 1));
|
||||
REQUIRE(result[1] == std::make_tuple(2, 2));
|
||||
REQUIRE(result[2] == std::make_tuple(3, 3));
|
||||
}
|
||||
|
||||
// Test tile_segment_intersection: no intersection
|
||||
p1 = Vec2(0.0f, 0.0f);
|
||||
p2 = Vec2(0.5f, 0.5f);
|
||||
{
|
||||
Vec2 inter = tile_segment_intersection(p1, p2, {2, 2});
|
||||
assert(!inter.is_valid());
|
||||
SECTION("vertical segment") {
|
||||
Vec2 p1(2.2f, 0.5f);
|
||||
Vec2 p2(5.7f, 0.5f);
|
||||
std::vector<std::tuple<int, int>> result;
|
||||
for (auto [i, j] : tiles_on_segment(p1, p2)) {
|
||||
result.emplace_back(i, j);
|
||||
}
|
||||
|
||||
// Should traverse rows 2 to 5, column 0
|
||||
REQUIRE(result.size() == 4);
|
||||
REQUIRE(result[0] == std::make_tuple(2, 0));
|
||||
REQUIRE(result[1] == std::make_tuple(3, 0));
|
||||
REQUIRE(result[2] == std::make_tuple(4, 0));
|
||||
REQUIRE(result[3] == std::make_tuple(5, 0));
|
||||
}
|
||||
|
||||
// Test tile_segment_intersection: segment starts inside tile
|
||||
p1 = Vec2(2.2f, 2.2f);
|
||||
p2 = Vec2(5.0f, 5.0f);
|
||||
{
|
||||
Vec2 inter = tile_segment_intersection(p1, p2, {2, 2});
|
||||
assert(inter.is_valid());
|
||||
assert(std::abs(inter.x - 2.2f) < 1e-6);
|
||||
assert(std::abs(inter.y - 2.2f) < 1e-6);
|
||||
}
|
||||
SECTION("single tile") {
|
||||
Vec2 p1(7.3f, 8.9f);
|
||||
Vec2 p2(7.7f, 8.1f);
|
||||
std::vector<std::tuple<int, int>> result;
|
||||
for (auto [i, j] : tiles_on_segment(p1, p2)) {
|
||||
result.emplace_back(i, j);
|
||||
}
|
||||
|
||||
return 0;
|
||||
REQUIRE(result.size() == 1);
|
||||
REQUIRE(result[0] == std::make_tuple(7, 8));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("tile_segment_intersection function", "[tile_geometry]") {
|
||||
SECTION("horizontal segment intersection") {
|
||||
Vec2 p1(0.5f, 1.2f);
|
||||
Vec2 p2(0.5f, 4.8f);
|
||||
Vec2 inter = tile_segment_intersection(p1, p2, {0, 2});
|
||||
|
||||
REQUIRE(inter.is_valid());
|
||||
REQUIRE(inter.x == Approx(0.5f).epsilon(1e-6));
|
||||
REQUIRE(inter.y >= 2.0f);
|
||||
REQUIRE(inter.y <= 3.0f);
|
||||
}
|
||||
|
||||
SECTION("diagonal segment intersection") {
|
||||
Vec2 p1(1.1f, 1.1f);
|
||||
Vec2 p2(3.9f, 3.9f);
|
||||
Vec2 inter = tile_segment_intersection(p1, p2, {2, 2});
|
||||
|
||||
REQUIRE(inter.is_valid());
|
||||
REQUIRE(inter.x >= 2.0f);
|
||||
REQUIRE(inter.x <= 3.0f);
|
||||
REQUIRE(inter.y >= 2.0f);
|
||||
REQUIRE(inter.y <= 3.0f);
|
||||
}
|
||||
|
||||
SECTION("no intersection") {
|
||||
Vec2 p1(0.0f, 0.0f);
|
||||
Vec2 p2(0.5f, 0.5f);
|
||||
Vec2 inter = tile_segment_intersection(p1, p2, {2, 2});
|
||||
|
||||
REQUIRE_FALSE(inter.is_valid());
|
||||
}
|
||||
|
||||
SECTION("segment starts inside tile") {
|
||||
Vec2 p1(2.2f, 2.2f);
|
||||
Vec2 p2(5.0f, 5.0f);
|
||||
Vec2 inter = tile_segment_intersection(p1, p2, {2, 2});
|
||||
|
||||
REQUIRE(inter.is_valid());
|
||||
REQUIRE(inter.x == Approx(2.2f).epsilon(1e-6));
|
||||
REQUIRE(inter.y == Approx(2.2f).epsilon(1e-6));
|
||||
}
|
||||
}
|
||||
|
@ -1,45 +1,89 @@
|
||||
#include "istd_util/vec2.h"
|
||||
#include <cassert>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <cmath>
|
||||
using namespace istd;
|
||||
|
||||
int main() {
|
||||
using namespace istd;
|
||||
using Catch::Approx;
|
||||
|
||||
TEST_CASE("Vec2 length operations", "[vec2]") {
|
||||
Vec2 v1(3.0f, 4.0f);
|
||||
|
||||
SECTION("length calculation") {
|
||||
REQUIRE(v1.length() == 5.0f);
|
||||
REQUIRE(v1.length_squared() == 25.0f);
|
||||
}
|
||||
|
||||
SECTION("normalized vector") {
|
||||
Vec2 n = v1.normalized();
|
||||
REQUIRE(n.length() == Approx(1.0f).epsilon(1e-6));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Vec2 arithmetic operations", "[vec2]") {
|
||||
Vec2 v1(3.0f, 4.0f);
|
||||
Vec2 v2(1.0f, 2.0f);
|
||||
|
||||
// Test length and length_squared
|
||||
assert(v1.length() == 5.0f);
|
||||
assert(v1.length_squared() == 25.0f);
|
||||
SECTION("addition") {
|
||||
Vec2 v3 = v1 + v2;
|
||||
REQUIRE(v3.x == 4.0f);
|
||||
REQUIRE(v3.y == 6.0f);
|
||||
}
|
||||
|
||||
// Test normalized
|
||||
Vec2 n = v1.normalized();
|
||||
assert(std::abs(n.length() - 1.0f) < 1e-6);
|
||||
|
||||
// Test addition and subtraction
|
||||
Vec2 v3 = v1 + v2;
|
||||
assert(v3.x == 4.0f && v3.y == 6.0f);
|
||||
Vec2 v4 = v1 - v2;
|
||||
assert(v4.x == 2.0f && v4.y == 2.0f);
|
||||
|
||||
// Test floor and round
|
||||
Vec2 v5(1.7f, -2.3f);
|
||||
auto f = v5.floor();
|
||||
auto r = v5.round();
|
||||
assert(f == std::make_tuple(1, -3));
|
||||
assert(r == std::make_tuple(2, -2));
|
||||
|
||||
// Test inf and invalid
|
||||
Vec2 vinf = Vec2::inf();
|
||||
assert(std::isinf(vinf.x) && std::isinf(vinf.y));
|
||||
Vec2 vinvalid = Vec2::invalid();
|
||||
assert(std::isnan(vinvalid.x) && std::isnan(vinvalid.y));
|
||||
|
||||
// Test is_valid
|
||||
assert(v1.is_valid());
|
||||
assert(!vinvalid.is_valid());
|
||||
|
||||
// Test static dot and cross
|
||||
assert(Vec2::dot(v1, v2) == 11.0f);
|
||||
assert(Vec2::cross(v1, v2) == 2.0f);
|
||||
return 0;
|
||||
SECTION("subtraction") {
|
||||
Vec2 v4 = v1 - v2;
|
||||
REQUIRE(v4.x == 2.0f);
|
||||
REQUIRE(v4.y == 2.0f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Vec2 rounding operations", "[vec2]") {
|
||||
Vec2 v5(1.7f, -2.3f);
|
||||
|
||||
SECTION("floor operation") {
|
||||
auto [i, j] = v5.floor();
|
||||
REQUIRE(i == 1);
|
||||
REQUIRE(j == -3);
|
||||
}
|
||||
|
||||
SECTION("round operation") {
|
||||
auto [i, j] = v5.round();
|
||||
REQUIRE(i == 2);
|
||||
REQUIRE(j == -2);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Vec2 special values", "[vec2]") {
|
||||
SECTION("infinity vector") {
|
||||
Vec2 vinf = Vec2::inf();
|
||||
REQUIRE(std::isinf(vinf.x));
|
||||
REQUIRE(std::isinf(vinf.y));
|
||||
}
|
||||
|
||||
SECTION("invalid vector") {
|
||||
Vec2 vinvalid = Vec2::invalid();
|
||||
REQUIRE(std::isnan(vinvalid.x));
|
||||
REQUIRE(std::isnan(vinvalid.y));
|
||||
}
|
||||
|
||||
SECTION("validity check") {
|
||||
Vec2 v1(3.0f, 4.0f);
|
||||
Vec2 vinvalid = Vec2::invalid();
|
||||
|
||||
REQUIRE(v1.is_valid());
|
||||
REQUIRE_FALSE(vinvalid.is_valid());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Vec2 static operations", "[vec2]") {
|
||||
Vec2 v1(3.0f, 4.0f);
|
||||
Vec2 v2(1.0f, 2.0f);
|
||||
|
||||
SECTION("dot product") {
|
||||
REQUIRE(Vec2::dot(v1, v2) == 11.0f);
|
||||
}
|
||||
|
||||
SECTION("cross product") {
|
||||
REQUIRE(Vec2::cross(v1, v2) == 2.0f);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user