1 // Copyright (c) 2012 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.
11 #include "base/file_util.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/logging.h"
14 #include "base/message_loop.h"
15 #include "base/message_loop_proxy.h"
16 #include "base/time.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "webkit/fileapi/async_file_test_helper.h"
19 #include "webkit/fileapi/file_system_context.h"
20 #include "webkit/fileapi/file_system_operation_context.h"
21 #include "webkit/fileapi/file_system_task_runners.h"
22 #include "webkit/fileapi/isolated_context.h"
23 #include "webkit/fileapi/isolated_file_util.h"
24 #include "webkit/fileapi/local_file_system_operation.h"
25 #include "webkit/fileapi/local_file_system_test_helper.h"
26 #include "webkit/fileapi/local_file_util.h"
27 #include "webkit/fileapi/mock_file_system_context.h"
28 #include "webkit/fileapi/native_file_util.h"
29 #include "webkit/fileapi/test_file_set.h"
31 using file_util::FileEnumerator
;
37 typedef AsyncFileTestHelper::FileEntryList FileEntryList
;
39 // Used in IsolatedFileUtilTest::SimulateDropFiles().
40 // Random root paths in which we create each file/directory of the
41 // RegularTestCases (so that we can simulate a drop with files/directories
42 // from multiple directories).
43 static const base::FilePath::CharType
* kRootPaths
[] = {
44 FILE_PATH_LITERAL("a"),
45 FILE_PATH_LITERAL("b/c"),
46 FILE_PATH_LITERAL("etc"),
49 base::FilePath
GetTopLevelPath(const base::FilePath
& path
) {
50 std::vector
<base::FilePath::StringType
> components
;
51 path
.GetComponents(&components
);
52 return base::FilePath(components
[0]);
55 bool IsDirectoryEmpty(FileSystemContext
* context
, const FileSystemURL
& url
) {
56 FileEntryList entries
;
57 EXPECT_EQ(base::PLATFORM_FILE_OK
,
58 AsyncFileTestHelper::ReadDirectory(context
, url
, &entries
));
59 return entries
.empty();
62 FileSystemURL
GetEntryURL(FileSystemContext
* file_system_context
,
63 const FileSystemURL
& dir
,
64 const base::FilePath::StringType
& name
) {
65 return file_system_context
->CreateCrackedFileSystemURL(
68 dir
.virtual_path().Append(name
));
71 base::FilePath
GetRelativeVirtualPath(const FileSystemURL
& root
,
72 const FileSystemURL
& url
) {
73 if (root
.virtual_path().empty())
74 return url
.virtual_path();
75 base::FilePath relative
;
76 const bool success
= root
.virtual_path().AppendRelativePath(
77 url
.virtual_path(), &relative
);
82 FileSystemURL
GetOtherURL(FileSystemContext
* file_system_context
,
83 const FileSystemURL
& root
,
84 const FileSystemURL
& other_root
,
85 const FileSystemURL
& url
) {
86 return file_system_context
->CreateCrackedFileSystemURL(
88 other_root
.mount_type(),
89 other_root
.virtual_path().Append(GetRelativeVirtualPath(root
, url
)));
94 // TODO(kinuko): we should have separate tests for DraggedFileUtil and
96 class IsolatedFileUtilTest
: public testing::Test
{
98 IsolatedFileUtilTest() {}
100 virtual void SetUp() {
101 ASSERT_TRUE(data_dir_
.CreateUniqueTempDir());
102 ASSERT_TRUE(partition_dir_
.CreateUniqueTempDir());
103 file_util_
.reset(new DraggedFileUtil());
105 // Register the files/directories of RegularTestCases (with random
106 // root paths) as dropped files.
109 file_system_context_
= CreateFileSystemContextForTesting(
110 NULL
/* quota_manager */,
111 partition_dir_
.path());
113 isolated_context()->AddReference(filesystem_id_
);
116 virtual void TearDown() {
117 isolated_context()->RemoveReference(filesystem_id_
);
121 IsolatedContext
* isolated_context() const {
122 return IsolatedContext::GetInstance();
124 const base::FilePath
& root_path() const {
125 return data_dir_
.path();
127 FileSystemContext
* file_system_context() const {
128 return file_system_context_
.get();
130 FileSystemFileUtil
* file_util() const { return file_util_
.get(); }
131 std::string
filesystem_id() const { return filesystem_id_
; }
133 base::FilePath
GetTestCasePlatformPath(
134 const base::FilePath::StringType
& path
) {
135 return toplevel_root_map_
[GetTopLevelPath(base::FilePath(path
))]
136 .Append(path
).NormalizePathSeparators();
139 base::FilePath
GetTestCaseLocalPath(const base::FilePath
& path
) {
140 base::FilePath relative
;
141 if (data_dir_
.path().AppendRelativePath(path
, &relative
))
146 FileSystemURL
GetFileSystemURL(const base::FilePath
& path
) const {
147 base::FilePath virtual_path
= isolated_context()->CreateVirtualRootPath(
148 filesystem_id()).Append(path
);
149 return file_system_context_
->CreateCrackedFileSystemURL(
150 GURL("http://example.com"),
151 kFileSystemTypeIsolated
,
155 FileSystemURL
GetOtherFileSystemURL(const base::FilePath
& path
) const {
156 return file_system_context()->CreateCrackedFileSystemURL(
157 GURL("http://example.com"),
158 kFileSystemTypeTemporary
,
159 base::FilePath().AppendASCII("dest").Append(path
));
162 void VerifyFilesHaveSameContent(const FileSystemURL
& url1
,
163 const FileSystemURL
& url2
) {
164 // Get the file info for url1.
165 base::PlatformFileInfo info1
;
166 base::FilePath platform_path1
;
167 ASSERT_EQ(base::PLATFORM_FILE_OK
,
168 AsyncFileTestHelper::GetMetadata(
169 file_system_context(), url1
, &info1
, &platform_path1
));
171 // Get the file info for url2.
172 base::PlatformFileInfo info2
;
173 base::FilePath platform_path2
;
174 ASSERT_EQ(base::PLATFORM_FILE_OK
,
175 AsyncFileTestHelper::GetMetadata(
176 file_system_context(), url2
, &info2
, &platform_path2
));
178 // See if file info matches with the other one.
179 EXPECT_EQ(info1
.is_directory
, info2
.is_directory
);
180 EXPECT_EQ(info1
.size
, info2
.size
);
181 EXPECT_EQ(info1
.is_symbolic_link
, info2
.is_symbolic_link
);
182 EXPECT_NE(platform_path1
, platform_path2
);
184 std::string content1
, content2
;
185 EXPECT_TRUE(file_util::ReadFileToString(platform_path1
, &content1
));
186 EXPECT_TRUE(file_util::ReadFileToString(platform_path2
, &content2
));
187 EXPECT_EQ(content1
, content2
);
190 void VerifyDirectoriesHaveSameContent(const FileSystemURL
& root1
,
191 const FileSystemURL
& root2
) {
192 base::FilePath root_path1
= root1
.path();
193 base::FilePath root_path2
= root2
.path();
195 FileEntryList entries
;
196 std::queue
<FileSystemURL
> directories
;
198 directories
.push(root1
);
199 std::set
<base::FilePath
> file_set1
;
200 while (!directories
.empty()) {
201 FileSystemURL dir
= directories
.front();
204 ASSERT_EQ(base::PLATFORM_FILE_OK
,
205 AsyncFileTestHelper::ReadDirectory(
206 file_system_context(), dir
, &entries
));
207 for (size_t i
= 0; i
< entries
.size(); ++i
) {
208 FileSystemURL url
= GetEntryURL(file_system_context(),
209 dir
, entries
[i
].name
);
210 if (entries
[i
].is_directory
) {
211 directories
.push(url
);
214 file_set1
.insert(GetRelativeVirtualPath(root1
, url
));
218 directories
.push(root2
);
219 while (!directories
.empty()) {
220 FileSystemURL dir
= directories
.front();
223 ASSERT_EQ(base::PLATFORM_FILE_OK
,
224 AsyncFileTestHelper::ReadDirectory(
225 file_system_context(), dir
, &entries
));
226 for (size_t i
= 0; i
< entries
.size(); ++i
) {
227 FileSystemURL url2
= GetEntryURL(file_system_context(),
228 dir
, entries
[i
].name
);
229 FileSystemURL url1
= GetOtherURL(file_system_context(),
231 if (entries
[i
].is_directory
) {
232 directories
.push(url2
);
233 EXPECT_EQ(IsDirectoryEmpty(file_system_context(), url1
),
234 IsDirectoryEmpty(file_system_context(), url2
));
237 base::FilePath relative
= GetRelativeVirtualPath(root2
, url2
);
238 EXPECT_TRUE(file_set1
.find(relative
) != file_set1
.end());
239 VerifyFilesHaveSameContent(url1
, url2
);
244 scoped_ptr
<FileSystemOperationContext
> GetOperationContext() {
245 return make_scoped_ptr(
246 new FileSystemOperationContext(file_system_context())).Pass();
251 void SimulateDropFiles() {
252 size_t root_path_index
= 0;
254 IsolatedContext::FileInfoSet toplevels
;
255 for (size_t i
= 0; i
< test::kRegularTestCaseSize
; ++i
) {
256 const test::TestCaseRecord
& test_case
= test::kRegularTestCases
[i
];
257 base::FilePath
path(test_case
.path
);
258 base::FilePath toplevel
= GetTopLevelPath(path
);
260 // We create the test case files under one of the kRootPaths
261 // to simulate a drop with multiple directories.
262 if (toplevel_root_map_
.find(toplevel
) == toplevel_root_map_
.end()) {
263 base::FilePath root
= root_path().Append(
264 kRootPaths
[(root_path_index
++) % arraysize(kRootPaths
)]);
265 toplevel_root_map_
[toplevel
] = root
;
266 toplevels
.AddPath(root
.Append(path
), NULL
);
269 test::SetUpOneTestCase(toplevel_root_map_
[toplevel
], test_case
);
272 // Register the toplevel entries.
273 filesystem_id_
= isolated_context()->RegisterDraggedFileSystem(toplevels
);
276 base::ScopedTempDir data_dir_
;
277 base::ScopedTempDir partition_dir_
;
278 base::MessageLoop message_loop_
;
279 std::string filesystem_id_
;
280 scoped_refptr
<FileSystemContext
> file_system_context_
;
281 std::map
<base::FilePath
, base::FilePath
> toplevel_root_map_
;
282 scoped_ptr
<IsolatedFileUtil
> file_util_
;
283 DISALLOW_COPY_AND_ASSIGN(IsolatedFileUtilTest
);
286 TEST_F(IsolatedFileUtilTest
, BasicTest
) {
287 for (size_t i
= 0; i
< test::kRegularTestCaseSize
; ++i
) {
288 SCOPED_TRACE(testing::Message() << "Testing RegularTestCases " << i
);
289 const test::TestCaseRecord
& test_case
= test::kRegularTestCases
[i
];
291 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
293 // See if we can query the file info via the isolated FileUtil.
294 // (This should succeed since we have registered all the top-level
295 // entries of the test cases in SetUp())
296 base::PlatformFileInfo info
;
297 base::FilePath platform_path
;
298 FileSystemOperationContext
context(file_system_context());
299 ASSERT_EQ(base::PLATFORM_FILE_OK
,
300 file_util()->GetFileInfo(&context
, url
, &info
, &platform_path
));
302 // See if the obtained file info is correct.
303 if (!test_case
.is_directory
)
304 ASSERT_EQ(test_case
.data_file_size
, info
.size
);
305 ASSERT_EQ(test_case
.is_directory
, info
.is_directory
);
306 ASSERT_EQ(GetTestCasePlatformPath(test_case
.path
),
307 platform_path
.NormalizePathSeparators());
311 TEST_F(IsolatedFileUtilTest
, UnregisteredPathsTest
) {
312 static const fileapi::test::TestCaseRecord kUnregisteredCases
[] = {
313 {true, FILE_PATH_LITERAL("nonexistent"), 0},
314 {true, FILE_PATH_LITERAL("nonexistent/dir foo"), 0},
315 {false, FILE_PATH_LITERAL("nonexistent/false"), 0},
316 {false, FILE_PATH_LITERAL("foo"), 30},
317 {false, FILE_PATH_LITERAL("bar"), 20},
320 for (size_t i
= 0; i
< arraysize(kUnregisteredCases
); ++i
) {
321 SCOPED_TRACE(testing::Message() << "Creating kUnregisteredCases " << i
);
322 const test::TestCaseRecord
& test_case
= kUnregisteredCases
[i
];
324 // Prepare the test file/directory.
325 SetUpOneTestCase(root_path(), test_case
);
327 // Make sure regular GetFileInfo succeeds.
328 base::PlatformFileInfo info
;
329 ASSERT_TRUE(file_util::GetFileInfo(
330 root_path().Append(test_case
.path
), &info
));
331 if (!test_case
.is_directory
)
332 ASSERT_EQ(test_case
.data_file_size
, info
.size
);
333 ASSERT_EQ(test_case
.is_directory
, info
.is_directory
);
336 for (size_t i
= 0; i
< arraysize(kUnregisteredCases
); ++i
) {
337 SCOPED_TRACE(testing::Message() << "Creating kUnregisteredCases " << i
);
338 const test::TestCaseRecord
& test_case
= kUnregisteredCases
[i
];
339 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
341 // We should not be able to get the valid URL for unregistered files.
342 ASSERT_FALSE(url
.is_valid());
344 // We should not be able to create a new operation for an invalid URL.
345 base::PlatformFileError error_code
;
346 FileSystemOperation
* operation
=
347 file_system_context()->CreateFileSystemOperation(url
, &error_code
);
348 ASSERT_EQ(NULL
, operation
);
349 ASSERT_EQ(base::PLATFORM_FILE_ERROR_INVALID_URL
, error_code
);
353 TEST_F(IsolatedFileUtilTest
, ReadDirectoryTest
) {
354 for (size_t i
= 0; i
< test::kRegularTestCaseSize
; ++i
) {
355 const test::TestCaseRecord
& test_case
= test::kRegularTestCases
[i
];
356 if (!test_case
.is_directory
)
359 SCOPED_TRACE(testing::Message() << "Testing RegularTestCases " << i
360 << ": " << test_case
.path
);
362 // Read entries in the directory to construct the expected results map.
363 typedef std::map
<base::FilePath::StringType
, DirectoryEntry
> EntryMap
;
364 EntryMap expected_entry_map
;
366 base::FilePath dir_path
= GetTestCasePlatformPath(test_case
.path
);
367 FileEnumerator
file_enum(
368 dir_path
, false /* not recursive */,
369 FileEnumerator::FILES
| FileEnumerator::DIRECTORIES
);
370 base::FilePath current
;
371 while (!(current
= file_enum
.Next()).empty()) {
372 FileEnumerator::FindInfo file_info
;
373 file_enum
.GetFindInfo(&file_info
);
374 DirectoryEntry entry
;
375 entry
.is_directory
= FileEnumerator::IsDirectory(file_info
);
376 entry
.name
= current
.BaseName().value();
377 entry
.size
= FileEnumerator::GetFilesize(file_info
);
378 entry
.last_modified_time
= FileEnumerator::GetLastModifiedTime(file_info
);
379 expected_entry_map
[entry
.name
] = entry
;
381 #if defined(OS_POSIX)
382 // Creates a symlink for each file/directory.
383 // They should be ignored by ReadDirectory, so we don't add them
384 // to expected_entry_map.
385 file_util::CreateSymbolicLink(
387 dir_path
.Append(current
.BaseName().AddExtension(
388 FILE_PATH_LITERAL("link"))));
392 // Perform ReadDirectory in the isolated filesystem.
393 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
394 FileEntryList entries
;
395 ASSERT_EQ(base::PLATFORM_FILE_OK
,
396 AsyncFileTestHelper::ReadDirectory(
397 file_system_context(), url
, &entries
));
399 EXPECT_EQ(expected_entry_map
.size(), entries
.size());
400 for (size_t i
= 0; i
< entries
.size(); ++i
) {
401 const DirectoryEntry
& entry
= entries
[i
];
402 EntryMap::iterator found
= expected_entry_map
.find(entry
.name
);
403 EXPECT_TRUE(found
!= expected_entry_map
.end());
404 EXPECT_EQ(found
->second
.name
, entry
.name
);
405 EXPECT_EQ(found
->second
.is_directory
, entry
.is_directory
);
406 EXPECT_EQ(found
->second
.size
, entry
.size
);
407 EXPECT_EQ(found
->second
.last_modified_time
.ToDoubleT(),
408 entry
.last_modified_time
.ToDoubleT());
413 TEST_F(IsolatedFileUtilTest
, GetLocalFilePathTest
) {
414 for (size_t i
= 0; i
< test::kRegularTestCaseSize
; ++i
) {
415 const test::TestCaseRecord
& test_case
= test::kRegularTestCases
[i
];
416 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
418 FileSystemOperationContext
context(file_system_context());
420 base::FilePath local_file_path
;
421 EXPECT_EQ(base::PLATFORM_FILE_OK
,
422 file_util()->GetLocalFilePath(&context
, url
, &local_file_path
));
423 EXPECT_EQ(GetTestCasePlatformPath(test_case
.path
).value(),
424 local_file_path
.value());
428 TEST_F(IsolatedFileUtilTest
, CopyOutFileTest
) {
429 FileSystemURL src_root
= GetFileSystemURL(base::FilePath());
430 FileSystemURL dest_root
= GetOtherFileSystemURL(base::FilePath());
432 FileEntryList entries
;
433 std::queue
<FileSystemURL
> directories
;
434 directories
.push(src_root
);
436 ASSERT_EQ(base::PLATFORM_FILE_OK
,
437 AsyncFileTestHelper::CreateDirectory(file_system_context(),
440 while (!directories
.empty()) {
441 FileSystemURL dir
= directories
.front();
443 ASSERT_EQ(base::PLATFORM_FILE_OK
,
444 AsyncFileTestHelper::ReadDirectory(file_system_context(),
446 for (size_t i
= 0; i
< entries
.size(); ++i
) {
447 FileSystemURL src_url
= GetEntryURL(file_system_context(),
448 dir
, entries
[i
].name
);
449 FileSystemURL dest_url
= GetOtherURL(file_system_context(),
450 src_root
, dest_root
, src_url
);
452 if (entries
[i
].is_directory
) {
453 ASSERT_EQ(base::PLATFORM_FILE_OK
,
454 AsyncFileTestHelper::CreateDirectory(file_system_context(),
456 directories
.push(src_url
);
459 SCOPED_TRACE(testing::Message() << "Testing file copy "
460 << src_url
.path().value());
461 ASSERT_EQ(base::PLATFORM_FILE_OK
,
462 AsyncFileTestHelper::Copy(file_system_context(),
464 VerifyFilesHaveSameContent(src_url
, dest_url
);
469 TEST_F(IsolatedFileUtilTest
, CopyOutDirectoryTest
) {
470 FileSystemURL src_root
= GetFileSystemURL(base::FilePath());
471 FileSystemURL dest_root
= GetOtherFileSystemURL(base::FilePath());
473 ASSERT_EQ(base::PLATFORM_FILE_OK
,
474 AsyncFileTestHelper::CreateDirectory(file_system_context(),
477 FileEntryList entries
;
478 ASSERT_EQ(base::PLATFORM_FILE_OK
,
479 AsyncFileTestHelper::ReadDirectory(file_system_context(),
480 src_root
, &entries
));
481 for (size_t i
= 0; i
< entries
.size(); ++i
) {
482 if (!entries
[i
].is_directory
)
484 FileSystemURL src_url
= GetEntryURL(file_system_context(),
485 src_root
, entries
[i
].name
);
486 FileSystemURL dest_url
= GetOtherURL(file_system_context(),
487 src_root
, dest_root
, src_url
);
488 SCOPED_TRACE(testing::Message() << "Testing file copy "
489 << src_url
.path().value());
490 ASSERT_EQ(base::PLATFORM_FILE_OK
,
491 AsyncFileTestHelper::Copy(file_system_context(),
493 VerifyDirectoriesHaveSameContent(src_url
, dest_url
);
497 TEST_F(IsolatedFileUtilTest
, TouchTest
) {
498 for (size_t i
= 0; i
< test::kRegularTestCaseSize
; ++i
) {
499 const test::TestCaseRecord
& test_case
= test::kRegularTestCases
[i
];
500 if (test_case
.is_directory
)
502 SCOPED_TRACE(testing::Message() << test_case
.path
);
503 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
505 base::Time last_access_time
= base::Time::FromTimeT(1000);
506 base::Time last_modified_time
= base::Time::FromTimeT(2000);
508 EXPECT_EQ(base::PLATFORM_FILE_OK
,
509 file_util()->Touch(GetOperationContext().get(), url
,
511 last_modified_time
));
514 base::PlatformFileInfo info
;
515 base::FilePath platform_path
;
516 ASSERT_EQ(base::PLATFORM_FILE_OK
,
517 file_util()->GetFileInfo(GetOperationContext().get(), url
,
518 &info
, &platform_path
));
519 EXPECT_EQ(last_access_time
.ToTimeT(), info
.last_accessed
.ToTimeT());
520 EXPECT_EQ(last_modified_time
.ToTimeT(), info
.last_modified
.ToTimeT());
524 TEST_F(IsolatedFileUtilTest
, TruncateTest
) {
525 for (size_t i
= 0; i
< test::kRegularTestCaseSize
; ++i
) {
526 const test::TestCaseRecord
& test_case
= test::kRegularTestCases
[i
];
527 if (test_case
.is_directory
)
530 SCOPED_TRACE(testing::Message() << test_case
.path
);
531 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
534 base::PlatformFileInfo info
;
535 base::FilePath platform_path
;
536 EXPECT_EQ(base::PLATFORM_FILE_OK
,
537 file_util()->Truncate(GetOperationContext().get(), url
, 0));
538 ASSERT_EQ(base::PLATFORM_FILE_OK
,
539 file_util()->GetFileInfo(GetOperationContext().get(), url
,
540 &info
, &platform_path
));
541 EXPECT_EQ(0, info
.size
);
543 // Truncate (extend) to 999.
544 EXPECT_EQ(base::PLATFORM_FILE_OK
,
545 file_util()->Truncate(GetOperationContext().get(), url
, 999));
546 ASSERT_EQ(base::PLATFORM_FILE_OK
,
547 file_util()->GetFileInfo(GetOperationContext().get(), url
,
548 &info
, &platform_path
));
549 EXPECT_EQ(999, info
.size
);
553 } // namespace fileapi