Android tests: add reflect flavor
This commit is contained in:
@@ -62,15 +62,19 @@ android {
|
||||
flavorDimensions "box"
|
||||
|
||||
productFlavors {
|
||||
ktest0 {
|
||||
common0 {
|
||||
dimension "box"
|
||||
}
|
||||
|
||||
ktest1 {
|
||||
common1 {
|
||||
dimension "box"
|
||||
}
|
||||
|
||||
ktest2 {
|
||||
common2 {
|
||||
dimension "box"
|
||||
}
|
||||
|
||||
reflect0 {
|
||||
dimension "box"
|
||||
}
|
||||
}
|
||||
@@ -80,9 +84,9 @@ android {
|
||||
task jarTestFolders() {
|
||||
println "Jar folders..."
|
||||
new File("${projectDir}/libs/").listFiles().each { File file ->
|
||||
if (file.isDirectory() && !file.name.equals("test")) {
|
||||
if (file.isDirectory()) {
|
||||
println "Jar '${file.name}' folder..."
|
||||
ant.jar(basedir: "libs/${file.name}/", destfile: "libs/test/" + file.name + ".jar")
|
||||
ant.jar(basedir: "libs/${file.name}/", destfile: "libs/" + file.name + ".jar")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,11 +96,18 @@ tasks.withType(JavaCompile) {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation fileTree(dir: 'libs', include: ['kotlin-test.jar', 'kotlin-stdlib.jar'])
|
||||
androidTestImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||
|
||||
ktest0Implementation fileTree(dir: 'libs/test', include: ['libtest0.jar'])
|
||||
ktest1Implementation fileTree(dir: 'libs/test', include: ['libtest1.jar'])
|
||||
ktest2Implementation fileTree(dir: 'libs/test', include: ['libtest2.jar'])
|
||||
android.applicationVariants.all { variant ->
|
||||
variant.productFlavors.each {
|
||||
def configuration = configurations.getByName(it.name + 'Implementation').name
|
||||
add(configuration, project.fileTree(dir: 'libs', include: [it.name + ".jar"]))
|
||||
|
||||
if (it.name.startsWith("reflect")) {
|
||||
add(configuration, project.fileTree(dir: 'libs', include: ['kotlin-reflect.jar']))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,8 +52,8 @@ public class PathManager {
|
||||
return getAndroidSdkRoot() + "/emulator";
|
||||
}
|
||||
|
||||
public String getOutputForCompiledFiles(int index) {
|
||||
return tmpFolder + "/libs/libtest" + index;
|
||||
public String getOutputForCompiledFiles(String flavor) {
|
||||
return tmpFolder + "/libs/" + flavor;
|
||||
}
|
||||
|
||||
public String getLibsFolderInAndroidTmpFolder() {
|
||||
|
||||
+80
-39
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.codegen.GenerationUtils
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.*
|
||||
@@ -31,22 +32,43 @@ import kotlin.test.assertTrue
|
||||
data class ConfigurationKey(val kind: ConfigurationKind, val jdkKind: TestJdkKind, val configuration: String)
|
||||
|
||||
class CodegenTestsOnAndroidGenerator private constructor(private val pathManager: PathManager) {
|
||||
private var writtenFilesCount = 0
|
||||
|
||||
private var currentModuleIndex = 1
|
||||
|
||||
private val pathFilter: String? = System.getProperties().getProperty("kotlin.test.android.path.filter")
|
||||
|
||||
private val pendingUnitTestGenerators = hashMapOf<Int, UnitTestFileWriter>()
|
||||
private val pendingUnitTestGenerators = hashMapOf<String, UnitTestFileWriter>()
|
||||
|
||||
//keep it globally to avoid test grouping on TC
|
||||
private val generatedTestNames = hashSetOf<String>()
|
||||
|
||||
fun getFlavorUnitTestFilePath(index: Int): String {
|
||||
return pathManager.srcFolderInAndroidTmpFolder + "/androidTestKtest$index/java/" + testClassPackage.replace(
|
||||
".",
|
||||
"/"
|
||||
) + "/" + testClassName + "$index.java"
|
||||
private val COMMON = FlavorConfig("common", 3);
|
||||
private val REFLECT = FlavorConfig("reflect", 1);
|
||||
private val JVM8 = FlavorConfig("jvm8", 1);
|
||||
private val JVM8REFLECT = FlavorConfig("reflectjvm8", 1);
|
||||
|
||||
class FlavorConfig(private val prefix: String, val limit: Int) {
|
||||
|
||||
private var writtenFilesCount = 0
|
||||
|
||||
fun printStatistics() {
|
||||
println("FlavorTestCompiler: $prefix, generated file count: $writtenFilesCount")
|
||||
}
|
||||
|
||||
fun getFlavorForNewFiles(newFilesCount: Int): String {
|
||||
writtenFilesCount += newFilesCount
|
||||
//2500 files per folder that would be used by flavor to avoid multidex usage,
|
||||
// each folder would be jared by build.gradle script
|
||||
val index = writtenFilesCount / 2500
|
||||
|
||||
return getFlavorName(index, prefix).also {
|
||||
assertTrue("Please Add new flavor in build.gradle for $it") { index < limit }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFlavorName(index: Int, prefix: String): String {
|
||||
return prefix + index
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun prepareAndroidModuleAndGenerateTests(skipSdkDirWriting: Boolean) {
|
||||
@@ -126,9 +148,15 @@ class CodegenTestsOnAndroidGenerator private constructor(private val pathManager
|
||||
holders.values.forEach {
|
||||
it.writeFilesOnDisk()
|
||||
}
|
||||
|
||||
COMMON.printStatistics()
|
||||
REFLECT.printStatistics()
|
||||
JVM8.printStatistics()
|
||||
JVM8REFLECT.printStatistics()
|
||||
}
|
||||
|
||||
internal inner class FilesWriter(
|
||||
private val flavorConfig: FlavorConfig,
|
||||
private val configuration: CompilerConfiguration
|
||||
) {
|
||||
private val rawFiles = arrayListOf<TestClassInfo>()
|
||||
@@ -143,42 +171,40 @@ class CodegenTestsOnAndroidGenerator private constructor(private val pathManager
|
||||
}
|
||||
|
||||
fun writeFilesOnDisk() {
|
||||
//don't convert to sam (identity lose)
|
||||
@Suppress("ObjectLiteralToLambda")
|
||||
val disposable = object : Disposable {
|
||||
override fun dispose() {}
|
||||
}
|
||||
|
||||
val disposable = Disposer.newDisposable()
|
||||
val environment = KotlinCoreEnvironment.createForTests(
|
||||
disposable,
|
||||
configuration.copy().apply { put(CommonConfigurationKeys.MODULE_NAME, "android-module-" + currentModuleIndex++) },
|
||||
EnvironmentConfigFiles.JVM_CONFIG_FILES
|
||||
)
|
||||
|
||||
writeFiles(
|
||||
rawFiles.map {
|
||||
try {
|
||||
CodegenTestFiles.create(it.name, it.content, environment.project).psiFile
|
||||
} catch (e: Throwable) {
|
||||
throw RuntimeException("Error on processing ${it.name}:\n${it.content}", e)
|
||||
}
|
||||
}, environment
|
||||
)
|
||||
Disposer.dispose(disposable)
|
||||
rawFiles.clear()
|
||||
unitTestDescriptions.clear()
|
||||
try {
|
||||
writeFiles(
|
||||
rawFiles.map {
|
||||
try {
|
||||
CodegenTestFiles.create(it.name, it.content, environment.project).psiFile
|
||||
} catch (e: Throwable) {
|
||||
throw RuntimeException("Error on processing ${it.name}:\n${it.content}", e)
|
||||
}
|
||||
}, environment, unitTestDescriptions
|
||||
)
|
||||
} finally {
|
||||
rawFiles.clear()
|
||||
unitTestDescriptions.clear()
|
||||
Disposer.dispose(disposable)
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeFiles(filesToCompile: List<KtFile>, environment: KotlinCoreEnvironment) {
|
||||
private fun writeFiles(
|
||||
filesToCompile: List<KtFile>,
|
||||
environment: KotlinCoreEnvironment,
|
||||
unitTestDescriptions: ArrayList<TestInfo>
|
||||
) {
|
||||
if (filesToCompile.isEmpty()) return
|
||||
|
||||
//2500 files per folder that would be used by flavor to avoid multidex usage,
|
||||
// each folder would be jared by build.gradle script
|
||||
writtenFilesCount += filesToCompile.size
|
||||
val index = writtenFilesCount / 2500
|
||||
val outputDir = File(pathManager.getOutputForCompiledFiles(index))
|
||||
assertTrue("Add flavors for ktest$index") { index < 3 }
|
||||
val flavorName = flavorConfig.getFlavorForNewFiles(filesToCompile.size)
|
||||
|
||||
val outputDir = File(pathManager.getOutputForCompiledFiles(flavorName))
|
||||
println("Generating ${filesToCompile.size} files into ${outputDir.name}, configuration: '${environment.configuration}'...")
|
||||
|
||||
val outputFiles = GenerationUtils.compileFiles(filesToCompile, environment).run { destroy(); factory }
|
||||
@@ -187,10 +213,10 @@ class CodegenTestsOnAndroidGenerator private constructor(private val pathManager
|
||||
outputDir.mkdirs()
|
||||
}
|
||||
Assert.assertTrue("Cannot create directory for compiled files", outputDir.exists())
|
||||
val unitTestFileWriter = pendingUnitTestGenerators.getOrPut(index) {
|
||||
val unitTestFileWriter = pendingUnitTestGenerators.getOrPut(flavorName) {
|
||||
UnitTestFileWriter(
|
||||
getFlavorUnitTestFilePath(index),
|
||||
index,
|
||||
getFlavorUnitTestFolder(flavorName),
|
||||
flavorName,
|
||||
generatedTestNames
|
||||
)
|
||||
}
|
||||
@@ -198,6 +224,12 @@ class CodegenTestsOnAndroidGenerator private constructor(private val pathManager
|
||||
outputFiles.writeAllTo(outputDir)
|
||||
}
|
||||
|
||||
private fun getFlavorUnitTestFolder(flavourName: String): String {
|
||||
return pathManager.srcFolderInAndroidTmpFolder +
|
||||
"/androidTest${flavourName.capitalize()}/java/" +
|
||||
testClassPackage.replace(".", "/") + "/"
|
||||
}
|
||||
|
||||
fun addTest(testFiles: List<TestClassInfo>, info: TestInfo) {
|
||||
rawFiles.addAll(testFiles)
|
||||
unitTestDescriptions.add(info)
|
||||
@@ -245,8 +277,14 @@ class CodegenTestsOnAndroidGenerator private constructor(private val pathManager
|
||||
if (fullFileText.contains("@file:JvmPackageName(")) continue
|
||||
// TODO: Support jvm assertions
|
||||
if (fullFileText.contains("// KOTLIN_CONFIGURATION_FLAGS: ASSERTIONS_MODE=jvm")) continue
|
||||
// TODO: support JVM 8 test with D8
|
||||
if (fullFileText.contains("// JVM_TARGET")) continue
|
||||
val targets = InTextDirectivesUtils.findLinesWithPrefixesRemoved(fullFileText, "// JVM_TARGET:")
|
||||
.also { it.remove(JvmTarget.JVM_1_6.description) }
|
||||
|
||||
val isJvm8Target =
|
||||
if (targets.isEmpty()) false
|
||||
else if (targets.contains(JvmTarget.JVM_1_8.description) && targets.size == 1) true
|
||||
else continue //TODO: support other targets on Android
|
||||
|
||||
// TODO: support SKIP_JDK6 on new platforms
|
||||
if (fullFileText.contains("// SKIP_JDK6")) continue
|
||||
|
||||
@@ -258,8 +296,11 @@ class CodegenTestsOnAndroidGenerator private constructor(private val pathManager
|
||||
CodegenTestCase.updateConfigurationByDirectivesInTestFiles(testFiles, keyConfiguration)
|
||||
|
||||
val key = ConfigurationKey(kind, jdkKind, keyConfiguration.toString())
|
||||
val compiler = if (isJvm8Target) {
|
||||
if (kind.withReflection) JVM8REFLECT else JVM8
|
||||
} else if (kind.withReflection) REFLECT else COMMON
|
||||
val filesHolder = holders.getOrPut(key) {
|
||||
FilesWriter(KotlinTestUtils.newConfiguration(kind, jdkKind, KotlinTestUtils.getAnnotationsJar()).apply {
|
||||
FilesWriter(compiler, KotlinTestUtils.newConfiguration(kind, jdkKind, KotlinTestUtils.getAnnotationsJar()).apply {
|
||||
println("Creating new configuration by $key")
|
||||
CodegenTestCase.updateConfigurationByDirectivesInTestFiles(testFiles, this)
|
||||
})
|
||||
|
||||
@@ -40,13 +40,6 @@ public class SpecialFiles {
|
||||
excludedFiles.add("nativePropertyAccessors.kt");
|
||||
excludedFiles.add("topLevel.kt");
|
||||
|
||||
//Test with no reflection at runtime
|
||||
excludedFiles.add("noReflectAtRuntime");
|
||||
excludedFiles.add("noReflect");
|
||||
excludedFiles.add("functionNtoStringNoReflect.kt");
|
||||
excludedFiles.add("getDelegateWithoutReflection.kt");
|
||||
excludedFiles.add("toStringNoReflect.kt");
|
||||
|
||||
// "IOOBE: Invalid index 4, size is 4" for java.lang.reflect.ParameterizedType on Android
|
||||
excludedFiles.add("innerGenericTypeArgument.kt");
|
||||
|
||||
|
||||
+7
-3
@@ -15,7 +15,11 @@ import java.io.FileWriter
|
||||
|
||||
class TestInfo(val name: String, val fqName: FqName, val file: File)
|
||||
|
||||
class UnitTestFileWriter(private val fileName: String, private val index: Int, private val generatedTestNames: MutableSet<String>) {
|
||||
class UnitTestFileWriter(
|
||||
private val flavourFolder: String,
|
||||
private val flavourName: String,
|
||||
private val generatedTestNames: MutableSet<String>
|
||||
) {
|
||||
private val infos = arrayListOf<TestInfo>()
|
||||
|
||||
fun addTests(info: List<TestInfo>) {
|
||||
@@ -23,7 +27,7 @@ class UnitTestFileWriter(private val fileName: String, private val index: Int, p
|
||||
}
|
||||
|
||||
fun generate() {
|
||||
FileWriter(File(fileName).also { it.parentFile.mkdirs() }).use { suite ->
|
||||
FileWriter(File(flavourFolder, flavourName.capitalize() + ".java").also { it.parentFile.mkdirs() }).use { suite ->
|
||||
val p = Printer(suite)
|
||||
p.println(
|
||||
"""package ${CodegenTestsOnAndroidGenerator.testClassPackage};
|
||||
@@ -31,7 +35,7 @@ class UnitTestFileWriter(private val fileName: String, private val index: Int, p
|
||||
|import ${CodegenTestsOnAndroidGenerator.baseTestClassPackage}.${CodegenTestsOnAndroidGenerator.baseTestClassName};
|
||||
|
|
||||
|/* This class is generated by ${CodegenTestsOnAndroidGenerator.generatorName}. DO NOT MODIFY MANUALLY */
|
||||
|public class ${CodegenTestsOnAndroidGenerator.testClassName}$index extends ${CodegenTestsOnAndroidGenerator.baseTestClassName} {
|
||||
|public class ${flavourName.capitalize()} extends ${CodegenTestsOnAndroidGenerator.baseTestClassName} {
|
||||
|
|
||||
""".trimMargin()
|
||||
)
|
||||
|
||||
+5
-4
@@ -1,6 +1,7 @@
|
||||
// TARGET_BACKEND: JVM
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
package test
|
||||
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@@ -18,10 +19,10 @@ fun defaultAndVararg(f: (A) -> CharSequence): Any = f
|
||||
fun allOfTheAbove(f: (A) -> Unit): Any = f
|
||||
|
||||
fun box(): String {
|
||||
check("Function3<A, java.lang.String, long[], kotlin.Unit>", coercionToUnit(A::foo))
|
||||
check("Function4<A, java.lang.String, java.lang.Long, java.lang.Long, java.lang.CharSequence>", varargToElement(A::foo))
|
||||
check("Function1<A, java.lang.CharSequence>", defaultAndVararg(A::foo))
|
||||
check("Function1<A, kotlin.Unit>", allOfTheAbove(A::foo))
|
||||
check("Function3<test.A, java.lang.String, long[], kotlin.Unit>", coercionToUnit(A::foo))
|
||||
check("Function4<test.A, java.lang.String, java.lang.Long, java.lang.Long, java.lang.CharSequence>", varargToElement(A::foo))
|
||||
check("Function1<test.A, java.lang.CharSequence>", defaultAndVararg(A::foo))
|
||||
check("Function1<test.A, kotlin.Unit>", allOfTheAbove(A::foo))
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user