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. 4 5#include <set> 6#include <string> 7 8#include "base/bind.h" 9#include "base/file_util.h" 10#include "base/files/scoped_temp_dir.h" 11#include "base/format_macros.h" 12#include "base/message_loop/message_loop.h" 13#include "base/message_loop/message_loop_proxy.h" 14#include "base/strings/stringprintf.h" 15#include "base/time/time.h" 16#include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" 17#include "chrome/browser/media_galleries/fileapi/native_media_file_util.h" 18#include "content/public/test/test_browser_thread.h" 19#include "content/public/test/test_file_system_options.h" 20#include "testing/gtest/include/gtest/gtest.h" 21#include "webkit/browser/fileapi/external_mount_points.h" 22#include "webkit/browser/fileapi/file_system_backend.h" 23#include "webkit/browser/fileapi/file_system_context.h" 24#include "webkit/browser/fileapi/file_system_operation_runner.h" 25#include "webkit/browser/fileapi/file_system_url.h" 26#include "webkit/browser/fileapi/isolated_context.h" 27#include "webkit/browser/fileapi/native_file_util.h" 28#include "webkit/browser/quota/mock_special_storage_policy.h" 29 30#define FPL(x) FILE_PATH_LITERAL(x) 31 32using fileapi::FileSystemOperation; 33using fileapi::FileSystemURL; 34 35namespace { 36 37typedef FileSystemOperation::FileEntryList FileEntryList; 38 39struct FilteringTestCase { 40 const base::FilePath::CharType* path; 41 bool is_directory; 42 bool visible; 43 bool media_file; 44 const char* content; 45}; 46 47const FilteringTestCase kFilteringTestCases[] = { 48 // Directory should always be visible. 49 { FPL("hoge"), true, true, false, NULL }, 50 { FPL("fuga.jpg"), true, true, false, NULL }, 51 { FPL("piyo.txt"), true, true, false, NULL }, 52 { FPL("moga.cod"), true, true, false, NULL }, 53 54 // File should be visible if it's a supported media file. 55 // File without extension. 56 { FPL("foo"), false, false, false, "abc" }, 57 // Supported media file. 58 { FPL("bar.jpg"), false, true, true, "\xFF\xD8\xFF" }, 59 // Unsupported masquerading file. 60 { FPL("sna.jpg"), false, true, false, "abc" }, 61 // Non-media file. 62 { FPL("baz.txt"), false, false, false, "abc" }, 63 // Unsupported media file. 64 { FPL("foobar.cod"), false, false, false, "abc" }, 65}; 66 67void ExpectEqHelper(const std::string& test_name, 68 base::PlatformFileError expected, 69 base::PlatformFileError actual) { 70 EXPECT_EQ(expected, actual) << test_name; 71} 72 73void ExpectMetadataEqHelper(const std::string& test_name, 74 base::PlatformFileError expected, 75 bool expected_is_directory, 76 base::PlatformFileError actual, 77 const base::PlatformFileInfo& file_info) { 78 EXPECT_EQ(expected, actual) << test_name; 79 if (actual == base::PLATFORM_FILE_OK) 80 EXPECT_EQ(expected_is_directory, file_info.is_directory) << test_name; 81} 82 83void DidReadDirectory(std::set<base::FilePath::StringType>* content, 84 bool* completed, 85 base::PlatformFileError error, 86 const FileEntryList& file_list, 87 bool has_more) { 88 EXPECT_TRUE(!*completed); 89 *completed = !has_more; 90 for (FileEntryList::const_iterator itr = file_list.begin(); 91 itr != file_list.end(); ++itr) 92 EXPECT_TRUE(content->insert(itr->name).second); 93} 94 95void PopulateDirectoryWithTestCases(const base::FilePath& dir, 96 const FilteringTestCase* test_cases, 97 size_t n) { 98 for (size_t i = 0; i < n; ++i) { 99 base::FilePath path = dir.Append(test_cases[i].path); 100 if (test_cases[i].is_directory) { 101 ASSERT_TRUE(base::CreateDirectory(path)); 102 } else { 103 ASSERT_TRUE(test_cases[i].content != NULL); 104 int len = strlen(test_cases[i].content); 105 ASSERT_EQ(len, file_util::WriteFile(path, test_cases[i].content, len)); 106 } 107 } 108} 109 110} // namespace 111 112class NativeMediaFileUtilTest : public testing::Test { 113 public: 114 NativeMediaFileUtilTest() 115 : io_thread_(content::BrowserThread::IO, &message_loop_) { 116 } 117 118 virtual void SetUp() { 119 ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); 120 ASSERT_TRUE(base::CreateDirectory(root_path())); 121 122 scoped_refptr<quota::SpecialStoragePolicy> storage_policy = 123 new quota::MockSpecialStoragePolicy(); 124 125 ScopedVector<fileapi::FileSystemBackend> additional_providers; 126 additional_providers.push_back(new MediaFileSystemBackend( 127 data_dir_.path(), base::MessageLoopProxy::current().get())); 128 129 file_system_context_ = new fileapi::FileSystemContext( 130 base::MessageLoopProxy::current().get(), 131 base::MessageLoopProxy::current().get(), 132 fileapi::ExternalMountPoints::CreateRefCounted().get(), 133 storage_policy.get(), 134 NULL, 135 additional_providers.Pass(), 136 data_dir_.path(), 137 fileapi::CreateAllowFileAccessOptions()); 138 139 filesystem_id_ = isolated_context()->RegisterFileSystemForPath( 140 fileapi::kFileSystemTypeNativeMedia, root_path(), NULL); 141 142 isolated_context()->AddReference(filesystem_id_); 143 } 144 145 virtual void TearDown() { 146 isolated_context()->RemoveReference(filesystem_id_); 147 file_system_context_ = NULL; 148 } 149 150 protected: 151 fileapi::FileSystemContext* file_system_context() { 152 return file_system_context_.get(); 153 } 154 155 FileSystemURL CreateURL(const base::FilePath::CharType* test_case_path) { 156 return file_system_context_->CreateCrackedFileSystemURL( 157 origin(), 158 fileapi::kFileSystemTypeIsolated, 159 GetVirtualPath(test_case_path)); 160 } 161 162 fileapi::IsolatedContext* isolated_context() { 163 return fileapi::IsolatedContext::GetInstance(); 164 } 165 166 base::FilePath root_path() { 167 return data_dir_.path().Append(FPL("Media Directory")); 168 } 169 170 base::FilePath GetVirtualPath( 171 const base::FilePath::CharType* test_case_path) { 172 return base::FilePath::FromUTF8Unsafe(filesystem_id_). 173 Append(FPL("Media Directory")). 174 Append(base::FilePath(test_case_path)); 175 } 176 177 GURL origin() { 178 return GURL("http://example.com"); 179 } 180 181 fileapi::FileSystemType type() { 182 return fileapi::kFileSystemTypeNativeMedia; 183 } 184 185 fileapi::FileSystemOperationRunner* operation_runner() { 186 return file_system_context_->operation_runner(); 187 } 188 189 private: 190 base::MessageLoop message_loop_; 191 content::TestBrowserThread io_thread_; 192 193 base::ScopedTempDir data_dir_; 194 scoped_refptr<fileapi::FileSystemContext> file_system_context_; 195 196 std::string filesystem_id_; 197 198 DISALLOW_COPY_AND_ASSIGN(NativeMediaFileUtilTest); 199}; 200 201TEST_F(NativeMediaFileUtilTest, DirectoryExistsAndFileExistsFiltering) { 202 PopulateDirectoryWithTestCases(root_path(), 203 kFilteringTestCases, 204 arraysize(kFilteringTestCases)); 205 206 for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { 207 FileSystemURL url = CreateURL(kFilteringTestCases[i].path); 208 209 base::PlatformFileError expectation = 210 kFilteringTestCases[i].visible ? 211 base::PLATFORM_FILE_OK : 212 base::PLATFORM_FILE_ERROR_NOT_FOUND; 213 214 std::string test_name = 215 base::StringPrintf("DirectoryExistsAndFileExistsFiltering %" PRIuS, i); 216 if (kFilteringTestCases[i].is_directory) { 217 operation_runner()->DirectoryExists( 218 url, base::Bind(&ExpectEqHelper, test_name, expectation)); 219 } else { 220 operation_runner()->FileExists( 221 url, base::Bind(&ExpectEqHelper, test_name, expectation)); 222 } 223 base::MessageLoop::current()->RunUntilIdle(); 224 } 225} 226 227TEST_F(NativeMediaFileUtilTest, ReadDirectoryFiltering) { 228 PopulateDirectoryWithTestCases(root_path(), 229 kFilteringTestCases, 230 arraysize(kFilteringTestCases)); 231 232 std::set<base::FilePath::StringType> content; 233 FileSystemURL url = CreateURL(FPL("")); 234 bool completed = false; 235 operation_runner()->ReadDirectory( 236 url, base::Bind(&DidReadDirectory, &content, &completed)); 237 base::MessageLoop::current()->RunUntilIdle(); 238 EXPECT_TRUE(completed); 239 EXPECT_EQ(6u, content.size()); 240 241 for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { 242 base::FilePath::StringType name = 243 base::FilePath(kFilteringTestCases[i].path).BaseName().value(); 244 std::set<base::FilePath::StringType>::const_iterator found = 245 content.find(name); 246 EXPECT_EQ(kFilteringTestCases[i].visible, found != content.end()); 247 } 248} 249 250TEST_F(NativeMediaFileUtilTest, CreateDirectoryFiltering) { 251 // Run the loop twice. The second loop attempts to create directories that are 252 // pre-existing. Though the result should be the same. 253 for (int loop_count = 0; loop_count < 2; ++loop_count) { 254 for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { 255 if (kFilteringTestCases[i].is_directory) { 256 FileSystemURL root_url = CreateURL(FPL("")); 257 FileSystemURL url = CreateURL(kFilteringTestCases[i].path); 258 259 std::string test_name = base::StringPrintf( 260 "CreateFileAndCreateDirectoryFiltering run %d, test %" PRIuS, 261 loop_count, i); 262 base::PlatformFileError expectation = 263 kFilteringTestCases[i].visible ? 264 base::PLATFORM_FILE_OK : 265 base::PLATFORM_FILE_ERROR_SECURITY; 266 operation_runner()->CreateDirectory( 267 url, false, false, 268 base::Bind(&ExpectEqHelper, test_name, expectation)); 269 } 270 base::MessageLoop::current()->RunUntilIdle(); 271 } 272 } 273} 274 275TEST_F(NativeMediaFileUtilTest, CopySourceFiltering) { 276 base::FilePath dest_path = root_path().AppendASCII("dest"); 277 FileSystemURL dest_url = CreateURL(FPL("dest")); 278 279 // Run the loop twice. The first run has no source files. The second run does. 280 for (int loop_count = 0; loop_count < 2; ++loop_count) { 281 if (loop_count == 1) { 282 PopulateDirectoryWithTestCases(root_path(), 283 kFilteringTestCases, 284 arraysize(kFilteringTestCases)); 285 } 286 for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { 287 // Always start with an empty destination directory. 288 // Copying to a non-empty destination directory is an invalid operation. 289 ASSERT_TRUE(base::DeleteFile(dest_path, true)); 290 ASSERT_TRUE(base::CreateDirectory(dest_path)); 291 292 FileSystemURL root_url = CreateURL(FPL("")); 293 FileSystemURL url = CreateURL(kFilteringTestCases[i].path); 294 295 std::string test_name = base::StringPrintf( 296 "CopySourceFiltering run %d test %" PRIuS, loop_count, i); 297 base::PlatformFileError expectation = base::PLATFORM_FILE_OK; 298 if (loop_count == 0 || !kFilteringTestCases[i].visible) { 299 // If the source does not exist or is not visible. 300 expectation = base::PLATFORM_FILE_ERROR_NOT_FOUND; 301 } else if (!kFilteringTestCases[i].is_directory) { 302 // Cannot copy a visible file to a directory. 303 expectation = base::PLATFORM_FILE_ERROR_INVALID_OPERATION; 304 } 305 operation_runner()->Copy( 306 url, dest_url, 307 fileapi::FileSystemOperation::OPTION_NONE, 308 fileapi::FileSystemOperationRunner::CopyProgressCallback(), 309 base::Bind(&ExpectEqHelper, test_name, expectation)); 310 base::MessageLoop::current()->RunUntilIdle(); 311 } 312 } 313} 314 315TEST_F(NativeMediaFileUtilTest, CopyDestFiltering) { 316 // Run the loop twice. The first run has no destination files. 317 // The second run does. 318 for (int loop_count = 0; loop_count < 2; ++loop_count) { 319 if (loop_count == 1) { 320 // Reset the test directory between the two loops to remove old 321 // directories and create new ones that should pre-exist. 322 ASSERT_TRUE(base::DeleteFile(root_path(), true)); 323 ASSERT_TRUE(base::CreateDirectory(root_path())); 324 PopulateDirectoryWithTestCases(root_path(), 325 kFilteringTestCases, 326 arraysize(kFilteringTestCases)); 327 } 328 329 // Always create a dummy source data file. 330 base::FilePath src_path = root_path().AppendASCII("foo.jpg"); 331 FileSystemURL src_url = CreateURL(FPL("foo.jpg")); 332 static const char kDummyData[] = "dummy"; 333 ASSERT_TRUE(file_util::WriteFile(src_path, kDummyData, strlen(kDummyData))); 334 335 for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { 336 if (loop_count == 0 && kFilteringTestCases[i].is_directory) { 337 // These directories do not exist in this case, so Copy() will not 338 // treat them as directories. Thus invalidating these test cases. 339 continue; 340 } 341 FileSystemURL root_url = CreateURL(FPL("")); 342 FileSystemURL url = CreateURL(kFilteringTestCases[i].path); 343 344 std::string test_name = base::StringPrintf( 345 "CopyDestFiltering run %d test %" PRIuS, loop_count, i); 346 base::PlatformFileError expectation; 347 if (loop_count == 0) { 348 // The destination path is a file here. The directory case has been 349 // handled above. 350 // If the destination path does not exist and is not visible, then 351 // creating it would be a security violation. 352 expectation = 353 kFilteringTestCases[i].visible ? 354 base::PLATFORM_FILE_OK : 355 base::PLATFORM_FILE_ERROR_SECURITY; 356 } else { 357 if (!kFilteringTestCases[i].visible) { 358 // If the destination path exist and is not visible, then to the copy 359 // operation, it looks like the file needs to be created, which is a 360 // security violation. 361 expectation = base::PLATFORM_FILE_ERROR_SECURITY; 362 } else if (kFilteringTestCases[i].is_directory) { 363 // Cannot copy a file to a directory. 364 expectation = base::PLATFORM_FILE_ERROR_INVALID_OPERATION; 365 } else { 366 // Copying from a file to a visible file that exists is ok. 367 expectation = base::PLATFORM_FILE_OK; 368 } 369 } 370 operation_runner()->Copy( 371 src_url, url, 372 fileapi::FileSystemOperation::OPTION_NONE, 373 fileapi::FileSystemOperationRunner::CopyProgressCallback(), 374 base::Bind(&ExpectEqHelper, test_name, expectation)); 375 base::MessageLoop::current()->RunUntilIdle(); 376 } 377 } 378} 379 380TEST_F(NativeMediaFileUtilTest, MoveSourceFiltering) { 381 base::FilePath dest_path = root_path().AppendASCII("dest"); 382 FileSystemURL dest_url = CreateURL(FPL("dest")); 383 384 // Run the loop twice. The first run has no source files. The second run does. 385 for (int loop_count = 0; loop_count < 2; ++loop_count) { 386 if (loop_count == 1) { 387 PopulateDirectoryWithTestCases(root_path(), 388 kFilteringTestCases, 389 arraysize(kFilteringTestCases)); 390 } 391 for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { 392 // Always start with an empty destination directory. 393 // Moving to a non-empty destination directory is an invalid operation. 394 ASSERT_TRUE(base::DeleteFile(dest_path, true)); 395 ASSERT_TRUE(base::CreateDirectory(dest_path)); 396 397 FileSystemURL root_url = CreateURL(FPL("")); 398 FileSystemURL url = CreateURL(kFilteringTestCases[i].path); 399 400 std::string test_name = base::StringPrintf( 401 "MoveSourceFiltering run %d test %" PRIuS, loop_count, i); 402 base::PlatformFileError expectation = base::PLATFORM_FILE_OK; 403 if (loop_count == 0 || !kFilteringTestCases[i].visible) { 404 // If the source does not exist or is not visible. 405 expectation = base::PLATFORM_FILE_ERROR_NOT_FOUND; 406 } else if (!kFilteringTestCases[i].is_directory) { 407 // Cannot move a visible file to a directory. 408 expectation = base::PLATFORM_FILE_ERROR_INVALID_OPERATION; 409 } 410 operation_runner()->Move( 411 url, dest_url, fileapi::FileSystemOperation::OPTION_NONE, 412 base::Bind(&ExpectEqHelper, test_name, expectation)); 413 base::MessageLoop::current()->RunUntilIdle(); 414 } 415 } 416} 417 418TEST_F(NativeMediaFileUtilTest, MoveDestFiltering) { 419 // Run the loop twice. The first run has no destination files. 420 // The second run does. 421 for (int loop_count = 0; loop_count < 2; ++loop_count) { 422 if (loop_count == 1) { 423 // Reset the test directory between the two loops to remove old 424 // directories and create new ones that should pre-exist. 425 ASSERT_TRUE(base::DeleteFile(root_path(), true)); 426 ASSERT_TRUE(base::CreateDirectory(root_path())); 427 PopulateDirectoryWithTestCases(root_path(), 428 kFilteringTestCases, 429 arraysize(kFilteringTestCases)); 430 } 431 432 for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { 433 if (loop_count == 0 && kFilteringTestCases[i].is_directory) { 434 // These directories do not exist in this case, so Copy() will not 435 // treat them as directories. Thus invalidating these test cases. 436 continue; 437 } 438 439 // Create the source file for every test case because it might get moved. 440 base::FilePath src_path = root_path().AppendASCII("foo.jpg"); 441 FileSystemURL src_url = CreateURL(FPL("foo.jpg")); 442 static const char kDummyData[] = "dummy"; 443 ASSERT_TRUE( 444 file_util::WriteFile(src_path, kDummyData, strlen(kDummyData))); 445 446 FileSystemURL root_url = CreateURL(FPL("")); 447 FileSystemURL url = CreateURL(kFilteringTestCases[i].path); 448 449 std::string test_name = base::StringPrintf( 450 "MoveDestFiltering run %d test %" PRIuS, loop_count, i); 451 base::PlatformFileError expectation; 452 if (loop_count == 0) { 453 // The destination path is a file here. The directory case has been 454 // handled above. 455 // If the destination path does not exist and is not visible, then 456 // creating it would be a security violation. 457 expectation = 458 kFilteringTestCases[i].visible ? 459 base::PLATFORM_FILE_OK : 460 base::PLATFORM_FILE_ERROR_SECURITY; 461 } else { 462 if (!kFilteringTestCases[i].visible) { 463 // If the destination path exist and is not visible, then to the move 464 // operation, it looks like the file needs to be created, which is a 465 // security violation. 466 expectation = base::PLATFORM_FILE_ERROR_SECURITY; 467 } else if (kFilteringTestCases[i].is_directory) { 468 // Cannot move a file to a directory. 469 expectation = base::PLATFORM_FILE_ERROR_INVALID_OPERATION; 470 } else { 471 // Moving from a file to a visible file that exists is ok. 472 expectation = base::PLATFORM_FILE_OK; 473 } 474 } 475 operation_runner()->Move( 476 src_url, url, fileapi::FileSystemOperation::OPTION_NONE, 477 base::Bind(&ExpectEqHelper, test_name, expectation)); 478 base::MessageLoop::current()->RunUntilIdle(); 479 } 480 } 481} 482 483TEST_F(NativeMediaFileUtilTest, GetMetadataFiltering) { 484 // Run the loop twice. The first run has no files. The second run does. 485 for (int loop_count = 0; loop_count < 2; ++loop_count) { 486 if (loop_count == 1) { 487 PopulateDirectoryWithTestCases(root_path(), 488 kFilteringTestCases, 489 arraysize(kFilteringTestCases)); 490 } 491 for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { 492 FileSystemURL root_url = CreateURL(FPL("")); 493 FileSystemURL url = CreateURL(kFilteringTestCases[i].path); 494 495 std::string test_name = base::StringPrintf( 496 "GetMetadataFiltering run %d test %" PRIuS, loop_count, i); 497 base::PlatformFileError expectation = base::PLATFORM_FILE_OK; 498 if (loop_count == 0 || !kFilteringTestCases[i].visible) { 499 // Cannot get metadata from files that do not exist or are not visible. 500 expectation = base::PLATFORM_FILE_ERROR_NOT_FOUND; 501 } 502 operation_runner()->GetMetadata( 503 url, 504 base::Bind(&ExpectMetadataEqHelper, 505 test_name, 506 expectation, 507 kFilteringTestCases[i].is_directory)); 508 base::MessageLoop::current()->RunUntilIdle(); 509 } 510 } 511} 512 513TEST_F(NativeMediaFileUtilTest, RemoveFileFiltering) { 514 // Run the loop twice. The first run has no files. The second run does. 515 for (int loop_count = 0; loop_count < 2; ++loop_count) { 516 if (loop_count == 1) { 517 PopulateDirectoryWithTestCases(root_path(), 518 kFilteringTestCases, 519 arraysize(kFilteringTestCases)); 520 } 521 for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { 522 FileSystemURL root_url = CreateURL(FPL("")); 523 FileSystemURL url = CreateURL(kFilteringTestCases[i].path); 524 525 std::string test_name = base::StringPrintf( 526 "RemoveFiltering run %d test %" PRIuS, loop_count, i); 527 base::PlatformFileError expectation = base::PLATFORM_FILE_OK; 528 if (loop_count == 0 || !kFilteringTestCases[i].visible) { 529 // Cannot remove files that do not exist or are not visible. 530 expectation = base::PLATFORM_FILE_ERROR_NOT_FOUND; 531 } else if (kFilteringTestCases[i].is_directory) { 532 expectation = base::PLATFORM_FILE_ERROR_NOT_A_FILE; 533 } 534 operation_runner()->RemoveFile( 535 url, base::Bind(&ExpectEqHelper, test_name, expectation)); 536 base::MessageLoop::current()->RunUntilIdle(); 537 } 538 } 539} 540 541void CreateSnapshotCallback(base::PlatformFileError* error, 542 base::PlatformFileError result, const base::PlatformFileInfo&, 543 const base::FilePath&, 544 const scoped_refptr<webkit_blob::ShareableFileReference>&) { 545 *error = result; 546} 547 548TEST_F(NativeMediaFileUtilTest, CreateSnapshot) { 549 PopulateDirectoryWithTestCases(root_path(), 550 kFilteringTestCases, 551 arraysize(kFilteringTestCases)); 552 for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { 553 if (kFilteringTestCases[i].is_directory || 554 !kFilteringTestCases[i].visible) { 555 continue; 556 } 557 FileSystemURL root_url = CreateURL(FPL("")); 558 FileSystemURL url = CreateURL(kFilteringTestCases[i].path); 559 base::PlatformFileError expected_error, error; 560 if (kFilteringTestCases[i].media_file) 561 expected_error = base::PLATFORM_FILE_OK; 562 else 563 expected_error = base::PLATFORM_FILE_ERROR_SECURITY; 564 error = base::PLATFORM_FILE_ERROR_FAILED; 565 operation_runner()->CreateSnapshotFile(url, 566 base::Bind(CreateSnapshotCallback, &error)); 567 base::MessageLoop::current()->RunUntilIdle(); 568 ASSERT_EQ(expected_error, error); 569 } 570} 571