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