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 "chrome/browser/undo/bookmark_undo_service.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
9 #include "chrome/browser/undo/bookmark_undo_service_factory.h"
10 #include "chrome/test/base/testing_profile.h"
11 #include "components/bookmarks/browser/bookmark_model.h"
12 #include "components/bookmarks/test/bookmark_test_helpers.h"
13 #include "content/public/test/test_browser_thread_bundle.h"
14 #include "testing/gtest/include/gtest/gtest.h"
16 using base::ASCIIToUTF16
;
17 using bookmarks::BookmarkModel
;
18 using bookmarks::BookmarkNode
;
22 class BookmarkUndoServiceTest
: public testing::Test
{
24 BookmarkUndoServiceTest();
26 void SetUp() override
;
27 void TearDown() override
;
29 BookmarkModel
* GetModel();
30 BookmarkUndoService
* GetUndoService();
33 scoped_ptr
<TestingProfile
> profile_
;
34 content::TestBrowserThreadBundle thread_bundle_
;
36 DISALLOW_COPY_AND_ASSIGN(BookmarkUndoServiceTest
);
39 BookmarkUndoServiceTest::BookmarkUndoServiceTest() {}
41 void BookmarkUndoServiceTest::SetUp() {
42 profile_
.reset(new TestingProfile
);
43 profile_
->CreateBookmarkModel(true);
44 bookmarks::test::WaitForBookmarkModelToLoad(GetModel());
47 BookmarkModel
* BookmarkUndoServiceTest::GetModel() {
48 return BookmarkModelFactory::GetForProfile(profile_
.get());
51 BookmarkUndoService
* BookmarkUndoServiceTest::GetUndoService() {
52 return BookmarkUndoServiceFactory::GetForProfile(profile_
.get());
55 void BookmarkUndoServiceTest::TearDown() {
59 TEST_F(BookmarkUndoServiceTest
, AddBookmark
) {
60 BookmarkModel
* model
= GetModel();
61 BookmarkUndoService
* undo_service
= GetUndoService();
62 model
->AddObserver(undo_service
);
64 const BookmarkNode
* parent
= model
->other_node();
65 model
->AddURL(parent
, 0, ASCIIToUTF16("foo"), GURL("http://www.bar.com"));
67 // Undo bookmark creation and test for no bookmarks.
68 undo_service
->undo_manager()->Undo();
69 EXPECT_EQ(0, model
->other_node()->child_count());
71 // Redo bookmark creation and ensure bookmark information is valid.
72 undo_service
->undo_manager()->Redo();
73 const BookmarkNode
* node
= parent
->GetChild(0);
74 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
75 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
78 // Test that a bookmark removal action can be undone and redone.
79 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkRemove
) {
80 BookmarkModel
* model
= GetModel();
81 BookmarkUndoService
* undo_service
= GetUndoService();
82 model
->AddObserver(undo_service
);
84 const BookmarkNode
* parent
= model
->other_node();
85 model
->AddURL(parent
, 0, ASCIIToUTF16("foo"), GURL("http://www.bar.com"));
86 model
->Remove(parent
, 0);
88 EXPECT_EQ(2U, undo_service
->undo_manager()->undo_count());
89 EXPECT_EQ(0U, undo_service
->undo_manager()->redo_count());
91 // Undo the deletion of the only bookmark and check the bookmark values.
92 undo_service
->undo_manager()->Undo();
93 EXPECT_EQ(1, model
->other_node()->child_count());
94 const BookmarkNode
* node
= parent
->GetChild(0);
95 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
96 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
98 EXPECT_EQ(1U, undo_service
->undo_manager()->undo_count());
99 EXPECT_EQ(1U, undo_service
->undo_manager()->redo_count());
101 // Redo the deletion and check that there are no bookmarks left.
102 undo_service
->undo_manager()->Redo();
103 EXPECT_EQ(0, model
->other_node()->child_count());
105 EXPECT_EQ(2U, undo_service
->undo_manager()->undo_count());
106 EXPECT_EQ(0U, undo_service
->undo_manager()->redo_count());
109 // Ensure the undo/redo works for editing of bookmark information grouped into
111 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkGroupedAction
) {
112 BookmarkModel
* model
= GetModel();
113 BookmarkUndoService
* undo_service
= GetUndoService();
114 model
->AddObserver(undo_service
);
116 const BookmarkNode
* n1
= model
->AddURL(model
->other_node(),
119 GURL("http://www.foo.com"));
120 undo_service
->undo_manager()->StartGroupingActions();
121 model
->SetTitle(n1
, ASCIIToUTF16("bar"));
122 model
->SetURL(n1
, GURL("http://www.bar.com"));
123 undo_service
->undo_manager()->EndGroupingActions();
125 EXPECT_EQ(2U, undo_service
->undo_manager()->undo_count());
126 EXPECT_EQ(0U, undo_service
->undo_manager()->redo_count());
128 // Undo the modification of the bookmark and check for the original values.
129 undo_service
->undo_manager()->Undo();
130 EXPECT_EQ(1, model
->other_node()->child_count());
131 const BookmarkNode
* node
= model
->other_node()->GetChild(0);
132 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
133 EXPECT_EQ(node
->url(), GURL("http://www.foo.com"));
135 // Redo the modifications and ensure the newer values are present.
136 undo_service
->undo_manager()->Redo();
137 EXPECT_EQ(1, model
->other_node()->child_count());
138 node
= model
->other_node()->GetChild(0);
139 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("bar"));
140 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
142 EXPECT_EQ(2U, undo_service
->undo_manager()->undo_count());
143 EXPECT_EQ(0U, undo_service
->undo_manager()->redo_count());
146 // Test moving bookmarks within a folder and between folders.
147 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkMoveWithinFolder
) {
148 BookmarkModel
* model
= GetModel();
149 BookmarkUndoService
* undo_service
= GetUndoService();
150 model
->AddObserver(undo_service
);
152 const BookmarkNode
* n1
= model
->AddURL(model
->other_node(),
155 GURL("http://www.foo.com"));
156 const BookmarkNode
* n2
= model
->AddURL(model
->other_node(),
159 GURL("http://www.moo.com"));
160 const BookmarkNode
* n3
= model
->AddURL(model
->other_node(),
163 GURL("http://www.bar.com"));
164 model
->Move(n1
, model
->other_node(), 3);
166 // Undo the move and check that the nodes are in order.
167 undo_service
->undo_manager()->Undo();
168 EXPECT_EQ(model
->other_node()->GetChild(0), n1
);
169 EXPECT_EQ(model
->other_node()->GetChild(1), n2
);
170 EXPECT_EQ(model
->other_node()->GetChild(2), n3
);
172 // Redo the move and check that the first node is in the last position.
173 undo_service
->undo_manager()->Redo();
174 EXPECT_EQ(model
->other_node()->GetChild(0), n2
);
175 EXPECT_EQ(model
->other_node()->GetChild(1), n3
);
176 EXPECT_EQ(model
->other_node()->GetChild(2), n1
);
179 // Test undo of a bookmark moved to a different folder.
180 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkMoveToOtherFolder
) {
181 BookmarkModel
* model
= GetModel();
182 BookmarkUndoService
* undo_service
= GetUndoService();
183 model
->AddObserver(undo_service
);
185 const BookmarkNode
* n1
= model
->AddURL(model
->other_node(),
188 GURL("http://www.foo.com"));
189 const BookmarkNode
* n2
= model
->AddURL(model
->other_node(),
192 GURL("http://www.moo.com"));
193 const BookmarkNode
* n3
= model
->AddURL(model
->other_node(),
196 GURL("http://www.bar.com"));
197 const BookmarkNode
* f1
=
198 model
->AddFolder(model
->other_node(), 3, ASCIIToUTF16("folder"));
199 model
->Move(n3
, f1
, 0);
201 // Undo the move and check that the bookmark and folder are in place.
202 undo_service
->undo_manager()->Undo();
203 ASSERT_EQ(4, model
->other_node()->child_count());
204 EXPECT_EQ(model
->other_node()->GetChild(0), n1
);
205 EXPECT_EQ(model
->other_node()->GetChild(1), n2
);
206 EXPECT_EQ(model
->other_node()->GetChild(2), n3
);
207 EXPECT_EQ(model
->other_node()->GetChild(3), f1
);
208 EXPECT_EQ(0, f1
->child_count());
210 // Redo the move back into the folder and check validity.
211 undo_service
->undo_manager()->Redo();
212 ASSERT_EQ(3, model
->other_node()->child_count());
213 EXPECT_EQ(model
->other_node()->GetChild(0), n1
);
214 EXPECT_EQ(model
->other_node()->GetChild(1), n2
);
215 EXPECT_EQ(model
->other_node()->GetChild(2), f1
);
216 ASSERT_EQ(1, f1
->child_count());
217 EXPECT_EQ(f1
->GetChild(0), n3
);
220 // Tests the handling of multiple modifications that include renumbering of the
221 // bookmark identifiers.
222 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkRenameDelete
) {
223 BookmarkModel
* model
= GetModel();
224 BookmarkUndoService
* undo_service
= GetUndoService();
225 model
->AddObserver(undo_service
);
227 const BookmarkNode
* f1
= model
->AddFolder(model
->other_node(),
229 ASCIIToUTF16("folder"));
230 model
->AddURL(f1
, 0, ASCIIToUTF16("foo"), GURL("http://www.foo.com"));
231 model
->SetTitle(f1
, ASCIIToUTF16("Renamed"));
232 model
->Remove(model
->other_node(), 0);
234 // Undo the folder removal and ensure the folder and bookmark were restored.
235 undo_service
->undo_manager()->Undo();
236 ASSERT_EQ(1, model
->other_node()->child_count());
237 ASSERT_EQ(1, model
->other_node()->GetChild(0)->child_count());
238 const BookmarkNode
* node
= model
->other_node()->GetChild(0);
239 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("Renamed"));
241 node
= model
->other_node()->GetChild(0)->GetChild(0);
242 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
243 EXPECT_EQ(node
->url(), GURL("http://www.foo.com"));
245 // Undo the title change and ensure the folder was updated even though the
247 undo_service
->undo_manager()->Undo();
248 node
= model
->other_node()->GetChild(0);
249 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("folder"));
251 // Undo bookmark creation and test for removal of bookmark.
252 undo_service
->undo_manager()->Undo();
253 ASSERT_EQ(0, model
->other_node()->GetChild(0)->child_count());
255 // Undo folder creation and confirm the bookmark model is empty.
256 undo_service
->undo_manager()->Undo();
257 ASSERT_EQ(0, model
->other_node()->child_count());
259 // Redo all the actions and ensure the folder and bookmark are restored.
260 undo_service
->undo_manager()->Redo(); // folder creation
261 undo_service
->undo_manager()->Redo(); // bookmark creation
262 undo_service
->undo_manager()->Redo(); // bookmark title change
263 ASSERT_EQ(1, model
->other_node()->child_count());
264 ASSERT_EQ(1, model
->other_node()->GetChild(0)->child_count());
265 node
= model
->other_node()->GetChild(0);
266 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("Renamed"));
267 node
= model
->other_node()->GetChild(0)->GetChild(0);
268 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
269 EXPECT_EQ(node
->url(), GURL("http://www.foo.com"));
271 undo_service
->undo_manager()->Redo(); // folder deletion
272 EXPECT_EQ(0, model
->other_node()->child_count());
275 // Test the undo of SortChildren and ReorderChildren.
276 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkReorder
) {
277 BookmarkModel
* model
= GetModel();
278 BookmarkUndoService
* undo_service
= GetUndoService();
279 model
->AddObserver(undo_service
);
281 const BookmarkNode
* parent
= model
->other_node();
282 model
->AddURL(parent
, 0, ASCIIToUTF16("foo"), GURL("http://www.foo.com"));
283 model
->AddURL(parent
, 1, ASCIIToUTF16("moo"), GURL("http://www.moo.com"));
284 model
->AddURL(parent
, 2, ASCIIToUTF16("bar"), GURL("http://www.bar.com"));
285 model
->SortChildren(parent
);
287 // Test the undo of SortChildren.
288 undo_service
->undo_manager()->Undo();
289 const BookmarkNode
* node
= parent
->GetChild(0);
290 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
291 EXPECT_EQ(node
->url(), GURL("http://www.foo.com"));
293 node
= parent
->GetChild(1);
294 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("moo"));
295 EXPECT_EQ(node
->url(), GURL("http://www.moo.com"));
297 node
= parent
->GetChild(2);
298 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("bar"));
299 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
301 // Test the redo of SortChildren.
302 undo_service
->undo_manager()->Redo();
303 node
= parent
->GetChild(0);
304 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("bar"));
305 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
307 node
= parent
->GetChild(1);
308 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
309 EXPECT_EQ(node
->url(), GURL("http://www.foo.com"));
311 node
= parent
->GetChild(2);
312 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("moo"));
313 EXPECT_EQ(node
->url(), GURL("http://www.moo.com"));
317 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkRemoveAll
) {
318 BookmarkModel
* model
= GetModel();
319 BookmarkUndoService
* undo_service
= GetUndoService();
320 model
->AddObserver(undo_service
);
322 // Setup bookmarks in the Other Bookmarks and the Bookmark Bar.
323 const BookmarkNode
* new_folder
;
324 const BookmarkNode
* parent
= model
->other_node();
325 model
->AddURL(parent
, 0, ASCIIToUTF16("foo"), GURL("http://www.google.com"));
326 new_folder
= model
->AddFolder(parent
, 1, ASCIIToUTF16("folder"));
327 model
->AddURL(new_folder
, 0, ASCIIToUTF16("bar"), GURL("http://www.bar.com"));
329 parent
= model
->bookmark_bar_node();
330 model
->AddURL(parent
, 0, ASCIIToUTF16("a"), GURL("http://www.a.com"));
331 new_folder
= model
->AddFolder(parent
, 1, ASCIIToUTF16("folder"));
332 model
->AddURL(new_folder
, 0, ASCIIToUTF16("b"), GURL("http://www.b.com"));
334 model
->RemoveAllUserBookmarks();
336 // Test that the undo of RemoveAllUserBookmarks restores all folders and
338 undo_service
->undo_manager()->Undo();
340 ASSERT_EQ(2, model
->other_node()->child_count());
341 EXPECT_EQ(1, model
->other_node()->GetChild(1)->child_count());
342 const BookmarkNode
* node
= model
->other_node()->GetChild(1)->GetChild(0);
343 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("bar"));
344 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
346 ASSERT_EQ(2, model
->bookmark_bar_node()->child_count());
347 EXPECT_EQ(1, model
->bookmark_bar_node()->GetChild(1)->child_count());
348 node
= model
->bookmark_bar_node()->GetChild(1)->GetChild(0);
349 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("b"));
350 EXPECT_EQ(node
->url(), GURL("http://www.b.com"));
352 // Test that the redo removes all folders and bookmarks.
353 undo_service
->undo_manager()->Redo();
354 EXPECT_EQ(0, model
->other_node()->child_count());
355 EXPECT_EQ(0, model
->bookmark_bar_node()->child_count());
358 TEST_F(BookmarkUndoServiceTest
, UndoRemoveFolderWithBookmarks
) {
359 BookmarkModel
* model
= GetModel();
360 BookmarkUndoService
* undo_service
= GetUndoService();
361 model
->AddObserver(undo_service
);
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
, 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();
400 model
->AddObserver(undo_service
);
402 // This maximum is set in undo_manager.cc
403 const size_t kMaxUndoGroups
= 100;
405 const BookmarkNode
* parent
= model
->other_node();
406 model
->AddURL(parent
, 0, ASCIIToUTF16("foo"), GURL("http://www.foo.com"));
407 for (size_t i
= 1; i
< kMaxUndoGroups
+ 1; ++i
)
408 model
->AddURL(parent
, i
, ASCIIToUTF16("bar"), GURL("http://www.bar.com"));
410 EXPECT_EQ(kMaxUndoGroups
, undo_service
->undo_manager()->undo_count());
412 // Undo as many operations as possible.
413 while (undo_service
->undo_manager()->undo_count())
414 undo_service
->undo_manager()->Undo();
416 EXPECT_EQ(1, parent
->child_count());
417 const BookmarkNode
* node
= model
->other_node()->GetChild(0);
418 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
419 EXPECT_EQ(node
->url(), GURL("http://www.foo.com"));