LCOV - code coverage report
Current view: top level - source/simulation2/system - ComponentTest.h (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 72 93 77.4 %
Date: 2023-01-19 00:18:29 Functions: 15 30 50.0 %

          Line data    Source code
       1             : /* Copyright (C) 2022 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #ifndef INCLUDED_COMPONENTTEST
      19             : #define INCLUDED_COMPONENTTEST
      20             : 
      21             : #include "lib/self_test.h"
      22             : 
      23             : #include "maths/Vector3D.h"
      24             : #include "ps/XML/Xeromyces.h"
      25             : #include "simulation2/MessageTypes.h"
      26             : #include "simulation2/system/Component.h"
      27             : #include "simulation2/components/ICmpTerrain.h"
      28             : #include "simulation2/serialization/DebugSerializer.h"
      29             : #include "simulation2/serialization/HashSerializer.h"
      30             : #include "simulation2/serialization/StdSerializer.h"
      31             : #include "simulation2/serialization/StdDeserializer.h"
      32             : 
      33             : #include <iostream>
      34             : 
      35             : /**
      36             :  * @file
      37             :  * Various common features for component test cases.
      38             :  */
      39             : 
      40             : /**
      41             :  * Class to test a single component.
      42             :  * - Create an instance of this class
      43             :  * - Use AddMock to add mock components that the tested component relies on
      44             :  * - Use Add to add the test component itself, and it returns a component pointer
      45             :  * - Call methods on the component pointer
      46             :  * - Use Roundtrip to test the consistency of serialization
      47             :  */
      48          22 : class ComponentTestHelper
      49             : {
      50             :     CSimContext m_Context;
      51             :     CComponentManager m_ComponentManager;
      52             :     CParamNode m_Param;
      53             :     IComponent* m_Cmp;
      54             :     EComponentTypeId m_Cid;
      55             :     bool m_isSystemEntityInit = false;
      56             : 
      57             : public:
      58          22 :     ComponentTestHelper(std::shared_ptr<ScriptContext> scriptContext) :
      59          22 :         m_Context(), m_ComponentManager(m_Context, scriptContext), m_Cmp(NULL)
      60             :     {
      61          22 :         m_ComponentManager.LoadComponentTypes();
      62          22 :     }
      63             : 
      64          46 :     const ScriptInterface& GetScriptInterface()
      65             :     {
      66          46 :         return m_ComponentManager.GetScriptInterface();
      67             :     }
      68             : 
      69             :     CSimContext& GetSimContext()
      70             :     {
      71             :         return m_Context;
      72             :     }
      73             : 
      74             :     /**
      75             :      * Call this once to initialise the test helper with a component.
      76             :      */
      77             :     template<typename T>
      78          17 :     T* Add(EComponentTypeId cid, const std::string& xml, entity_id_t ent = 10)
      79             :     {
      80          17 :         TS_ASSERT(m_Cmp == NULL);
      81             : 
      82          17 :         CEntityHandle handle;
      83          17 :         if (ent == SYSTEM_ENTITY)
      84             :         {
      85          14 :             if (!m_isSystemEntityInit)
      86             :             {
      87          14 :                 m_ComponentManager.InitSystemEntity();
      88          14 :                 m_isSystemEntityInit = true;
      89             :             }
      90          14 :             handle = m_ComponentManager.GetSystemEntity();
      91          14 :             m_Context.SetSystemEntity(handle);
      92             :         }
      93             :         else
      94           3 :             handle = m_ComponentManager.LookupEntityHandle(ent, true);
      95             : 
      96          17 :         m_Cid = cid;
      97          17 :         TS_ASSERT_EQUALS(CParamNode::LoadXMLString(m_Param, ("<test>" + xml + "</test>").c_str()), PSRETURN_OK);
      98          17 :         TS_ASSERT(m_ComponentManager.AddComponent(handle, m_Cid, m_Param.GetChild("test")));
      99          17 :         m_Cmp = m_ComponentManager.QueryInterface(ent, T::GetInterfaceId());
     100          17 :         TS_ASSERT(m_Cmp != NULL);
     101          17 :         return static_cast<T*> (m_Cmp);
     102             :     }
     103             : 
     104          19 :     void AddMock(entity_id_t ent, EInterfaceId iid, IComponent& component)
     105             :     {
     106          19 :         CEntityHandle handle;
     107          19 :         if (ent == SYSTEM_ENTITY)
     108             :         {
     109           5 :             if (!m_isSystemEntityInit)
     110             :             {
     111           3 :                 m_ComponentManager.InitSystemEntity();
     112           3 :                 m_isSystemEntityInit = true;
     113             :             }
     114           5 :             handle = m_ComponentManager.GetSystemEntity();
     115           5 :             m_Context.SetSystemEntity(handle);
     116             :         }
     117             :         else
     118          14 :             handle = m_ComponentManager.LookupEntityHandle(ent, true);
     119             : 
     120          19 :         m_ComponentManager.AddMockComponent(handle, iid, component);
     121          19 :     }
     122             : 
     123           1 :     void HandleMessage(IComponent* cmp, const CMessage& msg, bool global)
     124             :     {
     125           1 :         cmp->HandleMessage(msg, global);
     126           1 :     }
     127             : 
     128             :     /**
     129             :      * Checks that the object roundtrips through its serialize/deserialize functions correctly.
     130             :      * Computes the debug output, hash, and binary serialization; then deserializes into a new
     131             :      * system and checks the serialization outputs are unchanged.
     132             :      */
     133           5 :     void Roundtrip(bool verbose = false)
     134             :     {
     135          10 :         std::stringstream dbgstr1;
     136          10 :         CDebugSerializer dbg1(GetScriptInterface(), dbgstr1);
     137           5 :         m_Cmp->Serialize(dbg1);
     138             : 
     139           5 :         if (verbose)
     140           0 :             std::cout << "--------\n" << dbgstr1.str() << "--------\n";
     141             : 
     142          10 :         CHashSerializer hash1(GetScriptInterface());
     143           5 :         m_Cmp->Serialize(hash1);
     144             : 
     145          10 :         std::stringstream stdstr1;
     146          10 :         CStdSerializer std1(GetScriptInterface(), stdstr1);
     147           5 :         m_Cmp->Serialize(std1);
     148             : 
     149          10 :         ComponentTestHelper test2(GetScriptInterface().GetContext());
     150             :         // (We should never need to add any mock objects etc to test2, since deserialization
     151             :         // mustn't depend on other components already existing)
     152             : 
     153           5 :         CEntityHandle ent = test2.m_ComponentManager.LookupEntityHandle(10, true);
     154             : 
     155          10 :         CStdDeserializer stdde2(test2.GetScriptInterface(), stdstr1);
     156             : 
     157           5 :         IComponent* cmp2 = test2.m_ComponentManager.ConstructComponent(ent, m_Cid);
     158           5 :         cmp2->Deserialize(m_Param.GetChild("test"), stdde2);
     159             : 
     160           5 :         TS_ASSERT(stdstr1.peek() == EOF); // Deserialize must read whole stream
     161             : 
     162          10 :         std::stringstream dbgstr2;
     163          10 :         CDebugSerializer dbg2(test2.GetScriptInterface(), dbgstr2);
     164           5 :         cmp2->Serialize(dbg2);
     165             : 
     166           5 :         if (verbose)
     167           0 :             std::cout << "--------\n" << dbgstr2.str() << "--------\n";
     168             : 
     169          10 :         CHashSerializer hash2(test2.GetScriptInterface());
     170           5 :         cmp2->Serialize(hash2);
     171             : 
     172          10 :         std::stringstream stdstr2;
     173          10 :         CStdSerializer std2(test2.GetScriptInterface(), stdstr2);
     174           5 :         cmp2->Serialize(std2);
     175             : 
     176           5 :         TS_ASSERT_EQUALS(dbgstr1.str(), dbgstr2.str());
     177             : 
     178           5 :         TS_ASSERT_EQUALS(hash1.GetHashLength(), hash2.GetHashLength());
     179           5 :         TS_ASSERT_SAME_DATA(hash1.ComputeHash(), hash2.ComputeHash(), hash1.GetHashLength());
     180             : 
     181           5 :         TS_ASSERT_EQUALS(stdstr1.str(), stdstr2.str());
     182             : 
     183             :         // TODO: need to extend this so callers can run methods on the cloned component
     184             :         // to check that all its data is still correct
     185           5 :     }
     186             : };
     187             : 
     188             : /**
     189             :  * Simple terrain implementation with constant height of 50.
     190             :  */
     191           6 : class MockTerrain : public ICmpTerrain
     192             : {
     193             : public:
     194           0 :     DEFAULT_MOCK_COMPONENT()
     195             : 
     196           0 :     bool IsLoaded() const override
     197             :     {
     198           0 :         return true;
     199             :     }
     200             : 
     201           0 :     CFixedVector3D CalcNormal(entity_pos_t UNUSED(x), entity_pos_t UNUSED(z)) const override
     202             :     {
     203           0 :         return CFixedVector3D(fixed::FromInt(0), fixed::FromInt(1), fixed::FromInt(0));
     204             :     }
     205             : 
     206           0 :     CVector3D CalcExactNormal(float UNUSED(x), float UNUSED(z)) const override
     207             :     {
     208           0 :         return CVector3D(0.f, 1.f, 0.f);
     209             :     }
     210             : 
     211          18 :     entity_pos_t GetGroundLevel(entity_pos_t UNUSED(x), entity_pos_t UNUSED(z)) const override
     212             :     {
     213          18 :         return entity_pos_t::FromInt(50);
     214             :     }
     215             : 
     216          43 :     float GetExactGroundLevel(float UNUSED(x), float UNUSED(z)) const override
     217             :     {
     218          43 :         return 50.f;
     219             :     }
     220             : 
     221           0 :     u16 GetTilesPerSide() const override
     222             :     {
     223           0 :         return 16;
     224             :     }
     225             : 
     226           0 :     u32 GetMapSize() const override
     227             :     {
     228           0 :         return GetTilesPerSide() * TERRAIN_TILE_SIZE;
     229             :     }
     230             : 
     231           0 :     u16 GetVerticesPerSide() const override
     232             :     {
     233           0 :         return 17;
     234             :     }
     235             : 
     236           0 :     CTerrain* GetCTerrain() override
     237             :     {
     238           0 :         return nullptr;
     239             :     }
     240             : 
     241           0 :     void MakeDirty(i32 UNUSED(i0), i32 UNUSED(j0), i32 UNUSED(i1), i32 UNUSED(j1)) override
     242             :     {
     243           0 :     }
     244             : 
     245           0 :     void ReloadTerrain(bool UNUSED(ReloadWater)) override
     246             :     {
     247           0 :     }
     248             : };
     249             : 
     250             : #endif // INCLUDED_COMPONENTTEST

Generated by: LCOV version 1.13