Infrastructure: move compiler plugin tests back to their modules
This commit is contained in:
+197
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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.android.parcel
|
||||
|
||||
import org.jetbrains.kotlin.codegen.CodegenTestCase
|
||||
import org.jetbrains.kotlin.codegen.getClassFiles
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
import org.jetbrains.org.objectweb.asm.util.Printer
|
||||
import java.io.File
|
||||
|
||||
private val LINE_SEPARATOR = System.getProperty("line.separator")
|
||||
|
||||
abstract class AbstractAsmLikeInstructionListingTest : CodegenTestCase() {
|
||||
private companion object {
|
||||
val CURIOUS_ABOUT_DIRECTIVE = "// CURIOUS_ABOUT "
|
||||
val LOCAL_VARIABLES_TABLE_DIRECTIVE = "// LOCAL_VARIABLES_TABLE"
|
||||
}
|
||||
|
||||
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>, javaFilesDir: File?) {
|
||||
val txtFile = File(wholeFile.parentFile, wholeFile.nameWithoutExtension + ".txt")
|
||||
compile(files, javaFilesDir)
|
||||
|
||||
val classes = classFileFactory
|
||||
.getClassFiles()
|
||||
.sortedBy { it.relativePath }
|
||||
.map { file -> ClassNode().also { ClassReader(file.asByteArray()).accept(it, ClassReader.EXPAND_FRAMES) } }
|
||||
|
||||
val testFileLines = wholeFile.readLines()
|
||||
|
||||
val printBytecodeForTheseMethods = testFileLines
|
||||
.filter { it.startsWith(CURIOUS_ABOUT_DIRECTIVE) }
|
||||
.map { it.substring(CURIOUS_ABOUT_DIRECTIVE.length) }
|
||||
.flatMap { it.split(',').map { it.trim() } }
|
||||
|
||||
val showLocalVariables = testFileLines.any { it.trim() == LOCAL_VARIABLES_TABLE_DIRECTIVE }
|
||||
|
||||
KotlinTestUtils.assertEqualsToFile(txtFile, classes.joinToString(LINE_SEPARATOR.repeat(2)) {
|
||||
renderClassNode(it, printBytecodeForTheseMethods, showLocalVariables)
|
||||
})
|
||||
}
|
||||
|
||||
private fun renderClassNode(clazz: ClassNode, printBytecodeForTheseMethods: List<String>, showLocalVariables: Boolean): String {
|
||||
val fields = (clazz.fields ?: emptyList()).sortedBy { it.name }
|
||||
val methods = (clazz.methods ?: emptyList()).sortedBy { it.name }
|
||||
|
||||
val superTypes = (listOf(clazz.superName) + clazz.interfaces).filterNotNull()
|
||||
|
||||
return buildString {
|
||||
renderVisibilityModifiers(clazz.access)
|
||||
renderModalityModifiers(clazz.access)
|
||||
append(if ((clazz.access and ACC_INTERFACE) != 0) "interface " else "class ")
|
||||
append(clazz.name)
|
||||
|
||||
if (superTypes.isNotEmpty()) {
|
||||
append(" : " + superTypes.joinToString())
|
||||
}
|
||||
|
||||
appendln(" {")
|
||||
|
||||
fields.joinTo(this, LINE_SEPARATOR.repeat(2)) { renderField(it).withMargin() }
|
||||
|
||||
if (fields.isNotEmpty()) {
|
||||
appendln().appendln()
|
||||
}
|
||||
|
||||
methods.joinTo(this, LINE_SEPARATOR.repeat(2)) {
|
||||
val printBytecode = printBytecodeForTheseMethods.contains(it.name)
|
||||
renderMethod(it, printBytecode, showLocalVariables).withMargin()
|
||||
}
|
||||
|
||||
appendln().append("}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderField(field: FieldNode) = buildString {
|
||||
renderVisibilityModifiers(field.access)
|
||||
renderModalityModifiers(field.access)
|
||||
append(Type.getType(field.desc).className).append(' ')
|
||||
append(field.name)
|
||||
}
|
||||
|
||||
private fun renderMethod(method: MethodNode, printBytecode: Boolean, showLocalVariables: Boolean) = buildString {
|
||||
renderVisibilityModifiers(method.access)
|
||||
renderModalityModifiers(method.access)
|
||||
val (returnType, parameterTypes) = with(Type.getMethodType(method.desc)) { returnType to argumentTypes }
|
||||
append(returnType.className).append(' ')
|
||||
append(method.name)
|
||||
parameterTypes.mapIndexed { index, type -> "${type.className} p$index" }.joinTo(this, prefix = "(", postfix = ")")
|
||||
|
||||
if (printBytecode && (method.access and ACC_ABSTRACT) == 0) {
|
||||
appendln(" {")
|
||||
append(renderBytecodeInstructions(method.instructions).trimEnd().withMargin())
|
||||
|
||||
if (showLocalVariables) {
|
||||
val localVariableTable = buildLocalVariableTable(method)
|
||||
if (localVariableTable.isNotEmpty()) {
|
||||
appendln().appendln()
|
||||
append(localVariableTable.withMargin())
|
||||
}
|
||||
}
|
||||
|
||||
appendln().append("}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildLocalVariableTable(method: MethodNode): String {
|
||||
val localVariables = method.localVariables?.takeIf { it.isNotEmpty() } ?: return ""
|
||||
return buildString {
|
||||
append("Local variables:")
|
||||
for (variable in localVariables) {
|
||||
appendln().append((variable.name + ": " + variable.desc).withMargin())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderBytecodeInstructions(instructions: InsnList) = buildString {
|
||||
val labelMappings = LabelMappings()
|
||||
|
||||
var currentInsn = instructions.first
|
||||
while (currentInsn != null) {
|
||||
renderInstruction(currentInsn, labelMappings)
|
||||
currentInsn = currentInsn.next
|
||||
}
|
||||
}
|
||||
|
||||
private fun StringBuilder.renderInstruction(node: AbstractInsnNode, labelMappings: LabelMappings) {
|
||||
if (node is LabelNode) {
|
||||
appendln("LABEL (L" + labelMappings[node.label] + ")")
|
||||
return
|
||||
}
|
||||
|
||||
if (node is LineNumberNode) {
|
||||
appendln("LINENUMBER (" + node.line + ")")
|
||||
return
|
||||
}
|
||||
|
||||
if (node is FrameNode) return
|
||||
|
||||
append(" ").append(Printer.OPCODES[node.opcode] ?: error("Invalid opcode ${node.opcode}"))
|
||||
|
||||
when (node) {
|
||||
is FieldInsnNode -> append(" (" + node.name + ", " + node.desc + ")")
|
||||
is JumpInsnNode -> append(" (L" + labelMappings[node.label.label] + ")")
|
||||
is IntInsnNode -> append(" (" + node.operand + ")")
|
||||
is MethodInsnNode -> append(" (" + node.owner + ", "+ node.name + ", " + node.desc + ")")
|
||||
is VarInsnNode -> append(" (" + node.`var` + ")")
|
||||
is LdcInsnNode -> append(" (" + node.cst + ")")
|
||||
}
|
||||
|
||||
appendln()
|
||||
}
|
||||
|
||||
private fun String.withMargin(margin: String = " "): String {
|
||||
return lineSequence().map { margin + it }.joinToString(LINE_SEPARATOR)
|
||||
}
|
||||
|
||||
private fun StringBuilder.renderVisibilityModifiers(access: Int) {
|
||||
if ((access and ACC_PUBLIC) != 0) append("public ")
|
||||
if ((access and ACC_PRIVATE) != 0) append("private ")
|
||||
if ((access and ACC_PROTECTED) != 0) append("protected ")
|
||||
}
|
||||
|
||||
private fun StringBuilder.renderModalityModifiers(access: Int) {
|
||||
if ((access and ACC_FINAL) != 0) append("final ")
|
||||
if ((access and ACC_ABSTRACT) != 0) append("abstract ")
|
||||
if ((access and ACC_STATIC) != 0) append("static ")
|
||||
}
|
||||
|
||||
private class LabelMappings {
|
||||
private var mappings = hashMapOf<Int, Int>()
|
||||
private var currentIndex = 0
|
||||
|
||||
operator fun get(label: Label): Int {
|
||||
val hashCode = System.identityHashCode(label)
|
||||
return mappings.getOrPut(hashCode) { currentIndex++ }
|
||||
}
|
||||
}
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* 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.android.parcel
|
||||
|
||||
import org.jetbrains.kotlin.android.synthetic.AndroidComponentRegistrar
|
||||
import org.jetbrains.kotlin.android.synthetic.test.addAndroidExtensionsRuntimeLibrary
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||
import org.jetbrains.kotlin.codegen.CodegenTestCase
|
||||
import org.jetbrains.kotlin.codegen.getClassFiles
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import org.jetbrains.org.objectweb.asm.ClassWriter
|
||||
import org.jetbrains.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES
|
||||
import org.jetbrains.org.objectweb.asm.ClassWriter.COMPUTE_MAXS
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import org.junit.runner.JUnitCore
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
abstract class AbstractParcelBoxTest : CodegenTestCase() {
|
||||
protected companion object {
|
||||
val BASE_DIR = "plugins/android-extensions/android-extensions-compiler/testData/parcel/box"
|
||||
val LIBRARY_KT = File(File(BASE_DIR).parentFile, "boxLib.kt")
|
||||
|
||||
private val JUNIT_GENERATED_TEST_CLASS_BYTES by lazy { constructSyntheticTestClass() }
|
||||
private val JUNIT_GENERATED_TEST_CLASS_FQNAME = "test.JunitTest"
|
||||
|
||||
private fun constructSyntheticTestClass(): ByteArray {
|
||||
return with(ClassWriter(COMPUTE_MAXS or COMPUTE_FRAMES)) {
|
||||
visit(49, ACC_PUBLIC, JUNIT_GENERATED_TEST_CLASS_FQNAME.replace('.', '/'), null, "java/lang/Object", emptyArray())
|
||||
visitSource(null, null)
|
||||
|
||||
with(visitAnnotation("Lorg/junit/runner/RunWith;", true)) {
|
||||
visit("value", Type.getType("Lorg/robolectric/RobolectricTestRunner;"))
|
||||
visitEnd()
|
||||
}
|
||||
|
||||
with(visitAnnotation("Lorg/robolectric/annotation/Config;", true)) {
|
||||
visit("manifest", "--none")
|
||||
visitEnd()
|
||||
}
|
||||
|
||||
with(visitMethod(ACC_PUBLIC, "<init>", "()V", null, null)) {
|
||||
visitVarInsn(ALOAD, 0)
|
||||
visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false)
|
||||
|
||||
visitInsn(RETURN)
|
||||
visitMaxs(-1, -1)
|
||||
visitEnd()
|
||||
}
|
||||
|
||||
with(visitMethod(ACC_PUBLIC, "test", "()V", null, null)) {
|
||||
visitAnnotation("Lorg/junit/Test;", true).visitEnd()
|
||||
|
||||
val v = InstructionAdapter(this)
|
||||
|
||||
val assertionOk = Label()
|
||||
|
||||
v.invokestatic("test/TestKt", "box", "()Ljava/lang/String;", false) // -> ret
|
||||
v.dup() // -> ret, ret
|
||||
v.aconst("OK") // -> ret, ret, ok
|
||||
v.invokevirtual("java/lang/String", "equals", "(Ljava/lang/Object;)Z", false) // -> ret, eq
|
||||
v.ifne(assertionOk) // -> ret
|
||||
|
||||
val assertionErrorType = Type.getObjectType("java/lang/AssertionError")
|
||||
|
||||
v.anew(assertionErrorType) // -> ret, ae
|
||||
v.dupX1() // -> ae, ret, ae
|
||||
v.swap() // -> ae, ae, ret
|
||||
v.invokespecial(assertionErrorType.internalName, "<init>", "(Ljava/lang/Object;)V", false) // -> ae
|
||||
v.athrow()
|
||||
|
||||
v.visitLabel(assertionOk)
|
||||
v.pop() // -> [empty]
|
||||
v.areturn(Type.VOID_TYPE)
|
||||
|
||||
visitMaxs(-1, -1)
|
||||
visitEnd()
|
||||
}
|
||||
|
||||
visitEnd()
|
||||
toByteArray()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun doTest(filePath: String) {
|
||||
super.doTest(File(BASE_DIR, filePath + ".kt").absolutePath)
|
||||
}
|
||||
|
||||
private fun getClasspathForTest(): List<File> {
|
||||
val kotlinRuntimeJar = PathUtil.kotlinPathsForIdeaPlugin.stdlibPath
|
||||
val layoutLibJars = listOf(File("ideaSDK/plugins/android/lib/layoutlib.jar"), File("ideaSDK/plugins/android/lib/layoutlib-api.jar"))
|
||||
|
||||
val robolectricJars = File("dependencies/robolectric")
|
||||
.listFiles { f: File -> f.extension == "jar" }
|
||||
.sortedBy { it.nameWithoutExtension }
|
||||
|
||||
val junitCoreResourceName = JUnitCore::class.java.name.replace('.', '/') + ".class"
|
||||
val junitJar = File(JUnitCore::class.java.classLoader.getResource(junitCoreResourceName).file.substringBeforeLast('!'))
|
||||
|
||||
val androidExtensionsRuntimeCP =
|
||||
(System.getenv("ANDROID_EXTENSIONS_RUNTIME_CLASSES")?.split(File.pathSeparator)
|
||||
?: listOf("out/production/android-extensions-runtime")
|
||||
).map { File(it) }
|
||||
|
||||
return listOf(kotlinRuntimeJar) + layoutLibJars + robolectricJars + junitJar + androidExtensionsRuntimeCP
|
||||
}
|
||||
|
||||
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>, javaFilesDir: File?) {
|
||||
compile(files + TestFile(LIBRARY_KT.name, LIBRARY_KT.readText()), javaFilesDir)
|
||||
|
||||
val javaBin = File(System.getProperty("java.home").takeIf { it.isNotEmpty() } ?: error("JAVA_HOME is not set"), "bin")
|
||||
val javaExe = File(javaBin, "java.exe").takeIf { it.exists() } ?: File(javaBin, "java")
|
||||
assert(javaExe.exists()) { "Can't find 'java' executable in $javaBin" }
|
||||
|
||||
val libraryClasspath = getClasspathForTest()
|
||||
val dirForTestClasses = Files.createTempDirectory("parcel").toFile()
|
||||
|
||||
fun writeClass(fqNameOrPath: String, bytes: ByteArray) {
|
||||
val path = if (fqNameOrPath.endsWith(".class")) fqNameOrPath else (fqNameOrPath.replace('.', '/') + ".class")
|
||||
File(dirForTestClasses, path).also { it.parentFile.mkdirs() }.writeBytes(bytes)
|
||||
}
|
||||
|
||||
try {
|
||||
writeClass(JUNIT_GENERATED_TEST_CLASS_FQNAME, JUNIT_GENERATED_TEST_CLASS_BYTES)
|
||||
classFileFactory.getClassFiles().forEach { writeClass(it.relativePath, it.asByteArray()) }
|
||||
|
||||
val process = ProcessBuilder(
|
||||
javaExe.absolutePath,
|
||||
"-ea",
|
||||
"-classpath",
|
||||
(libraryClasspath + dirForTestClasses).joinToString(File.pathSeparator),
|
||||
JUnitCore::class.java.name,
|
||||
JUNIT_GENERATED_TEST_CLASS_FQNAME
|
||||
).inheritIO().start()
|
||||
|
||||
process.waitFor(3, TimeUnit.MINUTES)
|
||||
if (process.exitValue() != 0) {
|
||||
throw AssertionError(classFileFactory.createText())
|
||||
}
|
||||
} finally {
|
||||
if (!dirForTestClasses.deleteRecursively()) {
|
||||
throw AssertionError("Unable to delete $dirForTestClasses")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setupEnvironment(environment: KotlinCoreEnvironment) {
|
||||
AndroidComponentRegistrar.registerParcelExtensions(environment.project)
|
||||
addAndroidExtensionsRuntimeLibrary(environment)
|
||||
environment.updateClasspath(listOf(JvmClasspathRoot(File("ideaSDK/plugins/android/lib/layoutlib.jar"))))
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.android.parcel
|
||||
|
||||
import org.jetbrains.kotlin.android.synthetic.AndroidComponentRegistrar
|
||||
import org.jetbrains.kotlin.android.synthetic.test.addAndroidExtensionsRuntimeLibrary
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractParcelBytecodeListingTest : AbstractAsmLikeInstructionListingTest() {
|
||||
override fun setupEnvironment(environment: KotlinCoreEnvironment) {
|
||||
AndroidComponentRegistrar.registerParcelExtensions(environment.project)
|
||||
addAndroidExtensionsRuntimeLibrary(environment)
|
||||
environment.updateClasspath(listOf(JvmClasspathRoot(File("ideaSDK/plugins/android/lib/layoutlib.jar"))))
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.android.parcel
|
||||
|
||||
class ParcelBoxTest : AbstractParcelBoxTest() {
|
||||
fun testSimple() = doTest("simple")
|
||||
fun testPrimitiveTypes() = doTest("primitiveTypes")
|
||||
fun testBoxedTypes() = doTest("boxedTypes")
|
||||
fun testNullableTypesSimple() = doTest("nullableTypesSimple")
|
||||
fun testNullableTypes() = doTest("nullableTypes")
|
||||
fun testListSimple() = doTest("listSimple")
|
||||
fun testLists() = doTest("lists")
|
||||
fun testListKinds() = doTest("listKinds")
|
||||
fun testArraySimple() = doTest("arraySimple")
|
||||
fun testArrays() = doTest("arrays")
|
||||
fun testMapSimple() = doTest("mapSimple")
|
||||
fun testMaps() = doTest("maps")
|
||||
fun testMapKinds() = doTest("mapKinds")
|
||||
fun testSparseBooleanArray() = doTest("sparseBooleanArray")
|
||||
fun testBundle() = doTest("bundle")
|
||||
fun testSparseArrays() = doTest("sparseArrays")
|
||||
fun testCustomSimple() = doTest("customSimple")
|
||||
fun testCharSequence() = doTest("charSequence")
|
||||
fun testEnums() = doTest("enums")
|
||||
fun testObjects() = doTest("objects")
|
||||
fun testNestedParcelable() = doTest("nestedParcelable")
|
||||
fun testKt19749() = doTest("kt19749")
|
||||
fun testKt19747() = doTest("kt19747")
|
||||
fun testKt19747_2() = doTest("kt19747_2")
|
||||
fun test20002() = doTest("kt20002")
|
||||
fun test20021() = doTest("kt20021")
|
||||
fun testCustomSerializerSimple() = doTest("customSerializerSimple")
|
||||
fun testCustomSerializerWriteWith() = doTest("customSerializerWriteWith")
|
||||
fun testCustomSerializerBoxing() = doTest("customSerializerBoxing")
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.android.synthetic.test
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.util.ArrayUtil
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||
import org.jetbrains.kotlin.codegen.AbstractBlackBoxCodegenTest
|
||||
import org.jetbrains.kotlin.codegen.CodegenTestFiles
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.TestJdkKind
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
|
||||
abstract class AbstractAndroidBoxTest : AbstractBlackBoxCodegenTest() {
|
||||
|
||||
private fun createAndroidAPIEnvironment(path: String) {
|
||||
return createEnvironmentForConfiguration(KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.ANDROID_API), path)
|
||||
}
|
||||
|
||||
private fun createFakeAndroidEnvironment(path: String) {
|
||||
return createEnvironmentForConfiguration(KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK), path)
|
||||
}
|
||||
|
||||
private fun createEnvironmentForConfiguration(configuration: CompilerConfiguration, path: String) {
|
||||
val layoutPaths = File(path).listFiles { it -> it.name.startsWith("layout") && it.isDirectory }!!.map { "$path${it.name}/" }
|
||||
myEnvironment = createTestEnvironment(configuration, layoutPaths)
|
||||
}
|
||||
|
||||
fun doCompileAgainstAndroidSdkTest(path: String) {
|
||||
createAndroidAPIEnvironment(path)
|
||||
doMultiFileTest(path)
|
||||
}
|
||||
|
||||
fun doFakeInvocationTest(path: String) {
|
||||
if (needsInvocationTest(path)) {
|
||||
createFakeAndroidEnvironment(path)
|
||||
doMultiFileTest(path, getFakeFiles(path))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getClassPathURLs(): Array<URL> {
|
||||
return myEnvironment.configuration.jvmClasspathRoots.map { it.toURI().toURL() }.toTypedArray()
|
||||
}
|
||||
|
||||
private fun getFakeFiles(path: String): Collection<String> {
|
||||
return FileUtil.findFilesByMask(Pattern.compile("^Fake.*\\.kt$"), File(path.replace(getTestName(true), ""))).map { relativePath(it) }
|
||||
}
|
||||
|
||||
private fun needsInvocationTest(path: String): Boolean {
|
||||
return !FileUtil.findFilesByMask(Pattern.compile("^0.kt$"), File(path)).isEmpty()
|
||||
}
|
||||
|
||||
override fun codegenTestBasePath(): String {
|
||||
return "plugins/android-extensions/android-extensions-compiler/testData/codegen/"
|
||||
}
|
||||
|
||||
private fun doMultiFileTest(path: String, additionalFiles: Collection<String>? = null) {
|
||||
val files = mutableListOf<String>()
|
||||
FileUtil.processFilesRecursively(File(path), object : Processor<File> {
|
||||
override fun process(file: File?): Boolean {
|
||||
when (file!!.name) {
|
||||
"1.kt" -> {
|
||||
if (additionalFiles == null) files.add(relativePath(file))
|
||||
}
|
||||
"0.kt" -> {
|
||||
if (additionalFiles != null) files.add(relativePath(file))
|
||||
}
|
||||
else -> {
|
||||
if (file.name.endsWith(".kt")) files.add(relativePath(file))
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
for (file in File("plugins/android-extensions/android-extensions-runtime/src").walk()) {
|
||||
if (file.extension == "kt") files += relativePath(file.absoluteFile)
|
||||
}
|
||||
|
||||
Collections.sort(files)
|
||||
if (additionalFiles != null) {
|
||||
files.addAll(additionalFiles)
|
||||
}
|
||||
myFiles = CodegenTestFiles.create(
|
||||
myEnvironment!!.project,
|
||||
ArrayUtil.toStringArray(files),
|
||||
KotlinTestUtils.getHomeDirectory() + "/plugins/android-extensions/android-extensions-compiler/testData"
|
||||
)
|
||||
blackBox()
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.android.synthetic.test
|
||||
|
||||
import org.jetbrains.kotlin.codegen.AbstractBytecodeTextTest
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.TestJdkKind
|
||||
|
||||
abstract class AbstractAndroidBytecodeShapeTest : AbstractBytecodeTextTest() {
|
||||
private fun createAndroidAPIEnvironment(path: String) {
|
||||
return createEnvironmentForConfiguration(KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.ANDROID_API), path)
|
||||
}
|
||||
|
||||
private fun createEnvironmentForConfiguration(configuration: CompilerConfiguration, path: String) {
|
||||
val layoutPaths = getResPaths(path)
|
||||
myEnvironment = createTestEnvironment(configuration, layoutPaths)
|
||||
addAndroidExtensionsRuntimeLibrary(myEnvironment)
|
||||
}
|
||||
|
||||
override fun doTest(path: String) {
|
||||
val fileName = path + getTestName(true) + ".kt"
|
||||
createAndroidAPIEnvironment(path)
|
||||
loadFileByFullPath(fileName)
|
||||
val expected = readExpectedOccurrences(fileName)
|
||||
val actual = generateToText()
|
||||
checkGeneratedTextAgainstExpectedOccurrences(actual, expected)
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.android.synthetic.test
|
||||
|
||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidPackageFragmentProviderExtension
|
||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticPackageFragmentProvider
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.MemberComparator
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
|
||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.TestJdkKind
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractAndroidSyntheticPropertyDescriptorTest : KtUsefulTestCase() {
|
||||
fun doTest(path: String) {
|
||||
val config = KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.ANDROID_API)
|
||||
val env = createTestEnvironment(config, getResPaths(path))
|
||||
val project = env.project
|
||||
|
||||
val ext = PackageFragmentProviderExtension.getInstances(project).first { it is AndroidPackageFragmentProviderExtension }
|
||||
|
||||
val analysisResult = JvmResolveUtil.analyzeAndCheckForErrors(listOf(), env)
|
||||
|
||||
val fragmentProvider = ext.getPackageFragmentProvider(project, analysisResult.moduleDescriptor, LockBasedStorageManager.NO_LOCKS,
|
||||
KotlinTestUtils.DUMMY_EXCEPTION_ON_ERROR_TRACE, null) as AndroidSyntheticPackageFragmentProvider
|
||||
|
||||
val renderer = DescriptorRenderer.COMPACT_WITH_MODIFIERS
|
||||
val expected = fragmentProvider.packageFragments.sortedBy { it.fqName.asString() }.map {
|
||||
val descriptors = it.getMemberScope().getContributedDescriptors()
|
||||
.sortedWith(MemberComparator.INSTANCE)
|
||||
.map { " " + renderer.render(it) }.joinToString("\n")
|
||||
it.fqName.asString() + (if (descriptors.isNotEmpty()) "\n\n" + descriptors else "")
|
||||
}.joinToString("\n\n\n")
|
||||
|
||||
KotlinTestUtils.assertEqualsToFile(File(path, "result.txt"), expected)
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.android.synthetic.test
|
||||
|
||||
import com.intellij.testFramework.registerServiceInstance
|
||||
import kotlinx.android.extensions.CacheImplementation
|
||||
import org.jetbrains.kotlin.android.synthetic.AndroidConfigurationKeys
|
||||
import org.jetbrains.kotlin.android.synthetic.AndroidExtensionPropertiesComponentContainerContributor
|
||||
import org.jetbrains.kotlin.android.synthetic.codegen.CliAndroidExtensionsExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.android.synthetic.codegen.CliAndroidOnDestroyClassBuilderInterceptorExtension
|
||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
|
||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidVariant
|
||||
import org.jetbrains.kotlin.android.synthetic.res.CliAndroidLayoutXmlFileManager
|
||||
import org.jetbrains.kotlin.android.synthetic.res.CliAndroidPackageFragmentProviderExtension
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
||||
import org.jetbrains.kotlin.utils.KotlinPaths
|
||||
import org.jetbrains.kotlin.utils.KotlinPathsFromHomeDir
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import java.io.File
|
||||
|
||||
fun KtUsefulTestCase.createTestEnvironment(configuration: CompilerConfiguration, resDirectories: List<String>): KotlinCoreEnvironment {
|
||||
configuration.put(AndroidConfigurationKeys.VARIANT, resDirectories)
|
||||
configuration.put(AndroidConfigurationKeys.PACKAGE, "test")
|
||||
|
||||
val myEnvironment = KotlinCoreEnvironment.createForTests(testRootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
|
||||
val project = myEnvironment.project
|
||||
|
||||
val variants = listOf(AndroidVariant.createMainVariant(resDirectories))
|
||||
project.registerServiceInstance(AndroidLayoutXmlFileManager::class.java, CliAndroidLayoutXmlFileManager(project, "test", variants))
|
||||
|
||||
ExpressionCodegenExtension.registerExtension(project, CliAndroidExtensionsExpressionCodegenExtension(true, CacheImplementation.DEFAULT))
|
||||
StorageComponentContainerContributor.registerExtension(project, AndroidExtensionPropertiesComponentContainerContributor())
|
||||
ClassBuilderInterceptorExtension.registerExtension(project, CliAndroidOnDestroyClassBuilderInterceptorExtension(CacheImplementation.DEFAULT))
|
||||
PackageFragmentProviderExtension.registerExtension(project, CliAndroidPackageFragmentProviderExtension(true))
|
||||
|
||||
addAndroidExtensionsRuntimeLibrary(myEnvironment)
|
||||
|
||||
return myEnvironment
|
||||
}
|
||||
|
||||
fun addAndroidExtensionsRuntimeLibrary(environment: KotlinCoreEnvironment) {
|
||||
environment.apply {
|
||||
val runtimeLibrary = File(PathUtil.kotlinPathsForCompiler.libPath, "android-extensions-compiler.jar")
|
||||
updateClasspath(listOf(JvmClasspathRoot(runtimeLibrary)))
|
||||
}
|
||||
}
|
||||
|
||||
fun getResPaths(path: String): List<String> {
|
||||
return File(path).listFiles { it -> it.name.startsWith("res") && it.isDirectory }!!.map { "$path${it.name}/" }
|
||||
}
|
||||
Reference in New Issue
Block a user