unit-tests: Use file name as a suite name for top-level tests

The naming system used is the following. If there is a file
'foo.kt' with the following content:

// foo.kt
package bar.baz

@Test fun test() { ... }

Then 'bar.baz.fooKt' will be a test suite name and 'test' will be
a test name.
This commit is contained in:
Ilya Matveev
2017-09-26 11:43:54 +03:00
committed by ilmat192
parent d5fe0862ac
commit 1b8fc1a40b
2 changed files with 38 additions and 14 deletions
@@ -41,7 +41,7 @@ internal class KonanLower(val context: Context) {
val phaser = PhaseManager(context) val phaser = PhaseManager(context)
phaser.phase(KonanPhase.TEST_PROCESSOR) { phaser.phase(KonanPhase.TEST_PROCESSOR) {
irModule.files.forEach(TestProcessor(context)::lower) TestProcessor(context).process(irModule)
} }
phaser.phase(KonanPhase.LOWER_BEFORE_INLINE) { phaser.phase(KonanPhase.LOWER_BEFORE_INLINE) {
@@ -1,6 +1,5 @@
package org.jetbrains.kotlin.backend.konan.lower package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.descriptors.replace import org.jetbrains.kotlin.backend.common.descriptors.replace
import org.jetbrains.kotlin.backend.common.ir.createFakeOverrideDescriptor import org.jetbrains.kotlin.backend.common.ir.createFakeOverrideDescriptor
import org.jetbrains.kotlin.backend.common.lower.SimpleMemberScope import org.jetbrains.kotlin.backend.common.lower.SimpleMemberScope
@@ -10,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.reportWarning
import org.jetbrains.kotlin.backend.konan.KonanBackendContext import org.jetbrains.kotlin.backend.konan.KonanBackendContext
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.konan.reportCompilationError
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.isFunctionType import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
@@ -36,13 +36,13 @@ import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.createParameterDeclarations import org.jetbrains.kotlin.ir.util.createParameterDeclarations
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import java.io.File
internal class TestProcessor (val context: KonanBackendContext): FileLoweringPass { internal class TestProcessor (val context: KonanBackendContext) {
object TEST_SUITE_CLASS: IrDeclarationOriginImpl("TEST_SUITE_CLASS") object TEST_SUITE_CLASS: IrDeclarationOriginImpl("TEST_SUITE_CLASS")
object TEST_SUITE_GENERATED_MEMBER: IrDeclarationOriginImpl("TEST_SUITE_GENERATED_MEMBER") object TEST_SUITE_GENERATED_MEMBER: IrDeclarationOriginImpl("TEST_SUITE_GENERATED_MEMBER")
@@ -58,16 +58,22 @@ internal class TestProcessor (val context: KonanBackendContext): FileLoweringPas
val baseClassSuiteDescriptor = symbols.baseClassSuite.descriptor val baseClassSuiteDescriptor = symbols.baseClassSuite.descriptor
private val topLevelSuiteNames = mutableSetOf<String>()
// region Useful extensions. // region Useful extensions.
var testSuiteCnt = 0 var testSuiteCnt = 0
fun Name.synthesizeSuiteClassName() = identifier.synthesizeSuiteClassName() fun Name.synthesizeSuiteClassName() = identifier.synthesizeSuiteClassName()
fun String.synthesizeSuiteClassName() = "${splitToSequence('.') fun String.synthesizeSuiteClassName() = "$this\$test\$${testSuiteCnt++}".synthesizedName
.mapIndexed { i, it -> if (i != 0) it.capitalize() else it }
.joinToString("")}\$test\$${testSuiteCnt++}".synthesizedName
// IrFile always uses a forward slash as a directory separator. // IrFile always uses a forward slash as a directory separator.
private val IrFile.fileName get() = name.substringAfterLast('/') private val IrFile.fileName
private val IrFile.topLevelSuiteName get() = "Tests in file: $fileName" get() = name.substringAfterLast('/')
private val IrFile.topLevelSuiteName: String
get() {
val packageFqName = packageFragmentDescriptor.fqName
val shortFileName = PackagePartClassUtils.getFilePartShortName(fileName)
return if (packageFqName.isRoot) shortFileName else "$packageFqName.$shortFileName"
}
private fun MutableList<TestFunction>.registerFunction( private fun MutableList<TestFunction>.registerFunction(
function: IrFunctionSymbol, function: IrFunctionSymbol,
@@ -515,12 +521,28 @@ internal class TestProcessor (val context: KonanBackendContext): FileLoweringPas
) )
} }
/** Check if this fqName already used or not. */
private fun checkSuiteName(irFile: IrFile, name: String): Boolean {
if (topLevelSuiteNames.contains(name)) {
context.reportCompilationError("Package '${irFile.packageFragmentDescriptor.fqName}' has top-level test " +
"functions in several files with the same name: '${irFile.fileName}'")
return false
}
topLevelSuiteNames.add(name)
return true
}
private fun generateTopLevelSuite(irFile: IrFile, functions: Collection<TestFunction>) { private fun generateTopLevelSuite(irFile: IrFile, functions: Collection<TestFunction>) {
val builder = context.createIrBuilder(irFile.symbol) val builder = context.createIrBuilder(irFile.symbol)
val suiteName = irFile.topLevelSuiteName
if (!checkSuiteName(irFile, suiteName)) {
return
}
irFile.addTopLevelInitializer(builder.irBlock { irFile.addTopLevelInitializer(builder.irBlock {
val constructorCall = irCall(symbols.topLevelSuiteConstructor).apply { val constructorCall = irCall(symbols.topLevelSuiteConstructor).apply {
putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET,
context.builtIns.stringType, irFile.topLevelSuiteName)) context.builtIns.stringType, suiteName))
} }
val testSuiteVal = irTemporary(constructorCall, "topLevelTestSuite") val testSuiteVal = irTemporary(constructorCall, "topLevelTestSuite")
generateFunctionRegistration(testSuiteVal.symbol, generateFunctionRegistration(testSuiteVal.symbol,
@@ -542,9 +564,11 @@ internal class TestProcessor (val context: KonanBackendContext): FileLoweringPas
} }
// endregion // endregion
override fun lower(irFile: IrFile) { fun process(irModuleFragment: IrModuleFragment) {
val annotationCollector = AnnotationCollector(irFile) irModuleFragment.files.forEach {
irFile.acceptChildrenVoid(annotationCollector) val annotationCollector = AnnotationCollector(it)
createTestSuites(irFile, annotationCollector) it.acceptChildrenVoid(annotationCollector)
createTestSuites(it, annotationCollector)
}
} }
} }