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 "webkit/browser/fileapi/recursive_operation_delegate.h" 6 7 #include <vector> 8 9 #include "base/basictypes.h" 10 #include "base/bind.h" 11 #include "base/callback.h" 12 #include "base/files/scoped_temp_dir.h" 13 #include "base/message_loop/message_loop.h" 14 #include "base/message_loop/message_loop_proxy.h" 15 #include "base/run_loop.h" 16 #include "content/public/test/sandbox_file_system_test_helper.h" 17 #include "testing/gtest/include/gtest/gtest.h" 18 #include "webkit/browser/fileapi/file_system_file_util.h" 19 #include "webkit/browser/fileapi/file_system_operation.h" 20 #include "webkit/browser/fileapi/file_system_operation_runner.h" 21 22 namespace fileapi { 23 namespace { 24 25 class LoggingRecursiveOperation : public RecursiveOperationDelegate { 26 public: 27 struct LogEntry { 28 enum Type { 29 PROCESS_FILE, 30 PROCESS_DIRECTORY, 31 POST_PROCESS_DIRECTORY 32 }; 33 Type type; 34 FileSystemURL url; 35 }; 36 37 LoggingRecursiveOperation(FileSystemContext* file_system_context, 38 const FileSystemURL& root, 39 const StatusCallback& callback) 40 : RecursiveOperationDelegate(file_system_context), 41 root_(root), 42 callback_(callback), 43 weak_factory_(this) { 44 } 45 virtual ~LoggingRecursiveOperation() {} 46 47 const std::vector<LogEntry>& log_entries() const { return log_entries_; } 48 49 // RecursiveOperationDelegate overrides. 50 virtual void Run() OVERRIDE { 51 NOTREACHED(); 52 } 53 54 virtual void RunRecursively() OVERRIDE { 55 StartRecursiveOperation(root_, callback_); 56 } 57 58 virtual void ProcessFile(const FileSystemURL& url, 59 const StatusCallback& callback) OVERRIDE { 60 RecordLogEntry(LogEntry::PROCESS_FILE, url); 61 operation_runner()->GetMetadata( 62 url, 63 base::Bind(&LoggingRecursiveOperation::DidGetMetadata, 64 weak_factory_.GetWeakPtr(), callback)); 65 } 66 67 virtual void ProcessDirectory(const FileSystemURL& url, 68 const StatusCallback& callback) OVERRIDE { 69 RecordLogEntry(LogEntry::PROCESS_DIRECTORY, url); 70 callback.Run(base::PLATFORM_FILE_OK); 71 } 72 73 virtual void PostProcessDirectory(const FileSystemURL& url, 74 const StatusCallback& callback) OVERRIDE { 75 RecordLogEntry(LogEntry::POST_PROCESS_DIRECTORY, url); 76 callback.Run(base::PLATFORM_FILE_OK); 77 } 78 79 private: 80 void RecordLogEntry(LogEntry::Type type, const FileSystemURL& url) { 81 LogEntry entry; 82 entry.type = type; 83 entry.url = url; 84 log_entries_.push_back(entry); 85 } 86 87 void DidGetMetadata(const StatusCallback& callback, 88 base::PlatformFileError result, 89 const base::PlatformFileInfo& file_info) { 90 if (result != base::PLATFORM_FILE_OK) { 91 callback.Run(result); 92 return; 93 } 94 95 callback.Run(file_info.is_directory ? 96 base::PLATFORM_FILE_ERROR_NOT_A_FILE : 97 base::PLATFORM_FILE_OK); 98 } 99 100 FileSystemURL root_; 101 StatusCallback callback_; 102 std::vector<LogEntry> log_entries_; 103 104 base::WeakPtrFactory<LoggingRecursiveOperation> weak_factory_; 105 DISALLOW_COPY_AND_ASSIGN(LoggingRecursiveOperation); 106 }; 107 108 void ReportStatus(base::PlatformFileError* out_error, 109 base::PlatformFileError error) { 110 DCHECK(out_error); 111 *out_error = error; 112 } 113 114 // To test the Cancel() during operation, calls Cancel() of |operation| 115 // after |counter| times message posting. 116 void CallCancelLater(RecursiveOperationDelegate* operation, int counter) { 117 if (counter > 0) { 118 base::MessageLoopProxy::current()->PostTask( 119 FROM_HERE, 120 base::Bind(&CallCancelLater, base::Unretained(operation), counter - 1)); 121 return; 122 } 123 124 operation->Cancel(); 125 } 126 127 } // namespace 128 129 class RecursiveOperationDelegateTest : public testing::Test { 130 protected: 131 virtual void SetUp() OVERRIDE { 132 EXPECT_TRUE(base_.CreateUniqueTempDir()); 133 sandbox_file_system_.SetUp(base_.path().AppendASCII("filesystem")); 134 } 135 136 virtual void TearDown() OVERRIDE { 137 sandbox_file_system_.TearDown(); 138 } 139 140 scoped_ptr<FileSystemOperationContext> NewContext() { 141 FileSystemOperationContext* context = 142 sandbox_file_system_.NewOperationContext(); 143 // Grant enough quota for all test cases. 144 context->set_allowed_bytes_growth(1000000); 145 return make_scoped_ptr(context); 146 } 147 148 FileSystemFileUtil* file_util() { 149 return sandbox_file_system_.file_util(); 150 } 151 152 FileSystemURL URLForPath(const std::string& path) const { 153 return sandbox_file_system_.CreateURLFromUTF8(path); 154 } 155 156 FileSystemURL CreateFile(const std::string& path) { 157 FileSystemURL url = URLForPath(path); 158 bool created = false; 159 EXPECT_EQ(base::PLATFORM_FILE_OK, 160 file_util()->EnsureFileExists(NewContext().get(), 161 url, &created)); 162 EXPECT_TRUE(created); 163 return url; 164 } 165 166 FileSystemURL CreateDirectory(const std::string& path) { 167 FileSystemURL url = URLForPath(path); 168 EXPECT_EQ(base::PLATFORM_FILE_OK, 169 file_util()->CreateDirectory(NewContext().get(), url, 170 false /* exclusive */, true)); 171 return url; 172 } 173 174 private: 175 base::MessageLoop message_loop_; 176 177 // Common temp base for nondestructive uses. 178 base::ScopedTempDir base_; 179 SandboxFileSystemTestHelper sandbox_file_system_; 180 }; 181 182 TEST_F(RecursiveOperationDelegateTest, RootIsFile) { 183 FileSystemURL src_file(CreateFile("src")); 184 185 base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; 186 scoped_ptr<FileSystemOperationContext> context = NewContext(); 187 scoped_ptr<LoggingRecursiveOperation> operation( 188 new LoggingRecursiveOperation( 189 context->file_system_context(), src_file, 190 base::Bind(&ReportStatus, &error))); 191 operation->RunRecursively(); 192 base::RunLoop().RunUntilIdle(); 193 ASSERT_EQ(base::PLATFORM_FILE_OK, error); 194 195 const std::vector<LoggingRecursiveOperation::LogEntry>& log_entries = 196 operation->log_entries(); 197 ASSERT_EQ(1U, log_entries.size()); 198 const LoggingRecursiveOperation::LogEntry& entry = log_entries[0]; 199 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE, entry.type); 200 EXPECT_EQ(src_file, entry.url); 201 } 202 203 TEST_F(RecursiveOperationDelegateTest, RootIsDirectory) { 204 FileSystemURL src_root(CreateDirectory("src")); 205 FileSystemURL src_dir1(CreateDirectory("src/dir1")); 206 FileSystemURL src_file1(CreateFile("src/file1")); 207 FileSystemURL src_file2(CreateFile("src/dir1/file2")); 208 FileSystemURL src_file3(CreateFile("src/dir1/file3")); 209 210 base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; 211 scoped_ptr<FileSystemOperationContext> context = NewContext(); 212 scoped_ptr<LoggingRecursiveOperation> operation( 213 new LoggingRecursiveOperation( 214 context->file_system_context(), src_root, 215 base::Bind(&ReportStatus, &error))); 216 operation->RunRecursively(); 217 base::RunLoop().RunUntilIdle(); 218 ASSERT_EQ(base::PLATFORM_FILE_OK, error); 219 220 const std::vector<LoggingRecursiveOperation::LogEntry>& log_entries = 221 operation->log_entries(); 222 ASSERT_EQ(8U, log_entries.size()); 223 224 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE, 225 log_entries[0].type); 226 EXPECT_EQ(src_root, log_entries[0].url); 227 228 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY, 229 log_entries[1].type); 230 EXPECT_EQ(src_root, log_entries[1].url); 231 232 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE, 233 log_entries[2].type); 234 EXPECT_EQ(src_file1, log_entries[2].url); 235 236 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY, 237 log_entries[3].type); 238 EXPECT_EQ(src_dir1, log_entries[3].url); 239 240 // The order of src/dir1/file2 and src/dir1/file3 depends on the file system 241 // implementation (can be swapped). 242 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE, 243 log_entries[4].type); 244 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE, 245 log_entries[5].type); 246 EXPECT_TRUE((src_file2 == log_entries[4].url && 247 src_file3 == log_entries[5].url) || 248 (src_file3 == log_entries[4].url && 249 src_file2 == log_entries[5].url)); 250 251 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY, 252 log_entries[6].type); 253 EXPECT_EQ(src_dir1, log_entries[6].url); 254 255 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY, 256 log_entries[7].type); 257 EXPECT_EQ(src_root, log_entries[7].url); 258 } 259 260 TEST_F(RecursiveOperationDelegateTest, Cancel) { 261 FileSystemURL src_root(CreateDirectory("src")); 262 FileSystemURL src_dir1(CreateDirectory("src/dir1")); 263 FileSystemURL src_file1(CreateFile("src/file1")); 264 FileSystemURL src_file2(CreateFile("src/dir1/file2")); 265 266 base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; 267 scoped_ptr<FileSystemOperationContext> context = NewContext(); 268 scoped_ptr<LoggingRecursiveOperation> operation( 269 new LoggingRecursiveOperation( 270 context->file_system_context(), src_root, 271 base::Bind(&ReportStatus, &error))); 272 operation->RunRecursively(); 273 274 // Invoke Cancel(), after 5 times message posting. 275 CallCancelLater(operation.get(), 5); 276 base::RunLoop().RunUntilIdle(); 277 ASSERT_EQ(base::PLATFORM_FILE_ERROR_ABORT, error); 278 } 279 280 } // namespace fileapi 281