Move JvmRuntimeDescriptorLoaderTest to core/descriptors.runtime/tests

This allows to get rid of the dependency on descriptors.runtime from
compiler/IDE tests which is problematic: classes there clash with the
classes in kotlin-reflect, which reference declarations in shadowed
packages
This commit is contained in:
Alexander Udalov
2017-11-08 11:18:48 +01:00
parent 02981038f3
commit d26486b7df
11 changed files with 65 additions and 40 deletions
+21 -8
View File
@@ -1,20 +1,33 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
apply { plugin("kotlin") }
jvmTarget = "1.6"
javaHome = rootProject.extra["JDK_16"] as String
dependencies {
compile(project(":core:util.runtime"))
compile(project(":core:descriptors"))
compile(project(":core:descriptors.jvm"))
compileOnly(project(":core:util.runtime"))
compileOnly(project(":core:descriptors"))
compileOnly(project(":core:descriptors.jvm"))
testCompile(projectTests(":compiler:tests-common"))
testCompile(projectTests(":generators:test-generator"))
}
sourceSets {
"main" { projectDefault() }
"test" {}
"test" { projectDefault() }
}
tasks.withType<JavaCompile> {
val compileJava by tasks.getting(JavaCompile::class) {
sourceCompatibility = "1.6"
targetCompatibility = "1.6"
}
val compileKotlin by tasks.getting(KotlinCompile::class) {
kotlinOptions.jvmTarget = "1.6"
kotlinOptions.jdkHome = rootProject.extra["JDK_16"] as String
}
val generateTests by generator("org.jetbrains.kotlin.generators.tests.GenerateRuntimeDescriptorTestsKt")
projectTest {
workingDir = rootDir
}
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.generators.tests
import org.jetbrains.kotlin.generators.tests.generator.testGroup
import org.jetbrains.kotlin.jvm.runtime.AbstractJvm8RuntimeDescriptorLoaderTest
import org.jetbrains.kotlin.jvm.runtime.AbstractJvmRuntimeDescriptorLoaderTest
fun main(args: Array<String>) {
System.setProperty("java.awt.headless", "true")
testGroup("core/descriptors.runtime/tests", "compiler/testData") {
testClass<AbstractJvmRuntimeDescriptorLoaderTest> {
model("loadJava/compiledKotlin")
model("loadJava/compiledJava", extension = "java", excludeDirs = listOf("sam", "kotlinSignature/propagation"))
}
testClass<AbstractJvm8RuntimeDescriptorLoaderTest> {
model("loadJava8/compiledJava", extension = "java")
}
}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.jvm.runtime
import org.jetbrains.kotlin.test.TestJdkKind
abstract class AbstractJvm8RuntimeDescriptorLoaderTest : AbstractJvmRuntimeDescriptorLoaderTest() {
override val defaultJdkKind: TestJdkKind = TestJdkKind.FULL_JDK
}
@@ -0,0 +1,258 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.jvm.runtime
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.kotlin.checkers.KotlinMultiFileTestWithJava
import org.jetbrains.kotlin.codegen.GenerationUtils
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.jvm.compiler.ExpectedLoadErrorsUtil
import org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.renderer.*
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.scopes.ChainedMemberScope
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl
import org.jetbrains.kotlin.test.*
import org.jetbrains.kotlin.test.KotlinTestUtils.TestFileFactoryNoModules
import org.jetbrains.kotlin.test.util.DescriptorValidator.ValidationVisitor.errorTypesForbidden
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.Configuration
import org.jetbrains.kotlin.utils.Printer
import org.jetbrains.kotlin.utils.sure
import java.io.File
import java.net.URLClassLoader
import java.util.*
import java.util.regex.Pattern
import kotlin.reflect.jvm.internal.components.ReflectKotlinClass
import kotlin.reflect.jvm.internal.components.RuntimeModuleData
import kotlin.reflect.jvm.internal.structure.classId
abstract class AbstractJvmRuntimeDescriptorLoaderTest : TestCaseWithTmpdir() {
companion object {
private val renderer = DescriptorRenderer.withOptions {
withDefinedIn = false
excludedAnnotationClasses = setOf(
FqName(ExpectedLoadErrorsUtil.ANNOTATION_CLASS_NAME)
)
overrideRenderingPolicy = OverrideRenderingPolicy.RENDER_OPEN_OVERRIDE
parameterNameRenderingPolicy = ParameterNameRenderingPolicy.NONE
includePropertyConstant = false
verbose = true
annotationArgumentsRenderingPolicy = AnnotationArgumentsRenderingPolicy.UNLESS_EMPTY
renderDefaultAnnotationArguments = true
modifiers = DescriptorRendererModifier.ALL
}
}
protected open val defaultJdkKind: TestJdkKind = TestJdkKind.MOCK_JDK
// NOTE: this test does a dirty hack of text substitution to make all annotations defined in source code retain at runtime.
// Specifically each @interface in Java sources is extended by @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
// Also type related annotations are removed from Java because they are invisible at runtime
protected fun doTest(fileName: String) {
val file = File(fileName)
val text = FileUtil.loadFile(file, true)
if (InTextDirectivesUtils.isDirectiveDefined(text, "SKIP_IN_RUNTIME_TEST")) return
val jdkKind =
if (InTextDirectivesUtils.isDirectiveDefined(text, "FULL_JDK")) TestJdkKind.FULL_JDK
else defaultJdkKind
compileFile(file, text, jdkKind)
val classLoader = URLClassLoader(arrayOf(tmpdir.toURI().toURL()), ForTestCompileRuntime.runtimeAndReflectJarClassLoader())
val actual = createReflectedPackageView(classLoader, KotlinTestUtils.TEST_MODULE_NAME)
val comparatorConfiguration = Configuration(
/* checkPrimaryConstructors = */ fileName.endsWith(".kt"),
/* checkPropertyAccessors = */ true,
/* includeMethodsOfKotlinAny = */ false,
/* renderDeclarationsFromOtherModules = */ true,
/* checkFunctionContract = */ false,
// Skip Java annotation constructors because order of their parameters is not retained at runtime
{ descriptor -> !descriptor!!.isJavaAnnotationConstructor() },
errorTypesForbidden(), renderer
)
val differentResultFile = KotlinTestUtils.replaceExtension(file, "runtime.txt")
if (differentResultFile.exists()) {
RecursiveDescriptorComparator.validateAndCompareDescriptorWithFile(actual, comparatorConfiguration, differentResultFile)
return
}
val expected = LoadDescriptorUtil.loadTestPackageAndBindingContextFromJavaRoot(
tmpdir, testRootDisposable, jdkKind, ConfigurationKind.ALL, true, false, false, null
).first
RecursiveDescriptorComparator.validateAndCompareDescriptors(expected, actual, comparatorConfiguration, null)
}
private fun DeclarationDescriptor.isJavaAnnotationConstructor() =
this is ClassConstructorDescriptor &&
containingDeclaration is JavaClassDescriptor &&
containingDeclaration.kind == ClassKind.ANNOTATION_CLASS
private fun compileFile(file: File, text: String, jdkKind: TestJdkKind) {
val fileName = file.name
when {
fileName.endsWith(".java") -> {
val sources = KotlinTestUtils.createTestFiles(fileName, text, object : TestFileFactoryNoModules<File>() {
override fun create(fileName: String, text: String, directives: Map<String, String>): File {
val targetFile = File(tmpdir, fileName)
targetFile.writeText(adaptJavaSource(text))
return targetFile
}
})
LoadDescriptorUtil.compileJavaWithAnnotationsJar(sources, tmpdir)
}
fileName.endsWith(".kt") -> {
val environment = KotlinTestUtils.createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea(
myTestRootDisposable, ConfigurationKind.ALL, jdkKind
)
for (root in environment.configuration.getList(JVMConfigurationKeys.CONTENT_ROOTS)) {
LOG.info("root: " + root.toString())
}
val ktFile = KotlinTestUtils.createFile(file.path, text, environment.project)
GenerationUtils.compileFileTo(ktFile, environment, tmpdir)
}
}
}
private fun createReflectedPackageView(classLoader: URLClassLoader, moduleName: String): SyntheticPackageViewForTest {
val moduleData = RuntimeModuleData.create(classLoader)
moduleData.packagePartProvider.registerModule(moduleName)
val module = moduleData.module
val generatedPackageDir = File(tmpdir, LoadDescriptorUtil.TEST_PACKAGE_FQNAME.pathSegments().single().asString())
val allClassFiles = FileUtil.findFilesByMask(Pattern.compile(".*\\.class"), generatedPackageDir)
val packageScopes = arrayListOf<MemberScope>()
val classes = arrayListOf<ClassDescriptor>()
for (classFile in allClassFiles) {
val className = classFile.toRelativeString(tmpdir).substringBeforeLast(".class").replace('/', '.').replace('\\', '.')
val klass = classLoader.loadClass(className).sure { "Couldn't load class $className" }
val binaryClass = ReflectKotlinClass.create(klass)
val header = binaryClass?.classHeader
if (header?.kind == KotlinClassHeader.Kind.FILE_FACADE || header?.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS) {
val packageView = module.getPackage(LoadDescriptorUtil.TEST_PACKAGE_FQNAME)
if (!packageScopes.contains(packageView.memberScope)) {
packageScopes.add(packageView.memberScope)
}
}
else if (header == null || header.kind == KotlinClassHeader.Kind.CLASS) {
// Either a normal Kotlin class or a Java class
val classId = klass.classId
if (!classId.isLocal) {
val classDescriptor = module.findClassAcrossModuleDependencies(classId).sure { "Couldn't resolve class $className" }
if (DescriptorUtils.isTopLevelDeclaration(classDescriptor)) {
classes.add(classDescriptor)
}
}
}
}
// Since runtime package view descriptor doesn't support getAllDescriptors(), we construct a synthetic package view here.
// It has in its scope descriptors for all the classes and top level members generated by the compiler
return SyntheticPackageViewForTest(module, packageScopes, classes)
}
private fun adaptJavaSource(text: String): String {
val typeAnnotations = arrayOf("NotNull", "Nullable", "ReadOnly", "Mutable")
val adaptedSource = typeAnnotations.fold(text) { text, annotation -> text.replace("@$annotation", "") }
if ("@Retention" !in adaptedSource) {
return adaptedSource.replace(
"@interface",
"@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @interface"
)
}
return adaptedSource
}
private class SyntheticPackageViewForTest(override val module: ModuleDescriptor,
packageScopes: List<MemberScope>,
classes: List<ClassifierDescriptor>) : PackageViewDescriptor {
private val scope: MemberScope
init {
val list = ArrayList<MemberScope>(packageScopes.size + 1)
list.add(ScopeWithClassifiers(classes))
list.addAll(packageScopes)
scope = ChainedMemberScope("synthetic package view for test", list)
}
override val fqName: FqName
get() = LoadDescriptorUtil.TEST_PACKAGE_FQNAME
override val memberScope: MemberScope
get() = scope
override val fragments: List<PackageFragmentDescriptor> = listOf(
object : PackageFragmentDescriptorImpl(module, fqName) {
override fun getMemberScope(): MemberScope = scope
}
)
override fun <R, D> accept(visitor: DeclarationDescriptorVisitor<R, D>, data: D): R =
visitor.visitPackageViewDescriptor(this, data)
override fun getContainingDeclaration() = null
override fun getOriginal() = throw UnsupportedOperationException()
override fun acceptVoid(visitor: DeclarationDescriptorVisitor<Void, Void>?) = throw UnsupportedOperationException()
override fun getName() = throw UnsupportedOperationException()
override val annotations: Annotations
get() = throw UnsupportedOperationException()
}
private class ScopeWithClassifiers(classifiers: List<ClassifierDescriptor>) : MemberScopeImpl() {
private val classifierMap = HashMap<Name, ClassifierDescriptor>()
init {
for (classifier in classifiers) {
classifierMap.put(classifier.name, classifier)?.let {
throw IllegalStateException(String.format("Redeclaration: %s (%s) and %s (%s) (no line info available)",
DescriptorUtils.getFqName(it), it,
DescriptorUtils.getFqName(classifier), classifier))
}
}
}
override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? = classifierMap[name]
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection<DeclarationDescriptor> = classifierMap.values
override fun printScopeStructure(p: Printer) {
p.println("runtime descriptor loader test")
}
}
}
private val LOG = Logger.getInstance(KotlinMultiFileTestWithJava::class.java)
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.jvm.runtime;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("compiler/testData/loadJava8/compiledJava")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class Jvm8RuntimeDescriptorLoaderTestGenerated extends AbstractJvm8RuntimeDescriptorLoaderTest {
public void testAllFilesPresentInCompiledJava() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/loadJava8/compiledJava"), Pattern.compile("^(.+)\\.java$"), TargetBackend.ANY, true);
}
@TestMetadata("InnerClassTypeAnnotation.java")
public void testInnerClassTypeAnnotation() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava8/compiledJava/InnerClassTypeAnnotation.java");
doTest(fileName);
}
@TestMetadata("MapRemove.java")
public void testMapRemove() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava8/compiledJava/MapRemove.java");
doTest(fileName);
}
@TestMetadata("TypeAnnotations.java")
public void testTypeAnnotations() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava8/compiledJava/TypeAnnotations.java");
doTest(fileName);
}
@TestMetadata("TypeParameterAnnotations.java")
public void testTypeParameterAnnotations() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava8/compiledJava/TypeParameterAnnotations.java");
doTest(fileName);
}
}