Make Android codegen tests run as integration tests

This commit refactors the compiler/android-tests module,
streamlining its functionality to solely focus on generating the
Android project for compiler tests.
The tests are now executed as Gradle integration tests,
eliminating manual management of the emulator.
Instead, Gradle-managed Android virtual devices are utilized.
Some refactorings of the test project are done.
Necessary SDK tools versions are updated in dependencies/android-sdk,
with the emulator now being auto-downloaded by Android Gradle Tools.

^KT-66761 Fixed
diff --git a/compiler/android-tests/ReadMe.md b/compiler/android-tests/ReadMe.md
index 8aec6ad..5e1465c 100644
--- a/compiler/android-tests/ReadMe.md
+++ b/compiler/android-tests/ReadMe.md
@@ -1,15 +1,21 @@
-# Codegen tests on Android
+# Codegen Tests on Android
 
-This module runs codegen box tests (`compiler/testData/codegen/box`) on Android. It does so by compiling all of tests,
-except the excluded ones, in one big Android project and running it as an app on an emulator, which is downloaded during
-the first run of the tests. See which tests are excluded in `CodegenTestsOnAndroidGenerator`, but mainly those are the
-ones annotated with `// IGNORE_BACKEND: ANDROID`, those having Java source files, or using advanced Kotlin/JVM features.
+The module prepares codegen tests for running
+(`compiler/testData/codegen/box` and `compiler/testData/codegen/boxInline` directories) on an Android platform.
+It achieves this by compiling all tests, except those explicitly excluded, into a single Android project.
+The tests are then executed on an Android Virtual Device, leveraging the Kotlin Gradle Plugin integration tests
+(`libraries/tools/kotlin-gradle-plugin-integration-tests`).
+This testing environment is well-suited for building and running external Gradle projects.
+Exclusions in the `CodegenTestsOnAndroidGenerator` typically include tests marked with `// IGNORE_BACKEND: ANDROID`,
+tests that contain Java source files, or tests that use JVM features not supported by the Android Runtime.
 
-Run the tests via Gradle:
-
+To generate the project with tests, execute:
 ```
-./gradlew :compiler:android-tests:test
+./gradlew :compiler:android-tests:generateAndroidTests
 ```
 
-**Make sure your JAVA_HOME points to a JDK 1.8 installation**, otherwise, you'll get an exception, such as
-`java.lang.ClassNotFoundException: javax.xml.bind.annotation.XmlSchema`.
+Run the according Gradle integration tests by executing:
+
+```
+./gradlew :kotlin-gradle-plugin-integration-tests:kgpAndroidCodegenTests
+```
diff --git a/compiler/android-tests/android-module/build.gradle b/compiler/android-tests/android-module/build.gradle
deleted file mode 100644
index 2c8077d..0000000
--- a/compiler/android-tests/android-module/build.gradle
+++ /dev/null
@@ -1,112 +0,0 @@
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
-buildscript {
-    repositories {
-        google()
-        jcenter()
-    }
-    dependencies {        
-        classpath 'com.android.tools.build:gradle:4.1.2'
-    }
-}
-apply plugin: 'com.android.application'
-
-repositories {
-    google()
-    jcenter()
-}
-
-android {
-    compileSdkVersion 26
-    buildToolsVersion "29.0.3"
-
-    defaultConfig {
-        applicationId "org.jetbrains.kotlin.android.tests"
-        minSdkVersion 19
-        targetSdkVersion 19
-        versionCode 1
-        versionName "1.0"
-        testApplicationId "org.jetbrains.kotlin.android.tests.gradle"
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
-    }
-    buildTypes {
-        debug {
-            minifyEnabled false
-            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
-        }
-    }
-
-    packagingOptions { exclude 'META-INF/build.txt' }
-
-
-    dexOptions {
-        dexInProcess false
-        javaMaxHeapSize "1500m"
-        maxProcessCount 4
-        additionalParameters "--debug"
-    }
-
-    testOptions {
-        resultsDir = "build/test/results"
-    }
-
-    compileOptions {
-        sourceCompatibility = 1.8
-        targetCompatibility = 1.8
-    }
-
-    flavorDimensions "box"
-
-    productFlavors {
-        common_ir0 {
-            dimension "box"
-        }
-
-        common_ir1 {
-            dimension "box"
-        }
-
-        common_ir2 {
-            dimension "box"
-        }
-
-        common_ir3 {
-            dimension "box"
-        }
-
-        reflect_ir0 {
-            dimension "box"
-        }
-    }
-
-}
-
-task jarTestFolders() {
-    println "Jar folders..."
-    new File("${projectDir}/libs/").listFiles().each { File file ->
-        if (file.isDirectory()) {
-            println "Jar '${file.name}' folder..."
-            ant.jar(basedir: "libs/${file.name}/", destfile: "libs/" +  file.name  + ".jar")
-        }
-    }
-}
-
-tasks.withType(JavaCompile) {
-    compileTask -> compileTask.dependsOn jarTestFolders
-}
-
-dependencies {
-    implementation fileTree(dir: 'libs', include: ['kotlin-test.jar', 'kotlin-stdlib.jar'])
-    androidTestImplementation 'junit:junit:4.13.2'
-    androidTestImplementation 'com.android.support.test:runner:1.0.2'
-
-    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/android-module/gradle.properties b/compiler/android-tests/android-module/gradle.properties
deleted file mode 100644
index 2d32c80..0000000
--- a/compiler/android-tests/android-module/gradle.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-#don't try to download android specific tools within gradle: licence acceptance will be required
-android.builder.sdkDownload=false
\ No newline at end of file
diff --git a/compiler/android-tests/android-module/local.properties b/compiler/android-tests/android-module/local.properties
deleted file mode 100644
index db64df6..0000000
--- a/compiler/android-tests/android-module/local.properties
+++ /dev/null
@@ -1,12 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must *NOT* be checked into Version Control Systems,
-# as it contains information specific to your local configuration.
-
-# location of the SDK. This is only used by Ant
-# For customization when using a Version Control System, please read the
-# header note.
-#sdk.dir=compiler/android-tests/android-module/android-sdk/android-sdk-windows
-sdk.dir=../../../dependencies/android.tests.dependencies/android-sdk
-
diff --git a/compiler/android-tests/android-module/proguard-project.txt b/compiler/android-tests/android-module/proguard-project.txt
deleted file mode 100644
index f2fe155..0000000
--- a/compiler/android-tests/android-module/proguard-project.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-# To enable ProGuard in your project, edit project.properties
-# to define the proguard.config property as described in that file.
-#
-# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in ${sdk.dir}/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the ProGuard
-# include property in project.properties.
-#
-# For more details, see
-#   http://developer.android.com/guide/developing/tools/proguard.html
-
-# Add any project specific keep options here:
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-#   public *;
-#}
diff --git a/compiler/android-tests/android-module/src/main/AndroidManifest.xml b/compiler/android-tests/android-module/src/main/AndroidManifest.xml
deleted file mode 100644
index 8b7f6ac..0000000
--- a/compiler/android-tests/android-module/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="org.jetbrains.kotlin.android.tests"
-          android:versionCode="1"
-          android:versionName="1.0">
-  <application>
-    <uses-library android:name="android.test.runner" />
-  </application>
-</manifest>
diff --git a/compiler/android-tests/android-module/src/main/java/org/jetbrains/kotlin/android/tests/MyActivity.java b/compiler/android-tests/android-module/src/main/java/org/jetbrains/kotlin/android/tests/MyActivity.java
deleted file mode 100644
index 277753e..0000000
--- a/compiler/android-tests/android-module/src/main/java/org/jetbrains/kotlin/android/tests/MyActivity.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2010-2015 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.tests;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class MyActivity extends Activity {
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        System.out.println(R.string.app_name);
-    }
-}
diff --git a/compiler/android-tests/android-module/src/main/res/values/strings.xml b/compiler/android-tests/android-module/src/main/res/values/strings.xml
deleted file mode 100644
index ab73ea8..0000000
--- a/compiler/android-tests/android-module/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <string name="app_name">android1</string>
-</resources>
diff --git a/compiler/android-tests/build.gradle.kts b/compiler/android-tests/build.gradle.kts
index d5f5f5f..10a8463 100644
--- a/compiler/android-tests/build.gradle.kts
+++ b/compiler/android-tests/build.gradle.kts
@@ -1,5 +1,3 @@
-import org.jetbrains.kotlin.build.androidsdkprovisioner.ProvisioningType
-
 plugins {
     kotlin("jvm")
     id("jps-compatible")
@@ -7,29 +5,17 @@
 }
 
 dependencies {
-    testApi(project(":core:descriptors"))
-    testApi(project(":core:descriptors.jvm"))
-    testApi(project(":compiler:util"))
-    testApi(project(":compiler:cli"))
-    testApi(project(":compiler:frontend"))
-    testApi(project(":compiler:backend"))
-    testApi(project(":compiler:incremental-compilation-impl"))
-    testApi(project(":compiler:frontend.java"))
-
     testApi(kotlinStdlib())
-    testApi(projectTests(":compiler:tests-common"))
-    testImplementation(libs.junit4)
+    testApi(intellijCore())
+    testApi(project(":core:compiler.common"))
+    testApi(project(":compiler:config"))
+    testApi(project(":compiler:cli"))
+    testApi(project(":compiler:frontend.common.jvm"))
+    testApi(projectTests(":compiler:tests-compiler-utils"))
     testApi(projectTests(":compiler:test-infrastructure"))
     testApi(projectTests(":compiler:test-infrastructure-utils"))
-    testApi(projectTests(":compiler:tests-compiler-utils"))
     testApi(projectTests(":compiler:tests-common-new"))
-
-    testApi(jpsModel())
-
-    testRuntimeOnly(intellijCore())
-    testRuntimeOnly(commonDependency("org.jetbrains.intellij.deps.jna:jna"))
-
-    testImplementation(libs.junit.platform.launcher)
+    testApi(projectTests(":generators:test-generator"))
 }
 
 sourceSets {
@@ -37,29 +23,19 @@
     "test" { projectDefault() }
 }
 
-projectTest {
-    dependsOn(":dist")
-    val jdkHome = project.getToolchainJdkHomeFor(JdkMajorVersion.JDK_1_8)
-    doFirst {
-        environment("kotlin.tests.android.timeout", "45")
-        environment("JAVA_HOME", jdkHome.get())
-    }
-
-    if (project.hasProperty("teamcity") || project.hasProperty("kotlin.test.android.teamcity")) {
-        systemProperty("kotlin.test.android.teamcity", true)
-    }
-
-    project.findProperty("kotlin.test.android.path.filter")?.let {
-        systemProperty("kotlin.test.android.path.filter", it.toString())
-    }
-
-    workingDir = rootDir
-    androidSdkProvisioner {
-        provideToThisTaskAsSystemProperty(ProvisioningType.SDK_WITH_EMULATOR)
-    }
-}
-
 val generateAndroidTests by generator("org.jetbrains.kotlin.android.tests.CodegenTestsOnAndroidGenerator") {
     workingDir = rootDir
-    dependsOn(rootProject.tasks.named("dist"))
+
+    val destinationDirectory =
+        rootProject.file("libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests").absolutePath
+
+    val testDataDirectories = arrayOf(
+        rootProject.file("compiler/testData/codegen/box").absolutePath,
+        rootProject.file("compiler/testData/codegen/boxInline").absolutePath,
+    )
+
+    args(destinationDirectory, *testDataDirectories)
+    dependsOn(":dist", ":createIdeaHomeForTests")
+    systemProperty("idea.home.path", ideaHomePathForTests().get().asFile.canonicalPath)
+    systemProperty("idea.use.native.fs.for.win", false)
 }
diff --git a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/AndroidRunner.java b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/AndroidRunner.java
deleted file mode 100644
index 3d4b9c6..0000000
--- a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/AndroidRunner.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2010-2015 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.tests;
-
-import com.google.common.base.StandardSystemProperty;
-import com.intellij.openapi.util.io.FileUtil;
-import junit.framework.TestSuite;
-import org.jetbrains.annotations.NotNull;
-import org.junit.runner.RunWith;
-import org.junit.runners.AllTests;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.Objects;
-
-@RunWith(AllTests.class)
-public class AndroidRunner {
-
-    private static PathManager pathManager;
-
-    @NotNull
-    public static PathManager getPathManager() throws IOException {
-        if (pathManager == null) {
-            File tmpFolder =
-                    Files.createTempDirectory(Paths.get(Objects.requireNonNull(StandardSystemProperty.JAVA_IO_TMPDIR.value())), null)
-                            .toFile();
-            System.out.println("Created temporary folder for running android tests: " + tmpFolder.getAbsolutePath());
-            File rootFolder = new File("");
-            pathManager = new PathManager(rootFolder.getAbsolutePath(), tmpFolder.getAbsolutePath());
-        }
-        return pathManager;
-    }
-
-    public static TestSuite suite() throws Throwable {
-        PathManager pathManager = getPathManager();
-
-        CodegenTestsOnAndroidGenerator.generate(pathManager);
-
-        System.out.println("Run tests on Android...");
-        return CodegenTestsOnAndroidRunner.runTestsInEmulator(pathManager);
-    }
-
-    public void tearDown() throws Exception {
-        // Clear tmp folder where we run android tests
-        FileUtil.delete(new File(pathManager.getTmpFolder()));
-    }
-}
diff --git a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/AndroidTestGenerator.kt b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/AndroidTestGenerator.kt
index 53a1f17..d809d1c 100644
--- a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/AndroidTestGenerator.kt
+++ b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/AndroidTestGenerator.kt
@@ -16,7 +16,6 @@
 
 package org.jetbrains.kotlin.android.tests
 
-import com.intellij.openapi.util.Ref
 import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
 import org.jetbrains.kotlin.name.FqName
 import org.jetbrains.kotlin.name.Name
