1 // Copyright (c) 2011 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 <gtk/gtk.h> 6 7 #include <string> 8 9 #include "base/string_util.h" 10 #include "base/utf_string_conversions.h" 11 #include "chrome/browser/bookmarks/bookmark_model.h" 12 #include "chrome/browser/ui/gtk/bookmarks/bookmark_editor_gtk.h" 13 #include "chrome/browser/ui/gtk/bookmarks/bookmark_tree_model.h" 14 #include "chrome/test/testing_profile.h" 15 #include "content/browser/browser_thread.h" 16 #include "testing/gtest/include/gtest/gtest.h" 17 18 using base::Time; 19 using base::TimeDelta; 20 using bookmark_utils::GetTitleFromTreeIter; 21 22 // Base class for bookmark editor tests. This class is a copy from 23 // bookmark_editor_view_unittest.cc, and all the tests in this file are 24 // GTK-ifications of the corresponding views tests. Testing here is really 25 // important because on Linux, we make round trip copies from chrome's 26 // BookmarkModel class to GTK's native GtkTreeStore. 27 class BookmarkEditorGtkTest : public testing::Test { 28 public: 29 BookmarkEditorGtkTest() 30 : ui_thread_(BrowserThread::UI, &message_loop_), 31 file_thread_(BrowserThread::FILE, &message_loop_), 32 model_(NULL) { 33 } 34 35 virtual void SetUp() { 36 profile_.reset(new TestingProfile()); 37 profile_->CreateBookmarkModel(true); 38 profile_->BlockUntilBookmarkModelLoaded(); 39 40 model_ = profile_->GetBookmarkModel(); 41 42 AddTestData(); 43 } 44 45 virtual void TearDown() { 46 } 47 48 protected: 49 MessageLoopForUI message_loop_; 50 BrowserThread ui_thread_; 51 BrowserThread file_thread_; 52 BookmarkModel* model_; 53 scoped_ptr<TestingProfile> profile_; 54 55 std::string base_path() const { return "file:///c:/tmp/"; } 56 57 const BookmarkNode* GetNode(const std::string& name) { 58 return model_->GetMostRecentlyAddedNodeForURL(GURL(base_path() + name)); 59 } 60 61 private: 62 // Creates the following structure: 63 // bookmark bar node 64 // a 65 // F1 66 // f1a 67 // F11 68 // f11a 69 // F2 70 // other node 71 // oa 72 // OF1 73 // of1a 74 void AddTestData() { 75 std::string test_base = base_path(); 76 77 model_->AddURL(model_->GetBookmarkBarNode(), 0, ASCIIToUTF16("a"), 78 GURL(test_base + "a")); 79 const BookmarkNode* f1 = 80 model_->AddFolder(model_->GetBookmarkBarNode(), 1, ASCIIToUTF16("F1")); 81 model_->AddURL(f1, 0, ASCIIToUTF16("f1a"), GURL(test_base + "f1a")); 82 const BookmarkNode* f11 = model_->AddFolder(f1, 1, ASCIIToUTF16("F11")); 83 model_->AddURL(f11, 0, ASCIIToUTF16("f11a"), GURL(test_base + "f11a")); 84 model_->AddFolder(model_->GetBookmarkBarNode(), 2, ASCIIToUTF16("F2")); 85 86 // Children of the other node. 87 model_->AddURL(model_->other_node(), 0, ASCIIToUTF16("oa"), 88 GURL(test_base + "oa")); 89 const BookmarkNode* of1 = 90 model_->AddFolder(model_->other_node(), 1, ASCIIToUTF16("OF1")); 91 model_->AddURL(of1, 0, ASCIIToUTF16("of1a"), GURL(test_base + "of1a")); 92 } 93 }; 94 95 // Makes sure the tree model matches that of the bookmark bar model. 96 TEST_F(BookmarkEditorGtkTest, ModelsMatch) { 97 BookmarkEditorGtk editor(NULL, profile_.get(), NULL, 98 BookmarkEditor::EditDetails(), 99 BookmarkEditor::SHOW_TREE); 100 101 // The root should have two children, one for the bookmark bar node, 102 // the other for the 'other bookmarks' folder. 103 GtkTreeModel* store = GTK_TREE_MODEL(editor.tree_store_); 104 GtkTreeIter toplevel; 105 ASSERT_TRUE(gtk_tree_model_get_iter_first(store, &toplevel)); 106 GtkTreeIter bookmark_bar_node = toplevel; 107 ASSERT_TRUE(gtk_tree_model_iter_next(store, &toplevel)); 108 GtkTreeIter other_node = toplevel; 109 ASSERT_FALSE(gtk_tree_model_iter_next(store, &toplevel)); 110 111 // The bookmark bar should have 2 nodes: folder F1 and F2. 112 GtkTreeIter f1_iter; 113 GtkTreeIter child; 114 ASSERT_EQ(2, gtk_tree_model_iter_n_children(store, &bookmark_bar_node)); 115 ASSERT_TRUE(gtk_tree_model_iter_children(store, &child, &bookmark_bar_node)); 116 f1_iter = child; 117 ASSERT_EQ("F1", UTF16ToUTF8(GetTitleFromTreeIter(store, &child))); 118 ASSERT_TRUE(gtk_tree_model_iter_next(store, &child)); 119 ASSERT_EQ("F2", UTF16ToUTF8(GetTitleFromTreeIter(store, &child))); 120 ASSERT_FALSE(gtk_tree_model_iter_next(store, &child)); 121 122 // F1 should have one child, F11 123 ASSERT_EQ(1, gtk_tree_model_iter_n_children(store, &f1_iter)); 124 ASSERT_TRUE(gtk_tree_model_iter_children(store, &child, &f1_iter)); 125 ASSERT_EQ("F11", UTF16ToUTF8(GetTitleFromTreeIter(store, &child))); 126 ASSERT_FALSE(gtk_tree_model_iter_next(store, &child)); 127 128 // Other node should have one child (OF1). 129 ASSERT_EQ(1, gtk_tree_model_iter_n_children(store, &other_node)); 130 ASSERT_TRUE(gtk_tree_model_iter_children(store, &child, &other_node)); 131 ASSERT_EQ("OF1", UTF16ToUTF8(GetTitleFromTreeIter(store, &child))); 132 ASSERT_FALSE(gtk_tree_model_iter_next(store, &child)); 133 } 134 135 // Changes the title and makes sure parent/visual order doesn't change. 136 TEST_F(BookmarkEditorGtkTest, EditTitleKeepsPosition) { 137 BookmarkEditorGtk editor(NULL, profile_.get(), NULL, 138 BookmarkEditor::EditDetails(GetNode("a")), 139 BookmarkEditor::SHOW_TREE); 140 gtk_entry_set_text(GTK_ENTRY(editor.name_entry_), "new_a"); 141 142 GtkTreeIter bookmark_bar_node; 143 GtkTreeModel* store = GTK_TREE_MODEL(editor.tree_store_); 144 ASSERT_TRUE(gtk_tree_model_get_iter_first(store, &bookmark_bar_node)); 145 editor.ApplyEdits(&bookmark_bar_node); 146 147 const BookmarkNode* bb_node = 148 profile_->GetBookmarkModel()->GetBookmarkBarNode(); 149 ASSERT_EQ(ASCIIToUTF16("new_a"), bb_node->GetChild(0)->GetTitle()); 150 // The URL shouldn't have changed. 151 ASSERT_TRUE(GURL(base_path() + "a") == bb_node->GetChild(0)->GetURL()); 152 } 153 154 // Changes the url and makes sure parent/visual order doesn't change. 155 TEST_F(BookmarkEditorGtkTest, EditURLKeepsPosition) { 156 Time node_time = GetNode("a")->date_added(); 157 BookmarkEditorGtk editor(NULL, profile_.get(), NULL, 158 BookmarkEditor::EditDetails(GetNode("a")), 159 BookmarkEditor::SHOW_TREE); 160 gtk_entry_set_text(GTK_ENTRY(editor.url_entry_), 161 GURL(base_path() + "new_a").spec().c_str()); 162 163 GtkTreeIter bookmark_bar_node; 164 GtkTreeModel* store = GTK_TREE_MODEL(editor.tree_store_); 165 ASSERT_TRUE(gtk_tree_model_get_iter_first(store, &bookmark_bar_node)); 166 editor.ApplyEdits(&bookmark_bar_node); 167 168 const BookmarkNode* bb_node = 169 profile_->GetBookmarkModel()->GetBookmarkBarNode(); 170 ASSERT_EQ(ASCIIToUTF16("a"), bb_node->GetChild(0)->GetTitle()); 171 // The URL should have changed. 172 ASSERT_TRUE(GURL(base_path() + "new_a") == bb_node->GetChild(0)->GetURL()); 173 ASSERT_TRUE(node_time == bb_node->GetChild(0)->date_added()); 174 } 175 176 // Moves 'a' to be a child of the other node. 177 TEST_F(BookmarkEditorGtkTest, ChangeParent) { 178 BookmarkEditorGtk editor(NULL, profile_.get(), NULL, 179 BookmarkEditor::EditDetails(GetNode("a")), 180 BookmarkEditor::SHOW_TREE); 181 182 GtkTreeModel* store = GTK_TREE_MODEL(editor.tree_store_); 183 GtkTreeIter gtk_other_node; 184 ASSERT_TRUE(gtk_tree_model_get_iter_first(store, >k_other_node)); 185 ASSERT_TRUE(gtk_tree_model_iter_next(store, >k_other_node)); 186 editor.ApplyEdits(>k_other_node); 187 188 const BookmarkNode* other_node = profile_->GetBookmarkModel()->other_node(); 189 ASSERT_EQ(ASCIIToUTF16("a"), other_node->GetChild(2)->GetTitle()); 190 ASSERT_TRUE(GURL(base_path() + "a") == other_node->GetChild(2)->GetURL()); 191 } 192 193 // Moves 'a' to be a child of the other node. 194 // Moves 'a' to be a child of the other node and changes its url to new_a. 195 TEST_F(BookmarkEditorGtkTest, ChangeParentAndURL) { 196 Time node_time = GetNode("a")->date_added(); 197 BookmarkEditorGtk editor(NULL, profile_.get(), NULL, 198 BookmarkEditor::EditDetails(GetNode("a")), 199 BookmarkEditor::SHOW_TREE); 200 201 gtk_entry_set_text(GTK_ENTRY(editor.url_entry_), 202 GURL(base_path() + "new_a").spec().c_str()); 203 204 GtkTreeModel* store = GTK_TREE_MODEL(editor.tree_store_); 205 GtkTreeIter gtk_other_node; 206 ASSERT_TRUE(gtk_tree_model_get_iter_first(store, >k_other_node)); 207 ASSERT_TRUE(gtk_tree_model_iter_next(store, >k_other_node)); 208 editor.ApplyEdits(>k_other_node); 209 210 const BookmarkNode* other_node = profile_->GetBookmarkModel()->other_node(); 211 ASSERT_EQ(ASCIIToUTF16("a"), other_node->GetChild(2)->GetTitle()); 212 ASSERT_TRUE(GURL(base_path() + "new_a") == other_node->GetChild(2)->GetURL()); 213 ASSERT_TRUE(node_time == other_node->GetChild(2)->date_added()); 214 } 215 216 // Creates a new folder and moves a node to it. 217 TEST_F(BookmarkEditorGtkTest, MoveToNewParent) { 218 BookmarkEditorGtk editor(NULL, profile_.get(), NULL, 219 BookmarkEditor::EditDetails(GetNode("a")), 220 BookmarkEditor::SHOW_TREE); 221 222 GtkTreeIter bookmark_bar_node; 223 GtkTreeModel* store = GTK_TREE_MODEL(editor.tree_store_); 224 ASSERT_TRUE(gtk_tree_model_get_iter_first(store, &bookmark_bar_node)); 225 226 // The bookmark bar should have 2 nodes: folder F1 and F2. 227 GtkTreeIter f2_iter; 228 ASSERT_EQ(2, gtk_tree_model_iter_n_children(store, &bookmark_bar_node)); 229 ASSERT_TRUE(gtk_tree_model_iter_children(store, &f2_iter, 230 &bookmark_bar_node)); 231 ASSERT_TRUE(gtk_tree_model_iter_next(store, &f2_iter)); 232 233 // Create two nodes: "F21" as a child of "F2" and "F211" as a child of "F21". 234 GtkTreeIter f21_iter; 235 editor.AddNewFolder(&f2_iter, &f21_iter); 236 gtk_tree_store_set(editor.tree_store_, &f21_iter, 237 bookmark_utils::FOLDER_NAME, "F21", -1); 238 GtkTreeIter f211_iter; 239 editor.AddNewFolder(&f21_iter, &f211_iter); 240 gtk_tree_store_set(editor.tree_store_, &f211_iter, 241 bookmark_utils::FOLDER_NAME, "F211", -1); 242 243 ASSERT_EQ(1, gtk_tree_model_iter_n_children(store, &f2_iter)); 244 245 editor.ApplyEdits(&f2_iter); 246 247 const BookmarkNode* bb_node = 248 profile_->GetBookmarkModel()->GetBookmarkBarNode(); 249 const BookmarkNode* mf2 = bb_node->GetChild(1); 250 251 // F2 in the model should have two children now: F21 and the node edited. 252 ASSERT_EQ(2, mf2->child_count()); 253 // F21 should be first. 254 ASSERT_EQ(ASCIIToUTF16("F21"), mf2->GetChild(0)->GetTitle()); 255 // Then a. 256 ASSERT_EQ(ASCIIToUTF16("a"), mf2->GetChild(1)->GetTitle()); 257 258 // F21 should have one child, F211. 259 const BookmarkNode* mf21 = mf2->GetChild(0); 260 ASSERT_EQ(1, mf21->child_count()); 261 ASSERT_EQ(ASCIIToUTF16("F211"), mf21->GetChild(0)->GetTitle()); 262 } 263 264 // Brings up the editor, creating a new URL on the bookmark bar. 265 TEST_F(BookmarkEditorGtkTest, NewURL) { 266 BookmarkEditorGtk editor(NULL, profile_.get(), NULL, 267 BookmarkEditor::EditDetails(), 268 BookmarkEditor::SHOW_TREE); 269 270 gtk_entry_set_text(GTK_ENTRY(editor.url_entry_), 271 GURL(base_path() + "a").spec().c_str()); 272 gtk_entry_set_text(GTK_ENTRY(editor.name_entry_), "new_a"); 273 274 GtkTreeIter bookmark_bar_node; 275 GtkTreeModel* store = GTK_TREE_MODEL(editor.tree_store_); 276 ASSERT_TRUE(gtk_tree_model_get_iter_first(store, &bookmark_bar_node)); 277 editor.ApplyEdits(&bookmark_bar_node); 278 279 const BookmarkNode* bb_node = 280 profile_->GetBookmarkModel()->GetBookmarkBarNode(); 281 ASSERT_EQ(4, bb_node->child_count()); 282 283 const BookmarkNode* new_node = bb_node->GetChild(3); 284 EXPECT_EQ(ASCIIToUTF16("new_a"), new_node->GetTitle()); 285 EXPECT_TRUE(GURL(base_path() + "a") == new_node->GetURL()); 286 } 287 288 // Brings up the editor with no tree and modifies the url. 289 TEST_F(BookmarkEditorGtkTest, ChangeURLNoTree) { 290 BookmarkEditorGtk editor(NULL, profile_.get(), NULL, 291 BookmarkEditor::EditDetails( 292 model_->other_node()->GetChild(0)), 293 BookmarkEditor::NO_TREE); 294 295 gtk_entry_set_text(GTK_ENTRY(editor.url_entry_), 296 GURL(base_path() + "a").spec().c_str()); 297 gtk_entry_set_text(GTK_ENTRY(editor.name_entry_), "new_a"); 298 299 editor.ApplyEdits(NULL); 300 301 const BookmarkNode* other_node = profile_->GetBookmarkModel()->other_node(); 302 ASSERT_EQ(2, other_node->child_count()); 303 304 const BookmarkNode* new_node = other_node->GetChild(0); 305 306 EXPECT_EQ(ASCIIToUTF16("new_a"), new_node->GetTitle()); 307 EXPECT_TRUE(GURL(base_path() + "a") == new_node->GetURL()); 308 } 309 310 // Brings up the editor with no tree and modifies only the title. 311 TEST_F(BookmarkEditorGtkTest, ChangeTitleNoTree) { 312 BookmarkEditorGtk editor(NULL, profile_.get(), NULL, 313 BookmarkEditor::EditDetails( 314 model_->other_node()->GetChild(0)), 315 BookmarkEditor::NO_TREE); 316 gtk_entry_set_text(GTK_ENTRY(editor.name_entry_), "new_a"); 317 318 editor.ApplyEdits(); 319 320 const BookmarkNode* other_node = profile_->GetBookmarkModel()->other_node(); 321 ASSERT_EQ(2, other_node->child_count()); 322 323 const BookmarkNode* new_node = other_node->GetChild(0); 324 EXPECT_EQ(ASCIIToUTF16("new_a"), new_node->GetTitle()); 325 } 326