1 // Copyright (c) 2011 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 <set>
6 #include <vector>
7
8 #include "base/command_line.h"
9 #include "base/file_path.h"
10 #include "base/file_util.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/path_service.h"
14 #include "base/string16.h"
15 #include "base/utf_string_conversions.h"
16 #include "chrome/browser/bookmarks/bookmark_model.h"
17 #include "chrome/browser/history/history_backend.h"
18 #include "chrome/browser/history/history_notifications.h"
19 #include "chrome/browser/history/in_memory_database.h"
20 #include "chrome/browser/history/in_memory_history_backend.h"
21 #include "chrome/common/chrome_constants.h"
22 #include "chrome/common/chrome_paths.h"
23 #include "chrome/common/thumbnail_score.h"
24 #include "chrome/tools/profiles/thumbnail-inl.h"
25 #include "content/common/notification_details.h"
26 #include "content/common/notification_source.h"
27 #include "googleurl/src/gurl.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 #include "ui/gfx/codec/jpeg_codec.h"
30
31 using base::Time;
32
33 // This file only tests functionality where it is most convenient to call the
34 // backend directly. Most of the history backend functions are tested by the
35 // history unit test. Because of the elaborate callbacks involved, this is no
36 // harder than calling it directly for many things.
37
38 namespace {
39
40 // data we'll put into the thumbnail database
41 static const unsigned char blob1[] =
42 "12346102356120394751634516591348710478123649165419234519234512349134";
43
44 } // namepace
45
46 namespace history {
47
48 class HistoryBackendTest;
49
50 // This must be a separate object since HistoryBackend manages its lifetime.
51 // This just forwards the messages we're interested in to the test object.
52 class HistoryBackendTestDelegate : public HistoryBackend::Delegate {
53 public:
HistoryBackendTestDelegate(HistoryBackendTest * test)54 explicit HistoryBackendTestDelegate(HistoryBackendTest* test) : test_(test) {}
55
NotifyProfileError(sql::InitStatus init_status)56 virtual void NotifyProfileError(sql::InitStatus init_status) OVERRIDE {}
57 virtual void SetInMemoryBackend(InMemoryHistoryBackend* backend) OVERRIDE;
58 virtual void BroadcastNotifications(NotificationType type,
59 HistoryDetails* details) OVERRIDE;
60 virtual void DBLoaded() OVERRIDE;
61 virtual void StartTopSitesMigration() OVERRIDE;
62
63 private:
64 // Not owned by us.
65 HistoryBackendTest* test_;
66
67 DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestDelegate);
68 };
69
70 class HistoryBackendTest : public testing::Test {
71 public:
HistoryBackendTest()72 HistoryBackendTest() : bookmark_model_(NULL), loaded_(false) {}
~HistoryBackendTest()73 virtual ~HistoryBackendTest() {
74 }
75
76 protected:
77 scoped_refptr<HistoryBackend> backend_; // Will be NULL on init failure.
78 scoped_ptr<InMemoryHistoryBackend> mem_backend_;
79
AddRedirectChain(const char * sequence[],int page_id)80 void AddRedirectChain(const char* sequence[], int page_id) {
81 history::RedirectList redirects;
82 for (int i = 0; sequence[i] != NULL; ++i)
83 redirects.push_back(GURL(sequence[i]));
84
85 int int_scope = 1;
86 void* scope = 0;
87 memcpy(&scope, &int_scope, sizeof(int_scope));
88 scoped_refptr<history::HistoryAddPageArgs> request(
89 new history::HistoryAddPageArgs(
90 redirects.back(), Time::Now(), scope, page_id, GURL(),
91 redirects, PageTransition::LINK, history::SOURCE_BROWSED, true));
92 backend_->AddPage(request);
93 }
94
95 // Adds CLIENT_REDIRECT page transition.
96 // |url1| is the source URL and |url2| is the destination.
97 // |did_replace| is true if the transition is non-user initiated and the
98 // navigation entry for |url2| has replaced that for |url1|. The possibly
99 // updated transition code of the visit records for |url1| and |url2| is
100 // returned by filling in |*transition1| and |*transition2|, respectively.
AddClientRedirect(const GURL & url1,const GURL & url2,bool did_replace,int * transition1,int * transition2)101 void AddClientRedirect(const GURL& url1, const GURL& url2, bool did_replace,
102 int* transition1, int* transition2) {
103 void* const dummy_scope = reinterpret_cast<void*>(0x87654321);
104 history::RedirectList redirects;
105 if (url1.is_valid())
106 redirects.push_back(url1);
107 if (url2.is_valid())
108 redirects.push_back(url2);
109 scoped_refptr<HistoryAddPageArgs> request(
110 new HistoryAddPageArgs(url2, base::Time(), dummy_scope, 0, url1,
111 redirects, PageTransition::CLIENT_REDIRECT,
112 history::SOURCE_BROWSED, did_replace));
113 backend_->AddPage(request);
114
115 *transition1 = getTransition(url1);
116 *transition2 = getTransition(url2);
117 }
118
getTransition(const GURL & url)119 int getTransition(const GURL& url) {
120 if (!url.is_valid())
121 return 0;
122 URLRow row;
123 URLID id = backend_->db()->GetRowForURL(url, &row);
124 VisitVector visits;
125 EXPECT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
126 return visits[0].transition;
127 }
128
getTestDir()129 FilePath getTestDir() {
130 return test_dir_;
131 }
132
GetFavicon(const GURL & url,IconType icon_type)133 FaviconID GetFavicon(const GURL& url, IconType icon_type) {
134 IconMapping icon_mapping;
135 if (backend_->thumbnail_db_->GetIconMappingForPageURL(url, icon_type,
136 &icon_mapping))
137 return icon_mapping.icon_id;
138 else
139 return 0;
140 }
141
142 BookmarkModel bookmark_model_;
143
144 protected:
145 bool loaded_;
146
147 private:
148 friend class HistoryBackendTestDelegate;
149
150 // testing::Test
SetUp()151 virtual void SetUp() {
152 if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("BackendTest"),
153 &test_dir_))
154 return;
155 backend_ = new HistoryBackend(test_dir_,
156 new HistoryBackendTestDelegate(this),
157 &bookmark_model_);
158 backend_->Init(std::string(), false);
159 }
TearDown()160 virtual void TearDown() {
161 if (backend_.get())
162 backend_->Closing();
163 backend_ = NULL;
164 mem_backend_.reset();
165 file_util::Delete(test_dir_, true);
166 }
167
SetInMemoryBackend(InMemoryHistoryBackend * backend)168 void SetInMemoryBackend(InMemoryHistoryBackend* backend) {
169 mem_backend_.reset(backend);
170 }
171
BroadcastNotifications(NotificationType type,HistoryDetails * details)172 void BroadcastNotifications(NotificationType type,
173 HistoryDetails* details) {
174 // Send the notifications directly to the in-memory database.
175 Details<HistoryDetails> det(details);
176 mem_backend_->Observe(type, Source<HistoryBackendTest>(NULL), det);
177
178 // The backend passes ownership of the details pointer to us.
179 delete details;
180 }
181
182 MessageLoop message_loop_;
183 FilePath test_dir_;
184 };
185
SetInMemoryBackend(InMemoryHistoryBackend * backend)186 void HistoryBackendTestDelegate::SetInMemoryBackend(
187 InMemoryHistoryBackend* backend) {
188 test_->SetInMemoryBackend(backend);
189 }
190
BroadcastNotifications(NotificationType type,HistoryDetails * details)191 void HistoryBackendTestDelegate::BroadcastNotifications(
192 NotificationType type,
193 HistoryDetails* details) {
194 test_->BroadcastNotifications(type, details);
195 }
196
DBLoaded()197 void HistoryBackendTestDelegate::DBLoaded() {
198 test_->loaded_ = true;
199 }
200
StartTopSitesMigration()201 void HistoryBackendTestDelegate::StartTopSitesMigration() {
202 test_->backend_->MigrateThumbnailsDatabase();
203 }
204
TEST_F(HistoryBackendTest,Loaded)205 TEST_F(HistoryBackendTest, Loaded) {
206 ASSERT_TRUE(backend_.get());
207 ASSERT_TRUE(loaded_);
208 }
209
TEST_F(HistoryBackendTest,DeleteAll)210 TEST_F(HistoryBackendTest, DeleteAll) {
211 ASSERT_TRUE(backend_.get());
212
213 // Add two favicons, use the characters '1' and '2' for the image data. Note
214 // that we do these in the opposite order. This is so the first one gets ID
215 // 2 autoassigned to the database, which will change when the other one is
216 // deleted. This way we can test that updating works properly.
217 GURL favicon_url1("http://www.google.com/favicon.ico");
218 GURL favicon_url2("http://news.google.com/favicon.ico");
219 FaviconID favicon2 = backend_->thumbnail_db_->AddFavicon(favicon_url2,
220 FAVICON);
221 FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(favicon_url1,
222 FAVICON);
223
224 std::vector<unsigned char> data;
225 data.push_back('1');
226 EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(favicon1,
227 new RefCountedBytes(data), Time::Now()));
228
229 data[0] = '2';
230 EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(
231 favicon2, new RefCountedBytes(data), Time::Now()));
232
233 // First visit two URLs.
234 URLRow row1(GURL("http://www.google.com/"));
235 row1.set_visit_count(2);
236 row1.set_typed_count(1);
237 row1.set_last_visit(Time::Now());
238 backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1);
239
240 URLRow row2(GURL("http://news.google.com/"));
241 row2.set_visit_count(1);
242 row2.set_last_visit(Time::Now());
243 backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2);
244
245 std::vector<URLRow> rows;
246 rows.push_back(row2); // Reversed order for the same reason as favicons.
247 rows.push_back(row1);
248 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
249
250 URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL);
251 URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL);
252
253 // Get the two visits for the URLs we just added.
254 VisitVector visits;
255 backend_->db_->GetVisitsForURL(row1_id, &visits);
256 ASSERT_EQ(1U, visits.size());
257 VisitID visit1_id = visits[0].visit_id;
258
259 visits.clear();
260 backend_->db_->GetVisitsForURL(row2_id, &visits);
261 ASSERT_EQ(1U, visits.size());
262 VisitID visit2_id = visits[0].visit_id;
263
264 // The in-memory backend should have been set and it should have gotten the
265 // typed URL.
266 ASSERT_TRUE(mem_backend_.get());
267 URLRow outrow1;
268 EXPECT_TRUE(mem_backend_->db_->GetRowForURL(row1.url(), NULL));
269
270 // Add thumbnails for each page.
271 ThumbnailScore score(0.25, true, true);
272 scoped_ptr<SkBitmap> google_bitmap(
273 gfx::JPEGCodec::Decode(kGoogleThumbnail, sizeof(kGoogleThumbnail)));
274
275 Time time;
276 GURL gurl;
277 backend_->thumbnail_db_->SetPageThumbnail(gurl, row1_id, *google_bitmap,
278 score, time);
279 scoped_ptr<SkBitmap> weewar_bitmap(
280 gfx::JPEGCodec::Decode(kWeewarThumbnail, sizeof(kWeewarThumbnail)));
281 backend_->thumbnail_db_->SetPageThumbnail(gurl, row2_id, *weewar_bitmap,
282 score, time);
283
284 // Star row1.
285 bookmark_model_.AddURL(
286 bookmark_model_.GetBookmarkBarNode(), 0, string16(), row1.url());
287
288 // Set full text index for each one.
289 backend_->text_database_->AddPageData(row1.url(), row1_id, visit1_id,
290 row1.last_visit(),
291 UTF8ToUTF16("Title 1"),
292 UTF8ToUTF16("Body 1"));
293 backend_->text_database_->AddPageData(row2.url(), row2_id, visit2_id,
294 row2.last_visit(),
295 UTF8ToUTF16("Title 2"),
296 UTF8ToUTF16("Body 2"));
297
298 // Now finally clear all history.
299 backend_->DeleteAllHistory();
300
301 // The first URL should be preserved but the time should be cleared.
302 EXPECT_TRUE(backend_->db_->GetRowForURL(row1.url(), &outrow1));
303 EXPECT_EQ(row1.url(), outrow1.url());
304 EXPECT_EQ(0, outrow1.visit_count());
305 EXPECT_EQ(0, outrow1.typed_count());
306 EXPECT_TRUE(Time() == outrow1.last_visit());
307
308 // The second row should be deleted.
309 URLRow outrow2;
310 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &outrow2));
311
312 // All visits should be deleted for both URLs.
313 VisitVector all_visits;
314 backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
315 ASSERT_EQ(0U, all_visits.size());
316
317 // All thumbnails should be deleted.
318 std::vector<unsigned char> out_data;
319 EXPECT_FALSE(backend_->thumbnail_db_->GetPageThumbnail(outrow1.id(),
320 &out_data));
321 EXPECT_FALSE(backend_->thumbnail_db_->GetPageThumbnail(row2_id, &out_data));
322
323 // We should have a favicon for the first URL only. We look them up by favicon
324 // URL since the IDs may hav changed.
325 FaviconID out_favicon1 = backend_->thumbnail_db_->
326 GetFaviconIDForFaviconURL(favicon_url1, FAVICON, NULL);
327 EXPECT_TRUE(out_favicon1);
328 FaviconID out_favicon2 = backend_->thumbnail_db_->
329 GetFaviconIDForFaviconURL(favicon_url2, FAVICON, NULL);
330 EXPECT_FALSE(out_favicon2) << "Favicon not deleted";
331
332 // The remaining URL should still reference the same favicon, even if its
333 // ID has changed.
334 EXPECT_EQ(out_favicon1, GetFavicon(outrow1.url(), FAVICON));
335
336 // The first URL should still be bookmarked.
337 EXPECT_TRUE(bookmark_model_.IsBookmarked(row1.url()));
338
339 // The full text database should have no data.
340 std::vector<TextDatabase::Match> text_matches;
341 Time first_time_searched;
342 backend_->text_database_->GetTextMatches(UTF8ToUTF16("Body"),
343 QueryOptions(),
344 &text_matches,
345 &first_time_searched);
346 EXPECT_EQ(0U, text_matches.size());
347 }
348
TEST_F(HistoryBackendTest,URLsNoLongerBookmarked)349 TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) {
350 GURL favicon_url1("http://www.google.com/favicon.ico");
351 GURL favicon_url2("http://news.google.com/favicon.ico");
352 FaviconID favicon2 = backend_->thumbnail_db_->AddFavicon(favicon_url2,
353 FAVICON);
354 FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(favicon_url1,
355 FAVICON);
356
357 std::vector<unsigned char> data;
358 data.push_back('1');
359 EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(
360 favicon1, new RefCountedBytes(data), Time::Now()));
361
362 data[0] = '2';
363 EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(
364 favicon2, new RefCountedBytes(data), Time::Now()));
365
366 // First visit two URLs.
367 URLRow row1(GURL("http://www.google.com/"));
368 row1.set_visit_count(2);
369 row1.set_typed_count(1);
370 row1.set_last_visit(Time::Now());
371 EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
372
373 URLRow row2(GURL("http://news.google.com/"));
374 row2.set_visit_count(1);
375 row2.set_last_visit(Time::Now());
376 EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2));
377
378 std::vector<URLRow> rows;
379 rows.push_back(row2); // Reversed order for the same reason as favicons.
380 rows.push_back(row1);
381 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
382
383 URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL);
384 URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL);
385
386 // Star the two URLs.
387 bookmark_model_.SetURLStarred(row1.url(), string16(), true);
388 bookmark_model_.SetURLStarred(row2.url(), string16(), true);
389
390 // Delete url 2. Because url 2 is starred this won't delete the URL, only
391 // the visits.
392 backend_->expirer_.DeleteURL(row2.url());
393
394 // Make sure url 2 is still valid, but has no visits.
395 URLRow tmp_url_row;
396 EXPECT_EQ(row2_id, backend_->db_->GetRowForURL(row2.url(), NULL));
397 VisitVector visits;
398 backend_->db_->GetVisitsForURL(row2_id, &visits);
399 EXPECT_EQ(0U, visits.size());
400 // The favicon should still be valid.
401 EXPECT_EQ(favicon2,
402 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url2,
403 FAVICON,
404 NULL));
405
406 // Unstar row2.
407 bookmark_model_.SetURLStarred(row2.url(), string16(), false);
408 // Tell the backend it was unstarred. We have to explicitly do this as
409 // BookmarkModel isn't wired up to the backend during testing.
410 std::set<GURL> unstarred_urls;
411 unstarred_urls.insert(row2.url());
412 backend_->URLsNoLongerBookmarked(unstarred_urls);
413
414 // The URL should no longer exist.
415 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &tmp_url_row));
416 // And the favicon should be deleted.
417 EXPECT_EQ(0,
418 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url2,
419 FAVICON,
420 NULL));
421
422 // Unstar row 1.
423 bookmark_model_.SetURLStarred(row1.url(), string16(), false);
424 // Tell the backend it was unstarred. We have to explicitly do this as
425 // BookmarkModel isn't wired up to the backend during testing.
426 unstarred_urls.clear();
427 unstarred_urls.insert(row1.url());
428 backend_->URLsNoLongerBookmarked(unstarred_urls);
429
430 // The URL should still exist (because there were visits).
431 EXPECT_EQ(row1_id, backend_->db_->GetRowForURL(row1.url(), NULL));
432
433 // There should still be visits.
434 visits.clear();
435 backend_->db_->GetVisitsForURL(row1_id, &visits);
436 EXPECT_EQ(1U, visits.size());
437
438 // The favicon should still be valid.
439 EXPECT_EQ(favicon1,
440 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url1,
441 FAVICON,
442 NULL));
443 }
444
445 // Tests a handful of assertions for a navigation with a type of
446 // KEYWORD_GENERATED.
TEST_F(HistoryBackendTest,KeywordGenerated)447 TEST_F(HistoryBackendTest, KeywordGenerated) {
448 ASSERT_TRUE(backend_.get());
449
450 GURL url("http://google.com");
451
452 Time visit_time = Time::Now() - base::TimeDelta::FromDays(1);
453 scoped_refptr<HistoryAddPageArgs> request(
454 new HistoryAddPageArgs(url, visit_time, NULL, 0, GURL(),
455 history::RedirectList(),
456 PageTransition::KEYWORD_GENERATED,
457 history::SOURCE_BROWSED, false));
458 backend_->AddPage(request);
459
460 // A row should have been added for the url.
461 URLRow row;
462 URLID url_id = backend_->db()->GetRowForURL(url, &row);
463 ASSERT_NE(0, url_id);
464
465 // The typed count should be 1.
466 ASSERT_EQ(1, row.typed_count());
467
468 // KEYWORD_GENERATED urls should not be added to the segment db.
469 std::string segment_name = VisitSegmentDatabase::ComputeSegmentName(url);
470 EXPECT_EQ(0, backend_->db()->GetSegmentNamed(segment_name));
471
472 // One visit should be added.
473 VisitVector visits;
474 EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits));
475 EXPECT_EQ(1U, visits.size());
476
477 // But no visible visits.
478 visits.clear();
479 backend_->db()->GetVisibleVisitsInRange(base::Time(), base::Time(), 1,
480 &visits);
481 EXPECT_TRUE(visits.empty());
482
483 // Expire the visits.
484 std::set<GURL> restrict_urls;
485 backend_->expire_backend()->ExpireHistoryBetween(restrict_urls,
486 visit_time, Time::Now());
487
488 // The visit should have been nuked.
489 visits.clear();
490 EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits));
491 EXPECT_TRUE(visits.empty());
492
493 // As well as the url.
494 ASSERT_EQ(0, backend_->db()->GetRowForURL(url, &row));
495 }
496
TEST_F(HistoryBackendTest,ClientRedirect)497 TEST_F(HistoryBackendTest, ClientRedirect) {
498 ASSERT_TRUE(backend_.get());
499
500 int transition1;
501 int transition2;
502
503 // Initial transition to page A.
504 GURL url_a("http://google.com/a");
505 AddClientRedirect(GURL(), url_a, false, &transition1, &transition2);
506 EXPECT_TRUE(transition2 & PageTransition::CHAIN_END);
507
508 // User initiated redirect to page B.
509 GURL url_b("http://google.com/b");
510 AddClientRedirect(url_a, url_b, false, &transition1, &transition2);
511 EXPECT_TRUE(transition1 & PageTransition::CHAIN_END);
512 EXPECT_TRUE(transition2 & PageTransition::CHAIN_END);
513
514 // Non-user initiated redirect to page C.
515 GURL url_c("http://google.com/c");
516 AddClientRedirect(url_b, url_c, true, &transition1, &transition2);
517 EXPECT_FALSE(transition1 & PageTransition::CHAIN_END);
518 EXPECT_TRUE(transition2 & PageTransition::CHAIN_END);
519 }
520
TEST_F(HistoryBackendTest,ImportedFaviconsTest)521 TEST_F(HistoryBackendTest, ImportedFaviconsTest) {
522 // Setup test data - two Urls in the history, one with favicon assigned and
523 // one without.
524 GURL favicon_url1("http://www.google.com/favicon.ico");
525 FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(favicon_url1,
526 FAVICON);
527 std::vector<unsigned char> data;
528 data.push_back('1');
529 EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(favicon1,
530 RefCountedBytes::TakeVector(&data), Time::Now()));
531 URLRow row1(GURL("http://www.google.com/"));
532 row1.set_visit_count(1);
533 row1.set_last_visit(Time::Now());
534 EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
535
536 URLRow row2(GURL("http://news.google.com/"));
537 row2.set_visit_count(1);
538 row2.set_last_visit(Time::Now());
539 std::vector<URLRow> rows;
540 rows.push_back(row1);
541 rows.push_back(row2);
542 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
543 URLRow url_row1, url_row2;
544 EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0);
545 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0);
546 EXPECT_FALSE(GetFavicon(row1.url(), FAVICON) == 0);
547 EXPECT_TRUE(GetFavicon(row2.url(), FAVICON) == 0);
548
549 // Now provide one imported favicon for both URLs already in the registry.
550 // The new favicon should only be used with the URL that doesn't already have
551 // a favicon.
552 std::vector<history::ImportedFaviconUsage> favicons;
553 history::ImportedFaviconUsage favicon;
554 favicon.favicon_url = GURL("http://news.google.com/favicon.ico");
555 favicon.png_data.push_back('2');
556 favicon.urls.insert(row1.url());
557 favicon.urls.insert(row2.url());
558 favicons.push_back(favicon);
559 backend_->SetImportedFavicons(favicons);
560 EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0);
561 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0);
562 EXPECT_FALSE(GetFavicon(row1.url(), FAVICON) == 0);
563 EXPECT_FALSE(GetFavicon(row2.url(), FAVICON) == 0);
564 EXPECT_FALSE(GetFavicon(row1.url(), FAVICON) ==
565 GetFavicon(row2.url(), FAVICON));
566
567 // A URL should not be added to history (to store favicon), if
568 // the URL is not bookmarked.
569 GURL url3("http://mail.google.com");
570 favicons.clear();
571 favicon.favicon_url = GURL("http://mail.google.com/favicon.ico");
572 favicon.png_data.push_back('3');
573 favicon.urls.insert(url3);
574 favicons.push_back(favicon);
575 backend_->SetImportedFavicons(favicons);
576 URLRow url_row3;
577 EXPECT_TRUE(backend_->db_->GetRowForURL(url3, &url_row3) == 0);
578
579 // If the URL is bookmarked, it should get added to history with 0 visits.
580 bookmark_model_.AddURL(bookmark_model_.GetBookmarkBarNode(), 0, string16(),
581 url3);
582 backend_->SetImportedFavicons(favicons);
583 EXPECT_FALSE(backend_->db_->GetRowForURL(url3, &url_row3) == 0);
584 EXPECT_TRUE(url_row3.visit_count() == 0);
585 }
586
TEST_F(HistoryBackendTest,StripUsernamePasswordTest)587 TEST_F(HistoryBackendTest, StripUsernamePasswordTest) {
588 ASSERT_TRUE(backend_.get());
589
590 GURL url("http://anyuser:anypass@www.google.com");
591 GURL stripped_url("http://www.google.com");
592
593 // Clear all history.
594 backend_->DeleteAllHistory();
595
596 // Visit the url with username, password.
597 backend_->AddPageVisit(url, base::Time::Now(), 0,
598 PageTransition::GetQualifier(PageTransition::TYPED),
599 history::SOURCE_BROWSED);
600
601 // Fetch the row information about stripped url from history db.
602 VisitVector visits;
603 URLID row_id = backend_->db_->GetRowForURL(stripped_url, NULL);
604 backend_->db_->GetVisitsForURL(row_id, &visits);
605
606 // Check if stripped url is stored in database.
607 ASSERT_EQ(1U, visits.size());
608 }
609
TEST_F(HistoryBackendTest,AddPageVisitSource)610 TEST_F(HistoryBackendTest, AddPageVisitSource) {
611 ASSERT_TRUE(backend_.get());
612
613 GURL url("http://www.google.com");
614
615 // Clear all history.
616 backend_->DeleteAllHistory();
617
618 // Assume visiting the url from an externsion.
619 backend_->AddPageVisit(url, base::Time::Now(), 0, PageTransition::TYPED,
620 history::SOURCE_EXTENSION);
621 // Assume the url is imported from Firefox.
622 backend_->AddPageVisit(url, base::Time::Now(), 0, PageTransition::TYPED,
623 history::SOURCE_FIREFOX_IMPORTED);
624 // Assume this url is also synced.
625 backend_->AddPageVisit(url, base::Time::Now(), 0, PageTransition::TYPED,
626 history::SOURCE_SYNCED);
627
628 // Fetch the row information about the url from history db.
629 VisitVector visits;
630 URLID row_id = backend_->db_->GetRowForURL(url, NULL);
631 backend_->db_->GetVisitsForURL(row_id, &visits);
632
633 // Check if all the visits to the url are stored in database.
634 ASSERT_EQ(3U, visits.size());
635 VisitSourceMap visit_sources;
636 backend_->db_->GetVisitsSource(visits, &visit_sources);
637 ASSERT_EQ(3U, visit_sources.size());
638 int sources = 0;
639 for (int i = 0; i < 3; i++) {
640 switch (visit_sources[visits[i].visit_id]) {
641 case history::SOURCE_EXTENSION:
642 sources |= 0x1;
643 break;
644 case history::SOURCE_FIREFOX_IMPORTED:
645 sources |= 0x2;
646 break;
647 case history::SOURCE_SYNCED:
648 sources |= 0x4;
649 default:
650 break;
651 }
652 }
653 EXPECT_EQ(0x7, sources);
654 }
655
TEST_F(HistoryBackendTest,AddPageArgsSource)656 TEST_F(HistoryBackendTest, AddPageArgsSource) {
657 ASSERT_TRUE(backend_.get());
658
659 GURL url("http://testpageargs.com");
660
661 // Assume this page is browsed by user.
662 scoped_refptr<HistoryAddPageArgs> request1(
663 new HistoryAddPageArgs(url, base::Time::Now(), NULL, 0, GURL(),
664 history::RedirectList(),
665 PageTransition::KEYWORD_GENERATED,
666 history::SOURCE_BROWSED, false));
667 backend_->AddPage(request1);
668 // Assume this page is synced.
669 scoped_refptr<HistoryAddPageArgs> request2(
670 new HistoryAddPageArgs(url, base::Time::Now(), NULL, 0, GURL(),
671 history::RedirectList(),
672 PageTransition::LINK,
673 history::SOURCE_SYNCED, false));
674 backend_->AddPage(request2);
675 // Assume this page is browsed again.
676 scoped_refptr<HistoryAddPageArgs> request3(
677 new HistoryAddPageArgs(url, base::Time::Now(), NULL, 0, GURL(),
678 history::RedirectList(),
679 PageTransition::TYPED,
680 history::SOURCE_BROWSED, false));
681 backend_->AddPage(request3);
682
683 // Three visits should be added with proper sources.
684 VisitVector visits;
685 URLRow row;
686 URLID id = backend_->db()->GetRowForURL(url, &row);
687 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
688 ASSERT_EQ(3U, visits.size());
689 VisitSourceMap visit_sources;
690 backend_->db_->GetVisitsSource(visits, &visit_sources);
691 ASSERT_EQ(1U, visit_sources.size());
692 EXPECT_EQ(history::SOURCE_SYNCED, visit_sources.begin()->second);
693 }
694
TEST_F(HistoryBackendTest,AddVisitsSource)695 TEST_F(HistoryBackendTest, AddVisitsSource) {
696 ASSERT_TRUE(backend_.get());
697
698 GURL url1("http://www.cnn.com");
699 std::vector<base::Time> visits1;
700 visits1.push_back(Time::Now() - base::TimeDelta::FromDays(5));
701 visits1.push_back(Time::Now() - base::TimeDelta::FromDays(1));
702 visits1.push_back(Time::Now());
703
704 GURL url2("http://www.example.com");
705 std::vector<base::Time> visits2;
706 visits2.push_back(Time::Now() - base::TimeDelta::FromDays(10));
707 visits2.push_back(Time::Now());
708
709 // Clear all history.
710 backend_->DeleteAllHistory();
711
712 // Add the visits.
713 backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
714 backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED);
715
716 // Verify the visits were added with their sources.
717 VisitVector visits;
718 URLRow row;
719 URLID id = backend_->db()->GetRowForURL(url1, &row);
720 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
721 ASSERT_EQ(3U, visits.size());
722 VisitSourceMap visit_sources;
723 backend_->db_->GetVisitsSource(visits, &visit_sources);
724 ASSERT_EQ(3U, visit_sources.size());
725 for (int i = 0; i < 3; i++)
726 EXPECT_EQ(history::SOURCE_IE_IMPORTED, visit_sources[visits[i].visit_id]);
727 id = backend_->db()->GetRowForURL(url2, &row);
728 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
729 ASSERT_EQ(2U, visits.size());
730 backend_->db_->GetVisitsSource(visits, &visit_sources);
731 ASSERT_EQ(2U, visit_sources.size());
732 for (int i = 0; i < 2; i++)
733 EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]);
734 }
735
TEST_F(HistoryBackendTest,RemoveVisitsSource)736 TEST_F(HistoryBackendTest, RemoveVisitsSource) {
737 ASSERT_TRUE(backend_.get());
738
739 GURL url1("http://www.cnn.com");
740 std::vector<base::Time> visits1;
741 visits1.push_back(Time::Now() - base::TimeDelta::FromDays(5));
742 visits1.push_back(Time::Now());
743
744 GURL url2("http://www.example.com");
745 std::vector<base::Time> visits2;
746 visits2.push_back(Time::Now() - base::TimeDelta::FromDays(10));
747 visits2.push_back(Time::Now());
748
749 // Clear all history.
750 backend_->DeleteAllHistory();
751
752 // Add the visits.
753 backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
754 backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED);
755
756 // Verify the visits of url1 were added.
757 VisitVector visits;
758 URLRow row;
759 URLID id = backend_->db()->GetRowForURL(url1, &row);
760 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
761 ASSERT_EQ(2U, visits.size());
762 // Remove these visits.
763 ASSERT_TRUE(backend_->RemoveVisits(visits));
764
765 // Now check only url2's source in visit_source table.
766 VisitSourceMap visit_sources;
767 backend_->db_->GetVisitsSource(visits, &visit_sources);
768 ASSERT_EQ(0U, visit_sources.size());
769 id = backend_->db()->GetRowForURL(url2, &row);
770 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
771 ASSERT_EQ(2U, visits.size());
772 backend_->db_->GetVisitsSource(visits, &visit_sources);
773 ASSERT_EQ(2U, visit_sources.size());
774 for (int i = 0; i < 2; i++)
775 EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]);
776 }
777
778 // Test for migration of adding visit_source table.
TEST_F(HistoryBackendTest,MigrationVisitSource)779 TEST_F(HistoryBackendTest, MigrationVisitSource) {
780 ASSERT_TRUE(backend_.get());
781 backend_->Closing();
782 backend_ = NULL;
783
784 FilePath old_history_path;
785 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &old_history_path));
786 old_history_path = old_history_path.AppendASCII("History");
787 old_history_path = old_history_path.AppendASCII("HistoryNoSource");
788
789 // Copy history database file to current directory so that it will be deleted
790 // in Teardown.
791 FilePath new_history_path(getTestDir());
792 file_util::Delete(new_history_path, true);
793 file_util::CreateDirectory(new_history_path);
794 FilePath new_history_file = new_history_path.Append(chrome::kHistoryFilename);
795 ASSERT_TRUE(file_util::CopyFile(old_history_path, new_history_file));
796
797 backend_ = new HistoryBackend(new_history_path,
798 new HistoryBackendTestDelegate(this),
799 &bookmark_model_);
800 backend_->Init(std::string(), false);
801 backend_->Closing();
802 backend_ = NULL;
803
804 // Now the database should already be migrated.
805 // Check version first.
806 int cur_version = HistoryDatabase::GetCurrentVersion();
807 sql::Connection db;
808 ASSERT_TRUE(db.Open(new_history_file));
809 sql::Statement s(db.GetUniqueStatement(
810 "SELECT value FROM meta WHERE key = 'version'"));
811 ASSERT_TRUE(s.Step());
812 int file_version = s.ColumnInt(0);
813 EXPECT_EQ(cur_version, file_version);
814
815 // Check visit_source table is created and empty.
816 s.Assign(db.GetUniqueStatement(
817 "SELECT name FROM sqlite_master WHERE name=\"visit_source\""));
818 ASSERT_TRUE(s.Step());
819 s.Assign(db.GetUniqueStatement("SELECT * FROM visit_source LIMIT 10"));
820 EXPECT_FALSE(s.Step());
821 }
822
TEST_F(HistoryBackendTest,SetFaviconMapping)823 TEST_F(HistoryBackendTest, SetFaviconMapping) {
824 // Init recent_redirects_
825 const GURL url1("http://www.google.com");
826 const GURL url2("http://www.google.com/m");
827 URLRow url_info1(url1);
828 url_info1.set_visit_count(0);
829 url_info1.set_typed_count(0);
830 url_info1.set_last_visit(base::Time());
831 url_info1.set_hidden(false);
832 backend_->db_->AddURL(url_info1);
833
834 URLRow url_info2(url2);
835 url_info2.set_visit_count(0);
836 url_info2.set_typed_count(0);
837 url_info2.set_last_visit(base::Time());
838 url_info2.set_hidden(false);
839 backend_->db_->AddURL(url_info2);
840
841 history::RedirectList redirects;
842 redirects.push_back(url2);
843 redirects.push_back(url1);
844 backend_->recent_redirects_.Put(url1, redirects);
845
846 const GURL icon_url("http://www.google.com/icon");
847 std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1));
848 // Add a favicon
849 backend_->SetFavicon(
850 url1, icon_url, RefCountedBytes::TakeVector(&data), FAVICON);
851 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
852 url1, FAVICON, NULL));
853 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
854 url2, FAVICON, NULL));
855
856 // Add a touch_icon
857 backend_->SetFavicon(
858 url1, icon_url, RefCountedBytes::TakeVector(&data), TOUCH_ICON);
859 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
860 url1, TOUCH_ICON, NULL));
861 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
862 url2, TOUCH_ICON, NULL));
863 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
864 url1, FAVICON, NULL));
865
866 // Add a TOUCH_PRECOMPOSED_ICON
867 backend_->SetFavicon(url1,
868 icon_url,
869 RefCountedBytes::TakeVector(&data),
870 TOUCH_PRECOMPOSED_ICON);
871 // The touch_icon was replaced.
872 EXPECT_FALSE(backend_->thumbnail_db_->GetIconMappingForPageURL(
873 url1, TOUCH_ICON, NULL));
874 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
875 url1, FAVICON, NULL));
876 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
877 url1, TOUCH_PRECOMPOSED_ICON, NULL));
878 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
879 url2, TOUCH_PRECOMPOSED_ICON, NULL));
880
881 // Add a touch_icon
882 backend_->SetFavicon(
883 url1, icon_url, RefCountedBytes::TakeVector(&data), TOUCH_ICON);
884 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
885 url1, TOUCH_ICON, NULL));
886 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
887 url1, FAVICON, NULL));
888 // The TOUCH_PRECOMPOSED_ICON was replaced.
889 EXPECT_FALSE(backend_->thumbnail_db_->GetIconMappingForPageURL(
890 url1, TOUCH_PRECOMPOSED_ICON, NULL));
891
892 // Add a favicon
893 const GURL icon_url2("http://www.google.com/icon2");
894 backend_->SetFavicon(
895 url1, icon_url2, RefCountedBytes::TakeVector(&data), FAVICON);
896 FaviconID icon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
897 icon_url2, FAVICON, NULL);
898 EXPECT_NE(0, icon_id);
899 std::vector<IconMapping> icon_mapping;
900 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
901 url1, &icon_mapping));
902 // The old icon was replaced.
903 EXPECT_TRUE(icon_mapping.size() > 1);
904 EXPECT_EQ(icon_id, icon_mapping[1].icon_id);
905 }
906
TEST_F(HistoryBackendTest,AddOrUpdateIconMapping)907 TEST_F(HistoryBackendTest, AddOrUpdateIconMapping) {
908 // Test the same icon and page mapping will not be added twice. other case
909 // should be covered in TEST_F(HistoryBackendTest, SetFaviconMapping)
910 const GURL url("http://www.google.com/");
911 const GURL icon_url("http://www.google.com/icon");
912 std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1));
913
914 backend_->SetFavicon(
915 url, icon_url, RefCountedBytes::TakeVector(&data), FAVICON);
916 FaviconID icon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
917 icon_url, FAVICON, NULL);
918
919 // Add the same mapping
920 FaviconID replaced;
921 EXPECT_FALSE(backend_->AddOrUpdateIconMapping(
922 url, icon_id, FAVICON, &replaced));
923 EXPECT_EQ(0, replaced);
924
925 std::vector<IconMapping> icon_mapping;
926 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
927 url, &icon_mapping));
928 EXPECT_EQ(1u, icon_mapping.size());
929 }
930
931 } // namespace history
932