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() {
99 base::Time new_modified_time
=
100 test_file_modification_time() - base::TimeDelta::FromSeconds(1);
101 ASSERT_TRUE(base::TouchFile(test_path(),
102 test_file_modification_time(),
106 base::SingleThreadTaskRunner
* file_task_runner() const {
107 return file_thread_
.task_runner().get();
110 base::FilePath
test_dir() const { return dir_
.path(); }
111 base::FilePath
test_path() const { return dir_
.path().AppendASCII("test"); }
112 base::Time
test_file_modification_time() const {
113 return test_file_modification_time_
;
116 void EnsureFileTaskFinished() {
117 file_task_runner()->PostTaskAndReply(
118 FROM_HERE
, base::Bind(&EmptyCallback
), base::Bind(&QuitLoop
));
119 base::MessageLoop::current()->Run();
123 base::MessageLoopForIO message_loop_
;
124 base::Thread file_thread_
;
125 base::ScopedTempDir dir_
;
126 base::Time test_file_modification_time_
;
129 TEST_F(LocalFileStreamReaderTest
, NonExistent
) {
130 base::FilePath nonexistent_path
= test_dir().AppendASCII("nonexistent");
131 scoped_ptr
<LocalFileStreamReader
> reader(
132 CreateFileReader(nonexistent_path
, 0, base::Time()));
135 ReadFromReader(reader
.get(), &data
, 10, &result
);
136 ASSERT_EQ(net::ERR_FILE_NOT_FOUND
, result
);
137 ASSERT_EQ(0U, data
.size());
140 TEST_F(LocalFileStreamReaderTest
, Empty
) {
141 base::FilePath empty_path
= test_dir().AppendASCII("empty");
142 base::File
file(empty_path
, base::File::FLAG_CREATE
| base::File::FLAG_READ
);
143 ASSERT_TRUE(file
.IsValid());
146 scoped_ptr
<LocalFileStreamReader
> reader(
147 CreateFileReader(empty_path
, 0, base::Time()));
150 ReadFromReader(reader
.get(), &data
, 10, &result
);
151 ASSERT_EQ(net::OK
, result
);
152 ASSERT_EQ(0U, data
.size());
154 net::TestInt64CompletionCallback callback
;
155 int64 length_result
= reader
->GetLength(callback
.callback());
156 if (length_result
== net::ERR_IO_PENDING
)
157 length_result
= callback
.WaitForResult();
158 ASSERT_EQ(0, result
);
161 TEST_F(LocalFileStreamReaderTest
, GetLengthNormal
) {
162 scoped_ptr
<LocalFileStreamReader
> reader(
163 CreateFileReader(test_path(), 0, test_file_modification_time()));
164 net::TestInt64CompletionCallback callback
;
165 int64 result
= reader
->GetLength(callback
.callback());
166 if (result
== net::ERR_IO_PENDING
)
167 result
= callback
.WaitForResult();
168 ASSERT_EQ(kTestDataSize
, result
);
171 TEST_F(LocalFileStreamReaderTest
, GetLengthAfterModified
) {
172 // Touch file so that the file's modification time becomes different
173 // from what we expect.
176 scoped_ptr
<LocalFileStreamReader
> reader(
177 CreateFileReader(test_path(), 0, test_file_modification_time()));
178 net::TestInt64CompletionCallback callback
;
179 int64 result
= reader
->GetLength(callback
.callback());
180 if (result
== net::ERR_IO_PENDING
)
181 result
= callback
.WaitForResult();
182 ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED
, result
);
184 // With NULL expected modification time this should work.
185 reader
.reset(CreateFileReader(test_path(), 0, base::Time()));
186 result
= reader
->GetLength(callback
.callback());
187 if (result
== net::ERR_IO_PENDING
)
188 result
= callback
.WaitForResult();
189 ASSERT_EQ(kTestDataSize
, result
);
192 TEST_F(LocalFileStreamReaderTest
, GetLengthWithOffset
) {
193 scoped_ptr
<LocalFileStreamReader
> reader(
194 CreateFileReader(test_path(), 3, base::Time()));
195 net::TestInt64CompletionCallback callback
;
196 int64 result
= reader
->GetLength(callback
.callback());
197 if (result
== net::ERR_IO_PENDING
)
198 result
= callback
.WaitForResult();
199 // Initial offset does not affect the result of GetLength.
200 ASSERT_EQ(kTestDataSize
, result
);
203 TEST_F(LocalFileStreamReaderTest
, ReadNormal
) {
204 scoped_ptr
<LocalFileStreamReader
> reader(
205 CreateFileReader(test_path(), 0, test_file_modification_time()));
208 ReadFromReader(reader
.get(), &data
, kTestDataSize
, &result
);
209 ASSERT_EQ(net::OK
, result
);
210 ASSERT_EQ(kTestData
, data
);
213 TEST_F(LocalFileStreamReaderTest
, ReadAfterModified
) {
214 // Touch file so that the file's modification time becomes different
215 // from what we expect.
218 scoped_ptr
<LocalFileStreamReader
> reader(
219 CreateFileReader(test_path(), 0, test_file_modification_time()));
222 ReadFromReader(reader
.get(), &data
, kTestDataSize
, &result
);
223 ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED
, result
);
224 ASSERT_EQ(0U, data
.size());
226 // With NULL expected modification time this should work.
228 reader
.reset(CreateFileReader(test_path(), 0, base::Time()));
229 ReadFromReader(reader
.get(), &data
, kTestDataSize
, &result
);
230 ASSERT_EQ(net::OK
, result
);
231 ASSERT_EQ(kTestData
, data
);
234 TEST_F(LocalFileStreamReaderTest
, ReadWithOffset
) {
235 scoped_ptr
<LocalFileStreamReader
> reader(
236 CreateFileReader(test_path(), 3, base::Time()));
239 ReadFromReader(reader
.get(), &data
, kTestDataSize
, &result
);
240 ASSERT_EQ(net::OK
, result
);
241 ASSERT_EQ(&kTestData
[3], data
);
244 TEST_F(LocalFileStreamReaderTest
, DeleteWithUnfinishedRead
) {
245 scoped_ptr
<LocalFileStreamReader
> reader(
246 CreateFileReader(test_path(), 0, base::Time()));
248 net::TestCompletionCallback callback
;
249 scoped_refptr
<net::IOBufferWithSize
> buf(
250 new net::IOBufferWithSize(kTestDataSize
));
251 int rv
= reader
->Read(buf
.get(), buf
->size(), base::Bind(&NeverCalled
));
252 ASSERT_TRUE(rv
== net::ERR_IO_PENDING
|| rv
>= 0);
254 // Delete immediately.
255 // Should not crash; nor should NeverCalled be callback.
257 EnsureFileTaskFinished();
260 } // namespace content