Refactor bytecode listing & light analysis tests
Simplify AbstractLightAnalysisModeCodegenTest so that its logic can be merged with black box codegen tests
This commit is contained in:
+168
-182
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen
|
package org.jetbrains.kotlin.codegen
|
||||||
|
|
||||||
|
import com.intellij.openapi.Disposable
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||||
@@ -30,196 +31,181 @@ abstract class AbstractBytecodeListingTest : CodegenTestCase() {
|
|||||||
protected open val classBuilderFactory: ClassBuilderFactory
|
protected open val classBuilderFactory: ClassBuilderFactory
|
||||||
get() = ClassBuilderFactories.TEST
|
get() = ClassBuilderFactories.TEST
|
||||||
|
|
||||||
open fun getTextFile(ktFile: File): File = File(ktFile.parentFile, ktFile.nameWithoutExtension + ".txt")
|
|
||||||
|
|
||||||
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>, javaFilesDir: File?) {
|
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>, javaFilesDir: File?) {
|
||||||
val javaSources = javaFilesDir?.let { arrayOf(it) } ?: emptyArray()
|
val txtFile = File(wholeFile.parentFile, wholeFile.nameWithoutExtension + ".txt")
|
||||||
|
doTest(testRootDisposable, files, javaFilesDir, txtFile, classBuilderFactory, this::setupEnvironment)
|
||||||
var addRuntime = false
|
|
||||||
var addReflect = false
|
|
||||||
|
|
||||||
for (file in files) {
|
|
||||||
if (InTextDirectivesUtils.isDirectiveDefined(file.content, "WITH_RUNTIME")) {
|
|
||||||
addRuntime = true
|
|
||||||
}
|
|
||||||
if (InTextDirectivesUtils.isDirectiveDefined(file.content, "WITH_REFLECT")) {
|
|
||||||
addReflect = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val configurationKind = if (addReflect)
|
|
||||||
ConfigurationKind.ALL
|
|
||||||
else if (addRuntime)
|
|
||||||
ConfigurationKind.NO_KOTLIN_REFLECT
|
|
||||||
else
|
|
||||||
ConfigurationKind.JDK_ONLY
|
|
||||||
|
|
||||||
val jdkKind = getJdkKind(files)
|
|
||||||
if (myEnvironment != null) {
|
|
||||||
throw IllegalStateException("must not set up myEnvironment twice")
|
|
||||||
}
|
|
||||||
|
|
||||||
val configuration = createConfiguration(
|
|
||||||
configurationKind,
|
|
||||||
jdkKind,
|
|
||||||
listOf<File>(getAnnotationsJar()),
|
|
||||||
javaSources.filterNotNull(),
|
|
||||||
emptyList())
|
|
||||||
|
|
||||||
myEnvironment = KotlinCoreEnvironment.createForTests(
|
|
||||||
testRootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES
|
|
||||||
)
|
|
||||||
|
|
||||||
setupEnvironment(myEnvironment)
|
|
||||||
|
|
||||||
loadMultiFiles(files)
|
|
||||||
|
|
||||||
val txtFile = getTextFile(wholeFile)
|
|
||||||
val generatedFiles = CodegenTestUtil.generateFiles(myEnvironment, myFiles, classBuilderFactory)
|
|
||||||
.getClassFiles()
|
|
||||||
.sortedBy { it.relativePath }
|
|
||||||
.map {
|
|
||||||
val cr = ClassReader(it.asByteArray())
|
|
||||||
val visitor = TextCollectingVisitor()
|
|
||||||
cr.accept(visitor, ClassReader.SKIP_CODE)
|
|
||||||
KotlinTestUtils.replaceHash(visitor.text, "HASH")
|
|
||||||
}.joinToString("\n\n", postfix = "\n")
|
|
||||||
|
|
||||||
KotlinTestUtils.assertEqualsToFile(txtFile, generatedFiles)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun setupEnvironment(environment: KotlinCoreEnvironment) {}
|
protected open fun setupEnvironment(environment: KotlinCoreEnvironment) {}
|
||||||
|
|
||||||
private class TextCollectingVisitor : ClassVisitor(ASM5) {
|
companion object {
|
||||||
private class Declaration(val text: String, val annotations: MutableList<String> = arrayListOf())
|
@JvmStatic
|
||||||
|
fun doTest(
|
||||||
private val declarationsInsideClass = arrayListOf<Declaration>()
|
disposable: Disposable,
|
||||||
private val classAnnotations = arrayListOf<String>()
|
files: List<TestFile>,
|
||||||
private var className = ""
|
javaFilesDir: File?,
|
||||||
private var classAccess = 0
|
txtFile: File,
|
||||||
|
classBuilderFactory: ClassBuilderFactory,
|
||||||
private fun addAnnotation(desc: String, list: MutableList<String> = declarationsInsideClass.last().annotations) {
|
setupEnvironment: (KotlinCoreEnvironment) -> Unit = {}
|
||||||
val name = Type.getType(desc).className
|
|
||||||
list.add("@$name ")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addModifier(text: String, list: MutableList<String>) {
|
|
||||||
list.add("$text ")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleModifiers(access: Int, list: MutableList<String> = declarationsInsideClass.last().annotations) {
|
|
||||||
if (access and ACC_PUBLIC != 0) addModifier("public", list)
|
|
||||||
if (access and ACC_PROTECTED != 0) addModifier("protected", list)
|
|
||||||
if (access and ACC_PRIVATE != 0) addModifier("private", list)
|
|
||||||
|
|
||||||
if (access and ACC_SYNTHETIC != 0) addModifier("synthetic", list)
|
|
||||||
if (access and ACC_DEPRECATED != 0) addModifier("deprecated", list)
|
|
||||||
if (access and ACC_FINAL != 0) addModifier("final", list)
|
|
||||||
if (access and ACC_ABSTRACT != 0 && access and ACC_INTERFACE == 0) addModifier("abstract", list)
|
|
||||||
if (access and ACC_STATIC != 0) addModifier("static", list)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun classOrInterface(access: Int): String {
|
|
||||||
return when {
|
|
||||||
access and ACC_ANNOTATION != 0 -> "annotation class"
|
|
||||||
access and ACC_ENUM != 0 -> "enum class"
|
|
||||||
access and ACC_INTERFACE != 0 -> "interface"
|
|
||||||
else -> "class"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val text: String
|
|
||||||
get() = StringBuilder().apply {
|
|
||||||
if (classAnnotations.isNotEmpty()) {
|
|
||||||
append(classAnnotations.joinToString("\n", postfix = "\n"))
|
|
||||||
}
|
|
||||||
arrayListOf<String>().apply { handleModifiers(classAccess, this) }.forEach { append(it) }
|
|
||||||
append(classOrInterface(classAccess))
|
|
||||||
append(" ")
|
|
||||||
append(className)
|
|
||||||
if (declarationsInsideClass.isNotEmpty()) {
|
|
||||||
append(" {\n")
|
|
||||||
for (declaration in declarationsInsideClass.sortedBy { it.text }) {
|
|
||||||
append(" ").append(declaration.annotations.joinToString("")).append(declaration.text).append("\n")
|
|
||||||
}
|
|
||||||
append("}")
|
|
||||||
}
|
|
||||||
}.toString()
|
|
||||||
|
|
||||||
override fun visitMethod(
|
|
||||||
access: Int,
|
|
||||||
name: String,
|
|
||||||
desc: String,
|
|
||||||
signature: String?,
|
|
||||||
exceptions: Array<out String>?
|
|
||||||
): MethodVisitor? {
|
|
||||||
val returnType = Type.getReturnType(desc).className
|
|
||||||
val parameterTypes = Type.getArgumentTypes(desc).map { it.className }
|
|
||||||
val methodAnnotations = arrayListOf<String>()
|
|
||||||
val parameterAnnotations = hashMapOf<Int, MutableList<String>>()
|
|
||||||
|
|
||||||
handleModifiers(access, methodAnnotations)
|
|
||||||
|
|
||||||
return object : MethodVisitor(ASM5) {
|
|
||||||
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
|
|
||||||
val type = Type.getType(desc).className
|
|
||||||
methodAnnotations += "@$type "
|
|
||||||
return super.visitAnnotation(desc, visible)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitParameterAnnotation(parameter: Int, desc: String, visible: Boolean): AnnotationVisitor? {
|
|
||||||
val type = Type.getType(desc).className
|
|
||||||
parameterAnnotations.getOrPut(parameter, { arrayListOf() }).add("@$type ")
|
|
||||||
return super.visitParameterAnnotation(parameter, desc, visible)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitEnd() {
|
|
||||||
val parameterWithAnnotations = parameterTypes.mapIndexed { index, parameter ->
|
|
||||||
val annotations = parameterAnnotations.getOrElse(index, { emptyList<String>() }).joinToString("")
|
|
||||||
"${annotations}p$index: $parameter"
|
|
||||||
}.joinToString()
|
|
||||||
declarationsInsideClass.add(Declaration("method $name($parameterWithAnnotations): $returnType", methodAnnotations))
|
|
||||||
super.visitEnd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? {
|
|
||||||
val type = Type.getType(desc).className
|
|
||||||
val fieldDeclaration = Declaration("field $name: $type")
|
|
||||||
declarationsInsideClass.add(fieldDeclaration)
|
|
||||||
handleModifiers(access)
|
|
||||||
if (access and ACC_VOLATILE != 0) addModifier("volatile", fieldDeclaration.annotations)
|
|
||||||
|
|
||||||
return object : FieldVisitor(ASM5) {
|
|
||||||
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
|
|
||||||
addAnnotation(desc)
|
|
||||||
return super.visitAnnotation(desc, visible)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
|
|
||||||
val name = Type.getType(desc).className
|
|
||||||
classAnnotations.add("@$name")
|
|
||||||
return super.visitAnnotation(desc, visible)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(
|
|
||||||
version: Int,
|
|
||||||
access: Int,
|
|
||||||
name: String,
|
|
||||||
signature: String?,
|
|
||||||
superName: String?,
|
|
||||||
interfaces: Array<out String>?
|
|
||||||
) {
|
) {
|
||||||
className = name
|
val addRuntime = files.any { InTextDirectivesUtils.isDirectiveDefined(it.content, "WITH_RUNTIME") }
|
||||||
classAccess = access
|
val addReflect = files.any { InTextDirectivesUtils.isDirectiveDefined(it.content, "WITH_REFLECT") }
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitInnerClass(name: String, outerName: String?, innerName: String?, access: Int) {
|
val configurationKind = when {
|
||||||
declarationsInsideClass.add(Declaration("inner class $name"))
|
addReflect -> ConfigurationKind.ALL
|
||||||
|
addRuntime -> ConfigurationKind.NO_KOTLIN_REFLECT
|
||||||
|
else -> ConfigurationKind.JDK_ONLY
|
||||||
|
}
|
||||||
|
|
||||||
|
val configuration = createConfiguration(
|
||||||
|
configurationKind, getJdkKind(files), listOf(getAnnotationsJar()), javaFilesDir?.let(::listOf).orEmpty(), emptyList()
|
||||||
|
)
|
||||||
|
val environment = KotlinCoreEnvironment.createForTests(disposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
|
||||||
|
setupEnvironment(environment)
|
||||||
|
|
||||||
|
val generatedFiles = CodegenTestUtil.generateFiles(environment, loadMultiFiles(files, environment.project), classBuilderFactory)
|
||||||
|
.getClassFiles()
|
||||||
|
.sortedBy { it.relativePath }
|
||||||
|
.map {
|
||||||
|
val cr = ClassReader(it.asByteArray())
|
||||||
|
val visitor = BytecodeListingTextCollectingVisitor()
|
||||||
|
cr.accept(visitor, ClassReader.SKIP_CODE)
|
||||||
|
KotlinTestUtils.replaceHash(visitor.text, "HASH")
|
||||||
|
}.joinToString("\n\n", postfix = "\n")
|
||||||
|
|
||||||
|
KotlinTestUtils.assertEqualsToFile(txtFile, generatedFiles)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BytecodeListingTextCollectingVisitor : ClassVisitor(ASM5) {
|
||||||
|
private class Declaration(val text: String, val annotations: MutableList<String> = arrayListOf())
|
||||||
|
|
||||||
|
private val declarationsInsideClass = arrayListOf<Declaration>()
|
||||||
|
private val classAnnotations = arrayListOf<String>()
|
||||||
|
private var className = ""
|
||||||
|
private var classAccess = 0
|
||||||
|
|
||||||
|
private fun addAnnotation(desc: String, list: MutableList<String> = declarationsInsideClass.last().annotations) {
|
||||||
|
val name = Type.getType(desc).className
|
||||||
|
list.add("@$name ")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addModifier(text: String, list: MutableList<String>) {
|
||||||
|
list.add("$text ")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleModifiers(access: Int, list: MutableList<String> = declarationsInsideClass.last().annotations) {
|
||||||
|
if (access and ACC_PUBLIC != 0) addModifier("public", list)
|
||||||
|
if (access and ACC_PROTECTED != 0) addModifier("protected", list)
|
||||||
|
if (access and ACC_PRIVATE != 0) addModifier("private", list)
|
||||||
|
|
||||||
|
if (access and ACC_SYNTHETIC != 0) addModifier("synthetic", list)
|
||||||
|
if (access and ACC_DEPRECATED != 0) addModifier("deprecated", list)
|
||||||
|
if (access and ACC_FINAL != 0) addModifier("final", list)
|
||||||
|
if (access and ACC_ABSTRACT != 0 && access and ACC_INTERFACE == 0) addModifier("abstract", list)
|
||||||
|
if (access and ACC_STATIC != 0) addModifier("static", list)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun classOrInterface(access: Int): String {
|
||||||
|
return when {
|
||||||
|
access and ACC_ANNOTATION != 0 -> "annotation class"
|
||||||
|
access and ACC_ENUM != 0 -> "enum class"
|
||||||
|
access and ACC_INTERFACE != 0 -> "interface"
|
||||||
|
else -> "class"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val text: String
|
||||||
|
get() = StringBuilder().apply {
|
||||||
|
if (classAnnotations.isNotEmpty()) {
|
||||||
|
append(classAnnotations.joinToString("\n", postfix = "\n"))
|
||||||
|
}
|
||||||
|
arrayListOf<String>().apply { handleModifiers(classAccess, this) }.forEach { append(it) }
|
||||||
|
append(classOrInterface(classAccess))
|
||||||
|
append(" ")
|
||||||
|
append(className)
|
||||||
|
if (declarationsInsideClass.isNotEmpty()) {
|
||||||
|
append(" {\n")
|
||||||
|
for (declaration in declarationsInsideClass.sortedBy { it.text }) {
|
||||||
|
append(" ").append(declaration.annotations.joinToString("")).append(declaration.text).append("\n")
|
||||||
|
}
|
||||||
|
append("}")
|
||||||
|
}
|
||||||
|
}.toString()
|
||||||
|
|
||||||
|
override fun visitMethod(
|
||||||
|
access: Int,
|
||||||
|
name: String,
|
||||||
|
desc: String,
|
||||||
|
signature: String?,
|
||||||
|
exceptions: Array<out String>?
|
||||||
|
): MethodVisitor? {
|
||||||
|
val returnType = Type.getReturnType(desc).className
|
||||||
|
val parameterTypes = Type.getArgumentTypes(desc).map { it.className }
|
||||||
|
val methodAnnotations = arrayListOf<String>()
|
||||||
|
val parameterAnnotations = hashMapOf<Int, MutableList<String>>()
|
||||||
|
|
||||||
|
handleModifiers(access, methodAnnotations)
|
||||||
|
|
||||||
|
return object : MethodVisitor(ASM5) {
|
||||||
|
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
|
||||||
|
val type = Type.getType(desc).className
|
||||||
|
methodAnnotations += "@$type "
|
||||||
|
return super.visitAnnotation(desc, visible)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitParameterAnnotation(parameter: Int, desc: String, visible: Boolean): AnnotationVisitor? {
|
||||||
|
val type = Type.getType(desc).className
|
||||||
|
parameterAnnotations.getOrPut(parameter, { arrayListOf() }).add("@$type ")
|
||||||
|
return super.visitParameterAnnotation(parameter, desc, visible)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitEnd() {
|
||||||
|
val parameterWithAnnotations = parameterTypes.mapIndexed { index, parameter ->
|
||||||
|
val annotations = parameterAnnotations.getOrElse(index, { emptyList<String>() }).joinToString("")
|
||||||
|
"${annotations}p$index: $parameter"
|
||||||
|
}.joinToString()
|
||||||
|
declarationsInsideClass.add(Declaration("method $name($parameterWithAnnotations): $returnType", methodAnnotations))
|
||||||
|
super.visitEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? {
|
||||||
|
val type = Type.getType(desc).className
|
||||||
|
val fieldDeclaration = Declaration("field $name: $type")
|
||||||
|
declarationsInsideClass.add(fieldDeclaration)
|
||||||
|
handleModifiers(access)
|
||||||
|
if (access and ACC_VOLATILE != 0) addModifier("volatile", fieldDeclaration.annotations)
|
||||||
|
|
||||||
|
return object : FieldVisitor(ASM5) {
|
||||||
|
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
|
||||||
|
addAnnotation(desc)
|
||||||
|
return super.visitAnnotation(desc, visible)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
|
||||||
|
val name = Type.getType(desc).className
|
||||||
|
classAnnotations.add("@$name")
|
||||||
|
return super.visitAnnotation(desc, visible)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(
|
||||||
|
version: Int,
|
||||||
|
access: Int,
|
||||||
|
name: String,
|
||||||
|
signature: String?,
|
||||||
|
superName: String?,
|
||||||
|
interfaces: Array<out String>?
|
||||||
|
) {
|
||||||
|
className = name
|
||||||
|
classAccess = access
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitInnerClass(name: String, outerName: String?, innerName: String?, access: Int) {
|
||||||
|
declarationsInsideClass.add(Declaration("inner class $name"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-13
@@ -16,23 +16,18 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen
|
package org.jetbrains.kotlin.codegen
|
||||||
|
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
|
||||||
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
|
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
|
||||||
import org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension
|
import org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
abstract class AbstractLightAnalysisModeCodegenTest : AbstractBytecodeListingTest() {
|
abstract class AbstractLightAnalysisModeCodegenTest : AbstractBytecodeListingTest() {
|
||||||
override val classBuilderFactory: ClassBuilderFactory
|
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>, javaFilesDir: File?) {
|
||||||
get() = ClassBuilderFactories.TEST_KAPT3
|
|
||||||
|
|
||||||
override fun setupEnvironment(environment: KotlinCoreEnvironment) {
|
|
||||||
AnalysisHandlerExtension.registerExtension(environment.project, PartialAnalysisHandlerExtension())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun getTextFile(ktFile: File): File {
|
|
||||||
val boxTestsDir = File("compiler/testData/codegen/box")
|
val boxTestsDir = File("compiler/testData/codegen/box")
|
||||||
val outDir = File("compiler/testData/codegen/light-analysis", ktFile.toRelativeString(boxTestsDir)).parent
|
val outDir = File("compiler/testData/codegen/light-analysis", wholeFile.toRelativeString(boxTestsDir)).parent
|
||||||
return File(outDir, ktFile.nameWithoutExtension + ".txt")
|
val txtFile = File(outDir, wholeFile.nameWithoutExtension + ".txt")
|
||||||
|
|
||||||
|
doTest(testRootDisposable, files, javaFilesDir, txtFile, ClassBuilderFactories.TEST_KAPT3) { environment ->
|
||||||
|
AnalysisHandlerExtension.registerExtension(environment.project, PartialAnalysisHandlerExtension())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen;
|
|||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import com.intellij.openapi.project.Project;
|
||||||
import com.intellij.openapi.util.Ref;
|
import com.intellij.openapi.util.Ref;
|
||||||
import com.intellij.openapi.util.io.FileUtil;
|
import com.intellij.openapi.util.io.FileUtil;
|
||||||
import com.intellij.testFramework.TestDataFile;
|
import com.intellij.testFramework.TestDataFile;
|
||||||
@@ -278,17 +279,22 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void loadMultiFiles(@NotNull List<TestFile> files) {
|
protected void loadMultiFiles(@NotNull List<TestFile> files) {
|
||||||
|
myFiles = loadMultiFiles(files, myEnvironment.getProject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static CodegenTestFiles loadMultiFiles(@NotNull List<TestFile> files, @NotNull Project project) {
|
||||||
Collections.sort(files);
|
Collections.sort(files);
|
||||||
|
|
||||||
List<KtFile> ktFiles = new ArrayList<KtFile>(files.size());
|
List<KtFile> ktFiles = new ArrayList<KtFile>(files.size());
|
||||||
for (TestFile file : files) {
|
for (TestFile file : files) {
|
||||||
if (file.name.endsWith(".kt")) {
|
if (file.name.endsWith(".kt")) {
|
||||||
String content = CheckerTestUtil.parseDiagnosedRanges(file.content, new ArrayList<CheckerTestUtil.DiagnosedRange>(0));
|
String content = CheckerTestUtil.parseDiagnosedRanges(file.content, new ArrayList<CheckerTestUtil.DiagnosedRange>(0));
|
||||||
ktFiles.add(KotlinTestUtils.createFile(file.name, content, myEnvironment.getProject()));
|
ktFiles.add(KotlinTestUtils.createFile(file.name, content, project));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
myFiles = CodegenTestFiles.create(ktFiles);
|
return CodegenTestFiles.create(ktFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
Reference in New Issue
Block a user