@@ -40,20 +39,21 @@
     services: TestServices,
     filesHolder: CodegenTestsOnAndroidGenerator.FilesWriter
 ): FqName {
-    val newPackagePrefix = testFile.path.replace("\\\\|-|\\.|/".toRegex(), "_")
-    val oldPackage = Ref<FqName>()
-    val isJvmName = Ref<Boolean>(false)
+    val newPackagePrefix = testFile.path.substringAfter("testData" + File.separator).replace("""[\\\-./]""".toRegex(), "_")
     val testFiles = module.files
     val isSingle = testFiles.size == 1
     val resultFiles = testFiles.map {
         val fileName = if (isSingle) it.name else testFile.name.substringBeforeLast(".kt") + "/" + it.name
-        val content = services.sourceFileProvider.getContentOfSourceFile(it)
+        val (textChangedPackage, oldPackage, isJvmName) = changePackage(
+            newPackagePrefix = newPackagePrefix,
+            text = services.sourceFileProvider.getContentOfSourceFile(it)
+        )
         TestClassInfo(
             fileName,
-            changePackage(newPackagePrefix, content, oldPackage, isJvmName),
-            oldPackage.get(),
-            isJvmName.get(),
-            getGeneratedClassName(File(fileName), content, newPackagePrefix, oldPackage.get())
+            textChangedPackage,
+            oldPackage,
+            isJvmName,
+            getGeneratedClassName(File(fileName), textChangedPackage, newPackagePrefix, oldPackage)
         )
     }
     val packages =
@@ -111,7 +111,7 @@
         }
     }
 
-    val boxFiles = resultFiles.filter { hasBoxMethod(it.content) }
+    val boxFiles = resultFiles.filter { it.content.contains("fun box()") }
     if (boxFiles.size != 1) {
         println("Several box methods in $testFile")
     }
@@ -124,31 +124,25 @@
     return boxFiles.last().newPackagePartClassId
 }
 
-private fun hasBoxMethod(text: String): Boolean {
-    return text.contains("fun box()")
-}
-
 class TestClassInfo(val name: String, var content: String, val oldPackage: FqName, val isJvmName: Boolean, val newPackagePartClassId: FqName) {
     val newPackage = newPackagePartClassId.parent()
 }
 
+private data class ChangePackageResult(val text: String, val oldPackage: FqName, val isJvmName: Boolean)
 
