SettingsRegistryTests.cpp 77 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791
  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/Casting/numeric_cast.h>
  9. #include <AzCore/IO/SystemFile.h>
  10. #include <AzCore/Math/Uuid.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <AzCore/Serialization/Json/RegistrationContext.h>
  13. #include <AzCore/Serialization/Json/JsonSystemComponent.h>
  14. #include <AzCore/Settings/SettingsRegistryImpl.h>
  15. #include <AzCore/std/containers/vector.h>
  16. #include <AzCore/std/smart_ptr/unique_ptr.h>
  17. #include <AzCore/std/string/string.h>
  18. #include <AzCore/UnitTest/TestTypes.h>
  19. namespace SettingsRegistryTests
  20. {
  21. class TestClass
  22. {
  23. public:
  24. AZ_TYPE_INFO(TestClass, "{CDD9648A-27CA-4625-9FD9-DD3BB9CB093D}");
  25. int m_var1 = { 42 };
  26. double m_var2 = { 42.0 };
  27. static TestClass Initialize()
  28. {
  29. TestClass result;
  30. result.m_var1 = 88;
  31. result.m_var2 = 88.0;
  32. return result;
  33. }
  34. static void Reflect(AZ::SerializeContext& context)
  35. {
  36. context.Class<TestClass>()
  37. ->Field("Var1", &TestClass::m_var1)
  38. ->Field("Var2", &TestClass::m_var2);
  39. }
  40. };
  41. struct RegistryEntry
  42. {
  43. AZStd::string_view m_path;
  44. AZStd::string_view m_valueName;
  45. AZ::SettingsRegistryInterface::Type m_type;
  46. AZ::SettingsRegistryInterface::VisitAction m_action;
  47. AZ::SettingsRegistryInterface::VisitResponse m_response;
  48. RegistryEntry() = default;
  49. RegistryEntry(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type type,
  50. AZ::SettingsRegistryInterface::VisitAction action,
  51. AZ::SettingsRegistryInterface::VisitResponse response = AZ::SettingsRegistryInterface::VisitResponse::Continue)
  52. : m_path(path)
  53. , m_valueName(valueName)
  54. , m_type(type)
  55. , m_action(action)
  56. , m_response(response)
  57. {}
  58. };
  59. class SettingsRegistryTest
  60. : public UnitTest::LeakDetectionFixture
  61. {
  62. public:
  63. ~SettingsRegistryTest() override = default;
  64. void SetUp() override
  65. {
  66. m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
  67. m_registrationContext = AZStd::make_unique<AZ::JsonRegistrationContext>();
  68. m_registry = AZStd::make_unique<AZ::SettingsRegistryImpl>();
  69. m_registry->SetContext(m_serializeContext.get());
  70. m_registry->SetContext(m_registrationContext.get());
  71. AZ::JsonSystemComponent::Reflect(m_registrationContext.get());
  72. }
  73. void TearDown() override
  74. {
  75. m_registrationContext->EnableRemoveReflection();
  76. AZ::JsonSystemComponent::Reflect(m_registrationContext.get());
  77. m_registrationContext->DisableRemoveReflection();
  78. m_registry.reset();
  79. m_registrationContext.reset();
  80. m_serializeContext.reset();
  81. }
  82. void Visit(const AZStd::vector<RegistryEntry>& expected, AZStd::string_view path = "")
  83. {
  84. size_t counter = 0;
  85. auto callback = [&expected, &counter](const AZ::SettingsRegistryInterface::VisitArgs& visitArgs,
  86. AZ::SettingsRegistryInterface::VisitAction action)
  87. {
  88. EXPECT_LT(counter, expected.size());
  89. if (counter < expected.size())
  90. {
  91. const RegistryEntry& entry = expected[counter];
  92. EXPECT_STREQ(entry.m_path.data(), visitArgs.m_jsonKeyPath.data());
  93. EXPECT_STREQ(entry.m_valueName.data(), visitArgs.m_fieldName.data());
  94. EXPECT_EQ(entry.m_action, action);
  95. EXPECT_EQ(entry.m_type, visitArgs.m_type);
  96. counter++;
  97. return entry.m_response;
  98. }
  99. return AZ::SettingsRegistryInterface::VisitResponse::Done;
  100. };
  101. EXPECT_TRUE(m_registry->Visit(callback, path));
  102. EXPECT_EQ(counter, expected.size());
  103. }
  104. void MergeNotify(AZStd::string_view path, size_t index, size_t fileIdLength, const char* counterId, const char** fileIds)
  105. {
  106. EXPECT_TRUE(path.empty());
  107. AZ::s64 value = -1;
  108. bool result = m_registry->Get(value, counterId);
  109. EXPECT_TRUE(result);
  110. EXPECT_EQ(index, value);
  111. size_t i = 0;
  112. for (; i <= index; ++i)
  113. {
  114. bool configValue = false;
  115. result = m_registry->Get(configValue, fileIds[i]);
  116. EXPECT_TRUE(result);
  117. EXPECT_TRUE(configValue);
  118. }
  119. for (; i < fileIdLength; ++i)
  120. {
  121. AZ::SettingsRegistryInterface::Type type = m_registry->GetType(fileIds[i]);
  122. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, type);
  123. }
  124. }
  125. AZStd::unique_ptr<AZ::SettingsRegistryImpl> m_registry;
  126. AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
  127. AZStd::unique_ptr<AZ::JsonRegistrationContext> m_registrationContext;
  128. AZ::Test::ScopedAutoTempDirectory m_tempDirectory;
  129. };
  130. template<typename> struct SettingsType {};
  131. // Json makes a distinction between true and false as different types, so test for both
  132. // versions.
  133. struct BoolTrue {};
  134. struct BoolFalse {};
  135. template<> struct SettingsType<BoolTrue>
  136. {
  137. using DataType = bool;
  138. using ValueType = bool;
  139. constexpr static AZ::SettingsRegistryInterface::Type s_type = AZ::SettingsRegistryInterface::Type::Boolean;
  140. static AZStd::string_view GetStoredJson() { return "true"; }
  141. static DataType GetStoredValue() { return true; }
  142. static DataType GetDefaultValue() { return false; }
  143. static void ExpectEq(ValueType lhs, ValueType rhs) { EXPECT_EQ(lhs, rhs); }
  144. };
  145. template<> struct SettingsType<BoolFalse>
  146. {
  147. using DataType = bool;
  148. using ValueType = bool;
  149. constexpr static AZ::SettingsRegistryInterface::Type s_type = AZ::SettingsRegistryInterface::Type::Boolean;
  150. static AZStd::string_view GetStoredJson() { return "false"; }
  151. static DataType GetStoredValue() { return false; }
  152. static DataType GetDefaultValue() { return true; }
  153. static void ExpectEq(ValueType lhs, ValueType rhs) { EXPECT_EQ(lhs, rhs); }
  154. };
  155. template<> struct SettingsType<AZ::s64>
  156. {
  157. using DataType = AZ::s64;
  158. using ValueType = AZ::s64;
  159. constexpr static AZ::SettingsRegistryInterface::Type s_type = AZ::SettingsRegistryInterface::Type::Integer;
  160. static AZStd::string_view GetStoredJson() { return "-88"; }
  161. static DataType GetStoredValue() { return -88; }
  162. static DataType GetDefaultValue() { return 42; }
  163. static void ExpectEq(ValueType lhs, ValueType rhs) { EXPECT_EQ(lhs, rhs); }
  164. };
  165. template<> struct SettingsType<double>
  166. {
  167. using DataType = double;
  168. using ValueType = double;
  169. constexpr static AZ::SettingsRegistryInterface::Type s_type = AZ::SettingsRegistryInterface::Type::FloatingPoint;
  170. static AZStd::string_view GetStoredJson() { return "88.0"; }
  171. static DataType GetStoredValue() { return 88.0; }
  172. static DataType GetDefaultValue() { return 42.0; }
  173. static void ExpectEq(DataType lhs, DataType rhs) { EXPECT_DOUBLE_EQ(lhs, rhs); }
  174. };
  175. template<> struct SettingsType<AZStd::string>
  176. {
  177. using DataType = AZStd::string;
  178. using ValueType = AZStd::string_view;
  179. constexpr static AZ::SettingsRegistryInterface::Type s_type = AZ::SettingsRegistryInterface::Type::String;
  180. static AZStd::string_view GetStoredJson() { return R"("World")"; }
  181. static DataType GetStoredValue() { return "World"; }
  182. static DataType GetDefaultValue() { return AZStd::string{}; }
  183. static void ExpectEq(ValueType lhs, ValueType rhs) { EXPECT_STREQ(lhs.data(), rhs.data()); }
  184. };
  185. template<> struct SettingsType<AZ::SettingsRegistryInterface::FixedValueString>
  186. {
  187. using DataType = AZ::SettingsRegistryInterface::FixedValueString;
  188. using ValueType = AZStd::string_view;
  189. constexpr static AZ::SettingsRegistryInterface::Type s_type = AZ::SettingsRegistryInterface::Type::String;
  190. static AZStd::string_view GetStoredJson() { return R"("World")"; }
  191. static DataType GetStoredValue() { return "World"; }
  192. static DataType GetDefaultValue() { return DataType{}; }
  193. static void ExpectEq(ValueType lhs, ValueType rhs) { EXPECT_STREQ(lhs.data(), rhs.data()); }
  194. };
  195. template<typename SettingsType>
  196. class TypedSettingsRegistryTest
  197. : public SettingsRegistryTest
  198. {
  199. public:
  200. ~TypedSettingsRegistryTest() override = default;
  201. };
  202. using SettingsTypes = ::testing::Types<
  203. BoolTrue, BoolFalse, AZ::s64, double, AZStd::string, AZ::SettingsRegistryInterface::FixedValueString>;
  204. TYPED_TEST_CASE(TypedSettingsRegistryTest, SettingsTypes);
  205. TYPED_TEST(TypedSettingsRegistryTest, GetSet_SetAndGetValue_Success)
  206. {
  207. typename SettingsType<TypeParam>::DataType value = SettingsType<TypeParam>::GetStoredValue();
  208. typename SettingsType<TypeParam>::DataType readValue = SettingsType<TypeParam>::GetDefaultValue();
  209. AZStd::string_view testPath = "/Test/Path/Value";
  210. ASSERT_TRUE(this->m_registry->Set(testPath, value));
  211. ASSERT_TRUE(this->m_registry->Get(readValue, testPath));
  212. SettingsType<TypeParam>::ExpectEq(value, readValue);
  213. }
  214. TYPED_TEST(TypedSettingsRegistryTest, Get_InvalidPath_ReturnsFalse)
  215. {
  216. typename SettingsType<TypeParam>::DataType readValue = SettingsType<TypeParam>::GetDefaultValue();
  217. AZStd::string_view testPath = "#$%^";
  218. EXPECT_FALSE(this->m_registry->Get(readValue, testPath));
  219. }
  220. TYPED_TEST(TypedSettingsRegistryTest, Get_UnknownPath_ReturnsFalse)
  221. {
  222. typename SettingsType<TypeParam>::DataType readValue = SettingsType<TypeParam>::GetDefaultValue();
  223. AZStd::string_view testPath = "/Unknown/Path";
  224. EXPECT_FALSE(this->m_registry->Get(readValue, testPath));
  225. }
  226. TYPED_TEST(TypedSettingsRegistryTest, Get_InvalidType_ReturnsFalse)
  227. {
  228. ASSERT_TRUE(this->m_registry->MergeSettings(R"({ "Object": { "Value": 42 } })", AZ::SettingsRegistryInterface::Format::JsonMergePatch));
  229. typename SettingsType<TypeParam>::DataType readValue = SettingsType<TypeParam>::GetDefaultValue();
  230. AZStd::string_view testPath = "/Object";
  231. EXPECT_FALSE(this->m_registry->Get(readValue, testPath));
  232. }
  233. TYPED_TEST(TypedSettingsRegistryTest, Set_InvalidPath_ReturnsFalse)
  234. {
  235. typename SettingsType<TypeParam>::DataType value = SettingsType<TypeParam>::GetStoredValue();
  236. AZStd::string_view testPath = "#$%^";
  237. EXPECT_FALSE(this->m_registry->Set(testPath, value));
  238. }
  239. TYPED_TEST(TypedSettingsRegistryTest, Set_NotifiersCalled_ReturnsFalse)
  240. {
  241. size_t counter = 0;
  242. auto callback0 = [&counter](const AZ::SettingsRegistryInterface::NotifyEventArgs& notifyEventArgs)
  243. {
  244. EXPECT_EQ("/Object/Value", notifyEventArgs.m_jsonKeyPath);
  245. counter++;
  246. };
  247. auto callback1 = [&counter](const AZ::SettingsRegistryInterface::NotifyEventArgs& notifyEventArgs)
  248. {
  249. AZ::SettingsRegistryInterface::Type type = SettingsType<TypeParam>::s_type;
  250. EXPECT_EQ(notifyEventArgs.m_type, type);
  251. counter++;
  252. };
  253. auto testNotifier1 = this->m_registry->RegisterNotifier(callback0);
  254. auto testNotifier2 = this->m_registry->RegisterNotifier(AZStd::move(callback1));
  255. typename SettingsType<TypeParam>::DataType value = SettingsType<TypeParam>::GetStoredValue();
  256. AZStd::string_view testPath = "/Object/Value";
  257. EXPECT_TRUE(this->m_registry->Set(testPath, value));
  258. EXPECT_EQ(2, counter);
  259. }
  260. TYPED_TEST(TypedSettingsRegistryTest, Set_ConnectedNotifierCalledAndDisconnectedNotifierNotCalled_Success)
  261. {
  262. size_t counter1{};
  263. auto callback0 = [&counter1](const AZ::SettingsRegistryInterface::NotifyEventArgs& notifyEventArgs)
  264. {
  265. EXPECT_EQ("/Object/Value", notifyEventArgs.m_jsonKeyPath);
  266. counter1++;
  267. };
  268. size_t counter2{};
  269. auto callback1 = [&counter2](const AZ::SettingsRegistryInterface::NotifyEventArgs& notifyEventArgs)
  270. {
  271. EXPECT_EQ("/Object/Value", notifyEventArgs.m_jsonKeyPath);
  272. counter2++;
  273. };;
  274. auto testNotifier1 = this->m_registry->RegisterNotifier(callback0);
  275. auto testNotifier2 = this->m_registry->RegisterNotifier(AZStd::move(callback1));
  276. typename SettingsType<TypeParam>::DataType value = SettingsType<TypeParam>::GetStoredValue();
  277. AZStd::string_view testPath = "/Object/Value";
  278. EXPECT_TRUE(this->m_registry->Set(testPath, value));
  279. EXPECT_EQ(1, counter1);
  280. EXPECT_EQ(1, counter2);
  281. // Disconnect the second Notifier
  282. testNotifier2.Disconnect();
  283. EXPECT_TRUE(this->m_registry->Set(testPath, SettingsType<TypeParam>::GetDefaultValue()));
  284. EXPECT_EQ(2, counter1);
  285. EXPECT_EQ(1, counter2);
  286. }
  287. TYPED_TEST(TypedSettingsRegistryTest, GetType_TypeForTheStoredValue_TypeMatchesProvidedType)
  288. {
  289. typename SettingsType<TypeParam>::DataType value = SettingsType<TypeParam>::GetStoredValue();
  290. AZ::SettingsRegistryInterface::Type type = SettingsType<TypeParam>::s_type;
  291. AZStd::string_view testPath = "/Test/Path/Value";
  292. ASSERT_TRUE(this->m_registry->Set(testPath, value));
  293. EXPECT_EQ(type, this->m_registry->GetType(testPath));
  294. }
  295. TYPED_TEST(TypedSettingsRegistryTest, VisitWithVisitor_VisitingObject_ValuesInJsonAreVisited)
  296. {
  297. AZStd::string_view storedJson = SettingsType<TypeParam>::GetStoredJson();
  298. AZStd::string json = AZStd::string::format(
  299. R"({
  300. "Test":
  301. {
  302. "Object":{ "Type": %.*s }
  303. }
  304. })", aznumeric_cast<int>(storedJson.length()), storedJson.data());
  305. ASSERT_TRUE(this->m_registry->MergeSettings(json.c_str(), AZ::SettingsRegistryInterface::Format::JsonMergePatch));
  306. struct : public AZ::SettingsRegistryInterface::Visitor
  307. {
  308. using AZ::SettingsRegistryInterface::Visitor::Visit;
  309. using ValueType [[maybe_unused]] = typename SettingsType<TypeParam>::ValueType;
  310. void Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, ValueType value) override
  311. {
  312. AZ::SettingsRegistryInterface::Type expectedType = SettingsType<TypeParam>::s_type;
  313. EXPECT_EQ(expectedType, visitArgs.m_type);
  314. SettingsType<TypeParam>::ExpectEq(SettingsType<TypeParam>::GetStoredValue(), value);
  315. m_counter++;
  316. }
  317. size_t m_counter{ 0 };
  318. } visitor;
  319. EXPECT_TRUE(this->m_registry->Visit(visitor, "/Test"));
  320. EXPECT_EQ(1, visitor.m_counter);
  321. }
  322. TYPED_TEST(TypedSettingsRegistryTest, VisitWithVisitor_VisitingArray_ValuesInJsonAreVisited)
  323. {
  324. AZStd::string_view storedJson = SettingsType<TypeParam>::GetStoredJson();
  325. AZStd::string json = AZStd::string::format(
  326. R"({
  327. "Test":
  328. {
  329. "Array":[ %.*s ]
  330. }
  331. })", aznumeric_cast<int>(storedJson.length()), storedJson.data());
  332. ASSERT_TRUE(this->m_registry->MergeSettings(json.c_str(), AZ::SettingsRegistryInterface::Format::JsonMergePatch));
  333. struct : public AZ::SettingsRegistryInterface::Visitor
  334. {
  335. using AZ::SettingsRegistryInterface::Visitor::Visit;
  336. using ValueType [[maybe_unused]] = typename SettingsType<TypeParam>::ValueType;
  337. void Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, ValueType value) override
  338. {
  339. AZ::SettingsRegistryInterface::Type expectedType = SettingsType<TypeParam>::s_type;
  340. EXPECT_EQ(expectedType, visitArgs.m_type);
  341. SettingsType<TypeParam>::ExpectEq(SettingsType<TypeParam>::GetStoredValue(), value);
  342. m_counter++;
  343. }
  344. size_t m_counter{ 0 };
  345. } visitor;
  346. EXPECT_TRUE(this->m_registry->Visit(visitor, "/Test"));
  347. EXPECT_EQ(1, visitor.m_counter);
  348. }
  349. TEST_F(SettingsRegistryTest, VisitWithVisitor_VisitingIntegerGreaterThanNumericLimitsOfSigned64Bit_SuppliesCorrectBitPattern)
  350. {
  351. // Create a 64-bit value that is greater than what can be represented in a signed int64_t
  352. constexpr AZ::u64 unsigned64BitValue = aznumeric_cast<AZ::u64>((std::numeric_limits<AZ::s64>::max)()) + 1;
  353. AZStd::string json = AZStd::string::format(
  354. R"({
  355. "Test":
  356. {
  357. "Object":{ "Type": %llu }
  358. }
  359. })", unsigned64BitValue);
  360. ASSERT_TRUE(this->m_registry->MergeSettings(json.c_str(), AZ::SettingsRegistryInterface::Format::JsonMergePatch));
  361. struct : public AZ::SettingsRegistryInterface::Visitor
  362. {
  363. using AZ::SettingsRegistryInterface::Visitor::Visit;
  364. void Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, AZ::s64 value) override
  365. {
  366. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Integer, visitArgs.m_type);
  367. AZ::u64 testValue = reinterpret_cast<AZ::u64&>(value);
  368. EXPECT_EQ(expectedValue, testValue);
  369. }
  370. const AZ::u64 expectedValue{ unsigned64BitValue };
  371. } visitor;
  372. EXPECT_TRUE(this->m_registry->Visit(visitor, "/Test/Object/Type"));
  373. }
  374. TEST_F(SettingsRegistryTest, VisitWithVisitor_EndOfPathArguments_MatchesValueNameArgument_ForAllIterations)
  375. {
  376. // Validate that every invocation of the Traverse and Visit command supplies a 'path' parameter
  377. // whose end matches that of the 'valueName' parameter
  378. AZStd::string json{
  379. R"({
  380. "Test":
  381. {
  382. "Object":{ "Type": "TestString" }
  383. }
  384. })"
  385. };
  386. ASSERT_TRUE(this->m_registry->MergeSettings(json.c_str(), AZ::SettingsRegistryInterface::Format::JsonMergePatch));
  387. struct : public AZ::SettingsRegistryInterface::Visitor
  388. {
  389. AZ::SettingsRegistryInterface::VisitResponse Traverse(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs,
  390. AZ::SettingsRegistryInterface::VisitAction) override
  391. {
  392. EXPECT_TRUE(visitArgs.m_jsonKeyPath.ends_with(visitArgs.m_fieldName));
  393. return AZ::SettingsRegistryInterface::VisitResponse::Continue;
  394. }
  395. using AZ::SettingsRegistryInterface::Visitor::Visit;
  396. void Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs, AZStd::string_view)override
  397. {
  398. EXPECT_TRUE(visitArgs.m_jsonKeyPath.ends_with(visitArgs.m_fieldName));
  399. }
  400. } visitor;
  401. EXPECT_TRUE(this->m_registry->Visit(visitor, ""));
  402. EXPECT_TRUE(this->m_registry->Visit(visitor, "/Test"));
  403. }
  404. //
  405. // Object
  406. //
  407. TEST_F(SettingsRegistryTest, GetSetObject_SetAndGetValue_Success)
  408. {
  409. TestClass::Reflect(*m_serializeContext);
  410. AZStd::string_view testPath = "/Test/Path/Value";
  411. TestClass value;
  412. value.m_var1 = 88;
  413. value.m_var2 = 88.0;
  414. TestClass readValue;
  415. ASSERT_TRUE(m_registry->SetObject(testPath, &value, azrtti_typeid(value)));
  416. ASSERT_TRUE(m_registry->GetObject(&readValue, azrtti_typeid(readValue), testPath));
  417. EXPECT_EQ(value.m_var1, readValue.m_var1);
  418. EXPECT_DOUBLE_EQ(value.m_var2, readValue.m_var2);
  419. m_serializeContext->EnableRemoveReflection();
  420. TestClass::Reflect(*m_serializeContext);
  421. m_serializeContext->DisableRemoveReflection();
  422. }
  423. TEST_F(SettingsRegistryTest, GetObject_InvalidPath_ReturnsFalse)
  424. {
  425. TestClass::Reflect(*m_serializeContext);
  426. AZStd::string_view testPath = "$%^&";
  427. TestClass readValue;
  428. EXPECT_FALSE(m_registry->GetObject(&readValue, azrtti_typeid(readValue), testPath));
  429. m_serializeContext->EnableRemoveReflection();
  430. TestClass::Reflect(*m_serializeContext);
  431. m_serializeContext->DisableRemoveReflection();
  432. }
  433. TEST_F(SettingsRegistryTest, GetObject_UnknownPath_ReturnsFalse)
  434. {
  435. TestClass::Reflect(*m_serializeContext);
  436. AZStd::string_view testPath = "/Unknown/Path";
  437. TestClass readValue;
  438. EXPECT_FALSE(m_registry->GetObject(&readValue, azrtti_typeid(readValue), testPath));
  439. m_serializeContext->EnableRemoveReflection();
  440. TestClass::Reflect(*m_serializeContext);
  441. m_serializeContext->DisableRemoveReflection();
  442. }
  443. TEST_F(SettingsRegistryTest, SetObject_InvalidPath_ReturnFalse)
  444. {
  445. TestClass::Reflect(*m_serializeContext);
  446. AZStd::string_view testPath = "$%^&";
  447. TestClass value;
  448. EXPECT_FALSE(m_registry->SetObject(testPath, &value, azrtti_typeid(value)));
  449. m_serializeContext->EnableRemoveReflection();
  450. TestClass::Reflect(*m_serializeContext);
  451. m_serializeContext->DisableRemoveReflection();
  452. }
  453. TEST_F(SettingsRegistryTest, SetObject_NotifiersCalled_ReturnsFalse)
  454. {
  455. size_t counter = 0;
  456. auto callback0 = [&counter](const AZ::SettingsRegistryInterface::NotifyEventArgs& notifyEventArgs)
  457. {
  458. EXPECT_EQ("/Object/Value", notifyEventArgs.m_jsonKeyPath);
  459. counter++;
  460. };
  461. auto callback1 = [&counter](const AZ::SettingsRegistryInterface::NotifyEventArgs& notifyEventArgs)
  462. {
  463. EXPECT_EQ(notifyEventArgs.m_type, AZ::SettingsRegistryInterface::Type::Object);
  464. counter++;
  465. };
  466. auto testNotifier1 = m_registry->RegisterNotifier(callback0);
  467. auto testNotifier2 = m_registry->RegisterNotifier(AZStd::move(callback1));
  468. TestClass::Reflect(*m_serializeContext);
  469. AZStd::string_view testPath = "/Object/Value";
  470. TestClass value;
  471. EXPECT_TRUE(m_registry->SetObject(testPath, &value, azrtti_typeid(value)));
  472. EXPECT_EQ(2, counter);
  473. m_serializeContext->EnableRemoveReflection();
  474. TestClass::Reflect(*m_serializeContext);
  475. m_serializeContext->DisableRemoveReflection();
  476. }
  477. //
  478. // Remove
  479. //
  480. TYPED_TEST(TypedSettingsRegistryTest, Remove_SetAndRemoveValue_Success)
  481. {
  482. typename SettingsType<TypeParam>::DataType value = SettingsType<TypeParam>::GetStoredValue();
  483. typename SettingsType<TypeParam>::DataType newValue = SettingsType<TypeParam>::GetDefaultValue();
  484. AZStd::string_view testPath = "/Test/Path/Value";
  485. EXPECT_TRUE(this->m_registry->Set(testPath, value));
  486. EXPECT_TRUE(this->m_registry->Get(newValue, testPath));
  487. SettingsType<TypeParam>::ExpectEq(value, newValue);
  488. EXPECT_TRUE(this->m_registry->Remove(testPath));
  489. typename SettingsType<TypeParam>::DataType notFoundValue{};
  490. EXPECT_FALSE(this->m_registry->Get(notFoundValue, testPath));
  491. }
  492. TYPED_TEST(TypedSettingsRegistryTest, Remove_InvalidPath_ReturnsFalse)
  493. {
  494. AZStd::string_view testPath = "#$%^";
  495. EXPECT_FALSE(this->m_registry->Remove(testPath));
  496. typename SettingsType<TypeParam>::DataType notFoundValue{};
  497. EXPECT_FALSE(this->m_registry->Get(notFoundValue, testPath));
  498. }
  499. TYPED_TEST(TypedSettingsRegistryTest, Remove_UnknownPath_ReturnsFalse)
  500. {
  501. AZStd::string_view testPath = "/Unknown/Path";
  502. EXPECT_FALSE(this->m_registry->Remove(testPath));
  503. typename SettingsType<TypeParam>::DataType notFoundValue{};
  504. EXPECT_FALSE(this->m_registry->Get(notFoundValue, testPath));
  505. }
  506. //
  507. // Specializations::Append
  508. //
  509. TEST_F(SettingsRegistryTest, SpecializationsAppend_TooManySpecializations_ReportsErrorAndReturnsFalse)
  510. {
  511. AZ::SettingsRegistryInterface::Specializations specializations;
  512. for (size_t i = 0; i < AZ::SettingsRegistryInterface::Specializations::MaxCount; ++i)
  513. {
  514. specializations.Append("test");
  515. }
  516. AZ_TEST_START_TRACE_SUPPRESSION;
  517. bool result = specializations.Append("TooFar");
  518. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  519. EXPECT_FALSE(result);
  520. }
  521. //
  522. // GetType
  523. // Note: The typed tests already test several GetType versions.
  524. //
  525. TEST_F(SettingsRegistryTest, GetType_TypeForObject_TypeMatchesProvidedType)
  526. {
  527. ASSERT_TRUE(m_registry->MergeSettings(R"({ "Object": { "Value": 42 } })", AZ::SettingsRegistryInterface::Format::JsonMergePatch));
  528. AZStd::string_view testPath = "/Object";
  529. EXPECT_EQ(AZ::SettingsRegistryImpl::Type::Object, m_registry->GetType(testPath));
  530. }
  531. TEST_F(SettingsRegistryTest, GetType_TypeForArray_TypeMatchesProvidedType)
  532. {
  533. ASSERT_TRUE(m_registry->MergeSettings(R"({ "Array": [ 42 ] })", AZ::SettingsRegistryInterface::Format::JsonMergePatch));
  534. AZStd::string_view testPath = "/Array";
  535. EXPECT_EQ(AZ::SettingsRegistryImpl::Type::Array, m_registry->GetType(testPath));
  536. }
  537. TEST_F(SettingsRegistryTest, GetType_TypeForNull_TypeMatchesProvidedType)
  538. {
  539. ASSERT_TRUE(m_registry->MergeSettings(
  540. R"( [
  541. { "op": "add", "path": "/Object", "value": {"Value": null} }
  542. ])",
  543. AZ::SettingsRegistryInterface::Format::JsonPatch));
  544. AZStd::string_view testPath = "/Object/Value";
  545. EXPECT_EQ(AZ::SettingsRegistryImpl::Type::Null, m_registry->GetType(testPath));
  546. }
  547. TEST_F(SettingsRegistryTest, GetType_InvalidPath_ReturnNone)
  548. {
  549. AZ::SettingsRegistryInterface::Type type = m_registry->GetType("#$%");
  550. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, type);
  551. }
  552. TEST_F(SettingsRegistryTest, GetType_UnknownPath_ReturnNone)
  553. {
  554. AZ::SettingsRegistryInterface::Type type = m_registry->GetType("/Unknown/Path");
  555. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, type);
  556. }
  557. //
  558. // Visit
  559. //
  560. TEST_F(SettingsRegistryTest, VisitWithVisitor_InvalidPath_ReturnsFalse)
  561. {
  562. struct : public AZ::SettingsRegistryInterface::Visitor
  563. {
  564. } visitor;
  565. EXPECT_FALSE(m_registry->Visit(visitor, "#$%"));
  566. }
  567. TEST_F(SettingsRegistryTest, VisitWithVisitor_UnknownPath_ReturnsFalse)
  568. {
  569. struct : public AZ::SettingsRegistryInterface::Visitor
  570. {
  571. } visitor;
  572. EXPECT_FALSE(m_registry->Visit(visitor, "/Unknown/Path"));
  573. }
  574. TEST_F(SettingsRegistryTest, VisitWithCallback_FullIteration_AllFieldsAreReported)
  575. {
  576. using SRI = AZ::SettingsRegistryInterface;
  577. ASSERT_TRUE(m_registry->MergeSettings(
  578. R"( [
  579. { "op": "add", "path": "/Test", "value": { "Object": {} } },
  580. { "op": "add", "path": "/Test/Object/NullType", "value": null },
  581. { "op": "add", "path": "/Test/Object/TrueType", "value": true },
  582. { "op": "add", "path": "/Test/Object/FalseType", "value": false },
  583. { "op": "add", "path": "/Test/Object/IntType", "value": -42 },
  584. { "op": "add", "path": "/Test/Object/UIntType", "value": 42 },
  585. { "op": "add", "path": "/Test/Object/DoubleType", "value": 42.0 },
  586. { "op": "add", "path": "/Test/Object/StringType", "value": "Hello world" },
  587. { "op": "add", "path": "/Test/Array", "value": [ null, true, false, -42, 42, 42.0, "Hello world" ] }
  588. ])", SRI::Format::JsonPatch));
  589. AZStd::vector<RegistryEntry> expected =
  590. {
  591. RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
  592. RegistryEntry("/Test/Object", "Object", SRI::Type::Object, SRI::VisitAction::Begin),
  593. RegistryEntry("/Test/Object/NullType", "NullType", SRI::Type::Null, SRI::VisitAction::Value),
  594. RegistryEntry("/Test/Object/TrueType", "TrueType", SRI::Type::Boolean, SRI::VisitAction::Value),
  595. RegistryEntry("/Test/Object/FalseType", "FalseType", SRI::Type::Boolean, SRI::VisitAction::Value),
  596. RegistryEntry("/Test/Object/IntType", "IntType", SRI::Type::Integer, SRI::VisitAction::Value),
  597. RegistryEntry("/Test/Object/UIntType", "UIntType", SRI::Type::Integer, SRI::VisitAction::Value),
  598. RegistryEntry("/Test/Object/DoubleType", "DoubleType", SRI::Type::FloatingPoint, SRI::VisitAction::Value),
  599. RegistryEntry("/Test/Object/StringType", "StringType", SRI::Type::String, SRI::VisitAction::Value),
  600. RegistryEntry("/Test/Object", "Object", SRI::Type::Object, SRI::VisitAction::End),
  601. RegistryEntry("/Test/Array", "Array", SRI::Type::Array, SRI::VisitAction::Begin),
  602. RegistryEntry("/Test/Array/0", "0", SRI::Type::Null, SRI::VisitAction::Value),
  603. RegistryEntry("/Test/Array/1", "1", SRI::Type::Boolean, SRI::VisitAction::Value),
  604. RegistryEntry("/Test/Array/2", "2", SRI::Type::Boolean, SRI::VisitAction::Value),
  605. RegistryEntry("/Test/Array/3", "3", SRI::Type::Integer, SRI::VisitAction::Value),
  606. RegistryEntry("/Test/Array/4", "4", SRI::Type::Integer, SRI::VisitAction::Value),
  607. RegistryEntry("/Test/Array/5", "5", SRI::Type::FloatingPoint, SRI::VisitAction::Value),
  608. RegistryEntry("/Test/Array/6", "6", SRI::Type::String, SRI::VisitAction::Value),
  609. RegistryEntry("/Test/Array", "Array", SRI::Type::Array, SRI::VisitAction::End),
  610. RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::End)
  611. };
  612. Visit(expected, "/Test");
  613. }
  614. TEST_F(SettingsRegistryTest, VisitWithCallback_IterateWithOffsetInObject_AllFieldsInSubSectionAreListed)
  615. {
  616. using SRI = AZ::SettingsRegistryInterface;
  617. ASSERT_TRUE(m_registry->MergeSettings(
  618. R"({
  619. "Layer1":
  620. {
  621. "Layer2":
  622. {
  623. "Layer3":
  624. {
  625. "StringType": "Hello World"
  626. }
  627. }
  628. }
  629. })", SRI::Format::JsonMergePatch));
  630. AZStd::vector<RegistryEntry> expected =
  631. {
  632. RegistryEntry("/Layer1/Layer2", "Layer2", SRI::Type::Object, SRI::VisitAction::Begin),
  633. RegistryEntry("/Layer1/Layer2/Layer3", "Layer3", SRI::Type::Object, SRI::VisitAction::Begin),
  634. RegistryEntry("/Layer1/Layer2/Layer3/StringType", "StringType", SRI::Type::String, SRI::VisitAction::Value),
  635. RegistryEntry("/Layer1/Layer2/Layer3", "Layer3", SRI::Type::Object, SRI::VisitAction::End),
  636. RegistryEntry("/Layer1/Layer2", "Layer2", SRI::Type::Object, SRI::VisitAction::End)
  637. };
  638. Visit(expected, "/Layer1/Layer2");
  639. }
  640. TEST_F(SettingsRegistryTest, VisitWithCallback_IterateWithOffsetInArray_AllFieldsInSubSectionAreListed)
  641. {
  642. using SRI = AZ::SettingsRegistryInterface;
  643. ASSERT_TRUE(m_registry->MergeSettings(
  644. R"({
  645. "Object":
  646. [
  647. [
  648. [
  649. "Hello World"
  650. ]
  651. ]
  652. ]
  653. })", SRI::Format::JsonMergePatch));
  654. AZStd::vector<RegistryEntry> expected =
  655. {
  656. RegistryEntry("/Object/0/0", "0", SRI::Type::Array, SRI::VisitAction::Begin),
  657. RegistryEntry("/Object/0/0/0", "0", SRI::Type::String, SRI::VisitAction::Value),
  658. RegistryEntry("/Object/0/0", "0", SRI::Type::Array, SRI::VisitAction::End)
  659. };
  660. Visit(expected, "/Object/0/0");
  661. }
  662. TEST_F(SettingsRegistryTest, VisitWithCallback_SkipObject_FirstAndThirdObjectOnly)
  663. {
  664. using SRI = AZ::SettingsRegistryInterface;
  665. ASSERT_TRUE(m_registry->MergeSettings(
  666. R"({
  667. "Test":
  668. {
  669. "Object0": { "Field" : 142 },
  670. "Object1": { "Field" : 242 },
  671. "Object2": { "Field" : 342 }
  672. }
  673. })", SRI::Format::JsonMergePatch));
  674. AZStd::vector<RegistryEntry> expected =
  675. {
  676. RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
  677. RegistryEntry("/Test/Object0", "Object0", SRI::Type::Object, SRI::VisitAction::Begin),
  678. RegistryEntry("/Test/Object0/Field", "Field", SRI::Type::Integer, SRI::VisitAction::Value),
  679. RegistryEntry("/Test/Object0", "Object0", SRI::Type::Object, SRI::VisitAction::End),
  680. RegistryEntry("/Test/Object1", "Object1", SRI::Type::Object, SRI::VisitAction::Begin, SRI::VisitResponse::Skip),
  681. RegistryEntry("/Test/Object2", "Object2", SRI::Type::Object, SRI::VisitAction::Begin),
  682. RegistryEntry("/Test/Object2/Field", "Field", SRI::Type::Integer, SRI::VisitAction::Value),
  683. RegistryEntry("/Test/Object2", "Object2", SRI::Type::Object, SRI::VisitAction::End),
  684. RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::End)
  685. };
  686. Visit(expected, "/Test");
  687. }
  688. TEST_F(SettingsRegistryTest, VisitWithCallback_SkipArray_FirstAndThirdObjectOnly)
  689. {
  690. using SRI = AZ::SettingsRegistryInterface;
  691. ASSERT_TRUE(m_registry->MergeSettings(
  692. R"({
  693. "Test":
  694. {
  695. "Array0": [ 142, 188 ],
  696. "Array1": [ 242, 288 ],
  697. "Array2": [ 342, 388 ]
  698. }
  699. })", SRI::Format::JsonMergePatch));
  700. AZStd::vector<RegistryEntry> expected =
  701. {
  702. RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
  703. RegistryEntry("/Test/Array0", "Array0", SRI::Type::Array, SRI::VisitAction::Begin),
  704. RegistryEntry("/Test/Array0/0", "0", SRI::Type::Integer, SRI::VisitAction::Value),
  705. RegistryEntry("/Test/Array0/1", "1", SRI::Type::Integer, SRI::VisitAction::Value),
  706. RegistryEntry("/Test/Array0", "Array0", SRI::Type::Array, SRI::VisitAction::End),
  707. RegistryEntry("/Test/Array1", "Array1", SRI::Type::Array, SRI::VisitAction::Begin, SRI::VisitResponse::Skip),
  708. RegistryEntry("/Test/Array2", "Array2", SRI::Type::Array, SRI::VisitAction::Begin),
  709. RegistryEntry("/Test/Array2/0", "0", SRI::Type::Integer, SRI::VisitAction::Value),
  710. RegistryEntry("/Test/Array2/1", "1", SRI::Type::Integer, SRI::VisitAction::Value),
  711. RegistryEntry("/Test/Array2", "Array2", SRI::Type::Array, SRI::VisitAction::End),
  712. RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::End)
  713. };
  714. Visit(expected, "/Test");
  715. }
  716. TEST_F(SettingsRegistryTest, VisitWithCallback_StopMidwayThroughObject_FirstObjectOnly)
  717. {
  718. using SRI = AZ::SettingsRegistryInterface;
  719. ASSERT_TRUE(m_registry->MergeSettings(
  720. R"({
  721. "Test":
  722. {
  723. "Object0": { "Field" : 142 },
  724. "Object1": { "Field" : 242 },
  725. "Object2": { "Field" : 342 }
  726. }
  727. })", SRI::Format::JsonMergePatch));
  728. AZStd::vector<RegistryEntry> expected =
  729. {
  730. RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
  731. RegistryEntry("/Test/Object0", "Object0", SRI::Type::Object, SRI::VisitAction::Begin),
  732. RegistryEntry("/Test/Object0/Field", "Field", SRI::Type::Integer, SRI::VisitAction::Value),
  733. RegistryEntry("/Test/Object0", "Object0", SRI::Type::Object, SRI::VisitAction::End),
  734. RegistryEntry("/Test/Object1", "Object1", SRI::Type::Object, SRI::VisitAction::Begin, SRI::VisitResponse::Done)
  735. };
  736. Visit(expected, "/Test");
  737. }
  738. TEST_F(SettingsRegistryTest, VisitWithCallback_StopOnObjectEnd_FirstObjectOnly)
  739. {
  740. using SRI = AZ::SettingsRegistryInterface;
  741. ASSERT_TRUE(m_registry->MergeSettings(
  742. R"({
  743. "Test":
  744. {
  745. "Object0": { "Field" : 142 },
  746. "Object1": { "Field" : 242 },
  747. "Object2": { "Field" : 342 }
  748. }
  749. })", SRI::Format::JsonMergePatch));
  750. AZStd::vector<RegistryEntry> expected =
  751. {
  752. RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
  753. RegistryEntry("/Test/Object0", "Object0", SRI::Type::Object, SRI::VisitAction::Begin),
  754. RegistryEntry("/Test/Object0/Field", "Field", SRI::Type::Integer, SRI::VisitAction::Value),
  755. RegistryEntry("/Test/Object0", "Object0", SRI::Type::Object, SRI::VisitAction::End, SRI::VisitResponse::Done)
  756. };
  757. Visit(expected, "/Test");
  758. }
  759. TEST_F(SettingsRegistryTest, VisitWithCallback_StopMidwayThroughArray_FirstObjectOnly)
  760. {
  761. using SRI = AZ::SettingsRegistryInterface;
  762. ASSERT_TRUE(m_registry->MergeSettings(
  763. R"({
  764. "Test":
  765. {
  766. "Array0": [ 142, 188 ],
  767. "Array1": [ 242, 288 ],
  768. "Array2": [ 342, 388 ]
  769. }
  770. })", SRI::Format::JsonMergePatch));
  771. AZStd::vector<RegistryEntry> expected =
  772. {
  773. RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
  774. RegistryEntry("/Test/Array0", "Array0", SRI::Type::Array, SRI::VisitAction::Begin),
  775. RegistryEntry("/Test/Array0/0", "0", SRI::Type::Integer, SRI::VisitAction::Value),
  776. RegistryEntry("/Test/Array0/1", "1", SRI::Type::Integer, SRI::VisitAction::Value),
  777. RegistryEntry("/Test/Array0", "Array0", SRI::Type::Array, SRI::VisitAction::End),
  778. RegistryEntry("/Test/Array1", "Array1", SRI::Type::Array, SRI::VisitAction::Begin),
  779. RegistryEntry("/Test/Array1/0", "0", SRI::Type::Integer, SRI::VisitAction::Value, SRI::VisitResponse::Done),
  780. };
  781. Visit(expected, "/Test");
  782. }
  783. TEST_F(SettingsRegistryTest, VisitWithCallback_StopOnArrayEnd_FirstObjectOnly)
  784. {
  785. using SRI = AZ::SettingsRegistryInterface;
  786. ASSERT_TRUE(m_registry->MergeSettings(
  787. R"({
  788. "Test":
  789. {
  790. "Array0": [ 142, 188 ],
  791. "Array1": [ 242, 288 ],
  792. "Array2": [ 342, 388 ]
  793. }
  794. })", SRI::Format::JsonMergePatch));
  795. AZStd::vector<RegistryEntry> expected =
  796. {
  797. RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
  798. RegistryEntry("/Test/Array0", "Array0", SRI::Type::Array, SRI::VisitAction::Begin),
  799. RegistryEntry("/Test/Array0/0", "0", SRI::Type::Integer, SRI::VisitAction::Value),
  800. RegistryEntry("/Test/Array0/1", "1", SRI::Type::Integer, SRI::VisitAction::Value),
  801. RegistryEntry("/Test/Array0", "Array0", SRI::Type::Array, SRI::VisitAction::End, SRI::VisitResponse::Done)
  802. };
  803. Visit(expected, "/Test");
  804. }
  805. TEST_F(SettingsRegistryTest, VisitWithCallback_InvalidPath_ReturnsFalse)
  806. {
  807. auto callback = [](const AZ::SettingsRegistryInterface::VisitArgs&,
  808. AZ::SettingsRegistryInterface::VisitAction)
  809. {
  810. return AZ::SettingsRegistryInterface::VisitResponse::Continue;
  811. };
  812. EXPECT_FALSE(m_registry->Visit(callback, "#$%"));
  813. }
  814. TEST_F(SettingsRegistryTest, VisitWithCallback_UnknownPath_ReturnsFalse)
  815. {
  816. auto callback = [](const AZ::SettingsRegistryInterface::VisitArgs&,
  817. AZ::SettingsRegistryInterface::VisitAction)
  818. {
  819. return AZ::SettingsRegistryInterface::VisitResponse::Continue;
  820. };
  821. EXPECT_FALSE(m_registry->Visit(callback, "/Unknown/Path"));
  822. }
  823. TEST_F(SettingsRegistryTest, VisitWithCallback_WithEmptyStringViewPath_DoesNotCrash)
  824. {
  825. auto callback = [](const AZ::SettingsRegistryInterface::VisitArgs&,
  826. AZ::SettingsRegistryInterface::VisitAction)
  827. {
  828. return AZ::SettingsRegistryInterface::VisitResponse::Continue;
  829. };
  830. EXPECT_TRUE(m_registry->Visit(callback, {}));
  831. }
  832. //
  833. // MergeCommandLineArgument
  834. //
  835. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SetStringArgument_ValueAddedToRegistry)
  836. {
  837. ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test=Value", {}, {}));
  838. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
  839. AZStd::string value;
  840. ASSERT_TRUE(m_registry->Get(value, "/Test"));
  841. EXPECT_STREQ("Value", value.c_str());
  842. }
  843. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SetStringWithSpaceArgument_ValueAddedToRegistry)
  844. {
  845. ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test=Value of test", {}, {}));
  846. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
  847. AZStd::string value;
  848. ASSERT_TRUE(m_registry->Get(value, "/Test"));
  849. EXPECT_STREQ("Value of test", value.c_str());
  850. }
  851. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SetIntegerArgument_ValueAddedToRegistry)
  852. {
  853. ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test=42", {}, {}));
  854. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Integer, m_registry->GetType("/Test"));
  855. AZ::s64 value;
  856. ASSERT_TRUE(m_registry->Get(value, "/Test"));
  857. EXPECT_EQ(42, value);
  858. }
  859. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SetFloatingPointArgument_ValueAddedToRegistry)
  860. {
  861. ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test=42.0", {}, {}));
  862. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::FloatingPoint, m_registry->GetType("/Test"));
  863. double value;
  864. ASSERT_TRUE(m_registry->Get(value, "/Test"));
  865. EXPECT_EQ(42.0, value);
  866. }
  867. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SetTrueArgument_ValueAddedToRegistry)
  868. {
  869. ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test=true", {}, {}));
  870. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Boolean, m_registry->GetType("/Test"));
  871. bool value;
  872. ASSERT_TRUE(m_registry->Get(value, "/Test"));
  873. EXPECT_TRUE(value);
  874. }
  875. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SetFalseArgument_ValueAddedToRegistry)
  876. {
  877. ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test=false", {}, {}));
  878. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Boolean, m_registry->GetType("/Test"));
  879. bool value;
  880. ASSERT_TRUE(m_registry->Get(value, "/Test"));
  881. EXPECT_FALSE(value);
  882. }
  883. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyWithPath_ValueAddedToRegistry)
  884. {
  885. ASSERT_TRUE(m_registry->MergeCommandLineArgument("/Parent/Test=Value", {}, {}));
  886. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Parent/Test"));
  887. AZStd::string value;
  888. ASSERT_TRUE(m_registry->Get(value, "/Parent/Test"));
  889. EXPECT_STREQ("Value", value.c_str());
  890. }
  891. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyAndPath_ValueAddedToRegistry)
  892. {
  893. ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test=Value", "/Parent", {}));
  894. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Parent/Test"));
  895. AZStd::string value;
  896. ASSERT_TRUE(m_registry->Get(value, "/Parent/Test"));
  897. EXPECT_STREQ("Value", value.c_str());
  898. }
  899. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyWithDividerAndPath_ValueAddedToRegistry)
  900. {
  901. ASSERT_TRUE(m_registry->MergeCommandLineArgument("/Test=Value", "/Parent", {}));
  902. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Parent/Test"));
  903. AZStd::string value;
  904. ASSERT_TRUE(m_registry->Get(value, "/Parent/Test"));
  905. EXPECT_STREQ("Value", value.c_str());
  906. }
  907. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyAndPathWithDivider_ValueAddedToRegistry)
  908. {
  909. ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test=Value", "/Parent/", {}));
  910. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Parent/Test"));
  911. AZStd::string value;
  912. ASSERT_TRUE(m_registry->Get(value, "/Parent/Test"));
  913. EXPECT_STREQ("Value", value.c_str());
  914. }
  915. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyWithDividerAndPathWithDivider_ValueAddedToRegistry)
  916. {
  917. ASSERT_TRUE(m_registry->MergeCommandLineArgument("/Test=Value", "/Parent/", {}));
  918. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Parent/Test"));
  919. AZStd::string value;
  920. ASSERT_TRUE(m_registry->Get(value, "/Parent/Test"));
  921. EXPECT_STREQ("Value", value.c_str());
  922. }
  923. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SpacesBeforeKey_SpacesAreRemoved)
  924. {
  925. ASSERT_TRUE(m_registry->MergeCommandLineArgument(" Test=Value", {}, {}));
  926. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
  927. AZStd::string value;
  928. ASSERT_TRUE(m_registry->Get(value, "/Test"));
  929. EXPECT_STREQ("Value", value.c_str());
  930. }
  931. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SpacesAfterKey_SpacesAreRemoved)
  932. {
  933. ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test =Value", {}, {}));
  934. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
  935. AZStd::string value;
  936. ASSERT_TRUE(m_registry->Get(value, "/Test"));
  937. EXPECT_STREQ("Value", value.c_str());
  938. }
  939. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SpacesBeforeAndAfterKey_SpacesAreRemoved)
  940. {
  941. ASSERT_TRUE(m_registry->MergeCommandLineArgument(" Test =Value", {}, {}));
  942. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
  943. AZStd::string value;
  944. ASSERT_TRUE(m_registry->Get(value, "/Test"));
  945. EXPECT_STREQ("Value", value.c_str());
  946. }
  947. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SpacesInKey_SpacesAreNotRemoved)
  948. {
  949. ASSERT_TRUE(m_registry->MergeCommandLineArgument(" Test Key =Value", {}, {}));
  950. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test Key"));
  951. AZStd::string value;
  952. ASSERT_TRUE(m_registry->Get(value, "/Test Key"));
  953. EXPECT_STREQ("Value", value.c_str());
  954. }
  955. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SpacesBeforeValue_SpacesAreRemoved)
  956. {
  957. ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test= Value", {}, {}));
  958. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
  959. AZStd::string value;
  960. ASSERT_TRUE(m_registry->Get(value, "/Test"));
  961. EXPECT_STREQ("Value", value.c_str());
  962. }
  963. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SpacesAfterValue_SpacesAreRemoved)
  964. {
  965. ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test=Value ", {}, {}));
  966. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
  967. AZStd::string value;
  968. ASSERT_TRUE(m_registry->Get(value, "/Test"));
  969. EXPECT_STREQ("Value", value.c_str());
  970. }
  971. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_SpacesBeforeAndAfterValue_SpacesAreRemoved)
  972. {
  973. ASSERT_TRUE(m_registry->MergeCommandLineArgument("Test= Value ", {}, {}));
  974. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
  975. AZStd::string value;
  976. ASSERT_TRUE(m_registry->Get(value, "/Test"));
  977. EXPECT_STREQ("Value", value.c_str());
  978. }
  979. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyIsTooLong_ReturnsFalse)
  980. {
  981. constexpr int LongKeySize = 1024;
  982. AZStd::string argument = AZStd::string::format("Te%*cst=Value", LongKeySize, ' ');
  983. EXPECT_FALSE(m_registry->MergeCommandLineArgument(argument, {}, {}));
  984. }
  985. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyIsTooLongWithDivider_ReturnsFalse)
  986. {
  987. constexpr int LongKeySize = 1024;
  988. AZStd::string argument = AZStd::string::format("/Te%*cst=Value", LongKeySize, ' ');
  989. EXPECT_FALSE(m_registry->MergeCommandLineArgument(argument, "/Path", {}));
  990. }
  991. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_ValueIsTooLong_ReturnsFalse)
  992. {
  993. constexpr int LongValueSize = 1024;
  994. AZStd::string argument = AZStd::string::format("Test=Val%*cue", LongValueSize, ' ');
  995. EXPECT_FALSE(m_registry->MergeCommandLineArgument(argument, {}, {}));
  996. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, m_registry->GetType("/Test"));
  997. }
  998. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_MissingValue_ReturnsEmptyString)
  999. {
  1000. EXPECT_TRUE(m_registry->MergeCommandLineArgument("Test=", {}, {}));
  1001. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
  1002. AZ::SettingsRegistryInterface::FixedValueString value;
  1003. EXPECT_TRUE(m_registry->Get(value, "/Test"));
  1004. EXPECT_TRUE(value.empty());
  1005. }
  1006. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_MissingKey_ReturnsFalse)
  1007. {
  1008. EXPECT_FALSE(m_registry->MergeCommandLineArgument("=Value", {}, {}));
  1009. }
  1010. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_MissingKeyAndValue_ReturnsFalse)
  1011. {
  1012. EXPECT_FALSE(m_registry->MergeCommandLineArgument("=", {}, {}));
  1013. }
  1014. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_EmptyString_ReturnsFalse)
  1015. {
  1016. EXPECT_FALSE(m_registry->MergeCommandLineArgument("", {}, {}));
  1017. }
  1018. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyIsSpaces_ReturnsFalse)
  1019. {
  1020. EXPECT_FALSE(m_registry->MergeCommandLineArgument(" =Value", {}, {}));
  1021. }
  1022. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_ValueIsSpaces_ReturnsEmptyString)
  1023. {
  1024. EXPECT_TRUE(m_registry->MergeCommandLineArgument("Key= ", {}, {}));
  1025. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Key"));
  1026. AZ::SettingsRegistryInterface::FixedValueString value;
  1027. EXPECT_TRUE(m_registry->Get(value, "/Key"));
  1028. EXPECT_TRUE(value.empty());
  1029. }
  1030. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyAndValueAreSpaces_ReturnsFalse)
  1031. {
  1032. EXPECT_FALSE(m_registry->MergeCommandLineArgument(" = ", {}, {}));
  1033. }
  1034. TEST_F(SettingsRegistryTest, MergeCommandLineArgument_OnlySpaces_ReturnsFalse)
  1035. {
  1036. EXPECT_FALSE(m_registry->MergeCommandLineArgument(" ", {}, {}));
  1037. }
  1038. //
  1039. // MergeSettings
  1040. //
  1041. TEST_F(SettingsRegistryTest, MergeSettings_MergeJsonWithAnchorKey_StoresSettingsUnderneathKey)
  1042. {
  1043. constexpr AZStd::string_view anchorKey = "/Anchor/Root/0";
  1044. constexpr auto mergeFormat = AZ::SettingsRegistryInterface::Format::JsonMergePatch;
  1045. EXPECT_TRUE(m_registry->MergeSettings(R"({ "Test": "1" })", mergeFormat, anchorKey));
  1046. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Array, m_registry->GetType("/Anchor/Root"));
  1047. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType("/Anchor/Root/0"));
  1048. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Anchor/Root/0/Test"));
  1049. }
  1050. TEST_F(SettingsRegistryTest, MergeSettings_NotifierSignals_AtAnchorKeyAndStoresMergeType)
  1051. {
  1052. AZStd::string_view anchorKey = "/Anchor/Root";
  1053. bool callbackInvoked{};
  1054. auto callback = [anchorKey, &callbackInvoked](const AZ::SettingsRegistryInterface::NotifyEventArgs& notifyEventArgs)
  1055. {
  1056. EXPECT_EQ(anchorKey, notifyEventArgs.m_jsonKeyPath);
  1057. EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Array, notifyEventArgs.m_type);
  1058. callbackInvoked = true;
  1059. };
  1060. auto testNotifier1 = m_registry->RegisterNotifier(callback);
  1061. constexpr auto mergeFormat = AZ::SettingsRegistryInterface::Format::JsonMergePatch;
  1062. EXPECT_TRUE(m_registry->MergeSettings(R"([ "Test" ])", mergeFormat, anchorKey));
  1063. EXPECT_TRUE(callbackInvoked);
  1064. }
  1065. //
  1066. // MergeSettingsFile
  1067. //
  1068. TEST_F(SettingsRegistryTest, MergeSettingsFile_MergeTestFile_PatchAppliedAndReported)
  1069. {
  1070. auto path = AZ::Test::CreateTestFile(m_tempDirectory, "test.setreg", R"({ "Test": 1 })");
  1071. auto callback = [this](const AZ::SettingsRegistryInterface::NotifyEventArgs& notifyEventArgs)
  1072. {
  1073. EXPECT_TRUE(notifyEventArgs.m_jsonKeyPath.empty());
  1074. AZ::s64 value = -1;
  1075. bool result = m_registry->Get(value, "/Test");
  1076. EXPECT_TRUE(result);
  1077. EXPECT_EQ(1, value);
  1078. };
  1079. auto testNotifier1 = m_registry->RegisterNotifier(callback);
  1080. auto outcome = m_registry->MergeSettingsFile(path->Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, {}, nullptr);
  1081. ASSERT_TRUE(outcome);
  1082. }
  1083. TEST_F(SettingsRegistryTest, MergeSettingsFile_MergeTestFileWithRootKey_PatchAppliedAndReported)
  1084. {
  1085. auto path = AZ::Test::CreateTestFile(m_tempDirectory, "test.setreg", R"({ "Test": 1 })");
  1086. auto callback = [this](const AZ::SettingsRegistryInterface::NotifyEventArgs& notifyEventArgs)
  1087. {
  1088. EXPECT_EQ("/Path", notifyEventArgs.m_jsonKeyPath);
  1089. AZ::s64 value = -1;
  1090. bool result = m_registry->Get(value, "/Path/Test");
  1091. EXPECT_TRUE(result);
  1092. EXPECT_EQ(1, value);
  1093. };
  1094. auto testNotifier1 = m_registry->RegisterNotifier(callback);
  1095. auto outcome = m_registry->MergeSettingsFile(path->Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, "/Path", nullptr);
  1096. ASSERT_TRUE(outcome);
  1097. }
  1098. TEST_F(SettingsRegistryTest, MergeSettingsFile_MergeTestFileWithBuffer_PatchAppliedAndReported)
  1099. {
  1100. auto path = AZ::Test::CreateTestFile(m_tempDirectory, "test.setreg", R"({ "Test": 1 })");
  1101. auto result = m_registry->MergeSettingsFile(path->Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, {});
  1102. EXPECT_TRUE(result);
  1103. }
  1104. TEST_F(SettingsRegistryTest, MergeSettingsFile_EmptyPath_ReturnsFalse)
  1105. {
  1106. auto result = m_registry->MergeSettingsFile("", AZ::SettingsRegistryInterface::Format::JsonMergePatch, {}, nullptr);
  1107. EXPECT_FALSE(result);
  1108. }
  1109. TEST_F(SettingsRegistryTest, MergeSettingsFile_PathAsSubString_ReturnsTrue)
  1110. {
  1111. auto path = m_tempDirectory.GetDirectoryAsFixedMaxPath()
  1112. / AZ::SettingsRegistryInterface::RegistryFolder / "test.setreg1234";
  1113. AZ::Test::CreateTestFile(
  1114. m_tempDirectory, AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "test.setreg", R"({ "Test": 1 })");
  1115. AZStd::string_view subPath(path.c_str(), path.Native().size() - 4);
  1116. auto result = m_registry->MergeSettingsFile(subPath, AZ::SettingsRegistryInterface::Format::JsonMergePatch, {}, nullptr);
  1117. EXPECT_TRUE(result);
  1118. }
  1119. TEST_F(SettingsRegistryTest, MergeSettingsFile_PathAsSubStringThatsTooLong_ReturnsFalse)
  1120. {
  1121. constexpr AZStd::fixed_string<AZ::IO::MaxPathLength + 1> path(AZ::IO::MaxPathLength + 1, '1');
  1122. const AZStd::string_view subPath(path);
  1123. auto result = m_registry->MergeSettingsFile(subPath, AZ::SettingsRegistryInterface::Format::JsonMergePatch, {}, nullptr);
  1124. EXPECT_FALSE(result);
  1125. EXPECT_FALSE(result.GetMessages().empty());
  1126. }
  1127. TEST_F(SettingsRegistryTest, MergeSettingsFile_InvalidPath_ReturnsFalse)
  1128. {
  1129. auto result = m_registry->MergeSettingsFile("InvalidPath", AZ::SettingsRegistryInterface::Format::JsonMergePatch, {}, nullptr);
  1130. EXPECT_FALSE(result);
  1131. EXPECT_FALSE(result.GetMessages().empty());
  1132. }
  1133. TEST_F(SettingsRegistryTest, MergeSettingsFile_InvalidRootKey_ReturnsFalse)
  1134. {
  1135. auto path = AZ::Test::CreateTestFile(m_tempDirectory, "test.setreg", R"({ "Test": 1 })");
  1136. auto result = m_registry->MergeSettingsFile(path->Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, "$", nullptr);
  1137. EXPECT_FALSE(result);
  1138. EXPECT_FALSE(result.GetMessages().empty());
  1139. }
  1140. TEST_F(SettingsRegistryTest, MergeSettingsFile_ParseError_ReturnsFalse)
  1141. {
  1142. auto path = AZ::Test::CreateTestFile(m_tempDirectory, "test.setreg", "{ Test: 1 }");
  1143. auto result = m_registry->MergeSettingsFile(path->Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, {}, nullptr);
  1144. EXPECT_FALSE(result);
  1145. EXPECT_FALSE(result.GetMessages().empty());
  1146. }
  1147. TEST_F(SettingsRegistryTest, MergeSettingsFile_NonObjectRoot_EmptyAnchorKey_JsonMergePatch_ReturnsFalse)
  1148. {
  1149. auto path = AZ::Test::CreateTestFile(m_tempDirectory, "test.setreg", R"("BooleanValue": false)");
  1150. auto result = m_registry->MergeSettingsFile(path->Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, {});
  1151. EXPECT_FALSE(result);
  1152. EXPECT_FALSE(result.GetMessages().empty());
  1153. }
  1154. TEST_F(SettingsRegistryTest, MergeSettingsFile_NonObjectRoot_NonEmptyAnchorKey_JsonMergePatch_ReturnsTrue)
  1155. {
  1156. // Because the root key isn't empty the setting registry will not be be completely overridden and therefore
  1157. // it is safe to merge the .setreg file with a boolean element at the root
  1158. auto path = AZ::Test::CreateTestFile(m_tempDirectory, "test.setreg", R"("BooleanValue": false)");
  1159. auto result = m_registry->MergeSettingsFile(path->Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, "/Test");
  1160. EXPECT_TRUE(result);
  1161. }
  1162. TEST_F(SettingsRegistryTest, MergeSettingsFile_EmptyObjectRoot_EmptyAnchorKey_JsonMergePatch_PatchAppliedAndReported)
  1163. {
  1164. // In this scenario the object root is empty. The Merge should complete successfully
  1165. // but leave the settings registry unchanged.
  1166. auto path = AZ::Test::CreateTestFile(m_tempDirectory, "test.setreg", R"({})");
  1167. // Initialize the Settings Registry with an Object at /TestObject/IntValue
  1168. m_registry->MergeSettings(R"({ "TestObject": { "IntValue": 7 } })", AZ::SettingsRegistryInterface::Format::JsonMergePatch);
  1169. // Merging a file with a empty JSON Object should be effectively a no-op.
  1170. // There are some changes in the settings registry to record the merge history for introspection purposes.
  1171. auto outcome = m_registry->MergeSettingsFile(path->Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, {});
  1172. EXPECT_TRUE(outcome);
  1173. AZ::s64 intValue{};
  1174. EXPECT_TRUE(m_registry->Get(intValue, "/TestObject/IntValue"));
  1175. EXPECT_EQ(7, intValue);
  1176. }
  1177. TEST_F(SettingsRegistryTest, MergeSettingsFile_JsonWithImport_MergesImportedFiles_InOrder)
  1178. {
  1179. auto filePath = AZ::Test::CreateTestFile(m_tempDirectory, "Memory.phone.setreg", R"({ "pre_field": {"first": 7 },
  1180. "$import": "Memory.os.setreg", "non_override": 7, "post_field": {"2nd": "twenty-one"} })");
  1181. AZ::Test::CreateTestFile(m_tempDirectory, "Memory.os.setreg", R"({ "$import": { "filename": "Memory.mobile.setreg" },
  1182. "non_override2": "Hello World", "override_imported_settings": "override" })");
  1183. AZ::Test::CreateTestFile(m_tempDirectory, "Memory.mobile.setreg", R"({ "Memory": 1, "override_imported_settings": "initial",
  1184. "pre_field": {"first": 14, "second": "fourteen"}, "post_field": [2] })");
  1185. // anchor the settings to scalability
  1186. constexpr AZStd::string_view anchorKey = "/Test/Scalability";
  1187. auto result = m_registry->MergeSettingsFile(filePath->Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, anchorKey);
  1188. ASSERT_TRUE(result);
  1189. using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
  1190. AZ::s64 intValue{};
  1191. EXPECT_TRUE(m_registry->Get(intValue, FixedValueString(anchorKey) + "/non_override"));
  1192. EXPECT_EQ(7, intValue);
  1193. AZStd::string stringValue;
  1194. EXPECT_TRUE(m_registry->Get(stringValue, FixedValueString(anchorKey) + "/non_override2"));
  1195. EXPECT_EQ("Hello World", stringValue);
  1196. // Check the overridden imported settigns
  1197. stringValue.clear();
  1198. EXPECT_TRUE(m_registry->Get(stringValue, FixedValueString(anchorKey) + "/override_imported_settings"));
  1199. // As the Memory.os.setreg imports the Memory.mobile.setreg all of its settings
  1200. // should be merged afterwards and therefore overrides them
  1201. EXPECT_EQ("override", stringValue);
  1202. intValue = {};
  1203. EXPECT_TRUE(m_registry->Get(intValue, FixedValueString(anchorKey) + "/Memory"));
  1204. EXPECT_EQ(1, intValue);
  1205. // Check that the "pre_field" is object "first" field is overridden
  1206. // but the "post_field" overrides the import files value
  1207. // The "Memory.mobile.setreg" would merge over the "Memory.phone.setreg"
  1208. // because its import values appear afterwards
  1209. intValue = {};
  1210. EXPECT_TRUE(m_registry->Get(intValue, FixedValueString(anchorKey) + "/pre_field/first"));
  1211. EXPECT_EQ(14, intValue);
  1212. stringValue.clear();
  1213. EXPECT_TRUE(m_registry->Get(stringValue, FixedValueString(anchorKey) + "/pre_field/second"));
  1214. EXPECT_EQ("fourteen", stringValue);
  1215. stringValue.clear();
  1216. // The "Memory.phone.setreg" "post_field" should retain its value
  1217. // as that field appears after the import
  1218. EXPECT_TRUE(m_registry->Get(stringValue, FixedValueString(anchorKey) + "/post_field/2nd"));
  1219. EXPECT_EQ("twenty-one", stringValue);
  1220. }
  1221. TEST_F(SettingsRegistryTest, MergeSettingsFile_JsonWithImport_MultipleImports_MergesBasedOnOrder)
  1222. {
  1223. AZ::Test::CreateTestFile(m_tempDirectory, "number.setreg", R"({ "object_field": { "1": 23,
  1224. "2": 34 }})");
  1225. AZ::Test::CreateTestFile(m_tempDirectory, "string.setreg", R"({ "object_field": { "1": "Hello",
  1226. "3": "World" }})");
  1227. // anchor the settings underneath the Aggregate key
  1228. constexpr AZStd::string_view anchorKey = "/Test/Aggregate";
  1229. {
  1230. // Test by importing the files in order of number.setreg followed by
  1231. // string.setreg. In this scenario, the "object_field/1" from string.setreg
  1232. // should be the value in the Settings Registry
  1233. auto numberStringFilePath = AZ::Test::CreateTestFile(m_tempDirectory, "aggregate.setreg", R"({
  1234. "$import": "number.setreg",
  1235. "$import": "string.setreg" })");
  1236. auto result = m_registry->MergeSettingsFile(numberStringFilePath->Native(),
  1237. AZ::SettingsRegistryInterface::Format::JsonMergePatch, anchorKey);
  1238. ASSERT_TRUE(result);
  1239. using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
  1240. AZStd::string stringValue;
  1241. EXPECT_TRUE(m_registry->Get(stringValue, FixedValueString(anchorKey) + "/object_field/1"));
  1242. EXPECT_EQ("Hello", stringValue);
  1243. AZ::s64 intValue{};
  1244. EXPECT_TRUE(m_registry->Get(intValue, FixedValueString(anchorKey) + "/object_field/2"));
  1245. EXPECT_EQ(34, intValue);
  1246. stringValue.clear();
  1247. EXPECT_TRUE(m_registry->Get(stringValue, FixedValueString(anchorKey) + "/object_field/3"));
  1248. EXPECT_EQ("World", stringValue);
  1249. }
  1250. {
  1251. // Test by importing the files in order of string.setreg followed by
  1252. // number.setreg. In this scenario, the "object_field/1" from number.setreg
  1253. // should be the value in the Settings Registry
  1254. auto stringNumberFilePath = AZ::Test::CreateTestFile(m_tempDirectory, "aggregate.setreg", R"({
  1255. "$import": "string.setreg",
  1256. "$import": "number.setreg" })");
  1257. auto result = m_registry->MergeSettingsFile(stringNumberFilePath->Native(),
  1258. AZ::SettingsRegistryInterface::Format::JsonMergePatch, anchorKey);
  1259. ASSERT_TRUE(result);
  1260. using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
  1261. AZ::s64 intValue{};
  1262. EXPECT_TRUE(m_registry->Get(intValue, FixedValueString(anchorKey) + "/object_field/1"));
  1263. EXPECT_EQ(23, intValue);
  1264. intValue = {};
  1265. EXPECT_TRUE(m_registry->Get(intValue, FixedValueString(anchorKey) + "/object_field/2"));
  1266. EXPECT_EQ(34, intValue);
  1267. AZStd::string stringValue;
  1268. EXPECT_TRUE(m_registry->Get(stringValue, FixedValueString(anchorKey) + "/object_field/3"));
  1269. EXPECT_EQ("World", stringValue);
  1270. }
  1271. }
  1272. //
  1273. // MergeSettingsFolder
  1274. //
  1275. TEST_F(SettingsRegistryTest, MergeSettingsFolder_MergeTestFiles_FilesAppliedInSpecializationOrder)
  1276. {
  1277. AZ::Test::CreateTestFile(
  1278. m_tempDirectory,
  1279. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.setreg",
  1280. R"({ "Memory": 0, "MemoryRoot": true })");
  1281. AZ::Test::CreateTestFile(
  1282. m_tempDirectory,
  1283. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.editor.test.setreg",
  1284. R"({ "Memory": 3, "MemoryEditorTest": true })");
  1285. AZ::Test::CreateTestFile(
  1286. m_tempDirectory,
  1287. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.editor.setreg",
  1288. R"({ "Memory": 1, "MemoryEditor": true })");
  1289. AZ::Test::CreateTestFile(
  1290. m_tempDirectory,
  1291. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.test.setreg",
  1292. R"({ "Memory": 2, "MemoryTest": true })");
  1293. size_t counter = 0;
  1294. auto callback = [this, &counter](const AZ::SettingsRegistryInterface::NotifyEventArgs& notifyEventArgs)
  1295. {
  1296. const char* fileIds[] =
  1297. {
  1298. "/MemoryRoot",
  1299. "/MemoryEditor",
  1300. "/MemoryTest",
  1301. "/MemoryEditorTest"
  1302. };
  1303. MergeNotify(notifyEventArgs.m_jsonKeyPath, counter, AZ_ARRAY_SIZE(fileIds), "/Memory", fileIds);
  1304. counter++;
  1305. };
  1306. auto testNotifier1 = m_registry->RegisterNotifier(callback);
  1307. auto result = m_registry->MergeSettingsFolder((m_tempDirectory.GetDirectoryAsFixedMaxPath() / AZ::SettingsRegistryInterface::RegistryFolder).Native(), {"editor", "test"}, {});
  1308. EXPECT_TRUE(result);
  1309. EXPECT_EQ(4, counter);
  1310. }
  1311. TEST_F(SettingsRegistryTest, MergeSettingsFolder_WithPlatformFiles_FilesAppliedInSpecializationOrder)
  1312. {
  1313. AZ::Test::CreateTestFile(
  1314. m_tempDirectory,
  1315. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.setreg",
  1316. R"({ "Memory": 0, "MemoryRoot": true })");
  1317. AZ::Test::CreateTestFile(
  1318. m_tempDirectory,
  1319. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.editor.test.setreg",
  1320. R"({ "Memory": 5, "MemoryEditorTest": true })");
  1321. AZ::Test::CreateTestFile(
  1322. m_tempDirectory,
  1323. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.editor.setreg",
  1324. R"({ "Memory": 2, "MemoryEditor": true })");
  1325. AZ::Test::CreateTestFile(
  1326. m_tempDirectory,
  1327. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.test.setreg",
  1328. R"({ "Memory": 4, "MemoryTest": true })");
  1329. AZ::Test::CreateTestFile(
  1330. m_tempDirectory,
  1331. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Platform/Special/Memory.setreg",
  1332. R"({ "Memory": 1, "MemorySpecial": true })");
  1333. AZ::Test::CreateTestFile(
  1334. m_tempDirectory,
  1335. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Platform/Special/Memory.editor.setreg",
  1336. R"({ "Memory": 3, "MemoryEditorSpecial": true })");
  1337. size_t counter = 0;
  1338. auto callback = [this, &counter](const AZ::SettingsRegistryInterface::NotifyEventArgs& notifyEventArgs)
  1339. {
  1340. const char* fileIds[] =
  1341. {
  1342. "/MemoryRoot",
  1343. "/MemorySpecial",
  1344. "/MemoryEditor",
  1345. "/MemoryEditorSpecial",
  1346. "/MemoryTest",
  1347. "/MemoryEditorTest"
  1348. };
  1349. MergeNotify(notifyEventArgs.m_jsonKeyPath, counter, AZ_ARRAY_SIZE(fileIds), "/Memory", fileIds);
  1350. counter++;
  1351. };
  1352. auto testNotifier1 = m_registry->RegisterNotifier(callback);
  1353. auto result = m_registry->MergeSettingsFolder((m_tempDirectory.GetDirectoryAsFixedMaxPath() / AZ::SettingsRegistryInterface::RegistryFolder).Native(), { "editor", "test" }, "Special");
  1354. EXPECT_TRUE(result);
  1355. EXPECT_EQ(6, counter);
  1356. }
  1357. TEST_F(SettingsRegistryTest, MergeSettingsFolder_DifferentlyNamedFiles_FilesAppliedInAlphabeticAndSpecializationOrder)
  1358. {
  1359. AZ::Test::CreateTestFile(
  1360. m_tempDirectory, AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "c.setreg", R"({ "Id": 2, "c": true })");
  1361. AZ::Test::CreateTestFile(
  1362. m_tempDirectory,
  1363. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "a.editor.test.setreg",
  1364. R"({ "Id": 0, "a": true })");
  1365. AZ::Test::CreateTestFile(
  1366. m_tempDirectory,
  1367. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "b.editor.setreg",
  1368. R"({ "Id": 1, "b": true })");
  1369. AZ::Test::CreateTestFile(
  1370. m_tempDirectory,
  1371. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "c.test.setreg",
  1372. R"({ "Id": 3, "cTest": true })");
  1373. size_t counter = 0;
  1374. auto callback = [this, &counter](const AZ::SettingsRegistryInterface::NotifyEventArgs& notifyEventArgs)
  1375. {
  1376. const char* fileIds[] =
  1377. {
  1378. "/a",
  1379. "/b",
  1380. "/c",
  1381. "/cTest"
  1382. };
  1383. MergeNotify(notifyEventArgs.m_jsonKeyPath, counter, AZ_ARRAY_SIZE(fileIds), "/Id", fileIds);
  1384. counter++;
  1385. };
  1386. auto testNotifier1 = m_registry->RegisterNotifier(callback);
  1387. auto result = m_registry->MergeSettingsFolder((m_tempDirectory.GetDirectoryAsFixedMaxPath() / AZ::SettingsRegistryInterface::RegistryFolder).Native(), { "editor", "test" }, {});
  1388. EXPECT_TRUE(result);
  1389. EXPECT_EQ(4, counter);
  1390. }
  1391. TEST_F(SettingsRegistryTest, MergeSettingsFolder_JsonPatchFiles_FilesAppliedInAlphabeticAndSpecializationOrder)
  1392. {
  1393. AZ::Test::CreateTestFile(
  1394. m_tempDirectory,
  1395. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.setreg",
  1396. R"({ "Memory": 0, "MemoryRoot": true })");
  1397. AZ::Test::CreateTestFile(
  1398. m_tempDirectory,
  1399. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.editor.test.setreg",
  1400. R"({ "Memory": 3, "MemoryEditorTest": true })");
  1401. AZ::Test::CreateTestFile(
  1402. m_tempDirectory,
  1403. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.editor.setreg",
  1404. R"({ "Memory": 1, "MemoryEditor": true })");
  1405. AZ::Test::CreateTestFile(
  1406. m_tempDirectory, AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.test.setregpatch", R"(
  1407. [
  1408. { "op": "replace", "path": "/Memory", "value": 2 },
  1409. { "op": "add", "path": "/MemoryTest", "value": true }
  1410. ])");
  1411. size_t counter = 0;
  1412. auto callback = [this, &counter](const AZ::SettingsRegistryInterface::NotifyEventArgs& notifyEventArgs)
  1413. {
  1414. const char* fileIds[] =
  1415. {
  1416. "/MemoryRoot",
  1417. "/MemoryEditor",
  1418. "/MemoryTest",
  1419. "/MemoryEditorTest"
  1420. };
  1421. MergeNotify(notifyEventArgs.m_jsonKeyPath, counter, AZ_ARRAY_SIZE(fileIds), "/Memory", fileIds);
  1422. counter++;
  1423. };
  1424. auto testNotifier1 = m_registry->RegisterNotifier(callback);
  1425. auto result = m_registry->MergeSettingsFolder((m_tempDirectory.GetDirectoryAsFixedMaxPath() / AZ::SettingsRegistryInterface::RegistryFolder).Native(), { "editor", "test" }, {});
  1426. EXPECT_TRUE(result);
  1427. EXPECT_EQ(4, counter);
  1428. }
  1429. TEST_F(SettingsRegistryTest, MergeSettingsFolder_UnsupportedFilesAreIgnored_OneFileAppliedAndSuccessReturned)
  1430. {
  1431. AZ::Test::CreateTestFile(
  1432. m_tempDirectory,
  1433. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.setreg",
  1434. R"({ "Memory": 0, "MemoryRoot": true })");
  1435. AZ::Test::CreateTestFile(
  1436. m_tempDirectory,
  1437. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.wrong",
  1438. R"({ "Memory": 1, "Wrong": true })");
  1439. AZ::Test::CreateTestFile(
  1440. m_tempDirectory,
  1441. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Platform/Special/Memory.wrong",
  1442. R"({ "Memory": 2, "SpecialWrong": true })");
  1443. size_t counter = 0;
  1444. auto callback = [this, &counter](const AZ::SettingsRegistryInterface::NotifyEventArgs& notifyEventArgs)
  1445. {
  1446. const char* fileIds[] =
  1447. {
  1448. "/MemoryRoot"
  1449. };
  1450. MergeNotify(notifyEventArgs.m_jsonKeyPath, counter, AZ_ARRAY_SIZE(fileIds), "/Memory", fileIds);
  1451. counter++;
  1452. };
  1453. auto testNotifier1 = m_registry->RegisterNotifier(callback);
  1454. auto result = m_registry->MergeSettingsFolder((m_tempDirectory.GetDirectoryAsFixedMaxPath() / AZ::SettingsRegistryInterface::RegistryFolder).Native(), { "editor", "test" }, "Special");
  1455. EXPECT_TRUE(result);
  1456. EXPECT_EQ(1, counter);
  1457. }
  1458. TEST_F(SettingsRegistryTest, MergeSettingsFolder_WithScratchBuffer_FilesAppliedInSpecializationOrder)
  1459. {
  1460. AZ::Test::CreateTestFile(
  1461. m_tempDirectory,
  1462. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.setreg",
  1463. R"({ "Memory": 0, "MemoryRoot": true })");
  1464. AZ::Test::CreateTestFile(
  1465. m_tempDirectory,
  1466. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.editor.test.setreg",
  1467. R"({ "Memory": 3, "MemoryEditorTest": true })");
  1468. AZ::Test::CreateTestFile(
  1469. m_tempDirectory,
  1470. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.editor.setreg",
  1471. R"({ "Memory": 1, "MemoryEditor": true })");
  1472. AZ::Test::CreateTestFile(
  1473. m_tempDirectory,
  1474. AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.test.setreg",
  1475. R"({ "Memory": 2, "MemoryTest": true })");
  1476. size_t counter = 0;
  1477. auto callback = [this, &counter](const AZ::SettingsRegistryInterface::NotifyEventArgs& notifyEventArgs)
  1478. {
  1479. const char* fileIds[] =
  1480. {
  1481. "/MemoryRoot",
  1482. "/MemoryEditor",
  1483. "/MemoryTest",
  1484. "/MemoryEditorTest"
  1485. };
  1486. MergeNotify(notifyEventArgs.m_jsonKeyPath, counter, AZ_ARRAY_SIZE(fileIds), "/Memory", fileIds);
  1487. counter++;
  1488. };
  1489. auto testNotifier1 = m_registry->RegisterNotifier(callback);
  1490. AZStd::vector<char> buffer;
  1491. auto result = m_registry->MergeSettingsFolder((m_tempDirectory.GetDirectoryAsFixedMaxPath() / AZ::SettingsRegistryInterface::RegistryFolder).Native(), { "editor", "test" }, {}, "", &buffer);
  1492. EXPECT_TRUE(result);
  1493. EXPECT_EQ(4, counter);
  1494. }
  1495. TEST_F(SettingsRegistryTest, MergeSettingsFolder_EmptyFolder_ReportsSuccessButNothingAdded)
  1496. {
  1497. auto result = m_registry->MergeSettingsFolder(m_tempDirectory.GetDirectoryAsFixedMaxPath().Native(), { "editor", "test" }, {});
  1498. EXPECT_TRUE(result);
  1499. }
  1500. TEST_F(SettingsRegistryTest, MergeSettingsFolder_PathTooLong_ReportsErrorAndReturnsFalse)
  1501. {
  1502. constexpr AZStd::fixed_string<AZ::IO::MaxPathLength + 1> path(AZ::IO::MaxPathLength + 1, 'a');
  1503. auto result = m_registry->MergeSettingsFolder(path, { "editor", "test" }, {});
  1504. EXPECT_FALSE(result);
  1505. // The message structure should contain the error message
  1506. EXPECT_FALSE(result.GetMessages().empty());
  1507. }
  1508. TEST_F(SettingsRegistryTest, MergeSettingsFolder_ConflictingSpecializations_ReportsErrorAndReturnsFalse)
  1509. {
  1510. AZ::Test::CreateTestFile(
  1511. m_tempDirectory, AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.test.editor.setreg", "{}");
  1512. AZ::Test::CreateTestFile(
  1513. m_tempDirectory, AZ::IO::FixedMaxPath(AZ::SettingsRegistryInterface::RegistryFolder) / "Memory.editor.test.setreg", "{}");
  1514. auto result = m_registry->MergeSettingsFolder((m_tempDirectory.GetDirectoryAsFixedMaxPath() / AZ::SettingsRegistryInterface::RegistryFolder).Native(), { "editor", "test" }, {});
  1515. EXPECT_FALSE(result);
  1516. // The message structure should contain the error message
  1517. EXPECT_FALSE(result.GetMessages().empty());
  1518. }
  1519. } // namespace SettingsRegistryTests