1 // Copyright 2013 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 <string>
6
7 #include "base/files/file.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/run_loop.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "content/public/test/async_file_test_helper.h"
16 #include "content/public/test/test_file_system_context.h"
17 #include "storage/browser/fileapi/async_file_util_adapter.h"
18 #include "storage/browser/fileapi/file_system_context.h"
19 #include "storage/browser/fileapi/file_system_file_util.h"
20 #include "storage/browser/fileapi/file_system_operation_context.h"
21 #include "storage/browser/fileapi/local_file_util.h"
22 #include "storage/browser/fileapi/native_file_util.h"
23 #include "storage/common/fileapi/file_system_types.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 using content::AsyncFileTestHelper;
27 using storage::AsyncFileUtilAdapter;
28 using storage::FileSystemContext;
29 using storage::FileSystemOperationContext;
30 using storage::FileSystemURL;
31 using storage::LocalFileUtil;
32
33 namespace content {
34
35 namespace {
36
37 const GURL kOrigin("http://foo/");
38 const storage::FileSystemType kFileSystemType = storage::kFileSystemTypeTest;
39
40 } // namespace
41
42 class LocalFileUtilTest : public testing::Test {
43 public:
LocalFileUtilTest()44 LocalFileUtilTest() {}
45
SetUp()46 virtual void SetUp() {
47 ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
48 file_system_context_ = CreateFileSystemContextForTesting(
49 NULL, data_dir_.path());
50 }
51
TearDown()52 virtual void TearDown() {
53 file_system_context_ = NULL;
54 base::RunLoop().RunUntilIdle();
55 }
56
57 protected:
NewContext()58 FileSystemOperationContext* NewContext() {
59 FileSystemOperationContext* context =
60 new FileSystemOperationContext(file_system_context_.get());
61 context->set_update_observers(
62 *file_system_context_->GetUpdateObservers(kFileSystemType));
63 return context;
64 }
65
file_util()66 LocalFileUtil* file_util() {
67 AsyncFileUtilAdapter* adapter = static_cast<AsyncFileUtilAdapter*>(
68 file_system_context_->GetAsyncFileUtil(kFileSystemType));
69 return static_cast<LocalFileUtil*>(adapter->sync_file_util());
70 }
71
CreateURL(const std::string & file_name)72 FileSystemURL CreateURL(const std::string& file_name) {
73 return file_system_context_->CreateCrackedFileSystemURL(
74 kOrigin, kFileSystemType, base::FilePath().FromUTF8Unsafe(file_name));
75 }
76
LocalPath(const char * file_name)77 base::FilePath LocalPath(const char *file_name) {
78 base::FilePath path;
79 scoped_ptr<FileSystemOperationContext> context(NewContext());
80 file_util()->GetLocalFilePath(context.get(), CreateURL(file_name), &path);
81 return path;
82 }
83
FileExists(const char * file_name)84 bool FileExists(const char *file_name) {
85 return base::PathExists(LocalPath(file_name)) &&
86 !base::DirectoryExists(LocalPath(file_name));
87 }
88
DirectoryExists(const char * file_name)89 bool DirectoryExists(const char *file_name) {
90 return base::DirectoryExists(LocalPath(file_name));
91 }
92
GetSize(const char * file_name)93 int64 GetSize(const char *file_name) {
94 base::File::Info info;
95 base::GetFileInfo(LocalPath(file_name), &info);
96 return info.size;
97 }
98
CreateFile(const char * file_name)99 base::File CreateFile(const char* file_name) {
100 int file_flags = base::File::FLAG_CREATE |
101 base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
102
103 scoped_ptr<FileSystemOperationContext> context(NewContext());
104 return file_util()->CreateOrOpen(context.get(), CreateURL(file_name),
105 file_flags);
106 }
107
EnsureFileExists(const char * file_name,bool * created)108 base::File::Error EnsureFileExists(const char* file_name,
109 bool* created) {
110 scoped_ptr<FileSystemOperationContext> context(NewContext());
111 return file_util()->EnsureFileExists(context.get(),
112 CreateURL(file_name), created);
113 }
114
file_system_context()115 FileSystemContext* file_system_context() {
116 return file_system_context_.get();
117 }
118
119 private:
120 base::MessageLoop message_loop_;
121 scoped_refptr<FileSystemContext> file_system_context_;
122 base::ScopedTempDir data_dir_;
123
124 DISALLOW_COPY_AND_ASSIGN(LocalFileUtilTest);
125 };
126
TEST_F(LocalFileUtilTest,CreateAndClose)127 TEST_F(LocalFileUtilTest, CreateAndClose) {
128 const char *file_name = "test_file";
129 base::File file = CreateFile(file_name);
130 ASSERT_TRUE(file.IsValid());
131 ASSERT_TRUE(file.created());
132
133 EXPECT_TRUE(FileExists(file_name));
134 EXPECT_EQ(0, GetSize(file_name));
135
136 scoped_ptr<FileSystemOperationContext> context(NewContext());
137 }
138
139 // base::CreateSymbolicLink is only supported on POSIX.
140 #if defined(OS_POSIX)
TEST_F(LocalFileUtilTest,CreateFailForSymlink)141 TEST_F(LocalFileUtilTest, CreateFailForSymlink) {
142 // Create symlink target file.
143 const char *target_name = "symlink_target";
144 base::File target_file = CreateFile(target_name);
145 ASSERT_TRUE(target_file.IsValid());
146 ASSERT_TRUE(target_file.created());
147 base::FilePath target_path = LocalPath(target_name);
148
149 // Create symlink where target must be real file.
150 const char *symlink_name = "symlink_file";
151 base::FilePath symlink_path = LocalPath(symlink_name);
152 ASSERT_TRUE(base::CreateSymbolicLink(target_path, symlink_path));
153 ASSERT_TRUE(FileExists(symlink_name));
154
155 // Try to open the symlink file which should fail.
156 scoped_ptr<FileSystemOperationContext> context(NewContext());
157 FileSystemURL url = CreateURL(symlink_name);
158 int file_flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
159 base::File file = file_util()->CreateOrOpen(context.get(), url, file_flags);
160 ASSERT_FALSE(file.IsValid());
161 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, file.error_details());
162 }
163 #endif
164
TEST_F(LocalFileUtilTest,EnsureFileExists)165 TEST_F(LocalFileUtilTest, EnsureFileExists) {
166 const char *file_name = "foobar";
167 bool created;
168 ASSERT_EQ(base::File::FILE_OK, EnsureFileExists(file_name, &created));
169 ASSERT_TRUE(created);
170
171 EXPECT_TRUE(FileExists(file_name));
172 EXPECT_EQ(0, GetSize(file_name));
173
174 ASSERT_EQ(base::File::FILE_OK, EnsureFileExists(file_name, &created));
175 EXPECT_FALSE(created);
176 }
177
TEST_F(LocalFileUtilTest,TouchFile)178 TEST_F(LocalFileUtilTest, TouchFile) {
179 const char *file_name = "test_file";
180 base::File file = CreateFile(file_name);
181 ASSERT_TRUE(file.IsValid());
182 ASSERT_TRUE(file.created());
183
184 scoped_ptr<FileSystemOperationContext> context(NewContext());
185
186 base::File::Info info;
187 ASSERT_TRUE(base::GetFileInfo(LocalPath(file_name), &info));
188 const base::Time new_accessed =
189 info.last_accessed + base::TimeDelta::FromHours(10);
190 const base::Time new_modified =
191 info.last_modified + base::TimeDelta::FromHours(5);
192
193 EXPECT_EQ(base::File::FILE_OK,
194 file_util()->Touch(context.get(), CreateURL(file_name),
195 new_accessed, new_modified));
196
197 ASSERT_TRUE(base::GetFileInfo(LocalPath(file_name), &info));
198 EXPECT_EQ(new_accessed, info.last_accessed);
199 EXPECT_EQ(new_modified, info.last_modified);
200 }
201
TEST_F(LocalFileUtilTest,TouchDirectory)202 TEST_F(LocalFileUtilTest, TouchDirectory) {
203 const char *dir_name = "test_dir";
204 scoped_ptr<FileSystemOperationContext> context(NewContext());
205 ASSERT_EQ(base::File::FILE_OK,
206 file_util()->CreateDirectory(context.get(),
207 CreateURL(dir_name),
208 false /* exclusive */,
209 false /* recursive */));
210
211 base::File::Info info;
212 ASSERT_TRUE(base::GetFileInfo(LocalPath(dir_name), &info));
213 const base::Time new_accessed =
214 info.last_accessed + base::TimeDelta::FromHours(10);
215 const base::Time new_modified =
216 info.last_modified + base::TimeDelta::FromHours(5);
217
218 EXPECT_EQ(base::File::FILE_OK,
219 file_util()->Touch(context.get(), CreateURL(dir_name),
220 new_accessed, new_modified));
221
222 ASSERT_TRUE(base::GetFileInfo(LocalPath(dir_name), &info));
223 EXPECT_EQ(new_accessed, info.last_accessed);
224 EXPECT_EQ(new_modified, info.last_modified);
225 }
226
TEST_F(LocalFileUtilTest,Truncate)227 TEST_F(LocalFileUtilTest, Truncate) {
228 const char *file_name = "truncated";
229 bool created;
230 ASSERT_EQ(base::File::FILE_OK, EnsureFileExists(file_name, &created));
231 ASSERT_TRUE(created);
232
233 scoped_ptr<FileSystemOperationContext> context;
234
235 context.reset(NewContext());
236 ASSERT_EQ(base::File::FILE_OK,
237 file_util()->Truncate(context.get(), CreateURL(file_name), 1020));
238
239 EXPECT_TRUE(FileExists(file_name));
240 EXPECT_EQ(1020, GetSize(file_name));
241 }
242
TEST_F(LocalFileUtilTest,CopyFile)243 TEST_F(LocalFileUtilTest, CopyFile) {
244 const char *from_file = "fromfile";
245 const char *to_file1 = "tofile1";
246 const char *to_file2 = "tofile2";
247 bool created;
248 ASSERT_EQ(base::File::FILE_OK, EnsureFileExists(from_file, &created));
249 ASSERT_TRUE(created);
250
251 scoped_ptr<FileSystemOperationContext> context;
252 context.reset(NewContext());
253 ASSERT_EQ(base::File::FILE_OK,
254 file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
255
256 EXPECT_TRUE(FileExists(from_file));
257 EXPECT_EQ(1020, GetSize(from_file));
258
259 ASSERT_EQ(base::File::FILE_OK,
260 AsyncFileTestHelper::Copy(file_system_context(),
261 CreateURL(from_file),
262 CreateURL(to_file1)));
263
264 context.reset(NewContext());
265 ASSERT_EQ(base::File::FILE_OK,
266 AsyncFileTestHelper::Copy(file_system_context(),
267 CreateURL(from_file),
268 CreateURL(to_file2)));
269
270 EXPECT_TRUE(FileExists(from_file));
271 EXPECT_EQ(1020, GetSize(from_file));
272 EXPECT_TRUE(FileExists(to_file1));
273 EXPECT_EQ(1020, GetSize(to_file1));
274 EXPECT_TRUE(FileExists(to_file2));
275 EXPECT_EQ(1020, GetSize(to_file2));
276 }
277
TEST_F(LocalFileUtilTest,CopyDirectory)278 TEST_F(LocalFileUtilTest, CopyDirectory) {
279 const char *from_dir = "fromdir";
280 const char *from_file = "fromdir/fromfile";
281 const char *to_dir = "todir";
282 const char *to_file = "todir/fromfile";
283 bool created;
284 scoped_ptr<FileSystemOperationContext> context;
285
286 context.reset(NewContext());
287 ASSERT_EQ(base::File::FILE_OK,
288 file_util()->CreateDirectory(context.get(), CreateURL(from_dir),
289 false, false));
290 ASSERT_EQ(base::File::FILE_OK, EnsureFileExists(from_file, &created));
291 ASSERT_TRUE(created);
292
293 context.reset(NewContext());
294 ASSERT_EQ(base::File::FILE_OK,
295 file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
296
297 EXPECT_TRUE(DirectoryExists(from_dir));
298 EXPECT_TRUE(FileExists(from_file));
299 EXPECT_EQ(1020, GetSize(from_file));
300 EXPECT_FALSE(DirectoryExists(to_dir));
301
302 context.reset(NewContext());
303 ASSERT_EQ(base::File::FILE_OK,
304 AsyncFileTestHelper::Copy(file_system_context(),
305 CreateURL(from_dir), CreateURL(to_dir)));
306
307 EXPECT_TRUE(DirectoryExists(from_dir));
308 EXPECT_TRUE(FileExists(from_file));
309 EXPECT_EQ(1020, GetSize(from_file));
310 EXPECT_TRUE(DirectoryExists(to_dir));
311 EXPECT_TRUE(FileExists(to_file));
312 EXPECT_EQ(1020, GetSize(to_file));
313 }
314
TEST_F(LocalFileUtilTest,MoveFile)315 TEST_F(LocalFileUtilTest, MoveFile) {
316 const char *from_file = "fromfile";
317 const char *to_file = "tofile";
318 bool created;
319 ASSERT_EQ(base::File::FILE_OK, EnsureFileExists(from_file, &created));
320 ASSERT_TRUE(created);
321 scoped_ptr<FileSystemOperationContext> context;
322
323 context.reset(NewContext());
324 ASSERT_EQ(base::File::FILE_OK,
325 file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
326
327 EXPECT_TRUE(FileExists(from_file));
328 EXPECT_EQ(1020, GetSize(from_file));
329
330 context.reset(NewContext());
331 ASSERT_EQ(base::File::FILE_OK,
332 AsyncFileTestHelper::Move(file_system_context(),
333 CreateURL(from_file),
334 CreateURL(to_file)));
335
336 EXPECT_FALSE(FileExists(from_file));
337 EXPECT_TRUE(FileExists(to_file));
338 EXPECT_EQ(1020, GetSize(to_file));
339 }
340
TEST_F(LocalFileUtilTest,MoveDirectory)341 TEST_F(LocalFileUtilTest, MoveDirectory) {
342 const char *from_dir = "fromdir";
343 const char *from_file = "fromdir/fromfile";
344 const char *to_dir = "todir";
345 const char *to_file = "todir/fromfile";
346 bool created;
347 scoped_ptr<FileSystemOperationContext> context;
348
349 context.reset(NewContext());
350 ASSERT_EQ(base::File::FILE_OK,
351 file_util()->CreateDirectory(context.get(), CreateURL(from_dir),
352 false, false));
353 ASSERT_EQ(base::File::FILE_OK, EnsureFileExists(from_file, &created));
354 ASSERT_TRUE(created);
355
356 context.reset(NewContext());
357 ASSERT_EQ(base::File::FILE_OK,
358 file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
359
360 EXPECT_TRUE(DirectoryExists(from_dir));
361 EXPECT_TRUE(FileExists(from_file));
362 EXPECT_EQ(1020, GetSize(from_file));
363 EXPECT_FALSE(DirectoryExists(to_dir));
364
365 context.reset(NewContext());
366 ASSERT_EQ(base::File::FILE_OK,
367 AsyncFileTestHelper::Move(file_system_context(),
368 CreateURL(from_dir),
369 CreateURL(to_dir)));
370
371 EXPECT_FALSE(DirectoryExists(from_dir));
372 EXPECT_TRUE(DirectoryExists(to_dir));
373 EXPECT_TRUE(FileExists(to_file));
374 EXPECT_EQ(1020, GetSize(to_file));
375 }
376
377 } // namespace content
378