1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/undo/bookmark_undo_service.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "components/bookmarks/browser/bookmark_model.h"
9 #include "components/bookmarks/test/bookmark_test_helpers.h"
10 #include "components/bookmarks/test/test_bookmark_client.h"
11 #include "testing/gtest/include/gtest/gtest.h"
13 using base::ASCIIToUTF16
;
14 using bookmarks::BookmarkModel
;
15 using bookmarks::BookmarkNode
;
19 class BookmarkUndoServiceTest
: public testing::Test
{
21 BookmarkUndoServiceTest();
23 void SetUp() override
;
24 void TearDown() override
;
26 BookmarkModel
* GetModel();
27 BookmarkUndoService
* GetUndoService();
30 scoped_ptr
<bookmarks::TestBookmarkClient
> test_bookmark_client_
;
31 scoped_ptr
<bookmarks::BookmarkModel
> bookmark_model_
;
32 scoped_ptr
<BookmarkUndoService
> bookmark_undo_service_
;
34 DISALLOW_COPY_AND_ASSIGN(BookmarkUndoServiceTest
);
37 BookmarkUndoServiceTest::BookmarkUndoServiceTest() {}
39 void BookmarkUndoServiceTest::SetUp() {
40 DCHECK(!test_bookmark_client_
);
41 DCHECK(!bookmark_model_
);
42 DCHECK(!bookmark_undo_service_
);
43 test_bookmark_client_
.reset(new bookmarks::TestBookmarkClient
);
44 bookmark_model_
= test_bookmark_client_
->CreateModel();
45 bookmark_undo_service_
.reset(new BookmarkUndoService
);
46 bookmark_undo_service_
->Start(bookmark_model_
.get());
47 bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model_
.get());
50 BookmarkModel
* BookmarkUndoServiceTest::GetModel() {
51 return bookmark_model_
.get();
54 BookmarkUndoService
* BookmarkUndoServiceTest::GetUndoService() {
55 return bookmark_undo_service_
.get();
58 void BookmarkUndoServiceTest::TearDown() {
59 // Implement two-phase KeyedService shutdown for test KeyedServices.
60 bookmark_undo_service_
->Shutdown();
61 bookmark_model_
->Shutdown();
62 test_bookmark_client_
->Shutdown();
63 bookmark_undo_service_
.reset();
64 bookmark_model_
.reset();
65 test_bookmark_client_
.reset();
68 TEST_F(BookmarkUndoServiceTest
, AddBookmark
) {
69 BookmarkModel
* model
= GetModel();
70 BookmarkUndoService
* undo_service
= GetUndoService();
72 const BookmarkNode
* parent
= model
->other_node();
73 model
->AddURL(parent
, 0, ASCIIToUTF16("foo"), GURL("http://www.bar.com"));
75 // Undo bookmark creation and test for no bookmarks.
76 undo_service
->undo_manager()->Undo();
77 EXPECT_EQ(0, model
->other_node()->child_count());
79 // Redo bookmark creation and ensure bookmark information is valid.
80 undo_service
->undo_manager()->Redo();
81 const BookmarkNode
* node
= parent
->GetChild(0);
82 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
83 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
86 // Test that a bookmark removal action can be undone and redone.
87 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkRemove
) {
88 BookmarkModel
* model
= GetModel();
89 BookmarkUndoService
* undo_service
= GetUndoService();
91 const BookmarkNode
* parent
= model
->other_node();
92 model
->AddURL(parent
, 0, ASCIIToUTF16("foo"), GURL("http://www.bar.com"));
93 model
->Remove(parent
->GetChild(0));
95 EXPECT_EQ(2U, undo_service
->undo_manager()->undo_count());
96 EXPECT_EQ(0U, undo_service
->undo_manager()->redo_count());
98 // Undo the deletion of the only bookmark and check the bookmark values.
99 undo_service
->undo_manager()->Undo();
100 EXPECT_EQ(1, model
->other_node()->child_count());
101 const BookmarkNode
* node
= parent
->GetChild(0);
102 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
103 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
105 EXPECT_EQ(1U, undo_service
->undo_manager()->undo_count());
106 EXPECT_EQ(1U, undo_service
->undo_manager()->redo_count());
108 // Redo the deletion and check that there are no bookmarks left.
109 undo_service
->undo_manager()->Redo();
110 EXPECT_EQ(0, model
->other_node()->child_count());
112 EXPECT_EQ(2U, undo_service
->undo_manager()->undo_count());
113 EXPECT_EQ(0U, undo_service
->undo_manager()->redo_count());
116 // Ensure the undo/redo works for editing of bookmark information grouped into
118 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkGroupedAction
) {
119 BookmarkModel
* model
= GetModel();
120 BookmarkUndoService
* undo_service
= GetUndoService();
122 const BookmarkNode
* n1
= model
->AddURL(model
->other_node(),
125 GURL("http://www.foo.com"));
126 undo_service
->undo_manager()->StartGroupingActions();
127 model
->SetTitle(n1
, ASCIIToUTF16("bar"));
128 model
->SetURL(n1
, GURL("http://www.bar.com"));
129 undo_service
->undo_manager()->EndGroupingActions();
131 EXPECT_EQ(2U, undo_service
->undo_manager()->undo_count());
132 EXPECT_EQ(0U, undo_service
->undo_manager()->redo_count());
134 // Undo the modification of the bookmark and check for the original values.
135 undo_service
->undo_manager()->Undo();
136 EXPECT_EQ(1, model
->other_node()->child_count());
137 const BookmarkNode
* node
= model
->other_node()->GetChild(0);
138 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
139 EXPECT_EQ(node
->url(), GURL("http://www.foo.com"));
141 // Redo the modifications and ensure the newer values are present.
142 undo_service
->undo_manager()->Redo();
143 EXPECT_EQ(1, model
->other_node()->child_count());
144 node
= model
->other_node()->GetChild(0);
145 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("bar"));
146 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
148 EXPECT_EQ(2U, undo_service
->undo_manager()->undo_count());
149 EXPECT_EQ(0U, undo_service
->undo_manager()->redo_count());
152 // Test moving bookmarks within a folder and between folders.
153 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkMoveWithinFolder
) {
154 BookmarkModel
* model
= GetModel();
155 BookmarkUndoService
* undo_service
= GetUndoService();
157 const BookmarkNode
* n1
= model
->AddURL(model
->other_node(),
160 GURL("http://www.foo.com"));
161 const BookmarkNode
* n2
= model
->AddURL(model
->other_node(),
164 GURL("http://www.moo.com"));
165 const BookmarkNode
* n3
= model
->AddURL(model
->other_node(),
168 GURL("http://www.bar.com"));
169 model
->Move(n1
, model
->other_node(), 3);
171 // Undo the move and check that the nodes are in order.
172 undo_service
->undo_manager()->Undo();
173 EXPECT_EQ(model
->other_node()->GetChild(0), n1
);
174 EXPECT_EQ(model
->other_node()->GetChild(1), n2
);
175 EXPECT_EQ(model
->other_node()->GetChild(2), n3
);
177 // Redo the move and check that the first node is in the last position.
178 undo_service
->undo_manager()->Redo();
179 EXPECT_EQ(model
->other_node()->GetChild(0), n2
);
180 EXPECT_EQ(model
->other_node()->GetChild(1), n3
);
181 EXPECT_EQ(model
->other_node()->GetChild(2), n1
);
184 // Test undo of a bookmark moved to a different folder.
185 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkMoveToOtherFolder
) {
186 BookmarkModel
* model
= GetModel();
187 BookmarkUndoService
* undo_service
= GetUndoService();
189 const BookmarkNode
* n1
= model
->AddURL(model
->other_node(),
192 GURL("http://www.foo.com"));
193 const BookmarkNode
* n2
= model
->AddURL(model
->other_node(),
196 GURL("http://www.moo.com"));
197 const BookmarkNode
* n3
= model
->AddURL(model
->other_node(),
200 GURL("http://www.bar.com"));
201 const BookmarkNode
* f1
=
202 model
->AddFolder(model
->other_node(), 3, ASCIIToUTF16("folder"));
203 model
->Move(n3
, f1
, 0);
205 // Undo the move and check that the bookmark and folder are in place.
206 undo_service
->undo_manager()->Undo();
207 ASSERT_EQ(4, model
->other_node()->child_count());
208 EXPECT_EQ(model
->other_node()->GetChild(0), n1
);
209 EXPECT_EQ(model
->other_node()->GetChild(1), n2
);
210 EXPECT_EQ(model
->other_node()->GetChild(2), n3
);
211 EXPECT_EQ(model
->other_node()->GetChild(3), f1
);
212 EXPECT_EQ(0, f1
->child_count());
214 // Redo the move back into the folder and check validity.
215 undo_service
->undo_manager()->Redo();
216 ASSERT_EQ(3, model
->other_node()->child_count());
217 EXPECT_EQ(model
->other_node()->GetChild(0), n1
);
218 EXPECT_EQ(model
->other_node()->GetChild(1), n2
);
219 EXPECT_EQ(model
->other_node()->GetChild(2), f1
);
220 ASSERT_EQ(1, f1
->child_count());
221 EXPECT_EQ(f1
->GetChild(0), n3
);
224 // Tests the handling of multiple modifications that include renumbering of the
225 // bookmark identifiers.
226 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkRenameDelete
) {
227 BookmarkModel
* model
= GetModel();
228 BookmarkUndoService
* undo_service
= GetUndoService();
230 const BookmarkNode
* f1
= model
->AddFolder(model
->other_node(),
232 ASCIIToUTF16("folder"));
233 model
->AddURL(f1
, 0, ASCIIToUTF16("foo"), GURL("http://www.foo.com"));
234 model
->SetTitle(f1
, ASCIIToUTF16("Renamed"));
235 model
->Remove(model
->other_node()->GetChild(0));
237 // Undo the folder removal and ensure the folder and bookmark were restored.
238 undo_service
->undo_manager()->Undo();
239 ASSERT_EQ(1, model
->other_node()->child_count());
240 ASSERT_EQ(1, model
->other_node()->GetChild(0)->child_count());
241 const BookmarkNode
* node
= model
->other_node()->GetChild(0);
242 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("Renamed"));
244 node
= model
->other_node()->GetChild(0)->GetChild(0);
245 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
246 EXPECT_EQ(node
->url(), GURL("http://www.foo.com"));
248 // Undo the title change and ensure the folder was updated even though the
250 undo_service
->undo_manager()->Undo();
251 node
= model
->other_node()->GetChild(0);
252 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("folder"));
254 // Undo bookmark creation and test for removal of bookmark.
255 undo_service
->undo_manager()->Undo();
256 ASSERT_EQ(0, model
->other_node()->GetChild(0)->child_count());
258 // Undo folder creation and confirm the bookmark model is empty.
259 undo_service
->undo_manager()->Undo();
260 ASSERT_EQ(0, model
->other_node()->child_count());
262 // Redo all the actions and ensure the folder and bookmark are restored.
263 undo_service
->undo_manager()->Redo(); // folder creation
264 undo_service
->undo_manager()->Redo(); // bookmark creation
265 undo_service
->undo_manager()->Redo(); // bookmark title change
266 ASSERT_EQ(1, model
->other_node()->child_count());
267 ASSERT_EQ(1, model
->other_node()->GetChild(0)->child_count());
268 node
= model
->other_node()->GetChild(0);
269 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("Renamed"));
270 node
= model
->other_node()->GetChild(0)->GetChild(0);
271 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
272 EXPECT_EQ(node
->url(), GURL("http://www.foo.com"));
274 undo_service
->undo_manager()->Redo(); // folder deletion
275 EXPECT_EQ(0, model
->other_node()->child_count());
278 // Test the undo of SortChildren and ReorderChildren.
279 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkReorder
) {
280 BookmarkModel
* model
= GetModel();
281 BookmarkUndoService
* undo_service
= GetUndoService();
283 const BookmarkNode
* parent
= model
->other_node();
284 model
->AddURL(parent
, 0, ASCIIToUTF16("foo"), GURL("http://www.foo.com"));
285 model
->AddURL(parent
, 1, ASCIIToUTF16("moo"), GURL("http://www.moo.com"));
286 model
->AddURL(parent
, 2, ASCIIToUTF16("bar"), GURL("http://www.bar.com"));
287 model
->SortChildren(parent
);
289 // Test the undo of SortChildren.
290 undo_service
->undo_manager()->Undo();
291 const BookmarkNode
* node
= parent
->GetChild(0);
292 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
293 EXPECT_EQ(node
->url(), GURL("http://www.foo.com"));
295 node
= parent
->GetChild(1);
296 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("moo"));
297 EXPECT_EQ(node
->url(), GURL("http://www.moo.com"));
299 node
= parent
->GetChild(2);
300 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("bar"));
301 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
303 // Test the redo of SortChildren.
304 undo_service
->undo_manager()->Redo();
305 node
= parent
->GetChild(0);
306 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("bar"));
307 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
309 node
= parent
->GetChild(1);
310 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
311 EXPECT_EQ(node
->url(), GURL("http://www.foo.com"));
313 node
= parent
->GetChild(2);
314 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("moo"));
315 EXPECT_EQ(node
->url(), GURL("http://www.moo.com"));
319 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkRemoveAll
) {
320 BookmarkModel
* model
= GetModel();
321 BookmarkUndoService
* undo_service
= GetUndoService();
323 // Setup bookmarks in the Other Bookmarks and the Bookmark Bar.
324 const BookmarkNode
* new_folder
;
325 const BookmarkNode
* parent
= model
->other_node();
326 model
->AddURL(parent
, 0, ASCIIToUTF16("foo"), GURL("http://www.google.com"));
327 new_folder
= model
->AddFolder(parent
, 1, ASCIIToUTF16("folder"));
328 model
->AddURL(new_folder
, 0, ASCIIToUTF16("bar"), GURL("http://www.bar.com"));
330 parent
= model
->bookmark_bar_node();
331 model
->AddURL(parent
, 0, ASCIIToUTF16("a"), GURL("http://www.a.com"));
332 new_folder
= model
->AddFolder(parent
, 1, ASCIIToUTF16("folder"));
333 model
->AddURL(new_folder
, 0, ASCIIToUTF16("b"), GURL("http://www.b.com"));
335 model
->RemoveAllUserBookmarks();
337 // Test that the undo of RemoveAllUserBookmarks restores all folders and
339 undo_service
->undo_manager()->Undo();
341 ASSERT_EQ(2, model
->other_node()->child_count());
342 EXPECT_EQ(1, model
->other_node()->GetChild(1)->child_count());
343 const BookmarkNode
* node
= model
->other_node()->GetChild(1)->GetChild(0);
344 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("bar"));
345 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
347 ASSERT_EQ(2, model
->bookmark_bar_node()->child_count());
348 EXPECT_EQ(1, model
->bookmark_bar_node()->GetChild(1)->child_count());
349 node
= model
->bookmark_bar_node()->GetChild(1)->GetChild(0);
350 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("b"));
351 EXPECT_EQ(node
->url(), GURL("http://www.b.com"));
353 // Test that the redo removes all folders and bookmarks.
354 undo_service
->undo_manager()->Redo();
355 EXPECT_EQ(0, model
->other_node()->child_count());
356 EXPECT_EQ(0, model
->bookmark_bar_node()->child_count());
359 TEST_F(BookmarkUndoServiceTest
, UndoRemoveFolderWithBookmarks
) {
360 BookmarkModel
* model
= GetModel();
361 BookmarkUndoService
* undo_service
= GetUndoService();
363 // Setup bookmarks in the Other Bookmarks.
364 const BookmarkNode
* new_folder
;
365 const BookmarkNode
* parent
= model
->other_node();
366 new_folder
= model
->AddFolder(parent
, 0, ASCIIToUTF16("folder"));
367 model
->AddURL(new_folder
, 0, ASCIIToUTF16("bar"), GURL("http://www.bar.com"));
369 model
->Remove(parent
->GetChild(0));
371 // Test that the undo restores the bookmark and folder.
372 undo_service
->undo_manager()->Undo();
374 ASSERT_EQ(1, model
->other_node()->child_count());
375 new_folder
= model
->other_node()->GetChild(0);
376 EXPECT_EQ(1, new_folder
->child_count());
377 const BookmarkNode
* node
= new_folder
->GetChild(0);
378 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("bar"));
379 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
381 // Test that the redo restores the bookmark and folder.
382 undo_service
->undo_manager()->Redo();
384 ASSERT_EQ(0, model
->other_node()->child_count());
386 // Test that the undo after a redo restores the bookmark and folder.
387 undo_service
->undo_manager()->Undo();
389 ASSERT_EQ(1, model
->other_node()->child_count());
390 new_folder
= model
->other_node()->GetChild(0);
391 EXPECT_EQ(1, new_folder
->child_count());
392 node
= new_folder
->GetChild(0);
393 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("bar"));
394 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
397 TEST_F(BookmarkUndoServiceTest
, TestUpperLimit
) {
398 BookmarkModel
* model
= GetModel();
399 BookmarkUndoService
* undo_service
= GetUndoService();
401 // This maximum is set in undo_manager.cc
402 const size_t kMaxUndoGroups
= 100;
404 const BookmarkNode
* parent
= model
->other_node();
405 model
->AddURL(parent
, 0, ASCIIToUTF16("foo"), GURL("http://www.foo.com"));
406 for (size_t i
= 1; i
< kMaxUndoGroups
+ 1; ++i
)
407 model
->AddURL(parent
, i
, ASCIIToUTF16("bar"), GURL("http://www.bar.com"));
409 EXPECT_EQ(kMaxUndoGroups
, undo_service
->undo_manager()->undo_count());
411 // Undo as many operations as possible.
412 while (undo_service
->undo_manager()->undo_count())
413 undo_service
->undo_manager()->Undo();
415 EXPECT_EQ(1, parent
->child_count());
416 const BookmarkNode
* node
= model
->other_node()->GetChild(0);
417 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
418 EXPECT_EQ(node
->url(), GURL("http://www.foo.com"));