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 "base/basictypes.h"
6#include "base/bind.h"
7#include "base/files/file_path.h"
8#include "base/files/scoped_temp_dir.h"
9#include "base/run_loop.h"
10#include "content/public/test/async_file_test_helper.h"
11#include "content/public/test/mock_special_storage_policy.h"
12#include "content/public/test/test_file_system_backend.h"
13#include "content/public/test/test_file_system_context.h"
14#include "storage/browser/fileapi/copy_or_move_file_validator.h"
15#include "storage/browser/fileapi/external_mount_points.h"
16#include "storage/browser/fileapi/file_system_backend.h"
17#include "storage/browser/fileapi/file_system_context.h"
18#include "storage/browser/fileapi/file_system_url.h"
19#include "storage/browser/fileapi/isolated_context.h"
20#include "storage/common/blob/shareable_file_reference.h"
21#include "storage/common/fileapi/file_system_util.h"
22#include "testing/gtest/include/gtest/gtest.h"
23
24using content::AsyncFileTestHelper;
25using storage::CopyOrMoveFileValidator;
26using storage::CopyOrMoveFileValidatorFactory;
27using storage::FileSystemURL;
28
29namespace content {
30
31namespace {
32
33const storage::FileSystemType kNoValidatorType =
34    storage::kFileSystemTypeTemporary;
35const storage::FileSystemType kWithValidatorType = storage::kFileSystemTypeTest;
36
37void ExpectOk(const GURL& origin_url,
38              const std::string& name,
39              base::File::Error error) {
40  ASSERT_EQ(base::File::FILE_OK, error);
41}
42
43class CopyOrMoveFileValidatorTestHelper {
44 public:
45  CopyOrMoveFileValidatorTestHelper(const GURL& origin,
46                                    storage::FileSystemType src_type,
47                                    storage::FileSystemType dest_type)
48      : origin_(origin), src_type_(src_type), dest_type_(dest_type) {}
49
50  ~CopyOrMoveFileValidatorTestHelper() {
51    file_system_context_ = NULL;
52    base::RunLoop().RunUntilIdle();
53  }
54
55  void SetUp() {
56    ASSERT_TRUE(base_.CreateUniqueTempDir());
57    base::FilePath base_dir = base_.path();
58
59    file_system_context_ = CreateFileSystemContextForTesting(NULL, base_dir);
60
61    // Set up TestFileSystemBackend to require CopyOrMoveFileValidator.
62    storage::FileSystemBackend* test_file_system_backend =
63        file_system_context_->GetFileSystemBackend(kWithValidatorType);
64    static_cast<TestFileSystemBackend*>(test_file_system_backend)->
65        set_require_copy_or_move_validator(true);
66
67    // Sets up source.
68    storage::FileSystemBackend* src_file_system_backend =
69        file_system_context_->GetFileSystemBackend(src_type_);
70    src_file_system_backend->ResolveURL(
71        FileSystemURL::CreateForTest(origin_, src_type_, base::FilePath()),
72        storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
73        base::Bind(&ExpectOk));
74    base::RunLoop().RunUntilIdle();
75    ASSERT_EQ(base::File::FILE_OK, CreateDirectory(SourceURL("")));
76
77    // Sets up dest.
78    DCHECK_EQ(kWithValidatorType, dest_type_);
79    ASSERT_EQ(base::File::FILE_OK, CreateDirectory(DestURL("")));
80
81    copy_src_ = SourceURL("copy_src.jpg");
82    move_src_ = SourceURL("move_src.jpg");
83    copy_dest_ = DestURL("copy_dest.jpg");
84    move_dest_ = DestURL("move_dest.jpg");
85
86    ASSERT_EQ(base::File::FILE_OK, CreateFile(copy_src_, 10));
87    ASSERT_EQ(base::File::FILE_OK, CreateFile(move_src_, 10));
88
89    ASSERT_TRUE(FileExists(copy_src_, 10));
90    ASSERT_TRUE(FileExists(move_src_, 10));
91    ASSERT_FALSE(FileExists(copy_dest_, 10));
92    ASSERT_FALSE(FileExists(move_dest_, 10));
93  }
94
95  void SetMediaCopyOrMoveFileValidatorFactory(
96      scoped_ptr<storage::CopyOrMoveFileValidatorFactory> factory) {
97    TestFileSystemBackend* backend = static_cast<TestFileSystemBackend*>(
98        file_system_context_->GetFileSystemBackend(kWithValidatorType));
99    backend->InitializeCopyOrMoveFileValidatorFactory(factory.Pass());
100  }
101
102  void CopyTest(base::File::Error expected) {
103    ASSERT_TRUE(FileExists(copy_src_, 10));
104    ASSERT_FALSE(FileExists(copy_dest_, 10));
105
106    EXPECT_EQ(expected,
107              AsyncFileTestHelper::Copy(
108                  file_system_context_.get(), copy_src_, copy_dest_));
109
110    EXPECT_TRUE(FileExists(copy_src_, 10));
111    if (expected == base::File::FILE_OK)
112      EXPECT_TRUE(FileExists(copy_dest_, 10));
113    else
114      EXPECT_FALSE(FileExists(copy_dest_, 10));
115  };
116
117  void MoveTest(base::File::Error expected) {
118    ASSERT_TRUE(FileExists(move_src_, 10));
119    ASSERT_FALSE(FileExists(move_dest_, 10));
120
121    EXPECT_EQ(expected,
122              AsyncFileTestHelper::Move(
123                  file_system_context_.get(), move_src_, move_dest_));
124
125    if (expected == base::File::FILE_OK) {
126      EXPECT_FALSE(FileExists(move_src_, 10));
127      EXPECT_TRUE(FileExists(move_dest_, 10));
128    } else {
129      EXPECT_TRUE(FileExists(move_src_, 10));
130      EXPECT_FALSE(FileExists(move_dest_, 10));
131    }
132  };
133
134 private:
135  FileSystemURL SourceURL(const std::string& path) {
136    return file_system_context_->CreateCrackedFileSystemURL(
137        origin_, src_type_,
138        base::FilePath().AppendASCII("src").AppendASCII(path));
139  }
140
141  FileSystemURL DestURL(const std::string& path) {
142    return file_system_context_->CreateCrackedFileSystemURL(
143        origin_, dest_type_,
144        base::FilePath().AppendASCII("dest").AppendASCII(path));
145  }
146
147  base::File::Error CreateFile(const FileSystemURL& url, size_t size) {
148    base::File::Error result =
149        AsyncFileTestHelper::CreateFile(file_system_context_.get(), url);
150    if (result != base::File::FILE_OK)
151      return result;
152    return AsyncFileTestHelper::TruncateFile(
153        file_system_context_.get(), url, size);
154  }
155
156  base::File::Error CreateDirectory(const FileSystemURL& url) {
157    return AsyncFileTestHelper::CreateDirectory(file_system_context_.get(),
158                                                url);
159  }
160
161  bool FileExists(const FileSystemURL& url, int64 expected_size) {
162    return AsyncFileTestHelper::FileExists(
163        file_system_context_.get(), url, expected_size);
164  }
165
166  base::ScopedTempDir base_;
167
168  const GURL origin_;
169
170  const storage::FileSystemType src_type_;
171  const storage::FileSystemType dest_type_;
172  std::string src_fsid_;
173  std::string dest_fsid_;
174
175  base::MessageLoop message_loop_;
176  scoped_refptr<storage::FileSystemContext> file_system_context_;
177
178  FileSystemURL copy_src_;
179  FileSystemURL copy_dest_;
180  FileSystemURL move_src_;
181  FileSystemURL move_dest_;
182
183  DISALLOW_COPY_AND_ASSIGN(CopyOrMoveFileValidatorTestHelper);
184};
185
186// For TestCopyOrMoveFileValidatorFactory
187enum Validity {
188  VALID,
189  PRE_WRITE_INVALID,
190  POST_WRITE_INVALID
191};
192
193class TestCopyOrMoveFileValidatorFactory
194    : public storage::CopyOrMoveFileValidatorFactory {
195 public:
196  // A factory that creates validators that accept everything or nothing.
197  // TODO(gbillock): switch args to enum or something
198  explicit TestCopyOrMoveFileValidatorFactory(Validity validity)
199      : validity_(validity) {}
200  virtual ~TestCopyOrMoveFileValidatorFactory() {}
201
202  virtual storage::CopyOrMoveFileValidator* CreateCopyOrMoveFileValidator(
203      const FileSystemURL& /*src_url*/,
204      const base::FilePath& /*platform_path*/) OVERRIDE {
205    return new TestCopyOrMoveFileValidator(validity_);
206  }
207
208 private:
209  class TestCopyOrMoveFileValidator : public CopyOrMoveFileValidator {
210   public:
211    explicit TestCopyOrMoveFileValidator(Validity validity)
212        : result_(validity == VALID || validity == POST_WRITE_INVALID ?
213                  base::File::FILE_OK :
214                  base::File::FILE_ERROR_SECURITY),
215          write_result_(validity == VALID || validity == PRE_WRITE_INVALID ?
216                        base::File::FILE_OK :
217                        base::File::FILE_ERROR_SECURITY) {
218    }
219    virtual ~TestCopyOrMoveFileValidator() {}
220
221    virtual void StartPreWriteValidation(
222        const ResultCallback& result_callback) OVERRIDE {
223      // Post the result since a real validator must do work asynchronously.
224      base::MessageLoop::current()->PostTask(
225          FROM_HERE, base::Bind(result_callback, result_));
226    }
227
228    virtual void StartPostWriteValidation(
229        const base::FilePath& dest_platform_path,
230        const ResultCallback& result_callback) OVERRIDE {
231      // Post the result since a real validator must do work asynchronously.
232      base::MessageLoop::current()->PostTask(
233          FROM_HERE, base::Bind(result_callback, write_result_));
234    }
235
236   private:
237    base::File::Error result_;
238    base::File::Error write_result_;
239
240    DISALLOW_COPY_AND_ASSIGN(TestCopyOrMoveFileValidator);
241  };
242
243  Validity validity_;
244
245  DISALLOW_COPY_AND_ASSIGN(TestCopyOrMoveFileValidatorFactory);
246};
247
248}  // namespace
249
250TEST(CopyOrMoveFileValidatorTest, NoValidatorWithinSameFSType) {
251  // Within a file system type, validation is not expected, so it should
252  // work for kWithValidatorType without a validator set.
253  CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"),
254                                           kWithValidatorType,
255                                           kWithValidatorType);
256  helper.SetUp();
257  helper.CopyTest(base::File::FILE_OK);
258  helper.MoveTest(base::File::FILE_OK);
259}
260
261TEST(CopyOrMoveFileValidatorTest, MissingValidator) {
262  // Copying or moving into a kWithValidatorType requires a file
263  // validator.  An error is expected if copy is attempted without a validator.
264  CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"),
265                                           kNoValidatorType,
266                                           kWithValidatorType);
267  helper.SetUp();
268  helper.CopyTest(base::File::FILE_ERROR_SECURITY);
269  helper.MoveTest(base::File::FILE_ERROR_SECURITY);
270}
271
272TEST(CopyOrMoveFileValidatorTest, AcceptAll) {
273  CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"),
274                                           kNoValidatorType,
275                                           kWithValidatorType);
276  helper.SetUp();
277  scoped_ptr<CopyOrMoveFileValidatorFactory> factory(
278      new TestCopyOrMoveFileValidatorFactory(VALID));
279  helper.SetMediaCopyOrMoveFileValidatorFactory(factory.Pass());
280
281  helper.CopyTest(base::File::FILE_OK);
282  helper.MoveTest(base::File::FILE_OK);
283}
284
285TEST(CopyOrMoveFileValidatorTest, AcceptNone) {
286  CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"),
287                                           kNoValidatorType,
288                                           kWithValidatorType);
289  helper.SetUp();
290  scoped_ptr<CopyOrMoveFileValidatorFactory> factory(
291      new TestCopyOrMoveFileValidatorFactory(PRE_WRITE_INVALID));
292  helper.SetMediaCopyOrMoveFileValidatorFactory(factory.Pass());
293
294  helper.CopyTest(base::File::FILE_ERROR_SECURITY);
295  helper.MoveTest(base::File::FILE_ERROR_SECURITY);
296}
297
298TEST(CopyOrMoveFileValidatorTest, OverrideValidator) {
299  // Once set, you can not override the validator.
300  CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"),
301                                           kNoValidatorType,
302                                           kWithValidatorType);
303  helper.SetUp();
304  scoped_ptr<CopyOrMoveFileValidatorFactory> reject_factory(
305      new TestCopyOrMoveFileValidatorFactory(PRE_WRITE_INVALID));
306  helper.SetMediaCopyOrMoveFileValidatorFactory(reject_factory.Pass());
307
308  scoped_ptr<CopyOrMoveFileValidatorFactory> accept_factory(
309      new TestCopyOrMoveFileValidatorFactory(VALID));
310  helper.SetMediaCopyOrMoveFileValidatorFactory(accept_factory.Pass());
311
312  helper.CopyTest(base::File::FILE_ERROR_SECURITY);
313  helper.MoveTest(base::File::FILE_ERROR_SECURITY);
314}
315
316TEST(CopyOrMoveFileValidatorTest, RejectPostWrite) {
317  CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"),
318                                           kNoValidatorType,
319                                           kWithValidatorType);
320  helper.SetUp();
321  scoped_ptr<CopyOrMoveFileValidatorFactory> factory(
322      new TestCopyOrMoveFileValidatorFactory(POST_WRITE_INVALID));
323  helper.SetMediaCopyOrMoveFileValidatorFactory(factory.Pass());
324
325  helper.CopyTest(base::File::FILE_ERROR_SECURITY);
326  helper.MoveTest(base::File::FILE_ERROR_SECURITY);
327}
328
329}  // namespace content
330