safe_browsing_database_unittest.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1// Copyright (c) 2010 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// Unit tests for the SafeBrowsing storage system.
6
7#include "base/file_util.h"
8#include "base/format_macros.h"
9#include "base/logging.h"
10#include "base/path_service.h"
11#include "base/process_util.h"
12#include "base/sha2.h"
13#include "base/stats_counters.h"
14#include "base/string_util.h"
15#include "base/time.h"
16#include "chrome/browser/safe_browsing/protocol_parser.h"
17#include "chrome/browser/safe_browsing/safe_browsing_database.h"
18#include "chrome/browser/safe_browsing/safe_browsing_store_unittest_helper.h"
19#include "chrome/test/file_test_utils.h"
20#include "googleurl/src/gurl.h"
21#include "testing/gtest/include/gtest/gtest.h"
22#include "testing/platform_test.h"
23
24using base::Time;
25
26static const FilePath::CharType kBloomSuffix[] =  FILE_PATH_LITERAL(" Bloom");
27static const FilePath::CharType kFolderPrefix[] =
28    FILE_PATH_LITERAL("SafeBrowsingTestDatabase");
29
30namespace {
31
32SBPrefix Sha256Prefix(const std::string& str) {
33  SBPrefix prefix;
34  base::SHA256HashString(str, &prefix, sizeof(prefix));
35  return prefix;
36}
37
38SBFullHash Sha256Hash(const std::string& str) {
39  SBFullHash hash;
40  base::SHA256HashString(str, &hash, sizeof(hash));
41  return hash;
42}
43
44}  // namespace
45
46class SafeBrowsingDatabaseTest : public PlatformTest {
47 public:
48  virtual void SetUp() {
49    PlatformTest::SetUp();
50
51    // Temporary directory for the database files.
52    FilePath temp_dir;
53    ASSERT_TRUE(file_util::CreateNewTempDirectory(kFolderPrefix, &temp_dir));
54    file_deleter_.reset(new FileAutoDeleter(temp_dir));
55
56    FilePath filename(temp_dir);
57    filename = filename.AppendASCII("SafeBrowsingTestDatabase");
58
59    // In case it existed from a previous run.
60    file_util::Delete(FilePath(filename.value() + kBloomSuffix), false);
61    file_util::Delete(filename, false);
62
63    database_.reset(SafeBrowsingDatabase::Create());
64    database_->Init(filename);
65  }
66
67  virtual void TearDown() {
68    database_.reset();
69    file_deleter_.reset();
70
71    PlatformTest::TearDown();
72  }
73
74  void GetListsInfo(std::vector<SBListChunkRanges>* lists) {
75    EXPECT_TRUE(database_->UpdateStarted());
76    database_->GetListsInfo(lists);
77    database_->UpdateFinished(true);
78  }
79
80  // Helper function to do an AddDel or SubDel command.
81  void DelChunk(const std::string& list,
82                int chunk_id,
83                bool is_sub_del) {
84    std::vector<SBChunkDelete> deletes;
85    SBChunkDelete chunk_delete;
86    chunk_delete.list_name = list;
87    chunk_delete.is_sub_del = is_sub_del;
88    chunk_delete.chunk_del.push_back(ChunkRange(chunk_id));
89    deletes.push_back(chunk_delete);
90    database_->DeleteChunks(deletes);
91  }
92
93  void AddDelChunk(const std::string& list, int chunk_id) {
94    DelChunk(list, chunk_id, false);
95  }
96
97  void SubDelChunk(const std::string& list, int chunk_id) {
98    DelChunk(list, chunk_id, true);
99  }
100
101  // Utility function for setting up the database for the caching test.
102  void PopulateDatabaseForCacheTest();
103
104  scoped_ptr<FileAutoDeleter> file_deleter_;
105  scoped_ptr<SafeBrowsingDatabase> database_;
106};
107
108// Tests retrieving list name information.
109TEST_F(SafeBrowsingDatabaseTest, ListName) {
110  SBChunkList chunks;
111
112  // Insert some malware add chunks.
113  SBChunkHost host;
114  host.host = Sha256Prefix("www.evil.com/");
115  host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 1);
116  host.entry->set_chunk_id(1);
117  host.entry->SetPrefixAt(0, Sha256Prefix("www.evil.com/malware.html"));
118  SBChunk chunk;
119  chunk.chunk_number = 1;
120  chunk.is_add = true;
121  chunk.hosts.push_back(host);
122  chunks.clear();
123  chunks.push_back(chunk);
124  database_->UpdateStarted();
125  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
126
127  host.host = Sha256Prefix("www.foo.com/");
128  host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 1);
129  host.entry->set_chunk_id(2);
130  host.entry->SetPrefixAt(0, Sha256Prefix("www.foo.com/malware.html"));
131  chunk.chunk_number = 2;
132  chunk.is_add = true;
133  chunk.hosts.clear();
134  chunk.hosts.push_back(host);
135  chunks.clear();
136  chunks.push_back(chunk);
137  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
138
139  host.host = Sha256Prefix("www.whatever.com/");
140  host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 1);
141  host.entry->set_chunk_id(3);
142  host.entry->SetPrefixAt(0, Sha256Prefix("www.whatever.com/malware.html"));
143  chunk.chunk_number = 3;
144  chunk.is_add = true;
145  chunk.hosts.clear();
146  chunk.hosts.push_back(host);
147  chunks.clear();
148  chunks.push_back(chunk);
149  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
150  database_->UpdateFinished(true);
151
152  std::vector<SBListChunkRanges> lists;
153  GetListsInfo(&lists);
154  EXPECT_TRUE(lists[0].name == safe_browsing_util::kMalwareList);
155  EXPECT_EQ(lists[0].adds, "1-3");
156  EXPECT_TRUE(lists[0].subs.empty());
157  lists.clear();
158
159  // Insert a malware sub chunk.
160  host.host = Sha256Prefix("www.subbed.com/");
161  host.entry = SBEntry::Create(SBEntry::SUB_PREFIX, 1);
162  host.entry->set_chunk_id(7);
163  host.entry->SetChunkIdAtPrefix(0, 19);
164  host.entry->SetPrefixAt(0, Sha256Prefix("www.subbed.com/notevil1.html"));
165  chunk.chunk_number = 7;
166  chunk.is_add = false;
167  chunk.hosts.clear();
168  chunk.hosts.push_back(host);
169  chunks.clear();
170  chunks.push_back(chunk);
171
172  database_->UpdateStarted();
173  database_->GetListsInfo(&lists);
174  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
175  database_->UpdateFinished(true);
176  lists.clear();
177
178  GetListsInfo(&lists);
179  EXPECT_TRUE(lists[0].name == safe_browsing_util::kMalwareList);
180  EXPECT_EQ(lists[0].adds, "1-3");
181  EXPECT_EQ(lists[0].subs, "7");
182  if (lists.size() == 2) {
183    // Old style database won't have the second entry since it creates the lists
184    // when it receives an update containing that list. The new bloom filter
185    // based database has these values hard coded.
186    EXPECT_TRUE(lists[1].name == safe_browsing_util::kPhishingList);
187    EXPECT_TRUE(lists[1].adds.empty());
188    EXPECT_TRUE(lists[1].subs.empty());
189  }
190  lists.clear();
191
192  // Add a phishing add chunk.
193  host.host = Sha256Prefix("www.evil.com/");
194  host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 1);
195  host.entry->set_chunk_id(47);
196  host.entry->SetPrefixAt(0, Sha256Prefix("www.evil.com/phishing.html"));
197  chunk.chunk_number = 47;
198  chunk.is_add = true;
199  chunk.hosts.clear();
200  chunk.hosts.push_back(host);
201  chunks.clear();
202  chunks.push_back(chunk);
203  database_->UpdateStarted();
204  database_->GetListsInfo(&lists);
205  database_->InsertChunks(safe_browsing_util::kPhishingList, chunks);
206
207  // Insert some phishing sub chunks.
208  host.host = Sha256Prefix("www.phishy.com/");
209  host.entry = SBEntry::Create(SBEntry::SUB_PREFIX, 1);
210  host.entry->set_chunk_id(200);
211  host.entry->SetChunkIdAtPrefix(0, 1999);
212  host.entry->SetPrefixAt(0, Sha256Prefix("www.phishy.com/notevil1.html"));
213  chunk.chunk_number = 200;
214  chunk.is_add = false;
215  chunk.hosts.clear();
216  chunk.hosts.push_back(host);
217  chunks.clear();
218  chunks.push_back(chunk);
219  database_->InsertChunks(safe_browsing_util::kPhishingList, chunks);
220
221  host.host = Sha256Prefix("www.phishy2.com/");
222  host.entry = SBEntry::Create(SBEntry::SUB_PREFIX, 1);
223  host.entry->set_chunk_id(201);
224  host.entry->SetChunkIdAtPrefix(0, 1999);
225  host.entry->SetPrefixAt(0, Sha256Prefix("www.phishy2.com/notevil1.html"));
226  chunk.chunk_number = 201;
227  chunk.is_add = false;
228  chunk.hosts.clear();
229  chunk.hosts.push_back(host);
230  chunks.clear();
231  chunks.push_back(chunk);
232  database_->InsertChunks(safe_browsing_util::kPhishingList, chunks);
233  database_->UpdateFinished(true);
234  lists.clear();
235
236  GetListsInfo(&lists);
237  EXPECT_TRUE(lists[0].name == safe_browsing_util::kMalwareList);
238  EXPECT_EQ(lists[0].adds, "1-3");
239  EXPECT_EQ(lists[0].subs, "7");
240  EXPECT_TRUE(lists[1].name == safe_browsing_util::kPhishingList);
241  EXPECT_EQ(lists[1].adds, "47");
242  EXPECT_EQ(lists[1].subs, "200-201");
243  lists.clear();
244}
245
246// Checks database reading and writing.
247TEST_F(SafeBrowsingDatabaseTest, Database) {
248  SBChunkList chunks;
249
250  // Add a simple chunk with one hostkey.
251  SBChunkHost host;
252  host.host = Sha256Prefix("www.evil.com/");
253  host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 2);
254  host.entry->set_chunk_id(1);
255  host.entry->SetPrefixAt(0, Sha256Prefix("www.evil.com/phishing.html"));
256  host.entry->SetPrefixAt(1, Sha256Prefix("www.evil.com/malware.html"));
257
258  SBChunk chunk;
259  chunk.chunk_number = 1;
260  chunk.is_add = true;
261  chunk.hosts.push_back(host);
262
263  chunks.clear();
264  chunks.push_back(chunk);
265  std::vector<SBListChunkRanges> lists;
266  database_->UpdateStarted();
267  database_->GetListsInfo(&lists);
268  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
269
270  // Add another chunk with two different hostkeys.
271  host.host = Sha256Prefix("www.evil.com/");
272  host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 2);
273  host.entry->set_chunk_id(2);
274  host.entry->SetPrefixAt(0, Sha256Prefix("www.evil.com/notevil1.html"));
275  host.entry->SetPrefixAt(1, Sha256Prefix("www.evil.com/notevil2.html"));
276
277  chunk.chunk_number = 2;
278  chunk.hosts.clear();
279  chunk.hosts.push_back(host);
280
281  host.host = Sha256Prefix("www.good.com/");
282  host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 2);
283  host.entry->set_chunk_id(2);
284  host.entry->SetPrefixAt(0, Sha256Prefix("www.good.com/good1.html"));
285  host.entry->SetPrefixAt(1, Sha256Prefix("www.good.com/good2.html"));
286
287  chunk.hosts.push_back(host);
288
289  chunks.clear();
290  chunks.push_back(chunk);
291
292  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
293
294  // and a chunk with an IP-based host
295  host.host = Sha256Prefix("192.168.0.1/");
296  host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 1);
297  host.entry->set_chunk_id(3);
298  host.entry->SetPrefixAt(0, Sha256Prefix("192.168.0.1/malware.html"));
299
300  chunk.chunk_number = 3;
301  chunk.hosts.clear();
302  chunk.hosts.push_back(host);
303
304  chunks.clear();
305  chunks.push_back(chunk);
306  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
307  database_->UpdateFinished(true);
308  lists.clear();
309
310  // Make sure they were added correctly.
311  GetListsInfo(&lists);
312  EXPECT_TRUE(lists[0].name == safe_browsing_util::kMalwareList);
313  EXPECT_EQ(lists[0].adds, "1-3");
314  EXPECT_TRUE(lists[0].subs.empty());
315  lists.clear();
316
317  const Time now = Time::Now();
318  std::vector<SBFullHashResult> full_hashes;
319  std::vector<SBPrefix> prefix_hits;
320  std::string matching_list;
321  EXPECT_TRUE(database_->ContainsUrl(GURL("http://www.evil.com/phishing.html"),
322                                     &matching_list, &prefix_hits,
323                                     &full_hashes, now));
324  EXPECT_EQ(prefix_hits[0], Sha256Prefix("www.evil.com/phishing.html"));
325  EXPECT_EQ(prefix_hits.size(), 1U);
326
327  EXPECT_TRUE(database_->ContainsUrl(GURL("http://www.evil.com/malware.html"),
328                                     &matching_list, &prefix_hits,
329                                     &full_hashes, now));
330
331  EXPECT_TRUE(database_->ContainsUrl(GURL("http://www.evil.com/notevil1.html"),
332                                     &matching_list, &prefix_hits,
333                                     &full_hashes, now));
334
335  EXPECT_TRUE(database_->ContainsUrl(GURL("http://www.evil.com/notevil2.html"),
336                                     &matching_list, &prefix_hits,
337                                     &full_hashes, now));
338
339  EXPECT_TRUE(database_->ContainsUrl(GURL("http://www.good.com/good1.html"),
340                                     &matching_list, &prefix_hits,
341                                     &full_hashes, now));
342
343  EXPECT_TRUE(database_->ContainsUrl(GURL("http://www.good.com/good2.html"),
344                                     &matching_list, &prefix_hits,
345                                     &full_hashes, now));
346
347  EXPECT_TRUE(database_->ContainsUrl(GURL("http://192.168.0.1/malware.html"),
348                                     &matching_list, &prefix_hits,
349                                     &full_hashes, now));
350
351  EXPECT_FALSE(database_->ContainsUrl(GURL("http://www.evil.com/"),
352                                      &matching_list, &prefix_hits,
353                                      &full_hashes, now));
354  EXPECT_EQ(prefix_hits.size(), 0U);
355
356  EXPECT_FALSE(database_->ContainsUrl(GURL("http://www.evil.com/robots.txt"),
357                                      &matching_list, &prefix_hits,
358                                      &full_hashes, now));
359
360
361
362  // Attempt to re-add the first chunk (should be a no-op).
363  // see bug: http://code.google.com/p/chromium/issues/detail?id=4522
364  host.host = Sha256Prefix("www.evil.com/");
365  host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 2);
366  host.entry->set_chunk_id(1);
367  host.entry->SetPrefixAt(0, Sha256Prefix("www.evil.com/phishing.html"));
368  host.entry->SetPrefixAt(1, Sha256Prefix("www.evil.com/malware.html"));
369
370  chunk.chunk_number = 1;
371  chunk.is_add = true;
372  chunk.hosts.clear();
373  chunk.hosts.push_back(host);
374
375  chunks.clear();
376  chunks.push_back(chunk);
377  database_->UpdateStarted();
378  database_->GetListsInfo(&lists);
379  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
380  database_->UpdateFinished(true);
381  lists.clear();
382
383  GetListsInfo(&lists);
384  EXPECT_TRUE(lists[0].name == safe_browsing_util::kMalwareList);
385  EXPECT_EQ(lists[0].adds, "1-3");
386  EXPECT_TRUE(lists[0].subs.empty());
387  lists.clear();
388
389
390  // Test removing a single prefix from the add chunk.
391  host.host = Sha256Prefix("www.evil.com/");
392  host.entry = SBEntry::Create(SBEntry::SUB_PREFIX, 1);
393  host.entry->set_chunk_id(2);
394  host.entry->SetChunkIdAtPrefix(0, 2);
395  host.entry->SetPrefixAt(0, Sha256Prefix("www.evil.com/notevil1.html"));
396
397  chunk.is_add = false;
398  chunk.chunk_number = 4;
399  chunk.hosts.clear();
400  chunk.hosts.push_back(host);
401
402  chunks.clear();
403  chunks.push_back(chunk);
404
405  database_->UpdateStarted();
406  database_->GetListsInfo(&lists);
407  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
408  database_->UpdateFinished(true);
409  lists.clear();
410
411  EXPECT_TRUE(database_->ContainsUrl(GURL("http://www.evil.com/phishing.html"),
412                                     &matching_list, &prefix_hits,
413                                     &full_hashes, now));
414  EXPECT_EQ(prefix_hits[0], Sha256Prefix("www.evil.com/phishing.html"));
415  EXPECT_EQ(prefix_hits.size(), 1U);
416
417  EXPECT_FALSE(database_->ContainsUrl(GURL("http://www.evil.com/notevil1.html"),
418                                      &matching_list, &prefix_hits,
419                                      &full_hashes, now));
420  EXPECT_EQ(prefix_hits.size(), 0U);
421
422  EXPECT_TRUE(database_->ContainsUrl(GURL("http://www.evil.com/notevil2.html"),
423                                     &matching_list, &prefix_hits,
424                                     &full_hashes, now));
425
426  EXPECT_TRUE(database_->ContainsUrl(GURL("http://www.good.com/good1.html"),
427                                     &matching_list, &prefix_hits,
428                                     &full_hashes, now));
429
430  EXPECT_TRUE(database_->ContainsUrl(GURL("http://www.good.com/good2.html"),
431                                     &matching_list, &prefix_hits,
432                                     &full_hashes, now));
433
434  GetListsInfo(&lists);
435  EXPECT_TRUE(lists[0].name == safe_browsing_util::kMalwareList);
436  EXPECT_EQ(lists[0].subs, "4");
437  lists.clear();
438
439  // Test the same sub chunk again.  This should be a no-op.
440  // see bug: http://code.google.com/p/chromium/issues/detail?id=4522
441  host.host = Sha256Prefix("www.evil.com/");
442  host.entry = SBEntry::Create(SBEntry::SUB_PREFIX, 1);
443  host.entry->set_chunk_id(2);
444  host.entry->SetChunkIdAtPrefix(0, 2);
445  host.entry->SetPrefixAt(0, Sha256Prefix("www.evil.com/notevil1.html"));
446
447  chunk.is_add = false;
448  chunk.chunk_number = 4;
449  chunk.hosts.clear();
450  chunk.hosts.push_back(host);
451
452  chunks.clear();
453  chunks.push_back(chunk);
454
455  database_->UpdateStarted();
456  database_->GetListsInfo(&lists);
457  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
458  database_->UpdateFinished(true);
459  lists.clear();
460
461  GetListsInfo(&lists);
462  EXPECT_TRUE(lists[0].name == safe_browsing_util::kMalwareList);
463  EXPECT_EQ(lists[0].subs, "4");
464  lists.clear();
465
466
467  // Test removing all the prefixes from an add chunk.
468  database_->UpdateStarted();
469  database_->GetListsInfo(&lists);
470  AddDelChunk(safe_browsing_util::kMalwareList, 2);
471  database_->UpdateFinished(true);
472  lists.clear();
473
474  EXPECT_FALSE(database_->ContainsUrl(GURL("http://www.evil.com/notevil2.html"),
475                                      &matching_list, &prefix_hits,
476                                      &full_hashes, now));
477
478  EXPECT_FALSE(database_->ContainsUrl(GURL("http://www.good.com/good1.html"),
479                                      &matching_list, &prefix_hits,
480                                      &full_hashes, now));
481
482  EXPECT_FALSE(database_->ContainsUrl(GURL("http://www.good.com/good2.html"),
483                                      &matching_list, &prefix_hits,
484                                      &full_hashes, now));
485
486  GetListsInfo(&lists);
487  EXPECT_TRUE(lists[0].name == safe_browsing_util::kMalwareList);
488  EXPECT_EQ(lists[0].adds, "1,3");
489  EXPECT_EQ(lists[0].subs, "4");
490  lists.clear();
491
492  // The adddel command exposed a bug in the transaction code where any
493  // transaction after it would fail.  Add a dummy entry and remove it to
494  // make sure the transcation works fine.
495  host.host = Sha256Prefix("www.redherring.com/");
496  host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 1);
497  host.entry->set_chunk_id(1);
498  host.entry->SetPrefixAt(0, Sha256Prefix("www.redherring.com/index.html"));
499
500  chunk.is_add = true;
501  chunk.chunk_number = 44;
502  chunk.hosts.clear();
503  chunk.hosts.push_back(host);
504
505  chunks.clear();
506  chunks.push_back(chunk);
507  database_->UpdateStarted();
508  database_->GetListsInfo(&lists);
509  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
510
511  // Now remove the dummy entry.  If there are any problems with the
512  // transactions, asserts will fire.
513  AddDelChunk(safe_browsing_util::kMalwareList, 44);
514
515  // Test the subdel command.
516  SubDelChunk(safe_browsing_util::kMalwareList, 4);
517  database_->UpdateFinished(true);
518  lists.clear();
519
520  GetListsInfo(&lists);
521  EXPECT_TRUE(lists[0].name == safe_browsing_util::kMalwareList);
522  EXPECT_EQ(lists[0].adds, "1,3");
523  EXPECT_EQ(lists[0].subs, "");
524  lists.clear();
525
526  // Test a sub command coming in before the add.
527  host.host = Sha256Prefix("www.notevilanymore.com/");
528  host.entry = SBEntry::Create(SBEntry::SUB_PREFIX, 2);
529  host.entry->set_chunk_id(10);
530  host.entry->SetPrefixAt(0, Sha256Prefix("www.notevilanymore.com/index.html"));
531  host.entry->SetChunkIdAtPrefix(0, 10);
532  host.entry->SetPrefixAt(1, Sha256Prefix("www.notevilanymore.com/good.html"));
533  host.entry->SetChunkIdAtPrefix(1, 10);
534
535  chunk.is_add = false;
536  chunk.chunk_number = 5;
537  chunk.hosts.clear();
538  chunk.hosts.push_back(host);
539
540  chunks.clear();
541  chunks.push_back(chunk);
542  database_->UpdateStarted();
543  database_->GetListsInfo(&lists);
544  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
545  database_->UpdateFinished(true);
546  lists.clear();
547
548  EXPECT_FALSE(database_->ContainsUrl(
549      GURL("http://www.notevilanymore.com/index.html"),
550      &matching_list, &prefix_hits, &full_hashes, now));
551
552  // Now insert the tardy add chunk.
553  host.host = Sha256Prefix("www.notevilanymore.com/");
554  host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 2);
555  host.entry->SetPrefixAt(0, Sha256Prefix("www.notevilanymore.com/index.html"));
556  host.entry->SetPrefixAt(1, Sha256Prefix("www.notevilanymore.com/good.html"));
557
558  chunk.is_add = true;
559  chunk.chunk_number = 10;
560  chunk.hosts.clear();
561  chunk.hosts.push_back(host);
562
563  chunks.clear();
564  chunks.push_back(chunk);
565  database_->UpdateStarted();
566  database_->GetListsInfo(&lists);
567  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
568  database_->UpdateFinished(true);
569  lists.clear();
570
571  EXPECT_FALSE(database_->ContainsUrl(
572      GURL("http://www.notevilanymore.com/index.html"),
573      &matching_list, &prefix_hits, &full_hashes, now));
574
575  EXPECT_FALSE(database_->ContainsUrl(
576      GURL("http://www.notevilanymore.com/good.html"),
577      &matching_list, &prefix_hits, &full_hashes, now));
578}
579
580
581// Test adding zero length chunks to the database.
582TEST_F(SafeBrowsingDatabaseTest, ZeroSizeChunk) {
583  SBChunkList chunks;
584
585  // Populate with a couple of normal chunks.
586  SBChunkHost host;
587  host.host = Sha256Prefix("www.test.com/");
588  host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 2);
589  host.entry->SetPrefixAt(0, Sha256Prefix("www.test.com/test1.html"));
590  host.entry->SetPrefixAt(1, Sha256Prefix("www.test.com/test2.html"));
591  host.entry->set_chunk_id(1);
592
593  SBChunk chunk;
594  chunk.is_add = true;
595  chunk.chunk_number = 1;
596  chunk.hosts.push_back(host);
597
598  chunks.clear();
599  chunks.push_back(chunk);
600
601  host.host = Sha256Prefix("www.random.com/");
602  host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 2);
603  host.entry->SetPrefixAt(0, Sha256Prefix("www.random.com/random1.html"));
604  host.entry->SetPrefixAt(1, Sha256Prefix("www.random.com/random2.html"));
605  host.entry->set_chunk_id(10);
606  chunk.chunk_number = 10;
607  chunk.hosts.clear();
608  chunk.hosts.push_back(host);
609  chunks.push_back(chunk);
610
611  std::vector<SBListChunkRanges> lists;
612  database_->UpdateStarted();
613  database_->GetListsInfo(&lists);
614  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
615  database_->UpdateFinished(true);
616  lists.clear();
617
618  // Add an empty ADD and SUB chunk.
619  GetListsInfo(&lists);
620  EXPECT_EQ(lists[0].adds, "1,10");
621  lists.clear();
622
623  SBChunk empty_chunk;
624  empty_chunk.chunk_number = 19;
625  empty_chunk.is_add = true;
626  chunks.clear();
627  chunks.push_back(empty_chunk);
628  database_->UpdateStarted();
629  database_->GetListsInfo(&lists);
630  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
631  chunks.clear();
632  empty_chunk.chunk_number = 7;
633  empty_chunk.is_add = false;
634  chunks.push_back(empty_chunk);
635  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
636  database_->UpdateFinished(true);
637  lists.clear();
638
639  GetListsInfo(&lists);
640  EXPECT_EQ(lists[0].adds, "1,10,19");
641  EXPECT_EQ(lists[0].subs, "7");
642  lists.clear();
643
644  // Add an empty chunk along with a couple that contain data. This should
645  // result in the chunk range being reduced in size.
646  host.host = Sha256Prefix("www.notempty.com/");
647  host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 1);
648  host.entry->SetPrefixAt(0, Sha256Prefix("www.notempty.com/full1.html"));
649  host.entry->set_chunk_id(20);
650  empty_chunk.chunk_number = 20;
651  empty_chunk.is_add = true;
652  empty_chunk.hosts.clear();
653  empty_chunk.hosts.push_back(host);
654  chunks.clear();
655  chunks.push_back(empty_chunk);
656
657  empty_chunk.chunk_number = 21;
658  empty_chunk.is_add = true;
659  empty_chunk.hosts.clear();
660  chunks.push_back(empty_chunk);
661
662  host.host = Sha256Prefix("www.notempty.com/");
663  host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 1);
664  host.entry->SetPrefixAt(0, Sha256Prefix("www.notempty.com/full2.html"));
665  host.entry->set_chunk_id(22);
666  empty_chunk.hosts.clear();
667  empty_chunk.hosts.push_back(host);
668  empty_chunk.chunk_number = 22;
669  empty_chunk.is_add = true;
670  chunks.push_back(empty_chunk);
671
672  database_->UpdateStarted();
673  database_->GetListsInfo(&lists);
674  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
675  database_->UpdateFinished(true);
676  lists.clear();
677
678  const Time now = Time::Now();
679  std::vector<SBFullHashResult> full_hashes;
680  std::vector<SBPrefix> prefix_hits;
681  std::string matching_list;
682  EXPECT_TRUE(database_->ContainsUrl(GURL("http://www.notempty.com/full1.html"),
683                                     &matching_list, &prefix_hits,
684                                     &full_hashes, now));
685  EXPECT_TRUE(database_->ContainsUrl(GURL("http://www.notempty.com/full2.html"),
686                                     &matching_list, &prefix_hits,
687                                     &full_hashes, now));
688
689  GetListsInfo(&lists);
690  EXPECT_EQ(lists[0].adds, "1,10,19-22");
691  EXPECT_EQ(lists[0].subs, "7");
692  lists.clear();
693
694  // Handle AddDel and SubDel commands for empty chunks.
695  database_->UpdateStarted();
696  database_->GetListsInfo(&lists);
697  AddDelChunk(safe_browsing_util::kMalwareList, 21);
698  database_->UpdateFinished(true);
699  lists.clear();
700
701  GetListsInfo(&lists);
702  EXPECT_EQ(lists[0].adds, "1,10,19-20,22");
703  EXPECT_EQ(lists[0].subs, "7");
704  lists.clear();
705
706  database_->UpdateStarted();
707  database_->GetListsInfo(&lists);
708  SubDelChunk(safe_browsing_util::kMalwareList, 7);
709  database_->UpdateFinished(true);
710  lists.clear();
711
712  GetListsInfo(&lists);
713  EXPECT_EQ(lists[0].adds, "1,10,19-20,22");
714  EXPECT_EQ(lists[0].subs, "");
715  lists.clear();
716}
717
718// Utility function for setting up the database for the caching test.
719void SafeBrowsingDatabaseTest::PopulateDatabaseForCacheTest() {
720  // Add a simple chunk with one hostkey and cache it.
721  SBChunkHost host;
722  host.host = Sha256Prefix("www.evil.com/");
723  host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 2);
724  host.entry->set_chunk_id(1);
725  host.entry->SetPrefixAt(0, Sha256Prefix("www.evil.com/phishing.html"));
726  host.entry->SetPrefixAt(1, Sha256Prefix("www.evil.com/malware.html"));
727
728  SBChunk chunk;
729  chunk.chunk_number = 1;
730  chunk.is_add = true;
731  chunk.hosts.push_back(host);
732
733  SBChunkList chunks;
734  std::vector<SBListChunkRanges> lists;
735  chunks.push_back(chunk);
736  database_->UpdateStarted();
737  database_->GetListsInfo(&lists);
738  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
739  database_->UpdateFinished(true);
740  lists.clear();
741
742  // Add the GetHash results to the cache.
743  SBFullHashResult full_hash;
744  full_hash.hash = Sha256Hash("www.evil.com/phishing.html");
745  full_hash.list_name = safe_browsing_util::kMalwareList;
746  full_hash.add_chunk_id = 1;
747
748  std::vector<SBFullHashResult> results;
749  results.push_back(full_hash);
750
751  full_hash.hash = Sha256Hash("www.evil.com/malware.html");
752  results.push_back(full_hash);
753
754  std::vector<SBPrefix> prefixes;
755  database_->CacheHashResults(prefixes, results);
756}
757
758TEST_F(SafeBrowsingDatabaseTest, HashCaching) {
759  PopulateDatabaseForCacheTest();
760
761  // We should have both full hashes in the cache.
762  SafeBrowsingDatabase::HashCache* hash_cache = database_->hash_cache();
763  EXPECT_EQ(hash_cache->size(), 2U);
764
765  // Test the cache lookup for the first prefix.
766  std::string listname;
767  std::vector<SBPrefix> prefixes;
768  std::vector<SBFullHashResult> full_hashes;
769  database_->ContainsUrl(GURL("http://www.evil.com/phishing.html"),
770                         &listname, &prefixes, &full_hashes, Time::Now());
771  EXPECT_EQ(full_hashes.size(), 1U);
772  EXPECT_TRUE(SBFullHashEq(full_hashes[0].hash,
773                           Sha256Hash("www.evil.com/phishing.html")));
774
775  prefixes.clear();
776  full_hashes.clear();
777
778  // Test the cache lookup for the second prefix.
779  database_->ContainsUrl(GURL("http://www.evil.com/malware.html"),
780                         &listname, &prefixes, &full_hashes, Time::Now());
781  EXPECT_EQ(full_hashes.size(), 1U);
782  EXPECT_TRUE(SBFullHashEq(full_hashes[0].hash,
783                           Sha256Hash("www.evil.com/malware.html")));
784
785  prefixes.clear();
786  full_hashes.clear();
787
788  // Test removing a prefix via a sub chunk.
789  SBChunkHost host;
790  host.host = Sha256Prefix("www.evil.com/");
791  host.entry = SBEntry::Create(SBEntry::SUB_PREFIX, 1);
792  host.entry->set_chunk_id(1);
793  host.entry->SetChunkIdAtPrefix(0, 1);
794  host.entry->SetPrefixAt(0, Sha256Prefix("www.evil.com/phishing.html"));
795
796  SBChunk chunk;
797  chunk.chunk_number = 2;
798  chunk.is_add = false;
799  chunk.hosts.clear();
800  chunk.hosts.push_back(host);
801  SBChunkList chunks;
802  chunks.push_back(chunk);
803
804  std::vector<SBListChunkRanges> lists;
805  database_->UpdateStarted();
806  database_->GetListsInfo(&lists);
807  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
808  database_->UpdateFinished(true);
809  lists.clear();
810
811  // This prefix should still be there.
812  database_->ContainsUrl(GURL("http://www.evil.com/malware.html"),
813                         &listname, &prefixes, &full_hashes, Time::Now());
814  EXPECT_EQ(full_hashes.size(), 1U);
815  EXPECT_TRUE(SBFullHashEq(full_hashes[0].hash,
816                           Sha256Hash("www.evil.com/malware.html")));
817
818  prefixes.clear();
819  full_hashes.clear();
820
821  // This prefix should be gone.
822  database_->ContainsUrl(GURL("http://www.evil.com/phishing.html"),
823                         &listname, &prefixes, &full_hashes, Time::Now());
824  EXPECT_EQ(full_hashes.size(), 0U);
825
826  prefixes.clear();
827  full_hashes.clear();
828
829  // Test that an AddDel for the original chunk removes the last cached entry.
830  database_->UpdateStarted();
831  database_->GetListsInfo(&lists);
832  AddDelChunk(safe_browsing_util::kMalwareList, 1);
833  database_->UpdateFinished(true);
834  database_->ContainsUrl(GURL("http://www.evil.com/malware.html"),
835                         &listname, &prefixes, &full_hashes, Time::Now());
836  EXPECT_EQ(full_hashes.size(), 0U);
837  EXPECT_EQ(database_->hash_cache()->size(), 0U);
838
839  lists.clear();
840  prefixes.clear();
841  full_hashes.clear();
842
843  // Test that the cache won't return expired values. First we have to adjust
844  // the cached entries' received time to make them older, since the database
845  // cache insert uses Time::Now(). First, store some entries.
846  PopulateDatabaseForCacheTest();
847  hash_cache = database_->hash_cache();
848  EXPECT_EQ(hash_cache->size(), 2U);
849
850  // Now adjust one of the entries times to be in the past.
851  base::Time expired = base::Time::Now() - base::TimeDelta::FromMinutes(60);
852  const SBPrefix key = Sha256Prefix("www.evil.com/malware.html");
853  SafeBrowsingDatabase::HashList& entries = (*hash_cache)[key];
854  SafeBrowsingDatabase::HashCacheEntry entry = entries.front();
855  entries.pop_front();
856  entry.received = expired;
857  entries.push_back(entry);
858
859  database_->ContainsUrl(GURL("http://www.evil.com/malware.html"),
860                         &listname, &prefixes, &full_hashes, expired);
861  EXPECT_EQ(full_hashes.size(), 0U);
862
863  // This entry should still exist.
864  database_->ContainsUrl(GURL("http://www.evil.com/phishing.html"),
865                         &listname, &prefixes, &full_hashes, expired);
866  EXPECT_EQ(full_hashes.size(), 1U);
867
868
869  // Testing prefix miss caching. First, we clear out the existing database,
870  // Since PopulateDatabaseForCacheTest() doesn't handle adding duplicate
871  // chunks.
872  database_->UpdateStarted();
873  database_->GetListsInfo(&lists);
874  AddDelChunk(safe_browsing_util::kMalwareList, 1);
875  database_->UpdateFinished(true);
876  lists.clear();
877
878  std::vector<SBPrefix> prefix_misses;
879  std::vector<SBFullHashResult> empty_full_hash;
880  prefix_misses.push_back(Sha256Prefix("http://www.bad.com/malware.html"));
881  prefix_misses.push_back(Sha256Prefix("http://www.bad.com/phishing.html"));
882  database_->CacheHashResults(prefix_misses, empty_full_hash);
883
884  // Prefixes with no full results are misses.
885  EXPECT_EQ(database_->prefix_miss_cache()->size(), 2U);
886
887  // Update the database.
888  PopulateDatabaseForCacheTest();
889
890  // Prefix miss cache should be cleared.
891  EXPECT_EQ(database_->prefix_miss_cache()->size(), 0U);
892
893  // Cache a GetHash miss for a particular prefix, and even though the prefix is
894  // in the database, it is flagged as a miss so looking up the associated URL
895  // will not succeed.
896  prefixes.clear();
897  full_hashes.clear();
898  prefix_misses.clear();
899  empty_full_hash.clear();
900  prefix_misses.push_back(Sha256Prefix("www.evil.com/phishing.html"));
901  database_->CacheHashResults(prefix_misses, empty_full_hash);
902  EXPECT_FALSE(database_->ContainsUrl(GURL("http://www.evil.com/phishing.html"),
903                                      &listname, &prefixes,
904                                      &full_hashes, Time::Now()));
905
906  lists.clear();
907  prefixes.clear();
908  full_hashes.clear();
909
910  // Test receiving a full add chunk.
911  host.host = Sha256Prefix("www.fullevil.com/");
912  host.entry = SBEntry::Create(SBEntry::ADD_FULL_HASH, 2);
913  host.entry->set_chunk_id(20);
914  host.entry->SetFullHashAt(0, Sha256Hash("www.fullevil.com/bad1.html"));
915  host.entry->SetFullHashAt(1, Sha256Hash("www.fullevil.com/bad2.html"));
916
917  chunk.chunk_number = 20;
918  chunk.is_add = true;
919  chunk.hosts.clear();
920  chunk.hosts.push_back(host);
921  chunks.clear();
922  chunks.push_back(chunk);
923  database_->UpdateStarted();
924  database_->GetListsInfo(&lists);
925  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
926  database_->UpdateFinished(true);
927
928  EXPECT_TRUE(database_->ContainsUrl(GURL("http://www.fullevil.com/bad1.html"),
929                                     &listname, &prefixes, &full_hashes,
930                                     Time::Now()));
931  EXPECT_EQ(full_hashes.size(), 1U);
932  EXPECT_TRUE(SBFullHashEq(full_hashes[0].hash,
933                           Sha256Hash("www.fullevil.com/bad1.html")));
934  lists.clear();
935  prefixes.clear();
936  full_hashes.clear();
937
938  EXPECT_TRUE(database_->ContainsUrl(GURL("http://www.fullevil.com/bad2.html"),
939                                     &listname, &prefixes, &full_hashes,
940                                     Time::Now()));
941  EXPECT_EQ(full_hashes.size(), 1U);
942  EXPECT_TRUE(SBFullHashEq(full_hashes[0].hash,
943                           Sha256Hash("www.fullevil.com/bad2.html")));
944  lists.clear();
945  prefixes.clear();
946  full_hashes.clear();
947
948  // Test receiving a full sub chunk, which will remove one of the full adds.
949  host.host = Sha256Prefix("www.fullevil.com/");
950  host.entry = SBEntry::Create(SBEntry::SUB_FULL_HASH, 1);
951  host.entry->set_chunk_id(200);
952  host.entry->SetChunkIdAtPrefix(0, 20);
953  host.entry->SetFullHashAt(0, Sha256Hash("www.fullevil.com/bad1.html"));
954
955  chunk.chunk_number = 200;
956  chunk.is_add = false;
957  chunk.hosts.clear();
958  chunk.hosts.push_back(host);
959  chunks.clear();
960  chunks.push_back(chunk);
961  database_->UpdateStarted();
962  database_->GetListsInfo(&lists);
963  database_->InsertChunks(safe_browsing_util::kMalwareList, chunks);
964  database_->UpdateFinished(true);
965
966  EXPECT_FALSE(database_->ContainsUrl(GURL("http://www.fullevil.com/bad1.html"),
967                                      &listname, &prefixes, &full_hashes,
968                                      Time::Now()));
969  EXPECT_EQ(full_hashes.size(), 0U);
970
971  // There should be one remaining full add.
972  EXPECT_TRUE(database_->ContainsUrl(GURL("http://www.fullevil.com/bad2.html"),
973                                     &listname, &prefixes, &full_hashes,
974                                     Time::Now()));
975  EXPECT_EQ(full_hashes.size(), 1U);
976  EXPECT_TRUE(SBFullHashEq(full_hashes[0].hash,
977                           Sha256Hash("www.fullevil.com/bad2.html")));
978  lists.clear();
979  prefixes.clear();
980  full_hashes.clear();
981
982  // Now test an AddDel for the remaining full add.
983  database_->UpdateStarted();
984  database_->GetListsInfo(&lists);
985  AddDelChunk(safe_browsing_util::kMalwareList, 20);
986  database_->UpdateFinished(true);
987  lists.clear();
988
989  EXPECT_FALSE(database_->ContainsUrl(GURL("http://www.fullevil.com/bad1.html"),
990                                      &listname, &prefixes, &full_hashes,
991                                      Time::Now()));
992  EXPECT_FALSE(database_->ContainsUrl(GURL("http://www.fullevil.com/bad2.html"),
993                                      &listname, &prefixes, &full_hashes,
994                                      Time::Now()));
995}
996
997namespace {
998
999void PrintStat(const char* name) {
1000  int value = StatsTable::current()->GetCounterValue(name);
1001  LOG(INFO) << StringPrintf("%s %d", name, value);
1002}
1003
1004FilePath GetFullSBDataPath(const FilePath& path) {
1005  FilePath full_path;
1006  CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &full_path));
1007  full_path = full_path.AppendASCII("chrome");
1008  full_path = full_path.AppendASCII("test");
1009  full_path = full_path.AppendASCII("data");
1010  full_path = full_path.AppendASCII("safe_browsing");
1011  full_path = full_path.Append(path);
1012  CHECK(file_util::PathExists(full_path));
1013  return full_path;
1014}
1015
1016// TODO(shess): The clients of this structure manually manage
1017// |chunks|.  Improve this code to apply the RAII idiom to manage
1018// |chunks|.
1019struct ChunksInfo {
1020  SBChunkList* chunks;  // weak
1021  std::string listname;
1022};
1023
1024void PerformUpdate(const FilePath& initial_db,
1025                   const std::vector<ChunksInfo>& chunks,
1026                   const std::vector<SBChunkDelete>& deletes) {
1027  base::IoCounters before, after;
1028
1029  FilePath path;
1030  PathService::Get(base::DIR_TEMP, &path);
1031  path = path.AppendASCII("SafeBrowsingTestDatabase");
1032
1033  // In case it existed from a previous run.
1034  file_util::Delete(path, false);
1035
1036  if (!initial_db.empty()) {
1037    FilePath full_initial_db = GetFullSBDataPath(initial_db);
1038    ASSERT_TRUE(file_util::CopyFile(full_initial_db, path));
1039  }
1040
1041  SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
1042  database->Init(path);
1043
1044  Time before_time = Time::Now();
1045  base::ProcessHandle handle = base::Process::Current().handle();
1046  scoped_ptr<base::ProcessMetrics> metric(
1047#if !defined(OS_MACOSX)
1048      base::ProcessMetrics::CreateProcessMetrics(handle));
1049#else
1050      // Getting stats only for the current process is enough, so NULL is fine.
1051      base::ProcessMetrics::CreateProcessMetrics(handle, NULL));
1052#endif
1053  // Get IO stats.  These are currently not supported on Mac, and may not be
1054  // available for Linux, so we check the result and only show IO stats if
1055  // they are available.
1056  bool gotIOCounters = metric->GetIOCounters(&before);
1057
1058  std::vector<SBListChunkRanges> lists;
1059  database->UpdateStarted();
1060  database->GetListsInfo(&lists);
1061  database->DeleteChunks(deletes);
1062  for (size_t i = 0; i < chunks.size(); ++i)
1063    database->InsertChunks(chunks[i].listname, *chunks[i].chunks);
1064
1065  database->UpdateFinished(true);
1066  lists.clear();
1067
1068  gotIOCounters = gotIOCounters && metric->GetIOCounters(&after);
1069
1070  if (gotIOCounters) {
1071    LOG(INFO) << StringPrintf("I/O Read Bytes: %" PRIu64,
1072        after.ReadTransferCount - before.ReadTransferCount);
1073    LOG(INFO) << StringPrintf("I/O Write Bytes: %" PRIu64,
1074        after.WriteTransferCount - before.WriteTransferCount);
1075    LOG(INFO) << StringPrintf("I/O Reads: %" PRIu64,
1076        after.ReadOperationCount - before.ReadOperationCount);
1077    LOG(INFO) << StringPrintf("I/O Writes: %" PRIu64,
1078        after.WriteOperationCount - before.WriteOperationCount);
1079  }
1080  LOG(INFO) << StringPrintf("Finished in %" PRId64 " ms",
1081      (Time::Now() - before_time).InMilliseconds());
1082
1083  PrintStat("c:SB.HostSelect");
1084  PrintStat("c:SB.HostSelectForBloomFilter");
1085  PrintStat("c:SB.HostReplace");
1086  PrintStat("c:SB.HostInsert");
1087  PrintStat("c:SB.HostDelete");
1088  PrintStat("c:SB.ChunkSelect");
1089  PrintStat("c:SB.ChunkInsert");
1090  PrintStat("c:SB.ChunkDelete");
1091  PrintStat("c:SB.TransactionCommit");
1092
1093  delete database;
1094}
1095
1096void UpdateDatabase(const FilePath& initial_db,
1097                    const FilePath& response_path,
1098                    const FilePath& updates_path) {
1099  // First we read the chunks from disk, so that this isn't counted in IO bytes.
1100  std::vector<ChunksInfo> chunks;
1101
1102  SafeBrowsingProtocolParser parser;
1103  if (!updates_path.empty()) {
1104    FilePath data_dir = GetFullSBDataPath(updates_path);
1105    file_util::FileEnumerator file_enum(data_dir, false,
1106        file_util::FileEnumerator::FILES);
1107    while (true) {
1108      FilePath file = file_enum.Next();
1109      if (file.empty())
1110        break;
1111
1112      int64 size64;
1113      bool result = file_util::GetFileSize(file, &size64);
1114      CHECK(result);
1115
1116      int size = static_cast<int>(size64);
1117      scoped_array<char> data(new char[size]);
1118      file_util::ReadFile(file, data.get(), size);
1119
1120      ChunksInfo info;
1121      info.chunks = new SBChunkList;
1122
1123      bool re_key;
1124      result = parser.ParseChunk(data.get(), size, "", "",
1125                                 &re_key, info.chunks);
1126      CHECK(result);
1127
1128      info.listname = WideToASCII(file.BaseName().ToWStringHack());
1129      size_t index = info.listname.find('_');  // Get rid fo the _s or _a.
1130      info.listname.resize(index);
1131      info.listname.erase(0, 3);  // Get rid of the 000 etc.
1132
1133      chunks.push_back(info);
1134    }
1135  }
1136
1137  std::vector<SBChunkDelete> deletes;
1138  if (!response_path.empty()) {
1139    std::string update;
1140    FilePath full_response_path = GetFullSBDataPath(response_path);
1141    if (file_util::ReadFileToString(full_response_path, &update)) {
1142      int next_update;
1143      bool result, rekey, reset;
1144      std::vector<ChunkUrl> urls;
1145      result = parser.ParseUpdate(update.c_str(),
1146                                  static_cast<int>(update.length()),
1147                                  "",
1148                                  &next_update,
1149                                  &rekey,
1150                                  &reset,
1151                                  &deletes,
1152                                  &urls);
1153      DCHECK(result);
1154      if (!updates_path.empty())
1155        DCHECK(urls.size() == chunks.size());
1156    }
1157  }
1158
1159  PerformUpdate(initial_db, chunks, deletes);
1160
1161  // TODO(shess): Make ChunksInfo handle this via scoping.
1162  for (std::vector<ChunksInfo>::iterator iter = chunks.begin();
1163       iter != chunks.end(); ++iter) {
1164    delete iter->chunks;
1165    iter->chunks = NULL;
1166  }
1167}
1168
1169// Construct the shared base path used by the GetOld* functions.
1170FilePath BasePath() {
1171  return FilePath(FILE_PATH_LITERAL("old"));
1172}
1173
1174FilePath GetOldSafeBrowsingPath() {
1175  return BasePath().AppendASCII("SafeBrowsing");
1176}
1177
1178FilePath GetOldResponsePath() {
1179  return BasePath().AppendASCII("response");
1180}
1181
1182FilePath GetOldUpdatesPath() {
1183  return BasePath().AppendASCII("updates");
1184}
1185
1186}  // namespace
1187
1188// Counts the IO needed for the initial update of a database.
1189// test\data\safe_browsing\download_update.py was used to fetch the add/sub
1190// chunks that are read, in order to get repeatable runs.
1191TEST(SafeBrowsingDatabase, DatabaseInitialIO) {
1192  UpdateDatabase(FilePath(), FilePath(), FilePath().AppendASCII("initial"));
1193}
1194
1195// Counts the IO needed to update a month old database.
1196// The data files were generated by running "..\download_update.py postdata"
1197// in the "safe_browsing\old" directory.
1198TEST(SafeBrowsingDatabase, DatabaseOldIO) {
1199  UpdateDatabase(GetOldSafeBrowsingPath(), GetOldResponsePath(),
1200                 GetOldUpdatesPath());
1201}
1202
1203// Like DatabaseOldIO but only the deletes.
1204TEST(SafeBrowsingDatabase, DatabaseOldDeletesIO) {
1205  UpdateDatabase(GetOldSafeBrowsingPath(), GetOldResponsePath(), FilePath());
1206}
1207
1208// Like DatabaseOldIO but only the updates.
1209TEST(SafeBrowsingDatabase, DatabaseOldUpdatesIO) {
1210  UpdateDatabase(GetOldSafeBrowsingPath(), FilePath(), GetOldUpdatesPath());
1211}
1212
1213// Does a a lot of addel's on very large chunks.
1214TEST(SafeBrowsingDatabase, DatabaseOldLotsofDeletesIO) {
1215  std::vector<ChunksInfo> chunks;
1216  std::vector<SBChunkDelete> deletes;
1217  SBChunkDelete del;
1218  del.is_sub_del = false;
1219  del.list_name = safe_browsing_util::kMalwareList;
1220  del.chunk_del.push_back(ChunkRange(3539, 3579));
1221  deletes.push_back(del);
1222  PerformUpdate(GetOldSafeBrowsingPath(), chunks, deletes);
1223}
1224