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 "webkit/browser/fileapi/file_system_dir_url_request_job.h" 6 7 #include <string> 8 9 #include "base/files/file_path.h" 10 #include "base/files/scoped_temp_dir.h" 11 #include "base/format_macros.h" 12 #include "base/memory/weak_ptr.h" 13 #include "base/message_loop/message_loop.h" 14 #include "base/platform_file.h" 15 #include "base/strings/string_piece.h" 16 #include "base/strings/utf_string_conversions.h" 17 #include "net/base/net_errors.h" 18 #include "net/base/net_util.h" 19 #include "net/http/http_request_headers.h" 20 #include "net/url_request/url_request.h" 21 #include "net/url_request/url_request_context.h" 22 #include "net/url_request/url_request_test_util.h" 23 #include "testing/gtest/include/gtest/gtest.h" 24 #include "third_party/icu/source/i18n/unicode/regex.h" 25 #include "webkit/browser/fileapi/file_system_context.h" 26 #include "webkit/browser/fileapi/file_system_file_util.h" 27 #include "webkit/browser/fileapi/file_system_operation_context.h" 28 #include "webkit/browser/fileapi/file_system_url.h" 29 #include "webkit/browser/fileapi/mock_file_system_context.h" 30 #include "webkit/browser/quota/mock_special_storage_policy.h" 31 32 namespace fileapi { 33 namespace { 34 35 // We always use the TEMPORARY FileSystem in this test. 36 static const char kFileSystemURLPrefix[] = 37 "filesystem:http://remote/temporary/"; 38 39 } // namespace 40 41 class FileSystemDirURLRequestJobTest : public testing::Test { 42 protected: 43 FileSystemDirURLRequestJobTest() 44 : message_loop_(base::MessageLoop::TYPE_IO), // simulate an IO thread 45 weak_factory_(this) { 46 } 47 48 virtual void SetUp() OVERRIDE { 49 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 50 51 special_storage_policy_ = new quota::MockSpecialStoragePolicy; 52 file_system_context_ = CreateFileSystemContextForTesting( 53 NULL, temp_dir_.path()); 54 55 file_system_context_->OpenFileSystem( 56 GURL("http://remote/"), kFileSystemTypeTemporary, 57 OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, 58 base::Bind(&FileSystemDirURLRequestJobTest::OnOpenFileSystem, 59 weak_factory_.GetWeakPtr())); 60 base::MessageLoop::current()->RunUntilIdle(); 61 62 net::URLRequest::Deprecated::RegisterProtocolFactory( 63 "filesystem", &FileSystemDirURLRequestJobFactory); 64 } 65 66 virtual void TearDown() OVERRIDE { 67 // NOTE: order matters, request must die before delegate 68 request_.reset(NULL); 69 delegate_.reset(NULL); 70 71 net::URLRequest::Deprecated::RegisterProtocolFactory("filesystem", NULL); 72 ClearUnusedJob(); 73 } 74 75 void OnOpenFileSystem(base::PlatformFileError result, 76 const std::string& name, 77 const GURL& root_url) { 78 ASSERT_EQ(base::PLATFORM_FILE_OK, result); 79 } 80 81 void TestRequestHelper(const GURL& url, bool run_to_completion) { 82 delegate_.reset(new net::TestDelegate()); 83 delegate_->set_quit_on_redirect(true); 84 request_.reset(empty_context_.CreateRequest(url, delegate_.get())); 85 job_ = new FileSystemDirURLRequestJob( 86 request_.get(), NULL, file_system_context_.get()); 87 88 request_->Start(); 89 ASSERT_TRUE(request_->is_pending()); // verify that we're starting async 90 if (run_to_completion) 91 base::MessageLoop::current()->Run(); 92 } 93 94 void TestRequest(const GURL& url) { 95 TestRequestHelper(url, true); 96 } 97 98 void TestRequestNoRun(const GURL& url) { 99 TestRequestHelper(url, false); 100 } 101 102 FileSystemURL CreateURL(const base::FilePath& file_path) { 103 return file_system_context_->CreateCrackedFileSystemURL( 104 GURL("http://remote"), 105 fileapi::kFileSystemTypeTemporary, 106 file_path); 107 } 108 109 FileSystemOperationContext* NewOperationContext() { 110 FileSystemOperationContext* context( 111 new FileSystemOperationContext(file_system_context_.get())); 112 context->set_allowed_bytes_growth(1024); 113 return context; 114 } 115 116 void CreateDirectory(const base::StringPiece& dir_name) { 117 base::FilePath path = base::FilePath().AppendASCII(dir_name); 118 scoped_ptr<FileSystemOperationContext> context(NewOperationContext()); 119 ASSERT_EQ(base::PLATFORM_FILE_OK, file_util()->CreateDirectory( 120 context.get(), 121 CreateURL(path), 122 false /* exclusive */, 123 false /* recursive */)); 124 } 125 126 void EnsureFileExists(const base::StringPiece file_name) { 127 base::FilePath path = base::FilePath().AppendASCII(file_name); 128 scoped_ptr<FileSystemOperationContext> context(NewOperationContext()); 129 ASSERT_EQ(base::PLATFORM_FILE_OK, file_util()->EnsureFileExists( 130 context.get(), CreateURL(path), NULL)); 131 } 132 133 void TruncateFile(const base::StringPiece file_name, int64 length) { 134 base::FilePath path = base::FilePath().AppendASCII(file_name); 135 scoped_ptr<FileSystemOperationContext> context(NewOperationContext()); 136 ASSERT_EQ(base::PLATFORM_FILE_OK, file_util()->Truncate( 137 context.get(), CreateURL(path), length)); 138 } 139 140 base::PlatformFileError GetFileInfo(const base::FilePath& path, 141 base::PlatformFileInfo* file_info, 142 base::FilePath* platform_file_path) { 143 scoped_ptr<FileSystemOperationContext> context(NewOperationContext()); 144 return file_util()->GetFileInfo(context.get(), 145 CreateURL(path), 146 file_info, platform_file_path); 147 } 148 149 void VerifyListingEntry(const std::string& entry_line, 150 const std::string& name, 151 const std::string& url, 152 bool is_directory, 153 int64 size) { 154 #define STR "([^\"]*)" 155 icu::UnicodeString pattern("^<script>addRow\\(\"" STR "\",\"" STR 156 "\",(0|1),\"" STR "\",\"" STR "\"\\);</script>"); 157 #undef STR 158 icu::UnicodeString input(entry_line.c_str()); 159 160 UErrorCode status = U_ZERO_ERROR; 161 icu::RegexMatcher match(pattern, input, 0, status); 162 163 EXPECT_TRUE(match.find()); 164 EXPECT_EQ(5, match.groupCount()); 165 EXPECT_EQ(icu::UnicodeString(name.c_str()), match.group(1, status)); 166 EXPECT_EQ(icu::UnicodeString(url.c_str()), match.group(2, status)); 167 EXPECT_EQ(icu::UnicodeString(is_directory ? "1" : "0"), 168 match.group(3, status)); 169 icu::UnicodeString size_string(FormatBytesUnlocalized(size).c_str()); 170 EXPECT_EQ(size_string, match.group(4, status)); 171 172 base::Time date; 173 icu::UnicodeString date_ustr(match.group(5, status)); 174 std::string date_str; 175 UTF16ToUTF8(date_ustr.getBuffer(), date_ustr.length(), &date_str); 176 EXPECT_TRUE(base::Time::FromString(date_str.c_str(), &date)); 177 EXPECT_FALSE(date.is_null()); 178 } 179 180 GURL CreateFileSystemURL(const std::string path) { 181 return GURL(kFileSystemURLPrefix + path); 182 } 183 184 static net::URLRequestJob* FileSystemDirURLRequestJobFactory( 185 net::URLRequest* request, 186 net::NetworkDelegate* network_delegate, 187 const std::string& scheme) { 188 DCHECK(job_); 189 net::URLRequestJob* temp = job_; 190 job_ = NULL; 191 return temp; 192 } 193 194 static void ClearUnusedJob() { 195 if (job_) { 196 scoped_refptr<net::URLRequestJob> deleter = job_; 197 job_ = NULL; 198 } 199 } 200 201 FileSystemFileUtil* file_util() { 202 return file_system_context_->GetFileUtil(kFileSystemTypeTemporary); 203 } 204 205 // Put the message loop at the top, so that it's the last thing deleted. 206 // Delete all MessageLoopProxy objects before the MessageLoop, to help prevent 207 // leaks caused by tasks posted during shutdown. 208 base::MessageLoop message_loop_; 209 210 base::ScopedTempDir temp_dir_; 211 net::URLRequestContext empty_context_; 212 scoped_ptr<net::TestDelegate> delegate_; 213 scoped_ptr<net::URLRequest> request_; 214 scoped_refptr<quota::MockSpecialStoragePolicy> special_storage_policy_; 215 scoped_refptr<FileSystemContext> file_system_context_; 216 base::WeakPtrFactory<FileSystemDirURLRequestJobTest> weak_factory_; 217 218 static net::URLRequestJob* job_; 219 }; 220 221 // static 222 net::URLRequestJob* FileSystemDirURLRequestJobTest::job_ = NULL; 223 224 namespace { 225 226 TEST_F(FileSystemDirURLRequestJobTest, DirectoryListing) { 227 CreateDirectory("foo"); 228 CreateDirectory("foo/bar"); 229 CreateDirectory("foo/bar/baz"); 230 231 EnsureFileExists("foo/bar/hoge"); 232 TruncateFile("foo/bar/hoge", 10); 233 234 TestRequest(CreateFileSystemURL("foo/bar/")); 235 236 ASSERT_FALSE(request_->is_pending()); 237 EXPECT_EQ(1, delegate_->response_started_count()); 238 EXPECT_FALSE(delegate_->received_data_before_response()); 239 EXPECT_GT(delegate_->bytes_received(), 0); 240 241 std::istringstream in(delegate_->data_received()); 242 std::string line; 243 EXPECT_TRUE(std::getline(in, line)); 244 245 #if defined(OS_WIN) 246 EXPECT_EQ("<script>start(\"foo\\\\bar\");</script>", line); 247 #elif defined(OS_POSIX) 248 EXPECT_EQ("<script>start(\"/foo/bar\");</script>", line); 249 #endif 250 251 EXPECT_TRUE(std::getline(in, line)); 252 VerifyListingEntry(line, "hoge", "hoge", false, 10); 253 254 EXPECT_TRUE(std::getline(in, line)); 255 VerifyListingEntry(line, "baz", "baz", true, 0); 256 } 257 258 TEST_F(FileSystemDirURLRequestJobTest, InvalidURL) { 259 TestRequest(GURL("filesystem:/foo/bar/baz")); 260 ASSERT_FALSE(request_->is_pending()); 261 EXPECT_TRUE(delegate_->request_failed()); 262 ASSERT_FALSE(request_->status().is_success()); 263 EXPECT_EQ(net::ERR_INVALID_URL, request_->status().error()); 264 } 265 266 TEST_F(FileSystemDirURLRequestJobTest, NoSuchRoot) { 267 TestRequest(GURL("filesystem:http://remote/persistent/somedir/")); 268 ASSERT_FALSE(request_->is_pending()); 269 ASSERT_FALSE(request_->status().is_success()); 270 EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error()); 271 } 272 273 TEST_F(FileSystemDirURLRequestJobTest, NoSuchDirectory) { 274 TestRequest(CreateFileSystemURL("somedir/")); 275 ASSERT_FALSE(request_->is_pending()); 276 ASSERT_FALSE(request_->status().is_success()); 277 EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error()); 278 } 279 280 TEST_F(FileSystemDirURLRequestJobTest, Cancel) { 281 CreateDirectory("foo"); 282 TestRequestNoRun(CreateFileSystemURL("foo/")); 283 // Run StartAsync() and only StartAsync(). 284 base::MessageLoop::current()->DeleteSoon(FROM_HERE, request_.release()); 285 base::MessageLoop::current()->RunUntilIdle(); 286 // If we get here, success! we didn't crash! 287 } 288 289 } // namespace (anonymous) 290 } // namespace fileapi 291