1 // Copyright 2014 The Chromium Authors
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 <string_view>
11 #include <utility>
12
13 #include "base/containers/heap_array.h"
14 #include "base/files/file.h"
15 #include "base/files/file_util.h"
16 #include "base/files/scoped_temp_dir.h"
17 #include "base/functional/bind.h"
18 #include "base/memory/weak_ptr.h"
19 #include "base/run_loop.h"
20 #include "base/test/task_environment.h"
21 #include "base/threading/platform_thread.h"
22 #include "base/threading/thread.h"
23 #include "base/threading/thread_restrictions.h"
24 #include "build/build_config.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26
27 namespace base {
28
29 class FileProxyTest : public testing::Test {
30 public:
FileProxyTest()31 FileProxyTest()
32 : task_environment_(test::TaskEnvironment::MainThreadType::IO),
33 file_thread_("FileProxyTestFileThread"),
34 error_(File::FILE_OK),
35 bytes_written_(-1) {}
36
SetUp()37 void SetUp() override {
38 ASSERT_TRUE(dir_.CreateUniqueTempDir());
39 ASSERT_TRUE(file_thread_.Start());
40 }
41
DidFinish(base::RepeatingClosure continuation,File::Error error)42 void DidFinish(base::RepeatingClosure continuation, File::Error error) {
43 error_ = error;
44 continuation.Run();
45 }
46
DidCreateOrOpen(base::RepeatingClosure continuation,File::Error error)47 void DidCreateOrOpen(base::RepeatingClosure continuation, File::Error error) {
48 error_ = error;
49 continuation.Run();
50 }
51
DidCreateTemporary(base::RepeatingClosure continuation,File::Error error,const FilePath & path)52 void DidCreateTemporary(base::RepeatingClosure continuation,
53 File::Error error,
54 const FilePath& path) {
55 error_ = error;
56 path_ = path;
57 continuation.Run();
58 }
59
DidGetFileInfo(base::RepeatingClosure continuation,File::Error error,const File::Info & file_info)60 void DidGetFileInfo(base::RepeatingClosure continuation,
61 File::Error error,
62 const File::Info& file_info) {
63 error_ = error;
64 file_info_ = file_info;
65 continuation.Run();
66 }
67
DidRead(base::RepeatingClosure continuation,File::Error error,base::span<const char> data)68 void DidRead(base::RepeatingClosure continuation,
69 File::Error error,
70 base::span<const char> data) {
71 error_ = error;
72 buffer_ = base::HeapArray<char>::CopiedFrom(data);
73 continuation.Run();
74 }
75
DidWrite(base::RepeatingClosure continuation,File::Error error,int bytes_written)76 void DidWrite(base::RepeatingClosure continuation,
77 File::Error error,
78 int bytes_written) {
79 error_ = error;
80 bytes_written_ = bytes_written;
81 continuation.Run();
82 }
83
84 protected:
CreateProxy(uint32_t flags,FileProxy * proxy)85 void CreateProxy(uint32_t flags, FileProxy* proxy) {
86 RunLoop run_loop;
87 proxy->CreateOrOpen(
88 TestPath(), flags,
89 BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr(),
90 run_loop.QuitWhenIdleClosure()));
91 run_loop.Run();
92 EXPECT_TRUE(proxy->IsValid());
93 }
94
file_task_runner() const95 TaskRunner* file_task_runner() const {
96 return file_thread_.task_runner().get();
97 }
TestDirPath() const98 const FilePath& TestDirPath() const { return dir_.GetPath(); }
TestPath() const99 const FilePath TestPath() const { return dir_.GetPath().AppendASCII("test"); }
100
101 ScopedTempDir dir_;
102 test::TaskEnvironment task_environment_;
103 Thread file_thread_;
104
105 File::Error error_;
106 FilePath path_;
107 File::Info file_info_;
108 base::HeapArray<char> buffer_;
109 int bytes_written_;
110 WeakPtrFactory<FileProxyTest> weak_factory_{this};
111 };
112
TEST_F(FileProxyTest,CreateOrOpen_Create)113 TEST_F(FileProxyTest, CreateOrOpen_Create) {
114 FileProxy proxy(file_task_runner());
115 RunLoop run_loop;
116 proxy.CreateOrOpen(
117 TestPath(), File::FLAG_CREATE | File::FLAG_READ,
118 BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr(),
119 run_loop.QuitWhenIdleClosure()));
120 run_loop.Run();
121
122 EXPECT_EQ(File::FILE_OK, error_);
123 EXPECT_TRUE(proxy.IsValid());
124 EXPECT_TRUE(proxy.created());
125 EXPECT_TRUE(PathExists(TestPath()));
126 }
127
TEST_F(FileProxyTest,CreateOrOpen_Open)128 TEST_F(FileProxyTest, CreateOrOpen_Open) {
129 // Creates a file.
130 base::WriteFile(TestPath(), base::StringPiece());
131 ASSERT_TRUE(PathExists(TestPath()));
132
133 // Opens the created file.
134 FileProxy proxy(file_task_runner());
135 RunLoop run_loop;
136 proxy.CreateOrOpen(
137 TestPath(), File::FLAG_OPEN | File::FLAG_READ,
138 BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr(),
139 run_loop.QuitWhenIdleClosure()));
140 run_loop.Run();
141
142 EXPECT_EQ(File::FILE_OK, error_);
143 EXPECT_TRUE(proxy.IsValid());
144 EXPECT_FALSE(proxy.created());
145 }
146
TEST_F(FileProxyTest,CreateOrOpen_OpenNonExistent)147 TEST_F(FileProxyTest, CreateOrOpen_OpenNonExistent) {
148 FileProxy proxy(file_task_runner());
149 RunLoop run_loop;
150 proxy.CreateOrOpen(
151 TestPath(), File::FLAG_OPEN | File::FLAG_READ,
152 BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr(),
153 run_loop.QuitWhenIdleClosure()));
154 run_loop.Run();
155 EXPECT_EQ(File::FILE_ERROR_NOT_FOUND, error_);
156 EXPECT_FALSE(proxy.IsValid());
157 EXPECT_FALSE(proxy.created());
158 EXPECT_FALSE(PathExists(TestPath()));
159 }
160
TEST_F(FileProxyTest,CreateOrOpen_AbandonedCreate)161 TEST_F(FileProxyTest, CreateOrOpen_AbandonedCreate) {
162 {
163 base::ScopedDisallowBlocking disallow_blocking;
164 RunLoop run_loop;
165 {
166 FileProxy proxy(file_task_runner());
167 proxy.CreateOrOpen(
168 TestPath(), File::FLAG_CREATE | File::FLAG_READ,
169 BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr(),
170 run_loop.QuitWhenIdleClosure()));
171 }
172 run_loop.Run();
173 }
174
175 EXPECT_TRUE(PathExists(TestPath()));
176 }
177
TEST_F(FileProxyTest,Close)178 TEST_F(FileProxyTest, Close) {
179 // Creates a file.
180 FileProxy proxy(file_task_runner());
181 CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy);
182
183 #if BUILDFLAG(IS_WIN)
184 // This fails on Windows if the file is not closed.
185 EXPECT_FALSE(base::Move(TestPath(), TestDirPath().AppendASCII("new")));
186 #endif
187
188 RunLoop run_loop;
189 proxy.Close(BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr(),
190 run_loop.QuitWhenIdleClosure()));
191 run_loop.Run();
192 EXPECT_EQ(File::FILE_OK, error_);
193 EXPECT_FALSE(proxy.IsValid());
194
195 // Now it should pass on all platforms.
196 EXPECT_TRUE(base::Move(TestPath(), TestDirPath().AppendASCII("new")));
197 }
198
TEST_F(FileProxyTest,CreateTemporary)199 TEST_F(FileProxyTest, CreateTemporary) {
200 {
201 FileProxy proxy(file_task_runner());
202 {
203 RunLoop run_loop;
204 proxy.CreateTemporary(
205 0 /* additional_file_flags */,
206 BindOnce(&FileProxyTest::DidCreateTemporary,
207 weak_factory_.GetWeakPtr(), run_loop.QuitWhenIdleClosure()));
208 run_loop.Run();
209 }
210
211 EXPECT_TRUE(proxy.IsValid());
212 EXPECT_EQ(File::FILE_OK, error_);
213 EXPECT_TRUE(PathExists(path_));
214
215 // The file should be writable.
216 {
217 RunLoop run_loop;
218 proxy.Write(0, base::as_byte_span(std::string_view("test")),
219 BindOnce(&FileProxyTest::DidWrite, weak_factory_.GetWeakPtr(),
220 run_loop.QuitWhenIdleClosure()));
221 run_loop.Run();
222 }
223 EXPECT_EQ(File::FILE_OK, error_);
224 EXPECT_EQ(4, bytes_written_);
225 }
226
227 // Make sure the written data can be read from the returned path.
228 std::string data;
229 EXPECT_TRUE(ReadFileToString(path_, &data));
230 EXPECT_EQ("test", data);
231
232 // Make sure we can & do delete the created file to prevent leaks on the bots.
233 // Try a few times because files may be locked by anti-virus or other.
234 bool deleted_temp_file = false;
235 for (int i = 0; !deleted_temp_file && i < 3; ++i) {
236 if (base::DeleteFile(path_))
237 deleted_temp_file = true;
238 else
239 // Wait one second and then try again
240 PlatformThread::Sleep(Seconds(1));
241 }
242 EXPECT_TRUE(deleted_temp_file);
243 }
244
TEST_F(FileProxyTest,SetAndTake)245 TEST_F(FileProxyTest, SetAndTake) {
246 File file(TestPath(), File::FLAG_CREATE | File::FLAG_READ);
247 ASSERT_TRUE(file.IsValid());
248 FileProxy proxy(file_task_runner());
249 EXPECT_FALSE(proxy.IsValid());
250 proxy.SetFile(std::move(file));
251 EXPECT_TRUE(proxy.IsValid());
252 EXPECT_FALSE(file.IsValid());
253
254 file = proxy.TakeFile();
255 EXPECT_FALSE(proxy.IsValid());
256 EXPECT_TRUE(file.IsValid());
257 }
258
TEST_F(FileProxyTest,DuplicateFile)259 TEST_F(FileProxyTest, DuplicateFile) {
260 FileProxy proxy(file_task_runner());
261 CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy);
262 ASSERT_TRUE(proxy.IsValid());
263
264 base::File duplicate = proxy.DuplicateFile();
265 EXPECT_TRUE(proxy.IsValid());
266 EXPECT_TRUE(duplicate.IsValid());
267
268 FileProxy invalid_proxy(file_task_runner());
269 ASSERT_FALSE(invalid_proxy.IsValid());
270
271 base::File invalid_duplicate = invalid_proxy.DuplicateFile();
272 EXPECT_FALSE(invalid_proxy.IsValid());
273 EXPECT_FALSE(invalid_duplicate.IsValid());
274 }
275
TEST_F(FileProxyTest,GetInfo)276 TEST_F(FileProxyTest, GetInfo) {
277 // Setup.
278 ASSERT_TRUE(base::WriteFile(TestPath(), "test"));
279 File::Info expected_info;
280 GetFileInfo(TestPath(), &expected_info);
281
282 // Run.
283 FileProxy proxy(file_task_runner());
284 CreateProxy(File::FLAG_OPEN | File::FLAG_READ, &proxy);
285 RunLoop run_loop;
286 proxy.GetInfo(BindOnce(&FileProxyTest::DidGetFileInfo,
287 weak_factory_.GetWeakPtr(),
288 run_loop.QuitWhenIdleClosure()));
289 run_loop.Run();
290
291 // Verify.
292 EXPECT_EQ(File::FILE_OK, error_);
293 EXPECT_EQ(expected_info.size, file_info_.size);
294 EXPECT_EQ(expected_info.is_directory, file_info_.is_directory);
295 EXPECT_EQ(expected_info.is_symbolic_link, file_info_.is_symbolic_link);
296 EXPECT_EQ(expected_info.last_modified, file_info_.last_modified);
297 EXPECT_EQ(expected_info.creation_time, file_info_.creation_time);
298 }
299
TEST_F(FileProxyTest,Read)300 TEST_F(FileProxyTest, Read) {
301 // Setup.
302 constexpr base::StringPiece expected_data = "bleh";
303 ASSERT_TRUE(base::WriteFile(TestPath(), expected_data));
304
305 // Run.
306 FileProxy proxy(file_task_runner());
307 CreateProxy(File::FLAG_OPEN | File::FLAG_READ, &proxy);
308
309 RunLoop run_loop;
310 proxy.Read(0, 128,
311 BindOnce(&FileProxyTest::DidRead, weak_factory_.GetWeakPtr(),
312 run_loop.QuitWhenIdleClosure()));
313 run_loop.Run();
314
315 // Verify.
316 EXPECT_EQ(File::FILE_OK, error_);
317 EXPECT_EQ(expected_data, base::StringPiece(buffer_.data(), buffer_.size()));
318 }
319
TEST_F(FileProxyTest,WriteAndFlush)320 TEST_F(FileProxyTest, WriteAndFlush) {
321 FileProxy proxy(file_task_runner());
322 CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy);
323
324 auto write_span = base::as_byte_span("foo!");
325 EXPECT_EQ(write_span.size(), 5u); // Includes the NUL, too.
326 {
327 RunLoop run_loop;
328 proxy.Write(0, write_span,
329 BindOnce(&FileProxyTest::DidWrite, weak_factory_.GetWeakPtr(),
330 run_loop.QuitWhenIdleClosure()));
331 run_loop.Run();
332 }
333 EXPECT_EQ(File::FILE_OK, error_);
334 EXPECT_EQ(write_span.size(), static_cast<size_t>(bytes_written_));
335
336 // Flush the written data. (So that the following read should always
337 // succeed. On some platforms it may work with or without this flush.)
338 {
339 RunLoop run_loop;
340 proxy.Flush(BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr(),
341 run_loop.QuitWhenIdleClosure()));
342 run_loop.Run();
343 }
344 EXPECT_EQ(File::FILE_OK, error_);
345
346 // Verify the written data.
347 char read_buffer[10];
348 EXPECT_GE(std::size(read_buffer), write_span.size());
349 EXPECT_EQ(write_span.size(), base::ReadFile(TestPath(), read_buffer));
350 for (size_t i = 0; i < write_span.size(); ++i) {
351 EXPECT_EQ(write_span[i], read_buffer[i]);
352 }
353 }
354
355 #if BUILDFLAG(IS_ANDROID)
356 // Flaky on Android, see http://crbug.com/489602
357 #define MAYBE_SetTimes DISABLED_SetTimes
358 #else
359 #define MAYBE_SetTimes SetTimes
360 #endif
TEST_F(FileProxyTest,MAYBE_SetTimes)361 TEST_F(FileProxyTest, MAYBE_SetTimes) {
362 FileProxy proxy(file_task_runner());
363 CreateProxy(
364 File::FLAG_CREATE | File::FLAG_WRITE | File::FLAG_WRITE_ATTRIBUTES,
365 &proxy);
366
367 Time last_accessed_time = Time::Now() - Days(12345);
368 Time last_modified_time = Time::Now() - Hours(98765);
369
370 RunLoop run_loop;
371 proxy.SetTimes(last_accessed_time, last_modified_time,
372 BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr(),
373 run_loop.QuitWhenIdleClosure()));
374 run_loop.Run();
375 EXPECT_EQ(File::FILE_OK, error_);
376
377 File::Info info;
378 GetFileInfo(TestPath(), &info);
379
380 // The returned values may only have the seconds precision, so we cast
381 // the double values to int here.
382 EXPECT_EQ(static_cast<int>(last_modified_time.InSecondsFSinceUnixEpoch()),
383 static_cast<int>(info.last_modified.InSecondsFSinceUnixEpoch()));
384
385 #if !BUILDFLAG(IS_FUCHSIA)
386 // On Fuchsia, /tmp is noatime
387 EXPECT_EQ(static_cast<int>(last_accessed_time.InSecondsFSinceUnixEpoch()),
388 static_cast<int>(info.last_accessed.InSecondsFSinceUnixEpoch()));
389 #endif // BUILDFLAG(IS_FUCHSIA)
390 }
391
TEST_F(FileProxyTest,SetLength_Shrink)392 TEST_F(FileProxyTest, SetLength_Shrink) {
393 // Setup.
394 const char kTestData[] = "0123456789";
395 ASSERT_TRUE(base::WriteFile(TestPath(), kTestData));
396 File::Info info;
397 GetFileInfo(TestPath(), &info);
398 ASSERT_EQ(10, info.size);
399
400 // Run.
401 FileProxy proxy(file_task_runner());
402 CreateProxy(File::FLAG_OPEN | File::FLAG_WRITE, &proxy);
403 RunLoop run_loop;
404 proxy.SetLength(
405 7, BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr(),
406 run_loop.QuitWhenIdleClosure()));
407 run_loop.Run();
408
409 // Verify.
410 GetFileInfo(TestPath(), &info);
411 ASSERT_EQ(7, info.size);
412
413 char buffer[7];
414 EXPECT_EQ(7, base::ReadFile(TestPath(), buffer));
415 int i = 0;
416 for (; i < 7; ++i)
417 EXPECT_EQ(kTestData[i], buffer[i]);
418 }
419
TEST_F(FileProxyTest,SetLength_Expand)420 TEST_F(FileProxyTest, SetLength_Expand) {
421 // Setup.
422 const char kTestData[] = "9876543210";
423 ASSERT_TRUE(base::WriteFile(TestPath(), kTestData));
424 File::Info info;
425 GetFileInfo(TestPath(), &info);
426 ASSERT_EQ(10, info.size);
427
428 // Run.
429 FileProxy proxy(file_task_runner());
430 CreateProxy(File::FLAG_OPEN | File::FLAG_WRITE, &proxy);
431 RunLoop run_loop;
432 proxy.SetLength(
433 53, BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr(),
434 run_loop.QuitWhenIdleClosure()));
435 run_loop.Run();
436
437 // Verify.
438 GetFileInfo(TestPath(), &info);
439 ASSERT_EQ(53, info.size);
440
441 char buffer[53];
442 EXPECT_EQ(53, base::ReadFile(TestPath(), buffer));
443 int i = 0;
444 for (; i < 10; ++i)
445 EXPECT_EQ(kTestData[i], buffer[i]);
446 for (; i < 53; ++i)
447 EXPECT_EQ(0, buffer[i]);
448 }
449
450 } // namespace base
451