Skip to content

Example

#include "CppOverride.hpp"

#include <iostream>
#include <vector>
#include <string>

CO_DECLARE_INSTANCE(OverrideInstanceName);

namespace MyNameSpace
{
    int OverrideMyScopedFunc(int value1, float& value2)
    {
        CO_INSERT_IMPL(OverrideInstanceName, int, (value1, value2));
        //The rest of the implementations...
        return 0;
    }
}

int OverrideMyReturnValue(int value1, float value2)
{
    CO_INSERT_IMPL(OverrideInstanceName, int, (value1, value2));
    //The rest of the implementations...
    return 0;
}

int SomeRef = 0;

int& OverrideMyReturnRef(int value1, float value2)
{
    CO_INSERT_IMPL(OverrideInstanceName, int&, (value1, value2));
    //The rest of the implementations...
    return SomeRef;
}

void OverrideMyArgs(float& value1, int* value2)
{
    CO_INSERT_IMPL(OverrideInstanceName, void, (value1, value2));
}

bool OverrideMyArgsWithStstus(float& value1, int* value2)
{
    CO_INSERT_IMPL(OverrideInstanceName, bool, (value1, value2));
    return false;
}

#define CO_QUICK_ASSERT(expr)\
do\
{\
    bool assertion = (expr);\
    if(assertion)\
    {\
        std::cout << "Assertion succeeded: \"" << #expr << "\"" << std::endl;\
    }\
    else\
    {\
        std::cerr << "Assertion failed: \"" << #expr << "\"" << std::endl;\
        exit(-1);\
    }\
} while(0)

void ResetAll()
{
    CO_CLEAR_ALL_INSTRUCTS(OverrideInstanceName);
}

void OverrideReturnsExample()
{
    CO_INSTRUCT_REF(OverrideInstanceName, CO_GLOBAL, OverrideMyReturnValue).Returns<int>(1);

    CO_QUICK_ASSERT(OverrideMyReturnValue(0, 0.f) == 1);

    int returnRef = 1;
    CO_INSTRUCT_REF(OverrideInstanceName, CO_GLOBAL, OverrideMyReturnRef).Returns<int&>(returnRef);

    CO_QUICK_ASSERT(&returnRef == &OverrideMyReturnRef(0, 0.f));

    ResetAll();
}

void OverrideArgumentsExample()
{
    CO_INSTRUCT_REF(OverrideInstanceName, CO_GLOBAL, OverrideMyArgs).SetArgs<float&, int*>(1.f, 3);

    float arg1 = 0.f;
    int arg2 = 0;
    OverrideMyArgs(arg1, &arg2);
    CO_QUICK_ASSERT(arg1 == 1.f);
    CO_QUICK_ASSERT(arg2 == 3);

    ResetAll();
}

void OverrideScopedFunctionExample()
{
    CO_INSTRUCT_REF(OverrideInstanceName, MyNameSpace, OverrideMyScopedFunc).Returns<int>(1);

    float floatArg = 0.f;
    CO_QUICK_ASSERT(MyNameSpace::OverrideMyScopedFunc(1, floatArg) == 1);

    CO_REMOVE_INSTRUCT_REF(OverrideInstanceName, MyNameSpace, OverrideMyScopedFunc);

    CO_INSTRUCT_REF (OverrideInstanceName, MyNameSpace, OverrideMyScopedFunc)
                    .SetArgs<CO_ANY_TYPE, float&>(CO_DONT_SET, 3);

    MyNameSpace::OverrideMyScopedFunc(1, floatArg);
    CO_QUICK_ASSERT(floatArg == 3);
}

