[Native][tests] Rework PackageName entity
This commit is contained in:
+7
-6
@@ -7,10 +7,11 @@ package org.jetbrains.kotlin.konan.blackboxtest
|
||||
|
||||
import com.intellij.testFramework.TestDataFile
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.*
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.TreeNode
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.getAbsoluteFile
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.joinPackageNames
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.prependPackageName
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.PackageName
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCaseId
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestRun
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestRunProvider
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.*
|
||||
import org.junit.jupiter.api.DynamicNode
|
||||
import org.junit.jupiter.api.DynamicTest.dynamicTest
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
@@ -89,7 +90,7 @@ abstract class AbstractNativeBlackBoxTest {
|
||||
// }
|
||||
// }
|
||||
buildList {
|
||||
fun TreeNode<TestRun>.processItems(parentPackageSegment: String) {
|
||||
fun TreeNode<TestRun>.processItems(parentPackageSegment: PackageName) {
|
||||
val ownPackageSegment = joinPackageNames(parentPackageSegment, packageSegment)
|
||||
items.mapTo(this@buildList) { testRun ->
|
||||
val displayName = testRun.displayName.prependPackageName(ownPackageSegment)
|
||||
@@ -99,7 +100,7 @@ abstract class AbstractNativeBlackBoxTest {
|
||||
children.forEach { it.processItems(ownPackageSegment) }
|
||||
}
|
||||
|
||||
testRunNodes.forEach { testRunNode -> testRunNode.processItems("") }
|
||||
testRunNodes.forEach { testRunNode -> testRunNode.processItems(PackageName.EMPTY) }
|
||||
}
|
||||
|
||||
private fun performTestRun(testRun: TestRun) {
|
||||
|
||||
+6
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.konan.blackboxtest
|
||||
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.PackageName
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.compressedName
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.compressedPackageName
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.compressedSimpleName
|
||||
@@ -90,4 +91,9 @@ class TestInfra_CompressedNamesTest {
|
||||
"foo.bar.baz.foo.bar.baz.foo.bar.baz.foo.bar.baz.foo.bar.baz".compressedPackageName
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val String.compressedPackageName
|
||||
get() = PackageName(this).compressedPackageName
|
||||
}
|
||||
}
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
/**
|
||||
* Represents a package name.
|
||||
*/
|
||||
internal class PackageName private constructor(private val fqn: String, val segments: List<String>) {
|
||||
constructor(segments: List<String>) : this(segments.joinToString("."), segments)
|
||||
constructor(fqn: String) : this(fqn, if (fqn.isNotEmpty()) fqn.split('.') else emptyList())
|
||||
|
||||
fun isEmpty(): Boolean = segments.isEmpty()
|
||||
|
||||
override fun toString() = fqn
|
||||
override fun equals(other: Any?) = fqn == (other as? PackageName)?.fqn
|
||||
override fun hashCode() = fqn.hashCode()
|
||||
|
||||
companion object {
|
||||
val EMPTY = PackageName("", emptyList())
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -15,13 +15,12 @@ import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
||||
import java.io.File
|
||||
|
||||
internal typealias PackageFQN = String
|
||||
internal typealias FunctionName = String
|
||||
|
||||
/**
|
||||
* Represents a single test function (i.e. a function annotated with [kotlin.test.Test]) inside of a [TestFile].
|
||||
*/
|
||||
internal data class TestFunction(val packageName: PackageFQN, val functionName: FunctionName)
|
||||
internal data class TestFunction(val packageName: PackageName, val functionName: FunctionName)
|
||||
|
||||
/**
|
||||
* Represents a single file that will be supplied to the compiler.
|
||||
@@ -179,7 +178,7 @@ internal class TestCase(
|
||||
val kind: TestKind,
|
||||
val modules: Set<TestModule.Exclusive>,
|
||||
val freeCompilerArgs: TestCompilerArgs,
|
||||
val nominalPackageName: PackageFQN,
|
||||
val nominalPackageName: PackageName,
|
||||
val expectedOutputDataFile: File?,
|
||||
val extras: Extras
|
||||
) {
|
||||
|
||||
+3
-3
@@ -113,7 +113,7 @@ internal class TestCompilationFactory(private val settings: Settings) {
|
||||
private fun multiModuleArtifactFile(modules: Collection<TestModule>, extension: String): File {
|
||||
var filesCount = 0
|
||||
var hash = 0
|
||||
val uniquePackageNames = hashSetOf<PackageFQN>()
|
||||
val uniquePackageNames = hashSetOf<PackageName>()
|
||||
|
||||
modules.forEach { module ->
|
||||
module.files.forEach { file ->
|
||||
@@ -132,7 +132,7 @@ internal class TestCompilationFactory(private val settings: Settings) {
|
||||
repeat(4 - prefix.length) { append('0') }
|
||||
append(prefix).append('-')
|
||||
|
||||
if (commonPackageName != null)
|
||||
if (!commonPackageName.isEmpty())
|
||||
append(commonPackageName.compressedPackageName).append('-')
|
||||
|
||||
append(prettyHash(hash))
|
||||
@@ -143,7 +143,7 @@ internal class TestCompilationFactory(private val settings: Settings) {
|
||||
return artifactDirForPackageName(commonPackageName).resolve(artifactFileName)
|
||||
}
|
||||
|
||||
private fun artifactDirForPackageName(packageName: PackageFQN?): File {
|
||||
private fun artifactDirForPackageName(packageName: PackageName?): File {
|
||||
val baseDir = settings.get<Binaries>().testBinariesDir
|
||||
val outputDir = if (packageName != null) baseDir.resolve(packageName.compressedPackageName) else baseDir
|
||||
|
||||
|
||||
+6
-5
@@ -5,7 +5,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.konan.blackboxtest.support
|
||||
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.TestName
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertFalse
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import java.io.File
|
||||
|
||||
@@ -28,9 +29,9 @@ internal sealed interface TestRunParameter {
|
||||
abstract fun testMatches(testName: String): Boolean
|
||||
}
|
||||
|
||||
class WithPackageFilter(packageName: PackageFQN) : WithFilter() {
|
||||
class WithPackageFilter(packageName: PackageName) : WithFilter() {
|
||||
init {
|
||||
assertTrue(packageName.isNotEmpty())
|
||||
assertFalse(packageName.isEmpty())
|
||||
}
|
||||
|
||||
private val packagePrefix = "$packageName."
|
||||
@@ -39,11 +40,11 @@ internal sealed interface TestRunParameter {
|
||||
programArgs += "--ktest_filter=$packagePrefix*"
|
||||
}
|
||||
|
||||
override fun testMatches(testName: String) = testName.startsWith(packagePrefix)
|
||||
override fun testMatches(testName: TestName) = testName.startsWith(packagePrefix)
|
||||
}
|
||||
|
||||
class WithFunctionFilter(val testFunction: TestFunction) : WithFilter() {
|
||||
private val packagePrefix = if (testFunction.packageName.isNotEmpty()) "${testFunction.packageName}." else ""
|
||||
private val packagePrefix = if (testFunction.packageName.isEmpty()) "" else "${testFunction.packageName}."
|
||||
|
||||
override fun applyTo(programArgs: MutableList<String>) {
|
||||
programArgs += "--ktest_regex_filter=${packagePrefix.replace(".", "\\.")}([^\\.]+)\\.${testFunction.functionName}"
|
||||
|
||||
+2
-2
@@ -16,10 +16,10 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.runner.LocalTestFunctionE
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.LocalTestRunner
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.GlobalSettings
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Settings
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.*
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.ThreadSafeCache
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.TreeNode
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.buildTree
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.isSameOrSubpackageOf
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
||||
import org.junit.jupiter.api.Assumptions.assumeTrue
|
||||
@@ -199,7 +199,7 @@ internal class TestRunProvider(
|
||||
|
||||
private fun Collection<TestFunction>.filterIrrelevant(testCase: TestCase) =
|
||||
if (testCase.kind == TestKind.REGULAR)
|
||||
filter { testFunction -> testFunction.packageName.isSameOrSubpackageOf(testCase.nominalPackageName) }
|
||||
filter { testFunction -> testFunction.packageName.startsWith(testCase.nominalPackageName) }
|
||||
else
|
||||
this
|
||||
|
||||
|
||||
+2
-2
@@ -279,7 +279,7 @@ private class ExtTestDataFile(
|
||||
private fun patchPackageNames(isStandaloneTest: Boolean) = with(structure) {
|
||||
if (isStandaloneTest) return // Don't patch packages for standalone tests.
|
||||
|
||||
val basePackageName = FqName(testDataFileSettings.nominalPackageName)
|
||||
val basePackageName = FqName(testDataFileSettings.nominalPackageName.toString())
|
||||
|
||||
val oldPackageNames: Set<FqName> = filesToTransform.mapToSet { it.packageFqName }
|
||||
val oldToNewPackageNameMapping: Map<FqName, FqName> = oldPackageNames.associateWith { oldPackageName ->
|
||||
@@ -650,7 +650,7 @@ private class ExtTestDataFileSettings(
|
||||
val optInsForCompiler: Set<String>,
|
||||
val expectActualLinker: Boolean,
|
||||
val generatedSourcesDir: File,
|
||||
val nominalPackageName: PackageFQN
|
||||
val nominalPackageName: PackageName
|
||||
)
|
||||
|
||||
private typealias SharedModuleGenerator = (sharedModulesDir: File) -> TestModule.Shared?
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ internal class PredefinedTestCaseGroupProvider(annotation: PredefinedTestCases)
|
||||
modules = setOf(module),
|
||||
freeCompilerArgs = predefinedTestCase.freeCompilerArgs
|
||||
.parseCompilerArgs { "Failed to parse free compiler arguments for test case $testCaseId" },
|
||||
nominalPackageName = testCaseId.uniqueName,
|
||||
nominalPackageName = PackageName(testCaseId.uniqueName),
|
||||
expectedOutputDataFile = null,
|
||||
extras = WithTestRunnerExtras
|
||||
)
|
||||
|
||||
+2
-2
@@ -221,7 +221,7 @@ internal class StandardTestCaseGroupProvider(private val settings: Settings) : T
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun fixPackageNames(testModules: Collection<TestModule.Exclusive>, basePackageName: PackageFQN, testDataFile: File) {
|
||||
private fun fixPackageNames(testModules: Collection<TestModule.Exclusive>, basePackageName: PackageName, testDataFile: File) {
|
||||
testModules.forEach { testModule ->
|
||||
testModule.files.forEach { testFile ->
|
||||
val firstMeaningfulLine = testFile.text.dropNonMeaningfulLines().firstOrNull()
|
||||
@@ -230,7 +230,7 @@ internal class StandardTestCaseGroupProvider(private val settings: Settings) : T
|
||||
val existingPackageName = firstMeaningfulLine?.getExistingPackageName()
|
||||
if (existingPackageName != null) {
|
||||
// Validate it.
|
||||
assertTrue(existingPackageName.isSameOrSubpackageOf(basePackageName)) {
|
||||
assertTrue(existingPackageName.startsWith(basePackageName)) {
|
||||
val location = Location(testDataFile, firstMeaningfulLine.number)
|
||||
"""
|
||||
$location: Invalid package name declaration found: $firstMeaningfulLine
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@
|
||||
package org.jetbrains.kotlin.konan.blackboxtest.support.util
|
||||
|
||||
import org.apache.commons.lang.StringUtils.splitByCharacterTypeCamelCase
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.PackageFQN
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.PackageName
|
||||
import org.jetbrains.kotlin.konan.target.Architecture
|
||||
import org.jetbrains.kotlin.konan.target.Family
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
@@ -48,9 +48,9 @@ internal val Architecture.compressedName: String
|
||||
internal val Class<*>.compressedSimpleName: String
|
||||
get() = splitByCharacterTypeCamelCase(simpleName).joinToString("") { it.take(3) }
|
||||
|
||||
internal val PackageFQN.compressedPackageName: String
|
||||
internal val PackageName.compressedPackageName: String
|
||||
get() {
|
||||
val sanitizedName = replace('.', '_')
|
||||
val sanitizedName = segments.joinToString("_")
|
||||
return if (sanitizedName.length > COMPRESSED_PACKAGE_FQN_MAX_LENGTH) {
|
||||
val suffix = "-" + prettyHash(sanitizedName.hashCode())
|
||||
sanitizedName.substring(0, COMPRESSED_PACKAGE_FQN_MAX_LENGTH - suffix.length) + suffix
|
||||
|
||||
+32
-27
@@ -5,15 +5,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.konan.blackboxtest.support.util
|
||||
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.PackageFQN
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.PackageName
|
||||
import org.jetbrains.kotlin.renderer.KeywordStringsGenerated.KEYWORDS
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.name
|
||||
import kotlin.math.min
|
||||
|
||||
internal fun computePackageName(testDataBaseDir: File, testDataFile: File): PackageFQN {
|
||||
internal fun computePackageName(testDataBaseDir: File, testDataFile: File): PackageName {
|
||||
assertTrue(testDataFile.startsWith(testDataBaseDir)) {
|
||||
"""
|
||||
The file is outside of the directory.
|
||||
@@ -26,13 +25,13 @@ internal fun computePackageName(testDataBaseDir: File, testDataFile: File): Pack
|
||||
.relativeTo(testDataBaseDir)
|
||||
.resolve(testDataFile.nameWithoutExtension)
|
||||
.toPath()
|
||||
.map(Path::name)
|
||||
.joinToString(".") { packagePart ->
|
||||
if (packagePart in KEYWORDS)
|
||||
"_${packagePart}_"
|
||||
.map { pathSegment ->
|
||||
val packageSegment = pathSegment.name
|
||||
if (packageSegment in KEYWORDS)
|
||||
"_${packageSegment}_"
|
||||
else
|
||||
buildString {
|
||||
packagePart.forEachIndexed { index, ch ->
|
||||
packageSegment.forEachIndexed { index, ch ->
|
||||
if (ch.isJavaIdentifierPart()) {
|
||||
if (index == 0 && !ch.isJavaIdentifierStart()) {
|
||||
// If the first character is not suitable for start, escape it with '_'.
|
||||
@@ -46,36 +45,42 @@ internal fun computePackageName(testDataBaseDir: File, testDataFile: File): Pack
|
||||
}
|
||||
}
|
||||
}
|
||||
.let(::PackageName)
|
||||
}
|
||||
|
||||
internal fun Set<PackageFQN>.findCommonPackageName(): PackageFQN? = when (size) {
|
||||
0 -> null
|
||||
internal fun Set<PackageName>.findCommonPackageName(): PackageName = when (size) {
|
||||
0 -> PackageName.EMPTY
|
||||
1 -> first()
|
||||
else -> map { packageName: PackageFQN ->
|
||||
packageName.split('.')
|
||||
}.reduce { commonPackageNameParts: List<String>, packageNameParts: List<String> ->
|
||||
ArrayList<String>(min(commonPackageNameParts.size, packageNameParts.size)).apply {
|
||||
val i = commonPackageNameParts.iterator()
|
||||
val j = packageNameParts.iterator()
|
||||
else -> map { packageName -> packageName.segments }
|
||||
.reduce { commonSegments: List<String>, segments: List<String> ->
|
||||
buildList(min(commonSegments.size, segments.size)) {
|
||||
val i = commonSegments.iterator()
|
||||
val j = segments.iterator()
|
||||
|
||||
while (i.hasNext() && j.hasNext()) {
|
||||
val packageNamePart = i.next()
|
||||
if (packageNamePart == j.next()) add(packageNamePart) else break
|
||||
while (i.hasNext() && j.hasNext()) {
|
||||
val segment = i.next()
|
||||
if (segment == j.next()) add(segment) else break
|
||||
}
|
||||
}
|
||||
}
|
||||
}.takeIf { it.isNotEmpty() }?.joinToString(".")
|
||||
.let(::PackageName)
|
||||
}
|
||||
|
||||
internal fun joinPackageNames(a: PackageFQN, b: PackageFQN): PackageFQN = when {
|
||||
internal fun joinPackageNames(a: PackageName, b: PackageName): PackageName = when {
|
||||
a.isEmpty() -> b
|
||||
b.isEmpty() -> a
|
||||
else -> "$a.$b"
|
||||
else -> PackageName(a.segments + b.segments)
|
||||
}
|
||||
|
||||
internal fun String.prependPackageName(packageName: PackageFQN): String =
|
||||
internal fun String.prependPackageName(packageName: PackageName): String =
|
||||
if (packageName.isEmpty()) this else "$packageName.$this"
|
||||
|
||||
internal fun PackageFQN.isSameOrSubpackageOf(parentPackageName: PackageFQN): Boolean =
|
||||
parentPackageName.isEmpty()
|
||||
|| this == parentPackageName
|
||||
|| (length > parentPackageName.length && startsWith(parentPackageName) && this[parentPackageName.length] == '.')
|
||||
internal fun PackageName.startsWith(other: PackageName): Boolean {
|
||||
if (segments.size < other.segments.size) return false
|
||||
|
||||
for (i in other.segments.indices) {
|
||||
if (segments[i] != other.segments[i]) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
+3
-2
@@ -5,7 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.konan.blackboxtest.support.util
|
||||
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.PackageFQN
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.PackageName
|
||||
|
||||
internal typealias SourceText = CharSequence
|
||||
internal typealias SourceLine = String
|
||||
@@ -17,10 +17,11 @@ internal data class NumberedSourceLine(val number: Int, val text: SourceLine) :
|
||||
internal fun SourceText.hasAnythingButComments(): Boolean =
|
||||
dropNonMeaningfulLines().firstOrNull() != null
|
||||
|
||||
internal fun NumberedSourceLine.getExistingPackageName(): PackageFQN? =
|
||||
internal fun NumberedSourceLine.getExistingPackageName(): PackageName? =
|
||||
text.substringAfter("package ", missingDelimiterValue = "")
|
||||
.trimStart()
|
||||
.takeIf(String::isNotEmpty)
|
||||
?.let(::PackageName)
|
||||
|
||||
internal fun SourceText.dropNonMeaningfulLines(): Sequence<NumberedSourceLine> {
|
||||
var inMultilineComment = false
|
||||
|
||||
+7
-9
@@ -5,7 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.konan.blackboxtest.support.util
|
||||
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.PackageFQN
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.PackageName
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestFunction
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.GTestListingParseState as State
|
||||
@@ -48,14 +48,12 @@ internal fun parseGTestListing(rawGTestListing: String): Collection<TestFunction
|
||||
}
|
||||
else -> when (state) {
|
||||
is State.Begin, is State.NewTest -> {
|
||||
val packageParts = line.trimEnd().removeSuffix(".").split('.')
|
||||
if (packageParts.isEmpty()) parseError("Malformed test suite name")
|
||||
val packageSegments = line.trimEnd().removeSuffix(".").split('.')
|
||||
if (packageSegments.isEmpty()) parseError("Malformed test suite name")
|
||||
|
||||
// Drop the last part because it is related to class name (or file-class name).
|
||||
// TODO: How to handle nested classes?
|
||||
val packageName = packageParts.dropLast(1).joinToString(".")
|
||||
|
||||
State.NewTestSuite(packageName)
|
||||
State.NewTestSuite(PackageName(packageSegments.dropLast(1)))
|
||||
}
|
||||
else -> parseError("Unexpected test suite name")
|
||||
}
|
||||
@@ -66,11 +64,11 @@ internal fun parseGTestListing(rawGTestListing: String): Collection<TestFunction
|
||||
private sealed interface GTestListingParseState {
|
||||
object Begin : State
|
||||
object End : State
|
||||
class NewTestSuite(override val packageName: PackageFQN) : State, HasPackageName
|
||||
class NewTest(override val packageName: PackageFQN) : State, HasPackageName
|
||||
class NewTestSuite(override val packageName: PackageName) : State, HasPackageName
|
||||
class NewTest(override val packageName: PackageName) : State, HasPackageName
|
||||
|
||||
interface HasPackageName {
|
||||
val packageName: PackageFQN
|
||||
val packageName: PackageName
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-11
@@ -5,10 +5,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.konan.blackboxtest.support.util
|
||||
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.PackageFQN
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.PackageName
|
||||
|
||||
internal interface TreeNode<T> {
|
||||
val packageSegment: String
|
||||
val packageSegment: PackageName
|
||||
val items: Collection<T>
|
||||
val children: Collection<TreeNode<T>>
|
||||
|
||||
@@ -16,15 +16,15 @@ internal interface TreeNode<T> {
|
||||
fun <T> oneLevel(vararg items: T) = oneLevel(listOf(*items))
|
||||
|
||||
fun <T> oneLevel(items: Iterable<T>): List<TreeNode<T>> = listOf(object : TreeNode<T> {
|
||||
override val packageSegment get() = ""
|
||||
override val packageSegment get() = PackageName.EMPTY
|
||||
override val items = items.toList()
|
||||
override val children get() = emptyList<TreeNode<T>>()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <T, R> Collection<T>.buildTree(extractPackageName: (T) -> PackageFQN, transform: (T) -> R): Collection<TreeNode<R>> {
|
||||
val groupedItems: Map<PackageFQN, List<R>> = groupBy(extractPackageName).mapValues { (_, items) -> items.map(transform) }
|
||||
internal fun <T, R> Collection<T>.buildTree(extractPackageName: (T) -> PackageName, transform: (T) -> R): Collection<TreeNode<R>> {
|
||||
val groupedItems: Map<PackageName, List<R>> = groupBy(extractPackageName).mapValues { (_, items) -> items.map(transform) }
|
||||
|
||||
// Fast pass.
|
||||
when (groupedItems.size) {
|
||||
@@ -33,13 +33,14 @@ internal fun <T, R> Collection<T>.buildTree(extractPackageName: (T) -> PackageFQ
|
||||
}
|
||||
|
||||
// Long pass.
|
||||
val root = TreeBuilder<R>("")
|
||||
val root = TreeBuilder<R>(PackageName.EMPTY)
|
||||
|
||||
// Populate the tree.
|
||||
groupedItems.forEach { (packageFQN, items) ->
|
||||
groupedItems.forEach { (packageName, items) ->
|
||||
var node = root
|
||||
packageFQN.split('.').forEach { packageSegment ->
|
||||
node = node.childrenMap.computeIfAbsent(packageSegment) { TreeBuilder(packageSegment) }
|
||||
packageName.segments.forEach { packageSegment ->
|
||||
val packageSegmentAsName = PackageName(listOf(packageSegment))
|
||||
node = node.childrenMap.computeIfAbsent(packageSegmentAsName) { TreeBuilder(packageSegmentAsName) }
|
||||
}
|
||||
node.items += items
|
||||
}
|
||||
@@ -54,9 +55,9 @@ internal fun <T, R> Collection<T>.buildTree(extractPackageName: (T) -> PackageFQ
|
||||
meaningfulNode.childrenMap.values
|
||||
}
|
||||
|
||||
private class TreeBuilder<T>(override var packageSegment: String) : TreeNode<T> {
|
||||
private class TreeBuilder<T>(override var packageSegment: PackageName) : TreeNode<T> {
|
||||
override val items = mutableListOf<T>()
|
||||
val childrenMap = hashMapOf<String, TreeBuilder<T>>()
|
||||
val childrenMap = hashMapOf<PackageName, TreeBuilder<T>>()
|
||||
override val children: Collection<TreeBuilder<T>> get() = childrenMap.values
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user