1 // Copyright 2014 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 "components/bookmarks/browser/bookmark_utils.h"
6
7 #include <stddef.h>
8
9 #include <memory>
10 #include <utility>
11 #include <vector>
12
13 #include "base/stl_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/test/task_environment.h"
16 #include "build/build_config.h"
17 #include "components/bookmarks/browser/base_bookmark_model_observer.h"
18 #include "components/bookmarks/browser/bookmark_model.h"
19 #include "components/bookmarks/browser/bookmark_node_data.h"
20 #include "components/bookmarks/test/test_bookmark_client.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 #include "ui/base/clipboard/clipboard.h"
23 #include "ui/base/clipboard/scoped_clipboard_writer.h"
24
25 using base::ASCIIToUTF16;
26 using std::string;
27
28 namespace bookmarks {
29 namespace {
30
31 class BookmarkUtilsTest : public testing::Test,
32 public BaseBookmarkModelObserver {
33 public:
BookmarkUtilsTest()34 BookmarkUtilsTest()
35 : task_environment_(base::test::TaskEnvironment::MainThreadType::UI),
36 grouped_changes_beginning_count_(0),
37 grouped_changes_ended_count_(0) {}
38
~BookmarkUtilsTest()39 ~BookmarkUtilsTest() override {}
40
41 // Copy and paste is not yet supported on iOS. http://crbug.com/228147
42 #if !defined(OS_IOS)
TearDown()43 void TearDown() override {
44 ui::Clipboard::DestroyClipboardForCurrentThread();
45 }
46 #endif // !defined(OS_IOS)
47
48 // Certain user actions require multiple changes to the bookmark model,
49 // however these modifications need to be atomic for the undo framework. The
50 // BaseBookmarkModelObserver is used to inform the boundaries of the user
51 // action. For example, when multiple bookmarks are cut to the clipboard we
52 // expect one call each to GroupedBookmarkChangesBeginning/Ended.
ExpectGroupedChangeCount(int expected_beginning_count,int expected_ended_count)53 void ExpectGroupedChangeCount(int expected_beginning_count,
54 int expected_ended_count) {
55 // The undo framework is not used under Android. Thus the group change
56 // events will not be fired and so should not be tested for Android.
57 #if !defined(OS_ANDROID)
58 EXPECT_EQ(grouped_changes_beginning_count_, expected_beginning_count);
59 EXPECT_EQ(grouped_changes_ended_count_, expected_ended_count);
60 #endif
61 }
62
63 private:
64 // BaseBookmarkModelObserver:
BookmarkModelChanged()65 void BookmarkModelChanged() override {}
66
GroupedBookmarkChangesBeginning(BookmarkModel * model)67 void GroupedBookmarkChangesBeginning(BookmarkModel* model) override {
68 ++grouped_changes_beginning_count_;
69 }
70
GroupedBookmarkChangesEnded(BookmarkModel * model)71 void GroupedBookmarkChangesEnded(BookmarkModel* model) override {
72 ++grouped_changes_ended_count_;
73 }
74
75 // Clipboard requires a full TaskEnvironment.
76 base::test::TaskEnvironment task_environment_;
77
78 int grouped_changes_beginning_count_;
79 int grouped_changes_ended_count_;
80
81 DISALLOW_COPY_AND_ASSIGN(BookmarkUtilsTest);
82 };
83
TEST_F(BookmarkUtilsTest,GetBookmarksMatchingPropertiesWordPhraseQuery)84 TEST_F(BookmarkUtilsTest, GetBookmarksMatchingPropertiesWordPhraseQuery) {
85 std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
86 const BookmarkNode* node1 = model->AddURL(model->other_node(),
87 0,
88 ASCIIToUTF16("foo bar"),
89 GURL("http://www.google.com"));
90 const BookmarkNode* node2 = model->AddURL(model->other_node(),
91 0,
92 ASCIIToUTF16("baz buz"),
93 GURL("http://www.cnn.com"));
94 const BookmarkNode* folder1 =
95 model->AddFolder(model->other_node(), 0, ASCIIToUTF16("foo"));
96 std::vector<const BookmarkNode*> nodes;
97 QueryFields query;
98 query.word_phrase_query.reset(new base::string16);
99 // No nodes are returned for empty string.
100 *query.word_phrase_query = ASCIIToUTF16("");
101 GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
102 EXPECT_TRUE(nodes.empty());
103 nodes.clear();
104
105 // No nodes are returned for space-only string.
106 *query.word_phrase_query = ASCIIToUTF16(" ");
107 GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
108 EXPECT_TRUE(nodes.empty());
109 nodes.clear();
110
111 // Node "foo bar" and folder "foo" are returned in search results.
112 *query.word_phrase_query = ASCIIToUTF16("foo");
113 GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
114 ASSERT_EQ(2U, nodes.size());
115 EXPECT_TRUE(nodes[0] == folder1);
116 EXPECT_TRUE(nodes[1] == node1);
117 nodes.clear();
118
119 // Ensure url matches return in search results.
120 *query.word_phrase_query = ASCIIToUTF16("cnn");
121 GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
122 ASSERT_EQ(1U, nodes.size());
123 EXPECT_TRUE(nodes[0] == node2);
124 nodes.clear();
125
126 // Ensure folder "foo" is not returned in more specific search.
127 *query.word_phrase_query = ASCIIToUTF16("foo bar");
128 GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
129 ASSERT_EQ(1U, nodes.size());
130 EXPECT_TRUE(nodes[0] == node1);
131 nodes.clear();
132
133 // Bookmark Bar and Other Bookmarks are not returned in search results.
134 *query.word_phrase_query = ASCIIToUTF16("Bookmark");
135 GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
136 ASSERT_EQ(0U, nodes.size());
137 nodes.clear();
138 }
139
140 // Check exact matching against a URL query.
TEST_F(BookmarkUtilsTest,GetBookmarksMatchingPropertiesUrl)141 TEST_F(BookmarkUtilsTest, GetBookmarksMatchingPropertiesUrl) {
142 std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
143 const BookmarkNode* node1 = model->AddURL(model->other_node(),
144 0,
145 ASCIIToUTF16("Google"),
146 GURL("https://www.google.com/"));
147 model->AddURL(model->other_node(),
148 0,
149 ASCIIToUTF16("Google Calendar"),
150 GURL("https://www.google.com/calendar"));
151
152 model->AddFolder(model->other_node(), 0, ASCIIToUTF16("Folder"));
153
154 std::vector<const BookmarkNode*> nodes;
155 QueryFields query;
156 query.url.reset(new base::string16);
157 *query.url = ASCIIToUTF16("https://www.google.com/");
158 GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
159 ASSERT_EQ(1U, nodes.size());
160 EXPECT_TRUE(nodes[0] == node1);
161 nodes.clear();
162
163 *query.url = ASCIIToUTF16("calendar");
164 GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
165 ASSERT_EQ(0U, nodes.size());
166 nodes.clear();
167
168 // Empty URL should not match folders.
169 *query.url = ASCIIToUTF16("");
170 GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
171 ASSERT_EQ(0U, nodes.size());
172 nodes.clear();
173 }
174
175 // Check exact matching against a title query.
TEST_F(BookmarkUtilsTest,GetBookmarksMatchingPropertiesTitle)176 TEST_F(BookmarkUtilsTest, GetBookmarksMatchingPropertiesTitle) {
177 std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
178 const BookmarkNode* node1 = model->AddURL(model->other_node(),
179 0,
180 ASCIIToUTF16("Google"),
181 GURL("https://www.google.com/"));
182 model->AddURL(model->other_node(),
183 0,
184 ASCIIToUTF16("Google Calendar"),
185 GURL("https://www.google.com/calendar"));
186
187 const BookmarkNode* folder1 =
188 model->AddFolder(model->other_node(), 0, ASCIIToUTF16("Folder"));
189
190 std::vector<const BookmarkNode*> nodes;
191 QueryFields query;
192 query.title.reset(new base::string16);
193 *query.title = ASCIIToUTF16("Google");
194 GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
195 ASSERT_EQ(1U, nodes.size());
196 EXPECT_TRUE(nodes[0] == node1);
197 nodes.clear();
198
199 *query.title = ASCIIToUTF16("Calendar");
200 GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
201 ASSERT_EQ(0U, nodes.size());
202 nodes.clear();
203
204 // Title should match folders.
205 *query.title = ASCIIToUTF16("Folder");
206 GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
207 ASSERT_EQ(1U, nodes.size());
208 EXPECT_TRUE(nodes[0] == folder1);
209 nodes.clear();
210 }
211
212 // Check matching against a query with multiple predicates.
TEST_F(BookmarkUtilsTest,GetBookmarksMatchingPropertiesConjunction)213 TEST_F(BookmarkUtilsTest, GetBookmarksMatchingPropertiesConjunction) {
214 std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
215 const BookmarkNode* node1 = model->AddURL(model->other_node(),
216 0,
217 ASCIIToUTF16("Google"),
218 GURL("https://www.google.com/"));
219 model->AddURL(model->other_node(),
220 0,
221 ASCIIToUTF16("Google Calendar"),
222 GURL("https://www.google.com/calendar"));
223
224 model->AddFolder(model->other_node(), 0, ASCIIToUTF16("Folder"));
225
226 std::vector<const BookmarkNode*> nodes;
227 QueryFields query;
228
229 // Test all fields matching.
230 query.word_phrase_query.reset(new base::string16(ASCIIToUTF16("www")));
231 query.url.reset(new base::string16(ASCIIToUTF16("https://www.google.com/")));
232 query.title.reset(new base::string16(ASCIIToUTF16("Google")));
233 GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
234 ASSERT_EQ(1U, nodes.size());
235 EXPECT_TRUE(nodes[0] == node1);
236 nodes.clear();
237
238 std::unique_ptr<base::string16>* fields[] = {&query.word_phrase_query,
239 &query.url, &query.title};
240
241 // Test two fields matching.
242 for (size_t i = 0; i < base::size(fields); i++) {
243 std::unique_ptr<base::string16> original_value(fields[i]->release());
244 GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
245 ASSERT_EQ(1U, nodes.size());
246 EXPECT_TRUE(nodes[0] == node1);
247 nodes.clear();
248 *fields[i] = std::move(original_value);
249 }
250
251 // Test two fields matching with one non-matching field.
252 for (size_t i = 0; i < base::size(fields); i++) {
253 std::unique_ptr<base::string16> original_value(fields[i]->release());
254 fields[i]->reset(new base::string16(ASCIIToUTF16("fjdkslafjkldsa")));
255 GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
256 ASSERT_EQ(0U, nodes.size());
257 nodes.clear();
258 *fields[i] = std::move(original_value);
259 }
260 }
261
262 // Copy and paste is not yet supported on iOS. http://crbug.com/228147
263 #if !defined(OS_IOS)
TEST_F(BookmarkUtilsTest,DISABLED_PasteBookmarkFromURL)264 TEST_F(BookmarkUtilsTest, DISABLED_PasteBookmarkFromURL) {
265 std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
266 const base::string16 url_text = ASCIIToUTF16("http://www.google.com/");
267 const BookmarkNode* new_folder = model->AddFolder(
268 model->bookmark_bar_node(), 0, ASCIIToUTF16("New_Folder"));
269
270 // Write blank text to clipboard.
271 {
272 ui::ScopedClipboardWriter clipboard_writer(ui::ClipboardBuffer::kCopyPaste);
273 clipboard_writer.WriteText(base::string16());
274 }
275 // Now we shouldn't be able to paste from the clipboard.
276 EXPECT_FALSE(CanPasteFromClipboard(model.get(), new_folder));
277
278 // Write some valid url to the clipboard.
279 {
280 ui::ScopedClipboardWriter clipboard_writer(ui::ClipboardBuffer::kCopyPaste);
281 clipboard_writer.WriteText(url_text);
282 }
283 // Now we should be able to paste from the clipboard.
284 EXPECT_TRUE(CanPasteFromClipboard(model.get(), new_folder));
285
286 PasteFromClipboard(model.get(), new_folder, 0);
287 ASSERT_EQ(1u, new_folder->children().size());
288
289 // Url for added node should be same as url_text.
290 EXPECT_EQ(url_text,
291 ASCIIToUTF16(new_folder->children().front()->url().spec()));
292 }
293
294 // TODO(https://crbug.com/1010182): Fix flakes and re-enable this test.
295 #if defined(OS_WIN) || defined(OS_MACOSX)
296 #define MAYBE_CopyPaste DISABLED_CopyPaste
297 #else
298 #define MAYBE_CopyPaste CopyPaste
299 #endif
TEST_F(BookmarkUtilsTest,MAYBE_CopyPaste)300 TEST_F(BookmarkUtilsTest, MAYBE_CopyPaste) {
301 std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
302 const BookmarkNode* node = model->AddURL(model->other_node(),
303 0,
304 ASCIIToUTF16("foo bar"),
305 GURL("http://www.google.com"));
306
307 // Copy a node to the clipboard.
308 std::vector<const BookmarkNode*> nodes;
309 nodes.push_back(node);
310 CopyToClipboard(model.get(), nodes, false);
311
312 // And make sure we can paste a bookmark from the clipboard.
313 EXPECT_TRUE(CanPasteFromClipboard(model.get(), model->bookmark_bar_node()));
314
315 // Write some text to the clipboard.
316 {
317 ui::ScopedClipboardWriter clipboard_writer(ui::ClipboardBuffer::kCopyPaste);
318 clipboard_writer.WriteText(ASCIIToUTF16("foo"));
319 }
320
321 // Now we shouldn't be able to paste from the clipboard.
322 EXPECT_FALSE(CanPasteFromClipboard(model.get(), model->bookmark_bar_node()));
323 }
324
325 // Test for updating title such that url and title pair are unique among the
326 // children of parent.
TEST_F(BookmarkUtilsTest,DISABLED_MakeTitleUnique)327 TEST_F(BookmarkUtilsTest, DISABLED_MakeTitleUnique) {
328 std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
329 const base::string16 url_text = ASCIIToUTF16("http://www.google.com/");
330 const base::string16 title_text = ASCIIToUTF16("foobar");
331 const BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
332
333 const BookmarkNode* node =
334 model->AddURL(bookmark_bar_node, 0, title_text, GURL(url_text));
335
336 EXPECT_EQ(url_text,
337 ASCIIToUTF16(bookmark_bar_node->children()[0]->url().spec()));
338 EXPECT_EQ(title_text, bookmark_bar_node->children()[0]->GetTitle());
339
340 // Copy a node to the clipboard.
341 std::vector<const BookmarkNode*> nodes;
342 nodes.push_back(node);
343 CopyToClipboard(model.get(), nodes, false);
344
345 // Now we should be able to paste from the clipboard.
346 EXPECT_TRUE(CanPasteFromClipboard(model.get(), bookmark_bar_node));
347
348 PasteFromClipboard(model.get(), bookmark_bar_node, 1);
349 ASSERT_EQ(2u, bookmark_bar_node->children().size());
350
351 // Url for added node should be same as url_text.
352 EXPECT_EQ(url_text,
353 ASCIIToUTF16(bookmark_bar_node->children()[1]->url().spec()));
354 // Title for added node should be numeric subscript suffix with copied node
355 // title.
356 EXPECT_EQ(ASCIIToUTF16("foobar (1)"),
357 bookmark_bar_node->children()[1]->GetTitle());
358 }
359
TEST_F(BookmarkUtilsTest,DISABLED_CopyPasteMetaInfo)360 TEST_F(BookmarkUtilsTest, DISABLED_CopyPasteMetaInfo) {
361 std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
362 const BookmarkNode* node = model->AddURL(model->other_node(),
363 0,
364 ASCIIToUTF16("foo bar"),
365 GURL("http://www.google.com"));
366 model->SetNodeMetaInfo(node, "somekey", "somevalue");
367 model->SetNodeMetaInfo(node, "someotherkey", "someothervalue");
368
369 // Copy a node to the clipboard.
370 std::vector<const BookmarkNode*> nodes;
371 nodes.push_back(node);
372 CopyToClipboard(model.get(), nodes, false);
373
374 // Paste node to a different folder.
375 const BookmarkNode* folder =
376 model->AddFolder(model->bookmark_bar_node(), 0, ASCIIToUTF16("Folder"));
377 EXPECT_EQ(0u, folder->children().size());
378
379 // And make sure we can paste a bookmark from the clipboard.
380 EXPECT_TRUE(CanPasteFromClipboard(model.get(), folder));
381
382 PasteFromClipboard(model.get(), folder, 0);
383 ASSERT_EQ(1u, folder->children().size());
384
385 // Verify that the pasted node contains the same meta info.
386 const BookmarkNode* pasted = folder->children().front().get();
387 ASSERT_TRUE(pasted->GetMetaInfoMap());
388 EXPECT_EQ(2u, pasted->GetMetaInfoMap()->size());
389 std::string value;
390 EXPECT_TRUE(pasted->GetMetaInfo("somekey", &value));
391 EXPECT_EQ("somevalue", value);
392 EXPECT_TRUE(pasted->GetMetaInfo("someotherkey", &value));
393 EXPECT_EQ("someothervalue", value);
394 }
395
396 #if defined(OS_LINUX) || defined(OS_MACOSX)
397 // http://crbug.com/396472
398 #define MAYBE_CutToClipboard DISABLED_CutToClipboard
399 #else
400 #define MAYBE_CutToClipboard CutToClipboard
401 #endif
TEST_F(BookmarkUtilsTest,MAYBE_CutToClipboard)402 TEST_F(BookmarkUtilsTest, MAYBE_CutToClipboard) {
403 std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
404 model->AddObserver(this);
405
406 base::string16 title(ASCIIToUTF16("foo"));
407 GURL url("http://foo.com");
408 const BookmarkNode* n1 = model->AddURL(model->other_node(), 0, title, url);
409 const BookmarkNode* n2 = model->AddURL(model->other_node(), 1, title, url);
410
411 // Cut the nodes to the clipboard.
412 std::vector<const BookmarkNode*> nodes;
413 nodes.push_back(n1);
414 nodes.push_back(n2);
415 CopyToClipboard(model.get(), nodes, true);
416
417 // Make sure the nodes were removed.
418 EXPECT_EQ(0u, model->other_node()->children().size());
419
420 // Make sure observers were notified the set of changes should be grouped.
421 ExpectGroupedChangeCount(1, 1);
422
423 // And make sure we can paste from the clipboard.
424 EXPECT_TRUE(CanPasteFromClipboard(model.get(), model->other_node()));
425 }
426
TEST_F(BookmarkUtilsTest,PasteNonEditableNodes)427 TEST_F(BookmarkUtilsTest, PasteNonEditableNodes) {
428 // Load a model with an managed node that is not editable.
429 auto client = std::make_unique<TestBookmarkClient>();
430 BookmarkNode* managed_node = client->EnableManagedNode();
431
432 std::unique_ptr<BookmarkModel> model(
433 TestBookmarkClient::CreateModelWithClient(std::move(client)));
434 const BookmarkNode* node = model->AddURL(model->other_node(),
435 0,
436 ASCIIToUTF16("foo bar"),
437 GURL("http://www.google.com"));
438
439 // Copy a node to the clipboard.
440 std::vector<const BookmarkNode*> nodes;
441 nodes.push_back(node);
442 CopyToClipboard(model.get(), nodes, false);
443
444 // And make sure we can paste a bookmark from the clipboard.
445 EXPECT_TRUE(CanPasteFromClipboard(model.get(), model->bookmark_bar_node()));
446
447 // But it can't be pasted into a non-editable folder.
448 BookmarkClient* upcast = model->client();
449 EXPECT_FALSE(upcast->CanBeEditedByUser(managed_node));
450 EXPECT_FALSE(CanPasteFromClipboard(model.get(), managed_node));
451 }
452 #endif // !defined(OS_IOS)
453
TEST_F(BookmarkUtilsTest,GetParentForNewNodes)454 TEST_F(BookmarkUtilsTest, GetParentForNewNodes) {
455 std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
456 // This tests the case where selection contains one item and that item is a
457 // folder.
458 std::vector<const BookmarkNode*> nodes;
459 nodes.push_back(model->bookmark_bar_node());
460 size_t index = size_t{-1};
461 const BookmarkNode* real_parent =
462 GetParentForNewNodes(model->bookmark_bar_node(), nodes, &index);
463 EXPECT_EQ(real_parent, model->bookmark_bar_node());
464 EXPECT_EQ(0u, index);
465
466 nodes.clear();
467
468 // This tests the case where selection contains one item and that item is an
469 // url.
470 const BookmarkNode* page1 = model->AddURL(model->bookmark_bar_node(),
471 0,
472 ASCIIToUTF16("Google"),
473 GURL("http://google.com"));
474 nodes.push_back(page1);
475 real_parent = GetParentForNewNodes(model->bookmark_bar_node(), nodes, &index);
476 EXPECT_EQ(real_parent, model->bookmark_bar_node());
477 EXPECT_EQ(1u, index);
478
479 // This tests the case where selection has more than one item.
480 const BookmarkNode* folder1 =
481 model->AddFolder(model->bookmark_bar_node(), 1, ASCIIToUTF16("Folder 1"));
482 nodes.push_back(folder1);
483 real_parent = GetParentForNewNodes(model->bookmark_bar_node(), nodes, &index);
484 EXPECT_EQ(real_parent, model->bookmark_bar_node());
485 EXPECT_EQ(2u, index);
486
487 // This tests the case where selection doesn't contain any items.
488 nodes.clear();
489 real_parent = GetParentForNewNodes(model->bookmark_bar_node(), nodes, &index);
490 EXPECT_EQ(real_parent, model->bookmark_bar_node());
491 EXPECT_EQ(2u, index);
492 }
493
494 // Verifies that meta info is copied when nodes are cloned.
TEST_F(BookmarkUtilsTest,CloneMetaInfo)495 TEST_F(BookmarkUtilsTest, CloneMetaInfo) {
496 std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
497 // Add a node containing meta info.
498 const BookmarkNode* node = model->AddURL(model->other_node(),
499 0,
500 ASCIIToUTF16("foo bar"),
501 GURL("http://www.google.com"));
502 model->SetNodeMetaInfo(node, "somekey", "somevalue");
503 model->SetNodeMetaInfo(node, "someotherkey", "someothervalue");
504
505 // Clone node to a different folder.
506 const BookmarkNode* folder =
507 model->AddFolder(model->bookmark_bar_node(), 0, ASCIIToUTF16("Folder"));
508 std::vector<BookmarkNodeData::Element> elements;
509 BookmarkNodeData::Element node_data(node);
510 elements.push_back(node_data);
511 EXPECT_EQ(0u, folder->children().size());
512 CloneBookmarkNode(model.get(), elements, folder, 0, false);
513 ASSERT_EQ(1u, folder->children().size());
514
515 // Verify that the cloned node contains the same meta info.
516 const BookmarkNode* clone = folder->children().front().get();
517 ASSERT_TRUE(clone->GetMetaInfoMap());
518 EXPECT_EQ(2u, clone->GetMetaInfoMap()->size());
519 std::string value;
520 EXPECT_TRUE(clone->GetMetaInfo("somekey", &value));
521 EXPECT_EQ("somevalue", value);
522 EXPECT_TRUE(clone->GetMetaInfo("someotherkey", &value));
523 EXPECT_EQ("someothervalue", value);
524 }
525
526 // Verifies that meta info fields in the non cloned set are not copied when
527 // cloning a bookmark.
TEST_F(BookmarkUtilsTest,CloneBookmarkResetsNonClonedKey)528 TEST_F(BookmarkUtilsTest, CloneBookmarkResetsNonClonedKey) {
529 std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
530 model->AddNonClonedKey("foo");
531 const BookmarkNode* parent = model->other_node();
532 const BookmarkNode* node = model->AddURL(
533 parent, 0, ASCIIToUTF16("title"), GURL("http://www.google.com"));
534 model->SetNodeMetaInfo(node, "foo", "ignored value");
535 model->SetNodeMetaInfo(node, "bar", "kept value");
536 std::vector<BookmarkNodeData::Element> elements;
537 BookmarkNodeData::Element node_data(node);
538 elements.push_back(node_data);
539
540 // Cloning a bookmark should clear the non cloned key.
541 CloneBookmarkNode(model.get(), elements, parent, 0, true);
542 ASSERT_EQ(2u, parent->children().size());
543 std::string value;
544 EXPECT_FALSE(parent->children().front()->GetMetaInfo("foo", &value));
545
546 // Other keys should still be cloned.
547 EXPECT_TRUE(parent->children().front()->GetMetaInfo("bar", &value));
548 EXPECT_EQ("kept value", value);
549 }
550
551 // Verifies that meta info fields in the non cloned set are not copied when
552 // cloning a folder.
TEST_F(BookmarkUtilsTest,CloneFolderResetsNonClonedKey)553 TEST_F(BookmarkUtilsTest, CloneFolderResetsNonClonedKey) {
554 std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
555 model->AddNonClonedKey("foo");
556 const BookmarkNode* parent = model->other_node();
557 const BookmarkNode* node = model->AddFolder(parent, 0, ASCIIToUTF16("title"));
558 model->SetNodeMetaInfo(node, "foo", "ignored value");
559 model->SetNodeMetaInfo(node, "bar", "kept value");
560 std::vector<BookmarkNodeData::Element> elements;
561 BookmarkNodeData::Element node_data(node);
562 elements.push_back(node_data);
563
564 // Cloning a folder should clear the non cloned key.
565 CloneBookmarkNode(model.get(), elements, parent, 0, true);
566 ASSERT_EQ(2u, parent->children().size());
567 std::string value;
568 EXPECT_FALSE(parent->children().front()->GetMetaInfo("foo", &value));
569
570 // Other keys should still be cloned.
571 EXPECT_TRUE(parent->children().front()->GetMetaInfo("bar", &value));
572 EXPECT_EQ("kept value", value);
573 }
574
TEST_F(BookmarkUtilsTest,RemoveAllBookmarks)575 TEST_F(BookmarkUtilsTest, RemoveAllBookmarks) {
576 // Load a model with an managed node that is not editable.
577 auto client = std::make_unique<TestBookmarkClient>();
578 BookmarkNode* managed_node = client->EnableManagedNode();
579
580 std::unique_ptr<BookmarkModel> model(
581 TestBookmarkClient::CreateModelWithClient(std::move(client)));
582 EXPECT_TRUE(model->bookmark_bar_node()->children().empty());
583 EXPECT_TRUE(model->other_node()->children().empty());
584 EXPECT_TRUE(model->mobile_node()->children().empty());
585 EXPECT_TRUE(managed_node->children().empty());
586
587 const base::string16 title = base::ASCIIToUTF16("Title");
588 const GURL url("http://google.com");
589 model->AddURL(model->bookmark_bar_node(), 0, title, url);
590 model->AddURL(model->other_node(), 0, title, url);
591 model->AddURL(model->mobile_node(), 0, title, url);
592 model->AddURL(managed_node, 0, title, url);
593
594 std::vector<const BookmarkNode*> nodes;
595 model->GetNodesByURL(url, &nodes);
596 ASSERT_EQ(4u, nodes.size());
597
598 RemoveAllBookmarks(model.get(), url);
599
600 nodes.clear();
601 model->GetNodesByURL(url, &nodes);
602 ASSERT_EQ(1u, nodes.size());
603 EXPECT_TRUE(model->bookmark_bar_node()->children().empty());
604 EXPECT_TRUE(model->other_node()->children().empty());
605 EXPECT_TRUE(model->mobile_node()->children().empty());
606 EXPECT_EQ(1u, managed_node->children().size());
607 }
608
609 } // namespace
610 } // namespace bookmarks
611