Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / safe_browsing / safe_browsing_store_file_unittest.cc
index 28add4a..b028446 100644 (file)
@@ -5,14 +5,39 @@
 #include "chrome/browser/safe_browsing/safe_browsing_store_file.h"
 
 #include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/scoped_file.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/md5.h"
-#include "chrome/browser/safe_browsing/safe_browsing_store_unittest_helper.h"
+#include "base/path_service.h"
+#include "chrome/common/chrome_paths.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
 namespace {
 
+const int kAddChunk1 = 1;
+const int kAddChunk2 = 3;
+const int kAddChunk3 = 5;
+const int kAddChunk4 = 7;
+// Disjoint chunk numbers for subs to flush out typos.
+const int kSubChunk1 = 2;
+const int kSubChunk2 = 4;
+
+const SBFullHash kHash1 = SBFullHashForString("one");
+const SBFullHash kHash2 = SBFullHashForString("two");
+const SBFullHash kHash3 = SBFullHashForString("three");
+const SBFullHash kHash4 = SBFullHashForString("four");
+const SBFullHash kHash5 = SBFullHashForString("five");
+const SBFullHash kHash6 = SBFullHashForString("six");
+
+const SBPrefix kMinSBPrefix = 0u;
+const SBPrefix kMaxSBPrefix = ~kMinSBPrefix;
+
+}  // namespace
+
+namespace safe_browsing {
+
 class SafeBrowsingStoreFileTest : public PlatformTest {
  public:
   virtual void SetUp() {
@@ -41,13 +66,386 @@ class SafeBrowsingStoreFileTest : public PlatformTest {
     corruption_detected_ = true;
   }
 
+  // Populate the store with some testing data.
+  void PopulateStore() {
+    ASSERT_TRUE(store_->BeginUpdate());
+
+    EXPECT_TRUE(store_->BeginChunk());
+    store_->SetAddChunk(kAddChunk1);
+    EXPECT_TRUE(store_->CheckAddChunk(kAddChunk1));
+    EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash1.prefix));
+    EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash2.prefix));
+    EXPECT_TRUE(store_->FinishChunk());
+
+    EXPECT_TRUE(store_->BeginChunk());
+    store_->SetSubChunk(kSubChunk1);
+    EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
+    EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk1, kAddChunk3, kHash3.prefix));
+    EXPECT_TRUE(store_->WriteSubHash(kSubChunk1, kAddChunk3, kHash3));
+    EXPECT_TRUE(store_->FinishChunk());
+
+    EXPECT_TRUE(store_->BeginChunk());
+    store_->SetAddChunk(kAddChunk2);
+    EXPECT_TRUE(store_->CheckAddChunk(kAddChunk2));
+    EXPECT_TRUE(store_->WriteAddHash(kAddChunk2, kHash4));
+    EXPECT_TRUE(store_->FinishChunk());
+
+    // Chunk numbers shouldn't leak over.
+    EXPECT_FALSE(store_->CheckAddChunk(kSubChunk1));
+    EXPECT_FALSE(store_->CheckAddChunk(kAddChunk3));
+    EXPECT_FALSE(store_->CheckSubChunk(kAddChunk1));
+    EXPECT_FALSE(store_->CheckSubChunk(kAddChunk2));
+
+    safe_browsing::PrefixSetBuilder builder;
+    std::vector<SBAddFullHash> add_full_hashes_result;
+
+    EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
+  }
+
+  // Manually read the shard stride info from the file.
+  uint32 ReadStride() {
+    base::ScopedFILE file(base::OpenFile(filename_, "rb"));
+    const long kOffset = 4 * sizeof(uint32);
+    EXPECT_EQ(fseek(file.get(), kOffset, SEEK_SET), 0);
+    uint32 shard_stride = 0;
+    EXPECT_EQ(fread(&shard_stride, sizeof(shard_stride), 1, file.get()), 1U);
+    return shard_stride;
+  }
+
   base::ScopedTempDir temp_dir_;
   base::FilePath filename_;
   scoped_ptr<SafeBrowsingStoreFile> store_;
   bool corruption_detected_;
 };
 
