Commit 36b10442 authored by mtomasz@chromium.org's avatar mtomasz@chromium.org

[fsp] Add support for copying files within a provided file system.

This CL adds support for copying files which are on the same provided file
system. Note, that copying between file system instances will be done using
FileStreamWriter.

TEST=unit_tests, browser_tests: *FileSystemProvider*Copy*
BUG=391362

Review URL: https://codereview.chromium.org/384303002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@284910 0039d316-1c4b-4281-b951-d872f2087c98
parent e6e8238a
......@@ -92,4 +92,10 @@ IN_PROC_BROWSER_TEST_F(FileSystemProviderApiTest, CreateFile) {
<< message_;
}
IN_PROC_BROWSER_TEST_F(FileSystemProviderApiTest, CopyEntry) {
ASSERT_TRUE(RunPlatformAppTestWithFlags("file_system_provider/copy_entry",
kFlagLoadAsComponent))
<< message_;
}
} // namespace extensions
......@@ -250,6 +250,14 @@ void FakeProvidedFileSystem::CreateFile(
base::Bind(callback, result));
}
void FakeProvidedFileSystem::CopyEntry(
const base::FilePath& source_path,
const base::FilePath& target_path,
const fileapi::AsyncFileUtil::StatusCallback& callback) {
base::MessageLoopProxy::current()->PostTask(
FROM_HERE, base::Bind(callback, base::File::FILE_OK));
}
const ProvidedFileSystemInfo& FakeProvidedFileSystem::GetFileSystemInfo()
const {
return file_system_info_;
......
......@@ -98,6 +98,10 @@ class FakeProvidedFileSystem : public ProvidedFileSystemInterface {
virtual void CreateFile(
const base::FilePath& file_path,
const fileapi::AsyncFileUtil::StatusCallback& callback) OVERRIDE;
virtual void CopyEntry(
const base::FilePath& source_path,
const base::FilePath& target_path,
const fileapi::AsyncFileUtil::StatusCallback& callback) OVERRIDE;
virtual const ProvidedFileSystemInfo& GetFileSystemInfo() const OVERRIDE;
virtual RequestManager* GetRequestManager() OVERRIDE;
virtual base::WeakPtr<ProvidedFileSystemInterface> GetWeakPtr() OVERRIDE;
......
......@@ -159,6 +159,33 @@ void OnCreateFileForEnsureFileExists(
BrowserThread::IO, FROM_HERE, base::Bind(callback, error, created));
}
// Executes CopyEntry on the UI thread.
void CopyEntryOnUIThread(
scoped_ptr<fileapi::FileSystemOperationContext> context,
const fileapi::FileSystemURL& source_url,
const fileapi::FileSystemURL& target_url,
const fileapi::AsyncFileUtil::StatusCallback& callback) {
util::FileSystemURLParser source_parser(source_url);
util::FileSystemURLParser target_parser(target_url);
if (!source_parser.Parse() || !target_parser.Parse() ||
source_parser.file_system() != target_parser.file_system()) {
callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
return;
}
target_parser.file_system()->CopyEntry(
source_parser.file_path(), target_parser.file_path(), callback);
}
// Routes the response of CopyEntry to a callback of CopyLocalFile() on the
// IO thread.
void OnCopyEntry(const fileapi::AsyncFileUtil::StatusCallback& callback,
base::File::Error result) {
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE, base::Bind(callback, result));
}
} // namespace
ProviderAsyncFileUtil::ProviderAsyncFileUtil() {}
......@@ -269,7 +296,15 @@ void ProviderAsyncFileUtil::CopyFileLocal(
const CopyFileProgressCallback& progress_callback,
const StatusCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
callback.Run(base::File::FILE_ERROR_ACCESS_DENIED);
// TODO(mtomasz): Consier adding support for options (preserving last modified
// time) as well as the progress callback.
BrowserThread::PostTask(BrowserThread::UI,
FROM_HERE,
base::Bind(&CopyEntryOnUIThread,
base::Passed(&context),
src_url,
dest_url,
base::Bind(&OnCopyEntry, callback)));
}
void ProviderAsyncFileUtil::MoveFileLocal(
......
......@@ -337,9 +337,10 @@ TEST_F(FileSystemProviderProviderAsyncFileUtilTest, CopyFileLocal) {
fileapi::FileSystemOperation::OPTION_NONE,
base::Bind(&EventLogger::OnCopyFileProgress, base::Unretained(&logger)),
base::Bind(&EventLogger::OnStatus, base::Unretained(&logger)));
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(logger.result());
EXPECT_EQ(base::File::FILE_ERROR_ACCESS_DENIED, *logger.result());
EXPECT_EQ(base::File::FILE_OK, *logger.result());
}
TEST_F(FileSystemProviderProviderAsyncFileUtilTest, MoveFileLocal) {
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/file_system_provider/operations/copy_entry.h"
#include <string>
#include "chrome/common/extensions/api/file_system_provider.h"
#include "chrome/common/extensions/api/file_system_provider_internal.h"
namespace chromeos {
namespace file_system_provider {
namespace operations {
CopyEntry::CopyEntry(extensions::EventRouter* event_router,
const ProvidedFileSystemInfo& file_system_info,
const base::FilePath& source_path,
const base::FilePath& target_path,
const fileapi::AsyncFileUtil::StatusCallback& callback)
: Operation(event_router, file_system_info),
source_path_(source_path),
target_path_(target_path),
callback_(callback) {
}
CopyEntry::~CopyEntry() {
}
bool CopyEntry::Execute(int request_id) {
scoped_ptr<base::DictionaryValue> values(new base::DictionaryValue);
values->SetString("sourcePath", source_path_.AsUTF8Unsafe());
values->SetString("targetPath", target_path_.AsUTF8Unsafe());
return SendEvent(
request_id,
extensions::api::file_system_provider::OnCopyEntryRequested::kEventName,
values.Pass());
}
void CopyEntry::OnSuccess(int /* request_id */,
scoped_ptr<RequestValue> /* result */,
bool has_more) {
callback_.Run(base::File::FILE_OK);
}
void CopyEntry::OnError(int /* request_id */,
scoped_ptr<RequestValue> /* result */,
base::File::Error error) {
callback_.Run(error);
}
} // namespace operations
} // namespace file_system_provider
} // namespace chromeos
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_COPY_ENTRY_H_
#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_COPY_ENTRY_H_
#include "base/files/file.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
#include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
#include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
#include "chrome/browser/chromeos/file_system_provider/request_value.h"
#include "webkit/browser/fileapi/async_file_util.h"
namespace base {
class FilePath;
} // namespace base
namespace extensions {
class EventRouter;
} // namespace extensions
namespace chromeos {
namespace file_system_provider {
namespace operations {
// Copies an entry (recursively if a directory). Created per request.
class CopyEntry : public Operation {
public:
CopyEntry(extensions::EventRouter* event_router,
const ProvidedFileSystemInfo& file_system_info,
const base::FilePath& source_path,
const base::FilePath& target_path,
const fileapi::AsyncFileUtil::StatusCallback& callback);
virtual ~CopyEntry();
// Operation overrides.
virtual bool Execute(int request_id) OVERRIDE;
virtual void OnSuccess(int request_id,
scoped_ptr<RequestValue> result,
bool has_more) OVERRIDE;
virtual void OnError(int request_id,
scoped_ptr<RequestValue> result,
base::File::Error error) OVERRIDE;
private:
base::FilePath source_path_;
base::FilePath target_path_;
const fileapi::AsyncFileUtil::StatusCallback callback_;
DISALLOW_COPY_AND_ASSIGN(CopyEntry);
};
} // namespace operations
} // namespace file_system_provider
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_COPY_ENTRY_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/file_system_provider/operations/copy_entry.h"
#include <string>
#include <vector>
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
#include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
#include "chrome/common/extensions/api/file_system_provider.h"
#include "chrome/common/extensions/api/file_system_provider_internal.h"
#include "extensions/browser/event_router.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/browser/fileapi/async_file_util.h"
namespace chromeos {
namespace file_system_provider {
namespace operations {
namespace {
const char kExtensionId[] = "mbflcebpggnecokmikipoihdbecnjfoj";
const char kFileSystemId[] = "testing-file-system";
const int kRequestId = 2;
const base::FilePath::CharType kSourcePath[] = "/bunny/and/bear/happy";
const base::FilePath::CharType kTargetPath[] = "/kitty/and/puppy/happy";
} // namespace
class FileSystemProviderOperationsCopyEntryTest : public testing::Test {
protected:
FileSystemProviderOperationsCopyEntryTest() {}
virtual ~FileSystemProviderOperationsCopyEntryTest() {}
virtual void SetUp() OVERRIDE {
file_system_info_ =
ProvidedFileSystemInfo(kExtensionId,
kFileSystemId,
"" /* file_system_name */,
true /* writable */,
base::FilePath() /* mount_path */);
}
ProvidedFileSystemInfo file_system_info_;
};
TEST_F(FileSystemProviderOperationsCopyEntryTest, Execute) {
util::LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */);
util::StatusCallbackLog callback_log;
CopyEntry copy_entry(NULL,
file_system_info_,
base::FilePath::FromUTF8Unsafe(kSourcePath),
base::FilePath::FromUTF8Unsafe(kTargetPath),
base::Bind(&util::LogStatusCallback, &callback_log));
copy_entry.SetDispatchEventImplForTesting(
base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
base::Unretained(&dispatcher)));
EXPECT_TRUE(copy_entry.Execute(kRequestId));
ASSERT_EQ(1u, dispatcher.events().size());
extensions::Event* event = dispatcher.events()[0];
EXPECT_EQ(
extensions::api::file_system_provider::OnCopyEntryRequested::kEventName,
event->event_name);
base::ListValue* event_args = event->event_args.get();
ASSERT_EQ(1u, event_args->GetSize());
base::DictionaryValue* options = NULL;
ASSERT_TRUE(event_args->GetDictionary(0, &options));
std::string event_file_system_id;
EXPECT_TRUE(options->GetString("fileSystemId", &event_file_system_id));
EXPECT_EQ(kFileSystemId, event_file_system_id);
int event_request_id = -1;
EXPECT_TRUE(options->GetInteger("requestId", &event_request_id));
EXPECT_EQ(kRequestId, event_request_id);
std::string event_source_path;
EXPECT_TRUE(options->GetString("sourcePath", &event_source_path));
EXPECT_EQ(kSourcePath, event_source_path);
std::string event_target_path;
EXPECT_TRUE(options->GetString("targetPath", &event_target_path));
EXPECT_EQ(kTargetPath, event_target_path);
}
TEST_F(FileSystemProviderOperationsCopyEntryTest, Execute_NoListener) {
util::LoggingDispatchEventImpl dispatcher(false /* dispatch_reply */);
util::StatusCallbackLog callback_log;
CopyEntry copy_entry(NULL,
file_system_info_,
base::FilePath::FromUTF8Unsafe(kSourcePath),
base::FilePath::FromUTF8Unsafe(kTargetPath),
base::Bind(&util::LogStatusCallback, &callback_log));
copy_entry.SetDispatchEventImplForTesting(
base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
base::Unretained(&dispatcher)));
EXPECT_FALSE(copy_entry.Execute(kRequestId));
}
TEST_F(FileSystemProviderOperationsCopyEntryTest, OnSuccess) {
util::LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */);
util::StatusCallbackLog callback_log;
CopyEntry copy_entry(NULL,
file_system_info_,
base::FilePath::FromUTF8Unsafe(kSourcePath),
base::FilePath::FromUTF8Unsafe(kTargetPath),
base::Bind(&util::LogStatusCallback, &callback_log));
copy_entry.SetDispatchEventImplForTesting(
base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
base::Unretained(&dispatcher)));
EXPECT_TRUE(copy_entry.Execute(kRequestId));
copy_entry.OnSuccess(kRequestId,
scoped_ptr<RequestValue>(new RequestValue()),
false /* has_more */);
ASSERT_EQ(1u, callback_log.size());
EXPECT_EQ(base::File::FILE_OK, callback_log[0]);
}
TEST_F(FileSystemProviderOperationsCopyEntryTest, OnError) {
util::LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */);
util::StatusCallbackLog callback_log;
CopyEntry copy_entry(NULL,
file_system_info_,
base::FilePath::FromUTF8Unsafe(kSourcePath),
base::FilePath::FromUTF8Unsafe(kTargetPath),
base::Bind(&util::LogStatusCallback, &callback_log));
copy_entry.SetDispatchEventImplForTesting(
base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
base::Unretained(&dispatcher)));
EXPECT_TRUE(copy_entry.Execute(kRequestId));
copy_entry.OnError(kRequestId,
scoped_ptr<RequestValue>(new RequestValue()),
base::File::FILE_ERROR_TOO_MANY_OPENED);
ASSERT_EQ(1u, callback_log.size());
EXPECT_EQ(base::File::FILE_ERROR_TOO_MANY_OPENED, callback_log[0]);
}
} // namespace operations
} // namespace file_system_provider
} // namespace chromeos
......@@ -8,6 +8,7 @@
#include "base/files/file.h"
#include "chrome/browser/chromeos/file_system_provider/notification_manager.h"
#include "chrome/browser/chromeos/file_system_provider/operations/close_file.h"
#include "chrome/browser/chromeos/file_system_provider/operations/copy_entry.h"
#include "chrome/browser/chromeos/file_system_provider/operations/create_directory.h"
#include "chrome/browser/chromeos/file_system_provider/operations/create_file.h"
#include "chrome/browser/chromeos/file_system_provider/operations/delete_entry.h"
......@@ -179,6 +180,22 @@ void ProvidedFileSystem::CreateFile(
}
}
void ProvidedFileSystem::CopyEntry(
const base::FilePath& source_path,
const base::FilePath& target_path,
const fileapi::AsyncFileUtil::StatusCallback& callback) {
if (!request_manager_.CreateRequest(
COPY_ENTRY,
scoped_ptr<RequestManager::HandlerInterface>(
new operations::CopyEntry(event_router_,
file_system_info_,
source_path,
target_path,
callback)))) {
callback.Run(base::File::FILE_ERROR_SECURITY);
}
}
const ProvidedFileSystemInfo& ProvidedFileSystem::GetFileSystemInfo() const {
return file_system_info_;
}
......
......@@ -69,6 +69,10 @@ class ProvidedFileSystem : public ProvidedFileSystemInterface {
virtual void CreateFile(
const base::FilePath& file_path,
const fileapi::AsyncFileUtil::StatusCallback& callback) OVERRIDE;
virtual void CopyEntry(
const base::FilePath& source_path,
const base::FilePath& target_path,
const fileapi::AsyncFileUtil::StatusCallback& callback) OVERRIDE;
virtual const ProvidedFileSystemInfo& GetFileSystemInfo() const OVERRIDE;
virtual RequestManager* GetRequestManager() OVERRIDE;
virtual base::WeakPtr<ProvidedFileSystemInterface> GetWeakPtr() OVERRIDE;
......
......@@ -122,6 +122,13 @@ class ProvidedFileSystemInterface {
bool recursive,
const fileapi::AsyncFileUtil::StatusCallback& callback) = 0;
// Requests copying an entry (recursively in case of a directory) within the
// same file system.
virtual void CopyEntry(
const base::FilePath& source_path,
const base::FilePath& target_path,
const fileapi::AsyncFileUtil::StatusCallback& callback) = 0;
// Returns a provided file system info for this file system.
virtual const ProvidedFileSystemInfo& GetFileSystemInfo() const = 0;
......
......@@ -38,6 +38,8 @@ std::string RequestTypeToString(RequestType type) {
return "DELETE_ENTRY";
case CREATE_FILE:
return "CREATE_FILE";
case COPY_ENTRY:
return "COPY_ENTRY";
case TESTING:
return "TESTING";
}
......
......@@ -33,6 +33,7 @@ enum RequestType {
CREATE_DIRECTORY,
DELETE_ENTRY,
CREATE_FILE,
COPY_ENTRY,
TESTING
};
......
......@@ -401,6 +401,8 @@
'browser/chromeos/file_system_provider/observer.h',
'browser/chromeos/file_system_provider/operations/close_file.cc',
'browser/chromeos/file_system_provider/operations/close_file.h',
'browser/chromeos/file_system_provider/operations/copy_entry.cc',
'browser/chromeos/file_system_provider/operations/copy_entry.h',
'browser/chromeos/file_system_provider/operations/create_directory.cc',
'browser/chromeos/file_system_provider/operations/create_directory.h',
'browser/chromeos/file_system_provider/operations/create_file.cc',
......
......@@ -757,6 +757,7 @@
'browser/chromeos/file_system_provider/operations/test_util.cc',
'browser/chromeos/file_system_provider/operations/test_util.h',
'browser/chromeos/file_system_provider/operations/close_file_unittest.cc',
'browser/chromeos/file_system_provider/operations/copy_entry_unittest.cc',
'browser/chromeos/file_system_provider/operations/create_directory_unittest.cc',
'browser/chromeos/file_system_provider/operations/create_file_unittest.cc',
'browser/chromeos/file_system_provider/operations/delete_entry_unittest.cc',
......
......@@ -133,6 +133,14 @@ namespace fileSystemProvider {
DOMString filePath;
};
// Options for the <code>onCopyEntryRequested()</code> event.
dictionary CopyEntryRequestedOptions {
DOMString fileSystemId;
long requestId;
DOMString sourcePath;
DOMString targetPath;
};
// Callback to receive the result of mount() function.
callback MountCallback = void([nodoc, instanceOf=DOMError] object error);
......@@ -266,6 +274,13 @@ namespace fileSystemProvider {
CreateFileRequestedOptions options,
ProviderSuccessCallback successCallback,
ProviderErrorCallback errorCallback);
// Raised when copying an entry (recursively if a directory) is requested.
// If an error occurs, then <code>errorCallback</code> must be called.
[maxListeners=1, nodoc] static void onCopyEntryRequested(
CopyEntryRequestedOptions options,
ProviderSuccessCallback successCallback,
ProviderErrorCallback errorCallback);
};
};
......@@ -212,4 +212,8 @@ eventBindings.registerArgumentMassager(
'fileSystemProvider.onCreateFileRequested',
massageArgumentsDefault);
eventBindings.registerArgumentMassager(
'fileSystemProvider.onCopyEntryRequested',
massageArgumentsDefault);
exports.binding = binding.generate();
{
"key": "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDOuXEIuoK1kAkBe0SKiJn/N9oNn3oUxGa4dwj40MnJqPn+w0aR2vuyocm0R4Drp67aYwtLjOVPF4CICRq6ICP6eU07gGwQxGdZ7HJASXV8hm0tab5I70oJmRLfFJyVAMCeWlFaOGq05v2i6EbifZM0qO5xALKNGQt+yjXi5INM5wIBIw==",
"name": "chrome.fileSystemProvider.onCopyEntryRequested",
"version": "0.1",
"manifest_version": 2,
"description":
"Test for chrome.fileSystemProvider.onCopyEntryRequested().",
"permissions": [
"fileSystemProvider",
"fileBrowserPrivate",
"fileBrowserHandler"
],
"app": {
"background": {
"scripts": [
"chrome-extension://gfnblenhaahcnmfdbebgincjohfkbnch/test_util.js",
"test.js"
]
}
}
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
/**
* @type {Object}
* @const
*/
var TESTING_FILE = Object.freeze({
isDirectory: false,
name: 'kitty',
size: 1024,
modificationTime: new Date(2014, 4, 28, 10, 39, 15)
});
/**
* @type {string}
* @const
*/
var TESTING_NEW_FILE_NAME = 'puppy.txt';
/**
* Copies an entry within the same file system.
*
* @param {CopyEntryRequestedOptions} options Options.
* @param {function(Object)} onSuccess Success callback
* @param {function(string)} onError Error callback with an error code.
*/
function onCopyEntryRequested(options, onSuccess, onError) {
if (options.fileSystemId != test_util.FILE_SYSTEM_ID) {
onError('SECURITY'); // enum ProviderError.
return;
}
if (options.sourcePath == '/') {
onError('INVALID_OPERATION');
return;
}
if (!(options.sourcePath in test_util.defaultMetadata)) {
onError('NOT_FOUND');
return;
}
if (options.targetPath in test_util.defaultMetadata) {
onError('EXISTS');
return;
}
// Copy the metadata, but change the 'name' field.
var newMetadata =
JSON.parse(JSON.stringify(test_util.defaultMetadata[options.sourcePath]));
newMetadata.name = options.targetPath.split('/').pop();
test_util.defaultMetadata[options.targetPath] = newMetadata;
onSuccess(); // enum ProviderError.
}
/**
* Sets up the tests. Called once per all test cases. In case of a failure,
* the callback is not called.
*
* @param {function()} callback Success callback.
*/
function setUp(callback) {
chrome.fileSystemProvider.onGetMetadataRequested.addListener(
test_util.onGetMetadataRequestedDefault);
test_util.defaultMetadata['/' + TESTING_FILE.name] = TESTING_FILE;
chrome.fileSystemProvider.onCopyEntryRequested.addListener(
onCopyEntryRequested);
test_util.mountFileSystem(callback);
}
/**
* Runs all of the test cases, one by one.
*/
function runTests() {
chrome.test.runTests([
// Copy an existing file to a non-existing destination. Should succeed.
function copyEntrySuccess() {
var onSuccess = chrome.test.callbackPass();
test_util.fileSystem.root.getFile(
TESTING_FILE.name, {create: false},
function(sourceEntry) {
chrome.test.assertEq(TESTING_FILE.name, sourceEntry.name);
chrome.test.assertFalse(sourceEntry.isDirectory);
sourceEntry.copyTo(
test_util.fileSystem.root,
TESTING_NEW_FILE_NAME,
function(targetEntry) {
chrome.test.assertEq(TESTING_NEW_FILE_NAME, targetEntry.name);
chrome.test.assertFalse(targetEntry.isDirectory);
onSuccess();
}, function(error) {
chrome.test.fail(error.name);
});
}, function(error) {
chrome.test.fail(error.name);
});
},
// Copy an existing file to a location which already holds a file.
// Should fail.
function copyEntryExistsError() {
var onSuccess = chrome.test.callbackPass();
test_util.fileSystem.root.getFile(
TESTING_FILE.name, {create: false},
function(sourceEntry) {
chrome.test.assertEq(TESTING_FILE.name, sourceEntry.name);
chrome.test.assertFalse(sourceEntry.isDirectory);
sourceEntry.copyTo(
test_util.fileSystem.root,
TESTING_NEW_FILE_NAME,
function(targetEntry) {
chrome.test.fail('Succeeded, but should fail.');
}, function(error) {
chrome.test.assertEq('InvalidModificationError', error.name);
onSuccess();
});
}, function(error) {
chrome.test.fail(error.name);
});
}
]);
}
// Setup and run all of the test cases.
setUp(runTests);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment