Home | History | Annotate | Download | only in undo
      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