ScriptCanvasTestFixture.h 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #pragma once
  9. #include <AzCore/Asset/AssetManagerComponent.h>
  10. #include <AzCore/Component/ComponentApplicationBus.h>
  11. #include <AzCore/IO/FileIO.h>
  12. #include <AzCore/UnitTest/TestTypes.h>
  13. #include <AzCore/UserSettings/UserSettingsComponent.h>
  14. #include <AzCore/std/containers/vector.h>
  15. #include <AzFramework/IO/LocalFileIO.h>
  16. #include <AzTest/AzTest.h>
  17. #include <Nodes/BehaviorContextObjectTestNode.h>
  18. #include <Nodes/TestAutoGenFunctions.h>
  19. #include <ScriptCanvas/Components/EditorGraph.h>
  20. #include <ScriptCanvas/Core/Graph.h>
  21. #include <ScriptCanvas/Core/SlotConfigurationDefaults.h>
  22. #include <ScriptCanvas/ScriptCanvasGem.h>
  23. #include <ScriptCanvas/SystemComponent.h>
  24. #include <ScriptCanvas/Variable/GraphVariableManagerComponent.h>
  25. #include "EntityRefTests.h"
  26. #include "ScriptCanvasTestApplication.h"
  27. #include "ScriptCanvasTestBus.h"
  28. #include "ScriptCanvasTestNodes.h"
  29. #include "ScriptCanvasTestUtilities.h"
  30. #include <AutoGen/ScriptCanvasAutoGenRegistry.h>
  31. #define SC_EXPECT_DOUBLE_EQ(candidate, reference) EXPECT_NEAR(candidate, reference, 0.001)
  32. #define SC_EXPECT_FLOAT_EQ(candidate, reference) EXPECT_NEAR(candidate, reference, 0.001f)
  33. namespace ScriptCanvasTests
  34. {
  35. class ScriptCanvasTestFixture
  36. : public ::testing::Test
  37. //, protected NodeAccessor
  38. {
  39. public:
  40. static AZStd::atomic_bool s_asyncOperationActive;
  41. protected:
  42. static ScriptCanvasTests::Application* s_application;
  43. static void SetUpTestCase()
  44. {
  45. s_asyncOperationActive = false;
  46. if (s_application == nullptr)
  47. {
  48. AZ::ComponentApplication::StartupParameters appStartup;
  49. s_application = aznew ScriptCanvasTests::Application();
  50. {
  51. ScriptCanvasEditor::TraceSuppressionBus::Broadcast(&ScriptCanvasEditor::TraceSuppressionRequests::SuppressPrintf, true);
  52. AZ::ComponentApplication::Descriptor descriptor;
  53. descriptor.m_useExistingAllocator = true;
  54. AZ::DynamicModuleDescriptor dynamicModuleDescriptor;
  55. dynamicModuleDescriptor.m_dynamicLibraryPath = "GraphCanvas.Editor";
  56. descriptor.m_modules.push_back(dynamicModuleDescriptor);
  57. dynamicModuleDescriptor.m_dynamicLibraryPath = "ScriptCanvas.Editor";
  58. descriptor.m_modules.push_back(dynamicModuleDescriptor);
  59. dynamicModuleDescriptor.m_dynamicLibraryPath = "ExpressionEvaluation";
  60. descriptor.m_modules.push_back(dynamicModuleDescriptor);
  61. dynamicModuleDescriptor.m_dynamicLibraryPath = "ScriptEvents";
  62. descriptor.m_modules.push_back(dynamicModuleDescriptor);
  63. s_application->Start(descriptor, appStartup);
  64. // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
  65. // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
  66. // in the unit tests.
  67. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
  68. ScriptCanvasEditor::TraceSuppressionBus::Broadcast(&ScriptCanvasEditor::TraceSuppressionRequests::SuppressPrintf, false);
  69. }
  70. }
  71. AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
  72. AZ_Assert(fileIO, "SC unit tests require filehandling");
  73. s_setupSucceeded = fileIO->GetAlias("@engroot@") != nullptr;
  74. // Set the @gemroot:<gem-name> alias for active gems
  75. auto settingsRegistry = AZ::SettingsRegistry::Get();
  76. if (settingsRegistry)
  77. {
  78. AZ::Test::AddActiveGem("ScriptCanvasTesting", *settingsRegistry, fileIO);
  79. AZ::Test::AddActiveGem("GraphCanvas", *settingsRegistry, fileIO);
  80. AZ::Test::AddActiveGem("ScriptCanvas", *settingsRegistry, fileIO);
  81. AZ::Test::AddActiveGem("ScriptEvents", *settingsRegistry, fileIO);
  82. AZ::Test::AddActiveGem("ExpressionEvaluation", *settingsRegistry, fileIO);
  83. }
  84. AZ::TickBus::AllowFunctionQueuing(true);
  85. auto m_serializeContext = s_application->GetSerializeContext();
  86. auto m_behaviorContext = s_application->GetBehaviorContext();
  87. for (AZ::ReflectContext* context :
  88. {static_cast<AZ::ReflectContext*>(m_serializeContext), static_cast<AZ::ReflectContext*>(m_behaviorContext)})
  89. {
  90. ScriptCanvasTesting::Reflect(context);
  91. ScriptCanvasTestingNodes::BehaviorContextObjectTest::Reflect(context);
  92. TestNodeableObject::Reflect(context);
  93. TestBaseClass::Reflect(context);
  94. TestSubClass::Reflect(context);
  95. ScriptUnitTestEventHandler::Reflect(context);
  96. }
  97. }
  98. static void TearDownTestCase()
  99. {
  100. // don't hang on to dangling assets
  101. AZ::Data::AssetManager::Instance().DispatchEvents();
  102. auto m_serializeContext = s_application->GetSerializeContext();
  103. auto m_behaviorContext = s_application->GetBehaviorContext();
  104. for (AZ::ReflectContext* context :
  105. {static_cast<AZ::ReflectContext*>(m_serializeContext), static_cast<AZ::ReflectContext*>(m_behaviorContext)})
  106. {
  107. context->EnableRemoveReflection();
  108. ScriptCanvasTesting::Reflect(context);
  109. ScriptCanvasTestingNodes::BehaviorContextObjectTest::Reflect(context);
  110. TestNodeableObject::Reflect(context);
  111. TestBaseClass::Reflect(context);
  112. TestSubClass::Reflect(context);
  113. ScriptUnitTestEventHandler::Reflect(context);
  114. context->DisableRemoveReflection();
  115. }
  116. if (s_application)
  117. {
  118. s_application->Stop();
  119. delete s_application;
  120. s_application = nullptr;
  121. }
  122. s_leakDetection.CheckAllocatorsForLeaks();
  123. }
  124. template<class T>
  125. void RegisterComponentDescriptor()
  126. {
  127. AZ::ComponentDescriptor* descriptor = T::CreateDescriptor();
  128. auto insertResult = m_descriptors.insert(descriptor);
  129. if (insertResult.second)
  130. {
  131. GetApplication()->RegisterComponentDescriptor(descriptor);
  132. }
  133. }
  134. void SetUp() override
  135. {
  136. ASSERT_TRUE(s_setupSucceeded) << "ScriptCanvasTestFixture set up failed, unit tests can't work properly";
  137. m_serializeContext = s_application->GetSerializeContext();
  138. m_behaviorContext = s_application->GetBehaviorContext();
  139. AZ_Assert(AZ::IO::FileIOBase::GetInstance(), "File IO was not properly installed");
  140. RegisterComponentDescriptor<TestNodes::TestResult>();
  141. RegisterComponentDescriptor<TestNodes::ConfigurableUnitTestNode>();
  142. m_numericVectorType = ScriptCanvas::Data::Type::BehaviorContextObject(azrtti_typeid<AZStd::vector<ScriptCanvas::Data::NumberType>>());
  143. m_stringToNumberMapType = ScriptCanvas::Data::Type::BehaviorContextObject(azrtti_typeid<AZStd::unordered_map<ScriptCanvas::Data::StringType, ScriptCanvas::Data::NumberType>>());
  144. m_dataSlotConfigurationType = ScriptCanvas::Data::Type::BehaviorContextObject(azrtti_typeid<ScriptCanvas::DataSlotConfiguration>());
  145. m_baseClassType = ScriptCanvas::Data::Type::BehaviorContextObject(azrtti_typeid<TestBaseClass>());
  146. m_subClassType = ScriptCanvas::Data::Type::BehaviorContextObject(azrtti_typeid<TestSubClass>());
  147. }
  148. void TearDown() override
  149. {
  150. delete m_graph;
  151. m_graph = nullptr;
  152. ASSERT_TRUE(s_setupSucceeded) << "ScriptCanvasTestFixture set up failed, unit tests can't work properly";
  153. for (AZ::ComponentDescriptor* componentDescriptor : m_descriptors)
  154. {
  155. GetApplication()->UnregisterComponentDescriptor(componentDescriptor);
  156. delete componentDescriptor;
  157. }
  158. m_descriptors.clear();
  159. }
  160. ScriptCanvas::Graph* CreateGraph()
  161. {
  162. AZ_Assert(!m_graph, "Only one graph should be created per test.");
  163. m_graph = aznew ScriptCanvas::Graph();
  164. m_graph->Init();
  165. return m_graph;
  166. }
  167. ScriptCanvasEditor::EditorGraph* CreateEditorGraph()
  168. {
  169. AZ_Assert(!m_graph, "Only one graph should be created per test.");
  170. m_graph = aznew ScriptCanvasEditor::EditorGraph();
  171. m_graph->Init();
  172. return static_cast<ScriptCanvasEditor::EditorGraph*>(m_graph);
  173. }
  174. TestNodes::ConfigurableUnitTestNode* CreateConfigurableNode(AZStd::string entityName = "ConfigurableNodeEntity")
  175. {
  176. AZ::Entity* configurableNodeEntity = new AZ::Entity(entityName.c_str());
  177. auto configurableNode = configurableNodeEntity->CreateComponent<TestNodes::ConfigurableUnitTestNode>();
  178. AZ_Assert(m_graph, "A graph must be created before any nodes are created.");
  179. if (!m_graph)
  180. {
  181. return nullptr;
  182. }
  183. ScriptCanvas::ScriptCanvasId scriptCanvasId = m_graph->GetScriptCanvasId();
  184. configurableNodeEntity->CreateComponent<ScriptCanvas::GraphVariableManagerComponent>(scriptCanvasId);
  185. configurableNodeEntity->Init();
  186. m_graph->Activate();
  187. m_graph->AddNode(configurableNodeEntity->GetId());
  188. return configurableNode;
  189. }
  190. void ReportErrors(ScriptCanvas::Graph* graph, bool expectErrors = false, bool expectIrrecoverableErrors = false)
  191. {
  192. AZ_UNUSED(graph);
  193. AZ_UNUSED(expectErrors);
  194. AZ_UNUSED(expectIrrecoverableErrors);
  195. }
  196. void TestConnectionBetween(ScriptCanvas::Endpoint sourceEndpoint, ScriptCanvas::Endpoint targetEndpoint, bool isValid = true)
  197. {
  198. EXPECT_EQ(m_graph->CanConnectionExistBetween(sourceEndpoint, targetEndpoint).IsSuccess(), isValid);
  199. EXPECT_EQ(m_graph->CanConnectionExistBetween(targetEndpoint, sourceEndpoint).IsSuccess(), isValid);
  200. EXPECT_EQ(m_graph->CanCreateConnectionBetween(sourceEndpoint, targetEndpoint).IsSuccess(), isValid);
  201. EXPECT_EQ(m_graph->CanCreateConnectionBetween(targetEndpoint, sourceEndpoint).IsSuccess(), isValid);
  202. if (isValid)
  203. {
  204. EXPECT_TRUE(m_graph->ConnectByEndpoint(sourceEndpoint, targetEndpoint));
  205. }
  206. }
  207. void TestIsConnectionPossible(ScriptCanvas::Endpoint sourceEndpoint, ScriptCanvas::Endpoint targetEndpoint, bool isValid = true)
  208. {
  209. EXPECT_EQ(m_graph->CanConnectionExistBetween(sourceEndpoint, targetEndpoint).IsSuccess(), isValid);
  210. EXPECT_EQ(m_graph->CanConnectionExistBetween(targetEndpoint, sourceEndpoint).IsSuccess(), isValid);
  211. EXPECT_EQ(m_graph->CanCreateConnectionBetween(sourceEndpoint, targetEndpoint).IsSuccess(), isValid);
  212. EXPECT_EQ(m_graph->CanCreateConnectionBetween(targetEndpoint, sourceEndpoint).IsSuccess(), isValid);
  213. }
  214. // Test if there is an existing connection between the provided endpoints
  215. void TestIsConnectionBetween(const ScriptCanvas::Endpoint& sourceEndpoint, const ScriptCanvas::Endpoint& targetEndpoint, bool isValid = true)
  216. {
  217. AZ::Entity* ent;
  218. EXPECT_EQ(m_graph->FindConnection(ent, sourceEndpoint, targetEndpoint), isValid);
  219. }
  220. // Tests implicit connections between nodes by connecting and disconnecting every data source and data slot while checking to make
  221. // sure that a connection is maintained between the source and target execution slots as long as at least one set of source and target
  222. // data slots are connected, and that no other execution out slots are connected to the target execution slot
  223. void TestAllImplicitConnections(
  224. ScriptCanvasEditor::EditorGraph* editorGraph,
  225. AZStd::vector<ScriptCanvas::Endpoint> sourceDataSlots,
  226. AZStd::vector<ScriptCanvas::Endpoint> targetDataSlots,
  227. ScriptCanvas::Endpoint sourceExecSlot,
  228. ScriptCanvas::Endpoint targetExecSlot,
  229. AZStd::vector<ScriptCanvas::Endpoint> allExecutionOutSlots)
  230. {
  231. // Connect all of the data slots
  232. for (auto sourceDataSlot : sourceDataSlots)
  233. {
  234. for (auto targetDataSlot : targetDataSlots)
  235. {
  236. TestConnectionBetween(sourceDataSlot, targetDataSlot, true);
  237. editorGraph->UpdateCorrespondingImplicitConnection(sourceDataSlot, targetDataSlot);
  238. // Ensure the implicit connection exists
  239. TestIsConnectionBetween(sourceExecSlot, targetExecSlot, true);
  240. for (auto otherExecSlot : allExecutionOutSlots)
  241. {
  242. if (otherExecSlot.GetSlotId() != sourceExecSlot.GetSlotId())
  243. {
  244. // Ensure that no implicit connections exist between any of the other execution out slots and the target
  245. // execution slot
  246. TestIsConnectionBetween(otherExecSlot, targetExecSlot, false);
  247. }
  248. }
  249. }
  250. }
  251. // Disconnect all of the data slots
  252. for (int i = 0; i < sourceDataSlots.size(); i++)
  253. {
  254. for (int j = 0; j < targetDataSlots.size(); j++)
  255. {
  256. editorGraph->DisconnectByEndpoint(sourceDataSlots[i], targetDataSlots[j]);
  257. editorGraph->UpdateCorrespondingImplicitConnection(sourceDataSlots[i], targetDataSlots[j]);
  258. // Ensure the implicit connection exists only if this is not the last data connection. If it is, then ensure that
  259. // no implicit connection exists
  260. TestIsConnectionBetween(sourceExecSlot, targetExecSlot, (i < sourceDataSlots.size() - 1 || j < targetDataSlots.size() - 1));
  261. for (auto otherExecSlot : allExecutionOutSlots)
  262. {
  263. if (otherExecSlot.GetSlotId() != sourceExecSlot.GetSlotId())
  264. {
  265. // Ensure that no implicit connections exist between any of the other execution out slots and the target
  266. // execution slot
  267. TestIsConnectionBetween(otherExecSlot, targetExecSlot, false);
  268. }
  269. }
  270. }
  271. }
  272. }
  273. void CreateExecutionFlowBetween(AZStd::vector<TestNodes::ConfigurableUnitTestNode*> unitTestNodes)
  274. {
  275. ScriptCanvas::Slot* previousOutSlot = nullptr;
  276. for (TestNodes::ConfigurableUnitTestNode* testNode : unitTestNodes)
  277. {
  278. {
  279. ScriptCanvas::ExecutionSlotConfiguration inputSlot = ScriptCanvas::CommonSlots::GeneralInSlot();
  280. ScriptCanvas::Slot* slot = testNode->AddTestingSlot(inputSlot);
  281. if (slot && previousOutSlot)
  282. {
  283. TestConnectionBetween(previousOutSlot->GetEndpoint(), slot->GetEndpoint());
  284. }
  285. }
  286. {
  287. ScriptCanvas::ExecutionSlotConfiguration outputSlot = ScriptCanvas::CommonSlots::GeneralOutSlot();
  288. previousOutSlot = testNode->AddTestingSlot(outputSlot);
  289. }
  290. }
  291. }
  292. AZStd::vector< ScriptCanvas::Data::Type > GetContainerDataTypes() const
  293. {
  294. return { m_numericVectorType, m_stringToNumberMapType };
  295. }
  296. ScriptCanvas::Data::Type GetRandomContainerType() const
  297. {
  298. AZStd::vector< ScriptCanvas::Data::Type > containerTypes = GetContainerDataTypes();
  299. // We have no types to randomize. Just return.
  300. if (containerTypes.empty())
  301. {
  302. return m_numericVectorType;
  303. }
  304. int randomIndex = rand() % containerTypes.size();
  305. ScriptCanvas::Data::Type randomType = containerTypes[randomIndex];
  306. AZ_TracePrintf("ScriptCanvasTestFixture", "RandomContainerType: %s\n", randomType.GetAZType().ToString<AZStd::string>().c_str());
  307. return randomType;
  308. }
  309. AZStd::vector< ScriptCanvas::Data::Type > GetPrimitiveTypes() const
  310. {
  311. return{
  312. ScriptCanvas::Data::Type::AABB(),
  313. ScriptCanvas::Data::Type::Boolean(),
  314. ScriptCanvas::Data::Type::Color(),
  315. ScriptCanvas::Data::Type::CRC(),
  316. ScriptCanvas::Data::Type::EntityID(),
  317. ScriptCanvas::Data::Type::Matrix3x3(),
  318. ScriptCanvas::Data::Type::Matrix4x4(),
  319. ScriptCanvas::Data::Type::Number(),
  320. ScriptCanvas::Data::Type::OBB(),
  321. ScriptCanvas::Data::Type::Plane(),
  322. ScriptCanvas::Data::Type::Quaternion(),
  323. ScriptCanvas::Data::Type::String(),
  324. ScriptCanvas::Data::Type::Transform(),
  325. ScriptCanvas::Data::Type::Vector2(),
  326. ScriptCanvas::Data::Type::Vector3(),
  327. ScriptCanvas::Data::Type::Vector4()
  328. };
  329. }
  330. ScriptCanvas::Data::Type GetRandomPrimitiveType() const
  331. {
  332. AZStd::vector< ScriptCanvas::Data::Type > primitiveTypes = GetPrimitiveTypes();
  333. // We have no types to randomize. Just return.
  334. if (primitiveTypes.empty())
  335. {
  336. return ScriptCanvas::Data::Type::Number();
  337. }
  338. int randomIndex = rand() % primitiveTypes.size();
  339. ScriptCanvas::Data::Type randomType = primitiveTypes[randomIndex];
  340. AZ_TracePrintf("ScriptCanvasTestFixture", "RandomPrimitiveType: %s\n", randomType.GetAZType().ToString<AZStd::string>().c_str());
  341. return randomType;
  342. }
  343. AZStd::vector< ScriptCanvas::Data::Type > GetBehaviorObjectTypes() const
  344. {
  345. return {
  346. m_dataSlotConfigurationType
  347. };
  348. }
  349. ScriptCanvas::Data::Type GetRandomObjectType() const
  350. {
  351. AZStd::vector< ScriptCanvas::Data::Type > objectTypes = GetBehaviorObjectTypes();
  352. // We have no types to randomize. Just return.
  353. if (objectTypes.empty())
  354. {
  355. return m_dataSlotConfigurationType;
  356. }
  357. int randomIndex = rand() % objectTypes.size();
  358. ScriptCanvas::Data::Type randomType = objectTypes[randomIndex];
  359. AZ_TracePrintf("ScriptCanvasTestFixture", "RandomObjectType: %s\n", randomType.GetAZType().ToString<AZStd::string>().c_str());
  360. return randomType;
  361. }
  362. AZStd::vector< ScriptCanvas::Data::Type > GetTypes() const
  363. {
  364. auto primitives = GetPrimitiveTypes();
  365. auto containers = GetContainerDataTypes();
  366. auto objects = GetBehaviorObjectTypes();
  367. primitives.reserve(containers.size() + objects.size());
  368. primitives.insert(primitives.end(), containers.begin(), containers.end());
  369. primitives.insert(primitives.end(), objects.begin(), objects.end());
  370. return primitives;
  371. }
  372. ScriptCanvas::Data::Type GetRandomType() const
  373. {
  374. AZStd::vector< ScriptCanvas::Data::Type > types = GetTypes();
  375. // We have no types to randomize. Just return.
  376. if (types.empty())
  377. {
  378. return m_dataSlotConfigurationType;
  379. }
  380. int randomIndex = rand() % types.size();
  381. ScriptCanvas::Data::Type randomType = types[randomIndex];
  382. AZ_TracePrintf("ScriptCanvasTestFixture", "RandomType: %s\n", randomType.GetAZType().ToString<AZStd::string>().c_str());
  383. return randomType;
  384. }
  385. AZStd::string GenerateSlotName()
  386. {
  387. AZStd::string slotName = AZStd::string::format("Slot %i", m_slotCounter);
  388. ++m_slotCounter;
  389. return slotName;
  390. }
  391. AZ::SerializeContext* m_serializeContext;
  392. AZ::BehaviorContext* m_behaviorContext;
  393. UnitTestEntityContext m_entityContext;
  394. // Really big(visually) data types just storing here for ease of use in situations.
  395. ScriptCanvas::Data::Type m_numericVectorType;
  396. ScriptCanvas::Data::Type m_stringToNumberMapType;
  397. ScriptCanvas::Data::Type m_dataSlotConfigurationType;
  398. ScriptCanvas::Data::Type m_baseClassType;
  399. ScriptCanvas::Data::Type m_subClassType;
  400. ScriptCanvas::Graph* m_graph = nullptr;
  401. int m_slotCounter = 0;
  402. protected:
  403. static ScriptCanvasTests::Application* GetApplication() { return s_application; }
  404. private:
  405. static bool s_setupSucceeded;
  406. static inline UnitTest::LeakDetectionBase s_leakDetection{};
  407. AZStd::unordered_set< AZ::ComponentDescriptor* > m_descriptors;
  408. };
  409. }