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_model.h" 6 7 #include <set> 8 #include <string> 9 10 #include "base/base_paths.h" 11 #include "base/basictypes.h" 12 #include "base/command_line.h" 13 #include "base/compiler_specific.h" 14 #include "base/containers/hash_tables.h" 15 #include "base/path_service.h" 16 #include "base/strings/string16.h" 17 #include "base/strings/string_number_conversions.h" 18 #include "base/strings/string_split.h" 19 #include "base/strings/string_util.h" 20 #include "base/strings/utf_string_conversions.h" 21 #include "base/time/time.h" 22 #include "components/bookmarks/browser/bookmark_model_observer.h" 23 #include "components/bookmarks/browser/bookmark_utils.h" 24 #include "components/bookmarks/test/bookmark_test_helpers.h" 25 #include "components/bookmarks/test/test_bookmark_client.h" 26 #include "testing/gtest/include/gtest/gtest.h" 27 #include "ui/base/models/tree_node_iterator.h" 28 #include "ui/base/models/tree_node_model.h" 29 #include "url/gurl.h" 30 31 using base::ASCIIToUTF16; 32 using base::Time; 33 using base::TimeDelta; 34 35 namespace bookmarks { 36 namespace { 37 38 // Test cases used to test the removal of extra whitespace when adding 39 // a new folder/bookmark or updating a title of a folder/bookmark. 40 static struct { 41 const std::string input_title; 42 const std::string expected_title; 43 } url_whitespace_test_cases[] = { 44 {"foobar", "foobar"}, 45 // Newlines. 46 {"foo\nbar", "foo bar"}, 47 {"foo\n\nbar", "foo bar"}, 48 {"foo\n\n\nbar", "foo bar"}, 49 {"foo\r\nbar", "foo bar"}, 50 {"foo\r\n\r\nbar", "foo bar"}, 51 {"\nfoo\nbar\n", "foo bar"}, 52 // Spaces. 53 {"foo bar", "foo bar"}, 54 {" foo bar ", "foo bar"}, 55 {" foo bar ", "foo bar"}, 56 // Tabs. 57 {"\tfoo\tbar\t", "foo bar"}, 58 {"\tfoo bar\t", "foo bar"}, 59 // Mixed cases. 60 {"\tfoo\nbar\t", "foo bar"}, 61 {"\tfoo\r\nbar\t", "foo bar"}, 62 {" foo\tbar\n", "foo bar"}, 63 {"\t foo \t bar \t", "foo bar"}, 64 {"\n foo\r\n\tbar\n \t", "foo bar"}, 65 }; 66 67 // Test cases used to test the removal of extra whitespace when adding 68 // a new folder/bookmark or updating a title of a folder/bookmark. 69 static struct { 70 const std::string input_title; 71 const std::string expected_title; 72 } title_whitespace_test_cases[] = { 73 {"foobar", "foobar"}, 74 // Newlines. 75 {"foo\nbar", "foo bar"}, 76 {"foo\n\nbar", "foo bar"}, 77 {"foo\n\n\nbar", "foo bar"}, 78 {"foo\r\nbar", "foo bar"}, 79 {"foo\r\n\r\nbar", "foo bar"}, 80 {"\nfoo\nbar\n", " foo bar "}, 81 // Spaces. 82 {"foo bar", "foo bar"}, 83 {" foo bar ", " foo bar "}, 84 {" foo bar ", " foo bar "}, 85 // Tabs. 86 {"\tfoo\tbar\t", " foo bar "}, 87 {"\tfoo bar\t", " foo bar "}, 88 // Mixed cases. 89 {"\tfoo\nbar\t", " foo bar "}, 90 {"\tfoo\r\nbar\t", " foo bar "}, 91 {" foo\tbar\n", " foo bar "}, 92 {"\t foo \t bar \t", " foo bar "}, 93 {"\n foo\r\n\tbar\n \t", " foo bar "}, 94 }; 95 96 // Helper to get a mutable bookmark node. 97 BookmarkNode* AsMutable(const BookmarkNode* node) { 98 return const_cast<BookmarkNode*>(node); 99 } 100 101 void SwapDateAdded(BookmarkNode* n1, BookmarkNode* n2) { 102 Time tmp = n1->date_added(); 103 n1->set_date_added(n2->date_added()); 104 n2->set_date_added(tmp); 105 } 106 107 class BookmarkModelTest : public testing::Test, 108 public BookmarkModelObserver { 109 public: 110 struct ObserverDetails { 111 ObserverDetails() { 112 Set(NULL, NULL, -1, -1); 113 } 114 115 void Set(const BookmarkNode* node1, 116 const BookmarkNode* node2, 117 int index1, 118 int index2) { 119 node1_ = node1; 120 node2_ = node2; 121 index1_ = index1; 122 index2_ = index2; 123 } 124 125 void ExpectEquals(const BookmarkNode* node1, 126 const BookmarkNode* node2, 127 int index1, 128 int index2) { 129 EXPECT_EQ(node1_, node1); 130 EXPECT_EQ(node2_, node2); 131 EXPECT_EQ(index1_, index1); 132 EXPECT_EQ(index2_, index2); 133 } 134 135 private: 136 const BookmarkNode* node1_; 137 const BookmarkNode* node2_; 138 int index1_; 139 int index2_; 140 }; 141 142 BookmarkModelTest() : model_(client_.CreateModel()) { 143 model_->AddObserver(this); 144 ClearCounts(); 145 } 146 147 virtual void BookmarkModelLoaded(BookmarkModel* model, 148 bool ids_reassigned) OVERRIDE { 149 // We never load from the db, so that this should never get invoked. 150 NOTREACHED(); 151 } 152 153 virtual void BookmarkNodeMoved(BookmarkModel* model, 154 const BookmarkNode* old_parent, 155 int old_index, 156 const BookmarkNode* new_parent, 157 int new_index) OVERRIDE { 158 ++moved_count_; 159 observer_details_.Set(old_parent, new_parent, old_index, new_index); 160 } 161 162 virtual void BookmarkNodeAdded(BookmarkModel* model, 163 const BookmarkNode* parent, 164 int index) OVERRIDE { 165 ++added_count_; 166 observer_details_.Set(parent, NULL, index, -1); 167 } 168 169 virtual void OnWillRemoveBookmarks(BookmarkModel* model, 170 const BookmarkNode* parent, 171 int old_index, 172 const BookmarkNode* node) OVERRIDE { 173 ++before_remove_count_; 174 } 175 176 virtual void BookmarkNodeRemoved( 177 BookmarkModel* model, 178 const BookmarkNode* parent, 179 int old_index, 180 const BookmarkNode* node, 181 const std::set<GURL>& removed_urls) OVERRIDE { 182 ++removed_count_; 183 observer_details_.Set(parent, NULL, old_index, -1); 184 } 185 186 virtual void BookmarkNodeChanged(BookmarkModel* model, 187 const BookmarkNode* node) OVERRIDE { 188 ++changed_count_; 189 observer_details_.Set(node, NULL, -1, -1); 190 } 191 192 virtual void OnWillChangeBookmarkNode(BookmarkModel* model, 193 const BookmarkNode* node) OVERRIDE { 194 ++before_change_count_; 195 } 196 197 virtual void BookmarkNodeChildrenReordered( 198 BookmarkModel* model, 199 const BookmarkNode* node) OVERRIDE { 200 ++reordered_count_; 201 } 202 203 virtual void OnWillReorderBookmarkNode(BookmarkModel* model, 204 const BookmarkNode* node) OVERRIDE { 205 ++before_reorder_count_; 206 } 207 208 virtual void BookmarkNodeFaviconChanged(BookmarkModel* model, 209 const BookmarkNode* node) OVERRIDE { 210 // We never attempt to load favicons, so that this method never 211 // gets invoked. 212 } 213 214 virtual void ExtensiveBookmarkChangesBeginning( 215 BookmarkModel* model) OVERRIDE { 216 ++extensive_changes_beginning_count_; 217 } 218 219 virtual void ExtensiveBookmarkChangesEnded(BookmarkModel* model) OVERRIDE { 220 ++extensive_changes_ended_count_; 221 } 222 223 virtual void BookmarkAllUserNodesRemoved( 224 BookmarkModel* model, 225 const std::set<GURL>& removed_urls) OVERRIDE { 226 ++all_bookmarks_removed_; 227 } 228 229 virtual void OnWillRemoveAllUserBookmarks(BookmarkModel* model) OVERRIDE { 230 ++before_remove_all_count_; 231 } 232 233 void ClearCounts() { 234 added_count_ = moved_count_ = removed_count_ = changed_count_ = 235 reordered_count_ = extensive_changes_beginning_count_ = 236 extensive_changes_ended_count_ = all_bookmarks_removed_ = 237 before_remove_count_ = before_change_count_ = before_reorder_count_ = 238 before_remove_all_count_ = 0; 239 } 240 241 void AssertObserverCount(int added_count, 242 int moved_count, 243 int removed_count, 244 int changed_count, 245 int reordered_count, 246 int before_remove_count, 247 int before_change_count, 248 int before_reorder_count, 249 int before_remove_all_count) { 250 EXPECT_EQ(added_count_, added_count); 251 EXPECT_EQ(moved_count_, moved_count); 252 EXPECT_EQ(removed_count_, removed_count); 253 EXPECT_EQ(changed_count_, changed_count); 254 EXPECT_EQ(reordered_count_, reordered_count); 255 EXPECT_EQ(before_remove_count_, before_remove_count); 256 EXPECT_EQ(before_change_count_, before_change_count); 257 EXPECT_EQ(before_reorder_count_, before_reorder_count); 258 EXPECT_EQ(before_remove_all_count_, before_remove_all_count); 259 } 260 261 void AssertExtensiveChangesObserverCount( 262 int extensive_changes_beginning_count, 263 int extensive_changes_ended_count) { 264 EXPECT_EQ(extensive_changes_beginning_count_, 265 extensive_changes_beginning_count); 266 EXPECT_EQ(extensive_changes_ended_count_, extensive_changes_ended_count); 267 } 268 269 int AllNodesRemovedObserverCount() const { return all_bookmarks_removed_; } 270 271 BookmarkPermanentNode* ReloadModelWithExtraNode() { 272 BookmarkPermanentNode* extra_node = new BookmarkPermanentNode(100); 273 bookmarks::BookmarkPermanentNodeList extra_nodes; 274 extra_nodes.push_back(extra_node); 275 client_.SetExtraNodesToLoad(extra_nodes.Pass()); 276 277 model_->RemoveObserver(this); 278 model_ = client_.CreateModel(); 279 model_->AddObserver(this); 280 ClearCounts(); 281 282 if (model_->root_node()->GetIndexOf(extra_node) == -1) 283 ADD_FAILURE(); 284 285 return extra_node; 286 } 287 288 protected: 289 TestBookmarkClient client_; 290 scoped_ptr<BookmarkModel> model_; 291 ObserverDetails observer_details_; 292 293 private: 294 int added_count_; 295 int moved_count_; 296 int removed_count_; 297 int changed_count_; 298 int reordered_count_; 299 int extensive_changes_beginning_count_; 300 int extensive_changes_ended_count_; 301 int all_bookmarks_removed_; 302 int before_remove_count_; 303 int before_change_count_; 304 int before_reorder_count_; 305 int before_remove_all_count_; 306 307 DISALLOW_COPY_AND_ASSIGN(BookmarkModelTest); 308 }; 309 310 TEST_F(BookmarkModelTest, InitialState) { 311 const BookmarkNode* bb_node = model_->bookmark_bar_node(); 312 ASSERT_TRUE(bb_node != NULL); 313 EXPECT_EQ(0, bb_node->child_count()); 314 EXPECT_EQ(BookmarkNode::BOOKMARK_BAR, bb_node->type()); 315 316 const BookmarkNode* other_node = model_->other_node(); 317 ASSERT_TRUE(other_node != NULL); 318 EXPECT_EQ(0, other_node->child_count()); 319 EXPECT_EQ(BookmarkNode::OTHER_NODE, other_node->type()); 320 321 const BookmarkNode* mobile_node = model_->mobile_node(); 322 ASSERT_TRUE(mobile_node != NULL); 323 EXPECT_EQ(0, mobile_node->child_count()); 324 EXPECT_EQ(BookmarkNode::MOBILE, mobile_node->type()); 325 326 EXPECT_TRUE(bb_node->id() != other_node->id()); 327 EXPECT_TRUE(bb_node->id() != mobile_node->id()); 328 EXPECT_TRUE(other_node->id() != mobile_node->id()); 329 } 330 331 TEST_F(BookmarkModelTest, AddURL) { 332 const BookmarkNode* root = model_->bookmark_bar_node(); 333 const base::string16 title(ASCIIToUTF16("foo")); 334 const GURL url("http://foo.com"); 335 336 const BookmarkNode* new_node = model_->AddURL(root, 0, title, url); 337 AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0); 338 observer_details_.ExpectEquals(root, NULL, 0, -1); 339 340 ASSERT_EQ(1, root->child_count()); 341 ASSERT_EQ(title, new_node->GetTitle()); 342 ASSERT_TRUE(url == new_node->url()); 343 ASSERT_EQ(BookmarkNode::URL, new_node->type()); 344 ASSERT_TRUE(new_node == model_->GetMostRecentlyAddedUserNodeForURL(url)); 345 346 EXPECT_TRUE(new_node->id() != root->id() && 347 new_node->id() != model_->other_node()->id() && 348 new_node->id() != model_->mobile_node()->id()); 349 } 350 351 TEST_F(BookmarkModelTest, AddURLWithUnicodeTitle) { 352 const BookmarkNode* root = model_->bookmark_bar_node(); 353 const base::string16 title(base::WideToUTF16( 354 L"\u767e\u5ea6\u4e00\u4e0b\uff0c\u4f60\u5c31\u77e5\u9053")); 355 const GURL url("https://www.baidu.com/"); 356 357 const BookmarkNode* new_node = model_->AddURL(root, 0, title, url); 358 AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0); 359 observer_details_.ExpectEquals(root, NULL, 0, -1); 360 361 ASSERT_EQ(1, root->child_count()); 362 ASSERT_EQ(title, new_node->GetTitle()); 363 ASSERT_TRUE(url == new_node->url()); 364 ASSERT_EQ(BookmarkNode::URL, new_node->type()); 365 ASSERT_TRUE(new_node == model_->GetMostRecentlyAddedUserNodeForURL(url)); 366 367 EXPECT_TRUE(new_node->id() != root->id() && 368 new_node->id() != model_->other_node()->id() && 369 new_node->id() != model_->mobile_node()->id()); 370 } 371 372 TEST_F(BookmarkModelTest, AddURLWithWhitespaceTitle) { 373 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(url_whitespace_test_cases); ++i) { 374 const BookmarkNode* root = model_->bookmark_bar_node(); 375 const base::string16 title( 376 ASCIIToUTF16(url_whitespace_test_cases[i].input_title)); 377 const GURL url("http://foo.com"); 378 379 const BookmarkNode* new_node = model_->AddURL(root, i, title, url); 380 381 int size = i + 1; 382 EXPECT_EQ(size, root->child_count()); 383 EXPECT_EQ(ASCIIToUTF16(url_whitespace_test_cases[i].expected_title), 384 new_node->GetTitle()); 385 EXPECT_EQ(BookmarkNode::URL, new_node->type()); 386 } 387 } 388 389 TEST_F(BookmarkModelTest, AddURLWithCreationTimeAndMetaInfo) { 390 const BookmarkNode* root = model_->bookmark_bar_node(); 391 const base::string16 title(ASCIIToUTF16("foo")); 392 const GURL url("http://foo.com"); 393 const Time time = Time::Now() - TimeDelta::FromDays(1); 394 BookmarkNode::MetaInfoMap meta_info; 395 meta_info["foo"] = "bar"; 396 397 const BookmarkNode* new_node = model_->AddURLWithCreationTimeAndMetaInfo( 398 root, 0, title, url, time, &meta_info); 399 AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0); 400 observer_details_.ExpectEquals(root, NULL, 0, -1); 401 402 ASSERT_EQ(1, root->child_count()); 403 ASSERT_EQ(title, new_node->GetTitle()); 404 ASSERT_TRUE(url == new_node->url()); 405 ASSERT_EQ(BookmarkNode::URL, new_node->type()); 406 ASSERT_EQ(time, new_node->date_added()); 407 ASSERT_TRUE(new_node->GetMetaInfoMap()); 408 ASSERT_EQ(meta_info, *new_node->GetMetaInfoMap()); 409 ASSERT_TRUE(new_node == model_->GetMostRecentlyAddedUserNodeForURL(url)); 410 411 EXPECT_TRUE(new_node->id() != root->id() && 412 new_node->id() != model_->other_node()->id() && 413 new_node->id() != model_->mobile_node()->id()); 414 } 415 416 TEST_F(BookmarkModelTest, AddURLToMobileBookmarks) { 417 const BookmarkNode* root = model_->mobile_node(); 418 const base::string16 title(ASCIIToUTF16("foo")); 419 const GURL url("http://foo.com"); 420 421 const BookmarkNode* new_node = model_->AddURL(root, 0, title, url); 422 AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0); 423 observer_details_.ExpectEquals(root, NULL, 0, -1); 424 425 ASSERT_EQ(1, root->child_count()); 426 ASSERT_EQ(title, new_node->GetTitle()); 427 ASSERT_TRUE(url == new_node->url()); 428 ASSERT_EQ(BookmarkNode::URL, new_node->type()); 429 ASSERT_TRUE(new_node == model_->GetMostRecentlyAddedUserNodeForURL(url)); 430 431 EXPECT_TRUE(new_node->id() != root->id() && 432 new_node->id() != model_->other_node()->id() && 433 new_node->id() != model_->mobile_node()->id()); 434 } 435 436 TEST_F(BookmarkModelTest, AddFolder) { 437 const BookmarkNode* root = model_->bookmark_bar_node(); 438 const base::string16 title(ASCIIToUTF16("foo")); 439 440 const BookmarkNode* new_node = model_->AddFolder(root, 0, title); 441 AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0); 442 observer_details_.ExpectEquals(root, NULL, 0, -1); 443 444 ASSERT_EQ(1, root->child_count()); 445 ASSERT_EQ(title, new_node->GetTitle()); 446 ASSERT_EQ(BookmarkNode::FOLDER, new_node->type()); 447 448 EXPECT_TRUE(new_node->id() != root->id() && 449 new_node->id() != model_->other_node()->id() && 450 new_node->id() != model_->mobile_node()->id()); 451 452 // Add another folder, just to make sure folder_ids are incremented correctly. 453 ClearCounts(); 454 model_->AddFolder(root, 0, title); 455 AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0); 456 observer_details_.ExpectEquals(root, NULL, 0, -1); 457 } 458 459 TEST_F(BookmarkModelTest, AddFolderWithWhitespaceTitle) { 460 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(title_whitespace_test_cases); ++i) { 461 const BookmarkNode* root = model_->bookmark_bar_node(); 462 const base::string16 title( 463 ASCIIToUTF16(title_whitespace_test_cases[i].input_title)); 464 465 const BookmarkNode* new_node = model_->AddFolder(root, i, title); 466 467 int size = i + 1; 468 EXPECT_EQ(size, root->child_count()); 469 EXPECT_EQ(ASCIIToUTF16(title_whitespace_test_cases[i].expected_title), 470 new_node->GetTitle()); 471 EXPECT_EQ(BookmarkNode::FOLDER, new_node->type()); 472 } 473 } 474 475 TEST_F(BookmarkModelTest, RemoveURL) { 476 const BookmarkNode* root = model_->bookmark_bar_node(); 477 const base::string16 title(ASCIIToUTF16("foo")); 478 const GURL url("http://foo.com"); 479 model_->AddURL(root, 0, title, url); 480 ClearCounts(); 481 482 model_->Remove(root, 0); 483 ASSERT_EQ(0, root->child_count()); 484 AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0); 485 observer_details_.ExpectEquals(root, NULL, 0, -1); 486 487 // Make sure there is no mapping for the URL. 488 ASSERT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == NULL); 489 } 490 491 TEST_F(BookmarkModelTest, RemoveFolder) { 492 const BookmarkNode* root = model_->bookmark_bar_node(); 493 const BookmarkNode* folder = model_->AddFolder(root, 0, ASCIIToUTF16("foo")); 494 495 ClearCounts(); 496 497 // Add a URL as a child. 498 const base::string16 title(ASCIIToUTF16("foo")); 499 const GURL url("http://foo.com"); 500 model_->AddURL(folder, 0, title, url); 501 502 ClearCounts(); 503 504 // Now remove the folder. 505 model_->Remove(root, 0); 506 ASSERT_EQ(0, root->child_count()); 507 AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0); 508 observer_details_.ExpectEquals(root, NULL, 0, -1); 509 510 // Make sure there is no mapping for the URL. 511 ASSERT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == NULL); 512 } 513 514 TEST_F(BookmarkModelTest, RemoveAllUserBookmarks) { 515 const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node(); 516 517 ClearCounts(); 518 519 // Add a url to bookmark bar. 520 base::string16 title(ASCIIToUTF16("foo")); 521 GURL url("http://foo.com"); 522 model_->AddURL(bookmark_bar_node, 0, title, url); 523 524 // Add a folder with child URL. 525 const BookmarkNode* folder = model_->AddFolder(bookmark_bar_node, 0, title); 526 model_->AddURL(folder, 0, title, url); 527 528 AssertObserverCount(3, 0, 0, 0, 0, 0, 0, 0, 0); 529 ClearCounts(); 530 531 model_->RemoveAllUserBookmarks(); 532 533 EXPECT_EQ(0, bookmark_bar_node->child_count()); 534 // No individual BookmarkNodeRemoved events are fired, so removed count 535 // should be 0. 536 AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 0, 1); 537 AssertExtensiveChangesObserverCount(1, 1); 538 EXPECT_EQ(1, AllNodesRemovedObserverCount()); 539 } 540 541 TEST_F(BookmarkModelTest, SetTitle) { 542 const BookmarkNode* root = model_->bookmark_bar_node(); 543 base::string16 title(ASCIIToUTF16("foo")); 544 const GURL url("http://foo.com"); 545 const BookmarkNode* node = model_->AddURL(root, 0, title, url); 546 547 ClearCounts(); 548 549 title = ASCIIToUTF16("foo2"); 550 model_->SetTitle(node, title); 551 AssertObserverCount(0, 0, 0, 1, 0, 0, 1, 0, 0); 552 observer_details_.ExpectEquals(node, NULL, -1, -1); 553 EXPECT_EQ(title, node->GetTitle()); 554 } 555 556 TEST_F(BookmarkModelTest, SetTitleWithWhitespace) { 557 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(title_whitespace_test_cases); ++i) { 558 const BookmarkNode* root = model_->bookmark_bar_node(); 559 base::string16 title(ASCIIToUTF16("dummy")); 560 const GURL url("http://foo.com"); 561 const BookmarkNode* node = model_->AddURL(root, 0, title, url); 562 563 title = ASCIIToUTF16(title_whitespace_test_cases[i].input_title); 564 model_->SetTitle(node, title); 565 EXPECT_EQ(ASCIIToUTF16(title_whitespace_test_cases[i].expected_title), 566 node->GetTitle()); 567 } 568 } 569 570 TEST_F(BookmarkModelTest, SetURL) { 571 const BookmarkNode* root = model_->bookmark_bar_node(); 572 const base::string16 title(ASCIIToUTF16("foo")); 573 GURL url("http://foo.com"); 574 const BookmarkNode* node = model_->AddURL(root, 0, title, url); 575 576 ClearCounts(); 577 578 url = GURL("http://foo2.com"); 579 model_->SetURL(node, url); 580 AssertObserverCount(0, 0, 0, 1, 0, 0, 1, 0, 0); 581 observer_details_.ExpectEquals(node, NULL, -1, -1); 582 EXPECT_EQ(url, node->url()); 583 } 584 585 TEST_F(BookmarkModelTest, SetDateAdded) { 586 const BookmarkNode* root = model_->bookmark_bar_node(); 587 const base::string16 title(ASCIIToUTF16("foo")); 588 GURL url("http://foo.com"); 589 const BookmarkNode* node = model_->AddURL(root, 0, title, url); 590 591 ClearCounts(); 592 593 base::Time new_time = base::Time::Now() + base::TimeDelta::FromMinutes(20); 594 model_->SetDateAdded(node, new_time); 595 AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 0, 0); 596 EXPECT_EQ(new_time, node->date_added()); 597 EXPECT_EQ(new_time, model_->bookmark_bar_node()->date_folder_modified()); 598 } 599 600 TEST_F(BookmarkModelTest, Move) { 601 const BookmarkNode* root = model_->bookmark_bar_node(); 602 const base::string16 title(ASCIIToUTF16("foo")); 603 const GURL url("http://foo.com"); 604 const BookmarkNode* node = model_->AddURL(root, 0, title, url); 605 const BookmarkNode* folder1 = model_->AddFolder(root, 0, ASCIIToUTF16("foo")); 606 ClearCounts(); 607 608 model_->Move(node, folder1, 0); 609 610 AssertObserverCount(0, 1, 0, 0, 0, 0, 0, 0, 0); 611 observer_details_.ExpectEquals(root, folder1, 1, 0); 612 EXPECT_TRUE(folder1 == node->parent()); 613 EXPECT_EQ(1, root->child_count()); 614 EXPECT_EQ(folder1, root->GetChild(0)); 615 EXPECT_EQ(1, folder1->child_count()); 616 EXPECT_EQ(node, folder1->GetChild(0)); 617 618 // And remove the folder. 619 ClearCounts(); 620 model_->Remove(root, 0); 621 AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0); 622 observer_details_.ExpectEquals(root, NULL, 0, -1); 623 EXPECT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == NULL); 624 EXPECT_EQ(0, root->child_count()); 625 } 626 627 TEST_F(BookmarkModelTest, NonMovingMoveCall) { 628 const BookmarkNode* root = model_->bookmark_bar_node(); 629 const base::string16 title(ASCIIToUTF16("foo")); 630 const GURL url("http://foo.com"); 631 const base::Time old_date(base::Time::Now() - base::TimeDelta::FromDays(1)); 632 633 const BookmarkNode* node = model_->AddURL(root, 0, title, url); 634 model_->SetDateFolderModified(root, old_date); 635 636 // Since |node| is already at the index 0 of |root|, this is no-op. 637 model_->Move(node, root, 0); 638 639 // Check that the modification date is kept untouched. 640 EXPECT_EQ(old_date, root->date_folder_modified()); 641 } 642 643 TEST_F(BookmarkModelTest, Copy) { 644 const BookmarkNode* root = model_->bookmark_bar_node(); 645 static const std::string model_string("a 1:[ b c ] d 2:[ e f g ] h "); 646 test::AddNodesFromModelString(model_.get(), root, model_string); 647 648 // Validate initial model. 649 std::string actual_model_string = test::ModelStringFromNode(root); 650 EXPECT_EQ(model_string, actual_model_string); 651 652 // Copy 'd' to be after '1:b': URL item from bar to folder. 653 const BookmarkNode* node_to_copy = root->GetChild(2); 654 const BookmarkNode* destination = root->GetChild(1); 655 model_->Copy(node_to_copy, destination, 1); 656 actual_model_string = test::ModelStringFromNode(root); 657 EXPECT_EQ("a 1:[ b d c ] d 2:[ e f g ] h ", actual_model_string); 658 659 // Copy '1:d' to be after 'a': URL item from folder to bar. 660 const BookmarkNode* folder = root->GetChild(1); 661 node_to_copy = folder->GetChild(1); 662 model_->Copy(node_to_copy, root, 1); 663 actual_model_string = test::ModelStringFromNode(root); 664 EXPECT_EQ("a d 1:[ b d c ] d 2:[ e f g ] h ", actual_model_string); 665 666 // Copy '1' to be after '2:e': Folder from bar to folder. 667 node_to_copy = root->GetChild(2); 668 destination = root->GetChild(4); 669 model_->Copy(node_to_copy, destination, 1); 670 actual_model_string = test::ModelStringFromNode(root); 671 EXPECT_EQ("a d 1:[ b d c ] d 2:[ e 1:[ b d c ] f g ] h ", 672 actual_model_string); 673 674 // Copy '2:1' to be after '2:f': Folder within same folder. 675 folder = root->GetChild(4); 676 node_to_copy = folder->GetChild(1); 677 model_->Copy(node_to_copy, folder, 3); 678 actual_model_string = test::ModelStringFromNode(root); 679 EXPECT_EQ("a d 1:[ b d c ] d 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] h ", 680 actual_model_string); 681 682 // Copy first 'd' to be after 'h': URL item within the bar. 683 node_to_copy = root->GetChild(1); 684 model_->Copy(node_to_copy, root, 6); 685 actual_model_string = test::ModelStringFromNode(root); 686 EXPECT_EQ("a d 1:[ b d c ] d 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] h d ", 687 actual_model_string); 688 689 // Copy '2' to be after 'a': Folder within the bar. 690 node_to_copy = root->GetChild(4); 691 model_->Copy(node_to_copy, root, 1); 692 actual_model_string = test::ModelStringFromNode(root); 693 EXPECT_EQ("a 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] d 1:[ b d c ] " 694 "d 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] h d ", 695 actual_model_string); 696 } 697 698 // Tests that adding a URL to a folder updates the last modified time. 699 TEST_F(BookmarkModelTest, ParentForNewNodes) { 700 ASSERT_EQ(model_->bookmark_bar_node(), model_->GetParentForNewNodes()); 701 702 const base::string16 title(ASCIIToUTF16("foo")); 703 const GURL url("http://foo.com"); 704 705 model_->AddURL(model_->other_node(), 0, title, url); 706 ASSERT_EQ(model_->other_node(), model_->GetParentForNewNodes()); 707 } 708 709 // Tests that adding a URL to a folder updates the last modified time. 710 TEST_F(BookmarkModelTest, ParentForNewMobileNodes) { 711 ASSERT_EQ(model_->bookmark_bar_node(), model_->GetParentForNewNodes()); 712 713 const base::string16 title(ASCIIToUTF16("foo")); 714 const GURL url("http://foo.com"); 715 716 model_->AddURL(model_->mobile_node(), 0, title, url); 717 ASSERT_EQ(model_->mobile_node(), model_->GetParentForNewNodes()); 718 } 719 720 // Make sure recently modified stays in sync when adding a URL. 721 TEST_F(BookmarkModelTest, MostRecentlyModifiedFolders) { 722 // Add a folder. 723 const BookmarkNode* folder = 724 model_->AddFolder(model_->other_node(), 0, ASCIIToUTF16("foo")); 725 // Add a URL to it. 726 model_->AddURL(folder, 0, ASCIIToUTF16("blah"), GURL("http://foo.com")); 727 728 // Make sure folder is in the most recently modified. 729 std::vector<const BookmarkNode*> most_recent_folders = 730 bookmarks::GetMostRecentlyModifiedUserFolders(model_.get(), 1); 731 ASSERT_EQ(1U, most_recent_folders.size()); 732 ASSERT_EQ(folder, most_recent_folders[0]); 733 734 // Nuke the folder and do another fetch, making sure folder isn't in the 735 // returned list. 736 model_->Remove(folder->parent(), 0); 737 most_recent_folders = 738 bookmarks::GetMostRecentlyModifiedUserFolders(model_.get(), 1); 739 ASSERT_EQ(1U, most_recent_folders.size()); 740 ASSERT_TRUE(most_recent_folders[0] != folder); 741 } 742 743 // Make sure MostRecentlyAddedEntries stays in sync. 744 TEST_F(BookmarkModelTest, MostRecentlyAddedEntries) { 745 // Add a couple of nodes such that the following holds for the time of the 746 // nodes: n1 > n2 > n3 > n4. 747 Time base_time = Time::Now(); 748 BookmarkNode* n1 = AsMutable(model_->AddURL(model_->bookmark_bar_node(), 749 0, 750 ASCIIToUTF16("blah"), 751 GURL("http://foo.com/0"))); 752 BookmarkNode* n2 = AsMutable(model_->AddURL(model_->bookmark_bar_node(), 753 1, 754 ASCIIToUTF16("blah"), 755 GURL("http://foo.com/1"))); 756 BookmarkNode* n3 = AsMutable(model_->AddURL(model_->bookmark_bar_node(), 757 2, 758 ASCIIToUTF16("blah"), 759 GURL("http://foo.com/2"))); 760 BookmarkNode* n4 = AsMutable(model_->AddURL(model_->bookmark_bar_node(), 761 3, 762 ASCIIToUTF16("blah"), 763 GURL("http://foo.com/3"))); 764 n1->set_date_added(base_time + TimeDelta::FromDays(4)); 765 n2->set_date_added(base_time + TimeDelta::FromDays(3)); 766 n3->set_date_added(base_time + TimeDelta::FromDays(2)); 767 n4->set_date_added(base_time + TimeDelta::FromDays(1)); 768 769 // Make sure order is honored. 770 std::vector<const BookmarkNode*> recently_added; 771 bookmarks::GetMostRecentlyAddedEntries(model_.get(), 2, &recently_added); 772 ASSERT_EQ(2U, recently_added.size()); 773 ASSERT_TRUE(n1 == recently_added[0]); 774 ASSERT_TRUE(n2 == recently_added[1]); 775 776 // swap 1 and 2, then check again. 777 recently_added.clear(); 778 SwapDateAdded(n1, n2); 779 bookmarks::GetMostRecentlyAddedEntries(model_.get(), 4, &recently_added); 780 ASSERT_EQ(4U, recently_added.size()); 781 ASSERT_TRUE(n2 == recently_added[0]); 782 ASSERT_TRUE(n1 == recently_added[1]); 783 ASSERT_TRUE(n3 == recently_added[2]); 784 ASSERT_TRUE(n4 == recently_added[3]); 785 } 786 787 // Makes sure GetMostRecentlyAddedUserNodeForURL stays in sync. 788 TEST_F(BookmarkModelTest, GetMostRecentlyAddedUserNodeForURL) { 789 // Add a couple of nodes such that the following holds for the time of the 790 // nodes: n1 > n2 791 Time base_time = Time::Now(); 792 const GURL url("http://foo.com/0"); 793 BookmarkNode* n1 = AsMutable(model_->AddURL( 794 model_->bookmark_bar_node(), 0, ASCIIToUTF16("blah"), url)); 795 BookmarkNode* n2 = AsMutable(model_->AddURL( 796 model_->bookmark_bar_node(), 1, ASCIIToUTF16("blah"), url)); 797 n1->set_date_added(base_time + TimeDelta::FromDays(4)); 798 n2->set_date_added(base_time + TimeDelta::FromDays(3)); 799 800 // Make sure order is honored. 801 ASSERT_EQ(n1, model_->GetMostRecentlyAddedUserNodeForURL(url)); 802 803 // swap 1 and 2, then check again. 804 SwapDateAdded(n1, n2); 805 ASSERT_EQ(n2, model_->GetMostRecentlyAddedUserNodeForURL(url)); 806 } 807 808 // Makes sure GetBookmarks removes duplicates. 809 TEST_F(BookmarkModelTest, GetBookmarksWithDups) { 810 const GURL url("http://foo.com/0"); 811 const base::string16 title(ASCIIToUTF16("blah")); 812 model_->AddURL(model_->bookmark_bar_node(), 0, title, url); 813 model_->AddURL(model_->bookmark_bar_node(), 1, title, url); 814 815 std::vector<BookmarkModel::URLAndTitle> bookmarks; 816 model_->GetBookmarks(&bookmarks); 817 ASSERT_EQ(1U, bookmarks.size()); 818 EXPECT_EQ(url, bookmarks[0].url); 819 EXPECT_EQ(title, bookmarks[0].title); 820 821 model_->AddURL(model_->bookmark_bar_node(), 2, ASCIIToUTF16("Title2"), url); 822 // Only one returned, even titles are different. 823 bookmarks.clear(); 824 model_->GetBookmarks(&bookmarks); 825 EXPECT_EQ(1U, bookmarks.size()); 826 } 827 828 TEST_F(BookmarkModelTest, HasBookmarks) { 829 const GURL url("http://foo.com/"); 830 model_->AddURL(model_->bookmark_bar_node(), 0, ASCIIToUTF16("bar"), url); 831 832 EXPECT_TRUE(model_->HasBookmarks()); 833 } 834 835 // See comment in PopulateNodeFromString. 836 typedef ui::TreeNodeWithValue<BookmarkNode::Type> TestNode; 837 838 // Does the work of PopulateNodeFromString. index gives the index of the current 839 // element in description to process. 840 void PopulateNodeImpl(const std::vector<std::string>& description, 841 size_t* index, 842 TestNode* parent) { 843 while (*index < description.size()) { 844 const std::string& element = description[*index]; 845 (*index)++; 846 if (element == "[") { 847 // Create a new folder and recurse to add all the children. 848 // Folders are given a unique named by way of an ever increasing integer 849 // value. The folders need not have a name, but one is assigned to help 850 // in debugging. 851 static int next_folder_id = 1; 852 TestNode* new_node = 853 new TestNode(base::IntToString16(next_folder_id++), 854 BookmarkNode::FOLDER); 855 parent->Add(new_node, parent->child_count()); 856 PopulateNodeImpl(description, index, new_node); 857 } else if (element == "]") { 858 // End the current folder. 859 return; 860 } else { 861 // Add a new URL. 862 863 // All tokens must be space separated. If there is a [ or ] in the name it 864 // likely means a space was forgotten. 865 DCHECK(element.find('[') == std::string::npos); 866 DCHECK(element.find(']') == std::string::npos); 867 parent->Add(new TestNode(base::UTF8ToUTF16(element), BookmarkNode::URL), 868 parent->child_count()); 869 } 870 } 871 } 872 873 // Creates and adds nodes to parent based on description. description consists 874 // of the following tokens (all space separated): 875 // [ : creates a new USER_FOLDER node. All elements following the [ until the 876 // next balanced ] is encountered are added as children to the node. 877 // ] : closes the last folder created by [ so that any further nodes are added 878 // to the current folders parent. 879 // text: creates a new URL node. 880 // For example, "a [b] c" creates the following nodes: 881 // a 1 c 882 // | 883 // b 884 // In words: a node of type URL with the title a, followed by a folder node with 885 // the title 1 having the single child of type url with name b, followed by 886 // the url node with the title c. 887 // 888 // NOTE: each name must be unique, and folders are assigned a unique title by 889 // way of an increasing integer. 890 void PopulateNodeFromString(const std::string& description, TestNode* parent) { 891 std::vector<std::string> elements; 892 base::SplitStringAlongWhitespace(description, &elements); 893 size_t index = 0; 894 PopulateNodeImpl(elements, &index, parent); 895 } 896 897 // Populates the BookmarkNode with the children of parent. 898 void PopulateBookmarkNode(TestNode* parent, 899 BookmarkModel* model, 900 const BookmarkNode* bb_node) { 901 for (int i = 0; i < parent->child_count(); ++i) { 902 TestNode* child = parent->GetChild(i); 903 if (child->value == BookmarkNode::FOLDER) { 904 const BookmarkNode* new_bb_node = 905 model->AddFolder(bb_node, i, child->GetTitle()); 906 PopulateBookmarkNode(child, model, new_bb_node); 907 } else { 908 model->AddURL(bb_node, i, child->GetTitle(), 909 GURL("http://" + base::UTF16ToASCII(child->GetTitle()))); 910 } 911 } 912 } 913 914 // Test class that creates a BookmarkModel with a real history backend. 915 class BookmarkModelTestWithProfile : public testing::Test { 916 public: 917 BookmarkModelTestWithProfile() {} 918 919 protected: 920 // Verifies the contents of the bookmark bar node match the contents of the 921 // TestNode. 922 void VerifyModelMatchesNode(TestNode* expected, const BookmarkNode* actual) { 923 ASSERT_EQ(expected->child_count(), actual->child_count()); 924 for (int i = 0; i < expected->child_count(); ++i) { 925 TestNode* expected_child = expected->GetChild(i); 926 const BookmarkNode* actual_child = actual->GetChild(i); 927 ASSERT_EQ(expected_child->GetTitle(), actual_child->GetTitle()); 928 if (expected_child->value == BookmarkNode::FOLDER) { 929 ASSERT_TRUE(actual_child->type() == BookmarkNode::FOLDER); 930 // Recurse throught children. 931 VerifyModelMatchesNode(expected_child, actual_child); 932 if (HasFatalFailure()) 933 return; 934 } else { 935 // No need to check the URL, just the title is enough. 936 ASSERT_TRUE(actual_child->is_url()); 937 } 938 } 939 } 940 941 void VerifyNoDuplicateIDs(BookmarkModel* model) { 942 ui::TreeNodeIterator<const BookmarkNode> it(model->root_node()); 943 base::hash_set<int64> ids; 944 while (it.has_next()) 945 ASSERT_TRUE(ids.insert(it.Next()->id()).second); 946 } 947 948 TestBookmarkClient client_; 949 scoped_ptr<BookmarkModel> model_; 950 }; 951 952 // Creates a set of nodes in the bookmark bar model, then recreates the 953 // bookmark bar model which triggers loading from the db and checks the loaded 954 // structure to make sure it is what we first created. 955 TEST_F(BookmarkModelTestWithProfile, CreateAndRestore) { 956 struct TestData { 957 // Structure of the children of the bookmark bar model node. 958 const std::string bbn_contents; 959 // Structure of the children of the other node. 960 const std::string other_contents; 961 // Structure of the children of the synced node. 962 const std::string mobile_contents; 963 } data[] = { 964 // See PopulateNodeFromString for a description of these strings. 965 { "", "" }, 966 { "a", "b" }, 967 { "a [ b ]", "" }, 968 { "", "[ b ] a [ c [ d e [ f ] ] ]" }, 969 { "a [ b ]", "" }, 970 { "a b c [ d e [ f ] ]", "g h i [ j k [ l ] ]"}, 971 }; 972 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) { 973 model_ = client_.CreateModel(); 974 975 TestNode bbn; 976 PopulateNodeFromString(data[i].bbn_contents, &bbn); 977 PopulateBookmarkNode(&bbn, model_.get(), model_->bookmark_bar_node()); 978 979 TestNode other; 980 PopulateNodeFromString(data[i].other_contents, &other); 981 PopulateBookmarkNode(&other, model_.get(), model_->other_node()); 982 983 TestNode mobile; 984 PopulateNodeFromString(data[i].mobile_contents, &mobile); 985 PopulateBookmarkNode(&mobile, model_.get(), model_->mobile_node()); 986 987 VerifyModelMatchesNode(&bbn, model_->bookmark_bar_node()); 988 VerifyModelMatchesNode(&other, model_->other_node()); 989 VerifyModelMatchesNode(&mobile, model_->mobile_node()); 990 VerifyNoDuplicateIDs(model_.get()); 991 } 992 } 993 994 TEST_F(BookmarkModelTest, Sort) { 995 // Populate the bookmark bar node with nodes for 'B', 'a', 'd' and 'C'. 996 // 'C' and 'a' are folders. 997 TestNode bbn; 998 PopulateNodeFromString("B [ a ] d [ a ]", &bbn); 999 const BookmarkNode* parent = model_->bookmark_bar_node(); 1000 PopulateBookmarkNode(&bbn, model_.get(), parent); 1001 1002 BookmarkNode* child1 = AsMutable(parent->GetChild(1)); 1003 child1->SetTitle(ASCIIToUTF16("a")); 1004 delete child1->Remove(child1->GetChild(0)); 1005 BookmarkNode* child3 = AsMutable(parent->GetChild(3)); 1006 child3->SetTitle(ASCIIToUTF16("C")); 1007 delete child3->Remove(child3->GetChild(0)); 1008 1009 ClearCounts(); 1010 1011 // Sort the children of the bookmark bar node. 1012 model_->SortChildren(parent); 1013 1014 // Make sure we were notified. 1015 AssertObserverCount(0, 0, 0, 0, 1, 0, 0, 1, 0); 1016 1017 // Make sure the order matches (remember, 'a' and 'C' are folders and 1018 // come first). 1019 EXPECT_EQ(parent->GetChild(0)->GetTitle(), ASCIIToUTF16("a")); 1020 EXPECT_EQ(parent->GetChild(1)->GetTitle(), ASCIIToUTF16("C")); 1021 EXPECT_EQ(parent->GetChild(2)->GetTitle(), ASCIIToUTF16("B")); 1022 EXPECT_EQ(parent->GetChild(3)->GetTitle(), ASCIIToUTF16("d")); 1023 } 1024 1025 TEST_F(BookmarkModelTest, Reorder) { 1026 // Populate the bookmark bar node with nodes 'A', 'B', 'C' and 'D'. 1027 TestNode bbn; 1028 PopulateNodeFromString("A B C D", &bbn); 1029 BookmarkNode* parent = AsMutable(model_->bookmark_bar_node()); 1030 PopulateBookmarkNode(&bbn, model_.get(), parent); 1031 1032 ClearCounts(); 1033 1034 // Reorder bar node's bookmarks in reverse order. 1035 std::vector<const BookmarkNode*> new_order; 1036 new_order.push_back(parent->GetChild(3)); 1037 new_order.push_back(parent->GetChild(2)); 1038 new_order.push_back(parent->GetChild(1)); 1039 new_order.push_back(parent->GetChild(0)); 1040 model_->ReorderChildren(parent, new_order); 1041 1042 // Make sure we were notified. 1043 AssertObserverCount(0, 0, 0, 0, 1, 0, 0, 1, 0); 1044 1045 // Make sure the order matches is correct (it should be reversed). 1046 ASSERT_EQ(4, parent->child_count()); 1047 EXPECT_EQ("D", base::UTF16ToASCII(parent->GetChild(0)->GetTitle())); 1048 EXPECT_EQ("C", base::UTF16ToASCII(parent->GetChild(1)->GetTitle())); 1049 EXPECT_EQ("B", base::UTF16ToASCII(parent->GetChild(2)->GetTitle())); 1050 EXPECT_EQ("A", base::UTF16ToASCII(parent->GetChild(3)->GetTitle())); 1051 } 1052 1053 TEST_F(BookmarkModelTest, NodeVisibility) { 1054 // Mobile node invisible by default 1055 EXPECT_TRUE(model_->bookmark_bar_node()->IsVisible()); 1056 EXPECT_TRUE(model_->other_node()->IsVisible()); 1057 EXPECT_FALSE(model_->mobile_node()->IsVisible()); 1058 1059 // Visibility of permanent node can only be changed if they are not 1060 // forced to be visible by the client. 1061 model_->SetPermanentNodeVisible(BookmarkNode::BOOKMARK_BAR, false); 1062 EXPECT_TRUE(model_->bookmark_bar_node()->IsVisible()); 1063 model_->SetPermanentNodeVisible(BookmarkNode::OTHER_NODE, false); 1064 EXPECT_TRUE(model_->other_node()->IsVisible()); 1065 model_->SetPermanentNodeVisible(BookmarkNode::MOBILE, true); 1066 EXPECT_TRUE(model_->mobile_node()->IsVisible()); 1067 model_->SetPermanentNodeVisible(BookmarkNode::MOBILE, false); 1068 EXPECT_FALSE(model_->mobile_node()->IsVisible()); 1069 1070 // Arbitrary node should be visible 1071 TestNode bbn; 1072 PopulateNodeFromString("B", &bbn); 1073 const BookmarkNode* parent = model_->mobile_node(); 1074 PopulateBookmarkNode(&bbn, model_.get(), parent); 1075 EXPECT_TRUE(parent->GetChild(0)->IsVisible()); 1076 1077 // Mobile folder should be visible now that it has a child. 1078 EXPECT_TRUE(model_->mobile_node()->IsVisible()); 1079 } 1080 1081 TEST_F(BookmarkModelTest, MobileNodeVisibileWithChildren) { 1082 const BookmarkNode* root = model_->mobile_node(); 1083 const base::string16 title(ASCIIToUTF16("foo")); 1084 const GURL url("http://foo.com"); 1085 1086 model_->AddURL(root, 0, title, url); 1087 EXPECT_TRUE(model_->mobile_node()->IsVisible()); 1088 } 1089 1090 TEST_F(BookmarkModelTest, ExtensiveChangesObserver) { 1091 AssertExtensiveChangesObserverCount(0, 0); 1092 EXPECT_FALSE(model_->IsDoingExtensiveChanges()); 1093 model_->BeginExtensiveChanges(); 1094 EXPECT_TRUE(model_->IsDoingExtensiveChanges()); 1095 AssertExtensiveChangesObserverCount(1, 0); 1096 model_->EndExtensiveChanges(); 1097 EXPECT_FALSE(model_->IsDoingExtensiveChanges()); 1098 AssertExtensiveChangesObserverCount(1, 1); 1099 } 1100 1101 TEST_F(BookmarkModelTest, MultipleExtensiveChangesObserver) { 1102 AssertExtensiveChangesObserverCount(0, 0); 1103 EXPECT_FALSE(model_->IsDoingExtensiveChanges()); 1104 model_->BeginExtensiveChanges(); 1105 EXPECT_TRUE(model_->IsDoingExtensiveChanges()); 1106 AssertExtensiveChangesObserverCount(1, 0); 1107 model_->BeginExtensiveChanges(); 1108 EXPECT_TRUE(model_->IsDoingExtensiveChanges()); 1109 AssertExtensiveChangesObserverCount(1, 0); 1110 model_->EndExtensiveChanges(); 1111 EXPECT_TRUE(model_->IsDoingExtensiveChanges()); 1112 AssertExtensiveChangesObserverCount(1, 0); 1113 model_->EndExtensiveChanges(); 1114 EXPECT_FALSE(model_->IsDoingExtensiveChanges()); 1115 AssertExtensiveChangesObserverCount(1, 1); 1116 } 1117 1118 // Verifies that IsBookmarked is true if any bookmark matches the given URL, 1119 // and that IsBookmarkedByUser is true only if at least one of the matching 1120 // bookmarks can be edited by the user. 1121 TEST_F(BookmarkModelTest, IsBookmarked) { 1122 // Reload the model with an extra node that is not editable by the user. 1123 BookmarkPermanentNode* extra_node = ReloadModelWithExtraNode(); 1124 1125 // "google.com" is a "user" bookmark. 1126 model_->AddURL(model_->other_node(), 0, base::ASCIIToUTF16("User"), 1127 GURL("http://google.com")); 1128 // "youtube.com" is not. 1129 model_->AddURL(extra_node, 0, base::ASCIIToUTF16("Extra"), 1130 GURL("http://youtube.com")); 1131 1132 EXPECT_TRUE(model_->IsBookmarked(GURL("http://google.com"))); 1133 EXPECT_TRUE(model_->IsBookmarked(GURL("http://youtube.com"))); 1134 EXPECT_FALSE(model_->IsBookmarked(GURL("http://reddit.com"))); 1135 1136 EXPECT_TRUE( 1137 bookmarks::IsBookmarkedByUser(model_.get(), GURL("http://google.com"))); 1138 EXPECT_FALSE( 1139 bookmarks::IsBookmarkedByUser(model_.get(), GURL("http://youtube.com"))); 1140 EXPECT_FALSE( 1141 bookmarks::IsBookmarkedByUser(model_.get(), GURL("http://reddit.com"))); 1142 } 1143 1144 // Verifies that GetMostRecentlyAddedUserNodeForURL skips bookmarks that 1145 // are not owned by the user. 1146 TEST_F(BookmarkModelTest, GetMostRecentlyAddedUserNodeForURLSkipsManagedNodes) { 1147 // Reload the model with an extra node that is not editable by the user. 1148 BookmarkPermanentNode* extra_node = ReloadModelWithExtraNode(); 1149 1150 const base::string16 title = base::ASCIIToUTF16("Title"); 1151 const BookmarkNode* user_parent = model_->other_node(); 1152 const BookmarkNode* managed_parent = extra_node; 1153 const GURL url("http://google.com"); 1154 1155 // |url| is not bookmarked yet. 1156 EXPECT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == NULL); 1157 1158 // Having a managed node doesn't count. 1159 model_->AddURL(managed_parent, 0, title, url); 1160 EXPECT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == NULL); 1161 1162 // Now add a user node. 1163 const BookmarkNode* user = model_->AddURL(user_parent, 0, title, url); 1164 EXPECT_EQ(user, model_->GetMostRecentlyAddedUserNodeForURL(url)); 1165 1166 // Having a more recent managed node doesn't count either. 1167 const BookmarkNode* managed = model_->AddURL(managed_parent, 0, title, url); 1168 EXPECT_GE(managed->date_added(), user->date_added()); 1169 EXPECT_EQ(user, model_->GetMostRecentlyAddedUserNodeForURL(url)); 1170 } 1171 1172 TEST(BookmarkNodeTest, NodeMetaInfo) { 1173 GURL url; 1174 BookmarkNode node(url); 1175 EXPECT_FALSE(node.GetMetaInfoMap()); 1176 1177 EXPECT_TRUE(node.SetMetaInfo("key1", "value1")); 1178 std::string out_value; 1179 EXPECT_TRUE(node.GetMetaInfo("key1", &out_value)); 1180 EXPECT_EQ("value1", out_value); 1181 EXPECT_FALSE(node.SetMetaInfo("key1", "value1")); 1182 1183 EXPECT_FALSE(node.GetMetaInfo("key2.subkey1", &out_value)); 1184 EXPECT_TRUE(node.SetMetaInfo("key2.subkey1", "value2")); 1185 EXPECT_TRUE(node.GetMetaInfo("key2.subkey1", &out_value)); 1186 EXPECT_EQ("value2", out_value); 1187 1188 EXPECT_FALSE(node.GetMetaInfo("key2.subkey2.leaf", &out_value)); 1189 EXPECT_TRUE(node.SetMetaInfo("key2.subkey2.leaf", "")); 1190 EXPECT_TRUE(node.GetMetaInfo("key2.subkey2.leaf", &out_value)); 1191 EXPECT_EQ("", out_value); 1192 1193 EXPECT_TRUE(node.DeleteMetaInfo("key1")); 1194 EXPECT_TRUE(node.DeleteMetaInfo("key2.subkey1")); 1195 EXPECT_TRUE(node.DeleteMetaInfo("key2.subkey2.leaf")); 1196 EXPECT_FALSE(node.DeleteMetaInfo("key3")); 1197 EXPECT_FALSE(node.GetMetaInfo("key1", &out_value)); 1198 EXPECT_FALSE(node.GetMetaInfo("key2.subkey1", &out_value)); 1199 EXPECT_FALSE(node.GetMetaInfo("key2.subkey2", &out_value)); 1200 EXPECT_FALSE(node.GetMetaInfo("key2.subkey2.leaf", &out_value)); 1201 EXPECT_FALSE(node.GetMetaInfoMap()); 1202 } 1203 1204 } // namespace 1205 } // namespace bookmarks 1206