Home | History | Annotate | Download | only in fileapi
      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