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/files/file_enumerator.h"
12 #include "base/files/file_util.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/time/time.h"
17 #include "content/public/test/async_file_test_helper.h"
18 #include "content/public/test/test_file_system_context.h"
19 #include "content/test/fileapi_test_file_set.h"
20 #include "storage/browser/fileapi/dragged_file_util.h"
21 #include "storage/browser/fileapi/file_system_context.h"
22 #include "storage/browser/fileapi/file_system_operation_context.h"
23 #include "storage/browser/fileapi/isolated_context.h"
24 #include "storage/browser/fileapi/local_file_util.h"
25 #include "storage/browser/fileapi/native_file_util.h"
26 #include "testing/gtest/include/gtest/gtest.h"
28 using content::AsyncFileTestHelper
;
29 using storage::FileSystemContext
;
30 using storage::FileSystemOperationContext
;
31 using storage::FileSystemType
;
32 using storage::FileSystemURL
;
38 typedef AsyncFileTestHelper::FileEntryList FileEntryList
;
40 // Used in DraggedFileUtilTest::SimulateDropFiles().
41 // Random root paths in which we create each file/directory of the
42 // RegularTestCases (so that we can simulate a drop with files/directories
43 // from multiple directories).
44 static const base::FilePath::CharType
* kRootPaths
[] = {
45 FILE_PATH_LITERAL("a"),
46 FILE_PATH_LITERAL("b/c"),
47 FILE_PATH_LITERAL("etc"),
50 base::FilePath
GetTopLevelPath(const base::FilePath
& path
) {
51 std::vector
<base::FilePath::StringType
> components
;
52 path
.GetComponents(&components
);
53 return base::FilePath(components
[0]);
56 bool IsDirectoryEmpty(FileSystemContext
* context
, const FileSystemURL
& url
) {
57 FileEntryList entries
;
58 EXPECT_EQ(base::File::FILE_OK
,
59 AsyncFileTestHelper::ReadDirectory(context
, url
, &entries
));
60 return entries
.empty();
63 FileSystemURL
GetEntryURL(FileSystemContext
* file_system_context
,
64 const FileSystemURL
& dir
,
65 const base::FilePath::StringType
& name
) {
66 return file_system_context
->CreateCrackedFileSystemURL(
69 dir
.virtual_path().Append(name
));
72 base::FilePath
GetRelativeVirtualPath(const FileSystemURL
& root
,
73 const FileSystemURL
& url
) {
74 if (root
.virtual_path().empty())
75 return url
.virtual_path();
76 base::FilePath relative
;
77 const bool success
= root
.virtual_path().AppendRelativePath(
78 url
.virtual_path(), &relative
);
83 FileSystemURL
GetOtherURL(FileSystemContext
* file_system_context
,
84 const FileSystemURL
& root
,
85 const FileSystemURL
& other_root
,
86 const FileSystemURL
& url
) {
87 return file_system_context
->CreateCrackedFileSystemURL(
89 other_root
.mount_type(),
90 other_root
.virtual_path().Append(GetRelativeVirtualPath(root
, url
)));
95 class DraggedFileUtilTest
: public testing::Test
{
97 DraggedFileUtilTest() {}
99 void SetUp() override
{
100 ASSERT_TRUE(data_dir_
.CreateUniqueTempDir());
101 ASSERT_TRUE(partition_dir_
.CreateUniqueTempDir());
102 file_util_
.reset(new storage::DraggedFileUtil());
104 // Register the files/directories of RegularTestCases (with random
105 // root paths) as dropped files.
108 file_system_context_
= CreateFileSystemContextForTesting(
109 NULL
/* quota_manager */,
110 partition_dir_
.path());
112 isolated_context()->AddReference(filesystem_id_
);
115 void TearDown() override
{
116 isolated_context()->RemoveReference(filesystem_id_
);
120 storage::IsolatedContext
* isolated_context() const {
121 return storage::IsolatedContext::GetInstance();
123 const base::FilePath
& root_path() const {
124 return data_dir_
.path();
126 FileSystemContext
* file_system_context() const {
127 return file_system_context_
.get();
129 storage::FileSystemFileUtil
* file_util() const { return file_util_
.get(); }
130 std::string
filesystem_id() const { return filesystem_id_
; }
132 base::FilePath
GetTestCasePlatformPath(
133 const base::FilePath::StringType
& path
) {
134 return toplevel_root_map_
[GetTopLevelPath(base::FilePath(path
))]
135 .Append(path
).NormalizePathSeparators();
138 base::FilePath
GetTestCaseLocalPath(const base::FilePath
& path
) {
139 base::FilePath relative
;
140 if (data_dir_
.path().AppendRelativePath(path
, &relative
))
145 FileSystemURL
GetFileSystemURL(const base::FilePath
& path
) const {
146 base::FilePath virtual_path
= isolated_context()->CreateVirtualRootPath(
147 filesystem_id()).Append(path
);
148 return file_system_context_
->CreateCrackedFileSystemURL(
149 GURL("http://example.com"),
150 storage::kFileSystemTypeIsolated
,
154 FileSystemURL
GetOtherFileSystemURL(const base::FilePath
& path
) const {
155 return file_system_context()->CreateCrackedFileSystemURL(
156 GURL("http://example.com"),
157 storage::kFileSystemTypeTemporary
,
158 base::FilePath().AppendASCII("dest").Append(path
));
161 void VerifyFilesHaveSameContent(const FileSystemURL
& url1
,
162 const FileSystemURL
& url2
) {
163 // Get the file info and the platform path for url1.
164 base::File::Info info1
;
165 ASSERT_EQ(base::File::FILE_OK
,
166 AsyncFileTestHelper::GetMetadata(
167 file_system_context(), url1
, &info1
));
168 base::FilePath platform_path1
;
169 ASSERT_EQ(base::File::FILE_OK
,
170 AsyncFileTestHelper::GetPlatformPath(
171 file_system_context(), url1
, &platform_path1
));
173 // Get the file info and the platform path for url2.
174 base::File::Info info2
;
175 ASSERT_EQ(base::File::FILE_OK
,
176 AsyncFileTestHelper::GetMetadata(
177 file_system_context(), url2
, &info2
));
178 base::FilePath platform_path2
;
179 ASSERT_EQ(base::File::FILE_OK
,
180 AsyncFileTestHelper::GetPlatformPath(
181 file_system_context(), url2
, &platform_path2
));
183 // See if file info matches with the other one.
184 EXPECT_EQ(info1
.is_directory
, info2
.is_directory
);
185 EXPECT_EQ(info1
.size
, info2
.size
);
186 EXPECT_EQ(info1
.is_symbolic_link
, info2
.is_symbolic_link
);
187 EXPECT_NE(platform_path1
, platform_path2
);
189 std::string content1
, content2
;
190 EXPECT_TRUE(base::ReadFileToString(platform_path1
, &content1
));
191 EXPECT_TRUE(base::ReadFileToString(platform_path2
, &content2
));
192 EXPECT_EQ(content1
, content2
);
195 void VerifyDirectoriesHaveSameContent(const FileSystemURL
& root1
,
196 const FileSystemURL
& root2
) {
197 base::FilePath root_path1
= root1
.path();
198 base::FilePath root_path2
= root2
.path();
200 FileEntryList entries
;
201 std::queue
<FileSystemURL
> directories
;
203 directories
.push(root1
);
204 std::set
<base::FilePath
> file_set1
;
205 while (!directories
.empty()) {
206 FileSystemURL dir
= directories
.front();
209 ASSERT_EQ(base::File::FILE_OK
,
210 AsyncFileTestHelper::ReadDirectory(
211 file_system_context(), dir
, &entries
));
212 for (size_t i
= 0; i
< entries
.size(); ++i
) {
213 FileSystemURL url
= GetEntryURL(file_system_context(),
214 dir
, entries
[i
].name
);
215 if (entries
[i
].is_directory
) {
216 directories
.push(url
);
219 file_set1
.insert(GetRelativeVirtualPath(root1
, url
));
223 directories
.push(root2
);
224 while (!directories
.empty()) {
225 FileSystemURL dir
= directories
.front();
228 ASSERT_EQ(base::File::FILE_OK
,
229 AsyncFileTestHelper::ReadDirectory(
230 file_system_context(), dir
, &entries
));
231 for (size_t i
= 0; i
< entries
.size(); ++i
) {
232 FileSystemURL url2
= GetEntryURL(file_system_context(),
233 dir
, entries
[i
].name
);
234 FileSystemURL url1
= GetOtherURL(file_system_context(),
236 if (entries
[i
].is_directory
) {
237 directories
.push(url2
);
238 EXPECT_EQ(IsDirectoryEmpty(file_system_context(), url1
),
239 IsDirectoryEmpty(file_system_context(), url2
));
242 base::FilePath relative
= GetRelativeVirtualPath(root2
, url2
);
243 EXPECT_TRUE(file_set1
.find(relative
) != file_set1
.end());
244 VerifyFilesHaveSameContent(url1
, url2
);
249 scoped_ptr
<storage::FileSystemOperationContext
> GetOperationContext() {
250 return make_scoped_ptr(new storage::FileSystemOperationContext(
251 file_system_context())).Pass();
256 void SimulateDropFiles() {
257 size_t root_path_index
= 0;
259 storage::IsolatedContext::FileInfoSet toplevels
;
260 for (size_t i
= 0; i
< kRegularFileSystemTestCaseSize
; ++i
) {
261 const FileSystemTestCaseRecord
& test_case
=
262 kRegularFileSystemTestCases
[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 SetUpOneFileSystemTestCase(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 base::MessageLoopForIO 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
<storage::DraggedFileUtil
> file_util_
;
289 DISALLOW_COPY_AND_ASSIGN(DraggedFileUtilTest
);
292 TEST_F(DraggedFileUtilTest
, BasicTest
) {
293 for (size_t i
= 0; i
< kRegularFileSystemTestCaseSize
; ++i
) {
294 SCOPED_TRACE(testing::Message() << "Testing RegularTestCases " << i
);
295 const FileSystemTestCaseRecord
& test_case
=
296 kRegularFileSystemTestCases
[i
];
298 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
300 // See if we can query the file info via the isolated FileUtil.
301 // (This should succeed since we have registered all the top-level
302 // entries of the test cases in SetUp())
303 base::File::Info info
;
304 base::FilePath platform_path
;
305 FileSystemOperationContext
context(file_system_context());
306 ASSERT_EQ(base::File::FILE_OK
,
307 file_util()->GetFileInfo(&context
, url
, &info
, &platform_path
));
309 // See if the obtained file info is correct.
310 if (!test_case
.is_directory
)
311 ASSERT_EQ(test_case
.data_file_size
, info
.size
);
312 ASSERT_EQ(test_case
.is_directory
, info
.is_directory
);
313 ASSERT_EQ(GetTestCasePlatformPath(test_case
.path
),
314 platform_path
.NormalizePathSeparators());
318 TEST_F(DraggedFileUtilTest
, UnregisteredPathsTest
) {
319 static const FileSystemTestCaseRecord kUnregisteredCases
[] = {
320 {true, FILE_PATH_LITERAL("nonexistent"), 0},
321 {true, FILE_PATH_LITERAL("nonexistent/dir foo"), 0},
322 {false, FILE_PATH_LITERAL("nonexistent/false"), 0},
323 {false, FILE_PATH_LITERAL("foo"), 30},
324 {false, FILE_PATH_LITERAL("bar"), 20},
327 for (size_t i
= 0; i
< arraysize(kUnregisteredCases
); ++i
) {
328 SCOPED_TRACE(testing::Message() << "Creating kUnregisteredCases " << i
);
329 const FileSystemTestCaseRecord
& test_case
= kUnregisteredCases
[i
];
331 // Prepare the test file/directory.
332 SetUpOneFileSystemTestCase(root_path(), test_case
);
334 // Make sure regular GetFileInfo succeeds.
335 base::File::Info info
;
336 ASSERT_TRUE(base::GetFileInfo(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 FileSystemTestCaseRecord
& 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());
352 TEST_F(DraggedFileUtilTest
, ReadDirectoryTest
) {
353 for (size_t i
= 0; i
< kRegularFileSystemTestCaseSize
; ++i
) {
354 const FileSystemTestCaseRecord
& test_case
=
355 kRegularFileSystemTestCases
[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
, storage::DirectoryEntry
>
365 EntryMap expected_entry_map
;
367 base::FilePath dir_path
= GetTestCasePlatformPath(test_case
.path
);
368 base::FileEnumerator
file_enum(
369 dir_path
, false /* not recursive */,
370 base::FileEnumerator::FILES
| base::FileEnumerator::DIRECTORIES
);
371 base::FilePath current
;
372 while (!(current
= file_enum
.Next()).empty()) {
373 base::FileEnumerator::FileInfo file_info
= file_enum
.GetInfo();
374 storage::DirectoryEntry entry
;
375 entry
.is_directory
= file_info
.IsDirectory();
376 entry
.name
= current
.BaseName().value();
377 entry
.size
= file_info
.GetSize();
378 entry
.last_modified_time
= file_info
.GetLastModifiedTime();
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 base::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::File::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 storage::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(DraggedFileUtilTest
, GetLocalFilePathTest
) {
414 for (size_t i
= 0; i
< kRegularFileSystemTestCaseSize
; ++i
) {
415 const FileSystemTestCaseRecord
& test_case
=
416 kRegularFileSystemTestCases
[i
];
417 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
419 FileSystemOperationContext
context(file_system_context());
421 base::FilePath local_file_path
;
422 EXPECT_EQ(base::File::FILE_OK
,
423 file_util()->GetLocalFilePath(&context
, url
, &local_file_path
));
424 EXPECT_EQ(GetTestCasePlatformPath(test_case
.path
).value(),
425 local_file_path
.value());
429 TEST_F(DraggedFileUtilTest
, CopyOutFileTest
) {
430 FileSystemURL src_root
= GetFileSystemURL(base::FilePath());
431 FileSystemURL dest_root
= GetOtherFileSystemURL(base::FilePath());
433 FileEntryList entries
;
434 std::queue
<FileSystemURL
> directories
;
435 directories
.push(src_root
);
437 ASSERT_EQ(base::File::FILE_OK
,
438 AsyncFileTestHelper::CreateDirectory(file_system_context(),
441 while (!directories
.empty()) {
442 FileSystemURL dir
= directories
.front();
444 ASSERT_EQ(base::File::FILE_OK
,
445 AsyncFileTestHelper::ReadDirectory(file_system_context(),
447 for (size_t i
= 0; i
< entries
.size(); ++i
) {
448 FileSystemURL src_url
= GetEntryURL(file_system_context(),
449 dir
, entries
[i
].name
);
450 FileSystemURL dest_url
= GetOtherURL(file_system_context(),
451 src_root
, dest_root
, src_url
);
453 if (entries
[i
].is_directory
) {
454 ASSERT_EQ(base::File::FILE_OK
,
455 AsyncFileTestHelper::CreateDirectory(file_system_context(),
457 directories
.push(src_url
);
460 SCOPED_TRACE(testing::Message() << "Testing file copy "
461 << src_url
.path().value());
462 ASSERT_EQ(base::File::FILE_OK
,
463 AsyncFileTestHelper::Copy(file_system_context(),
465 VerifyFilesHaveSameContent(src_url
, dest_url
);
470 TEST_F(DraggedFileUtilTest
, CopyOutDirectoryTest
) {
471 FileSystemURL src_root
= GetFileSystemURL(base::FilePath());
472 FileSystemURL dest_root
= GetOtherFileSystemURL(base::FilePath());
474 ASSERT_EQ(base::File::FILE_OK
,
475 AsyncFileTestHelper::CreateDirectory(file_system_context(),
478 FileEntryList entries
;
479 ASSERT_EQ(base::File::FILE_OK
,
480 AsyncFileTestHelper::ReadDirectory(file_system_context(),
481 src_root
, &entries
));
482 for (size_t i
= 0; i
< entries
.size(); ++i
) {
483 if (!entries
[i
].is_directory
)
485 FileSystemURL src_url
= GetEntryURL(file_system_context(),
486 src_root
, entries
[i
].name
);
487 FileSystemURL dest_url
= GetOtherURL(file_system_context(),
488 src_root
, dest_root
, src_url
);
489 SCOPED_TRACE(testing::Message() << "Testing file copy "
490 << src_url
.path().value());
491 ASSERT_EQ(base::File::FILE_OK
,
492 AsyncFileTestHelper::Copy(file_system_context(),
494 VerifyDirectoriesHaveSameContent(src_url
, dest_url
);
498 TEST_F(DraggedFileUtilTest
, TouchTest
) {
499 for (size_t i
= 0; i
< kRegularFileSystemTestCaseSize
; ++i
) {
500 const FileSystemTestCaseRecord
& test_case
=
501 kRegularFileSystemTestCases
[i
];
502 if (test_case
.is_directory
)
504 SCOPED_TRACE(testing::Message() << test_case
.path
);
505 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
507 base::Time last_access_time
= base::Time::FromTimeT(1000);
508 base::Time last_modified_time
= base::Time::FromTimeT(2000);
510 EXPECT_EQ(base::File::FILE_OK
,
511 file_util()->Touch(GetOperationContext().get(), url
,
513 last_modified_time
));
516 base::File::Info info
;
517 base::FilePath platform_path
;
518 ASSERT_EQ(base::File::FILE_OK
,
519 file_util()->GetFileInfo(GetOperationContext().get(), url
,
520 &info
, &platform_path
));
521 EXPECT_EQ(last_access_time
.ToTimeT(), info
.last_accessed
.ToTimeT());
522 EXPECT_EQ(last_modified_time
.ToTimeT(), info
.last_modified
.ToTimeT());
526 TEST_F(DraggedFileUtilTest
, TruncateTest
) {
527 for (size_t i
= 0; i
< kRegularFileSystemTestCaseSize
; ++i
) {
528 const FileSystemTestCaseRecord
& test_case
=
529 kRegularFileSystemTestCases
[i
];
530 if (test_case
.is_directory
)
533 SCOPED_TRACE(testing::Message() << test_case
.path
);
534 FileSystemURL url
= GetFileSystemURL(base::FilePath(test_case
.path
));
537 base::File::Info info
;
538 base::FilePath platform_path
;
539 EXPECT_EQ(base::File::FILE_OK
,
540 file_util()->Truncate(GetOperationContext().get(), url
, 0));
541 ASSERT_EQ(base::File::FILE_OK
,
542 file_util()->GetFileInfo(GetOperationContext().get(), url
,
543 &info
, &platform_path
));
544 EXPECT_EQ(0, info
.size
);
546 // Truncate (extend) to 999.
547 EXPECT_EQ(base::File::FILE_OK
,
548 file_util()->Truncate(GetOperationContext().get(), url
, 999));
549 ASSERT_EQ(base::File::FILE_OK
,
550 file_util()->GetFileInfo(GetOperationContext().get(), url
,
551 &info
, &platform_path
));
552 EXPECT_EQ(999, info
.size
);
556 } // namespace content