bookmarks_helper.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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/sync/test/integration/bookmarks_helper.h" 6 7#include "base/compiler_specific.h" 8#include "base/rand_util.h" 9#include "base/string_number_conversions.h" 10#include "base/stringprintf.h" 11#include "base/synchronization/waitable_event.h" 12#include "base/utf_string_conversions.h" 13#include "chrome/browser/bookmarks/bookmark_model.h" 14#include "chrome/browser/bookmarks/bookmark_model_factory.h" 15#include "chrome/browser/bookmarks/bookmark_model_observer.h" 16#include "chrome/browser/bookmarks/bookmark_utils.h" 17#include "chrome/browser/favicon/favicon_service_factory.h" 18#include "chrome/browser/history/history_service_factory.h" 19#include "chrome/browser/history/history_types.h" 20#include "chrome/browser/profiles/profile.h" 21#include "chrome/browser/sync/profile_sync_service_harness.h" 22#include "chrome/browser/sync/test/integration/sync_test.h" 23#include "chrome/browser/sync/test/integration/sync_datatype_helper.h" 24#include "chrome/test/base/ui_test_utils.h" 25#include "testing/gtest/include/gtest/gtest.h" 26#include "third_party/skia/include/core/SkBitmap.h" 27#include "ui/base/models/tree_node_iterator.h" 28#include "ui/gfx/codec/png_codec.h" 29 30using sync_datatype_helper::test; 31 32namespace { 33 34// History task which runs all pending tasks on the history thread and 35// signals when the tasks have completed. 36class HistoryEmptyTask : public HistoryDBTask { 37 public: 38 explicit HistoryEmptyTask(base::WaitableEvent* done) : done_(done) {} 39 40 virtual bool RunOnDBThread(history::HistoryBackend* backend, 41 history::HistoryDatabase* db) { 42 content::RunAllPendingInMessageLoop(); 43 done_->Signal(); 44 return true; 45 } 46 47 virtual void DoneRunOnMainThread() {} 48 49 private: 50 virtual ~HistoryEmptyTask() {} 51 52 base::WaitableEvent* done_; 53}; 54 55// Helper class used to wait for changes to take effect on the favicon of a 56// particular bookmark node in a particular bookmark model. 57class FaviconChangeObserver : public BookmarkModelObserver { 58 public: 59 FaviconChangeObserver(BookmarkModel* model, const BookmarkNode* node) 60 : model_(model), 61 node_(node), 62 wait_for_load_(false) { 63 model->AddObserver(this); 64 } 65 virtual ~FaviconChangeObserver() { 66 model_->RemoveObserver(this); 67 } 68 void WaitForGetFavicon() { 69 wait_for_load_ = true; 70 content::RunMessageLoop(); 71 ASSERT_TRUE(node_->is_favicon_loaded()); 72 ASSERT_FALSE(model_->GetFavicon(node_).IsEmpty()); 73 } 74 void WaitForSetFavicon() { 75 wait_for_load_ = false; 76 content::RunMessageLoop(); 77 } 78 virtual void Loaded(BookmarkModel* model, bool ids_reassigned) OVERRIDE {} 79 virtual void BookmarkNodeMoved(BookmarkModel* model, 80 const BookmarkNode* old_parent, 81 int old_index, 82 const BookmarkNode* new_parent, 83 int new_index) OVERRIDE {} 84 virtual void BookmarkNodeAdded(BookmarkModel* model, 85 const BookmarkNode* parent, 86 int index) OVERRIDE {} 87 virtual void BookmarkNodeRemoved(BookmarkModel* model, 88 const BookmarkNode* parent, 89 int old_index, 90 const BookmarkNode* node) OVERRIDE {} 91 virtual void BookmarkNodeChanged(BookmarkModel* model, 92 const BookmarkNode* node) OVERRIDE { 93 if (model == model_ && node == node_) 94 model->GetFavicon(node); 95 } 96 virtual void BookmarkNodeChildrenReordered( 97 BookmarkModel* model, 98 const BookmarkNode* node) OVERRIDE {} 99 virtual void BookmarkNodeFaviconChanged( 100 BookmarkModel* model, 101 const BookmarkNode* node) OVERRIDE { 102 if (model == model_ && node == node_) { 103 if (!wait_for_load_ || (wait_for_load_ && node->is_favicon_loaded())) 104 MessageLoopForUI::current()->Quit(); 105 } 106 } 107 108 private: 109 BookmarkModel* model_; 110 const BookmarkNode* node_; 111 bool wait_for_load_; 112 DISALLOW_COPY_AND_ASSIGN(FaviconChangeObserver); 113}; 114 115// A collection of URLs for which we have added favicons. Since loading a 116// favicon is an asynchronous operation and doesn't necessarily invoke a 117// callback, this collection is used to determine if we must wait for a URL's 118// favicon to load or not. 119std::set<GURL>* urls_with_favicons_ = NULL; 120 121// Returns the number of nodes of node type |node_type| in |model| whose 122// titles match the string |title|. 123int CountNodesWithTitlesMatching(BookmarkModel* model, 124 BookmarkNode::Type node_type, 125 const string16& title) { 126 ui::TreeNodeIterator<const BookmarkNode> iterator(model->root_node()); 127 // Walk through the model tree looking for bookmark nodes of node type 128 // |node_type| whose titles match |title|. 129 int count = 0; 130 while (iterator.has_next()) { 131 const BookmarkNode* node = iterator.Next(); 132 if ((node->type() == node_type) && (node->GetTitle() == title)) 133 ++count; 134 } 135 return count; 136} 137 138// Checks if the favicon data in |bitmap_a| and |bitmap_b| are equivalent. 139// Returns true if they match. 140bool FaviconBitmapsMatch(const SkBitmap& bitmap_a, const SkBitmap& bitmap_b) { 141 if (bitmap_a.getSize() == 0U && bitmap_a.getSize() == 0U) 142 return true; 143 if ((bitmap_a.getSize() != bitmap_b.getSize()) || 144 (bitmap_a.width() != bitmap_b.width()) || 145 (bitmap_a.height() != bitmap_b.height())) { 146 LOG(ERROR) << "Favicon size mismatch: " << bitmap_a.getSize() << " (" 147 << bitmap_a.width() << "x" << bitmap_a.height() << ") vs. " 148 << bitmap_b.getSize() << " (" << bitmap_b.width() << "x" 149 << bitmap_b.height() << ")"; 150 return false; 151 } 152 SkAutoLockPixels bitmap_lock_a(bitmap_a); 153 SkAutoLockPixels bitmap_lock_b(bitmap_b); 154 void* node_pixel_addr_a = bitmap_a.getPixels(); 155 EXPECT_TRUE(node_pixel_addr_a); 156 void* node_pixel_addr_b = bitmap_b.getPixels(); 157 EXPECT_TRUE(node_pixel_addr_b); 158 if (memcmp(node_pixel_addr_a, node_pixel_addr_b, bitmap_a.getSize()) != 0) { 159 LOG(ERROR) << "Favicon bitmap mismatch"; 160 return false; 161 } else { 162 return true; 163 } 164} 165 166// Gets the favicon associated with |node| in |model|. 167gfx::Image GetFavicon(BookmarkModel* model, const BookmarkNode* node) { 168 // If a favicon wasn't explicitly set for a particular URL, simply return its 169 // blank favicon. 170 if (!urls_with_favicons_ || 171 urls_with_favicons_->find(node->url()) == urls_with_favicons_->end()) { 172 return gfx::Image(); 173 } 174 // If a favicon was explicitly set, we may need to wait for it to be loaded 175 // via BookmarkModel::GetFavicon(), which is an asynchronous operation. 176 if (!node->is_favicon_loaded()) { 177 FaviconChangeObserver observer(model, node); 178 model->GetFavicon(node); 179 observer.WaitForGetFavicon(); 180 } 181 EXPECT_TRUE(node->is_favicon_loaded()); 182 EXPECT_FALSE(model->GetFavicon(node).IsEmpty()); 183 return model->GetFavicon(node); 184} 185 186// Sets the favicon for |profile| and |node|. |profile| may be 187// |test()->verifier()|. 188void SetFaviconImpl(Profile* profile, 189 const BookmarkNode* node, 190 const gfx::Image& image) { 191 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile); 192 193 FaviconChangeObserver observer(model, node); 194 FaviconService* favicon_service = 195 FaviconServiceFactory::GetForProfile(profile, 196 Profile::EXPLICIT_ACCESS); 197 favicon_service->SetFavicons(node->url(), 198 node->url(), 199 history::FAVICON, 200 image); 201 202 // Wait for the favicon for |node| to be invalidated. 203 observer.WaitForSetFavicon(); 204 // Wait for the BookmarkModel to fetch the updated favicon and for the new 205 // favicon to be sent to BookmarkChangeProcessor. 206 GetFavicon(model, node); 207} 208 209// Wait for all currently scheduled tasks on the history thread for all 210// profiles to complete and any notifications sent to the UI thread to have 211// finished processing. 212void WaitForHistoryToProcessPendingTasks() { 213 // Skip waiting for history to complete for tests without favicons. 214 if (!urls_with_favicons_) 215 return; 216 217 std::vector<Profile*> profiles_which_need_to_wait; 218 if (test()->use_verifier()) 219 profiles_which_need_to_wait.push_back(test()->verifier()); 220 for (int i = 0; i < test()->num_clients(); ++i) 221 profiles_which_need_to_wait.push_back(test()->GetProfile(i)); 222 223 for (size_t i = 0; i < profiles_which_need_to_wait.size(); ++i) { 224 Profile* profile = profiles_which_need_to_wait[i]; 225 HistoryService* history_service = 226 HistoryServiceFactory::GetForProfileWithoutCreating(profile); 227 base::WaitableEvent done(false, false); 228 CancelableRequestConsumer request_consumer; 229 history_service->ScheduleDBTask(new HistoryEmptyTask(&done), 230 &request_consumer); 231 done.Wait(); 232 } 233 // Wait such that any notifications broadcast from one of the history threads 234 // to the UI thread are processed. 235 content::RunAllPendingInMessageLoop(); 236} 237 238// Checks if the favicon in |node_a| from |model_a| matches that of |node_b| 239// from |model_b|. Returns true if they match. 240bool FaviconsMatch(BookmarkModel* model_a, 241 BookmarkModel* model_b, 242 const BookmarkNode* node_a, 243 const BookmarkNode* node_b) { 244 const gfx::Image& bitmap_a = GetFavicon(model_a, node_a); 245 const gfx::Image& bitmap_b = GetFavicon(model_b, node_b); 246 247 if (bitmap_a.IsEmpty() && bitmap_b.IsEmpty()) 248 return true; // Two empty images are equivalent. 249 return !bitmap_a.IsEmpty() && !bitmap_b.IsEmpty() && 250 FaviconBitmapsMatch(*bitmap_a.ToSkBitmap(), *bitmap_b.ToSkBitmap()); 251} 252 253// Does a deep comparison of BookmarkNode fields in |model_a| and |model_b|. 254// Returns true if they are all equal. 255bool NodesMatch(const BookmarkNode* node_a, const BookmarkNode* node_b) { 256 if (node_a == NULL || node_b == NULL) 257 return node_a == node_b; 258 if (node_a->is_folder() != node_b->is_folder()) { 259 LOG(ERROR) << "Cannot compare folder with bookmark"; 260 return false; 261 } 262 if (node_a->GetTitle() != node_b->GetTitle()) { 263 LOG(ERROR) << "Title mismatch: " << node_a->GetTitle() << " vs. " 264 << node_b->GetTitle(); 265 return false; 266 } 267 if (node_a->url() != node_b->url()) { 268 LOG(ERROR) << "URL mismatch: " << node_a->url() << " vs. " 269 << node_b->url(); 270 return false; 271 } 272 if (node_a->parent()->GetIndexOf(node_a) != 273 node_b->parent()->GetIndexOf(node_b)) { 274 LOG(ERROR) << "Index mismatch: " 275 << node_a->parent()->GetIndexOf(node_a) << " vs. " 276 << node_b->parent()->GetIndexOf(node_b); 277 return false; 278 } 279 return true; 280} 281 282// Checks if the hierarchies in |model_a| and |model_b| are equivalent in 283// terms of the data model and favicon. Returns true if they both match. 284// Note: Some peripheral fields like creation times are allowed to mismatch. 285bool BookmarkModelsMatch(BookmarkModel* model_a, BookmarkModel* model_b) { 286 bool ret_val = true; 287 ui::TreeNodeIterator<const BookmarkNode> iterator_a(model_a->root_node()); 288 ui::TreeNodeIterator<const BookmarkNode> iterator_b(model_b->root_node()); 289 while (iterator_a.has_next()) { 290 const BookmarkNode* node_a = iterator_a.Next(); 291 if (!iterator_b.has_next()) { 292 LOG(ERROR) << "Models do not match."; 293 return false; 294 } 295 const BookmarkNode* node_b = iterator_b.Next(); 296 ret_val = ret_val && NodesMatch(node_a, node_b); 297 if (node_a->is_folder() || node_b->is_folder()) 298 continue; 299 ret_val = ret_val && FaviconsMatch(model_a, model_b, node_a, node_b); 300 } 301 ret_val = ret_val && (!iterator_b.has_next()); 302 return ret_val; 303} 304 305// Finds the node in the verifier bookmark model that corresponds to 306// |foreign_node| in |foreign_model| and stores its address in |result|. 307void FindNodeInVerifier(BookmarkModel* foreign_model, 308 const BookmarkNode* foreign_node, 309 const BookmarkNode** result) { 310 // Climb the tree. 311 std::stack<int> path; 312 const BookmarkNode* walker = foreign_node; 313 while (walker != foreign_model->root_node()) { 314 path.push(walker->parent()->GetIndexOf(walker)); 315 walker = walker->parent(); 316 } 317 318 // Swing over to the other tree. 319 walker = bookmarks_helper::GetVerifierBookmarkModel()->root_node(); 320 321 // Climb down. 322 while (!path.empty()) { 323 ASSERT_TRUE(walker->is_folder()); 324 ASSERT_LT(path.top(), walker->child_count()); 325 walker = walker->GetChild(path.top()); 326 path.pop(); 327 } 328 329 ASSERT_TRUE(NodesMatch(foreign_node, walker)); 330 *result = walker; 331} 332 333} // namespace 334 335 336namespace bookmarks_helper { 337 338BookmarkModel* GetBookmarkModel(int index) { 339 return BookmarkModelFactory::GetForProfile(test()->GetProfile(index)); 340} 341 342const BookmarkNode* GetBookmarkBarNode(int index) { 343 return GetBookmarkModel(index)->bookmark_bar_node(); 344} 345 346const BookmarkNode* GetOtherNode(int index) { 347 return GetBookmarkModel(index)->other_node(); 348} 349 350const BookmarkNode* GetSyncedBookmarksNode(int index) { 351 return GetBookmarkModel(index)->mobile_node(); 352} 353 354BookmarkModel* GetVerifierBookmarkModel() { 355 return BookmarkModelFactory::GetForProfile(test()->verifier()); 356} 357 358const BookmarkNode* AddURL(int profile, 359 const std::wstring& title, 360 const GURL& url) { 361 return AddURL(profile, GetBookmarkBarNode(profile), 0, title, url); 362} 363 364const BookmarkNode* AddURL(int profile, 365 int index, 366 const std::wstring& title, 367 const GURL& url) { 368 return AddURL(profile, GetBookmarkBarNode(profile), index, title, url); 369} 370 371const BookmarkNode* AddURL(int profile, 372 const BookmarkNode* parent, 373 int index, 374 const std::wstring& title, 375 const GURL& url) { 376 if (GetBookmarkModel(profile)->GetNodeByID(parent->id()) != parent) { 377 LOG(ERROR) << "Node " << parent->GetTitle() << " does not belong to " 378 << "Profile " << profile; 379 return NULL; 380 } 381 const BookmarkNode* result = GetBookmarkModel(profile)-> 382 AddURL(parent, index, WideToUTF16(title), url); 383 if (!result) { 384 LOG(ERROR) << "Could not add bookmark " << title << " to Profile " 385 << profile; 386 return NULL; 387 } 388 if (test()->use_verifier()) { 389 const BookmarkNode* v_parent = NULL; 390 FindNodeInVerifier(GetBookmarkModel(profile), parent, &v_parent); 391 const BookmarkNode* v_node = GetVerifierBookmarkModel()-> 392 AddURL(v_parent, index, WideToUTF16(title), url); 393 if (!v_node) { 394 LOG(ERROR) << "Could not add bookmark " << title << " to the verifier"; 395 return NULL; 396 } 397 EXPECT_TRUE(NodesMatch(v_node, result)); 398 } 399 return result; 400} 401 402const BookmarkNode* AddFolder(int profile, 403 const std::wstring& title) { 404 return AddFolder(profile, GetBookmarkBarNode(profile), 0, title); 405} 406 407const BookmarkNode* AddFolder(int profile, 408 int index, 409 const std::wstring& title) { 410 return AddFolder(profile, GetBookmarkBarNode(profile), index, title); 411} 412 413const BookmarkNode* AddFolder(int profile, 414 const BookmarkNode* parent, 415 int index, 416 const std::wstring& title) { 417 if (GetBookmarkModel(profile)->GetNodeByID(parent->id()) != parent) { 418 LOG(ERROR) << "Node " << parent->GetTitle() << " does not belong to " 419 << "Profile " << profile; 420 return NULL; 421 } 422 const BookmarkNode* result = 423 GetBookmarkModel(profile)->AddFolder(parent, index, WideToUTF16(title)); 424 EXPECT_TRUE(result); 425 if (!result) { 426 LOG(ERROR) << "Could not add folder " << title << " to Profile " 427 << profile; 428 return NULL; 429 } 430 if (test()->use_verifier()) { 431 const BookmarkNode* v_parent = NULL; 432 FindNodeInVerifier(GetBookmarkModel(profile), parent, &v_parent); 433 const BookmarkNode* v_node = GetVerifierBookmarkModel()->AddFolder( 434 v_parent, index, WideToUTF16(title)); 435 if (!v_node) { 436 LOG(ERROR) << "Could not add folder " << title << " to the verifier"; 437 return NULL; 438 } 439 EXPECT_TRUE(NodesMatch(v_node, result)); 440 } 441 return result; 442} 443 444void SetTitle(int profile, 445 const BookmarkNode* node, 446 const std::wstring& new_title) { 447 ASSERT_EQ(GetBookmarkModel(profile)->GetNodeByID(node->id()), node) 448 << "Node " << node->GetTitle() << " does not belong to " 449 << "Profile " << profile; 450 if (test()->use_verifier()) { 451 const BookmarkNode* v_node = NULL; 452 FindNodeInVerifier(GetBookmarkModel(profile), node, &v_node); 453 GetVerifierBookmarkModel()->SetTitle(v_node, WideToUTF16(new_title)); 454 } 455 GetBookmarkModel(profile)->SetTitle(node, WideToUTF16(new_title)); 456} 457 458void SetFavicon(int profile, 459 const BookmarkNode* node, 460 const std::vector<unsigned char>& icon_bytes_vector) { 461 scoped_refptr<base::RefCountedBytes> bitmap_data( 462 new base::RefCountedBytes(icon_bytes_vector)); 463 gfx::Image image(bitmap_data->front(), bitmap_data->size()); 464 ASSERT_EQ(GetBookmarkModel(profile)->GetNodeByID(node->id()), node) 465 << "Node " << node->GetTitle() << " does not belong to " 466 << "Profile " << profile; 467 ASSERT_EQ(BookmarkNode::URL, node->type()) 468 << "Node " << node->GetTitle() << " must be a url."; 469 if (urls_with_favicons_ == NULL) 470 urls_with_favicons_ = new std::set<GURL>(); 471 urls_with_favicons_->insert(node->url()); 472 if (test()->use_verifier()) { 473 const BookmarkNode* v_node = NULL; 474 FindNodeInVerifier(GetBookmarkModel(profile), node, &v_node); 475 SetFaviconImpl(test()->verifier(), v_node, image); 476 } 477 SetFaviconImpl(test()->GetProfile(profile), node, image); 478} 479 480const BookmarkNode* SetURL(int profile, 481 const BookmarkNode* node, 482 const GURL& new_url) { 483 if (GetBookmarkModel(profile)->GetNodeByID(node->id()) != node) { 484 LOG(ERROR) << "Node " << node->GetTitle() << " does not belong to " 485 << "Profile " << profile; 486 return NULL; 487 } 488 if (test()->use_verifier()) { 489 const BookmarkNode* v_node = NULL; 490 FindNodeInVerifier(GetBookmarkModel(profile), node, &v_node); 491 bookmark_utils::ApplyEditsWithNoFolderChange( 492 GetVerifierBookmarkModel(), 493 v_node->parent(), 494 BookmarkEditor::EditDetails::EditNode(v_node), 495 v_node->GetTitle(), 496 new_url); 497 } 498 return bookmark_utils::ApplyEditsWithNoFolderChange( 499 GetBookmarkModel(profile), 500 node->parent(), 501 BookmarkEditor::EditDetails::EditNode(node), 502 node->GetTitle(), 503 new_url); 504} 505 506void Move(int profile, 507 const BookmarkNode* node, 508 const BookmarkNode* new_parent, 509 int index) { 510 ASSERT_EQ(GetBookmarkModel(profile)->GetNodeByID(node->id()), node) 511 << "Node " << node->GetTitle() << " does not belong to " 512 << "Profile " << profile; 513 if (test()->use_verifier()) { 514 const BookmarkNode* v_new_parent = NULL; 515 const BookmarkNode* v_node = NULL; 516 FindNodeInVerifier(GetBookmarkModel(profile), new_parent, &v_new_parent); 517 FindNodeInVerifier(GetBookmarkModel(profile), node, &v_node); 518 GetVerifierBookmarkModel()->Move(v_node, v_new_parent, index); 519 } 520 GetBookmarkModel(profile)->Move(node, new_parent, index); 521} 522 523void Remove(int profile, 524 const BookmarkNode* parent, 525 int index) { 526 ASSERT_EQ(GetBookmarkModel(profile)->GetNodeByID(parent->id()), parent) 527 << "Node " << parent->GetTitle() << " does not belong to " 528 << "Profile " << profile; 529 if (test()->use_verifier()) { 530 const BookmarkNode* v_parent = NULL; 531 FindNodeInVerifier(GetBookmarkModel(profile), parent, &v_parent); 532 ASSERT_TRUE(NodesMatch(parent->GetChild(index), v_parent->GetChild(index))); 533 GetVerifierBookmarkModel()->Remove(v_parent, index); 534 } 535 GetBookmarkModel(profile)->Remove(parent, index); 536} 537 538void SortChildren(int profile, const BookmarkNode* parent) { 539 ASSERT_EQ(GetBookmarkModel(profile)->GetNodeByID(parent->id()), parent) 540 << "Node " << parent->GetTitle() << " does not belong to " 541 << "Profile " << profile; 542 if (test()->use_verifier()) { 543 const BookmarkNode* v_parent = NULL; 544 FindNodeInVerifier(GetBookmarkModel(profile), parent, &v_parent); 545 GetVerifierBookmarkModel()->SortChildren(v_parent); 546 } 547 GetBookmarkModel(profile)->SortChildren(parent); 548} 549 550void ReverseChildOrder(int profile, const BookmarkNode* parent) { 551 ASSERT_EQ(GetBookmarkModel(profile)->GetNodeByID(parent->id()), parent) 552 << "Node " << parent->GetTitle() << " does not belong to " 553 << "Profile " << profile; 554 int child_count = parent->child_count(); 555 if (child_count <= 0) 556 return; 557 for (int index = 0; index < child_count; ++index) { 558 Move(profile, parent->GetChild(index), parent, child_count - index); 559 } 560} 561 562bool ModelMatchesVerifier(int profile) { 563 if (!test()->use_verifier()) { 564 LOG(ERROR) << "Illegal to call ModelMatchesVerifier() after " 565 << "DisableVerifier(). Use ModelsMatch() instead."; 566 return false; 567 } 568 return BookmarkModelsMatch(GetVerifierBookmarkModel(), 569 GetBookmarkModel(profile)); 570} 571 572bool AllModelsMatchVerifier() { 573 // Ensure that all tasks have finished processing on the history thread 574 // and that any notifications the history thread may have sent have been 575 // processed before comparing models. 576 WaitForHistoryToProcessPendingTasks(); 577 578 for (int i = 0; i < test()->num_clients(); ++i) { 579 if (!ModelMatchesVerifier(i)) { 580 LOG(ERROR) << "Model " << i << " does not match the verifier."; 581 return false; 582 } 583 } 584 return true; 585} 586 587bool ModelsMatch(int profile_a, int profile_b) { 588 return BookmarkModelsMatch(GetBookmarkModel(profile_a), 589 GetBookmarkModel(profile_b)); 590} 591 592bool AllModelsMatch() { 593 // Ensure that all tasks have finished processing on the history thread 594 // and that any notifications the history thread may have sent have been 595 // processed before comparing models. 596 WaitForHistoryToProcessPendingTasks(); 597 598 for (int i = 1; i < test()->num_clients(); ++i) { 599 if (!ModelsMatch(0, i)) { 600 LOG(ERROR) << "Model " << i << " does not match Model 0."; 601 return false; 602 } 603 } 604 return true; 605} 606 607bool ContainsDuplicateBookmarks(int profile) { 608 ui::TreeNodeIterator<const BookmarkNode> iterator( 609 GetBookmarkModel(profile)->root_node()); 610 while (iterator.has_next()) { 611 const BookmarkNode* node = iterator.Next(); 612 if (node->is_folder()) 613 continue; 614 std::vector<const BookmarkNode*> nodes; 615 GetBookmarkModel(profile)->GetNodesByURL(node->url(), &nodes); 616 EXPECT_TRUE(nodes.size() >= 1); 617 for (std::vector<const BookmarkNode*>::const_iterator it = nodes.begin(); 618 it != nodes.end(); ++it) { 619 if (node->id() != (*it)->id() && 620 node->parent() == (*it)->parent() && 621 node->GetTitle() == (*it)->GetTitle()){ 622 return true; 623 } 624 } 625 } 626 return false; 627} 628 629bool HasNodeWithURL(int profile, const GURL& url) { 630 std::vector<const BookmarkNode*> nodes; 631 GetBookmarkModel(profile)->GetNodesByURL(url, &nodes); 632 return !nodes.empty(); 633} 634 635const BookmarkNode* GetUniqueNodeByURL(int profile, const GURL& url) { 636 std::vector<const BookmarkNode*> nodes; 637 GetBookmarkModel(profile)->GetNodesByURL(url, &nodes); 638 EXPECT_EQ(1U, nodes.size()); 639 if (nodes.empty()) 640 return NULL; 641 return nodes[0]; 642} 643 644int CountBookmarksWithTitlesMatching(int profile, const std::wstring& title) { 645 return CountNodesWithTitlesMatching(GetBookmarkModel(profile), 646 BookmarkNode::URL, 647 WideToUTF16(title)); 648} 649 650int CountFoldersWithTitlesMatching(int profile, const std::wstring& title) { 651 return CountNodesWithTitlesMatching(GetBookmarkModel(profile), 652 BookmarkNode::FOLDER, 653 WideToUTF16(title)); 654} 655 656std::vector<unsigned char> CreateFavicon(int seed) { 657 const int w = 16; 658 const int h = 16; 659 SkBitmap bmp; 660 bmp.setConfig(SkBitmap::kARGB_8888_Config, w, h); 661 bmp.allocPixels(); 662 uint32_t* src_data = bmp.getAddr32(0, 0); 663 for (int i = 0; i < w * h; ++i) { 664 src_data[i] = SkPreMultiplyARGB((seed + i) % 255, 665 (seed + i) % 250, 666 (seed + i) % 245, 667 (seed + i) % 240); 668 } 669 std::vector<unsigned char> favicon; 670 gfx::PNGCodec::EncodeBGRASkBitmap(bmp, false, &favicon); 671 return favicon; 672} 673 674std::string IndexedURL(int i) { 675 return StringPrintf("http://www.host.ext:1234/path/filename/%d", i); 676} 677 678std::wstring IndexedURLTitle(int i) { 679 return StringPrintf(L"URL Title %d", i); 680} 681 682std::wstring IndexedFolderName(int i) { 683 return StringPrintf(L"Folder Name %d", i); 684} 685 686std::wstring IndexedSubfolderName(int i) { 687 return StringPrintf(L"Subfolder Name %d", i); 688} 689 690std::wstring IndexedSubsubfolderName(int i) { 691 return StringPrintf(L"Subsubfolder Name %d", i); 692} 693 694} // namespace bookmarks_helper 695