15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)#include "base/file_util.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h"
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/scoped_temp_dir.h"
10ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "base/message_loop/message_loop.h"
11868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/message_loop/message_loop_proxy.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/platform_file.h"
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/strings/sys_string_conversions.h"
14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h"
1690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "webkit/browser/fileapi/async_file_test_helper.h"
1790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "webkit/browser/fileapi/file_system_context.h"
1890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "webkit/browser/fileapi/file_system_file_util.h"
1990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "webkit/browser/fileapi/file_system_operation_context.h"
2090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "webkit/browser/fileapi/local_file_util.h"
2190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "webkit/browser/fileapi/mock_file_system_context.h"
2290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "webkit/browser/fileapi/native_file_util.h"
2390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "webkit/common/fileapi/file_system_types.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace fileapi {
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)namespace {
2890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
2990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const GURL kOrigin("http://foo/");
3090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const FileSystemType kFileSystemType = kFileSystemTypeTest;
3190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
3290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}  // namespace
3390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class LocalFileUtilTest : public testing::Test {
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
3690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  LocalFileUtilTest() {}
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void SetUp() {
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
4090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    file_system_context_ = CreateFileSystemContextForTesting(
4190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        NULL, data_dir_.path());
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void TearDown() {
4590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    file_system_context_ = NULL;
4690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    base::MessageLoop::current()->RunUntilIdle();
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) protected:
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FileSystemOperationContext* NewContext() {
5190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    FileSystemOperationContext* context =
52868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        new FileSystemOperationContext(file_system_context_.get());
5390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    context->set_update_observers(
5490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        *file_system_context_->GetUpdateObservers(kFileSystemType));
5590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    context->set_root_path(data_dir_.path());
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return context;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  LocalFileUtil* file_util() {
6090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return static_cast<LocalFileUtil*>(
6190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        file_system_context_->GetFileUtil(kFileSystemType));
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  FileSystemURL CreateURL(const std::string& file_name) {
6590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return file_system_context_->CreateCrackedFileSystemURL(
6690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        kOrigin, kFileSystemType, base::FilePath().FromUTF8Unsafe(file_name));
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath LocalPath(const char *file_name) {
7090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    base::FilePath path;
7190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    scoped_ptr<FileSystemOperationContext> context(NewContext());
7290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    file_util()->GetLocalFilePath(context.get(), CreateURL(file_name), &path);
7390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return path;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool FileExists(const char *file_name) {
777dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    return base::PathExists(LocalPath(file_name)) &&
787dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        !base::DirectoryExists(LocalPath(file_name));
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool DirectoryExists(const char *file_name) {
827dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    return base::DirectoryExists(LocalPath(file_name));
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int64 GetSize(const char *file_name) {
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::PlatformFileInfo info;
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_util::GetFileInfo(LocalPath(file_name), &info);
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return info.size;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::PlatformFileError CreateFile(const char* file_name,
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                     base::PlatformFile* file_handle,
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                     bool* created) {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int file_flags = base::PLATFORM_FILE_CREATE |
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_ASYNC;
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_ptr<FileSystemOperationContext> context(NewContext());
9890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return file_util()->CreateOrOpen(
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        context.get(),
10090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        CreateURL(file_name),
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        file_flags, file_handle, created);
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::PlatformFileError EnsureFileExists(const char* file_name,
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bool* created) {
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_ptr<FileSystemOperationContext> context(NewContext());
10790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return file_util()->EnsureFileExists(
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        context.get(),
10990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        CreateURL(file_name), created);
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FileSystemContext* file_system_context() {
113868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    return file_system_context_.get();
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
117b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  base::MessageLoop message_loop_;
11890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  scoped_refptr<FileSystemContext> file_system_context_;
11990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  base::ScopedTempDir data_dir_;
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(LocalFileUtilTest);
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(LocalFileUtilTest, CreateAndClose) {
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char *file_name = "test_file";
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::PlatformFile file_handle;
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool created;
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK,
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            CreateFile(file_name, &file_handle, &created));
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(created);
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(FileExists(file_name));
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(0, GetSize(file_name));
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<FileSystemOperationContext> context(NewContext());
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(base::PLATFORM_FILE_OK,
13790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      file_util()->Close(context.get(), file_handle));
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
140c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// file_util::CreateSymbolicLink is only supported on POSIX.
141c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#if defined(OS_POSIX)
142c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)TEST_F(LocalFileUtilTest, CreateFailForSymlink) {
143c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Create symlink target file.
144c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const char *target_name = "symlink_target";
145c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::PlatformFile target_handle;
146c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  bool symlink_target_created = false;
147c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK,
148c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            CreateFile(target_name, &target_handle, &symlink_target_created));
149c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ASSERT_TRUE(symlink_target_created);
150c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::FilePath target_path = LocalPath(target_name);
151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
152c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Create symlink where target must be real file.
153c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const char *symlink_name = "symlink_file";
154c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::FilePath symlink_path = LocalPath(symlink_name);
155c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ASSERT_TRUE(file_util::CreateSymbolicLink(target_path, symlink_path));
156c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ASSERT_TRUE(FileExists(symlink_name));
157c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
158c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Try to open the symlink file which should fail.
159c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  scoped_ptr<FileSystemOperationContext> context(NewContext());
16090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  FileSystemURL url = CreateURL(symlink_name);
161c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int file_flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ;
162c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::PlatformFile file_handle;
163c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  bool created = false;
16490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, file_util()->CreateOrOpen(
165c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      context.get(), url, file_flags, &file_handle, &created));
166c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_FALSE(created);
167c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
168c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#endif
169c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(LocalFileUtilTest, EnsureFileExists) {
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char *file_name = "foobar";
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool created;
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(file_name, &created));
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(created);
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(FileExists(file_name));
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(0, GetSize(file_name));
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(file_name, &created));
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(created);
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(LocalFileUtilTest, TouchFile) {
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char *file_name = "test_file";
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::PlatformFile file_handle;
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool created;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK,
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            CreateFile(file_name, &file_handle, &created));
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(created);
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<FileSystemOperationContext> context(NewContext());
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::PlatformFileInfo info;
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(file_util::GetFileInfo(LocalPath(file_name), &info));
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const base::Time new_accessed =
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      info.last_accessed + base::TimeDelta::FromHours(10);
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const base::Time new_modified =
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      info.last_modified + base::TimeDelta::FromHours(5);
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(base::PLATFORM_FILE_OK,
20190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            file_util()->Touch(context.get(), CreateURL(file_name),
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              new_accessed, new_modified));
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(file_util::GetFileInfo(LocalPath(file_name), &info));
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(new_accessed, info.last_accessed);
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(new_modified, info.last_modified);
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(base::PLATFORM_FILE_OK,
20990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            file_util()->Close(context.get(), file_handle));
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(LocalFileUtilTest, TouchDirectory) {
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char *dir_name = "test_dir";
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<FileSystemOperationContext> context(NewContext());
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK,
21690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            file_util()->CreateDirectory(context.get(),
21790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                        CreateURL(dir_name),
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        false /* exclusive */,
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        false /* recursive */));
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::PlatformFileInfo info;
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(file_util::GetFileInfo(LocalPath(dir_name), &info));
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const base::Time new_accessed =
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      info.last_accessed + base::TimeDelta::FromHours(10);
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const base::Time new_modified =
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      info.last_modified + base::TimeDelta::FromHours(5);
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(base::PLATFORM_FILE_OK,
22990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            file_util()->Touch(context.get(), CreateURL(dir_name),
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              new_accessed, new_modified));
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(file_util::GetFileInfo(LocalPath(dir_name), &info));
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(new_accessed, info.last_accessed);
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(new_modified, info.last_modified);
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(LocalFileUtilTest, Truncate) {
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char *file_name = "truncated";
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool created;
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(file_name, &created));
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(created);
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<FileSystemOperationContext> context;
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.reset(NewContext());
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK,
24790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      file_util()->Truncate(context.get(), CreateURL(file_name), 1020));
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(FileExists(file_name));
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(1020, GetSize(file_name));
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(LocalFileUtilTest, CopyFile) {
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char *from_file = "fromfile";
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char *to_file1 = "tofile1";
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char *to_file2 = "tofile2";
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool created;
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(created);
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<FileSystemOperationContext> context;
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.reset(NewContext());
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK,
26490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(FileExists(from_file));
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(1020, GetSize(from_file));
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK,
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            AsyncFileTestHelper::Copy(file_system_context(),
27190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                      CreateURL(from_file),
27290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                      CreateURL(to_file1)));
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.reset(NewContext());
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK,
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            AsyncFileTestHelper::Copy(file_system_context(),
27790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                      CreateURL(from_file),
27890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                      CreateURL(to_file2)));
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(FileExists(from_file));
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(1020, GetSize(from_file));
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(FileExists(to_file1));
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(1020, GetSize(to_file1));
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(FileExists(to_file2));
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(1020, GetSize(to_file2));
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(LocalFileUtilTest, CopyDirectory) {
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char *from_dir = "fromdir";
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char *from_file = "fromdir/fromfile";
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char *to_dir = "todir";
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char *to_file = "todir/fromfile";
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool created;
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<FileSystemOperationContext> context;
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.reset(NewContext());
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK,
29890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      file_util()->CreateDirectory(context.get(), CreateURL(from_dir),
29990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                   false, false));
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(created);
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.reset(NewContext());
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK,
30590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(DirectoryExists(from_dir));
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(FileExists(from_file));
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(1020, GetSize(from_file));
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(DirectoryExists(to_dir));
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.reset(NewContext());
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK,
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            AsyncFileTestHelper::Copy(file_system_context(),
31590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                      CreateURL(from_dir), CreateURL(to_dir)));
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(DirectoryExists(from_dir));
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(FileExists(from_file));
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(1020, GetSize(from_file));
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(DirectoryExists(to_dir));
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(FileExists(to_file));
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(1020, GetSize(to_file));
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(LocalFileUtilTest, MoveFile) {
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char *from_file = "fromfile";
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char *to_file = "tofile";
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool created;
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(created);
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<FileSystemOperationContext> context;
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.reset(NewContext());
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK,
33590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(FileExists(from_file));
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(1020, GetSize(from_file));
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.reset(NewContext());
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK,
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            AsyncFileTestHelper::Move(file_system_context(),
34390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                      CreateURL(from_file),
34490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                      CreateURL(to_file)));
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(FileExists(from_file));
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(FileExists(to_file));
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(1020, GetSize(to_file));
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(LocalFileUtilTest, MoveDirectory) {
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char *from_dir = "fromdir";
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char *from_file = "fromdir/fromfile";
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char *to_dir = "todir";
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char *to_file = "todir/fromfile";
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool created;
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<FileSystemOperationContext> context;
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.reset(NewContext());
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK,
36190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      file_util()->CreateDirectory(context.get(), CreateURL(from_dir),
36290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                   false, false));
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(created);
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.reset(NewContext());
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK,
36890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(DirectoryExists(from_dir));
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(FileExists(from_file));
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(1020, GetSize(from_file));
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(DirectoryExists(to_dir));
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.reset(NewContext());
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(base::PLATFORM_FILE_OK,
3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            AsyncFileTestHelper::Move(file_system_context(),
37890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                      CreateURL(from_dir),
37990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                      CreateURL(to_dir)));
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(DirectoryExists(from_dir));
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(DirectoryExists(to_dir));
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(FileExists(to_file));
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(1020, GetSize(to_file));
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace fileapi
388