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)
phaser.phase(KonanPhase.TEST_PROCESSOR) {
irModule.files.forEach(TestProcessor(context)::lower)
TestProcessor(context).process(irModule)
}
phaser.phase(KonanPhase.LOWER_BEFORE_INLINE) {
@@ -1,6 +1,5 @@
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.ir.createFakeOverrideDescriptor
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.descriptors.isAbstract
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.isFunctionType
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.visitors.IrElementVisitorVoid
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.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
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_GENERATED_MEMBER: IrDeclarationOriginImpl("TEST_SUITE_GENERATED_MEMBER")
@@ -58,16 +58,22 @@ internal class TestProcessor (val context: KonanBackendContext): FileLoweringPas
val baseClassSuiteDescriptor = symbols.baseClassSuite.descriptor
private val topLevelSuiteNames = mutableSetOf<String>()
// region Useful extensions.
var testSuiteCnt = 0
fun Name.synthesizeSuiteClassName() = identifier.synthesizeSuiteClassName()
fun String.synthesizeSuiteClassName() = "${splitToSequence('.')
.mapIndexed { i, it -> if (i != 0) it.capitalize() else it }
.joinToString("")}\$test\$${testSuiteCnt++}".synthesizedName
fun String.synthesizeSuiteClassName() = "$this\$test\$${testSuiteCnt++}".synthesizedName
// IrFile always uses a forward slash as a directory separator.
private val IrFile.fileName get() = name.substringAfterLast('/')
private val IrFile.topLevelSuiteName get() = "Tests in file: $fileName"
private val IrFile.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(
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>) {
val builder = context.createIrBuilder(irFile.symbol)
val suiteName = irFile.topLevelSuiteName
if (!checkSuiteName(irFile, suiteName)) {
return
}
irFile.addTopLevelInitializer(builder.irBlock {
val constructorCall = irCall(symbols.topLevelSuiteConstructor).apply {
putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET,
context.builtIns.stringType, irFile.topLevelSuiteName))
context.builtIns.stringType, suiteName))
}
val testSuiteVal = irTemporary(constructorCall, "topLevelTestSuite")
generateFunctionRegistration(testSuiteVal.symbol,
@@ -542,9 +564,11 @@ internal class TestProcessor (val context: KonanBackendContext): FileLoweringPas
}
// endregion
override fun lower(irFile: IrFile) {
val annotationCollector = AnnotationCollector(irFile)
irFile.acceptChildrenVoid(annotationCollector)
createTestSuites(irFile, annotationCollector)
fun process(irModuleFragment: IrModuleFragment) {
irModuleFragment.files.forEach {
val annotationCollector = AnnotationCollector(it)
it.acceptChildrenVoid(annotationCollector)
createTestSuites(it, annotationCollector)
}
}
}