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 "storage/browser/fileapi/file_system_file_stream_reader.h"
6
7#include <limits>
8#include <string>
9
10#include "base/files/scoped_temp_dir.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/run_loop.h"
13#include "content/public/test/async_file_test_helper.h"
14#include "content/public/test/test_file_system_context.h"
15#include "net/base/io_buffer.h"
16#include "net/base/net_errors.h"
17#include "net/base/test_completion_callback.h"
18#include "storage/browser/fileapi/external_mount_points.h"
19#include "storage/browser/fileapi/file_system_context.h"
20#include "storage/browser/fileapi/file_system_file_util.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23using content::AsyncFileTestHelper;
24using storage::FileSystemContext;
25using storage::FileSystemFileStreamReader;
26using storage::FileSystemType;
27using storage::FileSystemURL;
28
29namespace content {
30
31namespace {
32
33const char kURLOrigin[] = "http://remote/";
34const char kTestFileName[] = "test.dat";
35const char kTestData[] = "0123456789";
36const int kTestDataSize = arraysize(kTestData) - 1;
37
38void ReadFromReader(storage::FileSystemFileStreamReader* reader,
39                    std::string* data,
40                    size_t size,
41                    int* result) {
42  ASSERT_TRUE(reader != NULL);
43  ASSERT_TRUE(result != NULL);
44  *result = net::OK;
45  net::TestCompletionCallback callback;
46  size_t total_bytes_read = 0;
47  while (total_bytes_read < size) {
48    scoped_refptr<net::IOBufferWithSize> buf(
49        new net::IOBufferWithSize(size - total_bytes_read));
50    int rv = reader->Read(buf.get(), buf->size(), callback.callback());
51    if (rv == net::ERR_IO_PENDING)
52      rv = callback.WaitForResult();
53    if (rv < 0)
54      *result = rv;
55    if (rv <= 0)
56      break;
57    total_bytes_read += rv;
58    data->append(buf->data(), rv);
59  }
60}
61
62void NeverCalled(int unused) { ADD_FAILURE(); }
63
64}  // namespace
65
66class FileSystemFileStreamReaderTest : public testing::Test {
67 public:
68  FileSystemFileStreamReaderTest() {}
69
70  virtual void SetUp() OVERRIDE {
71    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
72
73    file_system_context_ = CreateFileSystemContextForTesting(
74        NULL, temp_dir_.path());
75
76    file_system_context_->OpenFileSystem(
77        GURL(kURLOrigin),
78        storage::kFileSystemTypeTemporary,
79        storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
80        base::Bind(&OnOpenFileSystem));
81    base::RunLoop().RunUntilIdle();
82
83    WriteFile(kTestFileName, kTestData, kTestDataSize,
84              &test_file_modification_time_);
85  }
86
87  virtual void TearDown() OVERRIDE {
88    base::RunLoop().RunUntilIdle();
89  }
90
91 protected:
92  storage::FileSystemFileStreamReader* CreateFileReader(
93      const std::string& file_name,
94      int64 initial_offset,
95      const base::Time& expected_modification_time) {
96    return new FileSystemFileStreamReader(file_system_context_.get(),
97                                          GetFileSystemURL(file_name),
98                                          initial_offset,
99                                          expected_modification_time);
100  }
101
102  base::Time test_file_modification_time() const {
103    return test_file_modification_time_;
104  }
105
106  void WriteFile(const std::string& file_name,
107                 const char* buf,
108                 int buf_size,
109                 base::Time* modification_time) {
110    FileSystemURL url = GetFileSystemURL(file_name);
111
112    ASSERT_EQ(base::File::FILE_OK,
113              content::AsyncFileTestHelper::CreateFileWithData(
114                  file_system_context_.get(), url, buf, buf_size));
115
116    base::File::Info file_info;
117    ASSERT_EQ(base::File::FILE_OK,
118              AsyncFileTestHelper::GetMetadata(
119                  file_system_context_.get(), url, &file_info));
120    if (modification_time)
121      *modification_time = file_info.last_modified;
122  }
123
124 private:
125  static void OnOpenFileSystem(const GURL& root_url,
126                               const std::string& name,
127                               base::File::Error result) {
128    ASSERT_EQ(base::File::FILE_OK, result);
129  }
130
131  FileSystemURL GetFileSystemURL(const std::string& file_name) {
132    return file_system_context_->CreateCrackedFileSystemURL(
133        GURL(kURLOrigin),
134        storage::kFileSystemTypeTemporary,
135        base::FilePath().AppendASCII(file_name));
136  }
137
138  base::MessageLoopForIO message_loop_;
139  base::ScopedTempDir temp_dir_;
140  scoped_refptr<FileSystemContext> file_system_context_;
141  base::Time test_file_modification_time_;
142};
143
144TEST_F(FileSystemFileStreamReaderTest, NonExistent) {
145  const char kFileName[] = "nonexistent";
146  scoped_ptr<FileSystemFileStreamReader> reader(
147      CreateFileReader(kFileName, 0, base::Time()));
148  int result = 0;
149  std::string data;
150  ReadFromReader(reader.get(), &data, 10, &result);
151  ASSERT_EQ(net::ERR_FILE_NOT_FOUND, result);
152  ASSERT_EQ(0U, data.size());
153}
154
155TEST_F(FileSystemFileStreamReaderTest, Empty) {
156  const char kFileName[] = "empty";
157  WriteFile(kFileName, NULL, 0, NULL);
158
159  scoped_ptr<FileSystemFileStreamReader> reader(
160      CreateFileReader(kFileName, 0, base::Time()));
161  int result = 0;
162  std::string data;
163  ReadFromReader(reader.get(), &data, 10, &result);
164  ASSERT_EQ(net::OK, result);
165  ASSERT_EQ(0U, data.size());
166
167  net::TestInt64CompletionCallback callback;
168  int64 length_result = reader->GetLength(callback.callback());
169  if (length_result == net::ERR_IO_PENDING)
170    length_result = callback.WaitForResult();
171  ASSERT_EQ(0, length_result);
172}
173
174TEST_F(FileSystemFileStreamReaderTest, GetLengthNormal) {
175  scoped_ptr<FileSystemFileStreamReader> reader(
176      CreateFileReader(kTestFileName, 0, test_file_modification_time()));
177  net::TestInt64CompletionCallback callback;
178  int64 result = reader->GetLength(callback.callback());
179  if (result == net::ERR_IO_PENDING)
180    result = callback.WaitForResult();
181  ASSERT_EQ(kTestDataSize, result);
182}
183
184TEST_F(FileSystemFileStreamReaderTest, GetLengthAfterModified) {
185  // Pass a fake expected modifictaion time so that the expectation fails.
186  base::Time fake_expected_modification_time =
187      test_file_modification_time() - base::TimeDelta::FromSeconds(10);
188
189  scoped_ptr<FileSystemFileStreamReader> reader(
190      CreateFileReader(kTestFileName, 0, fake_expected_modification_time));
191  net::TestInt64CompletionCallback callback;
192  int64 result = reader->GetLength(callback.callback());
193  if (result == net::ERR_IO_PENDING)
194    result = callback.WaitForResult();
195  ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
196
197  // With NULL expected modification time this should work.
198  reader.reset(CreateFileReader(kTestFileName, 0, base::Time()));
199  result = reader->GetLength(callback.callback());
200  if (result == net::ERR_IO_PENDING)
201    result = callback.WaitForResult();
202  ASSERT_EQ(kTestDataSize, result);
203}
204
205TEST_F(FileSystemFileStreamReaderTest, GetLengthWithOffset) {
206  scoped_ptr<FileSystemFileStreamReader> reader(
207      CreateFileReader(kTestFileName, 3, base::Time()));
208  net::TestInt64CompletionCallback callback;
209  int64 result = reader->GetLength(callback.callback());
210  if (result == net::ERR_IO_PENDING)
211    result = callback.WaitForResult();
212  // Initial offset does not affect the result of GetLength.
213  ASSERT_EQ(kTestDataSize, result);
214}
215
216TEST_F(FileSystemFileStreamReaderTest, ReadNormal) {
217  scoped_ptr<FileSystemFileStreamReader> reader(
218      CreateFileReader(kTestFileName, 0, test_file_modification_time()));
219  int result = 0;
220  std::string data;
221  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
222  ASSERT_EQ(net::OK, result);
223  ASSERT_EQ(kTestData, data);
224}
225
226TEST_F(FileSystemFileStreamReaderTest, ReadAfterModified) {
227  // Pass a fake expected modifictaion time so that the expectation fails.
228  base::Time fake_expected_modification_time =
229      test_file_modification_time() - base::TimeDelta::FromSeconds(10);
230
231  scoped_ptr<FileSystemFileStreamReader> reader(
232      CreateFileReader(kTestFileName, 0, fake_expected_modification_time));
233  int result = 0;
234  std::string data;
235  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
236  ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
237  ASSERT_EQ(0U, data.size());
238
239  // With NULL expected modification time this should work.
240  data.clear();
241  reader.reset(CreateFileReader(kTestFileName, 0, base::Time()));
242  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
243  ASSERT_EQ(net::OK, result);
244  ASSERT_EQ(kTestData, data);
245}
246
247TEST_F(FileSystemFileStreamReaderTest, ReadWithOffset) {
248  scoped_ptr<FileSystemFileStreamReader> reader(
249      CreateFileReader(kTestFileName, 3, base::Time()));
250  int result = 0;
251  std::string data;
252  ReadFromReader(reader.get(), &data, kTestDataSize, &result);
253  ASSERT_EQ(net::OK, result);
254  ASSERT_EQ(&kTestData[3], data);
255}
256
257TEST_F(FileSystemFileStreamReaderTest, DeleteWithUnfinishedRead) {
258  scoped_ptr<FileSystemFileStreamReader> reader(
259      CreateFileReader(kTestFileName, 0, base::Time()));
260
261  net::TestCompletionCallback callback;
262  scoped_refptr<net::IOBufferWithSize> buf(
263      new net::IOBufferWithSize(kTestDataSize));
264  int rv = reader->Read(buf.get(), buf->size(), base::Bind(&NeverCalled));
265  ASSERT_TRUE(rv == net::ERR_IO_PENDING || rv >= 0);
266
267  // Delete immediately.
268  // Should not crash; nor should NeverCalled be callback.
269  reader.reset();
270}
271
272}  // namespace content
273