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