PropertyTreeEditorTests.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  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/Asset/AssetSerializer.h>
  9. #include <AzCore/Serialization/SerializeContext.h>
  10. #include <AzFramework/Components/TransformComponent.h>
  11. #include <AzFramework/Entity/EntityContext.h>
  12. #include <AzFramework/Asset/SimpleAsset.h>
  13. #include <AzTest/AzTest.h>
  14. #include <AzToolsFramework/Application/ToolsApplication.h>
  15. #include <AzCore/UnitTest/TestTypes.h>
  16. #include <AzCore/StringFunc/StringFunc.h>
  17. #include <AzToolsFramework/PropertyTreeEditor/PropertyTreeEditor.h>
  18. #include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
  19. #include <AzToolsFramework/UnitTest/ToolsTestApplication.h>
  20. #include <QtTest/QtTest>
  21. #include <QApplication>
  22. namespace UnitTest
  23. {
  24. using namespace AzToolsFramework;
  25. struct PropertyTreeEditorSubBlockTester
  26. {
  27. AZ_TYPE_INFO(PropertyTreeEditorSubBlockTester, "{E9497A1E-9B41-4A33-8F05-92CE41A0ABD9}");
  28. AZ::s16 m_myNegativeShort = -42;
  29. };
  30. struct MockAssetData
  31. : public AZ::Data::AssetData
  32. {
  33. AZ_RTTI(MockAssetData, "{8B0A8DCA-7F29-4B8E-B5D7-08E0EAB2C900}", AZ::Data::AssetData);
  34. MockAssetData(const AZ::Data::AssetId& assetId)
  35. : AssetData(assetId)
  36. {
  37. // to skip the automatic removal from the asset system
  38. m_useCount = 2;
  39. }
  40. };
  41. class TestSimpleAsset
  42. {
  43. public:
  44. AZ_TYPE_INFO(TestSimpleAsset, "{10A39072-9287-49FE-93C8-55F7715FC758}");
  45. bool m_data = false;
  46. static const char* GetFileFilter()
  47. {
  48. return "*.NaN";
  49. }
  50. static void Reflect(AZ::ReflectContext* reflection)
  51. {
  52. AZ::SerializeContext* serializeContext = AZ::RttiCast<AZ::SerializeContext*>(reflection);
  53. if (serializeContext)
  54. {
  55. serializeContext->Class<TestSimpleAsset>()
  56. ->Version(0)
  57. ->Field("data", &TestSimpleAsset::m_data)
  58. ;
  59. AzFramework::SimpleAssetReference<TestSimpleAsset>::Register(*serializeContext);
  60. if (AZ::EditContext* editContext = serializeContext->GetEditContext())
  61. {
  62. editContext->Class<TestSimpleAsset>("TestSimpleAsset", "Test data block for a simple asset mock data block")
  63. ->DataElement(nullptr, &TestSimpleAsset::m_data, "My Data", "A test bool value.")
  64. ;
  65. }
  66. }
  67. }
  68. };
  69. //! Test class
  70. struct PropertyTreeEditorTester
  71. {
  72. AZ_TYPE_INFO(PropertyTreeEditorTester, "{D3E17BE6-0FEB-4A04-B8BE-105A4666E79F}");
  73. int m_myInt = 42;
  74. int m_myNewInt = 43;
  75. bool m_myBool = true;
  76. float m_myFloat = 42.0f;
  77. AZStd::string m_myString = "StringValue";
  78. AZStd::string m_myGroupedString = "GroupedStringValue";
  79. PropertyTreeEditorSubBlockTester m_mySubBlock;
  80. double m_myHiddenDouble = 42.0;
  81. AZ::u16 m_myReadOnlyShort = 42;
  82. AZ::Data::Asset<MockAssetData> m_myAssetData;
  83. AzFramework::SimpleAssetReference<TestSimpleAsset> m_myTestSimpleAsset;
  84. struct PropertyTreeEditorNestedTester
  85. {
  86. AZ_TYPE_INFO(PropertyTreeEditorNestedTester, "{F5814544-424D-41C5-A5AB-632371615B6A}");
  87. AZStd::string m_myNestedString = "NestedString";
  88. };
  89. AZStd::vector<PropertyTreeEditorNestedTester> m_myList;
  90. AZStd::unordered_map<AZStd::string, PropertyTreeEditorNestedTester> m_myMap;
  91. PropertyTreeEditorNestedTester m_nestedTester;
  92. PropertyTreeEditorNestedTester m_nestedTesterHiddenChildren;
  93. void Reflect(AZ::ReflectContext* context)
  94. {
  95. TestSimpleAsset::Reflect(context);
  96. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  97. {
  98. serializeContext->Class<PropertyTreeEditorSubBlockTester>()
  99. ->Version(0)
  100. ->Field("myNegativeShort", &PropertyTreeEditorSubBlockTester::m_myNegativeShort);
  101. serializeContext->Class<PropertyTreeEditorTester>()
  102. ->Version(1)
  103. ->Field("myInt", &PropertyTreeEditorTester::m_myInt)
  104. ->Field("myBool", &PropertyTreeEditorTester::m_myBool)
  105. ->Field("myFloat", &PropertyTreeEditorTester::m_myFloat)
  106. ->Field("myString", &PropertyTreeEditorTester::m_myString)
  107. ->Field("NestedTester", &PropertyTreeEditorTester::m_nestedTester)
  108. ->Field("myNewInt", &PropertyTreeEditorTester::m_myNewInt)
  109. ->Field("myGroupedString", &PropertyTreeEditorTester::m_myGroupedString)
  110. ->Field("myList", &PropertyTreeEditorTester::m_myList)
  111. ->Field("myMap", &PropertyTreeEditorTester::m_myMap)
  112. ->Field("mySubBlock", &PropertyTreeEditorTester::m_mySubBlock)
  113. ->Field("myHiddenDouble", &PropertyTreeEditorTester::m_myHiddenDouble)
  114. ->Field("myReadOnlyShort", &PropertyTreeEditorTester::m_myReadOnlyShort)
  115. ->Field("nestedTesterHiddenChildren", &PropertyTreeEditorTester::m_nestedTesterHiddenChildren)
  116. ->Field("myAssetData", &PropertyTreeEditorTester::m_myAssetData)
  117. ->Field("myTestSimpleAsset", &PropertyTreeEditorTester::m_myTestSimpleAsset)
  118. ;
  119. serializeContext->Class<PropertyTreeEditorNestedTester>()
  120. ->Version(1)
  121. ->Field("myNestedString", &PropertyTreeEditorNestedTester::m_myNestedString)
  122. ;
  123. if (AZ::EditContext* editContext = serializeContext->GetEditContext())
  124. {
  125. editContext->Class<PropertyTreeEditorSubBlockTester>(
  126. "PropertyTreeEditorSubBlock Tester", "Tester sub block for the PropertyTreeEditor test")
  127. ->DataElement(AZ::Edit::UIHandlers::Default, &PropertyTreeEditorSubBlockTester::m_myNegativeShort, "My Negative Short", "A test short int.")
  128. ;
  129. editContext->Class<PropertyTreeEditorTester>(
  130. "PropertyTreeEditor Tester", "Tester for the PropertyTreeEditor")
  131. ->DataElement(AZ::Edit::UIHandlers::Default, &PropertyTreeEditorTester::m_myInt, "My Int", "A test int.")
  132. ->DataElement(AZ::Edit::UIHandlers::Default, &PropertyTreeEditorTester::m_myBool, "My Bool", "A test bool.")
  133. ->DataElement(AZ::Edit::UIHandlers::Default, &PropertyTreeEditorTester::m_myFloat, "My Float", "A test float.")
  134. ->DataElement(AZ::Edit::UIHandlers::Default, &PropertyTreeEditorTester::m_myString, "My String", "A test string.")
  135. ->DataElement(AZ::Edit::UIHandlers::Default, &PropertyTreeEditorTester::m_nestedTester, "Nested", "A nested class.")
  136. ->DataElement(AZ::Edit::UIHandlers::Default, &PropertyTreeEditorTester::m_myNewInt, "My New Int", "A test int.", "My Old Int")
  137. ->DataElement(AZ::Edit::UIHandlers::Default, &PropertyTreeEditorTester::m_myList, "My New List", "A test vector<>.", "My Old List")
  138. ->DataElement(AZ::Edit::UIHandlers::Default, &PropertyTreeEditorTester::m_myMap, "My Map", "A test unordered_map<>.", "My Old Map")
  139. ->DataElement(AZ::Edit::UIHandlers::Default, &PropertyTreeEditorTester::m_myAssetData, "My Asset Data", "An test asset data.")
  140. ->DataElement(AZ::Edit::UIHandlers::Default, &PropertyTreeEditorTester::m_myTestSimpleAsset, "My Test Simple Asset", "A test simple asset ref.")
  141. ->DataElement(AZ::Edit::UIHandlers::Default, &PropertyTreeEditorTester::m_myHiddenDouble, "My Hidden Double", "A test hidden node.", "My Old Double")
  142. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Hide)
  143. ->DataElement(AZ::Edit::UIHandlers::Default, &PropertyTreeEditorTester::m_nestedTesterHiddenChildren, "Nested Hidden Children", "A test node with hidden children.")
  144. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::HideChildren)
  145. ->DataElement(AZ::Edit::UIHandlers::Default, &PropertyTreeEditorTester::m_myReadOnlyShort, "My Read Only", "A test read only node.")
  146. ->Attribute(AZ::Edit::Attributes::ReadOnly, true)
  147. ->DataElement(nullptr, &PropertyTreeEditorTester::m_mySubBlock, "My Sub Block", "sub block test")
  148. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  149. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  150. ->ClassElement(AZ::Edit::ClassElements::Group, "Grouped")
  151. ->DataElement(AZ::Edit::UIHandlers::Default, &PropertyTreeEditorTester::m_myGroupedString, "My Grouped String", "A test grouped string.")
  152. ;
  153. editContext->Class<PropertyTreeEditorNestedTester>(
  154. "PropertyTreeEditor Nested Tester", "SubClass Tester for the PropertyTreeEditor")
  155. ->DataElement(AZ::Edit::UIHandlers::Default, &PropertyTreeEditorNestedTester::m_myNestedString, "My Nested String", "A test string.")
  156. ;
  157. }
  158. }
  159. }
  160. };
  161. class PropertyTreeEditorTests
  162. : public UnitTest::LeakDetectionFixture
  163. {
  164. public:
  165. void SetUp() override
  166. {
  167. AZ::ComponentApplication::StartupParameters startupParameters;
  168. startupParameters.m_loadSettingsRegistry = false;
  169. m_app.Start(AzFramework::Application::Descriptor(), startupParameters);
  170. // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
  171. // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
  172. // in the unit tests.
  173. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
  174. }
  175. void TearDown() override
  176. {
  177. m_app.Stop();
  178. }
  179. ToolsTestApplication m_app{ "PropertyTreeEditorTests" };
  180. AZ::SerializeContext* m_serializeContext = nullptr;
  181. };
  182. TEST_F(PropertyTreeEditorTests, ReadPropertyTreeValues)
  183. {
  184. AZ::ComponentApplicationBus::BroadcastResult(m_serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  185. PropertyTreeEditorTester propertyTreeEditorTester;
  186. propertyTreeEditorTester.Reflect(m_serializeContext);
  187. PropertyTreeEditor propertyTree = PropertyTreeEditor(&propertyTreeEditorTester, AZ::AzTypeInfo<PropertyTreeEditorTester>::Uuid());
  188. // Test existing properties of different types
  189. {
  190. PropertyTreeEditor::PropertyAccessOutcome boolOutcome = propertyTree.GetProperty("My Bool");
  191. EXPECT_TRUE(boolOutcome.IsSuccess());
  192. EXPECT_TRUE(AZStd::any_cast<bool>(boolOutcome.GetValue()));
  193. }
  194. {
  195. PropertyTreeEditor::PropertyAccessOutcome intOutcome = propertyTree.GetProperty("My Int");
  196. EXPECT_TRUE(intOutcome.IsSuccess());
  197. EXPECT_EQ(AZStd::any_cast<int>(intOutcome.GetValue()), propertyTreeEditorTester.m_myInt);
  198. }
  199. {
  200. PropertyTreeEditor::PropertyAccessOutcome floatOutcome = propertyTree.GetProperty("My Float");
  201. EXPECT_TRUE(floatOutcome.IsSuccess());
  202. EXPECT_FLOAT_EQ(AZStd::any_cast<float>(floatOutcome.GetValue()), propertyTreeEditorTester.m_myFloat);
  203. }
  204. {
  205. PropertyTreeEditor::PropertyAccessOutcome stringOutcome = propertyTree.GetProperty("My String");
  206. EXPECT_TRUE(stringOutcome.IsSuccess());
  207. EXPECT_STREQ(AZStd::any_cast<AZStd::string>(stringOutcome.GetValue()).data(), propertyTreeEditorTester.m_myString.data());
  208. }
  209. {
  210. PropertyTreeEditor::PropertyAccessOutcome nestedOutcome = propertyTree.GetProperty("Nested|My Nested String");
  211. EXPECT_TRUE(nestedOutcome.IsSuccess());
  212. EXPECT_STREQ(AZStd::any_cast<AZStd::string>(nestedOutcome.GetValue()).data(), propertyTreeEditorTester.m_nestedTester.m_myNestedString.data());
  213. }
  214. {
  215. PropertyTreeEditor::PropertyAccessOutcome groupedOutcome = propertyTree.GetProperty("Grouped|My Grouped String");
  216. EXPECT_TRUE(groupedOutcome.IsSuccess());
  217. EXPECT_STREQ(AZStd::any_cast<AZStd::string>(groupedOutcome.GetValue()).data(), propertyTreeEditorTester.m_myGroupedString.data());
  218. }
  219. // Test non-existing properties
  220. {
  221. PropertyTreeEditor::PropertyAccessOutcome intOutcome = propertyTree.GetProperty("Wrong Property");
  222. EXPECT_FALSE(intOutcome.IsSuccess());
  223. }
  224. {
  225. PropertyTreeEditor::PropertyAccessOutcome nestedOutcome = propertyTree.GetProperty("Nested|Wrong Nested Property");
  226. EXPECT_FALSE(nestedOutcome.IsSuccess());
  227. }
  228. {
  229. // Addressing the grouped property by name directly without the group should fail
  230. PropertyTreeEditor::PropertyAccessOutcome groupedOutcome = propertyTree.GetProperty("My Grouped String");
  231. EXPECT_FALSE(groupedOutcome.IsSuccess());
  232. }
  233. }
  234. TEST_F(PropertyTreeEditorTests, WritePropertyTreeValues)
  235. {
  236. AZ::ComponentApplicationBus::BroadcastResult(m_serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  237. PropertyTreeEditorTester propertyTreeEditorTester;
  238. propertyTreeEditorTester.Reflect(m_serializeContext);
  239. PropertyTreeEditor propertyTree = PropertyTreeEditor(&propertyTreeEditorTester, AZ::AzTypeInfo<PropertyTreeEditorTester>::Uuid());
  240. // Test existing properties of different types
  241. {
  242. PropertyTreeEditor::PropertyAccessOutcome boolOutcomeSet = propertyTree.SetProperty("My Bool", AZStd::any(false));
  243. EXPECT_TRUE(boolOutcomeSet.IsSuccess());
  244. PropertyTreeEditor::PropertyAccessOutcome boolOutcomeGet = propertyTree.GetProperty("My Bool");
  245. EXPECT_TRUE(boolOutcomeGet.IsSuccess());
  246. EXPECT_FALSE(AZStd::any_cast<bool>(boolOutcomeGet.GetValue()));
  247. }
  248. {
  249. PropertyTreeEditor::PropertyAccessOutcome intOutcomeSet = propertyTree.SetProperty("My Int", AZStd::any(48));
  250. EXPECT_TRUE(intOutcomeSet.IsSuccess());
  251. PropertyTreeEditor::PropertyAccessOutcome intOutcomeGet = propertyTree.GetProperty("My Int");
  252. EXPECT_TRUE(intOutcomeGet.IsSuccess());
  253. EXPECT_EQ(AZStd::any_cast<int>(intOutcomeGet.GetValue()), AZStd::any_cast<int>(intOutcomeSet.GetValue()));
  254. }
  255. {
  256. PropertyTreeEditor::PropertyAccessOutcome floatOutcomeSet = propertyTree.SetProperty("My Float", AZStd::any(48.0f));
  257. EXPECT_TRUE(floatOutcomeSet.IsSuccess());
  258. PropertyTreeEditor::PropertyAccessOutcome floatOutcomeGet = propertyTree.GetProperty("My Float");
  259. EXPECT_TRUE(floatOutcomeGet.IsSuccess());
  260. EXPECT_FLOAT_EQ(AZStd::any_cast<float>(floatOutcomeGet.GetValue()), AZStd::any_cast<float>(floatOutcomeSet.GetValue()));
  261. }
  262. {
  263. PropertyTreeEditor::PropertyAccessOutcome stringOutcomeSet = propertyTree.SetProperty("My String", AZStd::make_any<AZStd::string>("New Value"));
  264. EXPECT_TRUE(stringOutcomeSet.IsSuccess());
  265. PropertyTreeEditor::PropertyAccessOutcome stringOutcomeGet = propertyTree.GetProperty("My String");
  266. EXPECT_TRUE(stringOutcomeGet.IsSuccess());
  267. EXPECT_STREQ(AZStd::any_cast<AZStd::string>(stringOutcomeSet.GetValue()).data(), AZStd::any_cast<AZStd::string>(stringOutcomeGet.GetValue()).data());
  268. }
  269. {
  270. PropertyTreeEditor::PropertyAccessOutcome stringOutcomeSet = propertyTree.SetProperty("Nested|My Nested String", AZStd::make_any<AZStd::string>("New Nested Value"));
  271. EXPECT_TRUE(stringOutcomeSet.IsSuccess());
  272. PropertyTreeEditor::PropertyAccessOutcome stringOutcomeGet = propertyTree.GetProperty("Nested|My Nested String");
  273. EXPECT_TRUE(stringOutcomeGet.IsSuccess());
  274. EXPECT_STREQ(AZStd::any_cast<AZStd::string>(stringOutcomeSet.GetValue()).data(), AZStd::any_cast<AZStd::string>(stringOutcomeGet.GetValue()).data());
  275. }
  276. {
  277. PropertyTreeEditor::PropertyAccessOutcome stringOutcomeSet = propertyTree.SetProperty("Grouped|My Grouped String", AZStd::make_any<AZStd::string>("New Grouped Value"));
  278. EXPECT_TRUE(stringOutcomeSet.IsSuccess());
  279. PropertyTreeEditor::PropertyAccessOutcome stringOutcomeGet = propertyTree.GetProperty("Grouped|My Grouped String");
  280. EXPECT_TRUE(stringOutcomeGet.IsSuccess());
  281. EXPECT_STREQ(AZStd::any_cast<AZStd::string>(stringOutcomeSet.GetValue()).data(), AZStd::any_cast<AZStd::string>(stringOutcomeGet.GetValue()).data());
  282. }
  283. // Test non-existing properties
  284. {
  285. PropertyTreeEditor::PropertyAccessOutcome intOutcome = propertyTree.SetProperty("Wrong Property", AZStd::any(12));
  286. EXPECT_FALSE(intOutcome.IsSuccess());
  287. }
  288. {
  289. PropertyTreeEditor::PropertyAccessOutcome nestedOutcome = propertyTree.SetProperty("Nested|Wrong Nested Property", AZStd::make_any<AZStd::string>("Some Value"));
  290. EXPECT_FALSE(nestedOutcome.IsSuccess());
  291. }
  292. {
  293. PropertyTreeEditor::PropertyAccessOutcome groupedOutcome = propertyTree.SetProperty("Grouped|Wrong Grouped Property", AZStd::make_any<AZStd::string>("Some Value"));
  294. EXPECT_FALSE(groupedOutcome.IsSuccess());
  295. }
  296. {
  297. // Addressing the grouped property by name directly without the group should fail
  298. PropertyTreeEditor::PropertyAccessOutcome groupedOutcome = propertyTree.SetProperty("My Grouped String", AZStd::make_any<AZStd::string>("Some Value"));
  299. EXPECT_FALSE(groupedOutcome.IsSuccess());
  300. }
  301. // Test existing properties with wrong type
  302. {
  303. PropertyTreeEditor::PropertyAccessOutcome intOutcome = propertyTree.SetProperty("My Int", AZStd::any(12.0f));
  304. EXPECT_FALSE(intOutcome.IsSuccess());
  305. }
  306. {
  307. PropertyTreeEditor::PropertyAccessOutcome nestedOutcome = propertyTree.SetProperty("Nested|My Nested String", AZStd::any(42.0f));
  308. EXPECT_FALSE(nestedOutcome.IsSuccess());
  309. }
  310. {
  311. PropertyTreeEditor::PropertyAccessOutcome groupedOutcome = propertyTree.SetProperty("Grouped|My Grouped String", AZStd::any(42.0f));
  312. EXPECT_FALSE(groupedOutcome.IsSuccess());
  313. }
  314. }
  315. TEST_F(PropertyTreeEditorTests, PropertyTreeVectorContainerSupport)
  316. {
  317. AZ::ComponentApplicationBus::BroadcastResult(m_serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  318. PropertyTreeEditorTester propertyTreeEditorTester;
  319. propertyTreeEditorTester.Reflect(m_serializeContext);
  320. PropertyTreeEditor propertyTree = PropertyTreeEditor(&propertyTreeEditorTester, AZ::AzTypeInfo<PropertyTreeEditorTester>::Uuid());
  321. // IsContainer
  322. {
  323. EXPECT_FALSE(propertyTree.IsContainer("My New Int"));
  324. EXPECT_TRUE(propertyTree.IsContainer("My New List"));
  325. }
  326. // AddContainerItem
  327. {
  328. AZStd::any key = AZStd::make_any<AZ::s32>(0);
  329. AZStd::any value = AZStd::make_any<PropertyTreeEditorTester::PropertyTreeEditorNestedTester>();
  330. PropertyTreeEditor::PropertyAccessOutcome outcomeAdd0 = propertyTree.AddContainerItem("My New Int", key, value);
  331. EXPECT_FALSE(outcomeAdd0.IsSuccess());
  332. PropertyTreeEditor::PropertyAccessOutcome outcomeAdd1 = propertyTree.AddContainerItem("My New List", key, value);
  333. EXPECT_TRUE(outcomeAdd1.IsSuccess());
  334. }
  335. // GetContainerCount
  336. {
  337. EXPECT_FALSE(propertyTree.GetContainerCount("My New Int").IsSuccess());
  338. EXPECT_EQ(1, AZStd::any_cast<AZ::u64>(propertyTree.GetContainerCount("My New List").GetValue()));
  339. }
  340. // GetContainerItem
  341. {
  342. AZStd::any key = AZStd::make_any<AZ::s32>(0);
  343. AZStd::any keyString = AZStd::make_any<AZStd::string_view>("0");
  344. EXPECT_FALSE(propertyTree.GetContainerItem("My New Int", key).IsSuccess());
  345. AZ_TEST_START_TRACE_SUPPRESSION;
  346. EXPECT_FALSE(propertyTree.GetContainerItem("My New List", keyString).IsSuccess());
  347. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  348. PropertyTreeEditor::PropertyAccessOutcome outcome = propertyTree.GetContainerItem("My New List", key);
  349. EXPECT_TRUE(outcome.IsSuccess());
  350. if (outcome.IsSuccess())
  351. {
  352. auto&& testerValue = AZStd::any_cast<PropertyTreeEditorTester::PropertyTreeEditorNestedTester>(&outcome.GetValue());
  353. EXPECT_STREQ("NestedString", testerValue->m_myNestedString.c_str());
  354. }
  355. }
  356. // UpdateContainerItem
  357. {
  358. AZStd::any key = AZStd::make_any<AZ::s32>(0);
  359. AZStd::any keyString = AZStd::make_any<AZStd::string_view>("0");
  360. PropertyTreeEditorTester::PropertyTreeEditorNestedTester testUpdate;
  361. testUpdate.m_myNestedString = "a new value";
  362. AZStd::any value = AZStd::make_any<PropertyTreeEditorTester::PropertyTreeEditorNestedTester>(testUpdate);
  363. EXPECT_FALSE(propertyTree.UpdateContainerItem("My New Int", key, value).IsSuccess());
  364. AZ_TEST_START_TRACE_SUPPRESSION;
  365. EXPECT_FALSE(propertyTree.UpdateContainerItem("My New List", keyString, value).IsSuccess());
  366. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  367. EXPECT_TRUE(propertyTree.UpdateContainerItem("My New List", key, value).IsSuccess());
  368. PropertyTreeEditor::PropertyAccessOutcome outcome = propertyTree.GetContainerItem("My New List", key);
  369. EXPECT_TRUE(outcome.IsSuccess());
  370. if (outcome.IsSuccess())
  371. {
  372. auto&& testerValue = AZStd::any_cast<PropertyTreeEditorTester::PropertyTreeEditorNestedTester>(&outcome.GetValue());
  373. EXPECT_STREQ(testUpdate.m_myNestedString.c_str(), testerValue->m_myNestedString.c_str());
  374. }
  375. }
  376. // RemoveContainerItem
  377. {
  378. AZStd::any key = AZStd::make_any<AZ::s32>(0);
  379. AZStd::any keyString = AZStd::make_any<AZStd::string_view>("0");
  380. EXPECT_FALSE(propertyTree.RemoveContainerItem("My New Int", key).IsSuccess());
  381. AZ_TEST_START_TRACE_SUPPRESSION;
  382. EXPECT_FALSE(propertyTree.RemoveContainerItem("My New List", keyString).IsSuccess());
  383. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  384. PropertyTreeEditor::PropertyAccessOutcome outcomeAdd1 = propertyTree.RemoveContainerItem("My New List", key);
  385. EXPECT_TRUE(outcomeAdd1.IsSuccess());
  386. EXPECT_EQ(0, AZStd::any_cast<AZ::u64>(propertyTree.GetContainerCount("My New List").GetValue()));
  387. }
  388. // ResetContainer
  389. {
  390. AZStd::any value = AZStd::make_any<PropertyTreeEditorTester::PropertyTreeEditorNestedTester>();
  391. propertyTree.AddContainerItem("My New List", AZStd::make_any<AZ::s32>(0), value);
  392. propertyTree.AddContainerItem("My New List", AZStd::make_any<AZ::s32>(1), value);
  393. propertyTree.AddContainerItem("My New List", AZStd::make_any<AZ::s32>(2), value);
  394. EXPECT_EQ(3, AZStd::any_cast<AZ::u64>(propertyTree.GetContainerCount("My New List").GetValue()));
  395. propertyTree.ResetContainer("My New List");
  396. EXPECT_EQ(0, AZStd::any_cast<AZ::u64>(propertyTree.GetContainerCount("My New List").GetValue()));
  397. }
  398. // AppendContainerItem
  399. {
  400. AZStd::any value = AZStd::make_any<PropertyTreeEditorTester::PropertyTreeEditorNestedTester>();
  401. EXPECT_TRUE(propertyTree.AppendContainerItem("My New List", value).IsSuccess());
  402. EXPECT_TRUE(propertyTree.AppendContainerItem("My New List", value).IsSuccess());
  403. EXPECT_TRUE(propertyTree.AppendContainerItem("My New List", value).IsSuccess());
  404. EXPECT_EQ(3, AZStd::any_cast<AZ::u64>(propertyTree.GetContainerCount("My New List").GetValue()));
  405. propertyTree.ResetContainer("My New List");
  406. }
  407. }
  408. TEST_F(PropertyTreeEditorTests, PropertyTreeUnorderedMapContainerSupport)
  409. {
  410. AZ::ComponentApplicationBus::BroadcastResult(m_serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  411. using TestData = PropertyTreeEditorTester::PropertyTreeEditorNestedTester;
  412. PropertyTreeEditorTester propertyTreeEditorTester;
  413. propertyTreeEditorTester.Reflect(m_serializeContext);
  414. propertyTreeEditorTester.m_myMap.emplace(AZStd::make_pair("one", TestData()));
  415. const char* testDataString = "a test string";
  416. PropertyTreeEditor propertyTree = PropertyTreeEditor(&propertyTreeEditorTester, AZ::AzTypeInfo<PropertyTreeEditorTester>::Uuid());
  417. // AddContainerItem
  418. {
  419. AZStd::any key = AZStd::make_any<AZStd::string>("two");
  420. TestData testItem;
  421. testItem.m_myNestedString = testDataString;
  422. AZStd::any value = AZStd::make_any<TestData>(testItem);
  423. EXPECT_TRUE(propertyTree.AddContainerItem("My Map", key, value).IsSuccess());
  424. }
  425. // GetContainerCount
  426. {
  427. EXPECT_EQ(2, AZStd::any_cast<AZ::u64>(propertyTree.GetContainerCount("My Map").GetValue()));
  428. }
  429. // GetContainerItem
  430. {
  431. AZStd::any key = AZStd::make_any<AZStd::string>("two");
  432. PropertyTreeEditor::PropertyAccessOutcome outcome = propertyTree.GetContainerItem("My Map", key);
  433. EXPECT_TRUE(outcome.IsSuccess());
  434. if (outcome.IsSuccess())
  435. {
  436. auto&& testerValue = AZStd::any_cast<TestData>(&outcome.GetValue());
  437. EXPECT_STREQ(testDataString, testerValue->m_myNestedString.c_str());
  438. }
  439. }
  440. // UpdateContainerItem
  441. {
  442. AZStd::any key = AZStd::make_any<AZStd::string>("two");
  443. TestData testUpdate;
  444. testUpdate.m_myNestedString = "a new value";
  445. AZStd::any value = AZStd::make_any<TestData>(testUpdate);
  446. EXPECT_TRUE(propertyTree.UpdateContainerItem("My Map", key, value).IsSuccess());
  447. PropertyTreeEditor::PropertyAccessOutcome outcome = propertyTree.GetContainerItem("My Map", key);
  448. if (outcome.IsSuccess())
  449. {
  450. auto&& testerValue = AZStd::any_cast<PropertyTreeEditorTester::PropertyTreeEditorNestedTester>(&outcome.GetValue());
  451. EXPECT_STREQ(testUpdate.m_myNestedString.c_str(), testerValue->m_myNestedString.c_str());
  452. }
  453. }
  454. // RemoveContainerItem
  455. {
  456. AZStd::any key = AZStd::make_any<AZStd::string>("two");
  457. EXPECT_TRUE(propertyTree.RemoveContainerItem("My Map", key).IsSuccess());
  458. EXPECT_EQ(1, AZStd::any_cast<AZ::u64>(propertyTree.GetContainerCount("My Map").GetValue()));
  459. }
  460. // ResetContainer
  461. {
  462. EXPECT_EQ(1, AZStd::any_cast<AZ::u64>(propertyTree.GetContainerCount("My Map").GetValue()));
  463. propertyTree.ResetContainer("My Map");
  464. EXPECT_EQ(0, AZStd::any_cast<AZ::u64>(propertyTree.GetContainerCount("My Map").GetValue()));
  465. }
  466. // AppendContainerItem
  467. {
  468. AZStd::any value = AZStd::make_any<PropertyTreeEditorTester::PropertyTreeEditorNestedTester>();
  469. EXPECT_FALSE(propertyTree.AppendContainerItem("My Map", value).IsSuccess());
  470. EXPECT_EQ(0, AZStd::any_cast<AZ::u64>(propertyTree.GetContainerCount("My Map").GetValue()));
  471. }
  472. }
  473. TEST_F(PropertyTreeEditorTests, PropertyTreeInspection)
  474. {
  475. AZ::ComponentApplicationBus::BroadcastResult(m_serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  476. PropertyTreeEditorTester propertyTreeEditorTester;
  477. propertyTreeEditorTester.Reflect(m_serializeContext);
  478. PropertyTreeEditor propertyTree = PropertyTreeEditor(&propertyTreeEditorTester, AZ::AzTypeInfo<PropertyTreeEditorTester>::Uuid());
  479. // BuildPathsList
  480. {
  481. auto&& pathList = propertyTree.BuildPathsList();
  482. EXPECT_TRUE(!pathList.empty());
  483. EXPECT_TRUE(AZStd::any_of(pathList.begin(), pathList.end(), [](auto&& path) { return path == "My Map"; }));
  484. EXPECT_TRUE(AZStd::any_of(pathList.begin(), pathList.end(), [](auto&& path) { return path == "My New List"; }));
  485. EXPECT_TRUE(AZStd::any_of(pathList.begin(), pathList.end(), [](auto&& path) { return path == "Nested|My Nested String"; }));
  486. EXPECT_TRUE(AZStd::any_of(pathList.begin(), pathList.end(), [](auto&& path) { return path == "Grouped|My Grouped String"; }));
  487. EXPECT_TRUE(AZStd::any_of(pathList.begin(), pathList.end(), [](auto&& path) { return path == "My Hidden Double"; }));
  488. EXPECT_TRUE(AZStd::any_of(pathList.begin(), pathList.end(), [](auto&& path) { return path == "My Sub Block|My Negative Short"; }));
  489. }
  490. // BuildPathsListWithTypes
  491. {
  492. [[maybe_unused]] static auto stringContains = [](const AZStd::string& data, const char* subString) -> bool
  493. {
  494. return data.find(subString) != AZStd::string::npos;
  495. };
  496. auto&& pathList = propertyTree.BuildPathsListWithTypes();
  497. EXPECT_TRUE(!pathList.empty());
  498. EXPECT_TRUE(AZStd::any_of(pathList.begin(), pathList.end(), [](auto&& path) { return stringContains(path,"NotVisible"); }));
  499. EXPECT_TRUE(AZStd::any_of(pathList.begin(), pathList.end(), [](auto&& path) { return stringContains(path,"Visible"); }));
  500. EXPECT_TRUE(AZStd::any_of(pathList.begin(), pathList.end(), [](auto&& path) { return stringContains(path,"ShowChildrenOnly"); }));
  501. EXPECT_TRUE(AZStd::any_of(pathList.begin(), pathList.end(), [](auto&& path) { return stringContains(path,"HideChildren"); }));
  502. EXPECT_TRUE(AZStd::any_of(pathList.begin(), pathList.end(), [](auto&& path) { return stringContains(path,"ReadOnly"); }));
  503. }
  504. // GetPropertyType
  505. {
  506. EXPECT_TRUE(propertyTree.GetPropertyType("My Map").starts_with("AZStd::unordered_map"));
  507. EXPECT_TRUE(propertyTree.GetPropertyType("My New List").starts_with("AZStd::vector"));
  508. EXPECT_EQ("AZStd::string", propertyTree.GetPropertyType("Nested|My Nested String"));
  509. EXPECT_EQ("double", propertyTree.GetPropertyType("My Hidden Double"));
  510. EXPECT_EQ("PropertyTreeEditorNestedTester", propertyTree.GetPropertyType("Nested"));
  511. }
  512. // BuildPathsList after enforcement removes the "show children only" nodes from the paths
  513. {
  514. propertyTree.SetVisibleEnforcement(true);
  515. auto&& pathList = propertyTree.BuildPathsList();
  516. EXPECT_TRUE(AZStd::any_of(pathList.begin(), pathList.end(), [](auto&& path) { return path == "My Map"; }));
  517. EXPECT_TRUE(AZStd::any_of(pathList.begin(), pathList.end(), [](auto&& path) { return path == "My New List"; }));
  518. EXPECT_TRUE(AZStd::any_of(pathList.begin(), pathList.end(), [](auto&& path) { return path == "Nested|My Nested String"; }));
  519. EXPECT_TRUE(AZStd::any_of(pathList.begin(), pathList.end(), [](auto&& path) { return path == "Grouped|My Grouped String"; }));
  520. EXPECT_FALSE(AZStd::any_of(pathList.begin(), pathList.end(), [](auto&& path) { return path == "My Hidden Double"; }));
  521. EXPECT_FALSE(AZStd::any_of(pathList.begin(), pathList.end(), [](auto&& path) { return path == "My Sub Block|My Negative Short"; }));
  522. EXPECT_TRUE(AZStd::any_of(pathList.begin(), pathList.end(), [](auto&& path) { return path == "My Negative Short"; }));
  523. }
  524. }
  525. TEST_F(PropertyTreeEditorTests, PropertyTreeAttributeInspection)
  526. {
  527. AZ::ComponentApplicationBus::BroadcastResult(m_serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  528. PropertyTreeEditorTester propertyTreeEditorTester;
  529. propertyTreeEditorTester.Reflect(m_serializeContext);
  530. PropertyTreeEditor propertyTree = PropertyTreeEditor(&propertyTreeEditorTester, AZ::AzTypeInfo<PropertyTreeEditorTester>::Uuid());
  531. // HasAttribute
  532. {
  533. EXPECT_TRUE(propertyTree.HasAttribute("My Read Only", "ReadOnly"));
  534. EXPECT_TRUE(propertyTree.HasAttribute("My Hidden Double", "Visibility"));
  535. EXPECT_TRUE(propertyTree.HasAttribute("My Sub Block", "AutoExpand"));
  536. }
  537. }
  538. TEST_F(PropertyTreeEditorTests, HandlesVisibleEnforcement)
  539. {
  540. AZ::ComponentApplicationBus::BroadcastResult(m_serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  541. PropertyTreeEditorTester propertyTreeEditorTester;
  542. propertyTreeEditorTester.Reflect(m_serializeContext);
  543. PropertyTreeEditor propertyTree = PropertyTreeEditor(&propertyTreeEditorTester, AZ::AzTypeInfo<PropertyTreeEditorTester>::Uuid());
  544. // can access a hidden value with 'visible enforcement' set to false
  545. {
  546. PropertyTreeEditor::PropertyAccessOutcome outcomeGet = propertyTree.GetProperty("My Hidden Double");
  547. EXPECT_TRUE(outcomeGet.IsSuccess());
  548. EXPECT_EQ(42.0, AZStd::any_cast<double>(outcomeGet.GetValue()));
  549. }
  550. // can mutate a hidden value with 'visible enforcement' set to false
  551. {
  552. PropertyTreeEditor::PropertyAccessOutcome outcomeSet = propertyTree.SetProperty("My Hidden Double", AZStd::any(12.0));
  553. EXPECT_TRUE(outcomeSet.IsSuccess());
  554. PropertyTreeEditor::PropertyAccessOutcome outcomeGet = propertyTree.GetProperty("My Hidden Double");
  555. EXPECT_TRUE(outcomeGet.IsSuccess());
  556. EXPECT_EQ(12.0, AZStd::any_cast<double>(outcomeGet.GetValue()));
  557. }
  558. propertyTree.SetVisibleEnforcement(true);
  559. // can NOT access hidden value with 'visible enforcement' set to true
  560. {
  561. PropertyTreeEditor::PropertyAccessOutcome outcomeGet = propertyTree.GetProperty("My Hidden Double");
  562. EXPECT_FALSE(outcomeGet.IsSuccess());
  563. }
  564. // can NOT mutate a hidden value with 'visible enforcement' set to false
  565. {
  566. PropertyTreeEditor::PropertyAccessOutcome outcomeSet = propertyTree.SetProperty("My Hidden Double", AZStd::any(42.0));
  567. EXPECT_FALSE(outcomeSet.IsSuccess());
  568. }
  569. }
  570. TEST_F(PropertyTreeEditorTests, PropertyTreeDeprecatedNamesSupport)
  571. {
  572. AZ::ComponentApplicationBus::BroadcastResult(m_serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  573. PropertyTreeEditorTester propertyTreeEditorTester;
  574. propertyTreeEditorTester.Reflect(m_serializeContext);
  575. PropertyTreeEditor propertyTree = PropertyTreeEditor(&propertyTreeEditorTester, AZ::AzTypeInfo<PropertyTreeEditorTester>::Uuid());
  576. // Test that new and deprecated name both refer to the same property
  577. {
  578. int newIntValue = 0;
  579. // get current value of My New Int
  580. PropertyTreeEditor::PropertyAccessOutcome intOutcomeGet = propertyTree.GetProperty("My New Int");
  581. EXPECT_TRUE(intOutcomeGet.IsSuccess());
  582. newIntValue = AZStd::any_cast<int>(intOutcomeGet.GetValue());
  583. // Set new value to My Old Int
  584. PropertyTreeEditor::PropertyAccessOutcome intOutcomeSet = propertyTree.SetProperty("My Old Int", AZStd::any(12));
  585. EXPECT_TRUE(intOutcomeSet.IsSuccess());
  586. // Read value of My New Int again
  587. PropertyTreeEditor::PropertyAccessOutcome intOutcomeGetAgain = propertyTree.GetProperty("My New Int");
  588. EXPECT_TRUE(intOutcomeGetAgain.IsSuccess());
  589. // Verify that My Old Int and My New Int refer to the same property
  590. EXPECT_TRUE(AZStd::any_cast<int>(intOutcomeGetAgain.GetValue()) != newIntValue);
  591. }
  592. }
  593. TEST_F(PropertyTreeEditorTests, ClearWithEmptyAny)
  594. {
  595. AZ::ComponentApplicationBus::BroadcastResult(m_serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  596. AZ::Data::AssetId mockAssetId = AZ::Data::AssetId::CreateString("{66CC8A20-DC4D-4856-95FE-5C75A47B6A21}:0");
  597. MockAssetData mockAssetData(mockAssetId);
  598. AZ::Data::Asset<MockAssetData> mockAsset(&mockAssetData, AZ::Data::AssetLoadBehavior::Default);
  599. AzFramework::SimpleAssetReference<TestSimpleAsset> mockSimpleAsset;
  600. mockSimpleAsset.SetAssetPath("path/to/42");
  601. PropertyTreeEditorTester propertyTreeEditorTester;
  602. propertyTreeEditorTester.Reflect(m_serializeContext);
  603. propertyTreeEditorTester.m_myInt = 42;
  604. propertyTreeEditorTester.m_mySubBlock.m_myNegativeShort = -42;
  605. propertyTreeEditorTester.m_myList.push_back({});
  606. propertyTreeEditorTester.m_myAssetData = AZStd::move(mockAsset);
  607. propertyTreeEditorTester.m_myTestSimpleAsset = mockSimpleAsset;
  608. PropertyTreeEditor propertyTree(&propertyTreeEditorTester, AZ::AzTypeInfo<PropertyTreeEditorTester>::Uuid());
  609. propertyTree.SetVisibleEnforcement(true);
  610. // use an empty any<> to set properties back to a default value
  611. {
  612. AZStd::any anEmpty;
  613. EXPECT_TRUE(propertyTree.SetProperty("My Int", anEmpty).IsSuccess());
  614. EXPECT_TRUE(propertyTree.SetProperty("My Negative Short", anEmpty).IsSuccess());
  615. EXPECT_TRUE(propertyTree.SetProperty("My New List", anEmpty).IsSuccess());
  616. AZ_TEST_START_TRACE_SUPPRESSION;
  617. EXPECT_TRUE(propertyTree.SetProperty("My Asset Data", anEmpty).IsSuccess());
  618. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  619. EXPECT_TRUE(propertyTree.SetProperty("My Test Simple Asset", anEmpty).IsSuccess());
  620. }
  621. // check that the properties went back to default values
  622. {
  623. EXPECT_EQ(0, propertyTreeEditorTester.m_myInt);
  624. EXPECT_EQ(0, propertyTreeEditorTester.m_mySubBlock.m_myNegativeShort);
  625. EXPECT_TRUE(propertyTreeEditorTester.m_myList.empty());
  626. EXPECT_FALSE(propertyTreeEditorTester.m_myAssetData.GetId().IsValid());
  627. EXPECT_TRUE(propertyTreeEditorTester.m_myTestSimpleAsset.GetAssetPath().empty());
  628. }
  629. }
  630. } // namespace UnitTest