1/*
2 * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/* @test
25 * @bug 4313887 6838333 6917021 7006126 6950237 8006645
26 * @summary Unit test for java.nio.file.Files copy and move methods (use -Dseed=X to set PRNG seed)
27 * @library .. /lib/testlibrary/
28 * @build jdk.testlibrary.* CopyAndMove PassThroughFileSystem
29 * @run main/othervm CopyAndMove
30 * @key randomness
31 */
32
33import java.nio.ByteBuffer;
34import java.nio.file.*;
35import static java.nio.file.Files.*;
36import static java.nio.file.StandardCopyOption.*;
37import static java.nio.file.LinkOption.*;
38import java.nio.file.attribute.*;
39import java.io.*;
40import java.util.*;
41import java.util.concurrent.TimeUnit;
42import jdk.testlibrary.RandomFactory;
43
44public class CopyAndMove {
45    static final Random rand = RandomFactory.getRandom();
46    static boolean heads() { return rand.nextBoolean(); }
47    private static boolean testPosixAttributes = false;
48
49    public static void main(String[] args) throws Exception {
50        Path dir1 = TestUtil.createTemporaryDirectory();
51        try {
52
53            // Same directory
54            testPosixAttributes = getFileStore(dir1).supportsFileAttributeView("posix");
55            testCopyFileToFile(dir1, dir1, TestUtil.supportsLinks(dir1));
56            testMove(dir1, dir1, TestUtil.supportsLinks(dir1));
57
58            // Different directories. Use test.dir if possible as it might be
59            // a different volume/file system and so improve test coverage.
60            String testDir = System.getProperty("test.dir", ".");
61            Path dir2 = TestUtil.createTemporaryDirectory(testDir);
62            try {
63                boolean testSymbolicLinks =
64                    TestUtil.supportsLinks(dir1) && TestUtil.supportsLinks(dir2);
65                testPosixAttributes = getFileStore(dir1).supportsFileAttributeView("posix") &&
66                                      getFileStore(dir2).supportsFileAttributeView("posix");
67                testCopyFileToFile(dir1, dir2, testSymbolicLinks);
68                testMove(dir1, dir2, testSymbolicLinks);
69            } finally {
70                TestUtil.removeAll(dir2);
71            }
72
73            // Target is location associated with custom provider
74            Path dir3 = PassThroughFileSystem.create().getPath(dir1.toString());
75            testPosixAttributes = getFileStore(dir1).supportsFileAttributeView("posix") &&
76                                  getFileStore(dir3).supportsFileAttributeView("posix");
77            testCopyFileToFile(dir1, dir3, false);
78            testMove(dir1, dir3, false);
79
80            // Test copy(InputStream,Path) and copy(Path,OutputStream)
81            testCopyInputStreamToFile();
82            testCopyFileToOuputStream();
83
84        } finally {
85            TestUtil.removeAll(dir1);
86        }
87    }
88
89    static void checkBasicAttributes(BasicFileAttributes attrs1,
90                                     BasicFileAttributes attrs2)
91    {
92        // check file type
93        assertTrue(attrs1.isRegularFile() == attrs2.isRegularFile());
94        assertTrue(attrs1.isDirectory() == attrs2.isDirectory());
95        assertTrue(attrs1.isSymbolicLink() == attrs2.isSymbolicLink());
96        assertTrue(attrs1.isOther() == attrs2.isOther());
97
98        // check last modified time if not a symbolic link
99        if (!attrs1.isSymbolicLink()) {
100            long time1 = attrs1.lastModifiedTime().to(TimeUnit.SECONDS);
101            long time2 = attrs2.lastModifiedTime().to(TimeUnit.SECONDS);
102
103            if (time1 != time2) {
104                System.err.format("File time for %s is %s\n", attrs1.fileKey(), attrs1.lastModifiedTime());
105                System.err.format("File time for %s is %s\n", attrs2.fileKey(), attrs2.lastModifiedTime());
106                assertTrue(false);
107            }
108        }
109
110        // check size
111        if (attrs1.isRegularFile())
112            assertTrue(attrs1.size() == attrs2.size());
113    }
114
115    static void checkPosixAttributes(PosixFileAttributes attrs1,
116                                     PosixFileAttributes attrs2)
117    {
118        assertTrue(attrs1.permissions().equals(attrs2.permissions()));
119        assertTrue(attrs1.owner().equals(attrs2.owner()));
120        assertTrue(attrs1.group().equals(attrs2.group()));
121    }
122
123    static void checkDosAttributes(DosFileAttributes attrs1,
124                                   DosFileAttributes attrs2)
125    {
126        assertTrue(attrs1.isReadOnly() == attrs2.isReadOnly());
127        assertTrue(attrs1.isHidden() == attrs2.isHidden());
128        assertTrue(attrs1.isSystem() == attrs2.isSystem());
129    }
130
131    static void checkUserDefinedFileAttributes(Map<String,ByteBuffer> attrs1,
132                                     Map<String,ByteBuffer> attrs2)
133    {
134        assert attrs1.size() == attrs2.size();
135        for (String name: attrs1.keySet()) {
136            ByteBuffer bb1 = attrs1.get(name);
137            ByteBuffer bb2 = attrs2.get(name);
138            assertTrue(bb2 != null);
139            assertTrue(bb1.equals(bb2));
140        }
141    }
142
143    static Map<String,ByteBuffer> readUserDefinedFileAttributes(Path file)
144        throws IOException
145    {
146        UserDefinedFileAttributeView view =
147            getFileAttributeView(file, UserDefinedFileAttributeView.class);
148        Map<String,ByteBuffer> result = new HashMap<>();
149        for (String name: view.list()) {
150            int size = view.size(name);
151            ByteBuffer bb = ByteBuffer.allocate(size);
152            int n = view.read(name, bb);
153            assertTrue(n == size);
154            bb.flip();
155            result.put(name, bb);
156        }
157        return result;
158    }
159
160    // move source to target with verification
161    static void moveAndVerify(Path source, Path target, CopyOption... options)
162        throws IOException
163    {
164        // read attributes before file is moved
165        BasicFileAttributes basicAttributes = null;
166        PosixFileAttributes posixAttributes = null;
167        DosFileAttributes dosAttributes = null;
168        Map<String,ByteBuffer> namedAttributes = null;
169
170        // get file attributes of source file
171        String os = System.getProperty("os.name");
172        if (os.startsWith("Windows")) {
173            dosAttributes = readAttributes(source, DosFileAttributes.class, NOFOLLOW_LINKS);
174            basicAttributes = dosAttributes;
175        } else {
176            posixAttributes = readAttributes(source, PosixFileAttributes.class, NOFOLLOW_LINKS);
177            basicAttributes = posixAttributes;
178        }
179        if (basicAttributes == null)
180            basicAttributes = readAttributes(source, BasicFileAttributes.class, NOFOLLOW_LINKS);
181
182        // hash file contents if regular file
183        int hash = (basicAttributes.isRegularFile()) ? computeHash(source) : 0;
184
185        // record link target if symbolic link
186        Path linkTarget = null;
187        if (basicAttributes.isSymbolicLink())
188            linkTarget = readSymbolicLink(source);
189
190        // read named attributes if available (and file is not a sym link)
191        if (!basicAttributes.isSymbolicLink() &&
192            getFileStore(source).supportsFileAttributeView("xattr"))
193        {
194            namedAttributes = readUserDefinedFileAttributes(source);
195        }
196
197        // move file
198        Path result = move(source, target, options);
199        assertTrue(result == target);
200
201        // verify source does not exist
202        assertTrue(notExists(source));
203
204        // verify file contents
205        if (basicAttributes.isRegularFile()) {
206            if (computeHash(target) != hash)
207                throw new RuntimeException("Failed to verify move of regular file");
208        }
209
210        // verify link target
211        if (basicAttributes.isSymbolicLink()) {
212            if (!readSymbolicLink(target).equals(linkTarget))
213                throw new RuntimeException("Failed to verify move of symbolic link");
214        }
215
216        // verify basic attributes
217        checkBasicAttributes(basicAttributes,
218            readAttributes(target, BasicFileAttributes.class, NOFOLLOW_LINKS));
219
220        // verify other attributes when same provider
221        if (source.getFileSystem().provider() == target.getFileSystem().provider()) {
222
223            // verify POSIX attributes
224            if (posixAttributes != null &&
225                !basicAttributes.isSymbolicLink() &&
226                testPosixAttributes)
227            {
228                checkPosixAttributes(posixAttributes,
229                    readAttributes(target, PosixFileAttributes.class, NOFOLLOW_LINKS));
230            }
231
232            // verify DOS attributes
233            if (dosAttributes != null && !basicAttributes.isSymbolicLink()) {
234                DosFileAttributes attrs =
235                    readAttributes(target, DosFileAttributes.class, NOFOLLOW_LINKS);
236                checkDosAttributes(dosAttributes, attrs);
237            }
238
239            // verify named attributes
240            if (namedAttributes != null &&
241                getFileStore(target).supportsFileAttributeView("xattr"))
242            {
243                checkUserDefinedFileAttributes(namedAttributes,
244                                               readUserDefinedFileAttributes(target));
245            }
246        }
247    }
248
249    /**
250     * Tests all possible ways to invoke move
251     */
252    static void testMove(Path dir1, Path dir2, boolean supportsLinks)
253        throws IOException
254    {
255        Path source, target, entry;
256
257        boolean sameDevice = getFileStore(dir1).equals(getFileStore(dir2));
258
259        // -- regular file --
260
261        /**
262         * Test: move regular file, target does not exist
263         */
264        source = createSourceFile(dir1);
265        target = getTargetFile(dir2);
266        moveAndVerify(source, target);
267        delete(target);
268
269        /**
270         * Test: move regular file, target exists
271         */
272        source = createSourceFile(dir1);
273        target = getTargetFile(dir2);
274        createFile(target);
275        try {
276            moveAndVerify(source, target);
277            throw new RuntimeException("FileAlreadyExistsException expected");
278        } catch (FileAlreadyExistsException x) {
279        }
280        delete(target);
281        createDirectory(target);
282        try {
283            moveAndVerify(source, target);
284            throw new RuntimeException("FileAlreadyExistsException expected");
285        } catch (FileAlreadyExistsException x) {
286        }
287        delete(source);
288        delete(target);
289
290        /**
291         * Test: move regular file, target does not exist
292         */
293        source = createSourceFile(dir1);
294        target = getTargetFile(dir2);
295        moveAndVerify(source, target, REPLACE_EXISTING);
296        delete(target);
297
298        /**
299         * Test: move regular file, target exists
300         */
301        source = createSourceFile(dir1);
302        target = getTargetFile(dir2);
303        createFile(target);
304        moveAndVerify(source, target, REPLACE_EXISTING);
305        delete(target);
306
307        /**
308         * Test: move regular file, target exists and is empty directory
309         */
310        source = createSourceFile(dir1);
311        target = getTargetFile(dir2);
312        createDirectory(target);
313        moveAndVerify(source, target, REPLACE_EXISTING);
314        delete(target);
315
316        /**
317         * Test: move regular file, target exists and is non-empty directory
318         */
319        source = createSourceFile(dir1);
320        target = getTargetFile(dir2);
321        createDirectory(target);
322        entry = target.resolve("foo");
323        createFile(entry);
324        try {
325            moveAndVerify(source, target);
326            throw new RuntimeException("FileAlreadyExistsException expected");
327        } catch (FileAlreadyExistsException x) {
328        }
329        delete(entry);
330        delete(source);
331        delete(target);
332
333        /**
334         * Test atomic move of regular file (same file store)
335         */
336        source = createSourceFile(dir1);
337        target = getTargetFile(dir1);
338        moveAndVerify(source, target, ATOMIC_MOVE);
339        delete(target);
340
341        /**
342         * Test atomic move of regular file (different file store)
343         */
344        if (!sameDevice) {
345            source = createSourceFile(dir1);
346            target = getTargetFile(dir2);
347            try {
348                moveAndVerify(source, target, ATOMIC_MOVE);
349                throw new RuntimeException("AtomicMoveNotSupportedException expected");
350            } catch (AtomicMoveNotSupportedException x) {
351            }
352            delete(source);
353        }
354
355        // -- directories --
356
357        /*
358         * Test: move empty directory, target does not exist
359         */
360        source = createSourceDirectory(dir1);
361        target = getTargetFile(dir2);
362        moveAndVerify(source, target);
363        delete(target);
364
365        /**
366         * Test: move empty directory, target exists
367         */
368        source = createSourceDirectory(dir1);
369        target = getTargetFile(dir2);
370        createFile(target);
371        try {
372            moveAndVerify(source, target);
373            throw new RuntimeException("FileAlreadyExistsException expected");
374        } catch (FileAlreadyExistsException x) {
375        }
376        delete(target);
377        createDirectory(target);
378        try {
379            moveAndVerify(source, target);
380            throw new RuntimeException("FileAlreadyExistsException expected");
381        } catch (FileAlreadyExistsException x) {
382        }
383        delete(source);
384        delete(target);
385
386        /**
387         * Test: move empty directory, target does not exist
388         */
389        source = createSourceDirectory(dir1);
390        target = getTargetFile(dir2);
391        moveAndVerify(source, target, REPLACE_EXISTING);
392        delete(target);
393
394        /**
395         * Test: move empty directory, target exists
396         */
397        source = createSourceDirectory(dir1);
398        target = getTargetFile(dir2);
399        createFile(target);
400        moveAndVerify(source, target, REPLACE_EXISTING);
401        delete(target);
402
403        /**
404         * Test: move empty, target exists and is empty directory
405         */
406        source = createSourceDirectory(dir1);
407        target = getTargetFile(dir2);
408        createDirectory(target);
409        moveAndVerify(source, target, REPLACE_EXISTING);
410        delete(target);
411
412        /**
413         * Test: move empty directory, target exists and is non-empty directory
414         */
415        source = createSourceDirectory(dir1);
416        target = getTargetFile(dir2);
417        createDirectory(target);
418        entry = target.resolve("foo");
419        createFile(entry);
420        try {
421            moveAndVerify(source, target, REPLACE_EXISTING);
422            throw new RuntimeException("DirectoryNotEmptyException expected");
423        } catch (DirectoryNotEmptyException x) {
424        }
425        delete(entry);
426        delete(source);
427        delete(target);
428
429        /**
430         * Test: move non-empty directory (same file system)
431         */
432        source = createSourceDirectory(dir1);
433        createFile(source.resolve("foo"));
434        target = getTargetFile(dir1);
435        moveAndVerify(source, target);
436        delete(target.resolve("foo"));
437        delete(target);
438
439        /**
440         * Test: move non-empty directory (different file store)
441         */
442        if (!sameDevice) {
443            source = createSourceDirectory(dir1);
444            createFile(source.resolve("foo"));
445            target = getTargetFile(dir2);
446            try {
447                moveAndVerify(source, target);
448                throw new RuntimeException("IOException expected");
449            } catch (IOException x) {
450            }
451            delete(source.resolve("foo"));
452            delete(source);
453        }
454
455        /**
456         * Test atomic move of directory (same file store)
457         */
458        source = createSourceDirectory(dir1);
459        createFile(source.resolve("foo"));
460        target = getTargetFile(dir1);
461        moveAndVerify(source, target, ATOMIC_MOVE);
462        delete(target.resolve("foo"));
463        delete(target);
464
465        // -- symbolic links --
466
467        /**
468         * Test: Move symbolic link to file, target does not exist
469         */
470        if (supportsLinks) {
471            Path tmp = createSourceFile(dir1);
472            source = dir1.resolve("link");
473            createSymbolicLink(source, tmp);
474            target = getTargetFile(dir2);
475            moveAndVerify(source, target);
476            delete(target);
477            delete(tmp);
478        }
479
480        /**
481         * Test: Move symbolic link to directory, target does not exist
482         */
483        if (supportsLinks) {
484            source = dir1.resolve("link");
485            createSymbolicLink(source, dir2);
486            target = getTargetFile(dir2);
487            moveAndVerify(source, target);
488            delete(target);
489        }
490
491        /**
492         * Test: Move broken symbolic link, target does not exists
493         */
494        if (supportsLinks) {
495            Path tmp = Paths.get("doesnotexist");
496            source = dir1.resolve("link");
497            createSymbolicLink(source, tmp);
498            target = getTargetFile(dir2);
499            moveAndVerify(source, target);
500            delete(target);
501        }
502
503        /**
504         * Test: Move symbolic link, target exists
505         */
506        if (supportsLinks) {
507            source = dir1.resolve("link");
508            createSymbolicLink(source, dir2);
509            target = getTargetFile(dir2);
510            createFile(target);
511            try {
512                moveAndVerify(source, target);
513                throw new RuntimeException("FileAlreadyExistsException expected");
514            } catch (FileAlreadyExistsException x) {
515            }
516            delete(source);
517            delete(target);
518        }
519
520        /**
521         * Test: Move regular file, target exists
522         */
523        if (supportsLinks) {
524            source = dir1.resolve("link");
525            createSymbolicLink(source, dir2);
526            target = getTargetFile(dir2);
527            createFile(target);
528            moveAndVerify(source, target, REPLACE_EXISTING);
529            delete(target);
530        }
531
532        /**
533         * Test: move symbolic link, target exists and is empty directory
534         */
535        if (supportsLinks) {
536            source = dir1.resolve("link");
537            createSymbolicLink(source, dir2);
538            target = getTargetFile(dir2);
539            createDirectory(target);
540            moveAndVerify(source, target, REPLACE_EXISTING);
541            delete(target);
542        }
543
544        /**
545         * Test: symbolic link, target exists and is non-empty directory
546         */
547        if (supportsLinks) {
548            source = dir1.resolve("link");
549            createSymbolicLink(source, dir2);
550            target = getTargetFile(dir2);
551            createDirectory(target);
552            entry = target.resolve("foo");
553            createFile(entry);
554            try {
555                moveAndVerify(source, target);
556                throw new RuntimeException("FileAlreadyExistsException expected");
557            } catch (FileAlreadyExistsException x) {
558            }
559            delete(entry);
560            delete(source);
561            delete(target);
562        }
563
564        /**
565         * Test atomic move of symbolic link (same file store)
566         */
567        if (supportsLinks) {
568            source = dir1.resolve("link");
569            createSymbolicLink(source, dir1);
570            target = getTargetFile(dir2);
571            createFile(target);
572            moveAndVerify(source, target, REPLACE_EXISTING);
573            delete(target);
574        }
575
576        // -- misc. tests --
577
578        /**
579         * Test nulls
580         */
581        source = createSourceFile(dir1);
582        target = getTargetFile(dir2);
583        try {
584            move(null, target);
585            throw new RuntimeException("NullPointerException expected");
586        } catch (NullPointerException x) { }
587        try {
588            move(source, null);
589            throw new RuntimeException("NullPointerException expected");
590        } catch (NullPointerException x) { }
591        try {
592            move(source, target, (CopyOption[])null);
593            throw new RuntimeException("NullPointerException expected");
594        } catch (NullPointerException x) { }
595        try {
596            CopyOption[] opts = { REPLACE_EXISTING, null };
597            move(source, target, opts);
598            throw new RuntimeException("NullPointerException expected");
599        } catch (NullPointerException x) { }
600        delete(source);
601
602        /**
603         * Test UOE
604         */
605        source = createSourceFile(dir1);
606        target = getTargetFile(dir2);
607        try {
608            move(source, target, new CopyOption() { });
609        } catch (UnsupportedOperationException x) { }
610        try {
611            move(source, target, REPLACE_EXISTING,  new CopyOption() { });
612        } catch (UnsupportedOperationException x) { }
613        delete(source);
614    }
615
616    // copy source to target with verification
617    static void copyAndVerify(Path source, Path target, CopyOption... options)
618        throws IOException
619    {
620        Path result = copy(source, target, options);
621        assertTrue(result == target);
622
623        // get attributes of source and target file to verify copy
624        boolean followLinks = true;
625        LinkOption[] linkOptions = new LinkOption[0];
626        boolean copyAttributes = false;
627        for (CopyOption opt : options) {
628            if (opt == NOFOLLOW_LINKS) {
629                followLinks = false;
630                linkOptions = new LinkOption[] { NOFOLLOW_LINKS };
631            }
632            if (opt == COPY_ATTRIBUTES)
633                copyAttributes = true;
634        }
635        BasicFileAttributes basicAttributes =
636            readAttributes(source, BasicFileAttributes.class, linkOptions);
637
638        // check hash if regular file
639        if (basicAttributes.isRegularFile())
640            assertTrue(computeHash(source) == computeHash(target));
641
642        // check link target if symbolic link
643        if (basicAttributes.isSymbolicLink())
644            assert(readSymbolicLink(source).equals(readSymbolicLink(target)));
645
646        // check that attributes are copied
647        if (copyAttributes && followLinks) {
648            checkBasicAttributes(basicAttributes,
649                readAttributes(source, BasicFileAttributes.class, linkOptions));
650
651            // verify other attributes when same provider
652            if (source.getFileSystem().provider() == target.getFileSystem().provider()) {
653
654                // check POSIX attributes are copied
655                String os = System.getProperty("os.name");
656                if ((os.equals("SunOS") || os.equals("Linux")) &&
657                    testPosixAttributes)
658                {
659                    checkPosixAttributes(
660                        readAttributes(source, PosixFileAttributes.class, linkOptions),
661                        readAttributes(target, PosixFileAttributes.class, linkOptions));
662                }
663
664                // check DOS attributes are copied
665                if (os.startsWith("Windows")) {
666                    checkDosAttributes(
667                        readAttributes(source, DosFileAttributes.class, linkOptions),
668                        readAttributes(target, DosFileAttributes.class, linkOptions));
669                }
670
671                // check named attributes are copied
672                if (followLinks &&
673                    getFileStore(source).supportsFileAttributeView("xattr") &&
674                    getFileStore(target).supportsFileAttributeView("xattr"))
675                {
676                    checkUserDefinedFileAttributes(readUserDefinedFileAttributes(source),
677                                                   readUserDefinedFileAttributes(target));
678                }
679            }
680        }
681    }
682
683    /**
684     * Tests all possible ways to invoke copy to copy a file to a file
685     */
686    static void testCopyFileToFile(Path dir1, Path dir2, boolean supportsLinks)
687        throws IOException
688    {
689        Path source, target, link, entry;
690
691        // -- regular file --
692
693        /**
694         * Test: move regular file, target does not exist
695         */
696        source = createSourceFile(dir1);
697        target = getTargetFile(dir2);
698        copyAndVerify(source, target);
699        delete(source);
700        delete(target);
701
702        /**
703         * Test: copy regular file, target exists
704         */
705        source = createSourceFile(dir1);
706        target = getTargetFile(dir2);
707        createFile(target);
708        try {
709            copyAndVerify(source, target);
710            throw new RuntimeException("FileAlreadyExistsException expected");
711        } catch (FileAlreadyExistsException x) {
712        }
713        delete(target);
714        createDirectory(target);
715        try {
716            copyAndVerify(source, target);
717            throw new RuntimeException("FileAlreadyExistsException expected");
718        } catch (FileAlreadyExistsException x) {
719        }
720        delete(source);
721        delete(target);
722
723        /**
724         * Test: copy regular file, target does not exist
725         */
726        source = createSourceFile(dir1);
727        target = getTargetFile(dir2);
728        copyAndVerify(source, target, REPLACE_EXISTING);
729        delete(source);
730        delete(target);
731
732        /**
733         * Test: copy regular file, target exists
734         */
735        source = createSourceFile(dir1);
736        target = getTargetFile(dir2);
737        createFile(target);
738        copyAndVerify(source, target, REPLACE_EXISTING);
739        delete(source);
740        delete(target);
741
742        /**
743         * Test: copy regular file, target exists and is empty directory
744         */
745        source = createSourceFile(dir1);
746        target = getTargetFile(dir2);
747        createDirectory(target);
748        copyAndVerify(source, target, REPLACE_EXISTING);
749        delete(source);
750        delete(target);
751
752        /**
753         * Test: copy regular file, target exists and is non-empty directory
754         */
755        source = createSourceFile(dir1);
756        target = getTargetFile(dir2);
757        createDirectory(target);
758        entry = target.resolve("foo");
759        createFile(entry);
760        try {
761            copyAndVerify(source, target);
762            throw new RuntimeException("FileAlreadyExistsException expected");
763        } catch (FileAlreadyExistsException x) {
764        }
765        delete(entry);
766        delete(source);
767        delete(target);
768
769        /**
770         * Test: copy regular file + attributes
771         */
772        source = createSourceFile(dir1);
773        target = getTargetFile(dir2);
774        copyAndVerify(source, target, COPY_ATTRIBUTES);
775        delete(source);
776        delete(target);
777
778
779        // -- directory --
780
781        /*
782         * Test: copy directory, target does not exist
783         */
784        source = createSourceDirectory(dir1);
785        target = getTargetFile(dir2);
786        copyAndVerify(source, target);
787        delete(source);
788        delete(target);
789
790        /**
791         * Test: copy directory, target exists
792         */
793        source = createSourceDirectory(dir1);
794        target = getTargetFile(dir2);
795        createFile(target);
796        try {
797            copyAndVerify(source, target);
798            throw new RuntimeException("FileAlreadyExistsException expected");
799        } catch (FileAlreadyExistsException x) {
800        }
801        delete(target);
802        createDirectory(target);
803        try {
804            copyAndVerify(source, target);
805            throw new RuntimeException("FileAlreadyExistsException expected");
806        } catch (FileAlreadyExistsException x) {
807        }
808        delete(source);
809        delete(target);
810
811        /**
812         * Test: copy directory, target does not exist
813         */
814        source = createSourceDirectory(dir1);
815        target = getTargetFile(dir2);
816        copyAndVerify(source, target, REPLACE_EXISTING);
817        delete(source);
818        delete(target);
819
820        /**
821         * Test: copy directory, target exists
822         */
823        source = createSourceDirectory(dir1);
824        target = getTargetFile(dir2);
825        createFile(target);
826        copyAndVerify(source, target, REPLACE_EXISTING);
827        delete(source);
828        delete(target);
829
830        /**
831         * Test: copy directory, target exists and is empty directory
832         */
833        source = createSourceDirectory(dir1);
834        target = getTargetFile(dir2);
835        createDirectory(target);
836        copyAndVerify(source, target, REPLACE_EXISTING);
837        delete(source);
838        delete(target);
839
840        /**
841         * Test: copy directory, target exists and is non-empty directory
842         */
843        source = createSourceDirectory(dir1);
844        target = getTargetFile(dir2);
845        createDirectory(target);
846        entry = target.resolve("foo");
847        createFile(entry);
848        try {
849            copyAndVerify(source, target, REPLACE_EXISTING);
850            throw new RuntimeException("DirectoryNotEmptyException expected");
851        } catch (DirectoryNotEmptyException x) {
852        }
853        delete(entry);
854        delete(source);
855        delete(target);
856
857        /*
858         * Test: copy directory + attributes
859         */
860        source = createSourceDirectory(dir1);
861        target = getTargetFile(dir2);
862        copyAndVerify(source, target, COPY_ATTRIBUTES);
863        delete(source);
864        delete(target);
865
866        // -- symbolic links --
867
868        /**
869         * Test: Follow link
870         */
871        if (supportsLinks) {
872            source = createSourceFile(dir1);
873            link = dir1.resolve("link");
874            createSymbolicLink(link, source);
875            target = getTargetFile(dir2);
876            copyAndVerify(link, target);
877            delete(link);
878            delete(source);
879        }
880
881        /**
882         * Test: Copy link (to file)
883         */
884        if (supportsLinks) {
885            source = createSourceFile(dir1);
886            link = dir1.resolve("link");
887            createSymbolicLink(link, source);
888            target = getTargetFile(dir2);
889            copyAndVerify(link, target, NOFOLLOW_LINKS);
890            delete(link);
891            delete(source);
892        }
893
894        /**
895         * Test: Copy link (to directory)
896         */
897        if (supportsLinks) {
898            source = dir1.resolve("mydir");
899            createDirectory(source);
900            link = dir1.resolve("link");
901            createSymbolicLink(link, source);
902            target = getTargetFile(dir2);
903            copyAndVerify(link, target, NOFOLLOW_LINKS);
904            delete(link);
905            delete(source);
906        }
907
908        /**
909         * Test: Copy broken link
910         */
911        if (supportsLinks) {
912            assertTrue(notExists(source));
913            link = dir1.resolve("link");
914            createSymbolicLink(link, source);
915            target = getTargetFile(dir2);
916            copyAndVerify(link, target, NOFOLLOW_LINKS);
917            delete(link);
918        }
919
920        /**
921         * Test: Copy link to UNC (Windows only)
922         */
923        if (supportsLinks &&
924            System.getProperty("os.name").startsWith("Windows"))
925        {
926            Path unc = Paths.get("\\\\rialto\\share\\file");
927            link = dir1.resolve("link");
928            createSymbolicLink(link, unc);
929            target = getTargetFile(dir2);
930            copyAndVerify(link, target, NOFOLLOW_LINKS);
931            delete(link);
932        }
933
934        // -- misc. tests --
935
936        /**
937         * Test nulls
938         */
939        source = createSourceFile(dir1);
940        target = getTargetFile(dir2);
941        try {
942            copy(source, null);
943            throw new RuntimeException("NullPointerException expected");
944        } catch (NullPointerException x) { }
945        try {
946            copy(source, target, (CopyOption[])null);
947            throw new RuntimeException("NullPointerException expected");
948        } catch (NullPointerException x) { }
949        try {
950            CopyOption[] opts = { REPLACE_EXISTING, null };
951            copy(source, target, opts);
952            throw new RuntimeException("NullPointerException expected");
953        } catch (NullPointerException x) { }
954        delete(source);
955
956        /**
957         * Test UOE
958         */
959        source = createSourceFile(dir1);
960        target = getTargetFile(dir2);
961        try {
962            copy(source, target, new CopyOption() { });
963        } catch (UnsupportedOperationException x) { }
964        try {
965            copy(source, target, REPLACE_EXISTING,  new CopyOption() { });
966        } catch (UnsupportedOperationException x) { }
967        delete(source);
968    }
969
970    /**
971     * Test copy from an input stream to a file
972     */
973    static void testCopyInputStreamToFile() throws IOException {
974        testCopyInputStreamToFile(0);
975        for (int i=0; i<100; i++) {
976            testCopyInputStreamToFile(rand.nextInt(32000));
977        }
978
979        // FileAlreadyExistsException
980        Path target = createTempFile("blah", null);
981        try {
982            InputStream in = new ByteArrayInputStream(new byte[0]);
983            try {
984                copy(in, target);
985                throw new RuntimeException("FileAlreadyExistsException expected");
986            } catch (FileAlreadyExistsException ignore) { }
987        } finally {
988            delete(target);
989        }
990        Path tmpdir = createTempDirectory("blah");
991        try {
992            if (TestUtil.supportsLinks(tmpdir)) {
993                Path link = createSymbolicLink(tmpdir.resolve("link"),
994                                                  tmpdir.resolve("target"));
995                try {
996                    InputStream in = new ByteArrayInputStream(new byte[0]);
997                    try {
998                        copy(in, link);
999                        throw new RuntimeException("FileAlreadyExistsException expected");
1000                    } catch (FileAlreadyExistsException ignore) { }
1001                } finally {
1002                    delete(link);
1003                }
1004            }
1005        } finally {
1006            delete(tmpdir);
1007        }
1008
1009
1010        // nulls
1011        try {
1012            copy((InputStream)null, target);
1013            throw new RuntimeException("NullPointerException expected");
1014        } catch (NullPointerException ignore) { }
1015        try {
1016            copy(new ByteArrayInputStream(new byte[0]), (Path)null);
1017            throw new RuntimeException("NullPointerException expected");
1018        } catch (NullPointerException ignore) { }
1019    }
1020
1021    static void testCopyInputStreamToFile(int size) throws IOException {
1022        Path tmpdir = createTempDirectory("blah");
1023        Path source = tmpdir.resolve("source");
1024        Path target = tmpdir.resolve("target");
1025        try {
1026            boolean testReplaceExisting = rand.nextBoolean();
1027
1028            // create source file
1029            byte[] b = new byte[size];
1030            rand.nextBytes(b);
1031            write(source, b);
1032
1033            // target file might already exist
1034            if (testReplaceExisting && rand.nextBoolean()) {
1035                write(target, new byte[rand.nextInt(512)]);
1036            }
1037
1038            // copy from stream to file
1039            InputStream in = new FileInputStream(source.toFile());
1040            try {
1041                long n;
1042                if (testReplaceExisting) {
1043                    n = copy(in, target, StandardCopyOption.REPLACE_EXISTING);
1044                } else {
1045                    n = copy(in, target);
1046                }
1047                assertTrue(in.read() == -1);   // EOF
1048                assertTrue(n == size);
1049                assertTrue(size(target) == size);
1050            } finally {
1051                in.close();
1052            }
1053
1054            // check file
1055            byte[] read = readAllBytes(target);
1056            assertTrue(Arrays.equals(read, b));
1057
1058        } finally {
1059            deleteIfExists(source);
1060            deleteIfExists(target);
1061            delete(tmpdir);
1062        }
1063    }
1064
1065    /**
1066     * Test copy from file to output stream
1067     */
1068    static void testCopyFileToOuputStream() throws IOException {
1069        testCopyFileToOuputStream(0);
1070        for (int i=0; i<100; i++) {
1071            testCopyFileToOuputStream(rand.nextInt(32000));
1072        }
1073
1074        // nulls
1075        try {
1076            copy((Path)null, new ByteArrayOutputStream());
1077            throw new RuntimeException("NullPointerException expected");
1078        } catch (NullPointerException ignore) { }
1079        try {
1080            Path source = createTempFile("blah", null);
1081            delete(source);
1082            copy(source, (OutputStream)null);
1083            throw new RuntimeException("NullPointerException expected");
1084        } catch (NullPointerException ignore) { }
1085    }
1086
1087    static void testCopyFileToOuputStream(int size) throws IOException {
1088        Path source = createTempFile("blah", null);
1089        try {
1090            byte[] b = new byte[size];
1091            rand.nextBytes(b);
1092            write(source, b);
1093
1094            ByteArrayOutputStream out = new ByteArrayOutputStream();
1095
1096            long n = copy(source, out);
1097            assertTrue(n == size);
1098            assertTrue(out.size() == size);
1099
1100            byte[] read = out.toByteArray();
1101            assertTrue(Arrays.equals(read, b));
1102
1103            // check output stream is open
1104            out.write(0);
1105            assertTrue(out.size() == size+1);
1106        } finally {
1107            delete(source);
1108        }
1109    }
1110
1111    static void assertTrue(boolean value) {
1112        if (!value)
1113            throw new RuntimeException("Assertion failed");
1114    }
1115
1116    // computes simple hash of the given file
1117    static int computeHash(Path file) throws IOException {
1118        int h = 0;
1119
1120        try (InputStream in = newInputStream(file)) {
1121            byte[] buf = new byte[1024];
1122            int n;
1123            do {
1124                n = in.read(buf);
1125                for (int i=0; i<n; i++) {
1126                    h = 31*h + (buf[i] & 0xff);
1127                }
1128            } while (n > 0);
1129        }
1130        return h;
1131    }
1132
1133    // create file of random size in given directory
1134    static Path createSourceFile(Path dir) throws IOException {
1135        String name = "source" + Integer.toString(rand.nextInt());
1136        Path file = dir.resolve(name);
1137        createFile(file);
1138        byte[] bytes = new byte[rand.nextInt(128*1024)];
1139        rand.nextBytes(bytes);
1140        try (OutputStream out = newOutputStream(file)) {
1141            out.write(bytes);
1142        }
1143        randomizeAttributes(file);
1144        return file;
1145    }
1146
1147    // create directory in the given directory
1148    static Path createSourceDirectory(Path dir) throws IOException {
1149        String name = "sourcedir" + Integer.toString(rand.nextInt());
1150        Path subdir = dir.resolve(name);
1151        createDirectory(subdir);
1152        randomizeAttributes(subdir);
1153        return subdir;
1154    }
1155
1156    // "randomize" the file attributes of the given file.
1157    static void randomizeAttributes(Path file) throws IOException {
1158        String os = System.getProperty("os.name");
1159        boolean isWindows = os.startsWith("Windows");
1160        boolean isUnix = os.equals("SunOS") || os.equals("Linux");
1161        boolean isDirectory = isDirectory(file, NOFOLLOW_LINKS);
1162
1163        if (isUnix) {
1164            Set<PosixFilePermission> perms =
1165                getPosixFilePermissions(file, NOFOLLOW_LINKS);
1166            PosixFilePermission[] toChange = {
1167                PosixFilePermission.GROUP_READ,
1168                PosixFilePermission.GROUP_WRITE,
1169                PosixFilePermission.GROUP_EXECUTE,
1170                PosixFilePermission.OTHERS_READ,
1171                PosixFilePermission.OTHERS_WRITE,
1172                PosixFilePermission.OTHERS_EXECUTE
1173            };
1174            for (PosixFilePermission perm: toChange) {
1175                if (heads()) {
1176                    perms.add(perm);
1177                } else {
1178                    perms.remove(perm);
1179                }
1180            }
1181            setPosixFilePermissions(file, perms);
1182        }
1183
1184        if (isWindows) {
1185            DosFileAttributeView view =
1186                getFileAttributeView(file, DosFileAttributeView.class, NOFOLLOW_LINKS);
1187            // only set or unset the hidden attribute
1188            view.setHidden(heads());
1189        }
1190
1191        boolean addUserDefinedFileAttributes = heads() &&
1192            getFileStore(file).supportsFileAttributeView("xattr");
1193
1194        // remove this when copying a direcory copies its named streams
1195        if (isWindows && isDirectory) addUserDefinedFileAttributes = false;
1196
1197        if (addUserDefinedFileAttributes) {
1198            UserDefinedFileAttributeView view =
1199                getFileAttributeView(file, UserDefinedFileAttributeView.class);
1200            int n = rand.nextInt(16);
1201            while (n > 0) {
1202                byte[] value = new byte[1 + rand.nextInt(100)];
1203                view.write("user." + Integer.toString(n), ByteBuffer.wrap(value));
1204                n--;
1205            }
1206        }
1207    }
1208
1209    // create name for file in given directory
1210    static Path getTargetFile(Path dir) throws IOException {
1211        String name = "target" + Integer.toString(rand.nextInt());
1212        return dir.resolve(name);
1213    }
1214 }
1215