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 "content/browser/download/base_file.h"
7 #include "base/file_util.h"
8 #include "base/files/file.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/test/test_file_util.h"
14 #include "content/browser/browser_thread_impl.h"
15 #include "content/public/browser/download_interrupt_reasons.h"
16 #include "crypto/secure_hash.h"
17 #include "crypto/sha2.h"
18 #include "testing/gtest/include/gtest/gtest.h"
23 const char kTestData1
[] = "Let's write some data to the file!\n";
24 const char kTestData2
[] = "Writing more data.\n";
25 const char kTestData3
[] = "Final line.";
26 const char kTestData4
[] = "supercalifragilisticexpialidocious";
27 const int kTestDataLength1
= arraysize(kTestData1
) - 1;
28 const int kTestDataLength2
= arraysize(kTestData2
) - 1;
29 const int kTestDataLength3
= arraysize(kTestData3
) - 1;
30 const int kTestDataLength4
= arraysize(kTestData4
) - 1;
31 const int kElapsedTimeSeconds
= 5;
32 const base::TimeDelta kElapsedTimeDelta
= base::TimeDelta::FromSeconds(
37 class BaseFileTest
: public testing::Test
{
39 static const unsigned char kEmptySha256Hash
[crypto::kSHA256Length
];
42 : expect_file_survives_(false),
43 expect_in_progress_(true),
44 expected_error_(DOWNLOAD_INTERRUPT_REASON_NONE
),
45 file_thread_(BrowserThread::FILE, &message_loop_
) {
48 virtual void SetUp() {
50 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
51 base_file_
.reset(new BaseFile(base::FilePath(),
61 virtual void TearDown() {
62 EXPECT_FALSE(base_file_
->in_progress());
63 if (!expected_error_
) {
64 EXPECT_EQ(static_cast<int64
>(expected_data_
.size()),
65 base_file_
->bytes_so_far());
68 base::FilePath full_path
= base_file_
->full_path();
70 if (!expected_data_
.empty() && !expected_error_
) {
71 // Make sure the data has been properly written to disk.
72 std::string disk_data
;
73 EXPECT_TRUE(base::ReadFileToString(full_path
, &disk_data
));
74 EXPECT_EQ(expected_data_
, disk_data
);
77 // Make sure the mock BrowserThread outlives the BaseFile to satisfy
78 // thread checks inside it.
81 EXPECT_EQ(expect_file_survives_
, base::PathExists(full_path
));
85 secure_hash_
.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256
));
86 memcpy(sha256_hash_
, kEmptySha256Hash
, crypto::kSHA256Length
);
89 void UpdateHash(const char* data
, size_t length
) {
90 secure_hash_
->Update(data
, length
);
93 std::string
GetFinalHash() {
95 secure_hash_
->Finish(sha256_hash_
, crypto::kSHA256Length
);
96 hash
.assign(reinterpret_cast<const char*>(sha256_hash_
),
97 sizeof(sha256_hash_
));
101 void MakeFileWithHash() {
102 base_file_
.reset(new BaseFile(base::FilePath(),
109 net::BoundNetLog()));
112 bool InitializeFile() {
113 DownloadInterruptReason result
= base_file_
->Initialize(temp_dir_
.path());
114 EXPECT_EQ(expected_error_
, result
);
115 return result
== DOWNLOAD_INTERRUPT_REASON_NONE
;
118 bool AppendDataToFile(const std::string
& data
) {
119 EXPECT_EQ(expect_in_progress_
, base_file_
->in_progress());
120 DownloadInterruptReason result
=
121 base_file_
->AppendDataToFile(data
.data(), data
.size());
122 if (result
== DOWNLOAD_INTERRUPT_REASON_NONE
)
123 EXPECT_TRUE(expect_in_progress_
) << " result = " << result
;
125 EXPECT_EQ(expected_error_
, result
);
126 if (base_file_
->in_progress()) {
127 expected_data_
+= data
;
128 if (expected_error_
== DOWNLOAD_INTERRUPT_REASON_NONE
) {
129 EXPECT_EQ(static_cast<int64
>(expected_data_
.size()),
130 base_file_
->bytes_so_far());
133 return result
== DOWNLOAD_INTERRUPT_REASON_NONE
;
136 void set_expected_data(const std::string
& data
) { expected_data_
= data
; }
139 // Create a file. Returns the complete file path.
140 base::FilePath
CreateTestFile() {
141 base::FilePath file_name
;
142 BaseFile
file(base::FilePath(),
151 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
152 file
.Initialize(temp_dir_
.path()));
153 file_name
= file
.full_path();
154 EXPECT_NE(base::FilePath::StringType(), file_name
.value());
156 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
157 file
.AppendDataToFile(kTestData4
, kTestDataLength4
));
159 // Keep the file from getting deleted when existing_file_name is deleted.
165 // Create a file with the specified file name.
166 void CreateFileWithName(const base::FilePath
& file_name
) {
167 EXPECT_NE(base::FilePath::StringType(), file_name
.value());
168 BaseFile
duplicate_file(file_name
,
176 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
177 duplicate_file
.Initialize(temp_dir_
.path()));
178 // Write something into it.
179 duplicate_file
.AppendDataToFile(kTestData4
, kTestDataLength4
);
180 // Detach the file so it isn't deleted on destruction of |duplicate_file|.
181 duplicate_file
.Detach();
184 int64
CurrentSpeedAtTime(base::TimeTicks current_time
) {
185 EXPECT_TRUE(base_file_
.get());
186 return base_file_
->CurrentSpeedAtTime(current_time
);
189 base::TimeTicks
StartTick() {
190 EXPECT_TRUE(base_file_
.get());
191 return base_file_
->start_tick_
;
194 void set_expected_error(DownloadInterruptReason err
) {
195 expected_error_
= err
;
199 // BaseClass instance we are testing.
200 scoped_ptr
<BaseFile
> base_file_
;
202 // Temporary directory for renamed downloads.
203 base::ScopedTempDir temp_dir_
;
205 // Expect the file to survive deletion of the BaseFile instance.
206 bool expect_file_survives_
;
208 // Expect the file to be in progress.
209 bool expect_in_progress_
;
212 scoped_ptr
<crypto::SecureHash
> secure_hash_
;
214 unsigned char sha256_hash_
[crypto::kSHA256Length
];
217 // Keep track of what data should be saved to the disk file.
218 std::string expected_data_
;
219 DownloadInterruptReason expected_error_
;
221 // Mock file thread to satisfy debug checks in BaseFile.
222 base::MessageLoop message_loop_
;
223 BrowserThreadImpl file_thread_
;
226 // This will initialize the entire array to zero.
227 const unsigned char BaseFileTest::kEmptySha256Hash
[] = { 0 };
229 // Test the most basic scenario: just create the object and do a sanity check
230 // on all its accessors. This is actually a case that rarely happens
231 // in production, where we would at least Initialize it.
232 TEST_F(BaseFileTest
, CreateDestroy
) {
233 EXPECT_EQ(base::FilePath().value(), base_file_
->full_path().value());
236 // Cancel the download explicitly.
237 TEST_F(BaseFileTest
, Cancel
) {
238 ASSERT_TRUE(InitializeFile());
239 EXPECT_TRUE(base::PathExists(base_file_
->full_path()));
240 base_file_
->Cancel();
241 EXPECT_FALSE(base::PathExists(base_file_
->full_path()));
242 EXPECT_NE(base::FilePath().value(), base_file_
->full_path().value());
245 // Write data to the file and detach it, so it doesn't get deleted
246 // automatically when base_file_ is destructed.
247 TEST_F(BaseFileTest
, WriteAndDetach
) {
248 ASSERT_TRUE(InitializeFile());
249 ASSERT_TRUE(AppendDataToFile(kTestData1
));
250 base_file_
->Finish();
251 base_file_
->Detach();
252 expect_file_survives_
= true;
255 // Write data to the file and detach it, and calculate its sha256 hash.
256 TEST_F(BaseFileTest
, WriteWithHashAndDetach
) {
257 // Calculate the final hash.
259 UpdateHash(kTestData1
, kTestDataLength1
);
260 std::string expected_hash
= GetFinalHash();
261 std::string expected_hash_hex
=
262 base::HexEncode(expected_hash
.data(), expected_hash
.size());
265 ASSERT_TRUE(InitializeFile());
266 ASSERT_TRUE(AppendDataToFile(kTestData1
));
267 base_file_
->Finish();
270 base_file_
->GetHash(&hash
);
271 EXPECT_EQ("0B2D3F3F7943AD64B860DF94D05CB56A8A97C6EC5768B5B70B930C5AA7FA9ADE",
273 EXPECT_EQ(expected_hash_hex
, base::HexEncode(hash
.data(), hash
.size()));
275 base_file_
->Detach();
276 expect_file_survives_
= true;
279 // Rename the file after writing to it, then detach.
280 TEST_F(BaseFileTest
, WriteThenRenameAndDetach
) {
281 ASSERT_TRUE(InitializeFile());
283 base::FilePath
initial_path(base_file_
->full_path());
284 EXPECT_TRUE(base::PathExists(initial_path
));
285 base::FilePath
new_path(temp_dir_
.path().AppendASCII("NewFile"));
286 EXPECT_FALSE(base::PathExists(new_path
));
288 ASSERT_TRUE(AppendDataToFile(kTestData1
));
290 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
, base_file_
->Rename(new_path
));
291 EXPECT_FALSE(base::PathExists(initial_path
));
292 EXPECT_TRUE(base::PathExists(new_path
));
294 base_file_
->Finish();
295 base_file_
->Detach();
296 expect_file_survives_
= true;
299 // Write data to the file once.
300 TEST_F(BaseFileTest
, SingleWrite
) {
301 ASSERT_TRUE(InitializeFile());
302 ASSERT_TRUE(AppendDataToFile(kTestData1
));
303 base_file_
->Finish();
306 // Write data to the file multiple times.
307 TEST_F(BaseFileTest
, MultipleWrites
) {
308 ASSERT_TRUE(InitializeFile());
309 ASSERT_TRUE(AppendDataToFile(kTestData1
));
310 ASSERT_TRUE(AppendDataToFile(kTestData2
));
311 ASSERT_TRUE(AppendDataToFile(kTestData3
));
313 EXPECT_FALSE(base_file_
->GetHash(&hash
));
314 base_file_
->Finish();
317 // Write data to the file once and calculate its sha256 hash.
318 TEST_F(BaseFileTest
, SingleWriteWithHash
) {
319 // Calculate the final hash.
321 UpdateHash(kTestData1
, kTestDataLength1
);
322 std::string expected_hash
= GetFinalHash();
323 std::string expected_hash_hex
=
324 base::HexEncode(expected_hash
.data(), expected_hash
.size());
327 ASSERT_TRUE(InitializeFile());
328 // Can get partial hash states before Finish() is called.
329 EXPECT_STRNE(std::string().c_str(), base_file_
->GetHashState().c_str());
330 ASSERT_TRUE(AppendDataToFile(kTestData1
));
331 EXPECT_STRNE(std::string().c_str(), base_file_
->GetHashState().c_str());
332 base_file_
->Finish();
335 base_file_
->GetHash(&hash
);
336 EXPECT_EQ(expected_hash_hex
, base::HexEncode(hash
.data(), hash
.size()));
339 // Write data to the file multiple times and calculate its sha256 hash.
340 TEST_F(BaseFileTest
, MultipleWritesWithHash
) {
341 // Calculate the final hash.
343 UpdateHash(kTestData1
, kTestDataLength1
);
344 UpdateHash(kTestData2
, kTestDataLength2
);
345 UpdateHash(kTestData3
, kTestDataLength3
);
346 std::string expected_hash
= GetFinalHash();
347 std::string expected_hash_hex
=
348 base::HexEncode(expected_hash
.data(), expected_hash
.size());
352 ASSERT_TRUE(InitializeFile());
353 ASSERT_TRUE(AppendDataToFile(kTestData1
));
354 ASSERT_TRUE(AppendDataToFile(kTestData2
));
355 ASSERT_TRUE(AppendDataToFile(kTestData3
));
356 // No hash before Finish() is called.
357 EXPECT_FALSE(base_file_
->GetHash(&hash
));
358 base_file_
->Finish();
360 EXPECT_TRUE(base_file_
->GetHash(&hash
));
361 EXPECT_EQ("CBF68BF10F8003DB86B31343AFAC8C7175BD03FB5FC905650F8C80AF087443A8",
363 EXPECT_EQ(expected_hash_hex
, base::HexEncode(hash
.data(), hash
.size()));
366 // Write data to the file multiple times, interrupt it, and continue using
367 // another file. Calculate the resulting combined sha256 hash.
368 TEST_F(BaseFileTest
, MultipleWritesInterruptedWithHash
) {
369 // Calculate the final hash.
371 UpdateHash(kTestData1
, kTestDataLength1
);
372 UpdateHash(kTestData2
, kTestDataLength2
);
373 UpdateHash(kTestData3
, kTestDataLength3
);
374 std::string expected_hash
= GetFinalHash();
375 std::string expected_hash_hex
=
376 base::HexEncode(expected_hash
.data(), expected_hash
.size());
379 ASSERT_TRUE(InitializeFile());
381 ASSERT_TRUE(AppendDataToFile(kTestData1
));
382 ASSERT_TRUE(AppendDataToFile(kTestData2
));
383 // Get the hash state and file name.
384 std::string hash_state
;
385 hash_state
= base_file_
->GetHashState();
387 base_file_
->Finish();
389 base::FilePath
new_file_path(temp_dir_
.path().Append(
390 base::FilePath(FILE_PATH_LITERAL("second_file"))));
392 ASSERT_TRUE(base::CopyFile(base_file_
->full_path(), new_file_path
));
394 // Create another file
395 BaseFile
second_file(new_file_path
,
398 base_file_
->bytes_so_far(),
403 ASSERT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
404 second_file
.Initialize(base::FilePath()));
405 std::string
data(kTestData3
);
406 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
407 second_file
.AppendDataToFile(data
.data(), data
.size()));
408 second_file
.Finish();
411 EXPECT_TRUE(second_file
.GetHash(&hash
));
412 // This will fail until getting the hash state is supported in SecureHash.
413 EXPECT_STREQ(expected_hash_hex
.c_str(),
414 base::HexEncode(hash
.data(), hash
.size()).c_str());
417 // Rename the file after all writes to it.
418 TEST_F(BaseFileTest
, WriteThenRename
) {
419 ASSERT_TRUE(InitializeFile());
421 base::FilePath
initial_path(base_file_
->full_path());
422 EXPECT_TRUE(base::PathExists(initial_path
));
423 base::FilePath
new_path(temp_dir_
.path().AppendASCII("NewFile"));
424 EXPECT_FALSE(base::PathExists(new_path
));
426 ASSERT_TRUE(AppendDataToFile(kTestData1
));
428 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
429 base_file_
->Rename(new_path
));
430 EXPECT_FALSE(base::PathExists(initial_path
));
431 EXPECT_TRUE(base::PathExists(new_path
));
433 base_file_
->Finish();
436 // Rename the file while the download is still in progress.
437 TEST_F(BaseFileTest
, RenameWhileInProgress
) {
438 ASSERT_TRUE(InitializeFile());
440 base::FilePath
initial_path(base_file_
->full_path());
441 EXPECT_TRUE(base::PathExists(initial_path
));
442 base::FilePath
new_path(temp_dir_
.path().AppendASCII("NewFile"));
443 EXPECT_FALSE(base::PathExists(new_path
));
445 ASSERT_TRUE(AppendDataToFile(kTestData1
));
447 EXPECT_TRUE(base_file_
->in_progress());
448 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
, base_file_
->Rename(new_path
));
449 EXPECT_FALSE(base::PathExists(initial_path
));
450 EXPECT_TRUE(base::PathExists(new_path
));
452 ASSERT_TRUE(AppendDataToFile(kTestData2
));
454 base_file_
->Finish();
457 // Test that a failed rename reports the correct error.
458 TEST_F(BaseFileTest
, RenameWithError
) {
459 ASSERT_TRUE(InitializeFile());
461 // TestDir is a subdirectory in |temp_dir_| that we will make read-only so
462 // that the rename will fail.
463 base::FilePath
test_dir(temp_dir_
.path().AppendASCII("TestDir"));
464 ASSERT_TRUE(base::CreateDirectory(test_dir
));
466 base::FilePath
new_path(test_dir
.AppendASCII("TestFile"));
467 EXPECT_FALSE(base::PathExists(new_path
));
470 base::FilePermissionRestorer
restore_permissions_for(test_dir
);
471 ASSERT_TRUE(base::MakeFileUnwritable(test_dir
));
472 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED
,
473 base_file_
->Rename(new_path
));
476 base_file_
->Finish();
479 // Test that a failed write reports an error.
480 TEST_F(BaseFileTest
, WriteWithError
) {
482 ASSERT_TRUE(base::CreateTemporaryFile(&path
));
484 // Pass a file handle which was opened without the WRITE flag.
485 // This should result in an error when writing.
486 base::File
file(path
, base::File::FLAG_OPEN_ALWAYS
| base::File::FLAG_READ
);
487 base_file_
.reset(new BaseFile(path
,
494 net::BoundNetLog()));
495 ASSERT_TRUE(InitializeFile());
497 set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED
);
498 #elif defined (OS_POSIX)
499 set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_FAILED
);
501 ASSERT_FALSE(AppendDataToFile(kTestData1
));
502 base_file_
->Finish();
505 // Try to write to uninitialized file.
506 TEST_F(BaseFileTest
, UninitializedFile
) {
507 expect_in_progress_
= false;
508 set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_FAILED
);
509 EXPECT_FALSE(AppendDataToFile(kTestData1
));
512 // Create two |BaseFile|s with the same file, and attempt to write to both.
513 // Overwrite base_file_ with another file with the same name and
514 // non-zero contents, and make sure the last file to close 'wins'.
515 TEST_F(BaseFileTest
, DuplicateBaseFile
) {
516 ASSERT_TRUE(InitializeFile());
518 // Create another |BaseFile| referring to the file that |base_file_| owns.
519 CreateFileWithName(base_file_
->full_path());
521 ASSERT_TRUE(AppendDataToFile(kTestData1
));
522 base_file_
->Finish();
525 // Create a file and append to it.
526 TEST_F(BaseFileTest
, AppendToBaseFile
) {
527 // Create a new file.
528 base::FilePath existing_file_name
= CreateTestFile();
530 set_expected_data(kTestData4
);
532 // Use the file we've just created.
533 base_file_
.reset(new BaseFile(existing_file_name
,
540 net::BoundNetLog()));
542 ASSERT_TRUE(InitializeFile());
544 const base::FilePath file_name
= base_file_
->full_path();
545 EXPECT_NE(base::FilePath::StringType(), file_name
.value());
547 // Write into the file.
548 EXPECT_TRUE(AppendDataToFile(kTestData1
));
550 base_file_
->Finish();
551 base_file_
->Detach();
552 expect_file_survives_
= true;
555 // Create a read-only file and attempt to write to it.
556 TEST_F(BaseFileTest
, ReadonlyBaseFile
) {
557 // Create a new file.
558 base::FilePath readonly_file_name
= CreateTestFile();
560 // Restore permissions to the file when we are done with this test.
561 base::FilePermissionRestorer
restore_permissions(readonly_file_name
);
563 // Make it read-only.
564 EXPECT_TRUE(base::MakeFileUnwritable(readonly_file_name
));
566 // Try to overwrite it.
567 base_file_
.reset(new BaseFile(readonly_file_name
,
574 net::BoundNetLog()));
576 expect_in_progress_
= false;
577 set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED
);
578 EXPECT_FALSE(InitializeFile());
580 const base::FilePath file_name
= base_file_
->full_path();
581 EXPECT_NE(base::FilePath::StringType(), file_name
.value());
583 // Write into the file.
584 set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_FAILED
);
585 EXPECT_FALSE(AppendDataToFile(kTestData1
));
587 base_file_
->Finish();
588 base_file_
->Detach();
589 expect_file_survives_
= true;
592 TEST_F(BaseFileTest
, IsEmptyHash
) {
593 std::string
empty(crypto::kSHA256Length
, '\x00');
594 EXPECT_TRUE(BaseFile::IsEmptyHash(empty
));
595 std::string
not_empty(crypto::kSHA256Length
, '\x01');
596 EXPECT_FALSE(BaseFile::IsEmptyHash(not_empty
));
597 EXPECT_FALSE(BaseFile::IsEmptyHash(std::string()));
599 std::string also_not_empty
= empty
;
600 also_not_empty
[crypto::kSHA256Length
- 1] = '\x01';
601 EXPECT_FALSE(BaseFile::IsEmptyHash(also_not_empty
));
604 // Test that a temporary file is created in the default download directory.
605 TEST_F(BaseFileTest
, CreatedInDefaultDirectory
) {
606 ASSERT_TRUE(base_file_
->full_path().empty());
607 ASSERT_TRUE(InitializeFile());
608 EXPECT_FALSE(base_file_
->full_path().empty());
610 // On Windows, CreateTemporaryFileInDir() will cause a path with short names
611 // to be expanded into a path with long names. Thus temp_dir.path() might not
612 // be a string-wise match to base_file_->full_path().DirName() even though
613 // they are in the same directory.
614 base::FilePath temp_file
;
615 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_
.path(), &temp_file
));
616 ASSERT_FALSE(temp_file
.empty());
617 EXPECT_STREQ(temp_file
.DirName().value().c_str(),
618 base_file_
->full_path().DirName().value().c_str());
619 base_file_
->Finish();
622 TEST_F(BaseFileTest
, NoDoubleDeleteAfterCancel
) {
623 ASSERT_TRUE(InitializeFile());
624 base::FilePath full_path
= base_file_
->full_path();
625 ASSERT_FALSE(full_path
.empty());
626 ASSERT_TRUE(base::PathExists(full_path
));
628 base_file_
->Cancel();
629 ASSERT_FALSE(base::PathExists(full_path
));
631 const char kData
[] = "hello";
632 const int kDataLength
= static_cast<int>(arraysize(kData
) - 1);
633 ASSERT_EQ(kDataLength
, base::WriteFile(full_path
, kData
, kDataLength
));
634 // The file that we created here should stick around when the BaseFile is
635 // destroyed during TearDown.
636 expect_file_survives_
= true;
639 } // namespace content