Now fully off catch2. YAY!

This commit is contained in:
Zed A. Shaw 2026-06-17 00:32:31 -04:00
parent 903cf00661
commit 21e7700deb
16 changed files with 610 additions and 498 deletions

View file

@ -37,7 +37,7 @@ tracy_build:
meson compile -j 10 -C builddir meson compile -j 10 -C builddir
test: build test: build
./builddir/runtests -d yes ./builddir/fuc2it -d yes
run: build test run: build test
ifeq '$(OS)' 'Windows_NT' ifeq '$(OS)' 'Windows_NT'
@ -48,7 +48,7 @@ else
endif endif
debug: build debug: build
gdb --nx -x .gdbinit --ex run --args builddir/runtests gdb --nx -x .gdbinit --ex run --args builddir/fuc2it
debug_run: build debug_run: build
gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args builddir/zedcaster gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args builddir/zedcaster
@ -60,7 +60,7 @@ clean:
meson compile --clean -C builddir meson compile --clean -C builddir
debug_test: build debug_test: build
gdb --nx -x .gdbinit --ex run --ex bt --ex q --args builddir/runtests gdb --nx -x .gdbinit --ex run --ex bt --ex q --args builddir/fuc2it
win_installer: win_installer:
powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" scripts\win_installer.ifp' powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" scripts\win_installer.ifp'

View file

