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/ui/views/bookmarks/bookmark_menu_delegate.h"
6
7#include "base/strings/utf_string_conversions.h"
8#include "chrome/browser/bookmarks/bookmark_model_factory.h"
9#include "chrome/browser/bookmarks/bookmark_stats.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/test/base/browser_with_test_window_test.h"
12#include "chrome/test/base/testing_profile.h"
13#include "components/bookmarks/browser/bookmark_model.h"
14#include "components/bookmarks/test/bookmark_test_helpers.h"
15#include "ui/views/controls/menu/menu_item_view.h"
16#include "ui/views/controls/menu/menu_runner.h"
17#include "ui/views/controls/menu/submenu_view.h"
18
19using base::ASCIIToUTF16;
20
21class BookmarkMenuDelegateTest : public BrowserWithTestWindowTest {
22 public:
23  BookmarkMenuDelegateTest() : model_(NULL) {}
24
25  virtual void SetUp() OVERRIDE {
26    BrowserWithTestWindowTest::SetUp();
27
28    profile()->CreateBookmarkModel(true);
29
30    model_ = BookmarkModelFactory::GetForProfile(profile());
31    test::WaitForBookmarkModelToLoad(model_);
32
33    AddTestData();
34  }
35
36  virtual void TearDown() OVERRIDE {
37    if (bookmark_menu_delegate_.get()) {
38      // Since we never show the menu we need to pass the MenuItemView to
39      // MenuRunner so that the MenuItemView is destroyed.
40      views::MenuRunner menu_runner(bookmark_menu_delegate_->menu(), 0);
41      bookmark_menu_delegate_.reset();
42    }
43    BrowserWithTestWindowTest::TearDown();
44  }
45
46 protected:
47  void NewDelegate(int min_menu_id, int max_menu_id) {
48    // Destroy current menu if available, see comments in TearDown().
49    if (bookmark_menu_delegate_.get())
50      views::MenuRunner menu_runner(bookmark_menu_delegate_->menu(), 0);
51
52    bookmark_menu_delegate_.reset(
53        new BookmarkMenuDelegate(browser(), NULL, NULL,
54                                 min_menu_id, max_menu_id));
55  }
56
57  void NewAndInitDelegateForPermanent(int min_menu_id,
58                                      int max_menu_id) {
59    const BookmarkNode* node = model_->bookmark_bar_node();
60    NewDelegate(min_menu_id, max_menu_id);
61    bookmark_menu_delegate_->Init(&test_delegate_, NULL, node, 0,
62                                  BookmarkMenuDelegate::SHOW_PERMANENT_FOLDERS,
63                                  BOOKMARK_LAUNCH_LOCATION_NONE);
64  }
65
66  BookmarkModel* model_;
67
68  scoped_ptr<BookmarkMenuDelegate> bookmark_menu_delegate_;
69
70 private:
71  std::string base_path() const { return "file:///c:/tmp/"; }
72
73  // Creates the following structure:
74  // bookmark bar node
75  //   a
76  //   F1
77  //    f1a
78  //    F11
79  //     f11a
80  //   F2
81  // other node
82  //   oa
83  //   OF1
84  //     of1a
85  void AddTestData() {
86    const BookmarkNode* bb_node = model_->bookmark_bar_node();
87    std::string test_base = base_path();
88    model_->AddURL(bb_node, 0, ASCIIToUTF16("a"), GURL(test_base + "a"));
89    const BookmarkNode* f1 = model_->AddFolder(bb_node, 1, ASCIIToUTF16("F1"));
90    model_->AddURL(f1, 0, ASCIIToUTF16("f1a"), GURL(test_base + "f1a"));
91    const BookmarkNode* f11 = model_->AddFolder(f1, 1, ASCIIToUTF16("F11"));
92    model_->AddURL(f11, 0, ASCIIToUTF16("f11a"), GURL(test_base + "f11a"));
93    model_->AddFolder(bb_node, 2, ASCIIToUTF16("F2"));
94
95    // Children of the other node.
96    model_->AddURL(model_->other_node(), 0, ASCIIToUTF16("oa"),
97                   GURL(test_base + "oa"));
98    const BookmarkNode* of1 =
99        model_->AddFolder(model_->other_node(), 1, ASCIIToUTF16("OF1"));
100    model_->AddURL(of1, 0, ASCIIToUTF16("of1a"), GURL(test_base + "of1a"));
101  }
102
103  views::MenuDelegate test_delegate_;
104
105  DISALLOW_COPY_AND_ASSIGN(BookmarkMenuDelegateTest);
106};
107
108// Verifies WillRemoveBookmarks() doesn't attempt to access MenuItemViews that
109// have since been deleted.
110TEST_F(BookmarkMenuDelegateTest, RemoveBookmarks) {
111  views::MenuDelegate test_delegate;
112  const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(1);
113  NewDelegate(0, kint32max);
114  bookmark_menu_delegate_->Init(&test_delegate, NULL, node, 0,
115                                BookmarkMenuDelegate::HIDE_PERMANENT_FOLDERS,
116                                BOOKMARK_LAUNCH_LOCATION_NONE);
117  std::vector<const BookmarkNode*> nodes_to_remove;
118  nodes_to_remove.push_back(node->GetChild(1));
119  bookmark_menu_delegate_->WillRemoveBookmarks(nodes_to_remove);
120  nodes_to_remove.clear();
121  bookmark_menu_delegate_->DidRemoveBookmarks();
122}
123
124// Verifies menu ID's of items in menu fall within the specified range.
125TEST_F(BookmarkMenuDelegateTest, MenuIdRange) {
126  // Start with maximum menu Id of 10 - the number of items that AddTestData()
127  // populated.  Everything should be created.
128  NewAndInitDelegateForPermanent(0, 10);
129  views::MenuItemView* root_item = bookmark_menu_delegate_->menu();
130  ASSERT_TRUE(root_item->HasSubmenu());
131  EXPECT_EQ(4, root_item->GetSubmenu()->GetMenuItemCount());
132  EXPECT_EQ(5, root_item->GetSubmenu()->child_count());  // Includes separator.
133  views::MenuItemView* F1_item = root_item->GetSubmenu()->GetMenuItemAt(1);
134  ASSERT_TRUE(F1_item->HasSubmenu());
135  EXPECT_EQ(2, F1_item->GetSubmenu()->GetMenuItemCount());
136  views::MenuItemView* F11_item = F1_item->GetSubmenu()->GetMenuItemAt(1);
137  ASSERT_TRUE(F11_item->HasSubmenu());
138  EXPECT_EQ(1, F11_item->GetSubmenu()->GetMenuItemCount());
139  views::MenuItemView* other_item = root_item->GetSubmenu()->GetMenuItemAt(3);
140  ASSERT_TRUE(other_item->HasSubmenu());
141  EXPECT_EQ(2, other_item->GetSubmenu()->GetMenuItemCount());
142  views::MenuItemView* OF1_item = other_item->GetSubmenu()->GetMenuItemAt(1);
143  ASSERT_TRUE(OF1_item->HasSubmenu());
144  EXPECT_EQ(1, OF1_item->GetSubmenu()->GetMenuItemCount());
145
146  // Reduce maximum 9.  "of1a" item should not be created.
147  NewAndInitDelegateForPermanent(0, 9);
148  root_item = bookmark_menu_delegate_->menu();
149  EXPECT_EQ(4, root_item->GetSubmenu()->GetMenuItemCount());
150  EXPECT_EQ(5, root_item->GetSubmenu()->child_count());  // Includes separator.
151  other_item = root_item->GetSubmenu()->GetMenuItemAt(3);
152  OF1_item = other_item->GetSubmenu()->GetMenuItemAt(1);
153  EXPECT_EQ(0, OF1_item->GetSubmenu()->GetMenuItemCount());
154
155  // Reduce maximum 8.  "OF1" submenu should not be created.
156  NewAndInitDelegateForPermanent(0, 8);
157  root_item = bookmark_menu_delegate_->menu();
158  EXPECT_EQ(4, root_item->GetSubmenu()->GetMenuItemCount());
159  EXPECT_EQ(5, root_item->GetSubmenu()->child_count());  // Includes separator.
160  other_item = root_item->GetSubmenu()->GetMenuItemAt(3);
161  EXPECT_EQ(1, other_item->GetSubmenu()->GetMenuItemCount());
162
163  // Reduce maximum 7.  "Other" submenu should be empty.
164  NewAndInitDelegateForPermanent(0, 7);
165  root_item = bookmark_menu_delegate_->menu();
166  EXPECT_EQ(4, root_item->GetSubmenu()->GetMenuItemCount());
167  EXPECT_EQ(5, root_item->GetSubmenu()->child_count());  // Includes separator.
168  other_item = root_item->GetSubmenu()->GetMenuItemAt(3);
169  EXPECT_EQ(0, other_item->GetSubmenu()->GetMenuItemCount());
170
171  // Reduce maximum to 6.  "Other" submenu should not be created, and no
172  // separator.
173  NewAndInitDelegateForPermanent(0, 6);
174  root_item = bookmark_menu_delegate_->menu();
175  EXPECT_EQ(3, root_item->GetSubmenu()->GetMenuItemCount());
176  EXPECT_EQ(3, root_item->GetSubmenu()->child_count());  // No separator.
177
178  // Reduce maximum 5.  "F2" and "Other" submenus shouldn't be created.
179  NewAndInitDelegateForPermanent(0, 5);
180  root_item = bookmark_menu_delegate_->menu();
181  EXPECT_EQ(2, root_item->GetSubmenu()->GetMenuItemCount());
182  EXPECT_EQ(2, root_item->GetSubmenu()->child_count());  // No separator.
183  F1_item = root_item->GetSubmenu()->GetMenuItemAt(1);
184  F11_item = F1_item->GetSubmenu()->GetMenuItemAt(1);
185  EXPECT_EQ(1, F11_item->GetSubmenu()->GetMenuItemCount());
186
187  // Reduce maximum to 4.  "f11a" item and "F2" and "Other" submenus should
188  // not be created.
189  NewAndInitDelegateForPermanent(0, 4);
190  root_item = bookmark_menu_delegate_->menu();
191  EXPECT_EQ(2, root_item->GetSubmenu()->GetMenuItemCount());
192  EXPECT_EQ(2, root_item->GetSubmenu()->child_count());  // No separator.
193  F1_item = root_item->GetSubmenu()->GetMenuItemAt(1);
194  F11_item = F1_item->GetSubmenu()->GetMenuItemAt(1);
195  EXPECT_EQ(0, F11_item->GetSubmenu()->GetMenuItemCount());
196
197  // Reduce maximum to 3.  "F11", "F2" and "Other" submenus should not be
198  // created.
199  NewAndInitDelegateForPermanent(0, 3);
200  root_item = bookmark_menu_delegate_->menu();
201  EXPECT_EQ(2, root_item->GetSubmenu()->GetMenuItemCount());
202  EXPECT_EQ(2, root_item->GetSubmenu()->child_count());  // No separator.
203  F1_item = root_item->GetSubmenu()->GetMenuItemAt(1);
204  EXPECT_EQ(views::MenuItemView::SUBMENU, F1_item->GetType());
205  EXPECT_EQ(1, F1_item->GetSubmenu()->GetMenuItemCount());
206
207  // Reduce maximum 2.  Only "a" item and empty "F1" submenu should be created.
208  NewAndInitDelegateForPermanent(0, 2);
209  root_item = bookmark_menu_delegate_->menu();
210  EXPECT_EQ(2, root_item->GetSubmenu()->GetMenuItemCount());
211  EXPECT_EQ(2, root_item->GetSubmenu()->child_count());  // No separator.
212  F1_item = root_item->GetSubmenu()->GetMenuItemAt(1);
213  EXPECT_EQ(views::MenuItemView::SUBMENU, F1_item->GetType());
214  EXPECT_EQ(0, F1_item->GetSubmenu()->GetMenuItemCount());
215
216  // Reduce maximum to 1.  Only "a" item should be created.
217  NewAndInitDelegateForPermanent(0, 1);
218  root_item = bookmark_menu_delegate_->menu();
219  EXPECT_EQ(1, root_item->GetSubmenu()->GetMenuItemCount());
220  EXPECT_EQ(1, root_item->GetSubmenu()->child_count());  // No separator.
221
222  // Verify correct handling of integer overflow with range, set kint32max as
223  // maximum and 1 less as minimum.  Only "a" item should be created.
224  NewAndInitDelegateForPermanent(kint32max - 1, kint32max);
225  root_item = bookmark_menu_delegate_->menu();
226  EXPECT_EQ(1, root_item->GetSubmenu()->GetMenuItemCount());
227  EXPECT_EQ(1, root_item->GetSubmenu()->child_count());  // No separator.
228}
229