RangesViewTests.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  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/std/containers/list.h>
  10. #include <AzCore/std/containers/map.h>
  11. #include <AzCore/std/containers/vector.h>
  12. #include <AzCore/std/ranges/all_view.h>
  13. #include <AzCore/std/ranges/as_const_view.h>
  14. #include <AzCore/std/ranges/as_rvalue_view.h>
  15. #include <AzCore/std/ranges/common_view.h>
  16. #include <AzCore/std/ranges/counted_view.h>
  17. #include <AzCore/std/ranges/elements_view.h>
  18. #include <AzCore/std/ranges/empty_view.h>
  19. #include <AzCore/std/ranges/filter_view.h>
  20. #include <AzCore/std/ranges/iota_view.h>
  21. #include <AzCore/std/ranges/join_view.h>
  22. #include <AzCore/std/ranges/join_with_view.h>
  23. #include <AzCore/std/ranges/ranges_adaptor.h>
  24. #include <AzCore/std/ranges/repeat_view.h>
  25. #include <AzCore/std/ranges/reverse_view.h>
  26. #include <AzCore/std/ranges/single_view.h>
  27. #include <AzCore/std/ranges/split_view.h>
  28. #include <AzCore/std/ranges/subrange.h>
  29. #include <AzCore/std/ranges/transform_view.h>
  30. #include <AzCore/std/ranges/zip_view.h>
  31. #include <AzCore/std/string/string_view.h>
  32. namespace UnitTest
  33. {
  34. class RangesViewTestFixture
  35. : public LeakDetectionFixture
  36. {};
  37. TEST_F(RangesViewTestFixture, AllRangeAdaptor_Succeeds)
  38. {
  39. AZStd::string_view testString{ "Hello World" };
  40. auto testAllView = AZStd::ranges::views::all(testString);
  41. EXPECT_EQ(testString.size(), testAllView.size());
  42. EXPECT_EQ(testString.data(), testAllView.data());
  43. EXPECT_EQ(testString.begin(), testAllView.begin());
  44. EXPECT_EQ(testString.end(), testAllView.end());
  45. EXPECT_EQ(testString.empty(), testAllView.empty());
  46. EXPECT_EQ(testString.front(), testAllView.front());
  47. EXPECT_EQ(testString.back(), testAllView.back());
  48. EXPECT_EQ(testString[5], testAllView[5]);
  49. auto testAllViewChain = testString | AZStd::ranges::views::all;
  50. EXPECT_EQ(testString.size(), testAllViewChain.size());
  51. EXPECT_EQ(testString.data(), testAllViewChain.data());
  52. EXPECT_EQ(testString.begin(), testAllViewChain.begin());
  53. EXPECT_EQ(testString.end(), testAllViewChain.end());
  54. EXPECT_EQ(testString.empty(), testAllViewChain.empty());
  55. EXPECT_EQ(testString.front(), testAllViewChain.front());
  56. EXPECT_EQ(testString.back(), testAllViewChain.back());
  57. EXPECT_EQ(testString[5], testAllViewChain[5]);
  58. }
  59. TEST_F(RangesViewTestFixture, Subrange_DeductionGuides_Compile)
  60. {
  61. AZStd::string_view testString{ "Hello World" };
  62. AZStd::ranges::subrange rangeDeduction(testString);
  63. AZStd::ranges::subrange rangeDeductionWithSize(testString, testString.size());
  64. AZStd::ranges::subrange iteratorDeduction(testString.begin(), testString.end());
  65. AZStd::ranges::subrange iteratorDeductionWithSize(testString.begin(), testString.end(),
  66. testString.size());
  67. EXPECT_TRUE(rangeDeduction);
  68. EXPECT_TRUE(rangeDeductionWithSize);
  69. EXPECT_TRUE(iteratorDeduction);
  70. EXPECT_TRUE(iteratorDeductionWithSize);
  71. }
  72. TEST_F(RangesViewTestFixture, Subrange_CanTakeSubsetOfContainer_Succeeds)
  73. {
  74. AZStd::vector<int> testVector{ 1, 3, 5, 6, 7, 6, 89, -178 };
  75. AZStd::ranges::subrange subVector(testVector);
  76. EXPECT_TRUE(subVector);
  77. ASSERT_FALSE(subVector.empty());
  78. EXPECT_EQ(testVector.data(), subVector.data());
  79. EXPECT_EQ(testVector.begin(), subVector.begin());
  80. EXPECT_EQ(testVector.end(), subVector.end());
  81. EXPECT_EQ(testVector.size(), subVector.size());
  82. EXPECT_EQ(testVector[0], subVector[0]);
  83. EXPECT_EQ(testVector.front(), subVector.front());
  84. EXPECT_EQ(testVector.back(), subVector.back());
  85. // Now validate the iterator operations
  86. subVector.advance(2);
  87. subVector = subVector.prev();
  88. subVector.advance(2);
  89. subVector = subVector.next();
  90. subVector.advance(-4);
  91. EXPECT_EQ(testVector.begin(), subVector.begin());
  92. // Obtain a copy of the subrange with the first and last elements removed
  93. AZStd::ranges::subrange subVectorSplice(subVector.begin() + 1, subVector.end() - 1);
  94. EXPECT_TRUE(subVectorSplice);
  95. ASSERT_FALSE(subVector.empty());
  96. EXPECT_LT(testVector.data(), subVectorSplice.data());
  97. EXPECT_EQ(testVector.begin() + 1, subVectorSplice.begin());
  98. EXPECT_EQ(testVector.end() - 1, subVectorSplice.end());
  99. ASSERT_EQ(testVector.size() - 2, subVectorSplice.size());
  100. EXPECT_EQ(testVector[1], subVectorSplice.front());
  101. EXPECT_EQ(testVector[testVector.size() - 2], subVectorSplice.back());
  102. EXPECT_NE(testVector.front(), subVectorSplice.front());
  103. EXPECT_NE(testVector.back(), subVectorSplice.back());
  104. }
  105. TEST_F(RangesViewTestFixture, EmptyView_ReturnsEmptyViewRange_Succeeds)
  106. {
  107. AZStd::ranges::empty_view<int> emptyView;
  108. EXPECT_EQ(nullptr, emptyView.data());
  109. EXPECT_EQ(0, emptyView.size());
  110. EXPECT_TRUE(emptyView.empty());
  111. EXPECT_EQ(emptyView.end(), emptyView.begin());
  112. EXPECT_EQ(nullptr, AZStd::ranges::views::empty<AZStd::string_view>.data());
  113. EXPECT_EQ(0, AZStd::ranges::views::empty<AZStd::string_view>.size());
  114. EXPECT_TRUE(AZStd::ranges::views::empty<AZStd::string_view>.empty());
  115. EXPECT_EQ(AZStd::ranges::views::empty<AZStd::string_view>.end(), AZStd::ranges::views::empty<AZStd::string_view>.begin());
  116. }
  117. TEST_F(RangesViewTestFixture, SingleView_ReturnsViewOverSingleElement_Succeeds)
  118. {
  119. AZStd::string_view testString{ "Hello World" };
  120. AZStd::ranges::single_view singleView{ AZStd::move(testString) };
  121. EXPECT_NE(nullptr, singleView.data());
  122. EXPECT_EQ(1, singleView.size());
  123. EXPECT_FALSE(singleView.empty());
  124. ASSERT_NE(singleView.end(), singleView.begin());
  125. auto singleViewStringIt = singleView.begin();
  126. EXPECT_EQ("Hello World", *singleViewStringIt);
  127. }
  128. TEST_F(RangesViewTestFixture, RefView_CanWrapStringView_Succeeds)
  129. {
  130. AZStd::string_view testString{ "Hello World" };
  131. AZStd::ranges::ref_view refView(testString);
  132. EXPECT_EQ(testString.size(), refView.size());
  133. EXPECT_EQ(testString.data(), refView.data());
  134. EXPECT_EQ(testString.begin(), refView.begin());
  135. EXPECT_EQ(testString.end(), refView.end());
  136. EXPECT_EQ(testString.empty(), refView.empty());
  137. EXPECT_EQ(testString.front(), refView.front());
  138. EXPECT_EQ(testString.back(), refView.back());
  139. EXPECT_EQ(testString[5], refView[5]);
  140. }
  141. TEST_F(RangesViewTestFixture, OwningView_CanWrapStringView_Succeeds)
  142. {
  143. AZStd::string_view sourceView{ "Hello World" };
  144. AZStd::string_view testString{ sourceView };
  145. AZStd::ranges::owning_view owningView(AZStd::move(testString));
  146. EXPECT_TRUE(testString.empty());
  147. EXPECT_FALSE(owningView.empty());
  148. EXPECT_EQ(sourceView.size(), owningView.size());
  149. EXPECT_EQ(sourceView.data(), owningView.data());
  150. EXPECT_EQ(sourceView.begin(), owningView.begin());
  151. EXPECT_EQ(sourceView.end(), owningView.end());
  152. EXPECT_EQ(sourceView.empty(), owningView.empty());
  153. EXPECT_EQ(sourceView.front(), owningView.front());
  154. EXPECT_EQ(sourceView.back(), owningView.back());
  155. EXPECT_EQ(sourceView[5], owningView[5]);
  156. }
  157. MATCHER_P(ZipViewAtSentinel, sentinel, "") {
  158. *result_listener << "zip view has iterated to sentinel";
  159. return !(arg == sentinel);
  160. }
  161. TEST_F(RangesViewTestFixture, ZipView_CompilesWithRange_Succeeds)
  162. {
  163. AZStd::string_view sourceView{ "Hello World" };
  164. AZStd::ranges::zip_view zipView(AZStd::move(sourceView));
  165. auto zipItTuple = zipView.begin();
  166. auto zipSentinelTuple = zipView.end();
  167. ASSERT_THAT(zipItTuple, ZipViewAtSentinel(zipSentinelTuple));
  168. EXPECT_EQ('H', AZStd::get<0>(*zipItTuple));
  169. ptrdiff_t zipDistance = zipSentinelTuple - zipItTuple;
  170. EXPECT_EQ(11, zipDistance);
  171. AZStd::list<int> sourceList{ 1, 2, 3, 4, 5 };
  172. AZStd::ranges::zip_view zipView2(AZStd::move(sourceList));
  173. auto zipListTupleIt = zipView2.begin();
  174. auto zipListTupleEnd = zipView2.end();
  175. ASSERT_THAT(zipListTupleIt, ZipViewAtSentinel(zipListTupleEnd));
  176. }
  177. TEST_F(RangesViewTestFixture, ZipView_CanIteratOverMultipleContainers_Succeeds)
  178. {
  179. AZStd::string_view sourceView{ "abcdef" };
  180. AZStd::vector intVector{ 1, 2, 3, 4, 5 };
  181. AZStd::list<uint32_t> uintList{ 2, 4, 6, 8, 10 };
  182. constexpr int expectedIterations = 5;
  183. int iterationCount{};
  184. for (auto [charX, intY, uintZ] : (AZStd::ranges::views::zip(sourceView, AZStd::move(intVector), AZStd::move(uintList))))
  185. {
  186. ++iterationCount;
  187. switch (charX)
  188. {
  189. case 'a':
  190. EXPECT_EQ(1, intY);
  191. EXPECT_EQ(2, uintZ);
  192. break;
  193. case 'b':
  194. EXPECT_EQ(2, intY);
  195. EXPECT_EQ(4, uintZ);
  196. break;
  197. case 'c':
  198. EXPECT_EQ(3, intY);
  199. EXPECT_EQ(6, uintZ);
  200. break;
  201. case 'd':
  202. EXPECT_EQ(4, intY);
  203. EXPECT_EQ(8, uintZ);
  204. break;
  205. case 'e':
  206. EXPECT_EQ(5, intY);
  207. EXPECT_EQ(10, uintZ);
  208. break;
  209. default:
  210. ADD_FAILURE() << "Unexpected character value " << charX << " found when iterating zip view";
  211. }
  212. }
  213. EXPECT_EQ(expectedIterations, iterationCount);
  214. }
  215. TEST_F(RangesViewTestFixture, SplitView_CanSplitPatterns_Succeeds)
  216. {
  217. AZStd::string_view emptyView{ "" };
  218. auto splitView = AZStd::ranges::views::split(emptyView, " ");
  219. auto splitIt = splitView.begin();
  220. EXPECT_EQ(splitView.end(), splitIt);
  221. AZStd::string_view testView1{ "Hello" };
  222. auto splitViewCharPattern = AZStd::ranges::views::split(testView1, ' ');
  223. auto splitCharIt = splitViewCharPattern.begin();
  224. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  225. EXPECT_EQ("Hello", AZStd::string_view(*splitCharIt));
  226. ++splitCharIt;
  227. EXPECT_EQ(splitViewCharPattern.end(), splitCharIt);
  228. AZStd::string_view testView2{ "Hello World" };
  229. splitViewCharPattern = AZStd::ranges::views::split(testView2, ' ');
  230. splitCharIt = splitViewCharPattern.begin();
  231. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  232. EXPECT_EQ("Hello", AZStd::string_view(*splitCharIt));
  233. ++splitCharIt;
  234. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  235. EXPECT_EQ("World", AZStd::string_view(*splitCharIt));
  236. ++splitCharIt;
  237. EXPECT_EQ(splitViewCharPattern.end(), splitCharIt);
  238. AZStd::string_view testView3{ "Hello World Moon" };
  239. splitViewCharPattern = AZStd::ranges::views::split(testView3, ' ');
  240. splitCharIt = splitViewCharPattern.begin();
  241. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  242. EXPECT_EQ("Hello", AZStd::string_view(*splitCharIt));
  243. ++splitCharIt;
  244. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  245. EXPECT_EQ("World", AZStd::string_view(*splitCharIt));
  246. ++splitCharIt;
  247. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  248. EXPECT_EQ("Moon", AZStd::string_view(*splitCharIt));
  249. ++splitCharIt;
  250. EXPECT_EQ(splitViewCharPattern.end(), splitCharIt);
  251. AZStd::string_view testView4{ "Hello World Moon " };
  252. splitViewCharPattern = AZStd::ranges::views::split(testView4, ' ');
  253. splitCharIt = splitViewCharPattern.begin();
  254. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  255. EXPECT_EQ("Hello", AZStd::string_view(*splitCharIt));
  256. ++splitCharIt;
  257. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  258. EXPECT_EQ("World", AZStd::string_view(*splitCharIt));
  259. ++splitCharIt;
  260. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  261. EXPECT_EQ("Moon", AZStd::string_view(*splitCharIt));
  262. ++splitCharIt;
  263. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  264. EXPECT_EQ("", AZStd::string_view(*splitCharIt));
  265. ++splitCharIt;
  266. EXPECT_EQ(splitViewCharPattern.end(), splitCharIt);
  267. AZStd::string_view testView5{ "Hello World Moon " };
  268. splitViewCharPattern = AZStd::ranges::views::split(testView5, ' ');
  269. splitCharIt = splitViewCharPattern.begin();
  270. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  271. EXPECT_EQ("Hello", AZStd::string_view(*splitCharIt));
  272. ++splitCharIt;
  273. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  274. EXPECT_EQ("World", AZStd::string_view(*splitCharIt));
  275. ++splitCharIt;
  276. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  277. EXPECT_EQ("Moon", AZStd::string_view(*splitCharIt));
  278. ++splitCharIt;
  279. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  280. EXPECT_EQ("", AZStd::string_view(*splitCharIt));
  281. ++splitCharIt;
  282. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  283. EXPECT_EQ("", AZStd::string_view(*splitCharIt));
  284. ++splitCharIt;
  285. EXPECT_EQ(splitViewCharPattern.end(), splitCharIt);
  286. AZStd::string_view testView6{ "Hello World Moon" };
  287. splitViewCharPattern = AZStd::ranges::views::split(testView6, ' ');
  288. splitCharIt = splitViewCharPattern.begin();
  289. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  290. EXPECT_EQ("Hello", AZStd::string_view(*splitCharIt));
  291. ++splitCharIt;
  292. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  293. EXPECT_EQ("", AZStd::string_view(*splitCharIt));
  294. ++splitCharIt;
  295. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  296. EXPECT_EQ("World", AZStd::string_view(*splitCharIt));
  297. ++splitCharIt;
  298. ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
  299. EXPECT_EQ("Moon", AZStd::string_view(*splitCharIt));
  300. ++splitCharIt;
  301. EXPECT_EQ(splitViewCharPattern.end(), splitCharIt);
  302. }
  303. TEST_F(RangesViewTestFixture, SplitView_SplitsFromNonString_Succeeds)
  304. {
  305. const AZStd::vector<int> testVector{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  306. // Split the vector on 3
  307. auto splitView = testVector | AZStd::ranges::views::split(3);
  308. auto splitIt = splitView.begin();
  309. ASSERT_NE(splitView.end(), splitIt);
  310. auto splitSubrange = *splitIt;
  311. {
  312. AZStd::array expectedValue{ 1, 2 };
  313. EXPECT_TRUE(AZStd::ranges::equal(expectedValue, splitSubrange));
  314. }
  315. ++splitIt;
  316. ASSERT_NE(splitView.end(), splitIt);
  317. splitSubrange = *splitIt;
  318. {
  319. AZStd::array expectedValue{ 4, 5, 6, 7, 8, 9, 10 };
  320. EXPECT_TRUE(AZStd::ranges::equal(expectedValue, splitSubrange));
  321. }
  322. }
  323. TEST_F(RangesViewTestFixture, JoinView_IteratesOverInnerViews_Succeeds)
  324. {
  325. constexpr AZStd::string_view expectedString = "HelloWorldMoonSun";
  326. using Rope = AZStd::fixed_vector<AZStd::string_view, 32>;
  327. Rope rope{ "Hello", "World", "Moon", "Sun" };
  328. AZStd::fixed_string<128> accumString;
  329. for (auto&& charElement : AZStd::ranges::join_view(rope))
  330. {
  331. accumString.push_back(charElement);
  332. }
  333. EXPECT_EQ(expectedString, accumString);
  334. }
  335. TEST_F(RangesViewTestFixture, JoinView_IteratesCanIterateOverSplitView_Succeeds)
  336. {
  337. constexpr AZStd::string_view expectedString = "HelloWorldMoonSun";
  338. constexpr AZStd::string_view splitExpression = "Hello,World,Moon,Sun";
  339. AZStd::fixed_string<128> accumString;
  340. for (auto&& charElement : AZStd::ranges::views::join(AZStd::ranges::views::split(splitExpression, ',')))
  341. {
  342. accumString.push_back(charElement);
  343. }
  344. EXPECT_EQ(expectedString, accumString);
  345. }
  346. TEST_F(RangesViewTestFixture, JoinView_IterSwapCustomization_Succeeds)
  347. {
  348. AZStd::fixed_vector<AZStd::string, 8> testVector1{ "World", "Hello" };
  349. AZStd::fixed_vector<AZStd::string, 8> testVector2{ "Value", "First" };
  350. auto joinView1 = AZStd::ranges::views::join(testVector1);
  351. auto joinView2 = AZStd::ranges::views::join(testVector2);
  352. auto joinViewIter1 = joinView1.begin();
  353. auto joinViewIter2 = joinView2.begin();
  354. // swaps the 'W' and 'V'
  355. AZStd::ranges::iter_swap(joinViewIter1, joinViewIter2);
  356. // swaps the 'H' and 'F' of the second string of each vector
  357. /* Commented in out original AZStd::ranges::advanceand the second AZStd::ranges::iter_swap cll
  358. AZStd::ranges::advance(joinViewIter1, 5, joinView1.end());
  359. AZStd::ranges::advance(joinViewIter2, 5, joinView2.end());
  360. AZStd::ranges::iter_swap(joinViewIter1, joinViewIter2);
  361. //
  362. // There is a bug in MSVC compiler when swapping a char& that occurs only in profile configuration
  363. // The AZStd::ranges::iter_swap eventually calls AZStd::ranges::swap which should swap 5th characters
  364. // of each vector.
  365. // But instead the testVector2[5] gets the correct character of testVector1[5] `H` swapped to it.
  366. // But the testVector1[5] seems to get the character from testVector2[0] which is now 'V' swapped to it
  367. //
  368. // I believe the MSVC is probably performing a bad optimization where it comes to the read address
  369. // of the *joinViewIter2(char&) iterator
  370. //
  371. // The workaround that is working is to use AZStd::ranges::next to create a new join_view::iterator
  372. // and perform AZStd::ranges::iter_swap on those objects.
  373. */
  374. AZStd::ranges::iter_swap(AZStd::ranges::next(joinViewIter1, 5, joinView1.end()), AZStd::ranges::next(joinViewIter2, 5, joinView2.end()));
  375. EXPECT_EQ("Vorld", testVector1[0]);
  376. EXPECT_EQ("Fello", testVector1[1]);
  377. EXPECT_EQ("Walue", testVector2[0]);
  378. EXPECT_EQ("Hirst", testVector2[1]);
  379. }
  380. TEST_F(RangesViewTestFixture, JoinView_IterMoveCustomization_Succeeds)
  381. {
  382. using StringWrapper = AZStd::ranges::single_view<AZStd::string>;
  383. AZStd::fixed_vector<StringWrapper, 8> testVector1{ StringWrapper{"5"}, StringWrapper{"10"} };
  384. AZStd::fixed_vector<StringWrapper, 8> testVector2{ StringWrapper{"15"}, StringWrapper{"20"} };
  385. auto joinView1 = AZStd::ranges::views::join(testVector1);
  386. auto joinView2 = AZStd::ranges::views::join(testVector2);
  387. auto joinViewIter1 = joinView1.begin();
  388. auto joinViewIter2 = joinView2.begin();
  389. AZStd::string value = AZStd::ranges::iter_move(joinViewIter1);
  390. EXPECT_EQ("5", value);
  391. EXPECT_TRUE((*joinViewIter1).empty());
  392. ++joinViewIter2;
  393. value = AZStd::ranges::iter_move(joinViewIter2);
  394. EXPECT_EQ("20", value);
  395. EXPECT_TRUE((*joinViewIter2).empty());
  396. }
  397. TEST_F(RangesViewTestFixture, JoinView_ReverseIterationOverRangeOfRanges_Succeeds)
  398. {
  399. constexpr AZStd::string_view expectedString = "nuSnooMdlroWolleH";
  400. using Rope = AZStd::fixed_vector<AZStd::string_view, 32>;
  401. Rope rope{ "Hello", "World", "Moon", "Sun" };
  402. AZStd::fixed_string<128> accumString;
  403. auto joinView = AZStd::ranges::views::join(rope);
  404. // Iterate over view in reverse(can replace normal for loop with range based one, once AZStd::ranges::reverse_view is available)
  405. for (auto revIter = AZStd::ranges::rbegin(joinView); revIter != AZStd::ranges::rend(joinView); ++revIter)
  406. {
  407. accumString.push_back(*revIter);
  408. }
  409. EXPECT_EQ(expectedString, accumString);
  410. }
  411. // join_with_view
  412. TEST_F(RangesViewTestFixture, JoinWithView_IteratesOverRangeOfRangesWithSeparator_Succeeds)
  413. {
  414. constexpr AZStd::string_view expectedString = "Hello, World, Moon, Sun";
  415. using RopeWithSeparator = AZStd::fixed_vector<AZStd::string_view, 32>;
  416. RopeWithSeparator rope{ "Hello", "World", "Moon", "Sun" };
  417. AZStd::fixed_string<128> accumString;
  418. // Protip: Do not use a string literal directly with join_with
  419. // A string literal is actually a reference to a C array that includes the null-terminator character
  420. // Convert it to a string_view
  421. using namespace AZStd::literals::string_view_literals;
  422. for (auto&& charElement : AZStd::ranges::views::join_with(rope, ", "_sv))
  423. {
  424. accumString.push_back(charElement);
  425. }
  426. EXPECT_EQ(expectedString, accumString);
  427. }
  428. TEST_F(RangesViewTestFixture, JoinWithView_IteratesCanIterateOverSplitView_Succeeds)
  429. {
  430. constexpr AZStd::string_view expectedString = "Hello World Moon Sun";
  431. constexpr AZStd::string_view splitExpression = "Hello,World,Moon,Sun";
  432. {
  433. // Test range adaptor with char literal
  434. AZStd::fixed_string<128> accumString;
  435. for (auto&& charElement : splitExpression | AZStd::ranges::views::split(',') | AZStd::ranges::views::join_with(' '))
  436. {
  437. accumString.push_back(charElement);
  438. }
  439. EXPECT_EQ(expectedString, accumString);
  440. }
  441. {
  442. // Test range adaptor with string_view
  443. // DO NOT use string literal as it is deduced as an array that includes the NUL character
  444. // as part of the range
  445. AZStd::fixed_string<128> accumString;
  446. using namespace AZStd::literals::string_view_literals;
  447. for (auto&& charElement : splitExpression | AZStd::ranges::views::split(',') | AZStd::ranges::views::join_with(" "_sv))
  448. {
  449. accumString.push_back(charElement);
  450. }
  451. EXPECT_EQ(expectedString, accumString);
  452. }
  453. }
  454. TEST_F(RangesViewTestFixture, JoinWithView_ReverseIterationOverRangeOfRanges_Succeeds)
  455. {
  456. constexpr AZStd::string_view expectedString = "nuS ,nooM ,dlroW ,olleH";
  457. using RopeWithSeparator = AZStd::fixed_vector<AZStd::string_view, 32>;
  458. RopeWithSeparator rope{ "Hello", "World", "Moon", "Sun" };
  459. AZStd::fixed_string<128> accumString;
  460. using namespace AZStd::literals::string_view_literals;
  461. auto joinWithView = AZStd::ranges::views::join_with(rope, ", "_sv);
  462. // Iterate over view in reverse(can replace normal for loop with range based one, once AZStd::ranges::reverse_view is available)
  463. for (auto revIter = AZStd::ranges::rbegin(joinWithView); revIter != AZStd::ranges::rend(joinWithView); ++revIter)
  464. {
  465. accumString.push_back(*revIter);
  466. }
  467. EXPECT_EQ(expectedString, accumString);
  468. }
  469. TEST_F(RangesViewTestFixture, JoinWithView_IsConstexpr_Succeeds)
  470. {
  471. constexpr AZStd::string_view expectedString = "Hello, World, Moon, Sun";
  472. constexpr AZStd::array<AZStd::string_view, 4> rope{ "Hello", "World", "Moon", "Sun" };
  473. using namespace AZStd::literals::string_view_literals;
  474. constexpr AZStd::fixed_string<128> accumString(AZStd::from_range, rope | AZStd::views::join_with(", "_sv));
  475. static_assert(accumString == expectedString);
  476. }
  477. // elements_view
  478. TEST_F(RangesViewTestFixture, ElementsView_CanIterateVectorOfTuple_Succeeds)
  479. {
  480. using TestTuple = AZStd::tuple<int, AZStd::string, bool>;
  481. AZStd::vector testVector{ TestTuple{1, "hello", false}, TestTuple{2, "world", true},
  482. TestTuple{3, "Moon", false} };
  483. auto firstElementView = AZStd::ranges::views::elements<0>(testVector);
  484. auto firstElementBegin = AZStd::ranges::begin(firstElementView);
  485. auto firstElementEnd = AZStd::ranges::end(firstElementView);
  486. EXPECT_NE(firstElementEnd, firstElementBegin);
  487. ASSERT_EQ(3, firstElementView.size());
  488. EXPECT_EQ(1, firstElementView[0]);
  489. EXPECT_EQ(2, firstElementView[1]);
  490. EXPECT_EQ(3, firstElementView[2]);
  491. auto secondElementView = AZStd::ranges::views::elements<1>(testVector);
  492. ASSERT_EQ(3, secondElementView.size());
  493. EXPECT_EQ("hello", secondElementView[0]);
  494. EXPECT_EQ("world", secondElementView[1]);
  495. EXPECT_EQ("Moon", secondElementView[2]);
  496. auto thirdElementView = AZStd::ranges::views::elements<2>(testVector);
  497. ASSERT_EQ(3, thirdElementView.size());
  498. EXPECT_FALSE(thirdElementView[0]);
  499. EXPECT_TRUE(thirdElementView[1]);
  500. EXPECT_FALSE(thirdElementView[2]);
  501. using TestPair = AZStd::pair<AZStd::string, int>;
  502. AZStd::vector testPairVector{ TestPair{"hello", 5}, TestPair{"world", 10}, TestPair{"Sun", 15} };
  503. using ElementsViewBase = AZStd::ranges::views::all_t<decltype((testPairVector))>;
  504. using ElementsViewType = AZStd::ranges::elements_view<ElementsViewBase, 0>;
  505. constexpr AZStd::string_view expectedString = "helloworldSun";
  506. AZStd::string accumString;
  507. for (auto&& stringValue : ElementsViewType(testPairVector))
  508. {
  509. accumString += stringValue;
  510. }
  511. EXPECT_EQ(expectedString, accumString);
  512. }
  513. TEST_F(RangesViewTestFixture, ElementsView_KeysAlias_CanIterateAssociativeContainerKeyType)
  514. {
  515. using PairType = AZStd::pair<int, const char*>;
  516. AZStd::map testMap{ PairType{1, "Hello"}, PairType{2, "World"}, PairType{3, "Sun"}, PairType{45, "RandomText"} };
  517. int accumResult{};
  518. for (int key : AZStd::ranges::views::keys(testMap))
  519. {
  520. accumResult += key;
  521. }
  522. EXPECT_EQ(51, accumResult);
  523. }
  524. TEST_F(RangesViewTestFixture, ElementsView_ValuesAlias_CanIterateAssociativeContainerMappedType)
  525. {
  526. using PairType = AZStd::pair<int, const char*>;
  527. AZStd::map testMap{ PairType{1, "Hello"}, PairType{2, "World"}, PairType{3, "Sun"}, PairType{45, "RandomText"} };
  528. AZStd::string accumResult{};
  529. for (const char* value : AZStd::ranges::views::values(testMap))
  530. {
  531. accumResult += value;
  532. }
  533. EXPECT_EQ("HelloWorldSunRandomText", accumResult);
  534. }
  535. TEST_F(RangesViewTestFixture, ElementsView_ReverseIteration_Succeeds)
  536. {
  537. using PairType = AZStd::pair<int, const char*>;
  538. AZStd::map testMap{ PairType{1, "Hello"}, PairType{2, "World"}, PairType{3, "Sun"}, PairType{45, "RandomText"} };
  539. AZStd::string accumResult{};
  540. auto valuesView = AZStd::ranges::views::values(testMap);
  541. for (auto revIter = AZStd::ranges::rbegin(valuesView); revIter != AZStd::ranges::rend(valuesView); ++revIter)
  542. {
  543. accumResult += *revIter;
  544. }
  545. EXPECT_EQ("RandomTextSunWorldHello", accumResult);
  546. }
  547. TEST_F(RangesViewTestFixture, TransformView_TransformStringArrayOfNumbers_ToIntView_Succeeds)
  548. {
  549. constexpr int expectedResult = 1 + 2 + 3;
  550. AZStd::vector stringArray{ AZStd::string("1"), AZStd::string("2"), AZStd::string("3") };
  551. auto StringToInt = [](const AZStd::string& numString) -> int
  552. {
  553. constexpr int base = 10;
  554. return static_cast<int>(strtoll(numString.c_str(), nullptr, base));
  555. };
  556. int accumResult{};
  557. for (int value : stringArray | AZStd::ranges::views::transform(StringToInt))
  558. {
  559. accumResult += value;
  560. }
  561. EXPECT_EQ(expectedResult, accumResult);
  562. }
  563. TEST_F(RangesViewTestFixture, TransformView_GetMemberFromRangeElement_Succeeds)
  564. {
  565. struct IntWrapper
  566. {
  567. int m_value{};
  568. };
  569. constexpr int expectedResult = 1 + 2 + 3;
  570. AZStd::vector testArray{ IntWrapper{ 1 }, IntWrapper{ 2 }, IntWrapper{ 3 } };
  571. auto GetValueMember = [](const IntWrapper& wrapper) -> decltype(auto)
  572. {
  573. return wrapper.m_value;
  574. };
  575. int accumResult{};
  576. for (int value : testArray | AZStd::ranges::views::transform(GetValueMember))
  577. {
  578. accumResult += value;
  579. }
  580. EXPECT_EQ(expectedResult, accumResult);
  581. }
  582. TEST_F(RangesViewTestFixture, CommonView_CanPassDifferentIteratorAndSentinelTypes_ToInsertFunction)
  583. {
  584. constexpr AZStd::string_view expectedString = "Hello,World,Moon,Sun";
  585. static constexpr auto arrayOfLiterals{ AZStd::to_array<AZStd::string_view>({"Hello", "World", "Moon", "Sun"}) };
  586. auto commonView = AZStd::ranges::views::common(AZStd::ranges::views::join_with(arrayOfLiterals, ','));
  587. AZStd::fixed_string<128> accumString{ commonView.begin(), commonView.end() };
  588. EXPECT_EQ(expectedString, accumString);
  589. }
  590. TEST_F(RangesViewTestFixture, FilterView_CanFilterWhiteSpaceFromString_Succeeds)
  591. {
  592. constexpr AZStd::string_view expectedString = "Hello,World,Moon,Sun";
  593. constexpr AZStd::string_view testString = "Hello, World, Moon, Sun";
  594. AZStd::string resultString;
  595. for (char elem : testString | AZStd::ranges::views::filter([](char element) { return !::isspace(element); }))
  596. {
  597. resultString += elem;
  598. }
  599. EXPECT_EQ(expectedString, resultString);
  600. }
  601. TEST_F(RangesViewTestFixture, FilterView_CanIterateBidirectionalRangeInReverse_Succeeds)
  602. {
  603. constexpr AZStd::string_view expectedString = "nuS,nooM,dlroW,olleH";
  604. constexpr AZStd::string_view testString = "Hello, World, Moon, Sun";
  605. AZStd::ranges::filter_view testFilterView(testString, [](char element) { return !::isspace(element); });
  606. AZStd::string resultString;
  607. for (auto it = AZStd::ranges::rbegin(testFilterView); it != AZStd::ranges::rend(testFilterView); ++it)
  608. {
  609. resultString += *it;
  610. }
  611. EXPECT_EQ(expectedString, resultString);
  612. }
  613. TEST_F(RangesViewTestFixture, FilterView_CanAccessPredicate)
  614. {
  615. const AZStd::ranges::filter_view testFilterView("", [](char element) { return !::isspace(element); });
  616. const auto& filterViewPredicate = testFilterView.pred();
  617. EXPECT_TRUE(filterViewPredicate('a'));
  618. EXPECT_FALSE(filterViewPredicate(' '));
  619. EXPECT_FALSE(filterViewPredicate('\n'));
  620. }
  621. TEST_F(RangesViewTestFixture, ReverseView_CanIterateOverBidirectionalRange)
  622. {
  623. constexpr AZStd::string_view expectedString = "nuS,nooM,dlroW,olleH";
  624. constexpr AZStd::string_view testString = "Hello,World,Moon,Sun";
  625. const AZStd::ranges::reverse_view testReverseView(testString);
  626. EXPECT_EQ(testString.size(), testReverseView.size());
  627. AZStd::string resultString{ testReverseView.begin(), testReverseView.end() };
  628. EXPECT_EQ(expectedString, resultString);
  629. }
  630. TEST_F(RangesViewTestFixture, ReverseView_ReverseOfReverse_ReturnsOriginalView)
  631. {
  632. constexpr AZStd::string_view testString = "Hello,World,Moon,Sun";
  633. AZStd::string resultString = AZStd::views::reverse(testString) | AZStd::views::reverse;
  634. EXPECT_EQ(testString, resultString);
  635. }
  636. TEST_F(RangesViewTestFixture, ReverseView_SubrangeOfReverseIterators_ReturnsSubrangeOfOriginalIterators)
  637. {
  638. constexpr AZStd::string_view testString = "Hello,World,Moon,Sun";
  639. using namespace AZStd::literals::string_view_literals;
  640. // form a subrange of reverse iterators to "World"
  641. auto testSubrange = AZStd::ranges::subrange(testString.begin() + 6, testString.begin() + 11);
  642. EXPECT_TRUE(AZStd::ranges::equal(testSubrange, "World"_sv));
  643. auto testSubrangeOfReverse = testSubrange | AZStd::views::reverse;
  644. EXPECT_TRUE(AZStd::ranges::equal(testSubrangeOfReverse, "dlroW"_sv));
  645. auto testSubrangeOfReverseReverse = testSubrangeOfReverse | AZStd::views::reverse;
  646. static_assert(AZStd::same_as<decltype(testSubrange), decltype(testSubrangeOfReverseReverse)>);
  647. EXPECT_TRUE(AZStd::ranges::equal(testSubrangeOfReverseReverse, "World"_sv));
  648. }
  649. TEST_F(RangesViewTestFixture, CountedView_CanCreateSubrange_FromIteratorAndCounted)
  650. {
  651. using namespace AZStd::literals::string_view_literals;
  652. constexpr AZStd::string_view testString = "Hello,World,Moon,Sun";
  653. constexpr auto testView = AZStd::views::counted(testString.begin() + 6, 5);
  654. EXPECT_TRUE(AZStd::ranges::equal(testView, "World"_sv));
  655. }
  656. TEST_F(RangesViewTestFixture, CountedView_CanCreateSubrange_FromNonContiguousIterator)
  657. {
  658. using namespace AZStd::literals::string_view_literals;
  659. AZStd::list<int> testContainer{ 1, 2, 3, 4, 5 };
  660. auto testView = AZStd::views::counted(testContainer.begin(), 3);
  661. constexpr auto expectedResult = AZStd::to_array<int>({ 1, 2, 3 });
  662. EXPECT_TRUE(AZStd::ranges::equal(testView, expectedResult));
  663. }
  664. TEST_F(RangesViewTestFixture, AsRvalueView_MovesContainerElements_Succeeds)
  665. {
  666. using namespace AZStd::literals::string_view_literals;
  667. AZStd::vector testStringContainer{ AZStd::string("Hello"), AZStd::string("World") };
  668. AZStd::list movedStringContainer{ AZStd::from_range, testStringContainer | AZStd::views::as_rvalue };
  669. EXPECT_THAT(movedStringContainer, ::testing::ElementsAre(AZStd::string_view("Hello"), AZStd::string_view("World")));
  670. EXPECT_THAT(testStringContainer, ::testing::ElementsAre(AZStd::string_view(), AZStd::string_view()));
  671. }
  672. namespace RangesViewTestInternal
  673. {
  674. struct ConstMutableContainer
  675. {
  676. struct iterator
  677. {
  678. using value_type = int;
  679. using iterator_concept = AZStd::bidirectional_iterator_tag;
  680. using iterator_category = AZStd::bidirectional_iterator_tag;
  681. const int& operator*() const { return m_charElement; }
  682. int& operator*() { return m_intElement; }
  683. const int* operator->() const { return &m_charElement; }
  684. int* operator->() { return &m_intElement; }
  685. iterator& operator++() { ++m_intElement; return *this; }
  686. iterator operator++(int) { iterator tmp(*this); ++m_intElement; return tmp; }
  687. iterator& operator--() { --m_intElement; return *this; }
  688. iterator operator--(int) { iterator tmp(*this); --m_intElement; return tmp; }
  689. bool operator==(const iterator& y) const { return m_intElement == y.m_intElement; }
  690. bool operator!=(const iterator& y) const { return !operator==(y); }
  691. ptrdiff_t operator-(const iterator& y) const { return m_intElement - y.m_intElement; }
  692. int m_charElement = 'A';
  693. int m_intElement = 55;
  694. };
  695. using iterator = iterator;
  696. using const_iterator = const iterator;
  697. iterator begin() { return iterator{}; }
  698. iterator begin() const { return iterator{}; }
  699. iterator end() { return iterator{ 'A', 60 }; }
  700. iterator end() const { return iterator{ 'A', 60 }; }
  701. };
  702. }
  703. TEST_F(RangesViewTestFixture, AsConstView_ActAsConstantViewOfContainerSucceeds)
  704. {
  705. RangesViewTestInternal::ConstMutableContainer testContainer;
  706. AZStd::ranges::iterator_t<RangesViewTestInternal::ConstMutableContainer> foundIter = testContainer.begin();
  707. EXPECT_EQ(55, *foundIter);
  708. auto constView = testContainer | AZStd::views::as_const;
  709. ASSERT_NE(constView.end(), constView.begin());
  710. for (auto elem : constView)
  711. {
  712. EXPECT_EQ('A', elem);
  713. }
  714. AZStd::string_view testString;
  715. [[maybe_unused]] auto constStringView = testString | AZStd::views::as_const;
  716. static_assert(AZStd::same_as<decltype(testString), decltype(constStringView)>);
  717. }
  718. TEST_F(RangesViewTestFixture, IotaView_CanGenerateRangeUpToBound)
  719. {
  720. constexpr AZStd::array expectedValues{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
  721. AZStd::vector testValue(AZStd::from_range, AZStd::views::iota(0, 10));
  722. EXPECT_THAT(testValue, ::testing::ElementsAreArray(expectedValues));
  723. }
  724. TEST_F(RangesViewTestFixture, IotaView_CanGenerateUnboundedValues)
  725. {
  726. constexpr AZStd::array expectedValues{ -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
  727. constexpr int breakValue = 11;
  728. AZStd::vector<int> resultValues;
  729. for (int i : AZStd::views::iota(-3))
  730. {
  731. resultValues.push_back(i);
  732. if (i >= breakValue)
  733. {
  734. break;
  735. }
  736. }
  737. EXPECT_THAT(resultValues, ::testing::ElementsAreArray(expectedValues));
  738. }
  739. TEST_F(RangesViewTestFixture, IotaView_WithZipView_CanGenerateIndexForEveryElementOfOtherView)
  740. {
  741. const AZStd::map expectedValues{ AZStd::pair{0, 'H'}, {1, 'e'}, { 2, 'l'}, { 3, 'l'}, {4, 'o'} };
  742. constexpr AZStd::string_view testString = "Hello";
  743. AZStd::map<int, char> resultValues;
  744. for (auto [i, elem] : AZStd::views::zip(AZStd::views::iota(0), testString))
  745. {
  746. resultValues[static_cast<int>(i)] = elem;
  747. }
  748. EXPECT_THAT(resultValues, ::testing::ElementsAreArray(expectedValues));
  749. }
  750. TEST_F(RangesViewTestFixture, RepeatView_CanGenerateRangeUpToBound)
  751. {
  752. using namespace AZStd::literals::string_view_literals;
  753. constexpr AZStd::array expectedValues{ "Hello"_sv, "Hello"_sv, "Hello"_sv };
  754. AZStd::vector testValue(AZStd::from_range, AZStd::views::repeat("Hello"_sv, 3));
  755. EXPECT_THAT(testValue, ::testing::ElementsAreArray(expectedValues));
  756. }
  757. TEST_F(RangesViewTestFixture, RepeatView_CanGenerateUnboundedValues)
  758. {
  759. using namespace AZStd::literals::string_view_literals;
  760. constexpr int maxIterations = 4;
  761. constexpr AZStd::array<AZStd::string_view, maxIterations> expectedValues{ "Hello"_sv, "Hello"_sv, "Hello"_sv, "Hello"_sv };
  762. AZStd::vector<AZStd::string_view> resultValues;
  763. int i = 0;
  764. for (AZStd::string_view testView : AZStd::views::repeat("Hello"_sv))
  765. {
  766. if (i++ >= maxIterations)
  767. {
  768. break;
  769. }
  770. resultValues.push_back(testView);
  771. }
  772. EXPECT_THAT(resultValues, ::testing::ElementsAreArray(expectedValues));
  773. }
  774. }