void OverrideReturnsWithActionLambda()
{
    CO_INSTRUCT_REF (OverrideInstanceName, CO_GLOBAL, OverrideMyReturnValue)
                    .ReturnsByAction<int>
                    ( 
                        []( void*, 
                            const std::vector<CppOverride::TypedDataInfo>& args, 
                            const CppOverride::TypedInfo& returnInfo) -> CppOverride::TypedDataInfo
                        { 
                            (void)args;
                            //Can access args with type safety if needed
                            //e.g., if(args.at(0).IsType<int>()) { ... }

                            //When returning a value, you should use CreateValue<int>(int value)
                            if(returnInfo.IsType<int>())
                                return CppOverride::TypedDataInfo().CreateValue<int>(5);

                            //When returning a reference, 
                            //you should use CreateReference<int&>(int* valuePtr)

                            return CppOverride::TypedDataInfo();
                        }
                    );

    CO_QUICK_ASSERT(OverrideMyReturnValue(0, 0.f) == 5);

    ResetAll();
}

void OverrideArgumentsWithActionLambda()
{
    CO_INSTRUCT_REF (OverrideInstanceName, CO_GLOBAL, OverrideMyArgs)
                    .SetArgsByAction<float&, int*>
                    (
                        [](void*, std::vector<CppOverride::TypedDataInfo>& args)
                        {
                            if(args.at(0).IsType<float>())
                                *args.at(0).GetTypedDataPtr<float>() = 1.f;
                            if(args.at(1).IsType<int*>())
                                **args.at(1).GetTypedDataPtr<int*>() = 2;
                        }
                    );

    float arg1 = 0.f;
    int arg2 = 0;
    OverrideMyArgs(arg1, &arg2);
    CO_QUICK_ASSERT(arg1 == 1.f);
    CO_QUICK_ASSERT(arg2 == 2);

    ResetAll();
}

void WhenCalledWithExample()
{
    CO_INSTRUCT_REF (OverrideInstanceName, CO_GLOBAL, OverrideMyReturnValue)
                    .WhenCalledWith(2, 3.f)
                    .Returns<int>(1);

    int ret1 = OverrideMyReturnValue(2, 3.f);   //Returns 1
    int ret2 = OverrideMyReturnValue(1, 2.f);   //Won't return 1

    CO_QUICK_ASSERT(ret1 == 1);
    CO_QUICK_ASSERT(ret2 != 1);

    ResetAll();
}

void TimesExample()
{
    CO_INSTRUCT_REF (OverrideInstanceName, CO_GLOBAL, OverrideMyArgs)
                    .SetArgs<float&, int*>(1.f, 2)
                    .Times(1);

    float testFloat = 2.f;
    int testInt = 3;
    OverrideMyArgs(testFloat, &testInt);    //The argument values are set to 1.f and 2

    CO_QUICK_ASSERT(testFloat == 1.f);
    CO_QUICK_ASSERT(testInt == 2);

    testFloat = 2.f;
    testInt = 3;
    OverrideMyArgs(testFloat, &testInt);    //The argument values are still 2.f and 3

    CO_QUICK_ASSERT(testFloat == 2.f);
    CO_QUICK_ASSERT(testInt == 3);

    ResetAll();
}

void IfConditionLambdaExample()
{
    CO_INSTRUCT_REF (OverrideInstanceName, CO_GLOBAL, OverrideMyReturnValue)
                    .If
                    (
                        [](void*, const std::vector<CppOverride::TypedDataInfo>& args)
                        {
                            if(args.at(0).IsType<int>() && *args.at(0).GetTypedDataPtr<int>() == 1)
                                return true;
                            else
                                return false;
                        }
                    )
                    .Returns<int>(1);

    int ret1 = OverrideMyReturnValue(1, 2.f);   //Returns 1
    int ret2 = OverrideMyReturnValue(2, 3.f);   //Won't return 1

    CO_QUICK_ASSERT(ret1 == 1);
    CO_QUICK_ASSERT(ret2 != 1);

    ResetAll();
}

void WhenCalledExpectedlyDoLambdaExample()
{
    bool called = false;
    CO_INSTRUCT_REF (OverrideInstanceName, CO_GLOBAL, OverrideMyReturnValue)
                    .WhenCalledWith(2, 3.f)
                    .Returns<int>(1)
                    .WhenCalledExpectedly_Do
                    (
                        [&called](  void* instance, 
                                    const std::vector<CppOverride::TypedDataInfo>& args)
                        {
                            (void)instance;
                            (void)args;
                            //Can access args with type safety if needed
                            //e.g., if(args.at(0).IsType<int>()) { ... }

                            called = true;
                        }
                    );

    int ret1 = OverrideMyReturnValue(2, 3.f);   //Returns 1 and sets called to true
    CO_QUICK_ASSERT(ret1 == 1);
    CO_QUICK_ASSERT(called == true);

    ResetAll();
}

