EnumTests.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  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/Preprocessor/Enum.h>
  9. #include <AzCore/Preprocessor/EnumReflectUtils.h>
  10. #include <AzCore/RTTI/TypeInfo.h>
  11. #include <AzCore/UnitTest/TestTypes.h>
  12. #include <AzCore/Serialization/SerializeContext.h>
  13. #include <AzCore/RTTI/BehaviorContext.h>
  14. #include <AzCore/Script/ScriptContext.h>
  15. AZ_ENUM_CLASS(GlobalScopeTestEnum, Foo, Bar);
  16. AZ_ENUM_DEFINE_REFLECT_UTILITIES(GlobalScopeTestEnum)
  17. namespace OtherEnumTestNamespace
  18. {
  19. AZ_ENUM_CLASS(TestEnum, Walk, Run, Fly);
  20. AZ_ENUM_DEFINE_REFLECT_UTILITIES(TestEnum)
  21. }
  22. namespace UnitTest
  23. {
  24. AZ_ENUM_CLASS(TestEnum,
  25. A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z
  26. );
  27. AZ_ENUM_DEFINE_REFLECT_UTILITIES(TestEnum)
  28. AZ_ENUM(TestEnumUnscoped,
  29. X,
  30. (Y, 5)
  31. );
  32. AZ_ENUM_WITH_UNDERLYING_TYPE(TestEnum8, uint8_t,
  33. XX,
  34. (YY, 7),
  35. ZZ
  36. );
  37. AZ_ENUM_CLASS_WITH_UNDERLYING_TYPE(TestCEnum16, int16_t,
  38. (X, -10),
  39. Y,
  40. Z
  41. );
  42. class EnumTests : public LeakDetectionFixture
  43. {
  44. };
  45. struct TypeWithEnumMember
  46. {
  47. AZ_TYPE_INFO(TypeWithEnumMember, "{1ACB4CCC-8070-4D21-8A52-37FD4391EFF5}");
  48. TestEnum m_testEnum;
  49. };
  50. TEST_F(EnumTests, BasicProperties_AreConstexpr)
  51. {
  52. // basic properties verifications:
  53. static_assert(static_cast<int>(TestEnum::A) == 0);
  54. static_assert(static_cast<int>(TestEnum::Z) == 25);
  55. // members array:
  56. static_assert(TestEnumMembers.size() == 26);
  57. static_assert(TestEnumMembers[0].m_value == TestEnum::A);
  58. static_assert(TestEnumMembers[0].m_string == "A");
  59. static_assert(TestEnumMembers[1].m_value == TestEnum::B);
  60. static_assert(TestEnumMembers[1].m_string == "B");
  61. // Unscoped-version tests
  62. static_assert(Y == 5);
  63. // 'specific underlying type'-version tests
  64. static_assert(sizeof(TestEnum8) == sizeof(uint8_t));
  65. static_assert(XX == 0);
  66. static_assert(YY == 7);
  67. static_assert(ZZ == 8);
  68. }
  69. TEST_F(EnumTests, EnumerationOverEnum_Succeeds)
  70. {
  71. auto EnumerateTestEnum = []() constexpr -> bool
  72. {
  73. int count = 0;
  74. for ([[maybe_unused]] TestEnumEnumeratorValueAndString enumMember : TestEnumMembers)
  75. {
  76. ++count;
  77. }
  78. return count == 26;
  79. };
  80. static_assert(EnumerateTestEnum());
  81. }
  82. TEST_F(EnumTests, FromString_WithFoundString_ReturnsEnumValue)
  83. {
  84. // optional is not dereferencable in constexpr context because of addressof
  85. // therefore we can't verify the returned value in static_assert, but we can verify that the function found something.
  86. static_assert(FromStringToTestEnum("Y").has_value());
  87. static_assert(TestEnumNamespace::FromString("X").has_value());
  88. // value verification by runtime:
  89. EXPECT_TRUE(*FromStringToTestEnum("Y") == TestEnum::Y);
  90. EXPECT_TRUE(*FromStringToTestEnumUnscoped("Y") == Y);
  91. // namespace accessed functions versions
  92. EXPECT_TRUE(*TestEnumNamespace::FromString("Y") == TestEnum::Y);
  93. EXPECT_TRUE(*TestEnumUnscopedNamespace::FromString("Y") == Y);
  94. // and on runtime as well
  95. EXPECT_TRUE(*FromStringToTestEnumUnscoped("X") == X);
  96. }
  97. TEST_F(EnumTests, FromString_WithNotFoundString_ReturnsInvalid)
  98. {
  99. static_assert(!FromStringToTestEnum("alien")); // nullopt in boolean context should evaluate to false
  100. static_assert(!FromStringToTestEnumUnscoped("alien"));
  101. // namespace accessed functions versions
  102. static_assert(!TestEnumNamespace::FromString("alien"));
  103. static_assert(!TestEnumUnscopedNamespace::FromString("alien"));
  104. }
  105. TEST_F(EnumTests, ToString_WithValidEnumOption_ReturnsNonEmptyString)
  106. {
  107. static_assert(ToString(TestEnum::X) == "X");
  108. static_assert(ToString(TestEnum::Y) == "Y");
  109. static_assert(ToString(TestEnum8::XX) == "XX");
  110. static_assert(ToString(TestEnum8::YY) == "YY");
  111. }
  112. TEST_F(EnumTests, ToString_WithInvalidEnumValue_ReturnsEmptyString)
  113. {
  114. static_assert(ToString(static_cast<TestEnum>(40)).empty());
  115. static_assert(ToString(static_cast<TestEnumUnscoped>(15)).empty());
  116. static_assert(ToString(static_cast<TestEnum8>(20)).empty());
  117. }
  118. TEST_F(EnumTests, TraitsBehaviorRegular)
  119. {
  120. static_assert(AZ::AzEnumTraits<TestEnum>::Members.size() == 26);
  121. }
  122. }
  123. namespace AZ
  124. {
  125. AZ_TYPE_INFO_SPECIALIZE(GlobalScopeTestEnum, "{9779918F-E8CE-4B04-A619-FCAA280DCB98}");
  126. AZ_TYPE_INFO_SPECIALIZE(OtherEnumTestNamespace::TestEnum, "{84CD44D2-7A6F-4271-BA17-3BBFC9C163D0}");
  127. AZ_TYPE_INFO_SPECIALIZE(UnitTest::TestEnum, "{A826012C-18B9-453E-8A61-7C06503F759D}");
  128. }
  129. namespace UnitTest
  130. {
  131. TEST_F(EnumTests, ReflectToSerializeContext)
  132. {
  133. AZ::SerializeContext context;
  134. GlobalScopeTestEnumReflect(context);
  135. TestEnumReflect(context);
  136. OtherEnumTestNamespace::TestEnumReflect(context);
  137. // There should be 1 Attributes::EnumUnderlyingType and N Attributes::EnumValueKey
  138. auto enumClassData = context.FindClassData(azrtti_typeid<GlobalScopeTestEnum>());
  139. EXPECT_NE(nullptr, enumClassData);
  140. EXPECT_EQ(3, enumClassData->m_attributes.size());
  141. enumClassData = context.FindClassData(azrtti_typeid<TestEnum>());
  142. EXPECT_NE(nullptr, enumClassData);
  143. EXPECT_EQ(27, enumClassData->m_attributes.size());
  144. enumClassData = context.FindClassData(azrtti_typeid<OtherEnumTestNamespace::TestEnum>());
  145. EXPECT_NE(nullptr, enumClassData);
  146. EXPECT_EQ(4, enumClassData->m_attributes.size());
  147. }
  148. TEST_F(EnumTests, ReflectToBehaviorContext)
  149. {
  150. AZ::BehaviorContext context;
  151. GlobalScopeTestEnumReflect(context);
  152. TestEnumReflect(context);
  153. OtherEnumTestNamespace::TestEnumReflect(context, "OtherTestEnum");
  154. EXPECT_NE(context.m_properties.end(), context.m_properties.find("GlobalScopeTestEnum_Foo"));
  155. EXPECT_NE(context.m_properties.end(), context.m_properties.find("GlobalScopeTestEnum_Bar"));
  156. EXPECT_NE(context.m_properties.end(), context.m_properties.find("TestEnum_A"));
  157. EXPECT_NE(context.m_properties.end(), context.m_properties.find("TestEnum_Z"));
  158. EXPECT_NE(context.m_properties.end(), context.m_properties.find("OtherTestEnum_Walk"));
  159. EXPECT_NE(context.m_properties.end(), context.m_properties.find("OtherTestEnum_Run"));
  160. EXPECT_NE(context.m_properties.end(), context.m_properties.find("OtherTestEnum_Fly"));
  161. }
  162. TEST_F(EnumTests, BehaviorContextReflect_Contains_BehaviorClassOfEnum)
  163. {
  164. AZ::BehaviorContext context;
  165. TestEnumReflect(context);
  166. // Validate that the enum type is reflected as a BehaviorClass on the BehaviorContext
  167. auto enumClassIter = context.m_classes.find("UnitTest::TestEnum");
  168. ASSERT_NE(context.m_classes.end(), enumClassIter);
  169. // Verify that the enum type BehaviorClass contains properties for the enum options
  170. AZ::BehaviorClass* enumClass = enumClassIter->second;
  171. auto VerifyEnumOptionReflectionOnEnumType = [&properties = enumClass->m_properties]
  172. (TestEnum expectedValue, AZStd::string_view enumOptionName)
  173. {
  174. auto propertyIt = properties.find(enumOptionName);
  175. ASSERT_NE(properties.end(), propertyIt);
  176. TestEnum testValue{};
  177. EXPECT_TRUE(propertyIt->second->m_getter->InvokeResult(testValue));
  178. EXPECT_EQ(expectedValue, testValue);
  179. };
  180. AZ::AzEnumTraits<TestEnum>::Visit(VerifyEnumOptionReflectionOnEnumType);
  181. // Verify that the enum options are also available as global constants on the BehaviorContext
  182. auto VerifyEnumOptionReflectionAsGlobalConstant = [&properties = context.m_properties]
  183. (TestEnum expectedValue, AZStd::string_view enumOptionName)
  184. {
  185. // Compose the combinded global enum option name
  186. auto qualifiedOptionName = AZStd::fixed_string<256>::format("TestEnum_%.*s",
  187. AZ_STRING_ARG(enumOptionName));
  188. auto propertyIt = properties.find(AZStd::string_view(qualifiedOptionName));
  189. ASSERT_NE(properties.end(), propertyIt);
  190. TestEnum testValue{};
  191. EXPECT_TRUE(propertyIt->second->m_getter->InvokeResult(testValue));
  192. EXPECT_EQ(expectedValue, testValue);
  193. };
  194. AZ::AzEnumTraits<TestEnum>::Visit(VerifyEnumOptionReflectionAsGlobalConstant);
  195. }
  196. TEST_F(EnumTests, ScriptContext_BindTo_BehaviorContext_DoesNotAssert_WithEnumType_ReflectedAsProperty)
  197. {
  198. AZ::BehaviorContext context;
  199. TestEnumReflect(context);
  200. context.Class<TypeWithEnumMember>()
  201. ->Property("testenum", BehaviorValueProperty(&TypeWithEnumMember::m_testEnum))
  202. ;
  203. AZ::ScriptContext scriptContext;
  204. scriptContext.BindTo(&context);
  205. }
  206. }