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.
11 #include "base/file_util.h"
12 #include "base/files/file_enumerator.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/logging.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/message_loop/message_loop_proxy.h"
17 #include "base/time/time.h"
18 #include "content/public/test/async_file_test_helper.h"
19 #include "content/public/test/test_file_system_context.h"
20 #include "content/test/fileapi_test_file_set.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 #include "webkit/browser/fileapi/dragged_file_util.h"
23 #include "webkit/browser/fileapi/file_system_context.h"
24 #include "webkit/browser/fileapi/file_system_operation_context.h"
25 #include "webkit/browser/fileapi/isolated_context.h"
26 #include "webkit/browser/fileapi/local_file_util.h"
27 #include "webkit/browser/fileapi/native_file_util.h"
29 using content::AsyncFileTestHelper
;
30 using storage::FileSystemContext
;
31 using storage::FileSystemOperationContext
;
32 using storage::FileSystemType
;
33 using storage::FileSystemURL
;
39 typedef AsyncFileTestHelper::FileEntryList FileEntryList
;
41 // Used in DraggedFileUtilTest::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::File::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 class DraggedFileUtilTest
: public testing::Test
{
98 DraggedFileUtilTest() {}
100 virtual void SetUp() {
101 ASSERT_TRUE(data_dir_
.CreateUniqueTempDir());
102 ASSERT_TRUE(partition_dir_
.CreateUniqueTempDir());
103 file_util_
.reset(new storage::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 storage::IsolatedContext
* isolated_context() const {
122 return storage::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 storage::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 storage::kFileSystemTypeIsolated
,
155 FileSystemURL
GetOtherFileSystemURL(const base::FilePath
& path
) const {
156 return file_system_context()->CreateCrackedFileSystemURL(
157 GURL("http://example.com"),
158 storage::kFileSystemTypeTemporary
,
159 base::FilePath().AppendASCII("dest").Append(path
));
162 void VerifyFilesHaveSameContent(const FileSystemURL
& url1
,
163 const FileSystemURL
& url2
) {
164 // Get the file info and the platform path for url1.
165 base::File::Info info1
;
166 ASSERT_EQ(base::File::FILE_OK
,
167 AsyncFileTestHelper::GetMetadata(
168 file_system_context(), url1
, &info1
));
169 base::FilePath platform_path1
;
170 ASSERT_EQ(base::File::FILE_OK
,
171 AsyncFileTestHelper::GetPlatformPath(
172 file_system_context(), url1
, &platform_path1
));
174 // Get the file info and the platform path for url2.
175 base::File::Info info2
;
176 ASSERT_EQ(base::File::FILE_OK
,
177 AsyncFileTestHelper::GetMetadata(
178 file_system_context(), url2
, &info2
));
179 base::FilePath platform_path2
;
180 ASSERT_EQ(base::File::FILE_OK
,
181 AsyncFileTestHelper::GetPlatformPath(
182 file_system_context(), url2
, &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(base::ReadFileToString(platform_path1
, &content1
));
192 EXPECT_TRUE(base::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::File::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::File::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
<storage::FileSystemOperationContext
> GetOperationContext() {
251 return make_scoped_ptr(new storage::FileSystemOperationContext(
252 file_system_context())).Pass();
257 void SimulateDropFiles() {
258 size_t root_path_index
= 0;
260 storage::IsolatedContext::FileInfoSet toplevels
;
261 for (size_t i
= 0; i
< kRegularFileSystemTestCaseSize
; ++i
) {
262 const FileSystemTestCaseRecord
& test_case
=
263 kRegularFileSystemTestCases
[i
];
264 base::FilePath
path(test_case
.path
);
265 base::FilePath toplevel
= GetTopLevelPath(path
);
267 // We create the test case files under one of the kRootPaths
268 // to simulate a drop with multiple directories.
269 if (toplevel_root_map_
.find(toplevel
) == toplevel_root_map_
.end()) {
270 base::FilePath root
= root_path().Append(
271 kRootPaths
[(root_path_index
++) % arraysize(kRootPaths
)]);
272 toplevel_root_map_
[toplevel
] = root
;
273 toplevels
.AddPath(root
.Append(path
), NULL
);
276 SetUpOneFileSystemTestCase(toplevel_root_map_
[toplevel
], test_case
);
279 // Register the toplevel entries.
280 filesystem_id_
= isolated_context()->RegisterDraggedFileSystem(toplevels
);
283 base::ScopedTempDir data_dir_
;
284 base::ScopedTempDir partition_dir_
;
285 base::MessageLoopForIO message_loop_
;
286 std::string filesystem_id_
;
287 scoped_refptr
<FileSystemContext
> file_system_context_
;
288 std::map
<base::FilePath
, base::FilePath
> toplevel_root_map_
;
289 scoped_ptr
<storage::DraggedFileUtil
> file_util_
;
290 DISALLOW_COPY_AND_ASSIGN(DraggedFileUtilTest
);
293 TEST_F(DraggedFileUtilTest
, BasicTest
) {
294 for (size_t i
= 0; i
< kRegularFileSystemTestCaseSize
; ++i
) {
295 SCOPED_TRACE(testing::Message() << "Testing RegularTestCases " << i
);
296 const FileSystemTestCaseRecord
& test_case
=
297 kRegularFileSystemTestCases
[i
];
299 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
301 // See if we can query the file info via the isolated FileUtil.
302 // (This should succeed since we have registered all the top-level
303 // entries of the test cases in SetUp())
304 base::File::Info info
;
305 base::FilePath platform_path
;
306 FileSystemOperationContext
context(file_system_context());
307 ASSERT_EQ(base::File::FILE_OK
,
308 file_util()->GetFileInfo(&context
, url
, &info
, &platform_path
));
310 // See if the obtained file info is correct.
311 if (!test_case
.is_directory
)
312 ASSERT_EQ(test_case
.data_file_size
, info
.size
);
313 ASSERT_EQ(test_case
.is_directory
, info
.is_directory
);
314 ASSERT_EQ(GetTestCasePlatformPath(test_case
.path
),
315 platform_path
.NormalizePathSeparators());
319 TEST_F(DraggedFileUtilTest
, UnregisteredPathsTest
) {
320 static const FileSystemTestCaseRecord kUnregisteredCases
[] = {
321 {true, FILE_PATH_LITERAL("nonexistent"), 0},
322 {true, FILE_PATH_LITERAL("nonexistent/dir foo"), 0},
323 {false, FILE_PATH_LITERAL("nonexistent/false"), 0},
324 {false, FILE_PATH_LITERAL("foo"), 30},
325 {false, FILE_PATH_LITERAL("bar"), 20},
328 for (size_t i
= 0; i
< arraysize(kUnregisteredCases
); ++i
) {
329 SCOPED_TRACE(testing::Message() << "Creating kUnregisteredCases " << i
);
330 const FileSystemTestCaseRecord
& test_case
= kUnregisteredCases
[i
];
332 // Prepare the test file/directory.
333 SetUpOneFileSystemTestCase(root_path(), test_case
);
335 // Make sure regular GetFileInfo succeeds.
336 base::File::Info info
;
337 ASSERT_TRUE(base::GetFileInfo(root_path().Append(test_case
.path
), &info
));
338 if (!test_case
.is_directory
)
339 ASSERT_EQ(test_case
.data_file_size
, info
.size
);
340 ASSERT_EQ(test_case
.is_directory
, info
.is_directory
);
343 for (size_t i
= 0; i
< arraysize(kUnregisteredCases
); ++i
) {
344 SCOPED_TRACE(testing::Message() << "Creating kUnregisteredCases " << i
);
345 const FileSystemTestCaseRecord
& test_case
= kUnregisteredCases
[i
];
346 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
348 // We should not be able to get the valid URL for unregistered files.
349 ASSERT_FALSE(url
.is_valid());
353 TEST_F(DraggedFileUtilTest
, ReadDirectoryTest
) {
354 for (size_t i
= 0; i
< kRegularFileSystemTestCaseSize
; ++i
) {
355 const FileSystemTestCaseRecord
& test_case
=
356 kRegularFileSystemTestCases
[i
];
357 if (!test_case
.is_directory
)
360 SCOPED_TRACE(testing::Message() << "Testing RegularTestCases " << i
361 << ": " << test_case
.path
);
363 // Read entries in the directory to construct the expected results map.
364 typedef std::map
<base::FilePath::StringType
, storage::DirectoryEntry
>
366 EntryMap expected_entry_map
;
368 base::FilePath dir_path
= GetTestCasePlatformPath(test_case
.path
);
369 base::FileEnumerator
file_enum(
370 dir_path
, false /* not recursive */,
371 base::FileEnumerator::FILES
| base::FileEnumerator::DIRECTORIES
);
372 base::FilePath current
;
373 while (!(current
= file_enum
.Next()).empty()) {
374 base::FileEnumerator::FileInfo file_info
= file_enum
.GetInfo();
375 storage::DirectoryEntry entry
;
376 entry
.is_directory
= file_info
.IsDirectory();
377 entry
.name
= current
.BaseName().value();
378 entry
.size
= file_info
.GetSize();
379 entry
.last_modified_time
= file_info
.GetLastModifiedTime();
380 expected_entry_map
[entry
.name
] = entry
;
382 #if defined(OS_POSIX)
383 // Creates a symlink for each file/directory.
384 // They should be ignored by ReadDirectory, so we don't add them
385 // to expected_entry_map.
386 base::CreateSymbolicLink(
388 dir_path
.Append(current
.BaseName().AddExtension(
389 FILE_PATH_LITERAL("link"))));
393 // Perform ReadDirectory in the isolated filesystem.
394 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
395 FileEntryList entries
;
396 ASSERT_EQ(base::File::FILE_OK
,
397 AsyncFileTestHelper::ReadDirectory(
398 file_system_context(), url
, &entries
));
400 EXPECT_EQ(expected_entry_map
.size(), entries
.size());
401 for (size_t i
= 0; i
< entries
.size(); ++i
) {
402 const storage::DirectoryEntry
& entry
= entries
[i
];
403 EntryMap::iterator found
= expected_entry_map
.find(entry
.name
);
404 EXPECT_TRUE(found
!= expected_entry_map
.end());
405 EXPECT_EQ(found
->second
.name
, entry
.name
);
406 EXPECT_EQ(found
->second
.is_directory
, entry
.is_directory
);
407 EXPECT_EQ(found
->second
.size
, entry
.size
);
408 EXPECT_EQ(found
->second
.last_modified_time
.ToDoubleT(),
409 entry
.last_modified_time
.ToDoubleT());
414 TEST_F(DraggedFileUtilTest
, GetLocalFilePathTest
) {
415 for (size_t i
= 0; i
< kRegularFileSystemTestCaseSize
; ++i
) {
416 const FileSystemTestCaseRecord
& test_case
=
417 kRegularFileSystemTestCases
[i
];
418 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
420 FileSystemOperationContext
context(file_system_context());
422 base::FilePath local_file_path
;
423 EXPECT_EQ(base::File::FILE_OK
,
424 file_util()->GetLocalFilePath(&context
, url
, &local_file_path
));
425 EXPECT_EQ(GetTestCasePlatformPath(test_case
.path
).value(),
426 local_file_path
.value());
430 TEST_F(DraggedFileUtilTest
, CopyOutFileTest
) {
431 FileSystemURL src_root
= GetFileSystemURL(base::FilePath());
432 FileSystemURL dest_root
= GetOtherFileSystemURL(base::FilePath());
434 FileEntryList entries
;
435 std::queue
<FileSystemURL
> directories
;
436 directories
.push(src_root
);
438 ASSERT_EQ(base::File::FILE_OK
,
439 AsyncFileTestHelper::CreateDirectory(file_system_context(),
442 while (!directories
.empty()) {
443 FileSystemURL dir
= directories
.front();
445 ASSERT_EQ(base::File::FILE_OK
,
446 AsyncFileTestHelper::ReadDirectory(file_system_context(),
448 for (size_t i
= 0; i
< entries
.size(); ++i
) {
449 FileSystemURL src_url
= GetEntryURL(file_system_context(),
450 dir
, entries
[i
].name
);
451 FileSystemURL dest_url
= GetOtherURL(file_system_context(),
452 src_root
, dest_root
, src_url
);
454 if (entries
[i
].is_directory
) {
455 ASSERT_EQ(base::File::FILE_OK
,
456 AsyncFileTestHelper::CreateDirectory(file_system_context(),
458 directories
.push(src_url
);
461 SCOPED_TRACE(testing::Message() << "Testing file copy "
462 << src_url
.path().value());
463 ASSERT_EQ(base::File::FILE_OK
,
464 AsyncFileTestHelper::Copy(file_system_context(),
466 VerifyFilesHaveSameContent(src_url
, dest_url
);
471 TEST_F(DraggedFileUtilTest
, CopyOutDirectoryTest
) {
472 FileSystemURL src_root
= GetFileSystemURL(base::FilePath());
473 FileSystemURL dest_root
= GetOtherFileSystemURL(base::FilePath());
475 ASSERT_EQ(base::File::FILE_OK
,
476 AsyncFileTestHelper::CreateDirectory(file_system_context(),
479 FileEntryList entries
;
480 ASSERT_EQ(base::File::FILE_OK
,
481 AsyncFileTestHelper::ReadDirectory(file_system_context(),
482 src_root
, &entries
));
483 for (size_t i
= 0; i
< entries
.size(); ++i
) {
484 if (!entries
[i
].is_directory
)
486 FileSystemURL src_url
= GetEntryURL(file_system_context(),
487 src_root
, entries
[i
].name
);
488 FileSystemURL dest_url
= GetOtherURL(file_system_context(),
489 src_root
, dest_root
, src_url
);
490 SCOPED_TRACE(testing::Message() << "Testing file copy "
491 << src_url
.path().value());
492 ASSERT_EQ(base::File::FILE_OK
,
493 AsyncFileTestHelper::Copy(file_system_context(),
495 VerifyDirectoriesHaveSameContent(src_url
, dest_url
);
499 TEST_F(DraggedFileUtilTest
, TouchTest
) {
500 for (size_t i
= 0; i
< kRegularFileSystemTestCaseSize
; ++i
) {
501 const FileSystemTestCaseRecord
& test_case
=
502 kRegularFileSystemTestCases
[i
];
503 if (test_case
.is_directory
)
505 SCOPED_TRACE(testing::Message() << test_case
.path
);
506 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
508 base::Time last_access_time
= base::Time::FromTimeT(1000);
509 base::Time last_modified_time
= base::Time::FromTimeT(2000);
511 EXPECT_EQ(base::File::FILE_OK
,
512 file_util()->Touch(GetOperationContext().get(), url
,
514 last_modified_time
));
517 base::File::Info info
;
518 base::FilePath platform_path
;
519 ASSERT_EQ(base::File::FILE_OK
,
520 file_util()->GetFileInfo(GetOperationContext().get(), url
,
521 &info
, &platform_path
));
522 EXPECT_EQ(last_access_time
.ToTimeT(), info
.last_accessed
.ToTimeT());
523 EXPECT_EQ(last_modified_time
.ToTimeT(), info
.last_modified
.ToTimeT());
527 TEST_F(DraggedFileUtilTest
, TruncateTest
) {
528 for (size_t i
= 0; i
< kRegularFileSystemTestCaseSize
; ++i
) {
529 const FileSystemTestCaseRecord
& test_case
=
530 kRegularFileSystemTestCases
[i
];
531 if (test_case
.is_directory
)
534 SCOPED_TRACE(testing::Message() << test_case
.path
);
535 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
538 base::File::Info info
;
539 base::FilePath platform_path
;
540 EXPECT_EQ(base::File::FILE_OK
,
541 file_util()->Truncate(GetOperationContext().get(), url
, 0));
542 ASSERT_EQ(base::File::FILE_OK
,
543 file_util()->GetFileInfo(GetOperationContext().get(), url
,
544 &info
, &platform_path
));
545 EXPECT_EQ(0, info
.size
);
547 // Truncate (extend) to 999.
548 EXPECT_EQ(base::File::FILE_OK
,
549 file_util()->Truncate(GetOperationContext().get(), url
, 999));
550 ASSERT_EQ(base::File::FILE_OK
,
551 file_util()->GetFileInfo(GetOperationContext().get(), url
,
552 &info
, &platform_path
));
553 EXPECT_EQ(999, info
.size
);
557 } // namespace content