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