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