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/history/history_backend.h"
6
7 #include <algorithm>
8 #include <set>
9 #include <vector>
10
11 #include "base/basictypes.h"
12 #include "base/bind.h"
13 #include "base/command_line.h"
14 #include "base/file_util.h"
15 #include "base/files/file_path.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/path_service.h"
19 #include "base/run_loop.h"
20 #include "base/strings/string16.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "chrome/browser/chrome_notification_types.h"
24 #include "chrome/browser/history/history_notifications.h"
25 #include "chrome/browser/history/history_service.h"
26 #include "chrome/browser/history/history_service_factory.h"
27 #include "chrome/browser/history/in_memory_database.h"
28 #include "chrome/browser/history/in_memory_history_backend.h"
29 #include "chrome/browser/history/visit_filter.h"
30 #include "chrome/common/chrome_constants.h"
31 #include "chrome/common/chrome_paths.h"
32 #include "chrome/common/importer/imported_favicon_usage.h"
33 #include "chrome/test/base/testing_profile.h"
34 #include "components/history/core/test/history_client_fake_bookmarks.h"
35 #include "content/public/browser/notification_details.h"
36 #include "content/public/browser/notification_source.h"
37 #include "content/public/test/test_browser_thread.h"
38 #include "testing/gmock/include/gmock/gmock.h"
39 #include "testing/gtest/include/gtest/gtest.h"
40 #include "url/gurl.h"
41
42 using base::Time;
43
44 // This file only tests functionality where it is most convenient to call the
45 // backend directly. Most of the history backend functions are tested by the
46 // history unit test. Because of the elaborate callbacks involved, this is no
47 // harder than calling it directly for many things.
48
49 namespace {
50
51 const int kTinyEdgeSize = 10;
52 const int kSmallEdgeSize = 16;
53 const int kLargeEdgeSize = 32;
54
55 const gfx::Size kTinySize = gfx::Size(kTinyEdgeSize, kTinyEdgeSize);
56 const gfx::Size kSmallSize = gfx::Size(kSmallEdgeSize, kSmallEdgeSize);
57 const gfx::Size kLargeSize = gfx::Size(kLargeEdgeSize, kLargeEdgeSize);
58
59 // Comparison functions as to make it easier to check results of
60 // GetFaviconBitmaps() and GetIconMappingsForPageURL().
IconMappingLessThan(const history::IconMapping & a,const history::IconMapping & b)61 bool IconMappingLessThan(const history::IconMapping& a,
62 const history::IconMapping& b) {
63 return a.icon_url < b.icon_url;
64 }
65
FaviconBitmapLessThan(const history::FaviconBitmap & a,const history::FaviconBitmap & b)66 bool FaviconBitmapLessThan(const history::FaviconBitmap& a,
67 const history::FaviconBitmap& b) {
68 return a.pixel_size.GetArea() < b.pixel_size.GetArea();
69 }
70
71 class HistoryClientMock : public history::HistoryClientFakeBookmarks {
72 public:
73 MOCK_METHOD0(BlockUntilBookmarksLoaded, void());
74 };
75
76 } // namespace
77
78 namespace history {
79
80 class HistoryBackendTestBase;
81
82 // This must be a separate object since HistoryBackend manages its lifetime.
83 // This just forwards the messages we're interested in to the test object.
84 class HistoryBackendTestDelegate : public HistoryBackend::Delegate {
85 public:
HistoryBackendTestDelegate(HistoryBackendTestBase * test)86 explicit HistoryBackendTestDelegate(HistoryBackendTestBase* test)
87 : test_(test) {}
88
NotifyProfileError(sql::InitStatus init_status)89 virtual void NotifyProfileError(sql::InitStatus init_status) OVERRIDE {}
90 virtual void SetInMemoryBackend(
91 scoped_ptr<InMemoryHistoryBackend> backend) OVERRIDE;
92 virtual void BroadcastNotifications(
93 int type,
94 scoped_ptr<HistoryDetails> details) OVERRIDE;
95 virtual void DBLoaded() OVERRIDE;
NotifyVisitDBObserversOnAddVisit(const BriefVisitInfo & info)96 virtual void NotifyVisitDBObserversOnAddVisit(
97 const BriefVisitInfo& info) OVERRIDE {}
98
99 private:
100 // Not owned by us.
101 HistoryBackendTestBase* test_;
102
103 DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestDelegate);
104 };
105
106 class HistoryBackendCancelableRequest
107 : public CancelableRequestProvider,
108 public CancelableRequestConsumerTSimple<int> {
109 public:
HistoryBackendCancelableRequest()110 HistoryBackendCancelableRequest() {}
111
112 template<class RequestType>
MockScheduleOfRequest(RequestType * request)113 CancelableRequestProvider::Handle MockScheduleOfRequest(
114 RequestType* request) {
115 AddRequest(request, this);
116 return request->handle();
117 }
118 };
119
120 class HistoryBackendTestBase : public testing::Test {
121 public:
122 typedef std::vector<std::pair<int, HistoryDetails*> > NotificationList;
123
HistoryBackendTestBase()124 HistoryBackendTestBase()
125 : loaded_(false),
126 ui_thread_(content::BrowserThread::UI, &message_loop_) {}
127
~HistoryBackendTestBase()128 virtual ~HistoryBackendTestBase() {
129 STLDeleteValues(&broadcasted_notifications_);
130 }
131
132 protected:
num_broadcasted_notifications() const133 int num_broadcasted_notifications() const {
134 return broadcasted_notifications_.size();
135 }
136
broadcasted_notifications() const137 const NotificationList& broadcasted_notifications() const {
138 return broadcasted_notifications_;
139 }
140
ClearBroadcastedNotifications()141 void ClearBroadcastedNotifications() {
142 STLDeleteValues(&broadcasted_notifications_);
143 }
144
test_dir()145 base::FilePath test_dir() {
146 return test_dir_;
147 }
148
BroadcastNotifications(int type,scoped_ptr<HistoryDetails> details)149 void BroadcastNotifications(int type, scoped_ptr<HistoryDetails> details) {
150 // Send the notifications directly to the in-memory database.
151 content::Details<HistoryDetails> det(details.get());
152 mem_backend_->Observe(
153 type, content::Source<HistoryBackendTestBase>(NULL), det);
154
155 // The backend passes ownership of the details pointer to us.
156 broadcasted_notifications_.push_back(
157 std::make_pair(type, details.release()));
158 }
159
160 history::HistoryClientFakeBookmarks history_client_;
161 scoped_refptr<HistoryBackend> backend_; // Will be NULL on init failure.
162 scoped_ptr<InMemoryHistoryBackend> mem_backend_;
163 bool loaded_;
164
165 private:
166 friend class HistoryBackendTestDelegate;
167
168 // testing::Test
SetUp()169 virtual void SetUp() {
170 if (!base::CreateNewTempDirectory(FILE_PATH_LITERAL("BackendTest"),
171 &test_dir_))
172 return;
173 backend_ = new HistoryBackend(
174 test_dir_, new HistoryBackendTestDelegate(this), &history_client_);
175 backend_->Init(std::string(), false);
176 }
177
TearDown()178 virtual void TearDown() {
179 if (backend_.get())
180 backend_->Closing();
181 backend_ = NULL;
182 mem_backend_.reset();
183 base::DeleteFile(test_dir_, true);
184 base::RunLoop().RunUntilIdle();
185 history_client_.ClearAllBookmarks();
186 }
187
SetInMemoryBackend(scoped_ptr<InMemoryHistoryBackend> backend)188 void SetInMemoryBackend(scoped_ptr<InMemoryHistoryBackend> backend) {
189 mem_backend_.swap(backend);
190 }
191
192 // The types and details of notifications which were broadcasted.
193 NotificationList broadcasted_notifications_;
194
195 base::MessageLoop message_loop_;
196 base::FilePath test_dir_;
197 content::TestBrowserThread ui_thread_;
198
199 DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestBase);
200 };
201
SetInMemoryBackend(scoped_ptr<InMemoryHistoryBackend> backend)202 void HistoryBackendTestDelegate::SetInMemoryBackend(
203 scoped_ptr<InMemoryHistoryBackend> backend) {
204 test_->SetInMemoryBackend(backend.Pass());
205 }
206
BroadcastNotifications(int type,scoped_ptr<HistoryDetails> details)207 void HistoryBackendTestDelegate::BroadcastNotifications(
208 int type,
209 scoped_ptr<HistoryDetails> details) {
210 test_->BroadcastNotifications(type, details.Pass());
211 }
212
DBLoaded()213 void HistoryBackendTestDelegate::DBLoaded() {
214 test_->loaded_ = true;
215 }
216
217 class HistoryBackendTest : public HistoryBackendTestBase {
218 public:
HistoryBackendTest()219 HistoryBackendTest() {}
~HistoryBackendTest()220 virtual ~HistoryBackendTest() {}
221
222 // Callback for QueryMostVisited.
OnQueryMostVisited(CancelableRequestProvider::Handle handle,history::MostVisitedURLList data)223 void OnQueryMostVisited(CancelableRequestProvider::Handle handle,
224 history::MostVisitedURLList data) {
225 most_visited_list_.swap(data);
226 }
227
228 // Callback for QueryFiltered.
OnQueryFiltered(CancelableRequestProvider::Handle handle,const history::FilteredURLList & data)229 void OnQueryFiltered(CancelableRequestProvider::Handle handle,
230 const history::FilteredURLList& data) {
231 filtered_list_ = data;
232 }
233
234 protected:
get_most_visited_list() const235 const history::MostVisitedURLList& get_most_visited_list() const {
236 return most_visited_list_;
237 }
238
get_filtered_list() const239 const history::FilteredURLList& get_filtered_list() const {
240 return filtered_list_;
241 }
242
AddRedirectChain(const char * sequence[],int page_id)243 void AddRedirectChain(const char* sequence[], int page_id) {
244 AddRedirectChainWithTransitionAndTime(sequence, page_id,
245 content::PAGE_TRANSITION_LINK,
246 Time::Now());
247 }
248
AddRedirectChainWithTransitionAndTime(const char * sequence[],int page_id,content::PageTransition transition,base::Time time)249 void AddRedirectChainWithTransitionAndTime(
250 const char* sequence[],
251 int page_id,
252 content::PageTransition transition,
253 base::Time time) {
254 history::RedirectList redirects;
255 for (int i = 0; sequence[i] != NULL; ++i)
256 redirects.push_back(GURL(sequence[i]));
257
258 ContextID context_id = reinterpret_cast<ContextID>(1);
259 history::HistoryAddPageArgs request(
260 redirects.back(), time, context_id, page_id, GURL(),
261 redirects, transition, history::SOURCE_BROWSED,
262 true);
263 backend_->AddPage(request);
264 }
265
266 // Adds CLIENT_REDIRECT page transition.
267 // |url1| is the source URL and |url2| is the destination.
268 // |did_replace| is true if the transition is non-user initiated and the
269 // navigation entry for |url2| has replaced that for |url1|. The possibly
270 // updated transition code of the visit records for |url1| and |url2| is
271 // returned by filling in |*transition1| and |*transition2|, respectively.
272 // |time| is a time of the redirect.
AddClientRedirect(const GURL & url1,const GURL & url2,bool did_replace,base::Time time,int * transition1,int * transition2)273 void AddClientRedirect(const GURL& url1, const GURL& url2, bool did_replace,
274 base::Time time,
275 int* transition1, int* transition2) {
276 ContextID dummy_context_id = reinterpret_cast<ContextID>(0x87654321);
277 history::RedirectList redirects;
278 if (url1.is_valid())
279 redirects.push_back(url1);
280 if (url2.is_valid())
281 redirects.push_back(url2);
282 HistoryAddPageArgs request(
283 url2, time, dummy_context_id, 0, url1,
284 redirects, content::PAGE_TRANSITION_CLIENT_REDIRECT,
285 history::SOURCE_BROWSED, did_replace);
286 backend_->AddPage(request);
287
288 *transition1 = GetTransition(url1);
289 *transition2 = GetTransition(url2);
290 }
291
GetTransition(const GURL & url)292 int GetTransition(const GURL& url) {
293 if (!url.is_valid())
294 return 0;
295 URLRow row;
296 URLID id = backend_->db()->GetRowForURL(url, &row);
297 VisitVector visits;
298 EXPECT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
299 return visits[0].transition;
300 }
301
302 // Returns a vector with the small edge size.
GetEdgeSizesSmall()303 const std::vector<int> GetEdgeSizesSmall() {
304 std::vector<int> sizes_small;
305 sizes_small.push_back(kSmallEdgeSize);
306 return sizes_small;
307 }
308
309 // Returns a vector with the large edge size.
GetEdgeSizesLarge()310 const std::vector<int> GetEdgeSizesLarge() {
311 std::vector<int> sizes_large;
312 sizes_large.push_back(kLargeEdgeSize);
313 return sizes_large;
314 }
315
316 // Returns a vector with the small and large edge sizes.
GetEdgeSizesSmallAndLarge()317 const std::vector<int> GetEdgeSizesSmallAndLarge() {
318 std::vector<int> sizes_small_and_large;
319 sizes_small_and_large.push_back(kSmallEdgeSize);
320 sizes_small_and_large.push_back(kLargeEdgeSize);
321 return sizes_small_and_large;
322 }
323
324 // Returns a vector with the tiny, small, and large edge sizes.
GetEdgeSizesTinySmallAndLarge()325 const std::vector<int> GetEdgeSizesTinySmallAndLarge() {
326 std::vector<int> sizes_tiny_small_and_large;
327 sizes_tiny_small_and_large.push_back(kTinyEdgeSize);
328 sizes_tiny_small_and_large.push_back(kSmallEdgeSize);
329 sizes_tiny_small_and_large.push_back(kLargeEdgeSize);
330 return sizes_tiny_small_and_large;
331 }
332
333 // Returns the number of icon mappings of |icon_type| to |page_url|.
NumIconMappingsForPageURL(const GURL & page_url,favicon_base::IconType icon_type)334 size_t NumIconMappingsForPageURL(const GURL& page_url,
335 favicon_base::IconType icon_type) {
336 std::vector<IconMapping> icon_mappings;
337 backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url, icon_type,
338 &icon_mappings);
339 return icon_mappings.size();
340 }
341
342 // Returns the icon mappings for |page_url| sorted alphabetically by icon
343 // URL in ascending order. Returns true if there is at least one icon
344 // mapping.
GetSortedIconMappingsForPageURL(const GURL & page_url,std::vector<IconMapping> * icon_mappings)345 bool GetSortedIconMappingsForPageURL(
346 const GURL& page_url,
347 std::vector<IconMapping>* icon_mappings) {
348 if (!backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
349 icon_mappings)) {
350 return false;
351 }
352 std::sort(icon_mappings->begin(), icon_mappings->end(),
353 IconMappingLessThan);
354 return true;
355 }
356
357 // Returns the favicon bitmaps for |icon_id| sorted by pixel size in
358 // ascending order. Returns true if there is at least one favicon bitmap.
GetSortedFaviconBitmaps(favicon_base::FaviconID icon_id,std::vector<FaviconBitmap> * favicon_bitmaps)359 bool GetSortedFaviconBitmaps(favicon_base::FaviconID icon_id,
360 std::vector<FaviconBitmap>* favicon_bitmaps) {
361 if (!backend_->thumbnail_db_->GetFaviconBitmaps(icon_id, favicon_bitmaps))
362 return false;
363 std::sort(favicon_bitmaps->begin(), favicon_bitmaps->end(),
364 FaviconBitmapLessThan);
365 return true;
366 }
367
368 // Returns true if there is exactly one favicon bitmap associated to
369 // |favicon_id|. If true, returns favicon bitmap in output parameter.
GetOnlyFaviconBitmap(const favicon_base::FaviconID icon_id,FaviconBitmap * favicon_bitmap)370 bool GetOnlyFaviconBitmap(const favicon_base::FaviconID icon_id,
371 FaviconBitmap* favicon_bitmap) {
372 std::vector<FaviconBitmap> favicon_bitmaps;
373 if (!backend_->thumbnail_db_->GetFaviconBitmaps(icon_id, &favicon_bitmaps))
374 return false;
375 if (favicon_bitmaps.size() != 1)
376 return false;
377 *favicon_bitmap = favicon_bitmaps[0];
378 return true;
379 }
380
381 // Generates |favicon_bitmap_data| with entries for the icon_urls and sizes
382 // specified. The bitmap_data for entries are lowercase letters of the
383 // alphabet starting at 'a' for the entry at index 0.
GenerateFaviconBitmapData(const GURL & icon_url1,const std::vector<int> & icon_url1_sizes,std::vector<favicon_base::FaviconRawBitmapData> * favicon_bitmap_data)384 void GenerateFaviconBitmapData(
385 const GURL& icon_url1,
386 const std::vector<int>& icon_url1_sizes,
387 std::vector<favicon_base::FaviconRawBitmapData>* favicon_bitmap_data) {
388 GenerateFaviconBitmapData(icon_url1, icon_url1_sizes, GURL(),
389 std::vector<int>(), favicon_bitmap_data);
390 }
391
GenerateFaviconBitmapData(const GURL & icon_url1,const std::vector<int> & icon_url1_sizes,const GURL & icon_url2,const std::vector<int> & icon_url2_sizes,std::vector<favicon_base::FaviconRawBitmapData> * favicon_bitmap_data)392 void GenerateFaviconBitmapData(
393 const GURL& icon_url1,
394 const std::vector<int>& icon_url1_sizes,
395 const GURL& icon_url2,
396 const std::vector<int>& icon_url2_sizes,
397 std::vector<favicon_base::FaviconRawBitmapData>* favicon_bitmap_data) {
398 favicon_bitmap_data->clear();
399
400 char bitmap_char = 'a';
401 for (size_t i = 0; i < icon_url1_sizes.size(); ++i) {
402 std::vector<unsigned char> data;
403 data.push_back(bitmap_char);
404 favicon_base::FaviconRawBitmapData bitmap_data_element;
405 bitmap_data_element.bitmap_data =
406 base::RefCountedBytes::TakeVector(&data);
407 bitmap_data_element.pixel_size =
408 gfx::Size(icon_url1_sizes[i], icon_url1_sizes[i]);
409 bitmap_data_element.icon_url = icon_url1;
410 favicon_bitmap_data->push_back(bitmap_data_element);
411
412 ++bitmap_char;
413 }
414
415 for (size_t i = 0; i < icon_url2_sizes.size(); ++i) {
416 std::vector<unsigned char> data;
417 data.push_back(bitmap_char);
418 favicon_base::FaviconRawBitmapData bitmap_data_element;
419 bitmap_data_element.bitmap_data =
420 base::RefCountedBytes::TakeVector(&data);
421 bitmap_data_element.pixel_size =
422 gfx::Size(icon_url2_sizes[i], icon_url2_sizes[i]);
423 bitmap_data_element.icon_url = icon_url2;
424 favicon_bitmap_data->push_back(bitmap_data_element);
425
426 ++bitmap_char;
427 }
428 }
429
430 // Returns true if |bitmap_data| is equal to |expected_data|.
BitmapDataEqual(char expected_data,scoped_refptr<base::RefCountedMemory> bitmap_data)431 bool BitmapDataEqual(char expected_data,
432 scoped_refptr<base::RefCountedMemory> bitmap_data) {
433 return bitmap_data.get() &&
434 bitmap_data->size() == 1u &&
435 *bitmap_data->front() == expected_data;
436 }
437
438 private:
439 history::MostVisitedURLList most_visited_list_;
440 history::FilteredURLList filtered_list_;
441
442 DISALLOW_COPY_AND_ASSIGN(HistoryBackendTest);
443 };
444
445 class InMemoryHistoryBackendTest : public HistoryBackendTestBase {
446 public:
InMemoryHistoryBackendTest()447 InMemoryHistoryBackendTest() {}
~InMemoryHistoryBackendTest()448 virtual ~InMemoryHistoryBackendTest() {}
449
450 protected:
SimulateNotification(int type,const URLRow * row1,const URLRow * row2=NULL,const URLRow * row3=NULL)451 void SimulateNotification(int type,
452 const URLRow* row1,
453 const URLRow* row2 = NULL,
454 const URLRow* row3 = NULL) {
455 URLRows rows;
456 rows.push_back(*row1);
457 if (row2) rows.push_back(*row2);
458 if (row3) rows.push_back(*row3);
459
460 if (type == chrome::NOTIFICATION_HISTORY_URLS_MODIFIED) {
461 scoped_ptr<URLsModifiedDetails> details(new URLsModifiedDetails());
462 details->changed_urls.swap(rows);
463 BroadcastNotifications(type, details.PassAs<HistoryDetails>());
464 } else if (type == chrome::NOTIFICATION_HISTORY_URL_VISITED) {
465 for (URLRows::const_iterator it = rows.begin(); it != rows.end(); ++it) {
466 scoped_ptr<URLVisitedDetails> details(new URLVisitedDetails());
467 details->row = *it;
468 BroadcastNotifications(type, details.PassAs<HistoryDetails>());
469 }
470 } else if (type == chrome::NOTIFICATION_HISTORY_URLS_DELETED) {
471 scoped_ptr<URLsDeletedDetails> details(new URLsDeletedDetails());
472 details->rows = rows;
473 BroadcastNotifications(type, details.PassAs<HistoryDetails>());
474 } else {
475 NOTREACHED();
476 }
477 }
478
GetNumberOfMatchingSearchTerms(const int keyword_id,const base::string16 & prefix)479 size_t GetNumberOfMatchingSearchTerms(const int keyword_id,
480 const base::string16& prefix) {
481 std::vector<KeywordSearchTermVisit> matching_terms;
482 mem_backend_->db()->GetMostRecentKeywordSearchTerms(
483 keyword_id, prefix, 1, &matching_terms);
484 return matching_terms.size();
485 }
486
CreateTestTypedURL()487 static URLRow CreateTestTypedURL() {
488 URLRow url_row(GURL("https://www.google.com/"));
489 url_row.set_id(10);
490 url_row.set_title(base::UTF8ToUTF16("Google Search"));
491 url_row.set_typed_count(1);
492 url_row.set_visit_count(1);
493 url_row.set_last_visit(Time::Now() - base::TimeDelta::FromHours(1));
494 return url_row;
495 }
496
CreateAnotherTestTypedURL()497 static URLRow CreateAnotherTestTypedURL() {
498 URLRow url_row(GURL("https://maps.google.com/"));
499 url_row.set_id(20);
500 url_row.set_title(base::UTF8ToUTF16("Google Maps"));
501 url_row.set_typed_count(2);
502 url_row.set_visit_count(3);
503 url_row.set_last_visit(Time::Now() - base::TimeDelta::FromHours(2));
504 return url_row;
505 }
506
CreateTestNonTypedURL()507 static URLRow CreateTestNonTypedURL() {
508 URLRow url_row(GURL("https://news.google.com/"));
509 url_row.set_id(30);
510 url_row.set_title(base::UTF8ToUTF16("Google News"));
511 url_row.set_visit_count(5);
512 url_row.set_last_visit(Time::Now() - base::TimeDelta::FromHours(3));
513 return url_row;
514 }
515
516 void PopulateTestURLsAndSearchTerms(URLRow* row1,
517 URLRow* row2,
518 const base::string16& term1,
519 const base::string16& term2);
520
521 void TestAddingAndChangingURLRows(int notification_type);
522
523 static const TemplateURLID kTestKeywordId;
524 static const char kTestSearchTerm1[];
525 static const char kTestSearchTerm2[];
526
527 private:
528 DISALLOW_COPY_AND_ASSIGN(InMemoryHistoryBackendTest);
529 };
530
531 const TemplateURLID InMemoryHistoryBackendTest::kTestKeywordId = 42;
532 const char InMemoryHistoryBackendTest::kTestSearchTerm1[] = "banana";
533 const char InMemoryHistoryBackendTest::kTestSearchTerm2[] = "orange";
534
535 // http://crbug.com/114287
536 #if defined(OS_WIN)
537 #define MAYBE_Loaded DISABLED_Loaded
538 #else
539 #define MAYBE_Loaded Loaded
540 #endif // defined(OS_WIN)
TEST_F(HistoryBackendTest,MAYBE_Loaded)541 TEST_F(HistoryBackendTest, MAYBE_Loaded) {
542 ASSERT_TRUE(backend_.get());
543 ASSERT_TRUE(loaded_);
544 }
545
TEST_F(HistoryBackendTest,DeleteAll)546 TEST_F(HistoryBackendTest, DeleteAll) {
547 ASSERT_TRUE(backend_.get());
548
549 // Add two favicons, each with two bitmaps. Note that we add favicon2 before
550 // adding favicon1. This is so that favicon1 one gets ID 2 autoassigned to
551 // the database, which will change when the other one is deleted. This way
552 // we can test that updating works properly.
553 GURL favicon_url1("http://www.google.com/favicon.ico");
554 GURL favicon_url2("http://news.google.com/favicon.ico");
555 favicon_base::FaviconID favicon2 =
556 backend_->thumbnail_db_->AddFavicon(favicon_url2, favicon_base::FAVICON);
557 favicon_base::FaviconID favicon1 =
558 backend_->thumbnail_db_->AddFavicon(favicon_url1, favicon_base::FAVICON);
559
560 std::vector<unsigned char> data;
561 data.push_back('a');
562 EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon1,
563 new base::RefCountedBytes(data), Time::Now(), kSmallSize));
564 data[0] = 'b';
565 EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon1,
566 new base::RefCountedBytes(data), Time::Now(), kLargeSize));
567
568 data[0] = 'c';
569 EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon2,
570 new base::RefCountedBytes(data), Time::Now(), kSmallSize));
571 data[0] = 'd';
572 EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon2,
573 new base::RefCountedBytes(data), Time::Now(), kLargeSize));
574
575 // First visit two URLs.
576 URLRow row1(GURL("http://www.google.com/"));
577 row1.set_visit_count(2);
578 row1.set_typed_count(1);
579 row1.set_last_visit(Time::Now());
580 backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1);
581
582 URLRow row2(GURL("http://news.google.com/"));
583 row2.set_visit_count(1);
584 row2.set_last_visit(Time::Now());
585 backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2);
586
587 URLRows rows;
588 rows.push_back(row2); // Reversed order for the same reason as favicons.
589 rows.push_back(row1);
590 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
591
592 URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL);
593 URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL);
594
595 // Get the two visits for the URLs we just added.
596 VisitVector visits;
597 backend_->db_->GetVisitsForURL(row1_id, &visits);
598 ASSERT_EQ(1U, visits.size());
599
600 visits.clear();
601 backend_->db_->GetVisitsForURL(row2_id, &visits);
602 ASSERT_EQ(1U, visits.size());
603
604 // The in-memory backend should have been set and it should have gotten the
605 // typed URL.
606 ASSERT_TRUE(mem_backend_.get());
607 URLRow outrow1;
608 EXPECT_TRUE(mem_backend_->db_->GetRowForURL(row1.url(), NULL));
609
610 // Star row1.
611 history_client_.AddBookmark(row1.url());
612
613 // Now finally clear all history.
614 ClearBroadcastedNotifications();
615 backend_->DeleteAllHistory();
616
617 // The first URL should be preserved but the time should be cleared.
618 EXPECT_TRUE(backend_->db_->GetRowForURL(row1.url(), &outrow1));
619 EXPECT_EQ(row1.url(), outrow1.url());
620 EXPECT_EQ(0, outrow1.visit_count());
621 EXPECT_EQ(0, outrow1.typed_count());
622 EXPECT_TRUE(Time() == outrow1.last_visit());
623
624 // The second row should be deleted.
625 URLRow outrow2;
626 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &outrow2));
627
628 // All visits should be deleted for both URLs.
629 VisitVector all_visits;
630 backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
631 ASSERT_EQ(0U, all_visits.size());
632
633 // We should have a favicon and favicon bitmaps for the first URL only. We
634 // look them up by favicon URL since the IDs may have changed.
635 favicon_base::FaviconID out_favicon1 =
636 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
637 favicon_url1, favicon_base::FAVICON, NULL);
638 EXPECT_TRUE(out_favicon1);
639
640 std::vector<FaviconBitmap> favicon_bitmaps;
641 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
642 out_favicon1, &favicon_bitmaps));
643 ASSERT_EQ(2u, favicon_bitmaps.size());
644
645 FaviconBitmap favicon_bitmap1 = favicon_bitmaps[0];
646 FaviconBitmap favicon_bitmap2 = favicon_bitmaps[1];
647
648 // Favicon bitmaps do not need to be in particular order.
649 if (favicon_bitmap1.pixel_size == kLargeSize) {
650 FaviconBitmap tmp_favicon_bitmap = favicon_bitmap1;
651 favicon_bitmap1 = favicon_bitmap2;
652 favicon_bitmap2 = tmp_favicon_bitmap;
653 }
654
655 EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap1.bitmap_data));
656 EXPECT_EQ(kSmallSize, favicon_bitmap1.pixel_size);
657
658 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap2.bitmap_data));
659 EXPECT_EQ(kLargeSize, favicon_bitmap2.pixel_size);
660
661 favicon_base::FaviconID out_favicon2 =
662 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
663 favicon_url2, favicon_base::FAVICON, NULL);
664 EXPECT_FALSE(out_favicon2) << "Favicon not deleted";
665
666 // The remaining URL should still reference the same favicon, even if its
667 // ID has changed.
668 std::vector<IconMapping> mappings;
669 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
670 outrow1.url(), favicon_base::FAVICON, &mappings));
671 EXPECT_EQ(1u, mappings.size());
672 EXPECT_EQ(out_favicon1, mappings[0].icon_id);
673
674 // The first URL should still be bookmarked.
675 EXPECT_TRUE(history_client_.IsBookmarked(row1.url()));
676
677 // Check that we fire the notification about all history having been deleted.
678 ASSERT_EQ(1u, broadcasted_notifications().size());
679 ASSERT_EQ(chrome::NOTIFICATION_HISTORY_URLS_DELETED,
680 broadcasted_notifications()[0].first);
681 const URLsDeletedDetails* details = static_cast<const URLsDeletedDetails*>(
682 broadcasted_notifications()[0].second);
683 EXPECT_TRUE(details->all_history);
684 EXPECT_FALSE(details->expired);
685 }
686
687 // Checks that adding a visit, then calling DeleteAll, and then trying to add
688 // data for the visited page works. This can happen when clearing the history
689 // immediately after visiting a page.
TEST_F(HistoryBackendTest,DeleteAllThenAddData)690 TEST_F(HistoryBackendTest, DeleteAllThenAddData) {
691 ASSERT_TRUE(backend_.get());
692
693 Time visit_time = Time::Now();
694 GURL url("http://www.google.com/");
695 HistoryAddPageArgs request(url, visit_time, NULL, 0, GURL(),
696 history::RedirectList(),
697 content::PAGE_TRANSITION_KEYWORD_GENERATED,
698 history::SOURCE_BROWSED, false);
699 backend_->AddPage(request);
700
701 // Check that a row was added.
702 URLRow outrow;
703 EXPECT_TRUE(backend_->db_->GetRowForURL(url, &outrow));
704
705 // Check that the visit was added.
706 VisitVector all_visits;
707 backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
708 ASSERT_EQ(1U, all_visits.size());
709
710 // Clear all history.
711 backend_->DeleteAllHistory();
712
713 // The row should be deleted.
714 EXPECT_FALSE(backend_->db_->GetRowForURL(url, &outrow));
715
716 // The visit should be deleted.
717 backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
718 ASSERT_EQ(0U, all_visits.size());
719
720 // Try and set the title.
721 backend_->SetPageTitle(url, base::UTF8ToUTF16("Title"));
722
723 // The row should still be deleted.
724 EXPECT_FALSE(backend_->db_->GetRowForURL(url, &outrow));
725
726 // The visit should still be deleted.
727 backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
728 ASSERT_EQ(0U, all_visits.size());
729 }
730
TEST_F(HistoryBackendTest,URLsNoLongerBookmarked)731 TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) {
732 GURL favicon_url1("http://www.google.com/favicon.ico");
733 GURL favicon_url2("http://news.google.com/favicon.ico");
734
735 std::vector<unsigned char> data;
736 data.push_back('1');
737 favicon_base::FaviconID favicon1 =
738 backend_->thumbnail_db_->AddFavicon(favicon_url1,
739 favicon_base::FAVICON,
740 new base::RefCountedBytes(data),
741 Time::Now(),
742 gfx::Size());
743
744 data[0] = '2';
745 favicon_base::FaviconID favicon2 =
746 backend_->thumbnail_db_->AddFavicon(favicon_url2,
747 favicon_base::FAVICON,
748 new base::RefCountedBytes(data),
749 Time::Now(),
750 gfx::Size());
751
752 // First visit two URLs.
753 URLRow row1(GURL("http://www.google.com/"));
754 row1.set_visit_count(2);
755 row1.set_typed_count(1);
756 row1.set_last_visit(Time::Now());
757 EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
758
759 URLRow row2(GURL("http://news.google.com/"));
760 row2.set_visit_count(1);
761 row2.set_last_visit(Time::Now());
762 EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2));
763
764 URLRows rows;
765 rows.push_back(row2); // Reversed order for the same reason as favicons.
766 rows.push_back(row1);
767 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
768
769 URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL);
770 URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL);
771
772 // Star the two URLs.
773 history_client_.AddBookmark(row1.url());
774 history_client_.AddBookmark(row2.url());
775
776 // Delete url 2. Because url 2 is starred this won't delete the URL, only
777 // the visits.
778 backend_->expirer_.DeleteURL(row2.url());
779
780 // Make sure url 2 is still valid, but has no visits.
781 URLRow tmp_url_row;
782 EXPECT_EQ(row2_id, backend_->db_->GetRowForURL(row2.url(), NULL));
783 VisitVector visits;
784 backend_->db_->GetVisitsForURL(row2_id, &visits);
785 EXPECT_EQ(0U, visits.size());
786 // The favicon should still be valid.
787 EXPECT_EQ(favicon2,
788 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
789 favicon_url2, favicon_base::FAVICON, NULL));
790
791 // Unstar row2.
792 history_client_.DelBookmark(row2.url());
793
794 // Tell the backend it was unstarred. We have to explicitly do this as
795 // BookmarkModel isn't wired up to the backend during testing.
796 std::set<GURL> unstarred_urls;
797 unstarred_urls.insert(row2.url());
798 backend_->URLsNoLongerBookmarked(unstarred_urls);
799
800 // The URL should no longer exist.
801 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &tmp_url_row));
802 // And the favicon should be deleted.
803 EXPECT_EQ(0,
804 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
805 favicon_url2, favicon_base::FAVICON, NULL));
806
807 // Unstar row 1.
808 history_client_.DelBookmark(row1.url());
809
810 // Tell the backend it was unstarred. We have to explicitly do this as
811 // BookmarkModel isn't wired up to the backend during testing.
812 unstarred_urls.clear();
813 unstarred_urls.insert(row1.url());
814 backend_->URLsNoLongerBookmarked(unstarred_urls);
815
816 // The URL should still exist (because there were visits).
817 EXPECT_EQ(row1_id, backend_->db_->GetRowForURL(row1.url(), NULL));
818
819 // There should still be visits.
820 visits.clear();
821 backend_->db_->GetVisitsForURL(row1_id, &visits);
822 EXPECT_EQ(1U, visits.size());
823
824 // The favicon should still be valid.
825 EXPECT_EQ(favicon1,
826 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
827 favicon_url1, favicon_base::FAVICON, NULL));
828 }
829
830 // Tests a handful of assertions for a navigation with a type of
831 // KEYWORD_GENERATED.
TEST_F(HistoryBackendTest,KeywordGenerated)832 TEST_F(HistoryBackendTest, KeywordGenerated) {
833 ASSERT_TRUE(backend_.get());
834
835 GURL url("http://google.com");
836
837 Time visit_time = Time::Now() - base::TimeDelta::FromDays(1);
838 HistoryAddPageArgs request(url, visit_time, NULL, 0, GURL(),
839 history::RedirectList(),
840 content::PAGE_TRANSITION_KEYWORD_GENERATED,
841 history::SOURCE_BROWSED, false);
842 backend_->AddPage(request);
843
844 // A row should have been added for the url.
845 URLRow row;
846 URLID url_id = backend_->db()->GetRowForURL(url, &row);
847 ASSERT_NE(0, url_id);
848
849 // The typed count should be 1.
850 ASSERT_EQ(1, row.typed_count());
851
852 // KEYWORD_GENERATED urls should not be added to the segment db.
853 std::string segment_name = VisitSegmentDatabase::ComputeSegmentName(url);
854 EXPECT_EQ(0, backend_->db()->GetSegmentNamed(segment_name));
855
856 // One visit should be added.
857 VisitVector visits;
858 EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits));
859 EXPECT_EQ(1U, visits.size());
860
861 // But no visible visits.
862 visits.clear();
863 QueryOptions query_options;
864 query_options.max_count = 1;
865 backend_->db()->GetVisibleVisitsInRange(query_options, &visits);
866 EXPECT_TRUE(visits.empty());
867
868 // Expire the visits.
869 std::set<GURL> restrict_urls;
870 backend_->expire_backend()->ExpireHistoryBetween(restrict_urls,
871 visit_time, Time::Now());
872
873 // The visit should have been nuked.
874 visits.clear();
875 EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits));
876 EXPECT_TRUE(visits.empty());
877
878 // As well as the url.
879 ASSERT_EQ(0, backend_->db()->GetRowForURL(url, &row));
880 }
881
TEST_F(HistoryBackendTest,ClientRedirect)882 TEST_F(HistoryBackendTest, ClientRedirect) {
883 ASSERT_TRUE(backend_.get());
884
885 int transition1;
886 int transition2;
887
888 // Initial transition to page A.
889 GURL url_a("http://google.com/a");
890 AddClientRedirect(GURL(), url_a, false, base::Time(),
891 &transition1, &transition2);
892 EXPECT_TRUE(transition2 & content::PAGE_TRANSITION_CHAIN_END);
893
894 // User initiated redirect to page B.
895 GURL url_b("http://google.com/b");
896 AddClientRedirect(url_a, url_b, false, base::Time(),
897 &transition1, &transition2);
898 EXPECT_TRUE(transition1 & content::PAGE_TRANSITION_CHAIN_END);
899 EXPECT_TRUE(transition2 & content::PAGE_TRANSITION_CHAIN_END);
900
901 // Non-user initiated redirect to page C.
902 GURL url_c("http://google.com/c");
903 AddClientRedirect(url_b, url_c, true, base::Time(),
904 &transition1, &transition2);
905 EXPECT_FALSE(transition1 & content::PAGE_TRANSITION_CHAIN_END);
906 EXPECT_TRUE(transition2 & content::PAGE_TRANSITION_CHAIN_END);
907 }
908
TEST_F(HistoryBackendTest,AddPagesWithDetails)909 TEST_F(HistoryBackendTest, AddPagesWithDetails) {
910 ASSERT_TRUE(backend_.get());
911
912 // Import one non-typed URL, and two recent and one expired typed URLs.
913 URLRow row1(GURL("https://news.google.com/"));
914 row1.set_visit_count(1);
915 row1.set_last_visit(Time::Now());
916 URLRow row2(GURL("https://www.google.com/"));
917 row2.set_typed_count(1);
918 row2.set_last_visit(Time::Now());
919 URLRow row3(GURL("https://mail.google.com/"));
920 row3.set_visit_count(1);
921 row3.set_typed_count(1);
922 row3.set_last_visit(Time::Now() - base::TimeDelta::FromDays(7 - 1));
923 URLRow row4(GURL("https://maps.google.com/"));
924 row4.set_visit_count(1);
925 row4.set_typed_count(1);
926 row4.set_last_visit(Time::Now() - base::TimeDelta::FromDays(365 + 2));
927
928 URLRows rows;
929 rows.push_back(row1);
930 rows.push_back(row2);
931 rows.push_back(row3);
932 rows.push_back(row4);
933 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
934
935 // Verify that recent URLs have ended up in the main |db_|, while the already
936 // expired URL has been ignored.
937 URLRow stored_row1, stored_row2, stored_row3, stored_row4;
938 EXPECT_NE(0, backend_->db_->GetRowForURL(row1.url(), &stored_row1));
939 EXPECT_NE(0, backend_->db_->GetRowForURL(row2.url(), &stored_row2));
940 EXPECT_NE(0, backend_->db_->GetRowForURL(row3.url(), &stored_row3));
941 EXPECT_EQ(0, backend_->db_->GetRowForURL(row4.url(), &stored_row4));
942
943 // Ensure that a notification was fired for both typed and non-typed URLs.
944 // Further verify that the IDs in the notification are set to those that are
945 // in effect in the main database. The InMemoryHistoryBackend relies on this
946 // for caching.
947 ASSERT_EQ(1u, broadcasted_notifications().size());
948 ASSERT_EQ(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
949 broadcasted_notifications()[0].first);
950 const URLsModifiedDetails* details = static_cast<const URLsModifiedDetails*>(
951 broadcasted_notifications()[0].second);
952 EXPECT_EQ(3u, details->changed_urls.size());
953
954 URLRows::const_iterator it_row1 = std::find_if(
955 details->changed_urls.begin(),
956 details->changed_urls.end(),
957 history::URLRow::URLRowHasURL(row1.url()));
958 ASSERT_NE(details->changed_urls.end(), it_row1);
959 EXPECT_EQ(stored_row1.id(), it_row1->id());
960
961 URLRows::const_iterator it_row2 = std::find_if(
962 details->changed_urls.begin(),
963 details->changed_urls.end(),
964 history::URLRow::URLRowHasURL(row2.url()));
965 ASSERT_NE(details->changed_urls.end(), it_row2);
966 EXPECT_EQ(stored_row2.id(), it_row2->id());
967
968 URLRows::const_iterator it_row3 = std::find_if(
969 details->changed_urls.begin(),
970 details->changed_urls.end(),
971 history::URLRow::URLRowHasURL(row3.url()));
972 ASSERT_NE(details->changed_urls.end(), it_row3);
973 EXPECT_EQ(stored_row3.id(), it_row3->id());
974 }
975
976 // This verifies that a notification is fired. In-depth testing of logic should
977 // be done in HistoryTest.SetTitle.
TEST_F(HistoryBackendTest,SetPageTitleFiresNotificationWithCorrectDetails)978 TEST_F(HistoryBackendTest, SetPageTitleFiresNotificationWithCorrectDetails) {
979 const char kTestUrlTitle[] = "Google Search";
980
981 ASSERT_TRUE(backend_.get());
982
983 // Add two pages, then change the title of the second one.
984 URLRow row1(GURL("https://news.google.com/"));
985 row1.set_typed_count(1);
986 row1.set_last_visit(Time::Now());
987 URLRow row2(GURL("https://www.google.com/"));
988 row2.set_visit_count(2);
989 row2.set_last_visit(Time::Now());
990
991 URLRows rows;
992 rows.push_back(row1);
993 rows.push_back(row2);
994 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
995
996 ClearBroadcastedNotifications();
997 backend_->SetPageTitle(row2.url(), base::UTF8ToUTF16(kTestUrlTitle));
998
999 // Ensure that a notification was fired, and further verify that the IDs in
1000 // the notification are set to those that are in effect in the main database.
1001 // The InMemoryHistoryBackend relies on this for caching.
1002 URLRow stored_row2;
1003 EXPECT_TRUE(backend_->GetURL(row2.url(), &stored_row2));
1004 ASSERT_EQ(1u, broadcasted_notifications().size());
1005 ASSERT_EQ(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
1006 broadcasted_notifications()[0].first);
1007 const URLsModifiedDetails* details = static_cast<const URLsModifiedDetails*>(
1008 broadcasted_notifications()[0].second);
1009 ASSERT_EQ(1u, details->changed_urls.size());
1010 EXPECT_EQ(base::UTF8ToUTF16(kTestUrlTitle), details->changed_urls[0].title());
1011 EXPECT_EQ(stored_row2.id(), details->changed_urls[0].id());
1012 }
1013
TEST_F(HistoryBackendTest,ImportedFaviconsTest)1014 TEST_F(HistoryBackendTest, ImportedFaviconsTest) {
1015 // Setup test data - two Urls in the history, one with favicon assigned and
1016 // one without.
1017 GURL favicon_url1("http://www.google.com/favicon.ico");
1018 std::vector<unsigned char> data;
1019 data.push_back('1');
1020 favicon_base::FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(
1021 favicon_url1,
1022 favicon_base::FAVICON,
1023 base::RefCountedBytes::TakeVector(&data),
1024 Time::Now(),
1025 gfx::Size());
1026 URLRow row1(GURL("http://www.google.com/"));
1027 row1.set_visit_count(1);
1028 row1.set_last_visit(Time::Now());
1029 EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
1030
1031 URLRow row2(GURL("http://news.google.com/"));
1032 row2.set_visit_count(1);
1033 row2.set_last_visit(Time::Now());
1034 URLRows rows;
1035 rows.push_back(row1);
1036 rows.push_back(row2);
1037 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
1038 URLRow url_row1, url_row2;
1039 EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0);
1040 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0);
1041 EXPECT_EQ(1u, NumIconMappingsForPageURL(row1.url(), favicon_base::FAVICON));
1042 EXPECT_EQ(0u, NumIconMappingsForPageURL(row2.url(), favicon_base::FAVICON));
1043
1044 // Now provide one imported favicon for both URLs already in the registry.
1045 // The new favicon should only be used with the URL that doesn't already have
1046 // a favicon.
1047 std::vector<ImportedFaviconUsage> favicons;
1048 ImportedFaviconUsage favicon;
1049 favicon.favicon_url = GURL("http://news.google.com/favicon.ico");
1050 favicon.png_data.push_back('2');
1051 favicon.urls.insert(row1.url());
1052 favicon.urls.insert(row2.url());
1053 favicons.push_back(favicon);
1054 backend_->SetImportedFavicons(favicons);
1055 EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0);
1056 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0);
1057
1058 std::vector<IconMapping> mappings;
1059 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1060 row1.url(), favicon_base::FAVICON, &mappings));
1061 EXPECT_EQ(1u, mappings.size());
1062 EXPECT_EQ(favicon1, mappings[0].icon_id);
1063 EXPECT_EQ(favicon_url1, mappings[0].icon_url);
1064
1065 mappings.clear();
1066 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1067 row2.url(), favicon_base::FAVICON, &mappings));
1068 EXPECT_EQ(1u, mappings.size());
1069 EXPECT_EQ(favicon.favicon_url, mappings[0].icon_url);
1070
1071 // A URL should not be added to history (to store favicon), if
1072 // the URL is not bookmarked.
1073 GURL url3("http://mail.google.com");
1074 favicons.clear();
1075 favicon.favicon_url = GURL("http://mail.google.com/favicon.ico");
1076 favicon.png_data.push_back('3');
1077 favicon.urls.insert(url3);
1078 favicons.push_back(favicon);
1079 backend_->SetImportedFavicons(favicons);
1080 URLRow url_row3;
1081 EXPECT_TRUE(backend_->db_->GetRowForURL(url3, &url_row3) == 0);
1082
1083 // If the URL is bookmarked, it should get added to history with 0 visits.
1084 history_client_.AddBookmark(url3);
1085 backend_->SetImportedFavicons(favicons);
1086 EXPECT_FALSE(backend_->db_->GetRowForURL(url3, &url_row3) == 0);
1087 EXPECT_TRUE(url_row3.visit_count() == 0);
1088 }
1089
TEST_F(HistoryBackendTest,StripUsernamePasswordTest)1090 TEST_F(HistoryBackendTest, StripUsernamePasswordTest) {
1091 ASSERT_TRUE(backend_.get());
1092
1093 GURL url("http://anyuser:anypass@www.google.com");
1094 GURL stripped_url("http://www.google.com");
1095
1096 // Clear all history.
1097 backend_->DeleteAllHistory();
1098
1099 // Visit the url with username, password.
1100 backend_->AddPageVisit(url, base::Time::Now(), 0,
1101 content::PageTransitionFromInt(
1102 content::PageTransitionGetQualifier(content::PAGE_TRANSITION_TYPED)),
1103 history::SOURCE_BROWSED);
1104
1105 // Fetch the row information about stripped url from history db.
1106 VisitVector visits;
1107 URLID row_id = backend_->db_->GetRowForURL(stripped_url, NULL);
1108 backend_->db_->GetVisitsForURL(row_id, &visits);
1109
1110 // Check if stripped url is stored in database.
1111 ASSERT_EQ(1U, visits.size());
1112 }
1113
TEST_F(HistoryBackendTest,AddPageVisitSource)1114 TEST_F(HistoryBackendTest, AddPageVisitSource) {
1115 ASSERT_TRUE(backend_.get());
1116
1117 GURL url("http://www.google.com");
1118
1119 // Clear all history.
1120 backend_->DeleteAllHistory();
1121
1122 // Assume visiting the url from an externsion.
1123 backend_->AddPageVisit(
1124 url, base::Time::Now(), 0, content::PAGE_TRANSITION_TYPED,
1125 history::SOURCE_EXTENSION);
1126 // Assume the url is imported from Firefox.
1127 backend_->AddPageVisit(url, base::Time::Now(), 0,
1128 content::PAGE_TRANSITION_TYPED,
1129 history::SOURCE_FIREFOX_IMPORTED);
1130 // Assume this url is also synced.
1131 backend_->AddPageVisit(url, base::Time::Now(), 0,
1132 content::PAGE_TRANSITION_TYPED,
1133 history::SOURCE_SYNCED);
1134
1135 // Fetch the row information about the url from history db.
1136 VisitVector visits;
1137 URLID row_id = backend_->db_->GetRowForURL(url, NULL);
1138 backend_->db_->GetVisitsForURL(row_id, &visits);
1139
1140 // Check if all the visits to the url are stored in database.
1141 ASSERT_EQ(3U, visits.size());
1142 VisitSourceMap visit_sources;
1143 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
1144 ASSERT_EQ(3U, visit_sources.size());
1145 int sources = 0;
1146 for (int i = 0; i < 3; i++) {
1147 switch (visit_sources[visits[i].visit_id]) {
1148 case history::SOURCE_EXTENSION:
1149 sources |= 0x1;
1150 break;
1151 case history::SOURCE_FIREFOX_IMPORTED:
1152 sources |= 0x2;
1153 break;
1154 case history::SOURCE_SYNCED:
1155 sources |= 0x4;
1156 default:
1157 break;
1158 }
1159 }
1160 EXPECT_EQ(0x7, sources);
1161 }
1162
TEST_F(HistoryBackendTest,AddPageVisitNotLastVisit)1163 TEST_F(HistoryBackendTest, AddPageVisitNotLastVisit) {
1164 ASSERT_TRUE(backend_.get());
1165
1166 GURL url("http://www.google.com");
1167
1168 // Clear all history.
1169 backend_->DeleteAllHistory();
1170
1171 // Create visit times
1172 base::Time recent_time = base::Time::Now();
1173 base::TimeDelta visit_age = base::TimeDelta::FromDays(3);
1174 base::Time older_time = recent_time - visit_age;
1175
1176 // Visit the url with recent time.
1177 backend_->AddPageVisit(url, recent_time, 0,
1178 content::PageTransitionFromInt(
1179 content::PageTransitionGetQualifier(content::PAGE_TRANSITION_TYPED)),
1180 history::SOURCE_BROWSED);
1181
1182 // Add to the url a visit with older time (could be syncing from another
1183 // client, etc.).
1184 backend_->AddPageVisit(url, older_time, 0,
1185 content::PageTransitionFromInt(
1186 content::PageTransitionGetQualifier(content::PAGE_TRANSITION_TYPED)),
1187 history::SOURCE_SYNCED);
1188
1189 // Fetch the row information about url from history db.
1190 VisitVector visits;
1191 URLRow row;
1192 URLID row_id = backend_->db_->GetRowForURL(url, &row);
1193 backend_->db_->GetVisitsForURL(row_id, &visits);
1194
1195 // Last visit time should be the most recent time, not the most recently added
1196 // visit.
1197 ASSERT_EQ(2U, visits.size());
1198 ASSERT_EQ(recent_time, row.last_visit());
1199 }
1200
TEST_F(HistoryBackendTest,AddPageVisitFiresNotificationWithCorrectDetails)1201 TEST_F(HistoryBackendTest, AddPageVisitFiresNotificationWithCorrectDetails) {
1202 ASSERT_TRUE(backend_.get());
1203
1204 GURL url1("http://www.google.com");
1205 GURL url2("http://maps.google.com");
1206
1207 // Clear all history.
1208 backend_->DeleteAllHistory();
1209 ClearBroadcastedNotifications();
1210
1211 // Visit two distinct URLs, the second one twice.
1212 backend_->AddPageVisit(url1, base::Time::Now(), 0,
1213 content::PAGE_TRANSITION_LINK,
1214 history::SOURCE_BROWSED);
1215 for (int i = 0; i < 2; ++i) {
1216 backend_->AddPageVisit(url2, base::Time::Now(), 0,
1217 content::PAGE_TRANSITION_TYPED,
1218 history::SOURCE_BROWSED);
1219 }
1220
1221 URLRow stored_row1, stored_row2;
1222 EXPECT_NE(0, backend_->db_->GetRowForURL(url1, &stored_row1));
1223 EXPECT_NE(0, backend_->db_->GetRowForURL(url2, &stored_row2));
1224
1225 // Expect that NOTIFICATION_HISTORY_URLS_VISITED has been fired 3x, and that
1226 // each time, the URLRows have the correct URLs and IDs set.
1227 ASSERT_EQ(3, num_broadcasted_notifications());
1228 ASSERT_EQ(chrome::NOTIFICATION_HISTORY_URL_VISITED,
1229 broadcasted_notifications()[0].first);
1230 const URLVisitedDetails* details = static_cast<const URLVisitedDetails*>(
1231 broadcasted_notifications()[0].second);
1232 EXPECT_EQ(content::PAGE_TRANSITION_LINK,
1233 content::PageTransitionStripQualifier(details->transition));
1234 EXPECT_EQ(stored_row1.id(), details->row.id());
1235 EXPECT_EQ(stored_row1.url(), details->row.url());
1236
1237 // No further checking, this case analogous to the first one.
1238 ASSERT_EQ(chrome::NOTIFICATION_HISTORY_URL_VISITED,
1239 broadcasted_notifications()[1].first);
1240
1241 ASSERT_EQ(chrome::NOTIFICATION_HISTORY_URL_VISITED,
1242 broadcasted_notifications()[2].first);
1243 details = static_cast<const URLVisitedDetails*>(
1244 broadcasted_notifications()[2].second);
1245 EXPECT_EQ(content::PAGE_TRANSITION_TYPED,
1246 content::PageTransitionStripQualifier(details->transition));
1247 EXPECT_EQ(stored_row2.id(), details->row.id());
1248 EXPECT_EQ(stored_row2.url(), details->row.url());
1249 }
1250
TEST_F(HistoryBackendTest,AddPageArgsSource)1251 TEST_F(HistoryBackendTest, AddPageArgsSource) {
1252 ASSERT_TRUE(backend_.get());
1253
1254 GURL url("http://testpageargs.com");
1255
1256 // Assume this page is browsed by user.
1257 HistoryAddPageArgs request1(url, base::Time::Now(), NULL, 0, GURL(),
1258 history::RedirectList(),
1259 content::PAGE_TRANSITION_KEYWORD_GENERATED,
1260 history::SOURCE_BROWSED, false);
1261 backend_->AddPage(request1);
1262 // Assume this page is synced.
1263 HistoryAddPageArgs request2(url, base::Time::Now(), NULL, 0, GURL(),
1264 history::RedirectList(),
1265 content::PAGE_TRANSITION_LINK,
1266 history::SOURCE_SYNCED, false);
1267 backend_->AddPage(request2);
1268 // Assume this page is browsed again.
1269 HistoryAddPageArgs request3(url, base::Time::Now(), NULL, 0, GURL(),
1270 history::RedirectList(),
1271 content::PAGE_TRANSITION_TYPED,
1272 history::SOURCE_BROWSED, false);
1273 backend_->AddPage(request3);
1274
1275 // Three visits should be added with proper sources.
1276 VisitVector visits;
1277 URLRow row;
1278 URLID id = backend_->db()->GetRowForURL(url, &row);
1279 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1280 ASSERT_EQ(3U, visits.size());
1281 VisitSourceMap visit_sources;
1282 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
1283 ASSERT_EQ(1U, visit_sources.size());
1284 EXPECT_EQ(history::SOURCE_SYNCED, visit_sources.begin()->second);
1285 }
1286
TEST_F(HistoryBackendTest,AddVisitsSource)1287 TEST_F(HistoryBackendTest, AddVisitsSource) {
1288 ASSERT_TRUE(backend_.get());
1289
1290 GURL url1("http://www.cnn.com");
1291 std::vector<VisitInfo> visits1, visits2;
1292 visits1.push_back(VisitInfo(
1293 Time::Now() - base::TimeDelta::FromDays(5),
1294 content::PAGE_TRANSITION_LINK));
1295 visits1.push_back(VisitInfo(
1296 Time::Now() - base::TimeDelta::FromDays(1),
1297 content::PAGE_TRANSITION_LINK));
1298 visits1.push_back(VisitInfo(
1299 Time::Now(), content::PAGE_TRANSITION_LINK));
1300
1301 GURL url2("http://www.example.com");
1302 visits2.push_back(VisitInfo(
1303 Time::Now() - base::TimeDelta::FromDays(10),
1304 content::PAGE_TRANSITION_LINK));
1305 visits2.push_back(VisitInfo(Time::Now(), content::PAGE_TRANSITION_LINK));
1306
1307 // Clear all history.
1308 backend_->DeleteAllHistory();
1309
1310 // Add the visits.
1311 backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
1312 backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED);
1313
1314 // Verify the visits were added with their sources.
1315 VisitVector visits;
1316 URLRow row;
1317 URLID id = backend_->db()->GetRowForURL(url1, &row);
1318 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1319 ASSERT_EQ(3U, visits.size());
1320 VisitSourceMap visit_sources;
1321 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
1322 ASSERT_EQ(3U, visit_sources.size());
1323 for (int i = 0; i < 3; i++)
1324 EXPECT_EQ(history::SOURCE_IE_IMPORTED, visit_sources[visits[i].visit_id]);
1325 id = backend_->db()->GetRowForURL(url2, &row);
1326 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1327 ASSERT_EQ(2U, visits.size());
1328 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
1329 ASSERT_EQ(2U, visit_sources.size());
1330 for (int i = 0; i < 2; i++)
1331 EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]);
1332 }
1333
TEST_F(HistoryBackendTest,GetMostRecentVisits)1334 TEST_F(HistoryBackendTest, GetMostRecentVisits) {
1335 ASSERT_TRUE(backend_.get());
1336
1337 GURL url1("http://www.cnn.com");
1338 std::vector<VisitInfo> visits1;
1339 visits1.push_back(VisitInfo(
1340 Time::Now() - base::TimeDelta::FromDays(5),
1341 content::PAGE_TRANSITION_LINK));
1342 visits1.push_back(VisitInfo(
1343 Time::Now() - base::TimeDelta::FromDays(1),
1344 content::PAGE_TRANSITION_LINK));
1345 visits1.push_back(VisitInfo(
1346 Time::Now(), content::PAGE_TRANSITION_LINK));
1347
1348 // Clear all history.
1349 backend_->DeleteAllHistory();
1350
1351 // Add the visits.
1352 backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
1353
1354 // Verify the visits were added with their sources.
1355 VisitVector visits;
1356 URLRow row;
1357 URLID id = backend_->db()->GetRowForURL(url1, &row);
1358 ASSERT_TRUE(backend_->db()->GetMostRecentVisitsForURL(id, 1, &visits));
1359 ASSERT_EQ(1U, visits.size());
1360 EXPECT_EQ(visits1[2].first, visits[0].visit_time);
1361 }
1362
TEST_F(HistoryBackendTest,RemoveVisitsTransitions)1363 TEST_F(HistoryBackendTest, RemoveVisitsTransitions) {
1364 ASSERT_TRUE(backend_.get());
1365
1366 // Clear all history.
1367 backend_->DeleteAllHistory();
1368
1369 GURL url1("http://www.cnn.com");
1370 VisitInfo typed_visit(
1371 Time::Now() - base::TimeDelta::FromDays(6),
1372 content::PAGE_TRANSITION_TYPED);
1373 VisitInfo reload_visit(
1374 Time::Now() - base::TimeDelta::FromDays(5),
1375 content::PAGE_TRANSITION_RELOAD);
1376 VisitInfo link_visit(
1377 Time::Now() - base::TimeDelta::FromDays(4),
1378 content::PAGE_TRANSITION_LINK);
1379 std::vector<VisitInfo> visits_to_add;
1380 visits_to_add.push_back(typed_visit);
1381 visits_to_add.push_back(reload_visit);
1382 visits_to_add.push_back(link_visit);
1383
1384 // Add the visits.
1385 backend_->AddVisits(url1, visits_to_add, history::SOURCE_SYNCED);
1386
1387 // Verify that the various counts are what we expect.
1388 VisitVector visits;
1389 URLRow row;
1390 URLID id = backend_->db()->GetRowForURL(url1, &row);
1391 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1392 ASSERT_EQ(3U, visits.size());
1393 ASSERT_EQ(1, row.typed_count());
1394 ASSERT_EQ(2, row.visit_count());
1395
1396 // Now, delete the typed visit and verify that typed_count is updated.
1397 ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0])));
1398 id = backend_->db()->GetRowForURL(url1, &row);
1399 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1400 ASSERT_EQ(2U, visits.size());
1401 ASSERT_EQ(0, row.typed_count());
1402 ASSERT_EQ(1, row.visit_count());
1403
1404 // Delete the reload visit now and verify that none of the counts have
1405 // changed.
1406 ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0])));
1407 id = backend_->db()->GetRowForURL(url1, &row);
1408 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1409 ASSERT_EQ(1U, visits.size());
1410 ASSERT_EQ(0, row.typed_count());
1411 ASSERT_EQ(1, row.visit_count());
1412
1413 // Delete the last visit and verify that we delete the URL.
1414 ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0])));
1415 ASSERT_EQ(0, backend_->db()->GetRowForURL(url1, &row));
1416 }
1417
TEST_F(HistoryBackendTest,RemoveVisitsSource)1418 TEST_F(HistoryBackendTest, RemoveVisitsSource) {
1419 ASSERT_TRUE(backend_.get());
1420
1421 GURL url1("http://www.cnn.com");
1422 std::vector<VisitInfo> visits1, visits2;
1423 visits1.push_back(VisitInfo(
1424 Time::Now() - base::TimeDelta::FromDays(5),
1425 content::PAGE_TRANSITION_LINK));
1426 visits1.push_back(VisitInfo(Time::Now(),
1427 content::PAGE_TRANSITION_LINK));
1428
1429 GURL url2("http://www.example.com");
1430 visits2.push_back(VisitInfo(
1431 Time::Now() - base::TimeDelta::FromDays(10),
1432 content::PAGE_TRANSITION_LINK));
1433 visits2.push_back(VisitInfo(Time::Now(), content::PAGE_TRANSITION_LINK));
1434
1435 // Clear all history.
1436 backend_->DeleteAllHistory();
1437
1438 // Add the visits.
1439 backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
1440 backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED);
1441
1442 // Verify the visits of url1 were added.
1443 VisitVector visits;
1444 URLRow row;
1445 URLID id = backend_->db()->GetRowForURL(url1, &row);
1446 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1447 ASSERT_EQ(2U, visits.size());
1448 // Remove these visits.
1449 ASSERT_TRUE(backend_->RemoveVisits(visits));
1450
1451 // Now check only url2's source in visit_source table.
1452 VisitSourceMap visit_sources;
1453 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
1454 ASSERT_EQ(0U, visit_sources.size());
1455 id = backend_->db()->GetRowForURL(url2, &row);
1456 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1457 ASSERT_EQ(2U, visits.size());
1458 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
1459 ASSERT_EQ(2U, visit_sources.size());
1460 for (int i = 0; i < 2; i++)
1461 EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]);
1462 }
1463
1464 // Test for migration of adding visit_source table.
TEST_F(HistoryBackendTest,MigrationVisitSource)1465 TEST_F(HistoryBackendTest, MigrationVisitSource) {
1466 ASSERT_TRUE(backend_.get());
1467 backend_->Closing();
1468 backend_ = NULL;
1469
1470 base::FilePath old_history_path;
1471 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &old_history_path));
1472 old_history_path = old_history_path.AppendASCII("History");
1473 old_history_path = old_history_path.AppendASCII("HistoryNoSource");
1474
1475 // Copy history database file to current directory so that it will be deleted
1476 // in Teardown.
1477 base::FilePath new_history_path(test_dir());
1478 base::DeleteFile(new_history_path, true);
1479 base::CreateDirectory(new_history_path);
1480 base::FilePath new_history_file =
1481 new_history_path.Append(chrome::kHistoryFilename);
1482 ASSERT_TRUE(base::CopyFile(old_history_path, new_history_file));
1483
1484 backend_ = new HistoryBackend(
1485 new_history_path, new HistoryBackendTestDelegate(this), &history_client_);
1486 backend_->Init(std::string(), false);
1487 backend_->Closing();
1488 backend_ = NULL;
1489
1490 // Now the database should already be migrated.
1491 // Check version first.
1492 int cur_version = HistoryDatabase::GetCurrentVersion();
1493 sql::Connection db;
1494 ASSERT_TRUE(db.Open(new_history_file));
1495 sql::Statement s(db.GetUniqueStatement(
1496 "SELECT value FROM meta WHERE key = 'version'"));
1497 ASSERT_TRUE(s.Step());
1498 int file_version = s.ColumnInt(0);
1499 EXPECT_EQ(cur_version, file_version);
1500
1501 // Check visit_source table is created and empty.
1502 s.Assign(db.GetUniqueStatement(
1503 "SELECT name FROM sqlite_master WHERE name=\"visit_source\""));
1504 ASSERT_TRUE(s.Step());
1505 s.Assign(db.GetUniqueStatement("SELECT * FROM visit_source LIMIT 10"));
1506 EXPECT_FALSE(s.Step());
1507 }
1508
1509 // Test that SetFaviconMappingsForPageAndRedirects correctly updates icon
1510 // mappings based on redirects, icon URLs and icon types.
TEST_F(HistoryBackendTest,SetFaviconMappingsForPageAndRedirects)1511 TEST_F(HistoryBackendTest, SetFaviconMappingsForPageAndRedirects) {
1512 // Init recent_redirects_
1513 const GURL url1("http://www.google.com");
1514 const GURL url2("http://www.google.com/m");
1515 URLRow url_info1(url1);
1516 url_info1.set_visit_count(0);
1517 url_info1.set_typed_count(0);
1518 url_info1.set_last_visit(base::Time());
1519 url_info1.set_hidden(false);
1520 backend_->db_->AddURL(url_info1);
1521
1522 URLRow url_info2(url2);
1523 url_info2.set_visit_count(0);
1524 url_info2.set_typed_count(0);
1525 url_info2.set_last_visit(base::Time());
1526 url_info2.set_hidden(false);
1527 backend_->db_->AddURL(url_info2);
1528
1529 history::RedirectList redirects;
1530 redirects.push_back(url2);
1531 redirects.push_back(url1);
1532 backend_->recent_redirects_.Put(url1, redirects);
1533
1534 const GURL icon_url1("http://www.google.com/icon");
1535 const GURL icon_url2("http://www.google.com/icon2");
1536
1537 // Generate bitmap data for a page with two favicons.
1538 std::vector<favicon_base::FaviconRawBitmapData> two_favicon_bitmap_data;
1539 GenerateFaviconBitmapData(icon_url1, GetEdgeSizesSmallAndLarge(),
1540 icon_url2, GetEdgeSizesSmallAndLarge(), &two_favicon_bitmap_data);
1541
1542 // Generate bitmap data for a page with a single favicon.
1543 std::vector<favicon_base::FaviconRawBitmapData> one_favicon_bitmap_data;
1544 GenerateFaviconBitmapData(icon_url1, GetEdgeSizesSmallAndLarge(),
1545 &one_favicon_bitmap_data);
1546
1547 // Add two favicons
1548 backend_->SetFavicons(url1, favicon_base::FAVICON, two_favicon_bitmap_data);
1549 EXPECT_EQ(2u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
1550 EXPECT_EQ(2u, NumIconMappingsForPageURL(url2, favicon_base::FAVICON));
1551
1552 // Add one touch_icon
1553 backend_->SetFavicons(
1554 url1, favicon_base::TOUCH_ICON, one_favicon_bitmap_data);
1555 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON));
1556 EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, favicon_base::TOUCH_ICON));
1557 EXPECT_EQ(2u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
1558
1559 // Add one TOUCH_PRECOMPOSED_ICON
1560 backend_->SetFavicons(
1561 url1, favicon_base::TOUCH_PRECOMPOSED_ICON, one_favicon_bitmap_data);
1562 // The touch_icon was replaced.
1563 EXPECT_EQ(0u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON));
1564 EXPECT_EQ(2u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
1565 EXPECT_EQ(
1566 1u,
1567 NumIconMappingsForPageURL(url1, favicon_base::TOUCH_PRECOMPOSED_ICON));
1568 EXPECT_EQ(
1569 1u,
1570 NumIconMappingsForPageURL(url2, favicon_base::TOUCH_PRECOMPOSED_ICON));
1571
1572 // Add a touch_icon.
1573 backend_->SetFavicons(
1574 url1, favicon_base::TOUCH_ICON, one_favicon_bitmap_data);
1575 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON));
1576 EXPECT_EQ(2u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
1577 // The TOUCH_PRECOMPOSED_ICON was replaced.
1578 EXPECT_EQ(
1579 0u,
1580 NumIconMappingsForPageURL(url1, favicon_base::TOUCH_PRECOMPOSED_ICON));
1581
1582 // Add a single favicon.
1583 backend_->SetFavicons(url1, favicon_base::FAVICON, one_favicon_bitmap_data);
1584 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON));
1585 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
1586 EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, favicon_base::FAVICON));
1587
1588 // Add two favicons.
1589 backend_->SetFavicons(url1, favicon_base::FAVICON, two_favicon_bitmap_data);
1590 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON));
1591 EXPECT_EQ(2u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
1592 }
1593
1594 // Test that there is no churn in icon mappings from calling
1595 // SetFavicons() twice with the same |favicon_bitmap_data| parameter.
TEST_F(HistoryBackendTest,SetFaviconMappingsForPageDuplicates)1596 TEST_F(HistoryBackendTest, SetFaviconMappingsForPageDuplicates) {
1597 const GURL url("http://www.google.com/");
1598 const GURL icon_url("http://www.google.com/icon");
1599
1600 std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
1601 GenerateFaviconBitmapData(icon_url, GetEdgeSizesSmallAndLarge(),
1602 &favicon_bitmap_data);
1603
1604 backend_->SetFavicons(url, favicon_base::FAVICON, favicon_bitmap_data);
1605
1606 std::vector<IconMapping> icon_mappings;
1607 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1608 url, favicon_base::FAVICON, &icon_mappings));
1609 EXPECT_EQ(1u, icon_mappings.size());
1610 IconMappingID mapping_id = icon_mappings[0].mapping_id;
1611
1612 backend_->SetFavicons(url, favicon_base::FAVICON, favicon_bitmap_data);
1613
1614 icon_mappings.clear();
1615 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1616 url, favicon_base::FAVICON, &icon_mappings));
1617 EXPECT_EQ(1u, icon_mappings.size());
1618
1619 // The same row in the icon_mapping table should be used for the mapping as
1620 // before.
1621 EXPECT_EQ(mapping_id, icon_mappings[0].mapping_id);
1622 }
1623
1624 // Test that calling SetFavicons() with FaviconBitmapData of different pixel
1625 // sizes than the initially passed in FaviconBitmapData deletes the no longer
1626 // used favicon bitmaps.
TEST_F(HistoryBackendTest,SetFaviconsDeleteBitmaps)1627 TEST_F(HistoryBackendTest, SetFaviconsDeleteBitmaps) {
1628 const GURL page_url("http://www.google.com/");
1629 const GURL icon_url("http://www.google.com/icon");
1630
1631 std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
1632 GenerateFaviconBitmapData(icon_url, GetEdgeSizesSmallAndLarge(),
1633 &favicon_bitmap_data);
1634 backend_->SetFavicons(page_url, favicon_base::FAVICON, favicon_bitmap_data);
1635
1636 // Test initial state.
1637 std::vector<IconMapping> icon_mappings;
1638 EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url, &icon_mappings));
1639 EXPECT_EQ(1u, icon_mappings.size());
1640 EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
1641 EXPECT_EQ(favicon_base::FAVICON, icon_mappings[0].icon_type);
1642 favicon_base::FaviconID favicon_id = icon_mappings[0].icon_id;
1643
1644 std::vector<FaviconBitmap> favicon_bitmaps;
1645 EXPECT_TRUE(GetSortedFaviconBitmaps(favicon_id, &favicon_bitmaps));
1646 EXPECT_EQ(2u, favicon_bitmaps.size());
1647 FaviconBitmapID small_bitmap_id = favicon_bitmaps[0].bitmap_id;
1648 EXPECT_NE(0, small_bitmap_id);
1649 EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmaps[0].bitmap_data));
1650 EXPECT_EQ(kSmallSize, favicon_bitmaps[0].pixel_size);
1651 FaviconBitmapID large_bitmap_id = favicon_bitmaps[1].bitmap_id;
1652 EXPECT_NE(0, large_bitmap_id);
1653 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmaps[1].bitmap_data));
1654 EXPECT_EQ(kLargeSize, favicon_bitmaps[1].pixel_size);
1655
1656 // Call SetFavicons() with bitmap data for only the large bitmap. Check that
1657 // the small bitmap is in fact deleted.
1658 GenerateFaviconBitmapData(icon_url, GetEdgeSizesLarge(),
1659 &favicon_bitmap_data);
1660 backend_->SetFavicons(page_url, favicon_base::FAVICON, favicon_bitmap_data);
1661
1662 scoped_refptr<base::RefCountedMemory> bitmap_data_out;
1663 gfx::Size pixel_size_out;
1664 EXPECT_FALSE(backend_->thumbnail_db_->GetFaviconBitmap(small_bitmap_id,
1665 NULL, &bitmap_data_out, &pixel_size_out));
1666 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmap(large_bitmap_id,
1667 NULL, &bitmap_data_out, &pixel_size_out));
1668 EXPECT_TRUE(BitmapDataEqual('a', bitmap_data_out));
1669 EXPECT_EQ(kLargeSize, pixel_size_out);
1670
1671 icon_mappings.clear();
1672 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1673 &icon_mappings));
1674 EXPECT_EQ(1u, icon_mappings.size());
1675 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
1676
1677 // Call SetFavicons() with no bitmap data. Check that the bitmaps and icon
1678 // mappings are deleted.
1679 favicon_bitmap_data.clear();
1680 backend_->SetFavicons(page_url, favicon_base::FAVICON, favicon_bitmap_data);
1681
1682 EXPECT_FALSE(backend_->thumbnail_db_->GetFaviconBitmap(large_bitmap_id, NULL,
1683 NULL, NULL));
1684 icon_mappings.clear();
1685 EXPECT_FALSE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1686 &icon_mappings));
1687
1688 // Notifications should have been broadcast for each call to SetFavicons().
1689 EXPECT_EQ(3, num_broadcasted_notifications());
1690 }
1691
1692 // Test updating a single favicon bitmap's data via SetFavicons.
TEST_F(HistoryBackendTest,SetFaviconsReplaceBitmapData)1693 TEST_F(HistoryBackendTest, SetFaviconsReplaceBitmapData) {
1694 const GURL page_url("http://www.google.com/");
1695 const GURL icon_url("http://www.google.com/icon");
1696
1697 std::vector<unsigned char> data_initial;
1698 data_initial.push_back('a');
1699
1700 favicon_base::FaviconRawBitmapData bitmap_data_element;
1701 bitmap_data_element.bitmap_data =
1702 base::RefCountedBytes::TakeVector(&data_initial);
1703 bitmap_data_element.pixel_size = kSmallSize;
1704 bitmap_data_element.icon_url = icon_url;
1705 std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
1706 favicon_bitmap_data.push_back(bitmap_data_element);
1707
1708 // Add bitmap to the database.
1709 backend_->SetFavicons(page_url, favicon_base::FAVICON, favicon_bitmap_data);
1710
1711 favicon_base::FaviconID original_favicon_id =
1712 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
1713 icon_url, favicon_base::FAVICON, NULL);
1714 EXPECT_NE(0, original_favicon_id);
1715 FaviconBitmap original_favicon_bitmap;
1716 EXPECT_TRUE(
1717 GetOnlyFaviconBitmap(original_favicon_id, &original_favicon_bitmap));
1718 EXPECT_TRUE(BitmapDataEqual('a', original_favicon_bitmap.bitmap_data));
1719
1720 EXPECT_EQ(1, num_broadcasted_notifications());
1721
1722 // Call SetFavicons() with completely identical data.
1723 std::vector<unsigned char> updated_data;
1724 updated_data.push_back('a');
1725 favicon_bitmap_data[0].bitmap_data = new base::RefCountedBytes(updated_data);
1726 backend_->SetFavicons(page_url, favicon_base::FAVICON, favicon_bitmap_data);
1727
1728 favicon_base::FaviconID updated_favicon_id =
1729 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
1730 icon_url, favicon_base::FAVICON, NULL);
1731 EXPECT_NE(0, updated_favicon_id);
1732 FaviconBitmap updated_favicon_bitmap;
1733 EXPECT_TRUE(
1734 GetOnlyFaviconBitmap(updated_favicon_id, &updated_favicon_bitmap));
1735 EXPECT_TRUE(BitmapDataEqual('a', updated_favicon_bitmap.bitmap_data));
1736
1737 // Because the bitmap data is byte equivalent, no notifications should have
1738 // been broadcasted.
1739 EXPECT_EQ(1, num_broadcasted_notifications());
1740
1741 // Call SetFavicons() with identical data but a different bitmap.
1742 updated_data[0] = 'b';
1743 favicon_bitmap_data[0].bitmap_data = new base::RefCountedBytes(updated_data);
1744 backend_->SetFavicons(page_url, favicon_base::FAVICON, favicon_bitmap_data);
1745
1746 updated_favicon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
1747 icon_url, favicon_base::FAVICON, NULL);
1748 EXPECT_NE(0, updated_favicon_id);
1749 EXPECT_TRUE(
1750 GetOnlyFaviconBitmap(updated_favicon_id, &updated_favicon_bitmap));
1751 EXPECT_TRUE(BitmapDataEqual('b', updated_favicon_bitmap.bitmap_data));
1752
1753 // There should be no churn in FaviconIDs or FaviconBitmapIds even though
1754 // the bitmap data changed.
1755 EXPECT_EQ(original_favicon_bitmap.icon_id, updated_favicon_bitmap.icon_id);
1756 EXPECT_EQ(original_favicon_bitmap.bitmap_id,
1757 updated_favicon_bitmap.bitmap_id);
1758
1759 // A notification should have been broadcasted as the favicon bitmap data has
1760 // changed.
1761 EXPECT_EQ(2, num_broadcasted_notifications());
1762 }
1763
1764 // Test that if two pages share the same FaviconID, changing the favicon for
1765 // one page does not affect the other.
TEST_F(HistoryBackendTest,SetFaviconsSameFaviconURLForTwoPages)1766 TEST_F(HistoryBackendTest, SetFaviconsSameFaviconURLForTwoPages) {
1767 GURL icon_url("http://www.google.com/favicon.ico");
1768 GURL icon_url_new("http://www.google.com/favicon2.ico");
1769 GURL page_url1("http://www.google.com");
1770 GURL page_url2("http://www.google.ca");
1771
1772 std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
1773 GenerateFaviconBitmapData(icon_url, GetEdgeSizesSmallAndLarge(),
1774 &favicon_bitmap_data);
1775
1776 backend_->SetFavicons(page_url1, favicon_base::FAVICON, favicon_bitmap_data);
1777
1778 std::vector<GURL> icon_urls;
1779 icon_urls.push_back(icon_url);
1780
1781 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
1782 backend_->UpdateFaviconMappingsAndFetch(page_url2,
1783 icon_urls,
1784 favicon_base::FAVICON,
1785 GetEdgeSizesSmallAndLarge(),
1786 &bitmap_results);
1787
1788 // Check that the same FaviconID is mapped to both page URLs.
1789 std::vector<IconMapping> icon_mappings;
1790 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1791 page_url1, &icon_mappings));
1792 EXPECT_EQ(1u, icon_mappings.size());
1793 favicon_base::FaviconID favicon_id = icon_mappings[0].icon_id;
1794 EXPECT_NE(0, favicon_id);
1795
1796 icon_mappings.clear();
1797 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1798 page_url2, &icon_mappings));
1799 EXPECT_EQ(1u, icon_mappings.size());
1800 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
1801
1802 // Change the icon URL that |page_url1| is mapped to.
1803 GenerateFaviconBitmapData(icon_url_new, GetEdgeSizesSmall(),
1804 &favicon_bitmap_data);
1805 backend_->SetFavicons(page_url1, favicon_base::FAVICON, favicon_bitmap_data);
1806
1807 // |page_url1| should map to a new FaviconID and have valid bitmap data.
1808 icon_mappings.clear();
1809 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1810 page_url1, &icon_mappings));
1811 EXPECT_EQ(1u, icon_mappings.size());
1812 EXPECT_EQ(icon_url_new, icon_mappings[0].icon_url);
1813 EXPECT_NE(favicon_id, icon_mappings[0].icon_id);
1814
1815 std::vector<FaviconBitmap> favicon_bitmaps;
1816 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
1817 icon_mappings[0].icon_id, &favicon_bitmaps));
1818 EXPECT_EQ(1u, favicon_bitmaps.size());
1819
1820 // |page_url2| should still map to the same FaviconID and have valid bitmap
1821 // data.
1822 icon_mappings.clear();
1823 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1824 page_url2, &icon_mappings));
1825 EXPECT_EQ(1u, icon_mappings.size());
1826 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
1827
1828 favicon_bitmaps.clear();
1829 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(favicon_id,
1830 &favicon_bitmaps));
1831 EXPECT_EQ(2u, favicon_bitmaps.size());
1832
1833 // A notification should have been broadcast for each call to SetFavicons()
1834 // and each call to UpdateFaviconMappingsAndFetch().
1835 EXPECT_EQ(3, num_broadcasted_notifications());
1836 }
1837
1838 // Test that no notifications are broadcast as a result of calling
1839 // UpdateFaviconMappingsAndFetch() for an icon URL which is already
1840 // mapped to the passed in page URL.
TEST_F(HistoryBackendTest,UpdateFaviconMappingsAndFetchNoChange)1841 TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchNoChange) {
1842 GURL page_url("http://www.google.com");
1843 GURL icon_url("http://www.google.com/favicon.ico");
1844 std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
1845 GenerateFaviconBitmapData(icon_url, GetEdgeSizesSmall(),
1846 &favicon_bitmap_data);
1847
1848 backend_->SetFavicons(page_url, favicon_base::FAVICON, favicon_bitmap_data);
1849
1850 favicon_base::FaviconID icon_id =
1851 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
1852 icon_url, favicon_base::FAVICON, NULL);
1853 EXPECT_NE(0, icon_id);
1854 EXPECT_EQ(1, num_broadcasted_notifications());
1855
1856 std::vector<GURL> icon_urls;
1857 icon_urls.push_back(icon_url);
1858
1859 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
1860 backend_->UpdateFaviconMappingsAndFetch(page_url,
1861 icon_urls,
1862 favicon_base::FAVICON,
1863 GetEdgeSizesSmallAndLarge(),
1864 &bitmap_results);
1865
1866 EXPECT_EQ(icon_id,
1867 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
1868 icon_url, favicon_base::FAVICON, NULL));
1869
1870 // No notification should have been broadcast as no icon mapping, favicon,
1871 // or favicon bitmap was updated, added or removed.
1872 EXPECT_EQ(1, num_broadcasted_notifications());
1873 }
1874
1875 // Test repeatedly calling MergeFavicon(). |page_url| is initially not known
1876 // to the database.
TEST_F(HistoryBackendTest,MergeFaviconPageURLNotInDB)1877 TEST_F(HistoryBackendTest, MergeFaviconPageURLNotInDB) {
1878 GURL page_url("http://www.google.com");
1879 GURL icon_url("http:/www.google.com/favicon.ico");
1880
1881 std::vector<unsigned char> data;
1882 data.push_back('a');
1883 scoped_refptr<base::RefCountedBytes> bitmap_data(
1884 new base::RefCountedBytes(data));
1885
1886 backend_->MergeFavicon(
1887 page_url, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize);
1888
1889 // |page_url| should now be mapped to |icon_url| and the favicon bitmap should
1890 // not be expired.
1891 std::vector<IconMapping> icon_mappings;
1892 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1893 &icon_mappings));
1894 EXPECT_EQ(1u, icon_mappings.size());
1895 EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
1896
1897 FaviconBitmap favicon_bitmap;
1898 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
1899 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
1900 EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
1901 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
1902
1903 data[0] = 'b';
1904 bitmap_data = new base::RefCountedBytes(data);
1905 backend_->MergeFavicon(
1906 page_url, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize);
1907
1908 // |page_url| should still have a single favicon bitmap. The bitmap data
1909 // should be updated.
1910 icon_mappings.clear();
1911 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1912 &icon_mappings));
1913 EXPECT_EQ(1u, icon_mappings.size());
1914 EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
1915
1916 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
1917 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
1918 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
1919 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
1920 }
1921
1922 // Test calling MergeFavicon() when |page_url| is known to the database.
TEST_F(HistoryBackendTest,MergeFaviconPageURLInDB)1923 TEST_F(HistoryBackendTest, MergeFaviconPageURLInDB) {
1924 GURL page_url("http://www.google.com");
1925 GURL icon_url1("http:/www.google.com/favicon.ico");
1926 GURL icon_url2("http://www.google.com/favicon2.ico");
1927
1928 std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
1929 GenerateFaviconBitmapData(icon_url1, GetEdgeSizesSmall(),
1930 &favicon_bitmap_data);
1931
1932 backend_->SetFavicons(page_url, favicon_base::FAVICON, favicon_bitmap_data);
1933
1934 // Test initial state.
1935 std::vector<IconMapping> icon_mappings;
1936 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1937 &icon_mappings));
1938 EXPECT_EQ(1u, icon_mappings.size());
1939 EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
1940
1941 FaviconBitmap favicon_bitmap;
1942 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
1943 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
1944 EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
1945 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
1946
1947 EXPECT_EQ(1, num_broadcasted_notifications());
1948
1949 // 1) Merge identical favicon bitmap.
1950 std::vector<unsigned char> data;
1951 data.push_back('a');
1952 scoped_refptr<base::RefCountedBytes> bitmap_data(
1953 new base::RefCountedBytes(data));
1954 backend_->MergeFavicon(
1955 page_url, icon_url1, favicon_base::FAVICON, bitmap_data, kSmallSize);
1956
1957 // All the data should stay the same and no notifications should have been
1958 // sent.
1959 icon_mappings.clear();
1960 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1961 &icon_mappings));
1962 EXPECT_EQ(1u, icon_mappings.size());
1963 EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
1964
1965 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
1966 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
1967 EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
1968 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
1969
1970 EXPECT_EQ(1, num_broadcasted_notifications());
1971
1972 // 2) Merge favicon bitmap of the same size.
1973 data[0] = 'b';
1974 bitmap_data = new base::RefCountedBytes(data);
1975 backend_->MergeFavicon(
1976 page_url, icon_url1, favicon_base::FAVICON, bitmap_data, kSmallSize);
1977
1978 // The small favicon bitmap at |icon_url1| should be overwritten.
1979 icon_mappings.clear();
1980 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1981 &icon_mappings));
1982 EXPECT_EQ(1u, icon_mappings.size());
1983 EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
1984
1985 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
1986 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
1987 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
1988 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
1989
1990 // 3) Merge favicon for the same icon URL, but a pixel size for which there is
1991 // no favicon bitmap.
1992 data[0] = 'c';
1993 bitmap_data = new base::RefCountedBytes(data);
1994 backend_->MergeFavicon(
1995 page_url, icon_url1, favicon_base::FAVICON, bitmap_data, kTinySize);
1996
1997 // A new favicon bitmap should be created and the preexisting favicon bitmap
1998 // ('b') should be expired.
1999 icon_mappings.clear();
2000 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
2001 &icon_mappings));
2002 EXPECT_EQ(1u, icon_mappings.size());
2003 EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
2004
2005 std::vector<FaviconBitmap> favicon_bitmaps;
2006 EXPECT_TRUE(GetSortedFaviconBitmaps(icon_mappings[0].icon_id,
2007 &favicon_bitmaps));
2008 EXPECT_NE(base::Time(), favicon_bitmaps[0].last_updated);
2009 EXPECT_TRUE(BitmapDataEqual('c', favicon_bitmaps[0].bitmap_data));
2010 EXPECT_EQ(kTinySize, favicon_bitmaps[0].pixel_size);
2011 EXPECT_EQ(base::Time(), favicon_bitmaps[1].last_updated);
2012 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmaps[1].bitmap_data));
2013 EXPECT_EQ(kSmallSize, favicon_bitmaps[1].pixel_size);
2014
2015 // 4) Merge favicon for an icon URL different from the icon URLs already
2016 // mapped to page URL.
2017 data[0] = 'd';
2018 bitmap_data = new base::RefCountedBytes(data);
2019 backend_->MergeFavicon(
2020 page_url, icon_url2, favicon_base::FAVICON, bitmap_data, kSmallSize);
2021
2022 // The existing favicon bitmaps should be copied over to the newly created
2023 // favicon at |icon_url2|. |page_url| should solely be mapped to |icon_url2|.
2024 icon_mappings.clear();
2025 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
2026 &icon_mappings));
2027 EXPECT_EQ(1u, icon_mappings.size());
2028 EXPECT_EQ(icon_url2, icon_mappings[0].icon_url);
2029
2030 favicon_bitmaps.clear();
2031 EXPECT_TRUE(GetSortedFaviconBitmaps(icon_mappings[0].icon_id,
2032 &favicon_bitmaps));
2033 EXPECT_EQ(base::Time(), favicon_bitmaps[0].last_updated);
2034 EXPECT_TRUE(BitmapDataEqual('c', favicon_bitmaps[0].bitmap_data));
2035 EXPECT_EQ(kTinySize, favicon_bitmaps[0].pixel_size);
2036 // The favicon being merged should take precedence over the preexisting
2037 // favicon bitmaps.
2038 EXPECT_NE(base::Time(), favicon_bitmaps[1].last_updated);
2039 EXPECT_TRUE(BitmapDataEqual('d', favicon_bitmaps[1].bitmap_data));
2040 EXPECT_EQ(kSmallSize, favicon_bitmaps[1].pixel_size);
2041
2042 // A notification should have been broadcast for each call to SetFavicons()
2043 // and MergeFavicon().
2044 EXPECT_EQ(4, num_broadcasted_notifications());
2045 }
2046
2047 // Test calling MergeFavicon() when |icon_url| is known to the database but not
2048 // mapped to |page_url|.
TEST_F(HistoryBackendTest,MergeFaviconIconURLMappedToDifferentPageURL)2049 TEST_F(HistoryBackendTest, MergeFaviconIconURLMappedToDifferentPageURL) {
2050 GURL page_url1("http://www.google.com");
2051 GURL page_url2("http://news.google.com");
2052 GURL page_url3("http://maps.google.com");
2053 GURL icon_url("http:/www.google.com/favicon.ico");
2054
2055 std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
2056 GenerateFaviconBitmapData(icon_url, GetEdgeSizesSmall(),
2057 &favicon_bitmap_data);
2058
2059 backend_->SetFavicons(page_url1, favicon_base::FAVICON, favicon_bitmap_data);
2060
2061 // Test initial state.
2062 std::vector<IconMapping> icon_mappings;
2063 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1,
2064 &icon_mappings));
2065 EXPECT_EQ(1u, icon_mappings.size());
2066 EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
2067
2068 FaviconBitmap favicon_bitmap;
2069 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
2070 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
2071 EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
2072 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
2073
2074 // 1) Merge in an identical favicon bitmap data but for a different page URL.
2075 std::vector<unsigned char> data;
2076 data.push_back('a');
2077 scoped_refptr<base::RefCountedBytes> bitmap_data(
2078 new base::RefCountedBytes(data));
2079
2080 backend_->MergeFavicon(
2081 page_url2, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize);
2082
2083 favicon_base::FaviconID favicon_id =
2084 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
2085 icon_url, favicon_base::FAVICON, NULL);
2086 EXPECT_NE(0, favicon_id);
2087
2088 EXPECT_TRUE(GetOnlyFaviconBitmap(favicon_id, &favicon_bitmap));
2089 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
2090 EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
2091 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
2092
2093 // 2) Merging a favicon bitmap with different bitmap data for the same icon
2094 // URL should overwrite the small favicon bitmap at |icon_url|.
2095 bitmap_data->data()[0] = 'b';
2096 backend_->MergeFavicon(
2097 page_url3, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize);
2098
2099 favicon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
2100 icon_url, favicon_base::FAVICON, NULL);
2101 EXPECT_NE(0, favicon_id);
2102
2103 EXPECT_TRUE(GetOnlyFaviconBitmap(favicon_id, &favicon_bitmap));
2104 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
2105 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
2106 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
2107
2108 // |icon_url| should be mapped to all three page URLs.
2109 icon_mappings.clear();
2110 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1,
2111 &icon_mappings));
2112 EXPECT_EQ(1u, icon_mappings.size());
2113 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
2114
2115 icon_mappings.clear();
2116 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url2,
2117 &icon_mappings));
2118 EXPECT_EQ(1u, icon_mappings.size());
2119 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
2120
2121 icon_mappings.clear();
2122 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url3,
2123 &icon_mappings));
2124 EXPECT_EQ(1u, icon_mappings.size());
2125 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
2126
2127 // A notification should have been broadcast for each call to SetFavicons()
2128 // and MergeFavicon().
2129 EXPECT_EQ(3, num_broadcasted_notifications());
2130 }
2131
2132 // Test that MergeFavicon() does not add more than
2133 // |kMaxFaviconBitmapsPerIconURL| to a favicon.
TEST_F(HistoryBackendTest,MergeFaviconMaxFaviconBitmapsPerIconURL)2134 TEST_F(HistoryBackendTest, MergeFaviconMaxFaviconBitmapsPerIconURL) {
2135 GURL page_url("http://www.google.com");
2136 std::string icon_url_string("http://www.google.com/favicon.ico");
2137 size_t replace_index = icon_url_string.size() - 1;
2138
2139 std::vector<unsigned char> data;
2140 data.push_back('a');
2141 scoped_refptr<base::RefCountedMemory> bitmap_data =
2142 base::RefCountedBytes::TakeVector(&data);
2143
2144 int pixel_size = 1;
2145 for (size_t i = 0; i < kMaxFaviconBitmapsPerIconURL + 1; ++i) {
2146 icon_url_string[replace_index] = '0' + i;
2147 GURL icon_url(icon_url_string);
2148
2149 backend_->MergeFavicon(page_url,
2150 icon_url,
2151 favicon_base::FAVICON,
2152 bitmap_data,
2153 gfx::Size(pixel_size, pixel_size));
2154 ++pixel_size;
2155 }
2156
2157 // There should be a single favicon mapped to |page_url| with exactly
2158 // kMaxFaviconBitmapsPerIconURL favicon bitmaps.
2159 std::vector<IconMapping> icon_mappings;
2160 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
2161 &icon_mappings));
2162 EXPECT_EQ(1u, icon_mappings.size());
2163 std::vector<FaviconBitmap> favicon_bitmaps;
2164 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
2165 icon_mappings[0].icon_id, &favicon_bitmaps));
2166 EXPECT_EQ(kMaxFaviconBitmapsPerIconURL, favicon_bitmaps.size());
2167 }
2168
2169 // Tests that the favicon set by MergeFavicon() shows up in the result of
2170 // GetFaviconsForURL().
TEST_F(HistoryBackendTest,MergeFaviconShowsUpInGetFaviconsForURLResult)2171 TEST_F(HistoryBackendTest, MergeFaviconShowsUpInGetFaviconsForURLResult) {
2172 GURL page_url("http://www.google.com");
2173 GURL icon_url("http://www.google.com/favicon.ico");
2174 GURL merged_icon_url("http://wwww.google.com/favicon2.ico");
2175
2176 std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
2177 GenerateFaviconBitmapData(icon_url, GetEdgeSizesSmallAndLarge(),
2178 &favicon_bitmap_data);
2179
2180 // Set some preexisting favicons for |page_url|.
2181 backend_->SetFavicons(page_url, favicon_base::FAVICON, favicon_bitmap_data);
2182
2183 // Merge small favicon.
2184 std::vector<unsigned char> data;
2185 data.push_back('c');
2186 scoped_refptr<base::RefCountedBytes> bitmap_data(
2187 new base::RefCountedBytes(data));
2188 backend_->MergeFavicon(page_url,
2189 merged_icon_url,
2190 favicon_base::FAVICON,
2191 bitmap_data,
2192 kSmallSize);
2193
2194 // Request favicon bitmaps for both 1x and 2x to simulate request done by
2195 // BookmarkModel::GetFavicon().
2196 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
2197 backend_->GetFaviconsForURL(page_url,
2198 favicon_base::FAVICON,
2199 GetEdgeSizesSmallAndLarge(),
2200 &bitmap_results);
2201
2202 EXPECT_EQ(2u, bitmap_results.size());
2203 const favicon_base::FaviconRawBitmapResult& first_result = bitmap_results[0];
2204 const favicon_base::FaviconRawBitmapResult& result =
2205 (first_result.pixel_size == kSmallSize) ? first_result
2206 : bitmap_results[1];
2207 EXPECT_TRUE(BitmapDataEqual('c', result.bitmap_data));
2208 }
2209
2210 // Tests GetFaviconsForURL with icon_types priority,
TEST_F(HistoryBackendTest,TestGetFaviconsForURLWithIconTypesPriority)2211 TEST_F(HistoryBackendTest, TestGetFaviconsForURLWithIconTypesPriority) {
2212 GURL page_url("http://www.google.com");
2213 GURL icon_url("http://www.google.com/favicon.ico");
2214 GURL touch_icon_url("http://wwww.google.com/touch_icon.ico");
2215
2216 std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
2217 std::vector<int> favicon_size;
2218 favicon_size.push_back(16);
2219 favicon_size.push_back(32);
2220 GenerateFaviconBitmapData(icon_url, favicon_size, &favicon_bitmap_data);
2221 ASSERT_EQ(2u, favicon_bitmap_data.size());
2222
2223 std::vector<favicon_base::FaviconRawBitmapData> touch_icon_bitmap_data;
2224 std::vector<int> touch_icon_size;
2225 touch_icon_size.push_back(64);
2226 GenerateFaviconBitmapData(icon_url, touch_icon_size, &touch_icon_bitmap_data);
2227 ASSERT_EQ(1u, touch_icon_bitmap_data.size());
2228
2229 // Set some preexisting favicons for |page_url|.
2230 backend_->SetFavicons(page_url, favicon_base::FAVICON, favicon_bitmap_data);
2231 backend_->SetFavicons(
2232 page_url, favicon_base::TOUCH_ICON, touch_icon_bitmap_data);
2233
2234 favicon_base::FaviconRawBitmapResult result;
2235 std::vector<int> icon_types;
2236 icon_types.push_back(favicon_base::FAVICON);
2237 icon_types.push_back(favicon_base::TOUCH_ICON);
2238
2239 backend_->GetLargestFaviconForURL(page_url, icon_types, 16, &result);
2240
2241 // Verify the result icon is 32x32 favicon.
2242 EXPECT_EQ(gfx::Size(32, 32), result.pixel_size);
2243 EXPECT_EQ(favicon_base::FAVICON, result.icon_type);
2244
2245 // Change Minimal size to 32x32 and verify the 64x64 touch icon returned.
2246 backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result);
2247 EXPECT_EQ(gfx::Size(64, 64), result.pixel_size);
2248 EXPECT_EQ(favicon_base::TOUCH_ICON, result.icon_type);
2249 }
2250
2251 // Test the the first types of icon is returned if its size equal to the
2252 // second types icon.
TEST_F(HistoryBackendTest,TestGetFaviconsForURLReturnFavicon)2253 TEST_F(HistoryBackendTest, TestGetFaviconsForURLReturnFavicon) {
2254 GURL page_url("http://www.google.com");
2255 GURL icon_url("http://www.google.com/favicon.ico");
2256 GURL touch_icon_url("http://wwww.google.com/touch_icon.ico");
2257
2258 std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
2259 std::vector<int> favicon_size;
2260 favicon_size.push_back(16);
2261 favicon_size.push_back(32);
2262 GenerateFaviconBitmapData(icon_url, favicon_size, &favicon_bitmap_data);
2263 ASSERT_EQ(2u, favicon_bitmap_data.size());
2264
2265 std::vector<favicon_base::FaviconRawBitmapData> touch_icon_bitmap_data;
2266 std::vector<int> touch_icon_size;
2267 touch_icon_size.push_back(32);
2268 GenerateFaviconBitmapData(icon_url, touch_icon_size, &touch_icon_bitmap_data);
2269 ASSERT_EQ(1u, touch_icon_bitmap_data.size());
2270
2271 // Set some preexisting favicons for |page_url|.
2272 backend_->SetFavicons(page_url, favicon_base::FAVICON, favicon_bitmap_data);
2273 backend_->SetFavicons(
2274 page_url, favicon_base::TOUCH_ICON, touch_icon_bitmap_data);
2275
2276 favicon_base::FaviconRawBitmapResult result;
2277 std::vector<int> icon_types;
2278 icon_types.push_back(favicon_base::FAVICON);
2279 icon_types.push_back(favicon_base::TOUCH_ICON);
2280
2281 backend_->GetLargestFaviconForURL(page_url, icon_types, 16, &result);
2282
2283 // Verify the result icon is 32x32 favicon.
2284 EXPECT_EQ(gfx::Size(32, 32), result.pixel_size);
2285 EXPECT_EQ(favicon_base::FAVICON, result.icon_type);
2286
2287 // Change minimal size to 32x32 and verify the 32x32 favicon returned.
2288 favicon_base::FaviconRawBitmapResult result1;
2289 backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result1);
2290 EXPECT_EQ(gfx::Size(32, 32), result1.pixel_size);
2291 EXPECT_EQ(favicon_base::FAVICON, result1.icon_type);
2292 }
2293
2294 // Test the favicon is returned if its size is smaller than minimal size,
2295 // because it is only one available.
TEST_F(HistoryBackendTest,TestGetFaviconsForURLReturnFaviconEvenItSmaller)2296 TEST_F(HistoryBackendTest, TestGetFaviconsForURLReturnFaviconEvenItSmaller) {
2297 GURL page_url("http://www.google.com");
2298 GURL icon_url("http://www.google.com/favicon.ico");
2299
2300 std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
2301 std::vector<int> favicon_size;
2302 favicon_size.push_back(16);
2303 GenerateFaviconBitmapData(icon_url, favicon_size, &favicon_bitmap_data);
2304 ASSERT_EQ(1u, favicon_bitmap_data.size());
2305
2306 // Set preexisting favicons for |page_url|.
2307 backend_->SetFavicons(page_url, favicon_base::FAVICON, favicon_bitmap_data);
2308
2309 favicon_base::FaviconRawBitmapResult result;
2310 std::vector<int> icon_types;
2311 icon_types.push_back(favicon_base::FAVICON);
2312 icon_types.push_back(favicon_base::TOUCH_ICON);
2313
2314 backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result);
2315
2316 // Verify 16x16 icon is returned, even it small than minimal_size.
2317 EXPECT_EQ(gfx::Size(16, 16), result.pixel_size);
2318 EXPECT_EQ(favicon_base::FAVICON, result.icon_type);
2319 }
2320
2321 // Test UpdateFaviconMapingsAndFetch() when multiple icon types are passed in.
TEST_F(HistoryBackendTest,UpdateFaviconMappingsAndFetchMultipleIconTypes)2322 TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchMultipleIconTypes) {
2323 GURL page_url1("http://www.google.com");
2324 GURL page_url2("http://news.google.com");
2325 GURL page_url3("http://mail.google.com");
2326 GURL icon_urla("http://www.google.com/favicon1.ico");
2327 GURL icon_urlb("http://www.google.com/favicon2.ico");
2328 GURL icon_urlc("http://www.google.com/favicon3.ico");
2329
2330 // |page_url1| is mapped to |icon_urla| which if of type TOUCH_ICON.
2331 std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
2332 GenerateFaviconBitmapData(icon_urla, GetEdgeSizesSmall(),
2333 &favicon_bitmap_data);
2334 backend_->SetFavicons(
2335 page_url1, favicon_base::TOUCH_ICON, favicon_bitmap_data);
2336
2337 // |page_url2| is mapped to |icon_urlb| and |icon_urlc| which are of type
2338 // TOUCH_PRECOMPOSED_ICON.
2339 GenerateFaviconBitmapData(icon_urlb, GetEdgeSizesSmall(), icon_urlc,
2340 GetEdgeSizesSmall(), &favicon_bitmap_data);
2341 backend_->SetFavicons(
2342 page_url2, favicon_base::TOUCH_PRECOMPOSED_ICON, favicon_bitmap_data);
2343
2344 std::vector<GURL> icon_urls;
2345 icon_urls.push_back(icon_urla);
2346 icon_urls.push_back(icon_urlb);
2347 icon_urls.push_back(icon_urlc);
2348
2349 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
2350 backend_->UpdateFaviconMappingsAndFetch(
2351 page_url3,
2352 icon_urls,
2353 (favicon_base::TOUCH_ICON | favicon_base::TOUCH_PRECOMPOSED_ICON),
2354 GetEdgeSizesSmallAndLarge(),
2355 &bitmap_results);
2356
2357 // |page_url1| and |page_url2| should still be mapped to the same icon URLs.
2358 std::vector<IconMapping> icon_mappings;
2359 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1,
2360 &icon_mappings));
2361 EXPECT_EQ(1u, icon_mappings.size());
2362 EXPECT_EQ(icon_urla, icon_mappings[0].icon_url);
2363 EXPECT_EQ(favicon_base::TOUCH_ICON, icon_mappings[0].icon_type);
2364
2365 icon_mappings.clear();
2366 EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url2, &icon_mappings));
2367 EXPECT_EQ(2u, icon_mappings.size());
2368 EXPECT_EQ(icon_urlb, icon_mappings[0].icon_url);
2369 EXPECT_EQ(favicon_base::TOUCH_PRECOMPOSED_ICON, icon_mappings[0].icon_type);
2370 EXPECT_EQ(icon_urlc, icon_mappings[1].icon_url);
2371 EXPECT_EQ(favicon_base::TOUCH_PRECOMPOSED_ICON, icon_mappings[1].icon_type);
2372
2373 // |page_url3| should be mapped only to |icon_urlb| and |icon_urlc| as
2374 // TOUCH_PRECOMPOSED_ICON is the largest IconType.
2375 icon_mappings.clear();
2376 EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url3, &icon_mappings));
2377 EXPECT_EQ(2u, icon_mappings.size());
2378 EXPECT_EQ(icon_urlb, icon_mappings[0].icon_url);
2379 EXPECT_EQ(favicon_base::TOUCH_PRECOMPOSED_ICON, icon_mappings[0].icon_type);
2380 EXPECT_EQ(icon_urlc, icon_mappings[1].icon_url);
2381 EXPECT_EQ(favicon_base::TOUCH_PRECOMPOSED_ICON, icon_mappings[1].icon_type);
2382 }
2383
2384 // Test the results of GetFaviconsFromDB() when there are no found
2385 // favicons.
TEST_F(HistoryBackendTest,GetFaviconsFromDBEmpty)2386 TEST_F(HistoryBackendTest, GetFaviconsFromDBEmpty) {
2387 const GURL page_url("http://www.google.com/");
2388
2389 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
2390 EXPECT_FALSE(backend_->GetFaviconsFromDB(page_url,
2391 favicon_base::FAVICON,
2392 GetEdgeSizesSmallAndLarge(),
2393 &bitmap_results));
2394 EXPECT_TRUE(bitmap_results.empty());
2395 }
2396
2397 // Test the results of GetFaviconsFromDB() when there are matching favicons
2398 // but there are no associated favicon bitmaps.
TEST_F(HistoryBackendTest,GetFaviconsFromDBNoFaviconBitmaps)2399 TEST_F(HistoryBackendTest, GetFaviconsFromDBNoFaviconBitmaps) {
2400 const GURL page_url("http://www.google.com/");
2401 const GURL icon_url("http://www.google.com/icon1");
2402
2403 favicon_base::FaviconID icon_id =
2404 backend_->thumbnail_db_->AddFavicon(icon_url, favicon_base::FAVICON);
2405 EXPECT_NE(0, icon_id);
2406 EXPECT_NE(0, backend_->thumbnail_db_->AddIconMapping(page_url, icon_id));
2407
2408 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
2409 EXPECT_FALSE(backend_->GetFaviconsFromDB(page_url,
2410 favicon_base::FAVICON,
2411 GetEdgeSizesSmallAndLarge(),
2412 &bitmap_results_out));
2413 EXPECT_TRUE(bitmap_results_out.empty());
2414 }
2415
2416 // Test that GetFaviconsFromDB() returns results for the bitmaps which most
2417 // closely match the passed in the desired pixel sizes.
TEST_F(HistoryBackendTest,GetFaviconsFromDBSelectClosestMatch)2418 TEST_F(HistoryBackendTest, GetFaviconsFromDBSelectClosestMatch) {
2419 const GURL page_url("http://www.google.com/");
2420 const GURL icon_url("http://www.google.com/icon1");
2421
2422 std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
2423 GenerateFaviconBitmapData(icon_url, GetEdgeSizesTinySmallAndLarge(),
2424 &favicon_bitmap_data);
2425
2426 backend_->SetFavicons(page_url, favicon_base::FAVICON, favicon_bitmap_data);
2427
2428 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
2429 EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
2430 favicon_base::FAVICON,
2431 GetEdgeSizesSmallAndLarge(),
2432 &bitmap_results_out));
2433
2434 // The bitmap data for the small and large bitmaps should be returned as their
2435 // sizes match exactly.
2436 EXPECT_EQ(2u, bitmap_results_out.size());
2437 // No required order for results.
2438 if (bitmap_results_out[0].pixel_size == kLargeSize) {
2439 favicon_base::FaviconRawBitmapResult tmp_result = bitmap_results_out[0];
2440 bitmap_results_out[0] = bitmap_results_out[1];
2441 bitmap_results_out[1] = tmp_result;
2442 }
2443
2444 EXPECT_FALSE(bitmap_results_out[0].expired);
2445 EXPECT_TRUE(BitmapDataEqual('b', bitmap_results_out[0].bitmap_data));
2446 EXPECT_EQ(kSmallSize, bitmap_results_out[0].pixel_size);
2447 EXPECT_EQ(icon_url, bitmap_results_out[0].icon_url);
2448 EXPECT_EQ(favicon_base::FAVICON, bitmap_results_out[0].icon_type);
2449
2450 EXPECT_FALSE(bitmap_results_out[1].expired);
2451 EXPECT_TRUE(BitmapDataEqual('c', bitmap_results_out[1].bitmap_data));
2452 EXPECT_EQ(kLargeSize, bitmap_results_out[1].pixel_size);
2453 EXPECT_EQ(icon_url, bitmap_results_out[1].icon_url);
2454 EXPECT_EQ(favicon_base::FAVICON, bitmap_results_out[1].icon_type);
2455 }
2456
2457 // Test that GetFaviconsFromDB() returns results from the icon URL whose
2458 // bitmaps most closely match the passed in desired sizes.
TEST_F(HistoryBackendTest,GetFaviconsFromDBSingleIconURL)2459 TEST_F(HistoryBackendTest, GetFaviconsFromDBSingleIconURL) {
2460 const GURL page_url("http://www.google.com/");
2461
2462 const GURL icon_url1("http://www.google.com/icon1");
2463 const GURL icon_url2("http://www.google.com/icon2");
2464
2465 std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
2466 GenerateFaviconBitmapData(icon_url1, GetEdgeSizesSmall(), icon_url2,
2467 GetEdgeSizesLarge(), &favicon_bitmap_data);
2468
2469 backend_->SetFavicons(page_url, favicon_base::FAVICON, favicon_bitmap_data);
2470
2471 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
2472 EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
2473 favicon_base::FAVICON,
2474 GetEdgeSizesSmallAndLarge(),
2475 &bitmap_results_out));
2476
2477 // The results should have results for the icon URL with the large bitmap as
2478 // downscaling is preferred to upscaling.
2479 EXPECT_EQ(1u, bitmap_results_out.size());
2480 EXPECT_EQ(kLargeSize, bitmap_results_out[0].pixel_size);
2481 EXPECT_EQ(icon_url2, bitmap_results_out[0].icon_url);
2482 }
2483
2484 // Test the results of GetFaviconsFromDB() when called with different
2485 // |icon_types|.
TEST_F(HistoryBackendTest,GetFaviconsFromDBIconType)2486 TEST_F(HistoryBackendTest, GetFaviconsFromDBIconType) {
2487 const GURL page_url("http://www.google.com/");
2488 const GURL icon_url1("http://www.google.com/icon1.png");
2489 const GURL icon_url2("http://www.google.com/icon2.png");
2490
2491 std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
2492 GenerateFaviconBitmapData(icon_url1, GetEdgeSizesSmall(),
2493 &favicon_bitmap_data);
2494 backend_->SetFavicons(page_url, favicon_base::FAVICON, favicon_bitmap_data);
2495
2496 GenerateFaviconBitmapData(
2497 icon_url2, GetEdgeSizesSmall(), &favicon_bitmap_data);
2498 backend_->SetFavicons(
2499 page_url, favicon_base::TOUCH_ICON, favicon_bitmap_data);
2500
2501 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
2502 EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
2503 favicon_base::FAVICON,
2504 GetEdgeSizesSmallAndLarge(),
2505 &bitmap_results_out));
2506
2507 EXPECT_EQ(1u, bitmap_results_out.size());
2508 EXPECT_EQ(favicon_base::FAVICON, bitmap_results_out[0].icon_type);
2509 EXPECT_EQ(icon_url1, bitmap_results_out[0].icon_url);
2510
2511 bitmap_results_out.clear();
2512 EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
2513 favicon_base::TOUCH_ICON,
2514 GetEdgeSizesSmallAndLarge(),
2515 &bitmap_results_out));
2516
2517 EXPECT_EQ(1u, bitmap_results_out.size());
2518 EXPECT_EQ(favicon_base::TOUCH_ICON, bitmap_results_out[0].icon_type);
2519 EXPECT_EQ(icon_url2, bitmap_results_out[0].icon_url);
2520 }
2521
2522 // Test that GetFaviconsFromDB() correctly sets the expired flag for bitmap
2523 // reults.
TEST_F(HistoryBackendTest,GetFaviconsFromDBExpired)2524 TEST_F(HistoryBackendTest, GetFaviconsFromDBExpired) {
2525 const GURL page_url("http://www.google.com/");
2526 const GURL icon_url("http://www.google.com/icon.png");
2527
2528 std::vector<unsigned char> data;
2529 data.push_back('a');
2530 scoped_refptr<base::RefCountedBytes> bitmap_data(
2531 base::RefCountedBytes::TakeVector(&data));
2532 base::Time last_updated = base::Time::FromTimeT(0);
2533 favicon_base::FaviconID icon_id = backend_->thumbnail_db_->AddFavicon(
2534 icon_url, favicon_base::FAVICON, bitmap_data, last_updated, kSmallSize);
2535 EXPECT_NE(0, icon_id);
2536 EXPECT_NE(0, backend_->thumbnail_db_->AddIconMapping(page_url, icon_id));
2537
2538 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
2539 EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
2540 favicon_base::FAVICON,
2541 GetEdgeSizesSmallAndLarge(),
2542 &bitmap_results_out));
2543
2544 EXPECT_EQ(1u, bitmap_results_out.size());
2545 EXPECT_TRUE(bitmap_results_out[0].expired);
2546 }
2547
2548 // Check that UpdateFaviconMappingsAndFetch() call back to the UI when there is
2549 // no valid thumbnail database.
TEST_F(HistoryBackendTest,UpdateFaviconMappingsAndFetchNoDB)2550 TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchNoDB) {
2551 // Make the thumbnail database invalid.
2552 backend_->thumbnail_db_.reset();
2553
2554 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
2555
2556 backend_->UpdateFaviconMappingsAndFetch(GURL(),
2557 std::vector<GURL>(),
2558 favicon_base::FAVICON,
2559 GetEdgeSizesSmallAndLarge(),
2560 &bitmap_results);
2561
2562 EXPECT_TRUE(bitmap_results.empty());
2563 }
2564
TEST_F(HistoryBackendTest,CloneFaviconIsRestrictedToSameDomain)2565 TEST_F(HistoryBackendTest, CloneFaviconIsRestrictedToSameDomain) {
2566 const GURL url("http://www.google.com/");
2567 const GURL same_domain_url("http://www.google.com/subdir/index.html");
2568 const GURL foreign_domain_url("http://www.not-google.com/");
2569 const GURL icon_url("http://www.google.com/icon.png");
2570
2571 // Add a favicon
2572 std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
2573 GenerateFaviconBitmapData(icon_url, GetEdgeSizesSmall(),
2574 &favicon_bitmap_data);
2575 backend_->SetFavicons(url, favicon_base::FAVICON, favicon_bitmap_data);
2576 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
2577 url, favicon_base::FAVICON, NULL));
2578
2579 // Validate starting state.
2580 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
2581 EXPECT_TRUE(backend_->GetFaviconsFromDB(url,
2582 favicon_base::FAVICON,
2583 GetEdgeSizesSmallAndLarge(),
2584 &bitmap_results_out));
2585 EXPECT_FALSE(backend_->GetFaviconsFromDB(same_domain_url,
2586 favicon_base::FAVICON,
2587 GetEdgeSizesSmallAndLarge(),
2588 &bitmap_results_out));
2589 EXPECT_FALSE(backend_->GetFaviconsFromDB(foreign_domain_url,
2590 favicon_base::FAVICON,
2591 GetEdgeSizesSmallAndLarge(),
2592 &bitmap_results_out));
2593
2594 // Same-domain cloning should work.
2595 backend_->CloneFavicons(url, same_domain_url);
2596 EXPECT_TRUE(backend_->GetFaviconsFromDB(same_domain_url,
2597 favicon_base::FAVICON,
2598 GetEdgeSizesSmallAndLarge(),
2599 &bitmap_results_out));
2600
2601 // Foreign-domain cloning is forbidden.
2602 backend_->CloneFavicons(url, foreign_domain_url);
2603 EXPECT_FALSE(backend_->GetFaviconsFromDB(foreign_domain_url,
2604 favicon_base::FAVICON,
2605 GetEdgeSizesSmallAndLarge(),
2606 &bitmap_results_out));
2607 }
2608
TEST_F(HistoryBackendTest,QueryFilteredURLs)2609 TEST_F(HistoryBackendTest, QueryFilteredURLs) {
2610 const char* google = "http://www.google.com/";
2611 const char* yahoo = "http://www.yahoo.com/";
2612 const char* yahoo_sports = "http://sports.yahoo.com/";
2613 const char* yahoo_sports_with_article1 =
2614 "http://sports.yahoo.com/article1.htm";
2615 const char* yahoo_sports_with_article2 =
2616 "http://sports.yahoo.com/article2.htm";
2617 const char* yahoo_sports_soccer = "http://sports.yahoo.com/soccer";
2618 const char* apple = "http://www.apple.com/";
2619
2620 // Clear all history.
2621 backend_->DeleteAllHistory();
2622
2623 Time tested_time = Time::Now().LocalMidnight() +
2624 base::TimeDelta::FromHours(4);
2625 base::TimeDelta half_an_hour = base::TimeDelta::FromMinutes(30);
2626 base::TimeDelta one_hour = base::TimeDelta::FromHours(1);
2627 base::TimeDelta one_day = base::TimeDelta::FromDays(1);
2628
2629 const content::PageTransition kTypedTransition =
2630 content::PAGE_TRANSITION_TYPED;
2631 const content::PageTransition kKeywordGeneratedTransition =
2632 content::PAGE_TRANSITION_KEYWORD_GENERATED;
2633
2634 const char* redirect_sequence[2];
2635 redirect_sequence[1] = NULL;
2636
2637 redirect_sequence[0] = google;
2638 AddRedirectChainWithTransitionAndTime(
2639 redirect_sequence, 0, kTypedTransition,
2640 tested_time - one_day - half_an_hour * 2);
2641 AddRedirectChainWithTransitionAndTime(
2642 redirect_sequence, 0,
2643 kTypedTransition, tested_time - one_day);
2644 AddRedirectChainWithTransitionAndTime(
2645 redirect_sequence, 0,
2646 kTypedTransition, tested_time - half_an_hour / 2);
2647 AddRedirectChainWithTransitionAndTime(
2648 redirect_sequence, 0,
2649 kTypedTransition, tested_time);
2650
2651 // Add a visit with a transition that will make sure that no segment gets
2652 // created for this page (so the subsequent entries will have different URLIDs
2653 // and SegmentIDs).
2654 redirect_sequence[0] = apple;
2655 AddRedirectChainWithTransitionAndTime(
2656 redirect_sequence, 0, kKeywordGeneratedTransition,
2657 tested_time - one_day + one_hour * 6);
2658
2659 redirect_sequence[0] = yahoo;
2660 AddRedirectChainWithTransitionAndTime(
2661 redirect_sequence, 0, kTypedTransition,
2662 tested_time - one_day + half_an_hour);
2663 AddRedirectChainWithTransitionAndTime(
2664 redirect_sequence, 0, kTypedTransition,
2665 tested_time - one_day + half_an_hour * 2);
2666
2667 redirect_sequence[0] = yahoo_sports;
2668 AddRedirectChainWithTransitionAndTime(
2669 redirect_sequence, 0, kTypedTransition,
2670 tested_time - one_day - half_an_hour * 2);
2671 AddRedirectChainWithTransitionAndTime(
2672 redirect_sequence, 0, kTypedTransition,
2673 tested_time - one_day);
2674 int transition1, transition2;
2675 AddClientRedirect(GURL(yahoo_sports), GURL(yahoo_sports_with_article1), false,
2676 tested_time - one_day + half_an_hour,
2677 &transition1, &transition2);
2678 AddClientRedirect(GURL(yahoo_sports_with_article1),
2679 GURL(yahoo_sports_with_article2),
2680 false,
2681 tested_time - one_day + half_an_hour * 2,
2682 &transition1, &transition2);
2683
2684 redirect_sequence[0] = yahoo_sports_soccer;
2685 AddRedirectChainWithTransitionAndTime(redirect_sequence, 0,
2686 kTypedTransition,
2687 tested_time - half_an_hour);
2688 backend_->Commit();
2689
2690 scoped_refptr<QueryFilteredURLsRequest> request1 =
2691 new history::QueryFilteredURLsRequest(
2692 base::Bind(&HistoryBackendTest::OnQueryFiltered,
2693 base::Unretained(static_cast<HistoryBackendTest*>(this))));
2694 HistoryBackendCancelableRequest cancellable_request;
2695 cancellable_request.MockScheduleOfRequest<QueryFilteredURLsRequest>(
2696 request1.get());
2697
2698 VisitFilter filter;
2699 // Time limit is |tested_time| +/- 45 min.
2700 base::TimeDelta three_quarters_of_an_hour = base::TimeDelta::FromMinutes(45);
2701 filter.SetFilterTime(tested_time);
2702 filter.SetFilterWidth(three_quarters_of_an_hour);
2703 backend_->QueryFilteredURLs(request1, 100, filter, false);
2704
2705 ASSERT_EQ(4U, get_filtered_list().size());
2706 EXPECT_EQ(std::string(google), get_filtered_list()[0].url.spec());
2707 EXPECT_EQ(std::string(yahoo_sports_soccer),
2708 get_filtered_list()[1].url.spec());
2709 EXPECT_EQ(std::string(yahoo), get_filtered_list()[2].url.spec());
2710 EXPECT_EQ(std::string(yahoo_sports),
2711 get_filtered_list()[3].url.spec());
2712
2713 // Time limit is between |tested_time| and |tested_time| + 2 hours.
2714 scoped_refptr<QueryFilteredURLsRequest> request2 =
2715 new history::QueryFilteredURLsRequest(
2716 base::Bind(&HistoryBackendTest::OnQueryFiltered,
2717 base::Unretained(static_cast<HistoryBackendTest*>(this))));
2718 cancellable_request.MockScheduleOfRequest<QueryFilteredURLsRequest>(
2719 request2.get());
2720 filter.SetFilterTime(tested_time + one_hour);
2721 filter.SetFilterWidth(one_hour);
2722 backend_->QueryFilteredURLs(request2, 100, filter, false);
2723
2724 ASSERT_EQ(3U, get_filtered_list().size());
2725 EXPECT_EQ(std::string(google), get_filtered_list()[0].url.spec());
2726 EXPECT_EQ(std::string(yahoo), get_filtered_list()[1].url.spec());
2727 EXPECT_EQ(std::string(yahoo_sports), get_filtered_list()[2].url.spec());
2728
2729 // Time limit is between |tested_time| - 2 hours and |tested_time|.
2730 scoped_refptr<QueryFilteredURLsRequest> request3 =
2731 new history::QueryFilteredURLsRequest(
2732 base::Bind(&HistoryBackendTest::OnQueryFiltered,
2733 base::Unretained(static_cast<HistoryBackendTest*>(this))));
2734 cancellable_request.MockScheduleOfRequest<QueryFilteredURLsRequest>(
2735 request3.get());
2736 filter.SetFilterTime(tested_time - one_hour);
2737 filter.SetFilterWidth(one_hour);
2738 backend_->QueryFilteredURLs(request3, 100, filter, false);
2739
2740 ASSERT_EQ(3U, get_filtered_list().size());
2741 EXPECT_EQ(std::string(google), get_filtered_list()[0].url.spec());
2742 EXPECT_EQ(std::string(yahoo_sports_soccer),
2743 get_filtered_list()[1].url.spec());
2744 EXPECT_EQ(std::string(yahoo_sports), get_filtered_list()[2].url.spec());
2745
2746 filter.ClearFilters();
2747 base::Time::Exploded exploded_time;
2748 tested_time.LocalExplode(&exploded_time);
2749
2750 // Today.
2751 scoped_refptr<QueryFilteredURLsRequest> request4 =
2752 new history::QueryFilteredURLsRequest(
2753 base::Bind(&HistoryBackendTest::OnQueryFiltered,
2754 base::Unretained(static_cast<HistoryBackendTest*>(this))));
2755 cancellable_request.MockScheduleOfRequest<QueryFilteredURLsRequest>(
2756 request4.get());
2757 filter.SetFilterTime(tested_time);
2758 filter.SetDayOfTheWeekFilter(static_cast<int>(exploded_time.day_of_week));
2759 backend_->QueryFilteredURLs(request4, 100, filter, false);
2760
2761 ASSERT_EQ(2U, get_filtered_list().size());
2762 EXPECT_EQ(std::string(google), get_filtered_list()[0].url.spec());
2763 EXPECT_EQ(std::string(yahoo_sports_soccer),
2764 get_filtered_list()[1].url.spec());
2765
2766 // Today + time limit - only yahoo_sports_soccer should fit.
2767 scoped_refptr<QueryFilteredURLsRequest> request5 =
2768 new history::QueryFilteredURLsRequest(
2769 base::Bind(&HistoryBackendTest::OnQueryFiltered,
2770 base::Unretained(static_cast<HistoryBackendTest*>(this))));
2771 cancellable_request.MockScheduleOfRequest<QueryFilteredURLsRequest>(
2772 request5.get());
2773 filter.SetFilterTime(tested_time - base::TimeDelta::FromMinutes(40));
2774 filter.SetFilterWidth(base::TimeDelta::FromMinutes(20));
2775 backend_->QueryFilteredURLs(request5, 100, filter, false);
2776
2777 ASSERT_EQ(1U, get_filtered_list().size());
2778 EXPECT_EQ(std::string(yahoo_sports_soccer),
2779 get_filtered_list()[0].url.spec());
2780
2781 // Make sure we get debug data if we request it.
2782 scoped_refptr<QueryFilteredURLsRequest> request6 =
2783 new history::QueryFilteredURLsRequest(
2784 base::Bind(&HistoryBackendTest::OnQueryFiltered,
2785 base::Unretained(static_cast<HistoryBackendTest*>(this))));
2786 cancellable_request.MockScheduleOfRequest<QueryFilteredURLsRequest>(
2787 request6.get());
2788 filter.SetFilterTime(tested_time);
2789 filter.SetFilterWidth(one_hour * 2);
2790 backend_->QueryFilteredURLs(request6, 100, filter, true);
2791
2792 // If the SegmentID is used by QueryFilteredURLs when generating the debug
2793 // data instead of the URLID, the |total_visits| for the |yahoo_sports_soccer|
2794 // entry will be zero instead of 1.
2795 ASSERT_GE(get_filtered_list().size(), 2U);
2796 EXPECT_EQ(std::string(google), get_filtered_list()[0].url.spec());
2797 EXPECT_EQ(std::string(yahoo_sports_soccer),
2798 get_filtered_list()[1].url.spec());
2799 EXPECT_EQ(4U, get_filtered_list()[0].extended_info.total_visits);
2800 EXPECT_EQ(1U, get_filtered_list()[1].extended_info.total_visits);
2801 }
2802
TEST_F(HistoryBackendTest,UpdateVisitDuration)2803 TEST_F(HistoryBackendTest, UpdateVisitDuration) {
2804 // This unit test will test adding and deleting visit details information.
2805 ASSERT_TRUE(backend_.get());
2806
2807 GURL url1("http://www.cnn.com");
2808 std::vector<VisitInfo> visit_info1, visit_info2;
2809 Time start_ts = Time::Now() - base::TimeDelta::FromDays(5);
2810 Time end_ts = start_ts + base::TimeDelta::FromDays(2);
2811 visit_info1.push_back(VisitInfo(start_ts, content::PAGE_TRANSITION_LINK));
2812
2813 GURL url2("http://www.example.com");
2814 visit_info2.push_back(VisitInfo(Time::Now() - base::TimeDelta::FromDays(10),
2815 content::PAGE_TRANSITION_LINK));
2816
2817 // Clear all history.
2818 backend_->DeleteAllHistory();
2819
2820 // Add the visits.
2821 backend_->AddVisits(url1, visit_info1, history::SOURCE_BROWSED);
2822 backend_->AddVisits(url2, visit_info2, history::SOURCE_BROWSED);
2823
2824 // Verify the entries for both visits were added in visit_details.
2825 VisitVector visits1, visits2;
2826 URLRow row;
2827 URLID url_id1 = backend_->db()->GetRowForURL(url1, &row);
2828 ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id1, &visits1));
2829 ASSERT_EQ(1U, visits1.size());
2830 EXPECT_EQ(0, visits1[0].visit_duration.ToInternalValue());
2831
2832 URLID url_id2 = backend_->db()->GetRowForURL(url2, &row);
2833 ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id2, &visits2));
2834 ASSERT_EQ(1U, visits2.size());
2835 EXPECT_EQ(0, visits2[0].visit_duration.ToInternalValue());
2836
2837 // Update the visit to cnn.com.
2838 backend_->UpdateVisitDuration(visits1[0].visit_id, end_ts);
2839
2840 // Check the duration for visiting cnn.com was correctly updated.
2841 ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id1, &visits1));
2842 ASSERT_EQ(1U, visits1.size());
2843 base::TimeDelta expected_duration = end_ts - start_ts;
2844 EXPECT_EQ(expected_duration.ToInternalValue(),
2845 visits1[0].visit_duration.ToInternalValue());
2846
2847 // Remove the visit to cnn.com.
2848 ASSERT_TRUE(backend_->RemoveVisits(visits1));
2849 }
2850
2851 // Test for migration of adding visit_duration column.
TEST_F(HistoryBackendTest,MigrationVisitDuration)2852 TEST_F(HistoryBackendTest, MigrationVisitDuration) {
2853 ASSERT_TRUE(backend_.get());
2854 backend_->Closing();
2855 backend_ = NULL;
2856
2857 base::FilePath old_history_path, old_history;
2858 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &old_history_path));
2859 old_history_path = old_history_path.AppendASCII("History");
2860 old_history = old_history_path.AppendASCII("HistoryNoDuration");
2861
2862 // Copy history database file to current directory so that it will be deleted
2863 // in Teardown.
2864 base::FilePath new_history_path(test_dir());
2865 base::DeleteFile(new_history_path, true);
2866 base::CreateDirectory(new_history_path);
2867 base::FilePath new_history_file =
2868 new_history_path.Append(chrome::kHistoryFilename);
2869 ASSERT_TRUE(base::CopyFile(old_history, new_history_file));
2870
2871 backend_ = new HistoryBackend(
2872 new_history_path, new HistoryBackendTestDelegate(this), &history_client_);
2873 backend_->Init(std::string(), false);
2874 backend_->Closing();
2875 backend_ = NULL;
2876
2877 // Now the history database should already be migrated.
2878
2879 // Check version in history database first.
2880 int cur_version = HistoryDatabase::GetCurrentVersion();
2881 sql::Connection db;
2882 ASSERT_TRUE(db.Open(new_history_file));
2883 sql::Statement s(db.GetUniqueStatement(
2884 "SELECT value FROM meta WHERE key = 'version'"));
2885 ASSERT_TRUE(s.Step());
2886 int file_version = s.ColumnInt(0);
2887 EXPECT_EQ(cur_version, file_version);
2888
2889 // Check visit_duration column in visits table is created and set to 0.
2890 s.Assign(db.GetUniqueStatement(
2891 "SELECT visit_duration FROM visits LIMIT 1"));
2892 ASSERT_TRUE(s.Step());
2893 EXPECT_EQ(0, s.ColumnInt(0));
2894 }
2895
TEST_F(HistoryBackendTest,AddPageNoVisitForBookmark)2896 TEST_F(HistoryBackendTest, AddPageNoVisitForBookmark) {
2897 ASSERT_TRUE(backend_.get());
2898
2899 GURL url("http://www.google.com");
2900 base::string16 title(base::UTF8ToUTF16("Bookmark title"));
2901 backend_->AddPageNoVisitForBookmark(url, title);
2902
2903 URLRow row;
2904 backend_->GetURL(url, &row);
2905 EXPECT_EQ(url, row.url());
2906 EXPECT_EQ(title, row.title());
2907 EXPECT_EQ(0, row.visit_count());
2908
2909 backend_->DeleteURL(url);
2910 backend_->AddPageNoVisitForBookmark(url, base::string16());
2911 backend_->GetURL(url, &row);
2912 EXPECT_EQ(url, row.url());
2913 EXPECT_EQ(base::UTF8ToUTF16(url.spec()), row.title());
2914 EXPECT_EQ(0, row.visit_count());
2915 }
2916
TEST_F(HistoryBackendTest,ExpireHistoryForTimes)2917 TEST_F(HistoryBackendTest, ExpireHistoryForTimes) {
2918 ASSERT_TRUE(backend_.get());
2919
2920 HistoryAddPageArgs args[10];
2921 for (size_t i = 0; i < arraysize(args); ++i) {
2922 args[i].url = GURL("http://example" +
2923 std::string((i % 2 == 0 ? ".com" : ".net")));
2924 args[i].time = base::Time::FromInternalValue(i);
2925 backend_->AddPage(args[i]);
2926 }
2927 EXPECT_EQ(base::Time(), backend_->GetFirstRecordedTimeForTest());
2928
2929 URLRow row;
2930 for (size_t i = 0; i < arraysize(args); ++i) {
2931 EXPECT_TRUE(backend_->GetURL(args[i].url, &row));
2932 }
2933
2934 std::set<base::Time> times;
2935 times.insert(args[5].time);
2936 backend_->ExpireHistoryForTimes(times,
2937 base::Time::FromInternalValue(2),
2938 base::Time::FromInternalValue(8));
2939
2940 EXPECT_EQ(base::Time::FromInternalValue(0),
2941 backend_->GetFirstRecordedTimeForTest());
2942
2943 // Visits to http://example.com are untouched.
2944 VisitVector visit_vector;
2945 EXPECT_TRUE(backend_->GetVisitsForURL(
2946 backend_->db_->GetRowForURL(GURL("http://example.com"), NULL),
2947 &visit_vector));
2948 ASSERT_EQ(5u, visit_vector.size());
2949 EXPECT_EQ(base::Time::FromInternalValue(0), visit_vector[0].visit_time);
2950 EXPECT_EQ(base::Time::FromInternalValue(2), visit_vector[1].visit_time);
2951 EXPECT_EQ(base::Time::FromInternalValue(4), visit_vector[2].visit_time);
2952 EXPECT_EQ(base::Time::FromInternalValue(6), visit_vector[3].visit_time);
2953 EXPECT_EQ(base::Time::FromInternalValue(8), visit_vector[4].visit_time);
2954
2955 // Visits to http://example.net between [2,8] are removed.
2956 visit_vector.clear();
2957 EXPECT_TRUE(backend_->GetVisitsForURL(
2958 backend_->db_->GetRowForURL(GURL("http://example.net"), NULL),
2959 &visit_vector));
2960 ASSERT_EQ(2u, visit_vector.size());
2961 EXPECT_EQ(base::Time::FromInternalValue(1), visit_vector[0].visit_time);
2962 EXPECT_EQ(base::Time::FromInternalValue(9), visit_vector[1].visit_time);
2963
2964 EXPECT_EQ(base::Time::FromInternalValue(0),
2965 backend_->GetFirstRecordedTimeForTest());
2966 }
2967
TEST_F(HistoryBackendTest,ExpireHistory)2968 TEST_F(HistoryBackendTest, ExpireHistory) {
2969 ASSERT_TRUE(backend_.get());
2970 // Since history operations are dependent on the local timezone, make all
2971 // entries relative to a fixed, local reference time.
2972 base::Time reference_time = base::Time::UnixEpoch().LocalMidnight() +
2973 base::TimeDelta::FromHours(12);
2974
2975 // Insert 4 entries into the database.
2976 HistoryAddPageArgs args[4];
2977 for (size_t i = 0; i < arraysize(args); ++i) {
2978 args[i].url = GURL("http://example" + base::IntToString(i) + ".com");
2979 args[i].time = reference_time + base::TimeDelta::FromDays(i);
2980 backend_->AddPage(args[i]);
2981 }
2982
2983 URLRow url_rows[4];
2984 for (unsigned int i = 0; i < arraysize(args); ++i)
2985 ASSERT_TRUE(backend_->GetURL(args[i].url, &url_rows[i]));
2986
2987 std::vector<ExpireHistoryArgs> expire_list;
2988 VisitVector visits;
2989
2990 // Passing an empty map should be a no-op.
2991 backend_->ExpireHistory(expire_list);
2992 backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
2993 EXPECT_EQ(4U, visits.size());
2994
2995 // Trying to delete an unknown URL with the time of the first visit should
2996 // also be a no-op.
2997 expire_list.resize(expire_list.size() + 1);
2998 expire_list[0].SetTimeRangeForOneDay(args[0].time);
2999 expire_list[0].urls.insert(GURL("http://google.does-not-exist"));
3000 backend_->ExpireHistory(expire_list);
3001 backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
3002 EXPECT_EQ(4U, visits.size());
3003
3004 // Now add the first URL with the same time -- it should get deleted.
3005 expire_list.back().urls.insert(url_rows[0].url());
3006 backend_->ExpireHistory(expire_list);
3007
3008 backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
3009 ASSERT_EQ(3U, visits.size());
3010 EXPECT_EQ(visits[0].url_id, url_rows[1].id());
3011 EXPECT_EQ(visits[1].url_id, url_rows[2].id());
3012 EXPECT_EQ(visits[2].url_id, url_rows[3].id());
3013
3014 // The first recorded time should also get updated.
3015 EXPECT_EQ(backend_->GetFirstRecordedTimeForTest(), args[1].time);
3016
3017 // Now delete the rest of the visits in one call.
3018 for (unsigned int i = 1; i < arraysize(args); ++i) {
3019 expire_list.resize(expire_list.size() + 1);
3020 expire_list[i].SetTimeRangeForOneDay(args[i].time);
3021 expire_list[i].urls.insert(args[i].url);
3022 }
3023 backend_->ExpireHistory(expire_list);
3024
3025 backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
3026 ASSERT_EQ(0U, visits.size());
3027 }
3028
TEST_F(HistoryBackendTest,DeleteMatchingUrlsForKeyword)3029 TEST_F(HistoryBackendTest, DeleteMatchingUrlsForKeyword) {
3030 // Set up urls and keyword_search_terms
3031 GURL url1("https://www.bing.com/?q=bar");
3032 URLRow url_info1(url1);
3033 url_info1.set_visit_count(0);
3034 url_info1.set_typed_count(0);
3035 url_info1.set_last_visit(Time());
3036 url_info1.set_hidden(false);
3037 const URLID url1_id = backend_->db()->AddURL(url_info1);
3038 EXPECT_NE(0, url1_id);
3039
3040 TemplateURLID keyword_id = 1;
3041 base::string16 keyword = base::UTF8ToUTF16("bar");
3042 ASSERT_TRUE(backend_->db()->SetKeywordSearchTermsForURL(
3043 url1_id, keyword_id, keyword));
3044
3045 GURL url2("https://www.google.com/?q=bar");
3046 URLRow url_info2(url2);
3047 url_info2.set_visit_count(0);
3048 url_info2.set_typed_count(0);
3049 url_info2.set_last_visit(Time());
3050 url_info2.set_hidden(false);
3051 const URLID url2_id = backend_->db()->AddURL(url_info2);
3052 EXPECT_NE(0, url2_id);
3053
3054 TemplateURLID keyword_id2 = 2;
3055 ASSERT_TRUE(backend_->db()->SetKeywordSearchTermsForURL(
3056 url2_id, keyword_id2, keyword));
3057
3058 // Add another visit to the same URL
3059 URLRow url_info3(url2);
3060 url_info3.set_visit_count(0);
3061 url_info3.set_typed_count(0);
3062 url_info3.set_last_visit(Time());
3063 url_info3.set_hidden(false);
3064 const URLID url3_id = backend_->db()->AddURL(url_info3);
3065 EXPECT_NE(0, url3_id);
3066 ASSERT_TRUE(backend_->db()->SetKeywordSearchTermsForURL(
3067 url3_id, keyword_id2, keyword));
3068
3069 // Test that deletion works correctly
3070 backend_->DeleteMatchingURLsForKeyword(keyword_id2, keyword);
3071
3072 // Test that rows 2 and 3 are deleted, while 1 is intact
3073 URLRow row;
3074 EXPECT_TRUE(backend_->db()->GetURLRow(url1_id, &row));
3075 EXPECT_EQ(url1.spec(), row.url().spec());
3076 EXPECT_FALSE(backend_->db()->GetURLRow(url2_id, &row));
3077 EXPECT_FALSE(backend_->db()->GetURLRow(url3_id, &row));
3078
3079 // Test that corresponding keyword search terms are deleted for rows 2 & 3,
3080 // but not for row 1
3081 EXPECT_TRUE(backend_->db()->GetKeywordSearchTermRow(url1_id, NULL));
3082 EXPECT_FALSE(backend_->db()->GetKeywordSearchTermRow(url2_id, NULL));
3083 EXPECT_FALSE(backend_->db()->GetKeywordSearchTermRow(url3_id, NULL));
3084 }
3085
3086 // Simple test that removes a bookmark. This test exercises the code paths in
3087 // History that block till bookmark bar model is loaded.
TEST_F(HistoryBackendTest,RemoveNotification)3088 TEST_F(HistoryBackendTest, RemoveNotification) {
3089 scoped_ptr<TestingProfile> profile(new TestingProfile());
3090
3091 // Add a URL.
3092 GURL url("http://www.google.com");
3093 HistoryClientMock history_client;
3094 history_client.AddBookmark(url);
3095 scoped_ptr<HistoryService> service(
3096 new HistoryService(&history_client, profile.get()));
3097 EXPECT_TRUE(service->Init(profile->GetPath()));
3098
3099 service->AddPage(
3100 url, base::Time::Now(), NULL, 1, GURL(), RedirectList(),
3101 content::PAGE_TRANSITION_TYPED, SOURCE_BROWSED, false);
3102
3103 // This won't actually delete the URL, rather it'll empty out the visits.
3104 // This triggers blocking on the BookmarkModel.
3105 EXPECT_CALL(history_client, BlockUntilBookmarksLoaded());
3106 service->DeleteURL(url);
3107 }
3108
3109 // Test DeleteFTSIndexDatabases deletes expected files.
TEST_F(HistoryBackendTest,DeleteFTSIndexDatabases)3110 TEST_F(HistoryBackendTest, DeleteFTSIndexDatabases) {
3111 ASSERT_TRUE(backend_.get());
3112
3113 base::FilePath history_path(test_dir());
3114 base::FilePath db1(history_path.AppendASCII("History Index 2013-05"));
3115 base::FilePath db1_journal(db1.InsertBeforeExtensionASCII("-journal"));
3116 base::FilePath db1_wal(db1.InsertBeforeExtensionASCII("-wal"));
3117 base::FilePath db2_symlink(history_path.AppendASCII("History Index 2013-06"));
3118 base::FilePath db2_actual(history_path.AppendASCII("Underlying DB"));
3119
3120 // Setup dummy index database files.
3121 const char* data = "Dummy";
3122 const size_t data_len = 5;
3123 ASSERT_TRUE(base::WriteFile(db1, data, data_len));
3124 ASSERT_TRUE(base::WriteFile(db1_journal, data, data_len));
3125 ASSERT_TRUE(base::WriteFile(db1_wal, data, data_len));
3126 ASSERT_TRUE(base::WriteFile(db2_actual, data, data_len));
3127 #if defined(OS_POSIX)
3128 EXPECT_TRUE(base::CreateSymbolicLink(db2_actual, db2_symlink));
3129 #endif
3130
3131 // Delete all DTS index databases.
3132 backend_->DeleteFTSIndexDatabases();
3133 EXPECT_FALSE(base::PathExists(db1));
3134 EXPECT_FALSE(base::PathExists(db1_wal));
3135 EXPECT_FALSE(base::PathExists(db1_journal));
3136 EXPECT_FALSE(base::PathExists(db2_symlink));
3137 EXPECT_TRUE(base::PathExists(db2_actual)); // Symlinks shouldn't be followed.
3138 }
3139
3140 // Common implementation for the two tests below, given that the only difference
3141 // between them is the type of the notification sent out.
TestAddingAndChangingURLRows(int notification_type)3142 void InMemoryHistoryBackendTest::TestAddingAndChangingURLRows(
3143 int notification_type) {
3144 const char kTestTypedURLAlternativeTitle[] = "Google Search Again";
3145 const char kTestNonTypedURLAlternativeTitle[] = "Google News Again";
3146
3147 // Notify the in-memory database that a typed and non-typed URLRow (which were
3148 // never before seen by the cache) have been modified.
3149 URLRow row1(CreateTestTypedURL());
3150 URLRow row2(CreateTestNonTypedURL());
3151 SimulateNotification(notification_type, &row1, &row2);
3152
3153 // The in-memory database should only pick up the typed URL, and should ignore
3154 // the non-typed one. The typed URL should retain the ID that was present in
3155 // the notification.
3156 URLRow cached_row1, cached_row2;
3157 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
3158 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2));
3159 EXPECT_EQ(row1.id(), cached_row1.id());
3160
3161 // Try changing attributes (other than typed_count) for existing URLRows.
3162 row1.set_title(base::UTF8ToUTF16(kTestTypedURLAlternativeTitle));
3163 row2.set_title(base::UTF8ToUTF16(kTestNonTypedURLAlternativeTitle));
3164 SimulateNotification(notification_type, &row1, &row2);
3165
3166 // URLRows that are cached by the in-memory database should be updated.
3167 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
3168 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2));
3169 EXPECT_EQ(base::UTF8ToUTF16(kTestTypedURLAlternativeTitle),
3170 cached_row1.title());
3171
3172 // Now decrease the typed count for the typed URLRow, and increase it for the
3173 // previously non-typed URLRow.
3174 row1.set_typed_count(0);
3175 row2.set_typed_count(2);
3176 SimulateNotification(notification_type, &row1, &row2);
3177
3178 // The in-memory database should stop caching the first URLRow, and start
3179 // caching the second URLRow.
3180 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
3181 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2));
3182 EXPECT_EQ(row2.id(), cached_row2.id());
3183 EXPECT_EQ(base::UTF8ToUTF16(kTestNonTypedURLAlternativeTitle),
3184 cached_row2.title());
3185 }
3186
TEST_F(InMemoryHistoryBackendTest,OnURLsModified)3187 TEST_F(InMemoryHistoryBackendTest, OnURLsModified) {
3188 TestAddingAndChangingURLRows(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED);
3189 }
3190
TEST_F(InMemoryHistoryBackendTest,OnURLsVisisted)3191 TEST_F(InMemoryHistoryBackendTest, OnURLsVisisted) {
3192 TestAddingAndChangingURLRows(chrome::NOTIFICATION_HISTORY_URL_VISITED);
3193 }
3194
TEST_F(InMemoryHistoryBackendTest,OnURLsDeletedPiecewise)3195 TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedPiecewise) {
3196 // Add two typed and one non-typed URLRow to the in-memory database.
3197 URLRow row1(CreateTestTypedURL());
3198 URLRow row2(CreateAnotherTestTypedURL());
3199 URLRow row3(CreateTestNonTypedURL());
3200 SimulateNotification(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
3201 &row1, &row2, &row3);
3202
3203 // Notify the in-memory database that the second typed URL and the non-typed
3204 // URL has been deleted.
3205 SimulateNotification(chrome::NOTIFICATION_HISTORY_URLS_DELETED,
3206 &row2, &row3);
3207
3208 // Expect that the first typed URL remains intact, the second typed URL is
3209 // correctly removed, and the non-typed URL does not magically appear.
3210 URLRow cached_row1;
3211 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
3212 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), NULL));
3213 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row3.url(), NULL));
3214 EXPECT_EQ(row1.id(), cached_row1.id());
3215 }
3216
TEST_F(InMemoryHistoryBackendTest,OnURLsDeletedEnMasse)3217 TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedEnMasse) {
3218 // Add two typed and one non-typed URLRow to the in-memory database.
3219 URLRow row1(CreateTestTypedURL());
3220 URLRow row2(CreateAnotherTestTypedURL());
3221 URLRow row3(CreateTestNonTypedURL());
3222 SimulateNotification(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
3223 &row1, &row2, &row3);
3224
3225 // Now notify the in-memory database that all history has been deleted.
3226 scoped_ptr<URLsDeletedDetails> details(new URLsDeletedDetails());
3227 details->all_history = true;
3228 BroadcastNotifications(chrome::NOTIFICATION_HISTORY_URLS_DELETED,
3229 details.PassAs<HistoryDetails>());
3230
3231 // Expect that everything goes away.
3232 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row1.url(), NULL));
3233 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), NULL));
3234 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row3.url(), NULL));
3235 }
3236
PopulateTestURLsAndSearchTerms(URLRow * row1,URLRow * row2,const base::string16 & term1,const base::string16 & term2)3237 void InMemoryHistoryBackendTest::PopulateTestURLsAndSearchTerms(
3238 URLRow* row1,
3239 URLRow* row2,
3240 const base::string16& term1,
3241 const base::string16& term2) {
3242 // Add a typed and a non-typed URLRow to the in-memory database. This time,
3243 // though, do it through the history backend...
3244 URLRows rows;
3245 rows.push_back(*row1);
3246 rows.push_back(*row2);
3247 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
3248 backend_->db()->GetRowForURL(row1->url(), row1); // Get effective IDs from
3249 backend_->db()->GetRowForURL(row2->url(), row2); // the database.
3250
3251 // ... so that we can also use that for adding the search terms. This way, we
3252 // not only test that the notifications involved are handled correctly, but
3253 // also that they are fired correctly (in the history backend).
3254 backend_->SetKeywordSearchTermsForURL(row1->url(), kTestKeywordId, term1);
3255 backend_->SetKeywordSearchTermsForURL(row2->url(), kTestKeywordId, term2);
3256 }
3257
TEST_F(InMemoryHistoryBackendTest,SetKeywordSearchTerms)3258 TEST_F(InMemoryHistoryBackendTest, SetKeywordSearchTerms) {
3259 URLRow row1(CreateTestTypedURL());
3260 URLRow row2(CreateTestNonTypedURL());
3261 base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1));
3262 base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2));
3263 PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
3264
3265 // Both URLs now have associated search terms, so the in-memory database
3266 // should cache both of them, regardless whether they have been typed or not.
3267 URLRow cached_row1, cached_row2;
3268 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
3269 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2));
3270 EXPECT_EQ(row1.id(), cached_row1.id());
3271 EXPECT_EQ(row2.id(), cached_row2.id());
3272
3273 // Verify that lookups will actually return both search terms; and also check
3274 // at the low level that the rows are there.
3275 EXPECT_EQ(1u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1));
3276 EXPECT_EQ(1u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2));
3277 EXPECT_TRUE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL));
3278 EXPECT_TRUE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL));
3279 }
3280
TEST_F(InMemoryHistoryBackendTest,DeleteKeywordSearchTerms)3281 TEST_F(InMemoryHistoryBackendTest, DeleteKeywordSearchTerms) {
3282 URLRow row1(CreateTestTypedURL());
3283 URLRow row2(CreateTestNonTypedURL());
3284 base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1));
3285 base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2));
3286 PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
3287
3288 // Delete both search terms. This should be reflected in the in-memory DB.
3289 backend_->DeleteKeywordSearchTermForURL(row1.url());
3290 backend_->DeleteKeywordSearchTermForURL(row2.url());
3291
3292 // The typed URL should remain intact.
3293 // Note: we do not need to guarantee anything about the non-typed URL.
3294 URLRow cached_row1;
3295 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
3296 EXPECT_EQ(row1.id(), cached_row1.id());
3297
3298 // Verify that the search terms are no longer returned as results, and also
3299 // check at the low level that they are gone for good.
3300 EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1));
3301 EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2));
3302 EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL));
3303 EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL));
3304 }
3305
TEST_F(InMemoryHistoryBackendTest,DeleteAllSearchTermsForKeyword)3306 TEST_F(InMemoryHistoryBackendTest, DeleteAllSearchTermsForKeyword) {
3307 URLRow row1(CreateTestTypedURL());
3308 URLRow row2(CreateTestNonTypedURL());
3309 base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1));
3310 base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2));
3311 PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
3312
3313 // Removing a keyword should cause all corresponding search terms to be
3314 // deleted from the in-memory database (and also the main database).
3315 TemplateURLID id = kTestKeywordId;
3316 mem_backend_->Observe(chrome::NOTIFICATION_TEMPLATE_URL_REMOVED,
3317 content::Source<HistoryBackendTestBase>(NULL),
3318 content::Details<TemplateURLID>(&id));
3319
3320 // The typed URL should remain intact.
3321 // Note: we do not need to guarantee anything about the non-typed URL.
3322 URLRow cached_row1;
3323 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
3324 EXPECT_EQ(row1.id(), cached_row1.id());
3325
3326 // Verify that the search terms are no longer returned as results, and also
3327 // check at the low level that they are gone for good.
3328 EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1));
3329 EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2));
3330 EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL));
3331 EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL));
3332 }
3333
TEST_F(InMemoryHistoryBackendTest,OnURLsDeletedWithSearchTerms)3334 TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedWithSearchTerms) {
3335 URLRow row1(CreateTestTypedURL());
3336 URLRow row2(CreateTestNonTypedURL());
3337 base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1));
3338 base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2));
3339 PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
3340
3341 // Notify the in-memory database that the second typed URL has been deleted.
3342 SimulateNotification(chrome::NOTIFICATION_HISTORY_URLS_DELETED, &row2);
3343
3344 // Verify that the second term is no longer returned as result, and also check
3345 // at the low level that it is gone for good. The term corresponding to the
3346 // first URLRow should not be affected.
3347 EXPECT_EQ(1u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1));
3348 EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2));
3349 EXPECT_TRUE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL));
3350 EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL));
3351 }
3352
3353 } // namespace history
3354