AttributeDomInteropTests.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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. #include <AzCore/DOM/DomUtils.h>
  9. #include <AzCore/Math/Vector3.h>
  10. #include <AzCore/RTTI/ReflectContext.h>
  11. #include <AzCore/Serialization/Json/RegistrationContext.h>
  12. #include <AzCore/UnitTest/MockComponentApplication.h>
  13. #include <AzCore/UnitTest/TestTypes.h>
  14. #include <DOM/DomFixtures.h>
  15. namespace AZ::AttributeDomInteropTests
  16. {
  17. template<typename FunctionSignature>
  18. struct InvokeTestHelper;
  19. // Helper for testing DOM invocation with the three attribute types that support invocation:
  20. // AttributeInvocable, AttributeFunction, and AttributeMemberFunction
  21. // Only one InvokeTestHelper of a given type should be instantiated as a static instance pointer is used
  22. // to provide a function pointer for AttributeFunction.
  23. template<typename R, typename... Args>
  24. struct InvokeTestHelper<R(Args...)>
  25. {
  26. AZ_TYPE_INFO(InvokeTestHelper, "{BE273EAA-FB6A-464F-A9F8-8A5C59650D70}", R(Args...));
  27. using FunctionType = AZStd::function<R(Args...)>;
  28. FunctionType m_fn;
  29. template<class F>
  30. InvokeTestHelper(F fn)
  31. : m_fn(AZStd::move(fn))
  32. {
  33. s_instance = this;
  34. }
  35. ~InvokeTestHelper() = default;
  36. inline static InvokeTestHelper* s_instance = nullptr;
  37. static R GlobalFunction(Args... args)
  38. {
  39. return s_instance->m_fn(args...);
  40. }
  41. R MemberFunction(Args... args)
  42. {
  43. return m_fn(args...);
  44. }
  45. void TestInvokeWithDomValues(AZ::Dom::Value expectedResult, AZ::Dom::Value args, bool invokeExpectedToWork = true)
  46. {
  47. // The invocable method must accept the class instance as the first argument
  48. auto invocableClassMethod = [](InvokeTestHelper* self, Args... args) -> R
  49. {
  50. return self->m_fn(args...);
  51. };
  52. AZ::AttributeInvocable invokeAttribute(invocableClassMethod);
  53. AZ::AttributeFunction<R(Args...)> functionAttribute(&GlobalFunction);
  54. AZ::AttributeMemberFunction<R (InvokeTestHelper::*)(Args...)> memberFunctionAttribute(&InvokeTestHelper::MemberFunction);
  55. AZStd::array<AZ::Attribute*, 3> attributesToTest = { &invokeAttribute, &functionAttribute, &memberFunctionAttribute };
  56. AZ::Dom::Value instanceAndArgs(AZ::Dom::Type::Array);
  57. instanceAndArgs.ArrayPushBack(AZ::Dom::Utils::ValueFromType(this));
  58. instanceAndArgs.ArrayInsert(instanceAndArgs.ArrayEnd(), args.ArrayBegin(), args.ArrayEnd());
  59. for (AZ::Attribute* attribute : attributesToTest)
  60. {
  61. EXPECT_EQ(true, attribute->IsInvokable());
  62. EXPECT_EQ(invokeExpectedToWork, attribute->CanDomInvoke(instanceAndArgs));
  63. // Move the arguments into an instanceAndArgs array and prepend the instance to the dom array
  64. EXPECT_TRUE(AZ::Dom::Utils::DeepCompareIsEqual(expectedResult, attribute->DomInvoke(instanceAndArgs)));
  65. }
  66. }
  67. template<typename Result = R>
  68. void TestInvoke(Result expectedResult, Args... args)
  69. {
  70. AZ::Dom::Value domArgs(AZ::Dom::Type::Array);
  71. (domArgs.ArrayPushBack(AZ::Dom::Utils::ValueFromType<Args>(args)), ...);
  72. TestInvokeWithDomValues(AZ::Dom::Utils::ValueFromType(expectedResult), AZStd::move(domArgs));
  73. }
  74. };
  75. class AttributeDomInteropTests
  76. : public AZ::Dom::Tests::DomTestFixture
  77. {
  78. public:
  79. AttributeDomInteropTests()
  80. : m_serializeContext(AZStd::make_unique<AZ::SerializeContext>())
  81. , m_jsonRegistrationContext(AZStd::make_unique<AZ::JsonRegistrationContext>())
  82. {
  83. m_componentApplicationMock = AZStd::make_unique<::testing::NiceMock<UnitTest::MockComponentApplication>>();
  84. ON_CALL(*m_componentApplicationMock.get(), GetSerializeContext())
  85. .WillByDefault(::testing::Return(m_serializeContext.get()));
  86. ON_CALL(*m_componentApplicationMock.get(), GetJsonRegistrationContext())
  87. .WillByDefault(::testing::Return(m_jsonRegistrationContext.get()));
  88. }
  89. ~AttributeDomInteropTests()
  90. {
  91. m_componentApplicationMock.reset();
  92. m_serializeContext.reset();
  93. }
  94. protected:
  95. AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
  96. AZStd::unique_ptr<AZ::JsonRegistrationContext> m_jsonRegistrationContext;
  97. AZStd::unique_ptr<::testing::NiceMock<UnitTest::MockComponentApplication>> m_componentApplicationMock;
  98. };
  99. TEST_F(AttributeDomInteropTests, ArgumentHandling)
  100. {
  101. InvokeTestHelper<int(int, int)> tester(
  102. [](int a, int b)
  103. {
  104. return a + b;
  105. });
  106. tester.TestInvoke(5, 2, 3);
  107. tester.TestInvoke(-1, 2, -3);
  108. AZ::Dom::Value domArgs(AZ::Dom::Type::Array);
  109. // Insufficient args should skip invoking return null and CanDomInvoke should return false
  110. domArgs.ArrayPushBack(AZ::Dom::Value(2));
  111. tester.TestInvokeWithDomValues(AZ::Dom::Value(), domArgs, false);
  112. // Correct number of args should work
  113. domArgs.ArrayPushBack(AZ::Dom::Value(2));
  114. tester.TestInvokeWithDomValues(AZ::Dom::Value(4), domArgs);
  115. // Additional arguments should be discarded without failing the invocation
  116. domArgs.ArrayPushBack(AZ::Dom::Value(2));
  117. tester.TestInvokeWithDomValues(AZ::Dom::Value(4), domArgs);
  118. // Invalid argument signatures should also flag as not able to invoke
  119. domArgs.ClearArray();
  120. domArgs.ArrayPushBack(AZ::Dom::Value("string1", false));
  121. domArgs.ArrayPushBack(AZ::Dom::Value("string2", false));
  122. // the result will be 0 even though it's an invalid call due to a safety feature that defaults arguments
  123. // to their default constructed contents when receiving invalid data
  124. tester.TestInvokeWithDomValues(AZ::Dom::Value(0), domArgs, false);
  125. }
  126. TEST_F(AttributeDomInteropTests, NoReturnValue)
  127. {
  128. int callCount = 0;
  129. InvokeTestHelper<void()> tester(
  130. [&callCount]()
  131. {
  132. ++callCount;
  133. });
  134. // We should return null, but 3 invocations should have occurred (one for each tested attribute type)
  135. tester.TestInvoke(AZ::Dom::Value());
  136. EXPECT_EQ(3, callCount);
  137. }
  138. TEST_F(AttributeDomInteropTests, ArgumentOrder)
  139. {
  140. InvokeTestHelper<bool(int, float, AZStd::string)> tester(
  141. [](int i, float f, AZStd::string str)
  142. {
  143. return i > 0 && f > 0.f && !str.empty();
  144. });
  145. tester.TestInvoke(true, 1, 2.f, "test");
  146. }
  147. TEST_F(AttributeDomInteropTests, PointerArguments)
  148. {
  149. InvokeTestHelper<float(AZ::Vector3*)> tester(
  150. [](AZ::Vector3* arg)
  151. {
  152. return arg->GetX();
  153. });
  154. AZ::Vector3 arg;
  155. arg.SetX(0.f);
  156. tester.TestInvoke(0.f, &arg);
  157. arg.SetX(12.2f);
  158. tester.TestInvoke(12.2f, &arg);
  159. }
  160. TEST_F(AttributeDomInteropTests, RefArguments)
  161. {
  162. int callCount = 0;
  163. InvokeTestHelper<float(AZ::Vector3&, int&)> tester(
  164. [](AZ::Vector3& arg1, int& arg2)
  165. {
  166. ++arg2;
  167. arg1.SetX(1.f);
  168. return arg1.GetX();
  169. });
  170. AZ::Vector3 arg;
  171. arg.SetX(0.f);
  172. tester.TestInvoke(1.f, arg, callCount);
  173. EXPECT_EQ(1.0, arg.GetX());
  174. EXPECT_EQ(3, callCount);
  175. arg.SetX(12.2f);
  176. tester.TestInvoke(1.f, arg, callCount);
  177. EXPECT_EQ(1.0, arg.GetX());
  178. EXPECT_EQ(6, callCount);
  179. }
  180. TEST_F(AttributeDomInteropTests, ConstRefArguments)
  181. {
  182. InvokeTestHelper<float(const AZ::Vector3&, const int&)> tester(
  183. [](const AZ::Vector3& arg, const int& n)
  184. {
  185. return arg.GetX() + n;
  186. });
  187. AZ::Vector3 arg;
  188. int n = 1;
  189. arg.SetX(0.f);
  190. tester.TestInvoke(1.f, arg, n);
  191. arg.SetX(12.2f);
  192. tester.TestInvoke(13.2f, arg, n);
  193. }
  194. } // namespace AZ::AttributeDomInteropTests