1// Copyright 2013 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/fileapi/upload_file_system_file_element_reader.h"
6
7#include "base/files/scoped_temp_dir.h"
8#include "base/message_loop/message_loop.h"
9#include "base/run_loop.h"
10#include "content/public/test/async_file_test_helper.h"
11#include "content/public/test/test_file_system_context.h"
12#include "net/base/io_buffer.h"
13#include "net/base/test_completion_callback.h"
14#include "storage/browser/fileapi/file_system_backend.h"
15#include "storage/browser/fileapi/file_system_context.h"
16#include "storage/browser/fileapi/file_system_operation_context.h"
17#include "storage/browser/fileapi/file_system_url.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20using content::AsyncFileTestHelper;
21using storage::FileSystemContext;
22using storage::FileSystemType;
23using storage::FileSystemURL;
24
25namespace content {
26
27namespace {
28
29const char kFileSystemURLOrigin[] = "http://remote";
30const storage::FileSystemType kFileSystemType =
31    storage::kFileSystemTypeTemporary;
32
33}  // namespace
34
35class UploadFileSystemFileElementReaderTest : public testing::Test {
36 public:
37  UploadFileSystemFileElementReaderTest() {}
38
39  virtual void SetUp() OVERRIDE {
40    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
41
42    file_system_context_ = CreateFileSystemContextForTesting(
43        NULL, temp_dir_.path());
44
45    file_system_context_->OpenFileSystem(
46        GURL(kFileSystemURLOrigin),
47        kFileSystemType,
48        storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
49        base::Bind(&UploadFileSystemFileElementReaderTest::OnOpenFileSystem,
50                   base::Unretained(this)));
51    base::RunLoop().RunUntilIdle();
52    ASSERT_TRUE(file_system_root_url_.is_valid());
53
54    // Prepare a file on file system.
55    const char kTestData[] = "abcdefghijklmnop0123456789";
56    file_data_.assign(kTestData, kTestData + arraysize(kTestData) - 1);
57    const char kFilename[] = "File.dat";
58    file_url_ = GetFileSystemURL(kFilename);
59    WriteFileSystemFile(kFilename, &file_data_[0], file_data_.size(),
60                        &file_modification_time_);
61
62    // Create and initialize a reader.
63    reader_.reset(
64        new UploadFileSystemFileElementReader(file_system_context_.get(),
65                                              file_url_,
66                                              0,
67                                              kuint64max,
68                                              file_modification_time_));
69    net::TestCompletionCallback callback;
70    ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(callback.callback()));
71    EXPECT_EQ(net::OK, callback.WaitForResult());
72    EXPECT_EQ(file_data_.size(), reader_->GetContentLength());
73    EXPECT_EQ(file_data_.size(), reader_->BytesRemaining());
74    EXPECT_FALSE(reader_->IsInMemory());
75  }
76
77 virtual void TearDown() OVERRIDE {
78    reader_.reset();
79    base::RunLoop().RunUntilIdle();
80 }
81
82 protected:
83  GURL GetFileSystemURL(const std::string& filename) {
84    return GURL(file_system_root_url_.spec() + filename);
85  }
86
87  void WriteFileSystemFile(const std::string& filename,
88                           const char* buf,
89                           int buf_size,
90                           base::Time* modification_time) {
91    storage::FileSystemURL url =
92        file_system_context_->CreateCrackedFileSystemURL(
93            GURL(kFileSystemURLOrigin),
94            kFileSystemType,
95            base::FilePath().AppendASCII(filename));
96
97    ASSERT_EQ(base::File::FILE_OK,
98              AsyncFileTestHelper::CreateFileWithData(
99                  file_system_context_.get(), url, buf, buf_size));
100
101    base::File::Info file_info;
102    ASSERT_EQ(base::File::FILE_OK,
103              AsyncFileTestHelper::GetMetadata(
104                  file_system_context_.get(), url, &file_info));
105    *modification_time = file_info.last_modified;
106  }
107
108  void OnOpenFileSystem(const GURL& root,
109                        const std::string& name,
110                        base::File::Error result) {
111    ASSERT_EQ(base::File::FILE_OK, result);
112    ASSERT_TRUE(root.is_valid());
113    file_system_root_url_ = root;
114  }
115
116  base::MessageLoopForIO message_loop_;
117  base::ScopedTempDir temp_dir_;
118  scoped_refptr<FileSystemContext> file_system_context_;
119  GURL file_system_root_url_;
120  std::vector<char> file_data_;
121  GURL file_url_;
122  base::Time file_modification_time_;
123  scoped_ptr<UploadFileSystemFileElementReader> reader_;
124};
125
126TEST_F(UploadFileSystemFileElementReaderTest, ReadAll) {
127  scoped_refptr<net::IOBufferWithSize> buf =
128      new net::IOBufferWithSize(file_data_.size());
129  net::TestCompletionCallback read_callback;
130  ASSERT_EQ(net::ERR_IO_PENDING,
131            reader_->Read(buf.get(), buf->size(), read_callback.callback()));
132  EXPECT_EQ(buf->size(), read_callback.WaitForResult());
133  EXPECT_EQ(0U, reader_->BytesRemaining());
134  EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.end(), buf->data()));
135  // Try to read again.
136  EXPECT_EQ(0, reader_->Read(buf.get(), buf->size(), read_callback.callback()));
137}
138
139TEST_F(UploadFileSystemFileElementReaderTest, ReadPartially) {
140  const size_t kHalfSize = file_data_.size() / 2;
141  ASSERT_EQ(file_data_.size(), kHalfSize * 2);
142
143  scoped_refptr<net::IOBufferWithSize> buf =
144      new net::IOBufferWithSize(kHalfSize);
145
146  net::TestCompletionCallback read_callback1;
147  ASSERT_EQ(net::ERR_IO_PENDING,
148            reader_->Read(buf.get(), buf->size(), read_callback1.callback()));
149  EXPECT_EQ(buf->size(), read_callback1.WaitForResult());
150  EXPECT_EQ(file_data_.size() - buf->size(), reader_->BytesRemaining());
151  EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.begin() + kHalfSize,
152                         buf->data()));
153
154  net::TestCompletionCallback read_callback2;
155  EXPECT_EQ(net::ERR_IO_PENDING,
156            reader_->Read(buf.get(), buf->size(), read_callback2.callback()));
157  EXPECT_EQ(buf->size(), read_callback2.WaitForResult());
158  EXPECT_EQ(0U, reader_->BytesRemaining());
159  EXPECT_TRUE(std::equal(file_data_.begin() + kHalfSize, file_data_.end(),
160                         buf->data()));
161}
162
163TEST_F(UploadFileSystemFileElementReaderTest, ReadTooMuch) {
164  const size_t kTooLargeSize = file_data_.size() * 2;
165  scoped_refptr<net::IOBufferWithSize> buf =
166      new net::IOBufferWithSize(kTooLargeSize);
167  net::TestCompletionCallback read_callback;
168  ASSERT_EQ(net::ERR_IO_PENDING,
169            reader_->Read(buf.get(), buf->size(), read_callback.callback()));
170  EXPECT_EQ(static_cast<int>(file_data_.size()), read_callback.WaitForResult());
171  EXPECT_EQ(0U, reader_->BytesRemaining());
172  EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.end(), buf->data()));
173}
174
175TEST_F(UploadFileSystemFileElementReaderTest, MultipleInit) {
176  scoped_refptr<net::IOBufferWithSize> buf =
177      new net::IOBufferWithSize(file_data_.size());
178
179  // Read all.
180  net::TestCompletionCallback read_callback1;
181  ASSERT_EQ(net::ERR_IO_PENDING,
182            reader_->Read(buf.get(), buf->size(), read_callback1.callback()));
183  EXPECT_EQ(buf->size(), read_callback1.WaitForResult());
184  EXPECT_EQ(0U, reader_->BytesRemaining());
185  EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.end(), buf->data()));
186
187  // Call Init() again to reset the state.
188  net::TestCompletionCallback init_callback;
189  ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback.callback()));
190  EXPECT_EQ(net::OK, init_callback.WaitForResult());
191  EXPECT_EQ(file_data_.size(), reader_->GetContentLength());
192  EXPECT_EQ(file_data_.size(), reader_->BytesRemaining());
193
194  // Read again.
195  net::TestCompletionCallback read_callback2;
196  ASSERT_EQ(net::ERR_IO_PENDING,
197            reader_->Read(buf.get(), buf->size(), read_callback2.callback()));
198  EXPECT_EQ(buf->size(), read_callback2.WaitForResult());
199  EXPECT_EQ(0U, reader_->BytesRemaining());
200  EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.end(), buf->data()));
201}
202
203TEST_F(UploadFileSystemFileElementReaderTest, InitDuringAsyncOperation) {
204  scoped_refptr<net::IOBufferWithSize> buf =
205      new net::IOBufferWithSize(file_data_.size());
206
207  // Start reading all.
208  net::TestCompletionCallback read_callback1;
209  EXPECT_EQ(net::ERR_IO_PENDING,
210            reader_->Read(buf.get(), buf->size(), read_callback1.callback()));
211
212  // Call Init to cancel the previous read.
213  net::TestCompletionCallback init_callback1;
214  EXPECT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback1.callback()));
215
216  // Call Init again to cancel the previous init.
217  net::TestCompletionCallback init_callback2;
218  EXPECT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback2.callback()));
219  EXPECT_EQ(net::OK, init_callback2.WaitForResult());
220  EXPECT_EQ(file_data_.size(), reader_->GetContentLength());
221  EXPECT_EQ(file_data_.size(), reader_->BytesRemaining());
222
223  // Read half.
224  scoped_refptr<net::IOBufferWithSize> buf2 =
225      new net::IOBufferWithSize(file_data_.size() / 2);
226  net::TestCompletionCallback read_callback2;
227  EXPECT_EQ(net::ERR_IO_PENDING,
228            reader_->Read(buf2.get(), buf2->size(), read_callback2.callback()));
229  EXPECT_EQ(buf2->size(), read_callback2.WaitForResult());
230  EXPECT_EQ(file_data_.size() - buf2->size(), reader_->BytesRemaining());
231  EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.begin() + buf2->size(),
232                         buf2->data()));
233
234  // Make sure callbacks are not called for cancelled operations.
235  EXPECT_FALSE(read_callback1.have_result());
236  EXPECT_FALSE(init_callback1.have_result());
237}
238
239TEST_F(UploadFileSystemFileElementReaderTest, Range) {
240  const int kOffset = 2;
241  const int kLength = file_data_.size() - kOffset * 3;
242  reader_.reset(new UploadFileSystemFileElementReader(
243      file_system_context_.get(), file_url_, kOffset, kLength, base::Time()));
244  net::TestCompletionCallback init_callback;
245  ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback.callback()));
246  EXPECT_EQ(net::OK, init_callback.WaitForResult());
247  EXPECT_EQ(static_cast<uint64>(kLength), reader_->GetContentLength());
248  EXPECT_EQ(static_cast<uint64>(kLength), reader_->BytesRemaining());
249  scoped_refptr<net::IOBufferWithSize> buf = new net::IOBufferWithSize(kLength);
250  net::TestCompletionCallback read_callback;
251  ASSERT_EQ(net::ERR_IO_PENDING,
252            reader_->Read(buf.get(), buf->size(), read_callback.callback()));
253  EXPECT_EQ(kLength, read_callback.WaitForResult());
254  EXPECT_TRUE(std::equal(file_data_.begin() + kOffset,
255                         file_data_.begin() + kOffset + kLength,
256                         buf->data()));
257}
258
259TEST_F(UploadFileSystemFileElementReaderTest, FileChanged) {
260  // Expect one second before the actual modification time to simulate change.
261  const base::Time expected_modification_time =
262      file_modification_time_ - base::TimeDelta::FromSeconds(1);
263  reader_.reset(
264      new UploadFileSystemFileElementReader(file_system_context_.get(),
265                                            file_url_,
266                                            0,
267                                            kuint64max,
268                                            expected_modification_time));
269  net::TestCompletionCallback init_callback;
270  ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback.callback()));
271  EXPECT_EQ(net::ERR_UPLOAD_FILE_CHANGED, init_callback.WaitForResult());
272}
273
274TEST_F(UploadFileSystemFileElementReaderTest, WrongURL) {
275  const GURL wrong_url = GetFileSystemURL("wrong_file_name.dat");
276  reader_.reset(new UploadFileSystemFileElementReader(
277      file_system_context_.get(), wrong_url, 0, kuint64max, base::Time()));
278  net::TestCompletionCallback init_callback;
279  ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback.callback()));
280  EXPECT_EQ(net::ERR_FILE_NOT_FOUND, init_callback.WaitForResult());
281}
282
283}  // namespace content
284