-private fun changePackage(newPackagePrefix: String, text: String, oldPackage: Ref<FqName>, isJvmName: Ref<Boolean>): String {
+private fun changePackage(newPackagePrefix: String, text: String): ChangePackageResult {
     val matcher = packagePattern.matcher(text)
     if (matcher.find()) {
         val oldPackageName = matcher.toMatchResult().group(1)
-        oldPackage.set(FqName(oldPackageName))
-        return matcher.replaceAll("package $newPackagePrefix.$oldPackageName")
+        return ChangePackageResult(matcher.replaceAll("package $newPackagePrefix.$oldPackageName"), FqName(oldPackageName), false)
     } else {
-        oldPackage.set(FqName.ROOT)
         val packageDirective = "package $newPackagePrefix;\n"
         if (text.contains("@file:")) {
             val index = text.lastIndexOf("@file:")
             val packageDirectiveIndex = text.indexOf("\n", index)
-            isJvmName.set(true)
-            return text.substring(0, packageDirectiveIndex + 1) + packageDirective + text.substring(packageDirectiveIndex + 1)
+            return ChangePackageResult(text.substring(0, packageDirectiveIndex + 1) + packageDirective + text.substring(packageDirectiveIndex + 1), FqName.ROOT, true)
         } else {
-            return packageDirective + text
+            return ChangePackageResult(packageDirective + text, FqName.ROOT, false)
         }
     }
 }
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 5487a20..1a490cf 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
@@ -6,7 +6,6 @@
 package org.jetbrains.kotlin.android.tests
 
 import com.intellij.openapi.util.Disposer
-import com.intellij.openapi.util.SystemInfo
 import com.intellij.openapi.util.io.FileUtil
 import com.intellij.openapi.util.io.FileUtilRt
 import org.jetbrains.kotlin.cli.common.output.writeAllTo
@@ -14,7 +13,6 @@
 import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
 import org.jetbrains.kotlin.codegen.CodegenTestFiles
 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
@@ -42,136 +40,68 @@
 import org.jetbrains.kotlin.test.utils.TransformersFunctions.Android
 import org.junit.Assert
 import java.io.File
-import java.io.FileWriter
 import java.io.IOException
-import kotlin.io.path.ExperimentalPathApi
-import kotlin.io.path.Path
-import kotlin.io.path.createTempDirectory
-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 currentModuleIndex = 1
 
     private val pathFilter: String? = System.getProperties().getProperty("kotlin.test.android.path.filter")
 
-    private val pendingUnitTestGenerators = hashMapOf<String, UnitTestFileWriter>()
+    private val pendingTestSourceFileGenerators = hashMapOf<String, TestSourceFileGenerator>()
 
     //keep it globally to avoid test grouping on TC
     private val generatedTestNames = hashSetOf<String>()
 
-    private val COMMON_IR = FlavorConfig(TargetBackend.ANDROID_IR, "common_ir", 4)
-    private val REFLECT_IR = FlavorConfig(TargetBackend.ANDROID_IR, "reflect_ir", 1)
+    private val common = FlavorConfig(TargetBackend.ANDROID, "common", 4)
+    private val reflect = FlavorConfig(TargetBackend.ANDROID, "reflect")
+    private val commonIr = FlavorConfig(TargetBackend.ANDROID_IR, "common_ir", 4)
+    private val reflectIr = FlavorConfig(TargetBackend.ANDROID_IR, "reflect_ir")
 
-    class FlavorConfig(private val backend: TargetBackend, private val prefix: String, val limit: Int) {
-
+    class FlavorConfig(private val backend: TargetBackend, private val prefix: String, private val limit: Int = 1) {
         private var writtenFilesCount = 0
 
         fun printStatistics() {
             println("FlavorTestCompiler for $backend: $prefix, generated file count: $writtenFilesCount")
         }
 
-        fun getFlavorForNewFiles(newFilesCount: Int): String {
+        fun getFlavorNameForNewFiles(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
+            // Allocating up to 2500 files per folder should be fine for each app flavor,
+            // thus avoiding the need for multidex, which is necessary when there are more than 64K methods.
+            // Each folder will be archived using build.gradle.kts.
             val index = writtenFilesCount / 2500
-
-            return getFlavorName(index, prefix).also {
-                assertTrue("Please Add  new flavor in build.gradle for $it") { index < limit }
-            }
+            val name = "$prefix$index"
+            check(index < limit) { "Please add a new flavor in build.gradle for $name" }
+            return name
         }
-
-        private fun getFlavorName(index: Int, prefix: String): String {
-            return prefix + index
-        }
-
-    }
-
-    private fun prepareAndroidModuleAndGenerateTests(skipSdkDirWriting: Boolean) {
-        prepareAndroidModule(skipSdkDirWriting)
-        generateTestsAndFlavourSuites()
-    }
-
-    private fun prepareAndroidModule(skipSdkDirWriting: Boolean) {
-        FileUtil.copyDir(File(pathManager.androidModuleRoot), File(pathManager.tmpFolder))
-        if (!skipSdkDirWriting) {
-            writeAndroidSkdToLocalProperties(pathManager)
-        }
-
-        println("Copying kotlin-stdlib.jar and kotlin-reflect.jar in android module...")
-        copyKotlinRuntimeJars()
-        copyGradleWrapperAndPatch()
-    }
-
-    private fun copyGradleWrapperAndPatch() {
-        val projectRoot = File(pathManager.tmpFolder)
-        val target = File(projectRoot, "gradle/wrapper")
-        File("./gradle/wrapper/").copyRecursively(target)
-        val gradlew = File(projectRoot, "gradlew")
-        File("./gradlew").copyTo(gradlew).also {
-            if (!SystemInfo.isWindows) {
-                it.setExecutable(true)
-            }
-        }
-        File("./gradlew.bat").copyTo(File(projectRoot, "gradlew.bat"))
-        val file = File(target, "gradle-wrapper.properties")
-        file.readLines().map {
-            when {
-                it.startsWith("distributionUrl") -> "distributionUrl=https\\://services.gradle.org/distributions/gradle-$GRADLE_VERSION-bin.zip"
-                it.startsWith("distributionSha256Sum") -> "distributionSha256Sum=$GRADLE_SHA_256"
-                else -> it
-            }
-        }.let { lines ->
-            FileWriter(file).use { fw ->
-                lines.forEach { line ->
-                    fw.write("$line\n")
-                }
-            }
-        }
-    }
-
-
-    private fun copyKotlinRuntimeJars() {
-        FileUtil.copy(
-            ForTestCompileRuntime.runtimeJarForTests(),
-            File(pathManager.libsFolderInAndroidTmpFolder + "/kotlin-stdlib.jar")
-        )
-        FileUtil.copy(
-            ForTestCompileRuntime.reflectJarForTests(),
-            File(pathManager.libsFolderInAndroidTmpFolder + "/kotlin-reflect.jar")
-        )
-
-        FileUtil.copy(
-            ForTestCompileRuntime.kotlinTestJarForTests(),
-            File(pathManager.libsFolderInAndroidTmpFolder + "/kotlin-test.jar")
-        )
     }
 
     private fun generateTestsAndFlavourSuites() {
-        println("Generating test files...")
+        println("Clearing destination folder")
+        pathManager.prepareDestinationFolder()
+        println("Generating test files")
 
-        val folders = arrayOf(
-            File("compiler/testData/codegen/box"),
-            File("compiler/testData/codegen/boxInline")
+        generateTestMethodsForDirectories(
+            TargetBackend.ANDROID_IR,
+            commonIr,
+            reflectIr,
+            pathManager.testDataDirectories
         )
 
-        generateTestMethodsForDirectories(COMMON_IR, REFLECT_IR, *folders)
-
-        pendingUnitTestGenerators.values.forEach { it.generate() }
+        pendingTestSourceFileGenerators.values.forEach { it.generate() }
     }
 
     private fun generateTestMethodsForDirectories(
+        backend: TargetBackend,
         commonFlavor: FlavorConfig,
         reflectionFlavor: FlavorConfig,
-        vararg dirs: File
+        dirs: List<File>,
     ) {
         val holders = mutableMapOf<ConfigurationKey, FilesWriter>()
 
         for (dir in dirs) {
-            val files = dir.listFiles() ?: error("Folder with testData is empty: ${dir.absolutePath}")
-            processFiles(files, holders, commonFlavor, reflectionFlavor)
+            val files = dir.listFiles() ?: throw IOException("Cannot list files in $dir")
+            processFiles(files, holders, backend, commonFlavor, reflectionFlavor)
         }
 
         holders.values.forEach {
@@ -184,10 +114,10 @@
 
     internal inner class FilesWriter(
         private val flavorConfig: FlavorConfig,
-        private val configuration: CompilerConfiguration
+        private val configuration: CompilerConfiguration,
     ) {
         private val rawFiles = arrayListOf<TestClassInfo>()
-        private val unitTestDescriptions = arrayListOf<TestInfo>()
+        private val testInfos = arrayListOf<TestInfo>()
 
         private fun shouldWriteFilesOnDisk(): Boolean = rawFiles.size > 300
 
@@ -208,70 +138,62 @@
             )
 
             try {
-                writeFiles(
+                compileAndWriteFiles(
                     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
+                    }, environment, testInfos
                 )
             } finally {
                 rawFiles.clear()
-                unitTestDescriptions.clear()
+                testInfos.clear()
                 Disposer.dispose(disposable)
             }
         }
 
-        private fun writeFiles(
-            filesToCompile: List<KtFile>,
+        private fun compileAndWriteFiles(
+            files: List<KtFile>,
             environment: KotlinCoreEnvironment,
-            unitTestDescriptions: ArrayList<TestInfo>
+            testInfos: List<TestInfo>
         ) {
-            if (filesToCompile.isEmpty()) return
+            if (files.isEmpty()) return
 
-            val flavorName = flavorConfig.getFlavorForNewFiles(filesToCompile.size)
+            val flavorName = flavorConfig.getFlavorNameForNewFiles(files.size)
 
-            val outputDir = File(pathManager.getOutputForCompiledFiles(flavorName))
-            println("Generating ${filesToCompile.size} files into ${outputDir.name}, configuration: '${environment.configuration}'...")
+            val outputDir = pathManager.prepareFlavorTestClassesDirectory(flavorName)
+            println("Generating ${files.size} files into ${outputDir.name}, configuration: '${environment.configuration}'...")
 
-            val outputFiles = GenerationUtils.compileFiles(filesToCompile, environment).run { destroy(); factory }
-
-            if (!outputDir.exists()) {
-                outputDir.mkdirs()
+            val outputFiles = GenerationUtils.compileFiles(files, environment).run {
+                destroy()
+                factory
             }
-            Assert.assertTrue("Cannot create directory for compiled files", outputDir.exists())
-            val unitTestFileWriter = pendingUnitTestGenerators.getOrPut(flavorName) {
-                UnitTestFileWriter(
-                    getFlavorUnitTestFolder(flavorName),
+
+            val testSourceFileGenerator = pendingTestSourceFileGenerators.getOrPut(flavorName) {
+                TestSourceFileGenerator(
+                    pathManager.prepareFlavorTestSourceDirectory(flavorName),
                     flavorName,
                     generatedTestNames
                 )
             }
-            unitTestFileWriter.addTests(unitTestDescriptions)
+            testSourceFileGenerator.addTests(testInfos)
             outputFiles.writeAllTo(outputDir)
         }
 
-        private fun getFlavorUnitTestFolder(flavourName: String): String {
-            return pathManager.srcFolderInAndroidTmpFolder +
-                    "/androidTest${flavourName.replaceFirstChar(Char::uppercaseChar)}/java/" +
-                    testClassPackage.replace(".", "/") + "/"
-        }
-
-        fun addTest(testFiles: List<TestClassInfo>, info: TestInfo) {
+        fun addTest(testFiles: Collection<TestClassInfo>, info: TestInfo) {
             rawFiles.addAll(testFiles)
-            unitTestDescriptions.add(info)
+            testInfos.add(info)
         }
     }
 
-    @OptIn(TestInfrastructureInternals::class)
-    @Throws(IOException::class)
     private fun processFiles(
         files: Array<File>,
         holders: MutableMap<ConfigurationKey, FilesWriter>,
-        commonFlavor: FlavorConfig,
-        reflectionFlavor: FlavorConfig
+        backend: TargetBackend,
+        commmonFlavor: FlavorConfig,
+        reflectionFlavor: FlavorConfig,
     ) {
         holders.values.forEach {
             it.writeFilesOnDiskIfNeeded()
@@ -279,10 +201,13 @@
 
         for (file in files) {
             if (file.isDirectory) {
-                val listFiles = file.listFiles()
-                if (listFiles != null) {
-                    processFiles(listFiles, holders, commonFlavor, reflectionFlavor)
-                }
+                processFiles(
+                    file.listFiles() ?: throw IOException("Cannot list files in $file"),
+                    holders,
+                    backend,
+                    commmonFlavor,
+                    reflectionFlavor
+                )
             } else if (FileUtilRt.getExtension(file.name) != KotlinFileType.EXTENSION) {
                 // skip non kotlin files
             } else {
@@ -307,76 +232,74 @@
 
                 //TODO support JvmPackageName
                 if (fullFileText.contains("@file:JvmPackageName(")) continue
+
                 // TODO: Support jvm assertions
                 if (fullFileText.contains("// ASSERTIONS_MODE: jvm")) continue
+
                 if (fullFileText.contains("// MODULE: ")) continue
+
+                // TODO: add && backend != TargetBackend.JVM_IR
                 if (fullFileText.contains("// IGNORE_BACKEND_K1")) continue
+
+                if (fullFileText.contains("// IGNORE_DEXING")) continue
                 val targets = InTextDirectivesUtils.findLinesWithPrefixesRemoved(fullFileText, "// JVM_TARGET:")
 
                 val isAtLeastJvm8Target = !targets.contains(JvmTarget.JVM_1_6.description)
 
-                if (isAtLeastJvm8Target && fullFileText.contains("@Target(AnnotationTarget.TYPE)")) {
-                    //TODO: type annotations supported on sdk 26 emulator
-                    continue
-                }
+                // TODO: type annotations are supported on Android SDK 26. A separate flavor can be created
+                if (isAtLeastJvm8Target && fullFileText.contains("@Target(AnnotationTarget.TYPE)")) continue
 
-                // TODO: support SKIP_JDK6 on new platforms
+                // TODO: JDK 1.8 features are supported on Android SDK 26
                 if (fullFileText.contains("// SKIP_JDK6")) continue
 
-                if (hasBoxMethod(fullFileText)) {
-                    val testConfiguration = createTestConfiguration(file)
-                    val services = testConfiguration.testServices
+                if (!fullFileText.contains("fun box()")) continue
 
-                    val moduleStructure = try {
-                        testConfiguration.moduleStructureExtractor.splitTestDataByModules(
-                            file.path,
-                            testConfiguration.directives,
-                        ).also {
-                            services.register(TestModuleStructure::class, it)
-                        }
-                    } catch (e: ExceptionFromModuleStructureTransformer) {
-                        continue
+                val testConfiguration = createTestConfiguration(file, backend)
+                val services = testConfiguration.testServices
+
+                val moduleStructure = try {
+                    testConfiguration.moduleStructureExtractor.splitTestDataByModules(
+                        file.path,
+                        testConfiguration.directives,
+                    ).also {
+                        services.register(TestModuleStructure::class, it)
                     }
-                    val module = moduleStructure.modules.singleOrNull() ?: continue
-                    if (module.files.any { it.isJavaFile || it.isKtsFile }) continue
-                    if (module.files.isEmpty()) continue
-                    services.registerDependencyProvider(DependencyProviderImpl(services, moduleStructure.modules))
-
-                    val keyConfiguration = CompilerConfiguration()
-                    val configuratorForFlags = JvmEnvironmentConfigurator(services)
-                    with(configuratorForFlags) {
-                        val extractor = DirectiveToConfigurationKeyExtractor()
-                        extractor.provideConfigurationKeys()
-                        extractor.configure(keyConfiguration, module.directives)
-                    }
-                    val kind = JvmEnvironmentConfigurator.extractConfigurationKind(module.directives)
-                    val jdkKind = JvmEnvironmentConfigurator.extractJdkKind(module.directives)
-
-                    keyConfiguration.languageVersionSettings = module.languageVersionSettings
-                    keyConfiguration.put(
-                        CommonConfigurationKeys.ENABLE_IR_VISIBILITY_CHECKS,
-                        module.targetBackend !in module.directives[CodegenTestDirectives.DISABLE_IR_VISIBILITY_CHECKS] &&
-                                TargetBackend.ANY !in module.directives[CodegenTestDirectives.DISABLE_IR_VISIBILITY_CHECKS],
-                    )
-
-                    val key = ConfigurationKey(kind, jdkKind, keyConfiguration.toString())
-                    val compiler = if (kind.withReflection) reflectionFlavor else commonFlavor
-                    val compilerConfigurationProvider = services.compilerConfigurationProvider as CompilerConfigurationProviderImpl
-                    val filesHolder = holders.getOrPut(key) {
-                        FilesWriter(compiler, compilerConfigurationProvider.createCompilerConfiguration(module)).also {
-                            println("Creating new configuration by $key")
-                        }
-                    }
-
-                    patchFilesAndAddTest(file, module, services, filesHolder)
+                } catch (e: ExceptionFromModuleStructureTransformer) {
+                    continue
                 }
+                val module = moduleStructure.modules.singleOrNull() ?: continue
+                if (module.files.any { it.isJavaFile || it.isKtsFile }) continue
+                if (module.files.isEmpty()) continue
+                services.registerDependencyProvider(DependencyProviderImpl(services, moduleStructure.modules))
+
+                val keyConfiguration = CompilerConfiguration()
+                val configuratorForFlags = JvmEnvironmentConfigurator(services)
+                with(configuratorForFlags) {
+                    val extractor = DirectiveToConfigurationKeyExtractor()
+                    extractor.provideConfigurationKeys()
+                    extractor.configure(keyConfiguration, module.directives)
+                }
+                val kind = JvmEnvironmentConfigurator.extractConfigurationKind(module.directives)
+                val jdkKind = JvmEnvironmentConfigurator.extractJdkKind(module.directives)
+
+                keyConfiguration.languageVersionSettings = module.languageVersionSettings
+
+                val key = ConfigurationKey(kind, jdkKind, keyConfiguration.toString())
+                val compiler = if (kind.withReflection) reflectionFlavor else commmonFlavor
+                val compilerConfigurationProvider = services.compilerConfigurationProvider as CompilerConfigurationProviderImpl
+                val filesHolder = holders.getOrPut(key) {
+                    println("Creating new configuration by $key")
+                    FilesWriter(compiler, compilerConfigurationProvider.createCompilerConfiguration(module))
+                }
+
+                patchFilesAndAddTest(file, module, services, filesHolder)
             }
         }
     }
 
-    private fun createTestConfiguration(testDataFile: File): TestConfiguration {
+    private fun createTestConfiguration(testDataFile: File, backend: TargetBackend): TestConfiguration {
         return TestConfigurationBuilder().apply {
-            configure()
+            configure(backend)
             testInfo = KotlinTestInfo(
                 "org.jetbrains.kotlin.android.tests.AndroidRunner",
                 "test${testDataFile.nameWithoutExtension.replaceFirstChar(Char::uppercaseChar)}",
@@ -386,10 +309,10 @@
         }.build(testDataFile.path)
     }
 
-    private fun TestConfigurationBuilder.configure() {
+    private fun TestConfigurationBuilder.configure(backend: TargetBackend) {
         globalDefaults {
             frontend = FrontendKinds.ClassicFrontend
-            targetBackend = TargetBackend.ANDROID_IR
+            targetBackend = backend
             targetPlatform = JvmPlatforms.defaultJvmPlatform
             dependencyKind = DependencyKind.Binary
         }
@@ -421,42 +344,26 @@
     }
 
     companion object {
-        const val GRADLE_VERSION = "6.8.1" // update GRADLE_SHA_256 on change
-        const val GRADLE_SHA_256 = "fd591a34af7385730970399f473afabdb8b28d57fd97d6625c388d090039d6fd"
-        const val testClassPackage = "org.jetbrains.kotlin.android.tests"
-        const val testClassName = "CodegenTestCaseOnAndroid"
-        const val baseTestClassPackage = "org.jetbrains.kotlin.android.tests"
-        const val baseTestClassName = "AbstractCodegenTestCaseOnAndroid"
+        const val TEST_CLASS_PACKAGE = "org.jetbrains.kotlin.android.tests"
+        const val BASE_TEST_CLASS_PACKAGE = "org.jetbrains.kotlin.android.tests"
+        const val BASE_TEST_CLASS_NAME = "AbstractCodegenTestCaseOnAndroid"
 
-
-        @JvmOverloads
-        @JvmStatic
-        @Throws(Throwable::class)
-        fun generate(pathManager: PathManager, skipSdkDirWriting: Boolean = false) {
-            CodegenTestsOnAndroidGenerator(pathManager).prepareAndroidModuleAndGenerateTests(skipSdkDirWriting)
-        }
-
-        private fun hasBoxMethod(text: String): Boolean {
-            return text.contains("fun box()")
-        }
-
-        @Throws(IOException::class)
-        internal fun writeAndroidSkdToLocalProperties(pathManager: PathManager) {
-            val sdkRoot = KtTestUtil.getAndroidSdkSystemIndependentPath()
-            println("Writing android sdk to local.properties: $sdkRoot")
-            val file = File(pathManager.tmpFolder + "/local.properties")
-            FileWriter(file).use { fw -> fw.write("sdk.dir=$sdkRoot") }
-        }
-
-        @OptIn(ExperimentalPathApi::class)
+        /**
+         * Generates Android test sources and classes.
+         *
+         * @param args The first argument is destination directory, the rest are test data directories to walk.
+         */
         @JvmStatic
         fun main(args: Array<String>) {
-            val tmpFolder = createTempDirectory().toAbsolutePath().toString()
-            println("Created temporary folder for android tests: $tmpFolder")
-            val rootFolder = Path("").toAbsolutePath().toString()
-            val pathManager = PathManager(rootFolder, tmpFolder)
-            generate(pathManager, true)
-            println("Android test project is generated into $tmpFolder folder")
+            val destinationDirectory = File(requireNotNull(args.getOrNull(0)) { "No destination directory provided" })
+            val testDataDirectories = args.drop(1).map(::File)
+            require(testDataDirectories.isNotEmpty()) { "No test data directories provided" }
+            val notTestData = testDataDirectories.filterNot { "testData" in it.path }
+            require(notTestData.isEmpty()) { "Directories unrelated to testData are provided: $notTestData" }
+            println("Generating Android te`st sources and classes to $destinationDirectory")
+            val pathManager = PathManager(destinationDirectory, testDataDirectories)
+            CodegenTestsOnAndroidGenerator(pathManager).generateTestsAndFlavourSuites()
+            println("Generated Android test sources and classes to $destinationDirectory")
         }
     }
 }
diff --git a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/CodegenTestsOnAndroidRunner.kt b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/CodegenTestsOnAndroidRunner.kt
deleted file mode 100644
index 14562eb..0000000
--- a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/CodegenTestsOnAndroidRunner.kt
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright 2010-2015 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.tests
-
-import com.intellij.util.PlatformUtils
-import junit.framework.TestCase
-import junit.framework.TestSuite
-import org.jetbrains.kotlin.android.tests.emulator.Emulator
-import org.jetbrains.kotlin.android.tests.gradle.GradleRunner
-import org.junit.Assert
-import org.w3c.dom.Element
-import org.xml.sax.SAXException
-import java.io.File
-import java.io.IOException
-import javax.xml.parsers.DocumentBuilderFactory
-import javax.xml.parsers.ParserConfigurationException
-import kotlin.test.assertTrue
-
-class CodegenTestsOnAndroidRunner private constructor(private val pathManager: PathManager) {
-
-    private val isTeamcity = System.getProperty("kotlin.test.android.teamcity") != null || System.getenv("TEAMCITY_VERSION") != null
-
-    private fun runTestsInEmulator(): TestSuite {
-        val rootSuite = TestSuite("Root")
-
-        val emulatorType = if (isTeamcity) Emulator.ARM else Emulator.X86
-        println("Using $emulatorType emulator!")
-        val emulator = Emulator(pathManager, emulatorType)
-        emulator.createEmulator()
-
-        val gradleRunner = GradleRunner(pathManager)
-        //old dex
-        cleanAndBuildProject(gradleRunner)
-
-        try {
-            emulator.startEmulator()
-
-            try {
-                emulator.waitEmulatorStart()
-
-                runTestsOnEmulator(gradleRunner, TestSuite("D8")).apply {
-                    rootSuite.addTest(this)
-                }
-            } catch (e: RuntimeException) {
-                e.printStackTrace()
-                throw e
-            } finally {
-                emulator.stopEmulator()
-            }
-        } catch (e: RuntimeException) {
-            e.printStackTrace()
-            throw e
-        } finally {
-            emulator.finishEmulatorProcesses()
-        }
-
-        return rootSuite
-    }
-
-    private fun processReport(rootSuite: TestSuite, resultOutput: String) {
-        val reportFolder = File(flavorFolder())
-        try {
-            val folders = reportFolder.listFiles()
-            assertTrue(folders != null && folders.isNotEmpty(), "No folders in ${reportFolder.path}")
-
-            folders.forEach {
-                assertTrue("${it.path} is not directory") { it.isDirectory }
-                val isIr = it.name.contains("_ir")
-                val testCases = parseSingleReportInFolder(it)
-                testCases.forEach { aCase ->
-                    if (isIr) aCase.name += "_ir"
-                    rootSuite.addTest(aCase)
-                }
-                Assert.assertNotEquals("There is no test results in report", 0, testCases.size.toLong())
-            }
-        } catch (e: Throwable) {
-            throw RuntimeException("Can't parse test results in $reportFolder\n$resultOutput", e)
-        }
-    }
-
-
-    private fun flavorFolder() = pathManager.tmpFolder + "/build/test/results/connected/flavors"
-
-    private fun runTestsOnEmulator(gradleRunner: GradleRunner, suite: TestSuite): TestSuite {
-        val platformPrefixProperty = System.setProperty(PlatformUtils.PLATFORM_PREFIX_KEY, "Idea")
-        try {
-            val resultOutput = gradleRunner.connectedDebugAndroidTest()
-            processReport(suite, resultOutput)
-            return suite
-        } finally {
-            if (platformPrefixProperty != null) {
-                System.setProperty(PlatformUtils.PLATFORM_PREFIX_KEY, platformPrefixProperty)
-            } else {
-                System.clearProperty(PlatformUtils.PLATFORM_PREFIX_KEY)
-            }
-        }
-
-    }
-
-    companion object {
-
-        @JvmStatic
-        fun runTestsInEmulator(pathManager: PathManager): TestSuite {
-            return CodegenTestsOnAndroidRunner(pathManager).runTestsInEmulator()
-        }
-
-        private fun cleanAndBuildProject(gradleRunner: GradleRunner) {
-            gradleRunner.clean()
-            gradleRunner.assembleAndroidTest()
-        }
-
-        @Throws(IOException::class, SAXException::class, ParserConfigurationException::class)
-        private fun parseSingleReportInFolder(folder: File): List<TestCase> {
-            val files = folder.listFiles()!!
-            assert(files.size == 1) {
-                "Expecting one file but ${files.size}: ${files.joinToString { it.name }} in ${folder.path}"
-            }
-            val reportFile = files[0]
-
-            val dbFactory = DocumentBuilderFactory.newInstance()
-            val dBuilder = dbFactory.newDocumentBuilder()
-            val doc = dBuilder.parse(reportFile)
-            val root = doc.documentElement
-            val testCases = root.getElementsByTagName("testcase")
-
-            return (0 until testCases.length).map { i ->
-                val item = testCases.item(i) as Element
-                val failure = item.getElementsByTagName("failure").takeIf { it.length != 0 }?.item(0)
-                val name = item.getAttribute("name")
-
-                object : TestCase(name) {
-                    @Throws(Throwable::class)
-                    override fun runTest() {
-                        if (failure != null) {
-                            Assert.fail(failure.textContent)
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/ConfigurationKey.kt b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/ConfigurationKey.kt
new file mode 100644
index 0000000..4215274
--- /dev/null
+++ b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/ConfigurationKey.kt
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.android.tests
+
+import org.jetbrains.kotlin.test.ConfigurationKind
+import org.jetbrains.kotlin.test.TestJdkKind
+
+data class ConfigurationKey(val kind: ConfigurationKind, val jdkKind: TestJdkKind, val configuration: String)
diff --git a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/OutputUtils.java b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/OutputUtils.java
deleted file mode 100644
index 69d2de3..0000000
--- a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/OutputUtils.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2010-2015 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.tests;
-
-import org.jetbrains.annotations.Nullable;
-import org.jetbrains.kotlin.android.tests.run.RunResult;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class OutputUtils {
-
-    private final static Pattern EMULATOR_PROCESS_PATTERN = Pattern.compile("\\w*[\\s]+([0-9]*) .* java .* emulator .*");
-
-    public static boolean isBuildFailed(String output) {
-        return output.contains("BUILD FAILED") || output.contains("Build failed");
-    }
-
-    public static void checkResult(RunResult result) {
-        if (!result.getStatus()) {
-            throw new RuntimeException(result.getOutput());
-        }
-    }
-
-    @Nullable
-    public static String getPidFromPsCommand(String output) {
-        if (!output.isEmpty()) {
-            Matcher matcher = EMULATOR_PROCESS_PATTERN.matcher(output);
-            if (matcher.find()) {
-                return matcher.group(1);
-            }
-        }
-        return null;
-    }
-
-    private OutputUtils() {
-    }
-}
diff --git a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/PathManager.java b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/PathManager.java
deleted file mode 100644
index 8c135fc..0000000
--- a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/PathManager.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2010-2015 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.tests;
-
-import org.jetbrains.kotlin.test.util.KtTestUtil;
-
-import java.io.File;
-
-public class PathManager {
-
-    private final String tmpFolder;
-    private final String rootFolder;
-
-    public PathManager(String rootFolder, String tmpFolder) {
-        this.tmpFolder = tmpFolder;
-        this.rootFolder = rootFolder;
-    }
-
-    public String getPlatformFolderInAndroidSdk() {
-        return getAndroidSdkRoot() + "/platforms";
-    }
-
-    public String getAndroidAvdRoot() {
-        String androidEmulatorRoot = getAndroidSdkRoot() + "/compiler_box_test_avd";
-        new File(androidEmulatorRoot).mkdirs();
-        return androidEmulatorRoot;
-    }
-
-    public String getPlatformToolsFolderInAndroidSdk() {
-        return getAndroidSdkRoot() + "/platform-tools";
-    }
-
-    public String getToolsFolderInAndroidSdk() {
-        return getAndroidSdkRoot() + "/tools";
-    }
-
-    public String getEmulatorFolderInAndroidSdk() {
-        return getAndroidSdkRoot() + "/emulator";
-    }
-
-    public String getOutputForCompiledFiles(String flavor) {
-        return tmpFolder + "/libs/" + flavor;
-    }
-
-    public String getLibsFolderInAndroidTmpFolder() {
-        return tmpFolder + "/libs";
-    }
-
-    public String getSrcFolderInAndroidTmpFolder() {
-        return tmpFolder + "/src";
-    }
-
-    public String getAndroidTmpFolder() {
-        return tmpFolder;
-    }
-
-    public String getAndroidSdkRoot() {
-        return KtTestUtil.getAndroidSdkSystemIndependentPath();
-    }
-
-    public String getAndroidModuleRoot() {
-        return rootFolder + "/compiler/android-tests/android-module";
-    }
-
-    public String getTmpFolder() {
-        return tmpFolder;
-    }
-}
diff --git a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/PathManager.kt b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/PathManager.kt
new file mode 100644
index 0000000..6c191f7
--- /dev/null
+++ b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/PathManager.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2010-2015 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.tests
+
+import java.io.File
+import java.io.IOException
+
+class PathManager(destinationDirectory: File, val testDataDirectories: List<File>) {
+    private val destinationTestClassesDirectory: File = destinationDirectory.resolve("test-classes")
+    private val destinationSrcDirectory: File = destinationDirectory.resolve("src")
+
+    fun prepareDestinationFolder() {
+        if (destinationTestClassesDirectory.isDirectory) {
+            destinationTestClassesDirectory.listFiles()!!
+                .forEach { it.deleteRecursively() }
+        } else if (!destinationTestClassesDirectory.mkdirs()) {
+            throw IOException("Unable to create directory for test classes: $destinationTestClassesDirectory")
+        }
+
+        if (destinationSrcDirectory.isDirectory) {
+            destinationSrcDirectory.listFiles()!!
+                .filter { it.name != "main" }
+                .forEach { it.deleteRecursively() }
+        } else if (!destinationSrcDirectory.mkdirs()) {
+            throw IOException("Unable to create directory for sources: $destinationSrcDirectory")
+        }
+    }
+
+    fun prepareFlavorTestClassesDirectory(flavor: String): File {
+        val directory = destinationTestClassesDirectory.resolve(flavor)
+
+        if (!directory.isDirectory && !directory.mkdirs()) {
+            throw IOException("Unable to create test output directory for flavor: $directory")
+        }
+
+        return directory
+    }
+
+    fun prepareFlavorTestSourceDirectory(flavorName: String): File {
+        val directory = destinationSrcDirectory.resolve(
+            "androidTest${flavorName.replaceFirstChar(Char::uppercaseChar)}/java/" +
+                    CodegenTestsOnAndroidGenerator.TEST_CLASS_PACKAGE.replace(".", "/") + "/"
+        )
+
+        if (!directory.isDirectory && !directory.mkdirs()) {
+            throw IOException("Unable to create source directory for flavor: $directory")
+        }
+
+        return directory
+    }
+}
diff --git a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/TestSourceFileGenerator.kt b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/TestSourceFileGenerator.kt
new file mode 100644
index 0000000..843f0d8
--- /dev/null
+++ b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/TestSourceFileGenerator.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.android.tests
+
+import com.intellij.openapi.util.io.FileUtil
+import com.intellij.openapi.util.text.StringUtil
+import org.jetbrains.kotlin.generators.util.TestGeneratorUtil.getMainClassName
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.name.NameUtils
+import org.jetbrains.kotlin.utils.Printer
+import java.io.File
+
+class TestInfo(val name: String, val fqName: FqName, val file: File)
+
+class TestSourceFileGenerator(
+    private val flavorDirectory: File,
+    private val flavorName: String,
+    private val generatedTestNames: MutableSet<String>,
+) {
+    private val infos = arrayListOf<TestInfo>()
+
+    fun addTests(info: Collection<TestInfo>) {
+        infos.addAll(info)
+    }
+
+    fun generate() {
+        val sourceFile = flavorDirectory.resolve(flavorName.replaceFirstChar(Char::uppercaseChar) + ".java")
+        sourceFile.writer().use { suite ->
+            val p = Printer(suite)
+            p.println(
+                """package ${CodegenTestsOnAndroidGenerator.TEST_CLASS_PACKAGE};
+                |
+                |import ${CodegenTestsOnAndroidGenerator.BASE_TEST_CLASS_PACKAGE}.${CodegenTestsOnAndroidGenerator.BASE_TEST_CLASS_NAME};
+                |
+                |/* This class is generated by ${getMainClassName()}. DO NOT MODIFY MANUALLY */
+                |public class ${flavorName.replaceFirstChar(Char::uppercaseChar)} extends ${CodegenTestsOnAndroidGenerator.BASE_TEST_CLASS_NAME} {
+                |
+            """.trimMargin()
+            )
+            p.pushIndent()
+
+            for (info in infos) {
+                generateTestMethod(
+                    p,
+                    inventTestName(info.file.name),
+                    info.fqName.asString(),
+                    StringUtil.escapeStringCharacters(info.file.path)
+                )
+            }
+
+            p.popIndent()
+            p.println("}")
+        }
+    }
+
+    private fun inventTestName(fileName: String): String {
+        val baseName = NameUtils.sanitizeAsJavaIdentifier(FileUtil.getNameWithoutExtension(StringUtil.capitalize(fileName)))
+        var result = baseName
+        var i = 0
+
+        while (result in generatedTestNames) {
+            result = "${baseName}_${i}"
+            i++
+        }
+
+        generatedTestNames.add(result)
+        return result
+    }
+
+    private fun generateTestMethod(p: Printer, testName: String, className: String, filePath: String) {
+        p.println("public void test$testName() {")
+        p.pushIndent()
+        p.println("invokeBoxMethod($className.class, \"$filePath\", \"OK\");")
+        p.popIndent()
+        p.println("}")
+        p.println()
+    }
+
+}
\ No newline at end of file
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
deleted file mode 100644
index c72304c..0000000
--- a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/UnitTestFileWriter.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
- * that can be found in the license/LICENSE.txt file.
- */
-
-package org.jetbrains.kotlin.android.tests
-
-import com.intellij.openapi.util.io.FileUtil
-import com.intellij.openapi.util.text.StringUtil
-import org.jetbrains.kotlin.generators.util.TestGeneratorUtil.getMainClassName
-import org.jetbrains.kotlin.name.FqName
-import org.jetbrains.kotlin.name.NameUtils
-import org.jetbrains.kotlin.utils.Printer
-import java.io.File
-import java.io.FileWriter
-
-class TestInfo(val name: String, val fqName: FqName, val file: File)
-
-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>) {
-        infos.addAll(info)
-    }
-
-    fun generate() {
-        FileWriter(File(flavourFolder, flavourName.replaceFirstChar(Char::uppercaseChar) + ".java").also { it.parentFile.mkdirs() }).use { suite ->
-            val p = Printer(suite)
-            p.println(
-                """package ${CodegenTestsOnAndroidGenerator.testClassPackage};
-                |
-                |import ${CodegenTestsOnAndroidGenerator.baseTestClassPackage}.${CodegenTestsOnAndroidGenerator.baseTestClassName};
-                |
-                |/* This class is generated by ${getMainClassName()}. DO NOT MODIFY MANUALLY */
-                |public class ${flavourName.replaceFirstChar(Char::uppercaseChar)} extends ${CodegenTestsOnAndroidGenerator.baseTestClassName} {
-                |
-            """.trimMargin()
-            )
-            p.pushIndent()
-
-            infos.forEach { info ->
-                generateTestMethod(
-                    p,
-                    generateTestName(info.file.name),
-                    info.fqName.asString(),
-                    StringUtil.escapeStringCharacters(info.file.path)
-                )
-            }
-
-            p.popIndent()
-            p.println("}")
-        }
-    }
-
-    private fun generateTestName(fileName: String): String {
-        var result = NameUtils.sanitizeAsJavaIdentifier(FileUtil.getNameWithoutExtension(StringUtil.capitalize(fileName)))
-
-        var i = 0
-        while (generatedTestNames.contains(result)) {
-            result += "_" + i++
-        }
-        generatedTestNames.add(result)
-        return result
-    }
-
-    private fun generateTestMethod(p: Printer, testName: String, className: String, filePath: String) {
-        p.println("public void test$testName() throws Exception {")
-        p.pushIndent()
-        p.println("invokeBoxMethod($className.class, \"$filePath\", \"OK\");")
-        p.popIndent()
-        p.println("}")
-        p.println()
-    }
-
-}
\ No newline at end of file
diff --git a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/emulator/Emulator.java b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/emulator/Emulator.java
deleted file mode 100644
index d14a7ca..0000000
--- a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/emulator/Emulator.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright 2010-2015 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.tests.emulator;
-
-import com.intellij.execution.configurations.GeneralCommandLine;
-import com.intellij.openapi.util.SystemInfo;
-import com.intellij.openapi.util.text.StringUtil;
-import org.jetbrains.annotations.Nullable;
-import org.jetbrains.kotlin.android.tests.OutputUtils;
-import org.jetbrains.kotlin.android.tests.PathManager;
-import org.jetbrains.kotlin.android.tests.run.RunResult;
-import org.jetbrains.kotlin.android.tests.run.RunUtils;
-import org.junit.Assert;
-
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Emulator {
-
-    public static final String ARM = "arm";
-    public static final String X86 = "x86";
-    private static final String AVD_NAME = "kotlin_box_test_avd";
-
-    private final static Pattern EMULATOR_PATTERN = Pattern.compile("emulator-([0-9])*");
-
-    private final PathManager pathManager;
-    private final String platform;
-
-    public Emulator(PathManager pathManager, String platform) {
-        this.pathManager = pathManager;
-        this.platform = platform;
-    }
-
-    private GeneralCommandLine getCreateCommand() {
-        GeneralCommandLine commandLine = new GeneralCommandLine();
-        String androidCmdName = SystemInfo.isWindows ? "avdmanager.bat" : "avdmanager";
-        commandLine.setExePath(pathManager.getToolsFolderInAndroidSdk() + "/bin/" + androidCmdName);
-        commandLine.addParameter("create");
-        commandLine.addParameter("avd");
-        commandLine.addParameter("--force");
-        commandLine.addParameter("-n");
-        commandLine.addParameter(AVD_NAME);
-        commandLine.addParameter("-p");
-        commandLine.addParameter(pathManager.getAndroidAvdRoot());
-        commandLine.addParameter("-k");
-        if (platform == X86) {
-            commandLine.addParameter("system-images;android-19;default;x86");
-        } else {
-            commandLine.addParameter("system-images;android-19;default;armeabi-v7a");
-        }
-        return commandLine;
-    }
-
-    private GeneralCommandLine getStartCommand() {
-        GeneralCommandLine commandLine = new GeneralCommandLine();
-        commandLine.setExePath(pathManager.getEmulatorFolderInAndroidSdk() + "/" + "emulator");
-        commandLine.addParameter("-avd");
-        commandLine.addParameter(AVD_NAME);
-        commandLine.addParameter("-no-audio");
-        commandLine.addParameter("-no-window");
-        return commandLine;
-    }
-
-    private GeneralCommandLine getWaitCommand() {
-        GeneralCommandLine commandLine = createAdbCommand();
-        commandLine.addParameter("wait-for-device");
-        return commandLine;
-    }
-
-    private GeneralCommandLine getStopCommandForAdb() {
-        GeneralCommandLine commandLine = createAdbCommand();
-        commandLine.addParameter("kill-server");
-        return commandLine;
-    }
-
-    @Nullable
-    private GeneralCommandLine getStopCommand() {
-        if (SystemInfo.isWindows) {
-            GeneralCommandLine commandLine = new GeneralCommandLine();
-            commandLine.setExePath("taskkill");
-            commandLine.addParameter("/F");
-            commandLine.addParameter("/IM");
-            commandLine.addParameter("emulator-" + platform + ".exe");
-            return commandLine;
-        }
-        return null;
-    }
-
-    public void createEmulator() {
-        System.out.println("Creating emulator...");
-        OutputUtils.checkResult(RunUtils.execute(new RunUtils.RunSettings(getCreateCommand(), "no", true, null, false)));
-    }
-
-    private GeneralCommandLine createAdbCommand() {
-        GeneralCommandLine commandLine = new GeneralCommandLine();
-        commandLine.setExePath(pathManager.getPlatformToolsFolderInAndroidSdk() + "/" + "adb");
-        return commandLine;
-    }
-
-    public void startServer() {
-        GeneralCommandLine commandLine = createAdbCommand();
-        commandLine.addParameter("start-server");
-        System.out.println("Start adb server...");
-        OutputUtils.checkResult(RunUtils.execute(new RunUtils.RunSettings(commandLine, null, true, "ADB START:", true)));
-    }
-
-    public void startEmulator() {
-        startServer();
-        System.out.println("Starting emulator with ANDROID_HOME/ANDROID_SDK_ROOT: " + pathManager.getAndroidSdkRoot());
-        GeneralCommandLine startCommand = getStartCommand();
-        startCommand.withEnvironment("ANDROID_SDK_ROOT", pathManager.getAndroidSdkRoot());
-        startCommand.withEnvironment("ANDROID_HOME", pathManager.getAndroidSdkRoot());
-        RunUtils.executeOnSeparateThread(new RunUtils.RunSettings(startCommand, null, false, "START: ", true));
-        printLog();
-    }
-
-    public void printLog() {
-        GeneralCommandLine commandLine = createAdbCommand();
-        commandLine.addParameter("logcat");
-        commandLine.addParameter("-v");
-        commandLine.addParameter("time");
-        commandLine.addParameter("-s");
-        commandLine.addParameter("dalvikvm:W");
-        commandLine.addParameter("TestRunner:I");
-        RunUtils.executeOnSeparateThread(new RunUtils.RunSettings(commandLine, null, false, "LOGCAT: ", true));
-    }
-
-    public void waitEmulatorStart() {
-        System.out.println("Waiting for emulator start...");
-        OutputUtils.checkResult(RunUtils.execute(getWaitCommand()));
-        GeneralCommandLine bootCheckCommand = createAdbCommand();
-        bootCheckCommand.addParameter("shell");
-        bootCheckCommand.addParameter("getprop");
-        bootCheckCommand.addParameter("sys.boot_completed");
-
-        int counter = 0;
-        RunResult execute = RunUtils.execute(bootCheckCommand);
-        while (counter < 20) {
-            String output = execute.getOutput();
-            if (output.trim().endsWith("1")) {
-                System.out.println("Emulator fully booted!");
-                return;
-            }
-            System.out.println("Waiting for emulator boot (" + counter + ")...");
-            try {
-                Thread.sleep(10000);
-            }
-            catch (InterruptedException e) {
-                e.printStackTrace();
-            }
-            counter++;
-            execute = RunUtils.execute(bootCheckCommand);
-        }
-        Assert.fail("Can't find booted emulator: " + execute.getOutput());
-    }
-
-    public void stopEmulator() {
-        System.out.println("Stopping emulator...");
-
-        GeneralCommandLine command = createAdbCommand();
-        command.addParameter("-s");
-        command.addParameter("emulator-5554");
-        command.addParameter("emu");
-        command.addParameter("kill");
-        RunUtils.execute(command);
-
-        finishProcess("emulator64-" + platform);
-        finishProcess("emulator-" + platform);
-    }
-
-    //Only for Unix
-    private static void stopDdmsProcess() {
-        if (SystemInfo.isUnix) {
-            GeneralCommandLine listOfEmulatorProcess = new GeneralCommandLine();
-            listOfEmulatorProcess.setExePath("sh");
-            listOfEmulatorProcess.addParameter("-c");
-            listOfEmulatorProcess.addParameter("ps aux | grep emulator");
-            RunResult runResult = RunUtils.execute(listOfEmulatorProcess);
-            OutputUtils.checkResult(runResult);
-            String pidFromPsCommand = OutputUtils.getPidFromPsCommand(runResult.getOutput());
-            if (pidFromPsCommand != null) {
-                GeneralCommandLine killCommand = new GeneralCommandLine();
-                killCommand.setExePath("kill");
-                killCommand.addParameter(pidFromPsCommand);
-                RunUtils.execute(killCommand);
-            }
-        }
-    }
-
-    public void finishEmulatorProcesses() {
-        System.out.println("Stopping adb...");
-        OutputUtils.checkResult(RunUtils.execute(getStopCommandForAdb()));
-        finishProcess("adb");
-        stopDdmsProcess();
-    }
-
-    //Only for Unix
-    private static void finishProcess(String processName) {
-        if (SystemInfo.isUnix) {
-            GeneralCommandLine pidOfProcess = new GeneralCommandLine();
-            pidOfProcess.setExePath("pidof");
-            pidOfProcess.addParameter(processName);
-            RunResult runResult = RunUtils.execute(pidOfProcess);
-            String processIdsStr = runResult.getOutput().substring(("pidof " + processName).length());
-            List<String> processIds = StringUtil.getWordsIn(processIdsStr);
-            for (String pid : processIds) {
-                GeneralCommandLine killCommand = new GeneralCommandLine();
-                killCommand.setExePath("kill");
-                killCommand.addParameter("-s");
-                killCommand.addParameter("9");
-                killCommand.addParameter(pid);
-                RunUtils.execute(killCommand);
-            }
-        }
-    }
-
-    public String runTestsViaAdb() {
-        System.out.println("Running tests via adb...");
-        GeneralCommandLine adbCommand = createAdbCommand();
-        //adb shell am instrument -w -r org.jetbrains.kotlin.android.tests/android.test.InstrumentationTestRunner
-        adbCommand.addParameters("shell", "am", "instrument", "-w", "-r", "org.jetbrains.kotlin.android.tests/android.test.InstrumentationTestRunner");
-        RunResult execute = RunUtils.execute(adbCommand);
-        return execute.getOutput();
-    }
-
-    private void stopRedundantEmulators(PathManager pathManager) {
-        GeneralCommandLine commandLineForListOfDevices = createAdbCommand();
-        commandLineForListOfDevices.addParameter("devices");
-        RunResult runResult = RunUtils.execute(commandLineForListOfDevices);
-        OutputUtils.checkResult(runResult);
-
-        Matcher matcher = EMULATOR_PATTERN.matcher(runResult.getOutput());
-        boolean isDdmsStopped = false;
-        while (matcher.find()) {
-            System.out.println("Stopping redundant emulator...");
-            GeneralCommandLine commandLineForStoppingEmulators = new GeneralCommandLine();
-            if (SystemInfo.isWindows) {
-                commandLineForStoppingEmulators.setExePath("taskkill");
-                commandLineForStoppingEmulators.addParameter("/F");
-                commandLineForStoppingEmulators.addParameter("/IM");
-                commandLineForStoppingEmulators.addParameter("emulator-arm.exe");
-                OutputUtils.checkResult(RunUtils.execute(commandLineForStoppingEmulators));
-                break;
-            }
-            else {
-                if (!isDdmsStopped && SystemInfo.isUnix) {
-                    stopEmulator();
-                    stopDdmsProcess();
-                    isDdmsStopped = true;
-                }
-                commandLineForStoppingEmulators.setExePath(pathManager.getPlatformToolsFolderInAndroidSdk() + "/adb");
-                commandLineForStoppingEmulators.addParameter("-s");
-                commandLineForStoppingEmulators.addParameter(matcher.group());
-                commandLineForStoppingEmulators.addParameter("emu");
-                commandLineForStoppingEmulators.addParameter("kill");
-                OutputUtils.checkResult(RunUtils.execute(commandLineForStoppingEmulators));
-            }
-        }
-        OutputUtils.checkResult(RunUtils.execute(commandLineForListOfDevices));
-    }
-}
diff --git a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/gradle/GradleRunner.java b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/gradle/GradleRunner.java
deleted file mode 100644
index e773925..0000000
--- a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/gradle/GradleRunner.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.android.tests.gradle;
-
-import com.intellij.execution.configurations.GeneralCommandLine;
-import com.intellij.openapi.util.SystemInfo;
-import org.jetbrains.kotlin.android.tests.OutputUtils;
-import org.jetbrains.kotlin.android.tests.PathManager;
-import org.jetbrains.kotlin.android.tests.run.RunResult;
-import org.jetbrains.kotlin.android.tests.run.RunUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class GradleRunner {
-    private final List<String> listOfCommands;
-
-    public GradleRunner(PathManager pathManager) {
-        listOfCommands = new ArrayList<>();
-        String cmdName = SystemInfo.isWindows ? "gradlew.bat" : "gradlew";
-        listOfCommands.add(pathManager.getTmpFolder() + "/" + cmdName);
-        listOfCommands.add("--no-daemon");
-        listOfCommands.add("--build-file");
-        listOfCommands.add(pathManager.getTmpFolder() + "/build.gradle");
-    }
-
-
-    public void clean() {
-        System.out.println("Building gradle project...");
-        RunResult result = RunUtils.execute(generateCommandLine("clean"));
-        OutputUtils.checkResult(result);
-    }
-
-    public void assembleAndroidTest() {
-        System.out.println("Building gradle project...");
-        GeneralCommandLine build = generateCommandLine("assembleAndroidTest");
-        build.addParameter("--stacktrace");
-        build.addParameter("--warn");
-        RunResult result = RunUtils.execute(build);
-        OutputUtils.checkResult(result);
-    }
-
-    public void installDebugAndroidTest() {
-        System.out.println("Install tests...");
-        OutputUtils.checkResult(RunUtils.execute(generateCommandLine("installDebug")));
-        OutputUtils.checkResult(RunUtils.execute(generateCommandLine("installDebugAndroidTest")));
-    }
-
-    public void uninstallDebugAndroidTest() {
-        System.out.println("Uninstall tests...");
-        RunUtils.execute(generateCommandLine("uninstallDebugAndroidTest"));
-        RunUtils.execute(generateCommandLine("uninstallDebug"));
-    }
-
-    public String connectedDebugAndroidTest() {
-        System.out.println("Starting tests...");
-        GeneralCommandLine test = generateCommandLine("connectedAndroidTest");
-        test.addParameters("--stacktrace");
-        test.addParameters("--continue"); //run all flavors even if any fail
-        return RunUtils.execute(test).getOutput();
-    }
-
-    private GeneralCommandLine generateCommandLine(String taskName) {
-        GeneralCommandLine commandLine = new GeneralCommandLine(listOfCommands);
-        commandLine.addParameter(taskName);
-        return commandLine;
-    }
-}
diff --git a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/run/RunResult.java b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/run/RunResult.java
deleted file mode 100644
index 6db84cd..0000000
--- a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/run/RunResult.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2010-2015 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.tests.run;
-
-public class RunResult {
-    private final boolean status;
-    private final String output;
-
-    public RunResult(boolean ok, String output) {
-        this.status = ok;
-        this.output = output;
-    }
-
-    //true - ok
-    //false - fail
-    public boolean getStatus() {
-        return status;
-    }
-
-    public String getOutput() {
-        return output;
-    }
-}
diff --git a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/run/RunUtils.java b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/run/RunUtils.java
deleted file mode 100644
index 2169759..0000000
--- a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/run/RunUtils.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright 2010-2015 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.tests.run;
-
-import com.google.common.base.Charsets;
-import com.intellij.execution.ExecutionException;
-import com.intellij.execution.configurations.GeneralCommandLine;
-import com.intellij.execution.process.OSProcessHandler;
-import com.intellij.execution.process.ProcessAdapter;
-import com.intellij.execution.process.ProcessEvent;
-import com.intellij.execution.process.ProcessOutputTypes;
-import com.intellij.openapi.util.Key;
-import com.intellij.openapi.util.text.StringUtil;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.jetbrains.kotlin.android.tests.OutputUtils;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-public class RunUtils {
-    private RunUtils() {
-    }
-
-    public static class RunSettings {
-        public final GeneralCommandLine commandLine;
-        public final String input;
-        public final boolean waitForEnd;
-        public final String outputPrefix;
-        public final boolean printOutputAtAppearance;
-
-        public RunSettings(
-                GeneralCommandLine commandLine,
-                @Nullable String input,
-                boolean waitForEnd,
-                @Nullable String outputPrefix,
-                boolean printOutputAtAppearance
-        ) {
-            this.commandLine = commandLine;
-            this.input = input;
-            this.waitForEnd = waitForEnd;
-            this.outputPrefix = outputPrefix;
-            this.printOutputAtAppearance = printOutputAtAppearance;
-        }
-
-        public RunSettings(GeneralCommandLine commandLine) {
-            this.commandLine = commandLine;
-            this.input = null;
-            this.waitForEnd = true;
-            this.outputPrefix = null;
-            this.printOutputAtAppearance = false;
-        }
-
-        @Override
-        public String toString() {
-            return "commandLine=" + commandLine.getCommandLineString() + " " +
-                   "input=" + input + " " +
-                   "waitForEnd=" + waitForEnd + " " +
-                   "outputPrefix=" + outputPrefix + " " +
-                   "printOutputAtAppearance=" + printOutputAtAppearance + " ";
-        }
-    }
-
-    public static RunResult execute(@NotNull GeneralCommandLine commandLine) {
-        return run(new RunSettings(commandLine));
-    }
-
-    public static RunResult execute(@NotNull RunSettings settings) {
-        assert settings.waitForEnd : "Use executeOnSeparateThread() instead";
-        return run(settings);
-    }
-
-    public static void executeOnSeparateThread(@NotNull RunSettings settings) {
-        assert !settings.waitForEnd : "Use execute() instead";
-        new Thread(() -> run(settings)).start();
-    }
-
-    private static RunResult run(RunSettings settings) {
-        System.out.println("RUN COMMAND: " + settings);
-        StringBuilder stdOut = new StringBuilder();
-        StringBuilder stdErr = new StringBuilder();
-
-        OSProcessHandler handler;
-        try {
-            handler = new OSProcessHandler(settings.commandLine.createProcess(), settings.commandLine.getCommandLineString(), Charsets.UTF_8);
-            if (settings.input != null) {
-                handler.getProcessInput().write(settings.input.getBytes());
-            }
-            close(handler.getProcessInput());
-        }
-        catch (ExecutionException | IOException e) {
-            return new RunResult(false, getStackTrace(e));
-        }
-
-        handler.addProcessListener(new ProcessAdapter() {
-            @Override
-            public void processTerminated(ProcessEvent event) {
-                System.out.println("TERMINATED: " + settings.commandLine);
-                super.processTerminated(event);
-            }
-
-            @Override
-            public void onTextAvailable(ProcessEvent event, Key outputType) {
-                String str = event.getText();
-                if (outputType == ProcessOutputTypes.STDOUT || outputType == ProcessOutputTypes.SYSTEM) {
-                    appendToContent(stdOut, str);
-                }
-                else if (outputType == ProcessOutputTypes.STDERR) {
-                    appendToContent(stdErr, str);
-                }
-            }
-
-            private synchronized void appendToContent(StringBuilder content, String line) {
-                if (settings.printOutputAtAppearance) {
-                    System.out.println(getPrefixString() + StringUtil.trimTrailing(line));
-                    System.out.flush();
-                }
-                else {
-                    content.append(getPrefixString());
-                    content.append(StringUtil.trimTrailing(line));
-                    content.append("\n");
-                }
-            }
-
-            private String getPrefixString() {
-                return (settings.outputPrefix != null) ? settings.outputPrefix + " " : "";
-            }
-        });
-
-        handler.startNotify();
-
-        if (settings.waitForEnd) {
-            String timeoutAsString = System.getenv("kotlin.tests.android.timeout");
-            if (timeoutAsString == null) {
-                timeoutAsString = "30";
-                System.err.println("Default value for timeout used: timeout = 30 min. You can change it using 'kotlin.tests.android.timeout' environment variable");
-            }
-            int timeout;
-            try {
-                timeout = Integer.parseInt(timeoutAsString);
-            }
-            catch (NumberFormatException e) {
-                timeout = 30;
-                System.err.println("Timeout system property should be a number: " + timeoutAsString);
-            }
-            handler.waitFor(timeout * 60 * 1000);
-
-            if (!handler.isProcessTerminated()) {
-                System.out.println("Output before handler.isProcessTerminated() " + settings.commandLine);
-                System.out.println(stdOut);
-                System.err.println(stdErr);
-                return new RunResult(false, "Timeout exception: execution was terminated after ~" + timeout + " min.");
-            }
-        }
-        else {
-            handler.waitFor();
-        }
-
-        int exitCode = handler.getProcess().exitValue();
-
-        if (exitCode != 0) {
-            return new RunResult(false, builderToString(stdOut) + builderToString(stdErr));
-        }
-        else {
-            String output = builderToString(stdOut) + builderToString(stdErr);
-            if (OutputUtils.isBuildFailed(output)) {
-                return new RunResult(false, output);
-            }
-            if (!settings.commandLine.getCommandLineString().contains("install")) {
-                System.out.print(output);
-            }
-            return new RunResult(true, output);
-        }
-    }
-
-    private static String builderToString(StringBuilder builder) {
-        return builder.length() > 0 ? builder.toString() : "";
-    }
-
-    public static void close(Closeable closeable) {
-        try {
-            if (closeable != null) {
-                closeable.close();
-            }
-        }
-        catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    public static String getStackTrace(Throwable t) {
-        StringWriter writer = new StringWriter();
-        PrintWriter printWriter = new PrintWriter(writer);
-        try {
-            printWriter.write(t.getMessage());
-            printWriter.write("\n");
-            t.printStackTrace(printWriter);
-        }
-        finally {
-            close(printWriter);
-        }
-        return writer.toString();
-    }
-
-}
diff --git a/compiler/testData/codegen/box/annotations/typeAnnotations/annotationOnSecondParameter.kt b/compiler/testData/codegen/box/annotations/typeAnnotations/annotationOnSecondParameter.kt
index d4933a8..eff4cae 100644
--- a/compiler/testData/codegen/box/annotations/typeAnnotations/annotationOnSecondParameter.kt
+++ b/compiler/testData/codegen/box/annotations/typeAnnotations/annotationOnSecondParameter.kt
@@ -1,6 +1,8 @@
 // TARGET_BACKEND: JVM_IR
 // WITH_STDLIB
 // FULL_JDK
+// IGNORE_BACKEND: ANDROID
+// Android is ignored due to KT-66391
 
 @Target(AnnotationTarget.TYPE_PARAMETER)
 annotation class Ann(val value: String)
diff --git a/compiler/testData/codegen/box/intrinsics/monitorEnterMonitorExit.kt b/compiler/testData/codegen/box/intrinsics/monitorEnterMonitorExit.kt
index 3cfd3829..3a42569 100644
--- a/compiler/testData/codegen/box/intrinsics/monitorEnterMonitorExit.kt
+++ b/compiler/testData/codegen/box/intrinsics/monitorEnterMonitorExit.kt
@@ -1,6 +1,8 @@
 // TARGET_BACKEND: JVM
 // WITH_STDLIB
 // DISABLE_IR_VISIBILITY_CHECKS: ANY
+// IGNORE_BACKEND: ANDROID
+// Android test generator always runs IR validator
 
 import kotlin.jvm.internal.unsafe.*
 
diff --git a/compiler/testData/codegen/box/polymorphicSignature/insideComplexExpression.kt b/compiler/testData/codegen/box/polymorphicSignature/insideComplexExpression.kt
index 1a8e30c..93749c9 100644
--- a/compiler/testData/codegen/box/polymorphicSignature/insideComplexExpression.kt
+++ b/compiler/testData/codegen/box/polymorphicSignature/insideComplexExpression.kt
@@ -1,6 +1,7 @@
 // TARGET_BACKEND: JVM
 // WITH_STDLIB
 // FULL_JDK
+// SKIP_JDK6
 
 import java.lang.invoke.MethodHandles
 import java.lang.invoke.MethodType
diff --git a/compiler/testData/codegen/box/polymorphicSignature/insideIf.kt b/compiler/testData/codegen/box/polymorphicSignature/insideIf.kt
index 5f6385a..5cc6687 100644
--- a/compiler/testData/codegen/box/polymorphicSignature/insideIf.kt
+++ b/compiler/testData/codegen/box/polymorphicSignature/insideIf.kt
@@ -1,5 +1,6 @@
 // TARGET_BACKEND: JVM
 // WITH_STDLIB
+// SKIP_JDK6
 // FULL_JDK
 
 import java.lang.invoke.MethodHandles
diff --git a/compiler/testData/codegen/box/polymorphicSignature/insideTry.kt b/compiler/testData/codegen/box/polymorphicSignature/insideTry.kt
index eaa0244..4f76c77b 100644
--- a/compiler/testData/codegen/box/polymorphicSignature/insideTry.kt
+++ b/compiler/testData/codegen/box/polymorphicSignature/insideTry.kt
@@ -1,6 +1,7 @@
 // TARGET_BACKEND: JVM
 // WITH_STDLIB
 // FULL_JDK
+// SKIP_JDK6
 
 import java.lang.invoke.MethodHandles
 import java.lang.invoke.MethodType
diff --git a/compiler/testData/codegen/box/polymorphicSignature/insideWhen.kt b/compiler/testData/codegen/box/polymorphicSignature/insideWhen.kt
index a2c83b1..90ecafb 100644
--- a/compiler/testData/codegen/box/polymorphicSignature/insideWhen.kt
+++ b/compiler/testData/codegen/box/polymorphicSignature/insideWhen.kt
@@ -1,4 +1,5 @@
 // TARGET_BACKEND: JVM
+// SKIP_JDK6
 // WITH_STDLIB
 // FULL_JDK
 
diff --git a/compiler/testData/codegen/box/reflection/callBy/brokenDefaultParametersFromDifferentFunctionsJvmDefault.kt b/compiler/testData/codegen/box/reflection/callBy/brokenDefaultParametersFromDifferentFunctionsJvmDefault.kt
index 589eb46..7db0f2d 100644
--- a/compiler/testData/codegen/box/reflection/callBy/brokenDefaultParametersFromDifferentFunctionsJvmDefault.kt
+++ b/compiler/testData/codegen/box/reflection/callBy/brokenDefaultParametersFromDifferentFunctionsJvmDefault.kt
@@ -1,4 +1,6 @@
 // TARGET_BACKEND: JVM_IR
+// KT-66789
+// IGNORE_BACKEND: ANDROID
 // WITH_REFLECT
 // LANGUAGE: +ValueClasses
 // JVM_DEFAULT_MODE: all
diff --git a/compiler/testData/codegen/box/reflection/callBy/mfvcInterfaceJvmDefault.kt b/compiler/testData/codegen/box/reflection/callBy/mfvcInterfaceJvmDefault.kt
index a0c6ce2..98363b4 100644
--- a/compiler/testData/codegen/box/reflection/callBy/mfvcInterfaceJvmDefault.kt
+++ b/compiler/testData/codegen/box/reflection/callBy/mfvcInterfaceJvmDefault.kt
@@ -2,6 +2,8 @@
 // WITH_REFLECT
 // LANGUAGE: +ValueClasses
 // JVM_DEFAULT_MODE: all
+// KT-66789
+// IGNORE_BACKEND: ANDROID
 
 
 import kotlin.test.assertEquals
diff --git a/compiler/testData/codegen/box/reflection/classes/createInstance.kt b/compiler/testData/codegen/box/reflection/classes/createInstance.kt
index 9daba5a..233addd 100644
--- a/compiler/testData/codegen/box/reflection/classes/createInstance.kt
+++ b/compiler/testData/codegen/box/reflection/classes/createInstance.kt
@@ -1,5 +1,7 @@
 // TARGET_BACKEND: JVM
+// IGNORE_BACKEND: ANDROID
 // WITH_REFLECT
+// Android is ignored due to KT-66391
 
 import kotlin.reflect.full.createInstance
 import kotlin.test.assertTrue
diff --git a/compiler/testData/codegen/box/reflection/localClasses/localNestedClasses.jvm_abi.txt b/compiler/testData/codegen/box/reflection/localClasses/localNestedClasses.jvm_abi.txt
index 8dc8055..32ecf0e 100644
--- a/compiler/testData/codegen/box/reflection/localClasses/localNestedClasses.jvm_abi.txt
+++ b/compiler/testData/codegen/box/reflection/localClasses/localNestedClasses.jvm_abi.txt
@@ -1,12 +1,12 @@
 MODULE main
-	CLASS LocalNestedClassesKt$foo1$X$Y.class
+	CLASS test/LocalNestedClassesKt$foo1$X$Y.class
 		CLASS METADATA
 			Property: class.metadata.companionObject
 				K1
 					null
 				K2
 					Z
-	CLASS LocalNestedClassesKt$foo2$X$Y.class
+	CLASS test/LocalNestedClassesKt$foo2$X$Y.class
 		CLASS METADATA
 			Property: class.metadata.companionObject
 				K1
diff --git a/compiler/testData/codegen/box/reflection/localClasses/localNestedClasses.kt b/compiler/testData/codegen/box/reflection/localClasses/localNestedClasses.kt
index 6868c17..9e798d5 100644
--- a/compiler/testData/codegen/box/reflection/localClasses/localNestedClasses.kt
+++ b/compiler/testData/codegen/box/reflection/localClasses/localNestedClasses.kt
@@ -2,10 +2,13 @@
 // WITH_REFLECT
 // JVM_ABI_K1_K2_DIFF: K2 names companion objects in metadata correctly
 
+package test
+
+import kotlin.reflect.KClass
 import kotlin.reflect.KType
 import kotlin.reflect.full.memberProperties
 
-val KType.str get() = classifier.toString()
+val KType.str get() = (classifier as KClass<*>).java.name
 
 class A {
     fun foo(): String {
@@ -131,14 +134,14 @@
 }
 
 fun box(): String {
-    if (A().foo() != "class A\$foo\$Nested\$Inner") return "Fail 1"
-    if (foo1() != "class LocalNestedClassesKt\$foo1\$X\$Y\$Z") return "Fail 2"
-    if (foo2() != "class LocalNestedClassesKt\$foo2\$X\$Y\$Companion") return "Fail 3"
-    if (foo3() != "class LocalNestedClassesKt\$foo3\$X\$Y\$prop\$1") return "Fail 4"
-    if (foo4() != "class LocalNestedClassesKt\$foo4\$A\$B\$C\$bar\$D") return "Fail 5"
-    if (foo5() != "class LocalNestedClassesKt\$foo5\$1\$bar\$1\$foo\$A\$B") return "Fail 6"
-    if (foo6() != "class LocalNestedClassesKt\$foo6\$1\$bar\$A\$B\$C") return "Fail 7"
-    if (foo7() != "class LocalNestedClassesKt\$foo7\$x\$1\$y\$1\$z\$1") return "Fail 8"
+    if (A().foo() != "test.A\$foo\$Nested\$Inner") return "Fail 1"
+    if (foo1() != "test.LocalNestedClassesKt\$foo1\$X\$Y\$Z") return "Fail 2"
+    if (foo2() != "test.LocalNestedClassesKt\$foo2\$X\$Y\$Companion") return "Fail 3"
+    if (foo3() != "test.LocalNestedClassesKt\$foo3\$X\$Y\$prop\$1") return "Fail 4"
+    if (foo4() != "test.LocalNestedClassesKt\$foo4\$A\$B\$C\$bar\$D") return "Fail 5"
+    if (foo5() != "test.LocalNestedClassesKt\$foo5\$1\$bar\$1\$foo\$A\$B") return "Fail 6"
+    if (foo6() != "test.LocalNestedClassesKt\$foo6\$1\$bar\$A\$B\$C") return "Fail 7"
+    if (foo7() != "test.LocalNestedClassesKt\$foo7\$x\$1\$y\$1\$z\$1") return "Fail 8"
 
     return "OK"
 }
\ No newline at end of file
diff --git a/compiler/testData/codegen/box/syntheticAccessors/kt49316.kt b/compiler/testData/codegen/box/syntheticAccessors/kt49316.kt
index eca03bd..67e6284 100644
--- a/compiler/testData/codegen/box/syntheticAccessors/kt49316.kt
+++ b/compiler/testData/codegen/box/syntheticAccessors/kt49316.kt
@@ -15,6 +15,9 @@
 //   [IR VALIDATION] IrValidationBeforeLoweringPhase: The following element references a 'private' declaration that is invisible in the current scope.
 //   SET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:s type:kotlin.String visibility:private [final]' type=kotlin.Unit origin=null
 
+// IGNORE_BACKEND: ANDROID
+// Android test generator always runs IR validator
+
 // FILE: kt49316.kt
 import a.*
 
diff --git a/compiler/testData/codegen/boxInline/special/monitorEnterExit.kt b/compiler/testData/codegen/boxInline/special/monitorEnterExit.kt
index 6e91b1b..c4b84d0 100644
--- a/compiler/testData/codegen/boxInline/special/monitorEnterExit.kt
+++ b/compiler/testData/codegen/boxInline/special/monitorEnterExit.kt
@@ -1,6 +1,8 @@
 // TARGET_BACKEND: JVM_IR
 // WITH_STDLIB
 // DISABLE_IR_VISIBILITY_CHECKS: ANY
+// IGNORE_BACKEND: ANDROID
+// Android test generator always runs IR validator
 // FILE: mySynchronized.kt
 
 import kotlin.jvm.internal.unsafe.*
diff --git a/dependencies/android-sdk/build.gradle.kts b/dependencies/android-sdk/build.gradle.kts
index 4985822..8729d51 100644
--- a/dependencies/android-sdk/build.gradle.kts
+++ b/dependencies/android-sdk/build.gradle.kts
@@ -24,21 +24,29 @@
     }
 }
 
-val platformToolsVersion = "r28.0.1"
-val sdkToolsVersion = "4333796" /*26.1.1*/
+val buildToolsVersion = "r29.0.3"
+val platformToolsVersion = "r35.0.0"
+val sdkToolsVersion = "4333796" // 26.1.1
 val emulatorVersion = "5264690"
 
 dependencies {
-    listOf("linux", "windows", "darwin").forEach {
-        implicitDependencies("google:platform-tools:$platformToolsVersion:$it@zip")
-        implicitDependencies("google:sdk-tools-$it:$sdkToolsVersion@zip")
-        implicitDependencies("google:emulator-$it:$emulatorVersion@zip")
-    }
+    implicitDependencies("google:build-tools:$buildToolsVersion:linux@zip")
+    implicitDependencies("google:build-tools:$buildToolsVersion:windows@zip")
+    implicitDependencies("google:build-tools:$buildToolsVersion:macosx@zip")
+
+    implicitDependencies("google:platform-tools:$platformToolsVersion:linux@zip")
+    implicitDependencies("google:platform-tools:$platformToolsVersion:windows@zip")
+    implicitDependencies("google:platform-tools:$platformToolsVersion:darwin@zip")
+
+    implicitDependencies("google:sdk-tools-linux:$sdkToolsVersion@zip")
+    implicitDependencies("google:sdk-tools-windows:$sdkToolsVersion@zip")
+    implicitDependencies("google:sdk-tools-darwin:$sdkToolsVersion@zip")
 }
 
 val androidSdk by configurations.creating
 val androidJar by configurations.creating
-val androidEmulator by configurations.creating
+val androidPlatform by configurations.creating
+val buildTools by configurations.creating
 
 val sdkDestDirName = "androidSdk"
 
@@ -201,6 +209,7 @@
 unzipSdkTask("emulator-$toolsOsDarwin", emulatorVersion, "", "", prepareTask = prepareEmulator)
 unzipSdkTask("armeabi-v7a", "19", "system-images/android-19/default", "r05", prepareTask = prepareEmulator)
 unzipSdkTask("x86", "19", "system-images/android-19/default", "r06", prepareTask = prepareEmulator)
+unzipSdkTask("x86", "30", "system-images/android-30/aosp_atd", "r03", prepareTask = prepareEmulator)
 
 val clean by task<Delete> {
     delete(layout.buildDirectory)
@@ -213,7 +222,3 @@
 artifacts.add(androidJar.name, layout.buildDirectory.file("$sdkDestDirName/platforms/android-26/android.jar")) {
     builtBy(preparePlatform)
 }
-
-artifacts.add(androidEmulator.name, layout.buildDirectory.dir(sdkDestDirName)) {
-    builtBy(prepareEmulator)
-}
\ No newline at end of file
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 0225c6e..748e1ee 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -1852,6 +1852,16 @@
             <sha256 value="db78f726d5dc653706dcd15a462ab1b946c643f598df76906c4c1858411c54df" origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="google" name="platform-tools" version="r35.0.0">
+         <artifact name="platform-tools-r35.0.0-linux.zip">
+            <md5 value="8c054a42356820d29003be3ce33accd6" origin="Generated by Gradle"/>
+            <sha256 value="62fc977c1b7622ef8dbd6fe1312987d9b139aa8a0b06e88573c1b60129399d49" origin="Generated by Gradle"/>
+         </artifact>
+         <artifact name="platform-tools-r35.0.0-windows.zip">
+            <md5 value="e8a786dc533d91b1e6c3dc38722bbaba" origin="Generated by Gradle"/>
+            <sha256 value="7ab78a8f8b305ae4d0de647d99c43599744de61a0838d3a47bda0cdffefee87e" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="google" name="sdk-tools-darwin" version="4333796">
          <artifact name="sdk-tools-darwin-4333796.zip">
             <md5 value="d912ced50eea9f9a842aef9892c956f1" origin="Generated by Gradle"/>
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts
index c20fbb8..bb4cc5c 100644
--- a/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts
@@ -266,12 +266,12 @@
 
 tasks.register<Test>("kgpAllParallelTests") {
     group = KGP_TEST_TASKS_GROUP
-    description = "Runs all tests for Kotlin Gradle plugins except daemon ones"
+    description = "Runs all tests for Kotlin Gradle plugins except daemon and codegen ones"
 
     maxParallelForks = maxParallelTestForks
 
     useJUnitPlatform {
-        excludeTags("DaemonsKGP")
+        excludeTags("DaemonsKGP", "AndroidCodegen")
     }
 }
 
@@ -281,7 +281,7 @@
     maxParallelForks = maxParallelTestForks
     useJUnitPlatform {
         includeTags("JvmKGP")
-        excludeTags("JsKGP", "NativeKGP", "DaemonsKGP", "OtherKGP", "MppKGP", "AndroidKGP", "SwiftExportKGP")
+        excludeTags("JsKGP", "NativeKGP", "DaemonsKGP", "OtherKGP", "MppKGP", "AndroidKGP", "AndroidCodegen", "SwiftExportKGP")
     }
 }
 
@@ -291,7 +291,7 @@
     maxParallelForks = maxParallelTestForks
     useJUnitPlatform {
         includeTags("SwiftExportKGP")
-        excludeTags("JvmKGP", "JsKGP", "DaemonsKGP", "OtherKGP", "MppKGP", "AndroidKGP", "NativeKGP")
+        excludeTags("JvmKGP", "JsKGP", "DaemonsKGP", "OtherKGP", "MppKGP", "AndroidKGP", "AndroidCodegen", "NativeKGP")
     }
     applyKotlinNativeFromCurrentBranchIfNeeded()
 }
@@ -302,7 +302,7 @@
     maxParallelForks = maxParallelTestForks
     useJUnitPlatform {
         includeTags("JsKGP")
-        excludeTags("JvmKGP", "NativeKGP", "DaemonsKGP", "OtherKGP", "MppKGP", "AndroidKGP", "SwiftExportKGP")
+        excludeTags("JvmKGP", "NativeKGP", "DaemonsKGP", "OtherKGP", "MppKGP", "AndroidKGP", "AndroidCodegen", "SwiftExportKGP")
     }
 }
 
@@ -312,7 +312,7 @@
     maxParallelForks = maxParallelTestForks
     useJUnitPlatform {
         includeTags("NativeKGP")
-        excludeTags("JvmKGP", "JsKGP", "DaemonsKGP", "OtherKGP", "MppKGP", "AndroidKGP", "SwiftExportKGP")
+        excludeTags("JvmKGP", "JsKGP", "DaemonsKGP", "OtherKGP", "MppKGP", "AndroidKGP", "AndroidCodegen", "SwiftExportKGP")
     }
     applyKotlinNativeFromCurrentBranchIfNeeded()
 }
@@ -325,7 +325,7 @@
 
     useJUnitPlatform {
         includeTags("DaemonsKGP")
-        excludeTags("JvmKGP", "JsKGP", "NativeKGP", "OtherKGP", "MppKGP", "AndroidKGP", "SwiftExportKGP")
+        excludeTags("JvmKGP", "JsKGP", "NativeKGP", "OtherKGP", "MppKGP", "AndroidKGP", "AndroidCodegen", "SwiftExportKGP")
     }
 }
 
@@ -335,7 +335,7 @@
     maxParallelForks = maxParallelTestForks
     useJUnitPlatform {
         includeTags("OtherKGP")
-        excludeTags("JvmKGP", "JsKGP", "NativeKGP", "DaemonsKGP", "MppKGP", "AndroidKGP", "SwiftExportKGP")
+        excludeTags("JvmKGP", "JsKGP", "NativeKGP", "DaemonsKGP", "MppKGP", "AndroidKGP", "AndroidCodegen", "SwiftExportKGP")
     }
     applyKotlinNativeFromCurrentBranchIfNeeded()
 }
@@ -346,7 +346,7 @@
     maxParallelForks = maxParallelTestForks
     useJUnitPlatform {
         includeTags("MppKGP")
-        excludeTags("JvmKGP", "JsKGP", "NativeKGP", "DaemonsKGP", "OtherKGP", "AndroidKGP", "SwiftExportKGP")
+        excludeTags("JvmKGP", "JsKGP", "NativeKGP", "DaemonsKGP", "OtherKGP", "AndroidKGP", "AndroidCodegen", "SwiftExportKGP")
     }
     applyKotlinNativeFromCurrentBranchIfNeeded()
 }
@@ -357,7 +357,17 @@
     maxParallelForks = maxParallelTestForks
     useJUnitPlatform {
         includeTags("AndroidKGP")
-        excludeTags("JvmKGP", "JsKGP", "NativeKGP", "DaemonsKGP", "OtherKGP", "MppKGP", "SwiftExportKGP")
+        excludeTags("JvmKGP", "JsKGP", "NativeKGP", "DaemonsKGP", "OtherKGP", "MppKGP", "AndroidCodegen", "SwiftExportKGP")
+    }
+}
+
+val androidCodegenTestsTask = tasks.register<Test>("kgpAndroidCodegenTests") {
+    group = KGP_TEST_TASKS_GROUP
+    description = "Run Android codegen tests"
+    maxParallelForks = maxParallelTestForks
+    useJUnitPlatform {
+        includeTags("AndroidCodegen")
+        excludeTags("JvmKGP", "JsKGP", "NativeKGP", "DaemonsKGP", "OtherKGP", "MppKGP", "AndroidKGP", "SwiftExportKGP")
     }
 }
 
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/android/AndroidCodegenIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/android/AndroidCodegenIT.kt
new file mode 100644
index 0000000..79b3df7
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/android/AndroidCodegenIT.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.gradle.android
+
+import org.gradle.api.logging.LogLevel
+import org.gradle.util.GradleVersion
+import org.jetbrains.kotlin.gradle.testbase.*
+import org.junit.jupiter.api.DisplayName
+import org.junit.jupiter.api.condition.OS
+
+@DisplayName("codegen tests on android")
+@AndroidTestVersions(minVersion = TestVersions.AGP.AGP_84, maxVersion = TestVersions.AGP.AGP_84)
+/*
+ * Non-virtualized runners are necessary with current configuration.
+ * This build utilizes the Android Emulator to execute codegen tests,
+ * requiring a VM hypervisor for optimal performance.
+ * Nested virtualization on CI runners except physical Macs fails.
+ */
+@OsCondition(enabledOnCI = [OS.MAC])
+@AndroidCodegenTests
+class AndroidCodegenIT : KGPBaseTest() {
+    @GradleAndroidTest
+    fun testAndroidCodegen(
+        gradleVersion: GradleVersion,
+        agpVersion: String,
+        jdkVersion: JdkVersions.ProvidedJdk,
+    ) {
+        project(
+            "codegen-tests",
+            gradleVersion,
+            buildOptions = defaultBuildOptions.copy(
+                androidVersion = agpVersion,
+                freeArgs = listOf("-Pandroid.useAndroidX=true", "-Pandroid.experimental.testOptions.managedDevices.setupTimeoutMinutes=0"),
+                logLevel = LogLevel.LIFECYCLE
+            ),
+            enableGradleDebug = false,
+            buildJdk = jdkVersion.location
+        ) {
+            build("assembleAndroidTest", forceOutput = true, enableGradleDaemonMemoryLimitInMb = 6000)
+            build("nexusCheck", forceOutput = true, enableGradleDaemonMemoryLimitInMb = 6000)
+        }
+    }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/tagsValidator.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/tagsValidator.kt
index 5368ccb..4ddd696a20 100644
--- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/tagsValidator.kt
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/tagsValidator.kt
@@ -58,6 +58,7 @@
                 it is NativeGradlePluginTests ||
                 it is MppGradlePluginTests ||
                 it is AndroidGradlePluginTests ||
+                it is AndroidCodegenTests ||
                 it is OtherGradlePluginTests ||
                 it is SwiftExportGradlePluginTests
     }
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testTags.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testTags.kt
index 09ff9f8..8cf3158 100644
--- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testTags.kt
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testTags.kt
@@ -92,6 +92,18 @@
 annotation class AndroidGradlePluginTests
 
 /**
+ * Add it to the tests running codegen tests with Android emulator.
+ *
+ * You could add tag onto test suite once, but then all tests
+ * in test suite should be for the related tag.
+ * Preferably add tag for each test.
+ */
+@Target(AnnotationTarget.CLASS, AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION)
+@Retention(AnnotationRetention.RUNTIME)
+@Tag("AndroidCodegen")
+annotation class AndroidCodegenTests
+
+/**
  * Add it the tests that are not covered by tags above.
  *
  * Usually it would be tests for kapt, serialization plugins, etc...
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/.gitignore b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/.gitignore
new file mode 100644
index 0000000..be8766d
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/.gitignore
@@ -0,0 +1,6 @@
+test-classes/
+src/*
+!src/main/
+build/
+.gradle/
+
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/build.gradle.kts
new file mode 100644
index 0000000..bd292df
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/build.gradle.kts
@@ -0,0 +1,108 @@
+plugins {
+    id("com.android.application")
+}
+
+android {
+    namespace = "org.jetbrains.kotlin.android.tests"
+
+    defaultConfig {
+        applicationId = "org.jetbrains.kotlin.android.tests"
+        minSdk = 19
+        compileSdk = 34
+        targetSdk = 34
+        versionCode = 1
+        versionName = "1.0"
+        testApplicationId = "org.jetbrains.kotlin.android.tests.gradle"
+        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes {
+        debug {
+            isMinifyEnabled = false
+            proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
+        }
+    }
+
+    flavorDimensions += "box"
+
+    productFlavors {
+        listOf(
+            "common0",
+            "common1",
+            "common2",
+            "common3",
+            "reflect0",
+            "common_ir0",
+            "common_ir1",
+            "common_ir2",
+            "common_ir3",
+            "reflect_ir0"
+        ).forEach {
+            create(it) {
+                dimension = "box"
+            }
+        }
+    }
+
+    testOptions {
+        managedDevices {
+            devices {
+                register<com.android.build.api.dsl.ManagedVirtualDevice>("nexus") {
+                    device = "Nexus One"
+                    apiLevel = 30
+                    systemImageSource = "aosp-atd"
+                }
+            }
+        }
+    }
+}
+
+
+val jarTestFolders by tasks.registering {
+    inputs.dir("test-classes")
+        .withPropertyName("classDirectories")
+        .withPathSensitivity(PathSensitivity.RELATIVE)
+
+    val testClassesDirectories = file("test-classes").listFiles().filter { it.isDirectory }
+    val libsJars = testClassesDirectories.map { file("libs").resolve("${it.name}.jar") }
+    outputs.files(libsJars).withPropertyName("libsJars")
+
+    doLast {
+        testClassesDirectories.forEach { dir ->
+            logger.info("Jar {} folder", dir.name)
+            ant.withGroovyBuilder {
+                "jar"("basedir" to dir.path, "destfile" to "libs/${dir.name}.jar")
+            }
+        }
+    }
+}
+
+tasks.preBuild {
+    dependsOn(jarTestFolders)
+}
+
+val kotlin_version: String = "2.1.255-SNAPSHOT"
+
+repositories {
+    mavenCentral()
+    mavenLocal()
+    google()
+}
+
+dependencies {
+    implementation(kotlin("stdlib", kotlin_version))
+    implementation(kotlin("test-junit", kotlin_version))
+    implementation("junit:junit:4.13.2")
+    androidTestImplementation("androidx.test:runner:1.5.2")
+    androidTestImplementation("androidx.test.ext:junit:1.1.5")
+
+    android.applicationVariants.configureEach {
+        productFlavors.forEach { flavor ->
+            val configuration = "${flavor.name}Implementation"
+            configuration(project.fileTree("libs") { include("${flavor.name}.jar") })
+            if (flavor.name.startsWith("reflect")) {
+                configuration(kotlin("reflect", kotlin_version))
+            }
+        }
+    }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/gradle.properties
new file mode 100644
index 0000000..da87b2c
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/gradle.properties
@@ -0,0 +1,2 @@
+org.gradle.jvmargs=-Xmx4G
+android.useAndroidX=true
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/gradle/wrapper/gradle-wrapper.jar b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e644113
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/gradle/wrapper/gradle-wrapper.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..68e8816
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,8 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/gradlew b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/gradlew
new file mode 100644
index 0000000..b740cf1
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/gradlew
@@ -0,0 +1,249 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# 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
+#
+#      https://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.
+#
+
+##############################################################################
+#
+#   Gradle start up script for POSIX generated by Gradle.
+#
+#   Important for running:
+#
+#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+#       noncompliant, but you have some other compliant shell such as ksh or
+#       bash, then to run this script, type that shell name before the whole
+#       command line, like:
+#
+#           ksh Gradle
+#
+#       Busybox and similar reduced shells will NOT work, because this script
+#       requires all of these POSIX shell features:
+#         * functions;
+#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+#         * compound commands having a testable exit status, especially «case»;
+#         * various built-in commands including «command», «set», and «ulimit».
+#
+#   Important for patching:
+#
+#   (2) This script targets any POSIX shell, so it avoids extensions provided
+#       by Bash, Ksh, etc; in particular arrays are avoided.
+#
+#       The "traditional" practice of packing multiple parameters into a
+#       space-separated string is a well documented source of bugs and security
+#       problems, so this is (mostly) avoided, by progressively accumulating
+#       options in "$@", and eventually passing that to Java.
+#
+#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+#       see the in-line comments for details.
+#
+#       There are tweaks for specific operating systems such as AIX, CygWin,
+#       Darwin, MinGW, and NonStop.
+#
+#   (3) This script is generated from the Groovy template
+#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       within the Gradle project.
+#
+#       You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
+    [ -h "$app_path" ]
+do
+    ls=$( ls -ld "$app_path" )
+    link=${ls#*' -> '}
+    case $link in             #(
+      /*)   app_path=$link ;; #(
+      *)    app_path=$APP_HOME$link ;;
+    esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+    echo "$*"
+} >&2
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in                #(
+  CYGWIN* )         cygwin=true  ;; #(
+  Darwin* )         darwin=true  ;; #(
+  MSYS* | MINGW* )  msys=true    ;; #(
+  NONSTOP* )        nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD=$JAVA_HOME/jre/sh/java
+    else
+        JAVACMD=$JAVA_HOME/bin/java
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD=java
+    if ! command -v java >/dev/null 2>&1
+    then
+        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+    case $MAX_FD in #(
+      max*)
+        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
+        MAX_FD=$( ulimit -H -n ) ||
+            warn "Could not query maximum file descriptor limit"
+    esac
+    case $MAX_FD in  #(
+      '' | soft) :;; #(
+      *)
+        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
+        ulimit -n "$MAX_FD" ||
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
+    esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+#   * args from the command line
+#   * the main class name
+#   * -classpath
+#   * -D...appname settings
+#   * --module-path (only if needed)
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+    JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    for arg do
+        if
+            case $arg in                                #(
+              -*)   false ;;                            # don't mess with options #(
+              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
+                    [ -e "$t" ] ;;                      #(
+              *)    false ;;
+            esac
+        then
+            arg=$( cygpath --path --ignore --mixed "$arg" )
+        fi
+        # Roll the args list around exactly as many times as the number of
+        # args, so each arg winds up back in the position where it started, but
+        # possibly modified.
+        #
+        # NB: a `for` loop captures its iteration list before it begins, so
+        # changing the positional parameters here affects neither the number of
+        # iterations, nor the values presented in `arg`.
+        shift                   # remove old arg
+        set -- "$@" "$arg"      # push replacement arg
+    done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+#     and any embedded shellness will be escaped.
+#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+#     treated as '${Hostname}' itself on the command line.
+
+set -- \
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
+        -classpath "$CLASSPATH" \
+        org.gradle.wrapper.GradleWrapperMain \
+        "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+    die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+#   set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+        xargs -n1 |
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+        tr '\n' ' '
+    )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/gradlew.bat b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/gradlew.bat
new file mode 100644
index 0000000..25da30d
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/gradlew.bat
@@ -0,0 +1,92 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem      https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/libs/common_ir0.jar b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/libs/common_ir0.jar
new file mode 100644
index 0000000..d9f0c79
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/libs/common_ir0.jar
Binary files differ
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/libs/common_ir1.jar b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/libs/common_ir1.jar
new file mode 100644
index 0000000..a54efa1
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/libs/common_ir1.jar
Binary files differ
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/libs/common_ir2.jar b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/libs/common_ir2.jar
new file mode 100644
index 0000000..0172020
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/libs/common_ir2.jar
Binary files differ
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/libs/common_ir3.jar b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/libs/common_ir3.jar
new file mode 100644
index 0000000..7bcf35f
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/libs/common_ir3.jar
Binary files differ
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/libs/reflect_ir0.jar b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/libs/reflect_ir0.jar
new file mode 100644
index 0000000..7387bab
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/libs/reflect_ir0.jar
Binary files differ
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/settings.gradle.kts
new file mode 100644
index 0000000..cf639cf4
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/settings.gradle.kts
@@ -0,0 +1,15 @@
+pluginManagement {
+    repositories {
+        mavenLocal()
+        gradlePluginPortal()
+        google()
+    }
+
+//    val test_fixes_version: String by settings
+//    val android_tools_version: String by settings
+
+    plugins {
+//        id("org.jetbrains.kotlin.test.fixes.android") version test_fixes_version
+        id ("com.android.application") version "7.3.1"
+    }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/src/main/AndroidManifest.xml b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0710b01e
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/src/main/AndroidManifest.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <application>
+        <uses-library
+            android:name="android.test.runner"
+            android:required="false" />
+    </application>
+</manifest>
diff --git a/compiler/android-tests/android-module/src/main/java/org/jetbrains/kotlin/android/tests/AbstractCodegenTestCaseOnAndroid.java b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/src/main/java/org/jetbrains/kotlin/android/tests/AbstractCodegenTestCaseOnAndroid.java
similarity index 78%
rename from compiler/android-tests/android-module/src/main/java/org/jetbrains/kotlin/android/tests/AbstractCodegenTestCaseOnAndroid.java
rename to libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/src/main/java/org/jetbrains/kotlin/android/tests/AbstractCodegenTestCaseOnAndroid.java
index 91ee9e4..39248cd 100644
--- a/compiler/android-tests/android-module/src/main/java/org/jetbrains/kotlin/android/tests/AbstractCodegenTestCaseOnAndroid.java
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/codegen-tests/src/main/java/org/jetbrains/kotlin/android/tests/AbstractCodegenTestCaseOnAndroid.java
@@ -20,15 +20,14 @@
 
 import java.lang.reflect.Method;
 
-public class AbstractCodegenTestCaseOnAndroid extends TestCase {
-
-    protected void invokeBoxMethod(Class clazz, String filePath, String expectedResult) throws Exception {
+public abstract class AbstractCodegenTestCaseOnAndroid extends TestCase {
+    protected void invokeBoxMethod(Class<?> clazz, String filePath, String expectedResult) {
         try {
             Method method = clazz.getMethod("box");
             assertEquals(expectedResult, method.invoke(null));
         }
         catch (Throwable e) {
-            throw new RuntimeException("File: " + filePath, e);
+            throw new AssertionError("File: " + filePath, e);
         }
     }
 }
diff --git a/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/tasks.kt b/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/tasks.kt
index f651bb4..81b870d 100644
--- a/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/tasks.kt
+++ b/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/tasks.kt
@@ -316,6 +316,93 @@
     if (tasks.names.contains(taskName)) tasks.named(taskName, T::class.java).apply { configure(body) }
     else tasks.register(taskName, T::class.java, body)
 
+object TaskUtils {
+    fun useAndroidSdk(task: Task) {
+        task.useAndroidConfiguration(systemPropertyName = "android.sdk", configName = "androidSdk")
+    }
+
+    fun useAndroidJar(task: Task) {
+        task.useAndroidConfiguration(systemPropertyName = "android.jar", configName = "androidJar")
+    }
+}
+
+private fun Task.useAndroidConfiguration(systemPropertyName: String, configName: String) {
+    val configuration = with(project) {
+        configurations.getOrCreate(configName)
+            .also {
+                if (it.allDependencies.matching { dep ->
+                        dep is ProjectDependency &&
+                                dep.targetConfiguration == configName &&
+                                dep.dependencyProject.path == ":dependencies:android-sdk"
+                    }.isEmpty()
+                ) {
+                    dependencies.add(
+                        configName,
+                        dependencies.project(":dependencies:android-sdk", configuration = configName)
+                    )
+                }
+            }
+    }
+
+    dependsOn(configuration)
+
+    if (this is Test) {
+        val androidFilePath = configuration.singleFile.canonicalPath
+        doFirst {
+            systemProperty(systemPropertyName, androidFilePath)
+        }
+    }
+}
+
+fun Task.useAndroidSdk() {
+    TaskUtils.useAndroidSdk(this)
+}
+
+fun Task.useAndroidJar() {
+    TaskUtils.useAndroidJar(this)
+}
+
+fun Task.acceptAndroidSdkLicenses() {
+    val androidSdkConfiguration = project.configurations["androidSdk"]
+    val androidSdk = project.objects.fileProperty().apply { set { androidSdkConfiguration.singleFile } }
+
+    dependsOn(androidSdkConfiguration)
+
+    doFirst {
+        val sdkLicensesDir = androidSdk.get().asFile.resolve("licenses").also {
+            if (!it.exists()) it.mkdirs()
+        }
+
+        val sdkLicenses = listOf(
+            "8933bad161af4178b1185d1a37fbf41ea5269c55",
+            "d56f5187479451eabf01fb78af6dfcb131a6481e",
+            "24333f8a63b6825ea9c5514f83c2829b004d1fee",
+        )
+        val sdkPreviewLicense = "84831b9409646a918e30573bab4c9c91346d8abd"
+
+        val sdkLicenseFile = sdkLicensesDir.resolve("android-sdk-license")
+        if (!sdkLicenseFile.exists()) {
+            sdkLicenseFile.createNewFile()
+            sdkLicenseFile.writeText(
+                sdkLicenses.joinToString(separator = System.lineSeparator())
+            )
+        } else {
+            sdkLicenses
+                .subtract(sdkLicenseFile.readText().lines().toSet())
+                .forEach { sdkLicenseFile.appendText("$it${System.lineSeparator()}") }
+        }
+
+        val sdkPreviewLicenseFile = sdkLicensesDir.resolve("android-sdk-preview-license")
+        if (!sdkPreviewLicenseFile.exists()) {
+            sdkPreviewLicenseFile.writeText(sdkPreviewLicense)
+        } else {
+            if (sdkPreviewLicense != sdkPreviewLicenseFile.readText().trim()) {
+                sdkPreviewLicenseFile.writeText(sdkPreviewLicense)
+            }
+        }
+    }
+}
+
 fun Project.confugureFirPluginAnnotationsDependency(testTask: TaskProvider<Test>) {
     val firPluginJvmAnnotations: Configuration by configurations.creating
     val firPluginJsAnnotations: Configuration by configurations.creating {