build: reorganize modules to fix dependency resolution issues

This commit is contained in:
Akash Yadav
2024-05-28 22:54:49 +05:30
parent 255d8e9c27
commit c0c185cfb7
60 changed files with 81 additions and 68 deletions
-1
View File
@@ -1 +0,0 @@
/build
-65
View File
@@ -1,65 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
import com.itsaky.androidide.build.config.BuildConfig
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
}
android {
namespace = "${BuildConfig.packageName}.testing.android"
}
configurations.api {
// There is a transitive dependency on 'protobuf-lite' in 'androidTest' configurations
// which conflicts with the 'protobuf-javalite' library we use
// hence we exclude the dependency and add ours manually
//+--- androidx.test.espresso:espresso-core:<...>
// +--- com.google.android.material:material:<...>
// \--- com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:<...>
// +--- com.google.protobuf:protobuf-lite:3.0.1
exclude(group = "com.google.protobuf", module = "protobuf-lite")
}
dependencies {
// compileOnly so that we can provide the APIs for the related test rules
// if a module needs to use those APIs, it needs to explicitly add the database module as a
// dependency
compileOnly(projects.subprojects.database)
api(libs.google.protobuf)
api(libs.tests.androidx.espresso.core)
api(libs.tests.androidx.espresso.contrib)
api(libs.tests.androidx.junit)
api(libs.tests.androidx.test.core)
api(libs.tests.androidx.test.runner)
api(libs.tests.androidx.test.rules)
api(libs.tests.androidx.uiautomator)
api(libs.tests.junit)
api(libs.tests.google.truth)
api(libs.tests.barista) {
exclude("org.jetbrains.kotlin")
}
api(projects.buildInfo)
api(projects.common)
api(projects.shared)
api(projects.testing.common)
}
View File
-21
View File
@@ -1,21 +0,0 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# 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 *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -1,40 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package androidx.test.uiautomator;
import android.app.UiAutomation;
/**
* @author Akash Yadav
*/
public class UiDeviceAccessor {
/**
* Get the {@link UiAutomation} instance from the given {@link UiDevice}.
*/
public static UiAutomation getUiAutomation(UiDevice device) {
return device.getUiAutomation();
}
/**
* Get the {@link InteractionController} instance from the given {@link UiDevice}.
*/
public static InteractionController getInteractionController(UiDevice device) {
return device.getInteractionController();
}
}
@@ -1,34 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.testing.android
import android.content.Context
import androidx.annotation.StringRes
import androidx.test.core.app.ApplicationProvider
/**
* Get the application context.
*/
fun <AppContext : Context> getApplicationContext(): AppContext =
ApplicationProvider.getApplicationContext()
/**
* Get the value of the string resource.
*/
fun stringRes(@StringRes resource: Int, vararg formatArgs: Any) =
ApplicationProvider.getApplicationContext<Context>().getString(resource, *formatArgs)
@@ -1,37 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.testing.android.rules
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import com.itsaky.androidide.utils.Environment
import org.junit.rules.TestRule
/**
* Abstract class for Android testing rules.
*
* @author Akash Yadav
*/
abstract class AbstractAndroidTestRule : TestRule {
init {
Environment.init(ApplicationProvider.getApplicationContext())
}
protected val context: Context
get() = ApplicationProvider.getApplicationContext()
}
@@ -1,61 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.testing.android.rules
import com.itsaky.androidide.db.IRealmProvider
import com.itsaky.androidide.testing.android.util.NoOpStatement
import io.realm.Realm
import io.realm.log.LogLevel
import io.realm.log.RealmLog
import org.junit.runner.Description
import org.junit.runners.model.Statement
/**
* Rule for Realm DB tests.
*
* @author Akash Yadav
*/
class RealmDBTestRule(
val baseModule: Any? = null,
vararg val additionalModules: Array<Any>,
) : AbstractAndroidTestRule() {
inline fun withDb(dbName: String, deleteDbAfterTest: Boolean = true, action: Realm.() -> Unit) {
val name = dbName.replace(IRealmProvider.PATH_SEPARATOR, '-')
val realm = IRealmProvider.instance().get("/indexing/java/$name") {
baseModule?.let { baseModule ->
modules(baseModule, *additionalModules)
}
}
try {
realm.action()
} finally {
if (deleteDbAfterTest) {
realm.configuration.realmDirectory.deleteRecursively()
}
}
}
override fun apply(base: Statement?, description: Description?): Statement {
Realm.init(context.applicationContext)
RealmLog.setLevel(LogLevel.ALL)
base?.evaluate()
return NoOpStatement()
}
}
@@ -1,101 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.testing.android
import android.app.Activity
import android.app.UiAutomation
import android.content.Context
import android.content.Intent
import androidx.test.core.app.ApplicationProvider
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry
import androidx.test.runner.lifecycle.Stage
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiDeviceAccessor
import androidx.test.uiautomator.Until
import com.google.common.truth.Truth.assertThat
import com.itsaky.androidide.buildinfo.BuildInfo
import com.itsaky.androidide.utils.uncheckedCast
const val LAUNCH_TIMEOUT = 5000L
/**
* Launches the AndroidIDE application using UI Automator.
*
* @param fromLauncher Whether the application must be launched from the Android launcher.
* @param clearTasks Whether the application's previous tasks must be cleared.
*/
fun launchAndroidIDE(
fromLauncher: Boolean = true,
clearTasks: Boolean = true,
): UiDevice {
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
if (fromLauncher) {
device.pressHome()
val launcherPackage = device.launcherPackageName
assertThat(launcherPackage).isNotNull()
device.wait(
Until.hasObject(By.pkg(launcherPackage).depth(0)),
LAUNCH_TIMEOUT
)
}
// Launch the app
val context = ApplicationProvider.getApplicationContext<Context>()
val intent = context.packageManager.getLaunchIntentForPackage(BuildInfo.PACKAGE_NAME)
assertThat(intent).isNotNull()
if (clearTasks) {
intent!!.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
}
context.startActivity(intent)
// Wait for the app to appear
device.wait(
Until.hasObject(By.pkg(BuildInfo.PACKAGE_NAME).depth(0)),
LAUNCH_TIMEOUT
)
return device
}
/**
* Get the first activity in the given [stage].
*/
fun <T : Activity> getActivityInStage(stage: Stage): T? {
var activity: T? = null
InstrumentationRegistry.getInstrumentation().runOnMainSync {
val activities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(stage)
if (activities.isNotEmpty()) {
activity = uncheckedCast(activities.first())
}
}
return activity
}
/**
* @see UiDeviceAccessor.getUiAutomation
*/
val UiDevice.uiAutomation: UiAutomation
get() = UiDeviceAccessor.getUiAutomation(this)
@@ -1,81 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.testing.android
import android.view.KeyCharacterMap
import androidx.annotation.StringRes
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiObject2
import com.google.common.truth.Truth.assertThat
import com.itsaky.androidide.buildinfo.BuildInfo
private val keyCharMap by lazy {
KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD)
}
/**
* @see UiDevice.waitForWindowUpdate
*/
fun UiDevice.waitForWindowUpdate(timeout: Long) =
waitForWindowUpdate(BuildInfo.PACKAGE_NAME, timeout)
/**
* @see UiDevice.hasObject
*/
fun UiDevice.hasObjectWithText(text: String) =
hasObject(By.text(text))
/**
* @see UiDevice.hasObject
*/
fun UiDevice.hasObjectWithText(@StringRes text: Int) =
hasObjectWithText(stringRes(text))
/**
* @see UiDevice.findObject
*/
fun UiDevice.findObjectWithText(text: String): UiObject2? =
findObject(By.text(text))
/**
* @see UiDevice.findObjectWithText
*/
fun UiDevice.findObjectWithText(@StringRes text: Int): UiObject2? =
findObjectWithText(stringRes(text))
fun UiDevice.getObjectWithText(text: String): UiObject2 {
val obj = findObjectWithText(text)
assertThat(obj).isNotNull()
return obj!!
}
fun UiDevice.getObjectWithText(@StringRes text: Int): UiObject2 = getObjectWithText(stringRes(text))
/**
* Sends key events to this [UiDevice] to type the given [text].
*/
fun UiDevice.sendKeyEvents(text: String) {
waitForIdle()
val keyEvents = keyCharMap.getEvents(text.toCharArray())
for (keyEvent in keyEvents) {
uiAutomation.injectInputEvent(keyEvent, true)
waitForIdle()
}
}
@@ -1,81 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.testing.android
import androidx.annotation.StringRes
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
import com.google.common.truth.Truth.assertThat
/**
* Click this object and wait for window update with the given timeout duration.
*
* @see UiDevice.waitForWindowUpdate
*/
fun UiObject2?.clickAndWaitForWindowUpdate(device: UiDevice, timeout: Long = 500L) =
run {
assertThat(this).isNotNull()
this!!.click()
device.waitForWindowUpdate(timeout)
}
/**
* Click this object and wait for a new window to appear within the given timeout duration.
*
* @see UiObject2.clickAndWait
*/
fun UiObject2?.clickAndWaitForNewWindow(timeout: Long = LAUNCH_TIMEOUT) =
run {
assertThat(this).isNotNull()
this!!.clickAndWait(Until.newWindow(), timeout)
}
/**
* @see UiDevice.hasObject
*/
fun UiObject2.hasObjectWithText(text: String) =
hasObject(By.text(text))
/**
* @see UiObject2.hasObject
*/
fun UiObject2.hasObjectWithText(@StringRes text: Int) =
hasObjectWithText(stringRes(text))
/**
* @see UiObject2.findObject
*/
fun UiObject2.findObjectWithText(text: String): UiObject2? =
findObject(By.text(text))
/**
* @see UiObject2.findObjectWithText
*/
fun UiObject2.findObjectWithText(@StringRes text: Int): UiObject2? =
findObjectWithText(stringRes(text))
fun UiObject2.getObjectWithText(text: String): UiObject2 {
val obj = findObjectWithText(text)
assertThat(obj).isNotNull()
return obj!!
}
fun UiObject2.getObjectWithText(@StringRes text: Int): UiObject2 = getObjectWithText(stringRes(text))
@@ -1,30 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.testing.android.util
import org.junit.runners.model.Statement
/**
* A no-op statement.
*
* @author Akash Yadav
*/
class NoOpStatement : Statement() {
override fun evaluate() {
}
}
-1
View File
@@ -1 +0,0 @@
/build
@@ -1,37 +0,0 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# 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 *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-dontobfuscate
-ignorewarnings
-keepattributes *Annotation*
-dontnote junit.framework.**
-dontnote junit.runner.**
-dontwarn androidx.test.**
-dontwarn org.junit.**
-dontwarn org.hamcrest.**
-dontwarn com.squareup.javawriter.JavaWriter
-keepclasseswithmembers @org.junit.runner.RunWith public class *
-70
View File
@@ -1,70 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
import com.itsaky.androidide.build.config.BuildConfig
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.benchmark)
}
android {
namespace = "${BuildConfig.packageName}.benchmark"
sourceSets {
getByName("androidTest") {
assets.srcDirs(rootProject.file("subprojects/framework-stubs/libs"))
}
}
defaultConfig {
testInstrumentationRunner = "androidx.benchmark.junit4.AndroidBenchmarkRunner"
}
testBuildType = "release"
buildTypes {
debug {
// Since isDebuggable can"t be modified by gradle for library modules,
// it must be done in a manifest - see src/androidTest/AndroidManifest.xml
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"benchmark-proguard-rules.pro"
)
}
release {
isDefault = true
}
}
}
dependencies {
androidTestImplementation(libs.tests.androidx.test.runner)
androidTestImplementation(libs.tests.androidx.junit)
androidTestImplementation(libs.tests.junit)
androidTestImplementation(libs.tests.google.truth)
androidTestImplementation(libs.androidx.benchmark.junit4)
androidTestImplementation(projects.common)
androidTestImplementation(projects.lsp.java)
androidTestImplementation(projects.shared)
androidTestImplementation(projects.subprojects.database)
androidTestImplementation(projects.subprojects.indexing)
}
@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!--
Important: disable debugging for accurate performance results
In a com.android.library project, this flag must be disabled from this
manifest, as it is not possible to override this flag from Gradle.
-->
<application
android:debuggable="false"
tools:ignore="HardcodedDebugMode"
tools:replace="android:debuggable" />
</manifest>
@@ -1,61 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.benchmark.lsp.java
import android.content.Context
import androidx.benchmark.junit4.BenchmarkRule
import androidx.benchmark.junit4.measureRepeated
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import com.itsaky.androidide.lsp.java.indexing.JavaJarModelBuilder
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.io.File
/**
* @author Akash Yadav
*/
@RunWith(AndroidJUnit4::class)
class JavaIndexingWorkerBenchmark {
@get:Rule
val benchmarkRule = BenchmarkRule()
val androidJar: File by lazy {
val context = ApplicationProvider.getApplicationContext<Context>()
val file = File.createTempFile("ajar", null, context.cacheDir)
context.assets.open("android.jar").use { asset ->
assertThat(asset).isNotNull()
file.outputStream().buffered().use { out ->
asset.copyTo(out)
out.flush()
}
}
file
}
@Test
fun benchmarkJavaIndexingWorker() {
val worker = JavaJarModelBuilder(androidJar)
benchmarkRule.measureRepeated {
worker.buildTypes()
}
}
}
-1
View File
@@ -1 +0,0 @@
/build
-33
View File
@@ -1,33 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
@Suppress("JavaPluginLanguageLevel")
plugins {
id("java-library")
id("kotlin-kapt")
id("org.jetbrains.kotlin.jvm")
}
dependencies {
kapt(libs.google.auto.service)
implementation(libs.google.auto.service.annotations)
api(libs.tests.junit)
api(projects.logger)
implementation(projects.subprojects.toolingApiImpl)
}
@@ -1,29 +0,0 @@
package com.itsaky.androidide.testing.common
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
/**
* A [TestRule] which only runs the tests in a CI environment.
*/
class CIOnlyTestRule(
var isEnabled: Boolean = true
) : TestRule {
override fun apply(base: Statement, description: Description?): Statement {
if (!isEnabled) {
return base
}
return object : Statement() {
override fun evaluate() {
if (isCi()) {
base.evaluate()
}
}
}
}
private fun isCi() = System.getenv("CI").toBoolean()
}
@@ -1,31 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.testing.common
import org.junit.runners.model.Statement
/**
* A no-op statement.
*
* @author Akash Yadav
*/
class NoOpStatement : Statement() {
override fun evaluate() {
}
}
@@ -1,46 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.testing.common.logging
import ch.qos.logback.classic.Logger
import ch.qos.logback.classic.LoggerContext
import ch.qos.logback.classic.spi.Configurator
import ch.qos.logback.classic.spi.ConfiguratorRank
import ch.qos.logback.core.spi.ContextAwareBase
import com.google.auto.service.AutoService
import com.itsaky.androidide.logging.JvmStdErrAppender
import com.itsaky.androidide.logging.encoder.IDELogFormatEncoder
@ConfiguratorRank(ConfiguratorRank.CUSTOM_HIGH_PRIORITY)
@AutoService(Configurator::class)
@Suppress("UNUSED")
class ToolingLoggingConfigurator : ContextAwareBase(), Configurator {
override fun configure(context: LoggerContext): Configurator.ExecutionStatus {
addInfo("Setting up logging configuration for test runtime")
val stdErrAppender = JvmStdErrAppender()
stdErrAppender.encoder = IDELogFormatEncoder()
stdErrAppender.start()
val rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME)
rootLogger.addAppender(stdErrAppender)
return Configurator.ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY
}
}
@@ -1,53 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.testing.common.utils
/**
* Information about the operating system.
*
* @author Akash Yadav
*/
object OperatingSystem {
/**
* The name of the operating system.
*/
val OS = System.getProperty("os.name")
/**
* True if the operating system is Windows.
*/
var IS_WINDOWS: Boolean = OS.indexOf("win") >= 0
/**
* True if the operating system is Mac.
*/
var IS_MAC: Boolean = OS.indexOf("mac") >= 0
/**
* True if the operating system is Unix.
*/
var IS_UNIX: Boolean = OS.indexOf("nix") >= 0 ||
OS.indexOf("nux") >= 0 ||
OS.indexOf("aix") > 0
/**
* True if the operating system is Solaris.
*/
var IS_SOLARIS: Boolean = OS.indexOf("sunos") >= 0
}
@@ -1,38 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.testing.common.utils
fun findAndroidHome(): String {
var androidHome = System.getenv("ANDROID_HOME")
if (androidHome != null && androidHome.isNotBlank()) {
return androidHome
}
androidHome = System.getenv("ANDROID_SDK_ROOT")
if (androidHome != null && androidHome.isNotBlank()) {
return androidHome
}
val home = System.getProperty("user.home")
return when {
OperatingSystem.IS_UNIX -> "$home/Android/Sdk"
OperatingSystem.IS_MAC -> "$home/Library/Android/sdk"
OperatingSystem.IS_WINDOWS -> "$home\\AppData\\Local\\Android\\Sdk"
else -> throw IllegalStateException("Unsupported operating system")
}
}
-1
View File
@@ -1 +0,0 @@
/build
-44
View File
@@ -1,44 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
import com.itsaky.androidide.build.config.BuildConfig
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
}
android {
namespace = "${BuildConfig.packageName}.lsp.testing"
}
dependencies {
api(libs.androidx.core.ktx)
api(libs.common.editor)
api(libs.common.kotlin)
api(libs.tests.mockito.kotlin)
api(projects.actions)
api(projects.common)
api(projects.eventbusAndroid)
api(projects.lsp.api)
api(projects.testing.common)
api(projects.testing.android)
api(projects.testing.tooling)
api(projects.testing.unit)
}
-1
View File
@@ -1 +0,0 @@
<manifest/>
@@ -1,196 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.lsp.testing
import android.content.Context
import com.google.common.truth.Truth.assertThat
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.eventbus.events.editor.ChangeType.DELETE
import com.itsaky.androidide.eventbus.events.editor.DocumentChangeEvent
import com.itsaky.androidide.eventbus.events.editor.DocumentCloseEvent
import com.itsaky.androidide.eventbus.events.editor.DocumentOpenEvent
import com.itsaky.androidide.eventbus.events.file.FileDeletionEvent
import com.itsaky.androidide.eventbus.events.file.FileRenameEvent
import com.itsaky.androidide.lookup.Lookup
import com.itsaky.androidide.lsp.api.ILanguageServerRegistry
import com.itsaky.androidide.managers.PreferenceManager
import com.itsaky.androidide.models.Position
import com.itsaky.androidide.models.Range
import com.itsaky.androidide.preferences.internal.EditorPreferences
import com.itsaky.androidide.preferences.internal.prefManager
import com.itsaky.androidide.projects.FileManager
import com.itsaky.androidide.projects.ProjectManagerImpl
import com.itsaky.androidide.projects.builder.BuildService
import com.itsaky.androidide.testing.tooling.ToolingApiTestLauncher
import com.itsaky.androidide.tooling.api.IProject
import com.itsaky.androidide.tooling.api.IToolingApiServer
import com.itsaky.androidide.utils.Environment
import com.itsaky.androidide.utils.FileProvider
import io.github.rosemoe.sora.text.Content
import io.mockk.every
import io.mockk.mockkStatic
import kotlinx.coroutines.runBlocking
import org.greenrobot.eventbus.EventBus
import org.junit.Before
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.File
import java.nio.file.Path
/**
* Runs tests for a language server.
*
* @author Akash Yadav
*/
@RunWith(RobolectricTestRunner::class)
@Config(manifest = Config.DEFAULT_VALUE_STRING)
abstract class LSPTest {
protected lateinit var toolingServer: IToolingApiServer
protected lateinit var toolingProject: IProject
var cursor: Int = -1
private val cursorText = "@@cursor@@"
var file: Path? = null
var contents: StringBuilder? = null
companion object {
@JvmStatic
protected val log: Logger = LoggerFactory.getLogger(LSPTest::class.java)
@JvmStatic
protected var isInitialized: Boolean = false
}
@Before
open fun initProjectIfNeeded() {
if (isInitialized) {
return
}
mockkStatic(::prefManager)
every { prefManager } returns PreferenceManager(RuntimeEnvironment.getApplication())
mockkStatic(EditorPreferences::tabSize)
every { EditorPreferences.tabSize } returns 4
ToolingApiTestLauncher.launchServer {
assertThat(result?.isSuccessful).isTrue()
this@LSPTest.toolingProject = project
this@LSPTest.toolingServer = server
Lookup.getDefault().update(BuildService.KEY_PROJECT_PROXY, project)
Environment.ANDROID_JAR = FileProvider.resources().resolve("android.jar").toFile()
Environment.JAVA_HOME = File(System.getProperty("java.home")!!)
registerServer()
val projectManager = ProjectManagerImpl.getInstance()
projectManager.register()
runBlocking { projectManager.setupProject(project) }
// We need to manually setup the language server with the project here
// ProjectManager.notifyProjectUpdate()
ILanguageServerRegistry.getDefault()
.getServer(getServerId())!!
.setupWithProject(projectManager.rootProject!!)
isInitialized = true
}
}
protected abstract fun registerServer()
protected abstract fun getServerId(): String
abstract fun test()
fun requireCursor(): Int {
this.cursor = contents!!.indexOf(cursorText)
assertThat(cursor).isGreaterThan(-1)
return cursor
}
fun deleteCursorText() {
contents!!.delete(this.cursor, this.cursor + cursorText.length)
assertThat(contents!!.indexOf(cursorText)).isEqualTo(-1)
// As the content has been changed, we have to
// Update the content in language server
dispatchEvent(
DocumentChangeEvent(
file!!,
contents.toString(),
contents.toString(),
1,
DELETE,
0,
Range.NONE
)
)
}
@JvmOverloads
fun cursorPosition(deleteCursorText: Boolean = true): Position {
requireCursor()
if (deleteCursorText) {
deleteCursorText()
}
val pos = Content(contents!!).indexer.getCharPosition(cursor)
return Position(pos.line, pos.column, pos.index)
}
open fun openFile(fileName: String) {
file = FileProvider.sourceFile(fileName).normalize()
contents = FileProvider.contents(file!!)
dispatchEvent(DocumentOpenEvent(file!!, contents.toString(), 0))
}
open fun dispatchEvent(event: Any) {
when (event) {
is DocumentOpenEvent -> FileManager.onDocumentOpen(event)
is DocumentChangeEvent -> FileManager.onDocumentContentChange(event)
is DocumentCloseEvent -> FileManager.onDocumentClose(event)
is FileRenameEvent -> FileManager.onFileRenamed(event)
is FileDeletionEvent -> FileManager.onFileDeleted(event)
}
EventBus.getDefault().post(event)
}
open fun createActionData(vararg values: Any): ActionData {
val data = ActionData()
data.put(Context::class.java, RuntimeEnvironment.getApplication())
for (value in values) {
if (value is Path) {
data.put(Path::class.java, value)
} else {
data.put(value::javaClass.get(), value)
}
}
return data
}
}
@@ -1,35 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.lsp.testing.util
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.ILogger.Level
/** @author Akash Yadav */
class ReflectiveLogListener(private val receiver: Class<out Any>) : ILogger.LogListener {
override fun log(level: Level, tag: String, message: String) {
val method =
receiver.getDeclaredMethod(
"log",
Level::class.java,
String::class.java,
String::class.java
)
method.isAccessible = true
method.invoke(null, level, tag, message)
}
}
-1
View File
@@ -1 +0,0 @@
/build
-40
View File
@@ -1,40 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
@Suppress("JavaPluginLanguageLevel")
plugins {
id("java-library")
id("org.jetbrains.kotlin.jvm")
}
dependencies {
api(libs.common.jkotlin)
api(libs.tests.robolectric)
api(libs.tests.junit)
api(libs.tests.google.truth)
api(libs.tests.mockk)
api(projects.buildInfo)
api(projects.logger)
api(projects.shared)
api(projects.subprojects.toolingApi)
api(projects.testing.common)
// build tooling API before tests
compileOnly(projects.subprojects.toolingApiImpl)
}
@@ -1,345 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.testing.tooling
import ch.qos.logback.core.CoreConstants
import com.itsaky.androidide.testing.tooling.models.ToolingApiTestLauncherParams
import com.itsaky.androidide.testing.tooling.models.ToolingApiTestScope
import com.itsaky.androidide.tooling.api.IProject
import com.itsaky.androidide.tooling.api.IToolingApiClient
import com.itsaky.androidide.tooling.api.IToolingApiServer
import com.itsaky.androidide.tooling.api.messages.GradleDistributionParams
import com.itsaky.androidide.tooling.api.messages.InitializeProjectParams
import com.itsaky.androidide.tooling.api.messages.LogMessageParams
import com.itsaky.androidide.tooling.api.messages.result.BuildInfo
import com.itsaky.androidide.tooling.api.messages.result.BuildResult
import com.itsaky.androidide.tooling.api.messages.result.GradleWrapperCheckResult
import com.itsaky.androidide.tooling.api.messages.toLogLine
import com.itsaky.androidide.tooling.api.util.ToolingApiLauncher
import com.itsaky.androidide.tooling.api.util.ToolingProps
import com.itsaky.androidide.tooling.events.ProgressEvent
import com.itsaky.androidide.utils.FileProvider
import com.itsaky.androidide.utils.ILogger
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.BufferedReader
import java.io.File
import java.io.InputStream
import java.io.InputStreamReader
import java.nio.file.Path
import java.util.Collections
import java.util.concurrent.CompletableFuture
import kotlin.io.path.bufferedReader
import kotlin.io.path.bufferedWriter
import kotlin.io.path.deleteIfExists
import kotlin.io.path.pathString
/**
* Launches the Tooling API server in a separate JVM process.
*
* ***For testing purposes only!***
*
* @author Akash Yadav
*/
object ToolingApiTestLauncher {
private val opens =
mutableMapOf("java.base" to "java.lang", "java.base" to "java.util",
"java.base" to "java.io")
private val exports =
mutableMapOf("jdk.compiler" to "com.sun.tools.javac.api",
"jdk.compiler" to "com.sun.tools.javac.file",
"jdk.compiler" to "com.sun.tools.javac.parser",
"jdk.compiler" to "com.sun.tools.javac.tree",
"jdk.compiler" to "com.sun.tools.javac.util")
@JvmOverloads
@JvmStatic
fun launchServer(
projectDir: Path = FileProvider.testProjectRoot(),
client: MultiVersionTestClient = MultiVersionTestClient(),
initParams: InitializeProjectParams = InitializeProjectParams(
projectDir.pathString,
client.gradleDistParams
),
log: Logger = LoggerFactory.getLogger("BuildOutputLogger"),
sysProps: Map<String, String> = emptyMap(),
sysEnvs: Map<String, String> = emptyMap(),
action: ToolingApiTestScope.() -> Unit,
) = launchServer(
ToolingApiTestLauncherParams(projectDir, client, initParams, log, sysProps, sysEnvs), action)
@JvmOverloads
@JvmStatic
@JvmName("launchServerWithParams")
fun launchServer(
params: ToolingApiTestLauncherParams = ToolingApiTestLauncherParams(),
action: ToolingApiTestScope.() -> Unit
) {
return launchServerAsync(params) {
// wait for project initialization
initializeResult.get()
action()
}
}
@JvmOverloads
@JvmStatic
fun launchServerAsync(
params: ToolingApiTestLauncherParams = ToolingApiTestLauncherParams(),
action: ToolingApiTestScope.() -> Unit
) {
val cmdLine = createProcessCmd(
FileProvider.implModule()
.resolve("build/libs/tooling-api-all.jar").pathString,
params.sysProps
)
val builder = ProcessBuilder(cmdLine)
val androidHome = com.itsaky.androidide.testing.common.utils.findAndroidHome()
builder.environment()["ANDROID_SDK_ROOT"] = androidHome
builder.environment()["ANDROID_HOME"] = androidHome
builder.environment().putAll(params.sysEnvs)
val proc = builder.start()
proc.onExit().whenComplete { process, error ->
if (process != null) {
println(
"[ToolingApiTestLauncher] Tooling API server process finished with exit code: ${process.exitValue()}"
)
}
if (error != null) {
println("[ToolingApiTestLauncher] Tooling API server process error")
error.printStackTrace()
}
}
Thread(Reader(proc.errorStream, params.log)).start()
val launcher =
ToolingApiLauncher.newClientLauncher(
params.client, proc.inputStream,
proc.outputStream
)
launcher.startListening()
val server = launcher.remoteProxy as IToolingApiServer
val project = launcher.remoteProxy as IProject
val result = server.initialize(params.initParams)
try {
// perform the action
ToolingApiTestScope(server, project, result).action()
} finally {
server.cancelCurrentBuild().get()
server.shutdown().get()
}
}
private fun createProcessCmd(
jar: String,
sysProps: Map<String, String> = emptyMap()
): List<String> {
val cmd = mutableListOf("java")
System.getenv("JAVA_HOME")?.let {
val java = File(it, "bin/java")
if (java.exists() && java.isFile) {
if (!java.canExecute()) {
java.setExecutable(true)
}
cmd[0] = java.absolutePath
}
}
for (open in opens) {
cmd.add("--add-opens=${open.key}/${open.value}=ALL-UNNAMED")
}
for (export in exports) {
cmd.add("--add-exports=${export.key}/${export.value}=ALL-UNNAMED")
}
cmd.add("-D${ToolingProps.TESTING_IS_TEST_ENV}=true")
sysProps.forEach { (key, value) ->
cmd.add("-D$key=$value")
}
cmd.add(
"-D${CoreConstants.STATUS_LISTENER_CLASS_KEY}=com.itsaky.androidide.tooling.impl.util.LogbackStatusListener")
Collections.addAll(cmd, "-jar", jar)
println(
"[ToolingApiTestLauncher] Java cmd: " + cmd.joinToString(separator = " "))
return cmd
}
open class MultiVersionTestClient(
private val projectDir: Path = FileProvider.testProjectRoot(),
var agpVersion: String = DEFAULT_AGP_VERSION,
var gradleVersion: String = DEFAULT_GRADLE_VERSION,
private val androidBlockConfig: String = "",
private val log: Logger = LoggerFactory.getLogger(MultiVersionTestClient::class.java),
private val extraArgs: List<String> = emptyList(),
private var excludeUnresolvedDependency: Boolean = false,
var outputValidator: (String) -> Boolean = { true },
) : IToolingApiClient {
var isOutputValid = false
private set
val gradleDistParams: GradleDistributionParams
get() = GradleDistributionParams.forVersion(this.gradleVersion)
@Suppress("ConstPropertyName")
companion object {
const val buildFile = "build.gradle"
const val buildFileIn = "$buildFile.in"
const val appBuildFile = "app/build.gradle"
const val appBuildFileIn = "$appBuildFile.in"
const val DEFAULT_AGP_VERSION = "7.2.0"
const val DEFAULT_GRADLE_VERSION = "7.3.3"
const val GENERATED_FILE_WARNING =
"DO NOT EDIT - Automatically generated file"
}
override fun logMessage(params: LogMessageParams) {
val line = params.toLogLine()
val message = line.message
val logger = LoggerFactory.getLogger(line.tag)
when (line.level) {
ILogger.Level.DEBUG -> logger.debug(message)
ILogger.Level.WARNING -> logger.warn(message)
ILogger.Level.ERROR -> logger.error(message)
ILogger.Level.INFO -> logger.info(message)
else -> logger.trace(message)
}
}
override fun logOutput(line: String) {
val trimmed = line.trim()
val curr = this.isOutputValid;
this.isOutputValid = this.isOutputValid || outputValidator(trimmed)
if (!curr && this.isOutputValid) {
log.debug("Output validation succeeded")
}
log.debug(trimmed)
}
override fun prepareBuild(buildInfo: BuildInfo) {
log.debug("---------- PREPARE BUILD ----------")
log.debug("AGP Version : ${this.agpVersion}")
log.debug("Gradle Version : ${this.gradleVersion}")
log.debug("-----------------------------------")
projectDir.resolve(buildFileIn)
.replaceContents(dest = projectDir.resolve(buildFile),
candidate = "@@TOOLING_API_TEST_AGP_VERSION@@" to this.agpVersion)
projectDir.resolve(appBuildFileIn)
.replaceContents(
projectDir.resolve(appBuildFile),
"//",
"@@ANDROID_BLOCK_CONFIG@@" to androidBlockConfig,
"@@UNRESOLVED_DEPENDENCY@@" to if (!excludeUnresolvedDependency) "implementation 'unresolved:unresolved:unresolved'" else "")
}
override fun onBuildSuccessful(result: BuildResult) {
onBuildResult(result)
}
override fun onBuildFailed(result: BuildResult) {
onBuildResult(result)
}
private fun onBuildResult(@Suppress("UNUSED_PARAMETER") result: BuildResult) {
projectDir.resolve(buildFile).deleteIfExists()
projectDir.resolve(appBuildFile).deleteIfExists()
}
override fun onProgressEvent(event: ProgressEvent) {}
override fun getBuildArguments(): CompletableFuture<List<String>> {
return CompletableFuture.completedFuture(
mutableListOf("--stacktrace", "--info").also { it.addAll(extraArgs) })
}
override fun checkGradleWrapperAvailability(): CompletableFuture<GradleWrapperCheckResult> =
CompletableFuture.completedFuture(GradleWrapperCheckResult(true))
private fun Path.replaceContents(dest: Path, comment: String = "//",
candidate: Pair<String, String>
) = replaceContents(dest, comment, *arrayOf(candidate))
private fun Path.replaceContents(dest: Path, comment: String = "//",
vararg candidates: Pair<String, String>
) {
val contents = StringBuilder().append(comment)
.append(" ")
.append(GENERATED_FILE_WARNING)
.append(System.lineSeparator().repeat(2))
bufferedReader().use { reader ->
reader.readText().also { text ->
var t = text
for ((old, new) in candidates) {
t = t.replace(old, new)
}
contents.append(t)
}
}
dest.deleteIfExists()
dest.bufferedWriter().use { writer ->
writer.write(contents.toString())
writer.flush()
}
}
}
private class Reader(val input: InputStream, val log: Logger) : Runnable {
override fun run() {
try {
val reader = BufferedReader(InputStreamReader(input))
var line = reader.readLine()
while (line != null) {
log.debug(line)
line = reader.readLine()
}
} catch (error: Throwable) {
log.error("An error occurred reading from tooling API", error)
}
}
}
}
@@ -1,43 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.testing.tooling.models
import com.itsaky.androidide.testing.tooling.ToolingApiTestLauncher
import com.itsaky.androidide.tooling.api.messages.InitializeProjectParams
import com.itsaky.androidide.utils.FileProvider
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.nio.file.Path
import kotlin.io.path.pathString
/**
* Parameters for launching the tooling API server.
*
* @author Akash Yadav
*/
data class ToolingApiTestLauncherParams(
val projectDir: Path = FileProvider.testProjectRoot(),
val client: ToolingApiTestLauncher.MultiVersionTestClient = ToolingApiTestLauncher.MultiVersionTestClient(),
val initParams: InitializeProjectParams = InitializeProjectParams(
projectDir.pathString,
client.gradleDistParams
),
val log: Logger = LoggerFactory.getLogger("BuildOutputLogger"),
val sysProps: Map<String, String> = emptyMap(),
val sysEnvs: Map<String, String> = emptyMap()
)
@@ -1,38 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.testing.tooling.models
import com.itsaky.androidide.tooling.api.IProject
import com.itsaky.androidide.tooling.api.IToolingApiServer
import com.itsaky.androidide.tooling.api.messages.result.InitializeResult
import java.util.concurrent.CompletableFuture
/**
* Scope for Tooling API tests. Provides access to the [IToolingApiServer], [IProject] and the [InitializeResult].
*
* @author Akash Yadav
*/
class ToolingApiTestScope(
val server: IToolingApiServer,
val project: IProject,
val initializeResult: CompletableFuture<InitializeResult>
) {
val result: InitializeResult?
get() = initializeResult.get()
}
-1
View File
@@ -1 +0,0 @@
/build
-41
View File
@@ -1,41 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
import com.itsaky.androidide.build.config.BuildConfig
plugins {
id("com.android.library")
kotlin("android")
}
android {
namespace = "${BuildConfig.packageName}.testing.unit"
}
dependencies {
api(libs.tests.androidx.test.core)
api(libs.tests.robolectric)
api(libs.tests.junit)
api(libs.tests.google.truth)
api(libs.tests.mockk)
api(projects.buildInfo)
api(projects.common)
api(projects.shared)
api(projects.testing.common)
}
View File
-21
View File
@@ -1,21 +0,0 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# 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 *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile