Update broken references to image assets
[chromium-blink-merge.git] / chrome / browser / safe_browsing / safe_browsing_store_file_unittest.cc
bloba71dc0cc256bcc5d1a99de9d1d5210f266282d70
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.
5 #include "chrome/browser/safe_browsing/safe_browsing_store_file.h"
7 #include "base/bind.h"
8 #include "base/files/file_util.h"
9 #include "base/files/scoped_file.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/md5.h"
12 #include "base/path_service.h"
13 #include "base/test/test_simple_task_runner.h"
14 #include "chrome/common/chrome_paths.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "testing/platform_test.h"
18 namespace {
20 const int kAddChunk1 = 1;
21 const int kAddChunk2 = 3;
22 const int kAddChunk3 = 5;
23 const int kAddChunk4 = 7;
24 // Disjoint chunk numbers for subs to flush out typos.
25 const int kSubChunk1 = 2;
26 const int kSubChunk2 = 4;
28 const SBFullHash kHash1 = SBFullHashForString("one");
29 const SBFullHash kHash2 = SBFullHashForString("two");
30 const SBFullHash kHash3 = SBFullHashForString("three");
31 const SBFullHash kHash4 = SBFullHashForString("four");
32 const SBFullHash kHash5 = SBFullHashForString("five");
33 const SBFullHash kHash6 = SBFullHashForString("six");
35 const SBPrefix kMinSBPrefix = 0u;
36 const SBPrefix kMaxSBPrefix = ~kMinSBPrefix;
38 } // namespace
40 namespace safe_browsing {
42 class SafeBrowsingStoreFileTest : public PlatformTest {
43 public:
44 SafeBrowsingStoreFileTest()
45 : task_runner_(new base::TestSimpleTaskRunner),
46 corruption_detected_(false) {}
48 void SetUp() override {
49 PlatformTest::SetUp();
51 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
53 filename_ = temp_dir_.path();
54 filename_ = filename_.AppendASCII("SafeBrowsingTestStore");
56 store_.reset(new SafeBrowsingStoreFile(task_runner_));
57 store_->Init(filename_,
58 base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected,
59 base::Unretained(this)));
61 void TearDown() override {
62 if (store_.get())
63 store_->Delete();
64 store_.reset();
66 PlatformTest::TearDown();
69 void OnCorruptionDetected() {
70 corruption_detected_ = true;
73 // Populate the store with some testing data.
74 void PopulateStore() {
75 ASSERT_TRUE(store_->BeginUpdate());
77 EXPECT_TRUE(store_->BeginChunk());
78 store_->SetAddChunk(kAddChunk1);
79 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk1));
80 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash1.prefix));
81 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash2.prefix));
82 EXPECT_TRUE(store_->FinishChunk());
84 EXPECT_TRUE(store_->BeginChunk());
85 store_->SetSubChunk(kSubChunk1);
86 EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
87 EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk1, kAddChunk3, kHash3.prefix));
88 EXPECT_TRUE(store_->WriteSubHash(kSubChunk1, kAddChunk3, kHash3));
89 EXPECT_TRUE(store_->FinishChunk());
91 EXPECT_TRUE(store_->BeginChunk());
92 store_->SetAddChunk(kAddChunk2);
93 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk2));
94 EXPECT_TRUE(store_->WriteAddHash(kAddChunk2, kHash4));
95 EXPECT_TRUE(store_->FinishChunk());
97 // Chunk numbers shouldn't leak over.
98 EXPECT_FALSE(store_->CheckAddChunk(kSubChunk1));
99 EXPECT_FALSE(store_->CheckAddChunk(kAddChunk3));
100 EXPECT_FALSE(store_->CheckSubChunk(kAddChunk1));
101 EXPECT_FALSE(store_->CheckSubChunk(kAddChunk2));
103 safe_browsing::PrefixSetBuilder builder;
104 std::vector<SBAddFullHash> add_full_hashes_result;
106 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
109 // Manually read the shard stride info from the file.
110 uint32 ReadStride() {
111 base::ScopedFILE file(base::OpenFile(filename_, "rb"));
112 const long kOffset = 4 * sizeof(uint32);
113 EXPECT_EQ(fseek(file.get(), kOffset, SEEK_SET), 0);
114 uint32 shard_stride = 0;
115 EXPECT_EQ(fread(&shard_stride, sizeof(shard_stride), 1, file.get()), 1U);
116 return shard_stride;
119 scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
120 base::ScopedTempDir temp_dir_;
121 base::FilePath filename_;
122 scoped_ptr<SafeBrowsingStoreFile> store_;
123 bool corruption_detected_;
126 // Test that the empty store looks empty.
127 TEST_F(SafeBrowsingStoreFileTest, Empty) {
128 ASSERT_TRUE(store_->BeginUpdate());
130 std::vector<int> chunks;
131 store_->GetAddChunks(&chunks);
132 EXPECT_TRUE(chunks.empty());
133 store_->GetSubChunks(&chunks);
134 EXPECT_TRUE(chunks.empty());
136 // Shouldn't see anything, but anything is a big set to test.
137 EXPECT_FALSE(store_->CheckAddChunk(0));
138 EXPECT_FALSE(store_->CheckAddChunk(1));
139 EXPECT_FALSE(store_->CheckAddChunk(-1));
141 EXPECT_FALSE(store_->CheckSubChunk(0));
142 EXPECT_FALSE(store_->CheckSubChunk(1));
143 EXPECT_FALSE(store_->CheckSubChunk(-1));
145 safe_browsing::PrefixSetBuilder builder;
146 std::vector<SBAddFullHash> add_full_hashes_result;
148 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
149 EXPECT_TRUE(add_full_hashes_result.empty());
151 std::vector<SBPrefix> prefixes_result;
152 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
153 EXPECT_TRUE(prefixes_result.empty());
156 // Write some prefix and hash data to the store, add more data in another
157 // transaction, then verify that the union of all the data is present.
158 TEST_F(SafeBrowsingStoreFileTest, BasicStore) {
159 PopulateStore();
161 ASSERT_TRUE(store_->BeginUpdate());
163 std::vector<int> chunks;
164 store_->GetAddChunks(&chunks);
165 ASSERT_EQ(2U, chunks.size());
166 EXPECT_EQ(kAddChunk1, chunks[0]);
167 EXPECT_EQ(kAddChunk2, chunks[1]);
169 store_->GetSubChunks(&chunks);
170 ASSERT_EQ(1U, chunks.size());
171 EXPECT_EQ(kSubChunk1, chunks[0]);
173 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk1));
174 EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
176 EXPECT_TRUE(store_->BeginChunk());
177 store_->SetAddChunk(kAddChunk3);
178 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk3, kHash5.prefix));
179 EXPECT_TRUE(store_->FinishChunk());
181 // Still has the chunks expected in the next update.
183 safe_browsing::PrefixSetBuilder builder;
184 std::vector<SBAddFullHash> add_full_hashes_result;
185 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
187 std::vector<SBPrefix> prefixes_result;
188 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
189 ASSERT_EQ(3U, prefixes_result.size());
190 EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
191 EXPECT_EQ(kHash5.prefix, prefixes_result[1]);
192 EXPECT_EQ(kHash2.prefix, prefixes_result[2]);
194 ASSERT_EQ(1U, add_full_hashes_result.size());
195 EXPECT_EQ(kAddChunk2, add_full_hashes_result[0].chunk_id);
196 EXPECT_TRUE(SBFullHashEqual(kHash4, add_full_hashes_result[0].full_hash));
200 // Verify that the min and max prefixes are stored and operated on.
201 TEST_F(SafeBrowsingStoreFileTest, PrefixMinMax) {
202 PopulateStore();
204 ASSERT_TRUE(store_->BeginUpdate());
206 EXPECT_TRUE(store_->BeginChunk());
207 store_->SetAddChunk(kAddChunk3);
208 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk3, kMinSBPrefix));
209 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk3, kMaxSBPrefix));
210 EXPECT_TRUE(store_->FinishChunk());
213 safe_browsing::PrefixSetBuilder builder;
214 std::vector<SBAddFullHash> add_full_hashes_result;
215 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
217 std::vector<SBPrefix> prefixes_result;
218 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
219 ASSERT_EQ(4U, prefixes_result.size());
220 EXPECT_EQ(kMinSBPrefix, prefixes_result[0]);
221 EXPECT_EQ(kHash1.prefix, prefixes_result[1]);
222 EXPECT_EQ(kHash2.prefix, prefixes_result[2]);
223 EXPECT_EQ(kMaxSBPrefix, prefixes_result[3]);
226 ASSERT_TRUE(store_->BeginUpdate());
228 EXPECT_TRUE(store_->BeginChunk());
229 store_->SetAddChunk(kSubChunk2);
230 EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk2, kAddChunk3, kMinSBPrefix));
231 EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk2, kAddChunk3, kMaxSBPrefix));
232 EXPECT_TRUE(store_->FinishChunk());
235 safe_browsing::PrefixSetBuilder builder;
236 std::vector<SBAddFullHash> add_full_hashes_result;
237 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
239 std::vector<SBPrefix> prefixes_result;
240 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
241 ASSERT_EQ(2U, prefixes_result.size());
242 EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
243 EXPECT_EQ(kHash2.prefix, prefixes_result[1]);
247 // Test that subs knockout adds.
248 TEST_F(SafeBrowsingStoreFileTest, SubKnockout) {
249 ASSERT_TRUE(store_->BeginUpdate());
251 EXPECT_TRUE(store_->BeginChunk());
252 store_->SetAddChunk(kAddChunk1);
253 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash1.prefix));
254 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash2.prefix));
255 EXPECT_TRUE(store_->FinishChunk());
257 EXPECT_TRUE(store_->BeginChunk());
258 store_->SetAddChunk(kAddChunk2);
259 EXPECT_TRUE(store_->WriteAddHash(kAddChunk2, kHash4));
260 EXPECT_TRUE(store_->FinishChunk());
262 EXPECT_TRUE(store_->BeginChunk());
263 store_->SetSubChunk(kSubChunk1);
264 EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk1, kAddChunk3, kHash3.prefix));
265 EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk1, kAddChunk1, kHash2.prefix));
266 EXPECT_TRUE(store_->FinishChunk());
269 safe_browsing::PrefixSetBuilder builder;
270 std::vector<SBAddFullHash> add_full_hashes_result;
271 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
273 // Knocked out the chunk expected.
274 std::vector<SBPrefix> prefixes_result;
275 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
276 ASSERT_EQ(1U, prefixes_result.size());
277 EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
279 ASSERT_EQ(1U, add_full_hashes_result.size());
280 EXPECT_EQ(kAddChunk2, add_full_hashes_result[0].chunk_id);
281 EXPECT_TRUE(SBFullHashEqual(kHash4, add_full_hashes_result[0].full_hash));
284 ASSERT_TRUE(store_->BeginUpdate());
286 // This add should be knocked out by an existing sub.
287 EXPECT_TRUE(store_->BeginChunk());
288 store_->SetAddChunk(kAddChunk3);
289 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk3, kHash3.prefix));
290 EXPECT_TRUE(store_->FinishChunk());
293 safe_browsing::PrefixSetBuilder builder;
294 std::vector<SBAddFullHash> add_full_hashes_result;
295 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
297 std::vector<SBPrefix> prefixes_result;
298 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
299 ASSERT_EQ(1U, prefixes_result.size());
300 EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
302 ASSERT_EQ(1U, add_full_hashes_result.size());
303 EXPECT_EQ(kAddChunk2, add_full_hashes_result[0].chunk_id);
304 EXPECT_TRUE(SBFullHashEqual(kHash4, add_full_hashes_result[0].full_hash));
307 ASSERT_TRUE(store_->BeginUpdate());
309 // But by here the sub should be gone, so it should stick this time.
310 EXPECT_TRUE(store_->BeginChunk());
311 store_->SetAddChunk(kAddChunk3);
312 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk3, kHash3.prefix));
313 EXPECT_TRUE(store_->FinishChunk());
316 safe_browsing::PrefixSetBuilder builder;
317 std::vector<SBAddFullHash> add_full_hashes_result;
318 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
320 std::vector<SBPrefix> prefixes_result;
321 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
322 ASSERT_EQ(2U, prefixes_result.size());
323 EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
324 EXPECT_EQ(kHash3.prefix, prefixes_result[1]);
326 ASSERT_EQ(1U, add_full_hashes_result.size());
327 EXPECT_EQ(kAddChunk2, add_full_hashes_result[0].chunk_id);
328 EXPECT_TRUE(SBFullHashEqual(kHash4, add_full_hashes_result[0].full_hash));
332 // Test that deletes delete the chunk's data.
333 TEST_F(SafeBrowsingStoreFileTest, DeleteChunks) {
334 ASSERT_TRUE(store_->BeginUpdate());
336 // A prefix chunk which will be deleted.
337 EXPECT_FALSE(store_->CheckAddChunk(kAddChunk1));
338 store_->SetAddChunk(kAddChunk1);
339 EXPECT_TRUE(store_->BeginChunk());
340 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash1.prefix));
341 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash2.prefix));
342 EXPECT_TRUE(store_->FinishChunk());
344 // A prefix chunk which won't be deleted.
345 EXPECT_FALSE(store_->CheckAddChunk(kAddChunk2));
346 store_->SetAddChunk(kAddChunk2);
347 EXPECT_TRUE(store_->BeginChunk());
348 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk2, kHash3.prefix));
349 EXPECT_TRUE(store_->FinishChunk());
351 // A full-hash chunk which won't be deleted.
352 EXPECT_FALSE(store_->CheckAddChunk(kAddChunk3));
353 store_->SetAddChunk(kAddChunk3);
354 EXPECT_TRUE(store_->BeginChunk());
355 EXPECT_TRUE(store_->WriteAddHash(kAddChunk3, kHash6));
356 EXPECT_TRUE(store_->FinishChunk());
358 // A sub chunk to delete.
359 EXPECT_FALSE(store_->CheckSubChunk(kSubChunk1));
360 store_->SetSubChunk(kSubChunk1);
361 EXPECT_TRUE(store_->BeginChunk());
362 EXPECT_TRUE(store_->WriteSubHash(kSubChunk1, kAddChunk3, kHash4));
363 EXPECT_TRUE(store_->FinishChunk());
365 // A sub chunk to keep.
366 EXPECT_FALSE(store_->CheckSubChunk(kSubChunk2));
367 store_->SetSubChunk(kSubChunk2);
368 EXPECT_TRUE(store_->BeginChunk());
369 EXPECT_TRUE(store_->WriteSubHash(kSubChunk2, kAddChunk4, kHash5));
370 EXPECT_TRUE(store_->FinishChunk());
372 store_->DeleteAddChunk(kAddChunk1);
373 store_->DeleteSubChunk(kSubChunk1);
375 // Not actually deleted until finish.
376 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk1));
377 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk2));
378 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk3));
379 EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
380 EXPECT_TRUE(store_->CheckSubChunk(kSubChunk2));
383 safe_browsing::PrefixSetBuilder builder;
384 std::vector<SBAddFullHash> add_full_hashes_result;
385 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
387 std::vector<SBPrefix> prefixes_result;
388 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
389 ASSERT_EQ(1U, prefixes_result.size());
390 EXPECT_EQ(kHash3.prefix, prefixes_result[0]);
392 ASSERT_EQ(1U, add_full_hashes_result.size());
393 EXPECT_EQ(kAddChunk3, add_full_hashes_result[0].chunk_id);
394 EXPECT_TRUE(SBFullHashEqual(kHash6, add_full_hashes_result[0].full_hash));
397 // Expected chunks are there in another update.
398 ASSERT_TRUE(store_->BeginUpdate());
399 EXPECT_FALSE(store_->CheckAddChunk(kAddChunk1));
400 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk2));
401 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk3));
402 EXPECT_FALSE(store_->CheckSubChunk(kSubChunk1));
403 EXPECT_TRUE(store_->CheckSubChunk(kSubChunk2));
405 // Delete them, too.
406 store_->DeleteAddChunk(kAddChunk2);
407 store_->DeleteAddChunk(kAddChunk3);
408 store_->DeleteSubChunk(kSubChunk2);
411 safe_browsing::PrefixSetBuilder builder;
412 std::vector<SBAddFullHash> add_full_hashes_result;
413 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
415 std::vector<SBPrefix> prefixes_result;
416 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
417 EXPECT_TRUE(prefixes_result.empty());
418 EXPECT_TRUE(add_full_hashes_result.empty());
421 // Expect no more chunks.
422 ASSERT_TRUE(store_->BeginUpdate());
423 EXPECT_FALSE(store_->CheckAddChunk(kAddChunk1));
424 EXPECT_FALSE(store_->CheckAddChunk(kAddChunk2));
425 EXPECT_FALSE(store_->CheckAddChunk(kAddChunk3));
426 EXPECT_FALSE(store_->CheckSubChunk(kSubChunk1));
427 EXPECT_FALSE(store_->CheckSubChunk(kSubChunk2));
430 safe_browsing::PrefixSetBuilder builder;
431 std::vector<SBAddFullHash> add_full_hashes_result;
432 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
434 std::vector<SBPrefix> prefixes_result;
435 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
436 EXPECT_TRUE(prefixes_result.empty());
437 EXPECT_TRUE(add_full_hashes_result.empty());
441 // Test that deleting the store deletes the store.
442 TEST_F(SafeBrowsingStoreFileTest, Delete) {
443 // Delete should work if the file wasn't there in the first place.
444 EXPECT_FALSE(base::PathExists(filename_));
445 EXPECT_TRUE(store_->Delete());
447 // Create a store file.
448 PopulateStore();
450 EXPECT_TRUE(base::PathExists(filename_));
451 EXPECT_TRUE(store_->Delete());
452 EXPECT_FALSE(base::PathExists(filename_));
455 // Test that Delete() deletes the temporary store, if present.
456 TEST_F(SafeBrowsingStoreFileTest, DeleteTemp) {
457 const base::FilePath temp_file =
458 SafeBrowsingStoreFile::TemporaryFileForFilename(filename_);
460 EXPECT_FALSE(base::PathExists(filename_));
461 EXPECT_FALSE(base::PathExists(temp_file));
463 // Starting a transaction creates a temporary file.
464 ASSERT_TRUE(store_->BeginUpdate());
465 EXPECT_TRUE(base::PathExists(temp_file));
467 // Pull the rug out from under the existing store, simulating a
468 // crash.
469 store_.reset(new SafeBrowsingStoreFile(task_runner_));
470 store_->Init(filename_, base::Closure());
471 EXPECT_FALSE(base::PathExists(filename_));
472 EXPECT_TRUE(base::PathExists(temp_file));
474 // Make sure the temporary file is deleted.
475 EXPECT_TRUE(store_->Delete());
476 EXPECT_FALSE(base::PathExists(filename_));
477 EXPECT_FALSE(base::PathExists(temp_file));
480 // Test basic corruption-handling.
481 TEST_F(SafeBrowsingStoreFileTest, DetectsCorruption) {
482 // Load a store with some data.
483 PopulateStore();
485 // Can successfully open and read the store.
487 std::vector<SBPrefix> orig_prefixes;
488 std::vector<SBAddFullHash> orig_hashes;
489 safe_browsing::PrefixSetBuilder builder;
490 ASSERT_TRUE(store_->BeginUpdate());
491 EXPECT_TRUE(store_->FinishUpdate(&builder, &orig_hashes));
492 builder.GetPrefixSetNoHashes()->GetPrefixes(&orig_prefixes);
493 EXPECT_GT(orig_prefixes.size(), 0U);
494 EXPECT_GT(orig_hashes.size(), 0U);
495 EXPECT_FALSE(corruption_detected_);
498 // Corrupt the store.
499 base::ScopedFILE file(base::OpenFile(filename_, "rb+"));
500 const long kOffset = 60;
501 EXPECT_EQ(fseek(file.get(), kOffset, SEEK_SET), 0);
502 const uint32 kZero = 0;
503 uint32 previous = kZero;
504 EXPECT_EQ(fread(&previous, sizeof(previous), 1, file.get()), 1U);
505 EXPECT_NE(previous, kZero);
506 EXPECT_EQ(fseek(file.get(), kOffset, SEEK_SET), 0);
507 EXPECT_EQ(fwrite(&kZero, sizeof(kZero), 1, file.get()), 1U);
508 file.reset();
510 // Update fails and corruption callback is called.
511 std::vector<SBAddFullHash> add_hashes;
512 corruption_detected_ = false;
514 safe_browsing::PrefixSetBuilder builder;
515 ASSERT_TRUE(store_->BeginUpdate());
516 EXPECT_FALSE(store_->FinishUpdate(&builder, &add_hashes));
517 EXPECT_TRUE(corruption_detected_);
520 // Make it look like there is a lot of add-chunks-seen data.
521 const long kAddChunkCountOffset = 2 * sizeof(int32);
522 const int32 kLargeCount = 1000 * 1000 * 1000;
523 file.reset(base::OpenFile(filename_, "rb+"));
524 EXPECT_EQ(fseek(file.get(), kAddChunkCountOffset, SEEK_SET), 0);
525 EXPECT_EQ(fwrite(&kLargeCount, sizeof(kLargeCount), 1, file.get()), 1U);
526 file.reset();
528 // Detects corruption and fails to even begin the update.
529 corruption_detected_ = false;
530 EXPECT_FALSE(store_->BeginUpdate());
531 EXPECT_TRUE(corruption_detected_);
534 TEST_F(SafeBrowsingStoreFileTest, CheckValidity) {
535 // Empty store is valid.
536 EXPECT_FALSE(base::PathExists(filename_));
537 ASSERT_TRUE(store_->BeginUpdate());
538 EXPECT_FALSE(corruption_detected_);
539 EXPECT_TRUE(store_->CheckValidity());
540 EXPECT_FALSE(corruption_detected_);
541 EXPECT_TRUE(store_->CancelUpdate());
543 // A store with some data is valid.
544 EXPECT_FALSE(base::PathExists(filename_));
545 PopulateStore();
546 EXPECT_TRUE(base::PathExists(filename_));
547 ASSERT_TRUE(store_->BeginUpdate());
548 EXPECT_FALSE(corruption_detected_);
549 EXPECT_TRUE(store_->CheckValidity());
550 EXPECT_FALSE(corruption_detected_);
551 EXPECT_TRUE(store_->CancelUpdate());
554 // Corrupt the header.
555 TEST_F(SafeBrowsingStoreFileTest, CheckValidityHeader) {
556 PopulateStore();
557 EXPECT_TRUE(base::PathExists(filename_));
559 // 37 is the most random prime number. It's also past the initial header
560 // struct, somewhere in the chunk list.
561 const size_t kOffset = 37;
564 base::ScopedFILE file(base::OpenFile(filename_, "rb+"));
565 EXPECT_EQ(0, fseek(file.get(), kOffset, SEEK_SET));
566 EXPECT_GE(fputs("hello", file.get()), 0);
568 ASSERT_FALSE(store_->BeginUpdate());
569 EXPECT_TRUE(corruption_detected_);
572 // Corrupt the prefix payload.
573 TEST_F(SafeBrowsingStoreFileTest, CheckValidityPayload) {
574 PopulateStore();
575 EXPECT_TRUE(base::PathExists(filename_));
577 // 137 is the second most random prime number. It's also past the header and
578 // chunk-id area. Corrupting the header would fail BeginUpdate() in which
579 // case CheckValidity() cannot be called.
580 const size_t kOffset = 137;
583 base::ScopedFILE file(base::OpenFile(filename_, "rb+"));
584 EXPECT_EQ(0, fseek(file.get(), kOffset, SEEK_SET));
585 EXPECT_GE(fputs("hello", file.get()), 0);
587 ASSERT_TRUE(store_->BeginUpdate());
588 EXPECT_FALSE(corruption_detected_);
589 EXPECT_FALSE(store_->CheckValidity());
590 EXPECT_TRUE(corruption_detected_);
591 EXPECT_TRUE(store_->CancelUpdate());
594 // Corrupt the checksum.
595 TEST_F(SafeBrowsingStoreFileTest, CheckValidityChecksum) {
596 PopulateStore();
597 EXPECT_TRUE(base::PathExists(filename_));
599 // An offset from the end of the file which is in the checksum.
600 const int kOffset = -static_cast<int>(sizeof(base::MD5Digest));
603 base::ScopedFILE file(base::OpenFile(filename_, "rb+"));
604 EXPECT_EQ(0, fseek(file.get(), kOffset, SEEK_END));
605 EXPECT_GE(fputs("hello", file.get()), 0);
607 ASSERT_TRUE(store_->BeginUpdate());
608 EXPECT_FALSE(corruption_detected_);
609 EXPECT_FALSE(store_->CheckValidity());
610 EXPECT_TRUE(corruption_detected_);
611 EXPECT_TRUE(store_->CancelUpdate());
614 TEST_F(SafeBrowsingStoreFileTest, GetAddPrefixesAndHashes) {
615 ASSERT_TRUE(store_->BeginUpdate());
617 EXPECT_TRUE(store_->BeginChunk());
618 store_->SetAddChunk(kAddChunk1);
619 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk1));
620 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash1.prefix));
621 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash2.prefix));
622 EXPECT_TRUE(store_->FinishChunk());
624 EXPECT_TRUE(store_->BeginChunk());
625 store_->SetAddChunk(kAddChunk2);
626 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk2));
627 EXPECT_TRUE(store_->WriteAddHash(kAddChunk2, kHash4));
628 EXPECT_TRUE(store_->FinishChunk());
630 store_->SetSubChunk(kSubChunk1);
631 EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
632 EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk1, kAddChunk3, kHash3.prefix));
633 EXPECT_TRUE(store_->WriteSubHash(kSubChunk1, kAddChunk3, kHash3));
634 EXPECT_TRUE(store_->FinishChunk());
636 // Chunk numbers shouldn't leak over.
637 EXPECT_FALSE(store_->CheckAddChunk(kSubChunk1));
638 EXPECT_FALSE(store_->CheckAddChunk(kAddChunk3));
639 EXPECT_FALSE(store_->CheckSubChunk(kAddChunk1));
641 std::vector<int> chunks;
642 store_->GetAddChunks(&chunks);
643 ASSERT_EQ(2U, chunks.size());
644 EXPECT_EQ(kAddChunk1, chunks[0]);
645 EXPECT_EQ(kAddChunk2, chunks[1]);
647 store_->GetSubChunks(&chunks);
648 ASSERT_EQ(1U, chunks.size());
649 EXPECT_EQ(kSubChunk1, chunks[0]);
651 safe_browsing::PrefixSetBuilder builder;
652 std::vector<SBAddFullHash> add_full_hashes_result;
653 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
655 SBAddPrefixes add_prefixes;
656 EXPECT_TRUE(store_->GetAddPrefixes(&add_prefixes));
657 ASSERT_EQ(2U, add_prefixes.size());
658 EXPECT_EQ(kAddChunk1, add_prefixes[0].chunk_id);
659 EXPECT_EQ(kHash1.prefix, add_prefixes[0].prefix);
660 EXPECT_EQ(kAddChunk1, add_prefixes[1].chunk_id);
661 EXPECT_EQ(kHash2.prefix, add_prefixes[1].prefix);
663 std::vector<SBAddFullHash> add_hashes;
664 EXPECT_TRUE(store_->GetAddFullHashes(&add_hashes));
665 ASSERT_EQ(1U, add_hashes.size());
666 EXPECT_EQ(kAddChunk2, add_hashes[0].chunk_id);
667 EXPECT_TRUE(SBFullHashEqual(kHash4, add_hashes[0].full_hash));
670 // Test that the database handles resharding correctly, both when growing and
671 // which shrinking.
672 TEST_F(SafeBrowsingStoreFileTest, Resharding) {
673 // Loop through multiple stride boundaries (1<<32, 1<<31, 1<<30, 1<<29).
674 const uint32 kTargetStride = 1 << 29;
676 // Each chunk will require 8 bytes per prefix, plus 4 bytes for chunk
677 // information. It should be less than |kTargetFootprint| in the
678 // implementation, but high enough to keep the number of rewrites modest (to
679 // keep the test fast).
680 const size_t kPrefixesPerChunk = 10000;
682 uint32 shard_stride = 0;
683 int chunk_id = 1;
685 // Add a series of chunks, tracking that the stride size changes in a
686 // direction appropriate to increasing file size.
687 do {
688 ASSERT_TRUE(store_->BeginUpdate());
690 EXPECT_TRUE(store_->BeginChunk());
691 store_->SetAddChunk(chunk_id);
692 EXPECT_TRUE(store_->CheckAddChunk(chunk_id));
693 for (size_t i = 0; i < kPrefixesPerChunk; ++i) {
694 EXPECT_TRUE(store_->WriteAddPrefix(chunk_id, static_cast<SBPrefix>(i)));
696 EXPECT_TRUE(store_->FinishChunk());
698 safe_browsing::PrefixSetBuilder builder;
699 std::vector<SBAddFullHash> add_full_hashes_result;
700 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
702 SBAddPrefixes add_prefixes;
703 EXPECT_TRUE(store_->GetAddPrefixes(&add_prefixes));
704 ASSERT_EQ(chunk_id * kPrefixesPerChunk, add_prefixes.size());
706 // New stride should be the same, or shifted one right.
707 const uint32 new_shard_stride = ReadStride();
708 EXPECT_TRUE((new_shard_stride == shard_stride) ||
709 ((new_shard_stride << 1) == shard_stride));
710 shard_stride = new_shard_stride;
711 ++chunk_id;
712 } while (!shard_stride || shard_stride > kTargetStride);
714 // Guard against writing too many chunks. If this gets too big, adjust
715 // |kPrefixesPerChunk|.
716 EXPECT_LT(chunk_id, 20);
718 // Remove each chunk and check that the stride goes back to 0.
719 while (--chunk_id) {
720 ASSERT_TRUE(store_->BeginUpdate());
721 EXPECT_TRUE(store_->CheckAddChunk(chunk_id));
722 EXPECT_FALSE(store_->CheckAddChunk(chunk_id + 1));
723 store_->DeleteAddChunk(chunk_id);
725 safe_browsing::PrefixSetBuilder builder;
726 std::vector<SBAddFullHash> add_full_hashes_result;
727 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
729 // New stride should be the same, or shifted one left.
730 const uint32 new_shard_stride = ReadStride();
731 EXPECT_TRUE((new_shard_stride == shard_stride) ||
732 (new_shard_stride == (shard_stride << 1)));
733 shard_stride = new_shard_stride;
735 EXPECT_EQ(0u, shard_stride);
738 // Test that a golden v7 file can no longer be read. All platforms generating
739 // v7 files were little-endian, so there is no point to testing this transition
740 // if/when a big-endian port is added.
741 #if defined(ARCH_CPU_LITTLE_ENDIAN)
742 TEST_F(SafeBrowsingStoreFileTest, Version7) {
743 store_.reset();
745 // Copy the golden file into temporary storage. The golden file contains:
746 // - Add chunk kAddChunk1 containing kHash1.prefix and kHash2.
747 // - Sub chunk kSubChunk1 containing kHash3.
748 const char kBasename[] = "FileStoreVersion7";
749 base::FilePath golden_path;
750 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &golden_path));
751 golden_path = golden_path.AppendASCII("SafeBrowsing");
752 golden_path = golden_path.AppendASCII(kBasename);
753 ASSERT_TRUE(base::CopyFile(golden_path, filename_));
755 // Reset the store to make sure it re-reads the file.
756 ASSERT_TRUE(!task_runner_->HasPendingTask());
757 store_.reset(new SafeBrowsingStoreFile(task_runner_));
758 store_->Init(filename_,
759 base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected,
760 base::Unretained(this)));
761 EXPECT_FALSE(corruption_detected_);
763 // The unknown version should be encountered on the first read.
764 EXPECT_FALSE(store_->BeginUpdate());
765 EXPECT_TRUE(corruption_detected_);
767 // No more to test because corrupt file stores are cleaned up by the database
768 // containing them.
770 #endif
772 // Test that a golden v8 file can be read by the current code. All platforms
773 // generating v8 files are little-endian, so there is no point to testing this
774 // transition if/when a big-endian port is added.
775 #if defined(ARCH_CPU_LITTLE_ENDIAN)
776 TEST_F(SafeBrowsingStoreFileTest, Version8) {
777 store_.reset();
779 // Copy the golden file into temporary storage. The golden file contains:
780 // - Add chunk kAddChunk1 containing kHash1.prefix and kHash2.
781 // - Sub chunk kSubChunk1 containing kHash3.
782 const char kBasename[] = "FileStoreVersion8";
783 base::FilePath golden_path;
784 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &golden_path));
785 golden_path = golden_path.AppendASCII("SafeBrowsing");
786 golden_path = golden_path.AppendASCII(kBasename);
787 ASSERT_TRUE(base::CopyFile(golden_path, filename_));
789 // Reset the store to make sure it re-reads the file.
790 ASSERT_TRUE(!task_runner_->HasPendingTask());
791 store_.reset(new SafeBrowsingStoreFile(task_runner_));
792 store_->Init(filename_,
793 base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected,
794 base::Unretained(this)));
796 // Check that the expected prefixes and hashes are in place.
797 SBAddPrefixes add_prefixes;
798 EXPECT_TRUE(store_->GetAddPrefixes(&add_prefixes));
799 ASSERT_EQ(2U, add_prefixes.size());
800 EXPECT_EQ(kAddChunk1, add_prefixes[0].chunk_id);
801 EXPECT_EQ(kHash1.prefix, add_prefixes[0].prefix);
802 EXPECT_EQ(kAddChunk1, add_prefixes[1].chunk_id);
803 EXPECT_EQ(kHash2.prefix, add_prefixes[1].prefix);
805 std::vector<SBAddFullHash> add_hashes;
806 EXPECT_TRUE(store_->GetAddFullHashes(&add_hashes));
807 ASSERT_EQ(1U, add_hashes.size());
808 EXPECT_EQ(kAddChunk1, add_hashes[0].chunk_id);
809 EXPECT_TRUE(SBFullHashEqual(kHash2, add_hashes[0].full_hash));
811 // Attempt an update to make sure things work end-to-end.
812 EXPECT_TRUE(store_->BeginUpdate());
814 // Still has the chunks expected in the next update.
815 std::vector<int> chunks;
816 store_->GetAddChunks(&chunks);
817 ASSERT_EQ(1U, chunks.size());
818 EXPECT_EQ(kAddChunk1, chunks[0]);
820 store_->GetSubChunks(&chunks);
821 ASSERT_EQ(1U, chunks.size());
822 EXPECT_EQ(kSubChunk1, chunks[0]);
824 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk1));
825 EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
827 // Sub chunk kAddChunk1 hash kHash2.
828 // NOTE(shess): Having full hashes and prefixes in the same chunk is no longer
829 // supported, though it was when this code was written.
830 store_->SetSubChunk(kSubChunk2);
831 EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
832 EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk1, kAddChunk1, kHash2.prefix));
833 EXPECT_TRUE(store_->WriteSubHash(kSubChunk1, kAddChunk1, kHash2));
834 EXPECT_TRUE(store_->FinishChunk());
837 safe_browsing::PrefixSetBuilder builder;
838 std::vector<SBAddFullHash> add_full_hashes_result;
839 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
841 // The sub'ed prefix and hash are gone.
842 std::vector<SBPrefix> prefixes_result;
843 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
844 ASSERT_EQ(1U, prefixes_result.size());
845 EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
846 EXPECT_TRUE(add_full_hashes_result.empty());
849 #endif
851 } // namespace safe_browsing