[Native][tests] Simplify handling opt-ins in the test launcher
For a box test, the test engine generates a test launcher -- a function
that calls `box` function. The latter might require an opt-in. To deal
with this, the test engine was using some heuristics to detect possibly
used opt-ins and applied them to the test launcher.
This commit provides an alternative solution -- it uses
`@Suppress("OPT_IN_USAGE_ERROR")` annotation for the test launcher. This
way, it doesn't have to guess which opt-ins to apply.
This commit is contained in:
+7
-55
@@ -155,9 +155,9 @@ private class ExtTestDataFile(
|
|||||||
val isStandaloneTest = definitelyStandaloneTest || determineIfStandaloneTest()
|
val isStandaloneTest = definitelyStandaloneTest || determineIfStandaloneTest()
|
||||||
makeObjectsMutable()
|
makeObjectsMutable()
|
||||||
patchPackageNames(isStandaloneTest)
|
patchPackageNames(isStandaloneTest)
|
||||||
val fileLevelAnnotations = patchFileLevelAnnotations()
|
patchFileLevelAnnotations()
|
||||||
val entryPointFunctionFQN = findEntryPoint()
|
val entryPointFunctionFQN = findEntryPoint()
|
||||||
generateTestLauncher(isStandaloneTest, entryPointFunctionFQN, fileLevelAnnotations)
|
generateTestLauncher(isStandaloneTest, entryPointFunctionFQN)
|
||||||
|
|
||||||
return doCreateTestCase(isStandaloneTest, sharedModules)
|
return doCreateTestCase(isStandaloneTest, sharedModules)
|
||||||
}
|
}
|
||||||
@@ -405,54 +405,13 @@ private class ExtTestDataFile(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 1. Collect all file-level annotations that should be added to the launcher. See [generateTestLauncher].
|
* Make sure that the OptIns specified in test directives (see [ExtTestDataFileSettings.optInsForSourceCode]) are represented
|
||||||
* 2. Make sure that the OptIns specified in test directives (see [ExtTestDataFileSettings.optInsForSourceCode]) are represented
|
* as file-level annotations in every individual test file.
|
||||||
* as file-level annotations in every individual test file.
|
|
||||||
*/
|
*/
|
||||||
private fun patchFileLevelAnnotations(): List<String> = with(structure) {
|
private fun patchFileLevelAnnotations() = with(structure) {
|
||||||
val allFileLevelAnnotations = hashSetOf<String>()
|
|
||||||
|
|
||||||
fun getAnnotationText(fullyQualifiedName: String) = "@file:${OPT_IN_ANNOTATION_NAME.asString()}($fullyQualifiedName::class)"
|
fun getAnnotationText(fullyQualifiedName: String) = "@file:${OPT_IN_ANNOTATION_NAME.asString()}($fullyQualifiedName::class)"
|
||||||
|
|
||||||
// Every OptIn specified in test directive should be represented as a file-level annotation.
|
// Make sure that every test file contains all the necessary file-level annotations.
|
||||||
testDataFileSettings.optInsForSourceCode.mapTo(allFileLevelAnnotations, ::getAnnotationText)
|
|
||||||
|
|
||||||
// Now, collect file-level annotations already present in test files.
|
|
||||||
filesToTransform.forEach { handler ->
|
|
||||||
handler.accept(object : KtTreeVisitorVoid() {
|
|
||||||
override fun visitKtFile(file: KtFile) {
|
|
||||||
val importDirectives: Map<String, String> by lazy {
|
|
||||||
file.importDirectives.mapNotNull { importDirective ->
|
|
||||||
val importedFqName = importDirective.importedFqName ?: return@mapNotNull null
|
|
||||||
val name = importDirective.alias?.name ?: importedFqName.shortName().asString()
|
|
||||||
name to importedFqName.asString()
|
|
||||||
}.toMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
file.annotationEntries.mapNotNullTo(allFileLevelAnnotations) { annotationEntry ->
|
|
||||||
val constructorCallee = annotationEntry.getChildOfType<KtConstructorCalleeExpression>()
|
|
||||||
val constructorType = constructorCallee?.typeReference?.getChildOfType<KtUserType>()
|
|
||||||
val constructorTypeName = constructorType?.collectNames()?.singleOrNull()
|
|
||||||
|
|
||||||
if (constructorTypeName != OPT_IN_ANNOTATION_NAME) return@mapNotNullTo null
|
|
||||||
|
|
||||||
val valueArgument = annotationEntry.valueArguments.singleOrNull()
|
|
||||||
val classLiteral = valueArgument?.getArgumentExpression() as? KtClassLiteralExpression
|
|
||||||
|
|
||||||
if (classLiteral?.getChildOfType<KtDotQualifiedExpression>() != null)
|
|
||||||
annotationEntry.text
|
|
||||||
else
|
|
||||||
classLiteral?.getChildOfType<KtNameReferenceExpression>()?.getReferencedName()
|
|
||||||
?.let(importDirectives::get)
|
|
||||||
?.let { fullyQualifiedName -> getAnnotationText(fullyQualifiedName) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
val allFileLevelAnnotationsSorted = allFileLevelAnnotations.sorted()
|
|
||||||
|
|
||||||
// Finally, make sure that every test file contains all the necessary file-level annotations.
|
|
||||||
if (testDataFileSettings.optInsForSourceCode.isNotEmpty()) {
|
if (testDataFileSettings.optInsForSourceCode.isNotEmpty()) {
|
||||||
filesToTransform.forEach { handler ->
|
filesToTransform.forEach { handler ->
|
||||||
handler.accept(object : KtTreeVisitorVoid() {
|
handler.accept(object : KtTreeVisitorVoid() {
|
||||||
@@ -468,8 +427,6 @@ private class ExtTestDataFile(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return allFileLevelAnnotationsSorted
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtFile.addAnnotations(fileAnnotationList: KtFileAnnotationList) {
|
private fun KtFile.addAnnotations(fileAnnotationList: KtFileAnnotationList) {
|
||||||
@@ -514,13 +471,8 @@ private class ExtTestDataFile(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Adds a wrapper to run it as Kotlin test. */
|
/** Adds a wrapper to run it as Kotlin test. */
|
||||||
private fun generateTestLauncher(isStandaloneTest: Boolean, entryPointFunctionFQN: String, fileLevelAnnotations: List<String>) {
|
private fun generateTestLauncher(isStandaloneTest: Boolean, entryPointFunctionFQN: String) {
|
||||||
val fileText = buildString {
|
val fileText = buildString {
|
||||||
if (fileLevelAnnotations.isNotEmpty()) {
|
|
||||||
fileLevelAnnotations.forEach(this::appendLine)
|
|
||||||
appendLine()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isStandaloneTest) {
|
if (!isStandaloneTest) {
|
||||||
append("package ").appendLine(testDataFileSettings.nominalPackageName)
|
append("package ").appendLine(testDataFileSettings.nominalPackageName)
|
||||||
appendLine()
|
appendLine()
|
||||||
|
|||||||
+1
-1
@@ -36,7 +36,7 @@ internal fun generateBoxFunctionLauncher(entryPointFunctionFQN: String): String
|
|||||||
"""
|
"""
|
||||||
@kotlin.test.Test
|
@kotlin.test.Test
|
||||||
fun runTest() {
|
fun runTest() {
|
||||||
val result = $entryPointFunctionFQN()
|
val result = @Suppress("OPT_IN_USAGE_ERROR") $entryPointFunctionFQN()
|
||||||
kotlin.test.assertEquals("OK", result, "Test failed with: ${'$'}result")
|
kotlin.test.assertEquals("OK", result, "Test failed with: ${'$'}result")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user