ConsoleTests.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  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/UnitTest/TestTypes.h>
  9. #include <AzCore/Interface/Interface.h>
  10. #include <AzCore/Console/Console.h>
  11. #include <AzCore/Settings/SettingsRegistryImpl.h>
  12. #include <AzCore/Utils/Utils.h>
  13. namespace AZ
  14. {
  15. using namespace UnitTest;
  16. AZ_CVAR(bool, testBool, false, nullptr, ConsoleFunctorFlags::Null, "");
  17. AZ_CVAR(char, testChar, 0, nullptr, ConsoleFunctorFlags::Null, "");
  18. AZ_CVAR(int8_t, testInt8, 0, nullptr, ConsoleFunctorFlags::Null, "");
  19. AZ_CVAR(int16_t, testInt16, 0, nullptr, ConsoleFunctorFlags::Null, "");
  20. AZ_CVAR(int32_t, testInt32, 0, nullptr, ConsoleFunctorFlags::Null, "");
  21. AZ_CVAR(int64_t, testInt64, 0, nullptr, ConsoleFunctorFlags::Null, "");
  22. AZ_CVAR(uint8_t, testUInt8, 0, nullptr, ConsoleFunctorFlags::Null, "");
  23. AZ_CVAR(uint16_t, testUInt16, 0, nullptr, ConsoleFunctorFlags::Null, "");
  24. AZ_CVAR(uint32_t, testUInt32, 0, nullptr, ConsoleFunctorFlags::Null, "");
  25. AZ_CVAR(uint64_t, testUInt64, 0, nullptr, ConsoleFunctorFlags::Null, "");
  26. AZ_CVAR(float, testFloat, 0, nullptr, ConsoleFunctorFlags::Null, "");
  27. AZ_CVAR(double, testDouble, 0, nullptr, ConsoleFunctorFlags::Null, "");
  28. AZ_CVAR(AZ::CVarFixedString, testString, "default", nullptr, ConsoleFunctorFlags::Null, "");
  29. AZ_CVAR(AZ::Vector2, testVec2, AZ::Vector2(0.0f), nullptr, ConsoleFunctorFlags::Null, "");
  30. AZ_CVAR(AZ::Vector3, testVec3, AZ::Vector3(0.0f), nullptr, ConsoleFunctorFlags::Null, "");
  31. AZ_CVAR(AZ::Vector4, testVec4, AZ::Vector4(0.0f), nullptr, ConsoleFunctorFlags::Null, "");
  32. AZ_CVAR(AZ::Quaternion, testQuat, AZ::Quaternion(0.0f), nullptr, ConsoleFunctorFlags::Null, "");
  33. AZ_CVAR(AZ::Color, testColorNormalized, AZ::Color(0.0f, 0.0f, 0.0f, 0.0f), nullptr, ConsoleFunctorFlags::Null, "");
  34. AZ_CVAR(AZ::Color, testColorNormalizedMixed, AZ::Color(0.0f, 0.0f, 0.0f, 0.0f), nullptr, ConsoleFunctorFlags::Null, "");
  35. AZ_CVAR(AZ::Color, testColorRgba, AZ::Color(0.0f, 0.0f, 0.0f, 0.0f), nullptr, ConsoleFunctorFlags::Null, "");
  36. // Creates an enum class with values that aren't consecutive(0, 5, 6)
  37. AZ_ENUM_CLASS(ConsoleTestEnum,
  38. Option1,
  39. (Option2, 5),
  40. Option3);
  41. AZ_CVAR(ConsoleTestEnum, testEnum, ConsoleTestEnum::Option1, nullptr, ConsoleFunctorFlags::Null,
  42. "Supports setting the ConsoleTestEnum via the string or integer argument."
  43. " Option1/0 sets the CVar to the Option1 value"
  44. ", Option2/5 sets the CVar to the Option2 value"
  45. ", Option3/6 sets the CVar to the Option3 value");
  46. // Helper function to disambiguate between various operator== implementations with not exactly matching argument types in c++20.
  47. template<typename T, typename S, typename = AZStd::enable_if_t<AZStd::is_integral_v<T> && AZStd::is_integral_v<S>>>
  48. bool operator==(T lhs, const AZ::ConsoleDataWrapper<S, ConsoleThreadSafety<S>>& rhs)
  49. {
  50. return lhs == static_cast<T>(rhs);
  51. }
  52. class ConsoleTests
  53. : public LeakDetectionFixture
  54. {
  55. public:
  56. void SetUp() override
  57. {
  58. m_console = AZStd::make_unique<AZ::Console>();
  59. m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
  60. AZ::Interface<AZ::IConsole>::Register(m_console.get());
  61. }
  62. void TearDown() override
  63. {
  64. AZ::Interface<AZ::IConsole>::Unregister(m_console.get());
  65. m_console = nullptr;
  66. }
  67. void TestClassFunc(const AZ::ConsoleCommandContainer& someStrings)
  68. {
  69. m_classFuncArgs = someStrings.size();
  70. }
  71. AZ_CONSOLEFUNC(ConsoleTests, TestClassFunc, AZ::ConsoleFunctorFlags::Null, "");
  72. size_t m_classFuncArgs = 0;
  73. inline static size_t s_consoleFreeFuncArgs = 0;
  74. AZStd::unique_ptr<AZ::Console> m_console;
  75. template <typename _CVAR_TYPE, typename _TYPE>
  76. void TestCVarHelper(_CVAR_TYPE& cvarInstance, const char* cVarName, const char* setCommand, const char* failCommand, _TYPE testInit, _TYPE initialValue, _TYPE setValue)
  77. {
  78. AZ::IConsole* console = m_console.get();
  79. _TYPE getCVarTest = testInit;
  80. ConsoleFunctorBase* foundCommand = console->FindCommand(cVarName);
  81. ASSERT_NE(nullptr, foundCommand); // Find command works and returns a non-null result
  82. EXPECT_STREQ(cVarName, foundCommand->GetName()); // Find command actually returned the correct command
  83. EXPECT_EQ(GetValueResult::Success, console->GetCvarValue(cVarName, getCVarTest)); // Console finds and retrieves cvar value
  84. EXPECT_EQ(initialValue, getCVarTest); // Retrieved cvar value
  85. console->PerformCommand(setCommand);
  86. EXPECT_EQ(setValue, _TYPE(cvarInstance)); // Set works for type
  87. EXPECT_EQ(GetValueResult::Success, console->GetCvarValue(cVarName, getCVarTest)); // Console finds and retrieves cvar value
  88. EXPECT_EQ(setValue, getCVarTest); // Retrieved cvar value
  89. if (failCommand != nullptr)
  90. {
  91. console->PerformCommand(failCommand);
  92. EXPECT_EQ(setValue, _TYPE(cvarInstance)); // Failed command did not affect cvar state
  93. }
  94. }
  95. };
  96. void TestFreeFunc(const AZ::ConsoleCommandContainer& someStrings)
  97. {
  98. ConsoleTests::s_consoleFreeFuncArgs = someStrings.size();
  99. }
  100. AZ_CONSOLEFREEFUNC(TestFreeFunc, AZ::ConsoleFunctorFlags::Null, "");
  101. TEST_F(ConsoleTests, CVar_GetSetTest_Bool)
  102. {
  103. testBool = false; // Reset testBool to false for scenarios where gtest_repeat is invoked
  104. TestCVarHelper(testBool, "testBool", "testBool true", "testBool asdf", false, false, true);
  105. }
  106. TEST_F(ConsoleTests, CVar_GetSetTest_Char)
  107. {
  108. testChar = {};
  109. TestCVarHelper(testChar, "testChar", "testChar 1", nullptr, char(100), char(0), '1'); // Char interprets the input as ascii, so if you print it, you get back a '1'
  110. }
  111. TEST_F(ConsoleTests, CVar_GetSetTest_Int8)
  112. {
  113. testInt8 = {};
  114. TestCVarHelper(testInt8, "testInt8", "testInt8 1", "testInt8 asdf", int8_t(100), int8_t(0), int8_t(1));
  115. }
  116. TEST_F(ConsoleTests, CVar_GetSetTest_Int16)
  117. {
  118. testInt16 = {};
  119. TestCVarHelper(testInt16, "testInt16", "testInt16 1", "testInt16 asdf", int16_t(100), int16_t(0), int16_t(1));
  120. }
  121. TEST_F(ConsoleTests, CVar_GetSetTest_Int32)
  122. {
  123. testInt32 = {};
  124. TestCVarHelper(testInt32, "testInt32", "testInt32 1", "testInt32 asdf", int32_t(100), int32_t(0), int32_t(1));
  125. }
  126. TEST_F(ConsoleTests, CVar_GetSetTest_Int64)
  127. {
  128. testInt64 = {};
  129. TestCVarHelper(testInt64, "testInt64", "testInt64 1", "testInt64 asdf", int64_t(100), int64_t(0), int64_t(1));
  130. }
  131. TEST_F(ConsoleTests, CVar_GetSetTest_UInt8)
  132. {
  133. testUInt8 = {};
  134. TestCVarHelper(testUInt8, "testUInt8", "testUInt8 1", "testUInt8 asdf", uint8_t(100), uint8_t(0), uint8_t(1));
  135. }
  136. TEST_F(ConsoleTests, CVar_GetSetTest_UInt16)
  137. {
  138. testUInt16 = {};
  139. TestCVarHelper(testUInt16, "testUInt16", "testUInt16 1", "testUInt16 asdf", uint16_t(100), uint16_t(0), uint16_t(1));
  140. }
  141. TEST_F(ConsoleTests, CVar_GetSetTest_UInt32)
  142. {
  143. testUInt32 = {};
  144. TestCVarHelper(testUInt32, "testUInt32", "testUInt32 1", "testUInt32 asdf", uint32_t(100), uint32_t(0), uint32_t(1));
  145. }
  146. TEST_F(ConsoleTests, CVar_GetSetTest_UInt64)
  147. {
  148. testUInt64 = {};
  149. TestCVarHelper(testUInt64, "testUInt64", "testUInt64 1", "testUInt64 asdf", uint64_t(100), uint64_t(0), uint64_t(1));
  150. }
  151. TEST_F(ConsoleTests, CVar_GetSetTest_Float)
  152. {
  153. testFloat = {};
  154. TestCVarHelper(testFloat, "testFloat", "testFloat 1", "testFloat asdf", float(100), float(0), float(1));
  155. }
  156. TEST_F(ConsoleTests, CVar_GetSetTest_Double)
  157. {
  158. testDouble = {};
  159. TestCVarHelper(testDouble, "testDouble", "testDouble 1", "testDouble asdf", double(100), double(0), double(1));
  160. }
  161. TEST_F(ConsoleTests, CVar_GetSetTest_String)
  162. {
  163. testString = "default";
  164. // There is really no failure condition for string, since even an empty string simply causes the console to echo the current cvar value
  165. TestCVarHelper(testString, "testString", "testString notdefault", nullptr, AZ::CVarFixedString("garbage"), AZ::CVarFixedString("default"), AZ::CVarFixedString("notdefault"));
  166. }
  167. TEST_F(ConsoleTests, CVar_GetSetTest_Vector2)
  168. {
  169. testVec2 = AZ::Vector2{ 0.0f, 0.0f };
  170. TestCVarHelper(testVec2, "testVec2", "testVec2 1 1", "testVec2 asdf", AZ::Vector2(100, 100), AZ::Vector2(0, 0), AZ::Vector2(1, 1));
  171. }
  172. TEST_F(ConsoleTests, CVar_GetSetTest_Vector3)
  173. {
  174. testVec3 = AZ::Vector3{ 0.0f, 0.0f, 0.0f };
  175. TestCVarHelper(testVec3, "testVec3", "testVec3 1 1 1", "testVec3 asdf", AZ::Vector3(100, 100, 100), AZ::Vector3(0, 0, 0), AZ::Vector3(1, 1, 1));
  176. }
  177. TEST_F(ConsoleTests, CVar_GetSetTest_Vector4)
  178. {
  179. testVec4 = AZ::Vector4{ 0.0f, 0.0f, 0.0f, 0.0f };
  180. TestCVarHelper(testVec4, "testVec4", "testVec4 1 1 1 1", "testVec4 asdf", AZ::Vector4(100, 100, 100, 100), AZ::Vector4(0, 0, 0, 0), AZ::Vector4(1, 1, 1, 1));
  181. }
  182. TEST_F(ConsoleTests, CVar_GetSetTest_Quaternion)
  183. {
  184. testQuat = AZ::Quaternion{ 0.0f, 0.0f, 0.0f, 0.0f };
  185. TestCVarHelper(testQuat, "testQuat", "testQuat 1 1 1 1", "testQuat asdf", AZ::Quaternion(100, 100, 100, 100), AZ::Quaternion(0, 0, 0, 0), AZ::Quaternion(1, 1, 1, 1));
  186. }
  187. TEST_F(ConsoleTests, CVar_GetSetTest_Color_Normalized)
  188. {
  189. testColorNormalized = AZ::Color{ 0.0f, 0.0f, 0.0f, 0.0f };
  190. TestCVarHelper(
  191. testColorNormalized, "testColorNormalized", "testColorNormalized 1.0 1.0 1.0 1.0", "testColorNormalized asdf",
  192. AZ::Color(0.5f, 0.5f, 0.5f, 0.5f), AZ::Color(0.0f, 0.0f, 0.0f, 0.0f), AZ::Color(1.0f, 1.0f, 1.0f, 1.0f));
  193. }
  194. TEST_F(ConsoleTests, CVar_GetSetTest_Color_Normalized_Mixed)
  195. {
  196. testColorNormalizedMixed = AZ::Color{ 0.0f, 0.0f, 0.0f, 0.0f };
  197. TestCVarHelper(
  198. testColorNormalizedMixed, "testColorNormalizedMixed", "testColorNormalizedMixed 1.0 0 1.0 1", "testColorNormalizedMixed asdf",
  199. AZ::Color(0.5f, 0.5f, 0.5f, 0.5f), AZ::Color(0.0f, 0.0f, 0.0f, 0.0f), AZ::Color(1.0f, 0.0f, 1.0f, 1.0f));
  200. }
  201. TEST_F(ConsoleTests, CVar_GetSetTest_Color_Rgba)
  202. {
  203. testColorRgba = AZ::Color{ 0.0f, 0.0f, 0.0f, 0.0f };
  204. TestCVarHelper(
  205. testColorRgba, "testColorRgba", "testColorRgba 255 255 255 255", "testColorRgba asdf",
  206. AZ::Color(0.5f, 0.5f, 0.5f, 0.5f), AZ::Color(0.0f, 0.0f, 0.0f, 0.0f), AZ::Color(1.0f, 1.0f, 1.0f, 1.0f));
  207. }
  208. TEST_F(ConsoleTests, CVar_GetSetTest_EnumType_SupportsNumericValue)
  209. {
  210. testEnum = ConsoleTestEnum::Option1;
  211. TestCVarHelper(
  212. testEnum, "testEnum", "testEnum 0", "testEnum RandomOption",
  213. static_cast<ConsoleTestEnum>(2), ConsoleTestEnum::Option1, ConsoleTestEnum::Option1);
  214. testEnum = ConsoleTestEnum::Option1;
  215. TestCVarHelper(
  216. testEnum, "testEnum", "testEnum 3", "testEnum RandomOption",
  217. static_cast<ConsoleTestEnum>(2), ConsoleTestEnum::Option1, static_cast<ConsoleTestEnum>(3));
  218. }
  219. TEST_F(ConsoleTests, CVar_GetSetTest_EnumType_SupportsEnumOptionString)
  220. {
  221. testEnum = ConsoleTestEnum::Option1;
  222. TestCVarHelper(
  223. testEnum, "testEnum", "testEnum Option2", "testEnum RandomOption",
  224. static_cast<ConsoleTestEnum>(2), ConsoleTestEnum::Option1, ConsoleTestEnum::Option2);
  225. testEnum = ConsoleTestEnum::Option1;
  226. TestCVarHelper(
  227. testEnum, "testEnum", "testEnum Option3", "testEnum RandomOption",
  228. static_cast<ConsoleTestEnum>(47), ConsoleTestEnum{}, ConsoleTestEnum::Option3);
  229. }
  230. TEST_F(ConsoleTests, CVar_ConstructAfterDeferredInit)
  231. {
  232. // This cvar only has scope within the function body, it will be added and removed on function entry and exit
  233. AZ_CVAR_SCOPED(int32_t, testInit, 0, nullptr, ConsoleFunctorFlags::Null, "");
  234. testInit = {};
  235. TestCVarHelper(testInit, "testInit", "testInit 1", "testInit asdf", int32_t(100), int32_t(0), int32_t(1));
  236. }
  237. TEST_F(ConsoleTests, CVar_GetSetTest_FormatConversion)
  238. {
  239. AZ::IConsole* console = m_console.get();
  240. // This simply tests format conversion
  241. float testValue = 0.0f;
  242. console->PerformCommand("testString 100.5f");
  243. AZ_TEST_ASSERT(console->GetCvarValue("testString", testValue) == GetValueResult::Success); // Console finds and retrieves cvar value
  244. AZ_TEST_ASSERT(testValue == 100.5f); // Retrieved cvar value
  245. console->PerformCommand("testString asdf");
  246. AZ_TEST_ASSERT(static_cast<AZ::CVarFixedString>(testString) == "asdf"); // String changed state
  247. AZ_TEST_ASSERT(console->GetCvarValue("testString", testValue) != GetValueResult::Success); // Console can't convert an arbitrary string to a float
  248. }
  249. TEST_F(ConsoleTests, CVar_Autocomplete)
  250. {
  251. AZ::IConsole* console = m_console.get();
  252. // Empty input
  253. {
  254. AZStd::string completeCommand = console->AutoCompleteCommand("");
  255. AZ_TEST_ASSERT(completeCommand == "");
  256. }
  257. // Prefix
  258. {
  259. AZStd::string completeCommand = console->AutoCompleteCommand("te");
  260. AZ_TEST_ASSERT(completeCommand == "test");
  261. }
  262. // Prefix
  263. {
  264. AZStd::string completeCommand = console->AutoCompleteCommand("testV");
  265. AZ_TEST_ASSERT(completeCommand == "testVec");
  266. }
  267. // Unique
  268. {
  269. AZStd::string completeCommand = console->AutoCompleteCommand("testQ");
  270. AZ_TEST_ASSERT(completeCommand == "testQuat");
  271. }
  272. // Complete
  273. {
  274. AZStd::string completeCommand = console->AutoCompleteCommand("testVec3");
  275. AZ_TEST_ASSERT(completeCommand == "testVec3");
  276. }
  277. // Duplicate names
  278. {
  279. // Register two cvars with the same name
  280. auto id = AZ::TypeId();
  281. auto flag = AZ::ConsoleFunctorFlags::Null;
  282. auto signature = AZ::ConsoleFunctor<void, false>::FunctorSignature();
  283. AZ::ConsoleFunctor<void, false> cvarOne(*console, "testAutoCompleteDuplication", "", flag, id, signature);
  284. AZ::ConsoleFunctor<void, false> cvarTwo(*console, "testAutoCompleteDuplication", "", flag, id, signature);
  285. // Autocomplete given name expecting one match (not two)
  286. AZStd::vector<AZStd::string> matches;
  287. AZStd::string completeCommand = console->AutoCompleteCommand("testAutoCompleteD", &matches);
  288. AZ_TEST_ASSERT(matches.size() == 1 && completeCommand == "testAutoCompleteDuplication");
  289. }
  290. }
  291. TEST_F(ConsoleTests, ConsoleFunctor_FreeFunctorExecutionTest)
  292. {
  293. AZ::IConsole* console = m_console.get();
  294. ASSERT_TRUE(console);
  295. // Verify that we can successfully execute a free-standing console functor.
  296. // The test functor puts the number of arguments into s_consoleFreeFuncArgs.
  297. s_consoleFreeFuncArgs = 0;
  298. bool result = static_cast<bool>(console->PerformCommand("TestFreeFunc arg1 arg2"));
  299. EXPECT_TRUE(result);
  300. EXPECT_EQ(2, s_consoleFreeFuncArgs);
  301. }
  302. TEST_F(ConsoleTests, ConsoleFunctor_ClassFunctorExecutionTest)
  303. {
  304. AZ::IConsole* console = m_console.get();
  305. ASSERT_TRUE(console);
  306. // Verify that we can successfully execute a class instance console functor.
  307. // The test functor puts the number of arguments into m_classFuncArgs.
  308. m_classFuncArgs = 0;
  309. bool result = static_cast<bool>(console->PerformCommand("ConsoleTests.TestClassFunc arg1 arg2"));
  310. EXPECT_TRUE(result);
  311. EXPECT_EQ(2, m_classFuncArgs);
  312. }
  313. TEST_F(ConsoleTests, ConsoleFunctor_MultiInstanceClassFunctorExecutionTest)
  314. {
  315. // Verify that if multiple instances of a class all register the same class method,
  316. // the method will get called on every instance of the class.
  317. class Example
  318. {
  319. public:
  320. void TestClassFunc(const AZ::ConsoleCommandContainer& someStrings)
  321. {
  322. m_classFuncArgs = someStrings.size();
  323. }
  324. AZ_CONSOLEFUNC(Example, TestClassFunc, AZ::ConsoleFunctorFlags::Null, "");
  325. size_t m_classFuncArgs = 0;
  326. };
  327. constexpr int numInstances = 5;
  328. Example multiInstances[numInstances];
  329. AZ::IConsole* console = AZ::Interface<AZ::IConsole>::Get();
  330. ASSERT_TRUE(console);
  331. bool result = static_cast<bool>(console->PerformCommand("Example.TestClassFunc arg1 arg2"));
  332. EXPECT_TRUE(result);
  333. for (auto& instance : multiInstances)
  334. {
  335. EXPECT_EQ(2, instance.m_classFuncArgs);
  336. }
  337. }
  338. }
  339. namespace ConsoleSettingsRegistryTests
  340. {
  341. //! ConfigFile MergeUtils Test
  342. struct ConfigFileParams
  343. {
  344. AZStd::string_view m_testConfigFileName;
  345. AZStd::string_view m_testConfigContents;
  346. };
  347. class ConsoleSettingsRegistryFixture
  348. : public UnitTest::LeakDetectionFixture
  349. , public ::testing::WithParamInterface<ConfigFileParams>
  350. {
  351. public:
  352. void SetUp() override
  353. {
  354. m_registry = AZStd::make_unique<AZ::SettingsRegistryImpl>();
  355. // Store off the old global settings registry to restore after each test
  356. m_oldSettingsRegistry = AZ::SettingsRegistry::Get();
  357. if (m_oldSettingsRegistry != nullptr)
  358. {
  359. AZ::SettingsRegistry::Unregister(m_oldSettingsRegistry);
  360. }
  361. AZ::SettingsRegistry::Register(m_registry.get());
  362. // Create a TestFile in the Test Directory
  363. auto configFileParams = GetParam();
  364. AZ::Test::CreateTestFile(m_tempDirectory, configFileParams.m_testConfigFileName, configFileParams.m_testConfigContents);
  365. }
  366. void TearDown() override
  367. {
  368. // Restore the old global settings registry
  369. AZ::SettingsRegistry::Unregister(m_registry.get());
  370. if (m_oldSettingsRegistry != nullptr)
  371. {
  372. AZ::SettingsRegistry::Register(m_oldSettingsRegistry);
  373. m_oldSettingsRegistry = {};
  374. }
  375. m_registry.reset();
  376. }
  377. void TestClassFunc(const AZ::ConsoleCommandContainer& someStrings)
  378. {
  379. m_stringArgCount = someStrings.size();
  380. }
  381. AZ_CONSOLEFUNC(ConsoleSettingsRegistryFixture, TestClassFunc, AZ::ConsoleFunctorFlags::Null, "");
  382. protected:
  383. size_t m_stringArgCount{};
  384. AZStd::unique_ptr<AZ::SettingsRegistryInterface> m_registry;
  385. AZ::Test::ScopedAutoTempDirectory m_tempDirectory;
  386. private:
  387. AZ::SettingsRegistryInterface* m_oldSettingsRegistry{};
  388. };
  389. static bool s_consoleFreeFunctionInvoked = false;
  390. static void TestSettingsRegistryFreeFunc(const AZ::ConsoleCommandContainer& someStrings)
  391. {
  392. EXPECT_TRUE(someStrings.empty());
  393. s_consoleFreeFunctionInvoked = true;
  394. }
  395. AZ_CONSOLEFREEFUNC(TestSettingsRegistryFreeFunc, AZ::ConsoleFunctorFlags::Null, "");
  396. TEST_P(ConsoleSettingsRegistryFixture, Console_AbleToLoadSettingsFile_Successfully)
  397. {
  398. AZ::Console testConsole(*m_registry);
  399. testConsole.LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
  400. AZ::Interface<AZ::IConsole>::Register(&testConsole);
  401. AZ_CVAR_SCOPED(int32_t, testInit, 0, nullptr, AZ::ConsoleFunctorFlags::Null, "");
  402. s_consoleFreeFunctionInvoked = false;
  403. testInit = {};
  404. AZ::testChar = {};
  405. AZ::testBool = {};
  406. AZ::testInt8 = {};
  407. AZ::testInt16 = {};
  408. AZ::testInt32 = {};
  409. AZ::testInt64 = {};
  410. AZ::testUInt8 = {};
  411. AZ::testUInt16 = {};
  412. AZ::testUInt32 = {};
  413. AZ::testUInt64 = {};
  414. AZ::testFloat= {};
  415. AZ::testDouble = {};
  416. AZ::testString = {};
  417. auto configFileParams = GetParam();
  418. auto testFilePath = m_tempDirectory.GetDirectoryAsFixedMaxPath() / configFileParams.m_testConfigFileName;
  419. EXPECT_TRUE(AZ::IO::SystemFile::Exists(testFilePath.c_str()));
  420. testConsole.ExecuteConfigFile(testFilePath.Native());
  421. EXPECT_TRUE(s_consoleFreeFunctionInvoked);
  422. EXPECT_EQ(3, testInit);
  423. EXPECT_TRUE(static_cast<bool>(AZ::testBool));
  424. EXPECT_EQ('Q', AZ::testChar);
  425. EXPECT_EQ(24, AZ::testInt8);
  426. EXPECT_EQ(-32, AZ::testInt16);
  427. EXPECT_EQ(41, AZ::testInt32);
  428. EXPECT_EQ(-51, AZ::testInt64);
  429. EXPECT_EQ(3, AZ::testUInt8);
  430. EXPECT_EQ(5, AZ::testUInt16);
  431. EXPECT_EQ(6, AZ::testUInt32);
  432. EXPECT_EQ(0xFFFF'FFFF'FFFF'FFFF, AZ::testUInt64);
  433. EXPECT_FLOAT_EQ(1.0f, AZ::testFloat);
  434. EXPECT_DOUBLE_EQ(2, AZ::testDouble);
  435. EXPECT_STREQ("Stable", static_cast<AZ::CVarFixedString>(AZ::testString).c_str());
  436. EXPECT_EQ(3, m_stringArgCount);
  437. AZ::Interface<AZ::IConsole>::Unregister(&testConsole);
  438. }
  439. template<typename T>
  440. using ConsoleDataWrapper = AZ::ConsoleDataWrapper<T, ConsoleThreadSafety<T>>;
  441. TEST_P(ConsoleSettingsRegistryFixture, Console_RecordsUnregisteredCommands_And_IsAbleToDeferDispatchCommand_Successfully)
  442. {
  443. AZ::Console testConsole(*m_registry);
  444. AZ::Interface<AZ::IConsole>::Register(&testConsole);
  445. // GetDeferredHead is invoked for the side effect of to set the s_deferredHeadInvoked value to true
  446. // This allows scoped console variables to be attached immediately
  447. [[maybe_unused]] auto deferredHead = AZ::ConsoleFunctorBase::GetDeferredHead();
  448. ConsoleDataWrapper<int32_t> localTestInit{ {}, nullptr, "testInit", "", AZ::ConsoleFunctorFlags::Null };
  449. ConsoleDataWrapper<char> localTestChar{ {}, nullptr, "testChar", "", AZ::ConsoleFunctorFlags::Null };
  450. ConsoleDataWrapper<bool> localTestBool{ {}, nullptr, "testBool", "", AZ::ConsoleFunctorFlags::Null };
  451. s_consoleFreeFunctionInvoked = false;
  452. // Invoke the Commands for Scoped CVar variables above
  453. auto configFileParams = GetParam();
  454. auto testFilePath = m_tempDirectory.GetDirectoryAsFixedMaxPath() / configFileParams.m_testConfigFileName;
  455. EXPECT_TRUE(AZ::IO::SystemFile::Exists(testFilePath.c_str()));
  456. testConsole.ExecuteConfigFile(testFilePath.Native());
  457. EXPECT_EQ(3, localTestInit);
  458. EXPECT_TRUE(static_cast<bool>(localTestBool));
  459. EXPECT_EQ('Q', localTestChar);
  460. // The following commands from the config files should have been deferred
  461. ConsoleDataWrapper<int8_t> localTestInt8{ {}, nullptr, "testInt8", "", AZ::ConsoleFunctorFlags::Null };
  462. ConsoleDataWrapper<int16_t> localTestInt16{ {}, nullptr, "testInt16", "", AZ::ConsoleFunctorFlags::Null };
  463. ConsoleDataWrapper<int32_t> localTestInt32{ {}, nullptr, "testInt32", "", AZ::ConsoleFunctorFlags::Null };
  464. ConsoleDataWrapper<int64_t> localTestInt64{ {}, nullptr, "testInt64", "", AZ::ConsoleFunctorFlags::Null };
  465. ConsoleDataWrapper<uint8_t> localTestUInt8{ {}, nullptr, "testUInt8", "", AZ::ConsoleFunctorFlags::Null };
  466. ConsoleDataWrapper<uint16_t> localTestUInt16{ {}, nullptr, "testUInt16", "", AZ::ConsoleFunctorFlags::Null };
  467. ConsoleDataWrapper<uint32_t> localTestUInt32{ {}, nullptr, "testUInt32", "", AZ::ConsoleFunctorFlags::Null };
  468. ConsoleDataWrapper<uint64_t> localTestUInt64{ {}, nullptr, "testUInt64", "", AZ::ConsoleFunctorFlags::Null };
  469. ConsoleDataWrapper<float> localTestFloat{ {}, nullptr, "testFloat", "", AZ::ConsoleFunctorFlags::Null };
  470. ConsoleDataWrapper<double> localTestDouble{ {}, nullptr, "testDouble", "", AZ::ConsoleFunctorFlags::Null };
  471. ConsoleDataWrapper<AZ::CVarFixedString> localTestString{ {}, nullptr, "testString", "", AZ::ConsoleFunctorFlags::Null };
  472. // The scoped cvars just above should have all been deferred for execution
  473. // Each of them should have executed resulting in the expected return value
  474. EXPECT_TRUE(testConsole.ExecuteDeferredConsoleCommands());
  475. EXPECT_EQ(24, localTestInt8);
  476. EXPECT_EQ(-32, localTestInt16);
  477. EXPECT_EQ(41, localTestInt32);
  478. EXPECT_EQ(-51, localTestInt64);
  479. EXPECT_EQ(3, localTestUInt8);
  480. EXPECT_EQ(5, localTestUInt16);
  481. EXPECT_EQ(6, localTestUInt32);
  482. EXPECT_EQ(0xFFFF'FFFF'FFFF'FFFF, localTestUInt64);
  483. EXPECT_FLOAT_EQ(1.0f, localTestFloat);
  484. EXPECT_DOUBLE_EQ(2, localTestDouble);
  485. EXPECT_STREQ("Stable", static_cast<AZ::CVarFixedString>(localTestString).c_str());
  486. // All of the deferred console commands should have executed at this point
  487. // Therefore this invocation should return false
  488. EXPECT_FALSE(testConsole.ExecuteDeferredConsoleCommands());
  489. AZ::Interface<AZ::IConsole>::Unregister(&testConsole);
  490. }
  491. static constexpr AZStd::string_view UserINIStyleContent =
  492. R"(
  493. testInit = 3
  494. testBool true
  495. testChar Q
  496. testInt8 24
  497. testInt16 -32
  498. testInt32 41
  499. testInt64 -51
  500. testUInt8 3
  501. testUInt16 5
  502. testUInt32 6
  503. testUInt64 18446744073709551615
  504. testFloat 1.0
  505. testDouble 2
  506. testString Stable
  507. ConsoleSettingsRegistryFixture.testClassFunc Foo Bar Baz
  508. TestSettingsRegistryFreeFunc
  509. )";
  510. static constexpr AZStd::string_view UserJsonMergePatchContent =
  511. R"(
  512. {
  513. "Amazon": {
  514. "AzCore": {
  515. "Runtime": {
  516. "ConsoleCommands": {
  517. "testInit": 3,
  518. "testBool": true,
  519. "testChar": "Q",
  520. "testInt8": 24,
  521. "testInt16": -32,
  522. "testInt32": 41,
  523. "testInt64": -51,
  524. "testUInt8": 3,
  525. "testUInt16": 5,
  526. "testUInt32": 6,
  527. "testUInt64": 18446744073709551615,
  528. "testFloat": 1.0,
  529. "testDouble": 2,
  530. "testString": "Stable",
  531. "ConsoleSettingsRegistryFixture.testClassFunc": "Foo Bar Baz",
  532. "TestSettingsRegistryFreeFunc": ""
  533. }
  534. }
  535. }
  536. }
  537. }
  538. )";
  539. static constexpr AZStd::string_view UserJsonPatchContent =
  540. R"(
  541. [
  542. { "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testInit", "value": 3 },
  543. { "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testBool", "value": true },
  544. { "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testChar", "value": "Q" },
  545. { "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testInt8", "value": 24 },
  546. { "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testInt16", "value": -32 },
  547. { "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testInt32", "value": 41 },
  548. { "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testInt64", "value": -51 },
  549. { "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testUInt8", "value": 3 },
  550. { "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testUInt16", "value": 5 },
  551. { "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testUInt32", "value": 6 },
  552. { "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testUInt64", "value": 18446744073709551615 },
  553. { "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testFloat", "value": 1.0 },
  554. { "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testDouble", "value": 2 },
  555. { "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testString", "value": "Stable" },
  556. { "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/ConsoleSettingsRegistryFixture.testClassFunc", "value": "Foo Bar Baz" },
  557. { "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/TestSettingsRegistryFreeFunc", "value": "" }
  558. ]
  559. )";
  560. INSTANTIATE_TEST_SUITE_P(
  561. ExecuteCommandFromSettingsFile,
  562. ConsoleSettingsRegistryFixture,
  563. ::testing::Values(
  564. ConfigFileParams{"user.cfg", UserINIStyleContent},
  565. ConfigFileParams{"user.setreg", UserJsonMergePatchContent},
  566. ConfigFileParams{"user.setregpatch", UserJsonPatchContent}
  567. )
  568. );
  569. }