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