QualitySystemComponentTests.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  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/Console/Console.h>
  10. #include <AzCore/Settings/SettingsRegistry.h>
  11. #include <AzCore/Settings/SettingsRegistryImpl.h>
  12. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  13. #include <AzCore/Serialization/SerializeContext.h>
  14. #include <AzCore/std/string/regex.h>
  15. #include <AzCore/UserSettings/UserSettingsComponent.h>
  16. #include <AzFramework/Application/Application.h>
  17. #include <AzFramework/Quality/QualitySystemComponent.h>
  18. #include <AzFramework/Quality/QualityCVarGroup.h>
  19. #include "DeviceAttribute/TestDeviceAttribute.h"
  20. namespace UnitTest
  21. {
  22. class QualitySystemComponentTestFixture : public LeakDetectionFixture
  23. {
  24. public:
  25. QualitySystemComponentTestFixture()
  26. : LeakDetectionFixture()
  27. {
  28. }
  29. protected:
  30. void SetUp() override
  31. {
  32. using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
  33. m_settingsRegistry = AZStd::make_unique<AZ::SettingsRegistryImpl>();
  34. AZ::SettingsRegistry::Register(m_settingsRegistry.get());
  35. m_application = AZStd::make_unique<AzFramework::Application>();
  36. // setup the runtime paths for the FileTagComponent
  37. auto projectPathKey = FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
  38. AZ::IO::FixedMaxPath enginePath;
  39. m_settingsRegistry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
  40. m_settingsRegistry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
  41. AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry);
  42. m_console = AZ::Interface<AZ::IConsole>::Get();
  43. }
  44. void TearDown() override
  45. {
  46. m_application->Stop();
  47. m_application.reset();
  48. AZ::SettingsRegistry::Unregister(m_settingsRegistry.get());
  49. m_settingsRegistry.reset();
  50. }
  51. void StartApplicationWithSettings(AZStd::string_view settings)
  52. {
  53. auto result = m_settingsRegistry->MergeSettings(settings,
  54. AZ::SettingsRegistryInterface::Format::JsonMergePatch,
  55. "");
  56. ASSERT_TRUE(result);
  57. // When the application starts and activates the QualitySystemComponent
  58. // it will register CVARS based on what is in the registry
  59. AZ::ComponentApplication::Descriptor desc;
  60. AZ::ComponentApplication::StartupParameters startupParameters;
  61. startupParameters.m_loadSettingsRegistry = false;
  62. startupParameters.m_loadAssetCatalog = false;
  63. m_application->Start(desc, startupParameters);
  64. // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
  65. // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
  66. // in the unit tests.
  67. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
  68. // Create and register a test device attribute for evaluating rules
  69. AZStd::string name { "TestAttribute" };
  70. AZStd::string description { "Description" };
  71. AZStd::string valueString{ "ValidAttribute" };
  72. AZStd::any value{ valueString };
  73. TestDeviceAttribute::EvalFunc eval = [valueString](AZStd::string_view rule)
  74. {
  75. auto regex = AZStd::regex(rule.data(), rule.size());
  76. return AZStd::regex_match(valueString, regex);
  77. };
  78. m_testDeviceAttribute = AZStd::make_shared<TestDeviceAttribute>(AZStd::move(name), AZStd::move(description), AZStd::move(value), AZStd::move(eval));
  79. auto registrar = AzFramework::DeviceAttributeRegistrar::Get();
  80. ASSERT_NE(nullptr, registrar);
  81. EXPECT_TRUE(registrar->RegisterDeviceAttribute(m_testDeviceAttribute));
  82. }
  83. AZ::IConsole* m_console;
  84. AZStd::unique_ptr<AzFramework::Application> m_application;
  85. AZStd::unique_ptr<AZ::SettingsRegistryInterface> m_settingsRegistry;
  86. AZStd::shared_ptr<TestDeviceAttribute> m_testDeviceAttribute;
  87. };
  88. TEST_F(QualitySystemComponentTestFixture, QualitySystem_Registers_Group_CVars)
  89. {
  90. // when the quality system component registers group cvars
  91. StartApplicationWithSettings(R"(
  92. {
  93. "O3DE": {
  94. "Quality": {
  95. "Groups": {
  96. "q_test": {
  97. "Levels": [ "low", "high" ],
  98. "Default": 1,
  99. "Description": "q_test quality group",
  100. "Settings": {
  101. "q_test_sub": [0,1]
  102. }
  103. },
  104. "q_test_sub": {
  105. "Levels": [ "low", "high" ],
  106. "Default": 0,
  107. "Description": "q_test_sub quality group",
  108. "Settings": {
  109. "a_cvar": [123,234]
  110. }
  111. }
  112. }
  113. }
  114. }
  115. }
  116. )");
  117. // expect the cvars are created with their default values
  118. auto value = AzFramework::QualityLevel::LevelFromDeviceRules;
  119. EXPECT_EQ(m_console->GetCvarValue("q_test", value), AZ::GetValueResult::Success);
  120. EXPECT_EQ(value, AzFramework::QualityLevel{1});
  121. EXPECT_EQ(m_console->GetCvarValue("q_test_sub", value), AZ::GetValueResult::Success);
  122. EXPECT_EQ(value, AzFramework::QualityLevel::DefaultQualityLevel);
  123. }
  124. AZ_CVAR(int32_t, a_setting, 0, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Example integer setting 1");
  125. AZ_CVAR(AZ::CVarFixedString, b_setting, "default", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Example string setting 2");
  126. AZ_CVAR(int32_t, c_setting, -1, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Example integer setting 3");
  127. AZ_CVAR(double, d_setting, -2.0, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Example double setting");
  128. TEST_F(QualitySystemComponentTestFixture, QualitySystem_Loads_Group_Level)
  129. {
  130. // when the quality system component registers group cvars
  131. StartApplicationWithSettings(R"(
  132. {
  133. "O3DE": {
  134. "Quality": {
  135. "DefaultGroup":"q_test",
  136. "Groups": {
  137. "q_test": {
  138. "Levels": [ "low", "medium", "high", "veryhigh"],
  139. "Default": 2,
  140. "Description": "q_test quality group",
  141. "Settings": {
  142. "a_setting": [0,1,2,3],
  143. "b_setting": ["a","b","c","d"],
  144. "q_test_sub": [0,1,1,1]
  145. }
  146. },
  147. "q_test_sub": {
  148. "Levels": [ "low", "high" ],
  149. "Default": "DefaultQualityLevel",
  150. "Description": "q_test_sub quality group",
  151. "Settings": {
  152. "c_setting": [123,234],
  153. "d_setting": [42.0] // test missing high level setting
  154. }
  155. }
  156. }
  157. }
  158. }
  159. }
  160. )");
  161. auto value = AzFramework::QualityLevel::LevelFromDeviceRules;
  162. int32_t intValue = -42;
  163. double doubleValue = -42.0;
  164. AZ::CVarFixedString stringValue;
  165. // expect the value defaults
  166. EXPECT_EQ(m_console->GetCvarValue("a_setting", intValue), AZ::GetValueResult::Success);
  167. EXPECT_EQ(intValue, 0);
  168. EXPECT_EQ(m_console->GetCvarValue("b_setting", stringValue), AZ::GetValueResult::Success);
  169. EXPECT_EQ(stringValue, "default");
  170. EXPECT_EQ(m_console->GetCvarValue("c_setting", intValue), AZ::GetValueResult::Success);
  171. EXPECT_EQ(intValue, -1);
  172. EXPECT_EQ(m_console->GetCvarValue("d_setting", doubleValue), AZ::GetValueResult::Success);
  173. EXPECT_EQ(doubleValue, -2.0);
  174. // when the default group is loaded
  175. AzFramework::QualitySystemEvents::Bus::Broadcast(
  176. &AzFramework::QualitySystemEvents::LoadDefaultQualityGroup,
  177. AzFramework::QualityLevel::LevelFromDeviceRules);
  178. // expect the values are set based on the default for q_test which is 2
  179. EXPECT_EQ(m_console->GetCvarValue("q_test", value), AZ::GetValueResult::Success);
  180. EXPECT_EQ(value, AzFramework::QualityLevel{2});
  181. EXPECT_EQ(m_console->GetCvarValue("a_setting", intValue), AZ::GetValueResult::Success);
  182. EXPECT_EQ(intValue, 2);
  183. EXPECT_EQ(m_console->GetCvarValue("b_setting", stringValue), AZ::GetValueResult::Success);
  184. EXPECT_EQ(stringValue, "c");
  185. EXPECT_EQ(m_console->GetCvarValue("q_test_sub", value), AZ::GetValueResult::Success);
  186. EXPECT_EQ(value, AzFramework::QualityLevel{1});
  187. EXPECT_EQ(m_console->GetCvarValue("c_setting", intValue), AZ::GetValueResult::Success);
  188. EXPECT_EQ(intValue, 234);
  189. EXPECT_EQ(m_console->GetCvarValue("d_setting", doubleValue), AZ::GetValueResult::Success);
  190. EXPECT_EQ(doubleValue, 42.0);
  191. // when the group level 1 is loaded ("medium" which is "high" for q_test_sub)
  192. m_console->PerformCommand("q_test", { "1" });
  193. // expect the values are set based on the group level settings
  194. EXPECT_EQ(m_console->GetCvarValue("a_setting", intValue), AZ::GetValueResult::Success);
  195. EXPECT_EQ(intValue, 1);
  196. EXPECT_EQ(m_console->GetCvarValue("b_setting", stringValue), AZ::GetValueResult::Success);
  197. EXPECT_EQ(stringValue, "b");
  198. EXPECT_EQ(m_console->GetCvarValue("q_test_sub", value), AZ::GetValueResult::Success);
  199. EXPECT_EQ(value, AzFramework::QualityLevel{1});
  200. EXPECT_EQ(m_console->GetCvarValue("c_setting", intValue), AZ::GetValueResult::Success);
  201. EXPECT_EQ(intValue, 234);
  202. // d_settings doesn't specify a value for "high" so it should use highest available setting
  203. // which is "low" -> 42
  204. EXPECT_EQ(m_console->GetCvarValue("d_setting", doubleValue), AZ::GetValueResult::Success);
  205. EXPECT_EQ(doubleValue, 42.0);
  206. // when the group level 0 is loaded (low) using mixed-case name
  207. m_console->PerformCommand("q_test", { "LoW" });
  208. // settings at index 0 are correctly loaded
  209. EXPECT_EQ(m_console->GetCvarValue("a_setting", intValue), AZ::GetValueResult::Success);
  210. EXPECT_EQ(intValue, 0);
  211. EXPECT_EQ(m_console->GetCvarValue("b_setting", stringValue), AZ::GetValueResult::Success);
  212. EXPECT_EQ(stringValue, "a");
  213. EXPECT_EQ(m_console->GetCvarValue("q_test_sub", value), AZ::GetValueResult::Success);
  214. EXPECT_EQ(value, AzFramework::QualityLevel{0});
  215. EXPECT_EQ(m_console->GetCvarValue("c_setting", intValue), AZ::GetValueResult::Success);
  216. EXPECT_EQ(intValue, 123);
  217. EXPECT_EQ(m_console->GetCvarValue("d_setting", doubleValue), AZ::GetValueResult::Success);
  218. EXPECT_EQ(doubleValue, 42.0);
  219. }
  220. TEST_F(QualitySystemComponentTestFixture, QualitySystem_Evaluates_Device_Rules)
  221. {
  222. // when the quality system component registers group cvars
  223. StartApplicationWithSettings(R"(
  224. {
  225. "O3DE": {
  226. "Quality": {
  227. "DefaultGroup":"q_test",
  228. "Groups": {
  229. "q_test": {
  230. "Levels": [ "low", "medium", "high", "veryhigh"],
  231. "Default": 2,
  232. "Description": "q_test quality group",
  233. "Settings": {
  234. "d_setting": [0.0,1.0,4.0,8.0],
  235. "q_test_sub": [0,1,1,1]
  236. }
  237. },
  238. "q_test_sub": {
  239. "Levels": [ "low", "high" ],
  240. "Default": "DefaultQualityLevel",
  241. "Description": "q_test_sub quality group",
  242. "Settings": {
  243. "c_setting": [123,234],
  244. "d_setting": [42.0]
  245. }
  246. }
  247. },
  248. "Devices": {
  249. // settings from these rule sections will be applied
  250. // based on device rule resolution
  251. "Rule Group 1": {
  252. "Rules": {
  253. "Test Device": { "TestAttribute": "ValidAttribute" }
  254. },
  255. "Settings": {
  256. "q_test": 1,
  257. "c_setting": 4,
  258. "d_setting": 1.0
  259. }
  260. },
  261. "Rule Group 2": {
  262. "Rules": {
  263. "Test Device": { "TestAttribute": "ValidAttribute" }
  264. },
  265. "Settings": {
  266. "q_test": 3,
  267. "c_setting": 1,
  268. "d_setting": 2.0
  269. }
  270. },
  271. // this group should not match
  272. "Invalid Rule Group": {
  273. "Rules": {
  274. "Test Device": { "TestAttribute": "InvalidAttribute" }
  275. },
  276. "Settings": {
  277. "q_test": 0,
  278. "c_setting": 0,
  279. "d_setting": 0.0
  280. }
  281. }
  282. }
  283. }
  284. }
  285. }
  286. )");
  287. // when the default group is loaded
  288. AzFramework::QualitySystemEvents::Bus::Broadcast(
  289. &AzFramework::QualitySystemEvents::LoadDefaultQualityGroup,
  290. AzFramework::QualityLevel::LevelFromDeviceRules);
  291. // expect the values are set based on the device rules and the default resolution order of "Last"
  292. auto cvarValue = AzFramework::QualityLevel::LevelFromDeviceRules;
  293. EXPECT_EQ(m_console->GetCvarValue("q_test", cvarValue), AZ::GetValueResult::Success);
  294. EXPECT_EQ(cvarValue, AzFramework::QualityLevel{3});
  295. int32_t intValue = -42;
  296. EXPECT_EQ(m_console->GetCvarValue("c_setting", intValue), AZ::GetValueResult::Success);
  297. EXPECT_EQ(intValue, 1);
  298. double doubleValue = -2.0;
  299. EXPECT_EQ(m_console->GetCvarValue("d_setting", doubleValue), AZ::GetValueResult::Success);
  300. EXPECT_EQ(doubleValue, 2.0);
  301. // when the rule resolution order is changed to First and default groups are loaded
  302. m_settingsRegistry.get()->SetObject("/O3DE/Quality/DeviceRulesResolution", AzFramework::DeviceRuleResolution::First);
  303. AzFramework::QualitySystemEvents::Bus::Broadcast(
  304. &AzFramework::QualitySystemEvents::LoadDefaultQualityGroup,
  305. AzFramework::QualityLevel::LevelFromDeviceRules);
  306. // expect the values are set based on the device rules and the resolution order of "First"
  307. EXPECT_EQ(m_console->GetCvarValue("q_test", cvarValue), AZ::GetValueResult::Success);
  308. EXPECT_EQ(cvarValue, AzFramework::QualityLevel{1});
  309. EXPECT_EQ(m_console->GetCvarValue("c_setting", intValue), AZ::GetValueResult::Success);
  310. EXPECT_EQ(intValue, 4);
  311. EXPECT_EQ(m_console->GetCvarValue("d_setting", doubleValue), AZ::GetValueResult::Success);
  312. EXPECT_EQ(doubleValue, 1.0);
  313. // when the rule resolution order is changed to Min and default groups are loaded
  314. m_settingsRegistry.get()->SetObject("/O3DE/Quality/DeviceRulesResolution", AzFramework::DeviceRuleResolution::Min);
  315. AzFramework::QualitySystemEvents::Bus::Broadcast(
  316. &AzFramework::QualitySystemEvents::LoadDefaultQualityGroup,
  317. AzFramework::QualityLevel::LevelFromDeviceRules);
  318. // expect the values are set based on the device rules and the default resolution rule of "Min"
  319. EXPECT_EQ(m_console->GetCvarValue("q_test", cvarValue), AZ::GetValueResult::Success);
  320. EXPECT_EQ(cvarValue, AzFramework::QualityLevel{1});
  321. EXPECT_EQ(m_console->GetCvarValue("c_setting", intValue), AZ::GetValueResult::Success);
  322. EXPECT_EQ(intValue, 1);
  323. EXPECT_EQ(m_console->GetCvarValue("d_setting", doubleValue), AZ::GetValueResult::Success);
  324. EXPECT_EQ(doubleValue, 1.0);
  325. // when the rule resolution order is changed to Max and default groups are loaded
  326. m_settingsRegistry.get()->SetObject("/O3DE/Quality/DeviceRulesResolution", AzFramework::DeviceRuleResolution::Max);
  327. AzFramework::QualitySystemEvents::Bus::Broadcast(
  328. &AzFramework::QualitySystemEvents::LoadDefaultQualityGroup,
  329. AzFramework::QualityLevel::LevelFromDeviceRules);
  330. // expect the values are set based on the device rules and the default resolution rule of "Max"
  331. EXPECT_EQ(m_console->GetCvarValue("q_test", cvarValue), AZ::GetValueResult::Success);
  332. EXPECT_EQ(cvarValue, AzFramework::QualityLevel{3});
  333. // IMPORTANT the value for c_setting in the second group is lower than the value for
  334. // c_setting in the first rule group so it is ignored and would have been 4 but
  335. // the value for q_test is higher in the second group and it is a group cvar which
  336. // will set c_setting to 234
  337. EXPECT_EQ(m_console->GetCvarValue("c_setting", intValue), AZ::GetValueResult::Success);
  338. EXPECT_EQ(intValue, 234);
  339. // d_setting will be 2.0 because the value in the second group is higher than the
  340. // value in the first group
  341. EXPECT_EQ(m_console->GetCvarValue("d_setting", doubleValue), AZ::GetValueResult::Success);
  342. EXPECT_EQ(doubleValue, 2.0);
  343. }
  344. } // namespace UnitTest