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