@file:kotlin.jvm.JvmVersion package test.io import java.io.* import java.util.* import org.junit.Test import kotlin.io.walkTopDown import kotlin.test.* class FilesTest { private val isCaseInsensitiveFileSystem = File("C:/") == File("c:/") private val isBackslashSeparator = File.separatorChar == '\\' @Test fun testPath() { val fileSuf = System.currentTimeMillis().toString() val file1 = createTempFile("temp", fileSuf) assertTrue(file1.path.endsWith(fileSuf), file1.path) } @Test fun testCreateTempDir() { val dirSuf = System.currentTimeMillis().toString() val dir1 = createTempDir("temp", dirSuf) assertTrue(dir1.exists() && dir1.isDirectory && dir1.name.startsWith("temp") && dir1.name.endsWith(dirSuf)) assertFailsWith(IllegalArgumentException::class) { createTempDir("a") } val dir2 = createTempDir("temp") assertTrue(dir2.exists() && dir2.isDirectory && dir2.name.endsWith(".tmp")) val dir3 = createTempDir() assertTrue(dir3.exists() && dir3.isDirectory) dir1.delete() dir2.delete() dir3.delete() } @Test fun testCreateTempFile() { val fileSuf = System.currentTimeMillis().toString() val file1 = createTempFile("temp", fileSuf) assertTrue(file1.exists() && file1.name.startsWith("temp") && file1.name.endsWith(fileSuf)) assertFailsWith(IllegalArgumentException::class) { createTempFile("a") } val file2 = createTempFile("temp") assertTrue(file2.exists() && file2.name.endsWith(".tmp")) val file3 = createTempFile() assertTrue(file3.exists()) file1.delete() file2.delete() file3.delete() } @Test fun listFilesWithFilter() { val dir = createTempDir("temp") createTempFile("temp1", ".kt", dir) createTempFile("temp2", ".java", dir) createTempFile("temp3", ".kt", dir) // This line works only with Kotlin File.listFiles(filter) val result = dir.listFiles { it -> it.name.endsWith(".kt") } // todo ambiguity on SAM assertEquals(2, result!!.size) // This line works both with Kotlin File.listFiles(filter) and the same Java function because of SAM val result2 = dir.listFiles { it -> it.name.endsWith(".kt") } assertEquals(2, result2!!.size) } @Test fun relativeToRooted() { val file1 = File("/foo/bar/baz") val file2 = File("/foo/baa/ghoo") assertEquals("../../bar/baz", file1.relativeTo(file2).invariantSeparatorsPath) val file3 = File("/foo/bar") assertEquals("baz", file1.toRelativeString(file3)) assertEquals("..", file3.toRelativeString(file1)) val file4 = File("/foo/bar/") assertEquals("baz", file1.toRelativeString(file4)) assertEquals("..", file4.toRelativeString(file1)) assertEquals("", file3.toRelativeString(file4)) assertEquals("", file4.toRelativeString(file3)) val file5 = File("/foo/baran") assertEquals("../bar", file3.relativeTo(file5).invariantSeparatorsPath) assertEquals("../baran", file5.relativeTo(file3).invariantSeparatorsPath) assertEquals("../bar", file4.relativeTo(file5).invariantSeparatorsPath) assertEquals("../baran", file5.relativeTo(file4).invariantSeparatorsPath) if (isBackslashSeparator) { val file6 = File("C:\\Users\\Me") val file7 = File("C:\\Users\\Me\\Documents") assertEquals("..", file6.toRelativeString(file7)) assertEquals("Documents", file7.toRelativeString(file6)) val file8 = File("""\\my.host\home/user/documents/vip""") val file9 = File("""\\my.host\home/other/images/nice""") assertEquals("../../../user/documents/vip", file8.relativeTo(file9).invariantSeparatorsPath) assertEquals("../../../other/images/nice", file9.relativeTo(file8).invariantSeparatorsPath) } if (isCaseInsensitiveFileSystem) { assertEquals("bar", File("C:/bar").toRelativeString(File("c:/"))) } } @Test fun relativeToRelative() { val nested = File("foo/bar") val base = File("foo") assertEquals("bar", nested.toRelativeString(base)) assertEquals("..", base.toRelativeString(nested)) val empty = File("") val current = File(".") val parent = File("..") val outOfRoot = File("../bar") assertEquals(File("../bar"), outOfRoot.relativeTo(empty)) assertEquals(File("../../bar"), outOfRoot.relativeTo(base)) assertEquals("bar", outOfRoot.toRelativeString(parent)) assertEquals("..", parent.toRelativeString(outOfRoot)) val root = File("/root") val files = listOf(nested, base, empty, outOfRoot, current, parent) val bases = listOf(nested, base, empty, current) for (file in files) assertEquals("", file.toRelativeString(file), "file should have empty path relative to itself: $file") for (file in files) { @Suppress("NAME_SHADOWING") for (base in bases) { val rootedFile = root.resolve(file) val rootedBase = root.resolve(base) assertEquals(file.relativeTo(base), rootedFile.relativeTo(rootedBase), "nested: $file, base: $base") assertEquals(file.toRelativeString(base), rootedFile.toRelativeString(rootedBase), "strings, nested: $file, base: $base") } } } @Test fun relativeToFails() { val absolute = File("/foo/bar/baz") val relative = File("foo/bar") val networkShare1 = File("""\\my.host\share1/folder""") val networkShare2 = File("""\\my.host\share2\folder""") fun assertFailsRelativeTo(file: File, base: File) { val e = assertFailsWith("file: $file, base: $base") { file.relativeTo(base) } val message = assertNotNull(e.message) assert(file.toString() in message) assert(base.toString() in message) } val allFiles = listOf(absolute, relative) + if (isBackslashSeparator) listOf(networkShare1, networkShare2) else emptyList() for (file in allFiles) { for (base in allFiles) { if (file != base) assertFailsRelativeTo(file, base) } } assertFailsRelativeTo(File("y"), File("../x")) if (isBackslashSeparator) { val fileOnC = File("C:/dir1") val fileOnD = File("D:/dir2") assertFailsRelativeTo(fileOnC, fileOnD) } } @Test fun relativeTo() { assertEquals("kotlin", File("src/kotlin").toRelativeString(File("src"))) assertEquals("", File("dir").toRelativeString(File("dir"))) assertEquals("..", File("dir").toRelativeString(File("dir/subdir"))) assertEquals(File("../../test"), File("test").relativeTo(File("dir/dir"))) } @Suppress("INVISIBLE_MEMBER") private fun checkFilePathComponents(f: File, root: File, elements: List) { assertEquals(root, f.root) val components = f.toComponents() assertEquals(root, components.root) assertEquals(elements, components.segments.map { it.toString() }) } @Test fun filePathComponents() { checkFilePathComponents(File("/foo/bar"), File("/"), listOf("foo", "bar")) checkFilePathComponents(File("/foo/bar/gav"), File("/"), listOf("foo", "bar", "gav")) checkFilePathComponents(File("/foo/bar/gav/"), File("/"), listOf("foo", "bar", "gav")) checkFilePathComponents(File("bar/gav"), File(""), listOf("bar", "gav")) checkFilePathComponents(File("C:/bar/gav"), File("C:/"), listOf("bar", "gav")) checkFilePathComponents(File("C:/"), File("C:/"), listOf()) checkFilePathComponents(File("C:"), File("C:"), listOf()) if (isBackslashSeparator) { // Check only in Windows checkFilePathComponents(File("\\\\host.ru\\home\\mike"), File("\\\\host.ru\\home"), listOf("mike")) checkFilePathComponents(File("//host.ru/home/mike"), File("//host.ru/home"), listOf("mike")) checkFilePathComponents(File("\\foo\\bar"), File("\\"), listOf("foo", "bar")) checkFilePathComponents(File("C:\\bar\\gav"), File("C:\\"), listOf("bar", "gav")) checkFilePathComponents(File("C:\\"), File("C:\\"), listOf()) } checkFilePathComponents(File(""), File(""), listOf()) checkFilePathComponents(File("."), File(""), listOf(".")) checkFilePathComponents(File(".."), File(""), listOf("..")) } @Test fun fileRoot() { val rooted = File("/foo/bar") assertTrue(rooted.isRooted) // assertEquals("/", rooted.root.invariantSeparatorsPath) if (isBackslashSeparator) { val diskRooted = File("""C:\foo\bar""") assertTrue(diskRooted.isRooted) // assertEquals("""C:\""", diskRooted.rootName) val networkRooted = File("""\\network\share\""") assertTrue(networkRooted.isRooted) // assertEquals("""\\network\share""", networkRooted.rootName) } val relative = File("foo/bar") assertFalse(relative.isRooted) // assertEquals("", relative.rootName) } @Test fun startsWith() { assertTrue(File("foo/bar").startsWith(File("foo/bar"))) assertTrue(File("foo/bar").startsWith(File("foo"))) assertTrue(File("foo/bar").startsWith("")) assertFalse(File("foo/bar").startsWith(File("/"))) assertFalse(File("foo/bar").startsWith(File("/foo"))) assertFalse(File("foo/bar").startsWith("fo")) assertTrue(File("/foo/bar").startsWith(File("/foo/bar"))) assertTrue(File("/foo/bar").startsWith(File("/foo"))) assertTrue(File("/foo/bar").startsWith("/")) assertFalse(File("/foo/bar").startsWith("")) assertFalse(File("/foo/bar").startsWith(File("foo"))) assertFalse(File("/foo/bar").startsWith("/fo")) if (isBackslashSeparator) { assertTrue(File("C:\\Users\\Me\\Temp\\Game").startsWith("C:\\Users\\Me")) assertFalse(File("C:\\Users\\Me\\Temp\\Game").startsWith("C:\\Users\\He")) assertTrue(File("C:\\Users\\Me").startsWith("C:\\")) } if (isCaseInsensitiveFileSystem) { assertTrue(File("C:\\Users\\Me").startsWith("c:\\")) } } @Test fun endsWith() { assertTrue(File("/foo/bar").endsWith("bar")) assertTrue(File("/foo/bar").endsWith("foo/bar")) assertTrue(File("/foo/bar").endsWith("/foo/bar")) assertTrue(File("foo/bar").endsWith("foo/bar")) assertTrue(File("foo/bar").endsWith("bar/")) assertFalse(File("/foo/bar").endsWith("ar")) assertFalse(File("/foo/bar").endsWith("/bar")) assertFalse(File("/foo/bar/gav/bar").endsWith("/bar")) assertFalse(File("/foo/bar/gav/bar").endsWith("/gav/bar")) assertFalse(File("/foo/bar/gav").endsWith("/bar")) assertFalse(File("foo/bar").endsWith("/bar")) if (isCaseInsensitiveFileSystem) { assertTrue(File("/foo/bar").endsWith("Bar")) } } /* @Test fun subPath() { if (isBackslashSeparator) { // Check only in Windows assertEquals(File("mike"), File("//my.host.net/home/mike/temp").subPath(0, 1)) assertEquals(File("mike"), File("\\\\my.host.net\\home\\mike\\temp").subPath(0, 1)) } assertEquals(File("bar/gav"), File("/foo/bar/gav/hi").subPath(1, 3)) assertEquals(File("foo"), File("/foo/bar/gav/hi").subPath(0, 1)) assertEquals(File("gav/hi"), File("/foo/bar/gav/hi").subPath(2, 4)) } */ @Test fun normalize() { assertEquals(File("/foo/bar/baaz"), File("/foo/./bar/gav/../baaz").normalize()) assertEquals(File("/foo/bar/baaz"), File("/foo/bak/../bar/gav/../baaz").normalize()) assertEquals(File("../../bar"), File("../foo/../../bar").normalize()) // For Unix C:\windows is not correct so it's not the same as C:/windows if (isBackslashSeparator) { assertEquals(File("C:\\windows"), File("C:\\home\\..\\documents\\..\\windows").normalize()) assertEquals(File("C:/windows"), File("C:/home/../documents/../windows").normalize()) } assertEquals(File("foo"), File("gav/bar/../../foo").normalize()) assertEquals(File("/../foo"), File("/bar/../../foo").normalize()) } @Test fun resolve() { assertEquals(File("/foo/bar/gav"), File("/foo/bar").resolve("gav")) assertEquals(File("/foo/bar/gav"), File("/foo/bar/").resolve("gav")) assertEquals(File("/gav"), File("/foo/bar").resolve("/gav")) // For Unix C:\path is not correct so it's cannot be automatically converted if (isBackslashSeparator) { assertEquals(File("C:\\Users\\Me\\Documents\\important.doc"), File("C:\\Users\\Me").resolve("Documents\\important.doc")) assertEquals(File("C:/Users/Me/Documents/important.doc"), File("C:/Users/Me").resolve("Documents/important.doc")) } assertEquals(File(""), File("").resolve("")) assertEquals(File("bar"), File("").resolve("bar")) assertEquals(File("foo/bar"), File("foo").resolve("bar")) // should it normalize such paths? // assertEquals(File("bar"), File("foo").resolve("../bar")) // assertEquals(File("../bar"), File("foo").resolve("../../bar")) // assertEquals(File("foo/bar"), File("foo").resolve("./bar")) } @Test fun resolveSibling() { assertEquals(File("/foo/gav"), File("/foo/bar").resolveSibling("gav")) assertEquals(File("/foo/gav"), File("/foo/bar/").resolveSibling("gav")) assertEquals(File("/gav"), File("/foo/bar").resolveSibling("/gav")) // For Unix C:\path is not correct so it's cannot be automatically converted if (isBackslashSeparator) { assertEquals(File("C:\\Users\\Me\\Documents\\important.doc"), File("C:\\Users\\Me\\profile.ini").resolveSibling("Documents\\important.doc")) assertEquals(File("C:/Users/Me/Documents/important.doc"), File("C:/Users/Me/profile.ini").resolveSibling("Documents/important.doc")) } assertEquals(File("gav"), File("foo").resolveSibling("gav")) assertEquals(File("../gav"), File("").resolveSibling("gav")) } @Test fun extension() { assertEquals("bbb", File("aaa.bbb").extension) assertEquals("", File("aaa").extension) assertEquals("", File("aaa.").extension) // maybe we should think that such files have name .bbb and no extension assertEquals("bbb", File(".bbb").extension) assertEquals("", File("/my.dir/log").extension) } @Test fun nameWithoutExtension() { assertEquals("aaa", File("aaa.bbb").nameWithoutExtension) assertEquals("aaa", File("aaa").nameWithoutExtension) assertEquals("aaa", File("aaa.").nameWithoutExtension) assertEquals("", File(".bbb").nameWithoutExtension) assertEquals("log", File("/my.dir/log").nameWithoutExtension) } @Test fun testCopyTo() { val srcFile = createTempFile() val dstFile = createTempFile() try { srcFile.writeText("Hello, World!") assertFailsWith(FileAlreadyExistsException::class, "copy do not overwrite existing file") { srcFile.copyTo(dstFile) } var dst = srcFile.copyTo(dstFile, overwrite = true) assertTrue(dst === dstFile) compareFiles(srcFile, dst, "copy with overwrite over existing file") assertTrue(dstFile.delete()) dst = srcFile.copyTo(dstFile) compareFiles(srcFile, dst, "copy to new file") assertTrue(dstFile.delete()) dstFile.mkdir() val child = File(dstFile, "child") child.createNewFile() assertFailsWith(FileAlreadyExistsException::class, "copy with overwrite do not overwrite non-empty dir") { srcFile.copyTo(dstFile, overwrite = true) } child.delete() srcFile.copyTo(dstFile, overwrite = true) assertEquals(srcFile.readText(), dstFile.readText(), "copy with overwrite over empty dir") assertTrue(srcFile.delete()) assertTrue(dstFile.delete()) assertFailsWith(NoSuchFileException::class) { srcFile.copyTo(dstFile) } srcFile.mkdir() srcFile.resolve("somefile").writeText("some content") dstFile.writeText("") assertFailsWith(FileAlreadyExistsException::class, "copy dir do not overwrite file") { srcFile.copyTo(dstFile) } srcFile.copyTo(dstFile, overwrite = true) assertTrue(dstFile.isDirectory) assertTrue(dstFile.listFiles()!!.isEmpty(), "only directory is copied, but not its content") assertFailsWith(FileAlreadyExistsException::class, "copy dir do not overwrite dir") { srcFile.copyTo(dstFile) } srcFile.copyTo(dstFile, overwrite = true) assertTrue(dstFile.isDirectory) assertTrue(dstFile.listFiles()!!.isEmpty(), "only directory is copied, but not its content") dstFile.resolve("somefile2").writeText("some content2") assertFailsWith(FileAlreadyExistsException::class, "copy dir do not overwrite dir") { srcFile.copyTo(dstFile, overwrite = true) } } finally { srcFile.deleteRecursively() dstFile.deleteRecursively() } } @Test fun copyToNameWithoutParent() { val currentDir = File("").absoluteFile!! val srcFile = createTempFile() val dstFile = createTempFile(directory = currentDir) try { srcFile.writeText("Hello, World!", Charsets.UTF_8) dstFile.delete() val dstRelative = File(dstFile.name) srcFile.copyTo(dstRelative) assertEquals(srcFile.readText(), dstFile.readText()) } finally { dstFile.delete() srcFile.delete() } } @Test fun deleteRecursively() { val dir = createTempDir() dir.delete() dir.mkdir() val subDir = File(dir, "subdir"); subDir.mkdir() File(dir, "test1.txt").createNewFile() File(subDir, "test2.txt").createNewFile() assertTrue(dir.deleteRecursively()) assertFalse(dir.exists()) assertTrue(dir.deleteRecursively()) } @Test fun deleteRecursivelyWithFail() { val basedir = FileTreeWalkTest.createTestFiles() val restricted = File(basedir, "1") try { if (restricted.setReadable(false)) { if (File(basedir, "7.txt").setReadable(false)) { assertFalse(basedir.deleteRecursively(), "Expected incomplete recursive deletion.") restricted.setReadable(true) File(basedir, "7.txt").setReadable(true) var i = 0 for (file in basedir.walkTopDown()) { i++ } assertEquals(6, i) } } } finally { restricted.setReadable(true) File(basedir, "7.txt").setReadable(true) basedir.deleteRecursively() } } fun compareFiles(src: File, dst: File, message: String? = null) { assertTrue(dst.exists()) assertEquals(src.isFile, dst.isFile, message) if (dst.isFile) { assertTrue(Arrays.equals(src.readBytes(), dst.readBytes()), message) } } fun compareDirectories(src: File, dst: File) { for (srcFile in src.walkTopDown()) { val dstFile = dst.resolve(srcFile.relativeTo(src)) compareFiles(srcFile, dstFile) } } @Test fun copyRecursively() { val src = createTempDir() val dst = createTempDir() dst.delete() fun check() = compareDirectories(src, dst) try { val subDir1 = createTempDir(prefix = "d1_", directory = src) val subDir2 = createTempDir(prefix = "d2_", directory = src) createTempDir(prefix = "d1_", directory = subDir1) val file1 = createTempFile(prefix = "f1_", directory = src) val file2 = createTempFile(prefix = "f2_", directory = subDir1) file1.writeText("hello") file2.writeText("wazzup") createTempDir(prefix = "d1_", directory = subDir2) assertTrue(src.copyRecursively(dst)) check() assertFailsWith(FileAlreadyExistsException::class) { src.copyRecursively(dst) } var conflicts = 0 src.copyRecursively(dst) { _: File, e: IOException -> if (e is FileAlreadyExistsException) { conflicts++ OnErrorAction.SKIP } else { throw e } } assertEquals(2, conflicts) if (subDir1.setReadable(false)) { try { dst.deleteRecursively() var caught = false assertTrue(src.copyRecursively(dst) { _: File, e: IOException -> if (e is AccessDeniedException) { caught = true OnErrorAction.SKIP } else { throw e } }) assertTrue(caught) check() } finally { subDir1.setReadable(true) } } src.deleteRecursively() dst.deleteRecursively() assertFailsWith(NoSuchFileException::class) { src.copyRecursively(dst) } assertFalse(src.copyRecursively(dst) { _, _ -> OnErrorAction.TERMINATE }) } finally { src.deleteRecursively() dst.deleteRecursively() } } @Test fun copyRecursivelyWithOverwrite() { val src = createTempDir() val dst = createTempDir() fun check() = compareDirectories(src, dst) try { val srcFile = src.resolve("test") val dstFile = dst.resolve("test") srcFile.writeText("text1") src.copyRecursively(dst) srcFile.writeText("text1 modified") src.copyRecursively(dst, overwrite = true) check() dstFile.delete() dstFile.mkdir() dstFile.resolve("subFile").writeText("subfile") src.copyRecursively(dst, overwrite = true) check() srcFile.delete() srcFile.mkdir() srcFile.resolve("subFile").writeText("text2") src.copyRecursively(dst, overwrite = true) check() } finally { src.deleteRecursively() dst.deleteRecursively() } } @Test fun helpers1() { val str = "123456789\n" System.setIn(str.byteInputStream()) val reader = System.`in`.bufferedReader() assertEquals("123456789", reader.readLine()) val stringReader = str.reader() assertEquals('1', stringReader.read().toChar()) assertEquals('2', stringReader.read().toChar()) assertEquals('3', stringReader.read().toChar()) } @Test fun helpers2() { val file = createTempFile() val writer = file.printWriter() val str1 = "Hello, world!" val str2 = "Everything is wonderful!" writer.println(str1) writer.println(str2) writer.close() val reader = file.bufferedReader() assertEquals(str1, reader.readLine()) assertEquals(str2, reader.readLine()) } }