1 // Copyright (c) 2012 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/ui/gtk/bookmarks/bookmark_editor_gtk.h"
11 #include "base/compiler_specific.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/bookmarks/bookmark_model.h"
16 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
17 #include "chrome/browser/bookmarks/bookmark_test_helpers.h"
18 #include "chrome/browser/ui/gtk/bookmarks/bookmark_tree_model.h"
19 #include "chrome/test/base/testing_profile.h"
20 #include "content/public/test/test_browser_thread.h"
21 #include "testing/gtest/include/gtest/gtest.h"
23 using base::ASCIIToUTF16
;
24 using base::UTF16ToUTF8
;
26 using base::TimeDelta
;
27 using content::BrowserThread
;
29 // Base class for bookmark editor tests. This class is a copy from
30 // bookmark_editor_view_unittest.cc, and all the tests in this file are
31 // GTK-ifications of the corresponding views tests. Testing here is really
32 // important because on Linux, we make round trip copies from chrome's
33 // BookmarkModel class to GTK's native GtkTreeStore.
34 class BookmarkEditorGtkTest
: public testing::Test
{
36 BookmarkEditorGtkTest()
38 ui_thread_(BrowserThread::UI
, &message_loop_
),
39 file_thread_(BrowserThread::FILE, &message_loop_
) {
42 virtual void SetUp() OVERRIDE
{
43 profile_
.reset(new TestingProfile());
44 profile_
->CreateBookmarkModel(true);
46 model_
= BookmarkModelFactory::GetForProfile(profile_
.get());
47 test::WaitForBookmarkModelToLoad(model_
);
52 virtual void TearDown() OVERRIDE
{
56 std::string
base_path() const { return "file:///c:/tmp/"; }
58 const BookmarkNode
* GetNode(const std::string
& name
) {
59 return model_
->GetMostRecentlyAddedNodeForURL(GURL(base_path() + name
));
62 BookmarkModel
* model_
;
63 scoped_ptr
<TestingProfile
> profile_
;
66 // Creates the following structure:
81 std::string test_base
= base_path();
83 model_
->AddURL(model_
->bookmark_bar_node(), 0, ASCIIToUTF16("a"),
84 GURL(test_base
+ "a"));
85 const BookmarkNode
* f1
=
86 model_
->AddFolder(model_
->bookmark_bar_node(), 1, ASCIIToUTF16("F1"));
87 model_
->AddURL(f1
, 0, ASCIIToUTF16("f1a"), GURL(test_base
+ "f1a"));
88 const BookmarkNode
* f11
= model_
->AddFolder(f1
, 1, ASCIIToUTF16("F11"));
89 model_
->AddURL(f11
, 0, ASCIIToUTF16("f11a"), GURL(test_base
+ "f11a"));
90 model_
->AddFolder(model_
->bookmark_bar_node(), 2, ASCIIToUTF16("F2"));
92 // Children of the other node.
93 model_
->AddURL(model_
->other_node(), 0, ASCIIToUTF16("oa"),
94 GURL(test_base
+ "oa"));
95 const BookmarkNode
* of1
=
96 model_
->AddFolder(model_
->other_node(), 1, ASCIIToUTF16("OF1"));
97 model_
->AddURL(of1
, 0, ASCIIToUTF16("of1a"), GURL(test_base
+ "of1a"));
99 // Children of the mobile node.
100 model_
->AddURL(model_
->mobile_node(), 0, ASCIIToUTF16("sa"),
101 GURL(test_base
+ "sa"));
104 base::MessageLoopForUI message_loop_
;
105 content::TestBrowserThread ui_thread_
;
106 content::TestBrowserThread file_thread_
;
109 // Makes sure the tree model matches that of the bookmark bar model.
110 TEST_F(BookmarkEditorGtkTest
, ModelsMatch
) {
111 BookmarkEditorGtk
editor(
115 BookmarkEditor::EditDetails::AddNodeInFolder(
116 NULL
, -1, GURL(), base::string16()),
117 BookmarkEditor::SHOW_TREE
);
119 // The root should have two or three children, one for the bookmark bar node,
120 // another for the 'other bookmarks' folder, and depending on the visib
121 GtkTreeModel
* store
= GTK_TREE_MODEL(editor
.tree_store_
);
122 GtkTreeIter toplevel
;
123 ASSERT_TRUE(gtk_tree_model_get_iter_first(store
, &toplevel
));
124 GtkTreeIter bookmark_bar_node
= toplevel
;
125 ASSERT_TRUE(gtk_tree_model_iter_next(store
, &toplevel
));
126 GtkTreeIter other_node
= toplevel
;
127 if (model_
->mobile_node()->IsVisible()) {
128 // If we have a mobile node, then the iterator should find one element after
130 ASSERT_TRUE(gtk_tree_model_iter_next(store
, &toplevel
));
131 ASSERT_FALSE(gtk_tree_model_iter_next(store
, &toplevel
));
133 ASSERT_FALSE(gtk_tree_model_iter_next(store
, &toplevel
));
136 // The bookmark bar should have 2 nodes: folder F1 and F2.
139 ASSERT_EQ(2, gtk_tree_model_iter_n_children(store
, &bookmark_bar_node
));
140 ASSERT_TRUE(gtk_tree_model_iter_children(store
, &child
, &bookmark_bar_node
));
142 ASSERT_EQ("F1", UTF16ToUTF8(GetTitleFromTreeIter(store
, &child
)));
143 ASSERT_TRUE(gtk_tree_model_iter_next(store
, &child
));
144 ASSERT_EQ("F2", UTF16ToUTF8(GetTitleFromTreeIter(store
, &child
)));
145 ASSERT_FALSE(gtk_tree_model_iter_next(store
, &child
));
147 // F1 should have one child, F11
148 ASSERT_EQ(1, gtk_tree_model_iter_n_children(store
, &f1_iter
));
149 ASSERT_TRUE(gtk_tree_model_iter_children(store
, &child
, &f1_iter
));
150 ASSERT_EQ("F11", UTF16ToUTF8(GetTitleFromTreeIter(store
, &child
)));
151 ASSERT_FALSE(gtk_tree_model_iter_next(store
, &child
));
153 // Other node should have one child (OF1).
154 ASSERT_EQ(1, gtk_tree_model_iter_n_children(store
, &other_node
));
155 ASSERT_TRUE(gtk_tree_model_iter_children(store
, &child
, &other_node
));
156 ASSERT_EQ("OF1", UTF16ToUTF8(GetTitleFromTreeIter(store
, &child
)));
157 ASSERT_FALSE(gtk_tree_model_iter_next(store
, &child
));
160 // Changes the title and makes sure parent/visual order doesn't change.
161 TEST_F(BookmarkEditorGtkTest
, EditTitleKeepsPosition
) {
162 BookmarkEditorGtk
editor(NULL
, profile_
.get(), NULL
,
163 BookmarkEditor::EditDetails::EditNode(GetNode("a")),
164 BookmarkEditor::SHOW_TREE
);
165 gtk_entry_set_text(GTK_ENTRY(editor
.name_entry_
), "new_a");
167 GtkTreeIter bookmark_bar_node
;
168 GtkTreeModel
* store
= GTK_TREE_MODEL(editor
.tree_store_
);
169 ASSERT_TRUE(gtk_tree_model_get_iter_first(store
, &bookmark_bar_node
));
170 editor
.ApplyEdits(&bookmark_bar_node
);
172 const BookmarkNode
* bb_node
= model_
->bookmark_bar_node();
173 ASSERT_EQ(ASCIIToUTF16("new_a"), bb_node
->GetChild(0)->GetTitle());
174 // The URL shouldn't have changed.
175 ASSERT_TRUE(GURL(base_path() + "a") == bb_node
->GetChild(0)->url());
178 // Changes the url and makes sure parent/visual order doesn't change.
179 TEST_F(BookmarkEditorGtkTest
, EditURLKeepsPosition
) {
180 Time node_time
= GetNode("a")->date_added();
181 BookmarkEditorGtk
editor(NULL
, profile_
.get(), NULL
,
182 BookmarkEditor::EditDetails::EditNode(GetNode("a")),
183 BookmarkEditor::SHOW_TREE
);
184 gtk_entry_set_text(GTK_ENTRY(editor
.url_entry_
),
185 GURL(base_path() + "new_a").spec().c_str());
187 GtkTreeIter bookmark_bar_node
;
188 GtkTreeModel
* store
= GTK_TREE_MODEL(editor
.tree_store_
);
189 ASSERT_TRUE(gtk_tree_model_get_iter_first(store
, &bookmark_bar_node
));
190 editor
.ApplyEdits(&bookmark_bar_node
);
192 const BookmarkNode
* bb_node
= model_
->bookmark_bar_node();
193 ASSERT_EQ(ASCIIToUTF16("a"), bb_node
->GetChild(0)->GetTitle());
194 // The URL should have changed.
195 ASSERT_TRUE(GURL(base_path() + "new_a") == bb_node
->GetChild(0)->url());
196 ASSERT_TRUE(node_time
== bb_node
->GetChild(0)->date_added());
199 // Moves 'a' to be a child of the other node.
200 TEST_F(BookmarkEditorGtkTest
, ChangeParent
) {
201 BookmarkEditorGtk
editor(NULL
, profile_
.get(), NULL
,
202 BookmarkEditor::EditDetails::EditNode(GetNode("a")),
203 BookmarkEditor::SHOW_TREE
);
205 GtkTreeModel
* store
= GTK_TREE_MODEL(editor
.tree_store_
);
206 GtkTreeIter gtk_other_node
;
207 ASSERT_TRUE(gtk_tree_model_get_iter_first(store
, >k_other_node
));
208 ASSERT_TRUE(gtk_tree_model_iter_next(store
, >k_other_node
));
209 editor
.ApplyEdits(>k_other_node
);
211 const BookmarkNode
* other_node
= model_
->other_node();
212 ASSERT_EQ(ASCIIToUTF16("a"), other_node
->GetChild(2)->GetTitle());
213 ASSERT_TRUE(GURL(base_path() + "a") == other_node
->GetChild(2)->url());
216 // Moves 'a' to be a child of the other node.
217 // Moves 'a' to be a child of the other node and changes its url to new_a.
218 TEST_F(BookmarkEditorGtkTest
, ChangeParentAndURL
) {
219 Time node_time
= GetNode("a")->date_added();
220 BookmarkEditorGtk
editor(NULL
, profile_
.get(), NULL
,
221 BookmarkEditor::EditDetails::EditNode(GetNode("a")),
222 BookmarkEditor::SHOW_TREE
);
224 gtk_entry_set_text(GTK_ENTRY(editor
.url_entry_
),
225 GURL(base_path() + "new_a").spec().c_str());
227 GtkTreeModel
* store
= GTK_TREE_MODEL(editor
.tree_store_
);
228 GtkTreeIter gtk_other_node
;
229 ASSERT_TRUE(gtk_tree_model_get_iter_first(store
, >k_other_node
));
230 ASSERT_TRUE(gtk_tree_model_iter_next(store
, >k_other_node
));
231 editor
.ApplyEdits(>k_other_node
);
233 const BookmarkNode
* other_node
= model_
->other_node();
234 ASSERT_EQ(ASCIIToUTF16("a"), other_node
->GetChild(2)->GetTitle());
235 ASSERT_TRUE(GURL(base_path() + "new_a") == other_node
->GetChild(2)->url());
236 ASSERT_TRUE(node_time
== other_node
->GetChild(2)->date_added());
239 // Creates a new folder and moves a node to it.
240 TEST_F(BookmarkEditorGtkTest
, MoveToNewParent
) {
241 BookmarkEditorGtk
editor(NULL
, profile_
.get(), NULL
,
242 BookmarkEditor::EditDetails::EditNode(GetNode("a")),
243 BookmarkEditor::SHOW_TREE
);
245 GtkTreeIter bookmark_bar_node
;
246 GtkTreeModel
* store
= GTK_TREE_MODEL(editor
.tree_store_
);
247 ASSERT_TRUE(gtk_tree_model_get_iter_first(store
, &bookmark_bar_node
));
249 // The bookmark bar should have 2 nodes: folder F1 and F2.
251 ASSERT_EQ(2, gtk_tree_model_iter_n_children(store
, &bookmark_bar_node
));
252 ASSERT_TRUE(gtk_tree_model_iter_children(store
, &f2_iter
,
253 &bookmark_bar_node
));
254 ASSERT_TRUE(gtk_tree_model_iter_next(store
, &f2_iter
));
256 // Create two nodes: "F21" as a child of "F2" and "F211" as a child of "F21".
257 GtkTreeIter f21_iter
;
258 editor
.AddNewFolder(&f2_iter
, &f21_iter
);
259 gtk_tree_store_set(editor
.tree_store_
, &f21_iter
, FOLDER_NAME
, "F21", -1);
260 GtkTreeIter f211_iter
;
261 editor
.AddNewFolder(&f21_iter
, &f211_iter
);
262 gtk_tree_store_set(editor
.tree_store_
, &f211_iter
, FOLDER_NAME
, "F211", -1);
264 ASSERT_EQ(1, gtk_tree_model_iter_n_children(store
, &f2_iter
));
266 editor
.ApplyEdits(&f2_iter
);
268 const BookmarkNode
* bb_node
= model_
->bookmark_bar_node();
269 const BookmarkNode
* mf2
= bb_node
->GetChild(1);
271 // F2 in the model should have two children now: F21 and the node edited.
272 ASSERT_EQ(2, mf2
->child_count());
273 // F21 should be first.
274 ASSERT_EQ(ASCIIToUTF16("F21"), mf2
->GetChild(0)->GetTitle());
276 ASSERT_EQ(ASCIIToUTF16("a"), mf2
->GetChild(1)->GetTitle());
278 // F21 should have one child, F211.
279 const BookmarkNode
* mf21
= mf2
->GetChild(0);
280 ASSERT_EQ(1, mf21
->child_count());
281 ASSERT_EQ(ASCIIToUTF16("F211"), mf21
->GetChild(0)->GetTitle());
284 // Brings up the editor, creating a new URL on the bookmark bar.
285 TEST_F(BookmarkEditorGtkTest
, NewURL
) {
286 BookmarkEditorGtk
editor(
290 BookmarkEditor::EditDetails::AddNodeInFolder(
291 NULL
, -1, GURL(), base::string16()),
292 BookmarkEditor::SHOW_TREE
);
294 gtk_entry_set_text(GTK_ENTRY(editor
.url_entry_
),
295 GURL(base_path() + "a").spec().c_str());
296 gtk_entry_set_text(GTK_ENTRY(editor
.name_entry_
), "new_a");
298 GtkTreeIter bookmark_bar_node
;
299 GtkTreeModel
* store
= GTK_TREE_MODEL(editor
.tree_store_
);
300 ASSERT_TRUE(gtk_tree_model_get_iter_first(store
, &bookmark_bar_node
));
301 editor
.ApplyEdits(&bookmark_bar_node
);
303 const BookmarkNode
* bb_node
= model_
->bookmark_bar_node();
304 ASSERT_EQ(4, bb_node
->child_count());
306 const BookmarkNode
* new_node
= bb_node
->GetChild(3);
307 EXPECT_EQ(ASCIIToUTF16("new_a"), new_node
->GetTitle());
308 EXPECT_TRUE(GURL(base_path() + "a") == new_node
->url());
311 // Brings up the editor with no tree and modifies the url.
312 TEST_F(BookmarkEditorGtkTest
, ChangeURLNoTree
) {
313 BookmarkEditorGtk
editor(NULL
, profile_
.get(), NULL
,
314 BookmarkEditor::EditDetails::EditNode(
315 model_
->other_node()->GetChild(0)),
316 BookmarkEditor::NO_TREE
);
318 gtk_entry_set_text(GTK_ENTRY(editor
.url_entry_
),
319 GURL(base_path() + "a").spec().c_str());
320 gtk_entry_set_text(GTK_ENTRY(editor
.name_entry_
), "new_a");
322 editor
.ApplyEdits(NULL
);
324 const BookmarkNode
* other_node
= model_
->other_node();
325 ASSERT_EQ(2, other_node
->child_count());
327 const BookmarkNode
* new_node
= other_node
->GetChild(0);
329 EXPECT_EQ(ASCIIToUTF16("new_a"), new_node
->GetTitle());
330 EXPECT_TRUE(GURL(base_path() + "a") == new_node
->url());
333 // Brings up the editor with no tree and modifies only the title.
334 TEST_F(BookmarkEditorGtkTest
, ChangeTitleNoTree
) {
335 BookmarkEditorGtk
editor(NULL
, profile_
.get(), NULL
,
336 BookmarkEditor::EditDetails::EditNode(
337 model_
->other_node()->GetChild(0)),
338 BookmarkEditor::NO_TREE
);
339 gtk_entry_set_text(GTK_ENTRY(editor
.name_entry_
), "new_a");
343 const BookmarkNode
* other_node
= model_
->other_node();
344 ASSERT_EQ(2, other_node
->child_count());
346 const BookmarkNode
* new_node
= other_node
->GetChild(0);
347 EXPECT_EQ(ASCIIToUTF16("new_a"), new_node
->GetTitle());