diff --git a/compiler/android-tests/android-module/build.gradle b/compiler/android-tests/android-module/build.gradle index b73a4573830..f3a75c5e2f4 100644 --- a/compiler/android-tests/android-module/build.gradle +++ b/compiler/android-tests/android-module/build.gradle @@ -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'])) + } + } + } } diff --git a/compiler/android-tests/src/org/jetbrains/kotlin/android/tests/PathManager.java b/compiler/android-tests/src/org/jetbrains/kotlin/android/tests/PathManager.java index 014280b8b39..4004b564612 100644 --- a/compiler/android-tests/src/org/jetbrains/kotlin/android/tests/PathManager.java +++ b/compiler/android-tests/src/org/jetbrains/kotlin/android/tests/PathManager.java @@ -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() { diff --git a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/CodegenTestsOnAndroidGenerator.kt b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/CodegenTestsOnAndroidGenerator.kt index 1514df3d386..5c13cc4b940 100644 --- a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/CodegenTestsOnAndroidGenerator.kt +++ b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/CodegenTestsOnAndroidGenerator.kt @@ -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() + private val pendingUnitTestGenerators = hashMapOf() //keep it globally to avoid test grouping on TC private val generatedTestNames = hashSetOf() - 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() @@ -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, environment: KotlinCoreEnvironment) { + private fun writeFiles( + filesToCompile: List, + environment: KotlinCoreEnvironment, + unitTestDescriptions: ArrayList + ) { 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, 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) }) diff --git a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/SpecialFiles.java b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/SpecialFiles.java index 2fed036e25c..3487e1d339a 100644 --- a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/SpecialFiles.java +++ b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/SpecialFiles.java @@ -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"); diff --git a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/UnitTestFileWriter.kt b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/UnitTestFileWriter.kt index fa1581963ff..e30dd1adc31 100644 --- a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/UnitTestFileWriter.kt +++ b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/UnitTestFileWriter.kt @@ -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) { +class UnitTestFileWriter( + private val flavourFolder: String, + private val flavourName: String, + private val generatedTestNames: MutableSet +) { private val infos = arrayListOf() fun addTests(info: List) { @@ -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() ) diff --git a/compiler/testData/codegen/box/callableReference/adaptedReferences/toStringNoReflect.kt b/compiler/testData/codegen/box/callableReference/adaptedReferences/toStringNoReflect.kt index 44c510de3b9..364e67296f6 100644 --- a/compiler/testData/codegen/box/callableReference/adaptedReferences/toStringNoReflect.kt +++ b/compiler/testData/codegen/box/callableReference/adaptedReferences/toStringNoReflect.kt @@ -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", coercionToUnit(A::foo)) - check("Function4", varargToElement(A::foo)) - check("Function1", defaultAndVararg(A::foo)) - check("Function1", allOfTheAbove(A::foo)) + check("Function3", coercionToUnit(A::foo)) + check("Function4", varargToElement(A::foo)) + check("Function1", defaultAndVararg(A::foo)) + check("Function1", allOfTheAbove(A::foo)) return "OK" }