Home | History | Annotate | Download | only in local
      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 <string>
      6 
      7 #include "base/basictypes.h"
      8 #include "base/file_util.h"
      9 #include "base/location.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "chrome/browser/sync_file_system/local/canned_syncable_file_system.h"
     13 #include "chrome/browser/sync_file_system/local/local_file_change_tracker.h"
     14 #include "chrome/browser/sync_file_system/local/local_file_sync_context.h"
     15 #include "chrome/browser/sync_file_system/local/local_file_sync_status.h"
     16 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
     17 #include "chrome/browser/sync_file_system/local/syncable_file_operation_runner.h"
     18 #include "chrome/browser/sync_file_system/local/syncable_file_system_operation.h"
     19 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
     20 #include "testing/gtest/include/gtest/gtest.h"
     21 #include "webkit/browser/blob/mock_blob_url_request_context.h"
     22 #include "webkit/browser/fileapi/file_system_context.h"
     23 #include "webkit/browser/fileapi/file_system_operation_runner.h"
     24 
     25 using fileapi::FileSystemOperation;
     26 using fileapi::FileSystemURL;
     27 using webkit_blob::MockBlobURLRequestContext;
     28 using webkit_blob::ScopedTextBlob;
     29 using base::PlatformFileError;
     30 
     31 namespace sync_file_system {
     32 
     33 namespace {
     34 const std::string kParent = "foo";
     35 const std::string kFile   = "foo/file";
     36 const std::string kDir    = "foo/dir";
     37 const std::string kChild  = "foo/dir/bar";
     38 const std::string kOther  = "bar";
     39 }  // namespace
     40 
     41 class SyncableFileOperationRunnerTest : public testing::Test {
     42  protected:
     43   typedef FileSystemOperation::StatusCallback StatusCallback;
     44 
     45   // Use the current thread as IO thread so that we can directly call
     46   // operations in the tests.
     47   SyncableFileOperationRunnerTest()
     48       : message_loop_(base::MessageLoop::TYPE_IO),
     49         file_system_(GURL("http://example.com"),
     50                      base::MessageLoopProxy::current().get(),
     51                      base::MessageLoopProxy::current().get()),
     52         callback_count_(0),
     53         write_status_(base::PLATFORM_FILE_ERROR_FAILED),
     54         write_bytes_(0),
     55         write_complete_(false),
     56         url_request_context_(file_system_.file_system_context()),
     57         weak_factory_(this) {}
     58 
     59   virtual void SetUp() OVERRIDE {
     60     ASSERT_TRUE(dir_.CreateUniqueTempDir());
     61     file_system_.SetUp();
     62     sync_context_ =
     63         new LocalFileSyncContext(base::MessageLoopProxy::current().get(),
     64                                  base::MessageLoopProxy::current().get());
     65     ASSERT_EQ(
     66         SYNC_STATUS_OK,
     67         file_system_.MaybeInitializeFileSystemContext(sync_context_.get()));
     68 
     69     ASSERT_EQ(base::PLATFORM_FILE_OK, file_system_.OpenFileSystem());
     70     ASSERT_EQ(base::PLATFORM_FILE_OK,
     71               file_system_.CreateDirectory(URL(kParent)));
     72   }
     73 
     74   virtual void TearDown() OVERRIDE {
     75     if (sync_context_.get())
     76       sync_context_->ShutdownOnUIThread();
     77     sync_context_ = NULL;
     78 
     79     file_system_.TearDown();
     80     message_loop_.RunUntilIdle();
     81     RevokeSyncableFileSystem();
     82   }
     83 
     84   FileSystemURL URL(const std::string& path) {
     85     return file_system_.URL(path);
     86   }
     87 
     88   LocalFileSyncStatus* sync_status() {
     89     return file_system_.backend()->sync_context()->sync_status();
     90   }
     91 
     92   void ResetCallbackStatus() {
     93     write_status_ = base::PLATFORM_FILE_ERROR_FAILED;
     94     write_bytes_ = 0;
     95     write_complete_ = false;
     96     callback_count_ = 0;
     97   }
     98 
     99   StatusCallback ExpectStatus(const tracked_objects::Location& location,
    100                               PlatformFileError expect) {
    101     return base::Bind(&SyncableFileOperationRunnerTest::DidFinish,
    102                       weak_factory_.GetWeakPtr(), location, expect);
    103   }
    104 
    105   FileSystemOperation::WriteCallback GetWriteCallback(
    106       const tracked_objects::Location& location) {
    107     return base::Bind(&SyncableFileOperationRunnerTest::DidWrite,
    108                       weak_factory_.GetWeakPtr(), location);
    109   }
    110 
    111   void DidWrite(const tracked_objects::Location& location,
    112                 PlatformFileError status, int64 bytes, bool complete) {
    113     SCOPED_TRACE(testing::Message() << location.ToString());
    114     write_status_ = status;
    115     write_bytes_ += bytes;
    116     write_complete_ = complete;
    117     ++callback_count_;
    118   }
    119 
    120   void DidFinish(const tracked_objects::Location& location,
    121                  PlatformFileError expect, PlatformFileError status) {
    122     SCOPED_TRACE(testing::Message() << location.ToString());
    123     EXPECT_EQ(expect, status);
    124     ++callback_count_;
    125   }
    126 
    127   bool CreateTempFile(base::FilePath* path) {
    128     return file_util::CreateTemporaryFileInDir(dir_.path(), path);
    129   }
    130 
    131   ScopedEnableSyncFSDirectoryOperation enable_directory_operation_;
    132   base::ScopedTempDir dir_;
    133 
    134   base::MessageLoop message_loop_;
    135   CannedSyncableFileSystem file_system_;
    136   scoped_refptr<LocalFileSyncContext> sync_context_;
    137 
    138   int callback_count_;
    139   PlatformFileError write_status_;
    140   size_t write_bytes_;
    141   bool write_complete_;
    142 
    143   MockBlobURLRequestContext url_request_context_;
    144 
    145   base::WeakPtrFactory<SyncableFileOperationRunnerTest> weak_factory_;
    146 
    147   DISALLOW_COPY_AND_ASSIGN(SyncableFileOperationRunnerTest);
    148 };
    149 
    150 TEST_F(SyncableFileOperationRunnerTest, SimpleQueue) {
    151   sync_status()->StartSyncing(URL(kFile));
    152   ASSERT_FALSE(sync_status()->IsWritable(URL(kFile)));
    153 
    154   // The URL is in syncing so the write operations won't run.
    155   ResetCallbackStatus();
    156   file_system_.operation_runner()->CreateFile(
    157       URL(kFile), false /* exclusive */,
    158       ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
    159   file_system_.operation_runner()->Truncate(
    160       URL(kFile), 1,
    161       ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
    162   base::MessageLoop::current()->RunUntilIdle();
    163   EXPECT_EQ(0, callback_count_);
    164 
    165   // Read operations are not blocked (and are executed before queued ones).
    166   file_system_.operation_runner()->FileExists(
    167       URL(kFile), ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_NOT_FOUND));
    168   base::MessageLoop::current()->RunUntilIdle();
    169   EXPECT_EQ(1, callback_count_);
    170 
    171   // End syncing (to enable write).
    172   sync_status()->EndSyncing(URL(kFile));
    173   ASSERT_TRUE(sync_status()->IsWritable(URL(kFile)));
    174 
    175   ResetCallbackStatus();
    176   base::MessageLoop::current()->RunUntilIdle();
    177   EXPECT_EQ(2, callback_count_);
    178 
    179   // Now the file must have been created and updated.
    180   ResetCallbackStatus();
    181   file_system_.operation_runner()->FileExists(
    182       URL(kFile), ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
    183   base::MessageLoop::current()->RunUntilIdle();
    184   EXPECT_EQ(1, callback_count_);
    185 }
    186 
    187 TEST_F(SyncableFileOperationRunnerTest, WriteToParentAndChild) {
    188   // First create the kDir directory and kChild in the dir.
    189   EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateDirectory(URL(kDir)));
    190   EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateFile(URL(kChild)));
    191 
    192   // Start syncing the kDir directory.
    193   sync_status()->StartSyncing(URL(kDir));
    194   ASSERT_FALSE(sync_status()->IsWritable(URL(kDir)));
    195 
    196   // Writes to kParent and kChild should be all queued up.
    197   ResetCallbackStatus();
    198   file_system_.operation_runner()->Truncate(
    199       URL(kChild), 1, ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
    200   file_system_.operation_runner()->Remove(
    201       URL(kParent), true /* recursive */,
    202       ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
    203   base::MessageLoop::current()->RunUntilIdle();
    204   EXPECT_EQ(0, callback_count_);
    205 
    206   // Read operations are not blocked (and are executed before queued ones).
    207   file_system_.operation_runner()->DirectoryExists(
    208       URL(kDir), ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
    209   base::MessageLoop::current()->RunUntilIdle();
    210   EXPECT_EQ(1, callback_count_);
    211 
    212   // Writes to unrelated files must succeed as well.
    213   ResetCallbackStatus();
    214   file_system_.operation_runner()->CreateDirectory(
    215       URL(kOther), false /* exclusive */, false /* recursive */,
    216       ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
    217   base::MessageLoop::current()->RunUntilIdle();
    218   EXPECT_EQ(1, callback_count_);
    219 
    220   // End syncing (to enable write).
    221   sync_status()->EndSyncing(URL(kDir));
    222   ASSERT_TRUE(sync_status()->IsWritable(URL(kDir)));
    223 
    224   ResetCallbackStatus();
    225   base::MessageLoop::current()->RunUntilIdle();
    226   EXPECT_EQ(2, callback_count_);
    227 }
    228 
    229 TEST_F(SyncableFileOperationRunnerTest, CopyAndMove) {
    230   // First create the kDir directory and kChild in the dir.
    231   EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateDirectory(URL(kDir)));
    232   EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateFile(URL(kChild)));
    233 
    234   // Start syncing the kParent directory.
    235   sync_status()->StartSyncing(URL(kParent));
    236 
    237   // Copying kDir to other directory should succeed, while moving would fail
    238   // (since the source directory is in syncing).
    239   ResetCallbackStatus();
    240   file_system_.operation_runner()->Copy(
    241       URL(kDir), URL("dest-copy"),
    242       ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
    243   file_system_.operation_runner()->Move(
    244       URL(kDir), URL("dest-move"),
    245       ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
    246   base::MessageLoop::current()->RunUntilIdle();
    247   EXPECT_EQ(1, callback_count_);
    248 
    249   // Only "dest-copy1" should exist.
    250   EXPECT_EQ(base::PLATFORM_FILE_OK,
    251             file_system_.DirectoryExists(URL("dest-copy")));
    252   EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
    253             file_system_.DirectoryExists(URL("dest-move")));
    254 
    255   // Start syncing the "dest-copy2" directory.
    256   sync_status()->StartSyncing(URL("dest-copy2"));
    257 
    258   // Now the destination is also locked copying kDir should be queued.
    259   ResetCallbackStatus();
    260   file_system_.operation_runner()->Copy(
    261       URL(kDir), URL("dest-copy2"),
    262       ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
    263   base::MessageLoop::current()->RunUntilIdle();
    264   EXPECT_EQ(0, callback_count_);
    265 
    266   // Finish syncing the "dest-copy2" directory to unlock Copy.
    267   sync_status()->EndSyncing(URL("dest-copy2"));
    268   ResetCallbackStatus();
    269   base::MessageLoop::current()->RunUntilIdle();
    270   EXPECT_EQ(1, callback_count_);
    271 
    272   // Now we should have "dest-copy2".
    273   EXPECT_EQ(base::PLATFORM_FILE_OK,
    274             file_system_.DirectoryExists(URL("dest-copy2")));
    275 
    276   // Finish syncing the kParent to unlock Move.
    277   sync_status()->EndSyncing(URL(kParent));
    278   ResetCallbackStatus();
    279   base::MessageLoop::current()->RunUntilIdle();
    280   EXPECT_EQ(1, callback_count_);
    281 
    282   // Now we should have "dest-move".
    283   EXPECT_EQ(base::PLATFORM_FILE_OK,
    284             file_system_.DirectoryExists(URL("dest-move")));
    285 }
    286 
    287 TEST_F(SyncableFileOperationRunnerTest, Write) {
    288   EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateFile(URL(kFile)));
    289   const GURL kBlobURL("blob:foo");
    290   const std::string kData("Lorem ipsum.");
    291   ScopedTextBlob blob(url_request_context_, kBlobURL, kData);
    292 
    293   sync_status()->StartSyncing(URL(kFile));
    294 
    295   ResetCallbackStatus();
    296   file_system_.operation_runner()->Write(
    297       &url_request_context_,
    298       URL(kFile), kBlobURL, 0, GetWriteCallback(FROM_HERE));
    299   base::MessageLoop::current()->RunUntilIdle();
    300   EXPECT_EQ(0, callback_count_);
    301 
    302   sync_status()->EndSyncing(URL(kFile));
    303   ResetCallbackStatus();
    304 
    305   while (!write_complete_)
    306     base::MessageLoop::current()->RunUntilIdle();
    307 
    308   EXPECT_EQ(base::PLATFORM_FILE_OK, write_status_);
    309   EXPECT_EQ(kData.size(), write_bytes_);
    310   EXPECT_TRUE(write_complete_);
    311 }
    312 
    313 TEST_F(SyncableFileOperationRunnerTest, QueueAndCancel) {
    314   sync_status()->StartSyncing(URL(kFile));
    315   ASSERT_FALSE(sync_status()->IsWritable(URL(kFile)));
    316 
    317   ResetCallbackStatus();
    318   file_system_.operation_runner()->CreateFile(
    319       URL(kFile), false /* exclusive */,
    320       ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_ABORT));
    321   file_system_.operation_runner()->Truncate(
    322       URL(kFile), 1,
    323       ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_ABORT));
    324   base::MessageLoop::current()->RunUntilIdle();
    325   EXPECT_EQ(0, callback_count_);
    326 
    327   ResetCallbackStatus();
    328 
    329   // This shouldn't crash nor leak memory.
    330   sync_context_->ShutdownOnUIThread();
    331   sync_context_ = NULL;
    332   base::MessageLoop::current()->RunUntilIdle();
    333   EXPECT_EQ(2, callback_count_);
    334 }
    335 
    336 // Test if CopyInForeignFile runs cooperatively with other Sync operations
    337 // when it is called directly via AsFileSystemOperationImpl.
    338 TEST_F(SyncableFileOperationRunnerTest, CopyInForeignFile) {
    339   const std::string kTestData("test data");
    340 
    341   base::FilePath temp_path;
    342   ASSERT_TRUE(CreateTempFile(&temp_path));
    343   ASSERT_EQ(static_cast<int>(kTestData.size()),
    344             file_util::WriteFile(
    345                 temp_path, kTestData.data(), kTestData.size()));
    346 
    347   sync_status()->StartSyncing(URL(kFile));
    348   ASSERT_FALSE(sync_status()->IsWritable(URL(kFile)));
    349 
    350   // The URL is in syncing so CopyIn (which is a write operation) won't run.
    351   ResetCallbackStatus();
    352   file_system_.operation_runner()->CopyInForeignFile(
    353       temp_path, URL(kFile),
    354       ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
    355   base::MessageLoop::current()->RunUntilIdle();
    356   EXPECT_EQ(0, callback_count_);
    357 
    358   // End syncing (to enable write).
    359   sync_status()->EndSyncing(URL(kFile));
    360   ASSERT_TRUE(sync_status()->IsWritable(URL(kFile)));
    361 
    362   ResetCallbackStatus();
    363   base::MessageLoop::current()->RunUntilIdle();
    364   EXPECT_EQ(1, callback_count_);
    365 
    366   // Now the file must have been created and have the same content as temp_path.
    367   ResetCallbackStatus();
    368   file_system_.DoVerifyFile(
    369       URL(kFile), kTestData,
    370       ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
    371   base::MessageLoop::current()->RunUntilIdle();
    372   EXPECT_EQ(1, callback_count_);
    373 }
    374 
    375 TEST_F(SyncableFileOperationRunnerTest, Cancel) {
    376   // Prepare a file.
    377   file_system_.operation_runner()->CreateFile(
    378       URL(kFile), false /* exclusive */,
    379       ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
    380   base::MessageLoop::current()->RunUntilIdle();
    381   EXPECT_EQ(1, callback_count_);
    382 
    383   // Run Truncate and immediately cancel. This shouldn't crash.
    384   ResetCallbackStatus();
    385   fileapi::FileSystemOperationRunner::OperationID id =
    386       file_system_.operation_runner()->Truncate(
    387           URL(kFile), 10,
    388           ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_ABORT));
    389   file_system_.operation_runner()->Cancel(
    390       id, ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
    391   base::MessageLoop::current()->RunUntilIdle();
    392   EXPECT_EQ(2, callback_count_);
    393 }
    394 
    395 }  // namespace sync_file_system
    396