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 <string>
6
7#include "base/file_util.h"
8#include "base/files/file_path.h"
9#include "base/files/scoped_temp_dir.h"
10#include "base/message_loop/message_loop.h"
11#include "base/message_loop/message_loop_proxy.h"
12#include "base/platform_file.h"
13#include "base/strings/sys_string_conversions.h"
14#include "base/strings/utf_string_conversions.h"
15#include "testing/gtest/include/gtest/gtest.h"
16#include "webkit/browser/fileapi/async_file_test_helper.h"
17#include "webkit/browser/fileapi/file_system_context.h"
18#include "webkit/browser/fileapi/file_system_file_util.h"
19#include "webkit/browser/fileapi/file_system_operation_context.h"
20#include "webkit/browser/fileapi/local_file_util.h"
21#include "webkit/browser/fileapi/mock_file_system_context.h"
22#include "webkit/browser/fileapi/native_file_util.h"
23#include "webkit/common/fileapi/file_system_types.h"
24
25namespace fileapi {
26
27namespace {
28
29const GURL kOrigin("http://foo/");
30const FileSystemType kFileSystemType = kFileSystemTypeTest;
31
32}  // namespace
33
34class LocalFileUtilTest : public testing::Test {
35 public:
36  LocalFileUtilTest() {}
37
38  virtual void SetUp() {
39    ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
40    file_system_context_ = CreateFileSystemContextForTesting(
41        NULL, data_dir_.path());
42  }
43
44  virtual void TearDown() {
45    file_system_context_ = NULL;
46    base::MessageLoop::current()->RunUntilIdle();
47  }
48
49 protected:
50  FileSystemOperationContext* NewContext() {
51    FileSystemOperationContext* context =
52        new FileSystemOperationContext(file_system_context_.get());
53    context->set_update_observers(
54        *file_system_context_->GetUpdateObservers(kFileSystemType));
55    context->set_root_path(data_dir_.path());
56    return context;
57  }
58
59  LocalFileUtil* file_util() {
60    return static_cast<LocalFileUtil*>(
61        file_system_context_->GetFileUtil(kFileSystemType));
62  }
63
64  FileSystemURL CreateURL(const std::string& file_name) {
65    return file_system_context_->CreateCrackedFileSystemURL(
66        kOrigin, kFileSystemType, base::FilePath().FromUTF8Unsafe(file_name));
67  }
68
69  base::FilePath LocalPath(const char *file_name) {
70    base::FilePath path;
71    scoped_ptr<FileSystemOperationContext> context(NewContext());
72    file_util()->GetLocalFilePath(context.get(), CreateURL(file_name), &path);
73    return path;
74  }
75
76  bool FileExists(const char *file_name) {
77    return base::PathExists(LocalPath(file_name)) &&
78        !base::DirectoryExists(LocalPath(file_name));
79  }
80
81  bool DirectoryExists(const char *file_name) {
82    return base::DirectoryExists(LocalPath(file_name));
83  }
84
85  int64 GetSize(const char *file_name) {
86    base::PlatformFileInfo info;
87    file_util::GetFileInfo(LocalPath(file_name), &info);
88    return info.size;
89  }
90
91  base::PlatformFileError CreateFile(const char* file_name,
92                                     base::PlatformFile* file_handle,
93                                     bool* created) {
94    int file_flags = base::PLATFORM_FILE_CREATE |
95        base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_ASYNC;
96
97    scoped_ptr<FileSystemOperationContext> context(NewContext());
98    return file_util()->CreateOrOpen(
99        context.get(),
100        CreateURL(file_name),
101        file_flags, file_handle, created);
102  }
103
104  base::PlatformFileError EnsureFileExists(const char* file_name,
105      bool* created) {
106    scoped_ptr<FileSystemOperationContext> context(NewContext());
107    return file_util()->EnsureFileExists(
108        context.get(),
109        CreateURL(file_name), created);
110  }
111
112  FileSystemContext* file_system_context() {
113    return file_system_context_.get();
114  }
115
116 private:
117  base::MessageLoop message_loop_;
118  scoped_refptr<FileSystemContext> file_system_context_;
119  base::ScopedTempDir data_dir_;
120
121  DISALLOW_COPY_AND_ASSIGN(LocalFileUtilTest);
122};
123
124TEST_F(LocalFileUtilTest, CreateAndClose) {
125  const char *file_name = "test_file";
126  base::PlatformFile file_handle;
127  bool created;
128  ASSERT_EQ(base::PLATFORM_FILE_OK,
129            CreateFile(file_name, &file_handle, &created));
130  ASSERT_TRUE(created);
131
132  EXPECT_TRUE(FileExists(file_name));
133  EXPECT_EQ(0, GetSize(file_name));
134
135  scoped_ptr<FileSystemOperationContext> context(NewContext());
136  EXPECT_EQ(base::PLATFORM_FILE_OK,
137      file_util()->Close(context.get(), file_handle));
138}
139
140// file_util::CreateSymbolicLink is only supported on POSIX.
141#if defined(OS_POSIX)
142TEST_F(LocalFileUtilTest, CreateFailForSymlink) {
143  // Create symlink target file.
144  const char *target_name = "symlink_target";
145  base::PlatformFile target_handle;
146  bool symlink_target_created = false;
147  ASSERT_EQ(base::PLATFORM_FILE_OK,
148            CreateFile(target_name, &target_handle, &symlink_target_created));
149  ASSERT_TRUE(symlink_target_created);
150  base::FilePath target_path = LocalPath(target_name);
151
152  // Create symlink where target must be real file.
153  const char *symlink_name = "symlink_file";
154  base::FilePath symlink_path = LocalPath(symlink_name);
155  ASSERT_TRUE(file_util::CreateSymbolicLink(target_path, symlink_path));
156  ASSERT_TRUE(FileExists(symlink_name));
157
158  // Try to open the symlink file which should fail.
159  scoped_ptr<FileSystemOperationContext> context(NewContext());
160  FileSystemURL url = CreateURL(symlink_name);
161  int file_flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ;
162  base::PlatformFile file_handle;
163  bool created = false;
164  EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, file_util()->CreateOrOpen(
165      context.get(), url, file_flags, &file_handle, &created));
166  EXPECT_FALSE(created);
167}
168#endif
169
170TEST_F(LocalFileUtilTest, EnsureFileExists) {
171  const char *file_name = "foobar";
172  bool created;
173  ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(file_name, &created));
174  ASSERT_TRUE(created);
175
176  EXPECT_TRUE(FileExists(file_name));
177  EXPECT_EQ(0, GetSize(file_name));
178
179  ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(file_name, &created));
180  EXPECT_FALSE(created);
181}
182
183TEST_F(LocalFileUtilTest, TouchFile) {
184  const char *file_name = "test_file";
185  base::PlatformFile file_handle;
186  bool created;
187  ASSERT_EQ(base::PLATFORM_FILE_OK,
188            CreateFile(file_name, &file_handle, &created));
189  ASSERT_TRUE(created);
190
191  scoped_ptr<FileSystemOperationContext> context(NewContext());
192
193  base::PlatformFileInfo info;
194  ASSERT_TRUE(file_util::GetFileInfo(LocalPath(file_name), &info));
195  const base::Time new_accessed =
196      info.last_accessed + base::TimeDelta::FromHours(10);
197  const base::Time new_modified =
198      info.last_modified + base::TimeDelta::FromHours(5);
199
200  EXPECT_EQ(base::PLATFORM_FILE_OK,
201            file_util()->Touch(context.get(), CreateURL(file_name),
202                              new_accessed, new_modified));
203
204  ASSERT_TRUE(file_util::GetFileInfo(LocalPath(file_name), &info));
205  EXPECT_EQ(new_accessed, info.last_accessed);
206  EXPECT_EQ(new_modified, info.last_modified);
207
208  EXPECT_EQ(base::PLATFORM_FILE_OK,
209            file_util()->Close(context.get(), file_handle));
210}
211
212TEST_F(LocalFileUtilTest, TouchDirectory) {
213  const char *dir_name = "test_dir";
214  scoped_ptr<FileSystemOperationContext> context(NewContext());
215  ASSERT_EQ(base::PLATFORM_FILE_OK,
216            file_util()->CreateDirectory(context.get(),
217                                        CreateURL(dir_name),
218                                        false /* exclusive */,
219                                        false /* recursive */));
220
221  base::PlatformFileInfo info;
222  ASSERT_TRUE(file_util::GetFileInfo(LocalPath(dir_name), &info));
223  const base::Time new_accessed =
224      info.last_accessed + base::TimeDelta::FromHours(10);
225  const base::Time new_modified =
226      info.last_modified + base::TimeDelta::FromHours(5);
227
228  EXPECT_EQ(base::PLATFORM_FILE_OK,
229            file_util()->Touch(context.get(), CreateURL(dir_name),
230                              new_accessed, new_modified));
231
232  ASSERT_TRUE(file_util::GetFileInfo(LocalPath(dir_name), &info));
233  EXPECT_EQ(new_accessed, info.last_accessed);
234  EXPECT_EQ(new_modified, info.last_modified);
235}
236
237TEST_F(LocalFileUtilTest, Truncate) {
238  const char *file_name = "truncated";
239  bool created;
240  ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(file_name, &created));
241  ASSERT_TRUE(created);
242
243  scoped_ptr<FileSystemOperationContext> context;
244
245  context.reset(NewContext());
246  ASSERT_EQ(base::PLATFORM_FILE_OK,
247      file_util()->Truncate(context.get(), CreateURL(file_name), 1020));
248
249  EXPECT_TRUE(FileExists(file_name));
250  EXPECT_EQ(1020, GetSize(file_name));
251}
252
253TEST_F(LocalFileUtilTest, CopyFile) {
254  const char *from_file = "fromfile";
255  const char *to_file1 = "tofile1";
256  const char *to_file2 = "tofile2";
257  bool created;
258  ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
259  ASSERT_TRUE(created);
260
261  scoped_ptr<FileSystemOperationContext> context;
262  context.reset(NewContext());
263  ASSERT_EQ(base::PLATFORM_FILE_OK,
264      file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
265
266  EXPECT_TRUE(FileExists(from_file));
267  EXPECT_EQ(1020, GetSize(from_file));
268
269  ASSERT_EQ(base::PLATFORM_FILE_OK,
270            AsyncFileTestHelper::Copy(file_system_context(),
271                                      CreateURL(from_file),
272                                      CreateURL(to_file1)));
273
274  context.reset(NewContext());
275  ASSERT_EQ(base::PLATFORM_FILE_OK,
276            AsyncFileTestHelper::Copy(file_system_context(),
277                                      CreateURL(from_file),
278                                      CreateURL(to_file2)));
279
280  EXPECT_TRUE(FileExists(from_file));
281  EXPECT_EQ(1020, GetSize(from_file));
282  EXPECT_TRUE(FileExists(to_file1));
283  EXPECT_EQ(1020, GetSize(to_file1));
284  EXPECT_TRUE(FileExists(to_file2));
285  EXPECT_EQ(1020, GetSize(to_file2));
286}
287
288TEST_F(LocalFileUtilTest, CopyDirectory) {
289  const char *from_dir = "fromdir";
290  const char *from_file = "fromdir/fromfile";
291  const char *to_dir = "todir";
292  const char *to_file = "todir/fromfile";
293  bool created;
294  scoped_ptr<FileSystemOperationContext> context;
295
296  context.reset(NewContext());
297  ASSERT_EQ(base::PLATFORM_FILE_OK,
298      file_util()->CreateDirectory(context.get(), CreateURL(from_dir),
299                                   false, false));
300  ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
301  ASSERT_TRUE(created);
302
303  context.reset(NewContext());
304  ASSERT_EQ(base::PLATFORM_FILE_OK,
305      file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
306
307  EXPECT_TRUE(DirectoryExists(from_dir));
308  EXPECT_TRUE(FileExists(from_file));
309  EXPECT_EQ(1020, GetSize(from_file));
310  EXPECT_FALSE(DirectoryExists(to_dir));
311
312  context.reset(NewContext());
313  ASSERT_EQ(base::PLATFORM_FILE_OK,
314            AsyncFileTestHelper::Copy(file_system_context(),
315                                      CreateURL(from_dir), CreateURL(to_dir)));
316
317  EXPECT_TRUE(DirectoryExists(from_dir));
318  EXPECT_TRUE(FileExists(from_file));
319  EXPECT_EQ(1020, GetSize(from_file));
320  EXPECT_TRUE(DirectoryExists(to_dir));
321  EXPECT_TRUE(FileExists(to_file));
322  EXPECT_EQ(1020, GetSize(to_file));
323}
324
325TEST_F(LocalFileUtilTest, MoveFile) {
326  const char *from_file = "fromfile";
327  const char *to_file = "tofile";
328  bool created;
329  ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
330  ASSERT_TRUE(created);
331  scoped_ptr<FileSystemOperationContext> context;
332
333  context.reset(NewContext());
334  ASSERT_EQ(base::PLATFORM_FILE_OK,
335      file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
336
337  EXPECT_TRUE(FileExists(from_file));
338  EXPECT_EQ(1020, GetSize(from_file));
339
340  context.reset(NewContext());
341  ASSERT_EQ(base::PLATFORM_FILE_OK,
342            AsyncFileTestHelper::Move(file_system_context(),
343                                      CreateURL(from_file),
344                                      CreateURL(to_file)));
345
346  EXPECT_FALSE(FileExists(from_file));
347  EXPECT_TRUE(FileExists(to_file));
348  EXPECT_EQ(1020, GetSize(to_file));
349}
350
351TEST_F(LocalFileUtilTest, MoveDirectory) {
352  const char *from_dir = "fromdir";
353  const char *from_file = "fromdir/fromfile";
354  const char *to_dir = "todir";
355  const char *to_file = "todir/fromfile";
356  bool created;
357  scoped_ptr<FileSystemOperationContext> context;
358
359  context.reset(NewContext());
360  ASSERT_EQ(base::PLATFORM_FILE_OK,
361      file_util()->CreateDirectory(context.get(), CreateURL(from_dir),
362                                   false, false));
363  ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
364  ASSERT_TRUE(created);
365
366  context.reset(NewContext());
367  ASSERT_EQ(base::PLATFORM_FILE_OK,
368      file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
369
370  EXPECT_TRUE(DirectoryExists(from_dir));
371  EXPECT_TRUE(FileExists(from_file));
372  EXPECT_EQ(1020, GetSize(from_file));
373  EXPECT_FALSE(DirectoryExists(to_dir));
374
375  context.reset(NewContext());
376  ASSERT_EQ(base::PLATFORM_FILE_OK,
377            AsyncFileTestHelper::Move(file_system_context(),
378                                      CreateURL(from_dir),
379                                      CreateURL(to_dir)));
380
381  EXPECT_FALSE(DirectoryExists(from_dir));
382  EXPECT_TRUE(DirectoryExists(to_dir));
383  EXPECT_TRUE(FileExists(to_file));
384  EXPECT_EQ(1020, GetSize(to_file));
385}
386
387}  // namespace fileapi
388