Support Gradle build cache when using absolute path references (#13671)

* Add cacheability tests for same directory and different directory

(cherry picked from commit 46c96daf3b020ab02e13113166046d2383c04990)

* Clean up/add more cacheability tests

(cherry picked from commit 5d09d914ba7224b82dd7a3bd20beaf2b6fd3eb94)

* Add test for inputSpec

(cherry picked from commit 8d9e0dbb9d865ad3e61b60692b3ef6ca85b70b75)

* Add incremental build tests, run with multiple Gradle versions

(cherry picked from commit ba1d554c375068974d1799d6be6731ca1d59a783)

* Add proper Input annotations to task inputs

(cherry picked from commit 18da6161ba2b406876c516a3059850d9a0bc9ca0)

* Perform clean on tests where expectation is cleaned outputs

(cherry picked from commit 4670db92686c02d5dd2b69976488c33defd3a464)

* Ensure before & after files are the same

(cherry picked from commit 9150b4a5596b229a4404a92cfedbb795c6bb5b0d)
This commit is contained in:
Eric Haag
2022-10-17 10:13:57 -05:00
committed by GitHub
parent 9c2757b4e1
commit 31ea76b58b
4 changed files with 405 additions and 7 deletions

View File

@@ -21,6 +21,7 @@ import org.gradle.api.GradleException
import org.gradle.api.provider.Property
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Optional
@@ -98,7 +99,8 @@ open class GenerateTask : DefaultTask() {
* The template directory holding a custom template.
*/
@Optional
@Input
@InputDirectory
@PathSensitive(PathSensitivity.RELATIVE)
val templateDir = project.objects.property<String?>()
/**
@@ -122,7 +124,8 @@ open class GenerateTask : DefaultTask() {
* Supported options can be different for each language. Run config-help -g {generator name} command for language specific config options.
*/
@Optional
@Input
@InputFile
@PathSensitive(PathSensitivity.RELATIVE)
val configFile = project.objects.property<String>()
/**
@@ -320,7 +323,8 @@ open class GenerateTask : DefaultTask() {
* Specifies an override location for the .openapi-generator-ignore file. Most useful on initial generation.
*/
@Optional
@Input
@InputFile
@PathSensitive(PathSensitivity.RELATIVE)
val ignoreFileOverride = project.objects.property<String?>()
/**

View File

@@ -0,0 +1,200 @@
package org.openapitools.generator.gradle.plugin
import org.gradle.testkit.runner.TaskOutcome
import org.testng.annotations.BeforeMethod
import org.testng.annotations.DataProvider
import org.testng.annotations.Test
import java.io.File
import kotlin.test.assertEquals
class GenerateTaskFromCacheTest : TestBase() {
private lateinit var buildCacheDir: File
private lateinit var projectDir1: File
private lateinit var projectDir2: File
@BeforeMethod
override fun before() {
initialize()
buildCacheDir = temp.resolve("buildCacheDir").apply { mkdir() }
projectDir1 = temp.resolve("projectDir1").apply { mkdir() }
projectDir2 = temp.resolve("projectDir2").apply { mkdir() }
}
@DataProvider(name = "gradle_version_provider")
private fun gradleVersionProvider(): Array<Array<String>> = arrayOf(arrayOf("6.9.2"), arrayOf("7.5.1"))
// inputSpec tests
private val inputSpecExtensionContents = """
generatorName = "kotlin"
inputSpec = file("spec.yaml").absolutePath
""".trimIndent()
@Test(dataProvider = "gradle_version_provider")
fun `inputSpec - same directory - openApiGenerate task output should come from cache`(gradleVersion: String) {
runCacheabilityTestUsingSameDirectory(gradleVersion, inputSpecExtensionContents)
}
@Test(dataProvider = "gradle_version_provider")
fun `inputSpec - different directory - openApiGenerate task output should come from cache`(gradleVersion: String) {
runCacheabilityTestUsingDifferentDirectories(gradleVersion, inputSpecExtensionContents)
}
// templateDir tests
private val templateDirExtensionContents = """
generatorName = "kotlin"
inputSpec = file("spec.yaml").absolutePath
templateDir = file("templateDir").absolutePath
""".trimIndent()
private fun initializeTemplateDirTest() {
projectDir1.resolve("templateDir").mkdir()
}
@Test(dataProvider = "gradle_version_provider")
fun `templateDir - same directory - openApiGenerate task output should come from cache`(gradleVersion: String) {
initializeTemplateDirTest()
runCacheabilityTestUsingSameDirectory(gradleVersion, templateDirExtensionContents)
}
@Test(dataProvider = "gradle_version_provider")
fun `templateDir - different directory - openApiGenerate task output should come from cache`(gradleVersion: String) {
initializeTemplateDirTest()
runCacheabilityTestUsingDifferentDirectories(gradleVersion, templateDirExtensionContents)
}
// configFile tests
private val configFileExtensionContents = """
generatorName = "kotlin"
inputSpec = file("spec.yaml").absolutePath
configFile = file("configFile").absolutePath
""".trimIndent()
private fun initializeConfigFileTest() {
val configFile = projectDir1.resolve("configFile")
configFile.createNewFile()
configFile.writeText("""{"foo":"bar"}""")
}
@Test(dataProvider = "gradle_version_provider")
fun `configFile - same directory - openApiGenerate task output should come from cache`(gradleVersion: String) {
initializeConfigFileTest()
runCacheabilityTestUsingSameDirectory(gradleVersion, configFileExtensionContents)
}
@Test(dataProvider = "gradle_version_provider")
fun `configFile - different directory - openApiGenerate task output should come from cache`(gradleVersion: String) {
initializeConfigFileTest()
runCacheabilityTestUsingDifferentDirectories(gradleVersion, configFileExtensionContents)
}
// ignoreFileOverride tests
private val ignoreFileOverrideExtensionContents = """
generatorName = "kotlin"
inputSpec = file("spec.yaml").absolutePath
ignoreFileOverride = file(".openapi-generator-ignore").absolutePath
""".trimIndent()
private fun initializeIgnoreFileTest() {
projectDir1.resolve(".openapi-generator-ignore").createNewFile()
}
@Test(dataProvider = "gradle_version_provider")
fun `ignoreFileOverride - same directory - openApiGenerate task output should come from cache`(gradleVersion: String) {
initializeIgnoreFileTest()
runCacheabilityTestUsingSameDirectory(gradleVersion, ignoreFileOverrideExtensionContents)
}
@Test(dataProvider = "gradle_version_provider")
fun `ignoreFileOverride - different directory - openApiGenerate task output should come from cache`(gradleVersion: String) {
initializeIgnoreFileTest()
runCacheabilityTestUsingDifferentDirectories(gradleVersion, ignoreFileOverrideExtensionContents)
}
// Helper methods & test fixtures
private fun runCacheabilityTestUsingSameDirectory(gradleVersion: String, extensionContents: String) {
// Arrange
withProject(extensionContents)
// Act
val result1 = build {
withProjectDir(projectDir1)
withArguments("--build-cache", "clean", "openApiGenerate")
withGradleVersion(gradleVersion)
}
val expectedRelativeFilePathSet = projectDir1.toRelativeFilePathSet()
val result2 = build {
withProjectDir(projectDir1)
withArguments("--build-cache", "clean", "openApiGenerate")
withGradleVersion(gradleVersion)
}
// Assert
assertEquals(TaskOutcome.SUCCESS, result1.task(":openApiGenerate")?.outcome)
assertEquals(TaskOutcome.FROM_CACHE, result2.task(":openApiGenerate")?.outcome)
assertEquals(expectedRelativeFilePathSet, projectDir1.toRelativeFilePathSet())
}
private fun runCacheabilityTestUsingDifferentDirectories(gradleVersion: String, extensionContents: String) {
// Arrange
withProject(extensionContents)
projectDir1.copyRecursively(projectDir2)
// Act
val result1 = build {
withProjectDir(projectDir1)
withArguments("--build-cache", "clean", "openApiGenerate")
withGradleVersion(gradleVersion)
}
val result2 = build {
withProjectDir(projectDir2)
withArguments("--build-cache", "clean", "openApiGenerate")
withGradleVersion(gradleVersion)
}
// Assert
assertEquals(TaskOutcome.SUCCESS, result1.task(":openApiGenerate")?.outcome)
assertEquals(TaskOutcome.FROM_CACHE, result2.task(":openApiGenerate")?.outcome)
assertEquals(projectDir1.toRelativeFilePathSet(), projectDir2.toRelativeFilePathSet())
}
private fun File.toRelativeFilePathSet() =
resolve("build").walk().map { it.toRelativeString(resolve("build")) }.toSet()
private fun withProject(extensionContents: String) {
val settingsContents = """
buildCache {
local {
directory = file("$buildCacheDir")
}
}
rootProject.name = "openapi-generator"
""".trimIndent()
val buildContents = """
plugins {
id 'base'
id 'org.openapi.generator'
}
openApiGenerate {
$extensionContents
}
""".trimIndent()
val projectFiles = mapOf(
"spec.yaml" to javaClass.classLoader.getResourceAsStream("specs/petstore-v3.0.yaml")!!
)
withProject(
projectDir = projectDir1,
settingsContents = settingsContents,
buildContents = buildContents,
projectFiles = projectFiles
)
}
}

View File

@@ -0,0 +1,173 @@
package org.openapitools.generator.gradle.plugin
import org.gradle.testkit.runner.TaskOutcome
import org.testng.annotations.DataProvider
import org.testng.annotations.Test
import java.io.File
import kotlin.test.assertEquals
class GenerateTaskUpToDateTest : TestBase() {
@DataProvider(name = "gradle_version_provider")
private fun gradleVersionProvider(): Array<Array<String>> = arrayOf(arrayOf("6.9.2"), arrayOf("7.5.1"))
// inputSpec tests
private val inputSpecExtensionContents = """
generatorName = "kotlin"
inputSpec = file("spec.yaml").absolutePath
""".trimIndent()
@Test(dataProvider = "gradle_version_provider")
fun `inputSpec - no file changes - should be up-to-date`(gradleVersion: String) {
runShouldBeUpToDateTest(gradleVersion, inputSpecExtensionContents)
}
@Test(dataProvider = "gradle_version_provider")
fun `inputSpec - has file changes - should execute`(gradleVersion: String) {
runShouldExecuteTest(gradleVersion, inputSpecExtensionContents) {
val inputSpec = File(temp, "spec.yaml")
val newContents = inputSpec.readText().replace("version: 1.0.0", "version: 1.0.1")
inputSpec.writeText(newContents)
}
}
// templateDir tests
private val templateDirExtensionContents = """
generatorName = "kotlin"
inputSpec = file("spec.yaml").absolutePath
templateDir = file("templateDir").absolutePath
""".trimIndent()
private fun initializeTemplateDirTest(): File {
val templateDir = temp.resolve("templateDir")
templateDir.mkdir()
return templateDir.resolve("templateFile").apply { writeText("contents") }
}
@Test(dataProvider = "gradle_version_provider")
fun `templateDir - no file changes - should be up-to-date`(gradleVersion: String) {
initializeTemplateDirTest()
runShouldBeUpToDateTest(gradleVersion, templateDirExtensionContents)
}
@Test(dataProvider = "gradle_version_provider")
fun `templateDir - has file changes - should execute`(gradleVersion: String) {
val templateFile = initializeTemplateDirTest()
runShouldExecuteTest(gradleVersion, templateDirExtensionContents) {
templateFile.writeText("new contents")
}
}
// configFile tests
private val configFileExtensionContents = """
generatorName = "kotlin"
inputSpec = file("spec.yaml").absolutePath
configFile = file("configFile").absolutePath
""".trimIndent()
private fun initializeConfigFileTest(): File {
return temp.resolve("configFile").apply { writeText("""{"foo":"bar"}""") }
}
@Test(dataProvider = "gradle_version_provider")
fun `configFile - no file changes - should be up-to-date`(gradleVersion: String) {
initializeConfigFileTest()
runShouldBeUpToDateTest(gradleVersion, configFileExtensionContents)
}
@Test(dataProvider = "gradle_version_provider")
fun `configFile - has file changes - should execute`(gradleVersion: String) {
val configFile = initializeConfigFileTest()
runShouldExecuteTest(gradleVersion, configFileExtensionContents) {
configFile.writeText("""{"foo":"baz"}""")
}
}
// ignoreFileOverride tests
private val ignoreFileOverrideExtensionContents = """
generatorName = "kotlin"
inputSpec = file("spec.yaml").absolutePath
ignoreFileOverride = file(".openapi-generator-ignore").absolutePath
""".trimIndent()
private fun initializeIgnoreFileTest(): File {
return temp.resolve(".openapi-generator-ignore").apply { writeText(".some_file_to_ignore") }
}
@Test(dataProvider = "gradle_version_provider")
fun `ignoreFileOverride - no file changes - should be up-to-date`(gradleVersion: String) {
initializeIgnoreFileTest()
runShouldBeUpToDateTest(gradleVersion, ignoreFileOverrideExtensionContents)
}
@Test(dataProvider = "gradle_version_provider")
fun `ignoreFileOverride - has file changes - should execute`(gradleVersion: String) {
val ignoreFileOverride = initializeIgnoreFileTest()
runShouldExecuteTest(gradleVersion, ignoreFileOverrideExtensionContents) {
ignoreFileOverride.writeText(".new_file_to_ignore")
}
}
// Helper methods & test fixtures
private fun runShouldBeUpToDateTest(gradleVersion: String, extensionContents: String) {
// Arrange
withProject(extensionContents)
// Act
val result1 = build {
withArguments("clean", "openApiGenerate")
withGradleVersion(gradleVersion)
}
val result2 = build {
withArguments("openApiGenerate")
withGradleVersion(gradleVersion)
}
// Assert
assertEquals(TaskOutcome.SUCCESS, result1.task(":openApiGenerate")?.outcome)
assertEquals(TaskOutcome.UP_TO_DATE, result2.task(":openApiGenerate")?.outcome)
}
private fun runShouldExecuteTest(gradleVersion: String, extensionContents: String, action: () -> Unit) {
// Arrange
withProject(extensionContents)
// Act
val result1 = build {
withArguments("clean", "openApiGenerate")
withGradleVersion(gradleVersion)
}
action()
val result2 = build {
withArguments("openApiGenerate")
withGradleVersion(gradleVersion)
}
// Assert
assertEquals(TaskOutcome.SUCCESS, result1.task(":openApiGenerate")?.outcome)
assertEquals(TaskOutcome.SUCCESS, result2.task(":openApiGenerate")?.outcome)
}
private fun withProject(extensionContents: String) {
val buildContents = """
plugins {
id 'base'
id 'org.openapi.generator'
}
openApiGenerate {
$extensionContents
}
""".trimIndent()
File(temp, "build.gradle").writeText(buildContents)
File(javaClass.classLoader.getResource("specs/petstore-v3.0.yaml")!!.toURI())
.copyTo(File(temp, "spec.yaml"))
}
}

View File

@@ -1,5 +1,6 @@
package org.openapitools.generator.gradle.plugin
import org.gradle.testkit.runner.GradleRunner
import org.testng.annotations.AfterMethod
import org.testng.annotations.BeforeMethod
import java.io.File
@@ -10,7 +11,11 @@ abstract class TestBase {
protected open lateinit var temp: File
@BeforeMethod
protected fun before() {
protected open fun before() {
initialize()
}
protected fun initialize() {
temp = createTempDirectory(javaClass.simpleName).toFile()
temp.deleteOnExit()
}
@@ -20,12 +25,28 @@ abstract class TestBase {
temp.deleteRecursively()
}
protected fun withProject(buildContents: String, projectFiles: Map<String, InputStream> = mapOf()) {
File(temp, "build.gradle").writeText(buildContents)
protected fun withProject(
buildContents: String,
projectFiles: Map<String, InputStream> = mapOf(),
projectDir: File? = temp,
settingsContents: String? = null
) {
File(projectDir, "build.gradle").writeText(buildContents)
if (!settingsContents.isNullOrEmpty()) {
File(projectDir, "settings.gradle").writeText(settingsContents)
}
projectFiles.forEach { entry ->
val target = File(temp, entry.key)
val target = File(projectDir, entry.key)
entry.value.copyTo(target.outputStream())
}
}
protected fun build(configure: GradleRunner.() -> Unit = {}) =
GradleRunner.create()
.withProjectDir(temp)
.withPluginClasspath()
.forwardOutput()
.apply(configure)
.build()!!
}