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/external_mount_points.h"
20 #include "webkit/fileapi/file_system_context.h"
21 #include "webkit/fileapi/file_system_operation_context.h"
22 #include "webkit/fileapi/file_system_task_runners.h"
23 #include "webkit/fileapi/isolated_context.h"
24 #include "webkit/fileapi/isolated_file_util.h"
25 #include "webkit/fileapi/local_file_system_operation.h"
26 #include "webkit/fileapi/local_file_system_test_helper.h"
27 #include "webkit/fileapi/local_file_util.h"
28 #include "webkit/fileapi/mock_file_system_options.h"
29 #include "webkit/fileapi/native_file_util.h"
30 #include "webkit/fileapi/test_file_set.h"
31 #include "webkit/quota/mock_special_storage_policy.h"
33 using file_util::FileEnumerator
;
39 typedef AsyncFileTestHelper::FileEntryList FileEntryList
;
41 // Used in IsolatedFileUtilTest::SimulateDropFiles().
42 // Random root paths in which we create each file/directory of the
43 // RegularTestCases (so that we can simulate a drop with files/directories
44 // from multiple directories).
45 static const base::FilePath::CharType
* kRootPaths
[] = {
46 FILE_PATH_LITERAL("a"),
47 FILE_PATH_LITERAL("b/c"),
48 FILE_PATH_LITERAL("etc"),
51 base::FilePath
GetTopLevelPath(const base::FilePath
& path
) {
52 std::vector
<base::FilePath::StringType
> components
;
53 path
.GetComponents(&components
);
54 return base::FilePath(components
[0]);
57 bool IsDirectoryEmpty(FileSystemContext
* context
, const FileSystemURL
& url
) {
58 FileEntryList entries
;
59 EXPECT_EQ(base::PLATFORM_FILE_OK
,
60 AsyncFileTestHelper::ReadDirectory(context
, url
, &entries
));
61 return entries
.empty();
64 FileSystemURL
GetEntryURL(FileSystemContext
* file_system_context
,
65 const FileSystemURL
& dir
,
66 const base::FilePath::StringType
& name
) {
67 return file_system_context
->CreateCrackedFileSystemURL(
70 dir
.virtual_path().Append(name
));
73 base::FilePath
GetRelativeVirtualPath(const FileSystemURL
& root
,
74 const FileSystemURL
& url
) {
75 if (root
.virtual_path().empty())
76 return url
.virtual_path();
77 base::FilePath relative
;
78 const bool success
= root
.virtual_path().AppendRelativePath(
79 url
.virtual_path(), &relative
);
84 FileSystemURL
GetOtherURL(FileSystemContext
* file_system_context
,
85 const FileSystemURL
& root
,
86 const FileSystemURL
& other_root
,
87 const FileSystemURL
& url
) {
88 return file_system_context
->CreateCrackedFileSystemURL(
90 other_root
.mount_type(),
91 other_root
.virtual_path().Append(GetRelativeVirtualPath(root
, url
)));
96 // TODO(kinuko): we should have separate tests for DraggedFileUtil and
98 class IsolatedFileUtilTest
: public testing::Test
{
100 IsolatedFileUtilTest() {}
102 virtual void SetUp() {
103 ASSERT_TRUE(data_dir_
.CreateUniqueTempDir());
104 ASSERT_TRUE(partition_dir_
.CreateUniqueTempDir());
105 file_util_
.reset(new DraggedFileUtil());
107 // Register the files/directories of RegularTestCases (with random
108 // root paths) as dropped files.
111 file_system_context_
= new FileSystemContext(
112 FileSystemTaskRunners::CreateMockTaskRunners(),
113 ExternalMountPoints::CreateRefCounted().get(),
114 make_scoped_refptr(new quota::MockSpecialStoragePolicy()),
115 NULL
/* quota_manager */,
116 partition_dir_
.path(),
117 CreateAllowFileAccessOptions());
119 isolated_context()->AddReference(filesystem_id_
);
122 virtual void TearDown() {
123 isolated_context()->RemoveReference(filesystem_id_
);
127 IsolatedContext
* isolated_context() const {
128 return IsolatedContext::GetInstance();
130 const base::FilePath
& root_path() const {
131 return data_dir_
.path();
133 FileSystemContext
* file_system_context() const {
134 return file_system_context_
.get();
136 FileSystemFileUtil
* file_util() const { return file_util_
.get(); }
137 std::string
filesystem_id() const { return filesystem_id_
; }
139 base::FilePath
GetTestCasePlatformPath(
140 const base::FilePath::StringType
& path
) {
141 return toplevel_root_map_
[GetTopLevelPath(base::FilePath(path
))]
142 .Append(path
).NormalizePathSeparators();
145 base::FilePath
GetTestCaseLocalPath(const base::FilePath
& path
) {
146 base::FilePath relative
;
147 if (data_dir_
.path().AppendRelativePath(path
, &relative
))
152 FileSystemURL
GetFileSystemURL(const base::FilePath
& path
) const {
153 base::FilePath virtual_path
= isolated_context()->CreateVirtualRootPath(
154 filesystem_id()).Append(path
);
155 return file_system_context_
->CreateCrackedFileSystemURL(
156 GURL("http://example.com"),
157 kFileSystemTypeIsolated
,
161 FileSystemURL
GetOtherFileSystemURL(const base::FilePath
& path
) const {
162 return file_system_context()->CreateCrackedFileSystemURL(
163 GURL("http://example.com"),
164 kFileSystemTypeTemporary
,
165 base::FilePath().AppendASCII("dest").Append(path
));
168 void VerifyFilesHaveSameContent(const FileSystemURL
& url1
,
169 const FileSystemURL
& url2
) {
170 // Get the file info for url1.
171 base::PlatformFileInfo info1
;
172 base::FilePath platform_path1
;
173 ASSERT_EQ(base::PLATFORM_FILE_OK
,
174 AsyncFileTestHelper::GetMetadata(
175 file_system_context(), url1
, &info1
, &platform_path1
));
177 // Get the file info for url2.
178 base::PlatformFileInfo info2
;
179 base::FilePath platform_path2
;
180 ASSERT_EQ(base::PLATFORM_FILE_OK
,
181 AsyncFileTestHelper::GetMetadata(
182 file_system_context(), url2
, &info2
, &platform_path2
));
184 // See if file info matches with the other one.
185 EXPECT_EQ(info1
.is_directory
, info2
.is_directory
);
186 EXPECT_EQ(info1
.size
, info2
.size
);
187 EXPECT_EQ(info1
.is_symbolic_link
, info2
.is_symbolic_link
);
188 EXPECT_NE(platform_path1
, platform_path2
);
190 std::string content1
, content2
;
191 EXPECT_TRUE(file_util::ReadFileToString(platform_path1
, &content1
));
192 EXPECT_TRUE(file_util::ReadFileToString(platform_path2
, &content2
));
193 EXPECT_EQ(content1
, content2
);
196 void VerifyDirectoriesHaveSameContent(const FileSystemURL
& root1
,
197 const FileSystemURL
& root2
) {
198 base::FilePath root_path1
= root1
.path();
199 base::FilePath root_path2
= root2
.path();
201 FileEntryList entries
;
202 std::queue
<FileSystemURL
> directories
;
204 directories
.push(root1
);
205 std::set
<base::FilePath
> file_set1
;
206 while (!directories
.empty()) {
207 FileSystemURL dir
= directories
.front();
210 ASSERT_EQ(base::PLATFORM_FILE_OK
,
211 AsyncFileTestHelper::ReadDirectory(
212 file_system_context(), dir
, &entries
));
213 for (size_t i
= 0; i
< entries
.size(); ++i
) {
214 FileSystemURL url
= GetEntryURL(file_system_context(),
215 dir
, entries
[i
].name
);
216 if (entries
[i
].is_directory
) {
217 directories
.push(url
);
220 file_set1
.insert(GetRelativeVirtualPath(root1
, url
));
224 directories
.push(root2
);
225 while (!directories
.empty()) {
226 FileSystemURL dir
= directories
.front();
229 ASSERT_EQ(base::PLATFORM_FILE_OK
,
230 AsyncFileTestHelper::ReadDirectory(
231 file_system_context(), dir
, &entries
));
232 for (size_t i
= 0; i
< entries
.size(); ++i
) {
233 FileSystemURL url2
= GetEntryURL(file_system_context(),
234 dir
, entries
[i
].name
);
235 FileSystemURL url1
= GetOtherURL(file_system_context(),
237 if (entries
[i
].is_directory
) {
238 directories
.push(url2
);
239 EXPECT_EQ(IsDirectoryEmpty(file_system_context(), url1
),
240 IsDirectoryEmpty(file_system_context(), url2
));
243 base::FilePath relative
= GetRelativeVirtualPath(root2
, url2
);
244 EXPECT_TRUE(file_set1
.find(relative
) != file_set1
.end());
245 VerifyFilesHaveSameContent(url1
, url2
);
250 scoped_ptr
<FileSystemOperationContext
> GetOperationContext() {
251 return make_scoped_ptr(
252 new FileSystemOperationContext(file_system_context())).Pass();
257 void SimulateDropFiles() {
258 size_t root_path_index
= 0;
260 IsolatedContext::FileInfoSet toplevels
;
261 for (size_t i
= 0; i
< test::kRegularTestCaseSize
; ++i
) {
262 const test::TestCaseRecord
& test_case
= test::kRegularTestCases
[i
];
263 base::FilePath
path(test_case
.path
);
264 base::FilePath toplevel
= GetTopLevelPath(path
);
266 // We create the test case files under one of the kRootPaths
267 // to simulate a drop with multiple directories.
268 if (toplevel_root_map_
.find(toplevel
) == toplevel_root_map_
.end()) {
269 base::FilePath root
= root_path().Append(
270 kRootPaths
[(root_path_index
++) % arraysize(kRootPaths
)]);
271 toplevel_root_map_
[toplevel
] = root
;
272 toplevels
.AddPath(root
.Append(path
), NULL
);
275 test::SetUpOneTestCase(toplevel_root_map_
[toplevel
], test_case
);
278 // Register the toplevel entries.
279 filesystem_id_
= isolated_context()->RegisterDraggedFileSystem(toplevels
);
282 base::ScopedTempDir data_dir_
;
283 base::ScopedTempDir partition_dir_
;
284 MessageLoop message_loop_
;
285 std::string filesystem_id_
;
286 scoped_refptr
<FileSystemContext
> file_system_context_
;
287 std::map
<base::FilePath
, base::FilePath
> toplevel_root_map_
;
288 scoped_ptr
<IsolatedFileUtil
> file_util_
;
289 DISALLOW_COPY_AND_ASSIGN(IsolatedFileUtilTest
);
292 TEST_F(IsolatedFileUtilTest
, BasicTest
) {
293 for (size_t i
= 0; i
< test::kRegularTestCaseSize
; ++i
) {
294 SCOPED_TRACE(testing::Message() << "Testing RegularTestCases " << i
);
295 const test::TestCaseRecord
& test_case
= test::kRegularTestCases
[i
];
297 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
299 // See if we can query the file info via the isolated FileUtil.
300 // (This should succeed since we have registered all the top-level
301 // entries of the test cases in SetUp())
302 base::PlatformFileInfo info
;
303 base::FilePath platform_path
;
304 FileSystemOperationContext
context(file_system_context());
305 ASSERT_EQ(base::PLATFORM_FILE_OK
,
306 file_util()->GetFileInfo(&context
, url
, &info
, &platform_path
));
308 // See if the obtained file info is correct.
309 if (!test_case
.is_directory
)
310 ASSERT_EQ(test_case
.data_file_size
, info
.size
);
311 ASSERT_EQ(test_case
.is_directory
, info
.is_directory
);
312 ASSERT_EQ(GetTestCasePlatformPath(test_case
.path
),
313 platform_path
.NormalizePathSeparators());
317 TEST_F(IsolatedFileUtilTest
, UnregisteredPathsTest
) {
318 static const fileapi::test::TestCaseRecord kUnregisteredCases
[] = {
319 {true, FILE_PATH_LITERAL("nonexistent"), 0},
320 {true, FILE_PATH_LITERAL("nonexistent/dir foo"), 0},
321 {false, FILE_PATH_LITERAL("nonexistent/false"), 0},
322 {false, FILE_PATH_LITERAL("foo"), 30},
323 {false, FILE_PATH_LITERAL("bar"), 20},
326 for (size_t i
= 0; i
< arraysize(kUnregisteredCases
); ++i
) {
327 SCOPED_TRACE(testing::Message() << "Creating kUnregisteredCases " << i
);
328 const test::TestCaseRecord
& test_case
= kUnregisteredCases
[i
];
330 // Prepare the test file/directory.
331 SetUpOneTestCase(root_path(), test_case
);
333 // Make sure regular GetFileInfo succeeds.
334 base::PlatformFileInfo info
;
335 ASSERT_TRUE(file_util::GetFileInfo(
336 root_path().Append(test_case
.path
), &info
));
337 if (!test_case
.is_directory
)
338 ASSERT_EQ(test_case
.data_file_size
, info
.size
);
339 ASSERT_EQ(test_case
.is_directory
, info
.is_directory
);
342 for (size_t i
= 0; i
< arraysize(kUnregisteredCases
); ++i
) {
343 SCOPED_TRACE(testing::Message() << "Creating kUnregisteredCases " << i
);
344 const test::TestCaseRecord
& test_case
= kUnregisteredCases
[i
];
345 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
347 // We should not be able to get the valid URL for unregistered files.
348 ASSERT_FALSE(url
.is_valid());
350 // We should not be able to create a new operation for an invalid URL.
351 base::PlatformFileError error_code
;
352 FileSystemOperation
* operation
=
353 file_system_context()->CreateFileSystemOperation(url
, &error_code
);
354 ASSERT_EQ(NULL
, operation
);
355 ASSERT_EQ(base::PLATFORM_FILE_ERROR_INVALID_URL
, error_code
);
359 TEST_F(IsolatedFileUtilTest
, ReadDirectoryTest
) {
360 for (size_t i
= 0; i
< test::kRegularTestCaseSize
; ++i
) {
361 const test::TestCaseRecord
& test_case
= test::kRegularTestCases
[i
];
362 if (!test_case
.is_directory
)
365 SCOPED_TRACE(testing::Message() << "Testing RegularTestCases " << i
366 << ": " << test_case
.path
);
368 // Read entries in the directory to construct the expected results map.
369 typedef std::map
<base::FilePath::StringType
,
370 base::FileUtilProxy::Entry
> EntryMap
;
371 EntryMap expected_entry_map
;
373 FileEnumerator
file_enum(
374 GetTestCasePlatformPath(test_case
.path
), false /* not recursive */,
375 FileEnumerator::FILES
| FileEnumerator::DIRECTORIES
);
376 base::FilePath current
;
377 while (!(current
= file_enum
.Next()).empty()) {
378 FileEnumerator::FindInfo file_info
;
379 file_enum
.GetFindInfo(&file_info
);
380 base::FileUtilProxy::Entry entry
;
381 entry
.is_directory
= FileEnumerator::IsDirectory(file_info
);
382 entry
.name
= current
.BaseName().value();
383 entry
.size
= FileEnumerator::GetFilesize(file_info
);
384 entry
.last_modified_time
= FileEnumerator::GetLastModifiedTime(file_info
);
385 expected_entry_map
[entry
.name
] = entry
;
388 // Perform ReadDirectory in the isolated filesystem.
389 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
390 FileEntryList entries
;
391 ASSERT_EQ(base::PLATFORM_FILE_OK
,
392 AsyncFileTestHelper::ReadDirectory(
393 file_system_context(), url
, &entries
));
395 EXPECT_EQ(expected_entry_map
.size(), entries
.size());
396 for (size_t i
= 0; i
< entries
.size(); ++i
) {
397 const base::FileUtilProxy::Entry
& entry
= entries
[i
];
398 EntryMap::iterator found
= expected_entry_map
.find(entry
.name
);
399 EXPECT_TRUE(found
!= expected_entry_map
.end());
400 EXPECT_EQ(found
->second
.name
, entry
.name
);
401 EXPECT_EQ(found
->second
.is_directory
, entry
.is_directory
);
402 EXPECT_EQ(found
->second
.size
, entry
.size
);
403 EXPECT_EQ(found
->second
.last_modified_time
.ToDoubleT(),
404 entry
.last_modified_time
.ToDoubleT());
409 TEST_F(IsolatedFileUtilTest
, GetLocalFilePathTest
) {
410 for (size_t i
= 0; i
< test::kRegularTestCaseSize
; ++i
) {
411 const test::TestCaseRecord
& test_case
= test::kRegularTestCases
[i
];
412 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
414 FileSystemOperationContext
context(file_system_context());
416 base::FilePath local_file_path
;
417 EXPECT_EQ(base::PLATFORM_FILE_OK
,
418 file_util()->GetLocalFilePath(&context
, url
, &local_file_path
));
419 EXPECT_EQ(GetTestCasePlatformPath(test_case
.path
).value(),
420 local_file_path
.value());
424 TEST_F(IsolatedFileUtilTest
, CopyOutFileTest
) {
425 FileSystemURL src_root
= GetFileSystemURL(base::FilePath());
426 FileSystemURL dest_root
= GetOtherFileSystemURL(base::FilePath());
428 FileEntryList entries
;
429 std::queue
<FileSystemURL
> directories
;
430 directories
.push(src_root
);
432 ASSERT_EQ(base::PLATFORM_FILE_OK
,
433 AsyncFileTestHelper::CreateDirectory(file_system_context(),
436 while (!directories
.empty()) {
437 FileSystemURL dir
= directories
.front();
439 ASSERT_EQ(base::PLATFORM_FILE_OK
,
440 AsyncFileTestHelper::ReadDirectory(file_system_context(),
442 for (size_t i
= 0; i
< entries
.size(); ++i
) {
443 FileSystemURL src_url
= GetEntryURL(file_system_context(),
444 dir
, entries
[i
].name
);
445 FileSystemURL dest_url
= GetOtherURL(file_system_context(),
446 src_root
, dest_root
, src_url
);
448 if (entries
[i
].is_directory
) {
449 ASSERT_EQ(base::PLATFORM_FILE_OK
,
450 AsyncFileTestHelper::CreateDirectory(file_system_context(),
452 directories
.push(src_url
);
455 SCOPED_TRACE(testing::Message() << "Testing file copy "
456 << src_url
.path().value());
457 ASSERT_EQ(base::PLATFORM_FILE_OK
,
458 AsyncFileTestHelper::Copy(file_system_context(),
460 VerifyFilesHaveSameContent(src_url
, dest_url
);
465 TEST_F(IsolatedFileUtilTest
, CopyOutDirectoryTest
) {
466 FileSystemURL src_root
= GetFileSystemURL(base::FilePath());
467 FileSystemURL dest_root
= GetOtherFileSystemURL(base::FilePath());
469 ASSERT_EQ(base::PLATFORM_FILE_OK
,
470 AsyncFileTestHelper::CreateDirectory(file_system_context(),
473 FileEntryList entries
;
474 ASSERT_EQ(base::PLATFORM_FILE_OK
,
475 AsyncFileTestHelper::ReadDirectory(file_system_context(),
476 src_root
, &entries
));
477 for (size_t i
= 0; i
< entries
.size(); ++i
) {
478 if (!entries
[i
].is_directory
)
480 FileSystemURL src_url
= GetEntryURL(file_system_context(),
481 src_root
, entries
[i
].name
);
482 FileSystemURL dest_url
= GetOtherURL(file_system_context(),
483 src_root
, dest_root
, src_url
);
484 SCOPED_TRACE(testing::Message() << "Testing file copy "
485 << src_url
.path().value());
486 ASSERT_EQ(base::PLATFORM_FILE_OK
,
487 AsyncFileTestHelper::Copy(file_system_context(),
489 VerifyDirectoriesHaveSameContent(src_url
, dest_url
);
493 TEST_F(IsolatedFileUtilTest
, TouchTest
) {
494 for (size_t i
= 0; i
< test::kRegularTestCaseSize
; ++i
) {
495 const test::TestCaseRecord
& test_case
= test::kRegularTestCases
[i
];
496 if (test_case
.is_directory
)
498 SCOPED_TRACE(testing::Message() << test_case
.path
);
499 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
501 base::Time last_access_time
= base::Time::FromTimeT(1000);
502 base::Time last_modified_time
= base::Time::FromTimeT(2000);
504 EXPECT_EQ(base::PLATFORM_FILE_OK
,
505 file_util()->Touch(GetOperationContext().get(), url
,
507 last_modified_time
));
510 base::PlatformFileInfo info
;
511 base::FilePath platform_path
;
512 ASSERT_EQ(base::PLATFORM_FILE_OK
,
513 file_util()->GetFileInfo(GetOperationContext().get(), url
,
514 &info
, &platform_path
));
515 EXPECT_EQ(last_access_time
.ToTimeT(), info
.last_accessed
.ToTimeT());
516 EXPECT_EQ(last_modified_time
.ToTimeT(), info
.last_modified
.ToTimeT());
520 TEST_F(IsolatedFileUtilTest
, TruncateTest
) {
521 for (size_t i
= 0; i
< test::kRegularTestCaseSize
; ++i
) {
522 const test::TestCaseRecord
& test_case
= test::kRegularTestCases
[i
];
523 if (test_case
.is_directory
)
526 SCOPED_TRACE(testing::Message() << test_case
.path
);
527 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
530 base::PlatformFileInfo info
;
531 base::FilePath platform_path
;
532 EXPECT_EQ(base::PLATFORM_FILE_OK
,
533 file_util()->Truncate(GetOperationContext().get(), url
, 0));
534 ASSERT_EQ(base::PLATFORM_FILE_OK
,
535 file_util()->GetFileInfo(GetOperationContext().get(), url
,
536 &info
, &platform_path
));
537 EXPECT_EQ(0, info
.size
);
539 // Truncate (extend) to 999.
540 EXPECT_EQ(base::PLATFORM_FILE_OK
,
541 file_util()->Truncate(GetOperationContext().get(), url
, 999));
542 ASSERT_EQ(base::PLATFORM_FILE_OK
,
543 file_util()->GetFileInfo(GetOperationContext().get(), url
,
544 &info
, &platform_path
));
545 EXPECT_EQ(999, info
.size
);
549 } // namespace fileapi