dragged_file_util_unittest.cc revision f2477e01787aa58f445919b809d89e252beef54f
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 <map> 6#include <queue> 7#include <set> 8#include <string> 9#include <vector> 10 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/test_file_system_context.h" 19#include "testing/gtest/include/gtest/gtest.h" 20#include "webkit/browser/fileapi/async_file_test_helper.h" 21#include "webkit/browser/fileapi/dragged_file_util.h" 22#include "webkit/browser/fileapi/file_system_context.h" 23#include "webkit/browser/fileapi/file_system_operation_context.h" 24#include "webkit/browser/fileapi/isolated_context.h" 25#include "webkit/browser/fileapi/local_file_util.h" 26#include "webkit/browser/fileapi/native_file_util.h" 27#include "webkit/browser/fileapi/test_file_set.h" 28 29namespace fileapi { 30 31namespace { 32 33typedef AsyncFileTestHelper::FileEntryList FileEntryList; 34 35// Used in DraggedFileUtilTest::SimulateDropFiles(). 36// Random root paths in which we create each file/directory of the 37// RegularTestCases (so that we can simulate a drop with files/directories 38// from multiple directories). 39static const base::FilePath::CharType* kRootPaths[] = { 40 FILE_PATH_LITERAL("a"), 41 FILE_PATH_LITERAL("b/c"), 42 FILE_PATH_LITERAL("etc"), 43}; 44 45base::FilePath GetTopLevelPath(const base::FilePath& path) { 46 std::vector<base::FilePath::StringType> components; 47 path.GetComponents(&components); 48 return base::FilePath(components[0]); 49} 50 51bool IsDirectoryEmpty(FileSystemContext* context, const FileSystemURL& url) { 52 FileEntryList entries; 53 EXPECT_EQ(base::PLATFORM_FILE_OK, 54 AsyncFileTestHelper::ReadDirectory(context, url, &entries)); 55 return entries.empty(); 56} 57 58FileSystemURL GetEntryURL(FileSystemContext* file_system_context, 59 const FileSystemURL& dir, 60 const base::FilePath::StringType& name) { 61 return file_system_context->CreateCrackedFileSystemURL( 62 dir.origin(), 63 dir.mount_type(), 64 dir.virtual_path().Append(name)); 65} 66 67base::FilePath GetRelativeVirtualPath(const FileSystemURL& root, 68 const FileSystemURL& url) { 69 if (root.virtual_path().empty()) 70 return url.virtual_path(); 71 base::FilePath relative; 72 const bool success = root.virtual_path().AppendRelativePath( 73 url.virtual_path(), &relative); 74 DCHECK(success); 75 return relative; 76} 77 78FileSystemURL GetOtherURL(FileSystemContext* file_system_context, 79 const FileSystemURL& root, 80 const FileSystemURL& other_root, 81 const FileSystemURL& url) { 82 return file_system_context->CreateCrackedFileSystemURL( 83 other_root.origin(), 84 other_root.mount_type(), 85 other_root.virtual_path().Append(GetRelativeVirtualPath(root, url))); 86} 87 88} // namespace 89 90class DraggedFileUtilTest : public testing::Test { 91 public: 92 DraggedFileUtilTest() {} 93 94 virtual void SetUp() { 95 ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); 96 ASSERT_TRUE(partition_dir_.CreateUniqueTempDir()); 97 file_util_.reset(new DraggedFileUtil()); 98 99 // Register the files/directories of RegularTestCases (with random 100 // root paths) as dropped files. 101 SimulateDropFiles(); 102 103 file_system_context_ = CreateFileSystemContextForTesting( 104 NULL /* quota_manager */, 105 partition_dir_.path()); 106 107 isolated_context()->AddReference(filesystem_id_); 108 } 109 110 virtual void TearDown() { 111 isolated_context()->RemoveReference(filesystem_id_); 112 } 113 114 protected: 115 IsolatedContext* isolated_context() const { 116 return IsolatedContext::GetInstance(); 117 } 118 const base::FilePath& root_path() const { 119 return data_dir_.path(); 120 } 121 FileSystemContext* file_system_context() const { 122 return file_system_context_.get(); 123 } 124 FileSystemFileUtil* file_util() const { return file_util_.get(); } 125 std::string filesystem_id() const { return filesystem_id_; } 126 127 base::FilePath GetTestCasePlatformPath( 128 const base::FilePath::StringType& path) { 129 return toplevel_root_map_[GetTopLevelPath(base::FilePath(path))] 130 .Append(path).NormalizePathSeparators(); 131 } 132 133 base::FilePath GetTestCaseLocalPath(const base::FilePath& path) { 134 base::FilePath relative; 135 if (data_dir_.path().AppendRelativePath(path, &relative)) 136 return relative; 137 return path; 138 } 139 140 FileSystemURL GetFileSystemURL(const base::FilePath& path) const { 141 base::FilePath virtual_path = isolated_context()->CreateVirtualRootPath( 142 filesystem_id()).Append(path); 143 return file_system_context_->CreateCrackedFileSystemURL( 144 GURL("http://example.com"), 145 kFileSystemTypeIsolated, 146 virtual_path); 147 } 148 149 FileSystemURL GetOtherFileSystemURL(const base::FilePath& path) const { 150 return file_system_context()->CreateCrackedFileSystemURL( 151 GURL("http://example.com"), 152 kFileSystemTypeTemporary, 153 base::FilePath().AppendASCII("dest").Append(path)); 154 } 155 156 void VerifyFilesHaveSameContent(const FileSystemURL& url1, 157 const FileSystemURL& url2) { 158 // Get the file info and the platform path for url1. 159 base::PlatformFileInfo info1; 160 ASSERT_EQ(base::PLATFORM_FILE_OK, 161 AsyncFileTestHelper::GetMetadata( 162 file_system_context(), url1, &info1)); 163 base::FilePath platform_path1; 164 ASSERT_EQ(base::PLATFORM_FILE_OK, 165 AsyncFileTestHelper::GetPlatformPath( 166 file_system_context(), url1, &platform_path1)); 167 168 // Get the file info and the platform path for url2. 169 base::PlatformFileInfo info2; 170 ASSERT_EQ(base::PLATFORM_FILE_OK, 171 AsyncFileTestHelper::GetMetadata( 172 file_system_context(), url2, &info2)); 173 base::FilePath platform_path2; 174 ASSERT_EQ(base::PLATFORM_FILE_OK, 175 AsyncFileTestHelper::GetPlatformPath( 176 file_system_context(), url2, &platform_path2)); 177 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); 183 184 std::string content1, content2; 185 EXPECT_TRUE(base::ReadFileToString(platform_path1, &content1)); 186 EXPECT_TRUE(base::ReadFileToString(platform_path2, &content2)); 187 EXPECT_EQ(content1, content2); 188 } 189 190 void VerifyDirectoriesHaveSameContent(const FileSystemURL& root1, 191 const FileSystemURL& root2) { 192 base::FilePath root_path1 = root1.path(); 193 base::FilePath root_path2 = root2.path(); 194 195 FileEntryList entries; 196 std::queue<FileSystemURL> directories; 197 198 directories.push(root1); 199 std::set<base::FilePath> file_set1; 200 while (!directories.empty()) { 201 FileSystemURL dir = directories.front(); 202 directories.pop(); 203 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); 212 continue; 213 } 214 file_set1.insert(GetRelativeVirtualPath(root1, url)); 215 } 216 } 217 218 directories.push(root2); 219 while (!directories.empty()) { 220 FileSystemURL dir = directories.front(); 221 directories.pop(); 222 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(), 230 root2, root1, url2); 231 if (entries[i].is_directory) { 232 directories.push(url2); 233 EXPECT_EQ(IsDirectoryEmpty(file_system_context(), url1), 234 IsDirectoryEmpty(file_system_context(), url2)); 235 continue; 236 } 237 base::FilePath relative = GetRelativeVirtualPath(root2, url2); 238 EXPECT_TRUE(file_set1.find(relative) != file_set1.end()); 239 VerifyFilesHaveSameContent(url1, url2); 240 } 241 } 242 } 243 244 scoped_ptr<FileSystemOperationContext> GetOperationContext() { 245 return make_scoped_ptr( 246 new FileSystemOperationContext(file_system_context())).Pass(); 247 } 248 249 250 private: 251 void SimulateDropFiles() { 252 size_t root_path_index = 0; 253 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); 259 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); 267 } 268 269 test::SetUpOneTestCase(toplevel_root_map_[toplevel], test_case); 270 } 271 272 // Register the toplevel entries. 273 filesystem_id_ = isolated_context()->RegisterDraggedFileSystem(toplevels); 274 } 275 276 base::ScopedTempDir data_dir_; 277 base::ScopedTempDir partition_dir_; 278 base::MessageLoopForIO 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<DraggedFileUtil> file_util_; 283 DISALLOW_COPY_AND_ASSIGN(DraggedFileUtilTest); 284}; 285 286TEST_F(DraggedFileUtilTest, 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]; 290 291 FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path)); 292 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)); 301 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()); 308 } 309} 310 311TEST_F(DraggedFileUtilTest, 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}, 318 }; 319 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]; 323 324 // Prepare the test file/directory. 325 SetUpOneTestCase(root_path(), test_case); 326 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); 334 } 335 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)); 340 341 // We should not be able to get the valid URL for unregistered files. 342 ASSERT_FALSE(url.is_valid()); 343 } 344} 345 346TEST_F(DraggedFileUtilTest, ReadDirectoryTest) { 347 for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { 348 const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; 349 if (!test_case.is_directory) 350 continue; 351 352 SCOPED_TRACE(testing::Message() << "Testing RegularTestCases " << i 353 << ": " << test_case.path); 354 355 // Read entries in the directory to construct the expected results map. 356 typedef std::map<base::FilePath::StringType, DirectoryEntry> EntryMap; 357 EntryMap expected_entry_map; 358 359 base::FilePath dir_path = GetTestCasePlatformPath(test_case.path); 360 base::FileEnumerator file_enum( 361 dir_path, false /* not recursive */, 362 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); 363 base::FilePath current; 364 while (!(current = file_enum.Next()).empty()) { 365 base::FileEnumerator::FileInfo file_info = file_enum.GetInfo(); 366 DirectoryEntry entry; 367 entry.is_directory = file_info.IsDirectory(); 368 entry.name = current.BaseName().value(); 369 entry.size = file_info.GetSize(); 370 entry.last_modified_time = file_info.GetLastModifiedTime(); 371 expected_entry_map[entry.name] = entry; 372 373#if defined(OS_POSIX) 374 // Creates a symlink for each file/directory. 375 // They should be ignored by ReadDirectory, so we don't add them 376 // to expected_entry_map. 377 base::CreateSymbolicLink( 378 current, 379 dir_path.Append(current.BaseName().AddExtension( 380 FILE_PATH_LITERAL("link")))); 381#endif 382 } 383 384 // Perform ReadDirectory in the isolated filesystem. 385 FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path)); 386 FileEntryList entries; 387 ASSERT_EQ(base::PLATFORM_FILE_OK, 388 AsyncFileTestHelper::ReadDirectory( 389 file_system_context(), url, &entries)); 390 391 EXPECT_EQ(expected_entry_map.size(), entries.size()); 392 for (size_t i = 0; i < entries.size(); ++i) { 393 const DirectoryEntry& entry = entries[i]; 394 EntryMap::iterator found = expected_entry_map.find(entry.name); 395 EXPECT_TRUE(found != expected_entry_map.end()); 396 EXPECT_EQ(found->second.name, entry.name); 397 EXPECT_EQ(found->second.is_directory, entry.is_directory); 398 EXPECT_EQ(found->second.size, entry.size); 399 EXPECT_EQ(found->second.last_modified_time.ToDoubleT(), 400 entry.last_modified_time.ToDoubleT()); 401 } 402 } 403} 404 405TEST_F(DraggedFileUtilTest, GetLocalFilePathTest) { 406 for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { 407 const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; 408 FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path)); 409 410 FileSystemOperationContext context(file_system_context()); 411 412 base::FilePath local_file_path; 413 EXPECT_EQ(base::PLATFORM_FILE_OK, 414 file_util()->GetLocalFilePath(&context, url, &local_file_path)); 415 EXPECT_EQ(GetTestCasePlatformPath(test_case.path).value(), 416 local_file_path.value()); 417 } 418} 419 420TEST_F(DraggedFileUtilTest, CopyOutFileTest) { 421 FileSystemURL src_root = GetFileSystemURL(base::FilePath()); 422 FileSystemURL dest_root = GetOtherFileSystemURL(base::FilePath()); 423 424 FileEntryList entries; 425 std::queue<FileSystemURL> directories; 426 directories.push(src_root); 427 428 ASSERT_EQ(base::PLATFORM_FILE_OK, 429 AsyncFileTestHelper::CreateDirectory(file_system_context(), 430 dest_root)); 431 432 while (!directories.empty()) { 433 FileSystemURL dir = directories.front(); 434 directories.pop(); 435 ASSERT_EQ(base::PLATFORM_FILE_OK, 436 AsyncFileTestHelper::ReadDirectory(file_system_context(), 437 dir, &entries)); 438 for (size_t i = 0; i < entries.size(); ++i) { 439 FileSystemURL src_url = GetEntryURL(file_system_context(), 440 dir, entries[i].name); 441 FileSystemURL dest_url = GetOtherURL(file_system_context(), 442 src_root, dest_root, src_url); 443 444 if (entries[i].is_directory) { 445 ASSERT_EQ(base::PLATFORM_FILE_OK, 446 AsyncFileTestHelper::CreateDirectory(file_system_context(), 447 dest_url)); 448 directories.push(src_url); 449 continue; 450 } 451 SCOPED_TRACE(testing::Message() << "Testing file copy " 452 << src_url.path().value()); 453 ASSERT_EQ(base::PLATFORM_FILE_OK, 454 AsyncFileTestHelper::Copy(file_system_context(), 455 src_url, dest_url)); 456 VerifyFilesHaveSameContent(src_url, dest_url); 457 } 458 } 459} 460 461TEST_F(DraggedFileUtilTest, CopyOutDirectoryTest) { 462 FileSystemURL src_root = GetFileSystemURL(base::FilePath()); 463 FileSystemURL dest_root = GetOtherFileSystemURL(base::FilePath()); 464 465 ASSERT_EQ(base::PLATFORM_FILE_OK, 466 AsyncFileTestHelper::CreateDirectory(file_system_context(), 467 dest_root)); 468 469 FileEntryList entries; 470 ASSERT_EQ(base::PLATFORM_FILE_OK, 471 AsyncFileTestHelper::ReadDirectory(file_system_context(), 472 src_root, &entries)); 473 for (size_t i = 0; i < entries.size(); ++i) { 474 if (!entries[i].is_directory) 475 continue; 476 FileSystemURL src_url = GetEntryURL(file_system_context(), 477 src_root, entries[i].name); 478 FileSystemURL dest_url = GetOtherURL(file_system_context(), 479 src_root, dest_root, src_url); 480 SCOPED_TRACE(testing::Message() << "Testing file copy " 481 << src_url.path().value()); 482 ASSERT_EQ(base::PLATFORM_FILE_OK, 483 AsyncFileTestHelper::Copy(file_system_context(), 484 src_url, dest_url)); 485 VerifyDirectoriesHaveSameContent(src_url, dest_url); 486 } 487} 488 489TEST_F(DraggedFileUtilTest, TouchTest) { 490 for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { 491 const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; 492 if (test_case.is_directory) 493 continue; 494 SCOPED_TRACE(testing::Message() << test_case.path); 495 FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path)); 496 497 base::Time last_access_time = base::Time::FromTimeT(1000); 498 base::Time last_modified_time = base::Time::FromTimeT(2000); 499 500 EXPECT_EQ(base::PLATFORM_FILE_OK, 501 file_util()->Touch(GetOperationContext().get(), url, 502 last_access_time, 503 last_modified_time)); 504 505 // Verification. 506 base::PlatformFileInfo info; 507 base::FilePath platform_path; 508 ASSERT_EQ(base::PLATFORM_FILE_OK, 509 file_util()->GetFileInfo(GetOperationContext().get(), url, 510 &info, &platform_path)); 511 EXPECT_EQ(last_access_time.ToTimeT(), info.last_accessed.ToTimeT()); 512 EXPECT_EQ(last_modified_time.ToTimeT(), info.last_modified.ToTimeT()); 513 } 514} 515 516TEST_F(DraggedFileUtilTest, TruncateTest) { 517 for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { 518 const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; 519 if (test_case.is_directory) 520 continue; 521 522 SCOPED_TRACE(testing::Message() << test_case.path); 523 FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path)); 524 525 // Truncate to 0. 526 base::PlatformFileInfo info; 527 base::FilePath platform_path; 528 EXPECT_EQ(base::PLATFORM_FILE_OK, 529 file_util()->Truncate(GetOperationContext().get(), url, 0)); 530 ASSERT_EQ(base::PLATFORM_FILE_OK, 531 file_util()->GetFileInfo(GetOperationContext().get(), url, 532 &info, &platform_path)); 533 EXPECT_EQ(0, info.size); 534 535 // Truncate (extend) to 999. 536 EXPECT_EQ(base::PLATFORM_FILE_OK, 537 file_util()->Truncate(GetOperationContext().get(), url, 999)); 538 ASSERT_EQ(base::PLATFORM_FILE_OK, 539 file_util()->GetFileInfo(GetOperationContext().get(), url, 540 &info, &platform_path)); 541 EXPECT_EQ(999, info.size); 542 } 543} 544 545} // namespace fileapi 546