@ -59,7 +59,6 @@ elif build_machine.system() == 'darwin'
] ]
endif endif
catch2 = subproject('catch2').get_variable('catch2_with_main_dep')
fuc2 = subproject('fuc2').get_variable('fuc2_dep') fuc2 = subproject('fuc2').get_variable('fuc2_dep')
fmt = subproject('fmt').get_variable('fmt_dep') fmt = subproject('fmt').get_variable('fmt_dep')
json = subproject('nlohmann_json').get_variable('nlohmann_json_dep') json = subproject('nlohmann_json').get_variable('nlohmann_json_dep')
@ -108,13 +107,6 @@ caster_dep = declare_dependency(
dependencies += [ caster_dep ] dependencies += [ caster_dep ]
executable('runtests', sources + tests,
cpp_args: cpp_args,
link_args: link_args,
include_directories: inc_dirs,
override_options: exe_defaults,
dependencies: dependencies + [catch2])
executable('fuc2it', sources + fuc2_tests, executable('fuc2it', sources + fuc2_tests,
cpp_args: cpp_args, cpp_args: cpp_args,
link_args: link_args, link_args: link_args,

View file

@ -11,14 +11,14 @@ void dbc::log(const string &message, const std::source_location location) {
void dbc::sentinel(const string &message, const std::source_location location) { void dbc::sentinel(const string &message, const std::source_location location) {
string err = $F("[SENTINEL!] {}", message); string err = $F("[SENTINEL!] {}", message);
dbc::log(err, location); dbc::log(err, location);
throw dbc::SentinelError{err}; throw dbc::SentinelError(err);
} }
void dbc::pre(const string &message, bool test, const std::source_location location) { void dbc::pre(const string &message, bool test, const std::source_location location) {
if(!test) { if(!test) {
string err = $F("[PRE!] {}", message); string err = $F("[PRE!] {}", message);
dbc::log(err, location); dbc::log(err, location);
throw dbc::PreCondError{err}; throw dbc::PreCondError(err);
} }
} }
@ -30,7 +30,7 @@ void dbc::post(const string &message, bool test, const std::source_location loca
if(!test) { if(!test) {
string err = $F("[POST!] {}", message); string err = $F("[POST!] {}", message);
dbc::log(err, location); dbc::log(err, location);
throw dbc::PostCondError{err}; throw dbc::PostCondError(err);
} }
} }
@ -42,6 +42,6 @@ void dbc::check(bool test, const string &message, const std::source_location loc
if(!test) { if(!test) {
string err = $F("[CHECK!] {}\n", message); string err = $F("[CHECK!] {}\n", message);
dbc::log(err, location); dbc::log(err, location);
throw dbc::CheckError{err}; throw dbc::CheckError(err);
} }
} }

View file

@ -11,17 +11,10 @@
namespace dbc { namespace dbc {
using std::string; using std::string;
class Error { using CheckError = std::runtime_error;
public: using SentinelError = std::runtime_error;
const string message; using PreCondError = std::runtime_error;
Error(string m) : message{m} {} using PostCondError = std::runtime_error;
Error(const char *m) : message{m} {}
};
class CheckError : public Error {};
class SentinelError : public Error {};
class PreCondError : public Error {};
class PostCondError : public Error {};
void log(const string &message, void log(const string &message,
const std::source_location location = const std::source_location location =

View file

@ -18,22 +18,46 @@ TEST_SET(loot_tests);
TEST_SET(map_tests); TEST_SET(map_tests);
TEST_SET(matrix_tests); TEST_SET(matrix_tests);
TEST_SET(mazes_tests); TEST_SET(mazes_tests);
TEST_SET(palette_tests);
TEST_SET(pathing_tests);
TEST_SET(rituals_tests);
TEST_SET(shaders_tests);
TEST_SET(sound_tests);
TEST_SET(spatialmap_tests);
TEST_SET(stats_tests);
TEST_SET(systems_tests);
TEST_SET(textures_tests);
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
run(ai_tests::TESTS); std::vector<fuc2::Set> all_tests{
run(animation_tests::TESTS); ai_tests::TESTS,
run(base_tests::TESTS); animation_tests::TESTS,
run(battle_tests::TESTS); base_tests::TESTS,
run(camera_tests::TESTS); battle_tests::TESTS,
run(component_tests::TESTS); camera_tests::TESTS,
run(config_tests::TESTS); component_tests::TESTS,
run(dbc_tests::TESTS); config_tests::TESTS,
run(dinkyecs_tests::TESTS); dbc_tests::TESTS,
run(event_router_tests::TESTS); dinkyecs_tests::TESTS,
run(inventory_tests::TESTS); event_router_tests::TESTS,
run(lighting_tests::TESTS); inventory_tests::TESTS,
run(loot_tests::TESTS); lighting_tests::TESTS,
run(map_tests::TESTS); loot_tests::TESTS,
run(matrix_tests::TESTS); map_tests::TESTS,
run(mazes_tests::TESTS); matrix_tests::TESTS,
mazes_tests::TESTS,
palette_tests::TESTS,
pathing_tests::TESTS,
rituals_tests::TESTS,
shaders_tests::TESTS,
sound_tests::TESTS,
spatialmap_tests::TESTS,
stats_tests::TESTS,
systems_tests::TESTS,
textures_tests::TESTS,
};
for(auto& test_set : all_tests) {
run(test_set);
}
} }

View file

@ -1,16 +1,13 @@
tests = files(
'palette.cpp',
'pathing.cpp',
'rituals.cpp',
'shaders.cpp',
'sound.cpp',
'spatialmap.cpp',
'stats.cpp',
'systems.cpp',
'textures.cpp',
)
fuc2_tests = files( fuc2_tests = files(
'textures.cpp',
'systems.cpp',
'stats.cpp',
'spatialmap.cpp',
'sound.cpp',
'shaders.cpp',
'rituals.cpp',
'pathing.cpp',
'palette.cpp',
'mazes.cpp', 'mazes.cpp',
'matrix.cpp', 'matrix.cpp',
'map.cpp', 'map.cpp',

View file

@ -1,24 +1,34 @@
#include <catch2/catch_test_macros.hpp> #include <fuc2/testing.hpp>
#include <fmt/core.h> #include <fmt/core.h>
#include <string> #include <string>
#include "graphics/palette.hpp" #include "graphics/palette.hpp"
using namespace fmt; using namespace fmt;
using namespace fuc2;
TEST_CASE("color palette test", "[color-palette]") { namespace palette_tests {
void test_color_palette_test() {
palette::init(); palette::init();
REQUIRE(palette::initialized() == true); CHECK(palette::initialized() == true);
// confirm it's idempotent // confirm it's idempotent
palette::init(); palette::init();
sf::Color expect{10, 10, 10, 255}; sf::Color expect{10, 10, 10, 255};
auto gui_text = palette::get("gui/theme:dark_dark"); auto gui_text = palette::get("gui/theme:dark_dark");
REQUIRE(gui_text == expect); CHECK(gui_text == expect);
gui_text = palette::get("gui/theme", "mid"); gui_text = palette::get("gui/theme", "mid");
REQUIRE(gui_text != expect); CHECK(gui_text != expect);
expect = {100, 100, 100, 255}; expect = {100, 100, 100, 255};
REQUIRE(gui_text == expect); CHECK(gui_text == expect);
}
fuc2::Set TESTS{
.name="palette",
.tests={
TEST(test_color_palette_test),
}
};
} }

View file

@ -1,4 +1,4 @@
#include <catch2/catch_test_macros.hpp> #include <fuc2/testing.hpp>
#include <fmt/core.h> #include <fmt/core.h>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <fstream> #include <fstream>
@ -13,17 +13,19 @@
#include "constants.hpp" #include "constants.hpp"
using namespace fmt; using namespace fmt;
using namespace fuc2;
using namespace nlohmann; using namespace nlohmann;
using std::string; using std::string;
using namespace components; using namespace components;
using namespace std::chrono_literals; using namespace std::chrono_literals;
namespace pathing_tests {
json load_test_pathing(const string &fname) { json load_test_pathing(const string &fname) {
std::ifstream infile(fname); std::ifstream infile(fname);
return json::parse(infile); return json::parse(infile);
} }
TEST_CASE("multiple targets can path", "[pathing]") { void test_multiple_targets_can_path() {
GameDB::init(); GameDB::init();
auto level = GameDB::create_level(); auto level = GameDB::create_level();
auto walls_copy = level.map->$walls; auto walls_copy = level.map->$walls;
@ -48,6 +50,14 @@ TEST_CASE("multiple targets can path", "[pathing]") {
if(found == PathingResult::FOUND) { if(found == PathingResult::FOUND) {
fmt::println("FOUND!"); fmt::println("FOUND!");
} else if(found == PathingResult::FAIL && !diag) { } else if(found == PathingResult::FAIL && !diag) {
REQUIRE(found != PathingResult::FAIL); CHECK(found != PathingResult::FAIL);
} }
} }
fuc2::Set TESTS{
.name="pathing",
.tests={
TEST(test_multiple_targets_can_path),
}
};
}

View file

@ -1,9 +1,13 @@
#include <catch2/catch_test_macros.hpp> #include <fuc2/testing.hpp>
#include <iostream> #include <iostream>
#include "game/rituals.hpp" #include "game/rituals.hpp"
#include "algos/simplefsm.hpp" #include "algos/simplefsm.hpp"
TEST_CASE("ritual::Engine basic tests", "[rituals]") { using namespace fuc2;
namespace rituals_tests {
void test_ritual_engine_basics() {
ritual::Engine re("assets/rituals.json"); ritual::Engine re("assets/rituals.json");
auto craft_state = re.start(); auto craft_state = re.start();
@ -12,11 +16,11 @@ TEST_CASE("ritual::Engine basic tests", "[rituals]") {
fmt::println("\n\n------------ TEST WILL DO PIERCE"); fmt::println("\n\n------------ TEST WILL DO PIERCE");
craft_state.dump(); craft_state.dump();
REQUIRE(craft_state.will_do("pierce_type")); CHECK(craft_state.will_do("pierce_type"));
REQUIRE(craft_state.start != craft_state.original); CHECK(craft_state.start != craft_state.original);
craft_state.reset(); craft_state.reset();
REQUIRE(craft_state.start == craft_state.original); CHECK(craft_state.start == craft_state.original);
re.set_state(craft_state, "has_magick", true); re.set_state(craft_state, "has_magick", true);
re.set_state(craft_state, "has_spikes", true); re.set_state(craft_state, "has_spikes", true);
@ -24,10 +28,10 @@ TEST_CASE("ritual::Engine basic tests", "[rituals]") {
fmt::println("\n\n------------ TEST WILL DO MAGICK TOO"); fmt::println("\n\n------------ TEST WILL DO MAGICK TOO");
craft_state.dump(); craft_state.dump();
REQUIRE(craft_state.will_do("pierce_type")); CHECK(craft_state.will_do("pierce_type"));
craft_state.pop(); craft_state.pop();
REQUIRE(craft_state.will_do("magick_type")); CHECK(craft_state.will_do("magick_type"));
craft_state.reset(); craft_state.reset();
re.set_state(craft_state, "has_magick", true); re.set_state(craft_state, "has_magick", true);
@ -35,7 +39,7 @@ TEST_CASE("ritual::Engine basic tests", "[rituals]") {
re.set_state(craft_state, "shiny_bauble", true); re.set_state(craft_state, "shiny_bauble", true);
re.plan(craft_state); re.plan(craft_state);
REQUIRE(craft_state.is_combined()); CHECK(craft_state.is_combined());
fmt::println("\n\n------------ TEST WILL DO DAMAGE BOOST"); fmt::println("\n\n------------ TEST WILL DO DAMAGE BOOST");
craft_state.dump(); craft_state.dump();
@ -44,13 +48,13 @@ TEST_CASE("ritual::Engine basic tests", "[rituals]") {
re.set_state(craft_state, "cursed_item", true); re.set_state(craft_state, "cursed_item", true);
re.set_state(craft_state, "shiny_bauble", true); re.set_state(craft_state, "shiny_bauble", true);
re.plan(craft_state); re.plan(craft_state);
REQUIRE(craft_state.is_combined()); CHECK(craft_state.is_combined());
fmt::println("\n\n------------ TEST WILL DO LARGE DAMAGE BOOST"); fmt::println("\n\n------------ TEST WILL DO LARGE DAMAGE BOOST");
craft_state.dump(); craft_state.dump();
} }
TEST_CASE("craft_state can be finalized for the end result", "[rituals]") { void test_craft_state_can_be_finalized() {
ritual::Engine re("assets/rituals.json"); ritual::Engine re("assets/rituals.json");
auto craft_state = re.start(); auto craft_state = re.start();
@ -58,7 +62,7 @@ TEST_CASE("craft_state can be finalized for the end result", "[rituals]") {
re.set_state(craft_state, "cursed_item", true); re.set_state(craft_state, "cursed_item", true);
re.set_state(craft_state, "shiny_bauble", true); re.set_state(craft_state, "shiny_bauble", true);
re.plan(craft_state); re.plan(craft_state);
REQUIRE(craft_state.is_combined()); CHECK(craft_state.is_combined());
fmt::println("\n\n------------ CYCLES AVOIDED"); fmt::println("\n\n------------ CYCLES AVOIDED");
craft_state.dump(); craft_state.dump();
@ -68,20 +72,20 @@ TEST_CASE("craft_state can be finalized for the end result", "[rituals]") {
} }
TEST_CASE("the ritual belt works", "[rituals]") { void test_the_ritual_belt_works() {
ritual::Belt the_belt; ritual::Belt the_belt;
ritual::Engine re; ritual::Engine re;
auto craft_state = re.start(); auto craft_state = re.start();
re.set_state(craft_state, "has_magick", true); re.set_state(craft_state, "has_magick", true);
re.plan(craft_state); re.plan(craft_state);
REQUIRE(craft_state.is_combined()); CHECK(craft_state.is_combined());
craft_state.dump(); craft_state.dump();
{ {
auto ritual = re.finalize(craft_state); auto ritual = re.finalize(craft_state);
the_belt.equip(0, ritual); the_belt.equip(0, ritual);
REQUIRE(the_belt.has(0)); CHECK(the_belt.has(0));
} }
{ {
@ -91,46 +95,57 @@ TEST_CASE("the ritual belt works", "[rituals]") {
{ {
the_belt.unequip(0); the_belt.unequip(0);
REQUIRE(!the_belt.has(0)); CHECK(!the_belt.has(0));
} }
craft_state.reset(); craft_state.reset();
REQUIRE(craft_state.plan.script.size() == 0); CHECK(craft_state.plan.script.size() == 0);
REQUIRE(!craft_state.plan.complete); CHECK(!craft_state.plan.complete);
REQUIRE(craft_state.start == craft_state.original); CHECK(craft_state.start == craft_state.original);
REQUIRE(!craft_state.is_combined()); CHECK(!craft_state.is_combined());
} }
TEST_CASE("ritual blanket basic operations", "[rituals-blanket]") { void test_ritual_blanket_basic_operations() {
ritual::Blanket blanket; ritual::Blanket blanket;
ritual::Entity other = blanket.add("rusty_nails"); ritual::Entity other = blanket.add("rusty_nails");
ritual::Entity ent = blanket.add("severed_finger"); ritual::Entity ent = blanket.add("severed_finger");
auto& name = blanket.get(ent); auto& name = blanket.get(ent);
REQUIRE(name == "severed_finger"); CHECK(name == "severed_finger");
REQUIRE(blanket.has(ent)); CHECK(blanket.has(ent));
blanket.remove(ent); blanket.remove(ent);
REQUIRE(!blanket.has(ent)); CHECK(!blanket.has(ent));
REQUIRE(blanket.has(other)); CHECK(blanket.has(other));
REQUIRE(!blanket.is_selected(ent)); CHECK(!blanket.is_selected(ent));
REQUIRE(!blanket.is_selected(other)); CHECK(!blanket.is_selected(other));
blanket.select(ent); blanket.select(ent);
REQUIRE(blanket.is_selected(ent)); CHECK(blanket.is_selected(ent));
REQUIRE(!blanket.is_selected(other)); CHECK(!blanket.is_selected(other));
blanket.deselect(ent); blanket.deselect(ent);
REQUIRE(!blanket.is_selected(ent)); CHECK(!blanket.is_selected(ent));
blanket.select(ent); blanket.select(ent);
blanket.select(other); blanket.select(other);
REQUIRE(blanket.is_selected(ent)); CHECK(blanket.is_selected(ent));
REQUIRE(blanket.is_selected(other)); CHECK(blanket.is_selected(other));
blanket.reset(); blanket.reset();
REQUIRE(!blanket.is_selected(ent)); CHECK(!blanket.is_selected(ent));
REQUIRE(!blanket.is_selected(other)); CHECK(!blanket.is_selected(other));
REQUIRE(blanket.selected.empty()); CHECK(blanket.selected.empty());
REQUIRE(blanket.selected.size() == 0); CHECK(blanket.selected.size() == 0);
}
fuc2::Set TESTS{
.name="rituals",
.tests={
TEST(test_ritual_engine_basics),
TEST(test_ritual_blanket_basic_operations),
TEST(test_the_ritual_belt_works),
TEST(test_craft_state_can_be_finalized),
}
};
} }

View file

@ -1,26 +1,38 @@
#include <catch2/catch_test_macros.hpp> #include <fuc2/testing.hpp>
#include <fmt/core.h> #include <fmt/core.h>
#include <string> #include <string>
#include "graphics/shaders.hpp" #include "graphics/shaders.hpp"
using namespace fmt; using namespace fmt;
using namespace fuc2;
TEST_CASE("shader loading/init works", "[shaders]") { namespace shaders_tests {
void test_shader_loading_init_works() {
shaders::init(); shaders::init();
int version = shaders::version(); int version = shaders::version();
std::shared_ptr<sf::Shader> ui_shader = shaders::get("ui_shader"); std::shared_ptr<sf::Shader> ui_shader = shaders::get("ui_shader");
auto other_test = shaders::get("ui_shader"); auto other_test = shaders::get("ui_shader");
REQUIRE(ui_shader != nullptr); CHECK(ui_shader != nullptr);
REQUIRE(ui_shader == other_test); CHECK(ui_shader == other_test);
REQUIRE(shaders::updated(version) == false); CHECK(shaders::updated(version) == false);
int new_version = shaders::reload(); int new_version = shaders::reload();
REQUIRE(version != shaders::version()); CHECK(version != shaders::version());
REQUIRE(version != new_version); CHECK(version != new_version);
REQUIRE(shaders::version() == new_version); CHECK(shaders::version() == new_version);
REQUIRE(shaders::updated(version) == true); CHECK(shaders::updated(version) == true);
version = new_version; version = new_version;
} }
fuc2::Set TESTS{
.name="shaders",
.tests={
TEST(test_shader_loading_init_works),
}
};
}

View file

@ -1,14 +1,24 @@
#include <catch2/catch_test_macros.hpp> #include <fuc2/testing.hpp>
#include <fmt/core.h> #include <fmt/core.h>
#include <string> #include <string>
#include "game/sound.hpp" #include "game/sound.hpp"
using namespace fmt; using namespace fmt;
using namespace fuc2;
TEST_CASE("test sound manager", "[sound]") { namespace sound_tests {
void test_test_sound_manager() {
sound::init(); sound::init();
sound::play("blank"); sound::play("blank");
sound::play_at("blank", 0.1, 0.1, 0.1); sound::play_at("blank", 0.1, 0.1, 0.1);
} }
fuc2::Set TESTS{
.name="sound",
.tests={
TEST(test_test_sound_manager),
}
};
}

View file

@ -1,4 +1,4 @@
#include <catch2/catch_test_macros.hpp> #include <fuc2/testing.hpp>
#include <fmt/core.h> #include <fmt/core.h>
#include <string> #include <string>
#include "algos/spatialmap.hpp" #include "algos/spatialmap.hpp"
@ -9,9 +9,10 @@
using DinkyECS::Entity; using DinkyECS::Entity;
using namespace fmt; using namespace fmt;
using namespace fuc2;
namespace spatialmap_tests {
TEST_CASE("SpatialMap::insert", "[spatialmap]") { void test_SpatialMap_insert() {
DinkyECS::World world; DinkyECS::World world;
SpatialMap map; SpatialMap map;
@ -24,19 +25,21 @@ TEST_CASE("SpatialMap::insert", "[spatialmap]") {
map.insert(at, item, false); map.insert(at, item, false);
map.insert(at, potion, false); map.insert(at, potion, false);
REQUIRE(!map.occupied(at)); CHECK(!map.occupied(at));
map.insert(at, player, true); map.insert(at, player, true);
REQUIRE(map.occupied_by(at) == player); CHECK(map.occupied_by(at) == player);
REQUIRE_THROWS(map.insert(at, enemy, true)); BLOWS_UP([&]() {
map.insert(at, enemy, true);
});
map.insert(enemy_at, enemy, true); map.insert(enemy_at, enemy, true);
REQUIRE(map.occupied_by(enemy_at) == enemy); CHECK(map.occupied_by(enemy_at) == enemy);
REQUIRE(map.occupied(enemy_at)); CHECK(map.occupied(enemy_at));
} }
TEST_CASE("SpatialMap::remove", "[spatialmap]") { void test_SpatialMap_remove() {
DinkyECS::World world; DinkyECS::World world;
SpatialMap map; SpatialMap map;
@ -47,19 +50,21 @@ TEST_CASE("SpatialMap::remove", "[spatialmap]") {
// confirm that things can be in any order // confirm that things can be in any order
map.insert(at, player, true); map.insert(at, player, true);
map.insert(at, item, false); map.insert(at, item, false);
REQUIRE(map.occupied(at)); CHECK(map.occupied(at));
REQUIRE(map.occupied_by(at) == player); CHECK(map.occupied_by(at) == player);
auto data = map.remove(at, player); auto data = map.remove(at, player);
REQUIRE(!map.occupied(at)); CHECK(!map.occupied(at));
REQUIRE(data.entity == player); CHECK(data.entity == player);
REQUIRE(data.collision == true); CHECK(data.collision == true);
REQUIRE_THROWS(map.remove(at, player)); BLOWS_UP([&]() {
map.remove(at, player);
});
} }
TEST_CASE("SpatialMap::move", "[spatialmap]") { void test_SpatialMap_move() {
DinkyECS::World world; DinkyECS::World world;
SpatialMap map; SpatialMap map;
@ -68,38 +73,42 @@ TEST_CASE("SpatialMap::move", "[spatialmap]") {
Point at{10, 320}; Point at{10, 320};
map.insert(at, player, true); map.insert(at, player, true);
map.insert(at, item, false); map.insert(at, item, false);
REQUIRE(map.occupied(at)); CHECK(map.occupied(at));
auto enemy = world.entity(); auto enemy = world.entity();
auto potion = world.entity(); auto potion = world.entity();
Point enemy_at{11, 320}; Point enemy_at{11, 320};
map.insert(enemy_at, enemy, true); map.insert(enemy_at, enemy, true);
map.insert(enemy_at, potion, false); map.insert(enemy_at, potion, false);
REQUIRE(map.occupied(enemy_at)); CHECK(map.occupied(enemy_at));
REQUIRE(map.occupied_by(enemy_at) == enemy); CHECK(map.occupied_by(enemy_at) == enemy);
Point target{at.x + 1, at.y}; Point target{at.x + 1, at.y};
// try bad move with a slot that's empty // try bad move with a slot that's empty
REQUIRE_THROWS(map.move({0,0}, target, player)); BLOWS_UP([&]() {
map.move({0,0}, target, player);
});
// try move into an occupied spot also fails // try move into an occupied spot also fails
REQUIRE_THROWS(map.move(at, target, player)); BLOWS_UP([&]() {
map.move(at, target, player);
});
// now move to a new spot, need to add them back // now move to a new spot, need to add them back
map.insert(at, player, true); map.insert(at, player, true);
target.x++; // just move farther target.x++; // just move farther
map.move(at, target, player); map.move(at, target, player);
REQUIRE(map.occupied(target)); CHECK(map.occupied(target));
REQUIRE(map.occupied_by(target) == player); CHECK(map.occupied_by(target) == player);
auto data = map.remove(target, player); auto data = map.remove(target, player);
REQUIRE(data.entity == player); CHECK(data.entity == player);
REQUIRE(data.collision == true); CHECK(data.collision == true);
} }
TEST_CASE("SpatialMap::occupied/something_there", "[spatialmap]") { void test_SpatialMap_occupied() {
DinkyECS::World world; DinkyECS::World world;
SpatialMap map; SpatialMap map;
@ -108,42 +117,42 @@ TEST_CASE("SpatialMap::occupied/something_there", "[spatialmap]") {
Point at{1000, 20}; Point at{1000, 20};
// first test empty locations // first test empty locations
REQUIRE(!map.something_there(at)); CHECK(!map.something_there(at));
REQUIRE(!map.occupied(at)); CHECK(!map.occupied(at));
// then when there's something without collision // then when there's something without collision
map.insert(at, item, false); map.insert(at, item, false);
REQUIRE(map.something_there(at)); CHECK(map.something_there(at));
REQUIRE(!map.occupied(at)); CHECK(!map.occupied(at));
// finally with collision and an item there // finally with collision and an item there
map.insert(at, player, true); map.insert(at, player, true);
REQUIRE(map.something_there(at)); CHECK(map.something_there(at));
REQUIRE(map.occupied(at)); CHECK(map.occupied(at));
REQUIRE(map.occupied_by(at) == player); CHECK(map.occupied_by(at) == player);
// then remove the item and still have collision // then remove the item and still have collision
map.remove(at, item); map.remove(at, item);
REQUIRE(map.something_there(at)); CHECK(map.something_there(at));
REQUIRE(map.occupied(at)); CHECK(map.occupied(at));
REQUIRE(map.occupied_by(at) == player); CHECK(map.occupied_by(at) == player);
// remove player and back to no collision // remove player and back to no collision
map.remove(at, player); map.remove(at, player);
REQUIRE(!map.something_there(at)); CHECK(!map.something_there(at));
REQUIRE(!map.occupied(at)); CHECK(!map.occupied(at));
// last thing, put just the player in at a new spot // last thing, put just the player in at a new spot
Point target{at.x+1, at.y+10}; Point target{at.x+1, at.y+10};
map.insert(target, player, true); map.insert(target, player, true);
REQUIRE(map.something_there(target)); CHECK(map.something_there(target));
REQUIRE(map.occupied(target)); CHECK(map.occupied(target));
REQUIRE(map.occupied_by(target) == player); CHECK(map.occupied_by(target) == player);
} }
TEST_CASE("SpatialMap::get", "[spatialmap]") { void test_SpatialMap_get() {
DinkyECS::World world; DinkyECS::World world;
SpatialMap map; SpatialMap map;
@ -153,20 +162,20 @@ TEST_CASE("SpatialMap::get", "[spatialmap]") {
// finally with collision and an item there // finally with collision and an item there
map.insert(at, player, true); map.insert(at, player, true);
REQUIRE(map.occupied(at)); CHECK(map.occupied(at));
REQUIRE(map.occupied_by(at) == player); CHECK(map.occupied_by(at) == player);
auto entity = map.get(at); auto entity = map.get(at);
REQUIRE(player == entity); CHECK(player == entity);
// This probably doesn't work so need to // This probably doesn't work so need to
// rethink how get works. // rethink how get works.
map.insert(at, item, false); map.insert(at, item, false);
entity = map.get(at); entity = map.get(at);
REQUIRE(entity == item); CHECK(entity == item);
} }
TEST_CASE("SpatialMap::find", "[spatialmap-find]") { void test_SpatialMap_find() {
DinkyECS::World world; DinkyECS::World world;
SpatialMap map; SpatialMap map;
Point at{101, 31}; Point at{101, 31};
@ -185,16 +194,16 @@ TEST_CASE("SpatialMap::find", "[spatialmap-find]") {
return data.collision; return data.collision;
}); });
REQUIRE(collision == should_collide); CHECK(collision == should_collide);
auto no_collide = map.find(at, [&](auto data) -> bool { auto no_collide = map.find(at, [&](auto data) -> bool {
return !data.collision; return !data.collision;
}); });
REQUIRE(no_collide != should_collide); CHECK(no_collide != should_collide);
} }
TEST_CASE("SpatialMap::neighbors", "[spatialmap-neighbors]") { void test_SpatialMap_neighbors() {
DinkyECS::World world; DinkyECS::World world;
SpatialMap map; SpatialMap map;
@ -210,22 +219,22 @@ TEST_CASE("SpatialMap::neighbors", "[spatialmap-neighbors]") {
map.insert({at.x-1, at.y+1}, enemy2, true); map.insert({at.x-1, at.y+1}, enemy2, true);
auto result = map.neighbors(at, true); auto result = map.neighbors(at, true);
REQUIRE(result.found); CHECK(result.found);
REQUIRE(result.nearby.size() == 2); CHECK(result.nearby.size() == 2);
bool maybe = result.nearby[0] == enemy1 || result.nearby[1] == enemy1; bool maybe = result.nearby[0] == enemy1 || result.nearby[1] == enemy1;
REQUIRE(maybe); CHECK(maybe);
maybe = result.nearby[0] == enemy2 || result.nearby[1] == enemy2; maybe = result.nearby[0] == enemy2 || result.nearby[1] == enemy2;
REQUIRE(maybe); CHECK(maybe);
result = map.neighbors(at, false); result = map.neighbors(at, false);
REQUIRE(result.found); CHECK(result.found);
REQUIRE(result.nearby.size() == 1); CHECK(result.nearby.size() == 1);
REQUIRE(result.nearby[0] == enemy1); CHECK(result.nearby[0] == enemy1);
} }
TEST_CASE("SpatialMap::distance_sorted", "[spatialmap]") { void test_SpatialMap_distance_sorted() {
DinkyECS::World world; DinkyECS::World world;
SpatialMap map; SpatialMap map;
@ -239,14 +248,29 @@ TEST_CASE("SpatialMap::distance_sorted", "[spatialmap]") {
SortedEntities result; SortedEntities result;
map.distance_sorted(result, {1, 1}, 100); map.distance_sorted(result, {1, 1}, 100);
REQUIRE(result.size() == 3); CHECK(result.size() == 3);
REQUIRE(result[0].entity == enemy1); CHECK(result[0].entity == enemy1);
REQUIRE(result[1].entity == item); CHECK(result[1].entity == item);
REQUIRE(result[2].entity == player); CHECK(result[2].entity == player);
int prev_dist = std::numeric_limits<int>::max(); int prev_dist = std::numeric_limits<int>::max();
for(auto rec : result) { for(auto rec : result) {
REQUIRE(rec.dist_square < prev_dist); CHECK(rec.dist_square < prev_dist);
prev_dist = rec.dist_square; prev_dist = rec.dist_square;
} }
} }
fuc2::Set TESTS{
.name="spatialmap",
.tests={
TEST(test_SpatialMap_distance_sorted),
TEST(test_SpatialMap_neighbors),
TEST(test_SpatialMap_find),
TEST(test_SpatialMap_get),
TEST(test_SpatialMap_occupied),
TEST(test_SpatialMap_move),
TEST(test_SpatialMap_remove),
TEST(test_SpatialMap_insert),
}
};
}

View file

@ -1,27 +1,40 @@
#include <catch2/catch_test_macros.hpp> #include <fuc2/testing.hpp>
#include "algos/stats.hpp" #include "algos/stats.hpp"
#include "algos/rand.hpp" #include "algos/rand.hpp"
#include <cmath> #include <cmath>
#include <fmt/core.h> #include <fmt/core.h>
TEST_CASE("basic stats tests", "[stats]") { using namespace fuc2;
namespace stats_tests {
void test_basic_stats_tests() {
Stats stat1; Stats stat1;
stat1.sample(1.0); stat1.sample(1.0);
for(int i = 0; i < 20; i++) { for(int i = 0; i < 20; i++) {
double x = Random::normal(20.0,5.0); double x = Random::normal(20.0,5.0);
stat1.sample(x); stat1.sample(x);
REQUIRE(!std::isnan(stat1.stddev())); CHECK(!std::isnan(stat1.stddev()));
REQUIRE(stat1.mean() < stat1.mean() + stat1.stddev() * 4.0); CHECK(stat1.mean() < stat1.mean() + stat1.stddev() * 4.0);
} }
stat1.dump(); stat1.dump();
stat1.reset(); stat1.reset();
REQUIRE(stat1.n == 0.0); CHECK(stat1.n == 0.0);
auto timer = stat1.time_start(); auto timer = stat1.time_start();
for(int i = 0; i < 20; i++) { for(int i = 0; i < 20; i++) {
stat1.sample_time(timer); stat1.sample_time(timer);
} }
} }
fuc2::Set TESTS{
.name="stats",
.tests={
TEST(test_basic_stats_tests),
}
};
}

View file

@ -1,11 +1,14 @@
#include <catch2/catch_test_macros.hpp> #include <fuc2/testing.hpp>
#include <fmt/core.h> #include <fmt/core.h>
#include "game/systems.hpp" #include "game/systems.hpp"
#include <cmath> #include <cmath>
#include <numbers> #include <numbers>
using namespace fuc2;
TEST_CASE("figure out best rotation direction", "[systems-rotate]") { namespace systems_tests {
void test_figure_out_best_rotation_direction() {
Matrix map = matrix::make(3, 3); Matrix map = matrix::make(3, 3);
Point player_at{1, 1}; Point player_at{1, 1};
@ -27,10 +30,19 @@ TEST_CASE("figure out best rotation direction", "[systems-rotate]") {
float diff = target_angle - aiming_angle; float diff = target_angle - aiming_angle;
double normalized = fmod(diff + 360.0, 360.0); double normalized = fmod(diff + 360.0, 360.0);
REQUIRE(normalized >= 0); CHECK(normalized >= 0);
REQUIRE(normalized <= 360); CHECK(normalized <= 360);
map[aiming_at.y][aiming_at.x] = 0; map[aiming_at.y][aiming_at.x] = 0;
} }
} }
} }
fuc2::Set TESTS{
.name="systems",
.tests={
TEST(test_figure_out_best_rotation_direction),
}
};
}

View file

@ -1,4 +1,4 @@
#include <catch2/catch_test_macros.hpp> #include <fuc2/testing.hpp>
#include <fmt/core.h> #include <fmt/core.h>
#include <string> #include <string>
#include "graphics/textures.hpp" #include "graphics/textures.hpp"
@ -6,16 +6,18 @@
#include "game/components.hpp" #include "game/components.hpp"
using namespace fmt; using namespace fmt;
using namespace fuc2;
TEST_CASE("test texture management", "[textures]") { namespace textures_tests {
void test_test_texture_management() {
components::init(); components::init();
textures::init(); textures::init();
auto spider = textures::get_sprite("hairy_spider"); auto spider = textures::get_sprite("hairy_spider");
REQUIRE(spider.sprite != nullptr); CHECK(spider.sprite != nullptr);
REQUIRE(spider.texture != nullptr); CHECK(spider.texture != nullptr);
REQUIRE(spider.frame_size.x == TEXTURE_WIDTH); CHECK(spider.frame_size.x == TEXTURE_WIDTH);
REQUIRE(spider.frame_size.y == TEXTURE_HEIGHT); CHECK(spider.frame_size.y == TEXTURE_HEIGHT);
auto image = textures::load_image("assets/sprites/hairy_spider.png"); auto image = textures::load_image("assets/sprites/hairy_spider.png");
@ -23,21 +25,30 @@ TEST_CASE("test texture management", "[textures]") {
size_t gray_stone = textures::get_id("gray_stone_floor_light"); size_t gray_stone = textures::get_id("gray_stone_floor_light");
auto floor_ptr = textures::get_surface(floor_tile); auto floor_ptr = textures::get_surface(floor_tile);
REQUIRE(floor_ptr != nullptr); CHECK(floor_ptr != nullptr);
auto gray_stone_ptr = textures::get_surface(gray_stone); auto gray_stone_ptr = textures::get_surface(gray_stone);
REQUIRE(gray_stone_ptr != nullptr); CHECK(gray_stone_ptr != nullptr);
auto& light = textures::get_ambient_light(); auto& light = textures::get_ambient_light();
REQUIRE(light.size() > 0); CHECK(light.size() > 0);
REQUIRE(light[floor_tile] == 0); CHECK(light[floor_tile] == 0);
REQUIRE(light[gray_stone] > 0); CHECK(light[gray_stone] > 0);
auto& tiles = textures::get_map_tile_set(); auto& tiles = textures::get_map_tile_set();
REQUIRE(tiles.size() > 0); CHECK(tiles.size() > 0);
REQUIRE(tiles[floor_tile] > 0); CHECK(tiles[floor_tile] > 0);
REQUIRE(tiles[gray_stone] > 0); CHECK(tiles[gray_stone] > 0);
auto ceiling = textures::get_ceiling(floor_tile); auto ceiling = textures::get_ceiling(floor_tile);
REQUIRE(ceiling != nullptr); CHECK(ceiling != nullptr);
}
fuc2::Set TESTS{
.name="textures",
.tests={
TEST(test_test_texture_management),
}
};
} }

View file

@ -1,11 +0,0 @@
[wrap-file]
directory = Catch2-3.7.1
source_url = https://github.com/catchorg/Catch2/archive/v3.7.1.tar.gz
source_filename = Catch2-3.7.1.tar.gz
source_hash = c991b247a1a0d7bb9c39aa35faf0fe9e19764213f28ffba3109388e62ee0269c
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/catch2_3.7.1-1/Catch2-3.7.1.tar.gz
wrapdb_version = 3.7.1-1
[provide]
catch2 = catch2_dep
catch2-with-main = catch2_with_main_dep