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. 4 5 #include "chrome/browser/undo/bookmark_undo_service.h" 6 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" 15 16 using base::ASCIIToUTF16; 17 18 namespace { 19 20 class BookmarkUndoServiceTest : public testing::Test { 21 public: 22 BookmarkUndoServiceTest(); 23 24 virtual void SetUp() OVERRIDE; 25 virtual void TearDown() OVERRIDE; 26 27 BookmarkModel* GetModel(); 28 BookmarkUndoService* GetUndoService(); 29 30 private: 31 scoped_ptr<TestingProfile> profile_; 32 content::TestBrowserThreadBundle thread_bundle_; 33 34 DISALLOW_COPY_AND_ASSIGN(BookmarkUndoServiceTest); 35 }; 36 37 BookmarkUndoServiceTest::BookmarkUndoServiceTest() {} 38 39 void BookmarkUndoServiceTest::SetUp() { 40 profile_.reset(new TestingProfile); 41 profile_->CreateBookmarkModel(true); 42 test::WaitForBookmarkModelToLoad(GetModel()); 43 } 44 45 BookmarkModel* BookmarkUndoServiceTest::GetModel() { 46 return BookmarkModelFactory::GetForProfile(profile_.get()); 47 } 48 49 BookmarkUndoService* BookmarkUndoServiceTest::GetUndoService() { 50 return BookmarkUndoServiceFactory::GetForProfile(profile_.get()); 51 } 52 53 void BookmarkUndoServiceTest::TearDown() { 54 profile_.reset(NULL); 55 } 56 57 TEST_F(BookmarkUndoServiceTest, AddBookmark) { 58 BookmarkModel* model = GetModel(); 59 BookmarkUndoService* undo_service = GetUndoService(); 60 model->AddObserver(undo_service); 61 62 const BookmarkNode* parent = model->other_node(); 63 model->AddURL(parent, 0, ASCIIToUTF16("foo"), GURL("http://www.bar.com")); 64 65 // Undo bookmark creation and test for no bookmarks. 66 undo_service->undo_manager()->Undo(); 67 EXPECT_EQ(0, model->other_node()->child_count()); 68 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")); 74 } 75 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); 81 82 const BookmarkNode* parent = model->other_node(); 83 model->AddURL(parent, 0, ASCIIToUTF16("foo"), GURL("http://www.bar.com")); 84 model->Remove(parent, 0); 85 86 EXPECT_EQ(2U, undo_service->undo_manager()->undo_count()); 87 EXPECT_EQ(0U, undo_service->undo_manager()->redo_count()); 88 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")); 95 96 EXPECT_EQ(1U, undo_service->undo_manager()->undo_count()); 97 EXPECT_EQ(1U, undo_service->undo_manager()->redo_count()); 98 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()); 102 103 EXPECT_EQ(2U, undo_service->undo_manager()->undo_count()); 104 EXPECT_EQ(0U, undo_service->undo_manager()->redo_count()); 105 } 106 107 // Ensure the undo/redo works for editing of bookmark information grouped into 108 // one action. 109 TEST_F(BookmarkUndoServiceTest, UndoBookmarkGroupedAction) { 110 BookmarkModel* model = GetModel(); 111 BookmarkUndoService* undo_service = GetUndoService(); 112 model->AddObserver(undo_service); 113 114 const BookmarkNode* n1 = model->AddURL(model->other_node(), 115 0, 116 ASCIIToUTF16("foo"), 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(); 122 123 EXPECT_EQ(2U, undo_service->undo_manager()->undo_count()); 124 EXPECT_EQ(0U, undo_service->undo_manager()->redo_count()); 125 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")); 132 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")); 139 140 EXPECT_EQ(2U, undo_service->undo_manager()->undo_count()); 141 EXPECT_EQ(0U, undo_service->undo_manager()->redo_count()); 142 } 143 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); 149 150 const BookmarkNode* n1 = model->AddURL(model->other_node(), 151 0, 152 ASCIIToUTF16("foo"), 153 GURL("http://www.foo.com")); 154 const BookmarkNode* n2 = model->AddURL(model->other_node(), 155 1, 156 ASCIIToUTF16("moo"), 157 GURL("http://www.moo.com")); 158 const BookmarkNode* n3 = model->AddURL(model->other_node(), 159 2, 160 ASCIIToUTF16("bar"), 161 GURL("http://www.bar.com")); 162 model->Move(n1, model->other_node(), 3); 163 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); 169 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); 175 } 176 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); 182 183 const BookmarkNode* n1 = model->AddURL(model->other_node(), 184 0, 185 ASCIIToUTF16("foo"), 186 GURL("http://www.foo.com")); 187 const BookmarkNode* n2 = model->AddURL(model->other_node(), 188 1, 189 ASCIIToUTF16("moo"), 190 GURL("http://www.moo.com")); 191 const BookmarkNode* n3 = model->AddURL(model->other_node(), 192 2, 193 ASCIIToUTF16("bar"), 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); 198 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()); 207 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); 216 } 217 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); 224 225 const BookmarkNode* f1 = model->AddFolder(model->other_node(), 226 0, 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); 231 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")); 238 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")); 242 243 // Undo the title change and ensure the folder was updated even though the 244 // id has changed. 245 undo_service->undo_manager()->Undo(); 246 node = model->other_node()->GetChild(0); 247 EXPECT_EQ(node->GetTitle(), ASCIIToUTF16("folder")); 248 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()); 252 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()); 256 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")); 268 269 undo_service->undo_manager()->Redo(); // folder deletion 270 EXPECT_EQ(0, model->other_node()->child_count()); 271 } 272 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); 278 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); 284 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")); 290 291 node = parent->GetChild(1); 292 EXPECT_EQ(node->GetTitle(), ASCIIToUTF16("moo")); 293 EXPECT_EQ(node->url(), GURL("http://www.moo.com")); 294 295 node = parent->GetChild(2); 296 EXPECT_EQ(node->GetTitle(), ASCIIToUTF16("bar")); 297 EXPECT_EQ(node->url(), GURL("http://www.bar.com")); 298 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")); 304 305 node = parent->GetChild(1); 306 EXPECT_EQ(node->GetTitle(), ASCIIToUTF16("foo")); 307 EXPECT_EQ(node->url(), GURL("http://www.foo.com")); 308 309 node = parent->GetChild(2); 310 EXPECT_EQ(node->GetTitle(), ASCIIToUTF16("moo")); 311 EXPECT_EQ(node->url(), GURL("http://www.moo.com")); 312 313 } 314 315 TEST_F(BookmarkUndoServiceTest, UndoBookmarkRemoveAll) { 316 BookmarkModel* model = GetModel(); 317 BookmarkUndoService* undo_service = GetUndoService(); 318 model->AddObserver(undo_service); 319 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")); 326 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")); 331 332 model->RemoveAllUserBookmarks(); 333 334 // Test that the undo of RemoveAllUserBookmarks restores all folders and 335 // bookmarks. 336 undo_service->undo_manager()->Undo(); 337 338 ASSERT_EQ(2, model->other_node()->child_count()); 339 EXPECT_EQ(1, model->other_node()->GetChild(1)->child_count()); 340 const BookmarkNode* node = model->other_node()->GetChild(1)->GetChild(0); 341 EXPECT_EQ(node->GetTitle(), ASCIIToUTF16("bar")); 342 EXPECT_EQ(node->url(), GURL("http://www.bar.com")); 343 344 ASSERT_EQ(2, model->bookmark_bar_node()->child_count()); 345 EXPECT_EQ(1, model->bookmark_bar_node()->GetChild(1)->child_count()); 346 node = model->bookmark_bar_node()->GetChild(1)->GetChild(0); 347 EXPECT_EQ(node->GetTitle(), ASCIIToUTF16("b")); 348 EXPECT_EQ(node->url(), GURL("http://www.b.com")); 349 350 // Test that the redo removes all folders and bookmarks. 351 undo_service->undo_manager()->Redo(); 352 EXPECT_EQ(0, model->other_node()->child_count()); 353 EXPECT_EQ(0, model->bookmark_bar_node()->child_count()); 354 } 355 356 TEST_F(BookmarkUndoServiceTest, UndoRemoveFolderWithBookmarks) { 357 BookmarkModel* model = GetModel(); 358 BookmarkUndoService* undo_service = GetUndoService(); 359 model->AddObserver(undo_service); 360 361 // Setup bookmarks in the Other Bookmarks. 362 const BookmarkNode* new_folder; 363 const BookmarkNode* parent = model->other_node(); 364 new_folder = model->AddFolder(parent, 0, ASCIIToUTF16("folder")); 365 model->AddURL(new_folder, 0, ASCIIToUTF16("bar"), GURL("http://www.bar.com")); 366 367 model->Remove(parent, 0); 368 369 // Test that the undo restores the bookmark and folder. 370 undo_service->undo_manager()->Undo(); 371 372 ASSERT_EQ(1, model->other_node()->child_count()); 373 new_folder = model->other_node()->GetChild(0); 374 EXPECT_EQ(1, new_folder->child_count()); 375 const BookmarkNode* node = new_folder->GetChild(0); 376 EXPECT_EQ(node->GetTitle(), ASCIIToUTF16("bar")); 377 EXPECT_EQ(node->url(), GURL("http://www.bar.com")); 378 379 // Test that the redo restores the bookmark and folder. 380 undo_service->undo_manager()->Redo(); 381 382 ASSERT_EQ(0, model->other_node()->child_count()); 383 384 // Test that the undo after a redo restores the bookmark and folder. 385 undo_service->undo_manager()->Undo(); 386 387 ASSERT_EQ(1, model->other_node()->child_count()); 388 new_folder = model->other_node()->GetChild(0); 389 EXPECT_EQ(1, new_folder->child_count()); 390 node = new_folder->GetChild(0); 391 EXPECT_EQ(node->GetTitle(), ASCIIToUTF16("bar")); 392 EXPECT_EQ(node->url(), GURL("http://www.bar.com")); 393 } 394 395 TEST_F(BookmarkUndoServiceTest, TestUpperLimit) { 396 BookmarkModel* model = GetModel(); 397 BookmarkUndoService* undo_service = GetUndoService(); 398 model->AddObserver(undo_service); 399 400 // This maximum is set in undo_manager.cc 401 const size_t kMaxUndoGroups = 100; 402 403 const BookmarkNode* parent = model->other_node(); 404 model->AddURL(parent, 0, ASCIIToUTF16("foo"), GURL("http://www.foo.com")); 405 for (size_t i = 1; i < kMaxUndoGroups + 1; ++i) 406 model->AddURL(parent, i, ASCIIToUTF16("bar"), GURL("http://www.bar.com")); 407 408 EXPECT_EQ(kMaxUndoGroups, undo_service->undo_manager()->undo_count()); 409 410 // Undo as many operations as possible. 411 while (undo_service->undo_manager()->undo_count()) 412 undo_service->undo_manager()->Undo(); 413 414 EXPECT_EQ(1, parent->child_count()); 415 const BookmarkNode* node = model->other_node()->GetChild(0); 416 EXPECT_EQ(node->GetTitle(), ASCIIToUTF16("foo")); 417 EXPECT_EQ(node->url(), GURL("http://www.foo.com")); 418 } 419 420 } // namespace 421