-TEST_STORE(SafeBrowsingStoreFileTest, store_.get(), filename_);
+// Test that the empty store looks empty.
+TEST_F(SafeBrowsingStoreFileTest, Empty) {
+  ASSERT_TRUE(store_->BeginUpdate());
+
+  std::vector<int> chunks;
+  store_->GetAddChunks(&chunks);
+  EXPECT_TRUE(chunks.empty());
+  store_->GetSubChunks(&chunks);
+  EXPECT_TRUE(chunks.empty());
+
+  // Shouldn't see anything, but anything is a big set to test.
+  EXPECT_FALSE(store_->CheckAddChunk(0));
+  EXPECT_FALSE(store_->CheckAddChunk(1));
+  EXPECT_FALSE(store_->CheckAddChunk(-1));
+
+  EXPECT_FALSE(store_->CheckSubChunk(0));
+  EXPECT_FALSE(store_->CheckSubChunk(1));
+  EXPECT_FALSE(store_->CheckSubChunk(-1));
+
+  safe_browsing::PrefixSetBuilder builder;
+  std::vector<SBAddFullHash> add_full_hashes_result;
+
+  EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
+  EXPECT_TRUE(add_full_hashes_result.empty());
+
+  std::vector<SBPrefix> prefixes_result;
+  builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
+  EXPECT_TRUE(prefixes_result.empty());
+}
+
+// Write some prefix and hash data to the store, add more data in another
+// transaction, then verify that the union of all the data is present.
+TEST_F(SafeBrowsingStoreFileTest, BasicStore) {
+  PopulateStore();
+
+  ASSERT_TRUE(store_->BeginUpdate());
+
+  std::vector<int> chunks;
+  store_->GetAddChunks(&chunks);
+  ASSERT_EQ(2U, chunks.size());
+  EXPECT_EQ(kAddChunk1, chunks[0]);
+  EXPECT_EQ(kAddChunk2, chunks[1]);
+
+  store_->GetSubChunks(&chunks);
+  ASSERT_EQ(1U, chunks.size());
+  EXPECT_EQ(kSubChunk1, chunks[0]);
+
+  EXPECT_TRUE(store_->CheckAddChunk(kAddChunk1));
+  EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
+
+  EXPECT_TRUE(store_->BeginChunk());
+  store_->SetAddChunk(kAddChunk3);
+  EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk3, kHash5.prefix));
+  EXPECT_TRUE(store_->FinishChunk());
+
+  // Still has the chunks expected in the next update.
+  {
+    safe_browsing::PrefixSetBuilder builder;
+    std::vector<SBAddFullHash> add_full_hashes_result;
+    EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
+
+    std::vector<SBPrefix> prefixes_result;
+    builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
+    ASSERT_EQ(3U, prefixes_result.size());
+    EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
+    EXPECT_EQ(kHash5.prefix, prefixes_result[1]);
+    EXPECT_EQ(kHash2.prefix, prefixes_result[2]);
+
+    ASSERT_EQ(1U, add_full_hashes_result.size());
+    EXPECT_EQ(kAddChunk2, add_full_hashes_result[0].chunk_id);
+    EXPECT_TRUE(SBFullHashEqual(kHash4, add_full_hashes_result[0].full_hash));
+  }
+}
+
+// Verify that the min and max prefixes are stored and operated on.
+TEST_F(SafeBrowsingStoreFileTest, PrefixMinMax) {
+  PopulateStore();
+
+  ASSERT_TRUE(store_->BeginUpdate());
+
+  EXPECT_TRUE(store_->BeginChunk());
+  store_->SetAddChunk(kAddChunk3);
+  EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk3, kMinSBPrefix));
+  EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk3, kMaxSBPrefix));
+  EXPECT_TRUE(store_->FinishChunk());
+
+  {
+    safe_browsing::PrefixSetBuilder builder;
+    std::vector<SBAddFullHash> add_full_hashes_result;
+    EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
+
+    std::vector<SBPrefix> prefixes_result;
+    builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
+    ASSERT_EQ(4U, prefixes_result.size());
+    EXPECT_EQ(kMinSBPrefix, prefixes_result[0]);
+    EXPECT_EQ(kHash1.prefix, prefixes_result[1]);
+    EXPECT_EQ(kHash2.prefix, prefixes_result[2]);
+    EXPECT_EQ(kMaxSBPrefix, prefixes_result[3]);
+  }
+
+  ASSERT_TRUE(store_->BeginUpdate());
+
+  EXPECT_TRUE(store_->BeginChunk());
+  store_->SetAddChunk(kSubChunk2);
+  EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk2, kAddChunk3, kMinSBPrefix));
+  EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk2, kAddChunk3, kMaxSBPrefix));
+  EXPECT_TRUE(store_->FinishChunk());
+
+  {
+    safe_browsing::PrefixSetBuilder builder;
+    std::vector<SBAddFullHash> add_full_hashes_result;
+    EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
+
+    std::vector<SBPrefix> prefixes_result;
+    builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
+    ASSERT_EQ(2U, prefixes_result.size());
+    EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
+    EXPECT_EQ(kHash2.prefix, prefixes_result[1]);
+  }
+}
+
+// Test that subs knockout adds.
+TEST_F(SafeBrowsingStoreFileTest, SubKnockout) {
+  ASSERT_TRUE(store_->BeginUpdate());
+
+  EXPECT_TRUE(store_->BeginChunk());
+  store_->SetAddChunk(kAddChunk1);
+  EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash1.prefix));
+  EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash2.prefix));
+  EXPECT_TRUE(store_->FinishChunk());
+
+  EXPECT_TRUE(store_->BeginChunk());
+  store_->SetAddChunk(kAddChunk2);
+  EXPECT_TRUE(store_->WriteAddHash(kAddChunk2, kHash4));
+  EXPECT_TRUE(store_->FinishChunk());
+
+  EXPECT_TRUE(store_->BeginChunk());
+  store_->SetSubChunk(kSubChunk1);
+  EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk1, kAddChunk3, kHash3.prefix));
+  EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk1, kAddChunk1, kHash2.prefix));
+  EXPECT_TRUE(store_->FinishChunk());
+
+  {
+    safe_browsing::PrefixSetBuilder builder;
+    std::vector<SBAddFullHash> add_full_hashes_result;
+    EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
+
+    // Knocked out the chunk expected.
+    std::vector<SBPrefix> prefixes_result;
+    builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
+    ASSERT_EQ(1U, prefixes_result.size());
+    EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
+
+    ASSERT_EQ(1U, add_full_hashes_result.size());
+    EXPECT_EQ(kAddChunk2, add_full_hashes_result[0].chunk_id);
+    EXPECT_TRUE(SBFullHashEqual(kHash4, add_full_hashes_result[0].full_hash));
+  }
+
+  ASSERT_TRUE(store_->BeginUpdate());
+
+  // This add should be knocked out by an existing sub.
+  EXPECT_TRUE(store_->BeginChunk());
+  store_->SetAddChunk(kAddChunk3);
+  EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk3, kHash3.prefix));
+  EXPECT_TRUE(store_->FinishChunk());
+
+  {
+    safe_browsing::PrefixSetBuilder builder;
+    std::vector<SBAddFullHash> add_full_hashes_result;
+    EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
+
+    std::vector<SBPrefix> prefixes_result;
+    builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
+    ASSERT_EQ(1U, prefixes_result.size());
+    EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
+
+    ASSERT_EQ(1U, add_full_hashes_result.size());
+    EXPECT_EQ(kAddChunk2, add_full_hashes_result[0].chunk_id);
+    EXPECT_TRUE(SBFullHashEqual(kHash4, add_full_hashes_result[0].full_hash));
+  }
+
+  ASSERT_TRUE(store_->BeginUpdate());
+
+  // But by here the sub should be gone, so it should stick this time.
+  EXPECT_TRUE(store_->BeginChunk());
+  store_->SetAddChunk(kAddChunk3);
+  EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk3, kHash3.prefix));
+  EXPECT_TRUE(store_->FinishChunk());
+
+  {
+    safe_browsing::PrefixSetBuilder builder;
+    std::vector<SBAddFullHash> add_full_hashes_result;
+    EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
+
+    std::vector<SBPrefix> prefixes_result;
+    builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
+    ASSERT_EQ(2U, prefixes_result.size());
+    EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
+    EXPECT_EQ(kHash3.prefix, prefixes_result[1]);
+
+    ASSERT_EQ(1U, add_full_hashes_result.size());
+    EXPECT_EQ(kAddChunk2, add_full_hashes_result[0].chunk_id);
+    EXPECT_TRUE(SBFullHashEqual(kHash4, add_full_hashes_result[0].full_hash));
+  }
+}
+
+// Test that deletes delete the chunk's data.
+TEST_F(SafeBrowsingStoreFileTest, DeleteChunks) {
+  ASSERT_TRUE(store_->BeginUpdate());
+
+  // A prefix chunk which will be deleted.
+  EXPECT_FALSE(store_->CheckAddChunk(kAddChunk1));
+  store_->SetAddChunk(kAddChunk1);
+  EXPECT_TRUE(store_->BeginChunk());
+  EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash1.prefix));
+  EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash2.prefix));
+  EXPECT_TRUE(store_->FinishChunk());
+
+  // A prefix chunk which won't be deleted.
+  EXPECT_FALSE(store_->CheckAddChunk(kAddChunk2));
+  store_->SetAddChunk(kAddChunk2);
+  EXPECT_TRUE(store_->BeginChunk());
+  EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk2, kHash3.prefix));
+  EXPECT_TRUE(store_->FinishChunk());
+
+  // A full-hash chunk which won't be deleted.
+  EXPECT_FALSE(store_->CheckAddChunk(kAddChunk3));
+  store_->SetAddChunk(kAddChunk3);
+  EXPECT_TRUE(store_->BeginChunk());
+  EXPECT_TRUE(store_->WriteAddHash(kAddChunk3, kHash6));
+  EXPECT_TRUE(store_->FinishChunk());
+
+  // A sub chunk to delete.
+  EXPECT_FALSE(store_->CheckSubChunk(kSubChunk1));
+  store_->SetSubChunk(kSubChunk1);
+  EXPECT_TRUE(store_->BeginChunk());
+  EXPECT_TRUE(store_->WriteSubHash(kSubChunk1, kAddChunk3, kHash4));
+  EXPECT_TRUE(store_->FinishChunk());
+
+  // A sub chunk to keep.
+  EXPECT_FALSE(store_->CheckSubChunk(kSubChunk2));
+  store_->SetSubChunk(kSubChunk2);
+  EXPECT_TRUE(store_->BeginChunk());
+  EXPECT_TRUE(store_->WriteSubHash(kSubChunk2, kAddChunk4, kHash5));
+  EXPECT_TRUE(store_->FinishChunk());
+
+  store_->DeleteAddChunk(kAddChunk1);
+  store_->DeleteSubChunk(kSubChunk1);
+
+  // Not actually deleted until finish.
+  EXPECT_TRUE(store_->CheckAddChunk(kAddChunk1));
+  EXPECT_TRUE(store_->CheckAddChunk(kAddChunk2));
+  EXPECT_TRUE(store_->CheckAddChunk(kAddChunk3));
+  EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
+  EXPECT_TRUE(store_->CheckSubChunk(kSubChunk2));
+
+  {
+    safe_browsing::PrefixSetBuilder builder;
+    std::vector<SBAddFullHash> add_full_hashes_result;
+    EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
+
+    std::vector<SBPrefix> prefixes_result;
+    builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
+    ASSERT_EQ(1U, prefixes_result.size());
+    EXPECT_EQ(kHash3.prefix, prefixes_result[0]);
+
+    ASSERT_EQ(1U, add_full_hashes_result.size());
+    EXPECT_EQ(kAddChunk3, add_full_hashes_result[0].chunk_id);
+    EXPECT_TRUE(SBFullHashEqual(kHash6, add_full_hashes_result[0].full_hash));
+  }
+
+  // Expected chunks are there in another update.
+  ASSERT_TRUE(store_->BeginUpdate());
+  EXPECT_FALSE(store_->CheckAddChunk(kAddChunk1));
+  EXPECT_TRUE(store_->CheckAddChunk(kAddChunk2));
+  EXPECT_TRUE(store_->CheckAddChunk(kAddChunk3));
+  EXPECT_FALSE(store_->CheckSubChunk(kSubChunk1));
+  EXPECT_TRUE(store_->CheckSubChunk(kSubChunk2));
+
+  // Delete them, too.
+  store_->DeleteAddChunk(kAddChunk2);
+  store_->DeleteAddChunk(kAddChunk3);
+  store_->DeleteSubChunk(kSubChunk2);
+
+  {
+    safe_browsing::PrefixSetBuilder builder;
+    std::vector<SBAddFullHash> add_full_hashes_result;
+    EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
+
+    std::vector<SBPrefix> prefixes_result;
+    builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
+    EXPECT_TRUE(prefixes_result.empty());
+    EXPECT_TRUE(add_full_hashes_result.empty());
+  }
+
+  // Expect no more chunks.
+  ASSERT_TRUE(store_->BeginUpdate());
+  EXPECT_FALSE(store_->CheckAddChunk(kAddChunk1));
+  EXPECT_FALSE(store_->CheckAddChunk(kAddChunk2));
+  EXPECT_FALSE(store_->CheckAddChunk(kAddChunk3));
+  EXPECT_FALSE(store_->CheckSubChunk(kSubChunk1));
+  EXPECT_FALSE(store_->CheckSubChunk(kSubChunk2));
+
+  {
+    safe_browsing::PrefixSetBuilder builder;
+    std::vector<SBAddFullHash> add_full_hashes_result;
+    EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
+
+    std::vector<SBPrefix> prefixes_result;
+    builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
+    EXPECT_TRUE(prefixes_result.empty());
+    EXPECT_TRUE(add_full_hashes_result.empty());
+  }
+}
+
+// Test that deleting the store deletes the store.
+TEST_F(SafeBrowsingStoreFileTest, Delete) {
+  // Delete should work if the file wasn't there in the first place.
+  EXPECT_FALSE(base::PathExists(filename_));
+  EXPECT_TRUE(store_->Delete());
+
+  // Create a store file.
+  PopulateStore();
+
+  EXPECT_TRUE(base::PathExists(filename_));
+  EXPECT_TRUE(store_->Delete());
+  EXPECT_FALSE(base::PathExists(filename_));
+}
 
 // Test that Delete() deletes the temporary store, if present.
 TEST_F(SafeBrowsingStoreFileTest, DeleteTemp) {
@@ -58,7 +456,7 @@ TEST_F(SafeBrowsingStoreFileTest, DeleteTemp) {
   EXPECT_FALSE(base::PathExists(temp_file));
 
   // Starting a transaction creates a temporary file.
-  EXPECT_TRUE(store_->BeginUpdate());
+  ASSERT_TRUE(store_->BeginUpdate());
   EXPECT_TRUE(base::PathExists(temp_file));
 
   // Pull the rug out from under the existing store, simulating a
@@ -77,24 +475,27 @@ TEST_F(SafeBrowsingStoreFileTest, DeleteTemp) {
 // Test basic corruption-handling.
 TEST_F(SafeBrowsingStoreFileTest, DetectsCorruption) {
   // Load a store with some data.
-  SafeBrowsingStoreTestStorePrefix(store_.get());
+  PopulateStore();
 
   // Can successfully open and read the store.
-  std::vector<SBAddFullHash> pending_adds;
-  SBAddPrefixes orig_prefixes;
-  std::vector<SBAddFullHash> orig_hashes;
-  EXPECT_TRUE(store_->BeginUpdate());
-  EXPECT_TRUE(store_->FinishUpdate(pending_adds, &orig_prefixes, &orig_hashes));
-  EXPECT_GT(orig_prefixes.size(), 0U);
-  EXPECT_GT(orig_hashes.size(), 0U);
-  EXPECT_FALSE(corruption_detected_);
+  {
+    std::vector<SBPrefix> orig_prefixes;
+    std::vector<SBAddFullHash> orig_hashes;
+    safe_browsing::PrefixSetBuilder builder;
+    ASSERT_TRUE(store_->BeginUpdate());
+    EXPECT_TRUE(store_->FinishUpdate(&builder, &orig_hashes));
+    builder.GetPrefixSetNoHashes()->GetPrefixes(&orig_prefixes);
+    EXPECT_GT(orig_prefixes.size(), 0U);
+    EXPECT_GT(orig_hashes.size(), 0U);
+    EXPECT_FALSE(corruption_detected_);
+  }
 
   // Corrupt the store.
-  file_util::ScopedFILE file(base::OpenFile(filename_, "rb+"));
+  base::ScopedFILE file(base::OpenFile(filename_, "rb+"));
   const long kOffset = 60;
   EXPECT_EQ(fseek(file.get(), kOffset, SEEK_SET), 0);
-  const int32 kZero = 0;
-  int32 previous = kZero;
+  const uint32 kZero = 0;
+  uint32 previous = kZero;
   EXPECT_EQ(fread(&previous, sizeof(previous), 1, file.get()), 1U);
   EXPECT_NE(previous, kZero);
   EXPECT_EQ(fseek(file.get(), kOffset, SEEK_SET), 0);
@@ -102,14 +503,14 @@ TEST_F(SafeBrowsingStoreFileTest, DetectsCorruption) {
   file.reset();
 
   // Update fails and corruption callback is called.
-  SBAddPrefixes add_prefixes;
   std::vector<SBAddFullHash> add_hashes;
   corruption_detected_ = false;
-  EXPECT_TRUE(store_->BeginUpdate());
-  EXPECT_FALSE(store_->FinishUpdate(pending_adds, &add_prefixes, &add_hashes));
-  EXPECT_TRUE(corruption_detected_);
-  EXPECT_EQ(add_prefixes.size(), 0U);
-  EXPECT_EQ(add_hashes.size(), 0U);
+  {
+    safe_browsing::PrefixSetBuilder builder;
+    ASSERT_TRUE(store_->BeginUpdate());
+    EXPECT_FALSE(store_->FinishUpdate(&builder, &add_hashes));
+    EXPECT_TRUE(corruption_detected_);
+  }
 
   // Make it look like there is a lot of add-chunks-seen data.
   const long kAddChunkCountOffset = 2 * sizeof(int32);
@@ -136,7 +537,7 @@ TEST_F(SafeBrowsingStoreFileTest, CheckValidity) {
 
   // A store with some data is valid.
   EXPECT_FALSE(base::PathExists(filename_));
-  SafeBrowsingStoreTestStorePrefix(store_.get());
+  PopulateStore();
   EXPECT_TRUE(base::PathExists(filename_));
   ASSERT_TRUE(store_->BeginUpdate());
   EXPECT_FALSE(corruption_detected_);
@@ -145,18 +546,36 @@ TEST_F(SafeBrowsingStoreFileTest, CheckValidity) {
   EXPECT_TRUE(store_->CancelUpdate());
 }
 
-// Corrupt the payload.
-TEST_F(SafeBrowsingStoreFileTest, CheckValidityPayload) {
-  SafeBrowsingStoreTestStorePrefix(store_.get());
+// Corrupt the header.
+TEST_F(SafeBrowsingStoreFileTest, CheckValidityHeader) {
+  PopulateStore();
   EXPECT_TRUE(base::PathExists(filename_));
 
-  // 37 is the most random prime number.  It's also past the header,
-  // as corrupting the header would fail BeginUpdate() in which case
-  // CheckValidity() cannot be called.
+  // 37 is the most random prime number.  It's also past the initial header
+  // struct, somewhere in the chunk list.
   const size_t kOffset = 37;
 
   {
-    file_util::ScopedFILE file(base::OpenFile(filename_, "rb+"));
+    base::ScopedFILE file(base::OpenFile(filename_, "rb+"));
+    EXPECT_EQ(0, fseek(file.get(), kOffset, SEEK_SET));
+    EXPECT_GE(fputs("hello", file.get()), 0);
+  }
+  ASSERT_FALSE(store_->BeginUpdate());
+  EXPECT_TRUE(corruption_detected_);
+}
+
+// Corrupt the prefix payload.
+TEST_F(SafeBrowsingStoreFileTest, CheckValidityPayload) {
+  PopulateStore();
+  EXPECT_TRUE(base::PathExists(filename_));
+
+  // 137 is the second most random prime number.  It's also past the header and
+  // chunk-id area.  Corrupting the header would fail BeginUpdate() in which
+  // case CheckValidity() cannot be called.
+  const size_t kOffset = 137;
+
+  {
+    base::ScopedFILE file(base::OpenFile(filename_, "rb+"));
     EXPECT_EQ(0, fseek(file.get(), kOffset, SEEK_SET));
     EXPECT_GE(fputs("hello", file.get()), 0);
   }
@@ -169,14 +588,14 @@ TEST_F(SafeBrowsingStoreFileTest, CheckValidityPayload) {
 
 // Corrupt the checksum.
 TEST_F(SafeBrowsingStoreFileTest, CheckValidityChecksum) {
-  SafeBrowsingStoreTestStorePrefix(store_.get());
+  PopulateStore();
   EXPECT_TRUE(base::PathExists(filename_));
 
   // An offset from the end of the file which is in the checksum.
   const int kOffset = -static_cast<int>(sizeof(base::MD5Digest));
 
   {
-    file_util::ScopedFILE file(base::OpenFile(filename_, "rb+"));
+    base::ScopedFILE file(base::OpenFile(filename_, "rb+"));
     EXPECT_EQ(0, fseek(file.get(), kOffset, SEEK_END));
     EXPECT_GE(fputs("hello", file.get()), 0);
   }
@@ -187,4 +606,353 @@ TEST_F(SafeBrowsingStoreFileTest, CheckValidityChecksum) {
   EXPECT_TRUE(store_->CancelUpdate());
 }
 
-}  // namespace
+TEST_F(SafeBrowsingStoreFileTest, GetAddPrefixesAndHashes) {
+  ASSERT_TRUE(store_->BeginUpdate());
+
+  EXPECT_TRUE(store_->BeginChunk());
+  store_->SetAddChunk(kAddChunk1);
+  EXPECT_TRUE(store_->CheckAddChunk(kAddChunk1));
+  EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash1.prefix));
+  EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash2.prefix));
+  EXPECT_TRUE(store_->FinishChunk());
+
+  EXPECT_TRUE(store_->BeginChunk());
+  store_->SetAddChunk(kAddChunk2);
+  EXPECT_TRUE(store_->CheckAddChunk(kAddChunk2));
+  EXPECT_TRUE(store_->WriteAddHash(kAddChunk2, kHash4));
+  EXPECT_TRUE(store_->FinishChunk());
+
+  store_->SetSubChunk(kSubChunk1);
+  EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
+  EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk1, kAddChunk3, kHash3.prefix));
+  EXPECT_TRUE(store_->WriteSubHash(kSubChunk1, kAddChunk3, kHash3));
+  EXPECT_TRUE(store_->FinishChunk());
+
+  // Chunk numbers shouldn't leak over.
+  EXPECT_FALSE(store_->CheckAddChunk(kSubChunk1));
+  EXPECT_FALSE(store_->CheckAddChunk(kAddChunk3));
+  EXPECT_FALSE(store_->CheckSubChunk(kAddChunk1));
+
+  std::vector<int> chunks;
+  store_->GetAddChunks(&chunks);
+  ASSERT_EQ(2U, chunks.size());
+  EXPECT_EQ(kAddChunk1, chunks[0]);
+  EXPECT_EQ(kAddChunk2, chunks[1]);
+
+  store_->GetSubChunks(&chunks);
+  ASSERT_EQ(1U, chunks.size());
+  EXPECT_EQ(kSubChunk1, chunks[0]);
+
+  safe_browsing::PrefixSetBuilder builder;
+  std::vector<SBAddFullHash> add_full_hashes_result;
+  EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
+
+  SBAddPrefixes add_prefixes;
+  EXPECT_TRUE(store_->GetAddPrefixes(&add_prefixes));
+  ASSERT_EQ(2U, add_prefixes.size());
+  EXPECT_EQ(kAddChunk1, add_prefixes[0].chunk_id);
+  EXPECT_EQ(kHash1.prefix, add_prefixes[0].prefix);
+  EXPECT_EQ(kAddChunk1, add_prefixes[1].chunk_id);
+  EXPECT_EQ(kHash2.prefix, add_prefixes[1].prefix);
+
+  std::vector<SBAddFullHash> add_hashes;
+  EXPECT_TRUE(store_->GetAddFullHashes(&add_hashes));
+  ASSERT_EQ(1U, add_hashes.size());
+  EXPECT_EQ(kAddChunk2, add_hashes[0].chunk_id);
+  EXPECT_TRUE(SBFullHashEqual(kHash4, add_hashes[0].full_hash));
+}
+
+// Test that the database handles resharding correctly, both when growing and
+// which shrinking.
+TEST_F(SafeBrowsingStoreFileTest, Resharding) {
+  // Loop through multiple stride boundaries (1<<32, 1<<31, 1<<30, 1<<29).
+  const uint32 kTargetStride = 1 << 29;
+
+  // Each chunk will require 8 bytes per prefix, plus 4 bytes for chunk
+  // information.  It should be less than |kTargetFootprint| in the
+  // implementation, but high enough to keep the number of rewrites modest (to
+  // keep the test fast).
+  const size_t kPrefixesPerChunk = 10000;
+
+  uint32 shard_stride = 0;
+  int chunk_id = 1;
+
+  // Add a series of chunks, tracking that the stride size changes in a
+  // direction appropriate to increasing file size.
+  do {
+    ASSERT_TRUE(store_->BeginUpdate());
+
+    EXPECT_TRUE(store_->BeginChunk());
+    store_->SetAddChunk(chunk_id);
+    EXPECT_TRUE(store_->CheckAddChunk(chunk_id));
+    for (size_t i = 0; i < kPrefixesPerChunk; ++i) {
+      EXPECT_TRUE(store_->WriteAddPrefix(chunk_id, static_cast<SBPrefix>(i)));
+    }
+    EXPECT_TRUE(store_->FinishChunk());
+
+    safe_browsing::PrefixSetBuilder builder;
+    std::vector<SBAddFullHash> add_full_hashes_result;
+    EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
+
+    SBAddPrefixes add_prefixes;
+    EXPECT_TRUE(store_->GetAddPrefixes(&add_prefixes));
+    ASSERT_EQ(chunk_id * kPrefixesPerChunk, add_prefixes.size());
+
+    // New stride should be the same, or shifted one right.
+    const uint32 new_shard_stride = ReadStride();
+    EXPECT_TRUE((new_shard_stride == shard_stride) ||
+                ((new_shard_stride << 1) == shard_stride));
+    shard_stride = new_shard_stride;
+    ++chunk_id;
+  } while (!shard_stride || shard_stride > kTargetStride);
+
+  // Guard against writing too many chunks.  If this gets too big, adjust
+  // |kPrefixesPerChunk|.
+  EXPECT_LT(chunk_id, 20);
+
+  // Remove each chunk and check that the stride goes back to 0.
+  while (--chunk_id) {
+    ASSERT_TRUE(store_->BeginUpdate());
+    EXPECT_TRUE(store_->CheckAddChunk(chunk_id));
+    EXPECT_FALSE(store_->CheckAddChunk(chunk_id + 1));
+    store_->DeleteAddChunk(chunk_id);
+
+    safe_browsing::PrefixSetBuilder builder;
+    std::vector<SBAddFullHash> add_full_hashes_result;
+    EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
+
+    // New stride should be the same, or shifted one left.
+    const uint32 new_shard_stride = ReadStride();
+    EXPECT_TRUE((new_shard_stride == shard_stride) ||
+                (new_shard_stride == (shard_stride << 1)));
+    shard_stride = new_shard_stride;
+  }
+  EXPECT_EQ(0u, shard_stride);
+}
+
+// Test that a golden v7 file can be read by the current code.  All platforms
+// generating v7 files are little-endian, so there is no point to testing this
+// transition if/when a big-endian port is added.
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+TEST_F(SafeBrowsingStoreFileTest, Version7) {
+  store_.reset();
+
+  // Copy the golden file into temporary storage.  The golden file contains:
+  // - Add chunk kAddChunk1 containing kHash1.prefix and kHash2.
+  // - Sub chunk kSubChunk1 containing kHash3.
+  const char kBasename[] = "FileStoreVersion7";
+  base::FilePath golden_path;
+  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &golden_path));
+  golden_path = golden_path.AppendASCII("SafeBrowsing");
+  golden_path = golden_path.AppendASCII(kBasename);
+  ASSERT_TRUE(base::CopyFile(golden_path, filename_));
+
+  // Reset the store to make sure it re-reads the file.
+  store_.reset(new SafeBrowsingStoreFile());
+  store_->Init(filename_,
+               base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected,
+                          base::Unretained(this)));
+
+  // Check that the expected prefixes and hashes are in place.
+  SBAddPrefixes add_prefixes;
+  EXPECT_TRUE(store_->GetAddPrefixes(&add_prefixes));
+  ASSERT_EQ(2U, add_prefixes.size());
+  EXPECT_EQ(kAddChunk1, add_prefixes[0].chunk_id);
+  EXPECT_EQ(kHash1.prefix, add_prefixes[0].prefix);
+  EXPECT_EQ(kAddChunk1, add_prefixes[1].chunk_id);
+  EXPECT_EQ(kHash2.prefix, add_prefixes[1].prefix);
+
+  std::vector<SBAddFullHash> add_hashes;
+  EXPECT_TRUE(store_->GetAddFullHashes(&add_hashes));
+  ASSERT_EQ(1U, add_hashes.size());
+  EXPECT_EQ(kAddChunk1, add_hashes[0].chunk_id);
+  EXPECT_TRUE(SBFullHashEqual(kHash2, add_hashes[0].full_hash));
+
+  // Attempt an update to make sure things work end-to-end.
+  EXPECT_TRUE(store_->BeginUpdate());
+
+  // Still has the chunks expected in the next update.
+  std::vector<int> chunks;
+  store_->GetAddChunks(&chunks);
+  ASSERT_EQ(1U, chunks.size());
+  EXPECT_EQ(kAddChunk1, chunks[0]);
+
+  store_->GetSubChunks(&chunks);
+  ASSERT_EQ(1U, chunks.size());
+  EXPECT_EQ(kSubChunk1, chunks[0]);
+
+  EXPECT_TRUE(store_->CheckAddChunk(kAddChunk1));
+  EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
+
+  // Sub chunk kAddChunk1 hash kHash2.
+  store_->SetSubChunk(kSubChunk2);
+  EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
+  EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk1, kAddChunk1, kHash2.prefix));
+  EXPECT_TRUE(store_->WriteSubHash(kSubChunk1, kAddChunk1, kHash2));
+  EXPECT_TRUE(store_->FinishChunk());
+
+  {
+    safe_browsing::PrefixSetBuilder builder;
+    std::vector<SBAddFullHash> add_full_hashes_result;
+    EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
+
+    // The sub'ed prefix and hash are gone.
+    std::vector<SBPrefix> prefixes_result;
+    builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
+    ASSERT_EQ(1U, prefixes_result.size());
+    EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
+    EXPECT_TRUE(add_full_hashes_result.empty());
+  }
+}
+#endif
+
+// Test that a golden v8 file can be read by the current code.  All platforms
+// generating v8 files are little-endian, so there is no point to testing this
+// transition if/when a big-endian port is added.
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+TEST_F(SafeBrowsingStoreFileTest, Version8) {
+  store_.reset();
+
+  // Copy the golden file into temporary storage.  The golden file contains:
+  // - Add chunk kAddChunk1 containing kHash1.prefix and kHash2.
+  // - Sub chunk kSubChunk1 containing kHash3.
+  const char kBasename[] = "FileStoreVersion8";
+  base::FilePath golden_path;
+  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &golden_path));
+  golden_path = golden_path.AppendASCII("SafeBrowsing");
+  golden_path = golden_path.AppendASCII(kBasename);
+  ASSERT_TRUE(base::CopyFile(golden_path, filename_));
+
+  // Reset the store to make sure it re-reads the file.
+  store_.reset(new SafeBrowsingStoreFile());
+  store_->Init(filename_,
+               base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected,
+                          base::Unretained(this)));
+
+  // Check that the expected prefixes and hashes are in place.
+  SBAddPrefixes add_prefixes;
+  EXPECT_TRUE(store_->GetAddPrefixes(&add_prefixes));
+  ASSERT_EQ(2U, add_prefixes.size());
+  EXPECT_EQ(kAddChunk1, add_prefixes[0].chunk_id);
+  EXPECT_EQ(kHash1.prefix, add_prefixes[0].prefix);
+  EXPECT_EQ(kAddChunk1, add_prefixes[1].chunk_id);
+  EXPECT_EQ(kHash2.prefix, add_prefixes[1].prefix);
+
+  std::vector<SBAddFullHash> add_hashes;
+  EXPECT_TRUE(store_->GetAddFullHashes(&add_hashes));
+  ASSERT_EQ(1U, add_hashes.size());
+  EXPECT_EQ(kAddChunk1, add_hashes[0].chunk_id);
+  EXPECT_TRUE(SBFullHashEqual(kHash2, add_hashes[0].full_hash));
+
+  // Attempt an update to make sure things work end-to-end.
+  EXPECT_TRUE(store_->BeginUpdate());
+
+  // Still has the chunks expected in the next update.
+  std::vector<int> chunks;
+  store_->GetAddChunks(&chunks);
+  ASSERT_EQ(1U, chunks.size());
+  EXPECT_EQ(kAddChunk1, chunks[0]);
+
+  store_->GetSubChunks(&chunks);
+  ASSERT_EQ(1U, chunks.size());
+  EXPECT_EQ(kSubChunk1, chunks[0]);
+
+  EXPECT_TRUE(store_->CheckAddChunk(kAddChunk1));
+  EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
+
+  // Sub chunk kAddChunk1 hash kHash2.
+  store_->SetSubChunk(kSubChunk2);
+  EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
+  EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk1, kAddChunk1, kHash2.prefix));
+  EXPECT_TRUE(store_->WriteSubHash(kSubChunk1, kAddChunk1, kHash2));
+  EXPECT_TRUE(store_->FinishChunk());
+
+  {
+    safe_browsing::PrefixSetBuilder builder;
+    std::vector<SBAddFullHash> add_full_hashes_result;
+    EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
+
+    // The sub'ed prefix and hash are gone.
+    std::vector<SBPrefix> prefixes_result;
+    builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
+    ASSERT_EQ(1U, prefixes_result.size());
+    EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
+    EXPECT_TRUE(add_full_hashes_result.empty());
+  }
+}
+#endif
+
+// Test that when the v8 golden file is updated, the add prefix injected from
+// the full hash is removed.  All platforms generating v8 files are
+// little-endian, so there is no point to testing this transition if/when a
+// big-endian port is added.
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+TEST_F(SafeBrowsingStoreFileTest, KnockoutPrefixVolunteers) {
+  store_.reset();
+
+  // Copy the golden file into temporary storage.  The golden file contains:
+  // - Add chunk kAddChunk1 containing kHash1.prefix and kHash2.
+  // - Sub chunk kSubChunk1 containing kHash3.
+  const char kBasename[] = "FileStoreVersion8";
+  base::FilePath golden_path;
+  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &golden_path));
+  golden_path = golden_path.AppendASCII("SafeBrowsing");
+  golden_path = golden_path.AppendASCII(kBasename);
+  ASSERT_TRUE(base::CopyFile(golden_path, filename_));
+
+  // Reset the store to make sure it re-reads the file.
+  store_.reset(new SafeBrowsingStoreFile());
+  store_->Init(filename_,
+               base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected,
+                          base::Unretained(this)));
+
+  // Check that the expected prefixes and hashes are in place.
+  {
+    SBAddPrefixes add_prefixes;
+    EXPECT_TRUE(store_->GetAddPrefixes(&add_prefixes));
+    ASSERT_EQ(2U, add_prefixes.size());
+    EXPECT_EQ(kAddChunk1, add_prefixes[0].chunk_id);
+    EXPECT_EQ(kHash1.prefix, add_prefixes[0].prefix);
+    EXPECT_EQ(kAddChunk1, add_prefixes[1].chunk_id);
+    EXPECT_EQ(kHash2.prefix, add_prefixes[1].prefix);
+
+    std::vector<SBAddFullHash> add_hashes;
+    EXPECT_TRUE(store_->GetAddFullHashes(&add_hashes));
+    ASSERT_EQ(1U, add_hashes.size());
+    EXPECT_EQ(kAddChunk1, add_hashes[0].chunk_id);
+    EXPECT_TRUE(SBFullHashEqual(kHash2, add_hashes[0].full_hash));
+  }
+
+  // Update the store.
+  {
+    EXPECT_TRUE(store_->BeginUpdate());
+
+    safe_browsing::PrefixSetBuilder builder;
+    std::vector<SBAddFullHash> add_full_hashes_result;
+    ASSERT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
+  }
+
+  // Reset the store to make sure it re-reads the file.
+  store_.reset(new SafeBrowsingStoreFile());
+  store_->Init(filename_,
+               base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected,
+                          base::Unretained(this)));
+
+  // |kHash2.prefix| should have dropped.
+  {
+    SBAddPrefixes add_prefixes;
+    EXPECT_TRUE(store_->GetAddPrefixes(&add_prefixes));
+    ASSERT_EQ(1U, add_prefixes.size());
+    EXPECT_EQ(kAddChunk1, add_prefixes[0].chunk_id);
+    EXPECT_EQ(kHash1.prefix, add_prefixes[0].prefix);
+
+    std::vector<SBAddFullHash> add_hashes;
+    EXPECT_TRUE(store_->GetAddFullHashes(&add_hashes));
+    ASSERT_EQ(1U, add_hashes.size());
+    EXPECT_EQ(kAddChunk1, add_hashes[0].chunk_id);
+    EXPECT_TRUE(SBFullHashEqual(kHash2, add_hashes[0].full_hash));
+  }
+}
+#endif
+
+}  // namespace safe_browsing