mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
chore(ci): build android template projects
This commit is contained in:
@@ -124,6 +124,52 @@ jobs:
|
||||
name: test-reports
|
||||
path: '**/build/reports/tests/'
|
||||
|
||||
build_templates:
|
||||
name: Build templates
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: Change Gradle wrapper permissions
|
||||
run: chmod +x ./gradlew
|
||||
- name: Restore cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- name: Build debug templates
|
||||
run: ./gradlew :templates-impl:testDebug --continue
|
||||
env:
|
||||
ANDROIDIDE_TEST: true
|
||||
ANDROIDIDE_TEMPLATES_BUILD: true
|
||||
IDE_SIGNING_ALIAS: ${{ secrets.IDE_SIGNING_ALIAS }}
|
||||
IDE_SIGNING_AUTH_PASS: ${{ secrets.IDE_SIGNING_AUTH_PASS }}
|
||||
IDE_SIGNING_AUTH_USER: ${{ secrets.IDE_SIGNING_AUTH_USER }}
|
||||
IDE_SIGNING_KEY_PASS: ${{ secrets.IDE_SIGNING_KEY_PASS }}
|
||||
IDE_SIGNING_STORE_PASS: ${{ secrets.IDE_SIGNING_STORE_PASS }}
|
||||
IDE_SIGNING_URL: ${{ secrets.IDE_SIGNING_URL }}
|
||||
- name: Upload test reports
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: templates-test-reports
|
||||
path: 'templates-impl/build/reports/tests/'
|
||||
- name: Upload template APKs
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: templates-apks
|
||||
path: 'build/templates/'
|
||||
|
||||
test_tooling_api:
|
||||
name: Test Tooling API implementation
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
Regular → Executable
+30
-43
@@ -43,33 +43,26 @@ import kotlin.io.path.pathString
|
||||
*
|
||||
* ***For testing purposes only!***
|
||||
*
|
||||
* ***This must be used only from modules that are located in the 'build-tools' directory!***
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class ToolingApiTestLauncher {
|
||||
|
||||
private val opens =
|
||||
mutableMapOf("java.base" to "java.lang", "java.base" to "java.util", "java.base" to "java.io")
|
||||
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",
|
||||
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"
|
||||
)
|
||||
"jdk.compiler" to "com.sun.tools.javac.util")
|
||||
|
||||
fun launchServer(
|
||||
client: IToolingApiClient = MultiVersionTestClient(),
|
||||
): Pair<IToolingApiServer, IProject> {
|
||||
val builder =
|
||||
ProcessBuilder(
|
||||
createProcessCmd(
|
||||
FileProvider.implModule().resolve("build/libs/tooling-api-all.jar").pathString
|
||||
)
|
||||
)
|
||||
val builder = ProcessBuilder(createProcessCmd(FileProvider.implModule()
|
||||
.resolve("build/libs/tooling-api-all.jar").pathString))
|
||||
val androidHome = findAndroidHome()
|
||||
println("ANDROID_HOME=$androidHome")
|
||||
builder.environment()["ANDROID_SDK_ROOT"] = androidHome
|
||||
@@ -77,7 +70,9 @@ class ToolingApiTestLauncher {
|
||||
val proc = builder.start()
|
||||
|
||||
Thread(Reader(proc.errorStream)).start()
|
||||
val launcher = ToolingApiLauncher.newClientLauncher(client, proc.inputStream, proc.outputStream)
|
||||
val launcher =
|
||||
ToolingApiLauncher.newClientLauncher(client, proc.inputStream,
|
||||
proc.outputStream)
|
||||
|
||||
launcher.startListening()
|
||||
|
||||
@@ -96,7 +91,8 @@ class ToolingApiTestLauncher {
|
||||
|
||||
Collections.addAll(cmd, "-jar", jar)
|
||||
|
||||
println("[ToolingApiTestLauncher] Java cmd: " + cmd.joinToString(separator = " "))
|
||||
println(
|
||||
"[ToolingApiTestLauncher] Java cmd: " + cmd.joinToString(separator = " "))
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -108,6 +104,7 @@ class ToolingApiTestLauncher {
|
||||
) : IToolingApiClient {
|
||||
|
||||
companion object {
|
||||
|
||||
const val buildFile = "build.gradle"
|
||||
const val buildFileIn = "$buildFile.in"
|
||||
|
||||
@@ -117,10 +114,12 @@ class ToolingApiTestLauncher {
|
||||
const val DEFAULT_AGP_VERSION = "7.2.0"
|
||||
const val DEFAULT_GRADLE_VERSION = "7.5.1"
|
||||
|
||||
const val GENERATED_FILE_WARNING = "DO NOT EDIT - Automatically generated file"
|
||||
const val GENERATED_FILE_WARNING =
|
||||
"DO NOT EDIT - Automatically generated file"
|
||||
|
||||
private val log = ILogger.newInstance("MultiVersionTestClient")
|
||||
}
|
||||
|
||||
override fun logMessage(line: LogLine) {
|
||||
log.log(line.priority, line.formattedTagAndMessage())
|
||||
}
|
||||
@@ -135,20 +134,13 @@ class ToolingApiTestLauncher {
|
||||
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(buildFileIn)
|
||||
.replaceContents(dest = projectDir.resolve(buildFile),
|
||||
candidate = "@@TOOLING_API_TEST_AGP_VERSION@@" to this.agpVersion)
|
||||
|
||||
projectDir
|
||||
.resolve(gradlewPropsIn)
|
||||
.replaceContents(
|
||||
comment = "#",
|
||||
dest = projectDir.resolve(gradlewProps),
|
||||
candidate = "@@GRADLE_VERSION@@" to this.gradleVersion
|
||||
)
|
||||
projectDir.resolve(gradlewPropsIn)
|
||||
.replaceContents(comment = "#", dest = projectDir.resolve(gradlewProps),
|
||||
candidate = "@@GRADLE_VERSION@@" to this.gradleVersion)
|
||||
}
|
||||
|
||||
override fun onBuildSuccessful(result: BuildResult) {}
|
||||
@@ -163,23 +155,17 @@ class ToolingApiTestLauncher {
|
||||
override fun checkGradleWrapperAvailability(): CompletableFuture<GradleWrapperCheckResult> =
|
||||
CompletableFuture.completedFuture(GradleWrapperCheckResult(true))
|
||||
|
||||
private fun Path.replaceContents(
|
||||
dest: Path,
|
||||
comment: String = "//",
|
||||
candidate: Pair<String, String>
|
||||
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>
|
||||
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.getProperty("line.separator").repeat(2))
|
||||
val contents = StringBuilder().append(comment)
|
||||
.append(" ")
|
||||
.append(GENERATED_FILE_WARNING)
|
||||
.append(System.getProperty("line.separator").repeat(2))
|
||||
|
||||
bufferedReader().use { reader ->
|
||||
reader.readText().also { text ->
|
||||
@@ -201,6 +187,7 @@ class ToolingApiTestLauncher {
|
||||
}
|
||||
|
||||
private class Reader(val input: InputStream) : Runnable {
|
||||
|
||||
private val log = ILogger.newInstance(javaClass.simpleName)
|
||||
override fun run() {
|
||||
try {
|
||||
|
||||
@@ -42,6 +42,7 @@ dependencies {
|
||||
testImplementation(projects.lsp.api)
|
||||
testImplementation(projects.preferences)
|
||||
testImplementation(projects.shared)
|
||||
testImplementation(projects.subprojects.toolingApiTesting)
|
||||
testImplementation(libs.tests.androidx.test.core)
|
||||
testImplementation(libs.tests.google.truth)
|
||||
testImplementation(libs.tests.junit)
|
||||
|
||||
@@ -47,7 +47,7 @@ class TemplateBuilderTest {
|
||||
|
||||
@Test
|
||||
fun `root project generator test`() {
|
||||
val template = testTemplate("Root") {
|
||||
val template = testTemplate("Root", generate = false) {
|
||||
baseProject { }
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ class TemplateBuilderTest {
|
||||
|
||||
@Test
|
||||
fun `test project with module`() {
|
||||
testTemplate("Test") {
|
||||
testTemplate("Test", generate = false) {
|
||||
baseProject {
|
||||
|
||||
defaultAppModule {
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.itsaky.androidide.preferences.internal.prefManager
|
||||
import com.itsaky.androidide.templates.base.AndroidModuleTemplateBuilder
|
||||
import com.itsaky.androidide.templates.base.ModuleTemplateBuilder
|
||||
import com.itsaky.androidide.templates.base.ProjectTemplateBuilder
|
||||
import com.itsaky.androidide.tooling.testing.findAndroidHome
|
||||
import com.itsaky.androidide.utils.Environment
|
||||
import com.itsaky.androidide.utils.FileProvider
|
||||
import io.mockk.every
|
||||
@@ -37,6 +38,10 @@ val testProjectsDir: File by lazy {
|
||||
FileProvider.currentDir().resolve("build/templateTest").toFile()
|
||||
}
|
||||
|
||||
val isBuildTemplates by lazy {
|
||||
System.getenv("ANDROIDIDE_TEMPLATES_BUILD").toBoolean()
|
||||
}
|
||||
|
||||
fun mockPrefManager(configure: PreferenceManager.() -> Unit = {}) {
|
||||
mockkStatic(::prefManager)
|
||||
|
||||
@@ -88,7 +93,8 @@ private fun <T : Any> mockConstructors(klass: KClass<T>,
|
||||
configure()
|
||||
}
|
||||
|
||||
fun testTemplate(name: String, builder: () -> Template): Template {
|
||||
fun testTemplate(name: String, generate: Boolean = true, builder: () -> Template
|
||||
): Template {
|
||||
mockPrefManager()
|
||||
mockTemplateBuilders()
|
||||
testProjectsDir.apply {
|
||||
@@ -102,6 +108,15 @@ fun testTemplate(name: String, builder: () -> Template): Template {
|
||||
|
||||
val template = builder()
|
||||
|
||||
if (generate) {
|
||||
generateTemplateProject(name, template)
|
||||
}
|
||||
|
||||
return template
|
||||
}
|
||||
|
||||
private fun generateTemplateProject(name: String, template: Template
|
||||
) {
|
||||
for (language in Language.values()) {
|
||||
val packageName = "com.itsaky.androidide.template.${language.lang}"
|
||||
run {
|
||||
@@ -111,7 +126,7 @@ fun testTemplate(name: String, builder: () -> Template): Template {
|
||||
mockTemplateDatas(useKts = false)
|
||||
template.setupRootProjectParams(name = projectName,
|
||||
packageName = packageName, language = language)
|
||||
template.executeRecipe()
|
||||
template.executeRecipe(projectName)
|
||||
unmockTemplateDatas()
|
||||
}
|
||||
|
||||
@@ -121,12 +136,10 @@ fun testTemplate(name: String, builder: () -> Template): Template {
|
||||
mockTemplateDatas(useKts = true)
|
||||
template.setupRootProjectParams(name = projectName,
|
||||
packageName = "${packageName}.kts", language = language)
|
||||
template.executeRecipe()
|
||||
template.executeRecipe(projectName)
|
||||
unmockTemplateDatas()
|
||||
}
|
||||
}
|
||||
|
||||
return template
|
||||
}
|
||||
|
||||
fun Template.setupRootProjectParams(name: String = "TestTemplate",
|
||||
@@ -157,8 +170,35 @@ fun Template.setupRootProjectParams(name: String = "TestTemplate",
|
||||
(param as EnumParameter<Sdk>).value = minSdk
|
||||
}
|
||||
|
||||
fun Template.executeRecipe() {
|
||||
fun Template.executeRecipe(projectName: String) {
|
||||
TestRecipeExecutor().apply(recipe)
|
||||
|
||||
if (isBuildTemplates) {
|
||||
val projectDir = File(testProjectsDir, projectName)
|
||||
if (!projectDir.isDirectory) {
|
||||
return
|
||||
}
|
||||
|
||||
var name = "gradlew"
|
||||
if (isWindows()) {
|
||||
name += ".bat"
|
||||
}
|
||||
|
||||
val gradlew = File(projectDir, name)
|
||||
val process = wrapperProcess(gradlew)
|
||||
|
||||
// Wait for the build process to finish
|
||||
check(
|
||||
process.waitFor() == 0) { "Failed to build template project '$projectName' at location '$projectDir'" }
|
||||
|
||||
// if the build process is successful, copy the apk to a common location
|
||||
val apk = File(projectDir, "app/build/outputs/apk/debug/app-debug.apk")
|
||||
val dest = FileProvider.projectRoot()
|
||||
.resolve("build/templates/$projectName.apk")
|
||||
.toFile()
|
||||
dest.parentFile!!.mkdirs()
|
||||
apk.copyTo(dest)
|
||||
}
|
||||
}
|
||||
|
||||
fun Collection<Parameter<*>>.assertParameterTypes(
|
||||
@@ -174,4 +214,18 @@ fun <T : Any> Collection<T>.assertTypes(checker: (Int) -> KClass<out T>) {
|
||||
forEachIndexed { index, element ->
|
||||
assertThat(element).isInstanceOf(checker(index).java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun wrapperProcess(gradlew: File) = ProcessBuilder(cmd(gradlew)).apply {
|
||||
environment().apply {
|
||||
put("JAVA_HOME", System.getProperty("java.home"))
|
||||
put("ANDROID_HOME", findAndroidHome())
|
||||
}
|
||||
}.start()
|
||||
|
||||
private fun cmd(gradlew: File) =
|
||||
if (isWindows()) listOf("cmd", "/c", gradlew.absolutePath, "assembleDebug")
|
||||
else listOf("bash", gradlew.absolutePath, "assembleDebug")
|
||||
|
||||
private fun isWindows() =
|
||||
System.getProperty("os.name")?.contains("windows", ignoreCase = true) == true
|
||||
Reference in New Issue
Block a user