From b051d2f67db8f87208f3301c00603524cf35ad3b Mon Sep 17 00:00:00 2001 From: Dmitriy Dolovov Date: Fri, 26 Nov 2021 17:50:26 +0300 Subject: [PATCH] [Native][tests] Naive implementation of glob expansion --- .../konan/blackboxtest/GlobsExpansionTest.kt | 117 ++++++++++++++++++ .../konan/blackboxtest/support/util/Globs.kt | 45 +++++++ .../konan/blackboxtest/support/util/Names.kt | 18 ++- 3 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/GlobsExpansionTest.kt create mode 100644 native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/Globs.kt diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/GlobsExpansionTest.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/GlobsExpansionTest.kt new file mode 100644 index 00000000000..c47d2bccf48 --- /dev/null +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/GlobsExpansionTest.kt @@ -0,0 +1,117 @@ +/* + * Copyright 2010-2021 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 org.jetbrains.kotlin.konan.blackboxtest + +import org.jetbrains.kotlin.konan.blackboxtest.support.util.expandGlob +import org.jetbrains.kotlin.konan.blackboxtest.support.util.sanitizedName +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.fail +import java.io.File +import kotlin.io.path.createTempDirectory + +class GlobsExpansionTest { + private lateinit var testDir: File + + @BeforeEach + fun setUp() { + testDir = createTempDirectory(GlobsExpansionTest::class.java.sanitizedName).toFile() + } + + @Test + fun noGlobs() { + val fileName = "file.kt" + val dirNames = listOf(null, "foo", "foo/bar", "foo/bar/baz") + + dirNames.forEach { dirName -> + val fullPattern = testDir.resolveNullable(dirName).resolve(fileName) + val expansionResult = expandGlob(fullPattern) + + assertEquals(1, expansionResult.size) + assertEquals(fullPattern, expansionResult.first()) + } + } + + @Test + fun filePattern() { + val fileNames = listOf("one.kt", "two.kt", "three.kt", "four.java", "five.py") + val dirNames = listOf(null, "foo", "foo/bar", "foo/bar/baz") + val pattern = "*.kt" + + dirNames.forEach { dirName -> + val dir = createDirWithFiles(dirName, fileNames) + + val fullPattern = dir.resolve(pattern) + val expansionResult = expandGlob(fullPattern) + + val kotlinOnyFiles = findAllKotlinFiles(dirName) + + assertEquals(kotlinOnyFiles.size, expansionResult.size) + assertEquals(kotlinOnyFiles, expansionResult.toSet()) + } + } + + @Test + fun dirPattern() { + val fileNames = listOf("one.kt", "two.kt", "three.kt", "four.java", "five.py") + val dirNames = listOf(null, "foo", "bar", "baz") + val pattern = "ba*/*.kt" // covers "bar" & "baz" dirs + + dirNames.forEach { dirName -> createDirWithFiles(dirName, fileNames) } + + val fullPattern = testDir.resolve(pattern) + val expansionResult = expandGlob(fullPattern) + + val kotlinOnyFiles = findAllKotlinFiles("bar", "baz") + assertEquals(kotlinOnyFiles.size, expansionResult.size) + assertEquals(kotlinOnyFiles, expansionResult.toSet()) + } + + @Test + fun doubleStarPattern() { + val fileNames = listOf("one.kt", "two.kt", "three.kt", "four.java", "five.py") + val dirNames = listOf(null, "foo", "foo/bar", "foo/bar/baz") + val pattern = "foo/**.kt" // covers "foo" and all subdirectories + + dirNames.forEach { dirName -> createDirWithFiles(dirName, fileNames) } + + val fullPattern = testDir.resolve(pattern) + val expansionResult = expandGlob(fullPattern) + + val kotlinOnyFiles = findAllKotlinFiles("foo", "foo/bar", "foo/bar/baz") + assertEquals(kotlinOnyFiles.size, expansionResult.size) + assertEquals(kotlinOnyFiles, expansionResult.toSet()) + } + + private fun createDirWithFiles(dirName: String?, fileNames: Collection): File { + val dir = testDir.resolveNullable(dirName) + dir.mkdirs() + + fileNames.forEach { fileName -> + dir.resolve(fileName).apply { createNewFile() } + } + + return dir + } + + private fun findAllKotlinFiles(vararg dirNames: String?): Set = buildSet { + dirNames.forEach { dirName -> + val dir = testDir.resolveNullable(dirName) + assertTrue(dir.isDirectory) { "Directory does not exist or is not a directory: $dir ($dirName)." } + + val files = dir.listFiles()?.takeIf { it.isNotEmpty() } + ?: fail { "Unexpectedly empty directory: $dir ($dirName)." } + + files.mapNotNullTo(this) { if (it.isFile && it.extension == "kt") it else null } + } + } + + companion object { + private fun File.resolveNullable(nullable: String?): File = if (nullable != null) resolve(nullable) else this + } +} diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/Globs.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/Globs.kt new file mode 100644 index 00000000000..28a648968e4 --- /dev/null +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/Globs.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2021 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 org.jetbrains.kotlin.konan.blackboxtest.support.util + +import java.io.File +import java.nio.file.* +import java.nio.file.attribute.BasicFileAttributes +import kotlin.io.path.name + +/** + * Naive sub-optimal implementation of glob expansion. + */ +internal fun expandGlobTo(unexpendedPath: File, output: MutableCollection) { + val normalizedUnexpandedPath = Paths.get(unexpendedPath.path).toAbsolutePath().normalize() + + val paths = generateSequence(normalizedUnexpandedPath) { it.parent }.toMutableList().apply { reverse() } + for (index in 1 until paths.size) { + val path = paths[index] + + val isGlob = '*' in path.name + if (isGlob) { + val basePath = paths[index - 1] + + val pattern = basePath.relativize(normalizedUnexpandedPath).toString() + val matcher = FileSystems.getDefault().getPathMatcher("glob:$pattern") + + Files.walkFileTree(basePath, object : SimpleFileVisitor() { + override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult { + if (matcher.matches(basePath.relativize(file))) output += file.toFile() + return FileVisitResult.CONTINUE + } + }) + + return + } + } + + // No globs to expand. + output += normalizedUnexpandedPath.toFile() +} + +internal fun expandGlob(unexpendedPath: File): Collection = buildList { expandGlobTo(unexpendedPath, this) } diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/Names.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/Names.kt index 7cf4335e556..2a62aec88bc 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/Names.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/Names.kt @@ -5,11 +5,21 @@ package org.jetbrains.kotlin.konan.blackboxtest.support.util -internal val Class<*>.sanitizedName: String - get() = name.map { if (it.isLetterOrDigit() || it == '_') it else '_' }.joinToString("") +internal val Class<*>.sanitizedName: String get() = sanitize(name) -internal fun getSanitizedFileName(fileName: String): String = - fileName.map { if (it.isLetterOrDigit() || it == '_' || it == '.') it else '_' }.joinToString("") +internal fun getSanitizedFileName(fileName: String): String = sanitize(fileName, allowDots = true) + +private fun sanitize(s: String, allowDots: Boolean = false) = buildString { + s.forEach { ch -> + append( + when { + ch.isLetterOrDigit() || ch == '_' -> ch + allowDots && ch == '.' -> ch + else -> '_' + } + ) + } +} internal const val DEFAULT_FILE_NAME = "main.kt"