/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright EntityRef license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ScriptCanvas; using namespace ScriptCanvasTests; using namespace TestNodes; using namespace ScriptCanvas::Execution; // TODO: This fails to compile on Linux only //ScriptCanvas::Grammar::SubgraphInterface* CreateExecutionMap(AZ::BehaviorContext& behaviorContext) //{ // using namespace ScriptCanvas; // using namespace ScriptCanvas::Grammar; // // auto ins = CreateInsFromBehaviorContextMethods("TestNodeableObject", behaviorContext, { "Branch" } ); // auto branchOutVoidTrue = CreateOut("BranchTrue", { "condition", "message" }); // auto branchOutVoidFalse = CreateOut("BranchFalse", { "condition", "message", "vector" }); // // auto branchOut = FindInByName("Branch", ins); // // EXPECT_NE(branchOut, nullptr); // if (branchOut) // { // branchOut->outs.emplace_back(AZStd::move(branchOutVoidTrue)); // branchOut->outs.emplace_back(AZStd::move(branchOutVoidFalse)); // } // // return aznew SubgraphInterface(AZStd::move(ins)); //} TEST_F(ScriptCanvasTestFixture, TestLuaObjectOrientation) { using namespace ScriptCanvas; using namespace ScriptCanvas::Grammar; AZ::ScriptContext sc; sc.BindTo(m_behaviorContext); RegisterAPI(sc.NativeContext()); EXPECT_TRUE(sc.Execute( R"LUA( assert(Nodeable ~= nil, 'Nodeable was nill') assert(Nodeable.__call ~= nil, 'Nodeable.__call was nil') assert(type(Nodeable.__call) == 'function', 'Nodeable.__call was not a function') local nodeable = Nodeable() assert(nodeable ~= nil, 'nodeable was nil') assert(type(nodeable) == "userdata", 'nodeable not userdata') local SubGraph = {} SubGraph.s_name = "SubGraphery" SubGraph.s_createdCount = 0 function SubGraph:IncrementCreated() SubGraph.s_createdCount = 1 + SubGraph.s_createdCount end setmetatable(SubGraph, { __index = Nodeable }) -- exposed through BehaviorContext local SubGraphInstanceMetatable = { __index = SubGraph } assert(getmetatable(SubGraph).__index == Nodeable, 'getmetatable(SubGraph).__index = Nodeable') assert(type(getmetatable(SubGraph).__index) == 'table', "type(getmetatable(SubGraph).__index) ~= 'table'") function SubGraph.new() -- Add executionState input here and to Nodeable() -- now individual instance values can be initialized local instance = OverrideNodeableMetatable(Nodeable(), SubGraphInstanceMetatable) assert(type(instance.s_createdCount) == 'number', 'subgraph.s_createdCount was not a number') instance:IncrementCreated() instance.name = 'SubGraph '..tostring(instance.s_createdCount) return instance end function SubGraph.newTable() -- Add executionState input here and to Nodeable() -- now individual instance values can be initialized local instance = setmetatable({}, SubGraphInstanceMetatable) -- assert(getmetatable(instance) == SubGraphInstanceMetatable, "subgraphT") assert(type(instance.s_createdCount) == 'number', 'subgraphT.s_createdCount was not a number') instance:IncrementCreated() instance.name = 'SubGraph '..tostring(instance.s_createdCount) return instance end function SubGraph:Foo() return "I, " .. tostring(self.name) .. ", am a user function" end local subgraphT = SubGraph.newTable() assert(subgraphT ~= nil, "subgraphT was nil") assert(type(subgraphT) == 'table', 'subgraphT was not a table') assert(type(subgraphT.IsActive)== 'function', "subgraphT IsActive was not a function") assert(type(subgraphT.Foo) == 'function', 'subgraphT was not a function') local subgraphTResult = subgraphT:Foo() assert(subgraphTResult == "I, SubGraph 1, am a user function", 'subgraphT did not return the right results:' .. tostring(subgraphTResult)) assert(subgraphT.s_createdCount == 1, "subgraphT created count was not one: ".. tostring(subgraphT.s_createdCount)) subgraphT = SubGraph.newTable() assert(subgraphT.s_createdCount == 2, "subgraphT created count was not two: ".. tostring(subgraphT.s_createdCount)) local subgraph = SubGraph.new() assert(subgraph ~= nil, "subgraph was nil") assert(type(subgraph) == 'userdata', 'was not userdata') assert(type(subgraph.IsActive)== 'function', "IsActive was not a function") assert(not subgraph.IsActive(subgraph), "did not inherit properly") assert(not subgraph:IsActive(), "did not inherit properly") assert(type(subgraph.Foo) == 'function', 'was not a function') local subgraphResult = subgraph:Foo() assert(subgraphResult == "I, SubGraph 3, am a user function", 'subgraph:Foo() did not return the right results: ' .. tostring(subgraphResult)) assert(subgraph.s_createdCount == 3, "created count was not three: "..tostring(subgraph.s_createdCount)) local subgraph2 = SubGraph.new() assert(subgraph2 ~= nil, "subgraph2 was nil") assert(type(subgraph2) == 'userdata', 'subgraph2 was not userdata') assert(type(subgraph2.IsActive)== 'function', "subgraph2 IsActive was not a function") assert(not subgraph2.IsActive(subgraph2), "subgraph2 did not inherit properly") assert(not subgraph2:IsActive(), "subgraph2 did not inherit properly") assert(type(subgraph2.Foo) == 'function', 'subgraph2 was not a function') local subgraph2Result = subgraph2:Foo() assert(subgraph2Result == "I, SubGraph 4, am a user function", 'subgraph2:Foo() did not return the right results: ' .. tostring(subgraph2Result)) assert(subgraph2.s_createdCount == 4, "created count was not three: "..tostring(subgraph2.s_createdCount)) return SubGraph )LUA" )); } // // TEST_F(ScriptCanvasTestFixture, NativeNodeableStack) // { // TestNodeableObject nodeable; // nodeable.Initialize(); // // bool wasTrueCalled = false; // bool wasFalseCalled = false; // // nodeable.SetExecutionOut // ( AZ_CRC("BranchTrue", 0xd49f121c) // , CreateOut // ([&wasTrueCalled](ExecutionState&, Data::BooleanType condition, const Data::StringType& message) // { // EXPECT_TRUE(condition); // EXPECT_EQ(message, AZStd::string("called the true version!")); // wasTrueCalled = true; // } // , StackAllocatorType{})); // // nodeable.SetExecutionOut // ( AZ_CRC("BranchFalse", 0xaceca8bc) // , CreateOut // ([&wasFalseCalled](ExecutionState&, Data::BooleanType condition, const Data::StringType& message, const Data::Vector3Type& vector) // { // EXPECT_FALSE(condition); // EXPECT_EQ(message, AZStd::string("called the false version!")); // EXPECT_EQ(vector, AZ::Vector3(1, 2, 3)); // wasFalseCalled = true; // } // , StackAllocatorType{})); // // nodeable.Branch(true); // nodeable.Branch(false); // // EXPECT_TRUE(wasTrueCalled); // EXPECT_TRUE(wasFalseCalled); // } // // TEST_F(ScriptCanvasTestFixture, NativeNodeableHeap) // { // TestNodeableObject nodeable; // nodeable.Initialize(); // // AZStd::string routedArg("XYZ"); // AZStd::array bigArray; // std::fill(bigArray.begin(), bigArray.end(), 0); // // bigArray[0] = 7; // bigArray[2047] = 7; // // EXPECT_EQ(bigArray[0], 7); // EXPECT_EQ(bigArray[2047], 7); // // bool isHeapCalled = false; // // nodeable.SetExecutionOut // ( AZ_CRC("BranchTrue", 0xd49f121c) // , CreateOut // ([routedArg, &isHeapCalled, bigArray](ExecutionState&, Data::BooleanType condition, const Data::StringType& message) mutable // { // EXPECT_EQ(message, AZStd::string("called the true version!")); // routedArg = message; // isHeapCalled = true; // EXPECT_EQ(bigArray[0], 7); // EXPECT_EQ(bigArray[2047], 7); // bigArray[0] = 9; // bigArray[2047] = 9; // EXPECT_EQ(bigArray[0], 9); // EXPECT_EQ(bigArray[2047], 9); // } // , HeapAllocatorType{})); // // // bigArray[0] = 8; // bigArray[2047] = 8; // EXPECT_EQ(bigArray[0], 8); // EXPECT_EQ(bigArray[2047], 8); // // nodeable.Branch(true); // EXPECT_TRUE(isHeapCalled); // // // just making sure no crash occurs on unconnected outs // nodeable.Branch(false); // } class Grandparent { public: AZ_RTTI(Grandparent, "{76EF13EE-7F5E-41C8-A789-A86836D66D10}"); AZ_CLASS_ALLOCATOR(Grandparent, AZ::SystemAllocator, 0); virtual ~Grandparent() = default; static void Reflect(AZ::ReflectContext* reflectContext) { if (AZ::SerializeContext* serializeContext = azrtti_cast(reflectContext)) { serializeContext->Class(); } if (AZ::BehaviorContext* behaviorContext = azrtti_cast(reflectContext)) { behaviorContext->Class("Grandparent") ->Method("GetGeneration", &Grandparent::GetGeneration); ; } } virtual int GetGeneration() const { return 1; } }; class Parent : public Grandparent { public: AZ_RTTI(Parent, "{2ABA91B7-24F7-495A-ACC6-4F93DE47B507}", Grandparent); AZ_CLASS_ALLOCATOR(Parent, AZ::SystemAllocator, 0); ~Parent() override = default; static void Reflect(AZ::ReflectContext* reflectContext) { if (AZ::SerializeContext* serializeContext = azrtti_cast(reflectContext)) { serializeContext->Class(); } if (AZ::BehaviorContext* behaviorContext = azrtti_cast(reflectContext)) { behaviorContext->Class("Parent") ; } } int GetGeneration() const override { return 2; } }; class Child : public Parent { public: AZ_RTTI(Child, "{826DB77C-11B7-42C4-8F3F-3438AFE5B29B}", Parent); AZ_CLASS_ALLOCATOR(Parent, AZ::SystemAllocator, 0); ~Child() override = default; static void Reflect(AZ::ReflectContext* reflectContext) { if (AZ::SerializeContext* serializeContext = azrtti_cast(reflectContext)) { serializeContext->Class(); } if (AZ::BehaviorContext* behaviorContext = azrtti_cast(reflectContext)) { behaviorContext->Class("Child") ; } } int GetGeneration() const override { return 3; } }; // TEST_F(ScriptCanvasTestFixture, CreateUuidsFast) // { // using namespace ScriptCanvas::Execution; // // ScriptCanvasEditor::ScopedOutputSuppression outputSuppression; // // AZ::Uuid candidate; // AZ::Uuid reference; // AZStd::string candidateString; // AZStd::string referenceString; // // candidateString = /**************/ "0123ABCD4567EFAB0123ABCD4567EFAB"; // candidate = CreateIdFromStringFast(candidateString.data()); // reference = AZ::Uuid::CreateString("0123abcd4567efab0123abcd4567efab"); // EXPECT_EQ(candidate, reference); // referenceString = CreateStringFastFromId(candidate); // EXPECT_EQ(candidateString, referenceString); // // candidateString = /**************/ "12345678909876543345676524676553"; // candidate = CreateIdFromStringFast(candidateString.data()); // reference = AZ::Uuid::CreateString("12345678909876543345676524676553"); // EXPECT_EQ(candidate, reference); // referenceString = CreateStringFastFromId(candidate); // EXPECT_EQ(candidateString, referenceString); // // candidateString = /**************/ "ABCDEFABCDEFABCDEFABCDEFABCDEFAB"; // candidate = CreateIdFromStringFast(candidateString.data()); // reference = AZ::Uuid::CreateString("abcdefabcdefabcdefabcdefabcdefab"); // EXPECT_EQ(candidate, reference); // referenceString = CreateStringFastFromId(candidate); // EXPECT_EQ(candidateString, referenceString); // // candidateString = /**************/ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"; // candidate = CreateIdFromStringFast(candidateString.data()); // reference = AZ::Uuid::CreateString("ffffffffffffffffffffffffffffffff"); // EXPECT_EQ(candidate, reference); // referenceString = CreateStringFastFromId(candidate); // EXPECT_EQ(candidateString, referenceString); // // candidateString = /**************/ "00000000000000000000000000000000"; // candidate = CreateIdFromStringFast(candidateString.data()); // reference = AZ::Uuid::CreateString("00000000000000000000000000000000"); // EXPECT_EQ(candidate, reference); // referenceString = CreateStringFastFromId(candidate); // EXPECT_EQ(candidateString, referenceString); // // candidateString = /**************/ "00000000000000000000000000000001"; // candidate = CreateIdFromStringFast(candidateString.data()); // reference = AZ::Uuid::CreateString("00000000000000000000000000000001"); // EXPECT_EQ(candidate, reference); // referenceString = CreateStringFastFromId(candidate); // EXPECT_EQ(candidateString, referenceString); // // candidateString = /**************/ "80000000000000000000000000000000"; // candidate = CreateIdFromStringFast(candidateString.data()); // reference = AZ::Uuid::CreateString("80000000000000000000000000000000"); // EXPECT_EQ(candidate, reference); // referenceString = CreateStringFastFromId(candidate); // EXPECT_EQ(candidateString, referenceString); // // // ................................."0123ABCD4567EFAB0123ABCD4567EFAB" // EXPECT_DEATH(CreateIdFromStringFast("0123ABCD4567EFAB0123ABCD4567EFA"), ""); // EXPECT_DEATH(CreateIdFromStringFast("0123ABCD4567EFA"), ""); // EXPECT_DEATH(CreateIdFromStringFast("MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"), ""); // EXPECT_DEATH(CreateIdFromStringFast("0123ABCD4567EFAB0123ABCD4567EFAB0"), ""); // } TEST_F(ScriptCanvasTestFixture, TypeInheritance) { using namespace ScriptCanvas; using namespace ScriptCanvas::Grammar; // \todo make a version of this to test in the editor for node connections // \todo make a version of this using events to check the methods results using self and table explicit calls Grandparent::Reflect(m_serializeContext); Grandparent::Reflect(m_behaviorContext); Parent::Reflect(m_serializeContext); Parent::Reflect(m_behaviorContext); Child::Reflect(m_serializeContext); Child::Reflect(m_behaviorContext); Data::Type grandparentType = Data::Type::BehaviorContextObject(azrtti_typeid()); Data::Type grandparentType2 = Data::Type::BehaviorContextObject(azrtti_typeid()); Data::Type parentType = Data::Type::BehaviorContextObject(azrtti_typeid()); Data::Type parentType2 = Data::Type::BehaviorContextObject(azrtti_typeid()); Data::Type childType = Data::Type::BehaviorContextObject(azrtti_typeid()); Data::Type childType2 = Data::Type::BehaviorContextObject(azrtti_typeid()); EXPECT_TRUE(grandparentType.IS_A(grandparentType2)); EXPECT_TRUE(grandparentType.IS_EXACTLY_A(grandparentType2)); EXPECT_FALSE(grandparentType.IS_A(parentType)); EXPECT_FALSE(grandparentType.IS_EXACTLY_A(parentType)); EXPECT_FALSE(grandparentType.IS_A(childType)); EXPECT_FALSE(grandparentType.IS_EXACTLY_A(childType)); EXPECT_TRUE(parentType.IS_A(grandparentType)); EXPECT_FALSE(parentType.IS_EXACTLY_A(grandparentType)); EXPECT_TRUE(parentType.IS_A(parentType2)); EXPECT_TRUE(parentType.IS_EXACTLY_A(parentType2)); EXPECT_FALSE(parentType.IS_A(childType)); EXPECT_FALSE(parentType.IS_EXACTLY_A(childType)); EXPECT_TRUE(childType.IS_A(grandparentType)); EXPECT_FALSE(childType.IS_EXACTLY_A(grandparentType)); EXPECT_TRUE(childType.IS_A(parentType)); EXPECT_FALSE(childType.IS_EXACTLY_A(parentType)); EXPECT_TRUE(childType.IS_A(childType2)); EXPECT_TRUE(childType.IS_EXACTLY_A(childType2)); } // \todo turn this into a unit test nodeable that adds a unit test failure on destruction if it was never triggered (or triggered the required number of times) class Marker { public: AZ_TYPE_INFO(Marker, "{BEEB4BF4-81B8-45A0-AD3F-D1875703315B}"); AZ_CLASS_ALLOCATOR(Marker, AZ::SystemAllocator, 0); static AZStd::vector s_markedPositions; static void MarkPosition(int mark) { s_markedPositions.push_back(mark); } static void Reflect(AZ::ReflectContext* reflectContext) { if (AZ::BehaviorContext* behaviorContext = azrtti_cast(reflectContext)) { behaviorContext->Class("Marker")->Method("MarkPosition", &MarkPosition); } } }; AZStd::vector Marker::s_markedPositions;