From bf1599c4174cc5fc2a85eccd4ba99d08d258436a Mon Sep 17 00:00:00 2001 From: "hidehiko@chromium.org" Date: Wed, 18 Sep 2013 23:32:18 +0000 Subject: [PATCH] Refactor RecursiveOperationDelegate to support PostProcessDirectory. This is refactoring CL as follows: 1) RecursiveOperationDelegate assumes the root is a file at first, and if the ProcessFile is failed, actually starts recursive operation. This is what CopyOrMoveOperationDelegate does. 2) Introduces PostProcessDirectory, which makes RecursiveOperationDelegate simpler (done in a following CL), and is useful to implement timestamp preserving. 3) Adds unittests for the RecursiveOperationDelegate. For the testing, exports RecursiveOperationDelegate. BUG=282107 TEST=Ran content_unittests and unit_tests Review URL: https://chromiumcodereview.appspot.com/23950012 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@223988 0039d316-1c4b-4281-b951-d872f2087c98 --- content/content_tests.gypi | 1 + .../fileapi/copy_or_move_operation_delegate.cc | 6 + .../fileapi/copy_or_move_operation_delegate.h | 2 + .../fileapi/recursive_operation_delegate.cc | 213 +++++++++++----- .../browser/fileapi/recursive_operation_delegate.h | 76 ++++-- .../recursive_operation_delegate_unittest.cc | 280 +++++++++++++++++++++ .../browser/fileapi/remove_operation_delegate.cc | 5 + webkit/browser/fileapi/remove_operation_delegate.h | 2 + 8 files changed, 502 insertions(+), 83 deletions(-) create mode 100644 webkit/browser/fileapi/recursive_operation_delegate_unittest.cc diff --git a/content/content_tests.gypi b/content/content_tests.gypi index 5037e127d6e1..b20910156a3d 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -562,6 +562,7 @@ '../webkit/browser/fileapi/mock_file_system_options.h', '../webkit/browser/fileapi/native_file_util_unittest.cc', '../webkit/browser/fileapi/obfuscated_file_util_unittest.cc', + '../webkit/browser/fileapi/recursive_operation_delegate_unittest.cc', '../webkit/browser/fileapi/sandbox_database_test_helper.cc', '../webkit/browser/fileapi/sandbox_database_test_helper.h', '../webkit/browser/fileapi/sandbox_directory_database_unittest.cc', diff --git a/webkit/browser/fileapi/copy_or_move_operation_delegate.cc b/webkit/browser/fileapi/copy_or_move_operation_delegate.cc index 7634a8830d64..90787732a633 100644 --- a/webkit/browser/fileapi/copy_or_move_operation_delegate.cc +++ b/webkit/browser/fileapi/copy_or_move_operation_delegate.cc @@ -368,6 +368,12 @@ void CopyOrMoveOperationDelegate::ProcessDirectory( weak_factory_.GetWeakPtr(), src_url, callback)); } +void CopyOrMoveOperationDelegate::PostProcessDirectory( + const FileSystemURL& src_url, + const StatusCallback& callback) { + callback.Run(base::PLATFORM_FILE_OK); +} + void CopyOrMoveOperationDelegate::DidTryCopyOrMoveFile( base::PlatformFileError error) { if (error != base::PLATFORM_FILE_ERROR_NOT_A_FILE) { diff --git a/webkit/browser/fileapi/copy_or_move_operation_delegate.h b/webkit/browser/fileapi/copy_or_move_operation_delegate.h index cb409ce7ad2a..b452c22d116b 100644 --- a/webkit/browser/fileapi/copy_or_move_operation_delegate.h +++ b/webkit/browser/fileapi/copy_or_move_operation_delegate.h @@ -47,6 +47,8 @@ class CopyOrMoveOperationDelegate const StatusCallback& callback) OVERRIDE; virtual void ProcessDirectory(const FileSystemURL& url, const StatusCallback& callback) OVERRIDE; + virtual void PostProcessDirectory(const FileSystemURL& url, + const StatusCallback& callback) OVERRIDE; private: void DidTryCopyOrMoveFile(base::PlatformFileError error); diff --git a/webkit/browser/fileapi/recursive_operation_delegate.cc b/webkit/browser/fileapi/recursive_operation_delegate.cc index 659b16d8dbcc..e2bcdc4c6f3a 100644 --- a/webkit/browser/fileapi/recursive_operation_delegate.cc +++ b/webkit/browser/fileapi/recursive_operation_delegate.cc @@ -26,81 +26,85 @@ RecursiveOperationDelegate::~RecursiveOperationDelegate() { } void RecursiveOperationDelegate::Cancel() { - // Set the cancel flag and remove all pending tasks. canceled_ = true; - while (!pending_directories_.empty()) - pending_directories_.pop(); - while (!pending_files_.empty()) - pending_files_.pop(); } void RecursiveOperationDelegate::StartRecursiveOperation( const FileSystemURL& root, const StatusCallback& callback) { + DCHECK(pending_directory_stack_.empty()); + DCHECK(pending_files_.empty()); + DCHECK_EQ(0, inflight_operations_); + callback_ = callback; - pending_directories_.push(root); - ProcessNextDirectory(); + ++inflight_operations_; + ProcessFile( + root, + base::Bind(&RecursiveOperationDelegate::DidTryProcessFile, + AsWeakPtr(), root)); } FileSystemOperationRunner* RecursiveOperationDelegate::operation_runner() { return file_system_context_->operation_runner(); } -void RecursiveOperationDelegate::ProcessNextDirectory() { +void RecursiveOperationDelegate::DidTryProcessFile( + const FileSystemURL& root, + base::PlatformFileError error) { + DCHECK(pending_directory_stack_.empty()); DCHECK(pending_files_.empty()); - DCHECK_EQ(0, inflight_operations_); - - if (pending_directories_.empty()) { - Done(base::PLATFORM_FILE_OK); + DCHECK_EQ(1, inflight_operations_); + + --inflight_operations_; + // If the operation for a file is not permitted, the operation may fail + // with SECURITY, even if the path points a directory and the operation is + // permitted for a directory. + if (canceled_ || + (error != base::PLATFORM_FILE_ERROR_NOT_A_FILE && + error != base::PLATFORM_FILE_ERROR_SECURITY)) { + Done(error); return; } - FileSystemURL url = pending_directories_.top(); - pending_directories_.pop(); - inflight_operations_++; - ProcessDirectory( - url, base::Bind(&RecursiveOperationDelegate::DidProcessDirectory, - AsWeakPtr(), url)); -} -void RecursiveOperationDelegate::ProcessPendingFiles() { - if (pending_files_.empty() && inflight_operations_ == 0) { - ProcessNextDirectory(); - return; - } - while (!pending_files_.empty() && - inflight_operations_ < kMaxInflightOperations) { - FileSystemURL url = pending_files_.top(); - pending_files_.pop(); - inflight_operations_++; - base::MessageLoopProxy::current()->PostTask( - FROM_HERE, - base::Bind(&RecursiveOperationDelegate::ProcessFile, - AsWeakPtr(), url, - base::Bind(&RecursiveOperationDelegate::DidProcessFile, - AsWeakPtr()))); - } + pending_directory_stack_.push(std::queue()); + pending_directory_stack_.top().push(root); + ProcessNextDirectory(); } -void RecursiveOperationDelegate::DidProcessFile(base::PlatformFileError error) { - inflight_operations_--; - DCHECK_GE(inflight_operations_, 0); - if (error != base::PLATFORM_FILE_OK) { - Done(error); - return; - } - ProcessPendingFiles(); +void RecursiveOperationDelegate::ProcessNextDirectory() { + DCHECK(pending_files_.empty()); + DCHECK(!pending_directory_stack_.empty()); + DCHECK(!pending_directory_stack_.top().empty()); + DCHECK_EQ(0, inflight_operations_); + + const FileSystemURL& url = pending_directory_stack_.top().front(); + + ++inflight_operations_; + ProcessDirectory( + url, + base::Bind( + &RecursiveOperationDelegate::DidProcessDirectory, AsWeakPtr())); } void RecursiveOperationDelegate::DidProcessDirectory( - const FileSystemURL& url, base::PlatformFileError error) { + DCHECK(pending_files_.empty()); + DCHECK(!pending_directory_stack_.empty()); + DCHECK(!pending_directory_stack_.top().empty()); + DCHECK_EQ(1, inflight_operations_); + + --inflight_operations_; if (canceled_ || error != base::PLATFORM_FILE_OK) { Done(error); return; } + + const FileSystemURL& parent = pending_directory_stack_.top().front(); + pending_directory_stack_.push(std::queue()); operation_runner()->ReadDirectory( - url, base::Bind(&RecursiveOperationDelegate::DidReadDirectory, - AsWeakPtr(), url)); + parent, + base::Bind(&RecursiveOperationDelegate::DidReadDirectory, + AsWeakPtr(), parent)); } void RecursiveOperationDelegate::DidReadDirectory( @@ -108,49 +112,122 @@ void RecursiveOperationDelegate::DidReadDirectory( base::PlatformFileError error, const FileEntryList& entries, bool has_more) { - if (canceled_) { - Done(error); - return; - } + DCHECK(pending_files_.empty()); + DCHECK(!pending_directory_stack_.empty()); + DCHECK_EQ(0, inflight_operations_); - if (error != base::PLATFORM_FILE_OK) { - if (error == base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY) { - // The given path may have been a file, so try ProcessFile now. - ProcessFile(parent, - base::Bind(&RecursiveOperationDelegate::DidTryProcessFile, - AsWeakPtr(), error)); - return; - } + if (canceled_ || error != base::PLATFORM_FILE_OK) { Done(error); return; } + for (size_t i = 0; i < entries.size(); i++) { FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL( parent.origin(), parent.mount_type(), parent.virtual_path().Append(entries[i].name)); if (entries[i].is_directory) - pending_directories_.push(url); + pending_directory_stack_.top().push(url); else pending_files_.push(url); } + + // Wait for next entries. if (has_more) return; - inflight_operations_--; - DCHECK_GE(inflight_operations_, 0); ProcessPendingFiles(); } -void RecursiveOperationDelegate::DidTryProcessFile( - base::PlatformFileError previous_error, +void RecursiveOperationDelegate::ProcessPendingFiles() { + DCHECK(!pending_directory_stack_.empty()); + + if ((pending_files_.empty() || canceled_) && inflight_operations_ == 0) { + ProcessSubDirectory(); + return; + } + + // Do not post any new tasks. + if (canceled_) + return; + + // Run ProcessFile in parallel (upto kMaxInflightOperations). + scoped_refptr current_message_loop = + base::MessageLoopProxy::current(); + while (!pending_files_.empty() && + inflight_operations_ < kMaxInflightOperations) { + ++inflight_operations_; + current_message_loop->PostTask( + FROM_HERE, + base::Bind(&RecursiveOperationDelegate::ProcessFile, + AsWeakPtr(), pending_files_.front(), + base::Bind(&RecursiveOperationDelegate::DidProcessFile, + AsWeakPtr()))); + pending_files_.pop(); + } +} + +void RecursiveOperationDelegate::DidProcessFile( + base::PlatformFileError error) { + --inflight_operations_; + if (error != base::PLATFORM_FILE_OK) { + // If an error occurs, invoke Done immediately (even if there remain + // running operations). It is because in the callback, this instance is + // deleted. + Done(error); + return; + } + + ProcessPendingFiles(); +} + +void RecursiveOperationDelegate::ProcessSubDirectory() { + DCHECK(pending_files_.empty()); + DCHECK(!pending_directory_stack_.empty()); + DCHECK_EQ(0, inflight_operations_); + + if (canceled_) { + Done(base::PLATFORM_FILE_ERROR_ABORT); + return; + } + + if (!pending_directory_stack_.top().empty()) { + // There remain some sub directories. Process them first. + ProcessNextDirectory(); + return; + } + + // All subdirectories are processed. + pending_directory_stack_.pop(); + if (pending_directory_stack_.empty()) { + // All files/directories are processed. + Done(base::PLATFORM_FILE_OK); + return; + } + + DCHECK(!pending_directory_stack_.top().empty()); + ++inflight_operations_; + PostProcessDirectory( + pending_directory_stack_.top().front(), + base::Bind(&RecursiveOperationDelegate::DidPostProcessDirectory, + AsWeakPtr())); +} + +void RecursiveOperationDelegate::DidPostProcessDirectory( base::PlatformFileError error) { - if (error == base::PLATFORM_FILE_ERROR_NOT_A_FILE) { - // It wasn't a file either; returns with the previous error. - Done(previous_error); + DCHECK(pending_files_.empty()); + DCHECK(!pending_directory_stack_.empty()); + DCHECK(!pending_directory_stack_.top().empty()); + DCHECK_EQ(1, inflight_operations_); + + --inflight_operations_; + pending_directory_stack_.top().pop(); + if (canceled_ || error != base::PLATFORM_FILE_OK) { + Done(error); return; } - DidProcessFile(error); + + ProcessSubDirectory(); } void RecursiveOperationDelegate::Done(base::PlatformFileError error) { diff --git a/webkit/browser/fileapi/recursive_operation_delegate.h b/webkit/browser/fileapi/recursive_operation_delegate.h index 8ee5b4d792e0..a52667773415 100644 --- a/webkit/browser/fileapi/recursive_operation_delegate.h +++ b/webkit/browser/fileapi/recursive_operation_delegate.h @@ -5,6 +5,7 @@ #ifndef WEBKIT_BROWSER_FILEAPI_RECURSIVE_OPERATION_DELEGATE_H_ #define WEBKIT_BROWSER_FILEAPI_RECURSIVE_OPERATION_DELEGATE_H_ +#include #include #include "base/basictypes.h" @@ -23,13 +24,12 @@ class FileSystemOperationRunner; // In short, each subclass should override ProcessFile and ProcessDirectory // to process a directory or a file. To start the recursive operation it // should also call StartRecursiveOperation. -class RecursiveOperationDelegate +class WEBKIT_STORAGE_BROWSER_EXPORT RecursiveOperationDelegate : public base::SupportsWeakPtr { public: typedef FileSystemOperation::StatusCallback StatusCallback; typedef FileSystemOperation::FileEntryList FileEntryList; - RecursiveOperationDelegate(FileSystemContext* file_system_context); virtual ~RecursiveOperationDelegate(); // This is called when the consumer of this instance starts a non-recursive @@ -50,13 +50,58 @@ class RecursiveOperationDelegate virtual void ProcessDirectory(const FileSystemURL& url, const StatusCallback& callback) = 0; - // Cancels currently running operations. + + // This is called each time after files and subdirectories for a + // directory is processed while recursively performing an operation. + virtual void PostProcessDirectory(const FileSystemURL& url, + const StatusCallback& callback) = 0; + + // Cancels the currently running operation. void Cancel(); protected: + explicit RecursiveOperationDelegate(FileSystemContext* file_system_context); + // Starts to process files/directories recursively from the given |root|. - // This will call ProcessFile and ProcessDirectory on each directory or file. - // If the given |root| is a file this simply calls ProcessFile and exits. + // This will call ProcessFile and ProcessDirectory on each file or directory. + // + // First, this tries to call ProcessFile with |root| regardless whether it is + // actually a file or a directory. If it is a directory, ProcessFile should + // return PLATFORM_FILE_NOT_A_FILE. + // + // For each directory, the recursive operation works as follows: + // ProcessDirectory is called first for the directory. + // Then the directory contents are read (to obtain its sub directories and + // files in it). + // ProcessFile is called for found files. This may run in parallel. + // The same step is recursively applied to each subdirectory. + // After all files and subdirectories in a directory are processed, + // PostProcessDirectory is called for the directory. + // Here is an example; + // a_dir/ -+- b1_dir/ -+- c1_dir/ -+- d1_file + // | | | + // | +- c2_file +- d2_file + // | + // +- b2_dir/ --- e_dir/ + // | + // +- b3_file + // | + // +- b4_file + // Then traverse order is: + // ProcessFile(a_dir) (This should return PLATFORM_FILE_NOT_A_FILE). + // ProcessDirectory(a_dir). + // ProcessFile(b3_file), ProcessFile(b4_file). (in parallel). + // ProcessDirectory(b1_dir). + // ProcessFile(c2_file) + // ProcessDirectory(c1_dir). + // ProcessFile(d1_file), ProcessFile(d2_file). (in parallel). + // PostProcessDirectory(c1_dir) + // PostProcessDirectory(b1_dir). + // ProcessDirectory(b2_dir) + // ProcessDirectory(e_dir) + // PostProcessDirectory(e_dir) + // PostProcessDirectory(b2_dir) + // PostProcessDirectory(a_dir) // // |callback| is fired with base::PLATFORM_FILE_OK when every file/directory // under |root| is processed, or fired earlier when any suboperation fails. @@ -71,18 +116,18 @@ class RecursiveOperationDelegate FileSystemOperationRunner* operation_runner(); private: + void DidTryProcessFile(const FileSystemURL& root, + base::PlatformFileError error); void ProcessNextDirectory(); + void DidProcessDirectory(base::PlatformFileError error); + void DidReadDirectory(const FileSystemURL& parent, + base::PlatformFileError error, + const FileEntryList& entries, + bool has_more); void ProcessPendingFiles(); void DidProcessFile(base::PlatformFileError error); - void DidProcessDirectory(const FileSystemURL& url, - base::PlatformFileError error); - void DidReadDirectory( - const FileSystemURL& parent, - base::PlatformFileError error, - const FileEntryList& entries, - bool has_more); - void DidTryProcessFile(base::PlatformFileError previous_error, - base::PlatformFileError error); + void ProcessSubDirectory(); + void DidPostProcessDirectory(base::PlatformFileError error); // Called when all recursive operation is done (or an error occurs). void Done(base::PlatformFileError error); @@ -90,7 +135,8 @@ class RecursiveOperationDelegate FileSystemContext* file_system_context_; StatusCallback callback_; std::stack pending_directories_; - std::stack pending_files_; + std::stack > pending_directory_stack_; + std::queue pending_files_; int inflight_operations_; bool canceled_; diff --git a/webkit/browser/fileapi/recursive_operation_delegate_unittest.cc b/webkit/browser/fileapi/recursive_operation_delegate_unittest.cc new file mode 100644 index 000000000000..b38422794203 --- /dev/null +++ b/webkit/browser/fileapi/recursive_operation_delegate_unittest.cc @@ -0,0 +1,280 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/browser/fileapi/recursive_operation_delegate.h" + +#include + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/callback.h" +#include "base/files/scoped_temp_dir.h" +#include "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/run_loop.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/fileapi/file_system_file_util.h" +#include "webkit/browser/fileapi/file_system_operation.h" +#include "webkit/browser/fileapi/file_system_operation_runner.h" +#include "webkit/browser/fileapi/sandbox_file_system_test_helper.h" + +namespace fileapi { +namespace { + +class LoggingRecursiveOperation : public RecursiveOperationDelegate { + public: + struct LogEntry { + enum Type { + PROCESS_FILE, + PROCESS_DIRECTORY, + POST_PROCESS_DIRECTORY + }; + Type type; + FileSystemURL url; + }; + + LoggingRecursiveOperation(FileSystemContext* file_system_context, + const FileSystemURL& root, + const StatusCallback& callback) + : RecursiveOperationDelegate(file_system_context), + root_(root), + callback_(callback), + weak_factory_(this) { + } + virtual ~LoggingRecursiveOperation() {} + + const std::vector& log_entries() const { return log_entries_; } + + // RecursiveOperationDelegate overrides. + virtual void Run() OVERRIDE { + NOTREACHED(); + } + + virtual void RunRecursively() OVERRIDE { + StartRecursiveOperation(root_, callback_); + } + + virtual void ProcessFile(const FileSystemURL& url, + const StatusCallback& callback) OVERRIDE { + RecordLogEntry(LogEntry::PROCESS_FILE, url); + operation_runner()->GetMetadata( + url, + base::Bind(&LoggingRecursiveOperation::DidGetMetadata, + weak_factory_.GetWeakPtr(), callback)); + } + + virtual void ProcessDirectory(const FileSystemURL& url, + const StatusCallback& callback) OVERRIDE { + RecordLogEntry(LogEntry::PROCESS_DIRECTORY, url); + callback.Run(base::PLATFORM_FILE_OK); + } + + virtual void PostProcessDirectory(const FileSystemURL& url, + const StatusCallback& callback) OVERRIDE { + RecordLogEntry(LogEntry::POST_PROCESS_DIRECTORY, url); + callback.Run(base::PLATFORM_FILE_OK); + } + + private: + void RecordLogEntry(LogEntry::Type type, const FileSystemURL& url) { + LogEntry entry; + entry.type = type; + entry.url = url; + log_entries_.push_back(entry); + } + + void DidGetMetadata(const StatusCallback& callback, + base::PlatformFileError result, + const base::PlatformFileInfo& file_info) { + if (result != base::PLATFORM_FILE_OK) { + callback.Run(result); + return; + } + + callback.Run(file_info.is_directory ? + base::PLATFORM_FILE_ERROR_NOT_A_FILE : + base::PLATFORM_FILE_OK); + } + + FileSystemURL root_; + StatusCallback callback_; + std::vector log_entries_; + + base::WeakPtrFactory weak_factory_; + DISALLOW_COPY_AND_ASSIGN(LoggingRecursiveOperation); +}; + +void ReportStatus(base::PlatformFileError* out_error, + base::PlatformFileError error) { + DCHECK(out_error); + *out_error = error; +} + +// To test the Cancel() during operation, calls Cancel() of |operation| +// after |counter| times message posting. +void CallCancelLater(RecursiveOperationDelegate* operation, int counter) { + if (counter > 0) { + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(&CallCancelLater, base::Unretained(operation), counter - 1)); + return; + } + + operation->Cancel(); +} + +} // namespace + +class RecursiveOperationDelegateTest : public testing::Test { + protected: + virtual void SetUp() OVERRIDE { + EXPECT_TRUE(base_.CreateUniqueTempDir()); + sandbox_file_system_.SetUp(base_.path().AppendASCII("filesystem")); + } + + virtual void TearDown() OVERRIDE { + sandbox_file_system_.TearDown(); + } + + scoped_ptr NewContext() { + FileSystemOperationContext* context = + sandbox_file_system_.NewOperationContext(); + // Grant enough quota for all test cases. + context->set_allowed_bytes_growth(1000000); + return make_scoped_ptr(context); + } + + FileSystemFileUtil* file_util() { + return sandbox_file_system_.file_util(); + } + + FileSystemURL URLForPath(const std::string& path) const { + return sandbox_file_system_.CreateURLFromUTF8(path); + } + + FileSystemURL CreateFile(const std::string& path) { + FileSystemURL url = URLForPath(path); + bool created = false; + EXPECT_EQ(base::PLATFORM_FILE_OK, + file_util()->EnsureFileExists(NewContext().get(), + url, &created)); + EXPECT_TRUE(created); + return url; + } + + FileSystemURL CreateDirectory(const std::string& path) { + FileSystemURL url = URLForPath(path); + EXPECT_EQ(base::PLATFORM_FILE_OK, + file_util()->CreateDirectory(NewContext().get(), url, + false /* exclusive */, true)); + return url; + } + + private: + base::MessageLoop message_loop_; + + // Common temp base for nondestructive uses. + base::ScopedTempDir base_; + SandboxFileSystemTestHelper sandbox_file_system_; +}; + +TEST_F(RecursiveOperationDelegateTest, RootIsFile) { + FileSystemURL src_file(CreateFile("src")); + + base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; + scoped_ptr context = NewContext(); + scoped_ptr operation( + new LoggingRecursiveOperation( + context->file_system_context(), src_file, + base::Bind(&ReportStatus, &error))); + operation->RunRecursively(); + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(base::PLATFORM_FILE_OK, error); + + const std::vector& log_entries = + operation->log_entries(); + ASSERT_EQ(1U, log_entries.size()); + const LoggingRecursiveOperation::LogEntry& entry = log_entries[0]; + EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE, entry.type); + EXPECT_EQ(src_file, entry.url); +} + +TEST_F(RecursiveOperationDelegateTest, RootIsDirectory) { + FileSystemURL src_root(CreateDirectory("src")); + FileSystemURL src_dir1(CreateDirectory("src/dir1")); + FileSystemURL src_file1(CreateFile("src/file1")); + FileSystemURL src_file2(CreateFile("src/dir1/file2")); + FileSystemURL src_file3(CreateFile("src/dir1/file3")); + + base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; + scoped_ptr context = NewContext(); + scoped_ptr operation( + new LoggingRecursiveOperation( + context->file_system_context(), src_root, + base::Bind(&ReportStatus, &error))); + operation->RunRecursively(); + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(base::PLATFORM_FILE_OK, error); + + const std::vector& log_entries = + operation->log_entries(); + ASSERT_EQ(8U, log_entries.size()); + + EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE, + log_entries[0].type); + EXPECT_EQ(src_root, log_entries[0].url); + + EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY, + log_entries[1].type); + EXPECT_EQ(src_root, log_entries[1].url); + + EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE, + log_entries[2].type); + EXPECT_EQ(src_file1, log_entries[2].url); + + EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY, + log_entries[3].type); + EXPECT_EQ(src_dir1, log_entries[3].url); + + // The order of src/dir1/file2 and src/dir1/file3 depends on the file system + // implementation (can be swapped). + EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE, + log_entries[4].type); + EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE, + log_entries[5].type); + EXPECT_TRUE((src_file2 == log_entries[4].url && + src_file3 == log_entries[5].url) || + (src_file3 == log_entries[4].url && + src_file2 == log_entries[5].url)); + + EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY, + log_entries[6].type); + EXPECT_EQ(src_dir1, log_entries[6].url); + + EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY, + log_entries[7].type); + EXPECT_EQ(src_root, log_entries[7].url); +} + +TEST_F(RecursiveOperationDelegateTest, Cancel) { + FileSystemURL src_root(CreateDirectory("src")); + FileSystemURL src_dir1(CreateDirectory("src/dir1")); + FileSystemURL src_file1(CreateFile("src/file1")); + FileSystemURL src_file2(CreateFile("src/dir1/file2")); + + base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; + scoped_ptr context = NewContext(); + scoped_ptr operation( + new LoggingRecursiveOperation( + context->file_system_context(), src_root, + base::Bind(&ReportStatus, &error))); + operation->RunRecursively(); + + // Invoke Cancel(), after 5 times message posting. + CallCancelLater(operation.get(), 5); + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(base::PLATFORM_FILE_ERROR_ABORT, error); +} + +} // namespace fileapi diff --git a/webkit/browser/fileapi/remove_operation_delegate.cc b/webkit/browser/fileapi/remove_operation_delegate.cc index fcd9ba9cee2c..5988733cd92a 100644 --- a/webkit/browser/fileapi/remove_operation_delegate.cc +++ b/webkit/browser/fileapi/remove_operation_delegate.cc @@ -52,6 +52,11 @@ void RemoveOperationDelegate::ProcessDirectory(const FileSystemURL& url, callback.Run(base::PLATFORM_FILE_OK); } +void RemoveOperationDelegate::PostProcessDirectory( + const FileSystemURL& url, const StatusCallback& callback) { + callback.Run(base::PLATFORM_FILE_OK); +} + void RemoveOperationDelegate::DidTryRemoveFile( base::PlatformFileError error) { if (error == base::PLATFORM_FILE_OK || diff --git a/webkit/browser/fileapi/remove_operation_delegate.h b/webkit/browser/fileapi/remove_operation_delegate.h index ef9e1a09409f..0271e53cf861 100644 --- a/webkit/browser/fileapi/remove_operation_delegate.h +++ b/webkit/browser/fileapi/remove_operation_delegate.h @@ -26,6 +26,8 @@ class RemoveOperationDelegate const StatusCallback& callback) OVERRIDE; virtual void ProcessDirectory(const FileSystemURL& url, const StatusCallback& callback) OVERRIDE; + virtual void PostProcessDirectory(const FileSystemURL& url, + const StatusCallback& callback) OVERRIDE; private: void DidTryRemoveFile(base::PlatformFileError error); -- 2.11.4.GIT