| /* | |
| * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. | |
| * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. | |
| */ | |
| package kotlin.jdk7.test | |
| import java.io.FileOutputStream | |
| import java.nio.file.* | |
| import java.util.zip.ZipEntry | |
| import java.util.zip.ZipFile | |
| import java.util.zip.ZipOutputStream | |
| import kotlin.io.path.* | |
| import kotlin.jdk7.test.PathTreeWalkTest.Companion.createTestFiles | |
| import kotlin.jdk7.test.PathTreeWalkTest.Companion.referenceFilenames | |
| import kotlin.jdk7.test.PathTreeWalkTest.Companion.referenceFilesOnly | |
| import kotlin.jdk7.test.PathTreeWalkTest.Companion.testVisitedFiles | |
| import kotlin.test.* | |
| class FileVisitorBuilderTest : AbstractPathTest() { | |
| @Test | |
| fun visitOnce() { | |
| val basedir = createTestFiles().cleanupRecursively() | |
| val preVisit = hashSetOf<Path>() | |
| val postVisit = hashSetOf<Path>() | |
| val files = hashSetOf<Path>() | |
| val visitor = fileVisitor { | |
| onPreVisitDirectory { directory, _ -> | |
| assertFalse(directory in preVisit) | |
| if (directory == basedir) { | |
| assertTrue(preVisit.isEmpty()) | |
| } else { | |
| assertTrue(directory.parent in preVisit) | |
| } | |
| preVisit.add(directory) | |
| FileVisitResult.CONTINUE | |
| } | |
| onVisitFile { file, _ -> | |
| assertTrue(file.parent in preVisit) | |
| assertFalse(file.parent in postVisit) | |
| files.add(file) | |
| FileVisitResult.CONTINUE | |
| } | |
| onPostVisitDirectory { directory, exception -> | |
| assertNull(exception) | |
| assertTrue(directory in preVisit) | |
| assertFalse(directory in postVisit) | |
| if (directory != basedir) { | |
| assertFalse(directory.parent in postVisit) | |
| } | |
| postVisit.add(directory) | |
| FileVisitResult.CONTINUE | |
| } | |
| } | |
| basedir.visitFileTree(visitor) | |
| assertEquals(preVisit, postVisit) | |
| val referenceDirectoryNames = listOf("", "1", "1/2", "1/3", "6", "8") | |
| testVisitedFiles(referenceDirectoryNames, preVisit.asSequence(), basedir) | |
| testVisitedFiles(referenceFilesOnly, files.asSequence(), basedir) | |
| } | |
| @Test | |
| fun overrideOnce() { | |
| assertFailsWith<IllegalStateException> { | |
| fileVisitor { | |
| onVisitFile { _, _ -> FileVisitResult.CONTINUE } | |
| onVisitFile { _, _ -> FileVisitResult.CONTINUE } | |
| } | |
| } | |
| } | |
| @Test | |
| fun overrideInAlreadyBuilt() { | |
| val builder: FileVisitorBuilder | |
| val _ = fileVisitor { builder = this } | |
| assertFailsWith<IllegalStateException> { | |
| builder.onVisitFile { _, _ -> FileVisitResult.CONTINUE } | |
| } | |
| } | |
| @Test | |
| fun restrictedRead() { | |
| val basedir = createTestFiles().cleanupRecursively() | |
| val restrictedDir = basedir.resolve("1/3") | |
| withRestrictedRead(restrictedDir) { | |
| assertFailsWith<AccessDeniedException> { | |
| val visitor = fileVisitor { } | |
| basedir.visitFileTree(visitor) | |
| } | |
| var didFail = false | |
| basedir.visitFileTree { | |
| onVisitFileFailed { file, exception -> | |
| assertEquals(restrictedDir, file) | |
| assertIs<AccessDeniedException>(exception) | |
| didFail = true | |
| FileVisitResult.CONTINUE | |
| } | |
| } | |
| assertTrue(didFail) | |
| } | |
| } | |
| @Test | |
| fun skipDirectory() { | |
| val basedir = createTestFiles().cleanupRecursively() | |
| val dirToSkip = basedir.resolve("1/3") | |
| val visitedFiles = mutableListOf<Path>() | |
| basedir.visitFileTree { | |
| onPreVisitDirectory { directory, _ -> | |
| if (directory == dirToSkip) | |
| FileVisitResult.SKIP_SUBTREE | |
| else | |
| FileVisitResult.CONTINUE | |
| } | |
| onVisitFile { file, _ -> | |
| assertTrue(file.parent != dirToSkip) | |
| visitedFiles.add(file) | |
| FileVisitResult.CONTINUE | |
| } | |
| } | |
| testVisitedFiles(listOf("7.txt", "8/9.txt"), visitedFiles.asSequence(), basedir) | |
| } | |
| @Test | |
| fun maxDepth() { | |
| val basedir = createTestFiles().cleanupRecursively() | |
| val visitor = fileVisitor { | |
| onVisitFile { file, _ -> | |
| assertNotEquals("1/3/5.txt", file.relativeToOrSelf(basedir).invariantSeparatorsPathString) | |
| assertNotEquals("1/3/6.txt", file.relativeToOrSelf(basedir).invariantSeparatorsPathString) | |
| FileVisitResult.CONTINUE | |
| } | |
| } | |
| basedir.visitFileTree(visitor, maxDepth = 2) | |
| } | |
| @Test | |
| fun followLinks() { | |
| val basedir = createTestFiles().cleanupRecursively() | |
| val original = basedir.resolve("8") | |
| basedir.resolve("1/3/link").tryCreateSymbolicLinkTo(original) ?: return | |
| // directory "8" contains "9.txt" file | |
| var didFollowLinks = false | |
| val visitor = fileVisitor { | |
| onVisitFile { file, _ -> | |
| if (file.relativeToOrSelf(basedir).invariantSeparatorsPathString == "1/3/link/9.txt") { | |
| didFollowLinks = true | |
| } | |
| FileVisitResult.CONTINUE | |
| } | |
| } | |
| basedir.visitFileTree(visitor) | |
| assertFalse(didFollowLinks) | |
| basedir.visitFileTree(visitor, followLinks = true) | |
| assertTrue(didFollowLinks) | |
| } | |
| @Test | |
| fun copyRecursively() { | |
| val srcRoot = createTestFiles().cleanupRecursively() | |
| val dstRoot = createTempDirectory().cleanupRecursively() | |
| srcRoot.resolve("1/2/.dir").createDirectory() | |
| srcRoot.resolve("1/3/.file").createFile() | |
| srcRoot.visitFileTree { | |
| onPreVisitDirectory { directory, _ -> | |
| if (directory.name.startsWith(".")) { | |
| FileVisitResult.SKIP_SUBTREE | |
| } else { | |
| if (directory != srcRoot) { | |
| val dst = dstRoot.resolve(directory.relativeTo(srcRoot)) | |
| directory.copyTo(dst, StandardCopyOption.COPY_ATTRIBUTES) | |
| } | |
| FileVisitResult.CONTINUE | |
| } | |
| } | |
| onVisitFile { file, _ -> | |
| if (!file.name.startsWith(".")) { | |
| val dst = dstRoot.resolve(file.relativeTo(srcRoot)) | |
| file.copyTo(dst, StandardCopyOption.COPY_ATTRIBUTES) | |
| } | |
| FileVisitResult.CONTINUE | |
| } | |
| } | |
| val dstWalk = dstRoot.walk(PathWalkOption.INCLUDE_DIRECTORIES) | |
| testVisitedFiles(referenceFilenames + listOf(""), dstWalk, dstRoot) | |
| val srcWalk = srcRoot.walk(PathWalkOption.INCLUDE_DIRECTORIES) | |
| testVisitedFiles(referenceFilenames + listOf("", "1/2/.dir", "1/3/.file"), srcWalk, srcRoot) | |
| } | |
| @Test | |
| fun deleteRecursively() { | |
| val basedir = createTestFiles() | |
| basedir.visitFileTree { | |
| onVisitFile { file, _ -> | |
| file.deleteExisting() | |
| FileVisitResult.CONTINUE | |
| } | |
| onPostVisitDirectory { directory, _ -> | |
| directory.deleteExisting() | |
| FileVisitResult.CONTINUE | |
| } | |
| } | |
| assertFalse(basedir.exists()) | |
| } | |
| private fun deleteWith(excludePredicate: (Path) -> Boolean) = fileVisitor { | |
| onPreVisitDirectory { directory, _ -> | |
| if (excludePredicate(directory)) { | |
| FileVisitResult.SKIP_SUBTREE | |
| } else { | |
| FileVisitResult.CONTINUE | |
| } | |
| } | |
| onVisitFile { file, _ -> | |
| if (!excludePredicate(file)) { | |
| file.deleteExisting() | |
| } | |
| FileVisitResult.CONTINUE | |
| } | |
| onPostVisitDirectory { directory, _ -> | |
| val shouldDelete = directory.useDirectoryEntries { it.none() } | |
| if (shouldDelete) { | |
| directory.deleteExisting() | |
| } | |
| FileVisitResult.CONTINUE | |
| } | |
| } | |
| @Test | |
| fun deleteWithPredicate() { | |
| val basedir = createTestFiles().cleanupRecursively() | |
| basedir.resolve("image1.png").createFile() | |
| basedir.resolve("1/3/image2.png").createFile() | |
| val visitor = deleteWith { path -> | |
| when { | |
| path.name == "8" -> true | |
| path.extension == "png" -> true | |
| else -> false | |
| } | |
| } | |
| basedir.visitFileTree(visitor) | |
| val expected = listOf("", "1", "1/3", "1/3/image2.png", "8", "8/9.txt", "image1.png") | |
| testVisitedFiles(expected, basedir.walk(PathWalkOption.INCLUDE_DIRECTORIES), basedir) | |
| } | |
| private fun zipify(rootPath: Path, zipOutputStream: ZipOutputStream): FileVisitor<Path> = fileVisitor { | |
| onPreVisitDirectory { directory, _ -> | |
| if (directory != rootPath) { | |
| val entry = ZipEntry(directory.relativeTo(rootPath).toString()) | |
| zipOutputStream.putNextEntry(entry) | |
| zipOutputStream.closeEntry() | |
| } | |
| FileVisitResult.CONTINUE | |
| } | |
| onVisitFile { file, attributes -> | |
| val entry = ZipEntry(file.relativeTo(rootPath).toString()) | |
| entry.size = attributes.size() | |
| zipOutputStream.putNextEntry(entry) | |
| Files.copy(file, zipOutputStream) | |
| zipOutputStream.closeEntry() | |
| FileVisitResult.CONTINUE | |
| } | |
| onVisitFileFailed { _, exception -> | |
| zipOutputStream.close() | |
| throw exception | |
| } | |
| } | |
| @Test | |
| fun archive() { | |
| val basedir = createTestFiles().cleanupRecursively() | |
| val ninthFile = Path("8/9.txt") | |
| basedir.resolve(ninthFile).writeText("ninth") | |
| val zipFile = createTempFile("testFiles", ".zip").toFile() | |
| ZipOutputStream(FileOutputStream(zipFile)).use { zipOutputStream -> | |
| val visitor = zipify(basedir, zipOutputStream) | |
| basedir.visitFileTree(visitor) | |
| } | |
| ZipFile(zipFile).use { zip -> | |
| val entriesPath = zip.entries().toList().map { Path(it.name).invariantSeparatorsPathString } | |
| assertEquals(referenceFilenames.sorted(), entriesPath.sorted()) | |
| val ninthEntry = zip.getEntry(ninthFile.pathString) | |
| zip.getInputStream(ninthEntry).bufferedReader().use { reader -> | |
| assertEquals("ninth", reader.readText()) | |
| } | |
| } | |
| } | |
| } |