ConfigParserTests.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  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/IO/ByteContainerStream.h>
  9. #include <AzCore/Settings/ConfigParser.h>
  10. #include <AzCore/std/string/conversions.h>
  11. #include <AzCore/std/containers/fixed_unordered_map.h>
  12. #include <AzCore/UnitTest/TestTypes.h>
  13. namespace UnitTest
  14. {
  15. struct ConfigFileParams
  16. {
  17. AZStd::string_view m_testConfigFileName;
  18. AZStd::string_view m_testConfigContents;
  19. using SettingsValueVariant = AZStd::variant<AZ::s64, bool, double, AZStd::string_view>;
  20. struct SettingsKeyValueTuple
  21. {
  22. AZStd::string_view m_key;
  23. SettingsValueVariant m_value;
  24. AZStd::string_view m_sectionHeader;
  25. };
  26. // The following test below will not have more than 32 settings in their config files
  27. AZStd::fixed_vector<SettingsKeyValueTuple, 20> m_expectedSettings;
  28. };
  29. class ConfigParserTestFixture
  30. : public LeakDetectionFixture
  31. {
  32. protected:
  33. AZStd::string m_configBuffer;
  34. AZ::IO::ByteContainerStream<AZStd::string> m_configStream{ &m_configBuffer };
  35. };
  36. // Parameterized test fixture for the ConfigFile Params
  37. class ConfigParserParamFixture
  38. : public ConfigParserTestFixture
  39. , public ::testing::WithParamInterface<ConfigFileParams>
  40. {
  41. void SetUp() override
  42. {
  43. auto configFileParam = GetParam();
  44. // Create the test config stream
  45. m_configStream.Write(configFileParam.m_testConfigContents.size(),
  46. configFileParam.m_testConfigContents.data());
  47. // Seek back to the beginning of the stream for test to read written data
  48. m_configStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
  49. }
  50. };
  51. TEST_F(ConfigParserTestFixture, ParseConfigFile_WithEmptyFunction_Fails)
  52. {
  53. AZ::Settings::ConfigParserSettings parserSettings{ AZ::Settings::ConfigParserSettings::ParseConfigEntryFunc{} };
  54. m_configBuffer = R"(project_path=/TestProject
  55. engine_path=/TestEngine
  56. )";
  57. EXPECT_FALSE(AZ::Settings::ParseConfigFile(m_configStream, parserSettings));
  58. }
  59. TEST_F(ConfigParserTestFixture, ParseConfigFile_WithParseConfigEntryFunctionWhichAlwaysReturnsFalse_Fails)
  60. {
  61. auto parseConfigEntry = [](const AZ::Settings::ConfigParserSettings::ConfigEntry&)
  62. {
  63. return false;
  64. };
  65. AZ::Settings::ConfigParserSettings parserSettings{ parseConfigEntry };
  66. m_configBuffer = R"(project_path=/TestProject
  67. engine_path=/TestEngine
  68. )";
  69. EXPECT_FALSE(AZ::Settings::ParseConfigFile(m_configStream, parserSettings));
  70. }
  71. TEST_F(ConfigParserTestFixture, ParseConfigFile_WithLineLargerThan4096_Fails)
  72. {
  73. auto parseConfigEntry = [](const AZ::Settings::ConfigParserSettings::ConfigEntry&)
  74. {
  75. return true;
  76. };
  77. AZ::Settings::ConfigParserSettings parserSettings{ parseConfigEntry };
  78. m_configBuffer = "project_path=/TestProject\n";
  79. // append a line longer than 4096 characters
  80. m_configBuffer += "foo=";
  81. m_configBuffer.append(4096, 'a');
  82. m_configBuffer += '\n';
  83. EXPECT_FALSE(AZ::Settings::ParseConfigFile(m_configStream, parserSettings));
  84. }
  85. TEST_F(ConfigParserTestFixture, ParseConfigFile_WithAllLinesSmallerThan4097_Succeeds)
  86. {
  87. auto parseConfigEntry = [](const AZ::Settings::ConfigParserSettings::ConfigEntry&)
  88. {
  89. return true;
  90. };
  91. AZ::Settings::ConfigParserSettings parserSettings{ parseConfigEntry };
  92. m_configBuffer = "project_path=/TestProject\n";
  93. // append only 4000 characters
  94. m_configBuffer += "foo=";
  95. m_configBuffer.append(4000, 'a');
  96. m_configBuffer += '\n';
  97. EXPECT_TRUE(AZ::Settings::ParseConfigFile(m_configStream, parserSettings));
  98. }
  99. TEST_P(ConfigParserParamFixture, ParseConfigFile_ParseContents_Successfully)
  100. {
  101. auto configFileParam = GetParam();
  102. // Parse Config File and write output to a map for testing
  103. struct TestConfigEntry
  104. {
  105. AZStd::fixed_string<128> m_value;
  106. AZStd::fixed_string<128> m_sectionHeader;
  107. };
  108. using ParseSettingsMap = AZStd::fixed_unordered_map<AZStd::fixed_string<128>, TestConfigEntry, 7, 32>;
  109. ParseSettingsMap parseSettingsMap;
  110. auto parseConfigEntry = [&parseSettingsMap](const AZ::Settings::ConfigParserSettings::ConfigEntry& configEntry)
  111. {
  112. AZStd::fixed_string<128> fullKey(configEntry.m_sectionHeader);
  113. if (!fullKey.empty())
  114. {
  115. fullKey += '/';
  116. }
  117. fullKey += configEntry.m_keyValuePair.m_key;
  118. parseSettingsMap.emplace(fullKey, TestConfigEntry{
  119. AZStd::fixed_string<128>(configEntry.m_keyValuePair.m_value),
  120. AZStd::fixed_string<128>(configEntry.m_sectionHeader) });
  121. return true;
  122. };
  123. AZ::Settings::ConfigParserSettings parserSettings{ parseConfigEntry };
  124. // Update the comment prefix function to also support `--` as a comment chaacter
  125. parserSettings.m_commentPrefixFunc = [](AZStd::string_view line) -> AZStd::string_view
  126. {
  127. constexpr AZStd::string_view commentPrefixes[]{ "--", ";","#" };
  128. for (AZStd::string_view commentPrefix : commentPrefixes)
  129. {
  130. if (size_t commentOffset = line.find(commentPrefix); commentOffset != AZStd::string_view::npos)
  131. {
  132. line = line.substr(0, commentOffset);
  133. }
  134. }
  135. return line;
  136. };
  137. auto parseOutcome = AZ::Settings::ParseConfigFile(m_configStream, parserSettings);
  138. EXPECT_TRUE(parseOutcome);
  139. // Validate that map contains expected settings
  140. for (auto&& expectedSettingPair : configFileParam.m_expectedSettings)
  141. {
  142. auto ValidateExpectedSettings = [&parseSettingsMap, settingsKey = expectedSettingPair.m_key,
  143. sectionHeader = expectedSettingPair.m_sectionHeader](auto&& settingsValue)
  144. {
  145. auto foundIt = parseSettingsMap.find(AZStd::fixed_string<128>(settingsKey));
  146. ASSERT_NE(parseSettingsMap.end(), foundIt);
  147. // Check the section header
  148. EXPECT_EQ(sectionHeader, foundIt->second.m_sectionHeader);
  149. // Check the value
  150. using SettingsValueType = AZStd::remove_cvref_t<decltype(settingsValue)>;
  151. if constexpr (AZStd::is_same_v<SettingsValueType, AZ::s64>
  152. || AZStd::is_same_v<SettingsValueType, AZ::u64>
  153. || AZStd::is_same_v<SettingsValueType, double>
  154. || AZStd::is_same_v<SettingsValueType, bool>)
  155. {
  156. AZStd::fixed_string<32> settingsValueAsString;
  157. AZStd::to_string(settingsValueAsString, settingsValue);
  158. EXPECT_EQ(AZStd::string_view(settingsValueAsString), foundIt->second.m_value);
  159. }
  160. else if constexpr (AZStd::is_same_v<SettingsValueType, AZStd::string_view>)
  161. {
  162. EXPECT_EQ(settingsValue, foundIt->second.m_value);
  163. }
  164. };
  165. AZStd::visit(ValidateExpectedSettings, expectedSettingPair.m_value);
  166. }
  167. }
  168. INSTANTIATE_TEST_CASE_P(
  169. ReadConfigFile,
  170. ConfigParserParamFixture,
  171. ::testing::Values(
  172. // Processes a fake bootstrap.cfg file which contains no section headers
  173. // and properly terminates the file with a newline
  174. ConfigFileParams{ "fake_bootstrap.cfg", R"(
  175. -- When you see an option that does not have a platform preceding it, that is the default
  176. -- value for anything not specifically set per platform. So if remote_filesystem=0 and you have
  177. -- ios_remote_file_system=1 then remote filesystem will be off for all platforms except ios
  178. -- Any of the settings in this file can be prefixed with a platform name:
  179. -- android, ios, mac, linux, windows, etc...
  180. -- or left unprefixed, to set all platforms not specified. The rules apply in the order they're declared
  181. project_path=TestProject
  182. -- remote_filesystem - enable Virtual File System (VFS)
  183. -- This feature allows a remote instance of the game to run off assets
  184. -- on the asset processor computers cache instead of deploying them the remote device
  185. -- By default it is off and can be overridden for any platform
  186. remote_filesystem=0
  187. android_remote_filesystem=0
  188. ios_remote_filesystem=0
  189. mac_remote_filesystem=0
  190. -- What type of assets are we going to load?
  191. -- We need to know this before we establish VFS because different platform assets
  192. -- are stored in different root folders in the cache. These correspond to the names
  193. -- In the asset processor config file. This value also controls what config file is read
  194. -- when you read system_xxxx_xxxx.cfg (for example, system_windows_pc.cfg or system_android_android.cfg)
  195. -- by default, pc assets (in the 'pc' folder) are used, with RC being fed 'pc' as the platform
  196. -- by default on console we use the default assets=pc for better iteration times
  197. -- we should turn on console specific assets only when in release and/or testing assets and/or loading performance
  198. -- that way most people will not need to have 3 different caches taking up disk space
  199. assets = pc
  200. android_assets = android
  201. ios_assets = ios
  202. mac_assets = mac
  203. -- Add the IP address of your console to the white list that will connect to the asset processor here
  204. -- You can list addresses or CIDR's. CIDR's are helpful if you are using DHCP. A CIDR looks like an ip address with
  205. -- a /n on the end means how many bits are significant. 8bits.8bits.8bits.8bits = /32
  206. -- Example: 192.168.1.3
  207. -- Example: 192.168.1.3, 192.168.1.15
  208. -- Example: 192.168.1.0/24 will allow any address starting with 192.168.1.
  209. -- Example: 192.168.0.0/16 will allow any address starting with 192.168.
  210. -- Example: 192.168.0.0/8 will allow any address starting with 192.
  211. -- allowed_list =
  212. -- IP address and optionally port of the asset processor.
  213. -- Set your PC IP here: (and uncomment the next line)
  214. -- If you are running your asset processor on a windows machine you
  215. -- can find out your ip address by opening a cmd prompt and typing in ipconfig
  216. -- remote_ip = 127.0.0.1
  217. -- remote_port = 45643
  218. -- Which way do you want to connect the asset processor to the game: 1=game connects to AP "connect", 0=AP connects to game "listen"
  219. -- Note: android and IOS over USB port forwarding may need to listen instead of connect
  220. connect_to_remote=0
  221. windows_connect_to_remote=1
  222. android_connect_to_remote=0
  223. ios_connect_to_remote=0
  224. mac_connect_to_remote=0
  225. -- Should we tell the game to wait and not proceed unless we have a connection to the AP or
  226. -- do we allow it to continue to try to connect in the background without waiting
  227. -- Note: Certain options REQUIRE that we do not proceed unless we have a connection, and will override this option to 1 when set
  228. -- Since remote_filesystem=1 requires a connection to proceed it will override our option to 1
  229. wait_for_connect=0
  230. windows_wait_for_connect=1
  231. android_wait_for_connect=0
  232. ios_wait_for_connect=0
  233. mac_wait_for_connect=0
  234. -- How long applications should wait while attempting to connect to an already launched AP(in seconds)
  235. -- connect_ap_timeout=3
  236. -- How long application should wait when launching the AP and wait for the AP to connect back to it(in seconds)
  237. -- This time is dependent on Machine load as well as how long it takes for the new AP instance to initialize
  238. -- A debug AP takes longer to start up than a profile AP
  239. -- launch_ap_timeout=15
  240. -- How long to wait for the AssetProcessor to be ready(i.e have all critical assets processed)
  241. -- wait_ap_ready_timeout = 1200
  242. # Commented out line using a number sign character
  243. ; Commented out line using a semicolon
  244. )"
  245. , AZStd::fixed_vector<ConfigFileParams::SettingsKeyValueTuple, 20>{
  246. ConfigFileParams::SettingsKeyValueTuple{"project_path", AZStd::string_view{"TestProject"}, {}},
  247. ConfigFileParams::SettingsKeyValueTuple{"remote_filesystem", AZ::s64{0}, {}},
  248. ConfigFileParams::SettingsKeyValueTuple{"android_remote_filesystem", AZ::s64{0}, {}},
  249. ConfigFileParams::SettingsKeyValueTuple{"ios_remote_filesystem", AZ::s64{0}, {}},
  250. ConfigFileParams::SettingsKeyValueTuple{"mac_remote_filesystem", AZ::s64{0}, {}},
  251. ConfigFileParams::SettingsKeyValueTuple{"assets", AZStd::string_view{"pc"}, {}},
  252. ConfigFileParams::SettingsKeyValueTuple{"android_assets", AZStd::string_view{"android"}, {}},
  253. ConfigFileParams::SettingsKeyValueTuple{"ios_assets", AZStd::string_view{"ios"}, {}},
  254. ConfigFileParams::SettingsKeyValueTuple{"mac_assets", AZStd::string_view{"mac"}, {}},
  255. ConfigFileParams::SettingsKeyValueTuple{"connect_to_remote", AZ::s64{0}, {}},
  256. ConfigFileParams::SettingsKeyValueTuple{"windows_connect_to_remote", AZ::s64{1}, {}},
  257. ConfigFileParams::SettingsKeyValueTuple{"android_connect_to_remote", AZ::s64{0}, {}},
  258. ConfigFileParams::SettingsKeyValueTuple{"ios_connect_to_remote", AZ::s64{0}, {}},
  259. ConfigFileParams::SettingsKeyValueTuple{"mac_connect_to_remote", AZ::s64{0}, {}},
  260. ConfigFileParams::SettingsKeyValueTuple{"wait_for_connect", AZ::s64{0}, {}},
  261. ConfigFileParams::SettingsKeyValueTuple{"windows_wait_for_connect", AZ::s64{1}, {}},
  262. ConfigFileParams::SettingsKeyValueTuple{"android_wait_for_connect", AZ::s64{0}, {}},
  263. ConfigFileParams::SettingsKeyValueTuple{"ios_wait_for_connect", AZ::s64{0}, {}},
  264. ConfigFileParams::SettingsKeyValueTuple{"mac_wait_for_connect", AZ::s64{0}, {}},
  265. }},
  266. // Parses a fake AssetProcessorPlatformConfig file which contains sections headers
  267. // and does not end with a newline
  268. ConfigFileParams{ "fake_AssetProcessorPlatformConfig.ini", R"(
  269. ; ---- Enable/Disable platforms for the entire project. AssetProcessor will automatically add the current platform by default.
  270. ; PLATFORM DEFINITIONS
  271. ; [Platform (unique identifier)]
  272. ; tags=(comma-seperated-tags)
  273. ;
  274. ; note: the 'identifier' of a platform is the word(s) following the "Platform" keyword (so [Platform pc] means identifier
  275. ; is 'pc' for example. This is used to name its assets folder in the cache and should be used in your bootstrap.cfg
  276. ; or your main.cpp to choose what assets to load for that particular platform.
  277. ; Its primary use is to enable additional non-host platforms (Ios, android...) that are not the current platform.
  278. ; note: 'tags' is a comma-seperated list of tags to tag the platform with that builders can inspect to decide what to do.
  279. ; while builders can accept any tags you add in order to make decisions, common tags are
  280. ; tools - this platform can host the tools and editor and such
  281. ; renderer - this platform runs the client engine and renders on a GPU. If missing we could be on a server-only platform
  282. ; mobile - a mobile platform such as a set top box or phone with limited resources
  283. ; console - a console platform
  284. ; server - a server platform of some kind, usually headless, no renderer.
  285. test_asset_processor_tag = test_value
  286. [Platform pc]
  287. tags=tools,renderer,dx12,vulkan
  288. [Platform android]
  289. tags=android,mobile,renderer,vulkan ; With Comments at the end
  290. ; validate leading and trailing whitespace is not parsed when examining the section header
  291. [Platform ios]
  292. tags=mobile,renderer,metal
  293. [Platform mac]
  294. tags=tools,renderer,metal)"
  295. , AZStd::fixed_vector<ConfigFileParams::SettingsKeyValueTuple, 20>{
  296. ConfigFileParams::SettingsKeyValueTuple{"test_asset_processor_tag", AZStd::string_view{"test_value"}, {}},
  297. ConfigFileParams::SettingsKeyValueTuple{"Platform pc/tags", AZStd::string_view{"tools,renderer,dx12,vulkan"}, "Platform pc"},
  298. ConfigFileParams::SettingsKeyValueTuple{"Platform android/tags", AZStd::string_view{"android,mobile,renderer,vulkan"}, "Platform android"},
  299. ConfigFileParams::SettingsKeyValueTuple{"Platform ios/tags", AZStd::string_view{"mobile,renderer,metal"}, "Platform ios"},
  300. ConfigFileParams::SettingsKeyValueTuple{"Platform mac/tags", AZStd::string_view{"tools,renderer,metal"}, "Platform mac"},
  301. }}
  302. )
  303. );
  304. }