chore(ci): build android template projects

This commit is contained in:
Akash Yadav
2023-05-16 23:01:16 +05:30
parent 9a6a140b9f
commit 8a5d21e773
6 changed files with 140 additions and 52 deletions
+46
View File
@@ -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
View File
@@ -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 {
+1
View File
@@ -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