-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Currently, it is not possible to move or rename a file or directory without creating a new file/directory, copying over data (recursively, in the case of a directory), and removing the original. This CL allows for the atomic moving of a file or directory on the local file system without needing to duplicate data. Moves to non-local file systems will are not guaranteed to be atomic and will involve duplicating data. PR: WICG/file-system-access#317 Bug: 1140805 Change-Id: I774ed1d9616249b6ecc80783db48a7bfee915aab
- Loading branch information
1 parent
7eb7372
commit 10f0108
Showing
3 changed files
with
310 additions
and
0 deletions.
There are no files selected for viewing
10 changes: 10 additions & 0 deletions
10
file-system-access/local_FileSystemBaseHandle-move-manual.https.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<!doctype html> | ||
<meta charset=utf-8> | ||
|
||
<script src="/resources/testharness.js"></script> | ||
<script src="/resources/testharnessreport.js"></script> | ||
<script src="/resources/testdriver.js"></script> | ||
<script src="/resources/testdriver-vendor.js"></script> | ||
<script src="resources/test-helpers.js"></script> | ||
<script src="resources/local-fs-test-helpers.js"></script> | ||
<script src="script-tests/FileSystemBaseHandle-move.js"></script> |
3 changes: 3 additions & 0 deletions
3
file-system-access/sandboxed_FileSystemBaseHandle-move.https.any.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// META: script=resources/test-helpers.js | ||
// META: script=resources/sandboxed-fs-test-helpers.js | ||
// META: script=script-tests/FileSystemBaseHandle-move.js |
297 changes: 297 additions & 0 deletions
297
file-system-access/script-tests/FileSystemBaseHandle-move.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,297 @@ | ||
// META: script=resources/test-helpers.js | ||
|
||
// 'use strict'; | ||
|
||
directory_test(async (t, root) => { | ||
const handle = await createFileWithContents(t, 'file-before', 'foo', root); | ||
await handle.move('file-after'); | ||
|
||
assert_array_equals(await getSortedDirectoryEntries(root), ['file-after']); | ||
assert_equals(await getFileContents(handle), 'foo'); | ||
assert_equals(await getFileSize(handle), 3); | ||
}, 'move(name) to rename a file'); | ||
|
||
directory_test(async (t, root) => { | ||
const handle = await createFileWithContents(t, 'file-before', 'foo', root); | ||
await handle.move(root, 'file-after'); | ||
|
||
assert_array_equals(await getSortedDirectoryEntries(root), ['file-after']); | ||
assert_equals(await getFileContents(handle), 'foo'); | ||
assert_equals(await getFileSize(handle), 3); | ||
}, 'move(dir, name) to rename a file'); | ||
|
||
directory_test(async (t, root) => { | ||
const handle = await createFileWithContents(t, 'file-before', 'foo', root); | ||
await promise_rejects_js(t, TypeError, handle.move('')); | ||
|
||
assert_array_equals(await getSortedDirectoryEntries(root), ['file-before']); | ||
assert_equals(await getFileContents(handle), 'foo'); | ||
assert_equals(await getFileSize(handle), 3); | ||
}, 'move("") to rename a file fails'); | ||
|
||
directory_test(async (t, root) => { | ||
const dir = await root.getDirectoryHandle('dir-before', {create: true}); | ||
await dir.move('dir-after'); | ||
|
||
assert_array_equals(await getSortedDirectoryEntries(root), ['dir-after/']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir), []); | ||
}, 'move(name) to rename an empty directory'); | ||
|
||
directory_test(async (t, root) => { | ||
const dir = await root.getDirectoryHandle('dir-before', {create: true}); | ||
await dir.move(root, 'dir-after'); | ||
|
||
assert_array_equals(await getSortedDirectoryEntries(root), ['dir-after/']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir), []); | ||
}, 'move(dir, name) to rename an empty directory'); | ||
|
||
directory_test(async (t, root) => { | ||
const dir = await root.getDirectoryHandle('dir-before', {create: true}); | ||
await promise_rejects_js(t, TypeError, dir.move('')); | ||
|
||
assert_array_equals(await getSortedDirectoryEntries(root), ['dir-before/']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir), []); | ||
}, 'move("") to rename an empty directory fails'); | ||
|
||
directory_test(async (t, root) => { | ||
const dir = await root.getDirectoryHandle('dir-before', {create: true}); | ||
await createFileWithContents(t, 'file-in-dir', 'abc', dir); | ||
await dir.move('dir-after'); | ||
|
||
assert_array_equals(await getSortedDirectoryEntries(root), ['dir-after/']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir), ['file-in-dir']); | ||
}, 'move(name) to rename a non-empty directory'); | ||
|
||
directory_test(async (t, root) => { | ||
const dir = await root.getDirectoryHandle('dir-before', {create: true}); | ||
await createFileWithContents(t, 'file-in-dir', 'abc', dir); | ||
await dir.move(root, 'dir-after'); | ||
|
||
assert_array_equals(await getSortedDirectoryEntries(root), ['dir-after/']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir), ['file-in-dir']); | ||
}, 'move(dir, name) to rename a non-empty directory'); | ||
|
||
directory_test(async (t, root) => { | ||
const dir_src = await root.getDirectoryHandle('dir-src', {create: true}); | ||
const dir_dest = await root.getDirectoryHandle('dir-dest', {create: true}); | ||
const file = await createFileWithContents(t, 'file', 'abc', dir_src); | ||
await file.move(dir_dest, null); | ||
|
||
assert_array_equals( | ||
await getSortedDirectoryEntries(root), ['dir-dest/', 'dir-src/']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir_src), []); | ||
assert_array_equals(await getSortedDirectoryEntries(dir_dest), ['file']); | ||
assert_equals(await getFileContents(file), 'abc'); | ||
assert_equals(await getFileSize(file), 3); | ||
}, 'move(dir, _) to move a file to a new directory'); | ||
|
||
directory_test(async (t, root) => { | ||
const dir_src = await root.getDirectoryHandle('dir-src', {create: true}); | ||
const dir_dest = await root.getDirectoryHandle('dir-dest', {create: true}); | ||
const file = await createFileWithContents(t, 'file', 'abc', dir_src); | ||
await file.move(dir_dest, ''); | ||
|
||
assert_array_equals( | ||
await getSortedDirectoryEntries(root), ['dir-dest/', 'dir-src/']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir_src), []); | ||
assert_array_equals(await getSortedDirectoryEntries(dir_dest), ['file']); | ||
assert_equals(await getFileContents(file), 'abc'); | ||
assert_equals(await getFileSize(file), 3); | ||
}, 'move(dir, "") to move a file to a new directory'); | ||
|
||
directory_test(async (t, root) => { | ||
const dir_src = await root.getDirectoryHandle('dir-src', {create: true}); | ||
const dir_dest = await root.getDirectoryHandle('dir-dest', {create: true}); | ||
const file = | ||
await createFileWithContents(t, 'file-in-dir-src', 'abc', dir_src); | ||
await file.move(dir_dest, 'file-in-dir-dest'); | ||
|
||
assert_array_equals( | ||
await getSortedDirectoryEntries(root), ['dir-dest/', 'dir-src/']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir_src), []); | ||
assert_array_equals( | ||
await getSortedDirectoryEntries(dir_dest), ['file-in-dir-dest']); | ||
assert_equals(await getFileContents(file), 'abc'); | ||
assert_equals(await getFileSize(file), 3); | ||
}, 'move(dir, name) to move a file to a new directory'); | ||
|
||
directory_test(async (t, root) => { | ||
const dir_src = await root.getDirectoryHandle('dir-src', {create: true}); | ||
const dir_dest = await root.getDirectoryHandle('dir-dest', {create: true}); | ||
const dir_in_dir = | ||
await dir_src.getDirectoryHandle('dir-in-dir', {create: true}); | ||
await dir_in_dir.move(dir_dest, null); | ||
|
||
assert_array_equals( | ||
await getSortedDirectoryEntries(root), ['dir-dest/', 'dir-src/']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir_src), []); | ||
assert_array_equals( | ||
await getSortedDirectoryEntries(dir_dest), ['dir-in-dir/']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir_in_dir), []); | ||
}, 'move(dir, _) to move an empty directory to a new directory'); | ||
|
||
directory_test(async (t, root) => { | ||
const dir_src = await root.getDirectoryHandle('dir-src', {create: true}); | ||
const dir_dest = await root.getDirectoryHandle('dir-dest', {create: true}); | ||
const dir_in_dir = | ||
await dir_src.getDirectoryHandle('dir-in-dir', {create: true}); | ||
await dir_in_dir.move(dir_dest, 'dir-in-dir'); | ||
|
||
assert_array_equals( | ||
await getSortedDirectoryEntries(root), ['dir-dest/', 'dir-src/']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir_src), []); | ||
assert_array_equals( | ||
await getSortedDirectoryEntries(dir_dest), ['dir-in-dir/']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir_in_dir), []); | ||
}, 'move(dir, name) to move an empty directory to a new directory'); | ||
|
||
directory_test(async (t, root) => { | ||
const dir_src = await root.getDirectoryHandle('dir-src', {create: true}); | ||
const dir_dest = await root.getDirectoryHandle('dir-dest', {create: true}); | ||
const dir_in_dir = | ||
await dir_src.getDirectoryHandle('dir-in-dir', {create: true}); | ||
const file = | ||
await createFileWithContents(t, 'file-in-dir', 'abc', dir_in_dir); | ||
await dir_in_dir.move(dir_dest, null); | ||
|
||
assert_array_equals( | ||
await getSortedDirectoryEntries(root), ['dir-dest/', 'dir-src/']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir_src), []); | ||
assert_array_equals( | ||
await getSortedDirectoryEntries(dir_dest), ['dir-in-dir/']); | ||
assert_array_equals( | ||
await getSortedDirectoryEntries(dir_in_dir), ['file-in-dir']); | ||
// `file` should be invalidated after moving directories. | ||
await promise_rejects_dom(t, 'NotFoundError', getFileContents(file)); | ||
}, 'move(dir, _) to move a non-empty directory to a new directory'); | ||
|
||
directory_test(async (t, root) => { | ||
const dir_src = await root.getDirectoryHandle('dir-src', {create: true}); | ||
const dir_dest = await root.getDirectoryHandle('dir-dest', {create: true}); | ||
const dir_in_dir = | ||
await dir_src.getDirectoryHandle('dir-in-dir', {create: true}); | ||
const file = | ||
await createFileWithContents(t, 'file-in-dir', 'abc', dir_in_dir); | ||
await dir_in_dir.move(dir_dest, 'dir-in-dir'); | ||
|
||
assert_array_equals( | ||
await getSortedDirectoryEntries(root), ['dir-dest/', 'dir-src/']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir_src), []); | ||
assert_array_equals( | ||
await getSortedDirectoryEntries(dir_dest), ['dir-in-dir/']); | ||
assert_array_equals( | ||
await getSortedDirectoryEntries(dir_in_dir), ['file-in-dir']); | ||
// `file` should be invalidated after moving directories. | ||
await promise_rejects_dom(t, 'NotFoundError', getFileContents(file)); | ||
}, 'move(dir, name) to move a non-empty directory to a new directory'); | ||
|
||
directory_test(async (t, root) => { | ||
const handle = await createFileWithContents(t, 'file-1', 'foo', root); | ||
|
||
await handle.move('file-2'); | ||
assert_array_equals(await getSortedDirectoryEntries(root), ['file-2']); | ||
|
||
await handle.move('file-3'); | ||
assert_array_equals(await getSortedDirectoryEntries(root), ['file-3']); | ||
|
||
await handle.move('file-1'); | ||
assert_array_equals(await getSortedDirectoryEntries(root), ['file-1']); | ||
}, 'move(name) can be called multiple times'); | ||
|
||
directory_test(async (t, root) => { | ||
const dir1 = await root.getDirectoryHandle('dir1', {create: true}); | ||
const dir2 = await root.getDirectoryHandle('dir2', {create: true}); | ||
const handle = await createFileWithContents(t, 'file', 'foo', root); | ||
|
||
await handle.move(dir1, null); | ||
assert_array_equals( | ||
await getSortedDirectoryEntries(root), ['dir1/', 'dir2/']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir1), ['file']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir2), []); | ||
assert_equals(await getFileContents(handle), 'foo'); | ||
|
||
await handle.move(dir2, null); | ||
assert_array_equals( | ||
await getSortedDirectoryEntries(root), ['dir1/', 'dir2/']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir1), []); | ||
assert_array_equals(await getSortedDirectoryEntries(dir2), ['file']); | ||
assert_equals(await getFileContents(handle), 'foo'); | ||
|
||
await handle.move(root, null); | ||
assert_array_equals( | ||
await getSortedDirectoryEntries(root), ['dir1/', 'dir2/', 'file']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir1), []); | ||
assert_array_equals(await getSortedDirectoryEntries(dir2), []); | ||
assert_equals(await getFileContents(handle), 'foo'); | ||
}, 'move(dir, _) can be called multiple times'); | ||
|
||
directory_test(async (t, root) => { | ||
const dir1 = await root.getDirectoryHandle('dir1', {create: true}); | ||
const dir2 = await root.getDirectoryHandle('dir2', {create: true}); | ||
const handle = await createFileWithContents(t, 'file', 'foo', root); | ||
|
||
await handle.move(dir1, 'file-1'); | ||
assert_array_equals( | ||
await getSortedDirectoryEntries(root), ['dir1/', 'dir2/']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir1), ['file-1']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir2), []); | ||
assert_equals(await getFileContents(handle), 'foo'); | ||
|
||
await handle.move(dir2, 'file-2'); | ||
assert_array_equals( | ||
await getSortedDirectoryEntries(root), ['dir1/', 'dir2/']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir1), []); | ||
assert_array_equals(await getSortedDirectoryEntries(dir2), ['file-2']); | ||
assert_equals(await getFileContents(handle), 'foo'); | ||
|
||
await handle.move(root, 'file-3'); | ||
assert_array_equals( | ||
await getSortedDirectoryEntries(root), ['dir1/', 'dir2/', 'file-3']); | ||
assert_array_equals(await getSortedDirectoryEntries(dir1), []); | ||
assert_array_equals(await getSortedDirectoryEntries(dir2), []); | ||
assert_equals(await getFileContents(handle), 'foo'); | ||
}, 'move(dir, name) can be called multiple times'); | ||
|
||
directory_test(async (t, root) => { | ||
const handle = await createFileWithContents(t, 'file-before', 'foo', root); | ||
await promise_rejects_js( | ||
t, TypeError, | ||
handle.move( | ||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.')); | ||
|
||
assert_array_equals(await getSortedDirectoryEntries(root), ['file-before']); | ||
assert_equals(await getFileContents(handle), 'foo'); | ||
assert_equals(await getFileSize(handle), 3); | ||
}, 'move(name) with an invalid name should fail'); | ||
|
||
directory_test(async (t, root) => { | ||
const handle = await createFileWithContents(t, 'file-before', 'foo', root); | ||
await promise_rejects_js( | ||
t, TypeError, | ||
handle.move( | ||
root, | ||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.')); | ||
|
||
assert_array_equals(await getSortedDirectoryEntries(root), ['file-before']); | ||
assert_equals(await getFileContents(handle), 'foo'); | ||
assert_equals(await getFileSize(handle), 3); | ||
}, 'move(dir, name) with an invalid name should fail'); | ||
|
||
directory_test(async (t, root) => { | ||
const handle = await createFileWithContents(t, 'file-before', 'foo', root); | ||
await promise_rejects_js(t, TypeError, handle.move('#$23423@352^*3243')); | ||
|
||
assert_array_equals(await getSortedDirectoryEntries(root), ['file-before']); | ||
assert_equals(await getFileContents(handle), 'foo'); | ||
assert_equals(await getFileSize(handle), 3); | ||
}, 'move(name) with a dangerous name should fail'); | ||
|
||
directory_test(async (t, root) => { | ||
const handle = await createFileWithContents(t, 'file-before', 'foo', root); | ||
await promise_rejects_js( | ||
t, TypeError, handle.move(root, '#$23423@352^*3243')); | ||
|
||
assert_array_equals(await getSortedDirectoryEntries(root), ['file-before']); | ||
assert_equals(await getFileContents(handle), 'foo'); | ||
assert_equals(await getFileSize(handle), 3); | ||
}, 'move(dir, name) with a dangerous name should fail'); |