UndoStack.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  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/std/smart_ptr/unique_ptr.h>
  9. #include <AzTest/AzTest.h>
  10. #include <AzToolsFramework/Undo/UndoSystem.h>
  11. using namespace AZ;
  12. using namespace AzToolsFramework;
  13. using namespace AzToolsFramework::UndoSystem;
  14. namespace UnitTest
  15. {
  16. class SequencePointTest
  17. : public URSequencePoint
  18. {
  19. public:
  20. AZ_CLASS_ALLOCATOR(SequencePointTest, AZ::SystemAllocator)
  21. SequencePointTest(AZStd::string friendlyName, URCommandID id)
  22. : URSequencePoint(friendlyName, id)
  23. {}
  24. bool Changed() const override { return true; }
  25. void Redo() override { m_redoCalled = true; }
  26. void Undo() override { m_undoCalled = true; }
  27. using URSequencePoint::RemoveChild;
  28. bool m_redoCalled = false;
  29. bool m_undoCalled = false;
  30. };
  31. class DifferentTypeSequencePointTest
  32. : public URSequencePoint
  33. {
  34. public:
  35. AZ_RTTI(DifferentTypeSequencePointTest, "{D7A42B6F-DCF8-443F-B4F1-57731B1D3CB8}")
  36. DifferentTypeSequencePointTest(AZStd::string friendlyName, URCommandID id)
  37. : URSequencePoint(friendlyName, id)
  38. {}
  39. bool Changed() const override { return true; }
  40. };
  41. ///////////////////////////////////////////////////////////////////////////
  42. // URSequencePoint
  43. ///////////////////////////////////////////////////////////////////////////
  44. TEST(URSequencePoint, Find_IdAndTypeNotPresent_ExpectNullptr)
  45. {
  46. auto parent = AZStd::make_unique<SequencePointTest>("Parent", 0);
  47. auto child_1 = aznew SequencePointTest("Child", 1);
  48. auto child_2 = aznew SequencePointTest("Child", 2);
  49. auto child_3 = aznew SequencePointTest("Child", 3);
  50. auto child_1_1 = aznew SequencePointTest("Child", 4);
  51. auto child_1_2 = aznew SequencePointTest("Child", 5);
  52. child_1->SetParent(parent.get());
  53. child_2->SetParent(parent.get());
  54. child_3->SetParent(parent.get());
  55. child_1_1->SetParent(child_1);
  56. child_1_2->SetParent(child_1);
  57. auto result = parent->Find(6, AZ::Uuid::Create());
  58. EXPECT_FALSE(result);
  59. }
  60. TEST(URSequencePoint, Find_TypeNotPresent_ExpectNullptr)
  61. {
  62. auto parent = AZStd::make_unique<SequencePointTest>("Parent", 0);
  63. auto child_1 = aznew SequencePointTest("Child", 1);
  64. auto child_2 = aznew SequencePointTest("Child", 2);
  65. auto child_3 = aznew SequencePointTest("Child", 3);
  66. auto child_1_1 = aznew SequencePointTest("Child", 4);
  67. auto child_1_2 = aznew SequencePointTest("Child", 5);
  68. child_1->SetParent(parent.get());
  69. child_2->SetParent(parent.get());
  70. child_3->SetParent(parent.get());
  71. child_1_1->SetParent(child_1);
  72. child_1_2->SetParent(child_1);
  73. auto result = parent->Find(5, AZ::Uuid::Create());
  74. EXPECT_FALSE(result);
  75. }
  76. TEST(URSequencePoint, Find_IdNotPresent_ExpectNullptr)
  77. {
  78. auto parent = AZStd::make_unique<SequencePointTest>("Parent", 0);
  79. auto child_1 = aznew SequencePointTest("Child", 1);
  80. auto child_2 = aznew DifferentTypeSequencePointTest("Child", 2);
  81. auto child_3 = aznew SequencePointTest("Child", 3);
  82. auto child_1_1 = aznew SequencePointTest("Child", 4);
  83. auto child_1_2 = aznew SequencePointTest("Child", 5);
  84. child_1->SetParent(parent.get());
  85. child_2->SetParent(parent.get());
  86. child_3->SetParent(parent.get());
  87. child_1_1->SetParent(child_1);
  88. child_1_2->SetParent(child_1);
  89. auto result = parent->Find(6, AZ::Uuid("{D7A42B6F-DCF8-443F-B4F1-57731B1D3CB8}"));
  90. EXPECT_FALSE(result);
  91. }
  92. TEST(URSequencePoint, Find_MatchIsDirectChild_IdFound)
  93. {
  94. auto parent = AZStd::make_unique<SequencePointTest>("Parent", 0);
  95. auto child_1 = aznew SequencePointTest("Child", 1);
  96. auto child_2 = aznew DifferentTypeSequencePointTest("Child", static_cast<AZ::u64>(2));
  97. auto child_3 = aznew SequencePointTest("Child", 3);
  98. auto child_1_1 = aznew SequencePointTest("Child", 4);
  99. auto child_1_2 = aznew SequencePointTest("Child", 5);
  100. child_1->SetParent(parent.get());
  101. child_2->SetParent(parent.get());
  102. child_3->SetParent(parent.get());
  103. child_1_1->SetParent(child_1);
  104. child_1_2->SetParent(child_1);
  105. auto result = parent->Find(2, AZ::AzTypeInfo<DifferentTypeSequencePointTest>::Uuid());
  106. EXPECT_EQ(result, child_2);
  107. }
  108. TEST(URSequencePoint, Find_IdIsIndirectChild_IdFound)
  109. {
  110. auto parent = AZStd::make_unique<SequencePointTest>("Parent", 0);
  111. auto child_1 = aznew SequencePointTest("Child", 1);
  112. auto child_2 = aznew DifferentTypeSequencePointTest("Child", static_cast<AZ::u64>(2));
  113. auto child_3 = aznew SequencePointTest("Child", 3);
  114. auto child_1_1 = aznew SequencePointTest("Child", 4);
  115. auto child_1_2 = aznew DifferentTypeSequencePointTest("Child", 5);
  116. child_1->SetParent(parent.get());
  117. child_2->SetParent(parent.get());
  118. child_3->SetParent(parent.get());
  119. child_1_1->SetParent(child_1);
  120. child_1_2->SetParent(child_1);
  121. auto result = parent->Find(5, AZ::AzTypeInfo<DifferentTypeSequencePointTest>::Uuid());
  122. EXPECT_EQ(result, child_1_2);
  123. }
  124. TEST(URSequencePoint, RemoveChild)
  125. {
  126. auto parent = AZStd::make_unique<SequencePointTest>("Parent", 0);
  127. auto child_1 = aznew SequencePointTest("Child", 1);
  128. auto child_2 = aznew SequencePointTest("Child", 2);
  129. auto child_3 = aznew SequencePointTest("Child", 3);
  130. auto child_4 = aznew SequencePointTest("Child", 4);
  131. auto child_5 = aznew SequencePointTest("Child", 5);
  132. child_1->SetParent(parent.get());
  133. child_2->SetParent(parent.get());
  134. child_3->SetParent(parent.get());
  135. child_4->SetParent(parent.get());
  136. child_5->SetParent(parent.get());
  137. EXPECT_THAT(parent->GetChildren(), ::testing::Pointwise(::testing::Eq(), {child_1, child_2, child_3, child_4, child_5}));
  138. parent->RemoveChild(child_5);
  139. EXPECT_THAT(parent->GetChildren(), ::testing::Pointwise(::testing::Eq(), {child_1, child_2, child_3, child_4}));
  140. delete child_5;
  141. // The other children are owned by parent
  142. }
  143. TEST(URSequencePoint, SetParent_NotChildOfParent)
  144. {
  145. auto parent = AZStd::make_unique<SequencePointTest>("Parent", 0);
  146. auto child = aznew SequencePointTest("Child", 1);
  147. child->SetParent(parent.get());
  148. EXPECT_THAT(parent->GetChildren(), ::testing::Pointwise(::testing::Eq(), {child}));
  149. }
  150. TEST(URSequencePoint, SetParent_AlreadyChildOfParent)
  151. {
  152. auto parent = AZStd::make_unique<SequencePointTest>("Parent", 0);
  153. auto child_1 = aznew SequencePointTest("Child", 1);
  154. auto child_2 = aznew SequencePointTest("Child", 2);
  155. auto child_3 = aznew SequencePointTest("Child", 3);
  156. auto child_4 = aznew SequencePointTest("Child", 4);
  157. auto child_5 = aznew SequencePointTest("Child", 5);
  158. child_1->SetParent(parent.get());
  159. child_2->SetParent(parent.get());
  160. child_3->SetParent(parent.get());
  161. child_4->SetParent(parent.get());
  162. child_5->SetParent(parent.get());
  163. EXPECT_THAT(parent->GetChildren(), ::testing::Pointwise(::testing::Eq(), {child_1, child_2, child_3, child_4, child_5}));
  164. child_5->SetParent(parent.get());
  165. EXPECT_THAT(parent->GetChildren(), ::testing::Pointwise(::testing::Eq(), {child_1, child_2, child_3, child_4, child_5})) << "the parent did not de-dupe its children";
  166. }
  167. TEST(URSequencePoint, SetParent_AlreadyChildOfDifferentParent)
  168. {
  169. auto parent_1 = AZStd::make_unique<SequencePointTest>("Parent", 0);
  170. auto parent_2 = AZStd::make_unique<SequencePointTest>("Parent", 1);
  171. auto child = aznew SequencePointTest("Child", 5);
  172. child->SetParent(parent_1.get());
  173. EXPECT_THAT(parent_1->GetChildren(), ::testing::Pointwise(::testing::Eq(), {child}));
  174. child->SetParent(parent_2.get());
  175. EXPECT_THAT(parent_1->GetChildren(), ::testing::IsEmpty()) << "the original parent did not remove the child";
  176. EXPECT_THAT(parent_2->GetChildren(), ::testing::Pointwise(::testing::Eq(), {child})) << "child was not added to new parent properly";
  177. }
  178. TEST(URSequencePoint, RunUndo_NoChildren_UndoIsCalled)
  179. {
  180. auto object = AZStd::make_unique<SequencePointTest>("Object", 0);
  181. object->m_undoCalled = false;
  182. object->RunUndo();
  183. EXPECT_TRUE(object->m_undoCalled) << "Undo was not called on the object";
  184. }
  185. TEST(URSequencePoint, RunUndo_HasChildren_UndoIsCalled)
  186. {
  187. auto parent = AZStd::make_unique<SequencePointTest>("Parent", 0);
  188. auto child_1 = aznew SequencePointTest("Child", 1);
  189. auto child_2 = aznew SequencePointTest("Child", 2);
  190. auto child_1_1 = aznew SequencePointTest("Child", 3);
  191. auto child_1_2 = aznew SequencePointTest("Child", 4);
  192. parent->m_undoCalled = false;
  193. child_1->SetParent(parent.get());
  194. child_1->m_undoCalled = false;
  195. child_2->SetParent(parent.get());
  196. child_2->m_undoCalled = false;
  197. child_1_1->SetParent(child_1);
  198. child_1_1->m_undoCalled = false;
  199. child_1_2->SetParent(child_1);
  200. child_1_2->m_undoCalled = false;
  201. parent->RunUndo();
  202. EXPECT_TRUE(parent->m_undoCalled) << "Undo was not called on the parent";
  203. EXPECT_TRUE(child_1->m_undoCalled) << "Undo was not called on the child";
  204. EXPECT_TRUE(child_2->m_undoCalled) << "Undo was not called on the child";
  205. EXPECT_TRUE(child_1_1->m_undoCalled) << "Undo was not called on the grandchild";
  206. EXPECT_TRUE(child_1_2->m_undoCalled) << "Undo was not called on the grandchild";
  207. }
  208. TEST(URSequencePoint, RunRedo_NoChildren_RedoIsCalled)
  209. {
  210. auto object = AZStd::make_unique<SequencePointTest>("Object", 0);
  211. object->m_redoCalled = false;
  212. object->RunRedo();
  213. EXPECT_TRUE(object->m_redoCalled) << "Redo was not called on the object";
  214. }
  215. TEST(URSequencePoint, RunRedo_HasChildren_RedoIsCalled)
  216. {
  217. auto parent = AZStd::make_unique<SequencePointTest>("Parent", 0);
  218. auto child_1 = aznew SequencePointTest("Child", 1);
  219. auto child_2 = aznew SequencePointTest("Child", 2);
  220. auto child_1_1 = aznew SequencePointTest("Child", 3);
  221. auto child_1_2 = aznew SequencePointTest("Child", 4);
  222. parent->m_redoCalled = false;
  223. child_1->SetParent(parent.get());
  224. child_1->m_redoCalled = false;
  225. child_2->SetParent(parent.get());
  226. child_2->m_redoCalled = false;
  227. child_1_1->SetParent(child_1);
  228. child_1_1->m_redoCalled = false;
  229. child_1_2->SetParent(child_1);
  230. child_1_2->m_redoCalled = false;
  231. parent->RunRedo();
  232. EXPECT_TRUE(parent->m_redoCalled) << "Redo was not called on the parent";
  233. EXPECT_TRUE(child_1->m_redoCalled) << "Redo was not called on the child";
  234. EXPECT_TRUE(child_2->m_redoCalled) << "Redo was not called on the child";
  235. EXPECT_TRUE(child_1_1->m_redoCalled) << "Redo was not called on the grandchild";
  236. EXPECT_TRUE(child_1_2->m_redoCalled) << "Redo was not called on the grandchild";
  237. }
  238. TEST(URSequencePoint, SetName)
  239. {
  240. AZStd::string test_1("Test Point");
  241. AZStd::string test_2("A different Test Point");
  242. auto testPoint = AZStd::make_unique<SequencePointTest>("Test Point", 0);
  243. EXPECT_EQ(testPoint->GetName(), test_1);
  244. testPoint->SetName("A different Test Point");
  245. EXPECT_EQ(testPoint->GetName(), test_2);
  246. }
  247. TEST(URSequencePoint, HasRealChildren_NoChildren_ExpectFalse)
  248. {
  249. auto testPoint = AZStd::make_unique<SequencePointTest>("Test Point", 0);
  250. bool result = testPoint->HasRealChildren();
  251. EXPECT_FALSE(result);
  252. }
  253. TEST(URSequencePoint, HasRealChildren_AllChildrenAreFake_ExpectFalse)
  254. {
  255. auto parent = AZStd::make_unique<SequencePointTest>("Parent", 0);
  256. auto child_1 = aznew SequencePointTest("Child", 1);
  257. auto child_2 = aznew SequencePointTest("Child", 2);
  258. auto child_3 = aznew SequencePointTest("Child", 3);
  259. auto child_1_1 = aznew SequencePointTest("Child", 4);
  260. auto child_1_2 = aznew SequencePointTest("Child", 5);
  261. child_1->SetParent(parent.get());
  262. child_2->SetParent(parent.get());
  263. child_3->SetParent(parent.get());
  264. child_1_1->SetParent(child_1);
  265. child_1_2->SetParent(child_1);
  266. bool result = parent->HasRealChildren();
  267. EXPECT_FALSE(result);
  268. }
  269. TEST(URSequencePoint, HasRealChildren_OneChildIsReal_ExpectTrue)
  270. {
  271. auto parent = AZStd::make_unique<SequencePointTest>("Parent", 0);
  272. auto child_1 = aznew SequencePointTest("Child", 1);
  273. auto child_2 = aznew SequencePointTest("Child", 2);
  274. auto child_3 = aznew DifferentTypeSequencePointTest("Child", 3);
  275. auto child_1_1 = aznew SequencePointTest("Child", 4);
  276. auto child_1_2 = aznew SequencePointTest("Child", 5);
  277. child_1->SetParent(parent.get());
  278. child_2->SetParent(parent.get());
  279. child_3->SetParent(parent.get());
  280. child_1_1->SetParent(child_1);
  281. child_1_2->SetParent(child_1);
  282. bool result = parent->HasRealChildren();
  283. EXPECT_TRUE(result);
  284. }
  285. TEST(URSequencePoint, HasRealChildren_OneGrandChildIsReal_ExpectTrue)
  286. {
  287. auto parent = AZStd::make_unique<SequencePointTest>("Parent", 0);
  288. auto child_1 = aznew SequencePointTest("Child", 1);
  289. auto child_2 = aznew SequencePointTest("Child", 2);
  290. auto child_3 = aznew SequencePointTest("Child", 3);
  291. auto child_1_1 = aznew SequencePointTest("Child", 4);
  292. auto child_1_2 = aznew DifferentTypeSequencePointTest("Child", 5);
  293. child_1->SetParent(parent.get());
  294. child_2->SetParent(parent.get());
  295. child_3->SetParent(parent.get());
  296. child_1_1->SetParent(child_1);
  297. child_1_2->SetParent(child_1);
  298. bool result = parent->HasRealChildren();
  299. EXPECT_TRUE(result);
  300. }
  301. ///////////////////////////////////////////////////////////////////////////
  302. // UndoStack
  303. ///////////////////////////////////////////////////////////////////////////
  304. class UndoDestructorTest : public URSequencePoint
  305. {
  306. public:
  307. AZ_CLASS_ALLOCATOR(UndoDestructorTest, AZ::SystemAllocator)
  308. UndoDestructorTest(bool* completedFlag)
  309. : URSequencePoint("UndoDestructorTest", 0)
  310. , m_completedFlag(completedFlag)
  311. {
  312. *m_completedFlag = false;
  313. }
  314. ~UndoDestructorTest() override
  315. {
  316. *m_completedFlag = true;
  317. }
  318. bool Changed() const override { return true; }
  319. private:
  320. bool* m_completedFlag;
  321. };
  322. TEST(UndoStack, UndoRedoMemory)
  323. {
  324. UndoStack undoStack(nullptr);
  325. bool flag = false;
  326. undoStack.Post(aznew UndoDestructorTest(&flag));
  327. undoStack.Undo();
  328. undoStack.Slice();
  329. EXPECT_EQ(flag, true);
  330. }
  331. class UndoIntSetter : public URSequencePoint
  332. {
  333. public:
  334. AZ_CLASS_ALLOCATOR(UndoIntSetter, AZ::SystemAllocator)
  335. UndoIntSetter(int* value, int newValue)
  336. : URSequencePoint("UndoIntSetter", 0)
  337. , m_value(value)
  338. , m_newValue(newValue)
  339. , m_oldValue(*value)
  340. {
  341. Redo();
  342. }
  343. void Undo() override
  344. {
  345. *m_value = m_oldValue;
  346. }
  347. void Redo() override
  348. {
  349. *m_value = m_newValue;
  350. }
  351. bool Changed() const override { return true; }
  352. private:
  353. int* m_value;
  354. int m_newValue;
  355. int m_oldValue;
  356. };
  357. TEST(UndoStack, UndoRedoSequence)
  358. {
  359. UndoStack undoStack(nullptr);
  360. int tracker = 0;
  361. undoStack.Post(aznew UndoIntSetter(&tracker, 1));
  362. EXPECT_EQ(tracker, 1);
  363. undoStack.Undo();
  364. EXPECT_EQ(tracker, 0);
  365. undoStack.Redo();
  366. EXPECT_EQ(tracker, 1);
  367. undoStack.Undo();
  368. EXPECT_EQ(tracker, 0);
  369. undoStack.Redo();
  370. EXPECT_EQ(tracker, 1);
  371. undoStack.Post(aznew UndoIntSetter(&tracker, 100));
  372. EXPECT_EQ(tracker, 100);
  373. undoStack.Undo();
  374. EXPECT_EQ(tracker, 1);
  375. undoStack.Undo();
  376. EXPECT_EQ(tracker, 0);
  377. undoStack.Redo();
  378. EXPECT_EQ(tracker, 1);
  379. }
  380. TEST(UndoStack, UndoRedoLotsOfUndos)
  381. {
  382. UndoStack undoStack(nullptr);
  383. int tracker = 0;
  384. const int numUndos = 1000;
  385. for (int i = 0; i < 1000; i++)
  386. {
  387. undoStack.Post(aznew UndoIntSetter(&tracker, i + 1));
  388. EXPECT_EQ(tracker, i + 1);
  389. }
  390. int counter = 0;
  391. while (undoStack.CanUndo())
  392. {
  393. undoStack.Undo();
  394. counter++;
  395. }
  396. EXPECT_EQ(numUndos, counter);
  397. EXPECT_EQ(tracker, 0);
  398. counter = 0;
  399. while (undoStack.CanRedo())
  400. {
  401. undoStack.Redo();
  402. counter++;
  403. }
  404. EXPECT_EQ(numUndos, counter);
  405. EXPECT_EQ(tracker, numUndos);
  406. }
  407. }