Skip to content

Commit 89d6946

Browse files
authored
Add AGP 7.x line to the list of tested plugin versions (#231)
* Replace self-made template processor with actual engine Add the KorTE library for plugin tests, which provides a template engine akin to Jinja. Use this engine to process build scripts for functional tests and get rid of my previous, hilarious attempt at building this from scratch. Verify the behavior of the new engine, including some custom functionality to check AGP & Gradle versions, with some additional unit tests * Add AGP 7.x to the list of tested versions
1 parent 7421da6 commit 89d6946

7 files changed

Lines changed: 380 additions & 213 deletions

File tree

buildSrc/src/main/kotlin/Dependencies.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,18 @@ object Plugins {
1717
val android35x: Agp = Agp("com.android.tools.build:gradle:3.5.4")
1818
val android36x: Agp = Agp("com.android.tools.build:gradle:3.6.4")
1919
val android40x: Agp = Agp("com.android.tools.build:gradle:4.0.2")
20-
val android41x: Agp = Agp("com.android.tools.build:gradle:4.1.0", requiresGradle = "6.5")
21-
val android42x: Agp = Agp("com.android.tools.build:gradle:4.2.0-alpha16", requiresGradle = "6.7")
20+
val android41x: Agp = Agp("com.android.tools.build:gradle:4.1.1", requiresGradle = "6.5")
21+
val android42x: Agp = Agp("com.android.tools.build:gradle:4.2.0-beta02", requiresGradle = "6.7.1")
22+
val android70x: Agp = Agp("com.android.tools.build:gradle:7.0.0-alpha03", requiresGradle = "6.8-rc-1")
2223
val android: Agp = android35x
2324

2425
val supportedAndroidPlugins = listOf(
2526
android35x,
2627
android36x,
2728
android40x,
2829
android41x,
29-
android42x
30+
android42x,
31+
android70x
3032
)
3133

3234
// Documentation
@@ -51,6 +53,7 @@ object Libs {
5153
const val commonsIO: Lib = "commons-io:commons-io:2.6"
5254
const val commonsLang: Lib = "commons-lang:commons-lang:2.6"
5355
const val konfToml: Lib = "com.uchuhimo:konf-toml:0.22.1"
56+
const val korte: Lib = "com.soywiz.korlibs.korte:korte:1.10.15"
5457

5558
// JUnit 5
5659
private const val junitJupiterVersion = "5.7.0"

plugin/android-junit5/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ dependencies {
166166

167167
testImplementation(gradleTestKit())
168168
testImplementation(Plugins.android.dependency)
169+
testImplementation(Libs.korte)
169170
testImplementation(Libs.commonsIO)
170171
testImplementation(Libs.commonsLang)
171172
testImplementation(Libs.konfToml)
Lines changed: 39 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,55 @@
11
package de.mannodermaus.gradle.plugins.junit5.util.projects
22

3-
import kotlin.math.max
4-
5-
// Known tokens
6-
private val GET_MATCHER = Regex("//\\\$GET\\{(.*)}")
7-
private val IF_MATCHER = Regex("//\\\$IF\\{(.*)}")
8-
private val ELSE_MATCHER = Regex("//\\\$ELSE")
9-
private val IFGRADLE_MATCHER = Regex("//\\\$IFGRADLE\\{(.*)}")
10-
private val FOREACH_MATCHER = Regex("//\\\$FOREACH\\{(.+)}")
11-
private val END_MATCHER = Regex("//\\\$END")
3+
import com.soywiz.korte.TeFunction
4+
import com.soywiz.korte.TemplateConfig
5+
import com.soywiz.korte.TemplateProvider
6+
import com.soywiz.korte.Templates
7+
import kotlinx.coroutines.runBlocking
8+
import java.io.File
129

1310
/**
1411
* Processor class for virtual build script files, used by Functional Tests.
15-
* It utilizes a very, VERY crude token API for placeholders, which are dynamically
12+
* It utilizes a template engine to customize the processed output for the build scripts
1613
* injected into the virtual projects, based around template files located within src/test/resources.
1714
*/
18-
class BuildScriptTemplateProcessor(private val targetGradleVersion: String?,
19-
private val replacements: Map<String, Any>) {
20-
21-
fun process(rawText: String): String {
22-
var ignoredBlockCount = 0
23-
var loopBlockCount = 0
24-
25-
// Replace GET tokens first
26-
val text1 = rawText.replace(GET_MATCHER) { result ->
27-
val key = result.groupValues.last()
28-
if (key in replacements) {
29-
"\"${replacements[key].toString()}\""
30-
} else {
31-
"\"(missing: '$key')\""
32-
}
33-
}
34-
35-
// Iterate over the result and exclude all non-matching IF token blocks
36-
val text2 = StringBuilder()
37-
for (line in text1.lines()) {
38-
// Check if any conditional marker is included in the line
39-
val ifMatch = IF_MATCHER.find(line)
40-
val elseMatch = ELSE_MATCHER.find(line)
41-
val ifgradleMatch = IFGRADLE_MATCHER.find(line)
42-
val foreachMatch = FOREACH_MATCHER.find(line)
43-
val endMatch = END_MATCHER.find(line)
44-
45-
if (ignoredBlockCount == 0 && ifMatch != null) {
46-
val ifKey = ifMatch.groupValues.last()
47-
val conditionValue = replacements[ifKey]?.toString()
48-
val conditionEnabled = conditionValue != "false"
49-
&& (conditionValue == "true" || conditionValue != null)
50-
51-
if (!conditionEnabled) {
52-
// Ignore this block
53-
ignoredBlockCount++
54-
}
55-
}
56-
57-
if (ignoredBlockCount == 0 && ifgradleMatch != null) {
58-
val ifgradleExpression = ifgradleMatch.groupValues.last()
59-
60-
// When the given Gradle requirement is null, or if the requirement
61-
// is not fulfilled by the block, ignore it
62-
if (shouldIgnoreIfgradleBlock(ifgradleExpression)) {
63-
// Ignore this block
64-
ignoredBlockCount++
15+
class BuildScriptTemplateProcessor(
16+
folder: File,
17+
private val replacements: Map<String, Any>,
18+
private val agpVersion: String,
19+
private val gradleVersion: String
20+
) {
21+
22+
private val renderer = Templates(
23+
root = FileReadingTemplateProvider(folder),
24+
cache = true,
25+
config = TemplateConfig(
26+
// Allow checking for AGP & Gradle versions inside the templates
27+
extraFunctions = listOf(
28+
TeFunction("atLeastAgp") { args ->
29+
isVersionAtLeast(agpVersion, args[0].toDynamicString())
30+
},
31+
TeFunction("atLeastGradle") { args ->
32+
isVersionAtLeast(gradleVersion, args[0].toDynamicString())
6533
}
66-
}
34+
)
35+
)
36+
)
6737

68-
if (ignoredBlockCount == 0 && foreachMatch != null) {
69-
val foreachKey = foreachMatch.groupValues.last()
70-
71-
// Started a loop. Find the associated list of elements first
72-
val value = (replacements[foreachKey] as? List<Any?>)
73-
?.joinToString(separator = ",") { "\"$it\""}
74-
?: throw IllegalArgumentException("FOREACH replacement value should be a list, but got: ${replacements[foreachKey]}")
75-
76-
// Emit the beginning of a foreach loop and continue onward.
77-
// As soon as the next END marker is detected, the loop will be closed again
78-
if (value.isNotEmpty()) {
79-
text2.append("listOf($value).forEach { it ->").appendln()
80-
loopBlockCount++
81-
} else {
82-
// Empty list == ignore the entire block and don't emit it
83-
ignoredBlockCount++
84-
}
85-
}
86-
87-
if (elseMatch != null) {
88-
ignoredBlockCount = if (ignoredBlockCount == 0) {
89-
1
90-
} else {
91-
max(0, ignoredBlockCount - 1)
92-
}
93-
}
38+
fun process(fileName: String): String = runBlocking {
39+
renderer.render(fileName, replacements)
40+
}
9441

95-
// Lines with a marker shouldn't be appended in the first place;
96-
// normal lines are appended only if we are not excluding a previously false conditional
97-
val isNormalLine = ifMatch == null && ifgradleMatch == null && elseMatch == null && endMatch == null
98-
if (isNormalLine && ignoredBlockCount == 0) {
99-
text2.append(line).appendln()
100-
}
42+
/* Private */
10143

102-
if (endMatch != null) {
103-
if (loopBlockCount > 0) {
104-
// Emit the end of a prior loop
105-
text2.append("}").appendln()
106-
loopBlockCount = max(0, loopBlockCount - 1)
107-
} else {
108-
ignoredBlockCount = max(0, ignoredBlockCount - 1)
109-
}
110-
}
111-
}
112-
return text2.toString()
44+
private fun isVersionAtLeast(actual: String, required: String): Boolean {
45+
val actualVersion = SemanticVersion(actual)
46+
val requiredVersion = SemanticVersion(required)
47+
return actualVersion >= requiredVersion
11348
}
11449

115-
private fun shouldIgnoreIfgradleBlock(version: String): Boolean {
116-
if (targetGradleVersion == null) {
117-
return true
50+
private class FileReadingTemplateProvider(private val folder: File) : TemplateProvider {
51+
override suspend fun get(template: String): String {
52+
return File(folder, template).readText()
11853
}
119-
120-
val targetValue = SemanticVersion(targetGradleVersion)
121-
val compareValue = SemanticVersion(version)
122-
return compareValue > targetValue
12354
}
12455
}

0 commit comments

Comments
 (0)