void OtherwiseDoLambdaExample()
{
    bool called = false;
    CO_INSTRUCT_REF (OverrideInstanceName, CO_GLOBAL, OverrideMyReturnValue)
                    .WhenCalledWith(2, 3.f)
                    .Returns<int>(1)
                    .Otherwise_Do
                    (
                        [&called](  void* instance, 
                                    const std::vector<CppOverride::TypedDataInfo>& args)
                        {
                            (void)instance;
                            (void)args;
                            //Can access args with type safety if needed
                            //e.g., if(args.at(0).IsType<int>()) { ... }

                            called = true;
                        }
                    );

    int ret1 = OverrideMyReturnValue(1, 2.f);   //Won't return 1
    CO_QUICK_ASSERT(ret1 != 1);
    CO_QUICK_ASSERT(called == true);

    ResetAll();
}

void AssignStatusExample()
{
    CppOverride::ResultPtr result;
    CO_INSTRUCT_REF (OverrideInstanceName, CO_GLOBAL, OverrideMyReturnValue)
                    .WhenCalledWith(2, 3.f)
                    .Returns<int>(1)
                    .AssignsResult(result);

    int ret1 = OverrideMyReturnValue(1, 2.f);

    //status will be OverrideStatus::MATCHING_CONDITION_VALUE_FAILED
    CO_QUICK_ASSERT(ret1 != 1);
    CO_QUICK_ASSERT(result->GetLastStatus() == 
                    CppOverride::OverrideStatus::MATCHING_CONDITION_VALUE_FAILED);

    ResetAll();
}

void ExpectedExample()
{
    CO_INSTRUCT_REF (OverrideInstanceName, CO_GLOBAL, OverrideMyReturnValue)
                    .WhenCalledWith(2, 3.f)
                    .Times(1)
                    .Returns<int>(1)
                    .Expected();

    CO_INSTRUCT_REF (OverrideInstanceName, CO_GLOBAL, OverrideMyReturnValue)
                    .WhenCalledWith(3, 4.f)
                    .Returns<int>(1)
                    .ExpectedNotSatisfied();

    std::vector<CppOverride::FunctionName> failedFunctions;

    //Not triggering any instructed overrides when we expected to do so, 
    //therefore failed expectations.
    int ret1 = OverrideMyReturnValue(1, 2.f);
    failedFunctions = CO_GET_FAILED_FUNCTIONS(OverrideInstanceName);
    CO_QUICK_ASSERT(ret1 != 1);
    CO_QUICK_ASSERT(failedFunctions.size() == 1);

    //Triggered the first instructed override, filled all expectations.
    ret1 = OverrideMyReturnValue(2, 3.f);
    failedFunctions = CO_GET_FAILED_FUNCTIONS(OverrideInstanceName);
    CO_QUICK_ASSERT(ret1 == 1);
    CO_QUICK_ASSERT(failedFunctions.empty());

    //Triggered the second instructed override when we expect to not be triggered, 
    //therefore failing expectation
    OverrideMyReturnValue(3, 4.f);
    failedFunctions = CO_GET_FAILED_FUNCTIONS(OverrideInstanceName);
    CO_QUICK_ASSERT(failedFunctions.size() == 1);

    ResetAll();
}

int main(int, char**)
{
    OverrideReturnsExample();

    OverrideArgumentsExample();

    OverrideScopedFunctionExample();

    OverrideReturnsWithActionLambda();

    OverrideArgumentsWithActionLambda();

    WhenCalledWithExample();

    IfConditionLambdaExample();

    WhenCalledExpectedlyDoLambdaExample();

    OtherwiseDoLambdaExample();

    AssignStatusExample();

    ExpectedExample();

    std::cout << "All examples are running correctly" << std::endl;

    return 0;
}