dragged_file_util_unittest.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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(base::GetFileInfo(root_path().Append(test_case.path), &info)); 330 if (!test_case.is_directory) 331 ASSERT_EQ(test_case.data_file_size, info.size); 332 ASSERT_EQ(test_case.is_directory, info.is_directory); 333 } 334 335 for (size_t i = 0; i < arraysize(kUnregisteredCases); ++i) { 336 SCOPED_TRACE(testing::Message() << "Creating kUnregisteredCases " << i); 337 const test::TestCaseRecord& test_case = kUnregisteredCases[i]; 338 FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path)); 339 340 // We should not be able to get the valid URL for unregistered files. 341 ASSERT_FALSE(url.is_valid()); 342 } 343} 344 345TEST_F(DraggedFileUtilTest, ReadDirectoryTest) { 346 for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { 347 const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; 348 if (!test_case.is_directory) 349 continue; 350 351 SCOPED_TRACE(testing::Message() << "Testing RegularTestCases " << i 352 << ": " << test_case.path); 353 354 // Read entries in the directory to construct the expected results map. 355 typedef std::map<base::FilePath::StringType, DirectoryEntry> EntryMap; 356 EntryMap expected_entry_map; 357 358 base::FilePath dir_path = GetTestCasePlatformPath(test_case.path); 359 base::FileEnumerator file_enum( 360 dir_path, false /* not recursive */, 361 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); 362 base::FilePath current; 363 while (!(current = file_enum.Next()).empty()) { 364 base::FileEnumerator::FileInfo file_info = file_enum.GetInfo(); 365 DirectoryEntry entry; 366 entry.is_directory = file_info.IsDirectory(); 367 entry.name = current.BaseName().value(); 368 entry.size = file_info.GetSize(); 369 entry.last_modified_time = file_info.GetLastModifiedTime(); 370 expected_entry_map[entry.name] = entry; 371 372#if defined(OS_POSIX) 373 // Creates a symlink for each file/directory. 374 // They should be ignored by ReadDirectory, so we don't add them 375 // to expected_entry_map. 376 base::CreateSymbolicLink( 377 current, 378 dir_path.Append(current.BaseName().AddExtension( 379 FILE_PATH_LITERAL("link")))); 380#endif 381 } 382 383 // Perform ReadDirectory in the isolated filesystem. 384 FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path)); 385 FileEntryList entries; 386 ASSERT_EQ(base::PLATFORM_FILE_OK, 387 AsyncFileTestHelper::ReadDirectory( 388 file_system_context(), url, &entries)); 389 390 EXPECT_EQ(expected_entry_map.size(), entries.size()); 391 for (size_t i = 0; i < entries.size(); ++i) { 392 const DirectoryEntry& entry = entries[i]; 393 EntryMap::iterator found = expected_entry_map.find(entry.name); 394 EXPECT_TRUE(found != expected_entry_map.end()); 395 EXPECT_EQ(found->second.name, entry.name); 396 EXPECT_EQ(found->second.is_directory, entry.is_directory); 397 EXPECT_EQ(found->second.size, entry.size); 398 EXPECT_EQ(found->second.last_modified_time.ToDoubleT(), 399 entry.last_modified_time.ToDoubleT()); 400 } 401 } 402} 403 404TEST_F(DraggedFileUtilTest, GetLocalFilePathTest) { 405 for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { 406 const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; 407 FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path)); 408 409 FileSystemOperationContext context(file_system_context()); 410 411 base::FilePath local_file_path; 412 EXPECT_EQ(base::PLATFORM_FILE_OK, 413 file_util()->GetLocalFilePath(&context, url, &local_file_path)); 414 EXPECT_EQ(GetTestCasePlatformPath(test_case.path).value(), 415 local_file_path.value()); 416 } 417} 418 419TEST_F(DraggedFileUtilTest, CopyOutFileTest) { 420 FileSystemURL src_root = GetFileSystemURL(base::FilePath()); 421 FileSystemURL dest_root = GetOtherFileSystemURL(base::FilePath()); 422 423 FileEntryList entries; 424 std::queue<FileSystemURL> directories; 425 directories.push(src_root); 426 427 ASSERT_EQ(base::PLATFORM_FILE_OK, 428 AsyncFileTestHelper::CreateDirectory(file_system_context(), 429 dest_root)); 430 431 while (!directories.empty()) { 432 FileSystemURL dir = directories.front(); 433 directories.pop(); 434 ASSERT_EQ(base::PLATFORM_FILE_OK, 435 AsyncFileTestHelper::ReadDirectory(file_system_context(), 436 dir, &entries)); 437 for (size_t i = 0; i < entries.size(); ++i) { 438 FileSystemURL src_url = GetEntryURL(file_system_context(), 439 dir, entries[i].name); 440 FileSystemURL dest_url = GetOtherURL(file_system_context(), 441 src_root, dest_root, src_url); 442 443 if (entries[i].is_directory) { 444 ASSERT_EQ(base::PLATFORM_FILE_OK, 445 AsyncFileTestHelper::CreateDirectory(file_system_context(), 446 dest_url)); 447 directories.push(src_url); 448 continue; 449 } 450 SCOPED_TRACE(testing::Message() << "Testing file copy " 451 << src_url.path().value()); 452 ASSERT_EQ(base::PLATFORM_FILE_OK, 453 AsyncFileTestHelper::Copy(file_system_context(), 454 src_url, dest_url)); 455 VerifyFilesHaveSameContent(src_url, dest_url); 456 } 457 } 458} 459 460TEST_F(DraggedFileUtilTest, CopyOutDirectoryTest) { 461 FileSystemURL src_root = GetFileSystemURL(base::FilePath()); 462 FileSystemURL dest_root = GetOtherFileSystemURL(base::FilePath()); 463 464 ASSERT_EQ(base::PLATFORM_FILE_OK, 465 AsyncFileTestHelper::CreateDirectory(file_system_context(), 466 dest_root)); 467 468 FileEntryList entries; 469 ASSERT_EQ(base::PLATFORM_FILE_OK, 470 AsyncFileTestHelper::ReadDirectory(file_system_context(), 471 src_root, &entries)); 472 for (size_t i = 0; i < entries.size(); ++i) { 473 if (!entries[i].is_directory) 474 continue; 475 FileSystemURL src_url = GetEntryURL(file_system_context(), 476 src_root, entries[i].name); 477 FileSystemURL dest_url = GetOtherURL(file_system_context(), 478 src_root, dest_root, src_url); 479 SCOPED_TRACE(testing::Message() << "Testing file copy " 480 << src_url.path().value()); 481 ASSERT_EQ(base::PLATFORM_FILE_OK, 482 AsyncFileTestHelper::Copy(file_system_context(), 483 src_url, dest_url)); 484 VerifyDirectoriesHaveSameContent(src_url, dest_url); 485 } 486} 487 488TEST_F(DraggedFileUtilTest, TouchTest) { 489 for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { 490 const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; 491 if (test_case.is_directory) 492 continue; 493 SCOPED_TRACE(testing::Message() << test_case.path); 494 FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path)); 495 496 base::Time last_access_time = base::Time::FromTimeT(1000); 497 base::Time last_modified_time = base::Time::FromTimeT(2000); 498 499 EXPECT_EQ(base::PLATFORM_FILE_OK, 500 file_util()->Touch(GetOperationContext().get(), url, 501 last_access_time, 502 last_modified_time)); 503 504 // Verification. 505 base::PlatformFileInfo info; 506 base::FilePath platform_path; 507 ASSERT_EQ(base::PLATFORM_FILE_OK, 508 file_util()->GetFileInfo(GetOperationContext().get(), url, 509 &info, &platform_path)); 510 EXPECT_EQ(last_access_time.ToTimeT(), info.last_accessed.ToTimeT()); 511 EXPECT_EQ(last_modified_time.ToTimeT(), info.last_modified.ToTimeT()); 512 } 513} 514 515TEST_F(DraggedFileUtilTest, TruncateTest) { 516 for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { 517 const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; 518 if (test_case.is_directory) 519 continue; 520 521 SCOPED_TRACE(testing::Message() << test_case.path); 522 FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path)); 523 524 // Truncate to 0. 525 base::PlatformFileInfo info; 526 base::FilePath platform_path; 527 EXPECT_EQ(base::PLATFORM_FILE_OK, 528 file_util()->Truncate(GetOperationContext().get(), url, 0)); 529 ASSERT_EQ(base::PLATFORM_FILE_OK, 530 file_util()->GetFileInfo(GetOperationContext().get(), url, 531 &info, &platform_path)); 532 EXPECT_EQ(0, info.size); 533 534 // Truncate (extend) to 999. 535 EXPECT_EQ(base::PLATFORM_FILE_OK, 536 file_util()->Truncate(GetOperationContext().get(), url, 999)); 537 ASSERT_EQ(base::PLATFORM_FILE_OK, 538 file_util()->GetFileInfo(GetOperationContext().get(), url, 539 &info, &platform_path)); 540 EXPECT_EQ(999, info.size); 541 } 542} 543 544} // namespace fileapi 545