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.
4
5 #include "base/files/file_proxy.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <utility>
11
12 #include "base/bind.h"
13 #include "base/files/file.h"
14 #include "base/files/file_util.h"
15 #include "base/files/scoped_temp_dir.h"
16 #include "base/macros.h"
17 #include "base/memory/weak_ptr.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/run_loop.h"
20 #include "base/threading/thread.h"
21 #include "base/threading/thread_restrictions.h"
22 #include "build/build_config.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 namespace base {
26
27 class FileProxyTest : public testing::Test {
28 public:
FileProxyTest()29 FileProxyTest()
30 : file_thread_("FileProxyTestFileThread"),
31 error_(File::FILE_OK),
32 bytes_written_(-1),
33 weak_factory_(this) {}
34
SetUp()35 void SetUp() override {
36 ASSERT_TRUE(dir_.CreateUniqueTempDir());
37 ASSERT_TRUE(file_thread_.Start());
38 }
39
DidFinish(File::Error error)40 void DidFinish(File::Error error) {
41 error_ = error;
42 RunLoop::QuitCurrentWhenIdleDeprecated();
43 }
44
DidCreateOrOpen(File::Error error)45 void DidCreateOrOpen(File::Error error) {
46 error_ = error;
47 RunLoop::QuitCurrentWhenIdleDeprecated();
48 }
49
DidCreateTemporary(File::Error error,const FilePath & path)50 void DidCreateTemporary(File::Error error,
51 const FilePath& path) {
52 error_ = error;
53 path_ = path;
54 RunLoop::QuitCurrentWhenIdleDeprecated();
55 }
56
DidGetFileInfo(File::Error error,const File::Info & file_info)57 void DidGetFileInfo(File::Error error,
58 const File::Info& file_info) {
59 error_ = error;
60 file_info_ = file_info;
61 RunLoop::QuitCurrentWhenIdleDeprecated();
62 }
63
DidRead(File::Error error,const char * data,int bytes_read)64 void DidRead(File::Error error,
65 const char* data,
66 int bytes_read) {
67 error_ = error;
68 buffer_.resize(bytes_read);
69 memcpy(&buffer_[0], data, bytes_read);
70 RunLoop::QuitCurrentWhenIdleDeprecated();
71 }
72
DidWrite(File::Error error,int bytes_written)73 void DidWrite(File::Error error,
74 int bytes_written) {
75 error_ = error;
76 bytes_written_ = bytes_written;
77 RunLoop::QuitCurrentWhenIdleDeprecated();
78 }
79
80 protected:
CreateProxy(uint32_t flags,FileProxy * proxy)81 void CreateProxy(uint32_t flags, FileProxy* proxy) {
82 proxy->CreateOrOpen(
83 TestPath(), flags,
84 BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
85 RunLoop().Run();
86 EXPECT_TRUE(proxy->IsValid());
87 }
88
file_task_runner() const89 TaskRunner* file_task_runner() const {
90 return file_thread_.task_runner().get();
91 }
TestDirPath() const92 const FilePath& TestDirPath() const { return dir_.GetPath(); }
TestPath() const93 const FilePath TestPath() const { return dir_.GetPath().AppendASCII("test"); }
94
95 ScopedTempDir dir_;
96 MessageLoopForIO message_loop_;
97 Thread file_thread_;
98
99 File::Error error_;
100 FilePath path_;
101 File::Info file_info_;
102 std::vector<char> buffer_;
103 int bytes_written_;
104 WeakPtrFactory<FileProxyTest> weak_factory_;
105 };
106
TEST_F(FileProxyTest,CreateOrOpen_Create)107 TEST_F(FileProxyTest, CreateOrOpen_Create) {
108 FileProxy proxy(file_task_runner());
109 proxy.CreateOrOpen(
110 TestPath(), File::FLAG_CREATE | File::FLAG_READ,
111 BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
112 RunLoop().Run();
113
114 EXPECT_EQ(File::FILE_OK, error_);
115 EXPECT_TRUE(proxy.IsValid());
116 EXPECT_TRUE(proxy.created());
117 EXPECT_TRUE(PathExists(TestPath()));
118 }
119
TEST_F(FileProxyTest,CreateOrOpen_Open)120 TEST_F(FileProxyTest, CreateOrOpen_Open) {
121 // Creates a file.
122 base::WriteFile(TestPath(), nullptr, 0);
123 ASSERT_TRUE(PathExists(TestPath()));
124
125 // Opens the created file.
126 FileProxy proxy(file_task_runner());
127 proxy.CreateOrOpen(
128 TestPath(), File::FLAG_OPEN | File::FLAG_READ,
129 BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
130 RunLoop().Run();
131
132 EXPECT_EQ(File::FILE_OK, error_);
133 EXPECT_TRUE(proxy.IsValid());
134 EXPECT_FALSE(proxy.created());
135 }
136
TEST_F(FileProxyTest,CreateOrOpen_OpenNonExistent)137 TEST_F(FileProxyTest, CreateOrOpen_OpenNonExistent) {
138 FileProxy proxy(file_task_runner());
139 proxy.CreateOrOpen(
140 TestPath(), File::FLAG_OPEN | File::FLAG_READ,
141 BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
142 RunLoop().Run();
143 EXPECT_EQ(File::FILE_ERROR_NOT_FOUND, error_);
144 EXPECT_FALSE(proxy.IsValid());
145 EXPECT_FALSE(proxy.created());
146 EXPECT_FALSE(PathExists(TestPath()));
147 }
148
TEST_F(FileProxyTest,CreateOrOpen_AbandonedCreate)149 TEST_F(FileProxyTest, CreateOrOpen_AbandonedCreate) {
150 bool prev = ThreadRestrictions::SetIOAllowed(false);
151 {
152 FileProxy proxy(file_task_runner());
153 proxy.CreateOrOpen(
154 TestPath(), File::FLAG_CREATE | File::FLAG_READ,
155 BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
156 }
157 RunLoop().Run();
158 ThreadRestrictions::SetIOAllowed(prev);
159
160 EXPECT_TRUE(PathExists(TestPath()));
161 }
162
TEST_F(FileProxyTest,Close)163 TEST_F(FileProxyTest, Close) {
164 // Creates a file.
165 FileProxy proxy(file_task_runner());
166 CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy);
167
168 #if defined(OS_WIN)
169 // This fails on Windows if the file is not closed.
170 EXPECT_FALSE(base::Move(TestPath(), TestDirPath().AppendASCII("new")));
171 #endif
172
173 proxy.Close(BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
174 RunLoop().Run();
175 EXPECT_EQ(File::FILE_OK, error_);
176 EXPECT_FALSE(proxy.IsValid());
177
178 // Now it should pass on all platforms.
179 EXPECT_TRUE(base::Move(TestPath(), TestDirPath().AppendASCII("new")));
180 }
181
TEST_F(FileProxyTest,CreateTemporary)182 TEST_F(FileProxyTest, CreateTemporary) {
183 {
184 FileProxy proxy(file_task_runner());
185 proxy.CreateTemporary(0 /* additional_file_flags */,
186 BindOnce(&FileProxyTest::DidCreateTemporary,
187 weak_factory_.GetWeakPtr()));
188 RunLoop().Run();
189
190 EXPECT_TRUE(proxy.IsValid());
191 EXPECT_EQ(File::FILE_OK, error_);
192 EXPECT_TRUE(PathExists(path_));
193
194 // The file should be writable.
195 proxy.Write(0, "test", 4,
196 BindOnce(&FileProxyTest::DidWrite, weak_factory_.GetWeakPtr()));
197 RunLoop().Run();
198 EXPECT_EQ(File::FILE_OK, error_);
199 EXPECT_EQ(4, bytes_written_);
200 }
201
202 // Make sure the written data can be read from the returned path.
203 std::string data;
204 EXPECT_TRUE(ReadFileToString(path_, &data));
205 EXPECT_EQ("test", data);
206
207 // Make sure we can & do delete the created file to prevent leaks on the bots.
208 EXPECT_TRUE(base::DeleteFile(path_, false));
209 }
210
TEST_F(FileProxyTest,SetAndTake)211 TEST_F(FileProxyTest, SetAndTake) {
212 File file(TestPath(), File::FLAG_CREATE | File::FLAG_READ);
213 ASSERT_TRUE(file.IsValid());
214 FileProxy proxy(file_task_runner());
215 EXPECT_FALSE(proxy.IsValid());
216 proxy.SetFile(std::move(file));
217 EXPECT_TRUE(proxy.IsValid());
218 EXPECT_FALSE(file.IsValid());
219
220 file = proxy.TakeFile();
221 EXPECT_FALSE(proxy.IsValid());
222 EXPECT_TRUE(file.IsValid());
223 }
224
TEST_F(FileProxyTest,DuplicateFile)225 TEST_F(FileProxyTest, DuplicateFile) {
226 FileProxy proxy(file_task_runner());
227 CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy);
228 ASSERT_TRUE(proxy.IsValid());
229
230 base::File duplicate = proxy.DuplicateFile();
231 EXPECT_TRUE(proxy.IsValid());
232 EXPECT_TRUE(duplicate.IsValid());
233
234 FileProxy invalid_proxy(file_task_runner());
235 ASSERT_FALSE(invalid_proxy.IsValid());
236
237 base::File invalid_duplicate = invalid_proxy.DuplicateFile();
238 EXPECT_FALSE(invalid_proxy.IsValid());
239 EXPECT_FALSE(invalid_duplicate.IsValid());
240 }
241
TEST_F(FileProxyTest,GetInfo)242 TEST_F(FileProxyTest, GetInfo) {
243 // Setup.
244 ASSERT_EQ(4, base::WriteFile(TestPath(), "test", 4));
245 File::Info expected_info;
246 GetFileInfo(TestPath(), &expected_info);
247
248 // Run.
249 FileProxy proxy(file_task_runner());
250 CreateProxy(File::FLAG_OPEN | File::FLAG_READ, &proxy);
251 proxy.GetInfo(
252 BindOnce(&FileProxyTest::DidGetFileInfo, weak_factory_.GetWeakPtr()));
253 RunLoop().Run();
254
255 // Verify.
256 EXPECT_EQ(File::FILE_OK, error_);
257 EXPECT_EQ(expected_info.size, file_info_.size);
258 EXPECT_EQ(expected_info.is_directory, file_info_.is_directory);
259 EXPECT_EQ(expected_info.is_symbolic_link, file_info_.is_symbolic_link);
260 EXPECT_EQ(expected_info.last_modified, file_info_.last_modified);
261 EXPECT_EQ(expected_info.creation_time, file_info_.creation_time);
262 }
263
TEST_F(FileProxyTest,Read)264 TEST_F(FileProxyTest, Read) {
265 // Setup.
266 const char expected_data[] = "bleh";
267 int expected_bytes = arraysize(expected_data);
268 ASSERT_EQ(expected_bytes,
269 base::WriteFile(TestPath(), expected_data, expected_bytes));
270
271 // Run.
272 FileProxy proxy(file_task_runner());
273 CreateProxy(File::FLAG_OPEN | File::FLAG_READ, &proxy);
274
275 proxy.Read(0, 128,
276 BindOnce(&FileProxyTest::DidRead, weak_factory_.GetWeakPtr()));
277 RunLoop().Run();
278
279 // Verify.
280 EXPECT_EQ(File::FILE_OK, error_);
281 EXPECT_EQ(expected_bytes, static_cast<int>(buffer_.size()));
282 for (size_t i = 0; i < buffer_.size(); ++i) {
283 EXPECT_EQ(expected_data[i], buffer_[i]);
284 }
285 }
286
TEST_F(FileProxyTest,WriteAndFlush)287 TEST_F(FileProxyTest, WriteAndFlush) {
288 FileProxy proxy(file_task_runner());
289 CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy);
290
291 const char data[] = "foo!";
292 int data_bytes = arraysize(data);
293 proxy.Write(0, data, data_bytes,
294 BindOnce(&FileProxyTest::DidWrite, weak_factory_.GetWeakPtr()));
295 RunLoop().Run();
296 EXPECT_EQ(File::FILE_OK, error_);
297 EXPECT_EQ(data_bytes, bytes_written_);
298
299 // Flush the written data. (So that the following read should always
300 // succeed. On some platforms it may work with or without this flush.)
301 proxy.Flush(BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
302 RunLoop().Run();
303 EXPECT_EQ(File::FILE_OK, error_);
304
305 // Verify the written data.
306 char buffer[10];
307 EXPECT_EQ(data_bytes, base::ReadFile(TestPath(), buffer, data_bytes));
308 for (int i = 0; i < data_bytes; ++i) {
309 EXPECT_EQ(data[i], buffer[i]);
310 }
311 }
312
313 #if defined(OS_ANDROID) || defined(OS_FUCHSIA)
314 // Flaky on Android, see http://crbug.com/489602
315 // TODO(crbug.com/851734): Implementation depends on stat, which is not
316 // implemented on Fuchsia
317 #define MAYBE_SetTimes DISABLED_SetTimes
318 #else
319 #define MAYBE_SetTimes SetTimes
320 #endif
TEST_F(FileProxyTest,MAYBE_SetTimes)321 TEST_F(FileProxyTest, MAYBE_SetTimes) {
322 FileProxy proxy(file_task_runner());
323 CreateProxy(
324 File::FLAG_CREATE | File::FLAG_WRITE | File::FLAG_WRITE_ATTRIBUTES,
325 &proxy);
326
327 Time last_accessed_time = Time::Now() - TimeDelta::FromDays(12345);
328 Time last_modified_time = Time::Now() - TimeDelta::FromHours(98765);
329
330 proxy.SetTimes(
331 last_accessed_time, last_modified_time,
332 BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
333 RunLoop().Run();
334 EXPECT_EQ(File::FILE_OK, error_);
335
336 File::Info info;
337 GetFileInfo(TestPath(), &info);
338
339 // The returned values may only have the seconds precision, so we cast
340 // the double values to int here.
341 EXPECT_EQ(static_cast<int>(last_modified_time.ToDoubleT()),
342 static_cast<int>(info.last_modified.ToDoubleT()));
343 EXPECT_EQ(static_cast<int>(last_accessed_time.ToDoubleT()),
344 static_cast<int>(info.last_accessed.ToDoubleT()));
345 }
346
TEST_F(FileProxyTest,SetLength_Shrink)347 TEST_F(FileProxyTest, SetLength_Shrink) {
348 // Setup.
349 const char kTestData[] = "0123456789";
350 ASSERT_EQ(10, base::WriteFile(TestPath(), kTestData, 10));
351 File::Info info;
352 GetFileInfo(TestPath(), &info);
353 ASSERT_EQ(10, info.size);
354
355 // Run.
356 FileProxy proxy(file_task_runner());
357 CreateProxy(File::FLAG_OPEN | File::FLAG_WRITE, &proxy);
358 proxy.SetLength(
359 7, BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
360 RunLoop().Run();
361
362 // Verify.
363 GetFileInfo(TestPath(), &info);
364 ASSERT_EQ(7, info.size);
365
366 char buffer[7];
367 EXPECT_EQ(7, base::ReadFile(TestPath(), buffer, 7));
368 int i = 0;
369 for (; i < 7; ++i)
370 EXPECT_EQ(kTestData[i], buffer[i]);
371 }
372
TEST_F(FileProxyTest,SetLength_Expand)373 TEST_F(FileProxyTest, SetLength_Expand) {
374 // Setup.
375 const char kTestData[] = "9876543210";
376 ASSERT_EQ(10, base::WriteFile(TestPath(), kTestData, 10));
377 File::Info info;
378 GetFileInfo(TestPath(), &info);
379 ASSERT_EQ(10, info.size);
380
381 // Run.
382 FileProxy proxy(file_task_runner());
383 CreateProxy(File::FLAG_OPEN | File::FLAG_WRITE, &proxy);
384 proxy.SetLength(
385 53, BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
386 RunLoop().Run();
387
388 // Verify.
389 GetFileInfo(TestPath(), &info);
390 ASSERT_EQ(53, info.size);
391
392 char buffer[53];
393 EXPECT_EQ(53, base::ReadFile(TestPath(), buffer, 53));
394 int i = 0;
395 for (; i < 10; ++i)
396 EXPECT_EQ(kTestData[i], buffer[i]);
397 for (; i < 53; ++i)
398 EXPECT_EQ(0, buffer[i]);
399 }
400
401 } // namespace base
402