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