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