Skip to content

Commit 59a7cf4

Browse files
a-sullychromium-wpt-export-bot
authored andcommitted
Add FileSystemHandle::move() method
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
1 parent 37558f1 commit 59a7cf4

File tree

3 files changed

+373
-0
lines changed

3 files changed

+373
-0
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!doctype html>
2+
<meta charset=utf-8>
3+
4+
<script src="/resources/testharness.js"></script>
5+
<script src="/resources/testharnessreport.js"></script>
6+
<script src="/resources/testdriver.js"></script>
7+
<script src="/resources/testdriver-vendor.js"></script>
8+
<script src="resources/test-helpers.js"></script>
9+
<script src="resources/local-fs-test-helpers.js"></script>
10+
<script src="script-tests/FileSystemBaseHandle-move.js"></script>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// META: script=resources/test-helpers.js
2+
// META: script=resources/sandboxed-fs-test-helpers.js
3+
// META: script=script-tests/FileSystemBaseHandle-move.js
Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
1+
// META: script=resources/test-helpers.js
2+
3+
'use strict';
4+
5+
directory_test(async (t, root) => {
6+
const handle = await createFileWithContents(t, 'file-before', 'foo', root);
7+
await handle.move('file-after');
8+
9+
assert_array_equals(await getSortedDirectoryEntries(root), ['file-after']);
10+
assert_equals(await getFileContents(handle), 'foo');
11+
assert_equals(await getFileSize(handle), 3);
12+
}, 'move(name) to rename a file');
13+
14+
directory_test(async (t, root) => {
15+
const handle = await createFileWithContents(t, 'file-before', 'foo', root);
16+
await handle.move(root, 'file-after');
17+
18+
assert_array_equals(await getSortedDirectoryEntries(root), ['file-after']);
19+
assert_equals(await getFileContents(handle), 'foo');
20+
assert_equals(await getFileSize(handle), 3);
21+
}, 'move(dir, name) to rename a file');
22+
23+
directory_test(async (t, root) => {
24+
const handle = await createFileWithContents(t, 'file-before', 'foo', root);
25+
await handle.move('file-before');
26+
27+
assert_array_equals(await getSortedDirectoryEntries(root), ['file-before']);
28+
assert_equals(await getFileContents(handle), 'foo');
29+
assert_equals(await getFileSize(handle), 3);
30+
}, 'move(name) to rename a file the same name');
31+
32+
directory_test(async (t, root) => {
33+
const handle = await createFileWithContents(t, 'file-before', 'foo', root);
34+
await handle.move(root, 'file-before');
35+
36+
assert_array_equals(await getSortedDirectoryEntries(root), ['file-before']);
37+
assert_equals(await getFileContents(handle), 'foo');
38+
assert_equals(await getFileSize(handle), 3);
39+
}, 'move(dir, name) to rename a file the same name');
40+
41+
directory_test(async (t, root) => {
42+
const handle = await createFileWithContents(t, 'file-before', 'foo', root);
43+
await promise_rejects_js(t, TypeError, handle.move(''));
44+
45+
assert_array_equals(await getSortedDirectoryEntries(root), ['file-before']);
46+
assert_equals(await getFileContents(handle), 'foo');
47+
assert_equals(await getFileSize(handle), 3);
48+
}, 'move("") to rename a file fails');
49+
50+
directory_test(async (t, root) => {
51+
const dir = await root.getDirectoryHandle('dir-before', {create: true});
52+
await dir.move('dir-after');
53+
54+
assert_array_equals(await getSortedDirectoryEntries(root), ['dir-after/']);
55+
assert_array_equals(await getSortedDirectoryEntries(dir), []);
56+
}, 'move(name) to rename an empty directory');
57+
58+
directory_test(async (t, root) => {
59+
const dir = await root.getDirectoryHandle('dir-before', {create: true});
60+
await dir.move(root, 'dir-after');
61+
62+
assert_array_equals(await getSortedDirectoryEntries(root), ['dir-after/']);
63+
assert_array_equals(await getSortedDirectoryEntries(dir), []);
64+
}, 'move(dir, name) to rename an empty directory');
65+
66+
directory_test(async (t, root) => {
67+
const dir = await root.getDirectoryHandle('dir-before', {create: true});
68+
await promise_rejects_js(t, TypeError, dir.move(''));
69+
70+
assert_array_equals(await getSortedDirectoryEntries(root), ['dir-before/']);
71+
assert_array_equals(await getSortedDirectoryEntries(dir), []);
72+
}, 'move("") to rename an empty directory fails');
73+
74+
directory_test(async (t, root) => {
75+
const dir = await root.getDirectoryHandle('dir-before', {create: true});
76+
await createFileWithContents(t, 'file-in-dir', 'abc', dir);
77+
await dir.move('dir-after');
78+
79+
assert_array_equals(await getSortedDirectoryEntries(root), ['dir-after/']);
80+
assert_array_equals(await getSortedDirectoryEntries(dir), ['file-in-dir']);
81+
}, 'move(name) to rename a non-empty directory');
82+
83+
directory_test(async (t, root) => {
84+
const dir = await root.getDirectoryHandle('dir-before', {create: true});
85+
await createFileWithContents(t, 'file-in-dir', 'abc', dir);
86+
await dir.move(root, 'dir-after');
87+
88+
assert_array_equals(await getSortedDirectoryEntries(root), ['dir-after/']);
89+
assert_array_equals(await getSortedDirectoryEntries(dir), ['file-in-dir']);
90+
}, 'move(dir, name) to rename a non-empty directory');
91+
92+
directory_test(async (t, root) => {
93+
const dir_src = await root.getDirectoryHandle('dir-src', {create: true});
94+
const dir_dest = await root.getDirectoryHandle('dir-dest', {create: true});
95+
const file = await createFileWithContents(t, 'file', 'abc', dir_src);
96+
await file.move(dir_dest, null);
97+
98+
assert_array_equals(
99+
await getSortedDirectoryEntries(root), ['dir-dest/', 'dir-src/']);
100+
assert_array_equals(await getSortedDirectoryEntries(dir_src), []);
101+
assert_array_equals(await getSortedDirectoryEntries(dir_dest), ['file']);
102+
assert_equals(await getFileContents(file), 'abc');
103+
assert_equals(await getFileSize(file), 3);
104+
}, 'move(dir, null) to move a file to a new directory');
105+
106+
directory_test(async (t, root) => {
107+
const dir_src = await root.getDirectoryHandle('dir-src', {create: true});
108+
const dir_dest = await root.getDirectoryHandle('dir-dest', {create: true});
109+
const file = await createFileWithContents(t, 'file', 'abc', dir_src);
110+
await file.move(dir_dest, '');
111+
112+
assert_array_equals(
113+
await getSortedDirectoryEntries(root), ['dir-dest/', 'dir-src/']);
114+
assert_array_equals(await getSortedDirectoryEntries(dir_src), []);
115+
assert_array_equals(await getSortedDirectoryEntries(dir_dest), ['file']);
116+
assert_equals(await getFileContents(file), 'abc');
117+
assert_equals(await getFileSize(file), 3);
118+
}, 'move(dir, "") to move a file to a new directory');
119+
120+
directory_test(async (t, root) => {
121+
const dir_src = await root.getDirectoryHandle('dir-src', {create: true});
122+
const dir_dest = await root.getDirectoryHandle('dir-dest', {create: true});
123+
const file =
124+
await createFileWithContents(t, 'file-in-dir-src', 'abc', dir_src);
125+
await file.move(dir_dest, 'file-in-dir-dest');
126+
127+
assert_array_equals(
128+
await getSortedDirectoryEntries(root), ['dir-dest/', 'dir-src/']);
129+
assert_array_equals(await getSortedDirectoryEntries(dir_src), []);
130+
assert_array_equals(
131+
await getSortedDirectoryEntries(dir_dest), ['file-in-dir-dest']);
132+
assert_equals(await getFileContents(file), 'abc');
133+
assert_equals(await getFileSize(file), 3);
134+
}, 'move(dir, name) to move a file to a new directory');
135+
136+
directory_test(async (t, root) => {
137+
const dir_src = await root.getDirectoryHandle('dir-src', {create: true});
138+
const dir_dest = await root.getDirectoryHandle('dir-dest', {create: true});
139+
const dir_in_dir =
140+
await dir_src.getDirectoryHandle('dir-in-dir', {create: true});
141+
await dir_in_dir.move(dir_dest, null);
142+
143+
assert_array_equals(
144+
await getSortedDirectoryEntries(root), ['dir-dest/', 'dir-src/']);
145+
assert_array_equals(await getSortedDirectoryEntries(dir_src), []);
146+
assert_array_equals(
147+
await getSortedDirectoryEntries(dir_dest), ['dir-in-dir/']);
148+
assert_array_equals(await getSortedDirectoryEntries(dir_in_dir), []);
149+
}, 'move(dir, null) to move an empty directory to a new directory');
150+
151+
directory_test(async (t, root) => {
152+
const dir_src = await root.getDirectoryHandle('dir-src', {create: true});
153+
const dir_dest = await root.getDirectoryHandle('dir-dest', {create: true});
154+
const dir_in_dir =
155+
await dir_src.getDirectoryHandle('dir-in-dir', {create: true});
156+
await dir_in_dir.move(dir_dest, 'dir-in-dir');
157+
158+
assert_array_equals(
159+
await getSortedDirectoryEntries(root), ['dir-dest/', 'dir-src/']);
160+
assert_array_equals(await getSortedDirectoryEntries(dir_src), []);
161+
assert_array_equals(
162+
await getSortedDirectoryEntries(dir_dest), ['dir-in-dir/']);
163+
assert_array_equals(await getSortedDirectoryEntries(dir_in_dir), []);
164+
}, 'move(dir, name) to move an empty directory to a new directory');
165+
166+
directory_test(async (t, root) => {
167+
const dir_src = await root.getDirectoryHandle('dir-src', {create: true});
168+
const dir_dest = await root.getDirectoryHandle('dir-dest', {create: true});
169+
const dir_in_dir =
170+
await dir_src.getDirectoryHandle('dir-in-dir', {create: true});
171+
const file =
172+
await createFileWithContents(t, 'file-in-dir', 'abc', dir_in_dir);
173+
await dir_in_dir.move(dir_dest, null);
174+
175+
assert_array_equals(
176+
await getSortedDirectoryEntries(root), ['dir-dest/', 'dir-src/']);
177+
assert_array_equals(await getSortedDirectoryEntries(dir_src), []);
178+
assert_array_equals(
179+
await getSortedDirectoryEntries(dir_dest), ['dir-in-dir/']);
180+
assert_array_equals(
181+
await getSortedDirectoryEntries(dir_in_dir), ['file-in-dir']);
182+
// `file` should be invalidated after moving directories.
183+
await promise_rejects_dom(t, 'NotFoundError', getFileContents(file));
184+
}, 'move(dir, null) to move a non-empty directory to a new directory');
185+
186+
directory_test(async (t, root) => {
187+
const dir_src = await root.getDirectoryHandle('dir-src', {create: true});
188+
const dir_dest = await root.getDirectoryHandle('dir-dest', {create: true});
189+
const dir_in_dir =
190+
await dir_src.getDirectoryHandle('dir-in-dir', {create: true});
191+
const file =
192+
await createFileWithContents(t, 'file-in-dir', 'abc', dir_in_dir);
193+
await dir_in_dir.move(dir_dest, 'dir-in-dir');
194+
195+
assert_array_equals(
196+
await getSortedDirectoryEntries(root), ['dir-dest/', 'dir-src/']);
197+
assert_array_equals(await getSortedDirectoryEntries(dir_src), []);
198+
assert_array_equals(
199+
await getSortedDirectoryEntries(dir_dest), ['dir-in-dir/']);
200+
assert_array_equals(
201+
await getSortedDirectoryEntries(dir_in_dir), ['file-in-dir']);
202+
// `file` should be invalidated after moving directories.
203+
await promise_rejects_dom(t, 'NotFoundError', getFileContents(file));
204+
}, 'move(dir, name) to move a non-empty directory to a new directory');
205+
206+
directory_test(async (t, root) => {
207+
const handle = await createFileWithContents(t, 'file-1', 'foo', root);
208+
209+
await handle.move('file-2');
210+
assert_array_equals(await getSortedDirectoryEntries(root), ['file-2']);
211+
212+
await handle.move('file-3');
213+
assert_array_equals(await getSortedDirectoryEntries(root), ['file-3']);
214+
215+
await handle.move('file-1');
216+
assert_array_equals(await getSortedDirectoryEntries(root), ['file-1']);
217+
}, 'move(name) can be called multiple times');
218+
219+
directory_test(async (t, root) => {
220+
const dir1 = await root.getDirectoryHandle('dir1', {create: true});
221+
const dir2 = await root.getDirectoryHandle('dir2', {create: true});
222+
const handle = await createFileWithContents(t, 'file', 'foo', root);
223+
224+
await handle.move(dir1, null);
225+
assert_array_equals(
226+
await getSortedDirectoryEntries(root), ['dir1/', 'dir2/']);
227+
assert_array_equals(await getSortedDirectoryEntries(dir1), ['file']);
228+
assert_array_equals(await getSortedDirectoryEntries(dir2), []);
229+
assert_equals(await getFileContents(handle), 'foo');
230+
231+
await handle.move(dir2, null);
232+
assert_array_equals(
233+
await getSortedDirectoryEntries(root), ['dir1/', 'dir2/']);
234+
assert_array_equals(await getSortedDirectoryEntries(dir1), []);
235+
assert_array_equals(await getSortedDirectoryEntries(dir2), ['file']);
236+
assert_equals(await getFileContents(handle), 'foo');
237+
238+
await handle.move(root, null);
239+
assert_array_equals(
240+
await getSortedDirectoryEntries(root), ['dir1/', 'dir2/', 'file']);
241+
assert_array_equals(await getSortedDirectoryEntries(dir1), []);
242+
assert_array_equals(await getSortedDirectoryEntries(dir2), []);
243+
assert_equals(await getFileContents(handle), 'foo');
244+
}, 'move(dir, null) can be called multiple times');
245+
246+
directory_test(async (t, root) => {
247+
const dir1 = await root.getDirectoryHandle('dir1', {create: true});
248+
const dir2 = await root.getDirectoryHandle('dir2', {create: true});
249+
const handle = await createFileWithContents(t, 'file', 'foo', root);
250+
251+
await handle.move(dir1, 'file-1');
252+
assert_array_equals(
253+
await getSortedDirectoryEntries(root), ['dir1/', 'dir2/']);
254+
assert_array_equals(await getSortedDirectoryEntries(dir1), ['file-1']);
255+
assert_array_equals(await getSortedDirectoryEntries(dir2), []);
256+
assert_equals(await getFileContents(handle), 'foo');
257+
258+
await handle.move(dir2, 'file-2');
259+
assert_array_equals(
260+
await getSortedDirectoryEntries(root), ['dir1/', 'dir2/']);
261+
assert_array_equals(await getSortedDirectoryEntries(dir1), []);
262+
assert_array_equals(await getSortedDirectoryEntries(dir2), ['file-2']);
263+
assert_equals(await getFileContents(handle), 'foo');
264+
265+
await handle.move(root, 'file-3');
266+
assert_array_equals(
267+
await getSortedDirectoryEntries(root), ['dir1/', 'dir2/', 'file-3']);
268+
assert_array_equals(await getSortedDirectoryEntries(dir1), []);
269+
assert_array_equals(await getSortedDirectoryEntries(dir2), []);
270+
assert_equals(await getFileContents(handle), 'foo');
271+
}, 'move(dir, name) can be called multiple times');
272+
273+
directory_test(async (t, root) => {
274+
const dir = await root.getDirectoryHandle('dir', {create: true});
275+
const handle = await createFileWithContents(t, 'file-before', 'foo', dir);
276+
await handle.move(root);
277+
278+
assert_array_equals(await getSortedDirectoryEntries(root), ['dir/']);
279+
assert_array_equals(
280+
await getSortedDirectoryEntries(dir),
281+
['[object FileSystemDirectoryHandle]']);
282+
assert_equals(await getFileContents(handle), 'foo');
283+
assert_equals(await getFileSize(handle), 3);
284+
}, 'move(dir) should rename to stringified dir object');
285+
286+
directory_test(async (t, root) => {
287+
const dir = await root.getDirectoryHandle('dir', {create: true});
288+
const handle = await createFileWithContents(t, 'file-before', 'foo', dir);
289+
await promise_rejects_js(t, TypeError, handle.move('Lorem.'));
290+
291+
assert_array_equals(await getSortedDirectoryEntries(root), ['dir/']);
292+
assert_array_equals(await getSortedDirectoryEntries(dir), ['file-before']);
293+
assert_equals(await getFileContents(handle), 'foo');
294+
assert_equals(await getFileSize(handle), 3);
295+
}, 'move(name) with a name with a trailing period should fail');
296+
297+
directory_test(async (t, root) => {
298+
const handle = await createFileWithContents(t, 'file-before', 'foo', root);
299+
await promise_rejects_js(t, TypeError, handle.move('#$23423@352^*3243'));
300+
301+
assert_array_equals(await getSortedDirectoryEntries(root), ['file-before']);
302+
assert_equals(await getFileContents(handle), 'foo');
303+
assert_equals(await getFileSize(handle), 3);
304+
}, 'move(name) with a name with invalid characters should fail');
305+
306+
directory_test(async (t, root) => {
307+
const handle = await createFileWithContents(t, 'file-before', 'foo', root);
308+
await promise_rejects_js(
309+
t, TypeError, handle.move(root, '#$23423@352^*3243'));
310+
311+
assert_array_equals(await getSortedDirectoryEntries(root), ['file-before']);
312+
assert_equals(await getFileContents(handle), 'foo');
313+
assert_equals(await getFileSize(handle), 3);
314+
}, 'move(dir, name) with a name with invalid characters should fail');
315+
316+
directory_test(async (t, root) => {
317+
const dir = await root.getDirectoryHandle('dir', {create: true});
318+
await promise_rejects_dom(
319+
t, 'InvalidModificationError', dir.move(dir, 'dir'));
320+
321+
assert_array_equals(await getSortedDirectoryEntries(root), ['dir/']);
322+
assert_array_equals(await getSortedDirectoryEntries(dir), []);
323+
}, 'move(dir, name) to move a directory within itself fails');
324+
325+
directory_test(async (t, root) => {
326+
const dir = await root.getDirectoryHandle('dir', {create: true});
327+
await promise_rejects_dom(
328+
t, 'InvalidModificationError', dir.move(dir, 'dir-fail'));
329+
330+
assert_array_equals(await getSortedDirectoryEntries(root), ['dir/']);
331+
assert_array_equals(await getSortedDirectoryEntries(dir), []);
332+
}, 'move(dir, name) to move a directory within itself and rename fails');
333+
334+
directory_test(async (t, root) => {
335+
const parent_dir =
336+
await root.getDirectoryHandle('parent-dir', {create: true});
337+
const child_dir =
338+
await parent_dir.getDirectoryHandle('child-dir', {create: true});
339+
await promise_rejects_dom(
340+
t, 'InvalidModificationError', parent_dir.move(child_dir, 'dir'));
341+
342+
assert_array_equals(await getSortedDirectoryEntries(root), ['parent-dir/']);
343+
assert_array_equals(
344+
await getSortedDirectoryEntries(parent_dir), ['child-dir/']);
345+
assert_array_equals(await getSortedDirectoryEntries(child_dir), []);
346+
}, 'move(dir, name) to move a directory within a descendent fails');
347+
348+
directory_test(async (t, root) => {
349+
const parent_dir =
350+
await root.getDirectoryHandle('parent-dir', {create: true});
351+
const child_dir =
352+
await parent_dir.getDirectoryHandle('child-dir', {create: true});
353+
await promise_rejects_dom(
354+
t, 'InvalidModificationError', parent_dir.move(child_dir, 'dir-fail'));
355+
356+
assert_array_equals(await getSortedDirectoryEntries(root), ['parent-dir/']);
357+
assert_array_equals(
358+
await getSortedDirectoryEntries(parent_dir), ['child-dir/']);
359+
assert_array_equals(await getSortedDirectoryEntries(child_dir), []);
360+
}, 'move(dir, name) to move a directory within a descendent and rename fails');

0 commit comments

Comments
 (0)