1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/download/base_file.h"
6
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"
19
20namespace content {
21namespace {
22
23const char kTestData1[] = "Let's write some data to the file!\n";
24const char kTestData2[] = "Writing more data.\n";
25const char kTestData3[] = "Final line.";
26const char kTestData4[] = "supercalifragilisticexpialidocious";
27const int kTestDataLength1 = arraysize(kTestData1) - 1;
28const int kTestDataLength2 = arraysize(kTestData2) - 1;
29const int kTestDataLength3 = arraysize(kTestData3) - 1;
30const int kTestDataLength4 = arraysize(kTestData4) - 1;
31const int kElapsedTimeSeconds = 5;
32const base::TimeDelta kElapsedTimeDelta = base::TimeDelta::FromSeconds(
33    kElapsedTimeSeconds);
34
35}  // namespace
36
37class BaseFileTest : public testing::Test {
38 public:
39  static const unsigned char kEmptySha256Hash[crypto::kSHA256Length];
40
41  BaseFileTest()
42      : expect_file_survives_(false),
43        expect_in_progress_(true),
44        expected_error_(DOWNLOAD_INTERRUPT_REASON_NONE),
45        file_thread_(BrowserThread::FILE, &message_loop_) {
46  }
47
48  virtual void SetUp() {
49    ResetHash();
50    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
51    base_file_.reset(new BaseFile(base::FilePath(),
52                                  GURL(),
53                                  GURL(),
54                                  0,
55                                  false,
56                                  std::string(),
57                                  base::File(),
58                                  net::BoundNetLog()));
59  }
60
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());
66    }
67
68    base::FilePath full_path = base_file_->full_path();
69
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);
75    }
76
77    // Make sure the mock BrowserThread outlives the BaseFile to satisfy
78    // thread checks inside it.
79    base_file_.reset();
80
81    EXPECT_EQ(expect_file_survives_, base::PathExists(full_path));
82  }
83
84  void ResetHash() {
85    secure_hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256));
86    memcpy(sha256_hash_, kEmptySha256Hash, crypto::kSHA256Length);
87  }
88
89  void UpdateHash(const char* data, size_t length) {
90    secure_hash_->Update(data, length);
91  }
92
93  std::string GetFinalHash() {
94    std::string hash;
95    secure_hash_->Finish(sha256_hash_, crypto::kSHA256Length);
96    hash.assign(reinterpret_cast<const char*>(sha256_hash_),
97                sizeof(sha256_hash_));
98    return hash;
99  }
100
101  void MakeFileWithHash() {
102    base_file_.reset(new BaseFile(base::FilePath(),
103                                  GURL(),
104                                  GURL(),
105                                  0,
106                                  true,
107                                  std::string(),
108                                  base::File(),
109                                  net::BoundNetLog()));
110  }
111
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;
116  }
117
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;
124
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());
131      }
132    }
133    return result == DOWNLOAD_INTERRUPT_REASON_NONE;
134  }
135
136  void set_expected_data(const std::string& data) { expected_data_ = data; }
137
138  // Helper functions.
139  // Create a file.  Returns the complete file path.
140  base::FilePath CreateTestFile() {
141    base::FilePath file_name;
142    BaseFile file(base::FilePath(),
143                  GURL(),
144                  GURL(),
145                  0,
146                  false,
147                  std::string(),
148                  base::File(),
149                  net::BoundNetLog());
150
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());
155
156    EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
157              file.AppendDataToFile(kTestData4, kTestDataLength4));
158
159    // Keep the file from getting deleted when existing_file_name is deleted.
160    file.Detach();
161
162    return file_name;
163  }
164
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,
169                            GURL(),
170                            GURL(),
171                            0,
172                            false,
173                            std::string(),
174                            base::File(),
175                            net::BoundNetLog());
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();
182  }
183
184  int64 CurrentSpeedAtTime(base::TimeTicks current_time) {
185    EXPECT_TRUE(base_file_.get());
186    return base_file_->CurrentSpeedAtTime(current_time);
187  }
188
189  base::TimeTicks StartTick() {
190    EXPECT_TRUE(base_file_.get());
191    return base_file_->start_tick_;
192  }
193
194  void set_expected_error(DownloadInterruptReason err) {
195    expected_error_ = err;
196  }
197
198 protected:
199  // BaseClass instance we are testing.
200  scoped_ptr<BaseFile> base_file_;
201
202  // Temporary directory for renamed downloads.
203  base::ScopedTempDir temp_dir_;
204
205  // Expect the file to survive deletion of the BaseFile instance.
206  bool expect_file_survives_;
207
208  // Expect the file to be in progress.
209  bool expect_in_progress_;
210
211  // Hash calculator.
212  scoped_ptr<crypto::SecureHash> secure_hash_;
213
214  unsigned char sha256_hash_[crypto::kSHA256Length];
215
216 private:
217  // Keep track of what data should be saved to the disk file.
218  std::string expected_data_;
219  DownloadInterruptReason expected_error_;
220
221  // Mock file thread to satisfy debug checks in BaseFile.
222  base::MessageLoop message_loop_;
223  BrowserThreadImpl file_thread_;
224};
225
226// This will initialize the entire array to zero.
227const unsigned char BaseFileTest::kEmptySha256Hash[] = { 0 };
228
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.
232TEST_F(BaseFileTest, CreateDestroy) {
233  EXPECT_EQ(base::FilePath().value(), base_file_->full_path().value());
234}
235
236// Cancel the download explicitly.
237TEST_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());
243}
244
245// Write data to the file and detach it, so it doesn't get deleted
246// automatically when base_file_ is destructed.
247TEST_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;
253}
254
255// Write data to the file and detach it, and calculate its sha256 hash.
256TEST_F(BaseFileTest, WriteWithHashAndDetach) {
257  // Calculate the final hash.
258  ResetHash();
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());
263
264  MakeFileWithHash();
265  ASSERT_TRUE(InitializeFile());
266  ASSERT_TRUE(AppendDataToFile(kTestData1));
267  base_file_->Finish();
268
269  std::string hash;
270  base_file_->GetHash(&hash);
271  EXPECT_EQ("0B2D3F3F7943AD64B860DF94D05CB56A8A97C6EC5768B5B70B930C5AA7FA9ADE",
272            expected_hash_hex);
273  EXPECT_EQ(expected_hash_hex, base::HexEncode(hash.data(), hash.size()));
274
275  base_file_->Detach();
276  expect_file_survives_ = true;
277}
278
279// Rename the file after writing to it, then detach.
280TEST_F(BaseFileTest, WriteThenRenameAndDetach) {
281  ASSERT_TRUE(InitializeFile());
282
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));
287
288  ASSERT_TRUE(AppendDataToFile(kTestData1));
289
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));
293
294  base_file_->Finish();
295  base_file_->Detach();
296  expect_file_survives_ = true;
297}
298
299// Write data to the file once.
300TEST_F(BaseFileTest, SingleWrite) {
301  ASSERT_TRUE(InitializeFile());
302  ASSERT_TRUE(AppendDataToFile(kTestData1));
303  base_file_->Finish();
304}
305
306// Write data to the file multiple times.
307TEST_F(BaseFileTest, MultipleWrites) {
308  ASSERT_TRUE(InitializeFile());
309  ASSERT_TRUE(AppendDataToFile(kTestData1));
310  ASSERT_TRUE(AppendDataToFile(kTestData2));
311  ASSERT_TRUE(AppendDataToFile(kTestData3));
312  std::string hash;
313  EXPECT_FALSE(base_file_->GetHash(&hash));
314  base_file_->Finish();
315}
316
317// Write data to the file once and calculate its sha256 hash.
318TEST_F(BaseFileTest, SingleWriteWithHash) {
319  // Calculate the final hash.
320  ResetHash();
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());
325
326  MakeFileWithHash();
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();
333
334  std::string hash;
335  base_file_->GetHash(&hash);
336  EXPECT_EQ(expected_hash_hex, base::HexEncode(hash.data(), hash.size()));
337}
338
339// Write data to the file multiple times and calculate its sha256 hash.
340TEST_F(BaseFileTest, MultipleWritesWithHash) {
341  // Calculate the final hash.
342  ResetHash();
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());
349
350  std::string hash;
351  MakeFileWithHash();
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();
359
360  EXPECT_TRUE(base_file_->GetHash(&hash));
361  EXPECT_EQ("CBF68BF10F8003DB86B31343AFAC8C7175BD03FB5FC905650F8C80AF087443A8",
362            expected_hash_hex);
363  EXPECT_EQ(expected_hash_hex, base::HexEncode(hash.data(), hash.size()));
364}
365
366// Write data to the file multiple times, interrupt it, and continue using
367// another file.  Calculate the resulting combined sha256 hash.
368TEST_F(BaseFileTest, MultipleWritesInterruptedWithHash) {
369  // Calculate the final hash.
370  ResetHash();
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());
377
378  MakeFileWithHash();
379  ASSERT_TRUE(InitializeFile());
380  // Write some data
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();
386  // Finish the file.
387  base_file_->Finish();
388
389  base::FilePath new_file_path(temp_dir_.path().Append(
390      base::FilePath(FILE_PATH_LITERAL("second_file"))));
391
392  ASSERT_TRUE(base::CopyFile(base_file_->full_path(), new_file_path));
393
394  // Create another file
395  BaseFile second_file(new_file_path,
396                       GURL(),
397                       GURL(),
398                       base_file_->bytes_so_far(),
399                       true,
400                       hash_state,
401                       base::File(),
402                       net::BoundNetLog());
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();
409
410  std::string hash;
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());
415}
416
417// Rename the file after all writes to it.
418TEST_F(BaseFileTest, WriteThenRename) {
419  ASSERT_TRUE(InitializeFile());
420
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));
425
426  ASSERT_TRUE(AppendDataToFile(kTestData1));
427
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));
432
433  base_file_->Finish();
434}
435
436// Rename the file while the download is still in progress.
437TEST_F(BaseFileTest, RenameWhileInProgress) {
438  ASSERT_TRUE(InitializeFile());
439
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));
444
445  ASSERT_TRUE(AppendDataToFile(kTestData1));
446
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));
451
452  ASSERT_TRUE(AppendDataToFile(kTestData2));
453
454  base_file_->Finish();
455}
456
457// Test that a failed rename reports the correct error.
458TEST_F(BaseFileTest, RenameWithError) {
459  ASSERT_TRUE(InitializeFile());
460
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));
465
466  base::FilePath new_path(test_dir.AppendASCII("TestFile"));
467  EXPECT_FALSE(base::PathExists(new_path));
468
469  {
470    file_util::PermissionRestorer restore_permissions_for(test_dir);
471    ASSERT_TRUE(file_util::MakeFileUnwritable(test_dir));
472    EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED,
473              base_file_->Rename(new_path));
474  }
475
476  base_file_->Finish();
477}
478
479// Test that a failed write reports an error.
480TEST_F(BaseFileTest, WriteWithError) {
481  base::FilePath path;
482  ASSERT_TRUE(base::CreateTemporaryFile(&path));
483
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,
488                                GURL(),
489                                GURL(),
490                                0,
491                                false,
492                                std::string(),
493                                file.Pass(),
494                                net::BoundNetLog()));
495  ASSERT_TRUE(InitializeFile());
496#if defined(OS_WIN)
497  set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED);
498#elif defined (OS_POSIX)
499  set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
500#endif
501  ASSERT_FALSE(AppendDataToFile(kTestData1));
502  base_file_->Finish();
503}
504
505// Try to write to uninitialized file.
506TEST_F(BaseFileTest, UninitializedFile) {
507  expect_in_progress_ = false;
508  set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
509  EXPECT_FALSE(AppendDataToFile(kTestData1));
510}
511
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'.
515TEST_F(BaseFileTest, DuplicateBaseFile) {
516  ASSERT_TRUE(InitializeFile());
517
518  // Create another |BaseFile| referring to the file that |base_file_| owns.
519  CreateFileWithName(base_file_->full_path());
520
521  ASSERT_TRUE(AppendDataToFile(kTestData1));
522  base_file_->Finish();
523}
524
525// Create a file and append to it.
526TEST_F(BaseFileTest, AppendToBaseFile) {
527  // Create a new file.
528  base::FilePath existing_file_name = CreateTestFile();
529
530  set_expected_data(kTestData4);
531
532  // Use the file we've just created.
533  base_file_.reset(new BaseFile(existing_file_name,
534                                GURL(),
535                                GURL(),
536                                kTestDataLength4,
537                                false,
538                                std::string(),
539                                base::File(),
540                                net::BoundNetLog()));
541
542  ASSERT_TRUE(InitializeFile());
543
544  const base::FilePath file_name = base_file_->full_path();
545  EXPECT_NE(base::FilePath::StringType(), file_name.value());
546
547  // Write into the file.
548  EXPECT_TRUE(AppendDataToFile(kTestData1));
549
550  base_file_->Finish();
551  base_file_->Detach();
552  expect_file_survives_ = true;
553}
554
555// Create a read-only file and attempt to write to it.
556TEST_F(BaseFileTest, ReadonlyBaseFile) {
557  // Create a new file.
558  base::FilePath readonly_file_name = CreateTestFile();
559
560  // Restore permissions to the file when we are done with this test.
561  file_util::PermissionRestorer restore_permissions(readonly_file_name);
562
563  // Make it read-only.
564  EXPECT_TRUE(file_util::MakeFileUnwritable(readonly_file_name));
565
566  // Try to overwrite it.
567  base_file_.reset(new BaseFile(readonly_file_name,
568                                GURL(),
569                                GURL(),
570                                0,
571                                false,
572                                std::string(),
573                                base::File(),
574                                net::BoundNetLog()));
575
576  expect_in_progress_ = false;
577  set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED);
578  EXPECT_FALSE(InitializeFile());
579
580  const base::FilePath file_name = base_file_->full_path();
581  EXPECT_NE(base::FilePath::StringType(), file_name.value());
582
583  // Write into the file.
584  set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
585  EXPECT_FALSE(AppendDataToFile(kTestData1));
586
587  base_file_->Finish();
588  base_file_->Detach();
589  expect_file_survives_ = true;
590}
591
592TEST_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()));
598
599  std::string also_not_empty = empty;
600  also_not_empty[crypto::kSHA256Length - 1] = '\x01';
601  EXPECT_FALSE(BaseFile::IsEmptyHash(also_not_empty));
602}
603
604// Test that a temporary file is created in the default download directory.
605TEST_F(BaseFileTest, CreatedInDefaultDirectory) {
606  ASSERT_TRUE(base_file_->full_path().empty());
607  ASSERT_TRUE(InitializeFile());
608  EXPECT_FALSE(base_file_->full_path().empty());
609
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();
620}
621
622TEST_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));
627
628  base_file_->Cancel();
629  ASSERT_FALSE(base::PathExists(full_path));
630
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;
637}
638
639}  // namespace content
640