StringFunc.cpp 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076
  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 <gtest/gtest-param-test.h>
  9. #include <AzCore/UnitTest/TestTypes.h>
  10. #include <AzCore/StringFunc/StringFunc.h>
  11. #include <string.h>
  12. namespace AZ
  13. {
  14. using namespace UnitTest;
  15. using StringFuncTest = LeakDetectionFixture;
  16. TEST_F(StringFuncTest, Equal_CaseSensitive_OnNonNullTerminatedStringView_Success)
  17. {
  18. constexpr AZStd::string_view testString1 = "Hello World IceCream";
  19. constexpr AZStd::string_view testString2 = "IceCream World Hello";
  20. constexpr bool caseSensitive = true;
  21. EXPECT_TRUE(AZ::StringFunc::Equal(testString1.substr(6,5), testString2.substr(9,5), caseSensitive));
  22. }
  23. TEST_F(StringFuncTest, Equal_CaseInsensitive_OnNonNullTerminatedStringView_Success)
  24. {
  25. constexpr AZStd::string_view testString1 = "Hello World IceCream";
  26. constexpr AZStd::string_view testString2 = "IceCream woRLd Hello";
  27. constexpr bool caseSensitive = false;
  28. EXPECT_TRUE(AZ::StringFunc::Equal(testString1.substr(6, 5), testString2.substr(9, 5), caseSensitive));
  29. }
  30. TEST_F(StringFuncTest, Equal_CaseSensitive_OnNonNullTerminatedStringView_WithDifferentCases_Fails)
  31. {
  32. constexpr AZStd::string_view testString1 = "Hello World IceCream";
  33. constexpr AZStd::string_view testString2 = "IceCream woRLd Hello";
  34. constexpr bool caseSensitive = true;
  35. EXPECT_FALSE(AZ::StringFunc::Equal(testString1.substr(6, 5), testString2.substr(9, 5), caseSensitive));
  36. }
  37. TEST_F(StringFuncTest, Equal_OnNonNullTerminatedStringView_WithDifferentSize_Fails)
  38. {
  39. constexpr AZStd::string_view testString1 = "Hello World IceCream";
  40. constexpr AZStd::string_view testString2 = "IceCream World Hello";
  41. EXPECT_FALSE(AZ::StringFunc::Equal(testString1.substr(6, 6), testString2.substr(9, 5)));
  42. }
  43. // Strip out any trailing path separators
  44. TEST_F(StringFuncTest, Strip_ValidInputExtraEndingPathSeparators_Success)
  45. {
  46. AZStd::string samplePath = "F:\\w s 1\\dev\\/";
  47. AZStd::string expectedResult = "F:\\w s 1\\dev";
  48. const char* stripCharacters = AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING AZ_WRONG_FILESYSTEM_SEPARATOR_STRING;
  49. AZ::StringFunc::Strip(samplePath, stripCharacters, false, false, true);
  50. ASSERT_TRUE(samplePath == expectedResult);
  51. }
  52. TEST_F(StringFuncTest, Strip_AllEmptyResult1_Success)
  53. {
  54. AZStd::string input = "aa";
  55. const char stripToken = 'a';
  56. AZ::StringFunc::Strip(input, stripToken);
  57. ASSERT_TRUE(input.empty());
  58. }
  59. TEST_F(StringFuncTest, Strip_AllEmptyResult2_Success)
  60. {
  61. AZStd::string input = "aaaa";
  62. const char stripToken = 'a';
  63. AZ::StringFunc::Strip(input, stripToken);
  64. ASSERT_TRUE(input.empty());
  65. }
  66. TEST_F(StringFuncTest, Strip_BeginEndCaseSensitiveEmptyResult1_Success)
  67. {
  68. AZStd::string input = "aa";
  69. const char stripToken = 'a';
  70. AZ::StringFunc::Strip(input, stripToken, true, true, true);
  71. ASSERT_TRUE(input.empty());
  72. }
  73. TEST_F(StringFuncTest, Strip_BeginEndCaseSensitiveEmptyResult2_Success)
  74. {
  75. AZStd::string input = "aaaa";
  76. AZStd::string expectedResult = "aa";
  77. const char stripToken = 'a';
  78. AZ::StringFunc::Strip(input, stripToken, true, true, true);
  79. ASSERT_EQ(input, expectedResult);
  80. }
  81. TEST_F(StringFuncTest, Strip_BeginEndCaseInsensitiveEmptyResult1_Success)
  82. {
  83. AZStd::string input = "aa";
  84. const char stripToken = 'a';
  85. AZ::StringFunc::Strip(input, stripToken, false, true, true);
  86. ASSERT_TRUE(input.empty());
  87. }
  88. TEST_F(StringFuncTest, Strip_BeginEndCaseInsensitiveEmptyResult2_Success)
  89. {
  90. AZStd::string input = "aaaa";
  91. AZStd::string expectedResult = "aa";
  92. const char stripToken = 'a';
  93. AZ::StringFunc::Strip(input, stripToken, false, true, true);
  94. ASSERT_EQ(input, expectedResult);
  95. }
  96. TEST_F(StringFuncTest, Join_PathsWithNoOverlappingDirectoriesDisabled_Success)
  97. {
  98. AZStd::string path1 = "1/2/3/4";
  99. AZStd::string path2 = "5/6";
  100. AZStd::string expectedResult = "1/2/3/4/5/6";
  101. AZStd::string joinResult;
  102. AZ::StringFunc::Path::Normalize(path1);
  103. AZ::StringFunc::Path::Normalize(path2);
  104. AZ::StringFunc::Path::Normalize(expectedResult);
  105. AZ::StringFunc::Path::Join(path1.c_str(), path2.c_str(), joinResult);
  106. ASSERT_EQ(joinResult, expectedResult);
  107. }
  108. TEST_F(StringFuncTest, Join_PathsWithOverlappingDirectoriesDisabled_Success)
  109. {
  110. AZStd::string path1 = "1/2/3/4";
  111. AZStd::string path2 = "3/4/5/6";
  112. AZStd::string expectedResult = "1/2/3/4/3/4/5/6";
  113. AZStd::string joinResult;
  114. AZ::StringFunc::Path::Normalize(path1);
  115. AZ::StringFunc::Path::Normalize(path2);
  116. AZ::StringFunc::Path::Normalize(expectedResult);
  117. AZ::StringFunc::Path::Join(path1.c_str(), path2.c_str(), joinResult);
  118. ASSERT_EQ(joinResult, expectedResult);
  119. }
  120. TEST_F(StringFuncTest, Join_PathsWithNoOverlappingDirectories_Success)
  121. {
  122. AZStd::string path1 = "1/2/3/4";
  123. AZStd::string path2 = "5/6";
  124. AZStd::string expectedResult = "1/2/3/4/5/6";
  125. AZStd::string joinResult;
  126. AZ::StringFunc::Path::Normalize(path1);
  127. AZ::StringFunc::Path::Normalize(path2);
  128. AZ::StringFunc::Path::Normalize(expectedResult);
  129. AZ::StringFunc::Path::Join(path1.c_str(), path2.c_str(), joinResult);
  130. ASSERT_EQ(joinResult, expectedResult);
  131. }
  132. TEST_F(StringFuncTest, Join_PathsWithOverlappingDirectories_Success)
  133. {
  134. AZStd::string path1 = "1/2/3/4";
  135. AZStd::string path2 = "3/4/5/6";
  136. AZStd::string expectedResult = "1/2/3/4/3/4/5/6";
  137. AZStd::string joinResult;
  138. AZ::StringFunc::Path::Normalize(path1);
  139. AZ::StringFunc::Path::Normalize(path2);
  140. AZ::StringFunc::Path::Normalize(expectedResult);
  141. AZ::StringFunc::Path::Join(path1.c_str(), path2.c_str(), joinResult);
  142. ASSERT_EQ(joinResult, expectedResult);
  143. }
  144. TEST_F(StringFuncTest, Join_PathsWithSecondNameOverlappingDirectory_Success)
  145. {
  146. AZStd::string path1 = "1/2/3/4";
  147. AZStd::string path2 = "3";
  148. AZStd::string expectedResult = "1/2/3/4/3";
  149. AZStd::string joinResult;
  150. AZ::StringFunc::Path::Normalize(path1);
  151. AZ::StringFunc::Path::Normalize(path2);
  152. AZ::StringFunc::Path::Normalize(expectedResult);
  153. AZ::StringFunc::Path::Join(path1.c_str(), path2.c_str(), joinResult);
  154. ASSERT_EQ(joinResult, expectedResult);
  155. }
  156. TEST_F(StringFuncTest, Join_NonPathJoin_CanJoinRange)
  157. {
  158. AZStd::string result;
  159. AZ::StringFunc::Join(result, AZStd::initializer_list<const char*>{ "1", "2", "3", "4", "3" }, '/');
  160. EXPECT_EQ("1/2/3/4/3", result);
  161. result.clear();
  162. // Try joining with a string literal instead of a char literal
  163. AZ::StringFunc::Join(result, AZStd::initializer_list<const char*>{ "1", "2", "3", "4", "3" }, "/");
  164. EXPECT_EQ("1/2/3/4/3", result);
  165. }
  166. TEST_F(StringFuncTest, Tokenize_SingleDelimeter_Empty)
  167. {
  168. AZStd::string input = "";
  169. AZStd::vector<AZStd::string> tokens;
  170. AZ::StringFunc::Tokenize(input.c_str(), tokens, ' ');
  171. ASSERT_EQ(tokens.size(), 0);
  172. }
  173. TEST_F(StringFuncTest, Tokenize_SingleDelimeter)
  174. {
  175. AZStd::string input = "a b,c";
  176. AZStd::vector<AZStd::string> tokens;
  177. AZ::StringFunc::Tokenize(input.c_str(), tokens, ' ');
  178. ASSERT_EQ(tokens.size(), 2);
  179. ASSERT_TRUE(tokens[0] == "a");
  180. ASSERT_TRUE(tokens[1] == "b,c");
  181. }
  182. TEST_F(StringFuncTest, Tokenize_MultiDelimeter_Empty)
  183. {
  184. AZStd::string input = "";
  185. AZStd::vector<AZStd::string> tokens;
  186. AZ::StringFunc::Tokenize(input.c_str(), tokens, " ,");
  187. ASSERT_EQ(tokens.size(), 0);
  188. }
  189. TEST_F(StringFuncTest, Tokenize_MultiDelimeters)
  190. {
  191. AZStd::string input = " -a +b +c -d-e";
  192. AZStd::vector<AZStd::string> tokens;
  193. AZ::StringFunc::Tokenize(input.c_str(), tokens, "-+");
  194. ASSERT_EQ(tokens.size(), 5);
  195. ASSERT_TRUE(tokens[0] == "a ");
  196. ASSERT_TRUE(tokens[1] == "b ");
  197. ASSERT_TRUE(tokens[2] == "c ");
  198. ASSERT_TRUE(tokens[3] == "d");
  199. ASSERT_TRUE(tokens[4] == "e");
  200. }
  201. TEST_F(StringFuncTest, Tokenize_SubstringDelimeters_Empty)
  202. {
  203. AZStd::string input = "";
  204. AZStd::vector<AZStd::string> tokens;
  205. AZStd::vector<AZStd::string_view> delimeters = {" -", " +"};
  206. AZ::StringFunc::Tokenize(input.c_str(), tokens, delimeters);
  207. ASSERT_EQ(tokens.size(), 0);
  208. }
  209. TEST_F(StringFuncTest, Tokenize_SubstringDelimeters)
  210. {
  211. AZStd::string input = " -a +b +c -d-e";
  212. AZStd::vector<AZStd::string> tokens;
  213. AZStd::vector<AZStd::string_view> delimeters = { " -", " +" };
  214. AZ::StringFunc::Tokenize(input.c_str(), tokens, delimeters);
  215. ASSERT_EQ(tokens.size(), 4);
  216. ASSERT_TRUE(tokens[0] == "a");
  217. ASSERT_TRUE(tokens[1] == "b");
  218. ASSERT_TRUE(tokens[2] == "c");
  219. ASSERT_TRUE(tokens[3] == "d-e"); // Test for something like a guid, which contain typical separator characters
  220. }
  221. TEST_F(StringFuncTest, TokenizeVisitor_EmptyString_DoesNotInvokeVisitor)
  222. {
  223. int visitedCount{};
  224. auto visitor = [&visitedCount](AZStd::string_view)
  225. {
  226. ++visitedCount;
  227. };
  228. AZ::StringFunc::TokenizeVisitor("", visitor, " ");
  229. EXPECT_EQ(0, visitedCount);
  230. }
  231. TEST_F(StringFuncTest, TokenizeVisitor_NonDelimitedString_InvokeVisitorOnce)
  232. {
  233. int visitedCount{};
  234. auto visitor = [&visitedCount](AZStd::string_view)
  235. {
  236. ++visitedCount;
  237. };
  238. AZ::StringFunc::TokenizeVisitor("Hello", visitor, " ");
  239. EXPECT_EQ(1, visitedCount);
  240. }
  241. TEST_F(StringFuncTest, TokenizeVisitor_DelimitedString_InvokeVisitorMultipleTimes)
  242. {
  243. int visitedCount{};
  244. auto visitor = [&visitedCount](AZStd::string_view)
  245. {
  246. ++visitedCount;
  247. };
  248. AZ::StringFunc::TokenizeVisitor("Hello World", visitor, " ");
  249. EXPECT_EQ(2, visitedCount);
  250. visitedCount = {};
  251. AZ::StringFunc::TokenizeVisitor("Hello World Again", visitor, " ");
  252. EXPECT_EQ(3, visitedCount);
  253. }
  254. TEST_F(StringFuncTest, TokenizeVisitor_EmptyStringOption_InvokeVisitorWithEmptyString)
  255. {
  256. int visitedCount{};
  257. bool emptyStringFound{};
  258. auto visitor = [&visitedCount, &emptyStringFound](AZStd::string_view token)
  259. {
  260. ++visitedCount;
  261. if (token.empty())
  262. {
  263. emptyStringFound = true;
  264. }
  265. };
  266. AZ::StringFunc::TokenizeVisitor("Hello,,World", visitor, ",", true);
  267. EXPECT_EQ(3, visitedCount);
  268. EXPECT_TRUE(emptyStringFound);
  269. visitedCount = {};
  270. emptyStringFound = {};
  271. AZ::StringFunc::TokenizeVisitor(",Hello,World", visitor, ",", true);
  272. EXPECT_EQ(3, visitedCount);
  273. EXPECT_TRUE(emptyStringFound);
  274. }
  275. TEST_F(StringFuncTest, TokenizeVisitor_WhiteSpaceOption_InvokeVisitorWithWhiteSpaceAroundTokenString)
  276. {
  277. int visitedCount{};
  278. bool trailingWhitespaceTokenFound{};
  279. bool leadingWhitespaceTokenFound{};
  280. auto visitor = [&visitedCount, &trailingWhitespaceTokenFound, &leadingWhitespaceTokenFound](AZStd::string_view token)
  281. {
  282. ++visitedCount;
  283. if (token.starts_with(' '))
  284. {
  285. leadingWhitespaceTokenFound = true;
  286. }
  287. if (token.ends_with(' '))
  288. {
  289. trailingWhitespaceTokenFound = true;
  290. }
  291. };
  292. AZ::StringFunc::TokenizeVisitor("Hello , World", visitor, ",", false, true);
  293. EXPECT_EQ(2, visitedCount);
  294. EXPECT_TRUE(trailingWhitespaceTokenFound);
  295. EXPECT_TRUE(leadingWhitespaceTokenFound);
  296. visitedCount = {};
  297. trailingWhitespaceTokenFound = {};
  298. leadingWhitespaceTokenFound = {};
  299. AZ::StringFunc::TokenizeVisitor("Hello, World", visitor, ",", false, true);
  300. EXPECT_EQ(2, visitedCount);
  301. EXPECT_FALSE(trailingWhitespaceTokenFound);
  302. EXPECT_TRUE(leadingWhitespaceTokenFound);
  303. visitedCount = {};
  304. trailingWhitespaceTokenFound = {};
  305. leadingWhitespaceTokenFound = {};
  306. AZ::StringFunc::TokenizeVisitor("Hello ,World", visitor, ",", false, true);
  307. EXPECT_EQ(2, visitedCount);
  308. EXPECT_TRUE(trailingWhitespaceTokenFound);
  309. EXPECT_FALSE(leadingWhitespaceTokenFound);
  310. }
  311. TEST_F(StringFuncTest, TokenizeVisitor_DocExampleAboveFunctionDeclaration_Succeeds)
  312. {
  313. constexpr AZStd::array visitTokens = { "Hello", "World", "", "More", "", "", "Tokens" };
  314. size_t visitIndex{};
  315. AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
  316. // capture. Newer versions issue unused warning
  317. auto visitor = [&visitIndex, &visitTokens](AZStd::string_view token)
  318. AZ_POP_DISABLE_WARNING
  319. {
  320. if (visitIndex > visitTokens.size())
  321. {
  322. ADD_FAILURE() << "More tokens have been visited than are expected:" << visitTokens.size();
  323. return;
  324. }
  325. if (token != visitTokens[visitIndex])
  326. {
  327. AZStd::fixed_string<64> result{ token };
  328. ADD_FAILURE() << "Visited token \"" << result.c_str() << "\" does not match token \"" << visitTokens[visitIndex] << "\" at index (" << visitIndex << ") in the visitTokens array";
  329. }
  330. ++visitIndex;
  331. };
  332. AZ::StringFunc::TokenizeVisitor("Hello World More Tokens", visitor, " ", true, true);
  333. AZStd::vector<AZStd::string> resultTokens;
  334. AZ::StringFunc::Tokenize("Hello World More Tokens", resultTokens, ' ', true, true);
  335. ASSERT_EQ(visitTokens.size(), resultTokens.size());
  336. EXPECT_TRUE(AZStd::equal(resultTokens.begin(), resultTokens.end(), visitTokens.begin()));
  337. }
  338. TEST_F(StringFuncTest, TokenizeVisitorReverse_DocExampleAboveFunctionDeclaration_Succeeds)
  339. {
  340. constexpr AZStd::array visitTokens = { "Hello", "World", "", "More", "", "", "Tokens" };
  341. size_t visitIndex = visitTokens.size() - 1;
  342. AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
  343. // capture. Newer versions issue unused warning
  344. auto visitor = [&visitIndex, &visitTokens](AZStd::string_view token)
  345. AZ_POP_DISABLE_WARNING
  346. {
  347. if (visitIndex > visitTokens.size())
  348. {
  349. ADD_FAILURE() << "More tokens have been visited than are expected:" << visitTokens.size();
  350. return;
  351. }
  352. if (token != visitTokens[visitIndex])
  353. {
  354. AZStd::fixed_string<64> result{ token };
  355. ADD_FAILURE() << "Visited token \"" << result.c_str() << "\" does not match token \"" << visitTokens[visitIndex] << "\" at index (" << visitIndex << ") in the visitTokens array";
  356. }
  357. --visitIndex;
  358. };
  359. AZ::StringFunc::TokenizeVisitorReverse("Hello World More Tokens", visitor, " ", true, true);
  360. AZStd::vector<AZStd::string> resultTokens;
  361. // Visitor functor which replaces the regular Tokenize behavior of inserting a new token into a vector of strings
  362. auto addToVectorVisitor = [&resultTokens](AZStd::string_view token)
  363. {
  364. resultTokens.push_back(token);
  365. };
  366. AZ::StringFunc::TokenizeVisitorReverse("Hello World More Tokens", addToVectorVisitor, ' ', true, true);
  367. ASSERT_EQ(visitTokens.size(), resultTokens.size());
  368. EXPECT_TRUE(AZStd::equal(resultTokens.begin(), resultTokens.end(), visitTokens.rbegin()));
  369. }
  370. TEST_F(StringFuncTest, TokenizeNext_WithEmptyString_ReturnsInvalid)
  371. {
  372. AZStd::string_view input{ "" };
  373. AZStd::optional<AZStd::string_view> token = AZ::StringFunc::TokenizeNext(input, ",");
  374. EXPECT_FALSE(token);
  375. }
  376. TEST_F(StringFuncTest, TokenizeNext_WithNonEmptyString_ReturnsValid)
  377. {
  378. AZStd::string_view input{ " " };
  379. AZStd::optional<AZStd::string_view> token = AZ::StringFunc::TokenizeNext(input, ",");
  380. EXPECT_TRUE(token);
  381. EXPECT_EQ(" ", *token);
  382. }
  383. TEST_F(StringFuncTest, TokenizeNext_DocExample1AboveFunctionDeclaration_Succeeds)
  384. {
  385. AZStd::string_view input = "Hello World";
  386. AZStd::optional<AZStd::string_view> output;
  387. output = StringFunc::TokenizeNext(input, " ");
  388. EXPECT_EQ("World", input);
  389. EXPECT_TRUE(output);
  390. EXPECT_EQ("Hello", output);
  391. output = StringFunc::TokenizeNext(input, " ");
  392. EXPECT_EQ("", input);
  393. EXPECT_TRUE(output);
  394. EXPECT_EQ("World", output);
  395. output = StringFunc::TokenizeNext(input, " ");
  396. EXPECT_EQ("", input);
  397. EXPECT_FALSE(output);
  398. }
  399. TEST_F(StringFuncTest, TokenizeNext_DocExample2AboveFunctionDeclaration_Succeeds)
  400. {
  401. AZStd::string_view input = "Hello World More Tokens";
  402. AZStd::optional<AZStd::string_view> output;
  403. output = StringFunc::TokenizeNext(input, ' ');
  404. EXPECT_EQ("World More Tokens", input);
  405. EXPECT_TRUE(output);
  406. EXPECT_EQ("Hello", output);
  407. output = StringFunc::TokenizeNext(input, ' ');
  408. EXPECT_EQ(" More Tokens", input);
  409. EXPECT_TRUE(output);
  410. EXPECT_EQ("World", output);
  411. output = StringFunc::TokenizeNext(input, ' ');
  412. EXPECT_EQ("More Tokens", input);
  413. EXPECT_TRUE(output);
  414. EXPECT_EQ("", output);
  415. output = StringFunc::TokenizeNext(input, ' ');
  416. EXPECT_EQ(" Tokens", input);
  417. EXPECT_TRUE(output);
  418. EXPECT_EQ("More", output);
  419. output = StringFunc::TokenizeNext(input, ' ');
  420. EXPECT_EQ(" Tokens", input);
  421. EXPECT_TRUE(output);
  422. EXPECT_EQ("", output);
  423. output = StringFunc::TokenizeNext(input, ' ');
  424. EXPECT_EQ("Tokens", input);
  425. EXPECT_TRUE(output);
  426. EXPECT_EQ("", output);
  427. output = StringFunc::TokenizeNext(input, ' ');
  428. EXPECT_EQ("", input);
  429. EXPECT_TRUE(output);
  430. EXPECT_EQ("Tokens", output);
  431. output = StringFunc::TokenizeNext(input, ' ');
  432. EXPECT_EQ("", input);
  433. EXPECT_FALSE(output);
  434. }
  435. TEST_F(StringFuncTest, TokenizeLast_DocExample1AboveFunctionDeclaration_Succeeds)
  436. {
  437. AZStd::string_view input = "Hello World";
  438. AZStd::optional<AZStd::string_view> output;
  439. output = StringFunc::TokenizeLast(input, " ");
  440. EXPECT_EQ("Hello", input);
  441. EXPECT_TRUE(output);
  442. EXPECT_EQ("World", output);
  443. output = StringFunc::TokenizeLast(input, " ");
  444. EXPECT_EQ("", input);
  445. EXPECT_TRUE(output);
  446. EXPECT_EQ("Hello", output);
  447. output = StringFunc::TokenizeLast(input, " ");
  448. EXPECT_EQ("", input);
  449. EXPECT_FALSE(output);
  450. }
  451. TEST_F(StringFuncTest, TokenizeLast_DocExample2AboveFunctionDeclaration_Succeeds)
  452. {
  453. AZStd::string_view input = "Hello World More Tokens";
  454. AZStd::optional<AZStd::string_view> output;
  455. output = StringFunc::TokenizeLast(input, ' ');
  456. EXPECT_EQ("Hello World More ", input);
  457. EXPECT_TRUE(output);
  458. EXPECT_EQ("Tokens", output);
  459. output = StringFunc::TokenizeLast(input, ' ');
  460. EXPECT_EQ("Hello World More ", input);
  461. EXPECT_TRUE(output);
  462. EXPECT_EQ("", output);
  463. output = StringFunc::TokenizeLast(input, ' ');
  464. EXPECT_EQ("Hello World More", input);
  465. EXPECT_TRUE(output);
  466. EXPECT_EQ("", output);
  467. output = StringFunc::TokenizeLast(input, ' ');
  468. EXPECT_EQ("Hello World ", input);
  469. EXPECT_TRUE(output);
  470. EXPECT_EQ("More", output);
  471. output = StringFunc::TokenizeLast(input, ' ');
  472. EXPECT_EQ("Hello World", input);
  473. EXPECT_TRUE(output);
  474. EXPECT_EQ("", output);
  475. output = StringFunc::TokenizeLast(input, ' ');
  476. EXPECT_EQ("Hello", input);
  477. EXPECT_TRUE(output);
  478. EXPECT_EQ("World", output);
  479. output = StringFunc::TokenizeLast(input, ' ');
  480. EXPECT_EQ("", input);
  481. EXPECT_TRUE(output);
  482. EXPECT_EQ("Hello", output);
  483. output = StringFunc::TokenizeLast(input, ' ');
  484. EXPECT_EQ("", input);
  485. EXPECT_FALSE(output);
  486. }
  487. TEST_F(StringFuncTest, GroupDigits_BasicFunctionality)
  488. {
  489. // Test a bunch of numbers and other inputs
  490. const AZStd::pair<AZStd::string, AZStd::string> inputsAndExpectedOutputs[] =
  491. {
  492. { "0", "0" },
  493. { "10", "10" },
  494. { "100", "100" },
  495. { "1000", "1,000" },
  496. { "10000", "10,000" },
  497. { "100000", "100,000" },
  498. { "1000000", "1,000,000" },
  499. { "0.0", "0.0"},
  500. { "10.0", "10.0"},
  501. { "100.0", "100.0" },
  502. { "1000.0", "1,000.0" },
  503. { "10000.0", "10,000.0" },
  504. { "100000.0", "100,000.0" },
  505. { "1000000.0", "1,000,000.0" },
  506. { "-0.0", "-0.0" },
  507. { "-10.0", "-10.0" },
  508. { "-100.0", "-100.0" },
  509. { "-1000.0", "-1,000.0" },
  510. { "-10000.0", "-10,000.0" },
  511. { "-100000.0", "-100,000.0" },
  512. { "-1000000.0", "-1,000,000.0" },
  513. { "foo", "foo" },
  514. { "foo123.0", "foo123.0" },
  515. { "foo1234.0", "foo1,234.0" }
  516. };
  517. static const size_t BUFFER_SIZE = 32;
  518. char buffer[BUFFER_SIZE];
  519. for (const auto& inputAndExpectedOutput : inputsAndExpectedOutputs)
  520. {
  521. azstrncpy(buffer, BUFFER_SIZE, inputAndExpectedOutput.first.c_str(), inputAndExpectedOutput.first.length() + 1);
  522. auto endPos = StringFunc::NumberFormatting::GroupDigits(buffer, BUFFER_SIZE);
  523. EXPECT_STREQ(buffer, inputAndExpectedOutput.second.c_str());
  524. EXPECT_EQ(inputAndExpectedOutput.second.length(), endPos);
  525. }
  526. // Test valid inputs
  527. AZ_TEST_START_ASSERTTEST;
  528. StringFunc::NumberFormatting::GroupDigits(nullptr, 0); // Should assert twice, for null pointer and 0 being <= decimalPosHint
  529. StringFunc::NumberFormatting::GroupDigits(buffer, BUFFER_SIZE, 0, ',', '.', 0, 0); // Should assert for having a non-positive grouping size
  530. AZ_TEST_STOP_ASSERTTEST(3);
  531. // Test buffer overruns
  532. static const size_t SMALL_BUFFER_SIZE = 8;
  533. char smallBuffer[SMALL_BUFFER_SIZE];
  534. char prevSmallBuffer[SMALL_BUFFER_SIZE];
  535. for (const auto& inputAndExpectedOutput : inputsAndExpectedOutputs)
  536. {
  537. azstrncpy(smallBuffer, SMALL_BUFFER_SIZE, inputAndExpectedOutput.first.c_str(), AZStd::min(inputAndExpectedOutput.first.length() + 1, SMALL_BUFFER_SIZE - 1));
  538. smallBuffer[SMALL_BUFFER_SIZE - 1] = 0; // Force null-termination
  539. memcpy(prevSmallBuffer, smallBuffer, SMALL_BUFFER_SIZE);
  540. auto endPos = StringFunc::NumberFormatting::GroupDigits(smallBuffer, SMALL_BUFFER_SIZE);
  541. if (inputAndExpectedOutput.second.length() >= SMALL_BUFFER_SIZE)
  542. {
  543. EXPECT_STREQ(smallBuffer, prevSmallBuffer); // No change if buffer overruns
  544. }
  545. else
  546. {
  547. EXPECT_STREQ(smallBuffer, inputAndExpectedOutput.second.c_str());
  548. EXPECT_EQ(inputAndExpectedOutput.second.length(), endPos);
  549. }
  550. }
  551. }
  552. TEST_F(StringFuncTest, GroupDigits_DifferentSettings)
  553. {
  554. struct TestSettings
  555. {
  556. AZStd::string m_input;
  557. AZStd::string m_output;
  558. size_t m_decimalPosHint;
  559. char m_digitSeparator;
  560. char m_decimalSeparator;
  561. int m_groupingSize;
  562. int m_firstGroupingSize;
  563. };
  564. const TestSettings testSettings[] =
  565. {
  566. { "123456789.0123", "123,456,789.0123", 9, ',', '.', 3, 0 }, // Correct decimal position hint
  567. { "123456789.0123", "123,456,789.0123", 8, ',', '.', 3, 0 }, // Incorrect decimal position hint
  568. { "123456789,0123", "123.456.789,0123", 0, '.', ',', 3, 0 }, // Swap glyphs used for decimal and grouping
  569. { "123456789.0123", "1,23,45,67,89.0123", 8, ',', '.', 2, 0 }, // Different grouping size
  570. { "123456789.0123", "12,34,56,789.0123", 8, ',', '.', 2, 3 }, // Customized grouping size for first group
  571. };
  572. static const size_t BUFFER_SIZE = 32;
  573. char buffer[BUFFER_SIZE];
  574. for (const auto& settings : testSettings)
  575. {
  576. azstrncpy(buffer, BUFFER_SIZE, settings.m_input.c_str(), settings.m_input.length() + 1);
  577. auto endPos = StringFunc::NumberFormatting::GroupDigits(buffer, BUFFER_SIZE, settings.m_decimalPosHint, settings.m_digitSeparator,
  578. settings.m_decimalSeparator, settings.m_groupingSize, settings.m_firstGroupingSize);
  579. EXPECT_STREQ(buffer, settings.m_output.c_str());
  580. EXPECT_EQ(settings.m_output.length(), endPos);
  581. }
  582. }
  583. TEST_F(StringFuncTest, CalculateBranchToken_ValidInput_Success)
  584. {
  585. AZStd::string samplePath = "F:\\w s 1\\dev";
  586. AZStd::string expectedToken = "0x68E564C5";
  587. AZStd::string resultToken;
  588. AZ::StringFunc::AssetPath::CalculateBranchToken(samplePath, resultToken);
  589. ASSERT_TRUE(resultToken == expectedToken);
  590. }
  591. TEST_F(StringFuncTest, CalculateBranchToken_ValidInputExtra_Success)
  592. {
  593. AZStd::string samplePath = "F:\\w s 1\\dev\\";
  594. AZStd::string expectedToken = "0x68E564C5";
  595. AZStd::string resultToken;
  596. AZ::StringFunc::AssetPath::CalculateBranchToken(samplePath, resultToken);
  597. ASSERT_TRUE(resultToken == expectedToken);
  598. }
  599. TEST_F(StringFuncTest, HasDrive_EmptyInput_NoDriveFound)
  600. {
  601. EXPECT_FALSE(AZ::StringFunc::Path::HasDrive(""));
  602. }
  603. TEST_F(StringFuncTest, HasDrive_InputContainsDrive_DriveFound)
  604. {
  605. #if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
  606. AZStd::string input;
  607. input = "F:\\test\\to\\get\\drive\\";
  608. EXPECT_TRUE(AZ::StringFunc::Path::HasDrive(input.c_str()));
  609. #endif
  610. }
  611. TEST_F(StringFuncTest, HasDrive_InputDoesNotContainDrive_NoDriveFound)
  612. {
  613. AZStd::string input;
  614. #if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
  615. input = "test\\with\\no\\drive\\";
  616. #else
  617. input = "test/with/no/drive/";
  618. #endif
  619. EXPECT_FALSE(AZ::StringFunc::Path::HasDrive(input.c_str()));
  620. }
  621. TEST_F(StringFuncTest, HasDrive_CheckAllFileSystemFormats_InputContainsDrive_DriveFound)
  622. {
  623. AZStd::string input1 = "F:\\test\\to\\get\\drive\\";
  624. AZStd::string input2 = "/test/to/get/drive/";
  625. #if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
  626. EXPECT_TRUE(AZ::StringFunc::Path::HasDrive(input1.c_str(), true));
  627. #endif
  628. EXPECT_TRUE(AZ::StringFunc::Path::HasDrive(input2.c_str(), true));
  629. }
  630. TEST_F(StringFuncTest, HasDrive_CheckAllFileSystemFormats_InputDoesNotContainDrive_NoDriveFound)
  631. {
  632. AZStd::string input1 = "test\\with\\no\\drive\\";
  633. AZStd::string input2 = "test/with/no/drive/";
  634. EXPECT_FALSE(AZ::StringFunc::Path::HasDrive(input1.c_str(), true));
  635. EXPECT_FALSE(AZ::StringFunc::Path::HasDrive(input2.c_str(), true));
  636. }
  637. TEST_F(StringFuncTest, GetDrive_UseSameStringForInOut_Success)
  638. {
  639. #if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
  640. AZStd::string input = "F:\\test\\to\\get\\drive\\";
  641. AZStd::string expectedDriveResult = "F:";
  642. bool result = AZ::StringFunc::Path::GetDrive(input.c_str(), input);
  643. ASSERT_TRUE(result);
  644. ASSERT_EQ(input, expectedDriveResult);
  645. #endif
  646. }
  647. TEST_F(StringFuncTest, IsValid_LongPathComponent_Success)
  648. {
  649. #if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
  650. AZStd::string longFilename = "F:\\folder\\1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.txt";
  651. AZStd::string longFolder = "F:\\1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\\filename.ext";
  652. bool result = AZ::StringFunc::Path::IsValid(longFilename.c_str(), true, true);
  653. EXPECT_TRUE(result);
  654. result = AZ::StringFunc::Path::IsValid(longFolder.c_str(), true, true);
  655. EXPECT_TRUE(result);
  656. #else
  657. AZStd::string longFilename = "/folder/1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.txt";
  658. AZStd::string longFolder = "/1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890/filename.ext";
  659. bool result = AZ::StringFunc::Path::IsValid(longFilename.c_str(), false, true);
  660. EXPECT_TRUE(result);
  661. result = AZ::StringFunc::Path::IsValid(longFolder.c_str(), false, true);
  662. EXPECT_TRUE(result);
  663. #endif // AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
  664. }
  665. //! Strip
  666. TEST_F(StringFuncTest, Strip_InternalCharactersAll_Success)
  667. {
  668. AZStd::string input = "Hello World";
  669. AZStd::string expectedResult = "Heo Word";
  670. const char stripToken = 'l';
  671. AZ::StringFunc::Strip(input, stripToken);
  672. ASSERT_EQ(input, expectedResult);
  673. }
  674. TEST_F(StringFuncTest, Strip_InternalCharactersBeginning_NoChange)
  675. {
  676. AZStd::string input = "Hello World";
  677. AZStd::string expectedResult = "Hello World";
  678. const char stripToken = 'l';
  679. AZ::StringFunc::Strip(input, stripToken, false, true);
  680. ASSERT_EQ(input, expectedResult);
  681. }
  682. TEST_F(StringFuncTest, Strip_InternalCharactersEnd_NoChange)
  683. {
  684. AZStd::string input = "Hello World";
  685. AZStd::string expectedResult = "Hello World";
  686. const char stripToken = 'l';
  687. AZ::StringFunc::Strip(input, stripToken, false, false, true);
  688. ASSERT_EQ(input, expectedResult);
  689. }
  690. TEST_F(StringFuncTest, Strip_InternalCharacterBeginningEnd_NoChange)
  691. {
  692. AZStd::string input = "Hello World";
  693. AZStd::string expectedResult = "Hello World";
  694. const char stripToken = 'l';
  695. AZ::StringFunc::Strip(input, stripToken, false, true, true);
  696. ASSERT_EQ(input, expectedResult);
  697. }
  698. TEST_F(StringFuncTest, Strip_InternalCharactersBeginningEndInsensitive_NoChange)
  699. {
  700. AZStd::string input = "HeLlo HeLlo HELlO";
  701. AZStd::string expectedResult = "HeLlo HeLlo HELlO";
  702. const char stripToken = 'l';
  703. AZ::StringFunc::Strip(input, stripToken, true, true, true);
  704. ASSERT_EQ(input, expectedResult);
  705. }
  706. TEST_F(StringFuncTest, Strip_InternalCharactersBeginningEndString_Success)
  707. {
  708. AZStd::string input = "HeLlo HeLlo HELlO";
  709. AZStd::string expectedResult = " HeLlo ";
  710. const char* stripToken = "hello";
  711. AZ::StringFunc::Strip(input, stripToken, false, true, true);
  712. ASSERT_EQ(input, expectedResult);
  713. }
  714. TEST_F(StringFuncTest, Strip_Beginning_Success)
  715. {
  716. AZStd::string input = "AbrAcadabra";
  717. AZStd::string expectedResult = "brAcadabra";
  718. const char stripToken = 'a';
  719. AZ::StringFunc::Strip(input, stripToken, false, true);
  720. ASSERT_EQ(input, expectedResult);
  721. }
  722. TEST_F(StringFuncTest, Strip_End_Success)
  723. {
  724. AZStd::string input = "AbrAcadabra";
  725. AZStd::string expectedResult = "AbrAcadabr";
  726. const char stripToken = 'a';
  727. AZ::StringFunc::Strip(input, stripToken, false, false, true);
  728. ASSERT_EQ(input, expectedResult);
  729. }
  730. TEST_F(StringFuncTest, Strip_BeginningEnd_Success)
  731. {
  732. AZStd::string input = "AbrAcadabra";
  733. AZStd::string expectedResult = "brAcadabr";
  734. const char stripToken = 'a';
  735. AZ::StringFunc::Strip(input, stripToken, false, true, true);
  736. ASSERT_EQ(input, expectedResult);
  737. }
  738. TEST_F(StringFuncTest, Strip_BeginningEndCaseSensitive_EndOnly)
  739. {
  740. AZStd::string input = "AbrAcadabra";
  741. AZStd::string expectedResult = "AbrAcadabr";
  742. const char stripToken = 'a';
  743. AZ::StringFunc::Strip(input, stripToken, true, true, true);
  744. ASSERT_EQ(input, expectedResult);
  745. }
  746. using StringFuncPathTest = LeakDetectionFixture;
  747. TEST_F(StringFuncPathTest, GetParentDir_InvokedOnAbsoluteDirectory_ReturnsSameDirectory)
  748. {
  749. #if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
  750. const char* input = R"str(C:\path\to\some\resource\)str";
  751. const char* expectedResult = R"str(C:\path\to\some)str";
  752. #else
  753. const char* input = R"str(/path/to/some/resource/)str";
  754. const char* expectedResult = R"str(/path/to/some)str";
  755. #endif
  756. AZStd::optional<AZStd::string> result = AZ::StringFunc::Path::GetParentDir(input);
  757. EXPECT_TRUE(result.has_value());
  758. EXPECT_STREQ(expectedResult, result->c_str());
  759. }
  760. TEST_F(StringFuncPathTest, GetParentDir_InvokedOnRelativeDirectory_ReturnsSameDirectory)
  761. {
  762. #if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
  763. const char* input = R"str(some\resource\)str";
  764. const char* expectedResult = R"str(some)str";
  765. #else
  766. const char* input = R"str(some/resource/)str";
  767. const char* expectedResult = R"str(some)str";
  768. #endif
  769. AZStd::optional<AZStd::string> result = AZ::StringFunc::Path::GetParentDir(input);
  770. EXPECT_TRUE(result.has_value());
  771. EXPECT_STREQ(expectedResult, result->c_str());
  772. }
  773. TEST_F(StringFuncPathTest, GetParentDir_InvokedOnRelativePathWithoutTrailingSlash_ReturnsParentDirectory)
  774. {
  775. #if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
  776. const char* input = R"str(some\resource.exe)str";
  777. const char* expectedResult = R"str(some)str";
  778. #else
  779. const char* input = R"str(some/resource.exe)str";
  780. const char* expectedResult = R"str(some)str";
  781. #endif
  782. AZStd::optional<AZStd::string> result = AZ::StringFunc::Path::GetParentDir(input);
  783. EXPECT_TRUE(result.has_value());
  784. EXPECT_STREQ(expectedResult, result->c_str());
  785. }
  786. TEST_F(StringFuncPathTest, GetParentDir_InvokedOnRoot_ReturnsRoot)
  787. {
  788. #if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
  789. const char* input = R"str(C:\)str";
  790. const char* expectedResult = R"str(C:\)str";
  791. #else
  792. const char* input = R"str(/)str";
  793. const char* expectedResult = R"str(/)str";
  794. #endif
  795. AZStd::optional<AZStd::string> result = AZ::StringFunc::Path::GetParentDir(input);
  796. EXPECT_TRUE(result.has_value());
  797. EXPECT_STREQ(expectedResult, result->c_str());
  798. }
  799. TEST_F(StringFuncPathTest, GetParentDir_InvokedOnEmptyPath_ReturnFalse)
  800. {
  801. const char* input = "";
  802. AZStd::optional<AZStd::string> result = AZ::StringFunc::Path::GetParentDir(input);
  803. EXPECT_FALSE(result.has_value());
  804. }
  805. TEST_F(StringFuncPathTest, GetParentDir_InvokedOnPathWithoutParent_ReturnsFalse)
  806. {
  807. const char* input = "Test.exe";
  808. AZStd::optional<AZStd::string> result = AZ::StringFunc::Path::GetParentDir(input);
  809. EXPECT_FALSE(result.has_value());
  810. }
  811. TEST_F(StringFuncPathTest, GetParentDir_InvokedOnWindowsDriveWithoutTrailingSlash_ReturnsTrue)
  812. {
  813. const char* input = R"str(C:)str";
  814. AZStd::optional<AZStd::string> result = AZ::StringFunc::Path::GetParentDir(input);
  815. #if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
  816. EXPECT_TRUE(result.has_value());
  817. #else
  818. EXPECT_FALSE(result.has_value());
  819. #endif
  820. }
  821. TEST_F(StringFuncPathTest, ReplaceExtension_WithoutDot)
  822. {
  823. AZStd::string s = "D:\\p4\\some.file";
  824. AZ::StringFunc::Path::ReplaceExtension(s, "xml");
  825. EXPECT_STREQ("D:\\p4\\some.xml", s.c_str());
  826. }
  827. TEST_F(StringFuncPathTest, ReplaceExtension_WithDot)
  828. {
  829. AZStd::string s = "D:\\p4\\some.file";
  830. AZ::StringFunc::Path::ReplaceExtension(s, ".xml");
  831. EXPECT_STREQ("D:\\p4\\some.xml", s.c_str());
  832. }
  833. TEST_F(StringFuncPathTest, ReplaceExtension_Empty)
  834. {
  835. AZStd::string s = "D:\\p4\\some.file";
  836. AZ::StringFunc::Path::ReplaceExtension(s, "");
  837. EXPECT_STREQ("D:\\p4\\some", s.c_str());
  838. }
  839. TEST_F(StringFuncPathTest, ReplaceExtension_Null)
  840. {
  841. AZStd::string s = "D:\\p4\\some.file";
  842. AZ::StringFunc::Path::ReplaceExtension(s, nullptr);
  843. EXPECT_STREQ("D:\\p4\\some", s.c_str());
  844. }
  845. class TestPathStringArgs
  846. {
  847. public:
  848. TestPathStringArgs(const char* input, const char* expected_output)
  849. : m_input(input),
  850. m_expected(expected_output)
  851. {}
  852. const char* m_input;
  853. const char* m_expected;
  854. };
  855. class StringPathFuncTest
  856. : public StringFuncTest,
  857. public ::testing::WithParamInterface<TestPathStringArgs>
  858. {
  859. public:
  860. StringPathFuncTest() = default;
  861. ~StringPathFuncTest() override = default;
  862. };
  863. TEST_P(StringPathFuncTest, TestNormalizePath)
  864. {
  865. const TestPathStringArgs& param = GetParam();
  866. AZStd::string input = AZStd::string(param.m_input);
  867. AZStd::string expected = AZStd::string(param.m_expected);
  868. bool result = AZ::StringFunc::Path::Normalize(input);
  869. EXPECT_TRUE(result);
  870. EXPECT_STREQ(input.c_str(), expected.c_str());
  871. }
  872. #if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
  873. INSTANTIATE_TEST_SUITE_P(
  874. PathWithSingleDotSubFolders,
  875. StringPathFuncTest,
  876. ::testing::Values(
  877. TestPathStringArgs("F:\\test\\to\\get\\.\\drive\\", "F:\\test\\to\\get\\drive\\")
  878. ));
  879. INSTANTIATE_TEST_SUITE_P(
  880. PathWithDoubleDotSubFolders,
  881. StringPathFuncTest,
  882. ::testing::Values(
  883. TestPathStringArgs("C:\\One\\Two\\..\\Three\\", "C:\\One\\Three\\"),
  884. TestPathStringArgs("C:\\One\\..\\..\\Two\\", "C:\\Two\\"),
  885. TestPathStringArgs("C:\\One\\Two\\Three\\..\\", "C:\\One\\Two\\"),
  886. TestPathStringArgs("F:\\test\\to\\get\\..\\blue\\orchard\\..\\drive\\", "F:\\test\\to\\blue\\drive\\"),
  887. TestPathStringArgs("F:\\test\\to\\.\\.\\get\\..\\.\\.\\drive\\", "F:\\test\\to\\drive\\"),
  888. TestPathStringArgs("F:\\..\\test\\to\\.\\.\\get\\..\\.\\.\\drive\\", "F:\\test\\to\\drive\\"),
  889. TestPathStringArgs("F:\\..\\..\\..\\test\\to\\.\\.\\get\\..\\.\\.\\drive\\", "F:\\test\\to\\drive\\"),
  890. TestPathStringArgs("F:\\..\\..\\..\\test\\to\\.\\.\\get\\..\\.\\.\\drive\\..\\..\\..\\", "F:\\"),
  891. TestPathStringArgs("F:\\..\\", "F:\\")
  892. ));
  893. #else
  894. INSTANTIATE_TEST_SUITE_P(
  895. PathWithSingleDotSubFolders,
  896. StringPathFuncTest, ::testing::Values(
  897. TestPathStringArgs("/test/to/get/./drive/", "/test/to/get/drive/")
  898. ));
  899. INSTANTIATE_TEST_SUITE_P(
  900. PathWithDoubleDotSubFolders,
  901. StringPathFuncTest,
  902. ::testing::Values(
  903. TestPathStringArgs("/one/two/../three/", "/one/three/"),
  904. TestPathStringArgs("/one/../../two/", "/two/"),
  905. TestPathStringArgs("/one/two/three/../", "/one/two/"),
  906. TestPathStringArgs("/test/to/get/../blue/orchard/../drive/", "/test/to/blue/drive/"),
  907. TestPathStringArgs("/test/to/././get./././.drive/", "/test/to/get./.drive/"),
  908. TestPathStringArgs("/../../test/to/././get/../././drive/", "/test/to/drive/")
  909. ));
  910. #endif //AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
  911. }