Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / history / history_unittest.cc
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 // 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 "base/basictypes.h"
26 #include "base/bind.h"
27 #include "base/bind_helpers.h"
28 #include "base/callback.h"
29 #include "base/command_line.h"
30 #include "base/compiler_specific.h"
31 #include "base/file_util.h"
32 #include "base/files/file_path.h"
33 #include "base/files/scoped_temp_dir.h"
34 #include "base/logging.h"
35 #include "base/memory/scoped_ptr.h"
36 #include "base/memory/scoped_vector.h"
37 #include "base/message_loop/message_loop.h"
38 #include "base/path_service.h"
39 #include "base/strings/string_util.h"
40 #include "base/strings/stringprintf.h"
41 #include "base/strings/utf_string_conversions.h"
42 #include "base/threading/platform_thread.h"
43 #include "base/time/time.h"
44 #include "chrome/browser/history/download_row.h"
45 #include "chrome/browser/history/history_backend.h"
46 #include "chrome/browser/history/history_database.h"
47 #include "chrome/browser/history/history_db_task.h"
48 #include "chrome/browser/history/history_notifications.h"
49 #include "chrome/browser/history/history_service.h"
50 #include "chrome/browser/history/history_unittest_base.h"
51 #include "chrome/browser/history/in_memory_database.h"
52 #include "chrome/browser/history/in_memory_history_backend.h"
53 #include "chrome/browser/history/page_usage_data.h"
54 #include "chrome/common/chrome_constants.h"
55 #include "chrome/common/chrome_paths.h"
56 #include "chrome/common/thumbnail_score.h"
57 #include "chrome/tools/profiles/thumbnail-inl.h"
58 #include "content/public/browser/download_item.h"
59 #include "content/public/browser/notification_details.h"
60 #include "content/public/browser/notification_source.h"
61 #include "sql/connection.h"
62 #include "sql/statement.h"
63 #include "sync/api/attachments/attachment_id.h"
64 #include "sync/api/attachments/attachment_service_proxy_for_test.h"
65 #include "sync/api/fake_sync_change_processor.h"
66 #include "sync/api/sync_change.h"
67 #include "sync/api/sync_change_processor.h"
68 #include "sync/api/sync_change_processor_wrapper_for_test.h"
69 #include "sync/api/sync_error.h"
70 #include "sync/api/sync_error_factory.h"
71 #include "sync/api/sync_merge_result.h"
72 #include "sync/protocol/history_delete_directive_specifics.pb.h"
73 #include "sync/protocol/sync.pb.h"
74 #include "testing/gtest/include/gtest/gtest.h"
75 #include "third_party/skia/include/core/SkBitmap.h"
76 #include "ui/gfx/codec/jpeg_codec.h"
77
78 using base::Time;
79 using base::TimeDelta;
80 using content::DownloadItem;
81
82 namespace history {
83 class HistoryBackendDBTest;
84
85 // Delegate class for when we create a backend without a HistoryService.
86 //
87 // This must be outside the anonymous namespace for the friend statement in
88 // HistoryBackendDBTest to work.
89 class BackendDelegate : public HistoryBackend::Delegate {
90  public:
91   explicit BackendDelegate(HistoryBackendDBTest* history_test)
92       : history_test_(history_test) {
93   }
94
95   virtual void NotifyProfileError(sql::InitStatus init_status) OVERRIDE {}
96   virtual void SetInMemoryBackend(
97       scoped_ptr<InMemoryHistoryBackend> backend) OVERRIDE;
98   virtual void BroadcastNotifications(
99       int type,
100       scoped_ptr<HistoryDetails> details) OVERRIDE;
101   virtual void DBLoaded() OVERRIDE {}
102   virtual void NotifyVisitDBObserversOnAddVisit(
103       const BriefVisitInfo& info) OVERRIDE {}
104  private:
105   HistoryBackendDBTest* history_test_;
106 };
107
108 // This must be outside the anonymous namespace for the friend statement in
109 // HistoryBackend to work.
110 class HistoryBackendDBTest : public HistoryUnitTestBase {
111  public:
112   HistoryBackendDBTest() : db_(NULL) {
113   }
114
115   virtual ~HistoryBackendDBTest() {
116   }
117
118  protected:
119   friend class BackendDelegate;
120
121   // Creates the HistoryBackend and HistoryDatabase on the current thread,
122   // assigning the values to backend_ and db_.
123   void CreateBackendAndDatabase() {
124     backend_ =
125         new HistoryBackend(history_dir_, new BackendDelegate(this), NULL);
126     backend_->Init(std::string(), false);
127     db_ = backend_->db_.get();
128     DCHECK(in_mem_backend_) << "Mem backend should have been set by "
129         "HistoryBackend::Init";
130   }
131
132   void CreateDBVersion(int version) {
133     base::FilePath data_path;
134     ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path));
135     data_path = data_path.AppendASCII("History");
136     data_path =
137           data_path.AppendASCII(base::StringPrintf("history.%d.sql", version));
138     ASSERT_NO_FATAL_FAILURE(
139         ExecuteSQLScript(data_path, history_dir_.Append(
140             chrome::kHistoryFilename)));
141   }
142
143   // testing::Test
144   virtual void SetUp() {
145     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
146     history_dir_ = temp_dir_.path().AppendASCII("HistoryBackendDBTest");
147     ASSERT_TRUE(base::CreateDirectory(history_dir_));
148   }
149
150   void DeleteBackend() {
151     if (backend_.get()) {
152       backend_->Closing();
153       backend_ = NULL;
154     }
155   }
156
157   virtual void TearDown() {
158     DeleteBackend();
159
160     // Make sure we don't have any event pending that could disrupt the next
161     // test.
162     base::MessageLoop::current()->PostTask(FROM_HERE,
163                                            base::MessageLoop::QuitClosure());
164     base::MessageLoop::current()->Run();
165   }
166
167   bool AddDownload(uint32 id,
168                    DownloadItem::DownloadState state,
169                    const Time& time) {
170     std::vector<GURL> url_chain;
171     url_chain.push_back(GURL("foo-url"));
172
173     DownloadRow download(base::FilePath(FILE_PATH_LITERAL("current-path")),
174                          base::FilePath(FILE_PATH_LITERAL("target-path")),
175                          url_chain,
176                          GURL("http://referrer.com/"),
177                          time,
178                          time,
179                          std::string(),
180                          std::string(),
181                          0,
182                          512,
183                          state,
184                          content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
185                          content::DOWNLOAD_INTERRUPT_REASON_NONE,
186                          id,
187                          false,
188                          "by_ext_id",
189                          "by_ext_name");
190     return db_->CreateDownload(download);
191   }
192
193   base::ScopedTempDir temp_dir_;
194
195   base::MessageLoopForUI message_loop_;
196
197   // names of the database files
198   base::FilePath history_dir_;
199
200   // Created via CreateBackendAndDatabase.
201   scoped_refptr<HistoryBackend> backend_;
202   scoped_ptr<InMemoryHistoryBackend> in_mem_backend_;
203   HistoryDatabase* db_;  // Cached reference to the backend's database.
204 };
205
206 void BackendDelegate::SetInMemoryBackend(
207     scoped_ptr<InMemoryHistoryBackend> backend) {
208   // Save the in-memory backend to the history test object, this happens
209   // synchronously, so we don't have to do anything fancy.
210   history_test_->in_mem_backend_.swap(backend);
211 }
212
213 void BackendDelegate::BroadcastNotifications(
214     int type,
215     scoped_ptr<HistoryDetails> details) {
216   // Currently, just send the notifications directly to the in-memory database.
217   // We may want do do something more fancy in the future.
218   content::Details<HistoryDetails> det(details.get());
219   history_test_->in_mem_backend_->Observe(type,
220       content::Source<HistoryBackendDBTest>(NULL), det);
221 }
222
223 TEST_F(HistoryBackendDBTest, ClearBrowsingData_Downloads) {
224   CreateBackendAndDatabase();
225
226   // Initially there should be nothing in the downloads database.
227   std::vector<DownloadRow> downloads;
228   db_->QueryDownloads(&downloads);
229   EXPECT_EQ(0U, downloads.size());
230
231   // Add a download, test that it was added correctly, remove it, test that it
232   // was removed.
233   Time now = Time();
234   uint32 id = 1;
235   EXPECT_TRUE(AddDownload(id, DownloadItem::COMPLETE, Time()));
236   db_->QueryDownloads(&downloads);
237   EXPECT_EQ(1U, downloads.size());
238
239   EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("current-path")),
240             downloads[0].current_path);
241   EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("target-path")),
242             downloads[0].target_path);
243   EXPECT_EQ(1UL, downloads[0].url_chain.size());
244   EXPECT_EQ(GURL("foo-url"), downloads[0].url_chain[0]);
245   EXPECT_EQ(std::string("http://referrer.com/"),
246             std::string(downloads[0].referrer_url.spec()));
247   EXPECT_EQ(now, downloads[0].start_time);
248   EXPECT_EQ(now, downloads[0].end_time);
249   EXPECT_EQ(0, downloads[0].received_bytes);
250   EXPECT_EQ(512, downloads[0].total_bytes);
251   EXPECT_EQ(DownloadItem::COMPLETE, downloads[0].state);
252   EXPECT_EQ(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
253             downloads[0].danger_type);
254   EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
255             downloads[0].interrupt_reason);
256   EXPECT_FALSE(downloads[0].opened);
257   EXPECT_EQ("by_ext_id", downloads[0].by_ext_id);
258   EXPECT_EQ("by_ext_name", downloads[0].by_ext_name);
259
260   db_->QueryDownloads(&downloads);
261   EXPECT_EQ(1U, downloads.size());
262   db_->RemoveDownload(id);
263   db_->QueryDownloads(&downloads);
264   EXPECT_EQ(0U, downloads.size());
265 }
266
267 TEST_F(HistoryBackendDBTest, MigrateDownloadsState) {
268   // Create the db we want.
269   ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
270   {
271     // Open the db for manual manipulation.
272     sql::Connection db;
273     ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
274
275     // Manually insert corrupted rows; there's infrastructure in place now to
276     // make this impossible, at least according to the test above.
277     for (int state = 0; state < 5; ++state) {
278       sql::Statement s(db.GetUniqueStatement(
279             "INSERT INTO downloads (id, full_path, url, start_time, "
280             "received_bytes, total_bytes, state, end_time, opened) VALUES "
281             "(?, ?, ?, ?, ?, ?, ?, ?, ?)"));
282       s.BindInt64(0, 1 + state);
283       s.BindString(1, "path");
284       s.BindString(2, "url");
285       s.BindInt64(3, base::Time::Now().ToTimeT());
286       s.BindInt64(4, 100);
287       s.BindInt64(5, 100);
288       s.BindInt(6, state);
289       s.BindInt64(7, base::Time::Now().ToTimeT());
290       s.BindInt(8, state % 2);
291       ASSERT_TRUE(s.Run());
292     }
293   }
294
295   // Re-open the db using the HistoryDatabase, which should migrate from version
296   // 22 to the current version, fixing just the row whose state was 3.
297   // Then close the db so that we can re-open it directly.
298   CreateBackendAndDatabase();
299   DeleteBackend();
300   {
301     // Re-open the db for manual manipulation.
302     sql::Connection db;
303     ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
304     {
305       // The version should have been updated.
306       int cur_version = HistoryDatabase::GetCurrentVersion();
307       ASSERT_LT(22, cur_version);
308       sql::Statement s(db.GetUniqueStatement(
309           "SELECT value FROM meta WHERE key = 'version'"));
310       EXPECT_TRUE(s.Step());
311       EXPECT_EQ(cur_version, s.ColumnInt(0));
312     }
313     {
314       sql::Statement statement(db.GetUniqueStatement(
315           "SELECT id, state, opened "
316           "FROM downloads "
317           "ORDER BY id"));
318       int counter = 0;
319       while (statement.Step()) {
320         EXPECT_EQ(1 + counter, statement.ColumnInt64(0));
321         // The only thing that migration should have changed was state from 3 to
322         // 4.
323         EXPECT_EQ(((counter == 3) ? 4 : counter), statement.ColumnInt(1));
324         EXPECT_EQ(counter % 2, statement.ColumnInt(2));
325         ++counter;
326       }
327       EXPECT_EQ(5, counter);
328     }
329   }
330 }
331
332 TEST_F(HistoryBackendDBTest, MigrateDownloadsReasonPathsAndDangerType) {
333   Time now(base::Time::Now());
334
335   // Create the db we want.  The schema didn't change from 22->23, so just
336   // re-use the v22 file.
337   ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
338   {
339     // Re-open the db for manual manipulation.
340     sql::Connection db;
341     ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
342
343     // Manually insert some rows.
344     sql::Statement s(db.GetUniqueStatement(
345         "INSERT INTO downloads (id, full_path, url, start_time, "
346         "received_bytes, total_bytes, state, end_time, opened) VALUES "
347         "(?, ?, ?, ?, ?, ?, ?, ?, ?)"));
348
349     int64 id = 0;
350     // Null path.
351     s.BindInt64(0, ++id);
352     s.BindString(1, std::string());
353     s.BindString(2, "http://whatever.com/index.html");
354     s.BindInt64(3, now.ToTimeT());
355     s.BindInt64(4, 100);
356     s.BindInt64(5, 100);
357     s.BindInt(6, 1);
358     s.BindInt64(7, now.ToTimeT());
359     s.BindInt(8, 1);
360     ASSERT_TRUE(s.Run());
361     s.Reset(true);
362
363     // Non-null path.
364     s.BindInt64(0, ++id);
365     s.BindString(1, "/path/to/some/file");
366     s.BindString(2, "http://whatever.com/index1.html");
367     s.BindInt64(3, now.ToTimeT());
368     s.BindInt64(4, 100);
369     s.BindInt64(5, 100);
370     s.BindInt(6, 1);
371     s.BindInt64(7, now.ToTimeT());
372     s.BindInt(8, 1);
373     ASSERT_TRUE(s.Run());
374   }
375
376   // Re-open the db using the HistoryDatabase, which should migrate from version
377   // 23 to 24, creating the new tables and creating the new path, reason,
378   // and danger columns.
379   CreateBackendAndDatabase();
380   DeleteBackend();
381   {
382     // Re-open the db for manual manipulation.
383     sql::Connection db;
384     ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
385     {
386       // The version should have been updated.
387       int cur_version = HistoryDatabase::GetCurrentVersion();
388       ASSERT_LT(23, cur_version);
389       sql::Statement s(db.GetUniqueStatement(
390           "SELECT value FROM meta WHERE key = 'version'"));
391       EXPECT_TRUE(s.Step());
392       EXPECT_EQ(cur_version, s.ColumnInt(0));
393     }
394     {
395       base::Time nowish(base::Time::FromTimeT(now.ToTimeT()));
396
397       // Confirm downloads table is valid.
398       sql::Statement statement(db.GetUniqueStatement(
399           "SELECT id, interrupt_reason, current_path, target_path, "
400           "       danger_type, start_time, end_time "
401           "FROM downloads ORDER BY id"));
402       EXPECT_TRUE(statement.Step());
403       EXPECT_EQ(1, statement.ColumnInt64(0));
404       EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
405                 statement.ColumnInt(1));
406       EXPECT_EQ("", statement.ColumnString(2));
407       EXPECT_EQ("", statement.ColumnString(3));
408       // Implicit dependence on value of kDangerTypeNotDangerous from
409       // download_database.cc.
410       EXPECT_EQ(0, statement.ColumnInt(4));
411       EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(5));
412       EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(6));
413
414       EXPECT_TRUE(statement.Step());
415       EXPECT_EQ(2, statement.ColumnInt64(0));
416       EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
417                 statement.ColumnInt(1));
418       EXPECT_EQ("/path/to/some/file", statement.ColumnString(2));
419       EXPECT_EQ("/path/to/some/file", statement.ColumnString(3));
420       EXPECT_EQ(0, statement.ColumnInt(4));
421       EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(5));
422       EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(6));
423
424       EXPECT_FALSE(statement.Step());
425     }
426     {
427       // Confirm downloads_url_chains table is valid.
428       sql::Statement statement(db.GetUniqueStatement(
429           "SELECT id, chain_index, url FROM downloads_url_chains "
430           " ORDER BY id, chain_index"));
431       EXPECT_TRUE(statement.Step());
432       EXPECT_EQ(1, statement.ColumnInt64(0));
433       EXPECT_EQ(0, statement.ColumnInt(1));
434       EXPECT_EQ("http://whatever.com/index.html", statement.ColumnString(2));
435
436       EXPECT_TRUE(statement.Step());
437       EXPECT_EQ(2, statement.ColumnInt64(0));
438       EXPECT_EQ(0, statement.ColumnInt(1));
439       EXPECT_EQ("http://whatever.com/index1.html", statement.ColumnString(2));
440
441       EXPECT_FALSE(statement.Step());
442     }
443   }
444 }
445
446 TEST_F(HistoryBackendDBTest, MigrateReferrer) {
447   Time now(base::Time::Now());
448   ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
449   {
450     sql::Connection db;
451     ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
452     sql::Statement s(db.GetUniqueStatement(
453         "INSERT INTO downloads (id, full_path, url, start_time, "
454         "received_bytes, total_bytes, state, end_time, opened) VALUES "
455         "(?, ?, ?, ?, ?, ?, ?, ?, ?)"));
456     int64 db_handle = 0;
457     s.BindInt64(0, ++db_handle);
458     s.BindString(1, "full_path");
459     s.BindString(2, "http://whatever.com/index.html");
460     s.BindInt64(3, now.ToTimeT());
461     s.BindInt64(4, 100);
462     s.BindInt64(5, 100);
463     s.BindInt(6, 1);
464     s.BindInt64(7, now.ToTimeT());
465     s.BindInt(8, 1);
466     ASSERT_TRUE(s.Run());
467   }
468   // Re-open the db using the HistoryDatabase, which should migrate to version
469   // 26, creating the referrer column.
470   CreateBackendAndDatabase();
471   DeleteBackend();
472   {
473     // Re-open the db for manual manipulation.
474     sql::Connection db;
475     ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
476     // The version should have been updated.
477     int cur_version = HistoryDatabase::GetCurrentVersion();
478     ASSERT_LE(26, cur_version);
479     {
480       sql::Statement s(db.GetUniqueStatement(
481           "SELECT value FROM meta WHERE key = 'version'"));
482       EXPECT_TRUE(s.Step());
483       EXPECT_EQ(cur_version, s.ColumnInt(0));
484     }
485     {
486       sql::Statement s(db.GetUniqueStatement(
487           "SELECT referrer from downloads"));
488       EXPECT_TRUE(s.Step());
489       EXPECT_EQ(std::string(), s.ColumnString(0));
490     }
491   }
492 }
493
494 TEST_F(HistoryBackendDBTest, MigrateDownloadedByExtension) {
495   Time now(base::Time::Now());
496   ASSERT_NO_FATAL_FAILURE(CreateDBVersion(26));
497   {
498     sql::Connection db;
499     ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
500     {
501       sql::Statement s(db.GetUniqueStatement(
502           "INSERT INTO downloads (id, current_path, target_path, start_time, "
503           "received_bytes, total_bytes, state, danger_type, interrupt_reason, "
504           "end_time, opened, referrer) VALUES "
505           "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
506       s.BindInt64(0, 1);
507       s.BindString(1, "current_path");
508       s.BindString(2, "target_path");
509       s.BindInt64(3, now.ToTimeT());
510       s.BindInt64(4, 100);
511       s.BindInt64(5, 100);
512       s.BindInt(6, 1);
513       s.BindInt(7, 0);
514       s.BindInt(8, 0);
515       s.BindInt64(9, now.ToTimeT());
516       s.BindInt(10, 1);
517       s.BindString(11, "referrer");
518       ASSERT_TRUE(s.Run());
519     }
520     {
521       sql::Statement s(db.GetUniqueStatement(
522           "INSERT INTO downloads_url_chains (id, chain_index, url) VALUES "
523           "(?, ?, ?)"));
524       s.BindInt64(0, 4);
525       s.BindInt64(1, 0);
526       s.BindString(2, "url");
527       ASSERT_TRUE(s.Run());
528     }
529   }
530   // Re-open the db using the HistoryDatabase, which should migrate to version
531   // 27, creating the by_ext_id and by_ext_name columns.
532   CreateBackendAndDatabase();
533   DeleteBackend();
534   {
535     // Re-open the db for manual manipulation.
536     sql::Connection db;
537     ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
538     // The version should have been updated.
539     int cur_version = HistoryDatabase::GetCurrentVersion();
540     ASSERT_LE(27, cur_version);
541     {
542       sql::Statement s(db.GetUniqueStatement(
543           "SELECT value FROM meta WHERE key = 'version'"));
544       EXPECT_TRUE(s.Step());
545       EXPECT_EQ(cur_version, s.ColumnInt(0));
546     }
547     {
548       sql::Statement s(db.GetUniqueStatement(
549           "SELECT by_ext_id, by_ext_name from downloads"));
550       EXPECT_TRUE(s.Step());
551       EXPECT_EQ(std::string(), s.ColumnString(0));
552       EXPECT_EQ(std::string(), s.ColumnString(1));
553     }
554   }
555 }
556
557 TEST_F(HistoryBackendDBTest, MigrateDownloadValidators) {
558   Time now(base::Time::Now());
559   ASSERT_NO_FATAL_FAILURE(CreateDBVersion(27));
560   {
561     sql::Connection db;
562     ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
563     {
564       sql::Statement s(db.GetUniqueStatement(
565           "INSERT INTO downloads (id, current_path, target_path, start_time, "
566           "received_bytes, total_bytes, state, danger_type, interrupt_reason, "
567           "end_time, opened, referrer, by_ext_id, by_ext_name) VALUES "
568           "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
569       s.BindInt64(0, 1);
570       s.BindString(1, "current_path");
571       s.BindString(2, "target_path");
572       s.BindInt64(3, now.ToTimeT());
573       s.BindInt64(4, 100);
574       s.BindInt64(5, 100);
575       s.BindInt(6, 1);
576       s.BindInt(7, 0);
577       s.BindInt(8, 0);
578       s.BindInt64(9, now.ToTimeT());
579       s.BindInt(10, 1);
580       s.BindString(11, "referrer");
581       s.BindString(12, "by extension ID");
582       s.BindString(13, "by extension name");
583       ASSERT_TRUE(s.Run());
584     }
585     {
586       sql::Statement s(db.GetUniqueStatement(
587           "INSERT INTO downloads_url_chains (id, chain_index, url) VALUES "
588           "(?, ?, ?)"));
589       s.BindInt64(0, 4);
590       s.BindInt64(1, 0);
591       s.BindString(2, "url");
592       ASSERT_TRUE(s.Run());
593     }
594   }
595   // Re-open the db using the HistoryDatabase, which should migrate to the
596   // current version, creating the etag and last_modified columns.
597   CreateBackendAndDatabase();
598   DeleteBackend();
599   {
600     // Re-open the db for manual manipulation.
601     sql::Connection db;
602     ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
603     // The version should have been updated.
604     int cur_version = HistoryDatabase::GetCurrentVersion();
605     ASSERT_LE(28, cur_version);
606     {
607       sql::Statement s(db.GetUniqueStatement(
608           "SELECT value FROM meta WHERE key = 'version'"));
609       EXPECT_TRUE(s.Step());
610       EXPECT_EQ(cur_version, s.ColumnInt(0));
611     }
612     {
613       sql::Statement s(db.GetUniqueStatement(
614           "SELECT etag, last_modified from downloads"));
615       EXPECT_TRUE(s.Step());
616       EXPECT_EQ(std::string(), s.ColumnString(0));
617       EXPECT_EQ(std::string(), s.ColumnString(1));
618     }
619   }
620 }
621
622 TEST_F(HistoryBackendDBTest, ConfirmDownloadRowCreateAndDelete) {
623   // Create the DB.
624   CreateBackendAndDatabase();
625
626   base::Time now(base::Time::Now());
627
628   // Add some downloads.
629   uint32 id1 = 1, id2 = 2, id3 = 3;
630   AddDownload(id1, DownloadItem::COMPLETE, now);
631   AddDownload(id2, DownloadItem::COMPLETE, now + base::TimeDelta::FromDays(2));
632   AddDownload(id3, DownloadItem::COMPLETE, now - base::TimeDelta::FromDays(2));
633
634   // Confirm that resulted in the correct number of rows in the DB.
635   DeleteBackend();
636   {
637     sql::Connection db;
638     ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
639     sql::Statement statement(db.GetUniqueStatement(
640         "Select Count(*) from downloads"));
641     EXPECT_TRUE(statement.Step());
642     EXPECT_EQ(3, statement.ColumnInt(0));
643
644     sql::Statement statement1(db.GetUniqueStatement(
645         "Select Count(*) from downloads_url_chains"));
646     EXPECT_TRUE(statement1.Step());
647     EXPECT_EQ(3, statement1.ColumnInt(0));
648   }
649
650   // Delete some rows and make sure the results are still correct.
651   CreateBackendAndDatabase();
652   db_->RemoveDownload(id2);
653   db_->RemoveDownload(id3);
654   DeleteBackend();
655   {
656     sql::Connection db;
657     ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
658     sql::Statement statement(db.GetUniqueStatement(
659         "Select Count(*) from downloads"));
660     EXPECT_TRUE(statement.Step());
661     EXPECT_EQ(1, statement.ColumnInt(0));
662
663     sql::Statement statement1(db.GetUniqueStatement(
664         "Select Count(*) from downloads_url_chains"));
665     EXPECT_TRUE(statement1.Step());
666     EXPECT_EQ(1, statement1.ColumnInt(0));
667   }
668 }
669
670 TEST_F(HistoryBackendDBTest, DownloadNukeRecordsMissingURLs) {
671   CreateBackendAndDatabase();
672   base::Time now(base::Time::Now());
673   std::vector<GURL> url_chain;
674   DownloadRow download(base::FilePath(FILE_PATH_LITERAL("foo-path")),
675                        base::FilePath(FILE_PATH_LITERAL("foo-path")),
676                        url_chain,
677                        GURL(std::string()),
678                        now,
679                        now,
680                        std::string(),
681                        std::string(),
682                        0,
683                        512,
684                        DownloadItem::COMPLETE,
685                        content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
686                        content::DOWNLOAD_INTERRUPT_REASON_NONE,
687                        1,
688                        0,
689                        "by_ext_id",
690                        "by_ext_name");
691
692   // Creating records without any urls should fail.
693   EXPECT_FALSE(db_->CreateDownload(download));
694
695   download.url_chain.push_back(GURL("foo-url"));
696   EXPECT_TRUE(db_->CreateDownload(download));
697
698   // Pretend that the URLs were dropped.
699   DeleteBackend();
700   {
701     sql::Connection db;
702     ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
703     sql::Statement statement(db.GetUniqueStatement(
704         "DELETE FROM downloads_url_chains WHERE id=1"));
705     ASSERT_TRUE(statement.Run());
706   }
707   CreateBackendAndDatabase();
708   std::vector<DownloadRow> downloads;
709   db_->QueryDownloads(&downloads);
710   EXPECT_EQ(0U, downloads.size());
711
712   // QueryDownloads should have nuked the corrupt record.
713   DeleteBackend();
714   {
715     sql::Connection db;
716     ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
717     {
718       sql::Statement statement(db.GetUniqueStatement(
719             "SELECT count(*) from downloads"));
720       ASSERT_TRUE(statement.Step());
721       EXPECT_EQ(0, statement.ColumnInt(0));
722     }
723   }
724 }
725
726 TEST_F(HistoryBackendDBTest, ConfirmDownloadInProgressCleanup) {
727   // Create the DB.
728   CreateBackendAndDatabase();
729
730   base::Time now(base::Time::Now());
731
732   // Put an IN_PROGRESS download in the DB.
733   AddDownload(1, DownloadItem::IN_PROGRESS, now);
734
735   // Confirm that they made it into the DB unchanged.
736   DeleteBackend();
737   {
738     sql::Connection db;
739     ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
740     sql::Statement statement(db.GetUniqueStatement(
741         "Select Count(*) from downloads"));
742     EXPECT_TRUE(statement.Step());
743     EXPECT_EQ(1, statement.ColumnInt(0));
744
745     sql::Statement statement1(db.GetUniqueStatement(
746         "Select state, interrupt_reason from downloads"));
747     EXPECT_TRUE(statement1.Step());
748     EXPECT_EQ(DownloadDatabase::kStateInProgress, statement1.ColumnInt(0));
749     EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, statement1.ColumnInt(1));
750     EXPECT_FALSE(statement1.Step());
751   }
752
753   // Read in the DB through query downloads, then test that the
754   // right transformation was returned.
755   CreateBackendAndDatabase();
756   std::vector<DownloadRow> results;
757   db_->QueryDownloads(&results);
758   ASSERT_EQ(1u, results.size());
759   EXPECT_EQ(content::DownloadItem::INTERRUPTED, results[0].state);
760   EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_CRASH,
761             results[0].interrupt_reason);
762
763   // Allow the update to propagate, shut down the DB, and confirm that
764   // the query updated the on disk database as well.
765   base::MessageLoop::current()->RunUntilIdle();
766   DeleteBackend();
767   {
768     sql::Connection db;
769     ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
770     sql::Statement statement(db.GetUniqueStatement(
771         "Select Count(*) from downloads"));
772     EXPECT_TRUE(statement.Step());
773     EXPECT_EQ(1, statement.ColumnInt(0));
774
775     sql::Statement statement1(db.GetUniqueStatement(
776         "Select state, interrupt_reason from downloads"));
777     EXPECT_TRUE(statement1.Step());
778     EXPECT_EQ(DownloadDatabase::kStateInterrupted, statement1.ColumnInt(0));
779     EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_CRASH,
780               statement1.ColumnInt(1));
781     EXPECT_FALSE(statement1.Step());
782   }
783 }
784
785 struct InterruptReasonAssociation {
786   std::string name;
787   int value;
788 };
789
790 // Test is dependent on interrupt reasons being listed in header file
791 // in order.
792 const InterruptReasonAssociation current_reasons[] = {
793 #define INTERRUPT_REASON(a, b) { #a, b },
794 #include "content/public/browser/download_interrupt_reason_values.h"
795 #undef INTERRUPT_REASON
796 };
797
798 // This represents a list of all reasons we've previously used;
799 // Do Not Remove Any Entries From This List.
800 const InterruptReasonAssociation historical_reasons[] = {
801   {"FILE_FAILED",  1},
802   {"FILE_ACCESS_DENIED",  2},
803   {"FILE_NO_SPACE",  3},
804   {"FILE_NAME_TOO_LONG",  5},
805   {"FILE_TOO_LARGE",  6},
806   {"FILE_VIRUS_INFECTED",  7},
807   {"FILE_TRANSIENT_ERROR",  10},
808   {"FILE_BLOCKED",  11},
809   {"FILE_SECURITY_CHECK_FAILED",  12},
810   {"FILE_TOO_SHORT", 13},
811   {"NETWORK_FAILED",  20},
812   {"NETWORK_TIMEOUT",  21},
813   {"NETWORK_DISCONNECTED",  22},
814   {"NETWORK_SERVER_DOWN",  23},
815   {"NETWORK_INVALID_REQUEST", 24},
816   {"SERVER_FAILED",  30},
817   {"SERVER_NO_RANGE",  31},
818   {"SERVER_PRECONDITION",  32},
819   {"SERVER_BAD_CONTENT",  33},
820   {"USER_CANCELED",  40},
821   {"USER_SHUTDOWN",  41},
822   {"CRASH",  50},
823 };
824
825 // Make sure no one has changed a DownloadInterruptReason we've previously
826 // persisted.
827 TEST_F(HistoryBackendDBTest,
828        ConfirmDownloadInterruptReasonBackwardsCompatible) {
829   // Are there any cases in which a historical number has been repurposed
830   // for an error other than it's original?
831   for (size_t i = 0; i < arraysize(current_reasons); i++) {
832     const InterruptReasonAssociation& cur_reason(current_reasons[i]);
833     bool found = false;
834
835     for (size_t j = 0; j < arraysize(historical_reasons); ++j) {
836       const InterruptReasonAssociation& hist_reason(historical_reasons[j]);
837
838       if (hist_reason.value == cur_reason.value) {
839         EXPECT_EQ(cur_reason.name, hist_reason.name)
840             << "Same integer value used for old error \""
841             << hist_reason.name
842             << "\" as for new error \""
843             << cur_reason.name
844             << "\"." << std::endl
845             << "**This will cause database conflicts with persisted values**"
846             << std::endl
847             << "Please assign a new, non-conflicting value for the new error.";
848       }
849
850       if (hist_reason.name == cur_reason.name) {
851         EXPECT_EQ(cur_reason.value, hist_reason.value)
852             << "Same name (\"" << hist_reason.name
853             << "\") maps to a different value historically ("
854             << hist_reason.value << ") and currently ("
855             << cur_reason.value << ")" << std::endl
856             << "This may cause database conflicts with persisted values"
857             << std::endl
858             << "If this error is the same as the old one, you should"
859             << std::endl
860             << "use the old value, and if it is different, you should"
861             << std::endl
862             << "use a new name.";
863
864         found = true;
865       }
866     }
867
868     EXPECT_TRUE(found)
869         << "Error \"" << cur_reason.name << "\" not found in historical list."
870         << std::endl
871         << "Please add it.";
872   }
873 }
874
875 // The tracker uses RenderProcessHost pointers for scoping but never
876 // dereferences them. We use ints because it's easier. This function converts
877 // between the two.
878 static void* MakeFakeHost(int id) {
879   void* host = 0;
880   memcpy(&host, &id, sizeof(id));
881   return host;
882 }
883
884 class HistoryTest : public testing::Test {
885  public:
886   HistoryTest()
887       : got_thumbnail_callback_(false),
888         redirect_query_success_(false),
889         query_url_success_(false) {
890   }
891
892   virtual ~HistoryTest() {
893   }
894
895   void OnSegmentUsageAvailable(CancelableRequestProvider::Handle handle,
896                                std::vector<PageUsageData*>* data) {
897     page_usage_data_.swap(*data);
898     base::MessageLoop::current()->Quit();
899   }
900
901   void OnDeleteURLsDone(CancelableRequestProvider::Handle handle) {
902     base::MessageLoop::current()->Quit();
903   }
904
905   void OnMostVisitedURLsAvailable(CancelableRequestProvider::Handle handle,
906                                   MostVisitedURLList url_list) {
907     most_visited_urls_.swap(url_list);
908     base::MessageLoop::current()->Quit();
909   }
910
911  protected:
912   friend class BackendDelegate;
913
914   // testing::Test
915   virtual void SetUp() {
916     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
917     history_dir_ = temp_dir_.path().AppendASCII("HistoryTest");
918     ASSERT_TRUE(base::CreateDirectory(history_dir_));
919     history_service_.reset(new HistoryService);
920     if (!history_service_->Init(history_dir_, NULL)) {
921       history_service_.reset();
922       ADD_FAILURE();
923     }
924   }
925
926   virtual void TearDown() {
927     if (history_service_)
928       CleanupHistoryService();
929
930     // Make sure we don't have any event pending that could disrupt the next
931     // test.
932     base::MessageLoop::current()->PostTask(FROM_HERE,
933                                            base::MessageLoop::QuitClosure());
934     base::MessageLoop::current()->Run();
935   }
936
937   void CleanupHistoryService() {
938     DCHECK(history_service_);
939
940     history_service_->NotifyRenderProcessHostDestruction(0);
941     history_service_->SetOnBackendDestroyTask(base::MessageLoop::QuitClosure());
942     history_service_->Cleanup();
943     history_service_.reset();
944
945     // Wait for the backend class to terminate before deleting the files and
946     // moving to the next test. Note: if this never terminates, somebody is
947     // probably leaking a reference to the history backend, so it never calls
948     // our destroy task.
949     base::MessageLoop::current()->Run();
950   }
951
952   // Fills the query_url_row_ and query_url_visits_ structures with the
953   // information about the given URL and returns true. If the URL was not
954   // found, this will return false and those structures will not be changed.
955   bool QueryURL(HistoryService* history, const GURL& url) {
956     history_service_->QueryURL(url, true, &consumer_,
957                                base::Bind(&HistoryTest::SaveURLAndQuit,
958                                           base::Unretained(this)));
959     base::MessageLoop::current()->Run();  // Will be exited in SaveURLAndQuit.
960     return query_url_success_;
961   }
962
963   // Callback for HistoryService::QueryURL.
964   void SaveURLAndQuit(HistoryService::Handle handle,
965                       bool success,
966                       const URLRow* url_row,
967                       VisitVector* visit_vector) {
968     query_url_success_ = success;
969     if (query_url_success_) {
970       query_url_row_ = *url_row;
971       query_url_visits_.swap(*visit_vector);
972     } else {
973       query_url_row_ = URLRow();
974       query_url_visits_.clear();
975     }
976     base::MessageLoop::current()->Quit();
977   }
978
979   // Fills in saved_redirects_ with the redirect information for the given URL,
980   // returning true on success. False means the URL was not found.
981   bool QueryRedirectsFrom(HistoryService* history, const GURL& url) {
982     history_service_->QueryRedirectsFrom(
983         url, &consumer_,
984         base::Bind(&HistoryTest::OnRedirectQueryComplete,
985                    base::Unretained(this)));
986     base::MessageLoop::current()->Run();  // Will be exited in *QueryComplete.
987     return redirect_query_success_;
988   }
989
990   // Callback for QueryRedirects.
991   void OnRedirectQueryComplete(HistoryService::Handle handle,
992                                GURL url,
993                                bool success,
994                                history::RedirectList* redirects) {
995     redirect_query_success_ = success;
996     if (redirect_query_success_)
997       saved_redirects_.swap(*redirects);
998     else
999       saved_redirects_.clear();
1000     base::MessageLoop::current()->Quit();
1001   }
1002
1003   base::ScopedTempDir temp_dir_;
1004
1005   base::MessageLoopForUI message_loop_;
1006
1007   // PageUsageData vector to test segments.
1008   ScopedVector<PageUsageData> page_usage_data_;
1009
1010   MostVisitedURLList most_visited_urls_;
1011
1012   // When non-NULL, this will be deleted on tear down and we will block until
1013   // the backend thread has completed. This allows tests for the history
1014   // service to use this feature, but other tests to ignore this.
1015   scoped_ptr<HistoryService> history_service_;
1016
1017   // names of the database files
1018   base::FilePath history_dir_;
1019
1020   // Set by the thumbnail callback when we get data, you should be sure to
1021   // clear this before issuing a thumbnail request.
1022   bool got_thumbnail_callback_;
1023   std::vector<unsigned char> thumbnail_data_;
1024
1025   // Set by the redirect callback when we get data. You should be sure to
1026   // clear this before issuing a redirect request.
1027   history::RedirectList saved_redirects_;
1028   bool redirect_query_success_;
1029
1030   // For history requests.
1031   CancelableRequestConsumer consumer_;
1032
1033   // For saving URL info after a call to QueryURL
1034   bool query_url_success_;
1035   URLRow query_url_row_;
1036   VisitVector query_url_visits_;
1037 };
1038
1039 TEST_F(HistoryTest, AddPage) {
1040   ASSERT_TRUE(history_service_.get());
1041   // Add the page once from a child frame.
1042   const GURL test_url("http://www.google.com/");
1043   history_service_->AddPage(test_url, base::Time::Now(), NULL, 0, GURL(),
1044                             history::RedirectList(),
1045                             content::PAGE_TRANSITION_MANUAL_SUBFRAME,
1046                             history::SOURCE_BROWSED, false);
1047   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1048   EXPECT_EQ(1, query_url_row_.visit_count());
1049   EXPECT_EQ(0, query_url_row_.typed_count());
1050   EXPECT_TRUE(query_url_row_.hidden());  // Hidden because of child frame.
1051
1052   // Add the page once from the main frame (should unhide it).
1053   history_service_->AddPage(test_url, base::Time::Now(), NULL, 0, GURL(),
1054                    history::RedirectList(), content::PAGE_TRANSITION_LINK,
1055                    history::SOURCE_BROWSED, false);
1056   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1057   EXPECT_EQ(2, query_url_row_.visit_count());  // Added twice.
1058   EXPECT_EQ(0, query_url_row_.typed_count());  // Never typed.
1059   EXPECT_FALSE(query_url_row_.hidden());  // Because loaded in main frame.
1060 }
1061
1062 TEST_F(HistoryTest, AddRedirect) {
1063   ASSERT_TRUE(history_service_.get());
1064   const char* first_sequence[] = {
1065     "http://first.page.com/",
1066     "http://second.page.com/"};
1067   int first_count = arraysize(first_sequence);
1068   history::RedirectList first_redirects;
1069   for (int i = 0; i < first_count; i++)
1070     first_redirects.push_back(GURL(first_sequence[i]));
1071
1072   // Add the sequence of pages as a server with no referrer. Note that we need
1073   // to have a non-NULL page ID scope.
1074   history_service_->AddPage(
1075       first_redirects.back(), base::Time::Now(), MakeFakeHost(1),
1076       0, GURL(), first_redirects, content::PAGE_TRANSITION_LINK,
1077       history::SOURCE_BROWSED, true);
1078
1079   // The first page should be added once with a link visit type (because we set
1080   // LINK when we added the original URL, and a referrer of nowhere (0).
1081   EXPECT_TRUE(QueryURL(history_service_.get(), first_redirects[0]));
1082   EXPECT_EQ(1, query_url_row_.visit_count());
1083   ASSERT_EQ(1U, query_url_visits_.size());
1084   int64 first_visit = query_url_visits_[0].visit_id;
1085   EXPECT_EQ(content::PAGE_TRANSITION_LINK |
1086             content::PAGE_TRANSITION_CHAIN_START,
1087             query_url_visits_[0].transition);
1088   EXPECT_EQ(0, query_url_visits_[0].referring_visit);  // No referrer.
1089
1090   // The second page should be a server redirect type with a referrer of the
1091   // first page.
1092   EXPECT_TRUE(QueryURL(history_service_.get(), first_redirects[1]));
1093   EXPECT_EQ(1, query_url_row_.visit_count());
1094   ASSERT_EQ(1U, query_url_visits_.size());
1095   int64 second_visit = query_url_visits_[0].visit_id;
1096   EXPECT_EQ(content::PAGE_TRANSITION_SERVER_REDIRECT |
1097             content::PAGE_TRANSITION_CHAIN_END,
1098             query_url_visits_[0].transition);
1099   EXPECT_EQ(first_visit, query_url_visits_[0].referring_visit);
1100
1101   // Check that the redirect finding function successfully reports it.
1102   saved_redirects_.clear();
1103   QueryRedirectsFrom(history_service_.get(), first_redirects[0]);
1104   ASSERT_EQ(1U, saved_redirects_.size());
1105   EXPECT_EQ(first_redirects[1], saved_redirects_[0]);
1106
1107   // Now add a client redirect from that second visit to a third, client
1108   // redirects are tracked by the RenderView prior to updating history,
1109   // so we pass in a CLIENT_REDIRECT qualifier to mock that behavior.
1110   history::RedirectList second_redirects;
1111   second_redirects.push_back(first_redirects[1]);
1112   second_redirects.push_back(GURL("http://last.page.com/"));
1113   history_service_->AddPage(second_redirects[1], base::Time::Now(),
1114                    MakeFakeHost(1), 1, second_redirects[0], second_redirects,
1115                    static_cast<content::PageTransition>(
1116                        content::PAGE_TRANSITION_LINK |
1117                        content::PAGE_TRANSITION_CLIENT_REDIRECT),
1118                    history::SOURCE_BROWSED, true);
1119
1120   // The last page (source of the client redirect) should NOT have an
1121   // additional visit added, because it was a client redirect (normally it
1122   // would). We should only have 1 left over from the first sequence.
1123   EXPECT_TRUE(QueryURL(history_service_.get(), second_redirects[0]));
1124   EXPECT_EQ(1, query_url_row_.visit_count());
1125
1126   // The final page should be set as a client redirect from the previous visit.
1127   EXPECT_TRUE(QueryURL(history_service_.get(), second_redirects[1]));
1128   EXPECT_EQ(1, query_url_row_.visit_count());
1129   ASSERT_EQ(1U, query_url_visits_.size());
1130   EXPECT_EQ(content::PAGE_TRANSITION_CLIENT_REDIRECT |
1131             content::PAGE_TRANSITION_CHAIN_END,
1132             query_url_visits_[0].transition);
1133   EXPECT_EQ(second_visit, query_url_visits_[0].referring_visit);
1134 }
1135
1136 TEST_F(HistoryTest, MakeIntranetURLsTyped) {
1137   ASSERT_TRUE(history_service_.get());
1138
1139   // Add a non-typed visit to an intranet URL on an unvisited host.  This should
1140   // get promoted to a typed visit.
1141   const GURL test_url("http://intranet_host/path");
1142   history_service_->AddPage(
1143       test_url, base::Time::Now(), NULL, 0, GURL(),
1144       history::RedirectList(), content::PAGE_TRANSITION_LINK,
1145       history::SOURCE_BROWSED, false);
1146   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1147   EXPECT_EQ(1, query_url_row_.visit_count());
1148   EXPECT_EQ(1, query_url_row_.typed_count());
1149   ASSERT_EQ(1U, query_url_visits_.size());
1150   EXPECT_EQ(content::PAGE_TRANSITION_TYPED,
1151       content::PageTransitionStripQualifier(query_url_visits_[0].transition));
1152
1153   // Add more visits on the same host.  None of these should be promoted since
1154   // there is already a typed visit.
1155
1156   // Different path.
1157   const GURL test_url2("http://intranet_host/different_path");
1158   history_service_->AddPage(
1159       test_url2, base::Time::Now(), NULL, 0, GURL(),
1160       history::RedirectList(), content::PAGE_TRANSITION_LINK,
1161       history::SOURCE_BROWSED, false);
1162   EXPECT_TRUE(QueryURL(history_service_.get(), test_url2));
1163   EXPECT_EQ(1, query_url_row_.visit_count());
1164   EXPECT_EQ(0, query_url_row_.typed_count());
1165   ASSERT_EQ(1U, query_url_visits_.size());
1166   EXPECT_EQ(content::PAGE_TRANSITION_LINK,
1167       content::PageTransitionStripQualifier(query_url_visits_[0].transition));
1168
1169   // No path.
1170   const GURL test_url3("http://intranet_host/");
1171   history_service_->AddPage(
1172       test_url3, base::Time::Now(), NULL, 0, GURL(),
1173       history::RedirectList(), content::PAGE_TRANSITION_LINK,
1174       history::SOURCE_BROWSED, false);
1175   EXPECT_TRUE(QueryURL(history_service_.get(), test_url3));
1176   EXPECT_EQ(1, query_url_row_.visit_count());
1177   EXPECT_EQ(0, query_url_row_.typed_count());
1178   ASSERT_EQ(1U, query_url_visits_.size());
1179   EXPECT_EQ(content::PAGE_TRANSITION_LINK,
1180       content::PageTransitionStripQualifier(query_url_visits_[0].transition));
1181
1182   // Different scheme.
1183   const GURL test_url4("https://intranet_host/");
1184   history_service_->AddPage(
1185       test_url4, base::Time::Now(), NULL, 0, GURL(),
1186       history::RedirectList(), content::PAGE_TRANSITION_LINK,
1187       history::SOURCE_BROWSED, false);
1188   EXPECT_TRUE(QueryURL(history_service_.get(), test_url4));
1189   EXPECT_EQ(1, query_url_row_.visit_count());
1190   EXPECT_EQ(0, query_url_row_.typed_count());
1191   ASSERT_EQ(1U, query_url_visits_.size());
1192   EXPECT_EQ(content::PAGE_TRANSITION_LINK,
1193       content::PageTransitionStripQualifier(query_url_visits_[0].transition));
1194
1195   // Different transition.
1196   const GURL test_url5("http://intranet_host/another_path");
1197   history_service_->AddPage(
1198       test_url5, base::Time::Now(), NULL, 0, GURL(),
1199       history::RedirectList(),
1200       content::PAGE_TRANSITION_AUTO_BOOKMARK,
1201       history::SOURCE_BROWSED, false);
1202   EXPECT_TRUE(QueryURL(history_service_.get(), test_url5));
1203   EXPECT_EQ(1, query_url_row_.visit_count());
1204   EXPECT_EQ(0, query_url_row_.typed_count());
1205   ASSERT_EQ(1U, query_url_visits_.size());
1206   EXPECT_EQ(content::PAGE_TRANSITION_AUTO_BOOKMARK,
1207       content::PageTransitionStripQualifier(query_url_visits_[0].transition));
1208
1209   // Original URL.
1210   history_service_->AddPage(
1211       test_url, base::Time::Now(), NULL, 0, GURL(),
1212       history::RedirectList(), content::PAGE_TRANSITION_LINK,
1213       history::SOURCE_BROWSED, false);
1214   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1215   EXPECT_EQ(2, query_url_row_.visit_count());
1216   EXPECT_EQ(1, query_url_row_.typed_count());
1217   ASSERT_EQ(2U, query_url_visits_.size());
1218   EXPECT_EQ(content::PAGE_TRANSITION_LINK,
1219       content::PageTransitionStripQualifier(query_url_visits_[1].transition));
1220 }
1221
1222 TEST_F(HistoryTest, Typed) {
1223   ASSERT_TRUE(history_service_.get());
1224
1225   // Add the page once as typed.
1226   const GURL test_url("http://www.google.com/");
1227   history_service_->AddPage(
1228       test_url, base::Time::Now(), NULL, 0, GURL(),
1229       history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1230       history::SOURCE_BROWSED, false);
1231   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1232
1233   // We should have the same typed & visit count.
1234   EXPECT_EQ(1, query_url_row_.visit_count());
1235   EXPECT_EQ(1, query_url_row_.typed_count());
1236
1237   // Add the page again not typed.
1238   history_service_->AddPage(
1239       test_url, base::Time::Now(), NULL, 0, GURL(),
1240       history::RedirectList(), content::PAGE_TRANSITION_LINK,
1241       history::SOURCE_BROWSED, false);
1242   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1243
1244   // The second time should not have updated the typed count.
1245   EXPECT_EQ(2, query_url_row_.visit_count());
1246   EXPECT_EQ(1, query_url_row_.typed_count());
1247
1248   // Add the page again as a generated URL.
1249   history_service_->AddPage(
1250       test_url, base::Time::Now(), NULL, 0, GURL(),
1251       history::RedirectList(), content::PAGE_TRANSITION_GENERATED,
1252       history::SOURCE_BROWSED, false);
1253   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1254
1255   // This should have worked like a link click.
1256   EXPECT_EQ(3, query_url_row_.visit_count());
1257   EXPECT_EQ(1, query_url_row_.typed_count());
1258
1259   // Add the page again as a reload.
1260   history_service_->AddPage(
1261       test_url, base::Time::Now(), NULL, 0, GURL(),
1262       history::RedirectList(), content::PAGE_TRANSITION_RELOAD,
1263       history::SOURCE_BROWSED, false);
1264   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1265
1266   // This should not have incremented any visit counts.
1267   EXPECT_EQ(3, query_url_row_.visit_count());
1268   EXPECT_EQ(1, query_url_row_.typed_count());
1269 }
1270
1271 TEST_F(HistoryTest, SetTitle) {
1272   ASSERT_TRUE(history_service_.get());
1273
1274   // Add a URL.
1275   const GURL existing_url("http://www.google.com/");
1276   history_service_->AddPage(
1277       existing_url, base::Time::Now(), history::SOURCE_BROWSED);
1278
1279   // Set some title.
1280   const base::string16 existing_title = base::UTF8ToUTF16("Google");
1281   history_service_->SetPageTitle(existing_url, existing_title);
1282
1283   // Make sure the title got set.
1284   EXPECT_TRUE(QueryURL(history_service_.get(), existing_url));
1285   EXPECT_EQ(existing_title, query_url_row_.title());
1286
1287   // set a title on a nonexistent page
1288   const GURL nonexistent_url("http://news.google.com/");
1289   const base::string16 nonexistent_title = base::UTF8ToUTF16("Google News");
1290   history_service_->SetPageTitle(nonexistent_url, nonexistent_title);
1291
1292   // Make sure nothing got written.
1293   EXPECT_FALSE(QueryURL(history_service_.get(), nonexistent_url));
1294   EXPECT_EQ(base::string16(), query_url_row_.title());
1295
1296   // TODO(brettw) this should also test redirects, which get the title of the
1297   // destination page.
1298 }
1299
1300 // crbug.com/159387: This test fails when daylight savings time ends.
1301 TEST_F(HistoryTest, DISABLED_Segments) {
1302   ASSERT_TRUE(history_service_.get());
1303
1304   static const void* scope = static_cast<void*>(this);
1305
1306   // Add a URL.
1307   const GURL existing_url("http://www.google.com/");
1308   history_service_->AddPage(
1309       existing_url, base::Time::Now(), scope, 0, GURL(),
1310       history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1311       history::SOURCE_BROWSED, false);
1312
1313   // Make sure a segment was created.
1314   history_service_->QuerySegmentUsageSince(
1315       &consumer_, Time::Now() - TimeDelta::FromDays(1), 10,
1316       base::Bind(&HistoryTest::OnSegmentUsageAvailable,
1317                  base::Unretained(this)));
1318
1319   // Wait for processing.
1320   base::MessageLoop::current()->Run();
1321
1322   ASSERT_EQ(1U, page_usage_data_.size());
1323   EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url);
1324   EXPECT_DOUBLE_EQ(3.0, page_usage_data_[0]->GetScore());
1325
1326   // Add a URL which doesn't create a segment.
1327   const GURL link_url("http://yahoo.com/");
1328   history_service_->AddPage(
1329       link_url, base::Time::Now(), scope, 0, GURL(),
1330       history::RedirectList(), content::PAGE_TRANSITION_LINK,
1331       history::SOURCE_BROWSED, false);
1332
1333   // Query again
1334   history_service_->QuerySegmentUsageSince(
1335       &consumer_, Time::Now() - TimeDelta::FromDays(1), 10,
1336       base::Bind(&HistoryTest::OnSegmentUsageAvailable,
1337                  base::Unretained(this)));
1338
1339   // Wait for processing.
1340   base::MessageLoop::current()->Run();
1341
1342   // Make sure we still have one segment.
1343   ASSERT_EQ(1U, page_usage_data_.size());
1344   EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url);
1345
1346   // Add a page linked from existing_url.
1347   history_service_->AddPage(
1348       GURL("http://www.google.com/foo"), base::Time::Now(),
1349       scope, 3, existing_url, history::RedirectList(),
1350       content::PAGE_TRANSITION_LINK, history::SOURCE_BROWSED,
1351       false);
1352
1353   // Query again
1354   history_service_->QuerySegmentUsageSince(
1355       &consumer_, Time::Now() - TimeDelta::FromDays(1), 10,
1356       base::Bind(&HistoryTest::OnSegmentUsageAvailable,
1357                  base::Unretained(this)));
1358
1359   // Wait for processing.
1360   base::MessageLoop::current()->Run();
1361
1362   // Make sure we still have one segment.
1363   ASSERT_EQ(1U, page_usage_data_.size());
1364   EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url);
1365
1366   // However, the score should have increased.
1367   EXPECT_GT(page_usage_data_[0]->GetScore(), 5.0);
1368 }
1369
1370 TEST_F(HistoryTest, MostVisitedURLs) {
1371   ASSERT_TRUE(history_service_.get());
1372
1373   const GURL url0("http://www.google.com/url0/");
1374   const GURL url1("http://www.google.com/url1/");
1375   const GURL url2("http://www.google.com/url2/");
1376   const GURL url3("http://www.google.com/url3/");
1377   const GURL url4("http://www.google.com/url4/");
1378
1379   static const void* scope = static_cast<void*>(this);
1380
1381   // Add two pages.
1382   history_service_->AddPage(
1383       url0, base::Time::Now(), scope, 0, GURL(),
1384       history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1385       history::SOURCE_BROWSED, false);
1386   history_service_->AddPage(
1387       url1, base::Time::Now(), scope, 0, GURL(),
1388       history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1389       history::SOURCE_BROWSED, false);
1390   history_service_->QueryMostVisitedURLs(
1391       20, 90, &consumer_,
1392       base::Bind(
1393           &HistoryTest::OnMostVisitedURLsAvailable,
1394           base::Unretained(this)));
1395   base::MessageLoop::current()->Run();
1396
1397   EXPECT_EQ(2U, most_visited_urls_.size());
1398   EXPECT_EQ(url0, most_visited_urls_[0].url);
1399   EXPECT_EQ(url1, most_visited_urls_[1].url);
1400
1401   // Add another page.
1402   history_service_->AddPage(
1403       url2, base::Time::Now(), scope, 0, GURL(),
1404       history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1405       history::SOURCE_BROWSED, false);
1406   history_service_->QueryMostVisitedURLs(
1407       20, 90, &consumer_,
1408       base::Bind(
1409           &HistoryTest::OnMostVisitedURLsAvailable,
1410           base::Unretained(this)));
1411   base::MessageLoop::current()->Run();
1412
1413   EXPECT_EQ(3U, most_visited_urls_.size());
1414   EXPECT_EQ(url0, most_visited_urls_[0].url);
1415   EXPECT_EQ(url1, most_visited_urls_[1].url);
1416   EXPECT_EQ(url2, most_visited_urls_[2].url);
1417
1418   // Revisit url2, making it the top URL.
1419   history_service_->AddPage(
1420       url2, base::Time::Now(), scope, 0, GURL(),
1421       history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1422       history::SOURCE_BROWSED, false);
1423   history_service_->QueryMostVisitedURLs(
1424       20, 90, &consumer_,
1425       base::Bind(
1426           &HistoryTest::OnMostVisitedURLsAvailable,
1427           base::Unretained(this)));
1428   base::MessageLoop::current()->Run();
1429
1430   EXPECT_EQ(3U, most_visited_urls_.size());
1431   EXPECT_EQ(url2, most_visited_urls_[0].url);
1432   EXPECT_EQ(url0, most_visited_urls_[1].url);
1433   EXPECT_EQ(url1, most_visited_urls_[2].url);
1434
1435   // Revisit url1, making it the top URL.
1436   history_service_->AddPage(
1437       url1, base::Time::Now(), scope, 0, GURL(),
1438       history::RedirectList(), content::PAGE_TRANSITION_TYPED,
1439       history::SOURCE_BROWSED, false);
1440   history_service_->QueryMostVisitedURLs(
1441       20, 90, &consumer_,
1442       base::Bind(
1443           &HistoryTest::OnMostVisitedURLsAvailable,
1444           base::Unretained(this)));
1445   base::MessageLoop::current()->Run();
1446
1447   EXPECT_EQ(3U, most_visited_urls_.size());
1448   EXPECT_EQ(url1, most_visited_urls_[0].url);
1449   EXPECT_EQ(url2, most_visited_urls_[1].url);
1450   EXPECT_EQ(url0, most_visited_urls_[2].url);
1451
1452   // Redirects
1453   history::RedirectList redirects;
1454   redirects.push_back(url3);
1455   redirects.push_back(url4);
1456
1457   // Visit url4 using redirects.
1458   history_service_->AddPage(
1459       url4, base::Time::Now(), scope, 0, GURL(),
1460       redirects, content::PAGE_TRANSITION_TYPED,
1461       history::SOURCE_BROWSED, false);
1462   history_service_->QueryMostVisitedURLs(
1463       20, 90, &consumer_,
1464       base::Bind(
1465           &HistoryTest::OnMostVisitedURLsAvailable,
1466           base::Unretained(this)));
1467   base::MessageLoop::current()->Run();
1468
1469   EXPECT_EQ(4U, most_visited_urls_.size());
1470   EXPECT_EQ(url1, most_visited_urls_[0].url);
1471   EXPECT_EQ(url2, most_visited_urls_[1].url);
1472   EXPECT_EQ(url0, most_visited_urls_[2].url);
1473   EXPECT_EQ(url3, most_visited_urls_[3].url);
1474   EXPECT_EQ(2U, most_visited_urls_[3].redirects.size());
1475 }
1476
1477 namespace {
1478
1479 // A HistoryDBTask implementation. Each time RunOnDBThread is invoked
1480 // invoke_count is increment. When invoked kWantInvokeCount times, true is
1481 // returned from RunOnDBThread which should stop RunOnDBThread from being
1482 // invoked again. When DoneRunOnMainThread is invoked, done_invoked is set to
1483 // true.
1484 class HistoryDBTaskImpl : public HistoryDBTask {
1485  public:
1486   static const int kWantInvokeCount;
1487
1488   HistoryDBTaskImpl() : invoke_count(0), done_invoked(false) {}
1489
1490   virtual bool RunOnDBThread(HistoryBackend* backend,
1491                              HistoryDatabase* db) OVERRIDE {
1492     return (++invoke_count == kWantInvokeCount);
1493   }
1494
1495   virtual void DoneRunOnMainThread() OVERRIDE {
1496     done_invoked = true;
1497     base::MessageLoop::current()->Quit();
1498   }
1499
1500   int invoke_count;
1501   bool done_invoked;
1502
1503  private:
1504   virtual ~HistoryDBTaskImpl() {}
1505
1506   DISALLOW_COPY_AND_ASSIGN(HistoryDBTaskImpl);
1507 };
1508
1509 // static
1510 const int HistoryDBTaskImpl::kWantInvokeCount = 2;
1511
1512 }  // namespace
1513
1514 TEST_F(HistoryTest, HistoryDBTask) {
1515   ASSERT_TRUE(history_service_.get());
1516   CancelableRequestConsumerT<int, 0> request_consumer;
1517   scoped_refptr<HistoryDBTaskImpl> task(new HistoryDBTaskImpl());
1518   history_service_->ScheduleDBTask(task.get(), &request_consumer);
1519   // Run the message loop. When HistoryDBTaskImpl::DoneRunOnMainThread runs,
1520   // it will stop the message loop. If the test hangs here, it means
1521   // DoneRunOnMainThread isn't being invoked correctly.
1522   base::MessageLoop::current()->Run();
1523   CleanupHistoryService();
1524   // WARNING: history has now been deleted.
1525   history_service_.reset();
1526   ASSERT_EQ(HistoryDBTaskImpl::kWantInvokeCount, task->invoke_count);
1527   ASSERT_TRUE(task->done_invoked);
1528 }
1529
1530 TEST_F(HistoryTest, HistoryDBTaskCanceled) {
1531   ASSERT_TRUE(history_service_.get());
1532   CancelableRequestConsumerT<int, 0> request_consumer;
1533   scoped_refptr<HistoryDBTaskImpl> task(new HistoryDBTaskImpl());
1534   history_service_->ScheduleDBTask(task.get(), &request_consumer);
1535   request_consumer.CancelAllRequests();
1536   CleanupHistoryService();
1537   // WARNING: history has now been deleted.
1538   history_service_.reset();
1539   ASSERT_FALSE(task->done_invoked);
1540 }
1541
1542 // Create a local delete directive and process it while sync is
1543 // online, and then when offline. The delete directive should be sent to sync,
1544 // no error should be returned for the first time, and an error should be
1545 // returned for the second time.
1546 TEST_F(HistoryTest, ProcessLocalDeleteDirectiveSyncOnline) {
1547   ASSERT_TRUE(history_service_.get());
1548
1549   const GURL test_url("http://www.google.com/");
1550   for (int64 i = 1; i <= 10; ++i) {
1551     base::Time t =
1552         base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(i);
1553     history_service_->AddPage(test_url, t, NULL, 0, GURL(),
1554                               history::RedirectList(),
1555                               content::PAGE_TRANSITION_LINK,
1556                               history::SOURCE_BROWSED, false);
1557   }
1558
1559   sync_pb::HistoryDeleteDirectiveSpecifics delete_directive;
1560   sync_pb::GlobalIdDirective* global_id_directive =
1561       delete_directive.mutable_global_id_directive();
1562   global_id_directive->add_global_id(
1563       (base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(1))
1564       .ToInternalValue());
1565
1566   syncer::FakeSyncChangeProcessor change_processor;
1567
1568   EXPECT_FALSE(
1569       history_service_->MergeDataAndStartSyncing(
1570                             syncer::HISTORY_DELETE_DIRECTIVES,
1571                             syncer::SyncDataList(),
1572                             scoped_ptr<syncer::SyncChangeProcessor>(
1573                                 new syncer::SyncChangeProcessorWrapperForTest(
1574                                     &change_processor)),
1575                             scoped_ptr<syncer::SyncErrorFactory>())
1576           .error()
1577           .IsSet());
1578
1579   syncer::SyncError err =
1580       history_service_->ProcessLocalDeleteDirective(delete_directive);
1581   EXPECT_FALSE(err.IsSet());
1582   EXPECT_EQ(1u, change_processor.changes().size());
1583
1584   history_service_->StopSyncing(syncer::HISTORY_DELETE_DIRECTIVES);
1585   err = history_service_->ProcessLocalDeleteDirective(delete_directive);
1586   EXPECT_TRUE(err.IsSet());
1587   EXPECT_EQ(1u, change_processor.changes().size());
1588 }
1589
1590 // Closure function that runs periodically to check result of delete directive
1591 // processing. Stop when timeout or processing ends indicated by the creation
1592 // of sync changes.
1593 void CheckDirectiveProcessingResult(
1594     Time timeout,
1595     const syncer::FakeSyncChangeProcessor* change_processor,
1596     uint32 num_changes) {
1597   if (base::Time::Now() > timeout ||
1598       change_processor->changes().size() >= num_changes) {
1599     return;
1600   }
1601
1602   base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
1603   base::MessageLoop::current()->PostTask(
1604       FROM_HERE,
1605       base::Bind(&CheckDirectiveProcessingResult, timeout,
1606                  change_processor, num_changes));
1607 }
1608
1609 // Create a delete directive for a few specific history entries,
1610 // including ones that don't exist. The expected entries should be
1611 // deleted.
1612 TEST_F(HistoryTest, ProcessGlobalIdDeleteDirective) {
1613   ASSERT_TRUE(history_service_.get());
1614   const GURL test_url("http://www.google.com/");
1615   for (int64 i = 1; i <= 20; i++) {
1616     base::Time t =
1617         base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(i);
1618     history_service_->AddPage(test_url, t, NULL, 0, GURL(),
1619                               history::RedirectList(),
1620                               content::PAGE_TRANSITION_LINK,
1621                               history::SOURCE_BROWSED, false);
1622   }
1623
1624   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1625   EXPECT_EQ(20, query_url_row_.visit_count());
1626
1627   syncer::SyncDataList directives;
1628   // 1st directive.
1629   sync_pb::EntitySpecifics entity_specs;
1630   sync_pb::GlobalIdDirective* global_id_directive =
1631       entity_specs.mutable_history_delete_directive()
1632           ->mutable_global_id_directive();
1633   global_id_directive->add_global_id(
1634       (base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(6))
1635       .ToInternalValue());
1636   global_id_directive->set_start_time_usec(3);
1637   global_id_directive->set_end_time_usec(10);
1638   directives.push_back(syncer::SyncData::CreateRemoteData(
1639       1,
1640       entity_specs,
1641       base::Time(),
1642       syncer::AttachmentIdList(),
1643       syncer::AttachmentServiceProxyForTest::Create()));
1644
1645   // 2nd directive.
1646   global_id_directive->Clear();
1647   global_id_directive->add_global_id(
1648       (base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(17))
1649       .ToInternalValue());
1650   global_id_directive->set_start_time_usec(13);
1651   global_id_directive->set_end_time_usec(19);
1652   directives.push_back(syncer::SyncData::CreateRemoteData(
1653       2,
1654       entity_specs,
1655       base::Time(),
1656       syncer::AttachmentIdList(),
1657       syncer::AttachmentServiceProxyForTest::Create()));
1658
1659   syncer::FakeSyncChangeProcessor change_processor;
1660   EXPECT_FALSE(
1661       history_service_->MergeDataAndStartSyncing(
1662                             syncer::HISTORY_DELETE_DIRECTIVES,
1663                             directives,
1664                             scoped_ptr<syncer::SyncChangeProcessor>(
1665                                 new syncer::SyncChangeProcessorWrapperForTest(
1666                                     &change_processor)),
1667                             scoped_ptr<syncer::SyncErrorFactory>())
1668           .error()
1669           .IsSet());
1670
1671   // Inject a task to check status and keep message loop filled before directive
1672   // processing finishes.
1673   base::MessageLoop::current()->PostTask(
1674       FROM_HERE,
1675       base::Bind(&CheckDirectiveProcessingResult,
1676                  base::Time::Now() + base::TimeDelta::FromSeconds(10),
1677                  &change_processor, 2));
1678   base::MessageLoop::current()->RunUntilIdle();
1679   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1680   ASSERT_EQ(5, query_url_row_.visit_count());
1681   EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(1),
1682             query_url_visits_[0].visit_time);
1683   EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(2),
1684             query_url_visits_[1].visit_time);
1685   EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(11),
1686             query_url_visits_[2].visit_time);
1687   EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(12),
1688             query_url_visits_[3].visit_time);
1689   EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(20),
1690             query_url_visits_[4].visit_time);
1691
1692   // Expect two sync changes for deleting processed directives.
1693   const syncer::SyncChangeList& sync_changes = change_processor.changes();
1694   ASSERT_EQ(2u, sync_changes.size());
1695   EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[0].change_type());
1696   EXPECT_EQ(1, syncer::SyncDataRemote(sync_changes[0].sync_data()).GetId());
1697   EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[1].change_type());
1698   EXPECT_EQ(2, syncer::SyncDataRemote(sync_changes[1].sync_data()).GetId());
1699 }
1700
1701 // Create delete directives for time ranges.  The expected entries should be
1702 // deleted.
1703 TEST_F(HistoryTest, ProcessTimeRangeDeleteDirective) {
1704   ASSERT_TRUE(history_service_.get());
1705   const GURL test_url("http://www.google.com/");
1706   for (int64 i = 1; i <= 10; ++i) {
1707     base::Time t =
1708         base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(i);
1709     history_service_->AddPage(test_url, t, NULL, 0, GURL(),
1710                               history::RedirectList(),
1711                               content::PAGE_TRANSITION_LINK,
1712                               history::SOURCE_BROWSED, false);
1713   }
1714
1715   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1716   EXPECT_EQ(10, query_url_row_.visit_count());
1717
1718   syncer::SyncDataList directives;
1719   // 1st directive.
1720   sync_pb::EntitySpecifics entity_specs;
1721   sync_pb::TimeRangeDirective* time_range_directive =
1722       entity_specs.mutable_history_delete_directive()
1723           ->mutable_time_range_directive();
1724   time_range_directive->set_start_time_usec(2);
1725   time_range_directive->set_end_time_usec(5);
1726   directives.push_back(syncer::SyncData::CreateRemoteData(
1727       1,
1728       entity_specs,
1729       base::Time(),
1730       syncer::AttachmentIdList(),
1731       syncer::AttachmentServiceProxyForTest::Create()));
1732
1733   // 2nd directive.
1734   time_range_directive->Clear();
1735   time_range_directive->set_start_time_usec(8);
1736   time_range_directive->set_end_time_usec(10);
1737   directives.push_back(syncer::SyncData::CreateRemoteData(
1738       2,
1739       entity_specs,
1740       base::Time(),
1741       syncer::AttachmentIdList(),
1742       syncer::AttachmentServiceProxyForTest::Create()));
1743
1744   syncer::FakeSyncChangeProcessor change_processor;
1745   EXPECT_FALSE(
1746       history_service_->MergeDataAndStartSyncing(
1747                             syncer::HISTORY_DELETE_DIRECTIVES,
1748                             directives,
1749                             scoped_ptr<syncer::SyncChangeProcessor>(
1750                                 new syncer::SyncChangeProcessorWrapperForTest(
1751                                     &change_processor)),
1752                             scoped_ptr<syncer::SyncErrorFactory>())
1753           .error()
1754           .IsSet());
1755
1756   // Inject a task to check status and keep message loop filled before
1757   // directive processing finishes.
1758   base::MessageLoop::current()->PostTask(
1759       FROM_HERE,
1760       base::Bind(&CheckDirectiveProcessingResult,
1761                  base::Time::Now() + base::TimeDelta::FromSeconds(10),
1762                  &change_processor, 2));
1763   base::MessageLoop::current()->RunUntilIdle();
1764   EXPECT_TRUE(QueryURL(history_service_.get(), test_url));
1765   ASSERT_EQ(3, query_url_row_.visit_count());
1766   EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(1),
1767             query_url_visits_[0].visit_time);
1768   EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(6),
1769             query_url_visits_[1].visit_time);
1770   EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(7),
1771             query_url_visits_[2].visit_time);
1772
1773   // Expect two sync changes for deleting processed directives.
1774   const syncer::SyncChangeList& sync_changes = change_processor.changes();
1775   ASSERT_EQ(2u, sync_changes.size());
1776   EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[0].change_type());
1777   EXPECT_EQ(1, syncer::SyncDataRemote(sync_changes[0].sync_data()).GetId());
1778   EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, sync_changes[1].change_type());
1779   EXPECT_EQ(2, syncer::SyncDataRemote(sync_changes[1].sync_data()).GetId());
1780 }
1781
1782 TEST_F(HistoryBackendDBTest, MigratePresentations) {
1783   // Create the db we want. Use 22 since segments didn't change in that time
1784   // frame.
1785   ASSERT_NO_FATAL_FAILURE(CreateDBVersion(22));
1786
1787   const SegmentID segment_id = 2;
1788   const URLID url_id = 3;
1789   const GURL url("http://www.foo.com");
1790   const std::string url_name(VisitSegmentDatabase::ComputeSegmentName(url));
1791   const base::string16 title(base::ASCIIToUTF16("Title1"));
1792   const Time segment_time(Time::Now());
1793
1794   {
1795     // Re-open the db for manual manipulation.
1796     sql::Connection db;
1797     ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
1798
1799     // Add an entry to urls.
1800     {
1801       sql::Statement s(db.GetUniqueStatement(
1802                            "INSERT INTO urls "
1803                            "(id, url, title, last_visit_time) VALUES "
1804                            "(?, ?, ?, ?)"));
1805       s.BindInt64(0, url_id);
1806       s.BindString(1, url.spec());
1807       s.BindString16(2, title);
1808       s.BindInt64(3, segment_time.ToInternalValue());
1809       ASSERT_TRUE(s.Run());
1810     }
1811
1812     // Add an entry to segments.
1813     {
1814       sql::Statement s(db.GetUniqueStatement(
1815                            "INSERT INTO segments "
1816                            "(id, name, url_id, pres_index) VALUES "
1817                            "(?, ?, ?, ?)"));
1818       s.BindInt64(0, segment_id);
1819       s.BindString(1, url_name);
1820       s.BindInt64(2, url_id);
1821       s.BindInt(3, 4);  // pres_index
1822       ASSERT_TRUE(s.Run());
1823     }
1824
1825     // And one to segment_usage.
1826     {
1827       sql::Statement s(db.GetUniqueStatement(
1828                            "INSERT INTO segment_usage "
1829                            "(id, segment_id, time_slot, visit_count) VALUES "
1830                            "(?, ?, ?, ?)"));
1831       s.BindInt64(0, 4);  // id.
1832       s.BindInt64(1, segment_id);
1833       s.BindInt64(2, segment_time.ToInternalValue());
1834       s.BindInt(3, 5);  // visit count.
1835       ASSERT_TRUE(s.Run());
1836     }
1837   }
1838
1839   // Re-open the db, triggering migration.
1840   CreateBackendAndDatabase();
1841
1842   std::vector<PageUsageData*> results;
1843   db_->QuerySegmentUsage(segment_time, 10, &results);
1844   ASSERT_EQ(1u, results.size());
1845   EXPECT_EQ(url, results[0]->GetURL());
1846   EXPECT_EQ(segment_id, results[0]->GetID());
1847   EXPECT_EQ(title, results[0]->GetTitle());
1848   STLDeleteElements(&results);
1849 }
1850
1851 }  // namespace history