• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // History unit tests come in two flavors:
6 //
7 // 1. The more complicated style is that the unit test creates a full history
8 //    service. This spawns a background thread for the history backend, and
9 //    all communication is asynchronous. This is useful for testing more
10 //    complicated things or end-to-end behavior.
11 //
12 // 2. The simpler style is to create a history backend on this thread and
13 //    access it directly without a HistoryService object. This is much simpler
14 //    because communication is synchronous. Generally, sets should go through
15 //    the history backend (since there is a lot of logic) but gets can come
16 //    directly from the HistoryDatabase. This is because the backend generally
17 //    has no logic in the getter except threading stuff, which we don't want
18 //    to run.
19 
20 #include <time.h>
21 
22 #include <algorithm>
23 #include <string>
24 
25 #include "app/sql/connection.h"
26 #include "app/sql/statement.h"
27 #include "base/basictypes.h"
28 #include "base/callback.h"
29 #include "base/command_line.h"
30 #include "base/file_path.h"
31 #include "base/file_util.h"
32 #include "base/memory/scoped_temp_dir.h"
33 #include "base/memory/scoped_vector.h"
34 #include "base/message_loop.h"
35 #include "base/path_service.h"
36 #include "base/string_util.h"
37 #include "base/task.h"
38 #include "base/utf_string_conversions.h"
39 #include "chrome/browser/download/download_item.h"
40 #include "chrome/browser/history/download_create_info.h"
41 #include "chrome/browser/history/history.h"
42 #include "chrome/browser/history/history_backend.h"
43 #include "chrome/browser/history/history_database.h"
44 #include "chrome/browser/history/history_notifications.h"
45 #include "chrome/browser/history/in_memory_database.h"
46 #include "chrome/browser/history/in_memory_history_backend.h"
47 #include "chrome/browser/history/page_usage_data.h"
48 #include "chrome/common/chrome_paths.h"
49 #include "chrome/common/thumbnail_score.h"
50 #include "chrome/tools/profiles/thumbnail-inl.h"
51 #include "content/common/notification_details.h"
52 #include "content/common/notification_source.h"
53 #include "testing/gtest/include/gtest/gtest.h"
54 #include "third_party/skia/include/core/SkBitmap.h"
55 #include "ui/gfx/codec/jpeg_codec.h"
56 
57 using base::Time;
58 using base::TimeDelta;
59 
60 namespace history {
61 class HistoryTest;
62 }
63 
64 // Specialize RunnableMethodTraits for HistoryTest so we can create callbacks.
65 // None of these callbacks can outlast the test, so there is not need to retain
66 // the HistoryTest object.
67 DISABLE_RUNNABLE_METHOD_REFCOUNT(history::HistoryTest);
68 
69 namespace history {
70 
71 namespace {
72 
73 // The tracker uses RenderProcessHost pointers for scoping but never
74 // dereferences them. We use ints because it's easier. This function converts
75 // between the two.
MakeFakeHost(int id)76 static void* MakeFakeHost(int id) {
77   void* host = 0;
78   memcpy(&host, &id, sizeof(id));
79   return host;
80 }
81 
82 }  // namespace
83 
84 // Delegate class for when we create a backend without a HistoryService.
85 class BackendDelegate : public HistoryBackend::Delegate {
86  public:
BackendDelegate(HistoryTest * history_test)87   explicit BackendDelegate(HistoryTest* history_test)
88       : history_test_(history_test) {
89   }
90 
NotifyProfileError(sql::InitStatus init_status)91   virtual void NotifyProfileError(sql::InitStatus init_status) OVERRIDE {}
92   virtual void SetInMemoryBackend(InMemoryHistoryBackend* backend) OVERRIDE;
93   virtual void BroadcastNotifications(NotificationType type,
94                                       HistoryDetails* details) OVERRIDE;
DBLoaded()95   virtual void DBLoaded() OVERRIDE {}
StartTopSitesMigration()96   virtual void StartTopSitesMigration() OVERRIDE {}
97  private:
98   HistoryTest* history_test_;
99 };
100 
101 // This must be outside the anonymous namespace for the friend statement in
102 // HistoryBackend to work.
103 class HistoryTest : public testing::Test {
104  public:
HistoryTest()105   HistoryTest()
106       : history_service_(NULL),
107         got_thumbnail_callback_(false),
108         redirect_query_success_(false),
109         query_url_success_(false),
110         db_(NULL) {
111   }
~HistoryTest()112   ~HistoryTest() {
113   }
114 
115   // Creates the HistoryBackend and HistoryDatabase on the current thread,
116   // assigning the values to backend_ and db_.
CreateBackendAndDatabase()117   void CreateBackendAndDatabase() {
118     backend_ =
119         new HistoryBackend(history_dir_, new BackendDelegate(this), NULL);
120     backend_->Init(std::string(), false);
121     db_ = backend_->db_.get();
122     DCHECK(in_mem_backend_.get()) << "Mem backend should have been set by "
123         "HistoryBackend::Init";
124   }
125 
OnSegmentUsageAvailable(CancelableRequestProvider::Handle handle,std::vector<PageUsageData * > * data)126   void OnSegmentUsageAvailable(CancelableRequestProvider::Handle handle,
127                                std::vector<PageUsageData*>* data) {
128     page_usage_data_->swap(*data);
129     MessageLoop::current()->Quit();
130   }
131 
OnDeleteURLsDone(CancelableRequestProvider::Handle handle)132   void OnDeleteURLsDone(CancelableRequestProvider::Handle handle) {
133     MessageLoop::current()->Quit();
134   }
135 
OnMostVisitedURLsAvailable(CancelableRequestProvider::Handle handle,MostVisitedURLList url_list)136   void OnMostVisitedURLsAvailable(CancelableRequestProvider::Handle handle,
137                                   MostVisitedURLList url_list) {
138     most_visited_urls_.swap(url_list);
139     MessageLoop::current()->Quit();
140   }
141 
142  protected:
143   friend class BackendDelegate;
144 
145   // testing::Test
SetUp()146   virtual void SetUp() {
147     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
148     history_dir_ = temp_dir_.path().AppendASCII("HistoryTest");
149     ASSERT_TRUE(file_util::CreateDirectory(history_dir_));
150   }
151 
DeleteBackend()152   void DeleteBackend() {
153     if (backend_) {
154       backend_->Closing();
155       backend_ = NULL;
156     }
157   }
158 
TearDown()159   virtual void TearDown() {
160     DeleteBackend();
161 
162     if (history_service_)
163       CleanupHistoryService();
164 
165     // Make sure we don't have any event pending that could disrupt the next
166     // test.
167     MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask);
168     MessageLoop::current()->Run();
169   }
170 
CleanupHistoryService()171   void CleanupHistoryService() {
172     DCHECK(history_service_.get());
173 
174     history_service_->NotifyRenderProcessHostDestruction(0);
175     history_service_->SetOnBackendDestroyTask(new MessageLoop::QuitTask);
176     history_service_->Cleanup();
177     history_service_ = NULL;
178 
179     // Wait for the backend class to terminate before deleting the files and
180     // moving to the next test. Note: if this never terminates, somebody is
181     // probably leaking a reference to the history backend, so it never calls
182     // our destroy task.
183     MessageLoop::current()->Run();
184   }
185 
AddDownload(int32 state,const Time & time)186   int64 AddDownload(int32 state, const Time& time) {
187     DownloadCreateInfo download(FilePath(FILE_PATH_LITERAL("foo-path")),
188                                 GURL("foo-url"), time, 0, 512, state, 0, false);
189     return db_->CreateDownload(download);
190   }
191 
192   // Fills the query_url_row_ and query_url_visits_ structures with the
193   // information about the given URL and returns true. If the URL was not
194   // found, this will return false and those structures will not be changed.
QueryURL(HistoryService * history,const GURL & url)195   bool QueryURL(HistoryService* history, const GURL& url) {
196     history->QueryURL(url, true, &consumer_,
197                       NewCallback(this, &HistoryTest::SaveURLAndQuit));
198     MessageLoop::current()->Run();  // Will be exited in SaveURLAndQuit.
199     return query_url_success_;
200   }
201 
202   // Callback for HistoryService::QueryURL.
SaveURLAndQuit(HistoryService::Handle handle,bool success,const URLRow * url_row,VisitVector * visit_vector)203   void SaveURLAndQuit(HistoryService::Handle handle,
204                       bool success,
205                       const URLRow* url_row,
206                       VisitVector* visit_vector) {
207     query_url_success_ = success;
208     if (query_url_success_) {
209       query_url_row_ = *url_row;
210       query_url_visits_.swap(*visit_vector);
211     } else {
212       query_url_row_ = URLRow();
213       query_url_visits_.clear();
214     }
215     MessageLoop::current()->Quit();
216   }
217 
218   // Fills in saved_redirects_ with the redirect information for the given URL,
219   // returning true on success. False means the URL was not found.
QueryRedirectsFrom(HistoryService * history,const GURL & url)220   bool QueryRedirectsFrom(HistoryService* history, const GURL& url) {
221     history->QueryRedirectsFrom(url, &consumer_,
222         NewCallback(this, &HistoryTest::OnRedirectQueryComplete));
223     MessageLoop::current()->Run();  // Will be exited in *QueryComplete.
224     return redirect_query_success_;
225   }
226 
227   // Callback for QueryRedirects.
OnRedirectQueryComplete(HistoryService::Handle handle,GURL url,bool success,history::RedirectList * redirects)228   void OnRedirectQueryComplete(HistoryService::Handle handle,
229                                GURL url,
230                                bool success,
231                                history::RedirectList* redirects) {
232     redirect_query_success_ = success;
233     if (redirect_query_success_)
234       saved_redirects_.swap(*redirects);
235     else
236       saved_redirects_.clear();
237     MessageLoop::current()->Quit();
238   }
239 
240   ScopedTempDir temp_dir_;
241 
242   MessageLoopForUI message_loop_;
243 
244   // PageUsageData vector to test segments.
245   ScopedVector<PageUsageData> page_usage_data_;
246 
247   MostVisitedURLList most_visited_urls_;
248 
249   // When non-NULL, this will be deleted on tear down and we will block until
250   // the backend thread has completed. This allows tests for the history
251   // service to use this feature, but other tests to ignore this.
252   scoped_refptr<HistoryService> history_service_;
253 
254   // names of the database files
255   FilePath history_dir_;
256 
257   // Set by the thumbnail callback when we get data, you should be sure to
258   // clear this before issuing a thumbnail request.
259   bool got_thumbnail_callback_;
260   std::vector<unsigned char> thumbnail_data_;
261 
262   // Set by the redirect callback when we get data. You should be sure to
263   // clear this before issuing a redirect request.
264   history::RedirectList saved_redirects_;
265   bool redirect_query_success_;
266 
267   // For history requests.
268   CancelableRequestConsumer consumer_;
269 
270   // For saving URL info after a call to QueryURL
271   bool query_url_success_;
272   URLRow query_url_row_;
273   VisitVector query_url_visits_;
274 
275   // Created via CreateBackendAndDatabase.
276   scoped_refptr<HistoryBackend> backend_;
277   scoped_ptr<InMemoryHistoryBackend> in_mem_backend_;
278   HistoryDatabase* db_;  // Cached reference to the backend's database.
279 };
280 
SetInMemoryBackend(InMemoryHistoryBackend * backend)281 void BackendDelegate::SetInMemoryBackend(InMemoryHistoryBackend* backend) {
282   // Save the in-memory backend to the history test object, this happens
283   // synchronously, so we don't have to do anything fancy.
284   history_test_->in_mem_backend_.reset(backend);
285 }
286 
BroadcastNotifications(NotificationType type,HistoryDetails * details)287 void BackendDelegate::BroadcastNotifications(NotificationType type,
288                                              HistoryDetails* details) {
289   // Currently, just send the notifications directly to the in-memory database.
290   // We may want do do something more fancy in the future.
291   Details<HistoryDetails> det(details);
292   history_test_->in_mem_backend_->Observe(type,
293       Source<HistoryTest>(NULL), det);
294 
295   // The backend passes ownership of the details pointer to us.
296   delete details;
297 }
298 
TEST_F(HistoryTest,ClearBrowsingData_Downloads)299 TEST_F(HistoryTest, ClearBrowsingData_Downloads) {
300   CreateBackendAndDatabase();
301 
302   Time now = Time::Now();
303   TimeDelta one_day = TimeDelta::FromDays(1);
304   Time month_ago = now - TimeDelta::FromDays(30);
305 
306   // Initially there should be nothing in the downloads database.
307   std::vector<DownloadCreateInfo> downloads;
308   db_->QueryDownloads(&downloads);
309   EXPECT_EQ(0U, downloads.size());
310 
311   // Keep track of these as we need to update them later during the test.
312   DownloadID in_progress, removing;
313 
314   // Create one with a 0 time.
315   EXPECT_NE(0, AddDownload(DownloadItem::COMPLETE, Time()));
316   // Create one for now and +/- 1 day.
317   EXPECT_NE(0, AddDownload(DownloadItem::COMPLETE, now - one_day));
318   EXPECT_NE(0, AddDownload(DownloadItem::COMPLETE, now));
319   EXPECT_NE(0, AddDownload(DownloadItem::COMPLETE, now + one_day));
320   // Try the other four states.
321   EXPECT_NE(0, AddDownload(DownloadItem::COMPLETE, month_ago));
322   EXPECT_NE(0, in_progress = AddDownload(DownloadItem::IN_PROGRESS, month_ago));
323   EXPECT_NE(0, AddDownload(DownloadItem::CANCELLED, month_ago));
324   EXPECT_NE(0, AddDownload(DownloadItem::INTERRUPTED, month_ago));
325   EXPECT_NE(0, removing = AddDownload(DownloadItem::REMOVING, month_ago));
326 
327   // Test to see if inserts worked.
328   db_->QueryDownloads(&downloads);
329   EXPECT_EQ(9U, downloads.size());
330 
331   // Try removing from current timestamp. This should delete the one in the
332   // future and one very recent one.
333   db_->RemoveDownloadsBetween(now, Time());
334   db_->QueryDownloads(&downloads);
335   EXPECT_EQ(7U, downloads.size());
336 
337   // Try removing from two months ago. This should not delete items that are
338   // 'in progress' or in 'removing' state.
339   db_->RemoveDownloadsBetween(now - TimeDelta::FromDays(60), Time());
340   db_->QueryDownloads(&downloads);
341   EXPECT_EQ(3U, downloads.size());
342 
343   // Download manager converts to TimeT, which is lossy, so we do the same
344   // for comparison.
345   Time month_ago_lossy = Time::FromTimeT(month_ago.ToTimeT());
346 
347   // Make sure the right values remain.
348   EXPECT_EQ(DownloadItem::COMPLETE, downloads[0].state);
349   EXPECT_EQ(0, downloads[0].start_time.ToInternalValue());
350   EXPECT_EQ(DownloadItem::IN_PROGRESS, downloads[1].state);
351   EXPECT_EQ(month_ago_lossy.ToInternalValue(),
352             downloads[1].start_time.ToInternalValue());
353   EXPECT_EQ(DownloadItem::REMOVING, downloads[2].state);
354   EXPECT_EQ(month_ago_lossy.ToInternalValue(),
355             downloads[2].start_time.ToInternalValue());
356 
357   // Change state so we can delete the downloads.
358   EXPECT_TRUE(db_->UpdateDownload(512, DownloadItem::COMPLETE, in_progress));
359   EXPECT_TRUE(db_->UpdateDownload(512, DownloadItem::CANCELLED, removing));
360 
361   // Try removing from Time=0. This should delete all.
362   db_->RemoveDownloadsBetween(Time(), Time());
363   db_->QueryDownloads(&downloads);
364   EXPECT_EQ(0U, downloads.size());
365 
366   // Check removal of downloads stuck in IN_PROGRESS state.
367   EXPECT_NE(0, AddDownload(DownloadItem::COMPLETE,    month_ago));
368   EXPECT_NE(0, AddDownload(DownloadItem::IN_PROGRESS, month_ago));
369   db_->QueryDownloads(&downloads);
370   EXPECT_EQ(2U, downloads.size());
371   db_->RemoveDownloadsBetween(Time(), Time());
372   db_->QueryDownloads(&downloads);
373   // IN_PROGRESS download should remain. It it indicated as "Canceled"
374   EXPECT_EQ(1U, downloads.size());
375   db_->CleanUpInProgressEntries();
376   db_->QueryDownloads(&downloads);
377   EXPECT_EQ(1U, downloads.size());
378   db_->RemoveDownloadsBetween(Time(), Time());
379   db_->QueryDownloads(&downloads);
380   EXPECT_EQ(0U, downloads.size());
381 }
382 
TEST_F(HistoryTest,AddPage)383 TEST_F(HistoryTest, AddPage) {
384   scoped_refptr<HistoryService> history(new HistoryService);
385   history_service_ = history;
386   ASSERT_TRUE(history->Init(history_dir_, NULL));
387 
388   // Add the page once from a child frame.
389   const GURL test_url("http://www.google.com/");
390   history->AddPage(test_url, NULL, 0, GURL(),
391                    PageTransition::MANUAL_SUBFRAME,
392                    history::RedirectList(),
393                    history::SOURCE_BROWSED, false);
394   EXPECT_TRUE(QueryURL(history, test_url));
395   EXPECT_EQ(1, query_url_row_.visit_count());
396   EXPECT_EQ(0, query_url_row_.typed_count());
397   EXPECT_TRUE(query_url_row_.hidden());  // Hidden because of child frame.
398 
399   // Add the page once from the main frame (should unhide it).
400   history->AddPage(test_url, NULL, 0, GURL(), PageTransition::LINK,
401                    history::RedirectList(),
402                    history::SOURCE_BROWSED, false);
403   EXPECT_TRUE(QueryURL(history, test_url));
404   EXPECT_EQ(2, query_url_row_.visit_count());  // Added twice.
405   EXPECT_EQ(0, query_url_row_.typed_count());  // Never typed.
406   EXPECT_FALSE(query_url_row_.hidden());  // Because loaded in main frame.
407 }
408 
TEST_F(HistoryTest,AddPageSameTimes)409 TEST_F(HistoryTest, AddPageSameTimes) {
410   scoped_refptr<HistoryService> history(new HistoryService);
411   history_service_ = history;
412   ASSERT_TRUE(history->Init(history_dir_, NULL));
413 
414   Time now = Time::Now();
415   const GURL test_urls[] = {
416     GURL("http://timer.first.page/"),
417     GURL("http://timer.second.page/"),
418     GURL("http://timer.third.page/"),
419   };
420 
421   // Make sure that two pages added at the same time with no intervening
422   // additions have different timestamps.
423   history->AddPage(test_urls[0], now, NULL, 0, GURL(),
424                    PageTransition::LINK,
425                    history::RedirectList(),
426                    history::SOURCE_BROWSED, false);
427   EXPECT_TRUE(QueryURL(history, test_urls[0]));
428   EXPECT_EQ(1, query_url_row_.visit_count());
429   EXPECT_TRUE(now == query_url_row_.last_visit());  // gtest doesn't like Time
430 
431   history->AddPage(test_urls[1], now, NULL, 0, GURL(),
432                    PageTransition::LINK,
433                    history::RedirectList(),
434                    history::SOURCE_BROWSED, false);
435   EXPECT_TRUE(QueryURL(history, test_urls[1]));
436   EXPECT_EQ(1, query_url_row_.visit_count());
437   EXPECT_TRUE(now + TimeDelta::FromMicroseconds(1) ==
438       query_url_row_.last_visit());
439 
440   // Make sure the next page, at a different time, is also correct.
441   history->AddPage(test_urls[2], now + TimeDelta::FromMinutes(1),
442                    NULL, 0, GURL(),
443                    PageTransition::LINK,
444                    history::RedirectList(),
445                    history::SOURCE_BROWSED, false);
446   EXPECT_TRUE(QueryURL(history, test_urls[2]));
447   EXPECT_EQ(1, query_url_row_.visit_count());
448   EXPECT_TRUE(now + TimeDelta::FromMinutes(1) ==
449       query_url_row_.last_visit());
450 }
451 
TEST_F(HistoryTest,AddRedirect)452 TEST_F(HistoryTest, AddRedirect) {
453   scoped_refptr<HistoryService> history(new HistoryService);
454   history_service_ = history;
455   ASSERT_TRUE(history->Init(history_dir_, NULL));
456 
457   const char* first_sequence[] = {
458     "http://first.page/",
459     "http://second.page/"};
460   int first_count = arraysize(first_sequence);
461   history::RedirectList first_redirects;
462   for (int i = 0; i < first_count; i++)
463     first_redirects.push_back(GURL(first_sequence[i]));
464 
465   // Add the sequence of pages as a server with no referrer. Note that we need
466   // to have a non-NULL page ID scope.
467   history->AddPage(first_redirects.back(), MakeFakeHost(1), 0, GURL(),
468                    PageTransition::LINK, first_redirects,
469                    history::SOURCE_BROWSED,  true);
470 
471   // The first page should be added once with a link visit type (because we set
472   // LINK when we added the original URL, and a referrer of nowhere (0).
473   EXPECT_TRUE(QueryURL(history, first_redirects[0]));
474   EXPECT_EQ(1, query_url_row_.visit_count());
475   ASSERT_EQ(1U, query_url_visits_.size());
476   int64 first_visit = query_url_visits_[0].visit_id;
477   EXPECT_EQ(PageTransition::LINK |
478             PageTransition::CHAIN_START, query_url_visits_[0].transition);
479   EXPECT_EQ(0, query_url_visits_[0].referring_visit);  // No referrer.
480 
481   // The second page should be a server redirect type with a referrer of the
482   // first page.
483   EXPECT_TRUE(QueryURL(history, first_redirects[1]));
484   EXPECT_EQ(1, query_url_row_.visit_count());
485   ASSERT_EQ(1U, query_url_visits_.size());
486   int64 second_visit = query_url_visits_[0].visit_id;
487   EXPECT_EQ(PageTransition::SERVER_REDIRECT |
488             PageTransition::CHAIN_END, query_url_visits_[0].transition);
489   EXPECT_EQ(first_visit, query_url_visits_[0].referring_visit);
490 
491   // Check that the redirect finding function successfully reports it.
492   saved_redirects_.clear();
493   QueryRedirectsFrom(history, first_redirects[0]);
494   ASSERT_EQ(1U, saved_redirects_.size());
495   EXPECT_EQ(first_redirects[1], saved_redirects_[0]);
496 
497   // Now add a client redirect from that second visit to a third, client
498   // redirects are tracked by the RenderView prior to updating history,
499   // so we pass in a CLIENT_REDIRECT qualifier to mock that behavior.
500   history::RedirectList second_redirects;
501   second_redirects.push_back(first_redirects[1]);
502   second_redirects.push_back(GURL("http://last.page/"));
503   history->AddPage(second_redirects[1], MakeFakeHost(1), 1,
504                    second_redirects[0],
505                    static_cast<PageTransition::Type>(PageTransition::LINK |
506                        PageTransition::CLIENT_REDIRECT),
507                    second_redirects, history::SOURCE_BROWSED, true);
508 
509   // The last page (source of the client redirect) should NOT have an
510   // additional visit added, because it was a client redirect (normally it
511   // would). We should only have 1 left over from the first sequence.
512   EXPECT_TRUE(QueryURL(history, second_redirects[0]));
513   EXPECT_EQ(1, query_url_row_.visit_count());
514 
515   // The final page should be set as a client redirect from the previous visit.
516   EXPECT_TRUE(QueryURL(history, second_redirects[1]));
517   EXPECT_EQ(1, query_url_row_.visit_count());
518   ASSERT_EQ(1U, query_url_visits_.size());
519   EXPECT_EQ(PageTransition::CLIENT_REDIRECT |
520             PageTransition::CHAIN_END, query_url_visits_[0].transition);
521   EXPECT_EQ(second_visit, query_url_visits_[0].referring_visit);
522 }
523 
TEST_F(HistoryTest,Typed)524 TEST_F(HistoryTest, Typed) {
525   scoped_refptr<HistoryService> history(new HistoryService);
526   history_service_ = history;
527   ASSERT_TRUE(history->Init(history_dir_, NULL));
528 
529   // Add the page once as typed.
530   const GURL test_url("http://www.google.com/");
531   history->AddPage(test_url, NULL, 0, GURL(), PageTransition::TYPED,
532                    history::RedirectList(),
533                    history::SOURCE_BROWSED, false);
534   EXPECT_TRUE(QueryURL(history, test_url));
535 
536   // We should have the same typed & visit count.
537   EXPECT_EQ(1, query_url_row_.visit_count());
538   EXPECT_EQ(1, query_url_row_.typed_count());
539 
540   // Add the page again not typed.
541   history->AddPage(test_url, NULL, 0, GURL(), PageTransition::LINK,
542                    history::RedirectList(),
543                    history::SOURCE_BROWSED, false);
544   EXPECT_TRUE(QueryURL(history, test_url));
545 
546   // The second time should not have updated the typed count.
547   EXPECT_EQ(2, query_url_row_.visit_count());
548   EXPECT_EQ(1, query_url_row_.typed_count());
549 
550   // Add the page again as a generated URL.
551   history->AddPage(test_url, NULL, 0, GURL(),
552                    PageTransition::GENERATED, history::RedirectList(),
553                    history::SOURCE_BROWSED, false);
554   EXPECT_TRUE(QueryURL(history, test_url));
555 
556   // This should have worked like a link click.
557   EXPECT_EQ(3, query_url_row_.visit_count());
558   EXPECT_EQ(1, query_url_row_.typed_count());
559 
560   // Add the page again as a reload.
561   history->AddPage(test_url, NULL, 0, GURL(),
562                    PageTransition::RELOAD, history::RedirectList(),
563                    history::SOURCE_BROWSED, false);
564   EXPECT_TRUE(QueryURL(history, test_url));
565 
566   // This should not have incremented any visit counts.
567   EXPECT_EQ(3, query_url_row_.visit_count());
568   EXPECT_EQ(1, query_url_row_.typed_count());
569 }
570 
TEST_F(HistoryTest,SetTitle)571 TEST_F(HistoryTest, SetTitle) {
572   scoped_refptr<HistoryService> history(new HistoryService);
573   history_service_ = history;
574   ASSERT_TRUE(history->Init(history_dir_, NULL));
575 
576   // Add a URL.
577   const GURL existing_url("http://www.google.com/");
578   history->AddPage(existing_url, history::SOURCE_BROWSED);
579 
580   // Set some title.
581   const string16 existing_title = UTF8ToUTF16("Google");
582   history->SetPageTitle(existing_url, existing_title);
583 
584   // Make sure the title got set.
585   EXPECT_TRUE(QueryURL(history, existing_url));
586   EXPECT_EQ(existing_title, query_url_row_.title());
587 
588   // set a title on a nonexistent page
589   const GURL nonexistent_url("http://news.google.com/");
590   const string16 nonexistent_title = UTF8ToUTF16("Google News");
591   history->SetPageTitle(nonexistent_url, nonexistent_title);
592 
593   // Make sure nothing got written.
594   EXPECT_FALSE(QueryURL(history, nonexistent_url));
595   EXPECT_EQ(string16(), query_url_row_.title());
596 
597   // TODO(brettw) this should also test redirects, which get the title of the
598   // destination page.
599 }
600 
TEST_F(HistoryTest,Segments)601 TEST_F(HistoryTest, Segments) {
602   scoped_refptr<HistoryService> history(new HistoryService);
603   history_service_ = history;
604 
605   ASSERT_TRUE(history->Init(history_dir_, NULL));
606 
607   static const void* scope = static_cast<void*>(this);
608 
609   // Add a URL.
610   const GURL existing_url("http://www.google.com/");
611   history->AddPage(existing_url, scope, 0, GURL(),
612                    PageTransition::TYPED, history::RedirectList(),
613                    history::SOURCE_BROWSED, false);
614 
615   // Make sure a segment was created.
616   history->QuerySegmentUsageSince(
617       &consumer_, Time::Now() - TimeDelta::FromDays(1), 10,
618       NewCallback(static_cast<HistoryTest*>(this),
619                   &HistoryTest::OnSegmentUsageAvailable));
620 
621   // Wait for processing.
622   MessageLoop::current()->Run();
623 
624   ASSERT_EQ(1U, page_usage_data_->size());
625   EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url);
626   EXPECT_DOUBLE_EQ(3.0, page_usage_data_[0]->GetScore());
627 
628   // Add a URL which doesn't create a segment.
629   const GURL link_url("http://yahoo.com/");
630   history->AddPage(link_url, scope, 0, GURL(),
631                    PageTransition::LINK, history::RedirectList(),
632                    history::SOURCE_BROWSED, false);
633 
634   // Query again
635   history->QuerySegmentUsageSince(
636       &consumer_, Time::Now() - TimeDelta::FromDays(1), 10,
637       NewCallback(static_cast<HistoryTest*>(this),
638                   &HistoryTest::OnSegmentUsageAvailable));
639 
640   // Wait for processing.
641   MessageLoop::current()->Run();
642 
643   // Make sure we still have one segment.
644   ASSERT_EQ(1U, page_usage_data_->size());
645   EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url);
646 
647   // Add a page linked from existing_url.
648   history->AddPage(GURL("http://www.google.com/foo"), scope, 3, existing_url,
649                    PageTransition::LINK, history::RedirectList(),
650                    history::SOURCE_BROWSED, false);
651 
652   // Query again
653   history->QuerySegmentUsageSince(
654       &consumer_, Time::Now() - TimeDelta::FromDays(1), 10,
655       NewCallback(static_cast<HistoryTest*>(this),
656                   &HistoryTest::OnSegmentUsageAvailable));
657 
658   // Wait for processing.
659   MessageLoop::current()->Run();
660 
661   // Make sure we still have one segment.
662   ASSERT_EQ(1U, page_usage_data_->size());
663   EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url);
664 
665   // However, the score should have increased.
666   EXPECT_GT(page_usage_data_[0]->GetScore(), 5.0);
667 }
668 
TEST_F(HistoryTest,MostVisitedURLs)669 TEST_F(HistoryTest, MostVisitedURLs) {
670   scoped_refptr<HistoryService> history(new HistoryService);
671   history_service_ = history;
672   ASSERT_TRUE(history->Init(history_dir_, NULL));
673 
674   const GURL url0("http://www.google.com/url0/");
675   const GURL url1("http://www.google.com/url1/");
676   const GURL url2("http://www.google.com/url2/");
677   const GURL url3("http://www.google.com/url3/");
678   const GURL url4("http://www.google.com/url4/");
679 
680   static const void* scope = static_cast<void*>(this);
681 
682   // Add two pages.
683   history->AddPage(url0, scope, 0, GURL(),
684                    PageTransition::TYPED, history::RedirectList(),
685                    history::SOURCE_BROWSED, false);
686   history->AddPage(url1, scope, 0, GURL(),
687                    PageTransition::TYPED, history::RedirectList(),
688                    history::SOURCE_BROWSED, false);
689   history->QueryMostVisitedURLs(20, 90, &consumer_,
690                                 NewCallback(static_cast<HistoryTest*>(this),
691                                     &HistoryTest::OnMostVisitedURLsAvailable));
692   MessageLoop::current()->Run();
693 
694   EXPECT_EQ(2U, most_visited_urls_.size());
695   EXPECT_EQ(url0, most_visited_urls_[0].url);
696   EXPECT_EQ(url1, most_visited_urls_[1].url);
697 
698   // Add another page.
699   history->AddPage(url2, scope, 0, GURL(),
700                    PageTransition::TYPED, history::RedirectList(),
701                    history::SOURCE_BROWSED, false);
702   history->QueryMostVisitedURLs(20, 90, &consumer_,
703                                 NewCallback(static_cast<HistoryTest*>(this),
704                                     &HistoryTest::OnMostVisitedURLsAvailable));
705   MessageLoop::current()->Run();
706 
707   EXPECT_EQ(3U, most_visited_urls_.size());
708   EXPECT_EQ(url0, most_visited_urls_[0].url);
709   EXPECT_EQ(url1, most_visited_urls_[1].url);
710   EXPECT_EQ(url2, most_visited_urls_[2].url);
711 
712   // Revisit url2, making it the top URL.
713   history->AddPage(url2, scope, 0, GURL(),
714                    PageTransition::TYPED, history::RedirectList(),
715                    history::SOURCE_BROWSED, false);
716   history->QueryMostVisitedURLs(20, 90, &consumer_,
717                                 NewCallback(static_cast<HistoryTest*>(this),
718                                     &HistoryTest::OnMostVisitedURLsAvailable));
719   MessageLoop::current()->Run();
720 
721   EXPECT_EQ(3U, most_visited_urls_.size());
722   EXPECT_EQ(url2, most_visited_urls_[0].url);
723   EXPECT_EQ(url0, most_visited_urls_[1].url);
724   EXPECT_EQ(url1, most_visited_urls_[2].url);
725 
726   // Revisit url1, making it the top URL.
727   history->AddPage(url1, scope, 0, GURL(),
728                    PageTransition::TYPED, history::RedirectList(),
729                    history::SOURCE_BROWSED, false);
730   history->QueryMostVisitedURLs(20, 90, &consumer_,
731                                 NewCallback(static_cast<HistoryTest*>(this),
732                                     &HistoryTest::OnMostVisitedURLsAvailable));
733   MessageLoop::current()->Run();
734 
735   EXPECT_EQ(3U, most_visited_urls_.size());
736   EXPECT_EQ(url1, most_visited_urls_[0].url);
737   EXPECT_EQ(url2, most_visited_urls_[1].url);
738   EXPECT_EQ(url0, most_visited_urls_[2].url);
739 
740   // Redirects
741   history::RedirectList redirects;
742   redirects.push_back(url3);
743   redirects.push_back(url4);
744 
745   // Visit url4 using redirects.
746   history->AddPage(url4, scope, 0, GURL(),
747                    PageTransition::TYPED, redirects,
748                    history::SOURCE_BROWSED, false);
749   history->QueryMostVisitedURLs(20, 90, &consumer_,
750                                 NewCallback(static_cast<HistoryTest*>(this),
751                                     &HistoryTest::OnMostVisitedURLsAvailable));
752   MessageLoop::current()->Run();
753 
754   EXPECT_EQ(4U, most_visited_urls_.size());
755   EXPECT_EQ(url1, most_visited_urls_[0].url);
756   EXPECT_EQ(url2, most_visited_urls_[1].url);
757   EXPECT_EQ(url0, most_visited_urls_[2].url);
758   EXPECT_EQ(url3, most_visited_urls_[3].url);
759   EXPECT_EQ(2U, most_visited_urls_[3].redirects.size());
760 }
761 
762 // The version of the history database should be current in the "typical
763 // history" example file or it will be imported on startup, throwing off timing
764 // measurements.
765 //
766 // See test/data/profiles/typical_history/README.txt for instructions on
767 // how to up the version.
TEST(HistoryProfileTest,TypicalProfileVersion)768 TEST(HistoryProfileTest, TypicalProfileVersion) {
769   FilePath file;
770   ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &file));
771   file = file.AppendASCII("profiles");
772   file = file.AppendASCII("typical_history");
773   file = file.AppendASCII("Default");
774   file = file.AppendASCII("History");
775 
776   int cur_version = HistoryDatabase::GetCurrentVersion();
777 
778   sql::Connection db;
779   ASSERT_TRUE(db.Open(file));
780 
781   {
782     sql::Statement s(db.GetUniqueStatement(
783         "SELECT value FROM meta WHERE key = 'version'"));
784     EXPECT_TRUE(s.Step());
785     int file_version = s.ColumnInt(0);
786     EXPECT_EQ(cur_version, file_version);
787   }
788 }
789 
790 namespace {
791 
792 // A HistoryDBTask implementation. Each time RunOnDBThread is invoked
793 // invoke_count is increment. When invoked kWantInvokeCount times, true is
794 // returned from RunOnDBThread which should stop RunOnDBThread from being
795 // invoked again. When DoneRunOnMainThread is invoked, done_invoked is set to
796 // true.
797 class HistoryDBTaskImpl : public HistoryDBTask {
798  public:
799   static const int kWantInvokeCount;
800 
HistoryDBTaskImpl()801   HistoryDBTaskImpl() : invoke_count(0), done_invoked(false) {}
802 
RunOnDBThread(HistoryBackend * backend,HistoryDatabase * db)803   virtual bool RunOnDBThread(HistoryBackend* backend, HistoryDatabase* db) {
804     return (++invoke_count == kWantInvokeCount);
805   }
806 
DoneRunOnMainThread()807   virtual void DoneRunOnMainThread() {
808     done_invoked = true;
809     MessageLoop::current()->Quit();
810   }
811 
812   int invoke_count;
813   bool done_invoked;
814 
815  private:
~HistoryDBTaskImpl()816   virtual ~HistoryDBTaskImpl() {}
817 
818   DISALLOW_COPY_AND_ASSIGN(HistoryDBTaskImpl);
819 };
820 
821 // static
822 const int HistoryDBTaskImpl::kWantInvokeCount = 2;
823 
824 }  // namespace
825 
TEST_F(HistoryTest,HistoryDBTask)826 TEST_F(HistoryTest, HistoryDBTask) {
827   CancelableRequestConsumerT<int, 0> request_consumer;
828   HistoryService* history = new HistoryService();
829   ASSERT_TRUE(history->Init(history_dir_, NULL));
830   scoped_refptr<HistoryDBTaskImpl> task(new HistoryDBTaskImpl());
831   history_service_ = history;
832   history->ScheduleDBTask(task.get(), &request_consumer);
833   // Run the message loop. When HistoryDBTaskImpl::DoneRunOnMainThread runs,
834   // it will stop the message loop. If the test hangs here, it means
835   // DoneRunOnMainThread isn't being invoked correctly.
836   MessageLoop::current()->Run();
837   CleanupHistoryService();
838   // WARNING: history has now been deleted.
839   history = NULL;
840   ASSERT_EQ(HistoryDBTaskImpl::kWantInvokeCount, task->invoke_count);
841   ASSERT_TRUE(task->done_invoked);
842 }
843 
TEST_F(HistoryTest,HistoryDBTaskCanceled)844 TEST_F(HistoryTest, HistoryDBTaskCanceled) {
845   CancelableRequestConsumerT<int, 0> request_consumer;
846   HistoryService* history = new HistoryService();
847   ASSERT_TRUE(history->Init(history_dir_, NULL));
848   scoped_refptr<HistoryDBTaskImpl> task(new HistoryDBTaskImpl());
849   history_service_ = history;
850   history->ScheduleDBTask(task.get(), &request_consumer);
851   request_consumer.CancelAllRequests();
852   CleanupHistoryService();
853   // WARNING: history has now been deleted.
854   history = NULL;
855   ASSERT_FALSE(task->done_invoked);
856 }
857 
858 }  // namespace history
859