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.
5 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
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"
36 using sync_datatype_helper::test
;
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
{
44 explicit HistoryEmptyTask(base::WaitableEvent
* done
) : done_(done
) {}
46 virtual bool RunOnDBThread(history::HistoryBackend
* backend
,
47 history::HistoryDatabase
* db
) OVERRIDE
{
48 content::RunAllPendingInMessageLoop();
53 virtual void DoneRunOnMainThread() OVERRIDE
{}
56 virtual ~HistoryEmptyTask() {}
58 base::WaitableEvent
* done_
;
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
{
65 FaviconChangeObserver(BookmarkModel
* model
, const BookmarkNode
* node
)
68 wait_for_load_(false) {
69 model
->AddObserver(this);
71 virtual ~FaviconChangeObserver() {
72 model_
->RemoveObserver(this);
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());
80 void WaitForSetFavicon() {
81 wait_for_load_
= false;
82 content::RunMessageLoop();
84 virtual void BookmarkModelLoaded(BookmarkModel
* model
,
85 bool ids_reassigned
) OVERRIDE
{}
86 virtual void BookmarkNodeMoved(BookmarkModel
* model
,
87 const BookmarkNode
* old_parent
,
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
,
97 const BookmarkNode
* node
) OVERRIDE
{}
98 virtual void BookmarkAllNodesRemoved(BookmarkModel
* model
) OVERRIDE
{}
100 virtual void BookmarkNodeChanged(BookmarkModel
* model
,
101 const BookmarkNode
* node
) OVERRIDE
{
102 if (model
== model_
&& node
== node_
)
103 model
->GetFavicon(node
);
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();
118 BookmarkModel
* model_
;
119 const BookmarkNode
* node_
;
121 DISALLOW_COPY_AND_ASSIGN(FaviconChangeObserver
);
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.
128 std::set
<GURL
>* urls_with_favicons_
= NULL
;
130 // Returns the number of nodes of node type |node_type| in |model| whose
131 // titles match the string |title|.
132 int CountNodesWithTitlesMatching(BookmarkModel
* model
,
133 BookmarkNode::Type node_type
,
134 const base::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|.
139 while (iterator
.has_next()) {
140 const BookmarkNode
* node
= iterator
.Next();
141 if ((node
->type() == node_type
) && (node
->GetTitle() == title
))
147 // Checks if the favicon data in |bitmap_a| and |bitmap_b| are equivalent.
148 // Returns true if they match.
149 bool FaviconBitmapsMatch(const SkBitmap
& bitmap_a
, const SkBitmap
& bitmap_b
) {
150 if (bitmap_a
.getSize() == 0U && bitmap_b
.getSize() == 0U)
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() << ")";
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";
175 // Represents a favicon image and the icon URL associated with it.
180 FaviconData(const gfx::Image
& favicon_image
,
181 const GURL
& favicon_url
)
182 : image(favicon_image
),
183 icon_url(favicon_url
) {
193 // Gets the favicon and icon URL associated with |node| in |model|.
194 FaviconData
GetFaviconData(BookmarkModel
* model
,
195 const BookmarkNode
* node
) {
196 // If a favicon wasn't explicitly set for a particular URL, simply return its
198 if (!urls_with_favicons_
||
199 urls_with_favicons_
->find(node
->url()) == urls_with_favicons_
->end()) {
200 return FaviconData();
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();
209 EXPECT_TRUE(node
->is_favicon_loaded());
210 EXPECT_FALSE(model
->GetFavicon(node
).IsEmpty());
211 return FaviconData(model
->GetFavicon(node
), node
->icon_url());
214 // Sets the favicon for |profile| and |node|. |profile| may be
215 // |test()->verifier()|.
216 void 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
);
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(),
233 browser_sync::BookmarkChangeProcessor::ApplyBookmarkFavicon(
234 node
, profile
, icon_url
, image
.As1xPNGBytes());
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
);
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.
247 void WaitForHistoryToProcessPendingTasks() {
248 // Skip waiting for history to complete for tests without favicons.
249 if (!urls_with_favicons_
)
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
));
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
),
268 // Wait such that any notifications broadcast from one of the history threads
269 // to the UI thread are processed.
270 content::RunAllPendingInMessageLoop();
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.
275 bool 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
);
282 if (favicon_data_a
.icon_url
!= favicon_data_b
.icon_url
)
285 gfx::Image image_a
= favicon_data_a
.image
;
286 gfx::Image image_b
= favicon_data_b
.image
;
288 if (image_a
.IsEmpty() && image_b
.IsEmpty())
289 return true; // Two empty images are equivalent.
291 if (image_a
.IsEmpty() != image_b
.IsEmpty())
294 // Compare only the 1x bitmaps as only those are synced.
295 SkBitmap bitmap_a
= image_a
.AsImageSkia().GetRepresentation(
297 SkBitmap bitmap_b
= image_b
.AsImageSkia().GetRepresentation(
299 return FaviconBitmapsMatch(bitmap_a
, bitmap_b
);
302 // Does a deep comparison of BookmarkNode fields in |model_a| and |model_b|.
303 // Returns true if they are all equal.
304 bool 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";
311 if (node_a
->GetTitle() != node_b
->GetTitle()) {
312 LOG(ERROR
) << "Title mismatch: " << node_a
->GetTitle() << " vs. "
313 << node_b
->GetTitle();
316 if (node_a
->url() != node_b
->url()) {
317 LOG(ERROR
) << "URL mismatch: " << node_a
->url() << " vs. "
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
);
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.
334 bool BookmarkModelsMatch(BookmarkModel
* model_a
, BookmarkModel
* model_b
) {
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.";
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())
348 ret_val
= ret_val
&& FaviconsMatch(model_a
, model_b
, node_a
, node_b
);
350 ret_val
= ret_val
&& (!iterator_b
.has_next());
354 // Finds the node in the verifier bookmark model that corresponds to
355 // |foreign_node| in |foreign_model| and stores its address in |result|.
356 void FindNodeInVerifier(BookmarkModel
* foreign_model
,
357 const BookmarkNode
* foreign_node
,
358 const BookmarkNode
** result
) {
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();
367 // Swing over to the other tree.
368 walker
= bookmarks_helper::GetVerifierBookmarkModel()->root_node();
371 while (!path
.empty()) {
372 ASSERT_TRUE(walker
->is_folder());
373 ASSERT_LT(path
.top(), walker
->child_count());
374 walker
= walker
->GetChild(path
.top());
378 ASSERT_TRUE(NodesMatch(foreign_node
, walker
));
385 namespace bookmarks_helper
{
387 BookmarkModel
* GetBookmarkModel(int index
) {
388 return BookmarkModelFactory::GetForProfile(test()->GetProfile(index
));
391 const BookmarkNode
* GetBookmarkBarNode(int index
) {
392 return GetBookmarkModel(index
)->bookmark_bar_node();
395 const BookmarkNode
* GetOtherNode(int index
) {
396 return GetBookmarkModel(index
)->other_node();
399 const BookmarkNode
* GetSyncedBookmarksNode(int index
) {
400 return GetBookmarkModel(index
)->mobile_node();
403 BookmarkModel
* GetVerifierBookmarkModel() {
404 return BookmarkModelFactory::GetForProfile(test()->verifier());
407 const BookmarkNode
* AddURL(int profile
,
408 const std::wstring
& title
,
410 return AddURL(profile
, GetBookmarkBarNode(profile
), 0, title
, url
);
413 const BookmarkNode
* AddURL(int profile
,
415 const std::wstring
& title
,
417 return AddURL(profile
, GetBookmarkBarNode(profile
), index
, title
, url
);
420 const BookmarkNode
* AddURL(int profile
,
421 const BookmarkNode
* parent
,
423 const std::wstring
& title
,
425 if (GetBookmarkModel(profile
)->GetNodeByID(parent
->id()) != parent
) {
426 LOG(ERROR
) << "Node " << parent
->GetTitle() << " does not belong to "
427 << "Profile " << profile
;
430 const BookmarkNode
* result
= GetBookmarkModel(profile
)->
431 AddURL(parent
, index
, base::WideToUTF16(title
), url
);
433 LOG(ERROR
) << "Could not add bookmark " << title
<< " to Profile "
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
, base::WideToUTF16(title
), url
);
443 LOG(ERROR
) << "Could not add bookmark " << title
<< " to the verifier";
446 EXPECT_TRUE(NodesMatch(v_node
, result
));
451 const BookmarkNode
* AddFolder(int profile
,
452 const std::wstring
& title
) {
453 return AddFolder(profile
, GetBookmarkBarNode(profile
), 0, title
);
456 const BookmarkNode
* AddFolder(int profile
,
458 const std::wstring
& title
) {
459 return AddFolder(profile
, GetBookmarkBarNode(profile
), index
, title
);
462 const BookmarkNode
* AddFolder(int profile
,
463 const BookmarkNode
* parent
,
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
;
471 const BookmarkNode
* result
= GetBookmarkModel(profile
)->AddFolder(
472 parent
, index
, base::WideToUTF16(title
));
475 LOG(ERROR
) << "Could not add folder " << title
<< " to Profile "
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
, base::WideToUTF16(title
));
485 LOG(ERROR
) << "Could not add folder " << title
<< " to the verifier";
488 EXPECT_TRUE(NodesMatch(v_node
, result
));
493 void 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
, base::WideToUTF16(new_title
));
504 GetBookmarkModel(profile
)->SetTitle(node
, base::WideToUTF16(new_title
));
507 void 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
);
525 SetFaviconImpl(test()->GetProfile(profile
), node
, icon_url
, image
,
529 const 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
;
537 if (test()->use_verifier()) {
538 const BookmarkNode
* v_node
= NULL
;
539 FindNodeInVerifier(GetBookmarkModel(profile
), node
, &v_node
);
540 if (v_node
->is_url())
541 GetVerifierBookmarkModel()->SetURL(v_node
, new_url
);
544 GetBookmarkModel(profile
)->SetURL(node
, new_url
);
548 void Move(int profile
,
549 const BookmarkNode
* node
,
550 const BookmarkNode
* new_parent
,
552 ASSERT_EQ(GetBookmarkModel(profile
)->GetNodeByID(node
->id()), node
)
553 << "Node " << node
->GetTitle() << " does not belong to "
554 << "Profile " << profile
;
555 if (test()->use_verifier()) {
556 const BookmarkNode
* v_new_parent
= NULL
;
557 const BookmarkNode
* v_node
= NULL
;
558 FindNodeInVerifier(GetBookmarkModel(profile
), new_parent
, &v_new_parent
);
559 FindNodeInVerifier(GetBookmarkModel(profile
), node
, &v_node
);
560 GetVerifierBookmarkModel()->Move(v_node
, v_new_parent
, index
);
562 GetBookmarkModel(profile
)->Move(node
, new_parent
, index
);
565 void Remove(int profile
,
566 const BookmarkNode
* parent
,
568 ASSERT_EQ(GetBookmarkModel(profile
)->GetNodeByID(parent
->id()), parent
)
569 << "Node " << parent
->GetTitle() << " does not belong to "
570 << "Profile " << profile
;
571 if (test()->use_verifier()) {
572 const BookmarkNode
* v_parent
= NULL
;
573 FindNodeInVerifier(GetBookmarkModel(profile
), parent
, &v_parent
);
574 ASSERT_TRUE(NodesMatch(parent
->GetChild(index
), v_parent
->GetChild(index
)));
575 GetVerifierBookmarkModel()->Remove(v_parent
, index
);
577 GetBookmarkModel(profile
)->Remove(parent
, index
);
580 void RemoveAll(int profile
) {
581 if (test()->use_verifier()) {
582 const BookmarkNode
* root_node
= GetVerifierBookmarkModel()->root_node();
583 for (int i
= 0; i
< root_node
->child_count(); ++i
) {
584 const BookmarkNode
* permanent_node
= root_node
->GetChild(i
);
585 for (int j
= permanent_node
->child_count() - 1; j
>= 0; --j
) {
586 GetVerifierBookmarkModel()->Remove(permanent_node
, j
);
590 GetBookmarkModel(profile
)->RemoveAll();
593 void SortChildren(int profile
, const BookmarkNode
* parent
) {
594 ASSERT_EQ(GetBookmarkModel(profile
)->GetNodeByID(parent
->id()), parent
)
595 << "Node " << parent
->GetTitle() << " does not belong to "
596 << "Profile " << profile
;
597 if (test()->use_verifier()) {
598 const BookmarkNode
* v_parent
= NULL
;
599 FindNodeInVerifier(GetBookmarkModel(profile
), parent
, &v_parent
);
600 GetVerifierBookmarkModel()->SortChildren(v_parent
);
602 GetBookmarkModel(profile
)->SortChildren(parent
);
605 void ReverseChildOrder(int profile
, const BookmarkNode
* parent
) {
606 ASSERT_EQ(GetBookmarkModel(profile
)->GetNodeByID(parent
->id()), parent
)
607 << "Node " << parent
->GetTitle() << " does not belong to "
608 << "Profile " << profile
;
609 int child_count
= parent
->child_count();
610 if (child_count
<= 0)
612 for (int index
= 0; index
< child_count
; ++index
) {
613 Move(profile
, parent
->GetChild(index
), parent
, child_count
- index
);
617 bool ModelMatchesVerifier(int profile
) {
618 if (!test()->use_verifier()) {
619 LOG(ERROR
) << "Illegal to call ModelMatchesVerifier() after "
620 << "DisableVerifier(). Use ModelsMatch() instead.";
623 return BookmarkModelsMatch(GetVerifierBookmarkModel(),
624 GetBookmarkModel(profile
));
627 bool AllModelsMatchVerifier() {
628 // Ensure that all tasks have finished processing on the history thread
629 // and that any notifications the history thread may have sent have been
630 // processed before comparing models.
631 WaitForHistoryToProcessPendingTasks();
633 for (int i
= 0; i
< test()->num_clients(); ++i
) {
634 if (!ModelMatchesVerifier(i
)) {
635 LOG(ERROR
) << "Model " << i
<< " does not match the verifier.";
642 bool ModelsMatch(int profile_a
, int profile_b
) {
643 return BookmarkModelsMatch(GetBookmarkModel(profile_a
),
644 GetBookmarkModel(profile_b
));
647 bool AllModelsMatch() {
648 // Ensure that all tasks have finished processing on the history thread
649 // and that any notifications the history thread may have sent have been
650 // processed before comparing models.
651 WaitForHistoryToProcessPendingTasks();
653 for (int i
= 1; i
< test()->num_clients(); ++i
) {
654 if (!ModelsMatch(0, i
)) {
655 LOG(ERROR
) << "Model " << i
<< " does not match Model 0.";
662 bool ContainsDuplicateBookmarks(int profile
) {
663 ui::TreeNodeIterator
<const BookmarkNode
> iterator(
664 GetBookmarkModel(profile
)->root_node());
665 while (iterator
.has_next()) {
666 const BookmarkNode
* node
= iterator
.Next();
667 if (node
->is_folder())
669 std::vector
<const BookmarkNode
*> nodes
;
670 GetBookmarkModel(profile
)->GetNodesByURL(node
->url(), &nodes
);
671 EXPECT_TRUE(nodes
.size() >= 1);
672 for (std::vector
<const BookmarkNode
*>::const_iterator it
= nodes
.begin();
673 it
!= nodes
.end(); ++it
) {
674 if (node
->id() != (*it
)->id() &&
675 node
->parent() == (*it
)->parent() &&
676 node
->GetTitle() == (*it
)->GetTitle()){
684 bool HasNodeWithURL(int profile
, const GURL
& url
) {
685 std::vector
<const BookmarkNode
*> nodes
;
686 GetBookmarkModel(profile
)->GetNodesByURL(url
, &nodes
);
687 return !nodes
.empty();
690 const BookmarkNode
* GetUniqueNodeByURL(int profile
, const GURL
& url
) {
691 std::vector
<const BookmarkNode
*> nodes
;
692 GetBookmarkModel(profile
)->GetNodesByURL(url
, &nodes
);
693 EXPECT_EQ(1U, nodes
.size());
699 int CountBookmarksWithTitlesMatching(int profile
, const std::wstring
& title
) {
700 return CountNodesWithTitlesMatching(GetBookmarkModel(profile
),
702 base::WideToUTF16(title
));
705 int CountFoldersWithTitlesMatching(int profile
, const std::wstring
& title
) {
706 return CountNodesWithTitlesMatching(GetBookmarkModel(profile
),
707 BookmarkNode::FOLDER
,
708 base::WideToUTF16(title
));
711 gfx::Image
CreateFavicon(SkColor color
) {
712 const int dip_width
= 16;
713 const int dip_height
= 16;
714 std::vector
<ui::ScaleFactor
> favicon_scale_factors
=
715 FaviconUtil::GetFaviconScaleFactors();
716 gfx::ImageSkia favicon
;
717 for (size_t i
= 0; i
< favicon_scale_factors
.size(); ++i
) {
718 float scale
= ui::GetImageScale(favicon_scale_factors
[i
]);
719 int pixel_width
= dip_width
* scale
;
720 int pixel_height
= dip_height
* scale
;
722 bmp
.setConfig(SkBitmap::kARGB_8888_Config
, pixel_width
, pixel_height
);
724 bmp
.eraseColor(color
);
725 favicon
.AddRepresentation(
726 gfx::ImageSkiaRep(bmp
,
727 ui::GetImageScale(favicon_scale_factors
[i
])));
729 return gfx::Image(favicon
);
732 gfx::Image
Create1xFaviconFromPNGFile(const std::string
& path
) {
733 const char* kPNGExtension
= ".png";
734 if (!EndsWith(path
, kPNGExtension
, false))
737 base::FilePath full_path
;
738 if (!PathService::Get(chrome::DIR_TEST_DATA
, &full_path
))
741 full_path
= full_path
.AppendASCII("sync").AppendASCII(path
);
742 std::string contents
;
743 base::ReadFileToString(full_path
, &contents
);
744 return gfx::Image::CreateFrom1xPNGBytes(
745 base::RefCountedString::TakeString(&contents
));
748 std::string
IndexedURL(int i
) {
749 return base::StringPrintf("http://www.host.ext:1234/path/filename/%d", i
);
752 std::wstring
IndexedURLTitle(int i
) {
753 return base::StringPrintf(L
"URL Title %d", i
);
756 std::wstring
IndexedFolderName(int i
) {
757 return base::StringPrintf(L
"Folder Name %d", i
);
760 std::wstring
IndexedSubfolderName(int i
) {
761 return base::StringPrintf(L
"Subfolder Name %d", i
);
764 std::wstring
IndexedSubsubfolderName(int i
) {
765 return base::StringPrintf(L
"Subsubfolder Name %d", i
);
768 } // namespace bookmarks_helper