diff --git a/Makefile b/Makefile index 27ba792..0379c2c 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ tracy_build: meson compile -j 10 -C builddir test: build - ./builddir/runtests -d yes + ./builddir/fuc2it -d yes run: build test ifeq '$(OS)' 'Windows_NT' @@ -48,7 +48,7 @@ else endif debug: build - gdb --nx -x .gdbinit --ex run --args builddir/runtests + gdb --nx -x .gdbinit --ex run --args builddir/fuc2it debug_run: build gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args builddir/zedcaster @@ -60,7 +60,7 @@ clean: meson compile --clean -C builddir 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: powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" scripts\win_installer.ifp' diff --git a/meson.build b/meson.build index af4a500..c13c04c 100644 --- a/meson.build +++ b/meson.build @@ -59,7 +59,6 @@ elif build_machine.system() == 'darwin' ] endif -catch2 = subproject('catch2').get_variable('catch2_with_main_dep') fuc2 = subproject('fuc2').get_variable('fuc2_dep') fmt = subproject('fmt').get_variable('fmt_dep') json = subproject('nlohmann_json').get_variable('nlohmann_json_dep') @@ -108,13 +107,6 @@ caster_dep = declare_dependency( 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, cpp_args: cpp_args, link_args: link_args, diff --git a/src/dbc.cpp b/src/dbc.cpp index be045c9..63e3aeb 100644 --- a/src/dbc.cpp +++ b/src/dbc.cpp @@ -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) { string err = $F("[SENTINEL!] {}", message); 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) { if(!test) { string err = $F("[PRE!] {}", message); 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) { string err = $F("[POST!] {}", message); 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) { string err = $F("[CHECK!] {}\n", message); dbc::log(err, location); - throw dbc::CheckError{err}; + throw dbc::CheckError(err); } } diff --git a/src/dbc.hpp b/src/dbc.hpp index b2d054a..4b17b71 100644 --- a/src/dbc.hpp +++ b/src/dbc.hpp @@ -11,17 +11,10 @@ namespace dbc { using std::string; - class Error { - public: - const string message; - Error(string m) : message{m} {} - Error(const char *m) : message{m} {} - }; - - class CheckError : public Error {}; - class SentinelError : public Error {}; - class PreCondError : public Error {}; - class PostCondError : public Error {}; + using CheckError = std::runtime_error; + using SentinelError = std::runtime_error; + using PreCondError = std::runtime_error; + using PostCondError = std::runtime_error; void log(const string &message, const std::source_location location = diff --git a/tests/main.cpp b/tests/main.cpp index 9c39b2f..6ae14f2 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -18,22 +18,46 @@ TEST_SET(loot_tests); TEST_SET(map_tests); TEST_SET(matrix_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[]) { - run(ai_tests::TESTS); - run(animation_tests::TESTS); - run(base_tests::TESTS); - run(battle_tests::TESTS); - run(camera_tests::TESTS); - run(component_tests::TESTS); - run(config_tests::TESTS); - run(dbc_tests::TESTS); - run(dinkyecs_tests::TESTS); - run(event_router_tests::TESTS); - run(inventory_tests::TESTS); - run(lighting_tests::TESTS); - run(loot_tests::TESTS); - run(map_tests::TESTS); - run(matrix_tests::TESTS); - run(mazes_tests::TESTS); + std::vector all_tests{ + ai_tests::TESTS, + animation_tests::TESTS, + base_tests::TESTS, + battle_tests::TESTS, + camera_tests::TESTS, + component_tests::TESTS, + config_tests::TESTS, + dbc_tests::TESTS, + dinkyecs_tests::TESTS, + event_router_tests::TESTS, + inventory_tests::TESTS, + lighting_tests::TESTS, + loot_tests::TESTS, + map_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); + } } diff --git a/tests/meson.build b/tests/meson.build index 72bb61c..5d62f19 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -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( + 'textures.cpp', + 'systems.cpp', + 'stats.cpp', + 'spatialmap.cpp', + 'sound.cpp', + 'shaders.cpp', + 'rituals.cpp', + 'pathing.cpp', + 'palette.cpp', 'mazes.cpp', 'matrix.cpp', 'map.cpp', diff --git a/tests/palette.cpp b/tests/palette.cpp index a96ad81..403b51b 100644 --- a/tests/palette.cpp +++ b/tests/palette.cpp @@ -1,24 +1,34 @@ -#include +#include #include #include #include "graphics/palette.hpp" using namespace fmt; +using namespace fuc2; -TEST_CASE("color palette test", "[color-palette]") { - palette::init(); - REQUIRE(palette::initialized() == true); - // confirm it's idempotent - palette::init(); +namespace palette_tests { + void test_color_palette_test() { + palette::init(); + CHECK(palette::initialized() == true); + // confirm it's idempotent + 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"); - REQUIRE(gui_text == expect); + auto gui_text = palette::get("gui/theme:dark_dark"); + CHECK(gui_text == expect); - gui_text = palette::get("gui/theme", "mid"); - REQUIRE(gui_text != expect); + gui_text = palette::get("gui/theme", "mid"); + CHECK(gui_text != expect); - expect = {100, 100, 100, 255}; - REQUIRE(gui_text == expect); + expect = {100, 100, 100, 255}; + CHECK(gui_text == expect); + } + + fuc2::Set TESTS{ + .name="palette", + .tests={ + TEST(test_color_palette_test), + } + }; } diff --git a/tests/pathing.cpp b/tests/pathing.cpp index 1265dc1..844a015 100644 --- a/tests/pathing.cpp +++ b/tests/pathing.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -13,41 +13,51 @@ #include "constants.hpp" using namespace fmt; +using namespace fuc2; using namespace nlohmann; using std::string; using namespace components; using namespace std::chrono_literals; -json load_test_pathing(const string &fname) { - std::ifstream infile(fname); - return json::parse(infile); -} +namespace pathing_tests { + json load_test_pathing(const string &fname) { + std::ifstream infile(fname); + return json::parse(infile); + } -TEST_CASE("multiple targets can path", "[pathing]") { - GameDB::init(); - auto level = GameDB::create_level(); - auto walls_copy = level.map->$walls; - Pathing paths{matrix::width(walls_copy), matrix::height(walls_copy)}; + void test_multiple_targets_can_path() { + GameDB::init(); + auto level = GameDB::create_level(); + auto walls_copy = level.map->$walls; + Pathing paths{matrix::width(walls_copy), matrix::height(walls_copy)}; - System::multi_path(level, paths, walls_copy); + System::multi_path(level, paths, walls_copy); - bool diag = Random::uniform(0, 1); - auto pos = GameDB::player_position().location; - auto found = paths.find_path(pos, PATHING_TOWARD, diag); + bool diag = Random::uniform(0, 1); + auto pos = GameDB::player_position().location; + auto found = paths.find_path(pos, PATHING_TOWARD, diag); + + while(found == PathingResult::CONTINUE) { + // fmt::println("\033[2J\033[1;1H"); + // matrix::dump(diag ? "diag" : "simple", paths.$paths, pos.x, pos.y); + // std::this_thread::sleep_for(200ms); + found = paths.find_path(pos, PATHING_TOWARD, diag); + } - while(found == PathingResult::CONTINUE) { // fmt::println("\033[2J\033[1;1H"); // matrix::dump(diag ? "diag" : "simple", paths.$paths, pos.x, pos.y); - // std::this_thread::sleep_for(200ms); - found = paths.find_path(pos, PATHING_TOWARD, diag); + + if(found == PathingResult::FOUND) { + fmt::println("FOUND!"); + } else if(found == PathingResult::FAIL && !diag) { + CHECK(found != PathingResult::FAIL); + } } - // fmt::println("\033[2J\033[1;1H"); - // matrix::dump(diag ? "diag" : "simple", paths.$paths, pos.x, pos.y); - - if(found == PathingResult::FOUND) { - fmt::println("FOUND!"); - } else if(found == PathingResult::FAIL && !diag) { - REQUIRE(found != PathingResult::FAIL); - } + fuc2::Set TESTS{ + .name="pathing", + .tests={ + TEST(test_multiple_targets_can_path), + } + }; } diff --git a/tests/rituals.cpp b/tests/rituals.cpp index fb59e7f..4d15aba 100644 --- a/tests/rituals.cpp +++ b/tests/rituals.cpp @@ -1,136 +1,151 @@ -#include +#include #include #include "game/rituals.hpp" #include "algos/simplefsm.hpp" -TEST_CASE("ritual::Engine basic tests", "[rituals]") { - ritual::Engine re("assets/rituals.json"); - auto craft_state = re.start(); +using namespace fuc2; - re.set_state(craft_state, "has_spikes", true); - re.plan(craft_state); +namespace rituals_tests { - fmt::println("\n\n------------ TEST WILL DO PIERCE"); - craft_state.dump(); - REQUIRE(craft_state.will_do("pierce_type")); + void test_ritual_engine_basics() { + ritual::Engine re("assets/rituals.json"); + auto craft_state = re.start(); - REQUIRE(craft_state.start != craft_state.original); - craft_state.reset(); - REQUIRE(craft_state.start == craft_state.original); + re.set_state(craft_state, "has_spikes", true); + re.plan(craft_state); - re.set_state(craft_state, "has_magick", true); - re.set_state(craft_state, "has_spikes", true); - re.plan(craft_state); + fmt::println("\n\n------------ TEST WILL DO PIERCE"); + craft_state.dump(); + CHECK(craft_state.will_do("pierce_type")); - fmt::println("\n\n------------ TEST WILL DO MAGICK TOO"); - craft_state.dump(); - REQUIRE(craft_state.will_do("pierce_type")); + CHECK(craft_state.start != craft_state.original); + craft_state.reset(); + CHECK(craft_state.start == craft_state.original); - craft_state.pop(); - REQUIRE(craft_state.will_do("magick_type")); + re.set_state(craft_state, "has_magick", true); + re.set_state(craft_state, "has_spikes", true); + re.plan(craft_state); - craft_state.reset(); - re.set_state(craft_state, "has_magick", true); - re.set_state(craft_state, "has_spikes", true); - re.set_state(craft_state, "shiny_bauble", true); - re.plan(craft_state); + fmt::println("\n\n------------ TEST WILL DO MAGICK TOO"); + craft_state.dump(); + CHECK(craft_state.will_do("pierce_type")); - REQUIRE(craft_state.is_combined()); - fmt::println("\n\n------------ TEST WILL DO DAMAGE BOOST"); - craft_state.dump(); + craft_state.pop(); + CHECK(craft_state.will_do("magick_type")); - craft_state.reset(); - re.set_state(craft_state, "has_magick", true); - re.set_state(craft_state, "cursed_item", true); - re.set_state(craft_state, "shiny_bauble", true); - re.plan(craft_state); - REQUIRE(craft_state.is_combined()); + craft_state.reset(); + re.set_state(craft_state, "has_magick", true); + re.set_state(craft_state, "has_spikes", true); + re.set_state(craft_state, "shiny_bauble", true); + re.plan(craft_state); - fmt::println("\n\n------------ TEST WILL DO LARGE DAMAGE BOOST"); - craft_state.dump(); -} + CHECK(craft_state.is_combined()); + fmt::println("\n\n------------ TEST WILL DO DAMAGE BOOST"); + craft_state.dump(); -TEST_CASE("craft_state can be finalized for the end result", "[rituals]") { - ritual::Engine re("assets/rituals.json"); - auto craft_state = re.start(); + craft_state.reset(); + re.set_state(craft_state, "has_magick", true); + re.set_state(craft_state, "cursed_item", true); + re.set_state(craft_state, "shiny_bauble", true); + re.plan(craft_state); + CHECK(craft_state.is_combined()); - re.set_state(craft_state, "has_magick", true); - re.set_state(craft_state, "cursed_item", true); - re.set_state(craft_state, "shiny_bauble", true); - re.plan(craft_state); - REQUIRE(craft_state.is_combined()); - - fmt::println("\n\n------------ CYCLES AVOIDED"); - craft_state.dump(); - - auto ritual = re.finalize(craft_state); - ritual.dump(); -} - - -TEST_CASE("the ritual belt works", "[rituals]") { - ritual::Belt the_belt; - ritual::Engine re; - auto craft_state = re.start(); - - re.set_state(craft_state, "has_magick", true); - re.plan(craft_state); - REQUIRE(craft_state.is_combined()); - craft_state.dump(); - - { - auto ritual = re.finalize(craft_state); - the_belt.equip(0, ritual); - REQUIRE(the_belt.has(0)); + fmt::println("\n\n------------ TEST WILL DO LARGE DAMAGE BOOST"); + craft_state.dump(); } - { - auto ritual = the_belt.get(0); + void test_craft_state_can_be_finalized() { + ritual::Engine re("assets/rituals.json"); + auto craft_state = re.start(); + + re.set_state(craft_state, "has_magick", true); + re.set_state(craft_state, "cursed_item", true); + re.set_state(craft_state, "shiny_bauble", true); + re.plan(craft_state); + CHECK(craft_state.is_combined()); + + fmt::println("\n\n------------ CYCLES AVOIDED"); + craft_state.dump(); + + auto ritual = re.finalize(craft_state); ritual.dump(); } - { - the_belt.unequip(0); - REQUIRE(!the_belt.has(0)); + + void test_the_ritual_belt_works() { + ritual::Belt the_belt; + ritual::Engine re; + auto craft_state = re.start(); + + re.set_state(craft_state, "has_magick", true); + re.plan(craft_state); + CHECK(craft_state.is_combined()); + craft_state.dump(); + + { + auto ritual = re.finalize(craft_state); + the_belt.equip(0, ritual); + CHECK(the_belt.has(0)); + } + + { + auto ritual = the_belt.get(0); + ritual.dump(); + } + + { + the_belt.unequip(0); + CHECK(!the_belt.has(0)); + } + + craft_state.reset(); + CHECK(craft_state.plan.script.size() == 0); + CHECK(!craft_state.plan.complete); + CHECK(craft_state.start == craft_state.original); + CHECK(!craft_state.is_combined()); } - craft_state.reset(); - REQUIRE(craft_state.plan.script.size() == 0); - REQUIRE(!craft_state.plan.complete); - REQUIRE(craft_state.start == craft_state.original); - REQUIRE(!craft_state.is_combined()); -} - -TEST_CASE("ritual blanket basic operations", "[rituals-blanket]") { - ritual::Blanket blanket; - - ritual::Entity other = blanket.add("rusty_nails"); - ritual::Entity ent = blanket.add("severed_finger"); - auto& name = blanket.get(ent); - REQUIRE(name == "severed_finger"); - - REQUIRE(blanket.has(ent)); - blanket.remove(ent); - REQUIRE(!blanket.has(ent)); - REQUIRE(blanket.has(other)); - - REQUIRE(!blanket.is_selected(ent)); - REQUIRE(!blanket.is_selected(other)); - blanket.select(ent); - REQUIRE(blanket.is_selected(ent)); - REQUIRE(!blanket.is_selected(other)); - - blanket.deselect(ent); - REQUIRE(!blanket.is_selected(ent)); - - blanket.select(ent); - blanket.select(other); - REQUIRE(blanket.is_selected(ent)); - REQUIRE(blanket.is_selected(other)); - - blanket.reset(); - REQUIRE(!blanket.is_selected(ent)); - REQUIRE(!blanket.is_selected(other)); - REQUIRE(blanket.selected.empty()); - REQUIRE(blanket.selected.size() == 0); + void test_ritual_blanket_basic_operations() { + ritual::Blanket blanket; + + ritual::Entity other = blanket.add("rusty_nails"); + ritual::Entity ent = blanket.add("severed_finger"); + auto& name = blanket.get(ent); + CHECK(name == "severed_finger"); + + CHECK(blanket.has(ent)); + blanket.remove(ent); + CHECK(!blanket.has(ent)); + CHECK(blanket.has(other)); + + CHECK(!blanket.is_selected(ent)); + CHECK(!blanket.is_selected(other)); + blanket.select(ent); + CHECK(blanket.is_selected(ent)); + CHECK(!blanket.is_selected(other)); + + blanket.deselect(ent); + CHECK(!blanket.is_selected(ent)); + + blanket.select(ent); + blanket.select(other); + CHECK(blanket.is_selected(ent)); + CHECK(blanket.is_selected(other)); + + blanket.reset(); + CHECK(!blanket.is_selected(ent)); + CHECK(!blanket.is_selected(other)); + CHECK(blanket.selected.empty()); + 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), + } + }; } diff --git a/tests/shaders.cpp b/tests/shaders.cpp index cf86e4f..a367329 100644 --- a/tests/shaders.cpp +++ b/tests/shaders.cpp @@ -1,26 +1,38 @@ -#include +#include #include #include #include "graphics/shaders.hpp" using namespace fmt; +using namespace fuc2; -TEST_CASE("shader loading/init works", "[shaders]") { - shaders::init(); - int version = shaders::version(); +namespace shaders_tests { - std::shared_ptr ui_shader = shaders::get("ui_shader"); - auto other_test = shaders::get("ui_shader"); + void test_shader_loading_init_works() { + shaders::init(); + int version = shaders::version(); - REQUIRE(ui_shader != nullptr); - REQUIRE(ui_shader == other_test); - REQUIRE(shaders::updated(version) == false); + std::shared_ptr ui_shader = shaders::get("ui_shader"); + auto other_test = shaders::get("ui_shader"); - int new_version = shaders::reload(); - REQUIRE(version != shaders::version()); - REQUIRE(version != new_version); - REQUIRE(shaders::version() == new_version); - REQUIRE(shaders::updated(version) == true); - version = new_version; + CHECK(ui_shader != nullptr); + CHECK(ui_shader == other_test); + CHECK(shaders::updated(version) == false); + + int new_version = shaders::reload(); + CHECK(version != shaders::version()); + CHECK(version != new_version); + CHECK(shaders::version() == new_version); + CHECK(shaders::updated(version) == true); + version = new_version; + + } + + fuc2::Set TESTS{ + .name="shaders", + .tests={ + TEST(test_shader_loading_init_works), + } + }; } diff --git a/tests/sound.cpp b/tests/sound.cpp index 0d5d13d..4c53f18 100644 --- a/tests/sound.cpp +++ b/tests/sound.cpp @@ -1,14 +1,24 @@ -#include +#include #include #include #include "game/sound.hpp" using namespace fmt; +using namespace fuc2; -TEST_CASE("test sound manager", "[sound]") { - sound::init(); +namespace sound_tests { + void test_test_sound_manager() { + 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), + } + }; } diff --git a/tests/spatialmap.cpp b/tests/spatialmap.cpp index 1c366a5..cec718f 100644 --- a/tests/spatialmap.cpp +++ b/tests/spatialmap.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include "algos/spatialmap.hpp" @@ -9,244 +9,268 @@ using DinkyECS::Entity; using namespace fmt; +using namespace fuc2; + +namespace spatialmap_tests { + void test_SpatialMap_insert() { + DinkyECS::World world; + SpatialMap map; + + auto player = world.entity(); + auto item = world.entity(); + auto potion = world.entity(); + auto enemy = world.entity(); + Point at{10,10}; + Point enemy_at{11,11}; + + map.insert(at, item, false); + map.insert(at, potion, false); + CHECK(!map.occupied(at)); + + map.insert(at, player, true); + CHECK(map.occupied_by(at) == player); + + BLOWS_UP([&]() { + map.insert(at, enemy, true); + }); + + map.insert(enemy_at, enemy, true); + CHECK(map.occupied_by(enemy_at) == enemy); + CHECK(map.occupied(enemy_at)); + } + + void test_SpatialMap_remove() { + DinkyECS::World world; + SpatialMap map; + + auto player = world.entity(); + auto item = world.entity(); + Point at{120, 120}; + + // confirm that things can be in any order + map.insert(at, player, true); + map.insert(at, item, false); + CHECK(map.occupied(at)); + CHECK(map.occupied_by(at) == player); + + auto data = map.remove(at, player); + CHECK(!map.occupied(at)); + CHECK(data.entity == player); + CHECK(data.collision == true); + + BLOWS_UP([&]() { + map.remove(at, player); + }); + } -TEST_CASE("SpatialMap::insert", "[spatialmap]") { - DinkyECS::World world; - SpatialMap map; + void test_SpatialMap_move() { + DinkyECS::World world; + SpatialMap map; - auto player = world.entity(); - auto item = world.entity(); - auto potion = world.entity(); - auto enemy = world.entity(); - Point at{10,10}; - Point enemy_at{11,11}; + auto player = world.entity(); + auto item = world.entity(); + Point at{10, 320}; + map.insert(at, player, true); + map.insert(at, item, false); + CHECK(map.occupied(at)); - map.insert(at, item, false); - map.insert(at, potion, false); - REQUIRE(!map.occupied(at)); + auto enemy = world.entity(); + auto potion = world.entity(); + Point enemy_at{11, 320}; + map.insert(enemy_at, enemy, true); + map.insert(enemy_at, potion, false); + CHECK(map.occupied(enemy_at)); + CHECK(map.occupied_by(enemy_at) == enemy); - map.insert(at, player, true); - REQUIRE(map.occupied_by(at) == player); + Point target{at.x + 1, at.y}; - REQUIRE_THROWS(map.insert(at, enemy, true)); + // try bad move with a slot that's empty + BLOWS_UP([&]() { + map.move({0,0}, target, player); + }); - map.insert(enemy_at, enemy, true); - REQUIRE(map.occupied_by(enemy_at) == enemy); - REQUIRE(map.occupied(enemy_at)); -} + // try move into an occupied spot also fails + BLOWS_UP([&]() { + map.move(at, target, player); + }); -TEST_CASE("SpatialMap::remove", "[spatialmap]") { - DinkyECS::World world; - SpatialMap map; + // now move to a new spot, need to add them back + map.insert(at, player, true); + target.x++; // just move farther + map.move(at, target, player); - auto player = world.entity(); - auto item = world.entity(); - Point at{120, 120}; - - // confirm that things can be in any order - map.insert(at, player, true); - map.insert(at, item, false); - REQUIRE(map.occupied(at)); - REQUIRE(map.occupied_by(at) == player); - - auto data = map.remove(at, player); - REQUIRE(!map.occupied(at)); - REQUIRE(data.entity == player); - REQUIRE(data.collision == true); - - REQUIRE_THROWS(map.remove(at, player)); -} + CHECK(map.occupied(target)); + CHECK(map.occupied_by(target) == player); + auto data = map.remove(target, player); + CHECK(data.entity == player); + CHECK(data.collision == true); + } -TEST_CASE("SpatialMap::move", "[spatialmap]") { - DinkyECS::World world; - SpatialMap map; + void test_SpatialMap_occupied() { + DinkyECS::World world; + SpatialMap map; - auto player = world.entity(); - auto item = world.entity(); - Point at{10, 320}; - map.insert(at, player, true); - map.insert(at, item, false); - REQUIRE(map.occupied(at)); + auto player = world.entity(); + auto item = world.entity(); - auto enemy = world.entity(); - auto potion = world.entity(); - Point enemy_at{11, 320}; - map.insert(enemy_at, enemy, true); - map.insert(enemy_at, potion, false); - REQUIRE(map.occupied(enemy_at)); - REQUIRE(map.occupied_by(enemy_at) == enemy); + Point at{1000, 20}; + // first test empty locations + CHECK(!map.something_there(at)); + CHECK(!map.occupied(at)); - Point target{at.x + 1, at.y}; + // then when there's something without collision + map.insert(at, item, false); + CHECK(map.something_there(at)); + CHECK(!map.occupied(at)); - // try bad move with a slot that's empty - REQUIRE_THROWS(map.move({0,0}, target, player)); + // finally with collision and an item there + map.insert(at, player, true); + CHECK(map.something_there(at)); + CHECK(map.occupied(at)); + CHECK(map.occupied_by(at) == player); - // try move into an occupied spot also fails - REQUIRE_THROWS(map.move(at, target, player)); + // then remove the item and still have collision - // now move to a new spot, need to add them back - map.insert(at, player, true); - target.x++; // just move farther - map.move(at, target, player); + map.remove(at, item); + CHECK(map.something_there(at)); + CHECK(map.occupied(at)); + CHECK(map.occupied_by(at) == player); - REQUIRE(map.occupied(target)); - REQUIRE(map.occupied_by(target) == player); - auto data = map.remove(target, player); - REQUIRE(data.entity == player); - REQUIRE(data.collision == true); -} + // remove player and back to no collision + map.remove(at, player); + CHECK(!map.something_there(at)); + CHECK(!map.occupied(at)); + + // last thing, put just the player in at a new spot + Point target{at.x+1, at.y+10}; + map.insert(target, player, true); + CHECK(map.something_there(target)); + CHECK(map.occupied(target)); + CHECK(map.occupied_by(target) == player); + } -TEST_CASE("SpatialMap::occupied/something_there", "[spatialmap]") { - DinkyECS::World world; - SpatialMap map; + void test_SpatialMap_get() { + DinkyECS::World world; + SpatialMap map; - auto player = world.entity(); - auto item = world.entity(); + auto player = world.entity(); + auto item = world.entity(); + Point at{101, 31}; - Point at{1000, 20}; - // first test empty locations - REQUIRE(!map.something_there(at)); - REQUIRE(!map.occupied(at)); + // finally with collision and an item there + map.insert(at, player, true); + CHECK(map.occupied(at)); + CHECK(map.occupied_by(at) == player); - // then when there's something without collision - map.insert(at, item, false); - REQUIRE(map.something_there(at)); - REQUIRE(!map.occupied(at)); + auto entity = map.get(at); + CHECK(player == entity); - // finally with collision and an item there - map.insert(at, player, true); - REQUIRE(map.something_there(at)); - REQUIRE(map.occupied(at)); - REQUIRE(map.occupied_by(at) == player); + // This probably doesn't work so need to + // rethink how get works. + map.insert(at, item, false); + entity = map.get(at); + CHECK(entity == item); + } - // then remove the item and still have collision + void test_SpatialMap_find() { + DinkyECS::World world; + SpatialMap map; + Point at{101, 31}; + DinkyECS::Entity should_collide = DinkyECS::NONE; - map.remove(at, item); - REQUIRE(map.something_there(at)); - REQUIRE(map.occupied(at)); - REQUIRE(map.occupied_by(at) == player); + for(int i = 0; i < 10; i++) { + auto ent = world.entity(); + map.insert(at, ent, i == 8); - // remove player and back to no collision - map.remove(at, player); - REQUIRE(!map.something_there(at)); - REQUIRE(!map.occupied(at)); + if(i == 8) { + should_collide = ent; + } + } - // last thing, put just the player in at a new spot - Point target{at.x+1, at.y+10}; - map.insert(target, player, true); - REQUIRE(map.something_there(target)); - REQUIRE(map.occupied(target)); - REQUIRE(map.occupied_by(target) == player); -} + auto collision = map.find(at, [&](auto data) -> bool { + return data.collision; + }); + CHECK(collision == should_collide); -TEST_CASE("SpatialMap::get", "[spatialmap]") { - DinkyECS::World world; - SpatialMap map; + auto no_collide = map.find(at, [&](auto data) -> bool { + return !data.collision; + }); - auto player = world.entity(); - auto item = world.entity(); - Point at{101, 31}; + CHECK(no_collide != should_collide); + } - // finally with collision and an item there - map.insert(at, player, true); - REQUIRE(map.occupied(at)); - REQUIRE(map.occupied_by(at) == player); + void test_SpatialMap_neighbors() { + DinkyECS::World world; + SpatialMap map; - auto entity = map.get(at); - REQUIRE(player == entity); + auto player = world.entity(); + auto enemy1 = world.entity(); + auto enemy2 = world.entity(); + //auto item1 = world.entity(); + //auto item2 = world.entity(); + Point at{101, 31}; - // This probably doesn't work so need to - // rethink how get works. - map.insert(at, item, false); - entity = map.get(at); - REQUIRE(entity == item); -} + map.insert(at, player, true); + map.insert({at.x+1, at.y}, enemy1, true); + map.insert({at.x-1, at.y+1}, enemy2, true); -TEST_CASE("SpatialMap::find", "[spatialmap-find]") { - DinkyECS::World world; - SpatialMap map; - Point at{101, 31}; - DinkyECS::Entity should_collide = DinkyECS::NONE; + auto result = map.neighbors(at, true); + CHECK(result.found); + CHECK(result.nearby.size() == 2); - for(int i = 0; i < 10; i++) { - auto ent = world.entity(); - map.insert(at, ent, i == 8); + bool maybe = result.nearby[0] == enemy1 || result.nearby[1] == enemy1; + CHECK(maybe); - if(i == 8) { - should_collide = ent; + maybe = result.nearby[0] == enemy2 || result.nearby[1] == enemy2; + CHECK(maybe); + + result = map.neighbors(at, false); + CHECK(result.found); + CHECK(result.nearby.size() == 1); + CHECK(result.nearby[0] == enemy1); + } + + void test_SpatialMap_distance_sorted() { + DinkyECS::World world; + SpatialMap map; + + auto player = world.entity(); + auto enemy1 = world.entity(); + auto item = world.entity(); + + map.insert({1,1}, player, true); + map.insert({4,4}, enemy1, true); + map.insert({3, 3}, item, false); + + SortedEntities result; + map.distance_sorted(result, {1, 1}, 100); + CHECK(result.size() == 3); + CHECK(result[0].entity == enemy1); + CHECK(result[1].entity == item); + CHECK(result[2].entity == player); + + int prev_dist = std::numeric_limits::max(); + for(auto rec : result) { + CHECK(rec.dist_square < prev_dist); + prev_dist = rec.dist_square; } } - auto collision = map.find(at, [&](auto data) -> bool { - return data.collision; - }); - - REQUIRE(collision == should_collide); - - auto no_collide = map.find(at, [&](auto data) -> bool { - return !data.collision; - }); - - REQUIRE(no_collide != should_collide); -} - -TEST_CASE("SpatialMap::neighbors", "[spatialmap-neighbors]") { - DinkyECS::World world; - SpatialMap map; - - auto player = world.entity(); - auto enemy1 = world.entity(); - auto enemy2 = world.entity(); - //auto item1 = world.entity(); - //auto item2 = world.entity(); - Point at{101, 31}; - - map.insert(at, player, true); - map.insert({at.x+1, at.y}, enemy1, true); - map.insert({at.x-1, at.y+1}, enemy2, true); - - auto result = map.neighbors(at, true); - REQUIRE(result.found); - REQUIRE(result.nearby.size() == 2); - - bool maybe = result.nearby[0] == enemy1 || result.nearby[1] == enemy1; - REQUIRE(maybe); - - maybe = result.nearby[0] == enemy2 || result.nearby[1] == enemy2; - REQUIRE(maybe); - - result = map.neighbors(at, false); - REQUIRE(result.found); - REQUIRE(result.nearby.size() == 1); - REQUIRE(result.nearby[0] == enemy1); -} - -TEST_CASE("SpatialMap::distance_sorted", "[spatialmap]") { - DinkyECS::World world; - SpatialMap map; - - auto player = world.entity(); - auto enemy1 = world.entity(); - auto item = world.entity(); - - map.insert({1,1}, player, true); - map.insert({4,4}, enemy1, true); - map.insert({3, 3}, item, false); - - SortedEntities result; - map.distance_sorted(result, {1, 1}, 100); - REQUIRE(result.size() == 3); - REQUIRE(result[0].entity == enemy1); - REQUIRE(result[1].entity == item); - REQUIRE(result[2].entity == player); - - int prev_dist = std::numeric_limits::max(); - for(auto rec : result) { - REQUIRE(rec.dist_square < prev_dist); - 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), + } + }; } diff --git a/tests/stats.cpp b/tests/stats.cpp index a5783e2..fc14fa1 100644 --- a/tests/stats.cpp +++ b/tests/stats.cpp @@ -1,27 +1,40 @@ -#include +#include #include "algos/stats.hpp" #include "algos/rand.hpp" #include #include -TEST_CASE("basic stats tests", "[stats]") { - Stats stat1; - stat1.sample(1.0); +using namespace fuc2; - for(int i = 0; i < 20; i++) { - double x = Random::normal(20.0,5.0); - stat1.sample(x); - REQUIRE(!std::isnan(stat1.stddev())); - REQUIRE(stat1.mean() < stat1.mean() + stat1.stddev() * 4.0); +namespace stats_tests { + + void test_basic_stats_tests() { + Stats stat1; + stat1.sample(1.0); + + for(int i = 0; i < 20; i++) { + double x = Random::normal(20.0,5.0); + stat1.sample(x); + CHECK(!std::isnan(stat1.stddev())); + CHECK(stat1.mean() < stat1.mean() + stat1.stddev() * 4.0); + } + + stat1.dump(); + + stat1.reset(); + CHECK(stat1.n == 0.0); + + auto timer = stat1.time_start(); + for(int i = 0; i < 20; i++) { + stat1.sample_time(timer); + } } - stat1.dump(); + fuc2::Set TESTS{ + .name="stats", + .tests={ + TEST(test_basic_stats_tests), + } + }; - stat1.reset(); - REQUIRE(stat1.n == 0.0); - - auto timer = stat1.time_start(); - for(int i = 0; i < 20; i++) { - stat1.sample_time(timer); - } } diff --git a/tests/systems.cpp b/tests/systems.cpp index 18c6384..45b4376 100644 --- a/tests/systems.cpp +++ b/tests/systems.cpp @@ -1,36 +1,48 @@ -#include +#include #include #include "game/systems.hpp" #include #include +using namespace fuc2; -TEST_CASE("figure out best rotation direction", "[systems-rotate]") { - Matrix map = matrix::make(3, 3); +namespace systems_tests { - Point player_at{1, 1}; - map[player_at.y][player_at.x] = 2; + void test_figure_out_best_rotation_direction() { + Matrix map = matrix::make(3, 3); + + Point player_at{1, 1}; + map[player_at.y][player_at.x] = 2; - for(matrix::box target{map, player_at.x, player_at.y, 1}; target.next();) { - for(matrix::box aiming_at{map, player_at.x, player_at.y, 1}; aiming_at.next();) { - map[aiming_at.y][aiming_at.x] = 10; + for(matrix::box target{map, player_at.x, player_at.y, 1}; target.next();) { + for(matrix::box aiming_at{map, player_at.x, player_at.y, 1}; aiming_at.next();) { + map[aiming_at.y][aiming_at.x] = 10; - float target_dx = float(player_at.x) - float(target.x); - float target_dy = float(player_at.y) - float(target.y); - float aiming_dx = float(player_at.x) - float(aiming_at.x); - float aiming_dy = float(player_at.y) - float(aiming_at.y); + float target_dx = float(player_at.x) - float(target.x); + float target_dy = float(player_at.y) - float(target.y); + float aiming_dx = float(player_at.x) - float(aiming_at.x); + float aiming_dy = float(player_at.y) - float(aiming_at.y); - float target_angle = atan2(-target_dy, target_dx) * (180.0 / std::numbers::pi); - float aiming_angle = atan2(-aiming_dy, aiming_dx) * (180.0 / std::numbers::pi); + float target_angle = atan2(-target_dy, target_dx) * (180.0 / std::numbers::pi); + float aiming_angle = atan2(-aiming_dy, aiming_dx) * (180.0 / std::numbers::pi); - float diff = target_angle - aiming_angle; - double normalized = fmod(diff + 360.0, 360.0); + float diff = target_angle - aiming_angle; + double normalized = fmod(diff + 360.0, 360.0); - REQUIRE(normalized >= 0); - REQUIRE(normalized <= 360); + CHECK(normalized >= 0); + 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), + } + }; + } diff --git a/tests/textures.cpp b/tests/textures.cpp index d6cec02..dd0d003 100644 --- a/tests/textures.cpp +++ b/tests/textures.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include "graphics/textures.hpp" @@ -6,38 +6,49 @@ #include "game/components.hpp" using namespace fmt; +using namespace fuc2; -TEST_CASE("test texture management", "[textures]") { - components::init(); - textures::init(); +namespace textures_tests { + void test_test_texture_management() { + components::init(); + textures::init(); - auto spider = textures::get_sprite("hairy_spider"); - REQUIRE(spider.sprite != nullptr); - REQUIRE(spider.texture != nullptr); - REQUIRE(spider.frame_size.x == TEXTURE_WIDTH); - REQUIRE(spider.frame_size.y == TEXTURE_HEIGHT); + auto spider = textures::get_sprite("hairy_spider"); + CHECK(spider.sprite != nullptr); + CHECK(spider.texture != nullptr); + CHECK(spider.frame_size.x == TEXTURE_WIDTH); + 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"); - size_t floor_tile = textures::get_id("floor_tile"); - size_t gray_stone = textures::get_id("gray_stone_floor_light"); + size_t floor_tile = textures::get_id("floor_tile"); + size_t gray_stone = textures::get_id("gray_stone_floor_light"); - auto floor_ptr = textures::get_surface(floor_tile); - REQUIRE(floor_ptr != nullptr); + auto floor_ptr = textures::get_surface(floor_tile); + CHECK(floor_ptr != nullptr); - auto gray_stone_ptr = textures::get_surface(gray_stone); - REQUIRE(gray_stone_ptr != nullptr); + auto gray_stone_ptr = textures::get_surface(gray_stone); + CHECK(gray_stone_ptr != nullptr); - auto& light = textures::get_ambient_light(); - REQUIRE(light.size() > 0); - REQUIRE(light[floor_tile] == 0); - REQUIRE(light[gray_stone] > 0); + auto& light = textures::get_ambient_light(); + CHECK(light.size() > 0); + CHECK(light[floor_tile] == 0); + CHECK(light[gray_stone] > 0); - auto& tiles = textures::get_map_tile_set(); - REQUIRE(tiles.size() > 0); - REQUIRE(tiles[floor_tile] > 0); - REQUIRE(tiles[gray_stone] > 0); + auto& tiles = textures::get_map_tile_set(); + CHECK(tiles.size() > 0); + CHECK(tiles[floor_tile] > 0); + CHECK(tiles[gray_stone] > 0); + + auto ceiling = textures::get_ceiling(floor_tile); + CHECK(ceiling != nullptr); + } + + fuc2::Set TESTS{ + .name="textures", + .tests={ + TEST(test_test_texture_management), + } + }; - auto ceiling = textures::get_ceiling(floor_tile); - REQUIRE(ceiling != nullptr); } diff --git a/wraps/catch2.wrap b/wraps/catch2.wrap deleted file mode 100644 index f9bf436..0000000 --- a/wraps/catch2.wrap +++ /dev/null @@ -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