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 "base/file_util.h"
6#include "base/message_loop/message_loop.h"
7#include "base/strings/string_number_conversions.h"
8#include "base/test/test_file_util.h"
9#include "content/browser/browser_thread_impl.h"
10#include "content/browser/byte_stream.h"
11#include "content/browser/download/download_create_info.h"
12#include "content/browser/download/download_file_impl.h"
13#include "content/browser/download/download_request_handle.h"
14#include "content/public/browser/download_destination_observer.h"
15#include "content/public/browser/download_interrupt_reasons.h"
16#include "content/public/browser/download_manager.h"
17#include "content/public/test/mock_download_manager.h"
18#include "net/base/file_stream.h"
19#include "net/base/mock_file_stream.h"
20#include "net/base/net_errors.h"
21#include "testing/gmock/include/gmock/gmock.h"
22#include "testing/gtest/include/gtest/gtest.h"
23
24using ::testing::_;
25using ::testing::AnyNumber;
26using ::testing::DoAll;
27using ::testing::InSequence;
28using ::testing::Return;
29using ::testing::SetArgPointee;
30using ::testing::StrictMock;
31
32namespace content {
33namespace {
34
35class MockByteStreamReader : public ByteStreamReader {
36 public:
37  MockByteStreamReader() {}
38  ~MockByteStreamReader() {}
39
40  // ByteStream functions
41  MOCK_METHOD2(Read, ByteStreamReader::StreamState(
42      scoped_refptr<net::IOBuffer>*, size_t*));
43  MOCK_CONST_METHOD0(GetStatus, int());
44  MOCK_METHOD1(RegisterCallback, void(const base::Closure&));
45};
46
47class MockDownloadDestinationObserver : public DownloadDestinationObserver {
48 public:
49  MOCK_METHOD3(DestinationUpdate, void(int64, int64, const std::string&));
50  MOCK_METHOD1(DestinationError, void(DownloadInterruptReason));
51  MOCK_METHOD1(DestinationCompleted, void(const std::string&));
52
53  // Doesn't override any methods in the base class.  Used to make sure
54  // that the last DestinationUpdate before a Destination{Completed,Error}
55  // had the right values.
56  MOCK_METHOD3(CurrentUpdateStatus,
57               void(int64, int64, const std::string&));
58};
59
60MATCHER(IsNullCallback, "") { return (arg.is_null()); }
61
62}  // namespace
63
64class DownloadFileTest : public testing::Test {
65 public:
66
67  static const char* kTestData1;
68  static const char* kTestData2;
69  static const char* kTestData3;
70  static const char* kDataHash;
71  static const uint32 kDummyDownloadId;
72  static const int kDummyChildId;
73  static const int kDummyRequestId;
74
75  DownloadFileTest() :
76      observer_(new StrictMock<MockDownloadDestinationObserver>),
77      observer_factory_(observer_.get()),
78      input_stream_(NULL),
79      bytes_(-1),
80      bytes_per_sec_(-1),
81      hash_state_("xyzzy"),
82      ui_thread_(BrowserThread::UI, &loop_),
83      file_thread_(BrowserThread::FILE, &loop_) {
84  }
85
86  virtual ~DownloadFileTest() {
87  }
88
89  void SetUpdateDownloadInfo(int64 bytes, int64 bytes_per_sec,
90                             const std::string& hash_state) {
91    bytes_ = bytes;
92    bytes_per_sec_ = bytes_per_sec;
93    hash_state_ = hash_state;
94  }
95
96  void ConfirmUpdateDownloadInfo() {
97    observer_->CurrentUpdateStatus(bytes_, bytes_per_sec_, hash_state_);
98  }
99
100  virtual void SetUp() {
101    EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _, _))
102        .Times(AnyNumber())
103        .WillRepeatedly(Invoke(this, &DownloadFileTest::SetUpdateDownloadInfo));
104  }
105
106  // Mock calls to this function are forwarded here.
107  void RegisterCallback(base::Closure sink_callback) {
108    sink_callback_ = sink_callback;
109  }
110
111  void SetInterruptReasonCallback(bool* was_called,
112                                  DownloadInterruptReason* reason_p,
113                                  DownloadInterruptReason reason) {
114    *was_called = true;
115    *reason_p = reason;
116  }
117
118  bool CreateDownloadFile(int offset, bool calculate_hash) {
119    // There can be only one.
120    DCHECK(!download_file_.get());
121
122    input_stream_ = new StrictMock<MockByteStreamReader>();
123
124    // TODO: Need to actually create a function that'll set the variables
125    // based on the inputs from the callback.
126    EXPECT_CALL(*input_stream_, RegisterCallback(_))
127        .WillOnce(Invoke(this, &DownloadFileTest::RegisterCallback))
128        .RetiresOnSaturation();
129
130    scoped_ptr<DownloadSaveInfo> save_info(new DownloadSaveInfo());
131    download_file_.reset(
132        new DownloadFileImpl(save_info.Pass(),
133                             base::FilePath(),
134                             GURL(),  // Source
135                             GURL(),  // Referrer
136                             calculate_hash,
137                             scoped_ptr<ByteStreamReader>(input_stream_),
138                             net::BoundNetLog(),
139                             observer_factory_.GetWeakPtr()));
140    download_file_->SetClientGuid(
141        "12345678-ABCD-1234-DCBA-123456789ABC");
142
143    EXPECT_CALL(*input_stream_, Read(_, _))
144        .WillOnce(Return(ByteStreamReader::STREAM_EMPTY))
145        .RetiresOnSaturation();
146
147    base::WeakPtrFactory<DownloadFileTest> weak_ptr_factory(this);
148    bool called = false;
149    DownloadInterruptReason result;
150    download_file_->Initialize(base::Bind(
151        &DownloadFileTest::SetInterruptReasonCallback,
152        weak_ptr_factory.GetWeakPtr(), &called, &result));
153    loop_.RunUntilIdle();
154    EXPECT_TRUE(called);
155
156    ::testing::Mock::VerifyAndClearExpectations(input_stream_);
157    return result == DOWNLOAD_INTERRUPT_REASON_NONE;
158  }
159
160  void DestroyDownloadFile(int offset) {
161    EXPECT_FALSE(download_file_->InProgress());
162
163    // Make sure the data has been properly written to disk.
164    std::string disk_data;
165    EXPECT_TRUE(base::ReadFileToString(download_file_->FullPath(), &disk_data));
166    EXPECT_EQ(expected_data_, disk_data);
167
168    // Make sure the Browser and File threads outlive the DownloadFile
169    // to satisfy thread checks inside it.
170    download_file_.reset();
171  }
172
173  // Setup the stream to do be a data append; don't actually trigger
174  // the callback or do verifications.
175  void SetupDataAppend(const char **data_chunks, size_t num_chunks,
176                       ::testing::Sequence s) {
177    DCHECK(input_stream_);
178    for (size_t i = 0; i < num_chunks; i++) {
179      const char *source_data = data_chunks[i];
180      size_t length = strlen(source_data);
181      scoped_refptr<net::IOBuffer> data = new net::IOBuffer(length);
182      memcpy(data->data(), source_data, length);
183      EXPECT_CALL(*input_stream_, Read(_, _))
184          .InSequence(s)
185          .WillOnce(DoAll(SetArgPointee<0>(data),
186                          SetArgPointee<1>(length),
187                          Return(ByteStreamReader::STREAM_HAS_DATA)))
188          .RetiresOnSaturation();
189      expected_data_ += source_data;
190    }
191  }
192
193  void VerifyStreamAndSize() {
194    ::testing::Mock::VerifyAndClearExpectations(input_stream_);
195    int64 size;
196    EXPECT_TRUE(base::GetFileSize(download_file_->FullPath(), &size));
197    EXPECT_EQ(expected_data_.size(), static_cast<size_t>(size));
198  }
199
200  // TODO(rdsmith): Manage full percentage issues properly.
201  void AppendDataToFile(const char **data_chunks, size_t num_chunks) {
202    ::testing::Sequence s1;
203    SetupDataAppend(data_chunks, num_chunks, s1);
204    EXPECT_CALL(*input_stream_, Read(_, _))
205        .InSequence(s1)
206        .WillOnce(Return(ByteStreamReader::STREAM_EMPTY))
207        .RetiresOnSaturation();
208    sink_callback_.Run();
209    VerifyStreamAndSize();
210  }
211
212  void SetupFinishStream(DownloadInterruptReason interrupt_reason,
213                       ::testing::Sequence s) {
214    EXPECT_CALL(*input_stream_, Read(_, _))
215        .InSequence(s)
216        .WillOnce(Return(ByteStreamReader::STREAM_COMPLETE))
217        .RetiresOnSaturation();
218    EXPECT_CALL(*input_stream_, GetStatus())
219        .InSequence(s)
220        .WillOnce(Return(interrupt_reason))
221        .RetiresOnSaturation();
222    EXPECT_CALL(*input_stream_, RegisterCallback(_))
223        .RetiresOnSaturation();
224  }
225
226  void FinishStream(DownloadInterruptReason interrupt_reason,
227                    bool check_observer) {
228    ::testing::Sequence s1;
229    SetupFinishStream(interrupt_reason, s1);
230    sink_callback_.Run();
231    VerifyStreamAndSize();
232    if (check_observer) {
233      EXPECT_CALL(*(observer_.get()), DestinationCompleted(_));
234      loop_.RunUntilIdle();
235      ::testing::Mock::VerifyAndClearExpectations(observer_.get());
236      EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _, _))
237          .Times(AnyNumber())
238          .WillRepeatedly(Invoke(this,
239                                 &DownloadFileTest::SetUpdateDownloadInfo));
240    }
241  }
242
243  DownloadInterruptReason RenameAndUniquify(
244      const base::FilePath& full_path,
245      base::FilePath* result_path_p) {
246    base::WeakPtrFactory<DownloadFileTest> weak_ptr_factory(this);
247    DownloadInterruptReason result_reason(DOWNLOAD_INTERRUPT_REASON_NONE);
248    bool callback_was_called(false);
249    base::FilePath result_path;
250
251    download_file_->RenameAndUniquify(
252        full_path, base::Bind(&DownloadFileTest::SetRenameResult,
253                              weak_ptr_factory.GetWeakPtr(),
254                              &callback_was_called,
255                              &result_reason, result_path_p));
256    loop_.RunUntilIdle();
257
258    EXPECT_TRUE(callback_was_called);
259    return result_reason;
260  }
261
262  DownloadInterruptReason RenameAndAnnotate(
263      const base::FilePath& full_path,
264      base::FilePath* result_path_p) {
265    base::WeakPtrFactory<DownloadFileTest> weak_ptr_factory(this);
266    DownloadInterruptReason result_reason(DOWNLOAD_INTERRUPT_REASON_NONE);
267    bool callback_was_called(false);
268    base::FilePath result_path;
269
270    download_file_->RenameAndAnnotate(
271        full_path, base::Bind(&DownloadFileTest::SetRenameResult,
272                              weak_ptr_factory.GetWeakPtr(),
273                              &callback_was_called,
274                              &result_reason, result_path_p));
275    loop_.RunUntilIdle();
276
277    EXPECT_TRUE(callback_was_called);
278    return result_reason;
279  }
280
281 protected:
282  scoped_ptr<StrictMock<MockDownloadDestinationObserver> > observer_;
283  base::WeakPtrFactory<DownloadDestinationObserver> observer_factory_;
284
285  // DownloadFile instance we are testing.
286  scoped_ptr<DownloadFile> download_file_;
287
288  // Stream for sending data into the download file.
289  // Owned by download_file_; will be alive for lifetime of download_file_.
290  StrictMock<MockByteStreamReader>* input_stream_;
291
292  // Sink callback data for stream.
293  base::Closure sink_callback_;
294
295  // Latest update sent to the observer.
296  int64 bytes_;
297  int64 bytes_per_sec_;
298  std::string hash_state_;
299
300  base::MessageLoop loop_;
301
302 private:
303  void SetRenameResult(bool* called_p,
304                       DownloadInterruptReason* reason_p,
305                       base::FilePath* result_path_p,
306                       DownloadInterruptReason reason,
307                       const base::FilePath& result_path) {
308    if (called_p)
309      *called_p = true;
310    if (reason_p)
311      *reason_p = reason;
312    if (result_path_p)
313      *result_path_p = result_path;
314  }
315
316  // UI thread.
317  BrowserThreadImpl ui_thread_;
318  // File thread to satisfy debug checks in DownloadFile.
319  BrowserThreadImpl file_thread_;
320
321  // Keep track of what data should be saved to the disk file.
322  std::string expected_data_;
323};
324
325const char* DownloadFileTest::kTestData1 =
326    "Let's write some data to the file!\n";
327const char* DownloadFileTest::kTestData2 = "Writing more data.\n";
328const char* DownloadFileTest::kTestData3 = "Final line.";
329const char* DownloadFileTest::kDataHash =
330    "CBF68BF10F8003DB86B31343AFAC8C7175BD03FB5FC905650F8C80AF087443A8";
331
332const uint32 DownloadFileTest::kDummyDownloadId = 23;
333const int DownloadFileTest::kDummyChildId = 3;
334const int DownloadFileTest::kDummyRequestId = 67;
335
336// Rename the file before any data is downloaded, after some has, after it all
337// has, and after it's closed.
338TEST_F(DownloadFileTest, RenameFileFinal) {
339  ASSERT_TRUE(CreateDownloadFile(0, true));
340  base::FilePath initial_path(download_file_->FullPath());
341  EXPECT_TRUE(base::PathExists(initial_path));
342  base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1"));
343  base::FilePath path_2(initial_path.InsertBeforeExtensionASCII("_2"));
344  base::FilePath path_3(initial_path.InsertBeforeExtensionASCII("_3"));
345  base::FilePath path_4(initial_path.InsertBeforeExtensionASCII("_4"));
346  base::FilePath path_5(initial_path.InsertBeforeExtensionASCII("_5"));
347  base::FilePath output_path;
348
349  // Rename the file before downloading any data.
350  EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
351            RenameAndUniquify(path_1, &output_path));
352  base::FilePath renamed_path = download_file_->FullPath();
353  EXPECT_EQ(path_1, renamed_path);
354  EXPECT_EQ(path_1, output_path);
355
356  // Check the files.
357  EXPECT_FALSE(base::PathExists(initial_path));
358  EXPECT_TRUE(base::PathExists(path_1));
359
360  // Download the data.
361  const char* chunks1[] = { kTestData1, kTestData2 };
362  AppendDataToFile(chunks1, 2);
363
364  // Rename the file after downloading some data.
365  EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
366            RenameAndUniquify(path_2, &output_path));
367  renamed_path = download_file_->FullPath();
368  EXPECT_EQ(path_2, renamed_path);
369  EXPECT_EQ(path_2, output_path);
370
371  // Check the files.
372  EXPECT_FALSE(base::PathExists(path_1));
373  EXPECT_TRUE(base::PathExists(path_2));
374
375  const char* chunks2[] = { kTestData3 };
376  AppendDataToFile(chunks2, 1);
377
378  // Rename the file after downloading all the data.
379  EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
380            RenameAndUniquify(path_3, &output_path));
381  renamed_path = download_file_->FullPath();
382  EXPECT_EQ(path_3, renamed_path);
383  EXPECT_EQ(path_3, output_path);
384
385  // Check the files.
386  EXPECT_FALSE(base::PathExists(path_2));
387  EXPECT_TRUE(base::PathExists(path_3));
388
389  // Should not be able to get the hash until the file is closed.
390  std::string hash;
391  EXPECT_FALSE(download_file_->GetHash(&hash));
392  FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
393  loop_.RunUntilIdle();
394
395  // Rename the file after downloading all the data and closing the file.
396  EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
397            RenameAndUniquify(path_4, &output_path));
398  renamed_path = download_file_->FullPath();
399  EXPECT_EQ(path_4, renamed_path);
400  EXPECT_EQ(path_4, output_path);
401
402  // Check the files.
403  EXPECT_FALSE(base::PathExists(path_3));
404  EXPECT_TRUE(base::PathExists(path_4));
405
406  // Check the hash.
407  EXPECT_TRUE(download_file_->GetHash(&hash));
408  EXPECT_EQ(kDataHash, base::HexEncode(hash.data(), hash.size()));
409
410  // Check that a rename with overwrite to an existing file succeeds.
411  std::string file_contents;
412  ASSERT_FALSE(base::PathExists(path_5));
413  static const char file_data[] = "xyzzy";
414  ASSERT_EQ(static_cast<int>(sizeof(file_data) - 1),
415            base::WriteFile(path_5, file_data, sizeof(file_data) - 1));
416  ASSERT_TRUE(base::PathExists(path_5));
417  EXPECT_TRUE(base::ReadFileToString(path_5, &file_contents));
418  EXPECT_EQ(std::string(file_data), file_contents);
419
420  EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
421            RenameAndAnnotate(path_5, &output_path));
422  EXPECT_EQ(path_5, output_path);
423
424  file_contents = "";
425  EXPECT_TRUE(base::ReadFileToString(path_5, &file_contents));
426  EXPECT_NE(std::string(file_data), file_contents);
427
428  DestroyDownloadFile(0);
429}
430
431// Test to make sure the rename uniquifies if we aren't overwriting
432// and there's a file where we're aiming.
433TEST_F(DownloadFileTest, RenameUniquifies) {
434  ASSERT_TRUE(CreateDownloadFile(0, true));
435  base::FilePath initial_path(download_file_->FullPath());
436  EXPECT_TRUE(base::PathExists(initial_path));
437  base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1"));
438  base::FilePath path_1_suffixed(path_1.InsertBeforeExtensionASCII(" (1)"));
439
440  ASSERT_FALSE(base::PathExists(path_1));
441  static const char file_data[] = "xyzzy";
442  ASSERT_EQ(static_cast<int>(sizeof(file_data)),
443            base::WriteFile(path_1, file_data, sizeof(file_data)));
444  ASSERT_TRUE(base::PathExists(path_1));
445
446  EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, RenameAndUniquify(path_1, NULL));
447  EXPECT_TRUE(base::PathExists(path_1_suffixed));
448
449  FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
450  loop_.RunUntilIdle();
451  DestroyDownloadFile(0);
452}
453
454// Test to make sure we get the proper error on failure.
455TEST_F(DownloadFileTest, RenameError) {
456  ASSERT_TRUE(CreateDownloadFile(0, true));
457  base::FilePath initial_path(download_file_->FullPath());
458
459  // Create a subdirectory.
460  base::FilePath tempdir(
461      initial_path.DirName().Append(FILE_PATH_LITERAL("tempdir")));
462  ASSERT_TRUE(base::CreateDirectory(tempdir));
463  base::FilePath target_path(tempdir.Append(initial_path.BaseName()));
464
465  // Targets
466  base::FilePath target_path_suffixed(
467      target_path.InsertBeforeExtensionASCII(" (1)"));
468  ASSERT_FALSE(base::PathExists(target_path));
469  ASSERT_FALSE(base::PathExists(target_path_suffixed));
470
471  // Make the directory unwritable and try to rename within it.
472  {
473    file_util::PermissionRestorer restorer(tempdir);
474    ASSERT_TRUE(file_util::MakeFileUnwritable(tempdir));
475
476    // Expect nulling out of further processing.
477    EXPECT_CALL(*input_stream_, RegisterCallback(IsNullCallback()));
478    EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED,
479              RenameAndAnnotate(target_path, NULL));
480    EXPECT_FALSE(base::PathExists(target_path_suffixed));
481  }
482
483  FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
484  loop_.RunUntilIdle();
485  DestroyDownloadFile(0);
486}
487
488// Various tests of the StreamActive method.
489TEST_F(DownloadFileTest, StreamEmptySuccess) {
490  ASSERT_TRUE(CreateDownloadFile(0, true));
491  base::FilePath initial_path(download_file_->FullPath());
492  EXPECT_TRUE(base::PathExists(initial_path));
493
494  // Test that calling the sink_callback_ on an empty stream shouldn't
495  // do anything.
496  AppendDataToFile(NULL, 0);
497
498  // Finish the download this way and make sure we see it on the
499  // observer.
500  EXPECT_CALL(*(observer_.get()), DestinationCompleted(_));
501  FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, false);
502  loop_.RunUntilIdle();
503
504  DestroyDownloadFile(0);
505}
506
507TEST_F(DownloadFileTest, StreamEmptyError) {
508  ASSERT_TRUE(CreateDownloadFile(0, true));
509  base::FilePath initial_path(download_file_->FullPath());
510  EXPECT_TRUE(base::PathExists(initial_path));
511
512  // Finish the download in error and make sure we see it on the
513  // observer.
514  EXPECT_CALL(*(observer_.get()),
515              DestinationError(
516                  DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED))
517      .WillOnce(InvokeWithoutArgs(
518          this, &DownloadFileTest::ConfirmUpdateDownloadInfo));
519
520  // If this next EXPECT_CALL fails flakily, it's probably a real failure.
521  // We'll be getting a stream of UpdateDownload calls from the timer, and
522  // the last one may have the correct information even if the failure
523  // doesn't produce an update, as the timer update may have triggered at the
524  // same time.
525  EXPECT_CALL(*(observer_.get()), CurrentUpdateStatus(0, _, _));
526
527  FinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, false);
528
529  loop_.RunUntilIdle();
530
531  DestroyDownloadFile(0);
532}
533
534TEST_F(DownloadFileTest, StreamNonEmptySuccess) {
535  ASSERT_TRUE(CreateDownloadFile(0, true));
536  base::FilePath initial_path(download_file_->FullPath());
537  EXPECT_TRUE(base::PathExists(initial_path));
538
539  const char* chunks1[] = { kTestData1, kTestData2 };
540  ::testing::Sequence s1;
541  SetupDataAppend(chunks1, 2, s1);
542  SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, s1);
543  EXPECT_CALL(*(observer_.get()), DestinationCompleted(_));
544  sink_callback_.Run();
545  VerifyStreamAndSize();
546  loop_.RunUntilIdle();
547  DestroyDownloadFile(0);
548}
549
550TEST_F(DownloadFileTest, StreamNonEmptyError) {
551  ASSERT_TRUE(CreateDownloadFile(0, true));
552  base::FilePath initial_path(download_file_->FullPath());
553  EXPECT_TRUE(base::PathExists(initial_path));
554
555  const char* chunks1[] = { kTestData1, kTestData2 };
556  ::testing::Sequence s1;
557  SetupDataAppend(chunks1, 2, s1);
558  SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, s1);
559
560  EXPECT_CALL(*(observer_.get()),
561              DestinationError(
562                  DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED))
563      .WillOnce(InvokeWithoutArgs(
564          this, &DownloadFileTest::ConfirmUpdateDownloadInfo));
565
566  // If this next EXPECT_CALL fails flakily, it's probably a real failure.
567  // We'll be getting a stream of UpdateDownload calls from the timer, and
568  // the last one may have the correct information even if the failure
569  // doesn't produce an update, as the timer update may have triggered at the
570  // same time.
571  EXPECT_CALL(*(observer_.get()),
572              CurrentUpdateStatus(strlen(kTestData1) + strlen(kTestData2),
573                                  _, _));
574
575  sink_callback_.Run();
576  loop_.RunUntilIdle();
577  VerifyStreamAndSize();
578  DestroyDownloadFile(0);
579}
580
581// Send some data, wait 3/4s of a second, run the message loop, and
582// confirm the values the observer received are correct.
583TEST_F(DownloadFileTest, ConfirmUpdate) {
584  CreateDownloadFile(0, true);
585
586  const char* chunks1[] = { kTestData1, kTestData2 };
587  AppendDataToFile(chunks1, 2);
588
589  // Run the message loops for 750ms and check for results.
590  loop_.PostDelayedTask(FROM_HERE,
591                        base::MessageLoop::QuitClosure(),
592                        base::TimeDelta::FromMilliseconds(750));
593  loop_.Run();
594
595  EXPECT_EQ(static_cast<int64>(strlen(kTestData1) + strlen(kTestData2)),
596            bytes_);
597  EXPECT_EQ(download_file_->GetHashState(), hash_state_);
598
599  FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
600  DestroyDownloadFile(0);
601}
602
603}  // namespace content
604