1 // Copyright 2014 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 "storage/browser/fileapi/local_file_stream_reader.h"
9 #include "base/files/file.h"
10 #include "base/files/file_path.h"
11 #include "base/files/file_util.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/location.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/run_loop.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/threading/thread.h"
18 #include "net/base/io_buffer.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/test_completion_callback.h"
21 #include "testing/gtest/include/gtest/gtest.h"
23 using storage::LocalFileStreamReader
;
29 const char kTestData
[] = "0123456789";
30 const int kTestDataSize
= arraysize(kTestData
) - 1;
32 void ReadFromReader(LocalFileStreamReader
* reader
,
33 std::string
* data
, size_t size
,
35 ASSERT_TRUE(reader
!= NULL
);
36 ASSERT_TRUE(result
!= NULL
);
38 net::TestCompletionCallback callback
;
39 size_t total_bytes_read
= 0;
40 while (total_bytes_read
< size
) {
41 scoped_refptr
<net::IOBufferWithSize
> buf(
42 new net::IOBufferWithSize(size
- total_bytes_read
));
43 int rv
= reader
->Read(buf
.get(), buf
->size(), callback
.callback());
44 if (rv
== net::ERR_IO_PENDING
)
45 rv
= callback
.WaitForResult();
50 total_bytes_read
+= rv
;
51 data
->append(buf
->data(), rv
);
55 void NeverCalled(int) { ADD_FAILURE(); }
56 void EmptyCallback() {}
59 base::MessageLoop::current()->Quit();
64 class LocalFileStreamReaderTest
: public testing::Test
{
66 LocalFileStreamReaderTest()
67 : file_thread_("FileUtilProxyTestFileThread") {}
69 void SetUp() override
{
70 ASSERT_TRUE(file_thread_
.Start());
71 ASSERT_TRUE(dir_
.CreateUniqueTempDir());
73 base::WriteFile(test_path(), kTestData
, kTestDataSize
);
74 base::File::Info info
;
75 ASSERT_TRUE(base::GetFileInfo(test_path(), &info
));
76 test_file_modification_time_
= info
.last_modified
;
79 void TearDown() override
{
80 // Give another chance for deleted streams to perform Close.
81 base::RunLoop().RunUntilIdle();
83 base::RunLoop().RunUntilIdle();
87 LocalFileStreamReader
* CreateFileReader(
88 const base::FilePath
& path
,
90 const base::Time
& expected_modification_time
) {
91 return new LocalFileStreamReader(
95 expected_modification_time
);
98 void TouchTestFile(base::TimeDelta delta
) {
99 base::Time new_modified_time
= test_file_modification_time() + delta
;
100 ASSERT_TRUE(base::TouchFile(test_path(),
101 test_file_modification_time(),
105 base::SingleThreadTaskRunner
* file_task_runner() const {
106 return file_thread_
.task_runner().get();
109 base::FilePath
test_dir() const { return dir_
.path(); }
110 base::FilePath
test_path() const { return dir_
.path().AppendASCII("test"); }
111 base::Time
test_file_modification_time() const {
112 return test_file_modification_time_
;
115 void EnsureFileTaskFinished() {
116 file_task_runner()->PostTaskAndReply(
117 FROM_HERE
, base::Bind(&EmptyCallback
), base::Bind(&QuitLoop
));
118 base::MessageLoop::current()->Run();
122 base::MessageLoopForIO message_loop_
;
123 base::Thread file_thread_
;
124 base::ScopedTempDir dir_
;
125 base::Time test_file_modification_time_
;
128 TEST_F(LocalFileStreamReaderTest
, NonExistent
) {
129 base::FilePath nonexistent_path
= test_dir().AppendASCII("nonexistent");
130 scoped_ptr
<LocalFileStreamReader
> reader(
131 CreateFileReader(nonexistent_path
, 0, base::Time()));
134 ReadFromReader(reader
.get(), &data
, 10, &result
);
135 ASSERT_EQ(net::ERR_FILE_NOT_FOUND
, result
);
136 ASSERT_EQ(0U, data
.size());
139 TEST_F(LocalFileStreamReaderTest
, Empty
) {
140 base::FilePath empty_path
= test_dir().AppendASCII("empty");
141 base::File
file(empty_path
, base::File::FLAG_CREATE
| base::File::FLAG_READ
);
142 ASSERT_TRUE(file
.IsValid());
145 scoped_ptr
<LocalFileStreamReader
> reader(
146 CreateFileReader(empty_path
, 0, base::Time()));
149 ReadFromReader(reader
.get(), &data
, 10, &result
);
150 ASSERT_EQ(net::OK
, result
);
151 ASSERT_EQ(0U, data
.size());
153 net::TestInt64CompletionCallback callback
;
154 int64 length_result
= reader
->GetLength(callback
.callback());
155 if (length_result
== net::ERR_IO_PENDING
)
156 length_result
= callback
.WaitForResult();
157 ASSERT_EQ(0, result
);
160 TEST_F(LocalFileStreamReaderTest
, GetLengthNormal
) {
161 scoped_ptr
<LocalFileStreamReader
> reader(
162 CreateFileReader(test_path(), 0, test_file_modification_time()));
163 net::TestInt64CompletionCallback callback
;
164 int64 result
= reader
->GetLength(callback
.callback());
165 if (result
== net::ERR_IO_PENDING
)
166 result
= callback
.WaitForResult();
167 ASSERT_EQ(kTestDataSize
, result
);
170 TEST_F(LocalFileStreamReaderTest
, GetLengthAfterModified
) {
171 // Touch file so that the file's modification time becomes different
172 // from what we expect.
173 TouchTestFile(base::TimeDelta::FromSeconds(-1));
175 scoped_ptr
<LocalFileStreamReader
> reader(
176 CreateFileReader(test_path(), 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(net::ERR_UPLOAD_FILE_CHANGED
, result
);
183 // With NULL expected modification time this should work.
184 reader
.reset(CreateFileReader(test_path(), 0, base::Time()));
185 result
= reader
->GetLength(callback
.callback());
186 if (result
== net::ERR_IO_PENDING
)
187 result
= callback
.WaitForResult();
188 ASSERT_EQ(kTestDataSize
, result
);
191 TEST_F(LocalFileStreamReaderTest
, GetLengthWithOffset
) {
192 scoped_ptr
<LocalFileStreamReader
> reader(
193 CreateFileReader(test_path(), 3, base::Time()));
194 net::TestInt64CompletionCallback callback
;
195 int64 result
= reader
->GetLength(callback
.callback());
196 if (result
== net::ERR_IO_PENDING
)
197 result
= callback
.WaitForResult();
198 // Initial offset does not affect the result of GetLength.
199 ASSERT_EQ(kTestDataSize
, result
);
202 TEST_F(LocalFileStreamReaderTest
, ReadNormal
) {
203 scoped_ptr
<LocalFileStreamReader
> reader(
204 CreateFileReader(test_path(), 0, test_file_modification_time()));
207 ReadFromReader(reader
.get(), &data
, kTestDataSize
, &result
);
208 ASSERT_EQ(net::OK
, result
);
209 ASSERT_EQ(kTestData
, data
);
212 TEST_F(LocalFileStreamReaderTest
, ReadAfterModified
) {
213 // Touch file so that the file's modification time becomes different
214 // from what we expect. Note that the resolution on some filesystems
215 // is 1s so we can't test with deltas less than that.
216 TouchTestFile(base::TimeDelta::FromSeconds(-1));
217 scoped_ptr
<LocalFileStreamReader
> reader(
218 CreateFileReader(test_path(), 0, test_file_modification_time()));
221 ReadFromReader(reader
.get(), &data
, kTestDataSize
, &result
);
222 EXPECT_EQ(net::ERR_UPLOAD_FILE_CHANGED
, result
);
223 EXPECT_EQ(0U, data
.size());
225 // Due to precision loss converting int64->double->int64 (e.g. through
226 // Blink) the expected/actual time may vary by microseconds. With
227 // modification time delta < 10us this should work.
228 TouchTestFile(base::TimeDelta::FromMicroseconds(1));
230 reader
.reset(CreateFileReader(test_path(), 0, test_file_modification_time()));
231 ReadFromReader(reader
.get(), &data
, kTestDataSize
, &result
);
232 EXPECT_EQ(net::OK
, result
);
233 EXPECT_EQ(kTestData
, data
);
235 // With matching modification times time this should work.
236 TouchTestFile(base::TimeDelta());
238 reader
.reset(CreateFileReader(test_path(), 0, test_file_modification_time()));
239 ReadFromReader(reader
.get(), &data
, kTestDataSize
, &result
);
240 EXPECT_EQ(net::OK
, result
);
241 EXPECT_EQ(kTestData
, data
);
243 // And with NULL expected modification time this should work.
245 reader
.reset(CreateFileReader(test_path(), 0, base::Time()));
246 ReadFromReader(reader
.get(), &data
, kTestDataSize
, &result
);
247 EXPECT_EQ(net::OK
, result
);
248 EXPECT_EQ(kTestData
, data
);
251 TEST_F(LocalFileStreamReaderTest
, ReadWithOffset
) {
252 scoped_ptr
<LocalFileStreamReader
> reader(
253 CreateFileReader(test_path(), 3, base::Time()));
256 ReadFromReader(reader
.get(), &data
, kTestDataSize
, &result
);
257 ASSERT_EQ(net::OK
, result
);
258 ASSERT_EQ(&kTestData
[3], data
);
261 TEST_F(LocalFileStreamReaderTest
, DeleteWithUnfinishedRead
) {
262 scoped_ptr
<LocalFileStreamReader
> reader(
263 CreateFileReader(test_path(), 0, base::Time()));
265 net::TestCompletionCallback callback
;
266 scoped_refptr
<net::IOBufferWithSize
> buf(
267 new net::IOBufferWithSize(kTestDataSize
));
268 int rv
= reader
->Read(buf
.get(), buf
->size(), base::Bind(&NeverCalled
));
269 ASSERT_TRUE(rv
== net::ERR_IO_PENDING
|| rv
>= 0);
271 // Delete immediately.
272 // Should not crash; nor should NeverCalled be callback.
274 EnsureFileTaskFinished();
277 } // namespace content