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