View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.io.file;
19  
20  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertFalse;
23  import static org.junit.jupiter.api.Assertions.assertNotEquals;
24  import static org.junit.jupiter.api.Assertions.assertNotNull;
25  import static org.junit.jupiter.api.Assertions.assertNull;
26  import static org.junit.jupiter.api.Assertions.assertThrows;
27  import static org.junit.jupiter.api.Assertions.assertTrue;
28  import static org.junit.jupiter.api.Assumptions.assumeFalse;
29  
30  import java.io.File;
31  import java.io.IOException;
32  import java.io.OutputStream;
33  import java.net.URI;
34  import java.net.URISyntaxException;
35  import java.net.URL;
36  import java.nio.charset.StandardCharsets;
37  import java.nio.file.DirectoryStream;
38  import java.nio.file.FileSystem;
39  import java.nio.file.FileSystems;
40  import java.nio.file.Files;
41  import java.nio.file.LinkOption;
42  import java.nio.file.Path;
43  import java.nio.file.Paths;
44  import java.nio.file.attribute.DosFileAttributeView;
45  import java.nio.file.attribute.FileTime;
46  import java.nio.file.attribute.PosixFileAttributes;
47  import java.util.GregorianCalendar;
48  import java.util.HashMap;
49  import java.util.Iterator;
50  import java.util.Map;
51  
52  import org.apache.commons.io.FileUtils;
53  import org.apache.commons.io.FilenameUtils;
54  import org.apache.commons.io.filefilter.NameFileFilter;
55  import org.apache.commons.io.test.TestUtils;
56  import org.apache.commons.lang3.ArrayUtils;
57  import org.apache.commons.lang3.StringUtils;
58  import org.apache.commons.lang3.SystemProperties;
59  import org.apache.commons.lang3.SystemUtils;
60  import org.junit.jupiter.api.Test;
61  
62  /**
63   * Tests {@link PathUtils}.
64   */
65  public class PathUtilsTest extends AbstractTempDirTest {
66  
67      private static final String STRING_FIXTURE = "Hello World";
68  
69      private static final byte[] BYTE_ARRAY_FIXTURE = STRING_FIXTURE.getBytes(StandardCharsets.UTF_8);
70  
71      private static final String TEST_JAR_NAME = "test.jar";
72  
73      private static final String TEST_JAR_PATH = "src/test/resources/org/apache/commons/io/test.jar";
74  
75      private static final String PATH_FIXTURE = "NOTICE.txt";
76  
77      private Path current() {
78          return PathUtils.current();
79      }
80  
81      private Long getLastModifiedMillis(final Path file) throws IOException {
82          return Files.getLastModifiedTime(file).toMillis();
83      }
84  
85      private Path getNonExistentPath() {
86          return Paths.get("/does not exist/for/certain");
87      }
88  
89      private FileSystem openArchive(final Path p, final boolean createNew) throws IOException {
90          if (createNew) {
91              final Map<String, String> env = new HashMap<>();
92              env.put("create", "true");
93              final URI fileUri = p.toAbsolutePath().toUri();
94              final URI uri = URI.create("jar:" + fileUri.toASCIIString());
95              return FileSystems.newFileSystem(uri, env, null);
96          }
97          return FileSystems.newFileSystem(p, (ClassLoader) null);
98      }
99  
100     private void setLastModifiedMillis(final Path file, final long millis) throws IOException {
101         Files.setLastModifiedTime(file, FileTime.fromMillis(millis));
102     }
103 
104     @Test
105     public void testCopyDirectoryForDifferentFilesystemsWithAbsolutePath() throws IOException {
106         final Path archivePath = Paths.get(TEST_JAR_PATH);
107         try (FileSystem archive = openArchive(archivePath, false)) {
108             // relative jar -> absolute dir
109             Path sourceDir = archive.getPath("dir1");
110             PathUtils.copyDirectory(sourceDir, tempDirPath);
111             assertTrue(Files.exists(tempDirPath.resolve("f1")));
112 
113             // absolute jar -> absolute dir
114             sourceDir = archive.getPath("/next");
115             PathUtils.copyDirectory(sourceDir, tempDirPath);
116             assertTrue(Files.exists(tempDirPath.resolve("dir")));
117         }
118     }
119 
120     @Test
121     public void testCopyDirectoryForDifferentFilesystemsWithAbsolutePathReverse() throws IOException {
122         try (FileSystem archive = openArchive(tempDirPath.resolve(TEST_JAR_NAME), true)) {
123             // absolute dir -> relative jar
124             Path targetDir = archive.getPath("target");
125             Files.createDirectory(targetDir);
126             final Path sourceDir = Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2").toAbsolutePath();
127             PathUtils.copyDirectory(sourceDir, targetDir);
128             assertTrue(Files.exists(targetDir.resolve("dirs-a-file-size-1")));
129 
130             // absolute dir -> absolute jar
131             targetDir = archive.getPath("/");
132             PathUtils.copyDirectory(sourceDir, targetDir);
133             assertTrue(Files.exists(targetDir.resolve("dirs-a-file-size-1")));
134         }
135     }
136 
137     @Test
138     public void testCopyDirectoryForDifferentFilesystemsWithRelativePath() throws IOException {
139         final Path archivePath = Paths.get(TEST_JAR_PATH);
140         try (FileSystem archive = openArchive(archivePath, false);
141                 FileSystem targetArchive = openArchive(tempDirPath.resolve(TEST_JAR_NAME), true)) {
142             final Path targetDir = targetArchive.getPath("targetDir");
143             Files.createDirectory(targetDir);
144             // relative jar -> relative dir
145             Path sourceDir = archive.getPath("next");
146             PathUtils.copyDirectory(sourceDir, targetDir);
147             assertTrue(Files.exists(targetDir.resolve("dir")));
148 
149             // absolute jar -> relative dir
150             sourceDir = archive.getPath("/dir1");
151             PathUtils.copyDirectory(sourceDir, targetDir);
152             assertTrue(Files.exists(targetDir.resolve("f1")));
153         }
154     }
155 
156     @Test
157     public void testCopyDirectoryForDifferentFilesystemsWithRelativePathReverse() throws IOException {
158         try (FileSystem archive = openArchive(tempDirPath.resolve(TEST_JAR_NAME), true)) {
159             // relative dir -> relative jar
160             Path targetDir = archive.getPath("target");
161             Files.createDirectory(targetDir);
162             final Path sourceDir = Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2");
163             PathUtils.copyDirectory(sourceDir, targetDir);
164             assertTrue(Files.exists(targetDir.resolve("dirs-a-file-size-1")));
165 
166             // relative dir -> absolute jar
167             targetDir = archive.getPath("/");
168             PathUtils.copyDirectory(sourceDir, targetDir);
169             assertTrue(Files.exists(targetDir.resolve("dirs-a-file-size-1")));
170         }
171     }
172 
173     @Test
174     public void testCopyFile() throws IOException {
175         final Path sourceFile = Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1/file-size-1.bin");
176         final Path targetFile = PathUtils.copyFileToDirectory(sourceFile, tempDirPath);
177         assertTrue(Files.exists(targetFile));
178         assertEquals(Files.size(sourceFile), Files.size(targetFile));
179     }
180 
181     @Test
182     public void testCopyFileTwoFileSystem() throws IOException {
183         try (FileSystem archive = openArchive(Paths.get(TEST_JAR_PATH), false)) {
184             final Path sourceFile = archive.getPath("next/dir/test.log");
185             final Path targetFile = PathUtils.copyFileToDirectory(sourceFile, tempDirPath);
186             assertTrue(Files.exists(targetFile));
187             assertEquals(Files.size(sourceFile), Files.size(targetFile));
188         }
189     }
190 
191     @Test
192     public void testCopyURL() throws IOException {
193         final Path sourceFile = Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1/file-size-1.bin");
194         final URL url = new URL("file:///" + FilenameUtils.getPath(sourceFile.toAbsolutePath().toString()) + sourceFile.getFileName());
195         final Path targetFile = PathUtils.copyFileToDirectory(url, tempDirPath);
196         assertTrue(Files.exists(targetFile));
197         assertEquals(Files.size(sourceFile), Files.size(targetFile));
198     }
199 
200     @Test
201     public void testCreateDirectoriesAlreadyExists() throws IOException {
202         assertEquals(tempDirPath.getParent(), PathUtils.createParentDirectories(tempDirPath));
203     }
204 
205     @SuppressWarnings("resource") // FileSystems.getDefault() is a singleton
206     @Test
207     public void testCreateDirectoriesForRoots() throws IOException {
208         for (final Path path : FileSystems.getDefault().getRootDirectories()) {
209             final Path parent = path.getParent();
210             assertNull(parent);
211             assertEquals(parent, PathUtils.createParentDirectories(path));
212         }
213     }
214 
215     @Test
216     public void testCreateDirectoriesForRootsLinkOptionNull() throws IOException {
217         for (final File f : File.listRoots()) {
218             final Path path = f.toPath();
219             assertEquals(path.getParent(), PathUtils.createParentDirectories(path, (LinkOption) null));
220         }
221     }
222 
223     @Test
224     public void testCreateDirectoriesNew() throws IOException {
225         assertEquals(tempDirPath, PathUtils.createParentDirectories(tempDirPath.resolve("child")));
226     }
227 
228     @Test
229     public void testCreateDirectoriesSymlink() throws IOException {
230         final Path symlinkedDir = createTempSymbolicLinkedRelativeDir(tempDirPath);
231         final String leafDirName = "child";
232         final Path newDirFollowed = PathUtils.createParentDirectories(symlinkedDir.resolve(leafDirName), PathUtils.NULL_LINK_OPTION);
233         assertEquals(Files.readSymbolicLink(symlinkedDir), newDirFollowed);
234     }
235 
236     @Test
237     public void testCreateDirectoriesSymlinkClashing() throws IOException {
238         final Path symlinkedDir = createTempSymbolicLinkedRelativeDir(tempDirPath);
239         assertEquals(symlinkedDir, PathUtils.createParentDirectories(symlinkedDir.resolve("child")));
240     }
241 
242     @Test
243     public void testGetBaseNamePathBaseCases() {
244         assertEquals("bar", PathUtils.getBaseName(Paths.get("a/b/c/bar.foo")));
245         assertEquals("foo", PathUtils.getBaseName(Paths.get("foo")));
246         assertEquals("", PathUtils.getBaseName(Paths.get("")));
247         assertEquals("", PathUtils.getBaseName(Paths.get(".")));
248         for (final File f : File.listRoots()) {
249             assertNull(PathUtils.getBaseName(f.toPath()));
250         }
251         if (SystemUtils.IS_OS_WINDOWS) {
252             assertNull(PathUtils.getBaseName(Paths.get("C:\\")));
253         }
254     }
255 
256     @Test
257     public void testGetBaseNamePathCornerCases() {
258         assertNull(PathUtils.getBaseName((Path) null));
259         assertEquals("foo", PathUtils.getBaseName(Paths.get("foo.")));
260         assertEquals("", PathUtils.getBaseName(Paths.get("bar/.foo")));
261     }
262 
263     @Test
264     public void testGetDosFileAttributeView() {
265         // dir
266         final DosFileAttributeView dosFileAttributeView = PathUtils.getDosFileAttributeView(current());
267         final Path path = Paths.get("this-file-does-not-exist-at.all");
268         assertFalse(Files.exists(path));
269         if (SystemUtils.IS_OS_MAC) {
270             assertNull(dosFileAttributeView);
271             // missing file
272             assertNull(PathUtils.getDosFileAttributeView(path));
273         } else {
274             assertNotNull(dosFileAttributeView);
275             // missing file
276             assertNotNull(PathUtils.getDosFileAttributeView(path));
277         }
278         // null
279         assertThrows(NullPointerException.class, () -> PathUtils.getDosFileAttributeView(null));
280     }
281 
282     @Test
283     public void testGetExtension() {
284         assertNull(PathUtils.getExtension(null));
285         assertEquals("ext", PathUtils.getExtension(Paths.get("file.ext")));
286         assertEquals("", PathUtils.getExtension(Paths.get("README")));
287         assertEquals("com", PathUtils.getExtension(Paths.get("domain.dot.com")));
288         assertEquals("jpeg", PathUtils.getExtension(Paths.get("image.jpeg")));
289         assertEquals("", PathUtils.getExtension(Paths.get("a.b/c")));
290         assertEquals("txt", PathUtils.getExtension(Paths.get("a.b/c.txt")));
291         assertEquals("", PathUtils.getExtension(Paths.get("a/b/c")));
292         assertEquals("", PathUtils.getExtension(Paths.get("a.b\\c")));
293         assertEquals("txt", PathUtils.getExtension(Paths.get("a.b\\c.txt")));
294         assertEquals("", PathUtils.getExtension(Paths.get("a\\b\\c")));
295         assertEquals("", PathUtils.getExtension(Paths.get("C:\\temp\\foo.bar\\README")));
296         assertEquals("ext", PathUtils.getExtension(Paths.get("../filename.ext")));
297 
298         if (File.separatorChar != '\\') {
299             // Upwards compatibility:
300             assertEquals("txt", PathUtils.getExtension(Paths.get("foo.exe:bar.txt")));
301         }
302     }
303 
304     @Test
305     public void testGetFileName() {
306         assertNull(PathUtils.getFileName(null, null));
307         assertNull(PathUtils.getFileName(null, Path::toString));
308         assertNull(PathUtils.getFileName(Paths.get("/"), Path::toString));
309         assertNull(PathUtils.getFileName(Paths.get("/"), Path::toString));
310         assertEquals("", PathUtils.getFileName(Paths.get(""), Path::toString));
311         assertEquals("a", PathUtils.getFileName(Paths.get("a"), Path::toString));
312         assertEquals("a", PathUtils.getFileName(Paths.get("p", "a"), Path::toString));
313     }
314 
315     @Test
316     public void testGetFileNameString() {
317         assertNull(PathUtils.getFileNameString(Paths.get("/")));
318         assertEquals("", PathUtils.getFileNameString(Paths.get("")));
319         assertEquals("a", PathUtils.getFileNameString(Paths.get("a")));
320         assertEquals("a", PathUtils.getFileNameString(Paths.get("p", "a")));
321     }
322 
323     @Test
324     public void testGetLastModifiedFileTime_File_Present() throws IOException {
325         assertNotNull(PathUtils.getLastModifiedFileTime(current().toFile()));
326     }
327 
328     @Test
329     public void testGetLastModifiedFileTime_Path_Absent() throws IOException {
330         assertNull(PathUtils.getLastModifiedFileTime(getNonExistentPath()));
331     }
332 
333     @Test
334     public void testGetLastModifiedFileTime_Path_FileTime_Absent() throws IOException {
335         final FileTime fromMillis = FileTime.fromMillis(0);
336         assertEquals(fromMillis, PathUtils.getLastModifiedFileTime(getNonExistentPath(), fromMillis));
337     }
338 
339     @Test
340     public void testGetLastModifiedFileTime_Path_Present() throws IOException {
341         assertNotNull(PathUtils.getLastModifiedFileTime(current()));
342     }
343 
344     @Test
345     public void testGetLastModifiedFileTime_URI_Present() throws IOException {
346         assertNotNull(PathUtils.getLastModifiedFileTime(current().toUri()));
347     }
348 
349     @Test
350     public void testGetLastModifiedFileTime_URL_Present() throws IOException, URISyntaxException {
351         assertNotNull(PathUtils.getLastModifiedFileTime(current().toUri().toURL()));
352     }
353 
354     @Test
355     public void testGetTempDirectory() {
356         final Path tempDirectory = Paths.get(SystemProperties.getJavaIoTmpdir());
357         assertEquals(tempDirectory, PathUtils.getTempDirectory());
358     }
359 
360     @Test
361     public void testIsDirectory() throws IOException {
362         assertFalse(PathUtils.isDirectory(null));
363 
364         assertTrue(PathUtils.isDirectory(tempDirPath));
365         try (TempFile testFile1 = TempFile.create(tempDirPath, "prefix", null)) {
366             assertFalse(PathUtils.isDirectory(testFile1.get()));
367 
368             Path ref = null;
369             try (TempDirectory tempDir = TempDirectory.create(getClass().getCanonicalName())) {
370                 ref = tempDir.get();
371                 assertTrue(PathUtils.isDirectory(tempDir.get()));
372             }
373             assertFalse(PathUtils.isDirectory(ref));
374         }
375     }
376 
377     @Test
378     public void testIsPosix() throws IOException {
379         boolean isPosix;
380         try {
381             Files.getPosixFilePermissions(current());
382             isPosix = true;
383         } catch (final UnsupportedOperationException e) {
384             isPosix = false;
385         }
386         assertEquals(isPosix, PathUtils.isPosix(current()));
387     }
388 
389     @Test
390     public void testIsPosixAbsentFile() {
391         assertFalse(PathUtils.isPosix(Paths.get("ImNotHereAtAllEver.never")));
392         assertFalse(PathUtils.isPosix(null));
393     }
394 
395     @Test
396     public void testIsRegularFile() throws IOException {
397         assertFalse(PathUtils.isRegularFile(null));
398 
399         assertFalse(PathUtils.isRegularFile(tempDirPath));
400         try (TempFile testFile1 = TempFile.create(tempDirPath, "prefix", null)) {
401             assertTrue(PathUtils.isRegularFile(testFile1.get()));
402 
403             Files.delete(testFile1.get());
404             assertFalse(PathUtils.isRegularFile(testFile1.get()));
405         }
406     }
407 
408     @Test
409     public void testNewDirectoryStream() throws Exception {
410         final PathFilter pathFilter = new NameFileFilter(PATH_FIXTURE);
411         try (DirectoryStream<Path> stream = PathUtils.newDirectoryStream(current(), pathFilter)) {
412             final Iterator<Path> iterator = stream.iterator();
413             final Path path = iterator.next();
414             assertEquals(PATH_FIXTURE, PathUtils.getFileNameString(path));
415             assertFalse(iterator.hasNext());
416         }
417     }
418 
419     @Test
420     public void testNewOutputStreamExistingFileAppendFalse() throws IOException {
421         testNewOutputStreamNewFile(false);
422         testNewOutputStreamNewFile(false);
423     }
424 
425     @Test
426     public void testNewOutputStreamExistingFileAppendTrue() throws IOException {
427         testNewOutputStreamNewFile(true);
428         final Path file = writeToNewOutputStream(true);
429         assertArrayEquals(ArrayUtils.addAll(BYTE_ARRAY_FIXTURE, BYTE_ARRAY_FIXTURE), Files.readAllBytes(file));
430     }
431 
432     public void testNewOutputStreamNewFile(final boolean append) throws IOException {
433         final Path file = writeToNewOutputStream(append);
434         assertArrayEquals(BYTE_ARRAY_FIXTURE, Files.readAllBytes(file));
435     }
436 
437     @Test
438     public void testNewOutputStreamNewFileAppendFalse() throws IOException {
439         testNewOutputStreamNewFile(false);
440     }
441 
442     @Test
443     public void testNewOutputStreamNewFileAppendTrue() throws IOException {
444         testNewOutputStreamNewFile(true);
445     }
446 
447     @Test
448     public void testNewOutputStreamNewFileInsideExistingSymlinkedDir() throws IOException {
449         final Path symlinkDir = createTempSymbolicLinkedRelativeDir(tempDirPath);
450         final Path file = symlinkDir.resolve("test.txt");
451         try (OutputStream outputStream = PathUtils.newOutputStream(file, new LinkOption[] {})) {
452             // empty
453         }
454         try (OutputStream outputStream = PathUtils.newOutputStream(file, null)) {
455             // empty
456         }
457         try (OutputStream outputStream = PathUtils.newOutputStream(file, true)) {
458             // empty
459         }
460         try (OutputStream outputStream = PathUtils.newOutputStream(file, false)) {
461             // empty
462         }
463     }
464 
465     @Test
466     public void testReadAttributesPosix() throws IOException {
467         boolean isPosix;
468         try {
469             Files.getPosixFilePermissions(current());
470             isPosix = true;
471         } catch (final UnsupportedOperationException e) {
472             isPosix = false;
473         }
474         assertEquals(isPosix, PathUtils.readAttributes(current(), PosixFileAttributes.class) != null);
475     }
476 
477     @Test
478     public void testReadStringEmptyFile() throws IOException {
479         final Path path = Paths.get("src/test/resources/org/apache/commons/io/test-file-empty.bin");
480         assertEquals(StringUtils.EMPTY, PathUtils.readString(path, StandardCharsets.UTF_8));
481         assertEquals(StringUtils.EMPTY, PathUtils.readString(path, null));
482     }
483 
484     @Test
485     public void testReadStringSimpleUtf8() throws IOException {
486         final Path path = Paths.get("src/test/resources/org/apache/commons/io/test-file-simple-utf8.bin");
487         final String expected = "ABC\r\n";
488         assertEquals(expected, PathUtils.readString(path, StandardCharsets.UTF_8));
489         assertEquals(expected, PathUtils.readString(path, null));
490     }
491 
492     @Test
493     public void testSetReadOnlyFile() throws IOException {
494         final Path resolved = tempDirPath.resolve("testSetReadOnlyFile.txt");
495         // Ask now, as we are allowed before editing parent permissions.
496         final boolean isPosix = PathUtils.isPosix(tempDirPath);
497 
498         // TEMP HACK
499         assumeFalse(SystemUtils.IS_OS_LINUX);
500 
501         PathUtils.writeString(resolved, "test", StandardCharsets.UTF_8);
502         final boolean readable = Files.isReadable(resolved);
503         final boolean writable = Files.isWritable(resolved);
504         final boolean regularFile = Files.isRegularFile(resolved);
505         final boolean executable = Files.isExecutable(resolved);
506         final boolean hidden = Files.isHidden(resolved);
507         final boolean directory = Files.isDirectory(resolved);
508         final boolean symbolicLink = Files.isSymbolicLink(resolved);
509         // Sanity checks
510         assertTrue(readable);
511         assertTrue(writable);
512         // Test A
513         PathUtils.setReadOnly(resolved, false);
514         assertTrue(Files.isReadable(resolved), "isReadable");
515         assertTrue(Files.isWritable(resolved), "isWritable");
516         // Again, shouldn't blow up.
517         PathUtils.setReadOnly(resolved, false);
518         assertTrue(Files.isReadable(resolved), "isReadable");
519         assertTrue(Files.isWritable(resolved), "isWritable");
520         //
521         assertEquals(regularFile, Files.isReadable(resolved));
522         assertEquals(executable, Files.isExecutable(resolved));
523         assertEquals(hidden, Files.isHidden(resolved));
524         assertEquals(directory, Files.isDirectory(resolved));
525         assertEquals(symbolicLink, Files.isSymbolicLink(resolved));
526         // Test B
527         PathUtils.setReadOnly(resolved, true);
528         if (isPosix) {
529             // On POSIX, now that the parent is not WX, the file is not readable.
530             assertFalse(Files.isReadable(resolved), "isReadable");
531         } else {
532             assertTrue(Files.isReadable(resolved), "isReadable");
533         }
534         assertFalse(Files.isWritable(resolved), "isWritable");
535         final DosFileAttributeView dosFileAttributeView = PathUtils.getDosFileAttributeView(resolved);
536         if (dosFileAttributeView != null) {
537             assertTrue(dosFileAttributeView.readAttributes().isReadOnly());
538         }
539         if (isPosix) {
540             assertFalse(Files.isReadable(resolved));
541         } else {
542             assertEquals(regularFile, Files.isReadable(resolved));
543         }
544         assertEquals(executable, Files.isExecutable(resolved));
545         assertEquals(hidden, Files.isHidden(resolved));
546         assertEquals(directory, Files.isDirectory(resolved));
547         assertEquals(symbolicLink, Files.isSymbolicLink(resolved));
548         //
549         PathUtils.setReadOnly(resolved, false);
550         PathUtils.deleteFile(resolved);
551     }
552 
553     @Test
554     public void testSetReadOnlyFileAbsent() {
555         assertThrows(IOException.class, () -> PathUtils.setReadOnly(Paths.get("does-not-exist-at-all-ever-never"), true));
556     }
557 
558     @Test
559     public void testTouch() throws IOException {
560         assertThrows(NullPointerException.class, () -> FileUtils.touch(null));
561 
562         final Path file = managedTempDirPath.resolve("touch.txt");
563         Files.deleteIfExists(file);
564         assertFalse(Files.exists(file), "Bad test: test file still exists");
565         PathUtils.touch(file);
566         assertTrue(Files.exists(file), "touch() created file");
567         try (OutputStream out = Files.newOutputStream(file)) {
568             assertEquals(0, Files.size(file), "Created empty file.");
569             out.write(0);
570         }
571         assertEquals(1, Files.size(file), "Wrote one byte to file");
572         final long y2k = new GregorianCalendar(2000, 0, 1).getTime().getTime();
573         setLastModifiedMillis(file, y2k); // 0L fails on Win98
574         assertEquals(y2k, getLastModifiedMillis(file), "Bad test: set lastModified set incorrect value");
575         final long nowMillis = System.currentTimeMillis();
576         PathUtils.touch(file);
577         assertEquals(1, Files.size(file), "FileUtils.touch() didn't empty the file.");
578         assertNotEquals(y2k, getLastModifiedMillis(file), "FileUtils.touch() changed lastModified");
579         final int delta = 3000;
580         assertTrue(getLastModifiedMillis(file) >= nowMillis - delta, "FileUtils.touch() changed lastModified to more than now-3s");
581         assertTrue(getLastModifiedMillis(file) <= nowMillis + delta, "FileUtils.touch() changed lastModified to less than now+3s");
582     }
583 
584     @Test
585     public void testWriteStringToFile1() throws Exception {
586         final Path file = tempDirPath.resolve("write.txt");
587         PathUtils.writeString(file, "Hello \u1234", StandardCharsets.UTF_8);
588         final byte[] text = "Hello \u1234".getBytes(StandardCharsets.UTF_8);
589         TestUtils.assertEqualContent(text, file);
590     }
591 
592     /**
593      * Tests newOutputStream() here and don't use Files.write obviously.
594      */
595     private Path writeToNewOutputStream(final boolean append) throws IOException {
596         final Path file = tempDirPath.resolve("test1.txt");
597         try (OutputStream os = PathUtils.newOutputStream(file, append)) {
598             os.write(BYTE_ARRAY_FIXTURE);
599         }
600         return file;
601     }
602 
603 }