From 46d9232e1a84ae7994dbe4b1d0e62a4bad75b7b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henri=20Hyyryl=C3=A4inen?= Date: Sat, 26 Jan 2019 15:24:27 +0200 Subject: [PATCH] Wrote tests for initial cloud positioning by the cloud manager Includes a lot of supporting changes. Also did some changes unrelated to the tests to the cloud code, like undoing the variable I didn't like. Contains a few changes that I started to make to the cloud positioning, but I'll probably undo later. --- SetupThrive.rb | 2 +- src/ThriveGame.cpp | 25 ++- src/ThriveGame.h | 8 + src/general/json_registry.h | 7 + src/microbe_stage/compound_cloud_system.cpp | 126 ++++--------- src/microbe_stage/compound_cloud_system.h | 39 ++++ src/microbe_stage/compounds.cpp | 11 ++ src/microbe_stage/compounds.h | 8 + src/microbe_stage/player_microbe_control.cpp | 14 +- test/CMakeLists.txt | 2 + test/engine.cpp | 19 -- test/entity.cpp | 70 -------- test/entity_filter.cpp | 171 ------------------ test/rng.cpp | 75 -------- test/rolling_grid.cpp | 84 --------- test/script_bindings.cpp | 27 --- test/sky_system.cpp | 32 ---- test/test_clouds.cpp | 180 +++++++++++++++++++ test/test_component.h | 45 ----- test/test_thrive_game.h | 33 ++++ 20 files changed, 357 insertions(+), 621 deletions(-) delete mode 100644 test/engine.cpp delete mode 100644 test/entity.cpp delete mode 100644 test/entity_filter.cpp delete mode 100644 test/rng.cpp delete mode 100644 test/rolling_grid.cpp delete mode 100644 test/script_bindings.cpp delete mode 100644 test/sky_system.cpp delete mode 100644 test/test_component.h create mode 100644 test/test_thrive_game.h diff --git a/SetupThrive.rb b/SetupThrive.rb index f3cafa50d59..23a602a6d11 100755 --- a/SetupThrive.rb +++ b/SetupThrive.rb @@ -78,7 +78,7 @@ def parseExtraArgs leviathan = Leviathan.new( # Use this if you always want the latest commit # version: "develop", - version: "556a6584f4f74d2c16d85b85ba26281a88c02171", + version: "ebaf03d6086c5cac39b66a54093e3926e1c0c359", # Doesn't actually work, but leviathan doesn't install with sudo by # default, or install at all for that matter noInstallSudo: true diff --git a/src/ThriveGame.cpp b/src/ThriveGame.cpp index a72302ccc89..8616c61eafd 100644 --- a/src/ThriveGame.cpp +++ b/src/ThriveGame.cpp @@ -246,6 +246,10 @@ void netSettings.IsAuthoritative = true; netSettings.DoInterpolation = true; + // TODO: switch to + // Leviathan::WorldNetworkSettings::GetSettingsForSinglePlayer() once we + // no longer do the interpolation once variable rate ticks are supported + LOG_INFO("ThriveGame: startNewGame: Creating new cellstage world"); m_impl->m_cellStage = std::dynamic_pointer_cast(engine->CreateWorld( @@ -494,7 +498,8 @@ void m_impl->m_microbeEditor = std::dynamic_pointer_cast(engine->CreateWorld( window1, static_cast(THRIVE_WORLD_TYPE::MICROBE_EDITOR), - createPhysicsMaterials(), netSettings)); + createPhysicsMaterials(), + Leviathan::WorldNetworkSettings::GetSettingsForSinglePlayer())); } LEVIATHAN_ASSERT( @@ -984,17 +989,27 @@ void ThriveGame::Tick(int mspassed) {} -void - ThriveGame::CustomizeEnginePostLoad() +bool + ThriveGame::createImpl() { - Engine* engine = Engine::Get(); - try { m_impl = std::make_unique(*this); } catch(const Leviathan::InvalidArgument& e) { LOG_ERROR("ThriveGame: loading configuration data failed: "); e.PrintToLog(); + return false; + } + + return true; +} + +void + ThriveGame::CustomizeEnginePostLoad() +{ + Engine* engine = Engine::Get(); + + if(!createImpl()) { MarkAsClosing(); return; } diff --git a/src/ThriveGame.h b/src/ThriveGame.h index 8b0c26b2b52..f0ff6c49037 100644 --- a/src/ThriveGame.h +++ b/src/ThriveGame.h @@ -13,6 +13,10 @@ namespace thrive { +namespace test { +class TestThriveGame; +} + class CellStageWorld; class ThriveNetHandler; @@ -25,6 +29,7 @@ class PlayerMicrobeControl; //! running the engine and the event loop class ThriveGame : public Leviathan::ClientApplication, public ThriveCommon { class Implementation; + friend test::TestThriveGame; public: ThriveGame(); @@ -150,6 +155,9 @@ class ThriveGame : public Leviathan::ClientApplication, public ThriveCommon { void _ShutdownApplicationPacketHandler() override; + bool + createImpl(); + private: std::unique_ptr m_network; diff --git a/src/general/json_registry.h b/src/general/json_registry.h index 877fc5b89e8..ea18d13f97f 100644 --- a/src/general/json_registry.h +++ b/src/general/json_registry.h @@ -17,6 +17,13 @@ // Base class of things to register. class RegistryType { public: + RegistryType() {} + + //! \brief Helper for derived test constructors + RegistryType(size_t id, const std::string& name) : + id(id), displayName(name), internalName(name) + {} + // Used to search by id. size_t id = std::numeric_limits::max(); // This would mean an error. diff --git a/src/microbe_stage/compound_cloud_system.cpp b/src/microbe_stage/compound_cloud_system.cpp index a04e48073a8..1ca41d25519 100644 --- a/src/microbe_stage/compound_cloud_system.cpp +++ b/src/microbe_stage/compound_cloud_system.cpp @@ -350,6 +350,11 @@ void const auto meshName = "CompoundCloudSystem_Plane_" + std::to_string(++CloudMeshNumberCounter); + // TODO: fix this in the engine to make this method simpler + // This crashes when used with RenderDoc and doesn't render anything + // m_planeMesh = Leviathan::GeometryHelpers::CreateXZPlane( + // meshName, CLOUD_WIDTH, CLOUD_HEIGHT); + // Create a background plane on which the fluid clouds will be drawn. m_planeMesh = Ogre::MeshManager::getSingleton().createManual( meshName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); @@ -683,65 +688,29 @@ void const Float3& playerPos) { // Initial spawning if everything is empty - int cloudTypesNum = m_cloudTypes.size(); - if(m_managedClouds.empty()) { LOG_INFO("CompoundCloudSystem doing initial spawning"); m_cloudGridCenter = Float3(0, 0, 0); - for(size_t i = 0; i < cloudTypesNum; i += CLOUDS_IN_ONE) { - - // Center - _spawnCloud(world, m_cloudGridCenter, i); - - // Top left - _spawnCloud(world, - m_cloudGridCenter + - Float3(-CLOUD_WIDTH * 2, 0, -CLOUD_HEIGHT * 2), - i); - - // Up - _spawnCloud( - world, m_cloudGridCenter + Float3(0, 0, -CLOUD_HEIGHT * 2), i); - - // Top right - _spawnCloud(world, - m_cloudGridCenter + - Float3(CLOUD_WIDTH * 2, 0, -CLOUD_HEIGHT * 2), - i); - - // Left - _spawnCloud( - world, m_cloudGridCenter + Float3(-CLOUD_WIDTH * 2, 0, 0), i); - - // Right - _spawnCloud( - world, m_cloudGridCenter + Float3(CLOUD_WIDTH * 2, 0, 0), i); - - // Bottom left - _spawnCloud(world, - m_cloudGridCenter + - Float3(-CLOUD_WIDTH * 2, 0, CLOUD_HEIGHT * 2), - i); - - // Down - _spawnCloud( - world, m_cloudGridCenter + Float3(0, 0, CLOUD_HEIGHT * 2), i); - - // Bottom right - _spawnCloud(world, - m_cloudGridCenter + - Float3(CLOUD_WIDTH * 2, 0, CLOUD_HEIGHT * 2), - i); + + const auto requiredCloudPositions{ + calculateGridPositions(m_cloudGridCenter)}; + + for(size_t i = 0; i < m_cloudTypes.size(); i += CLOUDS_IN_ONE) { + + // All positions + for(const auto& pos : requiredCloudPositions) { + _spawnCloud(world, pos, i); + } } } // This rounds up to the nearest multiple of 4, // divides that by 4 and multiplies by 9 to get all the clouds we have // (if we have 5 compounds that are clouds, we need 18 clouds, if 4 we need // 9 etc) - LEVIATHAN_ASSERT( - m_managedClouds.size() == ((((cloudTypesNum + 4 - 1) / 4 * 4) / 4) * 9), + LEVIATHAN_ASSERT(m_managedClouds.size() == + ((((m_cloudTypes.size() + 4 - 1) / 4 * 4) / 4) * 9), "A CompoundCloud entity has mysteriously been destroyed"); const auto moved = playerPos - m_cloudGridCenter; @@ -767,42 +736,18 @@ void } // Calculate the new positions - const Float3 requiredCloudPositions[] = { - // Center - m_cloudGridCenter, - - // Top left - m_cloudGridCenter + Float3(-CLOUD_WIDTH * 2, 0, -CLOUD_HEIGHT * 2), - - // Up - m_cloudGridCenter + Float3(0, 0, -CLOUD_HEIGHT * 2), - - // Top right - m_cloudGridCenter + Float3(CLOUD_WIDTH * 2, 0, -CLOUD_HEIGHT * 2), - - // Left - m_cloudGridCenter + Float3(-CLOUD_WIDTH * 2, 0, 0), - - // Right - m_cloudGridCenter + Float3(CLOUD_WIDTH * 2, 0, 0), - - // Bottom left - m_cloudGridCenter + Float3(-CLOUD_WIDTH * 2, 0, CLOUD_HEIGHT * 2), - - // Down - m_cloudGridCenter + Float3(0, 0, CLOUD_HEIGHT * 2), - - // Bottom right - m_cloudGridCenter + Float3(CLOUD_WIDTH * 2, 0, CLOUD_HEIGHT * 2), - }; + const auto requiredCloudPositions{ + calculateGridPositions(m_cloudGridCenter)}; + // Reposition clouds according to the origin + // The max amount of clouds is that all need to be moved + const size_t MAX_FAR_CLOUDS = m_managedClouds.size(); + // According to spec this check is superfluous, but it makes me feel + // better + if(m_tooFarAwayClouds.size() != MAX_FAR_CLOUDS) + m_tooFarAwayClouds.resize(MAX_FAR_CLOUDS); - // Reposition clouds according to the origin - // MAX of our cloud compounds is nearest multiple of 4 , divided by 4 - // and multiplied by 9 This case only happens when you respawn. - constexpr size_t MAX_FAR_CLOUDS = 18; - std::array tooFarAwayClouds; size_t farAwayIndex = 0; // All clouds that aren't at one of the requiredCloudPositions needs to @@ -822,6 +767,7 @@ void // An exact check might work but just to be safe slight // inaccuracy is allowed here if((pos - requiredPos).HAddAbs() < Leviathan::EPSILON) { + matched = true; break; } @@ -836,7 +782,7 @@ void break; } - tooFarAwayClouds[farAwayIndex++] = iter->second; + m_tooFarAwayClouds[farAwayIndex++] = iter->second; } } @@ -846,8 +792,8 @@ void size_t farAwayRepositionedIndex = 0; // Loop through the cloud groups - for(size_t c = 0; c < cloudTypesNum; c += CLOUDS_IN_ONE) { - // Loop for moving clouds + for(size_t c = 0; c < m_cloudTypes.size(); c += CLOUDS_IN_ONE) { + // Loop for moving clouds to all needed positions for each group for(size_t i = 0; i < std::size(requiredCloudPositions); ++i) { bool hasCloud = false; const auto& requiredPos = requiredCloudPositions[i]; @@ -877,8 +823,8 @@ void break; } - tooFarAwayClouds[farAwayRepositionedIndex++]->recycleToPosition( - requiredPos); + m_tooFarAwayClouds[farAwayRepositionedIndex++] + ->recycleToPosition(requiredPos); } } } @@ -906,12 +852,13 @@ void nullptr; CompoundCloudComponent& cloud = world.Create_CompoundCloudComponent( entity, *this, first, second, third, fourth); + m_managedClouds[entity] = &cloud; // Set correct position // TODO: this should probably be made a constructor parameter cloud.m_position = pos; + initializeCloud(cloud, world.GetScene()); - m_managedClouds[entity] = &cloud; } @@ -1041,7 +988,6 @@ void auto* densityState = pass->createTextureUnitState(); densityState->setTexture(cloud.m_texture); - // densityState->setTextureName("TestImageThing.png"); densityState->setSamplerblock(wrappedBlock); Ogre::TexturePtr texturePtr = @@ -1052,8 +998,8 @@ void noiseState->setSamplerblock(wrappedBlock); - // Maybe compiling this here is the best place - cloud.m_planeMaterial->compile(); + // // Maybe compiling this here is the best place + // cloud.m_planeMaterial->compile(); // Needs to create a plane instance on which the material is used on cloud.m_compoundCloudsPlane = scene->createItem(m_planeMesh); diff --git a/src/microbe_stage/compound_cloud_system.h b/src/microbe_stage/compound_cloud_system.h index ae94e5c681a..3e7a550e557 100644 --- a/src/microbe_stage/compound_cloud_system.h +++ b/src/microbe_stage/compound_cloud_system.h @@ -331,6 +331,9 @@ class CompoundCloudComponent : public Leviathan::Component { //! \todo This can be removed once there is a proper clear method available //! for systems to detect CompoundCloudSystem& m_owner; + + // //! A helper value to handle cloud repositioning + // bool m_repositioned = false; }; @@ -443,6 +446,39 @@ class CompoundCloudSystem { convertWorldToCloudLocalForGrab(const Float3& cloudPosition, const Float3& worldPosition); + static inline auto + calculateGridPositions(const Float3& center) + { + return std::array{ + // Center + center, + + // Top left + center + Float3(-CLOUD_WIDTH * 2, 0, -CLOUD_HEIGHT * 2), + + // Up + center + Float3(0, 0, -CLOUD_HEIGHT * 2), + + // Top right + center + Float3(CLOUD_WIDTH * 2, 0, -CLOUD_HEIGHT * 2), + + // Left + center + Float3(-CLOUD_WIDTH * 2, 0, 0), + + // Right + center + Float3(CLOUD_WIDTH * 2, 0, 0), + + // Bottom left + center + Float3(-CLOUD_WIDTH * 2, 0, CLOUD_HEIGHT * 2), + + // Down + center + Float3(0, 0, CLOUD_HEIGHT * 2), + + // Bottom right + center + Float3(CLOUD_WIDTH * 2, 0, CLOUD_HEIGHT * 2), + }; + } + protected: //! \brief Removes deleted clouds from m_managedClouds void @@ -514,6 +550,9 @@ class CompoundCloudSystem { //! the best way to simulate fluid velocity std::vector> m_xVelocity; std::vector> m_yVelocity; + + //! This is here to not have to allocate memory every tick + std::vector m_tooFarAwayClouds; }; } // namespace thrive diff --git a/src/microbe_stage/compounds.cpp b/src/microbe_stage/compounds.cpp index 4a8189b0cfc..573ed878eb1 100644 --- a/src/microbe_stage/compounds.cpp +++ b/src/microbe_stage/compounds.cpp @@ -5,6 +5,17 @@ using namespace thrive; Compound::Compound() {} +Compound::Compound(size_t id, + const std::string& name, + bool isCloud, + bool isUseful, + bool isEnvironmental, + Ogre::ColourValue colour) : + RegistryType(id, name), + isCloud(isCloud), isUseful(isUseful), isEnvironmental(isEnvironmental), + colour(colour) +{} + Compound::Compound(Json::Value value) { volume = value["volume"].asDouble(); diff --git a/src/microbe_stage/compounds.h b/src/microbe_stage/compounds.h index 2e2c83eff4f..a2f92150dfc 100644 --- a/src/microbe_stage/compounds.h +++ b/src/microbe_stage/compounds.h @@ -16,6 +16,14 @@ class Compound : public RegistryType { Compound(); + //! \brief Constructor for test use + Compound(size_t id, + const std::string& name, + bool isCloud, + bool isUseful, + bool isEnvironmental, + Ogre::ColourValue colour); + Compound(Json::Value value); }; diff --git a/src/microbe_stage/player_microbe_control.cpp b/src/microbe_stage/player_microbe_control.cpp index 9db2102803f..73e1be6e43b 100644 --- a/src/microbe_stage/player_microbe_control.cpp +++ b/src/microbe_stage/player_microbe_control.cpp @@ -256,8 +256,14 @@ void auto module = thrive->getMicrobeScripts(); - if(!module) - LOG_FATAL("PlayerMicrobeControlSystem: microbe scripts aren't loaded"); + if(!module) { + // Skip here to allow running better in unit tests + // This makes finding errors about this a bit more difficult but the + // game shouldn't start with invalid scripts + // LOG_FATAL("PlayerMicrobeControlSystem: microbe scripts aren't + // loaded"); + return; + } // Debug for movement keys // std::stringstream msg; @@ -319,6 +325,10 @@ Float3 PlayerMicrobeControlSystem::getTargetPoint( Leviathan::GameWorld& worldWithCamera) { + // Skip when there is no window to allow running headless + if(!Engine::Get()->GetWindowEntity()) + return Float3(0, 0, 0); + float x, y; Engine::Get()->GetWindowEntity()->GetNormalizedRelativeMouse(x, y); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index deeaa041d46..528a7655048 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,6 +8,8 @@ include_directories("${LEVIATHAN_SRC}") set(CurrentProjectName ThriveTest) set(AllProjectFiles "test_main.cpp" + "test_thrive_game.h" + "test_script_compile.cpp" "test_simulation_parameters.cpp" "test_clouds.cpp" diff --git a/test/engine.cpp b/test/engine.cpp deleted file mode 100644 index b54e088f359..00000000000 --- a/test/engine.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "engine/engine.h" - -#include "engine/entity_manager.h" -#include "engine/tests/test_component.h" -#include "util/make_unique.h" - -#include -#include - -using namespace thrive; - -namespace { - -class TestEngine : public Engine { -public: - TestEngine(EntityManager& entityManager) : Engine(entityManager) {} -}; - -} // namespace diff --git a/test/entity.cpp b/test/entity.cpp deleted file mode 100644 index 1b9df70bc15..00000000000 --- a/test/entity.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "engine/entity.h" - -#include "engine/engine.h" -#include "engine/entity_manager.h" -#include "engine/game_state.h" -#include "engine/system.h" -#include "engine/tests/test_component.h" -#include "util/make_unique.h" - -#include - - -using namespace thrive; - -// TODO: this needs to be basically rewritten in Lua - -// struct EntityTest : public ::testing::Test { - -// EntityTest() -// : gameState(engine.createGameState("test", {}, -// GameState::Initializer(), "DragDropDemo")) -// { - -// } - -// Engine engine; - -// GameState* gameState = nullptr; - -// }; - -// TEST_F(EntityTest, Exists) { -// // Null Id should never exist -// Entity nullEntity(NULL_ENTITY, gameState); -// EXPECT_FALSE(nullEntity.exists()); -// // Entity without components doesn't exist either -// Entity entity(gameState); -// EXPECT_FALSE(entity.exists()); -// // Add some component, then it should exist -// entity.addComponent(make_unique>()); -// EXPECT_TRUE(entity.exists()); -// } - - -// TEST_F(EntityTest, HasComponent) { -// Entity entity(gameState); -// EXPECT_FALSE(entity.hasComponent(TestComponent<0>::TYPE_ID)); -// entity.addComponent(make_unique>()); -// EXPECT_TRUE(entity.hasComponent(TestComponent<0>::TYPE_ID)); -// } - - -// TEST_F(EntityTest, RemoveComponent) { -// Entity entity(gameState); -// EXPECT_FALSE(entity.hasComponent(TestComponent<0>::TYPE_ID)); -// entity.addComponent(make_unique>()); -// EXPECT_TRUE(entity.hasComponent(TestComponent<0>::TYPE_ID)); -// entity.removeComponent(TestComponent<0>::TYPE_ID); -// gameState->entityManager().processRemovals(); -// EXPECT_FALSE(entity.hasComponent(TestComponent<0>::TYPE_ID)); -// } - - -// TEST_F(EntityTest, NamedEntity) { -// Entity unnamed(gameState); -// Entity named("named", gameState); -// Entity namedCopy("named", gameState); -// EXPECT_FALSE(named == unnamed); -// EXPECT_TRUE(named == namedCopy); -// } diff --git a/test/entity_filter.cpp b/test/entity_filter.cpp deleted file mode 100644 index 5142fbfda56..00000000000 --- a/test/entity_filter.cpp +++ /dev/null @@ -1,171 +0,0 @@ -#include "engine/entity_filter.h" - -#include "engine/entity_manager.h" -#include "engine/tests/test_component.h" -#include "util/make_unique.h" - -#include - -using namespace thrive; - -TEST(EntityFilter, Initialization) -{ - EntityManager entityManager; - // Add component - EntityId entityId = entityManager.generateNewId(); - entityManager.addComponent(entityId, make_unique>()); - EXPECT_TRUE(nullptr != entityManager.getComponent( - entityId, TestComponent<0>::TYPE_ID)); - // Set up filter - EntityFilter> filter; - filter.setEntityManager(&entityManager); - // Check filter - auto filteredEntities = filter.entities(); - EXPECT_EQ(1, filteredEntities.count(entityId)); - EXPECT_EQ(1, filteredEntities.size()); -} - -TEST(EntityFilter, Single) -{ - EntityManager entityManager; - // Set up filter - EntityFilter> filter; - filter.setEntityManager(&entityManager); - // Add component - EntityId entityId = entityManager.generateNewId(); - entityManager.addComponent(entityId, make_unique>()); - // Check filter - auto filteredEntities = filter.entities(); - EXPECT_EQ(1, filteredEntities.count(entityId)); - EXPECT_EQ(1, filteredEntities.size()); - // Remove component - entityManager.removeComponent(entityId, TestComponent<0>::TYPE_ID); - entityManager.processRemovals(); - // Check filter - filteredEntities = filter.entities(); - EXPECT_EQ(0, filteredEntities.count(entityId)); - EXPECT_EQ(0, filteredEntities.size()); -} - - -TEST(EntityFilter, Multiple) -{ - EntityManager entityManager; - // Set up filter - EntityFilter, TestComponent<1>> filter; - filter.setEntityManager(&entityManager); - auto filteredEntities = filter.entities(); - // Add first component - EntityId entityId = entityManager.generateNewId(); - entityManager.addComponent(entityId, make_unique>()); - // Check filter - filteredEntities = filter.entities(); - // Add first component - EXPECT_EQ(0, filteredEntities.count(entityId)); - EXPECT_EQ(0, filteredEntities.size()); - // Add second component - entityManager.addComponent(entityId, make_unique>()); - // Check filter - filteredEntities = filter.entities(); - EXPECT_EQ(1, filteredEntities.count(entityId)); - EXPECT_EQ(1, filteredEntities.size()); - // Remove component - entityManager.removeComponent(entityId, TestComponent<1>::TYPE_ID); - entityManager.processRemovals(); - // Check filter - filteredEntities = filter.entities(); - EXPECT_EQ(0, filteredEntities.count(entityId)); - EXPECT_EQ(0, filteredEntities.size()); -} - - -TEST(EntityFilter, Optional) -{ - EntityManager entityManager; - using TestFilter = - EntityFilter, Optional>>; - // Set up filter - TestFilter filter; - filter.setEntityManager(&entityManager); - TestFilter::EntityMap filteredEntities = filter.entities(); - // Add first component - EntityId entityId = entityManager.generateNewId(); - entityManager.addComponent(entityId, make_unique>()); - // Check filter - filteredEntities = filter.entities(); - EXPECT_EQ(1, filteredEntities.count(entityId)); - EXPECT_EQ(1, filteredEntities.size()); - // Check group - TestFilter::ComponentGroup group = filteredEntities[entityId]; - EXPECT_TRUE(std::get<0>(group) != nullptr); - EXPECT_TRUE(std::get<1>(group) == nullptr); - // Add second component - entityManager.addComponent(entityId, make_unique>()); - // Check filter - filteredEntities = filter.entities(); - EXPECT_EQ(1, filteredEntities.count(entityId)); - EXPECT_EQ(1, filteredEntities.size()); - // Check group - group = filteredEntities[entityId]; - EXPECT_TRUE(std::get<0>(group) != nullptr); - EXPECT_TRUE(std::get<1>(group) != nullptr); - // Remove component - entityManager.removeComponent(entityId, TestComponent<1>::TYPE_ID); - entityManager.processRemovals(); - // Check filter - filteredEntities = filter.entities(); - EXPECT_EQ(1, filteredEntities.count(entityId)); - EXPECT_EQ(1, filteredEntities.size()); - // Check group - group = filteredEntities[entityId]; - EXPECT_TRUE(std::get<0>(group) != nullptr); -} - - -TEST(EntityFilter, OptionalOnly) -{ - EntityManager entityManager; - using TestFilter = EntityFilter>>; - // Set up filter - TestFilter filter; - filter.setEntityManager(&entityManager); - TestFilter::EntityMap filteredEntities = filter.entities(); - // Add first component - EntityId entityId = entityManager.generateNewId(); - entityManager.addComponent(entityId, make_unique>()); - // Check filter - filteredEntities = filter.entities(); - EXPECT_EQ(1, filteredEntities.count(entityId)); - EXPECT_EQ(1, filteredEntities.size()); - // Check group - TestFilter::ComponentGroup group = filteredEntities[entityId]; - EXPECT_TRUE(std::get<0>(group) != nullptr); - // Remove component - entityManager.removeComponent(entityId, TestComponent<0>::TYPE_ID); - entityManager.processRemovals(); - // Check filter - filteredEntities = filter.entities(); - EXPECT_EQ(0, filteredEntities.count(entityId)); - EXPECT_EQ(0, filteredEntities.size()); -} - - -TEST(EntityFilter, Record) -{ - EntityManager entityManager; - using TestFilter = EntityFilter>; - // Set up filter - TestFilter filter(true); - filter.setEntityManager(&entityManager); - TestFilter::EntityMap filteredEntities = filter.entities(); - // Add first component - EntityId entityId = entityManager.generateNewId(); - entityManager.addComponent(entityId, make_unique>()); - // Check added entities - EXPECT_EQ(1, filter.addedEntities().count(entityId)); - // Remove component - entityManager.removeComponent(entityId, TestComponent<0>::TYPE_ID); - entityManager.processRemovals(); - // Check removed entities - EXPECT_EQ(0, filter.removedEntities().count(entityId)); -} diff --git a/test/rng.cpp b/test/rng.cpp deleted file mode 100644 index bdfafb1ca4d..00000000000 --- a/test/rng.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "engine/rng.h" -#include "engine/tests/test_component.h" - -#include -#include -#include -#include - -#include - - -using namespace thrive; - - -TEST(RNG, getInt) -{ - RNG rng; - std::set rngIntValues; - // A series of random integers should not result in the same number - // repeatedly. (These test will unintentionally fail once every 1x10^200 - // times, feeling lucky?) - for(int i = 0; i < 100; ++i) - rngIntValues.insert(rng.getInt(1, 100)); - EXPECT_FALSE(rngIntValues.size() == 1); - // Random numbers produced must be in the provided range - EXPECT_TRUE((*rngIntValues.begin()) >= 1); - EXPECT_TRUE((*rngIntValues.end()) <= 100); -} - -TEST(RNG, getDouble) -{ - RNG rng; - // A series of random doubles should not result in the same number - // repeatedly. - std::set rngDoubleValues; - for(int i = 0; i < 100; ++i) - rngDoubleValues.insert(rng.getDouble(1.0, 100.0)); - EXPECT_FALSE(rngDoubleValues.size() == 1); - // Random numbers produced must be in the provided range - EXPECT_TRUE((*rngDoubleValues.begin()) >= 1.0); - EXPECT_TRUE((*rngDoubleValues.end()) <= 100.0); -} - -TEST(RNG, generateRandomSeed) -{ - RNG rng; - std::unordered_set rngSeedValues; - // A series of random seeds should not result in the same number repeatedly. - for(int i = 0; i < 100; ++i) - rngSeedValues.insert(rng.generateRandomSeed()); - EXPECT_FALSE(rngSeedValues.size() == 1); -} - -TEST(RNG, getSeed) -{ - RNG rng(1337); - EXPECT_EQ(1337, rng.getSeed()); -} - -TEST(RNG, setSeed) -{ - RNG rng(1337); - rng.setSeed(1234); - EXPECT_EQ(1234, rng.getSeed()); -} - -TEST(RNG, shuffle) -{ - RNG rng; - std::vector original{ - 5, 12, 16, 18, 19, 25, 33, 41, 53, 69, 71, 87, 90}; - std::vector shuffled(original); - rng.shuffle(shuffled.begin(), shuffled.end()); - EXPECT_TRUE(shuffled != original); -} diff --git a/test/rolling_grid.cpp b/test/rolling_grid.cpp deleted file mode 100644 index 9796f1a8ec4..00000000000 --- a/test/rolling_grid.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "engine/rolling_grid.h" -#include "gtest/gtest.h" - -using namespace thrive; - -TEST(RollingGrid, Initialization) -{ - RollingGrid grid(1920, 1080, 1); -} - -TEST(RollingGrid, Read) -{ - RollingGrid grid(1920, 1080, 1); - EXPECT_EQ(0, grid(0, 0)); // somewhere in-range - EXPECT_EQ(0, grid(15649, 986984)); // somewhere out-of-range -} - -TEST(RollingGrid, Edit) -{ - RollingGrid grid(1920, 1080, 1); - EXPECT_EQ(0, grid(20, 20)); - grid(20, 20) = 5; - // std::cout << "set (20,20) to 5" << std::endl; - EXPECT_EQ(5, grid(20, 20)); - // std::cout << "checked (20,20) == 5" << std::endl; - // make sure neighbors haven't been screwed with - for(int i = 18; i < 23; i++) { - for(int j = 18; j < 23; j++) { - if(i != 20 || j != 20) { - EXPECT_EQ(0, grid(i, j)); - } - } - } -} - -TEST(RollingGrid, SmallMove) -{ - RollingGrid grid(1920, 1080, 1); - grid(100, 100) = 1; - // std::cout << "set (100,100) to 1" << std::endl; - grid.move(1, 0); - // std::cout << "moved by (15, -12)" << std::endl; - EXPECT_EQ(1, grid(100, 100)); - // std::cout << "checked (100,100)" << std::endl; - EXPECT_EQ(0, grid(115, 88)); - EXPECT_EQ(0, grid(85, 112)); - for(int i = 0; i < 1920; i++) { - for(int j = 0; j < 1080; j++) { - int k = grid(i, j); - if(k) - std::cout << "(" << i << "," << j << ") = " << k << std::endl; - } - } -} - -TEST(RollingGrid, BigMove) -{ - RollingGrid grid(1920, 1080, 1); - grid(0, 0) = 1; - grid.move(71280, 90506); - EXPECT_EQ(0, grid(0, 0)); - grid(71281, 90507) = 1; - EXPECT_EQ(1, grid(71281, 90507)); -} - -TEST(RollingGrid, TinyGrid) -{ - RollingGrid grid(1, 2, 1); - grid(0, 0) = 1; - grid.move(0, -1); - EXPECT_EQ(1, grid(0, 0)); -} - -TEST(RollingGrid, NullMoves) -{ - RollingGrid grid(100, 200, 1); - grid(0, 0) = 1; - grid.move(0, 0); - EXPECT_EQ(1, grid(0, 0)); - - grid.move(0, -1); - grid.move(0, 1); - EXPECT_EQ(1, grid(0, 0)); -} diff --git a/test/script_bindings.cpp b/test/script_bindings.cpp deleted file mode 100644 index 800ebc7d925..00000000000 --- a/test/script_bindings.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "scripting/script_initializer.h" - -#include "scripting/luajit.h" - -#include -#include - -using namespace Ogre; -using namespace thrive; - -TEST(OgreVector3, Lua) -{ - - sol::state lua; - - initializeLua(lua); - - lua.do_string("a = Vector3(1, 2, 3)\n" - "b = Vector3(10, 20, 30)\n" - "sum = a + b\n" - "dot = a:dotProduct(b)\n"); - - Vector3 sum = lua.get("sum"); - Real dot = lua.get("dot"); - EXPECT_EQ(Vector3(11, 22, 33), sum); - EXPECT_EQ(140, dot); -} diff --git a/test/sky_system.cpp b/test/sky_system.cpp deleted file mode 100644 index 81ffee14257..00000000000 --- a/test/sky_system.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "ogre/sky_system.h" - -#include "scripting/script_initializer.h" - -#include "scripting/luajit.h" - -#include "util/make_unique.h" - -#include - -using namespace thrive; - - -TEST(SkyPlaneComponent, ScriptBindings) -{ - sol::state lua; - - initializeLua(lua); - - auto skyPlane = make_unique(); - lua["skyPlane"] = skyPlane.get(); - - ; - - // Enabled - EXPECT_TRUE(lua.do_string("skyPlane.properties.enabled = false").valid()); - - EXPECT_FALSE(skyPlane->m_properties.enabled); - // Plane.d - EXPECT_TRUE(lua.do_string("skyPlane.properties.plane.d = 42.0").valid()); - EXPECT_EQ(42.0f, skyPlane->m_properties.plane.d); -} diff --git a/test/test_clouds.cpp b/test/test_clouds.cpp index 1b1e5ea8a73..3497be6aeaa 100644 --- a/test/test_clouds.cpp +++ b/test/test_clouds.cpp @@ -1,9 +1,15 @@ //! Tests compound cloud operations that don't need Ogre +#include "engine/player_data.h" +#include "generated/cell_stage_world.h" #include "microbe_stage/compound_cloud_system.h" +#include "test_thrive_game.h" +#include +#include #include "catch.hpp" using namespace thrive; +using namespace thrive::test; TEST_CASE("Cloud contains check is correct", "[microbe]") { @@ -145,3 +151,177 @@ TEST_CASE("Cloud local coordinate calculation math is right", "[microbe]") Leviathan::InvalidArgument); } } + +class CloudManagerTestsFixture { +public: + CloudManagerTestsFixture() + { + thrive.lightweightInit(); + + world.SetRunInBackground(true); + + // TODO: change type when the clouds are made to run again with the + // variable rate ticks + REQUIRE(world.Init( + Leviathan::WorldNetworkSettings::GetSettingsForHybrid(), nullptr)); + + // Create player pos + player = world.CreateEntity(); + + thrive.playerData().setActiveCreature(player); + + REQUIRE_NOTHROW(playerPos = &world.Create_Position(player, + Float3(0, 0, 0), Float4::IdentityQuaternion())); + } + ~CloudManagerTestsFixture() + { + world.Release(); + } + + void + setCloudsAndRunInitial(const std::vector& cloudTypes) + { + world.GetCompoundCloudSystem().registerCloudTypes(world, cloudTypes); + + // Let it run a bit + world.Tick(1); + } + + std::vector + findClouds() + { + std::vector clouds; + + for(ObjectID entity : world.GetEntities()) { + + if(entity == player) + continue; + + REQUIRE_NOTHROW(clouds.emplace_back( + &world.GetComponent_CompoundCloudComponent(entity))); + } + + return clouds; + } + +protected: + Leviathan::Test::PartialEngine engine; + TestThriveGame thrive; + Leviathan::IDFactory ids; + + CellStageWorld world{nullptr}; + + Leviathan::Position* playerPos = nullptr; + ObjectID player = NULL_OBJECT; +}; + +TEST_CASE_METHOD(CloudManagerTestsFixture, + "Cloud manager creates and positions clouds with 1 compound type correctly", + "[microbe]") +{ + setCloudsAndRunInitial( + {Compound{1, "a", true, true, false, Ogre::ColourValue(0, 1, 2, 3)}}); + + // Find the cloud entities + const auto clouds = findClouds(); + + CHECK(clouds.size() == 9); + + // Check that cloud positioning has worked + const auto targetPositions{ + CompoundCloudSystem::calculateGridPositions(Float3(0, 0, 0))}; + + std::vector valid; + valid.resize(targetPositions.size(), false); + + for(size_t i = 0; i < targetPositions.size(); ++i) { + + const auto& pos = targetPositions[i]; + + for(CompoundCloudComponent* cloud : clouds) { + + if(cloud->getPosition() == pos) { + + CHECK(!valid[i]); + valid[i] = true; + } + } + } + + for(bool entry : valid) + CHECK(entry); +} + +TEST_CASE_METHOD(CloudManagerTestsFixture, + "Cloud manager creates and positions clouds with 5 compound types " + "correctly", + "[microbe]") +{ + // This test assumes + static_assert(CLOUDS_IN_ONE == 4, "this test assumes this"); + + const std::vector types{ + Compound{1, "a", true, true, false, Ogre::ColourValue(0, 1, 2, 1)}, + Compound{2, "b", true, true, false, Ogre::ColourValue(3, 4, 5, 1)}, + Compound{3, "c", true, true, false, Ogre::ColourValue(6, 7, 8, 1)}, + Compound{4, "d", true, true, false, Ogre::ColourValue(9, 10, 11, 1)}, + Compound{5, "e", true, true, false, Ogre::ColourValue(12, 13, 14, 1)}}; + + setCloudsAndRunInitial(types); + + // Find the cloud entities + const auto clouds = findClouds(); + + CHECK(clouds.size() == 18); + + // Check that cloud positioning has worked + const auto targetPositions{ + CompoundCloudSystem::calculateGridPositions(Float3(0, 0, 0))}; + + std::vector> valid; + valid.resize(targetPositions.size(), {false, false}); + + for(size_t i = 0; i < targetPositions.size(); ++i) { + + const auto& pos = targetPositions[i]; + + for(CompoundCloudComponent* cloud : clouds) { + + if(cloud->getPosition() == pos) { + + // First or second + CAPTURE(cloud->getCompoundId1()); + if(cloud->getCompoundId1() == types[0].id) { + + // Make sure the rest of the cloud entries are also right + CHECK(cloud->getCompoundId2() == types[1].id); + CHECK(cloud->getCompoundId3() == types[2].id); + CHECK(cloud->getCompoundId4() == types[3].id); + + // Position check + CHECK(!valid[i][0]); + valid[i][0] = true; + + } else if(cloud->getCompoundId1() == types[4].id) { + + // Make sure the rest of the cloud entries are also right + CHECK(cloud->getCompoundId2() == NULL_COMPOUND); + CHECK(cloud->getCompoundId3() == NULL_COMPOUND); + CHECK(cloud->getCompoundId4() == NULL_COMPOUND); + + // Position check + CHECK(!valid[i][1]); + valid[i][1] = true; + + } else { + FAIL_CHECK("cloud has unexpected id"); + } + } + } + } + + for(const auto& entry : valid) { + CHECK(entry[0]); + CHECK(entry[1]); + } +} diff --git a/test/test_component.h b/test/test_component.h deleted file mode 100644 index 9a17b44338c..00000000000 --- a/test/test_component.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include "engine/component.h" -#include "engine/serialization.h" -#include "engine/typedefs.h" - -#include - -template class TestComponent : public thrive::Component { - -public: - static const thrive::ComponentTypeId TYPE_ID = ID + 10000; - - thrive::ComponentTypeId - typeId() const override - { - return TYPE_ID; - }; - - static const std::string& - TYPE_NAME() - { - static std::string string = - "TestComponent" + boost::lexical_cast(ID); - return string; - } - - std::string - typeName() const override - { - return TYPE_NAME(); - }; - - void - load(const thrive::StorageContainer& storage) override - { - Component::load(storage); - } - - thrive::StorageContainer - storage() const override - { - return Component::storage(); - } -}; diff --git a/test/test_thrive_game.h b/test/test_thrive_game.h new file mode 100644 index 00000000000..467c48d51ca --- /dev/null +++ b/test/test_thrive_game.h @@ -0,0 +1,33 @@ +// Thrive Game +// Copyright (C) 2013-2019 Revolutionary Games +#pragma once +// ------------------------------------ // +#include "ThriveGame.h" + +#include "catch.hpp" + +namespace thrive { namespace test { +//! \brief A test dummy for tests needing ThriveGame +class TestThriveGame : public ThriveGame { +public: + TestThriveGame() + { + // We need to fake key configurations for things + ApplicationConfiguration = new Leviathan::AppDef(true); + ApplicationConfiguration->ReplaceGameAndKeyConfigInMemory( + nullptr, &ThriveGame::CheckGameKeyConfigVariables); + } + + ~TestThriveGame() + { + delete ApplicationConfiguration; + } + + void + lightweightInit() + { + REQUIRE(createImpl()); + } +}; + +}} // namespace thrive::test