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.h"
9 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
10 #include "chrome/browser/bookmarks/bookmark_test_helpers.h"
11 #include "chrome/browser/undo/bookmark_undo_service_factory.h"
12 #include "chrome/test/base/testing_profile.h"
13 #include "content/public/test/test_browser_thread_bundle.h"
14 #include "testing/gtest/include/gtest/gtest.h"
16 using base::ASCIIToUTF16
;
20 class BookmarkUndoServiceTest
: public testing::Test
{
22 BookmarkUndoServiceTest();
24 virtual void SetUp() OVERRIDE
;
25 virtual void TearDown() OVERRIDE
;
27 BookmarkModel
* GetModel();
28 BookmarkUndoService
* GetUndoService();
31 scoped_ptr
<TestingProfile
> profile_
;
32 content::TestBrowserThreadBundle thread_bundle_
;
34 DISALLOW_COPY_AND_ASSIGN(BookmarkUndoServiceTest
);
37 BookmarkUndoServiceTest::BookmarkUndoServiceTest() {}
39 void BookmarkUndoServiceTest::SetUp() {
40 profile_
.reset(new TestingProfile
);
41 profile_
->CreateBookmarkModel(true);
42 test::WaitForBookmarkModelToLoad(GetModel());
45 BookmarkModel
* BookmarkUndoServiceTest::GetModel() {
46 return BookmarkModelFactory::GetForProfile(profile_
.get());
49 BookmarkUndoService
* BookmarkUndoServiceTest::GetUndoService() {
50 return BookmarkUndoServiceFactory::GetForProfile(profile_
.get());
53 void BookmarkUndoServiceTest::TearDown() {
57 TEST_F(BookmarkUndoServiceTest
, AddBookmark
) {
58 BookmarkModel
* model
= GetModel();
59 BookmarkUndoService
* undo_service
= GetUndoService();
60 model
->AddObserver(undo_service
);
62 const BookmarkNode
* parent
= model
->other_node();
63 model
->AddURL(parent
, 0, ASCIIToUTF16("foo"), GURL("http://www.bar.com"));
65 // Undo bookmark creation and test for no bookmarks.
66 undo_service
->undo_manager()->Undo();
67 EXPECT_EQ(0, model
->other_node()->child_count());
69 // Redo bookmark creation and ensure bookmark information is valid.
70 undo_service
->undo_manager()->Redo();
71 const BookmarkNode
* node
= parent
->GetChild(0);
72 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
73 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
76 // Test that a bookmark removal action can be undone and redone.
77 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkRemove
) {
78 BookmarkModel
* model
= GetModel();
79 BookmarkUndoService
* undo_service
= GetUndoService();
80 model
->AddObserver(undo_service
);
82 const BookmarkNode
* parent
= model
->other_node();
83 model
->AddURL(parent
, 0, ASCIIToUTF16("foo"), GURL("http://www.bar.com"));
84 model
->Remove(parent
, 0);
86 EXPECT_EQ(2U, undo_service
->undo_manager()->undo_count());
87 EXPECT_EQ(0U, undo_service
->undo_manager()->redo_count());
89 // Undo the deletion of the only bookmark and check the bookmark values.
90 undo_service
->undo_manager()->Undo();
91 EXPECT_EQ(1, model
->other_node()->child_count());
92 const BookmarkNode
* node
= parent
->GetChild(0);
93 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
94 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
96 EXPECT_EQ(1U, undo_service
->undo_manager()->undo_count());
97 EXPECT_EQ(1U, undo_service
->undo_manager()->redo_count());
99 // Redo the deletion and check that there are no bookmarks left.
100 undo_service
->undo_manager()->Redo();
101 EXPECT_EQ(0, model
->other_node()->child_count());
103 EXPECT_EQ(2U, undo_service
->undo_manager()->undo_count());
104 EXPECT_EQ(0U, undo_service
->undo_manager()->redo_count());
107 // Ensure the undo/redo works for editing of bookmark information grouped into
109 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkGroupedAction
) {
110 BookmarkModel
* model
= GetModel();
111 BookmarkUndoService
* undo_service
= GetUndoService();
112 model
->AddObserver(undo_service
);
114 const BookmarkNode
* n1
= model
->AddURL(model
->other_node(),
117 GURL("http://www.foo.com"));
118 undo_service
->undo_manager()->StartGroupingActions();
119 model
->SetTitle(n1
, ASCIIToUTF16("bar"));
120 model
->SetURL(n1
, GURL("http://www.bar.com"));
121 undo_service
->undo_manager()->EndGroupingActions();
123 EXPECT_EQ(2U, undo_service
->undo_manager()->undo_count());
124 EXPECT_EQ(0U, undo_service
->undo_manager()->redo_count());
126 // Undo the modification of the bookmark and check for the original values.
127 undo_service
->undo_manager()->Undo();
128 EXPECT_EQ(1, model
->other_node()->child_count());
129 const BookmarkNode
* node
= model
->other_node()->GetChild(0);
130 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
131 EXPECT_EQ(node
->url(), GURL("http://www.foo.com"));
133 // Redo the modifications and ensure the newer values are present.
134 undo_service
->undo_manager()->Redo();
135 EXPECT_EQ(1, model
->other_node()->child_count());
136 node
= model
->other_node()->GetChild(0);
137 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("bar"));
138 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
140 EXPECT_EQ(2U, undo_service
->undo_manager()->undo_count());
141 EXPECT_EQ(0U, undo_service
->undo_manager()->redo_count());
144 // Test moving bookmarks within a folder and between folders.
145 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkMoveWithinFolder
) {
146 BookmarkModel
* model
= GetModel();
147 BookmarkUndoService
* undo_service
= GetUndoService();
148 model
->AddObserver(undo_service
);
150 const BookmarkNode
* n1
= model
->AddURL(model
->other_node(),
153 GURL("http://www.foo.com"));
154 const BookmarkNode
* n2
= model
->AddURL(model
->other_node(),
157 GURL("http://www.moo.com"));
158 const BookmarkNode
* n3
= model
->AddURL(model
->other_node(),
161 GURL("http://www.bar.com"));
162 model
->Move(n1
, model
->other_node(), 3);
164 // Undo the move and check that the nodes are in order.
165 undo_service
->undo_manager()->Undo();
166 EXPECT_EQ(model
->other_node()->GetChild(0), n1
);
167 EXPECT_EQ(model
->other_node()->GetChild(1), n2
);
168 EXPECT_EQ(model
->other_node()->GetChild(2), n3
);
170 // Redo the move and check that the first node is in the last position.
171 undo_service
->undo_manager()->Redo();
172 EXPECT_EQ(model
->other_node()->GetChild(0), n2
);
173 EXPECT_EQ(model
->other_node()->GetChild(1), n3
);
174 EXPECT_EQ(model
->other_node()->GetChild(2), n1
);
177 // Test undo of a bookmark moved to a different folder.
178 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkMoveToOtherFolder
) {
179 BookmarkModel
* model
= GetModel();
180 BookmarkUndoService
* undo_service
= GetUndoService();
181 model
->AddObserver(undo_service
);
183 const BookmarkNode
* n1
= model
->AddURL(model
->other_node(),
186 GURL("http://www.foo.com"));
187 const BookmarkNode
* n2
= model
->AddURL(model
->other_node(),
190 GURL("http://www.moo.com"));
191 const BookmarkNode
* n3
= model
->AddURL(model
->other_node(),
194 GURL("http://www.bar.com"));
195 const BookmarkNode
* f1
=
196 model
->AddFolder(model
->other_node(), 3, ASCIIToUTF16("folder"));
197 model
->Move(n3
, f1
, 0);
199 // Undo the move and check that the bookmark and folder are in place.
200 undo_service
->undo_manager()->Undo();
201 ASSERT_EQ(4, model
->other_node()->child_count());
202 EXPECT_EQ(model
->other_node()->GetChild(0), n1
);
203 EXPECT_EQ(model
->other_node()->GetChild(1), n2
);
204 EXPECT_EQ(model
->other_node()->GetChild(2), n3
);
205 EXPECT_EQ(model
->other_node()->GetChild(3), f1
);
206 EXPECT_EQ(0, f1
->child_count());
208 // Redo the move back into the folder and check validity.
209 undo_service
->undo_manager()->Redo();
210 ASSERT_EQ(3, model
->other_node()->child_count());
211 EXPECT_EQ(model
->other_node()->GetChild(0), n1
);
212 EXPECT_EQ(model
->other_node()->GetChild(1), n2
);
213 EXPECT_EQ(model
->other_node()->GetChild(2), f1
);
214 ASSERT_EQ(1, f1
->child_count());
215 EXPECT_EQ(f1
->GetChild(0), n3
);
218 // Tests the handling of multiple modifications that include renumbering of the
219 // bookmark identifiers.
220 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkRenameDelete
) {
221 BookmarkModel
* model
= GetModel();
222 BookmarkUndoService
* undo_service
= GetUndoService();
223 model
->AddObserver(undo_service
);
225 const BookmarkNode
* f1
= model
->AddFolder(model
->other_node(),
227 ASCIIToUTF16("folder"));
228 model
->AddURL(f1
, 0, ASCIIToUTF16("foo"), GURL("http://www.foo.com"));
229 model
->SetTitle(f1
, ASCIIToUTF16("Renamed"));
230 model
->Remove(model
->other_node(), 0);
232 // Undo the folder removal and ensure the folder and bookmark were restored.
233 undo_service
->undo_manager()->Undo();
234 ASSERT_EQ(1, model
->other_node()->child_count());
235 ASSERT_EQ(1, model
->other_node()->GetChild(0)->child_count());
236 const BookmarkNode
* node
= model
->other_node()->GetChild(0);
237 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("Renamed"));
239 node
= model
->other_node()->GetChild(0)->GetChild(0);
240 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
241 EXPECT_EQ(node
->url(), GURL("http://www.foo.com"));
243 // Undo the title change and ensure the folder was updated even though the
245 undo_service
->undo_manager()->Undo();
246 node
= model
->other_node()->GetChild(0);
247 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("folder"));
249 // Undo bookmark creation and test for removal of bookmark.
250 undo_service
->undo_manager()->Undo();
251 ASSERT_EQ(0, model
->other_node()->GetChild(0)->child_count());
253 // Undo folder creation and confirm the bookmark model is empty.
254 undo_service
->undo_manager()->Undo();
255 ASSERT_EQ(0, model
->other_node()->child_count());
257 // Redo all the actions and ensure the folder and bookmark are restored.
258 undo_service
->undo_manager()->Redo(); // folder creation
259 undo_service
->undo_manager()->Redo(); // bookmark creation
260 undo_service
->undo_manager()->Redo(); // bookmark title change
261 ASSERT_EQ(1, model
->other_node()->child_count());
262 ASSERT_EQ(1, model
->other_node()->GetChild(0)->child_count());
263 node
= model
->other_node()->GetChild(0);
264 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("Renamed"));
265 node
= model
->other_node()->GetChild(0)->GetChild(0);
266 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
267 EXPECT_EQ(node
->url(), GURL("http://www.foo.com"));
269 undo_service
->undo_manager()->Redo(); // folder deletion
270 EXPECT_EQ(0, model
->other_node()->child_count());
273 // Test the undo of SortChildren and ReorderChildren.
274 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkReorder
) {
275 BookmarkModel
* model
= GetModel();
276 BookmarkUndoService
* undo_service
= GetUndoService();
277 model
->AddObserver(undo_service
);
279 const BookmarkNode
* parent
= model
->other_node();
280 model
->AddURL(parent
, 0, ASCIIToUTF16("foo"), GURL("http://www.foo.com"));
281 model
->AddURL(parent
, 1, ASCIIToUTF16("moo"), GURL("http://www.moo.com"));
282 model
->AddURL(parent
, 2, ASCIIToUTF16("bar"), GURL("http://www.bar.com"));
283 model
->SortChildren(parent
);
285 // Test the undo of SortChildren.
286 undo_service
->undo_manager()->Undo();
287 const BookmarkNode
* node
= parent
->GetChild(0);
288 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
289 EXPECT_EQ(node
->url(), GURL("http://www.foo.com"));
291 node
= parent
->GetChild(1);
292 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("moo"));
293 EXPECT_EQ(node
->url(), GURL("http://www.moo.com"));
295 node
= parent
->GetChild(2);
296 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("bar"));
297 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
299 // Test the redo of SortChildren.
300 undo_service
->undo_manager()->Redo();
301 node
= parent
->GetChild(0);
302 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("bar"));
303 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
305 node
= parent
->GetChild(1);
306 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
307 EXPECT_EQ(node
->url(), GURL("http://www.foo.com"));
309 node
= parent
->GetChild(2);
310 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("moo"));
311 EXPECT_EQ(node
->url(), GURL("http://www.moo.com"));
315 TEST_F(BookmarkUndoServiceTest
, UndoBookmarkRemoveAll
) {
316 BookmarkModel
* model
= GetModel();
317 BookmarkUndoService
* undo_service
= GetUndoService();
318 model
->AddObserver(undo_service
);
320 // Setup bookmarks in the Other Bookmarks and the Bookmark Bar.
321 const BookmarkNode
* new_folder
;
322 const BookmarkNode
* parent
= model
->other_node();
323 model
->AddURL(parent
, 0, ASCIIToUTF16("foo"), GURL("http://www.google.com"));
324 new_folder
= model
->AddFolder(parent
, 1, ASCIIToUTF16("folder"));
325 model
->AddURL(new_folder
, 0, ASCIIToUTF16("bar"), GURL("http://www.bar.com"));
327 parent
= model
->bookmark_bar_node();
328 model
->AddURL(parent
, 0, ASCIIToUTF16("a"), GURL("http://www.a.com"));
329 new_folder
= model
->AddFolder(parent
, 1, ASCIIToUTF16("folder"));
330 model
->AddURL(new_folder
, 0, ASCIIToUTF16("b"), GURL("http://www.b.com"));
334 // Test that the undo of RemoveAll restores all folders and bookmarks.
335 undo_service
->undo_manager()->Undo();
337 ASSERT_EQ(2, model
->other_node()->child_count());
338 EXPECT_EQ(1, model
->other_node()->GetChild(1)->child_count());
339 const BookmarkNode
* node
= model
->other_node()->GetChild(1)->GetChild(0);
340 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("bar"));
341 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
343 ASSERT_EQ(2, model
->bookmark_bar_node()->child_count());
344 EXPECT_EQ(1, model
->bookmark_bar_node()->GetChild(1)->child_count());
345 node
= model
->bookmark_bar_node()->GetChild(1)->GetChild(0);
346 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("b"));
347 EXPECT_EQ(node
->url(), GURL("http://www.b.com"));
349 // Test that the redo removes all folders and bookmarks.
350 undo_service
->undo_manager()->Redo();
351 EXPECT_EQ(0, model
->other_node()->child_count());
352 EXPECT_EQ(0, model
->bookmark_bar_node()->child_count());
355 TEST_F(BookmarkUndoServiceTest
, UndoRemoveFolderWithBookmarks
) {
356 BookmarkModel
* model
= GetModel();
357 BookmarkUndoService
* undo_service
= GetUndoService();
358 model
->AddObserver(undo_service
);
360 // Setup bookmarks in the Other Bookmarks.
361 const BookmarkNode
* new_folder
;
362 const BookmarkNode
* parent
= model
->other_node();
363 new_folder
= model
->AddFolder(parent
, 0, ASCIIToUTF16("folder"));
364 model
->AddURL(new_folder
, 0, ASCIIToUTF16("bar"), GURL("http://www.bar.com"));
366 model
->Remove(parent
, 0);
368 // Test that the undo restores the bookmark and folder.
369 undo_service
->undo_manager()->Undo();
371 ASSERT_EQ(1, model
->other_node()->child_count());
372 new_folder
= model
->other_node()->GetChild(0);
373 EXPECT_EQ(1, new_folder
->child_count());
374 const BookmarkNode
* node
= new_folder
->GetChild(0);
375 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("bar"));
376 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
378 // Test that the redo restores the bookmark and folder.
379 undo_service
->undo_manager()->Redo();
381 ASSERT_EQ(0, model
->other_node()->child_count());
383 // Test that the undo after a redo restores the bookmark and folder.
384 undo_service
->undo_manager()->Undo();
386 ASSERT_EQ(1, model
->other_node()->child_count());
387 new_folder
= model
->other_node()->GetChild(0);
388 EXPECT_EQ(1, new_folder
->child_count());
389 node
= new_folder
->GetChild(0);
390 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("bar"));
391 EXPECT_EQ(node
->url(), GURL("http://www.bar.com"));
394 TEST_F(BookmarkUndoServiceTest
, TestUpperLimit
) {
395 BookmarkModel
* model
= GetModel();
396 BookmarkUndoService
* undo_service
= GetUndoService();
397 model
->AddObserver(undo_service
);
399 // This maximum is set in undo_manager.cc
400 const size_t kMaxUndoGroups
= 100;
402 const BookmarkNode
* parent
= model
->other_node();
403 model
->AddURL(parent
, 0, ASCIIToUTF16("foo"), GURL("http://www.foo.com"));
404 for (size_t i
= 1; i
< kMaxUndoGroups
+ 1; ++i
)
405 model
->AddURL(parent
, i
, ASCIIToUTF16("bar"), GURL("http://www.bar.com"));
407 EXPECT_EQ(kMaxUndoGroups
, undo_service
->undo_manager()->undo_count());
409 // Undo as many operations as possible.
410 while (undo_service
->undo_manager()->undo_count())
411 undo_service
->undo_manager()->Undo();
413 EXPECT_EQ(1, parent
->child_count());
414 const BookmarkNode
* node
= model
->other_node()->GetChild(0);
415 EXPECT_EQ(node
->GetTitle(), ASCIIToUTF16("foo"));
416 EXPECT_EQ(node
->url(), GURL("http://www.foo.com"));