| OLD | NEW | 
 | (Empty) | 
|    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 <stddef.h> |  | 
|    6 #include <stdint.h> |  | 
|    7 #include <map> |  | 
|    8 #include <queue> |  | 
|    9 #include <utility> |  | 
|   10  |  | 
|   11 #include "base/bind.h" |  | 
|   12 #include "base/files/file_util.h" |  | 
|   13 #include "base/files/scoped_temp_dir.h" |  | 
|   14 #include "base/location.h" |  | 
|   15 #include "base/macros.h" |  | 
|   16 #include "base/run_loop.h" |  | 
|   17 #include "base/single_thread_task_runner.h" |  | 
|   18 #include "base/stl_util.h" |  | 
|   19 #include "base/threading/thread_task_runner_handle.h" |  | 
|   20 #include "content/browser/quota/mock_quota_manager.h" |  | 
|   21 #include "content/browser/quota/mock_quota_manager_proxy.h" |  | 
|   22 #include "content/public/test/async_file_test_helper.h" |  | 
|   23 #include "content/public/test/test_file_system_backend.h" |  | 
|   24 #include "content/public/test/test_file_system_context.h" |  | 
|   25 #include "content/test/fileapi_test_file_set.h" |  | 
|   26 #include "storage/browser/fileapi/copy_or_move_file_validator.h" |  | 
|   27 #include "storage/browser/fileapi/copy_or_move_operation_delegate.h" |  | 
|   28 #include "storage/browser/fileapi/file_stream_reader.h" |  | 
|   29 #include "storage/browser/fileapi/file_stream_writer.h" |  | 
|   30 #include "storage/browser/fileapi/file_system_backend.h" |  | 
|   31 #include "storage/browser/fileapi/file_system_context.h" |  | 
|   32 #include "storage/browser/fileapi/file_system_operation.h" |  | 
|   33 #include "storage/browser/fileapi/file_system_url.h" |  | 
|   34 #include "storage/browser/quota/quota_manager.h" |  | 
|   35 #include "storage/common/fileapi/file_system_mount_option.h" |  | 
|   36 #include "storage/common/fileapi/file_system_util.h" |  | 
|   37 #include "testing/gtest/include/gtest/gtest.h" |  | 
|   38  |  | 
|   39 using content::AsyncFileTestHelper; |  | 
|   40 using storage::CopyOrMoveOperationDelegate; |  | 
|   41 using storage::FileStreamWriter; |  | 
|   42 using storage::FileSystemOperation; |  | 
|   43 using storage::FileSystemURL; |  | 
|   44  |  | 
|   45 namespace content { |  | 
|   46  |  | 
|   47 typedef storage::FileSystemOperation::FileEntryList FileEntryList; |  | 
|   48  |  | 
|   49 namespace { |  | 
|   50  |  | 
|   51 void ExpectOk(const GURL& origin_url, |  | 
|   52               const std::string& name, |  | 
|   53               base::File::Error error) { |  | 
|   54   ASSERT_EQ(base::File::FILE_OK, error); |  | 
|   55 } |  | 
|   56  |  | 
|   57 class TestValidatorFactory : public storage::CopyOrMoveFileValidatorFactory { |  | 
|   58  public: |  | 
|   59   // A factory that creates validators that accept everything or nothing. |  | 
|   60   TestValidatorFactory() {} |  | 
|   61   ~TestValidatorFactory() override {} |  | 
|   62  |  | 
|   63   storage::CopyOrMoveFileValidator* CreateCopyOrMoveFileValidator( |  | 
|   64       const FileSystemURL& /*src_url*/, |  | 
|   65       const base::FilePath& /*platform_path*/) override { |  | 
|   66     // Move arg management to TestValidator? |  | 
|   67     return new TestValidator(true, true, std::string("2")); |  | 
|   68   } |  | 
|   69  |  | 
|   70  private: |  | 
|   71   class TestValidator : public storage::CopyOrMoveFileValidator { |  | 
|   72    public: |  | 
|   73     explicit TestValidator(bool pre_copy_valid, |  | 
|   74                            bool post_copy_valid, |  | 
|   75                            const std::string& reject_string) |  | 
|   76         : result_(pre_copy_valid ? base::File::FILE_OK : |  | 
|   77                                    base::File::FILE_ERROR_SECURITY), |  | 
|   78           write_result_(post_copy_valid ? base::File::FILE_OK : |  | 
|   79                                           base::File::FILE_ERROR_SECURITY), |  | 
|   80           reject_string_(reject_string) { |  | 
|   81     } |  | 
|   82     ~TestValidator() override {} |  | 
|   83  |  | 
|   84     void StartPreWriteValidation( |  | 
|   85         const ResultCallback& result_callback) override { |  | 
|   86       // Post the result since a real validator must do work asynchronously. |  | 
|   87       base::ThreadTaskRunnerHandle::Get()->PostTask( |  | 
|   88           FROM_HERE, base::Bind(result_callback, result_)); |  | 
|   89     } |  | 
|   90  |  | 
|   91     void StartPostWriteValidation( |  | 
|   92         const base::FilePath& dest_platform_path, |  | 
|   93         const ResultCallback& result_callback) override { |  | 
|   94       base::File::Error result = write_result_; |  | 
|   95       std::string unsafe = dest_platform_path.BaseName().AsUTF8Unsafe(); |  | 
|   96       if (unsafe.find(reject_string_) != std::string::npos) { |  | 
|   97         result = base::File::FILE_ERROR_SECURITY; |  | 
|   98       } |  | 
|   99       // Post the result since a real validator must do work asynchronously. |  | 
|  100       base::ThreadTaskRunnerHandle::Get()->PostTask( |  | 
|  101           FROM_HERE, base::Bind(result_callback, result)); |  | 
|  102     } |  | 
|  103  |  | 
|  104    private: |  | 
|  105     base::File::Error result_; |  | 
|  106     base::File::Error write_result_; |  | 
|  107     std::string reject_string_; |  | 
|  108  |  | 
|  109     DISALLOW_COPY_AND_ASSIGN(TestValidator); |  | 
|  110   }; |  | 
|  111 }; |  | 
|  112  |  | 
|  113 // Records CopyProgressCallback invocations. |  | 
|  114 struct ProgressRecord { |  | 
|  115   storage::FileSystemOperation::CopyProgressType type; |  | 
|  116   FileSystemURL source_url; |  | 
|  117   FileSystemURL dest_url; |  | 
|  118   int64_t size; |  | 
|  119 }; |  | 
|  120  |  | 
|  121 void RecordProgressCallback(std::vector<ProgressRecord>* records, |  | 
|  122                             storage::FileSystemOperation::CopyProgressType type, |  | 
|  123                             const FileSystemURL& source_url, |  | 
|  124                             const FileSystemURL& dest_url, |  | 
|  125                             int64_t size) { |  | 
|  126   ProgressRecord record; |  | 
|  127   record.type = type; |  | 
|  128   record.source_url = source_url; |  | 
|  129   record.dest_url = dest_url; |  | 
|  130   record.size = size; |  | 
|  131   records->push_back(record); |  | 
|  132 } |  | 
|  133  |  | 
|  134 void RecordFileProgressCallback(std::vector<int64_t>* records, |  | 
|  135                                 int64_t progress) { |  | 
|  136   records->push_back(progress); |  | 
|  137 } |  | 
|  138  |  | 
|  139 void AssignAndQuit(base::RunLoop* run_loop, |  | 
|  140                    base::File::Error* result_out, |  | 
|  141                    base::File::Error result) { |  | 
|  142   *result_out = result; |  | 
|  143   run_loop->Quit(); |  | 
|  144 } |  | 
|  145  |  | 
|  146 class ScopedThreadStopper { |  | 
|  147  public: |  | 
|  148   ScopedThreadStopper(base::Thread* thread) : thread_(thread) { |  | 
|  149   } |  | 
|  150  |  | 
|  151   ~ScopedThreadStopper() { |  | 
|  152     if (thread_) { |  | 
|  153       // Give another chance for deleted streams to perform Close. |  | 
|  154       base::RunLoop run_loop; |  | 
|  155       thread_->task_runner()->PostTaskAndReply( |  | 
|  156           FROM_HERE, base::Bind(&base::DoNothing), run_loop.QuitClosure()); |  | 
|  157       run_loop.Run(); |  | 
|  158       thread_->Stop(); |  | 
|  159     } |  | 
|  160   } |  | 
|  161  |  | 
|  162   bool is_valid() const { return thread_; } |  | 
|  163  |  | 
|  164  private: |  | 
|  165   base::Thread* thread_; |  | 
|  166   DISALLOW_COPY_AND_ASSIGN(ScopedThreadStopper); |  | 
|  167 }; |  | 
|  168  |  | 
|  169 }  // namespace |  | 
|  170  |  | 
|  171 class CopyOrMoveOperationTestHelper { |  | 
|  172  public: |  | 
|  173   CopyOrMoveOperationTestHelper(const GURL& origin, |  | 
|  174                                 storage::FileSystemType src_type, |  | 
|  175                                 storage::FileSystemType dest_type) |  | 
|  176       : origin_(origin), src_type_(src_type), dest_type_(dest_type) {} |  | 
|  177  |  | 
|  178   ~CopyOrMoveOperationTestHelper() { |  | 
|  179     file_system_context_ = NULL; |  | 
|  180     quota_manager_proxy_->SimulateQuotaManagerDestroyed(); |  | 
|  181     quota_manager_ = NULL; |  | 
|  182     quota_manager_proxy_ = NULL; |  | 
|  183     base::RunLoop().RunUntilIdle(); |  | 
|  184   } |  | 
|  185  |  | 
|  186   void SetUp() { |  | 
|  187     SetUp(true, true); |  | 
|  188   } |  | 
|  189  |  | 
|  190   void SetUpNoValidator() { |  | 
|  191     SetUp(true, false); |  | 
|  192   } |  | 
|  193  |  | 
|  194   void SetUp(bool require_copy_or_move_validator, |  | 
|  195              bool init_copy_or_move_validator) { |  | 
|  196     ASSERT_TRUE(base_.CreateUniqueTempDir()); |  | 
|  197     base::FilePath base_dir = base_.GetPath(); |  | 
|  198     quota_manager_ = |  | 
|  199         new MockQuotaManager(false /* is_incognito */, base_dir, |  | 
|  200                              base::ThreadTaskRunnerHandle::Get().get(), |  | 
|  201                              base::ThreadTaskRunnerHandle::Get().get(), |  | 
|  202                              NULL /* special storage policy */); |  | 
|  203     quota_manager_proxy_ = new MockQuotaManagerProxy( |  | 
|  204         quota_manager_.get(), base::ThreadTaskRunnerHandle::Get().get()); |  | 
|  205     file_system_context_ = |  | 
|  206         CreateFileSystemContextForTesting(quota_manager_proxy_.get(), base_dir); |  | 
|  207  |  | 
|  208     // Prepare the origin's root directory. |  | 
|  209     storage::FileSystemBackend* backend = |  | 
|  210         file_system_context_->GetFileSystemBackend(src_type_); |  | 
|  211     backend->ResolveURL( |  | 
|  212         FileSystemURL::CreateForTest(origin_, src_type_, base::FilePath()), |  | 
|  213         storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, |  | 
|  214         base::Bind(&ExpectOk)); |  | 
|  215     backend = file_system_context_->GetFileSystemBackend(dest_type_); |  | 
|  216     if (dest_type_ == storage::kFileSystemTypeTest) { |  | 
|  217       TestFileSystemBackend* test_backend = |  | 
|  218           static_cast<TestFileSystemBackend*>(backend); |  | 
|  219       std::unique_ptr<storage::CopyOrMoveFileValidatorFactory> factory( |  | 
|  220           new TestValidatorFactory); |  | 
|  221       test_backend->set_require_copy_or_move_validator( |  | 
|  222           require_copy_or_move_validator); |  | 
|  223       if (init_copy_or_move_validator) |  | 
|  224         test_backend->InitializeCopyOrMoveFileValidatorFactory( |  | 
|  225             std::move(factory)); |  | 
|  226     } |  | 
|  227     backend->ResolveURL( |  | 
|  228         FileSystemURL::CreateForTest(origin_, dest_type_, base::FilePath()), |  | 
|  229         storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, |  | 
|  230         base::Bind(&ExpectOk)); |  | 
|  231     base::RunLoop().RunUntilIdle(); |  | 
|  232  |  | 
|  233     // Grant relatively big quota initially. |  | 
|  234     quota_manager_->SetQuota( |  | 
|  235         origin_, |  | 
|  236         storage::FileSystemTypeToQuotaStorageType(src_type_), |  | 
|  237         1024 * 1024); |  | 
|  238     quota_manager_->SetQuota( |  | 
|  239         origin_, |  | 
|  240         storage::FileSystemTypeToQuotaStorageType(dest_type_), |  | 
|  241         1024 * 1024); |  | 
|  242   } |  | 
|  243  |  | 
|  244   int64_t GetSourceUsage() { |  | 
|  245     int64_t usage = 0; |  | 
|  246     GetUsageAndQuota(src_type_, &usage, NULL); |  | 
|  247     return usage; |  | 
|  248   } |  | 
|  249  |  | 
|  250   int64_t GetDestUsage() { |  | 
|  251     int64_t usage = 0; |  | 
|  252     GetUsageAndQuota(dest_type_, &usage, NULL); |  | 
|  253     return usage; |  | 
|  254   } |  | 
|  255  |  | 
|  256   FileSystemURL SourceURL(const std::string& path) { |  | 
|  257     return file_system_context_->CreateCrackedFileSystemURL( |  | 
|  258         origin_, src_type_, base::FilePath::FromUTF8Unsafe(path)); |  | 
|  259   } |  | 
|  260  |  | 
|  261   FileSystemURL DestURL(const std::string& path) { |  | 
|  262     return file_system_context_->CreateCrackedFileSystemURL( |  | 
|  263         origin_, dest_type_, base::FilePath::FromUTF8Unsafe(path)); |  | 
|  264   } |  | 
|  265  |  | 
|  266   base::File::Error Copy(const FileSystemURL& src, |  | 
|  267                          const FileSystemURL& dest) { |  | 
|  268     return AsyncFileTestHelper::Copy(file_system_context_.get(), src, dest); |  | 
|  269   } |  | 
|  270  |  | 
|  271   base::File::Error CopyWithProgress( |  | 
|  272       const FileSystemURL& src, |  | 
|  273       const FileSystemURL& dest, |  | 
|  274       const AsyncFileTestHelper::CopyProgressCallback& progress_callback) { |  | 
|  275     return AsyncFileTestHelper::CopyWithProgress( |  | 
|  276         file_system_context_.get(), src, dest, progress_callback); |  | 
|  277   } |  | 
|  278  |  | 
|  279   base::File::Error Move(const FileSystemURL& src, |  | 
|  280                          const FileSystemURL& dest) { |  | 
|  281     return AsyncFileTestHelper::Move(file_system_context_.get(), src, dest); |  | 
|  282   } |  | 
|  283  |  | 
|  284   base::File::Error SetUpTestCaseFiles( |  | 
|  285       const FileSystemURL& root, |  | 
|  286       const FileSystemTestCaseRecord* const test_cases, |  | 
|  287       size_t test_case_size) { |  | 
|  288     base::File::Error result = base::File::FILE_ERROR_FAILED; |  | 
|  289     for (size_t i = 0; i < test_case_size; ++i) { |  | 
|  290       const FileSystemTestCaseRecord& test_case = test_cases[i]; |  | 
|  291       FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL( |  | 
|  292           root.origin(), |  | 
|  293           root.mount_type(), |  | 
|  294           root.virtual_path().Append(test_case.path)); |  | 
|  295       if (test_case.is_directory) |  | 
|  296         result = CreateDirectory(url); |  | 
|  297       else |  | 
|  298         result = CreateFile(url, test_case.data_file_size); |  | 
|  299       EXPECT_EQ(base::File::FILE_OK, result) << url.DebugString(); |  | 
|  300       if (result != base::File::FILE_OK) |  | 
|  301         return result; |  | 
|  302     } |  | 
|  303     return result; |  | 
|  304   } |  | 
|  305  |  | 
|  306   void VerifyTestCaseFiles( |  | 
|  307       const FileSystemURL& root, |  | 
|  308       const FileSystemTestCaseRecord* const test_cases, |  | 
|  309       size_t test_case_size) { |  | 
|  310     std::map<base::FilePath, const FileSystemTestCaseRecord*> test_case_map; |  | 
|  311     for (size_t i = 0; i < test_case_size; ++i) { |  | 
|  312       test_case_map[ |  | 
|  313           base::FilePath(test_cases[i].path).NormalizePathSeparators()] = |  | 
|  314               &test_cases[i]; |  | 
|  315     } |  | 
|  316  |  | 
|  317     std::queue<FileSystemURL> directories; |  | 
|  318     FileEntryList entries; |  | 
|  319     directories.push(root); |  | 
|  320     while (!directories.empty()) { |  | 
|  321       FileSystemURL dir = directories.front(); |  | 
|  322       directories.pop(); |  | 
|  323       ASSERT_EQ(base::File::FILE_OK, ReadDirectory(dir, &entries)); |  | 
|  324       for (size_t i = 0; i < entries.size(); ++i) { |  | 
|  325         FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL( |  | 
|  326             dir.origin(), |  | 
|  327             dir.mount_type(), |  | 
|  328             dir.virtual_path().Append(entries[i].name)); |  | 
|  329         base::FilePath relative; |  | 
|  330         root.virtual_path().AppendRelativePath(url.virtual_path(), &relative); |  | 
|  331         relative = relative.NormalizePathSeparators(); |  | 
|  332         ASSERT_TRUE(base::ContainsKey(test_case_map, relative)); |  | 
|  333         if (entries[i].is_directory) { |  | 
|  334           EXPECT_TRUE(test_case_map[relative]->is_directory); |  | 
|  335           directories.push(url); |  | 
|  336         } else { |  | 
|  337           EXPECT_FALSE(test_case_map[relative]->is_directory); |  | 
|  338           EXPECT_TRUE(FileExists(url, test_case_map[relative]->data_file_size)); |  | 
|  339         } |  | 
|  340         test_case_map.erase(relative); |  | 
|  341       } |  | 
|  342     } |  | 
|  343     EXPECT_TRUE(test_case_map.empty()); |  | 
|  344     std::map<base::FilePath, |  | 
|  345         const FileSystemTestCaseRecord*>::const_iterator it; |  | 
|  346     for (it = test_case_map.begin(); it != test_case_map.end(); ++it) { |  | 
|  347       LOG(ERROR) << "Extra entry: " << it->first.LossyDisplayName(); |  | 
|  348     } |  | 
|  349   } |  | 
|  350  |  | 
|  351   base::File::Error ReadDirectory(const FileSystemURL& url, |  | 
|  352                                   FileEntryList* entries) { |  | 
|  353     return AsyncFileTestHelper::ReadDirectory( |  | 
|  354         file_system_context_.get(), url, entries); |  | 
|  355   } |  | 
|  356  |  | 
|  357   base::File::Error CreateDirectory(const FileSystemURL& url) { |  | 
|  358     return AsyncFileTestHelper::CreateDirectory(file_system_context_.get(), |  | 
|  359                                                 url); |  | 
|  360   } |  | 
|  361  |  | 
|  362   base::File::Error CreateFile(const FileSystemURL& url, size_t size) { |  | 
|  363     base::File::Error result = |  | 
|  364         AsyncFileTestHelper::CreateFile(file_system_context_.get(), url); |  | 
|  365     if (result != base::File::FILE_OK) |  | 
|  366       return result; |  | 
|  367     return AsyncFileTestHelper::TruncateFile( |  | 
|  368         file_system_context_.get(), url, size); |  | 
|  369   } |  | 
|  370  |  | 
|  371   bool FileExists(const FileSystemURL& url, int64_t expected_size) { |  | 
|  372     return AsyncFileTestHelper::FileExists( |  | 
|  373         file_system_context_.get(), url, expected_size); |  | 
|  374   } |  | 
|  375  |  | 
|  376   bool DirectoryExists(const FileSystemURL& url) { |  | 
|  377     return AsyncFileTestHelper::DirectoryExists(file_system_context_.get(), |  | 
|  378                                                 url); |  | 
|  379   } |  | 
|  380  |  | 
|  381  private: |  | 
|  382   void GetUsageAndQuota(storage::FileSystemType type, |  | 
|  383                         int64_t* usage, |  | 
|  384                         int64_t* quota) { |  | 
|  385     storage::QuotaStatusCode status = AsyncFileTestHelper::GetUsageAndQuota( |  | 
|  386         quota_manager_.get(), origin_, type, usage, quota); |  | 
|  387     ASSERT_EQ(storage::kQuotaStatusOk, status); |  | 
|  388   } |  | 
|  389  |  | 
|  390  private: |  | 
|  391   base::ScopedTempDir base_; |  | 
|  392  |  | 
|  393   const GURL origin_; |  | 
|  394   const storage::FileSystemType src_type_; |  | 
|  395   const storage::FileSystemType dest_type_; |  | 
|  396  |  | 
|  397   base::MessageLoopForIO message_loop_; |  | 
|  398   scoped_refptr<storage::FileSystemContext> file_system_context_; |  | 
|  399   scoped_refptr<MockQuotaManagerProxy> quota_manager_proxy_; |  | 
|  400   scoped_refptr<MockQuotaManager> quota_manager_; |  | 
|  401  |  | 
|  402   DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOperationTestHelper); |  | 
|  403 }; |  | 
|  404  |  | 
|  405 TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFile) { |  | 
|  406   CopyOrMoveOperationTestHelper helper(GURL("http://foo"), |  | 
|  407                                        storage::kFileSystemTypeTemporary, |  | 
|  408                                        storage::kFileSystemTypePersistent); |  | 
|  409   helper.SetUp(); |  | 
|  410  |  | 
|  411   FileSystemURL src = helper.SourceURL("a"); |  | 
|  412   FileSystemURL dest = helper.DestURL("b"); |  | 
|  413   int64_t src_initial_usage = helper.GetSourceUsage(); |  | 
|  414   int64_t dest_initial_usage = helper.GetDestUsage(); |  | 
|  415  |  | 
|  416   // Set up a source file. |  | 
|  417   ASSERT_EQ(base::File::FILE_OK, helper.CreateFile(src, 10)); |  | 
|  418   int64_t src_increase = helper.GetSourceUsage() - src_initial_usage; |  | 
|  419  |  | 
|  420   // Copy it. |  | 
|  421   ASSERT_EQ(base::File::FILE_OK, helper.Copy(src, dest)); |  | 
|  422  |  | 
|  423   // Verify. |  | 
|  424   ASSERT_TRUE(helper.FileExists(src, 10)); |  | 
|  425   ASSERT_TRUE(helper.FileExists(dest, 10)); |  | 
|  426  |  | 
|  427   int64_t src_new_usage = helper.GetSourceUsage(); |  | 
|  428   ASSERT_EQ(src_initial_usage + src_increase, src_new_usage); |  | 
|  429  |  | 
|  430   int64_t dest_increase = helper.GetDestUsage() - dest_initial_usage; |  | 
|  431   ASSERT_EQ(src_increase, dest_increase); |  | 
|  432 } |  | 
|  433  |  | 
|  434 TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleFile) { |  | 
|  435   CopyOrMoveOperationTestHelper helper(GURL("http://foo"), |  | 
|  436                                        storage::kFileSystemTypeTemporary, |  | 
|  437                                        storage::kFileSystemTypePersistent); |  | 
|  438   helper.SetUp(); |  | 
|  439  |  | 
|  440   FileSystemURL src = helper.SourceURL("a"); |  | 
|  441   FileSystemURL dest = helper.DestURL("b"); |  | 
|  442   int64_t src_initial_usage = helper.GetSourceUsage(); |  | 
|  443   int64_t dest_initial_usage = helper.GetDestUsage(); |  | 
|  444  |  | 
|  445   // Set up a source file. |  | 
|  446   ASSERT_EQ(base::File::FILE_OK, helper.CreateFile(src, 10)); |  | 
|  447   int64_t src_increase = helper.GetSourceUsage() - src_initial_usage; |  | 
|  448  |  | 
|  449   // Move it. |  | 
|  450   ASSERT_EQ(base::File::FILE_OK, helper.Move(src, dest)); |  | 
|  451  |  | 
|  452   // Verify. |  | 
|  453   ASSERT_FALSE(helper.FileExists(src, AsyncFileTestHelper::kDontCheckSize)); |  | 
|  454   ASSERT_TRUE(helper.FileExists(dest, 10)); |  | 
|  455  |  | 
|  456   int64_t src_new_usage = helper.GetSourceUsage(); |  | 
|  457   ASSERT_EQ(src_initial_usage, src_new_usage); |  | 
|  458  |  | 
|  459   int64_t dest_increase = helper.GetDestUsage() - dest_initial_usage; |  | 
|  460   ASSERT_EQ(src_increase, dest_increase); |  | 
|  461 } |  | 
|  462  |  | 
|  463 TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleDirectory) { |  | 
|  464   CopyOrMoveOperationTestHelper helper(GURL("http://foo"), |  | 
|  465                                        storage::kFileSystemTypeTemporary, |  | 
|  466                                        storage::kFileSystemTypePersistent); |  | 
|  467   helper.SetUp(); |  | 
|  468  |  | 
|  469   FileSystemURL src = helper.SourceURL("a"); |  | 
|  470   FileSystemURL dest = helper.DestURL("b"); |  | 
|  471   int64_t src_initial_usage = helper.GetSourceUsage(); |  | 
|  472   int64_t dest_initial_usage = helper.GetDestUsage(); |  | 
|  473  |  | 
|  474   // Set up a source directory. |  | 
|  475   ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src)); |  | 
|  476   int64_t src_increase = helper.GetSourceUsage() - src_initial_usage; |  | 
|  477  |  | 
|  478   // Copy it. |  | 
|  479   ASSERT_EQ(base::File::FILE_OK, helper.Copy(src, dest)); |  | 
|  480  |  | 
|  481   // Verify. |  | 
|  482   ASSERT_TRUE(helper.DirectoryExists(src)); |  | 
|  483   ASSERT_TRUE(helper.DirectoryExists(dest)); |  | 
|  484  |  | 
|  485   int64_t src_new_usage = helper.GetSourceUsage(); |  | 
|  486   ASSERT_EQ(src_initial_usage + src_increase, src_new_usage); |  | 
|  487  |  | 
|  488   int64_t dest_increase = helper.GetDestUsage() - dest_initial_usage; |  | 
|  489   ASSERT_EQ(src_increase, dest_increase); |  | 
|  490 } |  | 
|  491  |  | 
|  492 TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleDirectory) { |  | 
|  493   CopyOrMoveOperationTestHelper helper(GURL("http://foo"), |  | 
|  494                                        storage::kFileSystemTypeTemporary, |  | 
|  495                                        storage::kFileSystemTypePersistent); |  | 
|  496   helper.SetUp(); |  | 
|  497  |  | 
|  498   FileSystemURL src = helper.SourceURL("a"); |  | 
|  499   FileSystemURL dest = helper.DestURL("b"); |  | 
|  500   int64_t src_initial_usage = helper.GetSourceUsage(); |  | 
|  501   int64_t dest_initial_usage = helper.GetDestUsage(); |  | 
|  502  |  | 
|  503   // Set up a source directory. |  | 
|  504   ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src)); |  | 
|  505   int64_t src_increase = helper.GetSourceUsage() - src_initial_usage; |  | 
|  506  |  | 
|  507   // Move it. |  | 
|  508   ASSERT_EQ(base::File::FILE_OK, helper.Move(src, dest)); |  | 
|  509  |  | 
|  510   // Verify. |  | 
|  511   ASSERT_FALSE(helper.DirectoryExists(src)); |  | 
|  512   ASSERT_TRUE(helper.DirectoryExists(dest)); |  | 
|  513  |  | 
|  514   int64_t src_new_usage = helper.GetSourceUsage(); |  | 
|  515   ASSERT_EQ(src_initial_usage, src_new_usage); |  | 
|  516  |  | 
|  517   int64_t dest_increase = helper.GetDestUsage() - dest_initial_usage; |  | 
|  518   ASSERT_EQ(src_increase, dest_increase); |  | 
|  519 } |  | 
|  520  |  | 
|  521 TEST(LocalFileSystemCopyOrMoveOperationTest, CopyDirectory) { |  | 
|  522   CopyOrMoveOperationTestHelper helper(GURL("http://foo"), |  | 
|  523                                        storage::kFileSystemTypeTemporary, |  | 
|  524                                        storage::kFileSystemTypePersistent); |  | 
|  525   helper.SetUp(); |  | 
|  526  |  | 
|  527   FileSystemURL src = helper.SourceURL("a"); |  | 
|  528   FileSystemURL dest = helper.DestURL("b"); |  | 
|  529   int64_t src_initial_usage = helper.GetSourceUsage(); |  | 
|  530   int64_t dest_initial_usage = helper.GetDestUsage(); |  | 
|  531  |  | 
|  532   // Set up a source directory. |  | 
|  533   ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src)); |  | 
|  534   ASSERT_EQ(base::File::FILE_OK, |  | 
|  535             helper.SetUpTestCaseFiles(src, |  | 
|  536                                       kRegularFileSystemTestCases, |  | 
|  537                                       kRegularFileSystemTestCaseSize)); |  | 
|  538   int64_t src_increase = helper.GetSourceUsage() - src_initial_usage; |  | 
|  539  |  | 
|  540   // Copy it. |  | 
|  541   ASSERT_EQ(base::File::FILE_OK, |  | 
|  542             helper.CopyWithProgress( |  | 
|  543                 src, dest, |  | 
|  544                 AsyncFileTestHelper::CopyProgressCallback())); |  | 
|  545  |  | 
|  546   // Verify. |  | 
|  547   ASSERT_TRUE(helper.DirectoryExists(src)); |  | 
|  548   ASSERT_TRUE(helper.DirectoryExists(dest)); |  | 
|  549  |  | 
|  550   helper.VerifyTestCaseFiles(dest, |  | 
|  551                              kRegularFileSystemTestCases, |  | 
|  552                              kRegularFileSystemTestCaseSize); |  | 
|  553  |  | 
|  554   int64_t src_new_usage = helper.GetSourceUsage(); |  | 
|  555   ASSERT_EQ(src_initial_usage + src_increase, src_new_usage); |  | 
|  556  |  | 
|  557   int64_t dest_increase = helper.GetDestUsage() - dest_initial_usage; |  | 
|  558   ASSERT_EQ(src_increase, dest_increase); |  | 
|  559 } |  | 
|  560  |  | 
|  561 TEST(LocalFileSystemCopyOrMoveOperationTest, MoveDirectory) { |  | 
|  562   CopyOrMoveOperationTestHelper helper(GURL("http://foo"), |  | 
|  563                                        storage::kFileSystemTypeTemporary, |  | 
|  564                                        storage::kFileSystemTypePersistent); |  | 
|  565   helper.SetUp(); |  | 
|  566  |  | 
|  567   FileSystemURL src = helper.SourceURL("a"); |  | 
|  568   FileSystemURL dest = helper.DestURL("b"); |  | 
|  569   int64_t src_initial_usage = helper.GetSourceUsage(); |  | 
|  570   int64_t dest_initial_usage = helper.GetDestUsage(); |  | 
|  571  |  | 
|  572   // Set up a source directory. |  | 
|  573   ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src)); |  | 
|  574   ASSERT_EQ(base::File::FILE_OK, |  | 
|  575             helper.SetUpTestCaseFiles(src, |  | 
|  576                                       kRegularFileSystemTestCases, |  | 
|  577                                       kRegularFileSystemTestCaseSize)); |  | 
|  578   int64_t src_increase = helper.GetSourceUsage() - src_initial_usage; |  | 
|  579  |  | 
|  580   // Move it. |  | 
|  581   ASSERT_EQ(base::File::FILE_OK, helper.Move(src, dest)); |  | 
|  582  |  | 
|  583   // Verify. |  | 
|  584   ASSERT_FALSE(helper.DirectoryExists(src)); |  | 
|  585   ASSERT_TRUE(helper.DirectoryExists(dest)); |  | 
|  586  |  | 
|  587   helper.VerifyTestCaseFiles(dest, |  | 
|  588                              kRegularFileSystemTestCases, |  | 
|  589                              kRegularFileSystemTestCaseSize); |  | 
|  590  |  | 
|  591   int64_t src_new_usage = helper.GetSourceUsage(); |  | 
|  592   ASSERT_EQ(src_initial_usage, src_new_usage); |  | 
|  593  |  | 
|  594   int64_t dest_increase = helper.GetDestUsage() - dest_initial_usage; |  | 
|  595   ASSERT_EQ(src_increase, dest_increase); |  | 
|  596 } |  | 
|  597  |  | 
|  598 TEST(LocalFileSystemCopyOrMoveOperationTest, |  | 
|  599      MoveDirectoryFailPostWriteValidation) { |  | 
|  600   CopyOrMoveOperationTestHelper helper(GURL("http://foo"), |  | 
|  601                                        storage::kFileSystemTypeTemporary, |  | 
|  602                                        storage::kFileSystemTypeTest); |  | 
|  603   helper.SetUp(); |  | 
|  604  |  | 
|  605   FileSystemURL src = helper.SourceURL("a"); |  | 
|  606   FileSystemURL dest = helper.DestURL("b"); |  | 
|  607  |  | 
|  608   // Set up a source directory. |  | 
|  609   ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src)); |  | 
|  610   ASSERT_EQ(base::File::FILE_OK, |  | 
|  611             helper.SetUpTestCaseFiles(src, |  | 
|  612                                       kRegularFileSystemTestCases, |  | 
|  613                                       kRegularFileSystemTestCaseSize)); |  | 
|  614  |  | 
|  615   // Move it. |  | 
|  616   helper.Move(src, dest); |  | 
|  617  |  | 
|  618   // Verify. |  | 
|  619   ASSERT_TRUE(helper.DirectoryExists(src)); |  | 
|  620   ASSERT_TRUE(helper.DirectoryExists(dest)); |  | 
|  621  |  | 
|  622   // In the move operation, [file 0, file 2, file 3] are processed as LIFO. |  | 
|  623   // After file 3 is processed, file 2 is rejected by the validator and the |  | 
|  624   // operation fails. That is, only file 3 should be in dest. |  | 
|  625   FileSystemTestCaseRecord kMoveDirResultCases[] = { |  | 
|  626     {false, FILE_PATH_LITERAL("file 3"), 0}, |  | 
|  627   }; |  | 
|  628  |  | 
|  629   helper.VerifyTestCaseFiles(dest, |  | 
|  630                              kMoveDirResultCases, |  | 
|  631                              arraysize(kMoveDirResultCases)); |  | 
|  632 } |  | 
|  633  |  | 
|  634 TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFileNoValidator) { |  | 
|  635   CopyOrMoveOperationTestHelper helper(GURL("http://foo"), |  | 
|  636                                        storage::kFileSystemTypeTemporary, |  | 
|  637                                        storage::kFileSystemTypeTest); |  | 
|  638   helper.SetUpNoValidator(); |  | 
|  639  |  | 
|  640   FileSystemURL src = helper.SourceURL("a"); |  | 
|  641   FileSystemURL dest = helper.DestURL("b"); |  | 
|  642  |  | 
|  643   // Set up a source file. |  | 
|  644   ASSERT_EQ(base::File::FILE_OK, helper.CreateFile(src, 10)); |  | 
|  645  |  | 
|  646   // The copy attempt should fail with a security error -- getting |  | 
|  647   // the factory returns a security error, and the copy operation must |  | 
|  648   // respect that. |  | 
|  649   ASSERT_EQ(base::File::FILE_ERROR_SECURITY, helper.Copy(src, dest)); |  | 
|  650 } |  | 
|  651  |  | 
|  652 TEST(LocalFileSystemCopyOrMoveOperationTest, ProgressCallback) { |  | 
|  653   CopyOrMoveOperationTestHelper helper(GURL("http://foo"), |  | 
|  654                                        storage::kFileSystemTypeTemporary, |  | 
|  655                                        storage::kFileSystemTypePersistent); |  | 
|  656   helper.SetUp(); |  | 
|  657  |  | 
|  658   FileSystemURL src = helper.SourceURL("a"); |  | 
|  659   FileSystemURL dest = helper.DestURL("b"); |  | 
|  660  |  | 
|  661   // Set up a source directory. |  | 
|  662   ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src)); |  | 
|  663   ASSERT_EQ(base::File::FILE_OK, |  | 
|  664             helper.SetUpTestCaseFiles(src, |  | 
|  665                                       kRegularFileSystemTestCases, |  | 
|  666                                       kRegularFileSystemTestCaseSize)); |  | 
|  667  |  | 
|  668   std::vector<ProgressRecord> records; |  | 
|  669   ASSERT_EQ(base::File::FILE_OK, |  | 
|  670             helper.CopyWithProgress(src, dest, |  | 
|  671                                     base::Bind(&RecordProgressCallback, |  | 
|  672                                                base::Unretained(&records)))); |  | 
|  673  |  | 
|  674   // Verify progress callback. |  | 
|  675   for (size_t i = 0; i < kRegularFileSystemTestCaseSize; ++i) { |  | 
|  676     const FileSystemTestCaseRecord& test_case = kRegularFileSystemTestCases[i]; |  | 
|  677  |  | 
|  678     FileSystemURL src_url = helper.SourceURL( |  | 
|  679         std::string("a/") + base::FilePath(test_case.path).AsUTF8Unsafe()); |  | 
|  680     FileSystemURL dest_url = helper.DestURL( |  | 
|  681         std::string("b/") + base::FilePath(test_case.path).AsUTF8Unsafe()); |  | 
|  682  |  | 
|  683     // Find the first and last progress record. |  | 
|  684     size_t begin_index = records.size(); |  | 
|  685     size_t end_index = records.size(); |  | 
|  686     for (size_t j = 0; j < records.size(); ++j) { |  | 
|  687       if (records[j].source_url == src_url) { |  | 
|  688         if (begin_index == records.size()) |  | 
|  689           begin_index = j; |  | 
|  690         end_index = j; |  | 
|  691       } |  | 
|  692     } |  | 
|  693  |  | 
|  694     // The record should be found. |  | 
|  695     ASSERT_NE(begin_index, records.size()); |  | 
|  696     ASSERT_NE(end_index, records.size()); |  | 
|  697     ASSERT_NE(begin_index, end_index); |  | 
|  698  |  | 
|  699     EXPECT_EQ(FileSystemOperation::BEGIN_COPY_ENTRY, |  | 
|  700               records[begin_index].type); |  | 
|  701     EXPECT_FALSE(records[begin_index].dest_url.is_valid()); |  | 
|  702     EXPECT_EQ(FileSystemOperation::END_COPY_ENTRY, records[end_index].type); |  | 
|  703     EXPECT_EQ(dest_url, records[end_index].dest_url); |  | 
|  704  |  | 
|  705     if (test_case.is_directory) { |  | 
|  706       // For directory copy, the progress shouldn't be interlaced. |  | 
|  707       EXPECT_EQ(begin_index + 1, end_index); |  | 
|  708     } else { |  | 
|  709       // PROGRESS event's size should be assending order. |  | 
|  710       int64_t current_size = 0; |  | 
|  711       for (size_t j = begin_index + 1; j < end_index; ++j) { |  | 
|  712         if (records[j].source_url == src_url) { |  | 
|  713           EXPECT_EQ(FileSystemOperation::PROGRESS, records[j].type); |  | 
|  714           EXPECT_FALSE(records[j].dest_url.is_valid()); |  | 
|  715           EXPECT_GE(records[j].size, current_size); |  | 
|  716           current_size = records[j].size; |  | 
|  717         } |  | 
|  718       } |  | 
|  719     } |  | 
|  720   } |  | 
|  721 } |  | 
|  722  |  | 
|  723 TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelper) { |  | 
|  724   base::ScopedTempDir temp_dir; |  | 
|  725   ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |  | 
|  726   base::FilePath source_path = temp_dir.GetPath().AppendASCII("source"); |  | 
|  727   base::FilePath dest_path = temp_dir.GetPath().AppendASCII("dest"); |  | 
|  728   const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789"; |  | 
|  729   base::WriteFile(source_path, kTestData, |  | 
|  730                   arraysize(kTestData) - 1);  // Exclude trailing '\0'. |  | 
|  731  |  | 
|  732   base::MessageLoopForIO message_loop; |  | 
|  733   base::Thread file_thread("file_thread"); |  | 
|  734   ASSERT_TRUE(file_thread.Start()); |  | 
|  735   ScopedThreadStopper thread_stopper(&file_thread); |  | 
|  736   ASSERT_TRUE(thread_stopper.is_valid()); |  | 
|  737  |  | 
|  738   scoped_refptr<base::SingleThreadTaskRunner> task_runner = |  | 
|  739       file_thread.task_runner(); |  | 
|  740  |  | 
|  741   std::unique_ptr<storage::FileStreamReader> reader( |  | 
|  742       storage::FileStreamReader::CreateForLocalFile( |  | 
|  743           task_runner.get(), source_path, 0, base::Time())); |  | 
|  744  |  | 
|  745   std::unique_ptr<FileStreamWriter> writer(FileStreamWriter::CreateForLocalFile( |  | 
|  746       task_runner.get(), dest_path, 0, FileStreamWriter::CREATE_NEW_FILE)); |  | 
|  747  |  | 
|  748   std::vector<int64_t> progress; |  | 
|  749   CopyOrMoveOperationDelegate::StreamCopyHelper helper( |  | 
|  750       std::move(reader), std::move(writer), |  | 
|  751       storage::FlushPolicy::NO_FLUSH_ON_COMPLETION, |  | 
|  752       10,  // buffer size |  | 
|  753       base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)), |  | 
|  754       base::TimeDelta());  // For testing, we need all the progress. |  | 
|  755  |  | 
|  756   base::File::Error error = base::File::FILE_ERROR_FAILED; |  | 
|  757   base::RunLoop run_loop; |  | 
|  758   helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error)); |  | 
|  759   run_loop.Run(); |  | 
|  760  |  | 
|  761   EXPECT_EQ(base::File::FILE_OK, error); |  | 
|  762   ASSERT_EQ(5U, progress.size()); |  | 
|  763   EXPECT_EQ(0, progress[0]); |  | 
|  764   EXPECT_EQ(10, progress[1]); |  | 
|  765   EXPECT_EQ(20, progress[2]); |  | 
|  766   EXPECT_EQ(30, progress[3]); |  | 
|  767   EXPECT_EQ(36, progress[4]); |  | 
|  768  |  | 
|  769   std::string content; |  | 
|  770   ASSERT_TRUE(base::ReadFileToString(dest_path, &content)); |  | 
|  771   EXPECT_EQ(kTestData, content); |  | 
|  772 } |  | 
|  773  |  | 
|  774 TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelperWithFlush) { |  | 
|  775   // Testing the same configuration as StreamCopyHelper, but with |need_flush| |  | 
|  776   // parameter set to true. Since it is hard to test that the flush is indeed |  | 
|  777   // taking place, this test just only verifies that the file is correctly |  | 
|  778   // written with or without the flag. |  | 
|  779   base::ScopedTempDir temp_dir; |  | 
|  780   ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |  | 
|  781   base::FilePath source_path = temp_dir.GetPath().AppendASCII("source"); |  | 
|  782   base::FilePath dest_path = temp_dir.GetPath().AppendASCII("dest"); |  | 
|  783   const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789"; |  | 
|  784   base::WriteFile(source_path, kTestData, |  | 
|  785                   arraysize(kTestData) - 1);  // Exclude trailing '\0'. |  | 
|  786  |  | 
|  787  |  | 
|  788   base::MessageLoopForIO message_loop; |  | 
|  789   base::Thread file_thread("file_thread"); |  | 
|  790   ASSERT_TRUE(file_thread.Start()); |  | 
|  791   ScopedThreadStopper thread_stopper(&file_thread); |  | 
|  792   ASSERT_TRUE(thread_stopper.is_valid()); |  | 
|  793  |  | 
|  794   scoped_refptr<base::SingleThreadTaskRunner> task_runner = |  | 
|  795       file_thread.task_runner(); |  | 
|  796  |  | 
|  797   std::unique_ptr<storage::FileStreamReader> reader( |  | 
|  798       storage::FileStreamReader::CreateForLocalFile( |  | 
|  799           task_runner.get(), source_path, 0, base::Time())); |  | 
|  800  |  | 
|  801   std::unique_ptr<FileStreamWriter> writer(FileStreamWriter::CreateForLocalFile( |  | 
|  802       task_runner.get(), dest_path, 0, FileStreamWriter::CREATE_NEW_FILE)); |  | 
|  803  |  | 
|  804   std::vector<int64_t> progress; |  | 
|  805   CopyOrMoveOperationDelegate::StreamCopyHelper helper( |  | 
|  806       std::move(reader), std::move(writer), |  | 
|  807       storage::FlushPolicy::NO_FLUSH_ON_COMPLETION, |  | 
|  808       10,  // buffer size |  | 
|  809       base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)), |  | 
|  810       base::TimeDelta());  // For testing, we need all the progress. |  | 
|  811  |  | 
|  812   base::File::Error error = base::File::FILE_ERROR_FAILED; |  | 
|  813   base::RunLoop run_loop; |  | 
|  814   helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error)); |  | 
|  815   run_loop.Run(); |  | 
|  816  |  | 
|  817   EXPECT_EQ(base::File::FILE_OK, error); |  | 
|  818   ASSERT_EQ(5U, progress.size()); |  | 
|  819   EXPECT_EQ(0, progress[0]); |  | 
|  820   EXPECT_EQ(10, progress[1]); |  | 
|  821   EXPECT_EQ(20, progress[2]); |  | 
|  822   EXPECT_EQ(30, progress[3]); |  | 
|  823   EXPECT_EQ(36, progress[4]); |  | 
|  824  |  | 
|  825   std::string content; |  | 
|  826   ASSERT_TRUE(base::ReadFileToString(dest_path, &content)); |  | 
|  827   EXPECT_EQ(kTestData, content); |  | 
|  828 } |  | 
|  829  |  | 
|  830 TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelper_Cancel) { |  | 
|  831   base::ScopedTempDir temp_dir; |  | 
|  832   ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |  | 
|  833   base::FilePath source_path = temp_dir.GetPath().AppendASCII("source"); |  | 
|  834   base::FilePath dest_path = temp_dir.GetPath().AppendASCII("dest"); |  | 
|  835   const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789"; |  | 
|  836   base::WriteFile(source_path, kTestData, |  | 
|  837                   arraysize(kTestData) - 1);  // Exclude trailing '\0'. |  | 
|  838  |  | 
|  839   base::MessageLoopForIO message_loop; |  | 
|  840   base::Thread file_thread("file_thread"); |  | 
|  841   ASSERT_TRUE(file_thread.Start()); |  | 
|  842   ScopedThreadStopper thread_stopper(&file_thread); |  | 
|  843   ASSERT_TRUE(thread_stopper.is_valid()); |  | 
|  844  |  | 
|  845   scoped_refptr<base::SingleThreadTaskRunner> task_runner = |  | 
|  846       file_thread.task_runner(); |  | 
|  847  |  | 
|  848   std::unique_ptr<storage::FileStreamReader> reader( |  | 
|  849       storage::FileStreamReader::CreateForLocalFile( |  | 
|  850           task_runner.get(), source_path, 0, base::Time())); |  | 
|  851  |  | 
|  852   std::unique_ptr<FileStreamWriter> writer(FileStreamWriter::CreateForLocalFile( |  | 
|  853       task_runner.get(), dest_path, 0, FileStreamWriter::CREATE_NEW_FILE)); |  | 
|  854  |  | 
|  855   std::vector<int64_t> progress; |  | 
|  856   CopyOrMoveOperationDelegate::StreamCopyHelper helper( |  | 
|  857       std::move(reader), std::move(writer), |  | 
|  858       storage::FlushPolicy::NO_FLUSH_ON_COMPLETION, |  | 
|  859       10,  // buffer size |  | 
|  860       base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)), |  | 
|  861       base::TimeDelta());  // For testing, we need all the progress. |  | 
|  862  |  | 
|  863   // Call Cancel() later. |  | 
|  864   base::ThreadTaskRunnerHandle::Get()->PostTask( |  | 
|  865       FROM_HERE, |  | 
|  866       base::Bind(&CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel, |  | 
|  867                  base::Unretained(&helper))); |  | 
|  868  |  | 
|  869   base::File::Error error = base::File::FILE_ERROR_FAILED; |  | 
|  870   base::RunLoop run_loop; |  | 
|  871   helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error)); |  | 
|  872   run_loop.Run(); |  | 
|  873  |  | 
|  874   EXPECT_EQ(base::File::FILE_ERROR_ABORT, error); |  | 
|  875 } |  | 
|  876  |  | 
|  877 }  // namespace content |  | 
| OLD | NEW |