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