You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4501 lines
175 KiB
C++
4501 lines
175 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
#include <AzCore/Script/ScriptContext.h>
|
|
#include <AzCore/RTTI/BehaviorContext.h>
|
|
#include <AzCore/Script/lua/lua.h>
|
|
#include <AzCore/Script/ScriptContextDebug.h>
|
|
#include <AzCore/UnitTest/TestTypes.h>
|
|
|
|
#include <AzCore/Component/Entity.h>
|
|
|
|
#include <AzCore/std/string/string.h>
|
|
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
|
#include <AzCore/std/smart_ptr/intrusive_ptr.h>
|
|
#include <AzCore/std/smart_ptr/make_shared.h>
|
|
#include <AzCore/std/containers/fixed_vector.h>
|
|
|
|
#include <math.h> // for pow
|
|
|
|
#include <AzCore/Math/MathReflection.h>
|
|
#include <AzCore/Math/Transform.h>
|
|
|
|
#include <AzCore/std/any.h>
|
|
|
|
// Components
|
|
#include <AzCore/Component/ComponentApplication.h>
|
|
#include <AzCore/Memory/MemoryComponent.h>
|
|
#include <AzCore/Script/ScriptSystemComponent.h>
|
|
#include <AzCore/Asset/AssetManager.h>
|
|
#include <AzCore/Asset/AssetManagerComponent.h>
|
|
|
|
#include <Tests/BehaviorContextFixture.h>
|
|
|
|
namespace UnitTest
|
|
{
|
|
using namespace AZ;
|
|
|
|
enum GlobalEnum
|
|
{
|
|
GE_VALUE1 = 0,
|
|
GE_VALUE2,
|
|
};
|
|
|
|
enum class GlobalClassEnum
|
|
{
|
|
Value1,
|
|
Value2,
|
|
Value3,
|
|
};
|
|
|
|
struct GlobalData
|
|
{
|
|
AZ_TYPE_INFO(GlobalData, "{4F35A5E6-568E-43C8-851A-4D2315E9BAD0}");
|
|
|
|
GlobalData()
|
|
{}
|
|
|
|
char data[512];
|
|
};
|
|
|
|
|
|
int g_globalValue = 501;
|
|
int g_globalData = 0;
|
|
int g_globalTestClassesConstructed = 0;
|
|
int g_globalTestClassesDestructed = 0;
|
|
|
|
GlobalClassEnum globalClassEnumValue = GlobalClassEnum::Value3;
|
|
|
|
int globalPropertyGetter()
|
|
{
|
|
return g_globalValue;
|
|
}
|
|
|
|
void globalPropertySetter(int value)
|
|
{
|
|
g_globalValue = value;
|
|
}
|
|
|
|
int globalMethod(int a)
|
|
{
|
|
return a + 3;
|
|
}
|
|
|
|
void globalMethod1()
|
|
{
|
|
g_globalData = 1001;
|
|
}
|
|
|
|
int globalMethod2WithDefaultArgument(int value)
|
|
{
|
|
return value + 1;
|
|
}
|
|
|
|
int globalMethodContainers(const AZStd::vector<int>& value)
|
|
{
|
|
return value[0];
|
|
}
|
|
|
|
int globalMethodPair(const AZStd::pair<int, bool>& value)
|
|
{
|
|
return value.second ? value.first : -1;
|
|
}
|
|
|
|
int globalMethodToOverride(int a)
|
|
{
|
|
return a * 3;
|
|
}
|
|
|
|
void globalMethodOverride(ScriptDataContext& dc)
|
|
{
|
|
(void)dc;
|
|
}
|
|
|
|
GlobalData globalMethodLarge()
|
|
{
|
|
return GlobalData();
|
|
}
|
|
|
|
GlobalClassEnum globalMethodGetClassEnum()
|
|
{
|
|
return GlobalClassEnum::Value1;
|
|
}
|
|
|
|
void globalMethodSetClassEnum(GlobalClassEnum value)
|
|
{
|
|
globalClassEnumValue = value;
|
|
}
|
|
|
|
// Global Enum class wrapper, till the "namespace" open/close functionallity is missing
|
|
struct BehaviorGlobalClassEnumWrapper
|
|
{
|
|
AZ_TYPE_INFO(BehaviorGlobalClassEnumWrapper,"{de36dcf3-7c25-4053-b747-1148240cda68}");
|
|
};
|
|
|
|
class BehaviorTestClass
|
|
{
|
|
public:
|
|
AZ_RTTI(BehaviorTestClass, "{26398112-E4F1-4912-80E6-D81152116D02}");
|
|
AZ_CLASS_ALLOCATOR(BehaviorTestClass, AZ::SystemAllocator, 0);
|
|
|
|
BehaviorTestClass()
|
|
: m_data(1)
|
|
, m_data1(2)
|
|
, m_dataReadOnly(3)
|
|
{
|
|
g_globalData = 1020;
|
|
g_globalTestClassesConstructed++;
|
|
}
|
|
|
|
BehaviorTestClass(const BehaviorTestClass& other)
|
|
: m_data(other.m_data)
|
|
, m_data1(other.m_data1)
|
|
, m_dataReadOnly(other.m_dataReadOnly)
|
|
{
|
|
g_globalData = 1025;
|
|
g_globalTestClassesConstructed++;
|
|
}
|
|
|
|
BehaviorTestClass(int data)
|
|
: m_data(data)
|
|
, m_data1(3)
|
|
, m_dataReadOnly(4)
|
|
{
|
|
g_globalData = 1030;
|
|
g_globalTestClassesConstructed++;
|
|
}
|
|
|
|
virtual ~BehaviorTestClass()
|
|
{
|
|
g_globalData = 1040;
|
|
g_globalTestClassesDestructed++;
|
|
}
|
|
|
|
enum MyEnum
|
|
{
|
|
ME_VALUE0,
|
|
ME_VALUE1,
|
|
};
|
|
|
|
enum class MyClassEnum
|
|
{
|
|
MCE_VALUE0,
|
|
MCE_VALUE1,
|
|
};
|
|
|
|
void Method1()
|
|
{
|
|
g_globalData = 1050;
|
|
}
|
|
|
|
int Method2(int* a) const
|
|
{
|
|
return *a + 1;
|
|
}
|
|
|
|
BehaviorTestClass* GetClass()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
int GetData() const
|
|
{
|
|
return m_data;
|
|
}
|
|
void SetData(int data)
|
|
{
|
|
m_data = data;
|
|
m_data1 = m_data + 10;
|
|
m_dataReadOnly = m_data1 + 100;
|
|
}
|
|
|
|
static void StaticMethod(int newData)
|
|
{
|
|
g_globalData = newData;
|
|
}
|
|
|
|
void SpecialScriptMethod(ScriptDataContext& dc)
|
|
{
|
|
dc.PushResult(303);
|
|
}
|
|
|
|
void MethodToOverrideInScript()
|
|
{
|
|
g_globalData = 1060;
|
|
}
|
|
|
|
void Method1ScriptOverride(ScriptDataContext&)
|
|
{
|
|
g_globalData = 1070;
|
|
}
|
|
|
|
void MethodToOverrideInScriptWithANonMember() const
|
|
{
|
|
g_globalData = 1080;
|
|
}
|
|
|
|
BehaviorTestClass AddOperator(BehaviorTestClass& rhs)
|
|
{
|
|
BehaviorTestClass result = *this;
|
|
result.m_data += rhs.m_data;
|
|
result.m_data1 += rhs.m_data1;
|
|
result.m_dataReadOnly += rhs.m_dataReadOnly;
|
|
return result;
|
|
}
|
|
|
|
AZStd::string ToString()
|
|
{
|
|
return AZStd::string::format("%d\n", m_data);
|
|
}
|
|
|
|
bool BoundsCheckMethodWithDefaultValue(float value, float epsilon, float minBounds, float maxBounds)
|
|
{
|
|
(void)epsilon;
|
|
return value >= minBounds && value < maxBounds;
|
|
}
|
|
|
|
int m_data;
|
|
int m_data1;
|
|
int m_dataReadOnly;
|
|
};
|
|
|
|
class BehaviorDerivedTestClass : public BehaviorTestClass
|
|
{
|
|
public:
|
|
AZ_RTTI(BehaviorDerivedTestClass, "{dba8a4e3-8fab-4223-94a6-874c6cff88e5}", BehaviorTestClass);
|
|
int m_data;
|
|
|
|
};
|
|
|
|
void TestMethodToScriptOverride(const BehaviorTestClass* thisPtr, ScriptDataContext&)
|
|
{
|
|
(void)thisPtr;
|
|
g_globalData = 1010;
|
|
}
|
|
|
|
void TestScriptNonIntrusiveConstructor(BehaviorTestClass* thisPtr, ScriptDataContext& dc)
|
|
{
|
|
new(thisPtr) BehaviorTestClass();
|
|
if (dc.GetNumArguments() && dc.IsNumber(0))
|
|
{
|
|
int data = 0;
|
|
dc.ReadArg(0, data);
|
|
thisPtr->m_data = data;
|
|
thisPtr->m_data1 = data * 2;
|
|
thisPtr->m_dataReadOnly = data * 3;
|
|
}
|
|
else
|
|
{
|
|
thisPtr->m_data = 1000;
|
|
thisPtr->m_data1 = 1005;
|
|
thisPtr->m_dataReadOnly = 1007;
|
|
}
|
|
}
|
|
|
|
class BehaviorTestBusEvents : public AZ::EBusTraits
|
|
{
|
|
public:
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Configure the EBus
|
|
static const EBusAddressPolicy AddressPolicy = EBusAddressPolicy::ById;
|
|
using BusIdType = int;
|
|
static const bool EnableEventQueue = true;
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
virtual void OnEvent(int data) = 0;
|
|
|
|
virtual int OnEventWithResult(int data)
|
|
{
|
|
return data + 2;
|
|
}
|
|
|
|
virtual int OnEventWithResultContainer(const AZStd::vector<int> values) = 0;
|
|
|
|
virtual GlobalClassEnum OnEventWithClassEnumResult() = 0;
|
|
|
|
virtual AZStd::string OnEventWithStringResult() { return ""; }
|
|
|
|
virtual BehaviorTestClass OnEventWithClassResult() { return BehaviorTestClass(); }
|
|
|
|
virtual AZStd::string OnEventWithDefaultValueAndStringResult(AZStd::string_view view1, AZStd::string_view) { return AZStd::string::format("Default Value: %s", view1.data()); }
|
|
|
|
virtual void OnEventConst() const {};
|
|
|
|
virtual BehaviorTestClass OnEventResultWithBehaviorClassParameter(BehaviorTestClass data) { return BehaviorTestClass(); };
|
|
};
|
|
|
|
typedef AZ::EBus<BehaviorTestBusEvents> BehaviorTestBus;
|
|
|
|
|
|
class BehaviorTestBusHandler : public BehaviorTestBus::Handler, public AZ::BehaviorEBusHandler
|
|
{
|
|
public:
|
|
AZ_EBUS_BEHAVIOR_BINDER(BehaviorTestBusHandler, "{19F5C8C8-4260-46B1-B624-997CD3F10CBD}", AZ::SystemAllocator
|
|
, OnEvent
|
|
, OnEventWithResult
|
|
, OnEventWithResultContainer
|
|
, OnEventWithClassEnumResult
|
|
, OnEventWithStringResult
|
|
, OnEventWithClassResult
|
|
, OnEventWithDefaultValueAndStringResult
|
|
, OnEventResultWithBehaviorClassParameter
|
|
);
|
|
|
|
// User code
|
|
void OnEvent(int data) override
|
|
{
|
|
// you can get the index yourself or use the FN_xxx enum FN_OnEvent
|
|
static int eventIndex = GetFunctionIndex("OnEvent");
|
|
AZ_Assert(eventIndex != -1, "We can't find event with name %s", "OnEvent");
|
|
Call(eventIndex, data);
|
|
}
|
|
|
|
int OnEventWithResult(int data) override
|
|
{
|
|
int result = 0; // default result as a function hook might not exists
|
|
CallResult(result, FN_OnEventWithResult, data);
|
|
return result;
|
|
}
|
|
|
|
int OnEventWithResultContainer(const AZStd::vector<int> values) override
|
|
{
|
|
int result = 0;
|
|
CallResult(result, FN_OnEventWithResultContainer, values);
|
|
return result;
|
|
}
|
|
|
|
GlobalClassEnum OnEventWithClassEnumResult() override
|
|
{
|
|
GlobalClassEnum result = GlobalClassEnum::Value1;
|
|
CallResult(result, FN_OnEventWithClassEnumResult);
|
|
return result;
|
|
}
|
|
|
|
AZStd::string OnEventWithStringResult() override
|
|
{
|
|
AZStd::string result;
|
|
CallResult(result, FN_OnEventWithStringResult);
|
|
return result;
|
|
}
|
|
|
|
BehaviorTestClass OnEventWithClassResult() override
|
|
{
|
|
BehaviorTestClass result;
|
|
CallResult(result, FN_OnEventWithClassResult);
|
|
return result;
|
|
}
|
|
|
|
AZStd::string OnEventWithDefaultValueAndStringResult(AZStd::string_view view1, AZStd::string_view view2) override
|
|
{
|
|
return BehaviorTestBus::Handler::OnEventWithDefaultValueAndStringResult(view1, view2);
|
|
}
|
|
|
|
|
|
BehaviorTestClass OnEventResultWithBehaviorClassParameter(BehaviorTestClass data) override
|
|
{
|
|
BehaviorTestClass result = data;
|
|
return result;
|
|
}
|
|
};
|
|
|
|
class BehaviorTestBusHandlerWithDoc : public BehaviorTestBus::Handler, public AZ::BehaviorEBusHandler
|
|
{
|
|
public:
|
|
AZ_EBUS_BEHAVIOR_BINDER_WITH_DOC(BehaviorTestBusHandlerWithDoc, "{C0D4FE98-DBE1-439A-ACBA-B3767863C560}", AZ::SystemAllocator
|
|
, OnEvent, ({"data", "Data to pass in"})
|
|
, OnEventWithResult, ({"data", "Data to pass in"})
|
|
, OnEventWithResultContainer, ({ "values", "Vector of integers to forward" })
|
|
, OnEventWithClassEnumResult, ()
|
|
, OnEventWithStringResult, ()
|
|
, OnEventWithClassResult, ()
|
|
, OnEventWithDefaultValueAndStringResult, ({ "defaultView", "string_view which contains literal to print by default" }, { "unusedView", "Unused test parameter" })
|
|
, OnEventResultWithBehaviorClassParameter, ()
|
|
);
|
|
|
|
// User code
|
|
void OnEvent(int data) override
|
|
{
|
|
// you can get the index yourself or use the FN_xxx enum FN_OnEvent
|
|
static int eventIndex = GetFunctionIndex("OnEvent");
|
|
AZ_Assert(eventIndex != -1, "We can't find event with name %s", "OnEvent");
|
|
Call(eventIndex, data);
|
|
}
|
|
|
|
int OnEventWithResult(int data) override
|
|
{
|
|
int result = 0; // default result as a function hook might not exists
|
|
CallResult(result, FN_OnEventWithResult, data);
|
|
return result;
|
|
}
|
|
|
|
int OnEventWithResultContainer(const AZStd::vector<int> values) override
|
|
{
|
|
int result = 0;
|
|
CallResult(result, FN_OnEventWithResultContainer, values);
|
|
return result;
|
|
}
|
|
|
|
GlobalClassEnum OnEventWithClassEnumResult() override
|
|
{
|
|
GlobalClassEnum result = GlobalClassEnum::Value1;
|
|
CallResult(result, FN_OnEventWithClassEnumResult);
|
|
return result;
|
|
}
|
|
|
|
AZStd::string OnEventWithStringResult() override
|
|
{
|
|
AZStd::string result;
|
|
CallResult(result, FN_OnEventWithStringResult);
|
|
return result;
|
|
}
|
|
|
|
BehaviorTestClass OnEventWithClassResult() override
|
|
{
|
|
BehaviorTestClass result;
|
|
CallResult(result, FN_OnEventWithClassResult);
|
|
return result;
|
|
}
|
|
|
|
AZStd::string OnEventWithDefaultValueAndStringResult(AZStd::string_view view1, AZStd::string_view view2) override
|
|
{
|
|
return BehaviorTestBus::Handler::OnEventWithDefaultValueAndStringResult(view1, view2);
|
|
}
|
|
|
|
|
|
BehaviorTestClass OnEventResultWithBehaviorClassParameter(BehaviorTestClass data) override
|
|
{
|
|
BehaviorTestClass result = data;
|
|
return result;
|
|
}
|
|
};
|
|
|
|
// traditional EBus Handler
|
|
class NativeBehaviorTestBusHandler : public BehaviorTestBus::Handler
|
|
{
|
|
public:
|
|
NativeBehaviorTestBusHandler()
|
|
{
|
|
m_enumResult = GlobalClassEnum::Value2;
|
|
BehaviorTestBus::Handler::BusConnect(1);
|
|
}
|
|
|
|
void OnEvent(int data) override
|
|
{
|
|
g_globalData = 2000 + data;
|
|
}
|
|
|
|
int OnEventWithResult(int data) override
|
|
{
|
|
g_globalData = 2100 + data;
|
|
return data + 3;
|
|
}
|
|
|
|
int OnEventWithResultContainer(const AZStd::vector<int> values) override
|
|
{
|
|
g_globalData = 2100 + values[0];
|
|
return values[0];
|
|
}
|
|
|
|
GlobalClassEnum OnEventWithClassEnumResult() override
|
|
{
|
|
return m_enumResult;
|
|
}
|
|
|
|
GlobalClassEnum m_enumResult;
|
|
};
|
|
|
|
/// Hooks
|
|
void OnEventHook(void* userData, int data)
|
|
{
|
|
(void)userData;
|
|
g_globalData = 2200 + data;
|
|
}
|
|
|
|
int OnEventWithResultHook(void* userData, int data)
|
|
{
|
|
(void)userData;
|
|
g_globalData = 2300 + data;
|
|
return data + 30;
|
|
}
|
|
|
|
void OnEventGenericHook(void* userData, const char* eventName, int eventIndex, BehaviorValueParameter* result, int numParameters, BehaviorValueParameter* parameters)
|
|
{
|
|
(void)userData; (void)numParameters; (void)result; (void)eventName; (void)eventIndex;
|
|
AZ_Assert(result == nullptr || strstr(eventName, "OnEventWithResult"), "We don't exepct result here");
|
|
AZ_Assert(numParameters == 1, "Invalid number of parameters!");
|
|
AZ_Assert(parameters[0].ConvertTo<int>(), "The parameter should be int, and convertable to it");
|
|
int* intData = parameters[0].GetAsUnsafe<int>();
|
|
g_globalData = 2400 + *intData;
|
|
if (result)
|
|
{
|
|
result->StoreResult(*intData + 50);
|
|
}
|
|
}
|
|
|
|
void globalGenericMethod(ScriptDataContext& dc)
|
|
{
|
|
if (dc.GetNumArguments())
|
|
{
|
|
if (dc.IsNumber(0))
|
|
{
|
|
int data;
|
|
dc.ReadArg(0, data);
|
|
dc.PushResult(data + 5);
|
|
}
|
|
}
|
|
}
|
|
|
|
class BehaviorTestSmartPtr
|
|
{
|
|
public:
|
|
AZ_CLASS_ALLOCATOR(BehaviorTestSmartPtr, AZ::SystemAllocator, 0);
|
|
AZ_TYPE_INFO(BehaviorTestSmartPtr, "{b59d3416-a77c-4e71-b638-4212705a07e6}");
|
|
|
|
BehaviorTestSmartPtr()
|
|
: m_intrusiveRefCount(0)
|
|
, m_data(303)
|
|
{
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// instrusive_ptr
|
|
// TODO: Add intrusive pointers helpers (RefCounted<T> and RefCountedThreadSave<T> or as a policy RefCounter<T, ThreadSafe>
|
|
void add_ref()
|
|
{
|
|
++m_intrusiveRefCount;
|
|
}
|
|
void release()
|
|
{
|
|
if (--m_intrusiveRefCount == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
}
|
|
int m_intrusiveRefCount;
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
void TestFunction()
|
|
{
|
|
g_globalData = 1090;
|
|
}
|
|
|
|
int m_data;
|
|
};
|
|
|
|
void IncrementBehaviorTestSmartPtrSharedPtr(AZStd::shared_ptr<BehaviorTestSmartPtr>& sp)
|
|
{
|
|
if (sp)
|
|
{
|
|
++sp->m_data;
|
|
}
|
|
};
|
|
|
|
void IncrementBehaviorTestSmartPtrIntrusivePtr(AZStd::intrusive_ptr<BehaviorTestSmartPtr>& sp)
|
|
{
|
|
if (sp)
|
|
{
|
|
++sp->m_data;
|
|
}
|
|
}
|
|
|
|
void PointerIsNullptr(BehaviorTestSmartPtr* value)
|
|
{
|
|
AZ_TEST_ASSERT(value == nullptr);
|
|
}
|
|
|
|
void BehaviorTestTemplatedOnDemandReflection(const AZStd::vector<int>& values)
|
|
{
|
|
g_globalData = 2500 + static_cast<int>(values.size());
|
|
}
|
|
|
|
int BehaviorGlobalMethodAfterBind()
|
|
{
|
|
return 3030;
|
|
}
|
|
|
|
int BehaviorGlobalPropertyGetAfterBind()
|
|
{
|
|
return g_globalData + 101;
|
|
}
|
|
|
|
void BehaviorGlobalPropertySetAfterBind(int data)
|
|
{
|
|
g_globalData = data;
|
|
}
|
|
|
|
struct BehaviorClassAfterBind
|
|
{
|
|
AZ_CLASS_ALLOCATOR(BehaviorClassAfterBind, AZ::SystemAllocator, 0);
|
|
AZ_TYPE_INFO(BehaviorClassAfterBind, "{72de521d-5880-43b7-93e9-ab06a3770946}");
|
|
int m_data;
|
|
};
|
|
|
|
class ClassRequestBusEvents : public AZ::EBusTraits
|
|
{
|
|
public:
|
|
virtual int GetData() = 0;
|
|
virtual void SetData(int data) = 0;
|
|
};
|
|
|
|
using ClassRequestEBus = EBus<ClassRequestBusEvents>;
|
|
|
|
class ClassInteractingWithEBus : public ClassRequestEBus::Handler
|
|
{
|
|
public:
|
|
AZ_TYPE_INFO(ClassInteractingWithEBus, "{28d9591f-d233-442f-af62-81096b64d703}");
|
|
|
|
ClassInteractingWithEBus()
|
|
: m_data(0)
|
|
{
|
|
BusConnect();
|
|
}
|
|
|
|
int GetData() override { return m_data; }
|
|
void SetData(int data) override { m_data = data; }
|
|
|
|
int m_data;
|
|
};
|
|
|
|
static void ScriptErrorAssertCB(AZ::ScriptContext*, AZ::ScriptContext::ErrorType, const char*)
|
|
{
|
|
AZ_TEST_ASSERT(false);
|
|
}
|
|
|
|
class BehaviorContextTest
|
|
: public BehaviorContextFixture
|
|
{
|
|
public:
|
|
|
|
void SetUp() override
|
|
{
|
|
BehaviorContextFixture::SetUp();
|
|
ResetGlobalVars();
|
|
}
|
|
|
|
void ResetGlobalVars()
|
|
{
|
|
g_globalValue = 501;
|
|
g_globalData = 0;
|
|
globalClassEnumValue = GlobalClassEnum::Value3;
|
|
}
|
|
|
|
void run()
|
|
{
|
|
// Constants/Enum
|
|
m_behaviorContext->Constant("globalConstant", []() { return 3.14f; });
|
|
m_behaviorContext->Constant("globalConstantMacro", BehaviorConstant(3.14f));
|
|
|
|
m_behaviorContext->Enum<(int)GE_VALUE1>("GE_VALUE1");
|
|
|
|
m_behaviorContext->Enum<(int)GlobalClassEnum::Value1>("Value1");
|
|
m_behaviorContext->Enum<(int)GlobalClassEnum::Value2>("Value2");
|
|
|
|
// Property
|
|
m_behaviorContext->Property("globalProperty", &globalPropertyGetter, &globalPropertySetter)
|
|
->Attribute("GlobalPropAttr", 1);
|
|
m_behaviorContext->Property("globalPropertyLambda", []() { return g_globalValue; }, [](int v) { g_globalValue = v; });
|
|
m_behaviorContext->Property("globalPropertyMacro", BehaviorValueProperty(&g_globalValue));
|
|
m_behaviorContext->Property("globalPropertyReadOnly", BehaviorValueGetter(&g_globalValue), nullptr); // read only property
|
|
// Property by address???
|
|
|
|
m_behaviorContext->Class<GlobalData>();
|
|
|
|
// Method
|
|
const int defaultIntValue = 20;
|
|
m_behaviorContext->Method("globalMethod", &globalMethod, m_behaviorContext->MakeDefaultValues(555))
|
|
->Attribute("GlobalMethodAttr", 5);
|
|
m_behaviorContext->Method("globalMethod1", &globalMethod1);
|
|
m_behaviorContext->Method("globalMethod2WithDefaultArgument", &globalMethod2WithDefaultArgument, { {{"Value", "An Integer argument", m_behaviorContext->MakeDefaultValue(defaultIntValue)}} });
|
|
|
|
m_behaviorContext->Method("globalMethodContainers", &globalMethodContainers);
|
|
m_behaviorContext->Method("globalMethodPair", &globalMethodPair);
|
|
|
|
m_behaviorContext->Method("globalMethodToOverride", &globalMethodToOverride)
|
|
->Attribute(Script::Attributes::MethodOverride, &globalMethodOverride);
|
|
m_behaviorContext->Method("globalMethodLarge", &globalMethodLarge);
|
|
m_behaviorContext->Method("TestTemplatedOnDemandReflection", &BehaviorTestTemplatedOnDemandReflection);
|
|
m_behaviorContext->Method("IncrementTestSmartPtrSharedPtr", &IncrementBehaviorTestSmartPtrSharedPtr);
|
|
m_behaviorContext->Method("IncrementTestSmartPtrIntrusivePtr", &IncrementBehaviorTestSmartPtrIntrusivePtr);
|
|
|
|
m_behaviorContext->Method("globalMethodGetClassEnum", &globalMethodGetClassEnum);
|
|
m_behaviorContext->Method("globalMethodSetClassEnum", &globalMethodSetClassEnum);
|
|
m_behaviorContext->Method("PointerIsNullptr", &PointerIsNullptr);
|
|
|
|
// Class
|
|
m_behaviorContext->Class<BehaviorTestClass>()->
|
|
Constructor<int>()->
|
|
Attribute("ClassAttr",10)->
|
|
Attribute(AZ::Script::Attributes::Storage,AZ::Script::Attributes::StorageType::Value)->
|
|
Attribute(AZ::Script::Attributes::ConstructorOverride,&TestScriptNonIntrusiveConstructor)->
|
|
Constant("epsilon", BehaviorConstant(0.001f))->
|
|
Enum<(int)BehaviorTestClass::ME_VALUE0>("ME_VALUE0")->
|
|
Enum<(int)BehaviorTestClass::MyClassEnum::MCE_VALUE0>("MCE_VALUE0")->
|
|
Enum<(int)BehaviorTestClass::MyClassEnum::MCE_VALUE1>("MCE_VALUE1")->
|
|
Method("Method1", &BehaviorTestClass::Method1)->
|
|
Attribute("MethodAttr", 20)->
|
|
Method("Method2", &BehaviorTestClass::Method2)->
|
|
Method("SpecialScriptMethod", &BehaviorTestClass::SpecialScriptMethod)->
|
|
Method("OverrideMethod", &BehaviorTestClass::MethodToOverrideInScript)->
|
|
Attribute(Script::Attributes::MethodOverride, &BehaviorTestClass::Method1ScriptOverride)->
|
|
Method("OverrideMethod1", &BehaviorTestClass::MethodToOverrideInScriptWithANonMember)->
|
|
Attribute(Script::Attributes::MethodOverride, &TestMethodToScriptOverride)->
|
|
Method("GetClass", &BehaviorTestClass::GetClass)->
|
|
Method("AddOperator", &BehaviorTestClass::AddOperator)->
|
|
Attribute(AZ::Script::Attributes::Operator,AZ::Script::Attributes::OperatorType::Add)->
|
|
Method("ToString", &BehaviorTestClass::ToString)->
|
|
Method("StaticMethod", &BehaviorTestClass::StaticMethod)->
|
|
Method("MemberWithDefaultValues", &BehaviorTestClass::BoundsCheckMethodWithDefaultValue, { {{"value", "Value which will be checked to be within the two bounds arguments"}, {"delta", "The epsilon value", m_behaviorContext->MakeDefaultValue(0.1f)},
|
|
{"minBound", "The minimum bounds value,", m_behaviorContext->MakeDefaultValue(0.0f)}, {"maxBound", "The maximum bounds value", m_behaviorContext->MakeDefaultValue(1.0f)}} })->
|
|
Property("data", &BehaviorTestClass::GetData, &BehaviorTestClass::SetData)->
|
|
Attribute("PropAttr", 30)->
|
|
Property("data1", BehaviorValueProperty(&BehaviorTestClass::m_data1))->
|
|
Property("dataReadOnly", BehaviorValueGetter(&BehaviorTestClass::m_dataReadOnly), nullptr);
|
|
|
|
m_behaviorContext->Class<BehaviorDerivedTestClass>()->
|
|
Property("derivedData", BehaviorValueProperty(&BehaviorDerivedTestClass::m_data));
|
|
|
|
m_behaviorContext->Class<BehaviorTestSmartPtr>()->
|
|
Method("TestFunction", &BehaviorTestSmartPtr::TestFunction)->
|
|
Property("data", BehaviorValueProperty(&BehaviorTestSmartPtr::m_data));
|
|
|
|
// Class Global Class enum wrapper
|
|
m_behaviorContext->Class<BehaviorGlobalClassEnumWrapper>()
|
|
->Constant("VALUE1", BehaviorConstant(GlobalClassEnum::Value1))
|
|
->Constant("VALUE2", BehaviorConstant(GlobalClassEnum::Value2))
|
|
;
|
|
|
|
// EBus
|
|
const AZStd::string_view defaultStringViewValue = "DEFAULT!!!!";
|
|
AZStd::string expectedDefaultValueAndStringResult = AZStd::string::format("Default Value: %s", defaultStringViewValue.data());
|
|
BehaviorDefaultValuePtr defaultStringViewBehaviorValue = aznew BehaviorDefaultValue(defaultStringViewValue);
|
|
BehaviorDefaultValuePtr superDefaultStringViewBehaviorValue = aznew BehaviorDefaultValue(AZStd::string_view("SUPER DEFAULT!!!!"));
|
|
|
|
m_behaviorContext->EBus<BehaviorTestBus>("TestBus")
|
|
->Attribute("EBusAttr", 40)
|
|
->Handler<BehaviorTestBusHandler>()
|
|
->Attribute("HandlerAttr", 50)
|
|
->Event("OnEvent", &BehaviorTestBus::Events::OnEvent)
|
|
->Attribute("EventAttr", 60)
|
|
->Event("OnEventWithResult", &BehaviorTestBus::Events::OnEventWithResult)
|
|
->Event("OnEventWithResultContainer", &BehaviorTestBus::Events::OnEventWithResultContainer)
|
|
->Event("OnEventWithClassEnumResult", &BehaviorTestBus::Events::OnEventWithClassEnumResult)
|
|
->Event("OnEventWithStringResult", &BehaviorTestBus::Events::OnEventWithStringResult)
|
|
->Event("OnEventResultWithBehaviorClassParameter", &BehaviorTestBus::Events::OnEventResultWithBehaviorClassParameter)
|
|
->Event("OnEventWithDefaultValueAndStringResult", &BehaviorTestBus::Events::OnEventWithDefaultValueAndStringResult,
|
|
{ {{"view1", "string_view without string trait", defaultStringViewBehaviorValue, AZ::BehaviorParameter::TR_NONE, AZ::BehaviorParameter::TR_STRING},
|
|
{"view2", "string_view with string trait", superDefaultStringViewBehaviorValue}} }) // Remove string trait from parameter
|
|
->Event("OnEventConst", &BehaviorTestBus::Events::OnEventConst)
|
|
;
|
|
|
|
// Calls
|
|
BehaviorMethod* method = m_behaviorContext->m_methods.find("globalMethod")->second;
|
|
int result = 0;
|
|
method->InvokeResult(result,3);
|
|
AZ_TEST_ASSERT(result == 6);
|
|
// check with default values
|
|
method->InvokeResult(result);
|
|
AZ_TEST_ASSERT(result == 558);
|
|
|
|
// Check attr
|
|
AZ_Assert(method->m_attributes[0].first == Crc32("GlobalMethodAttr"), "Expected attribute");
|
|
AttributeData<int>* attrData = azrtti_cast<AttributeData<int>*>(method->m_attributes[0].second);
|
|
AZ_Assert(attrData != nullptr, "This must be a valid attribute!");
|
|
int attrValue = attrData->Get(nullptr);
|
|
(void)attrValue;
|
|
AZ_Assert(attrValue == 5, "Data should be 5");
|
|
|
|
method = m_behaviorContext->m_methods.find("globalMethod1")->second;
|
|
method->Invoke();
|
|
|
|
method = m_behaviorContext->m_methods.find("globalMethod2WithDefaultArgument")->second;
|
|
int defaultValueMethodResult = 0;
|
|
method->InvokeResult(defaultValueMethodResult);
|
|
EXPECT_EQ(defaultIntValue + 1, defaultValueMethodResult);
|
|
|
|
AZStd::vector<int> values = { 10, 11, 12 };
|
|
method = m_behaviorContext->m_methods.find("globalMethodContainers")->second;
|
|
method->InvokeResult(result, values);
|
|
|
|
// Global property
|
|
BehaviorProperty* prop = m_behaviorContext->m_properties.find("globalProperty")->second;
|
|
prop->m_setter->Invoke(3);
|
|
prop->m_getter->InvokeResult(result);
|
|
// Check attr
|
|
AZ_Assert(prop->m_attributes[0].first == Crc32("GlobalPropAttr"), "Expected attribute");
|
|
attrData = azrtti_cast<AttributeData<int>*>(prop->m_attributes[0].second);
|
|
AZ_Assert(attrData != nullptr, "This must be a valid attribute!");
|
|
attrValue = attrData->Get(nullptr);
|
|
AZ_Assert(attrValue == 1, "Data should be 1");
|
|
|
|
// Global read only property
|
|
prop = m_behaviorContext->m_properties.find("globalPropertyReadOnly")->second;
|
|
AZ_Assert(prop->m_setter == nullptr, "This property should be read only!");
|
|
prop->m_getter->InvokeResult(result);
|
|
|
|
// Constant
|
|
prop = m_behaviorContext->m_properties.find("globalConstant")->second;
|
|
float fResult = 0.0f;
|
|
prop->m_getter->InvokeResult(fResult);
|
|
|
|
// Enum
|
|
GlobalClassEnum enumResult = GlobalClassEnum::Value2;
|
|
method = m_behaviorContext->m_methods.find("globalMethodGetClassEnum")->second;
|
|
method->InvokeResult(enumResult);
|
|
AZ_TEST_ASSERT(enumResult == GlobalClassEnum::Value1);
|
|
|
|
method = m_behaviorContext->m_methods.find("globalMethodSetClassEnum")->second;
|
|
method->Invoke(GlobalClassEnum::Value2);
|
|
|
|
// Classes
|
|
BehaviorClass* behaviorClass = m_behaviorContext->m_classes.find("BehaviorTestClass")->second;
|
|
BehaviorObject classInstance = behaviorClass->Create();
|
|
{
|
|
// Check attr
|
|
AZ_Assert(behaviorClass->m_attributes[0].first == Crc32("ClassAttr"), "Expected attribute");
|
|
attrData = azrtti_cast<AttributeData<int>*>(behaviorClass->m_attributes[0].second);
|
|
AZ_Assert(attrData != nullptr, "This must be a valid attribute!");
|
|
attrValue = attrData->Get(classInstance.m_address);
|
|
AZ_Assert(attrValue == 10, "Data should be 10");
|
|
}
|
|
method = behaviorClass->m_methods.find("Method1")->second;
|
|
method->Invoke(classInstance);
|
|
{
|
|
// Check attr
|
|
AZ_Assert(method->m_attributes[0].first == Crc32("MethodAttr"), "Expected attribute");
|
|
attrData = azrtti_cast<AttributeData<int>*>(method->m_attributes[0].second);
|
|
AZ_Assert(attrData != nullptr, "This must be a valid attribute!");
|
|
attrValue = attrData->Get(classInstance.m_address);
|
|
AZ_Assert(attrValue == 20, "Data should be 20");
|
|
}
|
|
method = behaviorClass->m_methods.find("Method2")->second;
|
|
EXPECT_TRUE(method->m_isConst);
|
|
// Generic class instance
|
|
int v = 5;
|
|
method->InvokeResult(result, classInstance, &v);
|
|
|
|
// Generic result
|
|
BehaviorObject tc;
|
|
method = behaviorClass->m_methods.find("GetClass")->second;
|
|
method->InvokeResult(tc, classInstance);
|
|
EXPECT_TRUE(method->IsMember());
|
|
|
|
method = behaviorClass->m_methods.find("StaticMethod")->second;
|
|
EXPECT_FALSE(method->IsMember());
|
|
|
|
method = behaviorClass->m_methods.find("MemberWithDefaultValues")->second;
|
|
EXPECT_TRUE(method->IsMember());
|
|
bool withinBounds = false;
|
|
method->InvokeResult(withinBounds, classInstance, 0.4f);
|
|
EXPECT_TRUE(withinBounds);
|
|
method->InvokeResult(withinBounds, classInstance, -1.1f);
|
|
EXPECT_FALSE(withinBounds);
|
|
|
|
// Class property
|
|
prop = behaviorClass->m_properties.find("data")->second;
|
|
EXPECT_TRUE(prop->m_getter->m_isConst);
|
|
EXPECT_FALSE(prop->m_setter->m_isConst);
|
|
prop->m_setter->Invoke(classInstance,12);
|
|
prop->m_getter->InvokeResult(result,classInstance);
|
|
{
|
|
// Check attr
|
|
AZ_Assert(prop->m_attributes[0].first == Crc32("PropAttr"), "Expected attribute");
|
|
attrData = azrtti_cast<AttributeData<int>*>(prop->m_attributes[0].second);
|
|
AZ_Assert(attrData != nullptr, "This must be a valid attribute!");
|
|
attrValue = attrData->Get(classInstance.m_address);
|
|
AZ_Assert(attrValue == 30, "Data should be 30");
|
|
}
|
|
|
|
|
|
// Class value property
|
|
prop = behaviorClass->m_properties.find("data1")->second;
|
|
prop->m_setter->Invoke(classInstance, 30);
|
|
prop->m_getter->InvokeResult(result, classInstance);
|
|
|
|
// Class value read only property
|
|
prop = behaviorClass->m_properties.find("dataReadOnly")->second;
|
|
AZ_Assert(prop->m_setter == nullptr, "Setter should be null!");
|
|
prop->m_getter->InvokeResult(result, classInstance);
|
|
|
|
// Test function conversion of parameters using RTTI (it this case base classes)
|
|
method = behaviorClass->m_methods.find("Method1")->second;
|
|
BehaviorDerivedTestClass tt;
|
|
method->Invoke(&tt);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// EBus
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// EBus - send events
|
|
BehaviorTestBus::BusIdType testId = 1;
|
|
BehaviorEBus* behaviorEBus = m_behaviorContext->m_ebuses.find("TestBus")->second;
|
|
|
|
{
|
|
// Check attr
|
|
AZ_Assert(behaviorEBus->m_attributes[0].first == Crc32("EBusAttr"), "Expected attribute");
|
|
attrData = azrtti_cast<AttributeData<int>*>(behaviorEBus->m_attributes[0].second);
|
|
AZ_Assert(attrData != nullptr, "This must be a valid attribute!");
|
|
attrValue = attrData->Get(nullptr);
|
|
AZ_Assert(attrValue == 40, "Data should be 40");
|
|
}
|
|
|
|
// create handler for events
|
|
BehaviorEBusHandler* testBusHandler = nullptr;
|
|
behaviorEBus->m_createHandler->InvokeResult(testBusHandler);
|
|
testBusHandler->InstallHook("OnEvent", &OnEventHook);
|
|
testBusHandler->InstallHook("OnEventWithResult", &OnEventWithResultHook);
|
|
|
|
// generic event handler
|
|
BehaviorEBusHandler* genericTestBusHandler = nullptr;
|
|
behaviorEBus->m_createHandler->InvokeResult(genericTestBusHandler);
|
|
genericTestBusHandler->InstallGenericHook("OnEvent", &OnEventGenericHook);
|
|
genericTestBusHandler->InstallGenericHook("OnEventWithResult", &OnEventGenericHook);
|
|
{
|
|
// Check attr
|
|
AZ_Assert(behaviorEBus->m_createHandler->m_attributes[0].first == Crc32("HandlerAttr"), "Expected attribute");
|
|
attrData = azrtti_cast<AttributeData<int>*>(behaviorEBus->m_createHandler->m_attributes[0].second);
|
|
AZ_Assert(attrData != nullptr, "This must be a valid attribute!");
|
|
attrValue = attrData->Get(nullptr);
|
|
AZ_Assert(attrValue == 50, "Data should be 50");
|
|
}
|
|
|
|
// connect handlers to receive events
|
|
testBusHandler->Connect(testId);
|
|
genericTestBusHandler->Connect(testId);
|
|
|
|
// fire some events
|
|
BehaviorEBusEventSender* ebusSender = &behaviorEBus->m_events.find("OnEvent")->second;
|
|
method = ebusSender->m_broadcast;
|
|
method->Invoke(10);
|
|
{
|
|
// Check attr
|
|
AZ_Assert(ebusSender->m_attributes[0].first == Crc32("EventAttr"), "Expected attribute");
|
|
attrData = azrtti_cast<AttributeData<int>*>(ebusSender->m_attributes[0].second);
|
|
AZ_Assert(attrData != nullptr, "This must be a valid attribute!");
|
|
attrValue = attrData->Get(nullptr);
|
|
AZ_Assert(attrValue == 60, "Data should be 60");
|
|
}
|
|
method = behaviorEBus->m_events.find("OnEventWithResult")->second.m_broadcast;
|
|
method->InvokeResult(result, 11);
|
|
method = behaviorEBus->m_events.find("OnEventWithResult")->second.m_event;
|
|
method->InvokeResult(result, testId, 15);
|
|
|
|
method = behaviorEBus->m_events.find("OnEvent")->second.m_queueBroadcast;
|
|
method->Invoke(1);
|
|
method = behaviorEBus->m_events.find("OnEvent")->second.m_queueEvent;
|
|
method->Invoke(testId, 2);
|
|
|
|
method = behaviorEBus->m_events.find("OnEventWithDefaultValueAndStringResult")->second.m_broadcast;
|
|
AZStd::string defaultStringResultValue;
|
|
method->InvokeResult(defaultStringResultValue);
|
|
EXPECT_EQ(expectedDefaultValueAndStringResult, defaultStringResultValue);
|
|
|
|
method = behaviorEBus->m_events.find("OnEventWithDefaultValueAndStringResult")->second.m_event;
|
|
defaultStringResultValue.clear();
|
|
method->InvokeResult(defaultStringResultValue, testId);
|
|
EXPECT_EQ(expectedDefaultValueAndStringResult, defaultStringResultValue);
|
|
|
|
EXPECT_EQ(3u, method->GetNumArguments());
|
|
EXPECT_EQ(1u, method->GetMinNumberOfArguments());
|
|
EXPECT_TRUE(method->HasResult());
|
|
const AZ::BehaviorParameter* stringResultParam = method->GetResult();
|
|
EXPECT_NE(nullptr, stringResultParam);
|
|
EXPECT_TRUE(AZ::BehaviorContextHelper::IsStringParameter(*stringResultParam));
|
|
|
|
// The first string_view param has removed the TR_STRING trait
|
|
const AZ::BehaviorParameter* stringView1Param = method->GetArgument(1);
|
|
EXPECT_NE(nullptr, stringView1Param);
|
|
EXPECT_FALSE(AZ::BehaviorContextHelper::IsStringParameter(*stringView1Param));
|
|
|
|
// The second string_view param has the TR_STRING trait
|
|
const AZ::BehaviorParameter* stringView2Param = method->GetArgument(2);
|
|
EXPECT_NE(nullptr, stringView2Param);
|
|
EXPECT_TRUE(AZ::BehaviorContextHelper::IsStringParameter(*stringView2Param));
|
|
|
|
method = behaviorEBus->m_events.find("OnEventConst")->second.m_event;
|
|
EXPECT_TRUE(method->m_isConst);
|
|
|
|
method = behaviorEBus->m_events.find("OnEventConst")->second.m_broadcast;
|
|
EXPECT_TRUE(method->m_isConst);
|
|
|
|
method = behaviorEBus->m_events.find("OnEventConst")->second.m_queueEvent;
|
|
EXPECT_TRUE(method->m_isConst);
|
|
|
|
method = behaviorEBus->m_events.find("OnEventConst")->second.m_queueBroadcast;
|
|
EXPECT_TRUE(method->m_isConst);
|
|
|
|
BehaviorTestBus::ExecuteQueuedEvents();
|
|
|
|
delete testBusHandler;
|
|
delete genericTestBusHandler;
|
|
|
|
// class enums
|
|
{
|
|
NativeBehaviorTestBusHandler nativeTestBusHandler;
|
|
ebusSender = &behaviorEBus->m_events.find("OnEventWithClassEnumResult")->second;
|
|
method = ebusSender->m_broadcast;
|
|
GlobalClassEnum classEnumResult = GlobalClassEnum::Value1;
|
|
method->InvokeResult(classEnumResult);
|
|
AZ_TEST_ASSERT(classEnumResult == GlobalClassEnum::Value2);
|
|
}
|
|
|
|
behaviorClass->Destroy(classInstance);
|
|
|
|
m_behaviorContext->Method("globalGenericMethod", &globalGenericMethod);
|
|
{
|
|
ScriptContext sc;
|
|
sc.BindTo(m_behaviorContext);
|
|
|
|
// global methods
|
|
sc.Execute("value = globalMethod(12)");
|
|
sc.Execute("value = globalGenericMethod(value)");
|
|
sc.Execute("value = globalMethodToOverride(20)");
|
|
sc.Execute("value = globalMethodLarge()");
|
|
|
|
// global method with a vector (OnDemandReflection)
|
|
sc.Execute("intVector = vector_int()");
|
|
sc.Execute("intVector:push_back(74)");
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
ScriptContext::ErrorHook oldHook = sc.GetErrorHook();
|
|
sc.SetErrorHook(ScriptErrorAssertCB);
|
|
sc.Execute("intVector.push_back(74)"); // missing self pointer
|
|
sc.SetErrorHook(oldHook);
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
|
|
// test index operators
|
|
sc.Execute("globalProperty = intVector[1]"); // read a value using custom operator
|
|
AZ_TEST_ASSERT(g_globalValue == 74);
|
|
//sc.Execute("intVector[6] = 128"); // write a value (set and resize) using custom operator
|
|
//sc.Execute("globalProperty = intVector[2]");
|
|
//AZ_TEST_ASSERT(g_globalValue == 0); // on resize we initialize with default value
|
|
//sc.Execute("globalProperty = intVector[6]");
|
|
//AZ_TEST_ASSERT(g_globalValue == 128);
|
|
|
|
sc.Execute("pair = pair_int_bool()");
|
|
sc.Execute("pair.first = 7; pair.second = true;");
|
|
sc.Execute("globalProperty = globalMethodPair(pair)");
|
|
EXPECT_EQ(7, g_globalValue);
|
|
|
|
sc.Execute("TestTemplatedOnDemandReflection(intVector)");
|
|
|
|
// global properties
|
|
sc.Execute("value = globalProperty");
|
|
sc.Execute("globalProperty = value + 10");
|
|
|
|
// classes
|
|
sc.Execute("testClass = BehaviorTestClass(11)");
|
|
|
|
// class methods
|
|
sc.Execute("testClass:Method1()");
|
|
sc.Execute("testClass:Method2(12)");
|
|
sc.Execute("value = testClass:SpecialScriptMethod(11)");
|
|
sc.Execute("value = testClass:OverrideMethod(12)");
|
|
sc.Execute("value = testClass:OverrideMethod1(13)");
|
|
|
|
sc.Execute("testClass1 = testClass:GetClass()");
|
|
|
|
// Test static methods
|
|
sc.Execute("BehaviorTestClass.StaticMethod(1090)");
|
|
EXPECT_EQ(1090, g_globalData);
|
|
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
sc.Execute("testClass:StaticMethod(1090)");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
sc.Execute("BehaviorTestClass.Method1(nil)");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
|
|
// class properties
|
|
sc.Execute("value = testClass.data");
|
|
sc.Execute("testClass.data = value + 11");
|
|
|
|
// class operators
|
|
sc.Execute("testClass1 = BehaviorTestClass() testClass1.data = 203");
|
|
sc.Execute("testClass2 = testClass1 + testClass");
|
|
sc.Execute("testClass1.data = testClass2.data + 23");
|
|
|
|
// derived classes
|
|
sc.Execute("derivedTestClass = BehaviorDerivedTestClass()");
|
|
sc.Execute("derivedTestClass.derivedData = 101");
|
|
sc.Execute("derivedTestClass.data = 13");
|
|
|
|
// smart ptr
|
|
sc.Execute("smartIntrusivePtr = intrusive_ptr_BehaviorTestSmartPtr(BehaviorTestSmartPtr())");
|
|
sc.Execute("IncrementTestSmartPtrIntrusivePtr(smartIntrusivePtr)"); // pass the smartPointer to a function as a smart pointer
|
|
sc.Execute("rawPointer = smartIntrusivePtr:get() rawPointer:TestFunction()"); // get the raw pointer and call a function
|
|
sc.Execute("smartIntrusivePtr:TestFunction()"); // call the wrapped function directly from the smart pointer
|
|
|
|
sc.Execute("PointerIsNullptr(nil)");
|
|
|
|
// ebus
|
|
NativeBehaviorTestBusHandler myTestBusHandler;
|
|
|
|
// broadcast
|
|
sc.Execute("TestBus.Broadcast.OnEvent(10)");
|
|
|
|
// class enum
|
|
sc.Execute("eventClassResult = TestBus.Broadcast.OnEventWithClassEnumResult()");
|
|
sc.Execute("globalMethodSetClassEnum(eventClassResult)");
|
|
|
|
// events
|
|
sc.Execute("TestBus.Event.OnEvent(0,20)"); // send event to ID 0 (not listener)
|
|
sc.Execute("TestBus.Event.OnEvent(1,30)"); // send event to ID 1
|
|
|
|
// queue broadcast
|
|
sc.Execute("TestBus.QueueBroadcast.OnEvent(50)");
|
|
|
|
// queue event
|
|
sc.Execute("TestBus.QueueEvent.OnEvent(1,60)");
|
|
|
|
// queue function
|
|
sc.Execute("function QueuedFunctionOnTestBus(a,b,c)\
|
|
globalProperty = a + b + c\
|
|
end\
|
|
TestBus.QueueFunction(QueuedFunctionOnTestBus,1,2,3)");
|
|
|
|
BehaviorTestBus::ExecuteQueuedEvents();
|
|
|
|
myTestBusHandler.BusDisconnect();
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// handling ebus multiple return values
|
|
NativeBehaviorTestBusHandler myTestBusHandler1;
|
|
NativeBehaviorTestBusHandler myTestBusHandler2;
|
|
myTestBusHandler1.m_enumResult = GlobalClassEnum::Value1;
|
|
myTestBusHandler2.m_enumResult = GlobalClassEnum::Value2;
|
|
|
|
// use a table as result container
|
|
sc.Execute("eventClassResult = { TestBus.Broadcast.OnEventWithClassEnumResult() }");
|
|
globalClassEnumValue = GlobalClassEnum::Value3;
|
|
sc.Execute("globalMethodSetClassEnum(eventClassResult[1])");
|
|
AZ_TEST_ASSERT(globalClassEnumValue == GlobalClassEnum::Value2);
|
|
sc.Execute("globalMethodSetClassEnum(eventClassResult[2])");
|
|
AZ_TEST_ASSERT(globalClassEnumValue == GlobalClassEnum::Value1);
|
|
|
|
// use multiple variables as result container
|
|
sc.Execute("result1, result2 = TestBus.Broadcast.OnEventWithClassEnumResult()");
|
|
globalClassEnumValue = GlobalClassEnum::Value3;
|
|
sc.Execute("globalMethodSetClassEnum(result1)");
|
|
AZ_TEST_ASSERT(globalClassEnumValue == GlobalClassEnum::Value2);
|
|
sc.Execute("globalMethodSetClassEnum(result2)");
|
|
AZ_TEST_ASSERT(globalClassEnumValue == GlobalClassEnum::Value1);
|
|
|
|
// collect garbage before running next test so any destructors get called
|
|
sc.Execute(R"LUA(
|
|
collectgarbage()
|
|
)LUA");
|
|
|
|
g_globalTestClassesConstructed = 0;
|
|
g_globalTestClassesDestructed = 0;
|
|
|
|
// test whether the behavior parameters that are passed by value have their
|
|
// constructors/destructors called equally
|
|
sc.Execute(R"LUA(
|
|
local behaviorParameter = BehaviorTestClass();
|
|
local result = TestBus.Broadcast.OnEventResultWithBehaviorClassParameter(behaviorParameter);
|
|
behaviorParameter = nil;
|
|
result = nil;
|
|
collectgarbage();
|
|
)LUA");
|
|
|
|
AZ_TEST_ASSERT(g_globalTestClassesConstructed > 0);
|
|
AZ_TEST_ASSERT(g_globalTestClassesDestructed > 0);
|
|
AZ_TEST_ASSERT(g_globalTestClassesConstructed == g_globalTestClassesDestructed);
|
|
|
|
myTestBusHandler1.BusDisconnect();
|
|
myTestBusHandler2.BusDisconnect();
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// create handler
|
|
sc.Execute(R"LUA(
|
|
testBusHandler = {}
|
|
function testBusHandler:OnEvent(data)
|
|
globalProperty = data
|
|
globalProperty = TestBus.GetCurrentBusId()
|
|
end
|
|
function testBusHandler:OnEventWithClassEnumResult()
|
|
return BehaviorGlobalClassEnumWrapper.VALUE2
|
|
end
|
|
function testBusHandler:OnEventWithStringResult()
|
|
return 'success';
|
|
end
|
|
function testBusHandler:OnEventWithClassResult()
|
|
local result = BehaviorTestClass(100);
|
|
result.data = 100;
|
|
return result;
|
|
end
|
|
testBusHandler = TestBus.Connect(testBusHandler,1)
|
|
)LUA");
|
|
|
|
// check if we can handle event
|
|
BehaviorTestBus::Broadcast(&BehaviorTestBus::Events::OnEvent, 101);
|
|
|
|
// broadcast a class enum
|
|
GlobalClassEnum globalClassEnumResult = GlobalClassEnum::Value1;
|
|
BehaviorTestBus::BroadcastResult(globalClassEnumResult, &BehaviorTestBus::Events::OnEventWithClassEnumResult);
|
|
AZ_TEST_ASSERT(globalClassEnumResult == GlobalClassEnum::Value2);
|
|
|
|
AZStd::string stringResult;
|
|
BehaviorTestBus::BroadcastResult(stringResult, &BehaviorTestBus::Events::OnEventWithStringResult);
|
|
EXPECT_STREQ("success", stringResult.c_str());
|
|
|
|
BehaviorTestClass classResult;
|
|
BehaviorTestBus::BroadcastResult(classResult, &BehaviorTestBus::Events::OnEventWithClassResult);
|
|
EXPECT_EQ(100, classResult.m_data);
|
|
|
|
// disconnect
|
|
sc.Execute("testBusHandler:Disconnect()");
|
|
|
|
// test events
|
|
BehaviorTestBus::Broadcast(&BehaviorTestBus::Events::OnEvent, 201); // it should have no effect
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Test reflecting after the bind
|
|
m_behaviorContext->Method("BehaviorGlobalMethodAfterBind", &BehaviorGlobalMethodAfterBind);
|
|
sc.Execute("afterBindValue = BehaviorGlobalMethodAfterBind()");
|
|
m_behaviorContext->Property("globalPropertyAfterBind", &BehaviorGlobalPropertyGetAfterBind, &BehaviorGlobalPropertySetAfterBind);
|
|
sc.Execute("globalPropertyAfterBind = afterBindValue");
|
|
AZ_TEST_ASSERT(g_globalData == 3030);
|
|
|
|
m_behaviorContext->Class<BehaviorClassAfterBind>()->
|
|
Property("data", BehaviorValueProperty(&BehaviorClassAfterBind::m_data));
|
|
|
|
sc.Execute("classAfterBind = BehaviorClassAfterBind() classAfterBind.data = 1012 globalPropertyAfterBind = classAfterBind.data");
|
|
AZ_TEST_ASSERT(g_globalData == 1012);
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Classes using EBuses for communication and virtual ebus properties
|
|
m_behaviorContext->Class<ClassInteractingWithEBus>()->
|
|
RequestBus("ClassRequestEBus");
|
|
|
|
m_behaviorContext->EBus<ClassRequestEBus>("ClassRequestEBus")->
|
|
Attribute(AZ::Script::Attributes::DisallowBroadcast, true)->
|
|
Event("GetData", &ClassRequestEBus::Events::GetData)->
|
|
Event("SetData", &ClassRequestEBus::Events::SetData)->
|
|
VirtualProperty("data", "GetData", "SetData");
|
|
|
|
BehaviorClass* classInteractingWithEBus = m_behaviorContext->m_classes.find("ClassInteractingWithEBus")->second;
|
|
AZ_TEST_ASSERT(classInteractingWithEBus->m_requestBuses.size() == 1);
|
|
AZ_TEST_ASSERT(classInteractingWithEBus->m_requestBuses.find("ClassRequestEBus") != classInteractingWithEBus->m_requestBuses.end());
|
|
|
|
BehaviorEBus* classRequestBus = m_behaviorContext->m_ebuses.find("ClassRequestEBus")->second;
|
|
AZ_TEST_ASSERT(classRequestBus->m_virtualProperties.size() == 1);
|
|
const BehaviorEBus::VirtualProperty& virtualEBusProperty = classRequestBus->m_virtualProperties.find("data")->second;
|
|
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
sc.Execute("ClassRequestEBus.Broadcast.GetData()");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
|
|
{
|
|
ClassInteractingWithEBus listenerClassInstance; // setup a listener, otherwise property value can't be stored
|
|
listenerClassInstance.m_data = 1010;
|
|
|
|
// get the value of the ebus property. In practice those request buses use ID to identify a single handler, then we just use m_event instead of m_broadcast
|
|
int data = 0;
|
|
virtualEBusProperty.m_getter->m_broadcast->InvokeResult(data);
|
|
AZ_TEST_ASSERT(data == 1010);
|
|
|
|
// set the value of the ebus property.
|
|
virtualEBusProperty.m_setter->m_broadcast->Invoke(3030);
|
|
AZ_TEST_ASSERT(listenerClassInstance.m_data == 3030);
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
}
|
|
}
|
|
};
|
|
|
|
TEST_F(BehaviorContextTest, LuaTest)
|
|
{
|
|
run();
|
|
}
|
|
|
|
TEST_F(BehaviorContextTest, LuaBehaviorEBusHandlerWithDocMacroCompilesSuccessfully)
|
|
{
|
|
AZ::BehaviorContext behaviorContext;
|
|
behaviorContext.EBus<BehaviorTestBus>("TestBusWithEbusHandlerThatSupportsNameAndTooltip")
|
|
->Handler<BehaviorTestBusHandlerWithDoc>()
|
|
;
|
|
auto BehaviorEBusFoundIt = behaviorContext.m_ebuses.find("TestBusWithEbusHandlerThatSupportsNameAndTooltip");
|
|
ASSERT_NE(behaviorContext.m_ebuses.end(), BehaviorEBusFoundIt);
|
|
AZ::BehaviorEBus* behaviorEBus = BehaviorEBusFoundIt->second;
|
|
ASSERT_NE(nullptr, behaviorEBus->m_createHandler);
|
|
|
|
AZ::BehaviorEBusHandler* handlerResult{};
|
|
EXPECT_TRUE(behaviorEBus->m_createHandler->InvokeResult(handlerResult));
|
|
ASSERT_NE(nullptr, handlerResult);
|
|
auto handlerDeleter = [behaviorEBus](AZ::BehaviorEBusHandler* handler)
|
|
{
|
|
behaviorEBus->m_destroyHandler->Invoke(handler);
|
|
};
|
|
AZStd::unique_ptr<AZ::BehaviorEBusHandler, decltype(handlerDeleter)> ebusHandler(handlerResult, AZStd::move(handlerDeleter));
|
|
|
|
const AZ::BehaviorEBusHandler::EventArray handlerEvents = ebusHandler->GetEvents();
|
|
auto stringViewEventIt = AZStd::find_if(handlerEvents.begin(), handlerEvents.end(), [](const AZ::BehaviorEBusHandler::BusForwarderEvent& handlerEvent)
|
|
{
|
|
return strcmp(handlerEvent.m_name, "OnEventWithDefaultValueAndStringResult") == 0;
|
|
});
|
|
ASSERT_NE(handlerEvents.end(), stringViewEventIt);
|
|
ASSERT_EQ(AZ::eBehaviorBusForwarderEventIndices::ParameterFirst + 2, stringViewEventIt->m_metadataParameters.size());
|
|
|
|
EXPECT_EQ("defaultView", stringViewEventIt->m_metadataParameters[AZ::eBehaviorBusForwarderEventIndices::ParameterFirst].m_name);
|
|
EXPECT_EQ("string_view which contains literal to print by default", stringViewEventIt->m_metadataParameters[AZ::eBehaviorBusForwarderEventIndices::ParameterFirst].m_toolTip);
|
|
EXPECT_EQ("unusedView", stringViewEventIt->m_metadataParameters[AZ::eBehaviorBusForwarderEventIndices::ParameterFirst + 1].m_name);
|
|
EXPECT_EQ("Unused test parameter", stringViewEventIt->m_metadataParameters[AZ::eBehaviorBusForwarderEventIndices::ParameterFirst + 1].m_toolTip);
|
|
}
|
|
} // namespace Unittest
|
|
|
|
#if !defined(AZCORE_EXCLUDE_LUA)
|
|
|
|
namespace UnitTest
|
|
{
|
|
class IncompleteType;
|
|
}
|
|
|
|
namespace AZ
|
|
{
|
|
AZ_TYPE_INFO_SPECIALIZE(UnitTest::IncompleteType, "{53CC592A-E4B8-4F89-A293-87C7838A6108}");
|
|
AZ_TYPE_INFO_SPECIALIZE(UnitTest::GlobalEnum, "{8A34A34C-B547-4724-8D3B-E12B8774E338}");
|
|
}
|
|
|
|
using namespace AZ;
|
|
|
|
namespace UnitTest
|
|
{
|
|
int s_globalVar = 0;
|
|
bool s_globalVarBool = false;
|
|
float s_globalVar1 = 0.0f;
|
|
float s_globalVar2ReadOnly = 10.0f;
|
|
float s_globalVar3WriteOnly = 0.0f;
|
|
|
|
AZStd::string s_globalVarString;
|
|
|
|
int s_globalField = 0;
|
|
int s_globalField1 = 12;
|
|
int s_globalField2 = 0;
|
|
|
|
static int s_errorCount = 0;
|
|
|
|
IncompleteType* s_globalIncompletePtr = static_cast<IncompleteType*>(AZ_INVALID_POINTER);
|
|
IncompleteType* s_globalIncompletePtr1 = nullptr;
|
|
|
|
void GlobalVarSet(int v)
|
|
{
|
|
s_globalVar = v;
|
|
}
|
|
|
|
int GlobalVarGet()
|
|
{
|
|
return s_globalVar;
|
|
}
|
|
|
|
void GlobalVarStringSet(const char* v)
|
|
{
|
|
s_globalVarString = v;
|
|
}
|
|
|
|
const char* GlobalVarStringGet()
|
|
{
|
|
return s_globalVarString.c_str();
|
|
}
|
|
|
|
void GlobalCheckString(AZStd::string globalVarString)
|
|
{
|
|
AZ_TEST_ASSERT(s_globalVarString == globalVarString);
|
|
}
|
|
|
|
void GlobalCheckStringRef(const AZStd::string& globalVarString)
|
|
{
|
|
AZ_TEST_ASSERT(s_globalVarString == globalVarString);
|
|
}
|
|
|
|
int GlobalFunc0()
|
|
{
|
|
return 0;
|
|
}
|
|
int GlobalFunc1(int)
|
|
{
|
|
return 1;
|
|
}
|
|
int GlobalFunc2(int, float)
|
|
{
|
|
return 2;
|
|
}
|
|
int GlobalFunc3(int, float, bool)
|
|
{
|
|
return 3;
|
|
}
|
|
int GlobalFunc4(int, float, bool, int)
|
|
{
|
|
return 4;
|
|
}
|
|
int GlobalFunc5(int, float, bool, int, float)
|
|
{
|
|
return 5;
|
|
}
|
|
int GlobalFunc0Override()
|
|
{
|
|
return 10;
|
|
}
|
|
void GlobalVoidFunc0()
|
|
{
|
|
s_globalVar = 0;
|
|
}
|
|
void GlobalVoidFunc1(int v)
|
|
{
|
|
s_globalVar = v;
|
|
}
|
|
void GlobalVoidFunc2(int v, float) { s_globalVar = v; }
|
|
void GlobalVoidFunc3(int v, float, bool) { s_globalVar = v; }
|
|
void GlobalVoidFunc4(int v, float, bool, int) { s_globalVar = v; }
|
|
void GlobalVoidFunc5(int v, float, bool, int, float) { s_globalVar = v; }
|
|
|
|
void GlobalEnumFunc(GlobalEnum value) { s_globalVar = static_cast<int>(value); }
|
|
|
|
void VariadicFunc(ScriptDataContext& context)
|
|
{
|
|
s_globalVar = context.GetNumArguments();
|
|
context.PushResult(s_globalVar * 2);
|
|
}
|
|
|
|
class ScriptClass2;
|
|
class ScriptClass4;
|
|
class ScriptClass5;
|
|
|
|
class ScriptBindTest
|
|
: public BehaviorContextFixture
|
|
{
|
|
static ScriptBindTest* s_scriptBindInstance;
|
|
public:
|
|
AZStd::fixed_vector<ScriptClass2*, 10> scriptClass2Instances;
|
|
|
|
AZStd::fixed_vector< AZStd::shared_ptr<ScriptClass4>, 10 > scriptClass4lockedInstances;
|
|
int scriptClass4numInstances = 0;
|
|
|
|
AZStd::fixed_vector< AZStd::intrusive_ptr<ScriptClass5>, 10 > scriptClass5lockedInstances;
|
|
int scriptClass5numInstances = 0;
|
|
|
|
void SetUp() override;
|
|
|
|
void TearDown() override;
|
|
|
|
void ResetGlobalVars();
|
|
|
|
static ScriptBindTest* GetScriptBindInstance()
|
|
{
|
|
return s_scriptBindInstance;
|
|
}
|
|
|
|
static bool EnumClass(const char* name, const AZ::Uuid& /*typeid*/, void* /*userData*/);
|
|
|
|
static bool EnumMethod(const AZ::Uuid* /*type id*/, const char* name, const char* dbgParamInfo, void* /*userData*/);
|
|
|
|
static bool EnumProperty(const AZ::Uuid* /*type id*/, const char* name, bool isRead, bool isWrite, void* /*userData*/);
|
|
|
|
static void ScriptErrorCB(AZ::ScriptContext*, AZ::ScriptContext::ErrorType, const char* details);
|
|
|
|
void run();
|
|
};
|
|
|
|
ScriptBindTest* ScriptBindTest::s_scriptBindInstance = nullptr;
|
|
|
|
class ScriptClass* s_scriptClassInstance = nullptr;
|
|
|
|
class ScriptClass
|
|
{
|
|
public:
|
|
AZ_TYPE_INFO(ScriptClass, "{7b91cb47-a271-4031-8114-56d38efabc4f}");
|
|
AZ_CLASS_ALLOCATOR(ScriptClass, SystemAllocator, 0);
|
|
|
|
enum LocalEnum
|
|
{
|
|
SC_ET_VALUE2 = 2,
|
|
SC_ET_VALUE3,
|
|
};
|
|
|
|
ScriptClass()
|
|
: m_data(0)
|
|
, m_data1(0.0f)
|
|
, m_data2ReadOnly(11.0f)
|
|
, m_data3WriteOnly(0.0f)
|
|
{
|
|
if (s_scriptClassInstance == nullptr)
|
|
{
|
|
s_scriptClassInstance = this;
|
|
}
|
|
}
|
|
ScriptClass(float dataReadOnly)
|
|
: m_data(0)
|
|
, m_data1(0.0f)
|
|
, m_data2ReadOnly(dataReadOnly)
|
|
, m_data3WriteOnly(0.0f)
|
|
{
|
|
if (s_scriptClassInstance == nullptr)
|
|
{
|
|
s_scriptClassInstance = this;
|
|
}
|
|
}
|
|
|
|
~ScriptClass()
|
|
{
|
|
if (s_scriptClassInstance == this)
|
|
{
|
|
s_scriptClassInstance = nullptr;
|
|
}
|
|
}
|
|
|
|
int MemberFunc0() { return 0; }
|
|
int MemberFunc1(int) { return 1; }
|
|
int MemberFunc2(int, float) { return 2; }
|
|
int MemberFunc3(int, float, bool) { return 3; }
|
|
int MemberFunc4(int, float, bool, int) { return 4; }
|
|
int MemberFunc5(int, float, bool, int, float) { return 5; }
|
|
void MemberVoidFunc0() { m_data = 0; }
|
|
void MemberVoidFunc1(int v) { m_data = v; }
|
|
void MemberVoidFunc2(int v, float) { m_data = v; }
|
|
void MemberVoidFunc3(int v, float, bool) { m_data = v; }
|
|
void MemberVoidFunc4(int v, float, bool, int) { m_data = v; }
|
|
void MemberVoidFunc5(int v, float, bool, int, float) { m_data = v; }
|
|
|
|
int GetData() const { return m_data; }
|
|
void SetData(int data) { m_data = data; }
|
|
|
|
void VariadicFunc(ScriptDataContext& context)
|
|
{
|
|
s_globalVar = context.GetNumArguments();
|
|
}
|
|
|
|
int m_data;
|
|
float m_data1;
|
|
float m_data2ReadOnly;
|
|
float m_data3WriteOnly;
|
|
|
|
struct ScriptClassNested
|
|
{
|
|
ScriptClassNested()
|
|
: m_data(100) {}
|
|
int m_data;
|
|
};
|
|
};
|
|
|
|
void* ScriptClassAllocate(void* userData)
|
|
{
|
|
(void)userData;
|
|
return azmalloc(sizeof(ScriptClass),AZStd::alignment_of<ScriptClass>::value,AZ::SystemAllocator,"ScriptClass");
|
|
}
|
|
|
|
void ScriptClassFree(void* obj, void* userData)
|
|
{
|
|
(void)userData;
|
|
azfree(obj, AZ::SystemAllocator, sizeof(ScriptClass), AZStd::alignment_of<ScriptClass>::value);
|
|
}
|
|
|
|
class ScriptClass1
|
|
{
|
|
public:
|
|
AZ_CLASS_ALLOCATOR(ScriptClass1, SystemAllocator, 0);
|
|
|
|
ScriptClass1()
|
|
: m_data(0.0f) {}
|
|
|
|
float m_data;
|
|
};
|
|
|
|
|
|
class ScriptClass2
|
|
{
|
|
public:
|
|
AZ_TYPE_INFO(ScriptClass2, "{b4482151-b246-4c95-9ab3-db4589b3ffa0}");
|
|
AZ_CLASS_ALLOCATOR(ScriptClass2, SystemAllocator, 0);
|
|
|
|
ScriptClass2(int data)
|
|
: m_data(data)
|
|
{
|
|
ScriptBindTest::GetScriptBindInstance()->scriptClass2Instances.push_back(this);
|
|
}
|
|
ScriptClass2(const ScriptClass2& rhs)
|
|
{
|
|
m_data = rhs.m_data;
|
|
ScriptBindTest::GetScriptBindInstance()->scriptClass2Instances.push_back(this);
|
|
}
|
|
~ScriptClass2()
|
|
{
|
|
auto& scriptClass2Instances = ScriptBindTest::GetScriptBindInstance()->scriptClass2Instances;
|
|
auto instanceIt = AZStd::find(scriptClass2Instances.begin(), scriptClass2Instances.end(), this);
|
|
if (instanceIt != scriptClass2Instances.end())
|
|
{
|
|
scriptClass2Instances.erase(instanceIt);
|
|
}
|
|
}
|
|
|
|
void VariadicFunc(ScriptDataContext& context)
|
|
{
|
|
(void)context;
|
|
}
|
|
|
|
bool operator<(const ScriptClass2& rhs) const { return m_data < rhs.m_data; }
|
|
bool operator<=(const ScriptClass2& rhs) const { return m_data < rhs.m_data; }
|
|
bool operator==(const ScriptClass2& rhs) const { return m_data == rhs.m_data; }
|
|
|
|
ScriptClass2 ScriptAdd(const ScriptClass2& rhs) const { return ScriptClass2(m_data + rhs.m_data); }
|
|
ScriptClass2 ScriptSub(const ScriptClass2& rhs) const { return ScriptClass2(m_data - rhs.m_data); }
|
|
ScriptClass2 ScriptMul(const ScriptClass2& rhs) const { return ScriptClass2(m_data * rhs.m_data); }
|
|
ScriptClass2 ScriptDiv(const ScriptClass2& rhs) const { return ScriptClass2(m_data / rhs.m_data); }
|
|
ScriptClass2 ScriptMod(const ScriptClass2& rhs) const { return ScriptClass2(m_data % rhs.m_data); }
|
|
ScriptClass2 ScriptPow(const ScriptClass2& rhs) const { return ScriptClass2((int)pow((float)m_data, rhs.m_data)); }
|
|
ScriptClass2 ScriptUnary() const
|
|
{
|
|
return ScriptClass2(-m_data);
|
|
}
|
|
ScriptClass2 ScriptConcat(const ScriptClass2& rhs) const { return ScriptClass2(m_data + rhs.m_data); }
|
|
int ScriptLength() const { return m_data; }
|
|
|
|
const char* ToString() const { return "This is ScriptClass2"; }
|
|
|
|
ScriptClass2* Forward() { return this; }
|
|
|
|
int m_data;
|
|
};
|
|
|
|
class ScriptClass3* s_scriptClass3Instance = nullptr;
|
|
class ScriptClass3
|
|
{
|
|
public:
|
|
AZ_TYPE_INFO(ScriptClass3, "{058b255b-1abf-4831-9ea9-8b8cb6a7a634}");
|
|
AZ_CLASS_ALLOCATOR(ScriptClass3, SystemAllocator, 0);
|
|
|
|
ScriptClass3()
|
|
{
|
|
m_bool = false;
|
|
m_intData = 0;
|
|
m_floatData = 0.0f;
|
|
m_incompletePtr = nullptr;
|
|
}
|
|
|
|
ScriptClass3(ScriptDataContext& ctx)
|
|
{
|
|
m_bool = false;
|
|
m_intData = 0;
|
|
m_floatData = 0.0f;
|
|
m_incompletePtr = nullptr;
|
|
// unpack variadic parameters
|
|
AZ_TEST_ASSERT(ctx.GetNumArguments() == 4);
|
|
AZ_TEST_ASSERT(ctx.IsBoolean(0));
|
|
AZ_TEST_ASSERT(ctx.IsNumber(1));
|
|
AZ_TEST_ASSERT(ctx.IsNumber(2));
|
|
AZ_TEST_ASSERT(ctx.IsRegisteredClass(3) == false);
|
|
ctx.ReadArg(0, m_bool);
|
|
ctx.ReadArg(1, m_intData);
|
|
ctx.ReadArg(2, m_floatData);
|
|
ctx.ReadArg(3, m_incompletePtr);
|
|
|
|
m_scriptClass2 = nullptr;
|
|
|
|
if (s_scriptClass3Instance == nullptr)
|
|
{
|
|
s_scriptClass3Instance = this;
|
|
}
|
|
}
|
|
|
|
~ScriptClass3()
|
|
{
|
|
if (s_scriptClass3Instance == this)
|
|
{
|
|
s_scriptClass3Instance = nullptr;
|
|
}
|
|
}
|
|
|
|
const char* ToString() const { return "This is ScriptClass3"; }
|
|
|
|
bool m_bool;
|
|
int m_intData;
|
|
float m_floatData;
|
|
IncompleteType* m_incompletePtr;
|
|
ScriptClass2* m_scriptClass2;
|
|
|
|
private:
|
|
// TODO: Simulate private copy constructor when AZStd::is_copy_constructible works
|
|
//ScriptClass3(const ScriptClass3&) = delete;
|
|
};
|
|
|
|
class ScriptClass4
|
|
{
|
|
public:
|
|
AZ_TYPE_INFO(ScriptClass4, "{4ecb714f-c73d-4a80-84d4-b55b145c3033}");
|
|
AZ_CLASS_ALLOCATOR(ScriptClass4, SystemAllocator, 0);
|
|
|
|
ScriptClass4(int data)
|
|
: m_data(data)
|
|
{
|
|
int& numClass4Instances = ScriptBindTest::GetScriptBindInstance()->scriptClass4numInstances;
|
|
numClass4Instances++;
|
|
}
|
|
~ScriptClass4()
|
|
{
|
|
int& numClass4Instances = ScriptBindTest::GetScriptBindInstance()->scriptClass4numInstances;
|
|
numClass4Instances--;
|
|
}
|
|
|
|
static void HoldInstance(const AZStd::shared_ptr<ScriptClass4>& ptr)
|
|
{
|
|
auto& class4lockedInstances = ScriptBindTest::GetScriptBindInstance()->scriptClass4lockedInstances;
|
|
class4lockedInstances.push_back(ptr);
|
|
}
|
|
void CopyData(AZStd::shared_ptr<ScriptClass4> rhs)
|
|
{
|
|
m_data = rhs->m_data;
|
|
}
|
|
int m_data;
|
|
|
|
};
|
|
|
|
class ScriptClass5
|
|
{
|
|
public:
|
|
AZ_TYPE_INFO(ScriptClass5, "{99a1e378-d457-45d0-bd49-72b21d3368c5}");
|
|
AZ_CLASS_ALLOCATOR(ScriptClass5, SystemAllocator, 0);
|
|
|
|
ScriptClass5(int data)
|
|
: m_data(data)
|
|
, m_refCount(0)
|
|
{
|
|
int& numClass5Instances = ScriptBindTest::GetScriptBindInstance()->scriptClass5numInstances;
|
|
numClass5Instances++;
|
|
}
|
|
~ScriptClass5()
|
|
{
|
|
int& numClass5Instances = ScriptBindTest::GetScriptBindInstance()->scriptClass5numInstances;
|
|
numClass5Instances--;
|
|
}
|
|
void add_ref() { m_refCount++; }
|
|
void release()
|
|
{
|
|
m_refCount--;
|
|
if (m_refCount == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
}
|
|
static void HoldInstance(const AZStd::intrusive_ptr<ScriptClass5>& ptr)
|
|
{
|
|
auto& class5lockedInstances = ScriptBindTest::GetScriptBindInstance()->scriptClass5lockedInstances;
|
|
class5lockedInstances.push_back(ptr);
|
|
}
|
|
|
|
void CopyData(const AZStd::intrusive_ptr<ScriptClass5>& rhs)
|
|
{
|
|
m_data = rhs->m_data;
|
|
}
|
|
|
|
int m_data;
|
|
int m_refCount;
|
|
|
|
|
|
};
|
|
|
|
struct ScriptValueClass
|
|
{
|
|
AZ_TYPE_INFO(ScriptValueClass, "{78b1baa9-483b-42c7-8c1e-4eba903bd529}");
|
|
AZ_CLASS_ALLOCATOR(ScriptValueClass, SystemAllocator, 0);
|
|
|
|
ScriptValueClass()
|
|
: m_data(10)
|
|
{
|
|
}
|
|
|
|
ScriptValueClass(const ScriptValueClass& rhs)
|
|
{
|
|
(void)rhs;
|
|
AZ_TEST_ASSERT(false); // we should not make copies
|
|
}
|
|
|
|
int m_data;
|
|
};
|
|
|
|
struct ScriptValueHolder
|
|
{
|
|
AZ_TYPE_INFO(ScriptValueHolder, "{86887e28-dfc6-4619-87d8-3658da0996ec}");
|
|
AZ_CLASS_ALLOCATOR(ScriptValueHolder, SystemAllocator, 0);
|
|
|
|
ScriptValueHolder()
|
|
{
|
|
}
|
|
ScriptValueHolder(const ScriptValueHolder& rhs)
|
|
{
|
|
(void)rhs;
|
|
AZ_TEST_ASSERT(false); // we should not make copies
|
|
}
|
|
ScriptValueClass m_value;
|
|
};
|
|
|
|
class ScriptUnregisteredBaseClass
|
|
{
|
|
public:
|
|
AZ_RTTI(ScriptUnregisteredBaseClass, "{0ebe10b0-617b-4086-bff1-d51523db8cd6}");
|
|
|
|
ScriptUnregisteredBaseClass()
|
|
: m_baseData(1) {}
|
|
virtual ~ScriptUnregisteredBaseClass() {}
|
|
|
|
virtual int GetData() const { return m_baseData; }
|
|
|
|
int m_baseData;
|
|
};
|
|
|
|
class ScriptUnregisteredDerivedClass
|
|
: public ScriptUnregisteredBaseClass
|
|
{
|
|
public:
|
|
AZ_RTTI(ScriptUnregisteredDerivedClass, "{5aba38c7-ae10-46d1-bc8e-3a0a00a3a97c}", ScriptUnregisteredBaseClass);
|
|
|
|
ScriptUnregisteredDerivedClass()
|
|
: m_derivedData(10) {}
|
|
|
|
int GetData() const override { return m_derivedData; }
|
|
|
|
int m_derivedData;
|
|
};
|
|
|
|
int GetUnregisteredScriptBaseData(ScriptUnregisteredBaseClass* base)
|
|
{
|
|
return base->GetData();
|
|
}
|
|
|
|
int GetUnregisteredScriptDerivedData(ScriptUnregisteredDerivedClass* derived)
|
|
{
|
|
return derived->m_derivedData;
|
|
}
|
|
|
|
ScriptUnregisteredBaseClass* s_globalUnregBaseClass = nullptr;
|
|
ScriptUnregisteredDerivedClass* s_globalUnregDerivedClass = nullptr;
|
|
|
|
class ScriptRegisteredBaseClass
|
|
{
|
|
public:
|
|
AZ_RTTI(ScriptRegisteredBaseClass, "{73c2f822-d1ef-4abc-accb-cb36599f6b66}");
|
|
AZ_CLASS_ALLOCATOR(ScriptRegisteredBaseClass, SystemAllocator, 0)
|
|
|
|
ScriptRegisteredBaseClass()
|
|
: m_baseData(2) {}
|
|
virtual ~ScriptRegisteredBaseClass() {}
|
|
|
|
virtual int VirtualFunction()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int m_baseData;
|
|
};
|
|
|
|
ScriptRegisteredBaseClass* s_globalRegisteredBaseClass = nullptr;
|
|
|
|
class ScriptRegisteredDerivedClass
|
|
: public ScriptRegisteredBaseClass
|
|
{
|
|
public:
|
|
AZ_RTTI(ScriptRegisteredDerivedClass, "{9f42df41-fdcc-44aa-909f-1f675804653c}", ScriptRegisteredBaseClass);
|
|
AZ_CLASS_ALLOCATOR(ScriptRegisteredDerivedClass, SystemAllocator, 0)
|
|
|
|
ScriptRegisteredDerivedClass()
|
|
: m_derivedData(11) {}
|
|
~ScriptRegisteredDerivedClass() override {}
|
|
|
|
int VirtualFunction() override
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
int m_derivedData;
|
|
};
|
|
|
|
class AbstractClass
|
|
{
|
|
public:
|
|
AZ_RTTI(AbstractClass, "{d7082d66-108f-4177-8fb3-5cf25b510aa7}");
|
|
|
|
virtual ~AbstractClass() {}
|
|
virtual int PureCall() = 0;
|
|
};
|
|
|
|
class AbstractImplementation
|
|
: public AbstractClass
|
|
{
|
|
public:
|
|
AZ_RTTI(AbstractImplementation, "{62e1514d-57be-49c5-b829-d937f07276cd}", AbstractClass);
|
|
AZ_CLASS_ALLOCATOR(AbstractImplementation, SystemAllocator, 0)
|
|
|
|
int PureCall() override { return 5; }
|
|
};
|
|
|
|
AZStd::shared_ptr<ScriptClass4> s_scriptMemberShared;
|
|
AZStd::intrusive_ptr<ScriptClass5> s_scriptMemberIntrusive;
|
|
|
|
|
|
void ScriptBindTest::SetUp()
|
|
{
|
|
BehaviorContextFixture::SetUp();
|
|
ResetGlobalVars();
|
|
s_scriptBindInstance = this;
|
|
s_scriptMemberShared = AZStd::shared_ptr<ScriptClass4>(aznew ScriptClass4(10));
|
|
s_scriptMemberIntrusive = AZStd::intrusive_ptr<ScriptClass5>(aznew ScriptClass5(10));
|
|
}
|
|
|
|
void ScriptBindTest::TearDown()
|
|
{
|
|
s_scriptMemberShared.reset();
|
|
s_scriptMemberIntrusive.reset();
|
|
s_scriptBindInstance = nullptr;
|
|
|
|
BehaviorContextFixture::TearDown();
|
|
}
|
|
|
|
void ScriptBindTest::ResetGlobalVars()
|
|
{
|
|
s_globalVar = 0;
|
|
s_globalVarBool = false;
|
|
s_globalVar1 = 0.0f;
|
|
s_globalVar2ReadOnly = 10.0f;
|
|
s_globalVar3WriteOnly = 0.0f;
|
|
|
|
s_globalVarString = AZStd::string();
|
|
|
|
s_globalField = 0;
|
|
s_globalField1 = 12;
|
|
s_globalField2 = 0;
|
|
|
|
s_errorCount = 0;
|
|
|
|
s_globalIncompletePtr = static_cast<IncompleteType*>(AZ_INVALID_POINTER);
|
|
s_globalIncompletePtr1 = nullptr;
|
|
}
|
|
|
|
bool ScriptBindTest::EnumClass(const char* name, const AZ::Uuid& /*typeid*/, void* /*userData*/)
|
|
{
|
|
AZ_TEST_ASSERT(name != nullptr);
|
|
return true;
|
|
}
|
|
|
|
bool ScriptBindTest::EnumMethod(const AZ::Uuid* /*type id*/, const char* name, const char* dbgParamInfo, void* /*userData*/)
|
|
{
|
|
// make sure the debug info works
|
|
if (strcmp(name, "GlobalFunc5") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(strcmp(dbgParamInfo, "int NumberArguments (int intValue,float floatValue,bool Value,int intValue2,float floatValue2") == 0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ScriptBindTest::EnumProperty(const AZ::Uuid* /*type id*/, const char* name, bool isRead, bool isWrite, void* /*userData*/)
|
|
{
|
|
if (strcmp(name, "globalVar") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(isWrite && isRead);
|
|
}
|
|
if (strcmp(name, "globalVar2") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(!isWrite && isRead);
|
|
}
|
|
if (strcmp(name, "globalVar3") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(isWrite && !isRead);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ScriptBindTest::ScriptErrorCB(AZ::ScriptContext*, AZ::ScriptContext::ErrorType, const char* details)
|
|
{
|
|
(void)details;
|
|
s_errorCount++;
|
|
AZ_Warning("Script", false, "%s", details);
|
|
}
|
|
|
|
void ScriptBindTest::run()
|
|
{
|
|
AZ::Uuid ch = AzTypeInfo<char>::Uuid();
|
|
AZ::Uuid ch1 = AzTypeInfo<const char*>::Uuid();
|
|
(void)ch; (void)ch1;
|
|
|
|
// enum
|
|
m_behaviorContext->Enum<GE_VALUE1>("GE_VALUE1")->
|
|
Enum<GE_VALUE2>("GE_VALUE2");
|
|
|
|
// constant
|
|
m_behaviorContext->Constant("PI", BehaviorConstant(3.14f));
|
|
|
|
// property
|
|
m_behaviorContext->Property("globalVar", &GlobalVarGet, &GlobalVarSet);
|
|
m_behaviorContext->Property("globalVar1", BehaviorValueProperty(&s_globalVar1));
|
|
m_behaviorContext->Property("globalVarBool", BehaviorValueProperty(&s_globalVarBool));
|
|
m_behaviorContext->Property("globalVar2", BehaviorValueGetter(&s_globalVar2ReadOnly), nullptr);
|
|
m_behaviorContext->Property("globalVar3", nullptr, BehaviorValueSetter(&s_globalVar3WriteOnly));
|
|
m_behaviorContext->Property("globalVarString", &GlobalVarStringGet, &GlobalVarStringSet);
|
|
|
|
// field
|
|
m_behaviorContext->Property("globalField", BehaviorValueProperty(&s_globalField));
|
|
m_behaviorContext->Property("globalField1", BehaviorValueGetter(&s_globalField1), nullptr);
|
|
m_behaviorContext->Property("globalField2", nullptr, BehaviorValueSetter(&s_globalField2));
|
|
m_behaviorContext->Property("globalIncomplete", BehaviorValueProperty(&s_globalIncompletePtr)); // unregistered type (passed as light userdata)
|
|
m_behaviorContext->Property("globalIncomplete1", BehaviorValueProperty(&s_globalIncompletePtr1)); // unregistered type (passed as light userdata)
|
|
|
|
// method
|
|
m_behaviorContext->Method("GlobalFunc0", &GlobalFunc0);
|
|
m_behaviorContext->Method("GlobalFunc1", &GlobalFunc1);
|
|
m_behaviorContext->Method("GlobalFunc2", &GlobalFunc2);
|
|
m_behaviorContext->Method("GlobalFunc3", &GlobalFunc3);
|
|
m_behaviorContext->Method("GlobalFunc4", &GlobalFunc4);
|
|
m_behaviorContext->Method("GlobalFunc5", &GlobalFunc5, nullptr, "int NumberArguments (int intValue,float floatValue,bool Value,int intValue2,float floatValue2");
|
|
m_behaviorContext->Method("GlobalVoidFunc0", &GlobalVoidFunc0);
|
|
m_behaviorContext->Method("GlobalVoidFunc1", &GlobalVoidFunc1);
|
|
m_behaviorContext->Method("GlobalVoidFunc2", &GlobalVoidFunc2);
|
|
m_behaviorContext->Method("GlobalVoidFunc3", &GlobalVoidFunc3);
|
|
m_behaviorContext->Method("GlobalVoidFunc4", &GlobalVoidFunc4);
|
|
m_behaviorContext->Method("GlobalVoidFunc5", &GlobalVoidFunc5);
|
|
m_behaviorContext->Method("GlobalEnumFunc", &GlobalEnumFunc);
|
|
m_behaviorContext->Method("GlobalCheckString", &GlobalCheckString);
|
|
m_behaviorContext->Method("GlobalCheckStringRef", &GlobalCheckStringRef);
|
|
|
|
// clases
|
|
m_behaviorContext->Class<ScriptClass>()->
|
|
Allocator(&ScriptClassAllocate, &ScriptClassFree)->
|
|
Enum<ScriptClass::SC_ET_VALUE2>("SC_ET_VALUE2")->
|
|
Enum<ScriptClass::SC_ET_VALUE3>("SC_ET_VALUE3")->
|
|
Constant("EPSILON", BehaviorConstant(0.001f))->
|
|
Method("MemberFunc0", &ScriptClass::MemberFunc0)->
|
|
Method("MemberFunc1", &ScriptClass::MemberFunc1)->
|
|
Method("MemberFunc2", &ScriptClass::MemberFunc2)->
|
|
Method("MemberFunc3", &ScriptClass::MemberFunc3)->
|
|
Method("MemberFunc4", &ScriptClass::MemberFunc4)->
|
|
Method("MemberFunc5", &ScriptClass::MemberFunc5)->
|
|
Method("MemberVoidFunc0", &ScriptClass::MemberVoidFunc0)->
|
|
Method("MemberVoidFunc1", &ScriptClass::MemberVoidFunc1)->
|
|
Method("MemberVoidFunc2", &ScriptClass::MemberVoidFunc2)->
|
|
Method("MemberVoidFunc3", &ScriptClass::MemberVoidFunc3)->
|
|
Method("MemberVoidFunc4", &ScriptClass::MemberVoidFunc4)->
|
|
Method("MemberVoidFunc5", &ScriptClass::MemberVoidFunc5)->
|
|
Method("VariadicFunc", &ScriptClass::VariadicFunc)->
|
|
Property("data", &ScriptClass::GetData, &ScriptClass::SetData)->
|
|
Property("data1", BehaviorValueProperty(&ScriptClass::m_data1))->
|
|
Property("data2", BehaviorValueGetter(&ScriptClass::m_data2ReadOnly), nullptr)->
|
|
Property("data3", nullptr, BehaviorValueSetter(&ScriptClass::m_data3WriteOnly));//->
|
|
//Class<ScriptClass::ScriptClassNested,ScriptContext::SP_RAW_SCRIPT_OWN>()->
|
|
// Field("data",&ScriptClass::ScriptClassNested::m_data)->
|
|
//ClassEnd();
|
|
|
|
m_behaviorContext->Class<ScriptClass2>()->
|
|
Constructor<int>()->
|
|
Method("Add", &ScriptClass2::ScriptAdd)->
|
|
Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Add)->
|
|
Method("Sub", &ScriptClass2::ScriptSub)->
|
|
Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Sub)->
|
|
Method("Mul", &ScriptClass2::ScriptMul)->
|
|
Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Mul)->
|
|
Method("Div", &ScriptClass2::ScriptDiv)->
|
|
Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Div)->
|
|
Method("Mod", &ScriptClass2::ScriptMod)->
|
|
Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Mod)->
|
|
Method("Pow", &ScriptClass2::ScriptPow)->
|
|
Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Pow)->
|
|
Method("Unary", &ScriptClass2::ScriptUnary)->
|
|
Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Unary)->
|
|
Method("Concat", &ScriptClass2::ScriptConcat)->
|
|
Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Concat)->
|
|
Method("Length", &ScriptClass2::ScriptLength)->
|
|
Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Length)->
|
|
Method("Equal", &ScriptClass2::operator==)->
|
|
Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Equal)->
|
|
Method("LessEqual", &ScriptClass2::operator<=)->
|
|
Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::LessEqualThan)->
|
|
Method("LessThan", &ScriptClass2::operator<)->
|
|
Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::LessThan)->
|
|
Method("ToString", &ScriptClass2::ToString)->
|
|
Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString)->
|
|
Method("Forward", &ScriptClass2::Forward)->
|
|
Property("data", BehaviorValueProperty(&ScriptClass2::m_data));
|
|
|
|
m_behaviorContext->Class<ScriptClass3>()->
|
|
Constructor<ScriptDataContext&>()->
|
|
Property("boolData", BehaviorValueProperty(&ScriptClass3::m_bool))->
|
|
Property("intData", BehaviorValueProperty(&ScriptClass3::m_intData))->
|
|
Property("floatData", BehaviorValueProperty(&ScriptClass3::m_floatData))->
|
|
Property("scriptClass2", BehaviorValueProperty(&ScriptClass3::m_scriptClass2));
|
|
|
|
m_behaviorContext->Class<ScriptClass4>()->
|
|
Constructor<int>()->
|
|
Method("CopyData", &ScriptClass4::CopyData)->
|
|
Property("data", BehaviorValueProperty(&ScriptClass4::m_data));
|
|
|
|
m_behaviorContext->Property("scriptClass4Member", BehaviorValueProperty(&s_scriptMemberShared));
|
|
m_behaviorContext->Method("HoldScriptClass4", &ScriptClass4::HoldInstance);
|
|
|
|
m_behaviorContext->Class<ScriptClass5>()->
|
|
Constructor<int>()->
|
|
Method("CopyData", &ScriptClass5::CopyData)->
|
|
Property("data", BehaviorValueProperty(&ScriptClass5::m_data));
|
|
|
|
m_behaviorContext->Property("scriptClass5Member", BehaviorValueProperty(&s_scriptMemberIntrusive));
|
|
m_behaviorContext->Method("HoldScriptClass5", &ScriptClass5::HoldInstance);
|
|
|
|
// Variadic method
|
|
m_behaviorContext->Method("VariadicFunc", &VariadicFunc);
|
|
|
|
// Check nested classes
|
|
m_behaviorContext->Class<ScriptValueClass>()->
|
|
Property("data", BehaviorValueProperty(&ScriptValueClass::m_data));
|
|
|
|
m_behaviorContext->Class<ScriptValueHolder>()->
|
|
Property("value", BehaviorValueProperty(&ScriptValueHolder::m_value));
|
|
|
|
// test class pointer casting for unregistered classes
|
|
m_behaviorContext->Property("globalUnregBaseClass", BehaviorValueProperty(&s_globalUnregBaseClass));
|
|
m_behaviorContext->Property("globalUnregDerivedClass", BehaviorValueProperty(&s_globalUnregDerivedClass));
|
|
|
|
m_behaviorContext->Method("GetUnregisteredScriptBaseData", &GetUnregisteredScriptBaseData);
|
|
m_behaviorContext->Method("GetUnregisteredScriptDerivedData", &GetUnregisteredScriptDerivedData);
|
|
|
|
// register a base and derived class test
|
|
m_behaviorContext->Class<ScriptRegisteredBaseClass>()->
|
|
Property("baseData", BehaviorValueProperty(&ScriptRegisteredBaseClass::m_baseData))->
|
|
Method("VirtualFunction", &ScriptRegisteredBaseClass::VirtualFunction);
|
|
|
|
// when using AZ_RTTI the bind will discover the 'ScriptRegisteredBaseClass' and import it's bindings
|
|
// into 'ScriptRegisteredDerivedClass' class so we access it (like in C++)
|
|
m_behaviorContext->Class<ScriptRegisteredDerivedClass>()->
|
|
Property("derivedData", BehaviorValueProperty(&ScriptRegisteredDerivedClass::m_derivedData));
|
|
|
|
// add a function that makes a derived class (can change at runtime) and return pointer to a base class.
|
|
m_behaviorContext->Property("globalRegisteredBaseClass", BehaviorValueProperty(&s_globalRegisteredBaseClass));
|
|
|
|
// abstract classes
|
|
m_behaviorContext->Class<AbstractClass>()->
|
|
Method("PureCall", &AbstractClass::PureCall);
|
|
|
|
m_behaviorContext->Class<AbstractImplementation>();
|
|
|
|
// create the lua script context and Bind it the behavior
|
|
ScriptContext script;
|
|
script.BindTo(m_behaviorContext);
|
|
|
|
// test globalVar with functions get and set
|
|
AZ_TEST_ASSERT(s_globalVar == 0);
|
|
script.Execute("globalVar = 5");
|
|
AZ_TEST_ASSERT(s_globalVar == 5);
|
|
script.Execute("globalVar = globalVar + 1");
|
|
AZ_TEST_ASSERT(s_globalVar == 6);
|
|
|
|
// test globalVar1 with variable address only
|
|
AZ_TEST_ASSERT(s_globalVar1 == 0.0f);
|
|
script.Execute("globalVar1 = 5");
|
|
AZ_TEST_ASSERT(s_globalVar1 == 5.0f);
|
|
script.Execute("globalVar1 = globalVar1 + 1.0");
|
|
AZ_TEST_ASSERT(s_globalVar1 == 6.0f);
|
|
|
|
// test globalVar2ReadOnly
|
|
s_globalVar1 = 0.0f;
|
|
AZ_TEST_ASSERT(s_globalVar2ReadOnly == 10.0f);
|
|
script.Execute("globalVar1 = globalVar2");
|
|
AZ_TEST_ASSERT(s_globalVar1 == 10.0f);
|
|
//script.Execute("globalVar2 = 5.0"); //- this will cause script error (we can improve the message by providing a fake set function);
|
|
|
|
// test globalVar2WriteOnly
|
|
AZ_TEST_ASSERT(s_globalVar3WriteOnly == 0.0f);
|
|
script.Execute("globalVar3 = 100");
|
|
AZ_TEST_ASSERT(s_globalVar3WriteOnly == 100.0f);
|
|
//script.Execute("print(\"globalVar3 is \",globalVar3)"); //- this will cause script error (we can improve the message by providing a fake set function);
|
|
|
|
// globalField with functions get and set
|
|
AZ_TEST_ASSERT(s_globalField == 0);
|
|
script.Execute("globalField = 5");
|
|
AZ_TEST_ASSERT(s_globalField == 5);
|
|
script.Execute("globalField = globalField +1");
|
|
AZ_TEST_ASSERT(s_globalField == 6);
|
|
|
|
// globalField1 read only
|
|
s_globalVar1 = 0.0f;
|
|
AZ_TEST_ASSERT(s_globalField1 == 12.0f);
|
|
script.Execute("globalVar1 = globalField1");
|
|
AZ_TEST_ASSERT(s_globalVar1 == 12.0f);
|
|
//script.Execute("globalField1 = 10.0"); //- this will cause script error (we can improve the message by providing a fake set function);
|
|
|
|
// globalField2 write only
|
|
AZ_TEST_ASSERT(s_globalField2 == 0.0f);
|
|
script.Execute("globalField2 = 100");
|
|
AZ_TEST_ASSERT(s_globalField2 == 100.0f);
|
|
//script.Execute("print(\"globalField2 is \",globalField2)"); //- this will cause script error (we can improve the message by providing a fake set function);
|
|
|
|
// s_globalVarString test
|
|
AZ_TEST_ASSERT(s_globalVarString.empty());
|
|
script.Execute("globalVarString = \"Hello\"");
|
|
AZ_TEST_ASSERT(s_globalVarString.compare("Hello") == 0);
|
|
|
|
// incomplete types passed by a light-user data (pointer reference)
|
|
AZ_TEST_ASSERT(s_globalIncompletePtr == reinterpret_cast<IncompleteType*>(AZ_INVALID_POINTER));
|
|
AZ_TEST_ASSERT(s_globalIncompletePtr1 == nullptr);
|
|
|
|
script.Execute("globalIncomplete1 = globalIncomplete");
|
|
AZ_TEST_ASSERT(s_globalIncompletePtr1 == s_globalIncompletePtr);
|
|
|
|
// enum
|
|
s_globalVar = 100;
|
|
script.Execute("globalVar = 0");
|
|
AZ_TEST_ASSERT(s_globalVar == 0);
|
|
s_globalVar = 100;
|
|
script.Execute("globalVar = GE_VALUE1");
|
|
AZ_TEST_ASSERT(s_globalVar == GE_VALUE1);
|
|
s_globalVar = 100;
|
|
script.Execute("globalVar = GE_VALUE2");
|
|
AZ_TEST_ASSERT(s_globalVar == GE_VALUE2);
|
|
s_globalVar = 100;
|
|
script.Execute("GlobalEnumFunc(GE_VALUE2)");
|
|
AZ_TEST_ASSERT(s_globalVar == GE_VALUE2);
|
|
|
|
// constant
|
|
s_globalVar1 = 0.0f;
|
|
script.Execute("globalVar1 = PI");
|
|
AZ_TEST_ASSERT_FLOAT_CLOSE(s_globalVar1, 3.14f);
|
|
|
|
// methods
|
|
s_globalVar = 5;
|
|
script.Execute("globalVar = GlobalFunc0()");
|
|
AZ_TEST_ASSERT(s_globalVar == 0);
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar = GlobalFunc1(2)");
|
|
AZ_TEST_ASSERT(s_globalVar == 1);
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar = GlobalFunc2(2,3)");
|
|
AZ_TEST_ASSERT(s_globalVar == 2);
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar = GlobalFunc3(2,3,true)");
|
|
AZ_TEST_ASSERT(s_globalVar == 3);
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar = GlobalFunc4(2,3,false,4)");
|
|
AZ_TEST_ASSERT(s_globalVar == 4);
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar = GlobalFunc5(2,3,true,5,2)");
|
|
AZ_TEST_ASSERT(s_globalVar == 5);
|
|
|
|
s_globalVar = 5;
|
|
script.Execute("GlobalVoidFunc0()");
|
|
AZ_TEST_ASSERT(s_globalVar == 0);
|
|
s_globalVar = 0;
|
|
script.Execute("GlobalVoidFunc1(2)");
|
|
AZ_TEST_ASSERT(s_globalVar == 2);
|
|
s_globalVar = 0;
|
|
script.Execute("GlobalVoidFunc2(10,1)");
|
|
AZ_TEST_ASSERT(s_globalVar == 10);
|
|
s_globalVar = 0;
|
|
script.Execute("GlobalVoidFunc3(15,3,true)");
|
|
AZ_TEST_ASSERT(s_globalVar == 15);
|
|
s_globalVar = 0;
|
|
script.Execute("GlobalVoidFunc4(21,4,false,2)");
|
|
AZ_TEST_ASSERT(s_globalVar == 21);
|
|
s_globalVar = 0;
|
|
script.Execute("GlobalVoidFunc5(101,102,true,2,1)");
|
|
AZ_TEST_ASSERT(s_globalVar == 101);
|
|
|
|
script.Execute("GlobalCheckString(globalVarString)");
|
|
script.Execute("GlobalCheckStringRef(globalVarString)");
|
|
|
|
// test a variadic function
|
|
s_globalVar = 5;
|
|
script.Execute("globalVar1 = VariadicFunc()");
|
|
AZ_TEST_ASSERT(s_globalVar == 0);
|
|
AZ_TEST_ASSERT_FLOAT_CLOSE(s_globalVar1, 0.0f);
|
|
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar1 = VariadicFunc(101,102,true,2,1)");
|
|
AZ_TEST_ASSERT(s_globalVar == 5);
|
|
AZ_TEST_ASSERT_FLOAT_CLOSE(s_globalVar1, 10.0f);
|
|
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar1 = VariadicFunc(101,102)");
|
|
AZ_TEST_ASSERT(s_globalVar == 2);
|
|
AZ_TEST_ASSERT_FLOAT_CLOSE(s_globalVar1, 4.0f);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Classes
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar = scriptClass4Member.data");
|
|
AZ_TEST_ASSERT(s_globalVar == 10);
|
|
script.Execute("scriptClass4Member = nil");
|
|
AZ_TEST_ASSERT(s_scriptMemberShared == nullptr);
|
|
script.Execute("scriptClass4Member = shared_ptr_ScriptClass4(ScriptClass4(12))");
|
|
AZ_TEST_ASSERT(s_scriptMemberShared->m_data == 12);
|
|
|
|
// test invalid assignment
|
|
{
|
|
s_errorCount = 0;
|
|
auto originalErrorCB = script.GetErrorHook();
|
|
script.SetErrorHook(&ScriptErrorCB);
|
|
script.Execute("scriptClass4Member = ScriptClass5(12)");
|
|
script.SetErrorHook(originalErrorCB);
|
|
AZ_TEST_ASSERT(s_errorCount == 1);
|
|
}
|
|
|
|
script.Execute("scriptClass4Member = nil");
|
|
script.GarbageCollect();
|
|
AZ_TEST_ASSERT(scriptClass4numInstances == 0)
|
|
|
|
// test assignments of nil to intrusive pointer
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar = scriptClass5Member.data");
|
|
AZ_TEST_ASSERT(s_globalVar == 10);
|
|
script.Execute("scriptClass5Member = nil");
|
|
AZ_TEST_ASSERT(s_scriptMemberShared == nullptr);
|
|
script.Execute("scriptClass5Member = intrusive_ptr_ScriptClass5(ScriptClass5(12))");
|
|
AZ_TEST_ASSERT(s_scriptMemberIntrusive->m_data == 12);
|
|
|
|
// test invalid assignment
|
|
{
|
|
s_errorCount = 0;
|
|
auto originalErrorCB = script.GetErrorHook();
|
|
script.SetErrorHook(&ScriptErrorCB);
|
|
script.Execute("scriptClass5Member = ScriptClass4(12)");
|
|
script.SetErrorHook(originalErrorCB);
|
|
AZ_TEST_ASSERT(s_errorCount == 1);
|
|
}
|
|
|
|
AZ_TEST_ASSERT(s_scriptMemberIntrusive->m_data == 12);
|
|
script.Execute("scriptClass5Member = nil");
|
|
script.GarbageCollect();
|
|
AZ_TEST_ASSERT(scriptClass5numInstances == 0)
|
|
|
|
// debug print
|
|
AZ_TEST_ASSERT(script.GetDebugContext() == nullptr);
|
|
script.EnableDebug();
|
|
if (script.GetDebugContext() != nullptr) // debug is NOT supported in release builds
|
|
{
|
|
script.GetDebugContext()->EnumRegisteredGlobals(&EnumMethod, &EnumProperty);
|
|
script.GetDebugContext()->EnumRegisteredClasses(&EnumClass, &EnumMethod, &EnumProperty);
|
|
script.DisableDebug();
|
|
AZ_TEST_ASSERT(script.GetDebugContext() == nullptr);
|
|
}
|
|
|
|
// create a ScriptClass instance from the script
|
|
AZ_TEST_ASSERT(s_scriptClassInstance == nullptr);
|
|
script.Execute("sc = ScriptClass()");
|
|
AZ_TEST_ASSERT(s_scriptClassInstance != nullptr);
|
|
|
|
// enum
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar = sc.SC_ET_VALUE2");
|
|
AZ_TEST_ASSERT(s_globalVar == 2);
|
|
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar = sc.SC_ET_VALUE3");
|
|
AZ_TEST_ASSERT(s_globalVar == 3);
|
|
|
|
// constant
|
|
s_globalVar1 = 0.0f;
|
|
script.Execute("globalVar1 = sc.EPSILON");
|
|
AZ_TEST_ASSERT_FLOAT_CLOSE(s_globalVar1, 0.001f);
|
|
|
|
// methods
|
|
s_globalVar = 1;
|
|
script.Execute("globalVar = sc:MemberFunc0()");
|
|
AZ_TEST_ASSERT(s_globalVar == 0);
|
|
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar = sc:MemberFunc1(1)");
|
|
AZ_TEST_ASSERT(s_globalVar == 1);
|
|
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar = sc:MemberFunc2(2,2.0)");
|
|
AZ_TEST_ASSERT(s_globalVar == 2);
|
|
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar = sc:MemberFunc3(3,3.0,false)");
|
|
AZ_TEST_ASSERT(s_globalVar == 3);
|
|
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar = sc:MemberFunc4(4,4.0,true,40)");
|
|
AZ_TEST_ASSERT(s_globalVar == 4);
|
|
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar = sc:MemberFunc5(5,5.0,false,50,50.0)");
|
|
AZ_TEST_ASSERT(s_globalVar == 5);
|
|
|
|
s_scriptClassInstance->m_data = 10;
|
|
script.Execute("sc:MemberVoidFunc0()");
|
|
AZ_TEST_ASSERT(s_scriptClassInstance->m_data == 0);
|
|
|
|
s_scriptClassInstance->m_data = 0;
|
|
script.Execute("sc:MemberVoidFunc1(11)");
|
|
AZ_TEST_ASSERT(s_scriptClassInstance->m_data == 11);
|
|
|
|
s_scriptClassInstance->m_data = 0;
|
|
script.Execute("sc:MemberVoidFunc2(7,11)");
|
|
AZ_TEST_ASSERT(s_scriptClassInstance->m_data == 7);
|
|
|
|
s_scriptClassInstance->m_data = 0;
|
|
script.Execute("sc:MemberVoidFunc3(13,11,false)");
|
|
AZ_TEST_ASSERT(s_scriptClassInstance->m_data == 13);
|
|
|
|
s_scriptClassInstance->m_data = 0;
|
|
script.Execute("sc:MemberVoidFunc4(21,11,false,40)");
|
|
AZ_TEST_ASSERT(s_scriptClassInstance->m_data == 21);
|
|
|
|
s_scriptClassInstance->m_data = 0;
|
|
script.Execute("sc:MemberVoidFunc5(30,11,false,50,50.0)");
|
|
AZ_TEST_ASSERT(s_scriptClassInstance->m_data == 30);
|
|
|
|
// test number of arguments in a variadic function
|
|
s_globalVar = 0;
|
|
script.Execute("sc:VariadicFunc(30,11,false)");
|
|
AZ_TEST_ASSERT(s_globalVar == 3);
|
|
|
|
s_scriptClassInstance->m_data = 0;
|
|
s_globalVar = 1;
|
|
script.Execute("globalVar = sc.data");
|
|
AZ_TEST_ASSERT(s_globalVar == 0);
|
|
|
|
script.Execute("sc.data = 10");
|
|
script.Execute("globalVar = sc.data");
|
|
AZ_TEST_ASSERT(s_globalVar == 10);
|
|
|
|
s_globalVar1 = 3.0f;
|
|
script.Execute("globalVar1 = sc.data1");
|
|
AZ_TEST_ASSERT(s_globalVar1 == 0.0f);
|
|
|
|
script.Execute("sc.data1 = 11 ");
|
|
script.Execute("globalVar1 = sc.data1");
|
|
AZ_TEST_ASSERT(s_globalVar1 == 11.0f);
|
|
|
|
s_globalVar1 = 0.0f;
|
|
script.Execute("globalVar1 = sc.data2");
|
|
AZ_TEST_ASSERT(s_globalVar1 == 11.0f);
|
|
|
|
AZ_TEST_ASSERT(s_scriptClassInstance->m_data3WriteOnly == 0.0f);
|
|
script.Execute("sc.data3 = 101");
|
|
AZ_TEST_ASSERT(s_scriptClassInstance->m_data3WriteOnly == 101.0f);
|
|
|
|
s_globalVarString.clear();
|
|
script.Execute("globalVarString = tostring(sc)");
|
|
AZ_TEST_ASSERT(s_globalVarString.find("ScriptClass") != AZStd::string::npos);
|
|
AZ_TEST_ASSERT(s_globalVarString.find("LuaUserData") != AZStd::string::npos);
|
|
|
|
// release test
|
|
script.Execute("sc = nil");
|
|
script.GarbageCollect();
|
|
AZ_TEST_ASSERT(s_scriptClassInstance == nullptr);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 0);
|
|
script.Execute("sc2 = ScriptClass2(10)");
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 1);
|
|
AZ_TEST_ASSERT(scriptClass2Instances[0]->m_data == 10);
|
|
|
|
script.Execute("sc21 = ScriptClass2(20)");
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 2);
|
|
AZ_TEST_ASSERT(scriptClass2Instances[1]->m_data == 20);
|
|
|
|
// addition
|
|
script.Execute("sc22 = sc2 + sc21");
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 3);
|
|
AZ_TEST_ASSERT(scriptClass2Instances[2]->m_data == 30);
|
|
script.Execute("sc22 = nil collectgarbage(\"collect\")");
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 2);
|
|
|
|
// sub
|
|
script.Execute("sc22 = sc2 - sc21");
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 3);
|
|
AZ_TEST_ASSERT(scriptClass2Instances[2]->m_data == -10);
|
|
script.Execute("sc22 = nil collectgarbage(\"collect\")");
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 2);
|
|
|
|
// mul
|
|
script.Execute("sc22 = sc2 * sc21");
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 3);
|
|
AZ_TEST_ASSERT(scriptClass2Instances[2]->m_data == 200);
|
|
script.Execute("sc22 = nil collectgarbage(\"collect\")");
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 2);
|
|
|
|
// div
|
|
script.Execute("sc22 = sc21 / sc2");
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 3);
|
|
AZ_TEST_ASSERT(scriptClass2Instances[2]->m_data == 2);
|
|
script.Execute("sc22 = nil collectgarbage(\"collect\")");
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 2);
|
|
|
|
// mod
|
|
script.Execute("sc21.data = 3 sc22 = sc2 % sc21 sc21.data = 20");
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 3);
|
|
AZ_TEST_ASSERT(scriptClass2Instances[2]->m_data == 1);
|
|
script.Execute("sc22 = nil collectgarbage(\"collect\")");
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 2);
|
|
|
|
// pow
|
|
script.Execute("sc21.data = 2 sc22 = sc2 ^ sc21 sc21.data = 20");
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 3);
|
|
AZ_TEST_ASSERT(scriptClass2Instances[2]->m_data == 100);
|
|
script.Execute("sc22 = nil collectgarbage(\"collect\")");
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 2);
|
|
|
|
// unary
|
|
script.Execute("sc22 = -sc2");
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 3);
|
|
AZ_TEST_ASSERT(scriptClass2Instances[2]->m_data == -10);
|
|
script.Execute("sc22 = nil collectgarbage(\"collect\")");
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 2);
|
|
|
|
// concat
|
|
script.Execute("sc22 = sc2 .. sc21");
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 3);
|
|
AZ_TEST_ASSERT(scriptClass2Instances[2]->m_data == 30);
|
|
script.Execute("sc22 = nil collectgarbage(\"collect\")");
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 2);
|
|
|
|
// compare
|
|
s_globalVarBool = true;
|
|
script.Execute("globalVarBool = (sc2 == sc21)");
|
|
AZ_TEST_ASSERT(!s_globalVarBool);
|
|
script.Execute("sc21.data = 10 globalVarBool = (sc2 == sc21) sc21.data = 20");
|
|
AZ_TEST_ASSERT(s_globalVarBool);
|
|
|
|
// compare less than
|
|
s_globalVarBool = false;
|
|
script.Execute("globalVarBool = (sc2 < sc21)");
|
|
AZ_TEST_ASSERT(s_globalVarBool);
|
|
script.Execute("globalVarBool = (sc21 < sc2)");
|
|
AZ_TEST_ASSERT(!s_globalVarBool);
|
|
|
|
// compare less than
|
|
s_globalVarBool = false;
|
|
script.Execute("globalVarBool = (sc2 <= sc21)");
|
|
AZ_TEST_ASSERT(s_globalVarBool);
|
|
script.Execute("globalVarBool = (sc21 <= sc2)");
|
|
AZ_TEST_ASSERT(!s_globalVarBool);
|
|
|
|
// len
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar = #sc2");
|
|
AZ_TEST_ASSERT(s_globalVar == 10);
|
|
|
|
// len
|
|
s_globalVarString.clear();
|
|
script.Execute("globalVarString = tostring(sc2)");
|
|
AZ_TEST_ASSERT(s_globalVarString.compare("This is ScriptClass2") == 0);
|
|
|
|
// test pass object by pointer (when it's script owned) back to script
|
|
// we should make sure that we find the original instance
|
|
script.Execute("sc2F = sc2:Forward()");
|
|
|
|
// release test
|
|
script.Execute("sc2 = nil sc21 = nil");
|
|
script.GarbageCollect();
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 1);
|
|
script.Execute("sc2F = nil");
|
|
script.GarbageCollect();
|
|
AZ_TEST_ASSERT(scriptClass2Instances.size() == 0);
|
|
|
|
// test class with variadic constructor
|
|
AZ_TEST_ASSERT(s_scriptClass3Instance == nullptr);
|
|
script.Execute("sc3 = ScriptClass3(true,10,20.0,globalIncomplete)");
|
|
AZ_TEST_ASSERT(s_scriptClass3Instance != nullptr);
|
|
AZ_TEST_ASSERT(s_scriptClass3Instance->m_bool == true)
|
|
AZ_TEST_ASSERT(s_scriptClass3Instance->m_intData == 10)
|
|
AZ_TEST_ASSERT(s_scriptClass3Instance->m_floatData == 20.0f)
|
|
AZ_TEST_ASSERT(s_scriptClass3Instance->m_incompletePtr == s_globalIncompletePtr);
|
|
AZ_TEST_ASSERT(s_scriptClass3Instance->m_scriptClass2 == nullptr);
|
|
|
|
// we need to transfer the ownership of the object
|
|
script.Execute("sc3.scriptClass2 = ScriptClass2(333):ReleaseOwnership()");
|
|
AZ_TEST_ASSERT(s_scriptClass3Instance->m_scriptClass2 != nullptr);
|
|
AZ_TEST_ASSERT(s_scriptClass3Instance->m_scriptClass2->m_data == 333);
|
|
script.Execute("sc3.scriptClass2.data = 444");
|
|
AZ_TEST_ASSERT(s_scriptClass3Instance->m_scriptClass2->m_data == 444);
|
|
ScriptClass2* scriptClass2ToDelete = s_scriptClass3Instance->m_scriptClass2; // we Lua released the ownership
|
|
script.Execute("sc3.scriptClass2 = nil");
|
|
AZ_TEST_ASSERT(s_scriptClass3Instance->m_scriptClass2 == nullptr);
|
|
|
|
script.Execute("sc3 = nil collectgarbage(\"collect\")");
|
|
delete scriptClass2ToDelete;
|
|
AZ_TEST_ASSERT(s_scriptClass3Instance == nullptr);
|
|
|
|
// test a class with shared_ptr owner policy
|
|
AZ_TEST_ASSERT(scriptClass4numInstances == 0);
|
|
script.Execute("sc4 = shared_ptr_ScriptClass4(ScriptClass4(101))"); // create an instance and DON'T HOLD A SHARED PTR
|
|
AZ_TEST_ASSERT(scriptClass4numInstances == 1);
|
|
AZ_TEST_ASSERT(scriptClass4lockedInstances.empty());
|
|
script.Execute("sc41 = shared_ptr_ScriptClass4(ScriptClass4(201)) HoldScriptClass4(sc41)"); // create an instance and HOLD A SHARED PTR
|
|
AZ_TEST_ASSERT(scriptClass4numInstances == 2);
|
|
AZ_TEST_ASSERT(scriptClass4lockedInstances.size() == 1);
|
|
AZ_TEST_ASSERT(scriptClass4lockedInstances[0]->m_data == 201);
|
|
script.Execute("sc41:CopyData(sc4)");
|
|
AZ_TEST_ASSERT(scriptClass4lockedInstances[0]->m_data == 101);
|
|
script.Execute("sc4 = nil sc41 = nil collectgarbage(\"collect\")"); // this should release and delete sc4 but NOT sc41
|
|
AZ_TEST_ASSERT(scriptClass4numInstances == 1);
|
|
AZ_TEST_ASSERT(scriptClass4lockedInstances.size() == 1);
|
|
scriptClass4lockedInstances.clear();
|
|
AZ_TEST_ASSERT(scriptClass4numInstances == 0);
|
|
|
|
// test a class with intrusive_ptr owner policy
|
|
AZ_TEST_ASSERT(scriptClass5numInstances == 0);
|
|
script.Execute("sc5 = intrusive_ptr_ScriptClass5(ScriptClass5(101))"); // create an instance and DON'T HOLD A INTRUSIVE PTR (check constructor)
|
|
AZ_TEST_ASSERT(scriptClass5numInstances == 1);
|
|
AZ_TEST_ASSERT(scriptClass5lockedInstances.empty());
|
|
script.Execute("sc51 = intrusive_ptr_ScriptClass5(ScriptClass5(201)) HoldScriptClass5(sc51)"); // create an instance and HOLD A INTRUSIVE PTR (check constructor)
|
|
AZ_TEST_ASSERT(scriptClass5numInstances == 2);
|
|
AZ_TEST_ASSERT(scriptClass5lockedInstances.size() == 1);
|
|
AZ_TEST_ASSERT(scriptClass5lockedInstances[0]->m_data == 201);
|
|
script.Execute("sc51:CopyData(sc5)");
|
|
AZ_TEST_ASSERT(scriptClass5lockedInstances[0]->m_data == 101);
|
|
script.Execute("sc5 = nil sc51 = nil collectgarbage(\"collect\")"); // this should release and delete sc4 but NOT sc41
|
|
AZ_TEST_ASSERT(scriptClass5numInstances == 1);
|
|
AZ_TEST_ASSERT(scriptClass5lockedInstances.size() == 1);
|
|
scriptClass5lockedInstances.clear();
|
|
AZ_TEST_ASSERT(scriptClass5numInstances == 0);
|
|
|
|
// Check nested classes
|
|
|
|
// create valueHolder
|
|
script.Execute("valueHolder = ScriptValueHolder()");
|
|
|
|
// test reading user value data type
|
|
script.Execute("globalVar1 = valueHolder.value.data");
|
|
AZ_TEST_ASSERT(s_globalVar1 == 10.0f);
|
|
|
|
// test writing user value data type
|
|
script.Execute("valueHolder.value.data = 5");
|
|
script.Execute("globalVar1 = valueHolder.value.data");
|
|
AZ_TEST_ASSERT(s_globalVar1 == 5.0f);
|
|
|
|
// create user value type
|
|
script.Execute("userValueType = ScriptValueClass()");
|
|
|
|
// test reading user value data type
|
|
s_globalVar1 = 0.0f;
|
|
script.Execute("globalVar1 = userValueType.data");
|
|
AZ_TEST_ASSERT(s_globalVar1 == 10.0f);
|
|
|
|
// test assignment of value types
|
|
s_globalVar1 = 0.0f;
|
|
script.Execute("valueHolder.value = userValueType");
|
|
script.Execute("globalVar1 = valueHolder.value.data");
|
|
AZ_TEST_ASSERT(s_globalVar1 == 10.0f);
|
|
|
|
// make sure we copy the userValueType, not store a reference to it!
|
|
script.Execute("userValueType.data = 100");
|
|
script.Execute("globalVar1 = valueHolder.value.data");
|
|
AZ_TEST_ASSERT(s_globalVar1 == 10.0f);
|
|
|
|
// now store a reference and make sure it works
|
|
script.Execute("userValueTypeRef = valueHolder.value");
|
|
script.Execute("userValueTypeRef.data = 100");
|
|
script.Execute("globalVar1 = valueHolder.value.data");
|
|
AZ_TEST_ASSERT(s_globalVar1 == 100.0f);
|
|
|
|
script.Execute("valueHolder = nil userValueType = nil");
|
|
|
|
// test class pointer casting for unregistered classes
|
|
ScriptUnregisteredBaseClass ubc;
|
|
ScriptUnregisteredDerivedClass udc;
|
|
|
|
s_globalUnregBaseClass = &udc;
|
|
s_globalUnregDerivedClass = &udc;
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar = GetUnregisteredScriptBaseData(globalUnregDerivedClass)"); // using downcast
|
|
AZ_TEST_ASSERT(s_globalVar == 10);
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar = GetUnregisteredScriptBaseData(globalUnregBaseClass)");
|
|
AZ_TEST_ASSERT(s_globalVar == 10);
|
|
s_globalVar = 0;
|
|
script.Execute("globalVar = GetUnregisteredScriptDerivedData(globalUnregBaseClass)"); // using RTTI upcast
|
|
AZ_TEST_ASSERT(s_globalVar == 10);
|
|
|
|
// register a base and derived class test
|
|
|
|
// test that derived classes copy the base class properties
|
|
s_globalVar = 0;
|
|
script.Execute("derivedWithBase = ScriptRegisteredDerivedClass()");
|
|
script.Execute("globalVar = derivedWithBase.baseData"); // access base class data (RTTI is needed to be discovered and imported into top class metatable)
|
|
AZ_TEST_ASSERT(s_globalVar == 2);
|
|
// check that virtual functions work properly.
|
|
script.Execute("globalRegisteredBaseClass = derivedWithBase"); // this will cast ScriptRegisteredDerivedClass to ScriptRegisteredBaseClass
|
|
script.Execute("registeredBase = globalRegisteredBaseClass"); // this should cast back to the top known/registered class using AZRTTI.
|
|
script.Execute("globalVar = registeredBase:VirtualFunction()");
|
|
AZ_TEST_ASSERT(s_globalVar == 1);
|
|
script.Execute("globalVar = derivedWithBase:VirtualFunction()");
|
|
AZ_TEST_ASSERT(s_globalVar == 1);
|
|
script.Execute("globalRegisteredBaseClass = nil registeredBase = nil derivedWithBase = nil");
|
|
|
|
// abstract classes
|
|
s_globalVar = 0;
|
|
script.Execute("abstract = AbstractImplementation()");
|
|
script.Execute("globalVar = abstract:PureCall()");
|
|
AZ_TEST_ASSERT(s_globalVar == 5);
|
|
script.Execute("abstract = nil");
|
|
|
|
s_globalVarString.set_capacity(0); // free all memory
|
|
}
|
|
|
|
TEST_F(ScriptBindTest, LuaTest)
|
|
{
|
|
run();
|
|
}
|
|
|
|
class ScriptContextTest
|
|
: public AllocatorsFixture
|
|
{
|
|
public:
|
|
void run()
|
|
{
|
|
ScriptContext script;
|
|
|
|
const char code[] = "\
|
|
GlobalValueA = 6\
|
|
GlobalValueB = 12.4\
|
|
GlobalValueC = \"GlobalTest\"\
|
|
GlobalTable = {\
|
|
valueA = 5,\
|
|
valueB = 11.3,\
|
|
valueC = \"TableTest\",\
|
|
SubTable = {\
|
|
valueA = 6,\
|
|
valueB = 12.4,\
|
|
SubTable = {\
|
|
valueA = 7,\
|
|
},\
|
|
},\
|
|
}\
|
|
function GlobalTable.TableFunction (a)\
|
|
return a\
|
|
end\
|
|
function GlobalFunction ()\
|
|
return 25\
|
|
end\
|
|
function GlobalSquare (a)\
|
|
return a,a*a\
|
|
end\
|
|
function GlobalFast (a)\
|
|
return a*2\
|
|
end\
|
|
GlobalTableWithMeta = {}\
|
|
setmetatable(GlobalTableWithMeta,GlobalTable)\
|
|
";
|
|
|
|
script.Execute(code);
|
|
|
|
ScriptDataContext dc;
|
|
bool status;
|
|
int intResult = 0;
|
|
float floatResult = 0.f;
|
|
const char* stringResult = nullptr;
|
|
|
|
// inspect test
|
|
|
|
// global int
|
|
status = script.FindGlobal("GlobalValueA", dc);
|
|
AZ_TEST_ASSERT(status);
|
|
AZ_TEST_ASSERT(dc.IsNumber(0));
|
|
status = dc.ReadValue(0, intResult);
|
|
AZ_TEST_ASSERT(status);
|
|
AZ_TEST_ASSERT(intResult == 6);
|
|
|
|
// global float
|
|
status = script.FindGlobal("GlobalValueB", dc);
|
|
AZ_TEST_ASSERT(status);
|
|
AZ_TEST_ASSERT(dc.IsNumber(0));
|
|
status = dc.ReadValue(0, floatResult);
|
|
AZ_TEST_ASSERT(status);
|
|
AZ_TEST_ASSERT_FLOAT_CLOSE(floatResult, 12.4f);
|
|
|
|
// string value
|
|
status = script.FindGlobal("GlobalValueC", dc);
|
|
AZ_TEST_ASSERT(status);
|
|
AZ_TEST_ASSERT(dc.IsString(0));
|
|
status = dc.ReadValue(0, stringResult);
|
|
AZ_TEST_ASSERT(status);
|
|
AZ_TEST_ASSERT(strcmp(stringResult, "GlobalTest") == 0);
|
|
|
|
// table value
|
|
status = script.FindGlobal("GlobalTable", dc);
|
|
AZ_TEST_ASSERT(status);
|
|
AZ_TEST_ASSERT(dc.IsTable(0));
|
|
|
|
AZ_TEST_ASSERT(dc.CheckTableElement(0, "valueA"));
|
|
status = dc.ReadTableElement(0, "valueA", intResult);
|
|
AZ_TEST_ASSERT(status);
|
|
AZ_TEST_ASSERT(intResult == 5);
|
|
|
|
AZ_TEST_ASSERT(dc.CheckTableElement(0, "valueB"));
|
|
status = dc.ReadTableElement(0, "valueB", floatResult);
|
|
AZ_TEST_ASSERT(status);
|
|
AZ_TEST_ASSERT_FLOAT_CLOSE(floatResult, 11.3f);
|
|
|
|
AZ_TEST_ASSERT(dc.CheckTableElement(0, "valueC"));
|
|
status = dc.ReadTableElement(0, "valueC", stringResult);
|
|
AZ_TEST_ASSERT(status);
|
|
AZ_TEST_ASSERT(strcmp(stringResult, "TableTest") == 0);
|
|
|
|
// inspect table
|
|
{
|
|
ScriptDataContext dcTable;
|
|
dc.InspectTable(0, dcTable);
|
|
const char* fieldName;
|
|
int fieldIndex;
|
|
int valueIndex;
|
|
while (dcTable.InspectNextElement(valueIndex, fieldName, fieldIndex))
|
|
{
|
|
AZ_TEST_ASSERT(fieldIndex == -1); // we don't have indexed elements
|
|
if (strcmp(fieldName, "valueA") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(dcTable.IsNumber(valueIndex));
|
|
int intValue = 0;
|
|
dcTable.ReadValue(valueIndex, intValue);
|
|
AZ_TEST_ASSERT(intValue == 5);
|
|
}
|
|
else if (strcmp(fieldName, "valueB") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(dcTable.IsNumber(valueIndex));
|
|
float floatValue = 0.f;
|
|
dcTable.ReadValue(valueIndex, floatValue);
|
|
AZ_TEST_ASSERT(fabsf(floatValue - 11.3f) < 0.001f);
|
|
}
|
|
else if (strcmp(fieldName, "valueC") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(dcTable.IsString(valueIndex));
|
|
const char* stringValue;
|
|
dcTable.ReadValue(valueIndex, stringValue);
|
|
AZ_TEST_ASSERT(strcmp(stringResult, "TableTest") == 0);
|
|
}
|
|
else if (strcmp(fieldName, "SubTable") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(dcTable.IsTable(valueIndex));
|
|
ScriptDataContext subTable;
|
|
dcTable.InspectTable(valueIndex, subTable);
|
|
while (subTable.InspectNextElement(valueIndex, fieldName, fieldIndex))
|
|
{
|
|
AZ_TEST_ASSERT(fieldIndex == -1); // we don't have indexed elements
|
|
if (strcmp(fieldName, "valueA") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(subTable.IsNumber(valueIndex));
|
|
int intValue = 0;
|
|
subTable.ReadValue(valueIndex, intValue);
|
|
AZ_TEST_ASSERT(intValue == 6);
|
|
}
|
|
else if (strcmp(fieldName, "valueB") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(subTable.IsNumber(valueIndex));
|
|
float floatValue = 0.f;
|
|
subTable.ReadValue(valueIndex, floatValue);
|
|
AZ_TEST_ASSERT(fabsf(floatValue - 12.4f) < 0.001f);
|
|
}
|
|
else if (strcmp(fieldName, "SubTable") == 0)
|
|
{
|
|
ScriptDataContext subTable1;
|
|
subTable.InspectTable(valueIndex, subTable1);
|
|
while (subTable1.InspectNextElement(valueIndex, fieldName, fieldIndex))
|
|
{
|
|
AZ_TEST_ASSERT(fieldIndex == -1); // we don't have indexed elements
|
|
if (strcmp(fieldName, "valueA") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(subTable1.IsNumber(valueIndex));
|
|
int intValue = 0;
|
|
subTable1.ReadValue(valueIndex, intValue);
|
|
AZ_TEST_ASSERT(intValue == 7);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (strcmp(fieldName, "TableFunction") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(dcTable.IsFunction(valueIndex));
|
|
ScriptDataContext callContext;
|
|
dcTable.Call(valueIndex, callContext);
|
|
callContext.PushArg(11);
|
|
callContext.CallExecute();
|
|
AZ_TEST_ASSERT(callContext.GetNumResults() == 1);
|
|
int intValue = 0;
|
|
callContext.ReadResult(0, intValue);
|
|
AZ_TEST_ASSERT(intValue == 11);
|
|
}
|
|
else
|
|
{
|
|
AZ_TEST_ASSERT(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// table replace
|
|
dc.AddReplaceTableElement(0, "valueA", 4);
|
|
status = dc.ReadTableElement(0, "valueA", intResult);
|
|
AZ_TEST_ASSERT(status);
|
|
AZ_TEST_ASSERT(intResult == 4);
|
|
|
|
// table add
|
|
AZ_TEST_ASSERT(dc.CheckTableElement(0, "valueD") == false);
|
|
dc.AddReplaceTableElement(0, "valueD", 11);
|
|
AZ_TEST_ASSERT(dc.CheckTableElement(0, "valueD"));
|
|
dc.ReadTableElement(0, "valueD", intResult);
|
|
AZ_TEST_ASSERT(intResult == 11);
|
|
|
|
// global function call (no arguments 1 result)
|
|
status = script.FindGlobal("GlobalFunction", dc);
|
|
AZ_TEST_ASSERT(status);
|
|
|
|
{
|
|
ScriptDataContext callContext;
|
|
status = dc.Call(0, callContext);
|
|
AZ_TEST_ASSERT(status);
|
|
status = callContext.CallExecute();
|
|
AZ_TEST_ASSERT(status);
|
|
AZ_TEST_ASSERT(callContext.GetNumResults() == 1);
|
|
AZ_TEST_ASSERT(callContext.IsNumber(0));
|
|
callContext.ReadResult(0, intResult);
|
|
AZ_TEST_ASSERT(intResult == 25);
|
|
}
|
|
|
|
// global function call (1 argument 2 results)
|
|
{
|
|
ScriptDataContext callContext;
|
|
status = script.FindGlobal("GlobalSquare", dc);
|
|
AZ_TEST_ASSERT(status);
|
|
status = dc.Call(0, callContext);
|
|
AZ_TEST_ASSERT(status);
|
|
AZ_TEST_ASSERT(callContext.GetNumArguments() == 0);
|
|
callContext.PushArg(3);
|
|
AZ_TEST_ASSERT(callContext.GetNumArguments() == 1);
|
|
status = callContext.CallExecute();
|
|
AZ_TEST_ASSERT(status);
|
|
AZ_TEST_ASSERT(callContext.GetNumResults() == 2);
|
|
AZ_TEST_ASSERT(callContext.IsNumber(0));
|
|
AZ_TEST_ASSERT(callContext.IsNumber(1));
|
|
int intResult2 = 0;
|
|
callContext.ReadResult(0, intResult);
|
|
callContext.ReadResult(1, intResult2);
|
|
AZ_TEST_ASSERT(intResult == 3);
|
|
AZ_TEST_ASSERT(intResult2 == 9);
|
|
}
|
|
|
|
// global cached function call
|
|
int cachedIndex = script.CacheGlobal("GlobalFast");
|
|
AZ_TEST_ASSERT(cachedIndex != -1);
|
|
int value = 2;
|
|
for (int i = 0; i < 6; ++i)
|
|
{
|
|
ScriptDataContext callContext;
|
|
status = script.FindGlobal(cachedIndex, dc);
|
|
AZ_TEST_ASSERT(status);
|
|
status = dc.Call(0, callContext);
|
|
AZ_TEST_ASSERT(status);
|
|
callContext.PushArg(value);
|
|
status = callContext.CallExecute();
|
|
AZ_TEST_ASSERT(status);
|
|
AZ_TEST_ASSERT(callContext.GetNumResults() == 1);
|
|
AZ_TEST_ASSERT(callContext.IsNumber(0));
|
|
callContext.ReadResult(0, value);
|
|
}
|
|
AZ_TEST_ASSERT(value == 128);
|
|
script.ReleaseCached(cachedIndex);
|
|
|
|
// metatable inspect
|
|
status = script.FindGlobal("GlobalTableWithMeta", dc);
|
|
AZ_TEST_ASSERT(status);
|
|
AZ_TEST_ASSERT(dc.IsTable(0));
|
|
|
|
{
|
|
ScriptDataContext dcTable;
|
|
dc.InspectTable(0, dcTable);
|
|
const char* fieldName;
|
|
int fieldIndex;
|
|
int valueIndex;
|
|
while (dcTable.InspectNextElement(valueIndex, fieldName, fieldIndex))
|
|
{
|
|
AZ_TEST_ASSERT(false); // this table doesn't have any elements
|
|
}
|
|
|
|
status = dc.InspectMetaTable(0, dcTable);
|
|
AZ_TEST_ASSERT(status);
|
|
while (dcTable.InspectNextElement(valueIndex, fieldName, fieldIndex))
|
|
{
|
|
if (strcmp(fieldName, "valueA") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(dcTable.IsNumber(valueIndex));
|
|
int intValue = 0;
|
|
dcTable.ReadValue(valueIndex, intValue);
|
|
AZ_TEST_ASSERT(intValue == 4);
|
|
}
|
|
else if (strcmp(fieldName, "valueB") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(dcTable.IsNumber(valueIndex));
|
|
float floatValue = 0.f;
|
|
dcTable.ReadValue(valueIndex, floatValue);
|
|
AZ_TEST_ASSERT(fabsf(floatValue - 11.3f) < 0.001f);
|
|
}
|
|
else if (strcmp(fieldName, "valueC") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(dcTable.IsString(valueIndex));
|
|
const char* stringValue;
|
|
dcTable.ReadValue(valueIndex, stringValue);
|
|
AZ_TEST_ASSERT(strcmp(stringResult, "TableTest") == 0);
|
|
}
|
|
else if (strcmp(fieldName, "valueD") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(dcTable.IsNumber(valueIndex));
|
|
int intValue = 0;
|
|
dcTable.ReadValue(valueIndex, intValue);
|
|
AZ_TEST_ASSERT(intValue == 11);
|
|
}
|
|
else if (strcmp(fieldName, "SubTable") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(dcTable.IsTable(valueIndex));
|
|
ScriptDataContext subTable;
|
|
dcTable.InspectTable(valueIndex, subTable);
|
|
while (subTable.InspectNextElement(valueIndex, fieldName, fieldIndex))
|
|
{
|
|
AZ_TEST_ASSERT(fieldIndex == -1); // we don't have indexed elements
|
|
if (strcmp(fieldName, "valueA") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(subTable.IsNumber(valueIndex));
|
|
int intValue = 0;
|
|
subTable.ReadValue(valueIndex, intValue);
|
|
AZ_TEST_ASSERT(intValue == 6);
|
|
}
|
|
else if (strcmp(fieldName, "valueB") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(subTable.IsNumber(valueIndex));
|
|
float floatValue = 0.f;
|
|
subTable.ReadValue(valueIndex, floatValue);
|
|
AZ_TEST_ASSERT(fabsf(floatValue - 12.4f) < 0.001f);
|
|
}
|
|
else if (strcmp(fieldName, "SubTable") == 0)
|
|
{
|
|
ScriptDataContext subTable1;
|
|
subTable.InspectTable(valueIndex, subTable1);
|
|
while (subTable1.InspectNextElement(valueIndex, fieldName, fieldIndex))
|
|
{
|
|
AZ_TEST_ASSERT(fieldIndex == -1); // we don't have indexed elements
|
|
if (strcmp(fieldName, "valueA") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(subTable1.IsNumber(valueIndex));
|
|
int intValue = 0;
|
|
subTable1.ReadValue(valueIndex, intValue);
|
|
AZ_TEST_ASSERT(intValue == 7);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (strcmp(fieldName, "TableFunction") == 0)
|
|
{
|
|
AZ_TEST_ASSERT(dcTable.IsFunction(valueIndex));
|
|
ScriptDataContext callContext;
|
|
dcTable.Call(valueIndex, callContext);
|
|
callContext.PushArg(11);
|
|
callContext.CallExecute();
|
|
AZ_TEST_ASSERT(callContext.GetNumResults() == 1);
|
|
int intValue = 0;
|
|
callContext.ReadResult(0, intValue);
|
|
AZ_TEST_ASSERT(intValue == 11);
|
|
}
|
|
else
|
|
{
|
|
AZ_TEST_ASSERT(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// script loaders
|
|
|
|
// from load function
|
|
|
|
// from data buffer
|
|
|
|
// from string
|
|
}
|
|
};
|
|
|
|
TEST_F(ScriptContextTest, LuaTest)
|
|
{
|
|
run();
|
|
}
|
|
|
|
class ScriptDebugTest
|
|
: public AllocatorsFixture
|
|
{
|
|
int m_numBreakpointHits;
|
|
|
|
static ScriptContext* s_currentSC;
|
|
public:
|
|
ScriptDebugTest()
|
|
: m_numBreakpointHits(0) {}
|
|
|
|
bool EnumLocal(const char* name, ScriptDataContext& dataContext)
|
|
{
|
|
AZ_TEST_ASSERT(strcmp(name, "a") == 0 || strcmp(name, "result") == 0);
|
|
//AZ_Printf("Script","Local variable %s = ",name);
|
|
if (dataContext.IsNumber(0))
|
|
{
|
|
double number = 0.;
|
|
dataContext.ReadValue(0, number);
|
|
//AZ_Printf("Script","%f [number]\n",number);
|
|
AZ_TEST_ASSERT(number == 10.0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void BreakpointCallback(ScriptContextDebug* debugContext, const ScriptContextDebug::Breakpoint* breakpoint)
|
|
{
|
|
//AZ_Printf("Script","\nBreakpoint at '%s(%d)' triggered!",breakpoint->m_sourceName,breakpoint->m_lineNumber);
|
|
AZ_TEST_ASSERT(breakpoint->m_sourceName == "DebugCode");
|
|
|
|
if (m_numBreakpointHits == 0)
|
|
{
|
|
AZ_TEST_ASSERT(breakpoint->m_lineNumber == 23);
|
|
ScriptContextDebug::EnumLocalCallback enumCB = AZStd::bind(&ScriptDebugTest::EnumLocal, this, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
|
debugContext->EnumLocals(enumCB);
|
|
debugContext->StepInto(); // go inside the script to line 20
|
|
}
|
|
else if (m_numBreakpointHits == 1)
|
|
{
|
|
char stackOutput[2048];
|
|
debugContext->StackTrace(stackOutput, AZ_ARRAY_SIZE(stackOutput));
|
|
AZ_Printf("Script", "%s", stackOutput);
|
|
AZ_TEST_ASSERT(strstr(stackOutput, "GlobalFunction") != nullptr);
|
|
AZ_TEST_ASSERT(breakpoint->m_lineNumber == 20);
|
|
}
|
|
else if (m_numBreakpointHits == 2)
|
|
{
|
|
AZ_TEST_ASSERT(breakpoint->m_lineNumber == 23);
|
|
// check A a local variable
|
|
ScriptContextDebug::DebugValue value;
|
|
value.m_name = "a";
|
|
bool valueResult = debugContext->GetValue(value);
|
|
AZ_TEST_ASSERT(valueResult);
|
|
AZ_TEST_ASSERT(value.m_type == LUA_TNUMBER);
|
|
AZ_TEST_ASSERT(value.m_value.find("11.0") != OSString::npos);
|
|
value.m_value = "3.0"; // modify the value
|
|
debugContext->SetValue(value);
|
|
debugContext->StepOver(); // same place as before, but now step over to line 24
|
|
}
|
|
else if (m_numBreakpointHits == 3)
|
|
{
|
|
AZ_TEST_ASSERT(breakpoint->m_lineNumber == 24);
|
|
}
|
|
else if (m_numBreakpointHits == 4)
|
|
{
|
|
AZ_TEST_ASSERT(breakpoint->m_lineNumber == 23);
|
|
debugContext->StepOut(); // same place as before, but now step out to line 28
|
|
}
|
|
else if (m_numBreakpointHits == 5)
|
|
{
|
|
AZ_TEST_ASSERT(breakpoint->m_lineNumber == 28);
|
|
}
|
|
else if (m_numBreakpointHits == 6)
|
|
{
|
|
// called from C->script->C->script scenario
|
|
char stackOutput[2048];
|
|
debugContext->StackTrace(stackOutput, AZ_ARRAY_SIZE(stackOutput));
|
|
AZ_Printf("Script", "%s", stackOutput);
|
|
AZ_TEST_ASSERT(strstr(stackOutput, "GlobalMult") != nullptr);
|
|
AZ_TEST_ASSERT(breakpoint->m_lineNumber == 23);
|
|
}
|
|
|
|
++m_numBreakpointHits;
|
|
}
|
|
|
|
static int CallCFuncOperation(int value)
|
|
{
|
|
int result = 0;
|
|
// call a script function
|
|
if (s_currentSC)
|
|
{
|
|
ScriptDataContext call;
|
|
if (s_currentSC->Call("GlobalFast", call))
|
|
{
|
|
call.PushArg(value);
|
|
if (call.CallExecute())
|
|
{
|
|
if (call.GetNumResults())
|
|
{
|
|
call.ReadResult(0, result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void run()
|
|
{
|
|
BehaviorContext behaviorContext;
|
|
behaviorContext.Class<ScriptClass>()->
|
|
Allocator(&ScriptClassAllocate, &ScriptClassFree)->
|
|
Enum<ScriptClass::SC_ET_VALUE2>("SC_ET_VALUE2")->
|
|
Enum<ScriptClass::SC_ET_VALUE3>("SC_ET_VALUE3")->
|
|
Constant("EPSILON", BehaviorConstant(0.001f))->
|
|
Method("MemberFunc0", &ScriptClass::MemberFunc0)->
|
|
Method("MemberFunc1", &ScriptClass::MemberFunc1)->
|
|
Method("MemberFunc2", &ScriptClass::MemberFunc2)->
|
|
Method("MemberFunc3", &ScriptClass::MemberFunc3)->
|
|
Method("MemberFunc4", &ScriptClass::MemberFunc4)->
|
|
Method("MemberFunc5", &ScriptClass::MemberFunc5)->
|
|
Method("MemberVoidFunc0", &ScriptClass::MemberVoidFunc0)->
|
|
Method("MemberVoidFunc1", &ScriptClass::MemberVoidFunc1)->
|
|
Method("MemberVoidFunc2", &ScriptClass::MemberVoidFunc2)->
|
|
Method("MemberVoidFunc3", &ScriptClass::MemberVoidFunc3)->
|
|
Method("MemberVoidFunc4", &ScriptClass::MemberVoidFunc4)->
|
|
Method("MemberVoidFunc5", &ScriptClass::MemberVoidFunc5)->
|
|
Method("VariadicFunc", &ScriptClass::VariadicFunc)->
|
|
Property("data", &ScriptClass::GetData, &ScriptClass::SetData)->
|
|
Property("data1", BehaviorValueProperty(&ScriptClass::m_data1))->
|
|
Property("data2", BehaviorValueGetter(&ScriptClass::m_data2ReadOnly), nullptr)->
|
|
Property("data3", nullptr, BehaviorValueSetter(&ScriptClass::m_data3WriteOnly));
|
|
|
|
behaviorContext.Property("globalField", BehaviorValueProperty(&s_globalField));
|
|
|
|
behaviorContext.Method("CallCFuncOperation", &CallCFuncOperation);
|
|
|
|
behaviorContext.Class<ScriptValueClass>()->
|
|
Property("data", BehaviorValueProperty(&ScriptValueClass::m_data));
|
|
|
|
behaviorContext.Class<ScriptValueHolder>()->
|
|
Property("value", BehaviorValueProperty(&ScriptValueHolder::m_value));
|
|
|
|
ScriptContext script;
|
|
|
|
script.BindTo(&behaviorContext);
|
|
|
|
const char code[] = "GlobalValueA = 6\n\
|
|
GlobalValueB = 12.4\n\
|
|
GlobalValueC = \"GlobalTest\"\n\
|
|
GlobalTable = {\n\
|
|
valueA = 5,\n\
|
|
valueB = 11.3,\n\
|
|
valueC = \"TableTest\",\n\
|
|
SubTable = {\n\
|
|
valueA = 6,\n\
|
|
valueB = 12.4,\n\
|
|
SubTable = {\n\
|
|
valueA = 7,\n\
|
|
},\n\
|
|
},\n\
|
|
}\n\
|
|
function GlobalTable.TableFunction (a)\n\
|
|
return a\n\
|
|
end\n\
|
|
function GlobalFunction ()\n\
|
|
return 25\n\
|
|
end\n\
|
|
function GlobalMult (a)\n\
|
|
local result = a*GlobalFunction()\n\
|
|
return result\n\
|
|
end\n\
|
|
function GlobalFast (a)\n\
|
|
local result = GlobalMult(a)*2\n\
|
|
return result\n\
|
|
end\n\
|
|
Result=GlobalFast(10)\n\
|
|
Result=GlobalFast(11)\n\
|
|
Result=GlobalFast(12)\n\
|
|
GlobalValueB=Result*2\n\
|
|
GlobalTableDerived={}\n\
|
|
setmetatable(GlobalTableDerived,GlobalTable)";
|
|
|
|
const char code2[] = "function CallACFunction(a)\n\
|
|
return CallCFuncOperation(a)\n\
|
|
end\n\
|
|
Result = CallACFunction(10)";
|
|
|
|
// enable script debug
|
|
script.EnableDebug();
|
|
|
|
// get debug context
|
|
ScriptContextDebug* debugContext = script.GetDebugContext();
|
|
|
|
if (debugContext == nullptr)
|
|
{
|
|
return; // don't test when not supported.
|
|
}
|
|
|
|
// add a breakpoint code line 23 (return a*a)
|
|
ScriptContextDebug::Breakpoint bp;
|
|
bp.m_sourceName = "DebugCode";
|
|
bp.m_lineNumber = 23;
|
|
debugContext->AddBreakpoint(bp);
|
|
|
|
// enable breakpoints
|
|
ScriptContextDebug::BreakpointCallback breakpointCallback = AZStd::bind(&ScriptDebugTest::BreakpointCallback, this, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
|
debugContext->EnableBreakpoints(breakpointCallback);
|
|
|
|
// enabled 'advanced' stack tracing
|
|
debugContext->EnableStackRecord();
|
|
|
|
// execute the code (that will trigger a breakpoint)
|
|
script.Execute(code, "DebugCode");
|
|
|
|
// now check debug break with script1->C Code->script2
|
|
s_currentSC = &script;
|
|
script.Execute(code2, "DebugCode2");
|
|
s_currentSC = nullptr;
|
|
|
|
// disable breakpoints (not this doesn't delete them, just disables them)
|
|
debugContext->DisableBreakpoints();
|
|
|
|
// test get value from globals, tables, function, and user class
|
|
ScriptContextDebug::DebugValue values[9];
|
|
bool getValueResult;
|
|
|
|
// make sure we detect non existing variables
|
|
values[0].m_name = "InvalidVariable";
|
|
getValueResult = debugContext->GetValue(values[0]);
|
|
AZ_TEST_ASSERT(getValueResult == false);
|
|
|
|
// get global number
|
|
values[0].m_name = "GlobalValueA";
|
|
getValueResult = debugContext->GetValue(values[0]);
|
|
AZ_TEST_ASSERT(getValueResult);
|
|
AZ_TEST_ASSERT(values[0].m_type == LUA_TNUMBER);
|
|
AZ_TEST_ASSERT(values[0].m_value.find("6.0") != OSString::npos);
|
|
|
|
// get global string
|
|
values[1].m_name = "GlobalValueC";
|
|
getValueResult = debugContext->GetValue(values[1]);
|
|
AZ_TEST_ASSERT(getValueResult);
|
|
AZ_TEST_ASSERT(values[1].m_type == LUA_TSTRING);
|
|
AZ_TEST_ASSERT(values[1].m_value == "GlobalTest");
|
|
|
|
// get global table
|
|
values[2].m_name = "GlobalTable";
|
|
getValueResult = debugContext->GetValue(values[2]);
|
|
AZ_TEST_ASSERT(getValueResult);
|
|
AZ_TEST_ASSERT(values[2].m_type == LUA_TTABLE);
|
|
AZ_TEST_ASSERT(values[2].m_elements.size() == 5); // valueA,valueB,valueC,SubTable and TableFunction
|
|
|
|
// get global table with a metatable
|
|
values[3].m_name = "GlobalTableDerived";
|
|
getValueResult = debugContext->GetValue(values[3]);
|
|
AZ_TEST_ASSERT(getValueResult);
|
|
AZ_TEST_ASSERT(values[3].m_type == LUA_TTABLE);
|
|
AZ_TEST_ASSERT(values[3].m_elements.size() == 1); // valueA,valueB,valueC,SubTable and TableFunction
|
|
AZ_TEST_ASSERT(values[3].m_elements[0].m_elements.size() == 5); // valueA,valueB,valueC,SubTable and TableFunction
|
|
|
|
// get global function
|
|
values[4].m_name = "GlobalFunction";
|
|
getValueResult = debugContext->GetValue(values[4]);
|
|
AZ_TEST_ASSERT(getValueResult);
|
|
AZ_TEST_ASSERT(values[4].m_type == LUA_TFUNCTION);
|
|
|
|
// get global user class
|
|
script.Execute("sc = ScriptClass()"); // create a C++ class
|
|
values[5].m_name = "sc";
|
|
getValueResult = debugContext->GetValue(values[5]);
|
|
AZ_TEST_ASSERT(getValueResult);
|
|
AZ_TEST_ASSERT(values[5].m_type == LUA_TUSERDATA);
|
|
|
|
// check nested user classes
|
|
script.Execute("sv = ScriptValueHolder()");
|
|
values[6].m_name = "sv";
|
|
getValueResult = debugContext->GetValue(values[6]);
|
|
AZ_TEST_ASSERT(getValueResult);
|
|
|
|
// get global property/field
|
|
s_globalField = 23;
|
|
values[7].m_name = "globalField";
|
|
getValueResult = debugContext->GetValue(values[7]);
|
|
AZ_TEST_ASSERT(getValueResult);
|
|
AZ_TEST_ASSERT(values[7].m_value.find("23") != OSString::npos);
|
|
|
|
// make sure we can get the global table safely (if we want to support cyclic tables)
|
|
values[8].m_name = "_G";
|
|
getValueResult = debugContext->GetValue(values[8]);
|
|
AZ_TEST_ASSERT(getValueResult == true);
|
|
|
|
ScriptContextDebug::DebugValue valueCheck;
|
|
// set a global number and change it's type to a string
|
|
values[0].m_value = "BlaBla";
|
|
values[0].m_type = LUA_TSTRING;
|
|
debugContext->SetValue(values[0]);
|
|
// check the SetValue
|
|
valueCheck.m_name = values[0].m_name;
|
|
debugContext->GetValue(valueCheck);
|
|
AZ_TEST_ASSERT(valueCheck.m_type == LUA_TSTRING);
|
|
AZ_TEST_ASSERT(valueCheck.m_value == "BlaBla");
|
|
|
|
// set a global string
|
|
values[1].m_value = "BlaBla1";
|
|
debugContext->SetValue(values[1]);
|
|
// check the set value
|
|
valueCheck.m_name = values[1].m_name;
|
|
debugContext->GetValue(valueCheck);
|
|
AZ_TEST_ASSERT(valueCheck.m_value == "BlaBla1");
|
|
|
|
// set a table value
|
|
values[2].m_elements[0].m_value = "Aha";
|
|
values[2].m_elements[0].m_type = LUA_TSTRING;
|
|
debugContext->SetValue(values[2]);
|
|
// check
|
|
valueCheck.m_name = values[2].m_name;
|
|
debugContext->GetValue(valueCheck);
|
|
AZ_TEST_ASSERT(values[2].m_elements.size() == valueCheck.m_elements.size());
|
|
|
|
// set a table value with metatable
|
|
values[3].m_elements.push_back();
|
|
ScriptContextDebug::DebugValue& subValue = values[3].m_elements.back();
|
|
subValue.m_name = "valueA";
|
|
subValue.m_type = LUA_TNUMBER;
|
|
subValue.m_value = "16";
|
|
debugContext->SetValue(values[3]);
|
|
// check
|
|
valueCheck.m_name = values[3].m_name;
|
|
debugContext->GetValue(valueCheck);
|
|
AZ_TEST_ASSERT(values[3].m_elements.size() == valueCheck.m_elements.size());
|
|
|
|
int data1Element = -1;
|
|
for (size_t i = 0; i < values[5].m_elements.size(); ++i)
|
|
{
|
|
if (values[5].m_elements[i].m_name == "data1")
|
|
{
|
|
data1Element = static_cast<int>(i);
|
|
}
|
|
}
|
|
AZ_TEST_ASSERT(data1Element != -1); // make sure we have data 1 element
|
|
|
|
// user class
|
|
AZ_TEST_ASSERT(values[5].m_elements[data1Element].m_name == "data1"); // make sure we are modifying the correct element
|
|
values[5].m_elements[data1Element].m_value = "6.0";
|
|
debugContext->SetValue(values[5]);
|
|
// check
|
|
valueCheck.m_name = values[5].m_name;
|
|
debugContext->GetValue(valueCheck);
|
|
AZ_TEST_ASSERT(values[5].m_elements.size() == valueCheck.m_elements.size());
|
|
AZ_TEST_ASSERT(valueCheck.m_elements[data1Element].m_value.find("6.0") != OSString::npos);
|
|
|
|
// user nested class
|
|
values[6].m_elements[0].m_elements[0].m_value = "3.0";
|
|
debugContext->SetValue(values[6]);
|
|
// check
|
|
valueCheck.m_name = values[6].m_name;
|
|
debugContext->GetValue(valueCheck);
|
|
AZ_TEST_ASSERT(valueCheck.m_elements[0].m_elements[0].m_value.find("3.0") != OSString::npos);
|
|
|
|
// global table
|
|
// modify todo
|
|
debugContext->SetValue(values[8]);
|
|
// check (TODO)
|
|
|
|
// disable debug support (this will delete all breakpoints and everything debug related!)
|
|
script.DisableDebug();
|
|
}
|
|
};
|
|
|
|
ScriptContext* ScriptDebugTest::s_currentSC = nullptr;
|
|
|
|
// TEST_F(ScriptDebugTest, LuaTest)
|
|
// {
|
|
// run();
|
|
// }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Ebus script test
|
|
//-----------------------------------------------------------------------------
|
|
void TestAssert(bool check)
|
|
{
|
|
(void)check;
|
|
AZ_Assert(!check, "Script Test assert");
|
|
}
|
|
|
|
class TestBusMessages
|
|
: public AZ::EBusTraits
|
|
{
|
|
public:
|
|
virtual ~TestBusMessages() {}
|
|
|
|
virtual void Set() = 0;
|
|
virtual void SetNotImplemented() = 0;
|
|
virtual void SetSum1(int) = 0;
|
|
virtual void SetSum2(int, int) = 0;
|
|
virtual void SetSum3(int, int, int) = 0;
|
|
virtual void SetSum4(int, int, int, int) = 0;
|
|
virtual void SetSum5(int, int, int, int, int) = 0;
|
|
virtual int Get() = 0;
|
|
virtual int GetNotImplemented() = 0;
|
|
virtual int GetSum1(int) = 0;
|
|
virtual int GetSum2(int, int) = 0;
|
|
virtual int GetSum3(int, int, int) = 0;
|
|
virtual int GetSum4(int, int, int, int) = 0;
|
|
virtual int GetSum5(int, int, int, int, int) = 0;
|
|
};
|
|
typedef AZ::EBus<TestBusMessages> TestBus;
|
|
|
|
class TestBusHandler
|
|
: public TestBus::Handler
|
|
, public AZ::BehaviorEBusHandler
|
|
{
|
|
public:
|
|
AZ_EBUS_BEHAVIOR_BINDER(TestBusHandler, "{CD26E702-6F40-4FF9-816D-4DCB652D97DF}", AZ::SystemAllocator,
|
|
Set, SetNotImplemented, SetSum1, SetSum2, SetSum3, SetSum4, SetSum5, Get, GetNotImplemented, GetSum1, GetSum2, GetSum3, GetSum4, GetSum5);
|
|
|
|
void Set() override
|
|
{
|
|
Call(FN_Set);
|
|
}
|
|
void SetNotImplemented() override
|
|
{
|
|
Call(FN_SetNotImplemented);
|
|
}
|
|
void SetSum1(int d1) override
|
|
{
|
|
Call(FN_SetSum1, d1);
|
|
}
|
|
void SetSum2(int d1, int d2) override
|
|
{
|
|
Call(FN_SetSum2, d1, d2);
|
|
}
|
|
void SetSum3(int d1, int d2, int d3) override
|
|
{
|
|
Call(FN_SetSum3, d1, d2, d3);
|
|
}
|
|
void SetSum4(int d1, int d2, int d3, int d4) override
|
|
{
|
|
Call(FN_SetSum4, d1, d2, d3, d4);
|
|
}
|
|
void SetSum5(int d1, int d2, int d3, int d4, int d5) override
|
|
{
|
|
Call(FN_SetSum5, d1, d2, d3, d4, d5);
|
|
}
|
|
int Get() override
|
|
{
|
|
int result = 0;
|
|
CallResult(result, FN_Get);
|
|
return result;
|
|
}
|
|
int GetNotImplemented() override
|
|
{
|
|
int result = -1;
|
|
CallResult(result, FN_GetNotImplemented);
|
|
return result;
|
|
}
|
|
int GetSum1(int d1) override
|
|
{
|
|
int result = 0;
|
|
CallResult(result, FN_GetSum1, d1);
|
|
return result;
|
|
}
|
|
int GetSum2(int d1, int d2) override
|
|
{
|
|
int result = 0;
|
|
CallResult(result, FN_GetSum2, d1, d2);
|
|
return result;
|
|
}
|
|
int GetSum3(int d1, int d2, int d3) override
|
|
{
|
|
int result = 0;
|
|
CallResult(result, FN_GetSum3, d1, d2, d3);
|
|
return result;
|
|
}
|
|
int GetSum4(int d1, int d2, int d3, int d4) override
|
|
{
|
|
int result = 0;
|
|
CallResult(result, FN_GetSum4, d1, d2, d3, d4);
|
|
return result;
|
|
}
|
|
int GetSum5(int d1, int d2, int d3, int d4, int d5) override
|
|
{
|
|
int result = 0;
|
|
CallResult(result, FN_GetSum5, d1, d2, d3, d4, d5);
|
|
return result;
|
|
}
|
|
};
|
|
|
|
//class TestBusSenderCheck : public TestBus::Handler
|
|
//{
|
|
//public:
|
|
// virtual void Set() { TestAssert(true); }
|
|
// virtual void SetNotImplemented() { /*Not used*/ }
|
|
// virtual void SetSum1(int _1) { TestAssert(_1 == 1); }
|
|
// virtual void SetSum2(int _1, int _2) { TestAssert(_1 + _2 == 3); }
|
|
// virtual void SetSum3(int _1, int _2, int _3) { TestAssert(_1 + _2 + _3 == 6); }
|
|
// virtual void SetSum4(int _1, int _2, int _3, int _4) { TestAssert(_1 + _2 + _3 + _4 == 10); }
|
|
// virtual void SetSum5(int _1, int _2, int _3, int _4, int _5) { TestAssert(_1 + _2 + _3 + _4 + _5 == 15); }
|
|
// virtual int Get() { return -1; }
|
|
// virtual int GetNotImplemented() { return -2; /*Not used*/ }
|
|
// virtual int GetSum1(int _1) { return _1; }
|
|
// virtual int GetSum2(int _1, int _2) { return _1 + _2; }
|
|
// virtual int GetSum3(int _1, int _2, int _3) { return _1 + _2 + _3; }
|
|
// virtual int GetSum4(int _1, int _2, int _3, int _4) { return _1 + _2 + _3 + _4; }
|
|
// virtual int GetSum5(int _1, int _2, int _3, int _4, int _5) { return _1 + _2 + _3 + _4 + _5; }
|
|
//};
|
|
|
|
//class MyInterface
|
|
//{
|
|
//public:
|
|
// // AZ_TYPE_INFO(MyInterface,"{96a83332-8cd1-4299-9972-e670be92ab99}");
|
|
|
|
// virtual int GetInt() const = 0;
|
|
//};
|
|
|
|
/* class MyImpl : public MyInterface
|
|
{
|
|
public:
|
|
int GetInt() const override { return 5; }
|
|
};*/
|
|
|
|
//class MyRequests : public AZ::EBusTraits
|
|
//{
|
|
//public:
|
|
// virtual const MyInterface* GetIFace() = 0;
|
|
//};
|
|
//using MyRequestBus = AZ::EBus<MyRequests>;
|
|
|
|
//AZ_SCRIPTABLE_EBUS(
|
|
// MyRequestBus,
|
|
// MyRequestBus,
|
|
// "{DD4261AD-416E-4824-97A8-EFEF0F54AD91}",
|
|
// "{9312EB0C-0717-4F91-97F8-30A946096E9B}",
|
|
// AZ_SCRIPTABLE_EBUS_EVENT_RESULT(const MyInterface*, nullptr, GetIFace)
|
|
// );
|
|
|
|
class ScriptedBusTest
|
|
: public AllocatorsFixture {
|
|
public:
|
|
void run()
|
|
{
|
|
const char luaCode[] = "\
|
|
MyBusHandlerMetaTable1 = {\
|
|
Set = function(self)\
|
|
TestAssert(true)\
|
|
end,\
|
|
SetSum1 = function(self, _1)\
|
|
TestAssert(_1 == 1)\
|
|
end,\
|
|
SetSum2 = function(self, _1, _2)\
|
|
TestAssert(_1 + _2 == 3)\
|
|
end,\
|
|
SetSum3 = function(self, _1, _2, _3)\
|
|
TestAssert(_1 + _2 + _3 == 6)\
|
|
end,\
|
|
SetSum4 = function(self, _1, _2, _3, _4)\
|
|
TestAssert(_1 + _2 + _3 + _4 == 10)\
|
|
end,\
|
|
SetSum5 = function(self, _1, _2, _3, _4, _5)\
|
|
TestAssert(_1 + _2 + _3 + _4 + _5 == 15)\
|
|
end,\
|
|
Get = function(self)\
|
|
return -1\
|
|
end,\
|
|
GetSum1 = function(self, _1)\
|
|
return _1\
|
|
end,\
|
|
GetSum2 = function(self, _1, _2)\
|
|
return _1 + _2\
|
|
end,\
|
|
GetSum3 = function(self, _1, _2, _3)\
|
|
return _1 + _2 + _3\
|
|
end,\
|
|
GetSum4 = function(self, _1, _2, _3, _4)\
|
|
return _1 + _2 + _3 + _4\
|
|
end,\
|
|
GetSum5 = function(self, _1, _2, _3, _4, _5)\
|
|
return _1 + _2 + _3 + _4 + _5\
|
|
end\
|
|
}\
|
|
MyBusHandlerMetaTable2 = {\
|
|
Get = function(self)\
|
|
return -2\
|
|
end\
|
|
}\
|
|
setmetatable(MyBusHandlerMetaTable2, MyBusHandlerMetaTable1)\
|
|
";
|
|
BehaviorContext behaviorContext;
|
|
behaviorContext.Method("TestAssert", &TestAssert);
|
|
behaviorContext.EBus<TestBus>("TestBus")->
|
|
Handler<TestBusHandler>()->
|
|
Event("Set", &TestBus::Events::Set)->
|
|
Event("SetNotImplemented", &TestBus::Events::SetNotImplemented)->
|
|
Event("SetSum1", &TestBus::Events::SetSum1)->
|
|
Event("SetSum2", &TestBus::Events::SetSum2)->
|
|
Event("SetSum3", &TestBus::Events::SetSum3)->
|
|
Event("SetSum4", &TestBus::Events::SetSum4)->
|
|
Event("SetSum5", &TestBus::Events::SetSum5)->
|
|
Event("Get", &TestBus::Events::Get)->
|
|
Event("GetNotImplemented", &TestBus::Events::GetNotImplemented)->
|
|
Event("GetSum1", &TestBus::Events::GetSum1)->
|
|
Event("GetSum2", &TestBus::Events::GetSum2)->
|
|
Event("GetSum3", &TestBus::Events::GetSum3)->
|
|
Event("GetSum4", &TestBus::Events::GetSum4)->
|
|
Event("GetSum5", &TestBus::Events::GetSum5)
|
|
;
|
|
|
|
ScriptContext script;
|
|
script.BindTo(&behaviorContext);
|
|
|
|
script.Execute(luaCode);
|
|
|
|
// Test sending messages from C++ to LUA handler
|
|
script.Execute("handler = TestBus.Connect(MyBusHandlerMetaTable1)");
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
EBUS_EVENT(TestBus, Set);
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
EBUS_EVENT(TestBus, SetNotImplemented); // nothing should happen here
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
EBUS_EVENT(TestBus, SetSum1, 1);
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
EBUS_EVENT(TestBus, SetSum2, 1, 2);
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
EBUS_EVENT(TestBus, SetSum3, 1, 2, 3);
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
EBUS_EVENT(TestBus, SetSum4, 1, 2, 3, 4);
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
EBUS_EVENT(TestBus, SetSum5, 1, 2, 3, 4, 5);
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
script.Execute("handler:Disconnect()");
|
|
|
|
script.Execute("handler = TestBus.Connect(MyBusHandlerMetaTable2)");
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("TestBus.Broadcast.Set()");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("TestBus.Broadcast.SetNotImplemented()");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(0);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("TestBus.Broadcast.SetSum1(1)");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("TestBus.Broadcast.SetSum2(1, 2)");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("TestBus.Broadcast.SetSum3(1, 2, 3)");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("TestBus.Broadcast.SetSum4(1, 2, 3, 4)");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("TestBus.Broadcast.SetSum5(1, 2, 3, 4, 5)");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("TestAssert(TestBus.Broadcast.Get() == -2)");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("TestAssert(TestBus.Broadcast.GetNotImplemented() == -1)");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("TestAssert(TestBus.Broadcast.GetSum1(1) == 1)");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("TestAssert(TestBus.Broadcast.GetSum2(1, 2) == 3)");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("TestAssert(TestBus.Broadcast.GetSum3(1, 2, 3) == 6)");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("TestAssert(TestBus.Broadcast.GetSum4(1, 2, 3, 4) == 10)");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("TestAssert(TestBus.Broadcast.GetSum5(1, 2, 3, 4, 5) == 15)");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
script.Execute("handler:Disconnect()\
|
|
handler = nil");
|
|
}
|
|
};
|
|
|
|
TEST_F(ScriptedBusTest, LuaTest)
|
|
{
|
|
run();
|
|
}
|
|
|
|
// RamenRequests
|
|
class RamenRequests
|
|
: public AZ::EBusTraits
|
|
{
|
|
public:
|
|
typedef unsigned int BusIdType;
|
|
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
|
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
|
|
|
|
virtual void AddPepper() = 0;
|
|
};
|
|
using RamenRequestBus = AZ::EBus<RamenRequests>;
|
|
|
|
class RamenRequestHandler
|
|
: public RamenRequestBus::Handler
|
|
{
|
|
public:
|
|
void AddPepper() override { };
|
|
};
|
|
|
|
class RamenRequestBehaviorHandler
|
|
: public RamenRequestBus::Handler
|
|
, public BehaviorEBusHandler
|
|
{
|
|
public:
|
|
AZ_EBUS_BEHAVIOR_BINDER(RamenRequestBehaviorHandler, "{EB4E043B-AD1A-4745-A725-91100E191517}", AZ::SystemAllocator, AddPepper);
|
|
|
|
void AddPepper() override
|
|
{
|
|
Call(FN_AddPepper);
|
|
}
|
|
};
|
|
// RamenRequests
|
|
|
|
// RamenShopNotifications
|
|
class RamenShopNotifications
|
|
: public AZ::EBusTraits
|
|
{
|
|
public:
|
|
typedef unsigned int BusIdType;
|
|
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
|
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
|
|
|
|
virtual void OnOrderCancelled(int id) = 0;
|
|
};
|
|
using RamenShopNotificationBus = AZ::EBus<RamenShopNotifications>;
|
|
|
|
class RamenShopNotificationHandler
|
|
: public RamenShopNotificationBus::MultiHandler
|
|
{
|
|
public:
|
|
void OnOrderCancelled(int /*id*/) override {};
|
|
};
|
|
|
|
class RamenShopNotificationBehaviorHandler
|
|
: public RamenShopNotificationBus::MultiHandler
|
|
, public BehaviorEBusHandler
|
|
{
|
|
public:
|
|
AZ_EBUS_BEHAVIOR_BINDER(RamenShopNotificationBehaviorHandler, "{98A0C1B1-0B81-4563-886A-03204DDBE146}", AZ::SystemAllocator, OnOrderCancelled);
|
|
|
|
void OnOrderCancelled(int id) override
|
|
{
|
|
Call(FN_OnOrderCancelled, id);
|
|
}
|
|
};
|
|
// RamenShopNotifications
|
|
|
|
class ScriptBehaviorHandlerIsConnectedTest
|
|
: public AllocatorsFixture
|
|
{
|
|
public:
|
|
static void TestAssert([[maybe_unused]] bool check)
|
|
{
|
|
AZ_Assert(check, "Script Test assert");
|
|
}
|
|
|
|
void SetUp() override
|
|
{
|
|
AllocatorsFixture::SetUp();
|
|
|
|
m_behaviorContext = aznew BehaviorContext();
|
|
m_behaviorContext->Method("TestAssert", &ScriptBehaviorHandlerIsConnectedTest::TestAssert);
|
|
|
|
m_behaviorContext->EBus<RamenRequestBus>("RamenRequestBus")->
|
|
Handler<RamenRequestBehaviorHandler>()->
|
|
Event("AddPepper", &RamenRequestBus::Events::AddPepper);
|
|
|
|
m_behaviorContext->EBus<RamenShopNotificationBus>("RamenShopNotificationBus")->
|
|
Handler<RamenShopNotificationBehaviorHandler>()->
|
|
Event("OnOrderCancelled", &RamenShopNotificationBus::Events::OnOrderCancelled);
|
|
|
|
m_scriptContext = aznew ScriptContext();
|
|
m_scriptContext->BindTo(m_behaviorContext);
|
|
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
delete m_scriptContext;
|
|
delete m_behaviorContext;
|
|
|
|
AllocatorsFixture::TearDown();
|
|
}
|
|
|
|
ScriptContext* m_scriptContext;
|
|
BehaviorContext* m_behaviorContext;
|
|
};
|
|
|
|
TEST_F(ScriptBehaviorHandlerIsConnectedTest, LuaIsConnected_UberTest)
|
|
{
|
|
const char luaCode[] = R"(
|
|
ramen = {
|
|
handler1 = nil,
|
|
handler2 = nil
|
|
}
|
|
ramen.handler1 = RamenRequestBus.Connect(ramen, 1)
|
|
ramen.handler2 = RamenRequestBus.Connect(ramen, 2)
|
|
|
|
ramenShop = {
|
|
handler = nil
|
|
}
|
|
ramenShop.handler = RamenShopNotificationBus.CreateHandler(ramenShop)
|
|
ramenShop.handler:Connect(3);
|
|
ramenShop.handler:Connect(4);
|
|
)";
|
|
m_scriptContext->Execute(luaCode);
|
|
|
|
m_scriptContext->Execute("TestAssert(ramen.handler1:IsConnected())");
|
|
m_scriptContext->Execute("TestAssert(ramen.handler1:IsConnectedId(1))");
|
|
m_scriptContext->Execute("TestAssert(ramen.handler2:IsConnectedId(2))");
|
|
|
|
m_scriptContext->Execute("TestAssert(ramenShop.handler:IsConnected())");
|
|
m_scriptContext->Execute("TestAssert(ramenShop.handler:IsConnectedId(3))");
|
|
m_scriptContext->Execute("TestAssert(ramenShop.handler:IsConnectedId(4))");
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Ebus script with bus id test
|
|
//-----------------------------------------------------------------------------
|
|
class TestIdBusMessages
|
|
: public AZ::EBusTraits
|
|
{
|
|
public:
|
|
typedef unsigned int BusIdType;
|
|
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
|
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
|
|
|
|
virtual ~TestIdBusMessages() {}
|
|
|
|
virtual void VoidFunc0() = 0;
|
|
virtual float Pick(float, float, float) = 0;
|
|
};
|
|
typedef AZ::EBus<TestIdBusMessages> TestIdBus;
|
|
|
|
class TestIdBusCPPHandler
|
|
: public TestIdBus::Handler
|
|
{
|
|
public:
|
|
void VoidFunc0() override { TestAssert(true); }
|
|
float Pick(float /*_1*/, float /*_2*/, float _3) override { return _3; }
|
|
};
|
|
|
|
//AZ_SCRIPTABLE_EBUS(
|
|
// TestIdBus,
|
|
// TestIdBus,
|
|
// "{2DACEBCC-CB6B-403A-9E91-4E7390F567C9}", // Handler type UUID
|
|
// "{EB13A5D0-D642-49E2-9D3C-DC15F96FD97A}", // Sender type UUID
|
|
// AZ_SCRIPTABLE_EBUS_EVENT(VoidFunc0)
|
|
// AZ_SCRIPTABLE_EBUS_EVENT_RESULT(float, 0.f, Pick, float, float, float)
|
|
// )
|
|
|
|
class TestIdBusHandler
|
|
: public TestIdBus::Handler
|
|
, public AZ::BehaviorEBusHandler
|
|
{
|
|
public:
|
|
AZ_EBUS_BEHAVIOR_BINDER(TestIdBusHandler, "{2DACEBCC-CB6B-403A-9E91-4E7390F567C9}", AZ::SystemAllocator,
|
|
VoidFunc0, Pick);
|
|
|
|
void VoidFunc0() override
|
|
{
|
|
Call(FN_VoidFunc0);
|
|
}
|
|
float Pick(float a, float b, float c) override
|
|
{
|
|
float result = 0.0f;
|
|
CallResult(result, FN_Pick, a, b, c);
|
|
return result;
|
|
}
|
|
};
|
|
|
|
class ScriptedIdBusTest
|
|
: public AllocatorsFixture
|
|
{
|
|
public:
|
|
void run()
|
|
{
|
|
const char luaCode[] = "\
|
|
MyBusHandlerMetaTable1 = {\
|
|
VoidFunc0 = function(self)\
|
|
TestAssert(true)\
|
|
end,\
|
|
Pick = function(self, _1, _2, _3)\
|
|
return _1\
|
|
end\
|
|
}\
|
|
MyBusHandlerMetaTable2 = {\
|
|
Pick = function(self, _1, _2, _3)\
|
|
return _2\
|
|
end\
|
|
}\
|
|
setmetatable(MyBusHandlerMetaTable2, MyBusHandlerMetaTable1)\
|
|
handler1 = TestIdBus.Connect(MyBusHandlerMetaTable1, 1)\
|
|
handler2 = TestIdBus.CreateHandler(MyBusHandlerMetaTable2)\
|
|
handler2:Connect(2)\
|
|
";
|
|
BehaviorContext behaviorContext;
|
|
behaviorContext.Method("TestAssert", &TestAssert);
|
|
behaviorContext.EBus<TestIdBus>("TestIdBus")->
|
|
Handler<TestIdBusHandler>()->
|
|
Event("VoidFunc0", &TestIdBus::Events::VoidFunc0)->
|
|
Event("Pick", &TestIdBus::Events::Pick);
|
|
|
|
ScriptContext script;
|
|
script.BindTo(&behaviorContext);
|
|
|
|
script.Execute(luaCode);
|
|
|
|
// Test sending messages from C++ to two LUA handlers on different bus ids
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
EBUS_EVENT_ID(1, TestIdBus, VoidFunc0);
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
EBUS_EVENT_ID(2, TestIdBus, VoidFunc0);
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
float ret = 0.f;
|
|
EBUS_EVENT_ID_RESULT(ret, 1, TestIdBus, Pick, 1.f, 2.f, 3.f);
|
|
AZ_TEST_ASSERT(ret == 1.f);
|
|
ret = 0;
|
|
EBUS_EVENT_ID_RESULT(ret, 2, TestIdBus, Pick, 1.f, 2.f, 3.f);
|
|
AZ_TEST_ASSERT(ret == 2.f);
|
|
script.Execute("handler1:Disconnect()");
|
|
script.Execute("handler2:Disconnect()");
|
|
|
|
// Test sending messages from LUA to a LUA handler on id 1 and a C++ handler on id 3.
|
|
script.Execute("handler = TestIdBus.Connect(MyBusHandlerMetaTable1, 1)");
|
|
TestIdBusCPPHandler cppHandler;
|
|
cppHandler.BusConnect(3);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("TestIdBus.Event.VoidFunc0(1)"); // send even on id=1
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("TestAssert(TestIdBus.Event.Pick(1, 1.0, 2.0, 3.0) == 1.0)");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("TestIdBus.Event.VoidFunc0(3)");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("TestAssert(TestIdBus.Event.Pick(3, 1.0, 2.0, 3.0) == 3.0)");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
script.Execute("handler:Disconnect()");
|
|
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("handler:Connect('my fake bus id')");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
script.Execute("handler = TestIdBus.Connect({ }, 'my fake bus id')");
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
|
script.Execute("TestAssert(handler == nil)");
|
|
}
|
|
};
|
|
|
|
TEST_F(ScriptedIdBusTest, LuaTest)
|
|
{
|
|
run();
|
|
}
|
|
|
|
void AZTestAssert(bool check)
|
|
{
|
|
(void)check;
|
|
AZ_Assert(check, "Script Test assert");
|
|
}
|
|
|
|
class AnyScriptBindTest
|
|
: public AllocatorsFixture
|
|
{
|
|
public:
|
|
struct DataContainer
|
|
{
|
|
public:
|
|
AZ_TYPE_INFO(DataContainer, "{FC0B12F1-165F-4D0C-ABAE-E9F657370352}");
|
|
|
|
DataContainer(int initData)
|
|
: m_data(initData)
|
|
{ }
|
|
|
|
int m_data;
|
|
};
|
|
|
|
static bool s_wasCalled;
|
|
static void TestThatDataIs10(AZStd::any container)
|
|
{
|
|
ASSERT_TRUE(container.is<DataContainer>());
|
|
ASSERT_EQ(10, AZStd::any_cast<DataContainer&>(container).m_data);
|
|
s_wasCalled = true;
|
|
}
|
|
|
|
void SetUp() override
|
|
{
|
|
AllocatorsFixture::SetUp();
|
|
|
|
s_wasCalled = false;
|
|
|
|
m_behavior = aznew BehaviorContext();
|
|
|
|
m_behavior->Class<DataContainer>("DataContainer")
|
|
->Constructor<int>()
|
|
->Property("data", BehaviorValueProperty(&DataContainer::m_data))
|
|
;
|
|
|
|
m_behavior->Method("TestThatDataIs10", &TestThatDataIs10);
|
|
m_behavior->Property("wasCalled", BehaviorValueGetter(&s_wasCalled), nullptr);
|
|
|
|
m_script = aznew ScriptContext();
|
|
m_script->BindTo(m_behavior);
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
delete m_script;
|
|
delete m_behavior;
|
|
|
|
AllocatorsFixture::TearDown();
|
|
}
|
|
|
|
BehaviorContext* m_behavior;
|
|
ScriptContext* m_script;
|
|
};
|
|
bool AnyScriptBindTest::s_wasCalled = false;
|
|
|
|
TEST_F(AnyScriptBindTest, LuaScriptContextAny_AnyFromLua)
|
|
{
|
|
m_script->Execute("TestThatDataIs10(DataContainer(10))");
|
|
EXPECT_TRUE(s_wasCalled);
|
|
}
|
|
|
|
class BaseScriptTest
|
|
: public AllocatorsFixture
|
|
{
|
|
public:
|
|
void SetUp() override
|
|
{
|
|
AllocatorsFixture::SetUp();
|
|
|
|
m_behavior = aznew BehaviorContext();
|
|
m_behavior->Method("AZTestAssert", &AZTestAssert);
|
|
SetupBehaviorContext(*m_behavior);
|
|
|
|
m_script = aznew ScriptContext();
|
|
m_script->BindTo(m_behavior);
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
delete m_script;
|
|
delete m_behavior;
|
|
AllocatorsFixture::TearDown();
|
|
}
|
|
|
|
BehaviorContext* m_behavior;
|
|
ScriptContext* m_script;
|
|
|
|
virtual void SetupBehaviorContext(BehaviorContext& bc) { (void)bc; }
|
|
};
|
|
|
|
TEST_F(BaseScriptTest, LuaUnreflect)
|
|
{
|
|
m_behavior->Class<ScriptClass>();
|
|
m_script->Execute("AZTestAssert(ScriptClass ~= nil)");
|
|
|
|
m_behavior->EnableRemoveReflection();
|
|
m_behavior->Class<ScriptClass>();
|
|
m_behavior->DisableRemoveReflection();
|
|
|
|
m_script->Execute("AZTestAssert(ScriptClass == nil)");
|
|
}
|
|
|
|
class MathScriptTest
|
|
: public BaseScriptTest
|
|
{
|
|
static void ScriptAssertEqual(double lhs, double rhs)
|
|
{
|
|
EXPECT_TRUE(AZ::IsClose(lhs, rhs, static_cast<double>(AZ::Constants::FloatEpsilon)));
|
|
}
|
|
|
|
void SetupBehaviorContext(BehaviorContext& bc) override
|
|
{
|
|
MathReflect(&bc);
|
|
bc.Method("ScriptAssertEqual", &ScriptAssertEqual);
|
|
}
|
|
};
|
|
|
|
TEST_F(MathScriptTest, LuaScriptMath_BasicRandomTests)
|
|
{
|
|
// tests pseudo random number generator
|
|
m_script->Execute(R"LUA(
|
|
local r = Random();
|
|
r = Random(4321);
|
|
r:SetSeed(1212);
|
|
ScriptAssertEqual(r:GetRandom(), 2783974736);
|
|
ScriptAssertEqual(r:GetRandomFloat(), 0.61152875423431396);
|
|
)LUA");
|
|
}
|
|
|
|
class ScriptTypeidTest
|
|
: public AllocatorsFixture
|
|
{
|
|
public:
|
|
void SetUp() override
|
|
{
|
|
AllocatorsFixture::SetUp();
|
|
|
|
m_behavior = aznew BehaviorContext();
|
|
|
|
AZ::MathReflect(m_behavior);
|
|
|
|
m_behavior->Class<ScriptClass>();
|
|
m_behavior->Method("AZTestAssert", &AZTestAssert);
|
|
m_behavior->Method("CheckScriptClassTypeid", &CheckScriptTypeid<ScriptClass>);
|
|
m_behavior->Method("CheckScriptBoolTypeid", &CheckScriptTypeid<bool>);
|
|
m_behavior->Method("CheckScriptStringTypeid", &CheckScriptTypeid<AZStd::string>);
|
|
m_behavior->Method("CheckScriptNumberTypeid", &CheckScriptTypeid<double>);
|
|
m_behavior->Method("CheckScriptTypeidNullptr", &CheckScriptTypeidNullptr);
|
|
|
|
m_script = aznew ScriptContext();
|
|
m_script->BindTo(m_behavior);
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
delete m_script;
|
|
delete m_behavior;
|
|
|
|
AllocatorsFixture::TearDown();
|
|
}
|
|
|
|
template <typename T>
|
|
static void CheckScriptTypeid(const AZ::Uuid& typeId)
|
|
{
|
|
EXPECT_EQ(azrtti_typeid<T>(), typeId);
|
|
}
|
|
|
|
static void CheckScriptTypeidNullptr(const AZ::Uuid& typeId)
|
|
{
|
|
EXPECT_TRUE(typeId.IsNull());
|
|
}
|
|
|
|
BehaviorContext* m_behavior = nullptr;
|
|
ScriptContext* m_script = nullptr;
|
|
};
|
|
|
|
TEST_F(ScriptTypeidTest, LuaTypeId_Class)
|
|
{
|
|
m_script->Execute("CheckScriptClassTypeid(typeid(ScriptClass))");
|
|
m_script->Execute("local v = ScriptClass(); CheckScriptClassTypeid(typeid(v))");
|
|
}
|
|
|
|
TEST_F(ScriptTypeidTest, LuaTypeId_Bool)
|
|
{
|
|
m_script->Execute("CheckScriptBoolTypeid(typeid(true))");
|
|
}
|
|
|
|
TEST_F(ScriptTypeidTest, LuaTypeId_String)
|
|
{
|
|
m_script->Execute("CheckScriptStringTypeid(typeid(''))");
|
|
}
|
|
|
|
TEST_F(ScriptTypeidTest, LuaTypeId_Number)
|
|
{
|
|
m_script->Execute("CheckScriptNumberTypeid(typeid(1))");
|
|
}
|
|
|
|
TEST_F(ScriptTypeidTest, LuaTypeId_ArgMiscount)
|
|
{
|
|
m_script->Execute("CheckScriptTypeidNullptr(typeid())");
|
|
m_script->Execute("CheckScriptTypeidNullptr(typeid(1, 2, 3))");
|
|
}
|
|
|
|
TEST_F(ScriptTypeidTest, LuaTypeId_UnhandledType)
|
|
{
|
|
m_script->Execute("CheckScriptTypeidNullptr(typeid(function() end))");
|
|
m_script->Execute("CheckScriptTypeidNullptr(typeid({ }))");
|
|
}
|
|
|
|
class ScriptCacheTableTest
|
|
: public AllocatorsFixture
|
|
{
|
|
public:
|
|
void SetUp() override
|
|
{
|
|
AllocatorsFixture::SetUp();
|
|
|
|
m_script = aznew ScriptContext();
|
|
m_lua = m_script->NativeContext();
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
m_lua = nullptr;
|
|
delete m_script;
|
|
|
|
AllocatorsFixture::TearDown();
|
|
}
|
|
|
|
ScriptContext* m_script = nullptr;
|
|
lua_State* m_lua = nullptr;
|
|
};
|
|
|
|
TEST_F(ScriptCacheTableTest, LuaNilRef)
|
|
{
|
|
lua_pushnil(m_lua);
|
|
EXPECT_EQ(LUA_REFNIL, Internal::LuaCacheValue(m_lua, -1));
|
|
lua_pop(m_lua, 1);
|
|
|
|
Internal::LuaLoadCached(m_lua, LUA_REFNIL);
|
|
EXPECT_TRUE(lua_isnil(m_lua, -1));
|
|
lua_pop(m_lua, 1);
|
|
}
|
|
|
|
TEST_F(ScriptCacheTableTest, LuaInsertElement_RetreiveElement_IsSame)
|
|
{
|
|
lua_pushliteral(m_lua, "TEST_STRING");
|
|
int ref = Internal::LuaCacheValue(m_lua, -1);
|
|
lua_pop(m_lua, 1);
|
|
ASSERT_GT(ref, 0);
|
|
|
|
Internal::LuaLoadCached(m_lua, ref);
|
|
ASSERT_EQ(LUA_TSTRING, lua_type(m_lua, -1));
|
|
EXPECT_STREQ("TEST_STRING", lua_tostring(m_lua, -1));
|
|
lua_pop(m_lua, 1);
|
|
|
|
Internal::LuaReleaseCached(m_lua, ref);
|
|
}
|
|
|
|
TEST_F(ScriptCacheTableTest, LuaInsertElements_RemoveFromMiddle_RemoveFromEnd)
|
|
{
|
|
int refs[6] = { };
|
|
int removedRefs[] = { 2, 4, 5 };
|
|
|
|
// Create a bunch of references
|
|
lua_pushinteger(m_lua, 42);
|
|
for (int& ref : refs)
|
|
{
|
|
ref = Internal::LuaCacheValue(m_lua, -1);
|
|
ASSERT_GT(ref, 0);
|
|
}
|
|
|
|
// All refs should be valid at the beginning
|
|
for (const int& ref : refs)
|
|
{
|
|
Internal::LuaLoadCached(m_lua, ref);
|
|
ASSERT_FALSE(lua_isnil(m_lua, -1));
|
|
EXPECT_EQ(42, lua_tointeger(m_lua, -1));
|
|
lua_pop(m_lua, 1);
|
|
}
|
|
|
|
// Remove some refs
|
|
for (const int& refIdx : removedRefs)
|
|
{
|
|
Internal::LuaReleaseCached(m_lua, refs[refIdx]);
|
|
refs[refIdx] = LUA_NOREF;
|
|
}
|
|
|
|
// All non-removed refs here should be valid
|
|
for (const int& ref : refs)
|
|
{
|
|
// Skip removed refs
|
|
if (ref == LUA_NOREF)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Internal::LuaLoadCached(m_lua, ref);
|
|
ASSERT_FALSE(lua_isnil(m_lua, -1));
|
|
EXPECT_EQ(42, lua_tointeger(m_lua, -1));
|
|
lua_pop(m_lua, 1);
|
|
}
|
|
|
|
// Reinsert the refs
|
|
for (const int& refIdx : removedRefs)
|
|
{
|
|
refs[refIdx] = Internal::LuaCacheValue(m_lua, -1);
|
|
}
|
|
|
|
// All refs should be valid again
|
|
for (const int& ref : refs)
|
|
{
|
|
Internal::LuaLoadCached(m_lua, ref);
|
|
ASSERT_FALSE(lua_isnil(m_lua, -1));
|
|
EXPECT_EQ(42, lua_tointeger(m_lua, -1));
|
|
lua_pop(m_lua, 1);
|
|
}
|
|
|
|
for (int& ref : refs)
|
|
{
|
|
Internal::LuaReleaseCached(m_lua, ref);
|
|
ref = LUA_NOREF;
|
|
}
|
|
|
|
// Get cache table to ensure it's reset
|
|
lua_rawgeti(m_lua, LUA_REGISTRYINDEX, AZ_LUA_WEAK_CACHE_TABLE_REF);
|
|
|
|
lua_rawgeti(m_lua, -1, 2); // Get first free list index
|
|
EXPECT_EQ(3, (int)lua_tointeger(m_lua, -1)) << "Free list index does not point to first assignable element";
|
|
lua_pop(m_lua, 1);
|
|
|
|
lua_rawgeti(m_lua, -1, 3); // Get the first free element
|
|
EXPECT_TRUE(lua_isnil(m_lua, -1)) << "First assignable element is not empty";
|
|
lua_pop(m_lua, 1);
|
|
|
|
EXPECT_EQ(2u, lua_rawlen(m_lua, -1)) << "Cache table contains more than just header";
|
|
|
|
lua_pop(m_lua, 1);
|
|
}
|
|
|
|
class UnregisteredSharedPointerTest
|
|
: public BehaviorContextFixture
|
|
{
|
|
public:
|
|
struct UnregisteredType
|
|
{
|
|
AZ_CLASS_ALLOCATOR(UnregisteredType, SystemAllocator, 0);
|
|
AZ_TYPE_INFO(UnregisteredType, "{6C5C91A6-A9F6-470D-8841-179201A0D9E6}");
|
|
};
|
|
|
|
static AZStd::shared_ptr<UnregisteredType> GetPtr()
|
|
{
|
|
return AZStd::make_shared<UnregisteredType>();
|
|
}
|
|
|
|
static void TestIsPtrNullptr(AZStd::shared_ptr<UnregisteredType> ptr)
|
|
{
|
|
ASSERT_NE(nullptr, ptr);
|
|
}
|
|
|
|
void SetUp() override
|
|
{
|
|
BehaviorContextFixture::SetUp();
|
|
|
|
m_behaviorContext->Method("AZTestAssert", &AZTestAssert);
|
|
m_behaviorContext->Method("GetPtr", &GetPtr);
|
|
m_behaviorContext->Method("TestIsPtrNullptr", &TestIsPtrNullptr);
|
|
|
|
m_script = aznew ScriptContext();
|
|
m_script->BindTo(m_behaviorContext);
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
delete m_script;
|
|
|
|
BehaviorContextFixture::TearDown();
|
|
}
|
|
|
|
ScriptContext* m_script = nullptr;
|
|
};
|
|
|
|
TEST_F(UnregisteredSharedPointerTest, LuaTest)
|
|
{
|
|
// Test that the pointer remains valid
|
|
m_script->Execute(R"LUA(
|
|
instance = GetPtr();
|
|
TestIsPtrNullptr(instance);
|
|
AZTestAssert(instance:get() ~= nil);
|
|
)LUA");
|
|
|
|
ScriptContext::ErrorHook errorHook = [](ScriptContext*, ScriptContext::ErrorType error, const char*) {
|
|
ASSERT_GT(error, ScriptContext::ErrorType::Log);
|
|
};
|
|
|
|
// Test that actually doing stuff with it alerts the user
|
|
auto oldHook = m_script->GetErrorHook();
|
|
m_script->SetErrorHook(errorHook);
|
|
m_script->Execute(R"LUA(
|
|
instance:DoThing();
|
|
)LUA");
|
|
m_script->SetErrorHook(oldHook);
|
|
}
|
|
}
|
|
|
|
|
|
#endif // #if !defined(AZCORE_EXCLUDE_LUA)
|