#include "BsEditorTestSuite.h" #include "BsSceneObject.h" #include "BsCmdRecordSO.h" #include "BsCmdDeleteSO.h" #include "BsUndoRedo.h" #include "BsRTTIType.h" #include "BsGameObjectRTTI.h" #include "BsBinarySerializer.h" #include "BsMemorySerializer.h" #include "BsBinaryDiff.h" #include "BsPrefab.h" #include "BsResources.h" #include "BsPrefabDiff.h" #include "BsFrameAlloc.h" namespace BansheeEngine { class TestComponentARTTI : public RTTIType { private: HSceneObject& getRef1(TestComponentA* obj) { return obj->ref1; } void setRef1(TestComponentA* obj, HSceneObject& val) { obj->ref1 = val; } HComponent& getRef2(TestComponentA* obj) { return obj->ref2; } void setRef2(TestComponentA* obj, HComponent& val) { obj->ref2 = val; } public: TestComponentARTTI() { addReflectableField("ref1", 0, &TestComponentARTTI::getRef1, &TestComponentARTTI::setRef1); addReflectableField("ref2", 1, &TestComponentARTTI::getRef2, &TestComponentARTTI::setRef2); } virtual const String& getRTTIName() { static String name = "TestComponentA"; return name; } virtual UINT32 getRTTIId() { return TID_TestComponentA; } virtual std::shared_ptr newRTTIObject() { return GameObjectRTTI::createGameObject(); } }; class TestComponentBRTTI : public RTTIType { private: HSceneObject& getRef1(TestComponentB* obj) { return obj->ref1; } void setRef1(TestComponentB* obj, HSceneObject& val) { obj->ref1 = val; } String& getVal1(TestComponentB* obj) { return obj->val1; } void setVal1(TestComponentB* obj, String& val) { obj->val1 = val; } public: TestComponentBRTTI() { addReflectableField("ref1", 0, &TestComponentBRTTI::getRef1, &TestComponentBRTTI::setRef1); addPlainField("val1", 1, &TestComponentBRTTI::getVal1, &TestComponentBRTTI::setVal1); } virtual const String& getRTTIName() { static String name = "TestComponentB"; return name; } virtual UINT32 getRTTIId() { return TID_TestComponentB; } virtual std::shared_ptr newRTTIObject() { return GameObjectRTTI::createGameObject(); } }; TestComponentA::TestComponentA(const HSceneObject& parent) :Component(parent) {} RTTITypeBase* TestComponentA::getRTTIStatic() { return TestComponentARTTI::instance(); } RTTITypeBase* TestComponentA::getRTTI() const { return TestComponentA::getRTTIStatic(); } TestComponentB::TestComponentB(const HSceneObject& parent) :Component(parent) {} RTTITypeBase* TestComponentB::getRTTIStatic() { return TestComponentBRTTI::instance(); } RTTITypeBase* TestComponentB::getRTTI() const { return TestComponentB::getRTTIStatic(); } struct TestObjectB : IReflectable { UINT32 intA = 100; String strA = "100"; /************************************************************************/ /* RTTI */ /************************************************************************/ public: friend class TestObjectBRTTI; static RTTITypeBase* getRTTIStatic(); virtual RTTITypeBase* getRTTI() const; }; struct TestObjectA : IReflectable { TestObjectA() { arrStrA = { "10", "11", "12" }; arrStrB = { "13", "14", "15" }; arrStrC = { "16", "17", "18" }; arrObjA = { TestObjectB(), TestObjectB(), TestObjectB() }; arrObjB = { TestObjectB(), TestObjectB(), TestObjectB() }; arrObjPtrA = { bs_shared_ptr_new(), bs_shared_ptr_new(), bs_shared_ptr_new() }; arrObjPtrB = { bs_shared_ptr_new(), bs_shared_ptr_new(), bs_shared_ptr_new() }; } UINT32 intA = 5; String strA = "5"; String strB = "7"; TestObjectB objA; TestObjectB objB; SPtr objPtrA = bs_shared_ptr_new(); SPtr objPtrB = bs_shared_ptr_new(); SPtr objPtrC = bs_shared_ptr_new(); SPtr objPtrD = nullptr; Vector arrStrA; Vector arrStrB; Vector arrStrC; Vector arrObjA; Vector arrObjB; Vector> arrObjPtrA; Vector> arrObjPtrB; /************************************************************************/ /* RTTI */ /************************************************************************/ public: friend class TestObjectARTTI; static RTTITypeBase* getRTTIStatic(); virtual RTTITypeBase* getRTTI() const; }; class TestObjectARTTI : public RTTIType < TestObjectA, IReflectable, TestObjectARTTI > { private: BS_PLAIN_MEMBER(intA); BS_PLAIN_MEMBER(strA); BS_PLAIN_MEMBER(strB); BS_REFL_MEMBER(objA); BS_REFL_MEMBER(objB); BS_REFLPTR_MEMBER(objPtrA); BS_REFLPTR_MEMBER(objPtrB); BS_REFLPTR_MEMBER(objPtrC); BS_REFLPTR_MEMBER(objPtrD); BS_PLAIN_MEMBER_VEC(arrStrA); BS_PLAIN_MEMBER_VEC(arrStrB); BS_PLAIN_MEMBER_VEC(arrStrC); BS_REFL_MEMBER_VEC(arrObjA); BS_REFL_MEMBER_VEC(arrObjB); BS_REFLPTR_MEMBER_VEC(arrObjPtrA); BS_REFLPTR_MEMBER_VEC(arrObjPtrB); public: TestObjectARTTI() { BS_ADD_PLAIN_FIELD(intA, 0); BS_ADD_PLAIN_FIELD(strA, 1); BS_ADD_PLAIN_FIELD(strB, 2); BS_ADD_REFL_FIELD(objA, 3); BS_ADD_REFL_FIELD(objB, 4); BS_ADD_REFLPTR_FIELD(objPtrA, 5); BS_ADD_REFLPTR_FIELD(objPtrB, 6); BS_ADD_REFLPTR_FIELD(objPtrC, 7); BS_ADD_REFLPTR_FIELD(objPtrD, 8); BS_ADD_PLAIN_FIELD_ARR(arrStrA, 9); BS_ADD_PLAIN_FIELD_ARR(arrStrB, 10); BS_ADD_PLAIN_FIELD_ARR(arrStrC, 11); BS_ADD_REFL_FIELD_ARR(arrObjA, 12); BS_ADD_REFL_FIELD_ARR(arrObjB, 13); BS_ADD_REFLPTR_FIELD_ARR(arrObjPtrA, 14); BS_ADD_REFLPTR_FIELD_ARR(arrObjPtrB, 15); } virtual const String& getRTTIName() { static String name = "TestObjectA"; return name; } virtual UINT32 getRTTIId() { return TID_TestObjectA; } virtual std::shared_ptr newRTTIObject() { return bs_shared_ptr_new(); } }; class TestObjectBRTTI : public RTTIType < TestObjectB, IReflectable, TestObjectBRTTI > { private: BS_PLAIN_MEMBER(intA); BS_PLAIN_MEMBER(strA); public: TestObjectBRTTI() { BS_ADD_PLAIN_FIELD(intA, 0); BS_ADD_PLAIN_FIELD(strA, 1); } virtual const String& getRTTIName() { static String name = "TestObjectB"; return name; } virtual UINT32 getRTTIId() { return TID_TestObjectB; } virtual std::shared_ptr newRTTIObject() { return bs_shared_ptr_new(); } }; RTTITypeBase* TestObjectB::getRTTIStatic() { return TestObjectBRTTI::instance(); } RTTITypeBase* TestObjectB::getRTTI() const { return TestObjectB::getRTTIStatic(); } RTTITypeBase* TestObjectA::getRTTIStatic() { return TestObjectARTTI::instance(); } RTTITypeBase* TestObjectA::getRTTI() const { return TestObjectA::getRTTIStatic(); } class TestComponentC : public Component { public: TestObjectA obj; /************************************************************************/ /* COMPONENT OVERRIDES */ /************************************************************************/ protected: friend class SceneObject; TestComponentC(const HSceneObject& parent) :Component(parent) {} /************************************************************************/ /* RTTI */ /************************************************************************/ public: friend class TestComponentCRTTI; static RTTITypeBase* getRTTIStatic(); virtual RTTITypeBase* getRTTI() const; protected: TestComponentC() {} // Serialization only }; class TestComponentD : public Component { public: TestObjectB obj; /************************************************************************/ /* COMPONENT OVERRIDES */ /************************************************************************/ protected: friend class SceneObject; TestComponentD(const HSceneObject& parent) :Component(parent) {} /************************************************************************/ /* RTTI */ /************************************************************************/ public: friend class TestComponentDRTTI; static RTTITypeBase* getRTTIStatic(); virtual RTTITypeBase* getRTTI() const; protected: TestComponentD() {} // Serialization only }; class TestComponentCRTTI : public RTTIType < TestComponentC, Component, TestComponentCRTTI > { private: BS_REFL_MEMBER(obj) public: TestComponentCRTTI() { BS_ADD_REFL_FIELD(obj, 0); } virtual const String& getRTTIName() { static String name = "TestComponentC"; return name; } virtual UINT32 getRTTIId() { return TID_TestComponentC; } virtual std::shared_ptr newRTTIObject() { return GameObjectRTTI::createGameObject(); } }; class TestComponentDRTTI : public RTTIType < TestComponentD, Component, TestComponentDRTTI > { private: BS_REFL_MEMBER(obj) public: TestComponentDRTTI() { BS_ADD_REFL_FIELD(obj, 0); } virtual const String& getRTTIName() { static String name = "TestComponentD"; return name; } virtual UINT32 getRTTIId() { return TID_TestComponentD; } virtual std::shared_ptr newRTTIObject() { return GameObjectRTTI::createGameObject(); } }; RTTITypeBase* TestComponentC::getRTTIStatic() { return TestComponentCRTTI::instance(); } RTTITypeBase* TestComponentC::getRTTI() const { return TestComponentC::getRTTIStatic(); } RTTITypeBase* TestComponentD::getRTTIStatic() { return TestComponentDRTTI::instance(); } RTTITypeBase* TestComponentD::getRTTI() const { return TestComponentD::getRTTIStatic(); } EditorTestSuite::EditorTestSuite() { BS_ADD_TEST(EditorTestSuite::SceneObjectRecord_UndoRedo); BS_ADD_TEST(EditorTestSuite::SceneObjectDelete_UndoRedo); BS_ADD_TEST(EditorTestSuite::BinaryDiff); BS_ADD_TEST(EditorTestSuite::TestPrefabDiff); BS_ADD_TEST(EditorTestSuite::TestFrameAlloc) } void EditorTestSuite::SceneObjectRecord_UndoRedo() { HSceneObject so0_0 = SceneObject::create("so0_0"); HSceneObject so1_0 = SceneObject::create("so1_0"); HSceneObject so1_1 = SceneObject::create("so1_1"); HSceneObject so2_0 = SceneObject::create("so2_0"); so1_0->setParent(so0_0); so1_1->setParent(so0_0); so2_0->setParent(so1_0); GameObjectHandle cmpA1_1 = so1_1->addComponent(); GameObjectHandle cmpB1_1 = so1_1->addComponent(); HSceneObject soExternal = SceneObject::create("soExternal"); GameObjectHandle cmpExternal = soExternal->addComponent(); cmpA1_1->ref1 = so2_0; cmpA1_1->ref2 = cmpB1_1; cmpB1_1->ref1 = soExternal; cmpB1_1->val1 = "InitialValue"; cmpExternal->ref1 = so1_1; cmpExternal->ref2 = cmpA1_1; CmdRecordSO::execute(so0_0); cmpB1_1->val1 = "ModifiedValue"; so0_0->setName("modified"); UndoRedo::instance().undo(); BS_TEST_ASSERT(!so0_0.isDestroyed()); BS_TEST_ASSERT(!so1_0.isDestroyed()); BS_TEST_ASSERT(!so1_1.isDestroyed()); BS_TEST_ASSERT(!so2_0.isDestroyed()); BS_TEST_ASSERT(!cmpA1_1.isDestroyed()); BS_TEST_ASSERT(!cmpB1_1.isDestroyed()); BS_TEST_ASSERT(!cmpA1_1->ref1.isDestroyed()); BS_TEST_ASSERT(!cmpA1_1->ref2.isDestroyed()); BS_TEST_ASSERT(!cmpB1_1->ref1.isDestroyed()); BS_TEST_ASSERT(!cmpExternal->ref1.isDestroyed()); BS_TEST_ASSERT(!cmpExternal->ref2.isDestroyed()); BS_TEST_ASSERT(cmpB1_1->val1 == "InitialValue"); BS_TEST_ASSERT(so0_0->getName() == "so0_0"); so0_0->destroy(); } void EditorTestSuite::SceneObjectDelete_UndoRedo() { HSceneObject so0_0 = SceneObject::create("so0_0"); HSceneObject so1_0 = SceneObject::create("so1_0"); HSceneObject so1_1 = SceneObject::create("so1_1"); HSceneObject so2_0 = SceneObject::create("so2_0"); so1_0->setParent(so0_0); so1_1->setParent(so0_0); so2_0->setParent(so1_0); GameObjectHandle cmpA1_1 = so1_1->addComponent(); GameObjectHandle cmpB1_1 = so1_1->addComponent(); HSceneObject soExternal = SceneObject::create("soExternal"); GameObjectHandle cmpExternal = soExternal->addComponent(); cmpA1_1->ref1 = so2_0; cmpA1_1->ref2 = cmpB1_1; cmpB1_1->ref1 = soExternal; cmpB1_1->val1 = "InitialValue"; cmpExternal->ref1 = so1_1; cmpExternal->ref2 = cmpA1_1; CmdDeleteSO::execute(so0_0); UndoRedo::instance().undo(); BS_TEST_ASSERT(!so0_0.isDestroyed()); BS_TEST_ASSERT(!so1_0.isDestroyed()); BS_TEST_ASSERT(!so1_1.isDestroyed()); BS_TEST_ASSERT(!so2_0.isDestroyed()); BS_TEST_ASSERT(!cmpA1_1.isDestroyed()); BS_TEST_ASSERT(!cmpB1_1.isDestroyed()); BS_TEST_ASSERT(!cmpA1_1->ref1.isDestroyed()); BS_TEST_ASSERT(!cmpA1_1->ref2.isDestroyed()); BS_TEST_ASSERT(!cmpB1_1->ref1.isDestroyed()); BS_TEST_ASSERT(!cmpExternal->ref1.isDestroyed()); BS_TEST_ASSERT(!cmpExternal->ref2.isDestroyed()); BS_TEST_ASSERT(cmpB1_1->val1 == "InitialValue"); so0_0->destroy(); } void EditorTestSuite::BinaryDiff() { SPtr orgObj = bs_shared_ptr_new(); SPtr newObj = bs_shared_ptr_new(); newObj->intA = 995; newObj->strA = "potato"; newObj->arrStrB = { "orange", "carrot" }; newObj->arrStrC[2] = "banana"; newObj->objB.intA = 9940; newObj->objPtrB->strA = "kiwi"; newObj->objPtrC = nullptr; newObj->objPtrD = bs_shared_ptr_new(); newObj->arrObjB[1].strA = "strawberry"; newObj->arrObjPtrB[0]->intA = 99100; MemorySerializer ms; UINT32 orgDataLength = 0; UINT8* orgData = ms.encode(orgObj.get(), orgDataLength, &bs_alloc); UINT32 newDataLength = 0; UINT8* newData = ms.encode(newObj.get(), newDataLength, &bs_alloc); BinarySerializer bs; SPtr orgSerialized = bs._decodeIntermediate(orgData, orgDataLength); SPtr newSerialized = bs._decodeIntermediate(newData, newDataLength); IDiff& diffHandler = orgObj->getRTTI()->getDiffHandler(); SPtr objDiff = diffHandler.generateDiff(orgSerialized, newSerialized); diffHandler.applyDiff(orgObj, objDiff); bs_free(orgData); bs_free(newData); BS_TEST_ASSERT(orgObj->intA == newObj->intA); BS_TEST_ASSERT(orgObj->strA == newObj->strA); BS_TEST_ASSERT(orgObj->strB == newObj->strB); BS_TEST_ASSERT(orgObj->objA.intA == newObj->objA.intA); BS_TEST_ASSERT(orgObj->objB.intA == newObj->objB.intA); BS_TEST_ASSERT(orgObj->objPtrA->strA == newObj->objPtrA->strA); BS_TEST_ASSERT(orgObj->objPtrB->strA == newObj->objPtrB->strA); BS_TEST_ASSERT(orgObj->objPtrD->strA == newObj->objPtrD->strA); BS_TEST_ASSERT(orgObj->objPtrC == newObj->objPtrC); BS_TEST_ASSERT(orgObj->arrStrA.size() == newObj->arrStrA.size()); for (UINT32 i = 0; i < (UINT32)orgObj->arrStrA.size(); i++) BS_TEST_ASSERT(orgObj->arrStrA[i] == newObj->arrStrA[i]); BS_TEST_ASSERT(orgObj->arrStrB.size() == newObj->arrStrB.size()); for (UINT32 i = 0; i < (UINT32)orgObj->arrStrB.size(); i++) BS_TEST_ASSERT(orgObj->arrStrB[i] == newObj->arrStrB[i]); BS_TEST_ASSERT(orgObj->arrStrC.size() == newObj->arrStrC.size()); for (UINT32 i = 0; i < (UINT32)orgObj->arrStrC.size(); i++) BS_TEST_ASSERT(orgObj->arrStrC[i] == newObj->arrStrC[i]); BS_TEST_ASSERT(orgObj->arrObjA.size() == newObj->arrObjA.size()); for (UINT32 i = 0; i < (UINT32)orgObj->arrObjA.size(); i++) BS_TEST_ASSERT(orgObj->arrObjA[i].strA == newObj->arrObjA[i].strA); BS_TEST_ASSERT(orgObj->arrObjB.size() == newObj->arrObjB.size()); for (UINT32 i = 0; i < (UINT32)orgObj->arrObjB.size(); i++) BS_TEST_ASSERT(orgObj->arrObjB[i].strA == newObj->arrObjB[i].strA); BS_TEST_ASSERT(orgObj->arrObjPtrA.size() == newObj->arrObjPtrA.size()); for (UINT32 i = 0; i < (UINT32)orgObj->arrObjPtrA.size(); i++) BS_TEST_ASSERT(orgObj->arrObjPtrA[i]->intA == newObj->arrObjPtrA[i]->intA); BS_TEST_ASSERT(orgObj->arrObjPtrB.size() == newObj->arrObjPtrB.size()); for (UINT32 i = 0; i < (UINT32)orgObj->arrObjPtrB.size(); i++) BS_TEST_ASSERT(orgObj->arrObjPtrB[i]->intA == newObj->arrObjPtrB[i]->intA); } void EditorTestSuite::TestPrefabDiff() { HSceneObject root = SceneObject::create("root"); HSceneObject so0 = SceneObject::create("so0"); HSceneObject so1 = SceneObject::create("so1"); HSceneObject so2 = SceneObject::create("so2"); HSceneObject so0_0 = SceneObject::create("so0_0"); HSceneObject so0_1 = SceneObject::create("so0_1"); HSceneObject so1_0 = SceneObject::create("so1_0"); HSceneObject so1_1 = SceneObject::create("so1_1"); HSceneObject so1_2 = SceneObject::create("so1_2"); HSceneObject so2_0 = SceneObject::create("so2_0"); so0->setParent(root); so1->setParent(root); so2->setParent(root); so0_0->setParent(so0); so0_1->setParent(so0); so1_0->setParent(so1); so1_1->setParent(so1); so1_2->setParent(so1); so2_0->setParent(so2); GameObjectHandle cmp0 = so0->addComponent(); GameObjectHandle cmp0_1_A = so0_1->addComponent(); GameObjectHandle cmp0_1_B = so0_1->addComponent(); GameObjectHandle cmp1 = so1->addComponent(); GameObjectHandle cmp1_2 = so1_2->addComponent(); GameObjectHandle cmp2 = so2->addComponent(); HPrefab prefab = Prefab::create(root); gResources().save(prefab, "C:\\testprefab.asset", true); // Perform modifications GameObjectHandle cmp1_3; GameObjectHandle cmp3; HSceneObject so1_3, so2_1, so3; { cmp0->obj.strA = "banana"; so0_0->destroy(); cmp0_1_A->destroy(); so1_3 = SceneObject::create("so1_2"); so1_3->setParent(so1); cmp1_3 = so1_3->addComponent(); cmp1_3->obj.intA = 999; so1_0->setName("apple"); so1_1->destroy(); cmp1_2->destroy(); cmp1_2 = so1_2->addComponent(); cmp1_2->obj.strA = "orange"; so2_1 = SceneObject::create("so2_1"); so2_1->setParent(so2); so2_0->addComponent(); so3 = SceneObject::create("so3"); so3->setParent(root); cmp3 = so3->addComponent(); } SPtr prefabDiff = PrefabDiff::create(prefab->getRoot(), root); prefab = gResources().load("C:\\testprefab.asset"); HSceneObject newRoot = prefab->instantiate(); prefabDiff->apply(newRoot); // Compare and assert BS_TEST_ASSERT(root->getNumChildren() == newRoot->getNumChildren()); HSceneObject nso0 = newRoot->getChild(0); GameObjectHandle ncmp0 = nso0->getComponent(); BS_TEST_ASSERT(cmp0->obj.strA == ncmp0->obj.strA); BS_TEST_ASSERT(so0->getNumChildren() == nso0->getNumChildren()); HSceneObject nso0_1 = nso0->getChild(0); GameObjectHandle ncmp0_1 = nso0_1->getComponent(); BS_TEST_ASSERT(ncmp0_1 != nullptr); HSceneObject nso1 = newRoot->getChild(1); BS_TEST_ASSERT(so1->getNumChildren() == nso1->getNumChildren()); HSceneObject nso1_0 = nso1->getChild(0); BS_TEST_ASSERT(so1_0->getName() == nso1_0->getName()); HSceneObject nso1_2 = nso1->getChild(1); GameObjectHandle ncmp1_2 = nso1_2->getComponent(); BS_TEST_ASSERT(cmp1_2->obj.strA == ncmp1_2->obj.strA); HSceneObject nso1_3 = nso1->getChild(2); GameObjectHandle ncmp1_3 = nso1_3->getComponent(); BS_TEST_ASSERT(cmp1_3->obj.intA == ncmp1_3->obj.intA); HSceneObject nso2 = newRoot->getChild(2); BS_TEST_ASSERT(so2->getNumChildren() == nso2->getNumChildren()); HSceneObject nso2_0 = nso2->getChild(0); GameObjectHandle ncmp2_0 = nso2_0->getComponent(); BS_TEST_ASSERT(ncmp2_0 != nullptr); HSceneObject nso3 = newRoot->getChild(3); GameObjectHandle ncmp3 = nso3->getComponent(); BS_TEST_ASSERT(ncmp3 != nullptr); root->destroy(); } void EditorTestSuite::TestFrameAlloc() { FrameAlloc alloc(128); alloc.markFrame(); UINT8* a1 = alloc.alloc(5); UINT8* a2 = alloc.alloc(10); UINT8* a3 = alloc.alloc(130); UINT8* a4 = alloc.alloc(5); alloc.dealloc(a1); alloc.dealloc(a2); alloc.dealloc(a3); alloc.dealloc(a4); alloc.clear(); alloc.markFrame(); UINT8* a5 = alloc.alloc(5); UINT8* a6 = alloc.alloc(10); UINT8* a7 = alloc.alloc(130); UINT8* a8 = alloc.alloc(5); alloc.dealloc(a5); alloc.dealloc(a6); alloc.dealloc(a7); alloc.dealloc(a8); alloc.markFrame(); UINT8* a9 = alloc.alloc(5); UINT8* a10 = alloc.alloc(10); UINT8* a11 = alloc.alloc(130); UINT8* a12 = alloc.alloc(5); alloc.dealloc(a9); alloc.dealloc(a10); alloc.dealloc(a11); alloc.dealloc(a12); alloc.clear(); alloc.clear(); UINT8* a13 = alloc.alloc(5); alloc.dealloc(a13); alloc.clear(); } }