#include #include #include #include "core/command/Functor.h" namespace orxonox { namespace { // Fixture class FunctorTest : public ::testing::Test { public: virtual void SetUp() override { } virtual void TearDown() override { } }; class DestroyableClass : public Destroyable { public: virtual void method0() { return; } virtual int method1(int arg1) { return arg1; } virtual float method2(int arg1, float arg2) { return arg2; } virtual bool method3(int arg1, float arg2, bool arg3) { return arg3; } virtual std::string method4(int arg1, float arg2, bool arg3, const std::string& arg4) { return arg4; } virtual Vector3 method5(int arg1, float arg2, bool arg3, const std::string& arg4, const Vector3& arg5) { return arg5; } static void staticmethod0() { return staticinstance->method0(); } static int staticmethod1(int arg1) { return staticinstance->method1(arg1); } static float staticmethod2(int arg1, float arg2) { return staticinstance->method2(arg1, arg2); } static bool staticmethod3(int arg1, float arg2, bool arg3) { return staticinstance->method3(arg1, arg2, arg3); } static std::string staticmethod4(int arg1, float arg2, bool arg3, const std::string& arg4) { return staticinstance->method4(arg1, arg2, arg3, arg4); } static Vector3 staticmethod5(int arg1, float arg2, bool arg3, const std::string& arg4, const Vector3& arg5) { return staticinstance->method5(arg1, arg2, arg3, arg4, arg5); } static DestroyableClass* staticinstance; }; DestroyableClass* DestroyableClass::staticinstance = nullptr; class MockDestroyableClass : public DestroyableClass { public: MOCK_METHOD0(method0, void()); MOCK_METHOD1(method1, int(int)); MOCK_METHOD2(method2, float(int, float)); MOCK_METHOD3(method3, bool(int, float, bool)); MOCK_METHOD4(method4, std::string(int, float, bool, const std::string&)); MOCK_METHOD5(method5, Vector3(int, float, bool, const std::string&, const Vector3&)); }; class NonDestroyableClass { public: void method() {} }; void f1(int, double) {} void f2(int, double, double) {} int f3(int, double, double) { return 0; } int f4a(int arg) { return arg*2; } int f4b(int arg) { return arg*3; } int multiply(int a, int b) { return a*b; } struct CallableStruct { public: int operator()() { return 1; } }; } /////////////////////////////////////////////////////////////// ////////////////////// Functor (general) ////////////////////// /////////////////////////////////////////////////////////////// TEST_F(FunctorTest, HasCorrectType) { FunctorMemberPtr functor1 = createFunctor(&DestroyableClass::method0); FunctorStaticPtr functor2 = createFunctor(&DestroyableClass::staticmethod0); EXPECT_EQ(Functor::Type::Member, functor1->getType()); EXPECT_EQ(Functor::Type::Static, functor2->getType()); } TEST_F(FunctorTest, ReturnsCorrectParamCount) { EXPECT_EQ(0u, createFunctor(&DestroyableClass::method0)->getParamCount()); EXPECT_EQ(1u, createFunctor(&DestroyableClass::method1)->getParamCount()); EXPECT_EQ(2u, createFunctor(&DestroyableClass::method2)->getParamCount()); EXPECT_EQ(3u, createFunctor(&DestroyableClass::method3)->getParamCount()); EXPECT_EQ(4u, createFunctor(&DestroyableClass::method4)->getParamCount()); EXPECT_EQ(5u, createFunctor(&DestroyableClass::method5)->getParamCount()); } TEST_F(FunctorTest, HasReturnValue) { EXPECT_FALSE(createFunctor(&DestroyableClass::method0)->hasReturnvalue()); EXPECT_TRUE(createFunctor(&DestroyableClass::method1)->hasReturnvalue()); EXPECT_TRUE(createFunctor(&DestroyableClass::method2)->hasReturnvalue()); EXPECT_TRUE(createFunctor(&DestroyableClass::method3)->hasReturnvalue()); EXPECT_TRUE(createFunctor(&DestroyableClass::method4)->hasReturnvalue()); EXPECT_TRUE(createFunctor(&DestroyableClass::method5)->hasReturnvalue()); } TEST_F(FunctorTest, GetTypenameParam) { FunctorPtr functor = createFunctor(&DestroyableClass::method5); EXPECT_EQ("int", functor->getTypenameParam(0)); EXPECT_EQ("float", functor->getTypenameParam(1)); EXPECT_EQ("bool", functor->getTypenameParam(2)); EXPECT_EQ("string", functor->getTypenameParam(3)); EXPECT_EQ("Vector3", functor->getTypenameParam(4)); EXPECT_EQ("", functor->getTypenameParam(5)); // max 5 arguments supported (index 0-4) -> this returns empty string } TEST_F(FunctorTest, TypenameParamIsAlwaysVoidForFunctionWithoutParams) { FunctorPtr functor = createFunctor(&DestroyableClass::method0); EXPECT_EQ("void", functor->getTypenameParam(0)); EXPECT_EQ("void", functor->getTypenameParam(1)); EXPECT_EQ("void", functor->getTypenameParam(2)); EXPECT_EQ("void", functor->getTypenameParam(3)); EXPECT_EQ("void", functor->getTypenameParam(4)); EXPECT_EQ("", functor->getTypenameParam(5)); // max 5 arguments supported (index 0-4) -> this returns empty string } TEST_F(FunctorTest, GetTypenameReturnvalue) { EXPECT_EQ("void", createFunctor(&DestroyableClass::method0)->getTypenameReturnvalue()); EXPECT_EQ("int", createFunctor(&DestroyableClass::method1)->getTypenameReturnvalue()); EXPECT_EQ("float", createFunctor(&DestroyableClass::method2)->getTypenameReturnvalue()); EXPECT_EQ("bool", createFunctor(&DestroyableClass::method3)->getTypenameReturnvalue()); EXPECT_EQ("string", createFunctor(&DestroyableClass::method4)->getTypenameReturnvalue()); EXPECT_EQ("Vector3", createFunctor(&DestroyableClass::method5)->getTypenameReturnvalue()); } TEST_F(FunctorTest, GetFullIdentifier) { // static EXPECT_EQ(typeid( void(*)()), createFunctor(&DestroyableClass::staticmethod0)->getFullIdentifier()); EXPECT_EQ(typeid( int(*)(int)), createFunctor(&DestroyableClass::staticmethod1)->getFullIdentifier()); EXPECT_EQ(typeid( float(*)(int, float)), createFunctor(&DestroyableClass::staticmethod2)->getFullIdentifier()); EXPECT_EQ(typeid( bool(*)(int, float, bool)), createFunctor(&DestroyableClass::staticmethod3)->getFullIdentifier()); EXPECT_EQ(typeid(std::string(*)(int, float, bool, const std::string&)), createFunctor(&DestroyableClass::staticmethod4)->getFullIdentifier()); EXPECT_EQ(typeid( Vector3(*)(int, float, bool, const std::string&, const Vector3&)), createFunctor(&DestroyableClass::staticmethod5)->getFullIdentifier()); // member EXPECT_EQ(typeid( void(DestroyableClass::*)()), createFunctor(&DestroyableClass::method0)->getFullIdentifier()); EXPECT_EQ(typeid( int(DestroyableClass::*)(int)), createFunctor(&DestroyableClass::method1)->getFullIdentifier()); EXPECT_EQ(typeid( float(DestroyableClass::*)(int, float)), createFunctor(&DestroyableClass::method2)->getFullIdentifier()); EXPECT_EQ(typeid( bool(DestroyableClass::*)(int, float, bool)), createFunctor(&DestroyableClass::method3)->getFullIdentifier()); EXPECT_EQ(typeid(std::string(DestroyableClass::*)(int, float, bool, const std::string&)), createFunctor(&DestroyableClass::method4)->getFullIdentifier()); EXPECT_EQ(typeid( Vector3(DestroyableClass::*)(int, float, bool, const std::string&, const Vector3&)), createFunctor(&DestroyableClass::method5)->getFullIdentifier()); } TEST_F(FunctorTest, GetHeaderIdentifier) { // static EXPECT_EQ(typeid( void(*)()), createFunctor(&DestroyableClass::staticmethod0)->getHeaderIdentifier()); EXPECT_EQ(typeid( int(*)(int)), createFunctor(&DestroyableClass::staticmethod1)->getHeaderIdentifier()); EXPECT_EQ(typeid( float(*)(int, float)), createFunctor(&DestroyableClass::staticmethod2)->getHeaderIdentifier()); EXPECT_EQ(typeid( bool(*)(int, float, bool)), createFunctor(&DestroyableClass::staticmethod3)->getHeaderIdentifier()); EXPECT_EQ(typeid(std::string(*)(int, float, bool, const std::string&)), createFunctor(&DestroyableClass::staticmethod4)->getHeaderIdentifier()); EXPECT_EQ(typeid( Vector3(*)(int, float, bool, const std::string&, const Vector3&)), createFunctor(&DestroyableClass::staticmethod5)->getHeaderIdentifier()); // member EXPECT_EQ(typeid( void(*)()), createFunctor(&DestroyableClass::method0)->getHeaderIdentifier()); EXPECT_EQ(typeid( int(*)(int)), createFunctor(&DestroyableClass::method1)->getHeaderIdentifier()); EXPECT_EQ(typeid( float(*)(int, float)), createFunctor(&DestroyableClass::method2)->getHeaderIdentifier()); EXPECT_EQ(typeid( bool(*)(int, float, bool)), createFunctor(&DestroyableClass::method3)->getHeaderIdentifier()); EXPECT_EQ(typeid(std::string(*)(int, float, bool, const std::string&)), createFunctor(&DestroyableClass::method4)->getHeaderIdentifier()); EXPECT_EQ(typeid( Vector3(*)(int, float, bool, const std::string&, const Vector3&)), createFunctor(&DestroyableClass::method5)->getHeaderIdentifier()); } TEST_F(FunctorTest, GetHeaderIdentifierFor2Params) { // static EXPECT_EQ(typeid( void(*)()), createFunctor(&DestroyableClass::staticmethod0)->getHeaderIdentifier(2)); EXPECT_EQ(typeid( int(*)(int)), createFunctor(&DestroyableClass::staticmethod1)->getHeaderIdentifier(2)); EXPECT_EQ(typeid( float(*)(int, float)), createFunctor(&DestroyableClass::staticmethod2)->getHeaderIdentifier(2)); EXPECT_EQ(typeid( bool(*)(int, float)), createFunctor(&DestroyableClass::staticmethod3)->getHeaderIdentifier(2)); EXPECT_EQ(typeid(std::string(*)(int, float)), createFunctor(&DestroyableClass::staticmethod4)->getHeaderIdentifier(2)); EXPECT_EQ(typeid( Vector3(*)(int, float)), createFunctor(&DestroyableClass::staticmethod5)->getHeaderIdentifier(2)); // member EXPECT_EQ(typeid( void(*)()), createFunctor(&DestroyableClass::method0)->getHeaderIdentifier(2)); EXPECT_EQ(typeid( int(*)(int)), createFunctor(&DestroyableClass::method1)->getHeaderIdentifier(2)); EXPECT_EQ(typeid( float(*)(int, float)), createFunctor(&DestroyableClass::method2)->getHeaderIdentifier(2)); EXPECT_EQ(typeid( bool(*)(int, float)), createFunctor(&DestroyableClass::method3)->getHeaderIdentifier(2)); EXPECT_EQ(typeid(std::string(*)(int, float)), createFunctor(&DestroyableClass::method4)->getHeaderIdentifier(2)); EXPECT_EQ(typeid( Vector3(*)(int, float)), createFunctor(&DestroyableClass::method5)->getHeaderIdentifier(2)); } TEST_F(FunctorTest, GetHeaderIdentifierForNParams) { // static FunctorStaticPtr functorStatic = createFunctor(&DestroyableClass::staticmethod5); EXPECT_EQ(typeid(Vector3(*)()), functorStatic->getHeaderIdentifier(0)); EXPECT_EQ(typeid(Vector3(*)(int)), functorStatic->getHeaderIdentifier(1)); EXPECT_EQ(typeid(Vector3(*)(int, float)), functorStatic->getHeaderIdentifier(2)); EXPECT_EQ(typeid(Vector3(*)(int, float, bool)), functorStatic->getHeaderIdentifier(3)); EXPECT_EQ(typeid(Vector3(*)(int, float, bool, const std::string&)), functorStatic->getHeaderIdentifier(4)); EXPECT_EQ(typeid(Vector3(*)(int, float, bool, const std::string&, const Vector3&)), functorStatic->getHeaderIdentifier(5)); EXPECT_EQ(typeid(Vector3(*)(int, float, bool, const std::string&, const Vector3&)), functorStatic->getHeaderIdentifier(6)); // member FunctorMemberPtr functorMember = createFunctor(&DestroyableClass::method5); EXPECT_EQ(typeid(Vector3(*)()), functorMember->getHeaderIdentifier(0)); EXPECT_EQ(typeid(Vector3(*)(int)), functorMember->getHeaderIdentifier(1)); EXPECT_EQ(typeid(Vector3(*)(int, float)), functorMember->getHeaderIdentifier(2)); EXPECT_EQ(typeid(Vector3(*)(int, float, bool)), functorMember->getHeaderIdentifier(3)); EXPECT_EQ(typeid(Vector3(*)(int, float, bool, const std::string&)), functorMember->getHeaderIdentifier(4)); EXPECT_EQ(typeid(Vector3(*)(int, float, bool, const std::string&, const Vector3&)), functorMember->getHeaderIdentifier(5)); EXPECT_EQ(typeid(Vector3(*)(int, float, bool, const std::string&, const Vector3&)), functorMember->getHeaderIdentifier(6)); } TEST_F(FunctorTest, CanClone) { FunctorPtr functor = createFunctor(&f4a); FunctorPtr clone = functor->clone(); EXPECT_NE(nullptr, clone.get()); } TEST_F(FunctorTest, ClonedFunctorCanBeCalled) { FunctorPtr functor = createFunctor(&f4a); FunctorPtr clone = functor->clone(); EXPECT_EQ(8, (*functor)(4).get()); EXPECT_EQ(8, (*clone)(4).get()); } TEST_F(FunctorTest, ClonedFunctorKeepsFunction) { FunctorPtr functor = createFunctor(&f4a); FunctorPointer* pointer = static_cast*>(functor.get()); FunctorPtr clone = functor->clone(); FunctorPointer* clonePointer = static_cast*>(clone.get()); EXPECT_EQ(&f4a, pointer->getFunction()); EXPECT_EQ(&f4a, clonePointer->getFunction()); } TEST_F(FunctorTest, ClonedFunctorKeepsObject) { DestroyableClass object; FunctorPtr functor = createFunctor(&DestroyableClass::method0, &object); FunctorPtr clone = functor->clone(); EXPECT_EQ(&object, functor->getRawObjectPointer()); EXPECT_EQ(&object, clone->getRawObjectPointer()); } // TODO: test fails... fix or delete clone() // TEST_F(FunctorTest, ClonedFunctorKeepsSafeMode) // { // DestroyableClass* object = new DestroyableClass(); // FunctorPtr functor = createFunctor(&DestroyableClass::method0, object); // functor->setSafeMode(true); // FunctorPtr clone = functor->clone(); // // EXPECT_EQ(object, functor->getRawObjectPointer()); // EXPECT_EQ(object, clone->getRawObjectPointer()); // object->destroy(); // EXPECT_EQ(nullptr, functor->getRawObjectPointer()); // EXPECT_EQ(nullptr, clone->getRawObjectPointer()); // // } TEST_F(FunctorTest, CanEvaluateArgument) { FunctorPtr functor = createFunctor(&f4a); MultiType value("5"); EXPECT_FALSE(value.isType()); EXPECT_TRUE(value.isType()); functor->evaluateArgument(0, value); EXPECT_TRUE(value.isType()); EXPECT_FALSE(value.isType()); } TEST_F(FunctorTest, CanGetAndSetFunctionPointer) { FunctorPtr functor = createFunctor(&f4a); FunctorPointer* pointer = static_cast*>(functor.get()); EXPECT_EQ(6, (*functor)(3).get()); // 3*2 = 6 EXPECT_EQ(&f4a, pointer->getFunction()); pointer->setFunction(&f4b); EXPECT_EQ(9, (*functor)(3).get()); // 3*3 = 9 EXPECT_EQ(&f4b, pointer->getFunction()); } TEST_F(FunctorTest, ReturnsValue) { DestroyableClass object; { FunctorMemberPtr functor = createFunctor(&DestroyableClass::method1); int result = (*functor)(&object, 13); EXPECT_EQ(13, result); } { FunctorMemberPtr functor = createFunctor(&DestroyableClass::method2); float result = (*functor)(&object, 0, 111.11f); EXPECT_FLOAT_EQ(111.11f, result); } { FunctorMemberPtr functor = createFunctor(&DestroyableClass::method3); bool result = (*functor)(&object, 0, 0, true); EXPECT_EQ(true, result); } { FunctorMemberPtr functor = createFunctor(&DestroyableClass::method4); std::string result = (*functor)(&object, 0, 0, false, "test"); EXPECT_EQ("test", result); } { FunctorMemberPtr functor = createFunctor(&DestroyableClass::method5); Vector3 result = (*functor)(&object, 0, 0, false, "", Vector3(3, 2, 1)); EXPECT_EQ(Vector3(3, 2, 1), result); } } /////////////////////////////////////////////////////////// ////////////////////// FunctorMember ////////////////////// /////////////////////////////////////////////////////////// TEST_F(FunctorTest, FunctorMember_CanSetObject) { DestroyableClass object; FunctorMemberPtr functor = createFunctor(&DestroyableClass::method0); EXPECT_EQ(nullptr, functor->getObject()); functor->setObject(&object); EXPECT_EQ(&object, functor->getObject()); functor->setObject(nullptr); EXPECT_EQ(nullptr, functor->getObject()); } TEST_F(FunctorTest, FunctorMember_CanSetObjectInConstructor) { DestroyableClass object; FunctorMemberPtr functor = createFunctor(&DestroyableClass::method0, &object); EXPECT_EQ(&object, functor->getObject()); functor->setObject(nullptr); EXPECT_EQ(nullptr, functor->getObject()); } TEST_F(FunctorTest, FunctorMember_CanSetRawObjectPointer) { DestroyableClass object; FunctorMemberPtr functor = createFunctor(&DestroyableClass::method0); EXPECT_EQ(nullptr, functor->getRawObjectPointer()); functor->setRawObjectPointer(&object); EXPECT_EQ(&object, functor->getRawObjectPointer()); functor->setRawObjectPointer(nullptr); EXPECT_EQ(nullptr, functor->getRawObjectPointer()); } TEST_F(FunctorTest, FunctorMember_RawObjectPointerAndObjectAreEqual) { DestroyableClass object1; DestroyableClass object2; FunctorMemberPtr functor = createFunctor(&DestroyableClass::method0); EXPECT_EQ(nullptr, functor->getRawObjectPointer()); EXPECT_EQ(nullptr, functor->getObject()); functor->setObject(&object1); EXPECT_EQ(&object1, functor->getRawObjectPointer()); EXPECT_EQ(&object1, functor->getObject()); functor->setObject(&object2); EXPECT_EQ(&object2, functor->getRawObjectPointer()); EXPECT_EQ(&object2, functor->getObject()); } TEST_F(FunctorTest, FunctorMember_SafeModeWorks_SetObjectThenActivateSafeMode) { FunctorMemberPtr functor = createFunctor(&DestroyableClass::method0); { DestroyableClass object; functor->setObject(&object); functor->setSafeMode(true); EXPECT_EQ(&object, functor->getObject()); } EXPECT_EQ(nullptr, functor->getObject()); } TEST_F(FunctorTest, FunctorMember_SafeModeWorks_ActivateSafeModeThenSetObject) { FunctorMemberPtr functor = createFunctor(&DestroyableClass::method0); { DestroyableClass object; functor->setSafeMode(true); functor->setObject(&object); EXPECT_EQ(&object, functor->getObject()); } EXPECT_EQ(nullptr, functor->getObject()); } TEST_F(FunctorTest, FunctorMember_SafeModeCanBeDisabledAfterObjectWasSet) { DestroyableClass* ptr; FunctorMemberPtr functor = createFunctor(&DestroyableClass::method0); { DestroyableClass object; functor->setSafeMode(true); functor->setObject(&object); functor->setSafeMode(false); ptr = &object; // remember the pointer EXPECT_EQ(ptr, functor->getObject()); } EXPECT_EQ(ptr, functor->getObject()); } TEST_F(FunctorTest, FunctorMember_SafeModeWorks_CanChangeObject) { FunctorMemberPtr functor = createFunctor(&DestroyableClass::method0); DestroyableClass* object1 = new DestroyableClass(); DestroyableClass* object2 = new DestroyableClass(); functor->setSafeMode(true); functor->setObject(object1); EXPECT_EQ(object1, functor->getObject()); functor->setObject(object2); EXPECT_EQ(object2, functor->getObject()); object1->destroy(); EXPECT_EQ(object2, functor->getObject()); object2->destroy(); EXPECT_EQ(nullptr, functor->getObject()); } TEST_F(FunctorTest, FunctorMember_CanDestroyFunctorWhenObjectInSafeModeStillExists) { DestroyableClass object; { FunctorMemberPtr functor = createFunctor(&DestroyableClass::method0); functor->setSafeMode(true); functor->setObject(&object); EXPECT_EQ(&object, functor->getObject()); } // functor is destroyed here } TEST_F(FunctorTest, FunctorMember_SafeModeDoesntDoAnythingWithObjectsNotInheritedFromDestroyable) { NonDestroyableClass* ptr; FunctorMemberPtr functor = createFunctor(&NonDestroyableClass::method); { NonDestroyableClass object; functor->setSafeMode(true); functor->setObject(&object); ptr = &object; // remember the pointer EXPECT_EQ(ptr, functor->getObject()); } EXPECT_EQ(ptr, functor->getObject()); // pointer is still set because safe mode doesn't work on NonDestroyableClass } TEST_F(FunctorTest, FunctorMember_CanCall_WithObjectInCall) { MockDestroyableClass mock; { EXPECT_CALL(mock, method0()); FunctorMemberPtr functor = createFunctor(&DestroyableClass::method0); (*functor)(&mock); } { EXPECT_CALL(mock, method1(13)).WillOnce(::testing::Return(0)); FunctorMemberPtr functor = createFunctor(&DestroyableClass::method1); (*functor)(&mock, 13); } { EXPECT_CALL(mock, method2(13, 111.11f)).WillOnce(::testing::Return(0.0f)); FunctorMemberPtr functor = createFunctor(&DestroyableClass::method2); (*functor)(&mock, 13, 111.11f); } { EXPECT_CALL(mock, method3(13, 111.11f, true)).WillOnce(::testing::Return(false)); FunctorMemberPtr functor = createFunctor(&DestroyableClass::method3); (*functor)(&mock, 13, 111.11f, true); } { EXPECT_CALL(mock, method4(13, 111.11f, true, "test")).WillOnce(::testing::Return("")); FunctorMemberPtr functor = createFunctor(&DestroyableClass::method4); (*functor)(&mock, 13, 111.11f, true, "test"); } { EXPECT_CALL(mock, method5(13, 111.11f, true, "test", Vector3(3, 2, 1))).WillOnce(::testing::Return(Vector3())); FunctorMemberPtr functor = createFunctor(&DestroyableClass::method5); (*functor)(&mock, 13, 111.11f, true, "test", Vector3(3, 2, 1)); } } TEST_F(FunctorTest, FunctorMember_CanCall_WithObjectInConstructor) { MockDestroyableClass mock; { EXPECT_CALL(mock, method0()); FunctorMemberPtr functor = createFunctor(&DestroyableClass::method0, &mock); (*functor)(); } { EXPECT_CALL(mock, method1(13)).WillOnce(::testing::Return(0)); FunctorMemberPtr functor = createFunctor(&DestroyableClass::method1, &mock); (*functor)(13); } { EXPECT_CALL(mock, method2(13, 111.11f)).WillOnce(::testing::Return(0.0f)); FunctorMemberPtr functor = createFunctor(&DestroyableClass::method2, &mock); (*functor)(13, 111.11f); } { EXPECT_CALL(mock, method3(13, 111.11f, true)).WillOnce(::testing::Return(false)); FunctorMemberPtr functor = createFunctor(&DestroyableClass::method3, &mock); (*functor)(13, 111.11f, true); } { EXPECT_CALL(mock, method4(13, 111.11f, true, "test")).WillOnce(::testing::Return("")); FunctorMemberPtr functor = createFunctor(&DestroyableClass::method4, &mock); (*functor)(13, 111.11f, true, "test"); } { EXPECT_CALL(mock, method5(13, 111.11f, true, "test", Vector3(3, 2, 1))).WillOnce(::testing::Return(Vector3())); FunctorMemberPtr functor = createFunctor(&DestroyableClass::method5, &mock); (*functor)(13, 111.11f, true, "test", Vector3(3, 2, 1)); } } TEST_F(FunctorTest, FunctorMember_ReturnsZeroIfCalledWithoutObject) { DestroyableClass object; { FunctorMemberPtr functor = createFunctor(&DestroyableClass::method1); int resultWithObject = (*functor)(&object, 13); int resultWithoutObject = (*functor)(13); EXPECT_EQ(13, resultWithObject); EXPECT_EQ(0, resultWithoutObject); } } /////////////////////////////////////////////////////////// ////////////////////// FunctorStatic ////////////////////// /////////////////////////////////////////////////////////// TEST_F(FunctorTest, FunctorStatic_CanCall) { MockDestroyableClass mock; DestroyableClass::staticinstance = &mock; { EXPECT_CALL(mock, method0()); FunctorStaticPtr functor = createFunctor(&DestroyableClass::staticmethod0); (*functor)(); } { EXPECT_CALL(mock, method1(13)).WillOnce(::testing::Return(0)); FunctorStaticPtr functor = createFunctor(&DestroyableClass::staticmethod1); (*functor)(13); } { EXPECT_CALL(mock, method2(13, 111.11f)).WillOnce(::testing::Return(0.0f)); FunctorStaticPtr functor = createFunctor(&DestroyableClass::staticmethod2); (*functor)(13, 111.11f); } { EXPECT_CALL(mock, method3(13, 111.11f, true)).WillOnce(::testing::Return(false)); FunctorStaticPtr functor = createFunctor(&DestroyableClass::staticmethod3); (*functor)(13, 111.11f, true); } { EXPECT_CALL(mock, method4(13, 111.11f, true, "test")).WillOnce(::testing::Return("")); FunctorStaticPtr functor = createFunctor(&DestroyableClass::staticmethod4); (*functor)(13, 111.11f, true, "test"); } { EXPECT_CALL(mock, method5(13, 111.11f, true, "test", Vector3(3, 2, 1))).WillOnce(::testing::Return(Vector3())); FunctorStaticPtr functor = createFunctor(&DestroyableClass::staticmethod5); (*functor)(13, 111.11f, true, "test", Vector3(3, 2, 1)); } } TEST_F(FunctorTest, FunctorStatic_CanCallWithObject) { MockDestroyableClass mock; DestroyableClass::staticinstance = &mock; { EXPECT_CALL(mock, method0()); FunctorStaticPtr functor = createFunctor(&DestroyableClass::staticmethod0); (*functor)(&mock); // object is ignored } } TEST_F(FunctorTest, FunctorStatic_CanCallWithNull) { MockDestroyableClass mock; DestroyableClass::staticinstance = &mock; { EXPECT_CALL(mock, method0()); FunctorStaticPtr functor = createFunctor(&DestroyableClass::staticmethod0); (*functor)(nullptr); // pointer is ignored } } TEST_F(FunctorTest, FunctorStatic_RawObjectPointerIsAlwaysNull) { DestroyableClass object; FunctorStaticPtr functor = createFunctor(&DestroyableClass::staticmethod0); EXPECT_EQ(nullptr, functor->getRawObjectPointer()); functor->setRawObjectPointer(&object); EXPECT_EQ(nullptr, functor->getRawObjectPointer()); functor->setRawObjectPointer(nullptr); EXPECT_EQ(nullptr, functor->getRawObjectPointer()); } ////////////////////////////// Various tests ////////////////////////////// TEST_F(FunctorTest, CreateFunctorWithVariousFunctionTypes) { FunctorPtr c1 = createFunctor(&f4a); FunctorPtr c2 = createFunctor(&f4b); FunctorPtr c3 = createFunctor([] (int arg) { return arg*4; }); auto lambda = [] (int arg1, int arg2) { return arg1*arg2; }; FunctorPtr c4 = createFunctor(lambda); std::function bind0 = std::bind(multiply, 4, 6); std::function bind1 = std::bind(multiply, std::placeholders::_1, 7); std::function bind2 = std::bind(multiply, std::placeholders::_1, std::placeholders::_2); std::function function = f4a; FunctorPtr c5 = createFunctor(bind0); FunctorPtr c6 = createFunctor(bind1); FunctorPtr c7 = createFunctor(bind2); FunctorPtr c8 = createFunctor(function); EXPECT_EQ(8, (*c1)(4).get()); EXPECT_EQ(12, (*c2)(4).get()); EXPECT_EQ(16, (*c3)(4).get()); EXPECT_EQ(20, (*c4)(4, 5).get()); EXPECT_EQ(24, (*c5)(4).get()); EXPECT_EQ(28, (*c6)(4).get()); EXPECT_EQ(32, (*c7)(4, 8).get()); EXPECT_EQ(8, (*c8)(4).get()); EXPECT_EQ(1u, c1->getParamCount()); EXPECT_EQ(1u, c2->getParamCount()); EXPECT_EQ(1u, c3->getParamCount()); EXPECT_EQ(2u, c4->getParamCount()); EXPECT_EQ(0u, c5->getParamCount()); EXPECT_EQ(1u, c6->getParamCount()); EXPECT_EQ(2u, c7->getParamCount()); EXPECT_EQ(1u, c8->getParamCount()); EXPECT_EQ(typeid(int(*)(int)), c1->getFullIdentifier()); EXPECT_EQ(typeid(int(*)(int)), c2->getFullIdentifier()); EXPECT_EQ(typeid(int(*)(int)), c3->getFullIdentifier()); EXPECT_EQ(typeid(int(*)(int, int)), c4->getFullIdentifier()); EXPECT_EQ(typeid(int(*)()), c5->getFullIdentifier()); EXPECT_EQ(typeid(int(*)(int)), c6->getFullIdentifier()); EXPECT_EQ(typeid(int(*)(int, int)), c7->getFullIdentifier()); EXPECT_EQ(typeid(int(*)(int)), c8->getFullIdentifier()); EXPECT_EQ(typeid(int(*)(int)), c1->getHeaderIdentifier()); EXPECT_EQ(typeid(int(*)(int)), c2->getHeaderIdentifier()); EXPECT_EQ(typeid(int(*)(int)), c3->getHeaderIdentifier()); EXPECT_EQ(typeid(int(*)(int, int)), c4->getHeaderIdentifier()); EXPECT_EQ(typeid(int(*)()), c5->getHeaderIdentifier()); EXPECT_EQ(typeid(int(*)(int)), c6->getHeaderIdentifier()); EXPECT_EQ(typeid(int(*)(int, int)), c7->getHeaderIdentifier()); EXPECT_EQ(typeid(int(*)(int)), c8->getHeaderIdentifier()); EXPECT_EQ(typeid(int(*)(int)), c1->getHeaderIdentifier(1)); EXPECT_EQ(typeid(int(*)(int)), c2->getHeaderIdentifier(1)); EXPECT_EQ(typeid(int(*)(int)), c3->getHeaderIdentifier(1)); EXPECT_EQ(typeid(int(*)(int)), c4->getHeaderIdentifier(1)); EXPECT_EQ(typeid(int(*)()), c5->getHeaderIdentifier(1)); EXPECT_EQ(typeid(int(*)(int)), c6->getHeaderIdentifier(1)); EXPECT_EQ(typeid(int(*)(int)), c7->getHeaderIdentifier(1)); EXPECT_EQ(typeid(int(*)(int)), c8->getHeaderIdentifier(1)); EXPECT_EQ(typeid(int(*)()), c1->getHeaderIdentifier(0)); EXPECT_EQ(typeid(int(*)()), c2->getHeaderIdentifier(0)); EXPECT_EQ(typeid(int(*)()), c3->getHeaderIdentifier(0)); EXPECT_EQ(typeid(int(*)()), c4->getHeaderIdentifier(0)); EXPECT_EQ(typeid(int(*)()), c5->getHeaderIdentifier(0)); EXPECT_EQ(typeid(int(*)()), c6->getHeaderIdentifier(0)); EXPECT_EQ(typeid(int(*)()), c7->getHeaderIdentifier(0)); EXPECT_EQ(typeid(int(*)()), c8->getHeaderIdentifier(0)); } TEST_F(FunctorTest, CanCallFunctorWithLambdaWichIsAlreadyOutOfScope) { int var1 = 1; FunctorPtr fp1a = createFunctor([&var1] () { return var1++; }); FunctorPtr fp1b = createFunctor([&var1] () { return var1; }); FunctorPtr fp2; FunctorPtr fp3; { int var2 = 2; fp2 = createFunctor([var2] () { return var2; }); } { int var3 = 3; fp3 = createFunctor([var3] () { return var3; }); } EXPECT_EQ(1, var1); EXPECT_EQ(1, (*fp1a)().get()); EXPECT_EQ(2, var1); EXPECT_EQ(2, (*fp1b)().get()); EXPECT_EQ(2, (*fp2)().get()); EXPECT_EQ(3, (*fp3)().get()); } TEST_F(FunctorTest, canCompareHeaderIdentifiers) { FunctorPtr fp1 = createFunctor(&f1); FunctorPtr fp2 = createFunctor(&f2); FunctorPtr fp3 = createFunctor(&f3); ASSERT_STREQ(fp1->getHeaderIdentifier(1).name(), fp2->getHeaderIdentifier(1).name()); ASSERT_STREQ(fp1->getHeaderIdentifier(2).name(), fp2->getHeaderIdentifier(2).name()); ASSERT_STRNE(fp1->getHeaderIdentifier(1).name(), fp2->getHeaderIdentifier(2).name()); ASSERT_STRNE(fp1->getHeaderIdentifier(10).name(), fp2->getHeaderIdentifier(10).name()); ASSERT_STRNE(fp2->getHeaderIdentifier(2).name(), fp3->getHeaderIdentifier(2).name()); ASSERT_STRNE(fp1->getHeaderIdentifier().name(), fp2->getHeaderIdentifier().name()); ASSERT_STRNE(fp2->getHeaderIdentifier().name(), fp3->getHeaderIdentifier().name()); } TEST_F(FunctorTest, canCompareTypenames) { FunctorPtr fp1 = createFunctor(&f1); FunctorPtr fp2 = createFunctor(&f2); FunctorPtr fp3 = createFunctor(&f3); ASSERT_EQ(fp1->getTypenameReturnvalue(), fp2->getTypenameReturnvalue()); ASSERT_EQ(fp1->getTypenameParam(0), fp3->getTypenameParam(0)); ASSERT_EQ("void", fp1->getTypenameReturnvalue()); ASSERT_EQ("int", fp3->getTypenameReturnvalue()); ASSERT_EQ("int", fp2->getTypenameParam(0)); ASSERT_EQ("double", fp3->getTypenameParam(2)); ASSERT_EQ("", fp3->getTypenameParam(6)); } TEST_F(FunctorTest, testGetParamCountAndHasReturnValue) { FunctorPtr fp1 = createFunctor(&f1); FunctorPtr fp2 = createFunctor(&f2); FunctorPtr fp3 = createFunctor(&f3); ASSERT_EQ(3u, fp2->getParamCount()); ASSERT_NE(fp1->getParamCount(), fp3->getParamCount()); ASSERT_FALSE(fp2->hasReturnvalue()); ASSERT_TRUE(fp3->hasReturnvalue()); } TEST_F(FunctorTest, canEvaluateArgument) { FunctorPtr fp1 = createFunctor(&f1); MultiType mttype = "2"; fp1->evaluateArgument(0, mttype); ASSERT_TRUE(mttype.isType()); ASSERT_EQ(2, mttype.get()); fp1->evaluateArgument(1, mttype); ASSERT_TRUE(mttype.isType()); ASSERT_EQ(2.0, mttype.get()); mttype.reset(); fp1->evaluateArgument(5, mttype); ASSERT_TRUE(mttype.null()); } TEST_F(FunctorTest, canUseCallables) { int a = 0; FunctorPtr fp1 = createFunctor(CallableStruct{}); FunctorPtr fp2 = createFunctor([](bool val) { return val; }); FunctorPtr fp3 = createFunctor([&a]() {return a++; }); FunctorPtr fp4 = createFunctor([a]() {return a; }); ASSERT_EQ(1, (*fp1)().get()); ASSERT_EQ(true, (*fp2)(true).get()); ASSERT_EQ(0, (*fp3)().get()); ASSERT_EQ(1, a); ASSERT_EQ(0, (*fp4)().get()); ASSERT_STREQ(fp1->getHeaderIdentifier().name(), fp3->getHeaderIdentifier().name()); } TEST_F(FunctorTest, SafeModeWorks) { DestroyableClass* testclass = new DestroyableClass(); DestroyableClass* testclass2 = new DestroyableClass(); FunctorPtr fp1 = createFunctor(&DestroyableClass::method0, testclass); fp1->setSafeMode(true); FunctorPtr fp2 = createFunctor(&DestroyableClass::method0, testclass); fp2->setSafeMode(true); FunctorPtr fp3 = createFunctor(&DestroyableClass::method0, testclass); fp2->setRawObjectPointer(testclass2); ASSERT_NE(nullptr, fp1->getRawObjectPointer()); ASSERT_NE(nullptr, fp2->getRawObjectPointer()); ASSERT_NE(nullptr, fp3->getRawObjectPointer()); testclass->destroy(); ASSERT_EQ(nullptr, fp1->getRawObjectPointer()); ASSERT_NE(nullptr, fp2->getRawObjectPointer()); ASSERT_NE(nullptr, fp3->getRawObjectPointer()); fp3->setRawObjectPointer(testclass2); fp3->setSafeMode(true); fp2->setSafeMode(false); testclass2->destroy(); ASSERT_NE(nullptr, fp2->getRawObjectPointer()); ASSERT_EQ(nullptr, fp3->getRawObjectPointer()); } }