gradle-plugin: Improve test harness

This commit is contained in:
Ilya Matveev
2017-10-19 19:29:25 +03:00
committed by ilmat192
parent a04f9e423b
commit 2eac1d33ee
6 changed files with 88 additions and 86 deletions
@@ -11,7 +11,7 @@ class DefaultSpecification extends BaseKonanSpecification {
plugins { id 'konan' } plugins { id 'konan' }
konanArtifacts { konanArtifacts {
interop('stdio') interop('stdio')
program('main') library('main')
} }
""".stripIndent()) """.stripIndent())
it.generateDefFile("stdio.def", "") it.generateDefFile("stdio.def", "")
@@ -15,8 +15,8 @@ class IncrementalSpecification extends BaseKonanSpecification {
return new Tuple(project, firstResult, secondResult) return new Tuple(project, firstResult, secondResult)
} }
Tuple buildTwice(Closure change) { Tuple buildTwice(ArtifactType mainArtifactType = ArtifactType.LIBRARY, Closure change) {
return buildTwice(KonanProject.createWithInterop(projectDirectory), change) return buildTwice(KonanProject.createWithInterop(projectDirectory, mainArtifactType), change)
} }
Boolean noRecompilationHappened(KonanProject project, BuildResult firstResult, BuildResult secondResult) { Boolean noRecompilationHappened(KonanProject project, BuildResult firstResult, BuildResult secondResult) {
@@ -74,14 +74,6 @@ class IncrementalSpecification extends BaseKonanSpecification {
recompilationAndInteropProcessingHappened(*results) recompilationAndInteropProcessingHappened(*results)
} }
def 'Compilation is up-to-date if there is no changes in empty project'() {
when:
def results = buildTwice {}
then:
noRecompilationHappened(*results)
}
@Unroll("#parameter change for a compilation task should cause only recompilation") @Unroll("#parameter change for a compilation task should cause only recompilation")
def 'Parameter changes should cause only recompilaton'() { def 'Parameter changes should cause only recompilaton'() {
when: when:
@@ -98,24 +90,22 @@ class IncrementalSpecification extends BaseKonanSpecification {
"baseDir" | "'build/new/outputDir'" "baseDir" | "'build/new/outputDir'"
"enableOptimizations" | "true" "enableOptimizations" | "true"
"linkerOpts" | "'--help'" "linkerOpts" | "'--help'"
"languageVersion" | "'1.2'"
"apiVersion" | "'1.0'"
"enableAssertions" | "true" "enableAssertions" | "true"
"enableDebug" | "true" "enableDebug" | "true"
"outputName" | "'foo'" "artifactName" | "'foo'"
"extraOpts" | "'--time'" "extraOpts" | "'--time'"
"noDefaultLibs" | "true" "noDefaultLibs" | "true"
} }
def 'inputFiles change for a compilation task should cause only recompilation'() { def 'srcFiles change for a compilation task should cause only recompilation'() {
when: when:
def project = KonanProject.createWithInterop(projectDirectory) { KonanProject it -> def project = KonanProject.createWithInterop(projectDirectory, ArtifactType.LIBRARY) { KonanProject it ->
it.generateSrcFile(["src", "foo", "kotlin"], 'bar.kt', """ it.generateSrcFile(["src", "foo", "kotlin"], 'bar.kt', """
fun main(args: Array<String>) { println("Hello!") } fun main(args: Array<String>) { println("Hello!") }
""".stripIndent()) """.stripIndent())
} }
def results = buildTwice(project) { KonanProject it -> def results = buildTwice(project) { KonanProject it ->
it.addSetting("main", "inputFiles", "project.fileTree('src/foo/kotlin')") it.addSetting("main", "srcFiles", "project.fileTree('src/foo/kotlin')")
} }
then: then:
@@ -124,12 +114,12 @@ class IncrementalSpecification extends BaseKonanSpecification {
def 'Library change for a compilation task should cause only recompilation'() { def 'Library change for a compilation task should cause only recompilation'() {
when: when:
def project = KonanProject.create(projectDirectory) { KonanProject it -> def project = KonanProject.create(projectDirectory, ArtifactType.LIBRARY) { KonanProject it ->
it.generateSrcFile(["src", "lib", "kotlin"], "lib.kt", "fun bar() { println(\"Hello!\") }") it.generateSrcFile(["src", "lib", "kotlin"], "lib.kt", "fun bar() { println(\"Hello!\") }")
it.buildFile.append(""" it.buildFile.append("""
konanArtifacts { konanArtifacts {
library('lib') { library('lib') {
inputFiles fileTree('src/lib/kotlin') srcFiles fileTree('src/lib/kotlin')
} }
} }
""".stripIndent()) """.stripIndent())
@@ -142,14 +132,14 @@ class IncrementalSpecification extends BaseKonanSpecification {
onlyRecompilationHappened(*results) onlyRecompilationHappened(*results)
} }
def 'Library changes should cause only recompilaton'() { def 'Native library change for a compilation task should cause only recompilaton'() {
when: when:
def project = KonanProject.createWithInterop(projectDirectory) { KonanProject it -> def project = KonanProject.createWithInterop(projectDirectory, ArtifactType.LIBRARY) { KonanProject it ->
it.generateSrcFile(["src", "lib", "kotlin"], "lib.kt", "fun bar() { println(\"Hello!\") }") it.generateSrcFile(["src", "lib", "kotlin"], "lib.kt", "fun bar() { println(\"Hello!\") }")
it.buildFile.append(""" it.buildFile.append("""
konanArtifacts { konanArtifacts {
bitcode('lib') { bitcode('lib') {
inputFiles fileTree('src/lib/kotlin') srcFiles fileTree('src/lib/kotlin')
} }
} }
""".stripIndent()) """.stripIndent())
@@ -176,7 +166,7 @@ class IncrementalSpecification extends BaseKonanSpecification {
where: where:
parameter | value parameter | value
"pkg" | "'org.sample'" "packageName" | "'org.sample'"
"compilerOpts" | "'-g'" "compilerOpts" | "'-g'"
"linkerOpts" | "'--help'" "linkerOpts" | "'--help'"
"includeDirs" | "'src'" "includeDirs" | "'src'"
@@ -186,7 +176,7 @@ class IncrementalSpecification extends BaseKonanSpecification {
def 'defFile change for an interop task should cause recompilation and interop reprocessing'() { def 'defFile change for an interop task should cause recompilation and interop reprocessing'() {
when: when:
def project = KonanProject.createWithInterop(projectDirectory) def project = KonanProject.createWithInterop(projectDirectory, ArtifactType.LIBRARY)
def defFile = project.generateDefFile("foo.def", "#some content") def defFile = project.generateDefFile("foo.def", "#some content")
def results = buildTwice(project) { KonanProject it -> def results = buildTwice(project) { KonanProject it ->
it.addSetting("stdio", "defFile", defFile) it.addSetting("stdio", "defFile", defFile)
@@ -198,7 +188,7 @@ class IncrementalSpecification extends BaseKonanSpecification {
def 'header change for an interop task should cause recompilation and interop reprocessing'() { def 'header change for an interop task should cause recompilation and interop reprocessing'() {
when: when:
def project = KonanProject.createWithInterop(projectDirectory) def project = KonanProject.createWithInterop(projectDirectory, ArtifactType.LIBRARY)
def header = project.generateSrcFile('header.h', "#define CONST 1") def header = project.generateSrcFile('header.h', "#define CONST 1")
def results = buildTwice(project) { KonanProject it -> def results = buildTwice(project) { KonanProject it ->
it.addSetting("stdio", "headers", header) it.addSetting("stdio", "headers", header)
@@ -210,12 +200,12 @@ class IncrementalSpecification extends BaseKonanSpecification {
def 'link change for an interop task should cause recompilation and interop reprocessing'() { def 'link change for an interop task should cause recompilation and interop reprocessing'() {
when: when:
def project = KonanProject.createWithInterop(projectDirectory) { KonanProject it -> def project = KonanProject.createWithInterop(projectDirectory, ArtifactType.LIBRARY) { KonanProject it ->
it.generateSrcFile(["src", "lib", "kotlin"], 'lib.kt', 'fun foo() { println(42) }') it.generateSrcFile(["src", "lib", "kotlin"], 'lib.kt', 'fun foo() { println(42) }')
it.buildFile.append(""" it.buildFile.append("""
konanArtifacts { konanArtifacts {
bitcode('lib') { bitcode('lib') {
inputFiles fileTree('src/lib/kotlin') srcFiles fileTree('src/lib/kotlin')
} }
} }
""".stripIndent()) """.stripIndent())
@@ -231,7 +221,7 @@ class IncrementalSpecification extends BaseKonanSpecification {
def 'konan version change should cause recompilation and interop reprocessing'() { def 'konan version change should cause recompilation and interop reprocessing'() {
when: when:
def project = KonanProject.createWithInterop(projectDirectory) { KonanProject it -> def project = KonanProject.createWithInterop(projectDirectory, ArtifactType.LIBRARY) { KonanProject it ->
it.propertiesFile.append("konan.version=0.3\n") it.propertiesFile.append("konan.version=0.3\n")
} }
def results = buildTwice(project) { KonanProject it -> def results = buildTwice(project) { KonanProject it ->
@@ -243,5 +233,7 @@ class IncrementalSpecification extends BaseKonanSpecification {
recompilationAndInteropProcessingHappened(*results) recompilationAndInteropProcessingHappened(*results)
} }
// TODO: Add incremental tests for the 'libraries' block.
//endregion //endregion
} }
@@ -8,6 +8,17 @@ import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
enum ArtifactType {
PROGRAM("program"),
LIBRARY("library"),
BITCODE("bitcode"),
INTEROP("interop")
String type
ArtifactType(String type) { this.type = type }
String toString() { return type }
}
class KonanProject { class KonanProject {
static String DEFAULT_ARTIFACT_NAME = 'main' static String DEFAULT_ARTIFACT_NAME = 'main'
@@ -37,12 +48,6 @@ class KonanProject {
List<String> getBuildingTasks() { return compilationTasks + interopTasks } List<String> getBuildingTasks() { return compilationTasks + interopTasks }
List<String> getKonanTasks() { return getBuildingTasks() + downloadTask } List<String> getKonanTasks() { return getBuildingTasks() + downloadTask }
// TODO: Must be an enum.
static String PROGRAM = "program"
static String LIBRARY = "library"
static String BITCODE = "bitcode"
static String INTEROP = "interop"
static String DEFAULT_SRC_CONTENT = """ static String DEFAULT_SRC_CONTENT = """
fun main(args: Array<String>) { fun main(args: Array<String>) {
println(42) println(42)
@@ -133,10 +138,10 @@ class KonanProject {
*/ */
File generateBuildFile() { File generateBuildFile() {
def result = generateBuildFile(""" def result = generateBuildFile("""
plugins { id 'konan' } |plugins { id 'konan' }
|
konanTargets = [${targets.collect { "'$it'" }.join(", ")}] |konan.targets = [${targets.collect { "'$it'" }.join(", ")}]
""".stripIndent() """.stripMargin()
) )
compilationTasks = [":compileKonan", ":build"] compilationTasks = [":compileKonan", ":build"]
return result return result
@@ -233,68 +238,83 @@ class KonanProject {
} }
/** Returns the path of compileKonan... task for the default artifact. */ /** Returns the path of compileKonan... task for the default artifact. */
String defaultCompilationTask(String target = HOST) { static String defaultCompilationTask(String target = HOST) {
return compilationTask(DEFAULT_ARTIFACT_NAME, target) return compilationTask(DEFAULT_ARTIFACT_NAME, target)
} }
String defaultInteropTask(String target = HOST) { static String defaultInteropTask(String target = HOST) {
return compilationTask(DEFAULT_INTEROP_NAME, target) return compilationTask(DEFAULT_INTEROP_NAME, target)
} }
/** Returns the path of compileKonan... task for the artifact specified. */ /** Returns the path of compileKonan... task for the artifact specified. */
String compilationTask(String artifactName, String target = HOST) { static String compilationTask(String artifactName, String target = HOST) {
return ":compileKonan${artifactName.capitalize()}${target.capitalize()}" return ":compileKonan${artifactName.capitalize()}${target.capitalize()}"
} }
String defaultCompilationConfig() { static String defaultCompilationConfig() {
return artifactConfig(DEFAULT_ARTIFACT_NAME) return artifactConfig(DEFAULT_ARTIFACT_NAME)
} }
String defaultInteropConfig() { static String defaultInteropConfig() {
return artifactConfig(DEFAULT_INTEROP_NAME) return artifactConfig(DEFAULT_INTEROP_NAME)
} }
String artifactConfig(String artifactName) { static String artifactConfig(String artifactName) {
return "konanArtifacts.$artifactName" return "konanArtifacts.$artifactName"
} }
void addCompilerArtifact(String name, String content = "", String type = PROGRAM) { static String outputAccessCode(String artifact, String target = HOST) {
return "${artifactConfig(artifact)}.${target}.artifact"
}
void addCompilerArtifact(String name, String content = "", ArtifactType type = ArtifactType.PROGRAM) {
def newTasks = targets.collect { compilationTask(name, it) } + ":compileKonan${name.capitalize()}".toString() def newTasks = targets.collect { compilationTask(name, it) } + ":compileKonan${name.capitalize()}".toString()
if (type == INTEROP) { buildFile.append("konanArtifacts { $type('$name') }\n")
if (type == ArtifactType.INTEROP) {
defFiles += generateDefFile("${name}.def", content) defFiles += generateDefFile("${name}.def", content)
interopTasks += newTasks interopTasks += newTasks
} else { } else {
srcFiles += generateSrcFile(projectPath.resolve("src/$name/kotlin"), "source.kt", content) def src = generateSrcFile(projectPath.resolve("src/$name/kotlin"), "source.kt", content)
addSetting(name, "srcFiles", src)
srcFiles += src
compilationTasks += newTasks compilationTasks += newTasks
} }
buildFile.append("konanArtifacts { $type('$name') }\n")
} }
/** Creates a project with default build and source files. */ /** Creates a project with default build and source files. */
static KonanProject create(File projectDir, List<String> targets = [HOST]) { static KonanProject create(File projectDir,
ArtifactType artifactType = ArtifactType.PROGRAM,
List<String> targets = [HOST]) {
return createEmpty(projectDir, targets) { KonanProject p -> return createEmpty(projectDir, targets) { KonanProject p ->
p.addCompilerArtifact(DEFAULT_ARTIFACT_NAME, DEFAULT_SRC_CONTENT) p.addCompilerArtifact(DEFAULT_ARTIFACT_NAME, DEFAULT_SRC_CONTENT, artifactType)
} }
} }
// TODO: Add 'type parameter'
/** Creates a project with default build and source files. */ /** Creates a project with default build and source files. */
static KonanProject create(File projectDir, List<String> targets = [HOST], Closure config) { static KonanProject create(File projectDir,
def result = create(projectDir, targets) ArtifactType artifactType = ArtifactType.PROGRAM,
List<String> targets = [HOST],
Closure config) {
def result = create(projectDir, artifactType, targets)
config(result) config(result)
return result return result
} }
static KonanProject createWithInterop(File projectDir, List<String> targets = [HOST]) { static KonanProject createWithInterop(File projectDir,
return create(projectDir, targets) { KonanProject p -> ArtifactType mainArtifactType = ArtifactType.PROGRAM,
p.addCompilerArtifact(DEFAULT_INTEROP_NAME, DEFAULT_DEF_CONTENT, INTEROP) List<String> targets = [HOST]) {
return create(projectDir, mainArtifactType, targets) { KonanProject p ->
p.addCompilerArtifact(DEFAULT_INTEROP_NAME, DEFAULT_DEF_CONTENT, ArtifactType.INTEROP)
p.addLibraryToArtifact() p.addLibraryToArtifact()
} }
} }
static KonanProject createWithInterop(File projectDir, List<String> targets = [HOST], Closure config) { static KonanProject createWithInterop(File projectDir,
def result = createWithInterop(projectDir, targets) ArtifactType mainArtifactType = ArtifactType.PROGRAM,
List<String> targets = [HOST],
Closure config) {
def result = createWithInterop(projectDir, mainArtifactType, targets)
config(result) config(result)
return result return result
} }
@@ -1,8 +1,6 @@
package org.jetbrains.kotlin.gradle.plugin.test package org.jetbrains.kotlin.gradle.plugin.test
import org.gradle.testkit.runner.TaskOutcome import org.gradle.testkit.runner.TaskOutcome
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.TargetManager
class PathSpecification extends BaseKonanSpecification { class PathSpecification extends BaseKonanSpecification {
@@ -13,10 +11,9 @@ class PathSpecification extends BaseKonanSpecification {
def 'Plugin should create all necessary directories'() { def 'Plugin should create all necessary directories'() {
when: when:
def project = KonanProject.createWithInterop(projectDirectory) def project = KonanProject.createWithInterop(projectDirectory)
project.addCompilerArtifact("lib", "fun foo() {}", KonanProject.LIBRARY) project.addCompilerArtifact("lib", "fun foo() {}", ArtifactType.LIBRARY)
project.addCompilerArtifact("bit", "fun bar() {}", KonanProject.BITCODE) project.addCompilerArtifact("bit", "fun bar() {}", ArtifactType.BITCODE)
println(project.buildFile.text) project.createRunner().withArguments('build').build()
def result = project.createRunner().withArguments('build').build()
then: then:
project.konanBuildDir.toPath().resolve("bin/$KonanProject.HOST").toFile().listFiles().findAll { project.konanBuildDir.toPath().resolve("bin/$KonanProject.HOST").toFile().listFiles().findAll {
@@ -31,7 +28,7 @@ class PathSpecification extends BaseKonanSpecification {
def 'Plugin should stop building if the compiler classpath is empty'() { def 'Plugin should stop building if the compiler classpath is empty'() {
when: when:
def project = KonanProject.create(projectDirectory) def project = KonanProject.create(projectDirectory)
project.propertiesFile.write("konan.home=${projectDirectory.canonicalPath}}") project.propertiesFile.write("konan.home=fakepath")
def result = project.createRunner().withArguments('build').buildAndFail() def result = project.createRunner().withArguments('build').buildAndFail()
then: then:
@@ -41,7 +38,7 @@ class PathSpecification extends BaseKonanSpecification {
def 'Plugin should stop building if the stub generator classpath is empty'() { def 'Plugin should stop building if the stub generator classpath is empty'() {
when: when:
def project = KonanProject.createWithInterop(projectDirectory) def project = KonanProject.createWithInterop(projectDirectory)
project.propertiesFile.write("konan.home=${projectDirectory.canonicalPath}}") project.propertiesFile.write("konan.home=fakepath")
def result = project.createRunner().withArguments('build').buildAndFail() def result = project.createRunner().withArguments('build').buildAndFail()
then: then:
@@ -51,26 +48,24 @@ class PathSpecification extends BaseKonanSpecification {
def 'Plugin should remove custom output directories'() { def 'Plugin should remove custom output directories'() {
when: when:
def customOutputDir = projectDirectory.toPath().resolve("foo").toFile() def customOutputDir = projectDirectory.toPath().resolve("foo").toFile()
def project = KonanProject.create(projectDirectory) { KonanProject it -> def project = KonanProject.create(projectDirectory, ArtifactType.LIBRARY) { KonanProject it ->
it.addSetting("baseDir", customOutputDir) it.addSetting("baseDir", customOutputDir)
} }
def res1 = project.createRunner().withArguments("build").build() def res1 = project.createRunner().withArguments("build").build()
def artifactExistsAfterBuild = customOutputDir.toPath() def artifactExistsAfterBuild = customOutputDir.toPath()
.resolve("${KonanProject.HOST}").toFile() .resolve("${KonanProject.HOST}/${KonanProject.DEFAULT_ARTIFACT_NAME}.klib").toFile()
.listFiles() .exists()
.findAll { it.name.matches("^${KonanProject.DEFAULT_ARTIFACT_NAME}\\.[^.]+") }.size() > 0
def res2 = project.createRunner().withArguments("clean").build() def res2 = project.createRunner().withArguments("clean").build()
def artifactDoesntNotExistAfterClean = customOutputDir.toPath() def artifactExistsAfterClean = customOutputDir.toPath()
.resolve("${KonanProject.HOST}").toFile() .resolve("${KonanProject.HOST}/${KonanProject.DEFAULT_ARTIFACT_NAME}.klib").toFile()
.listFiles() .exists()
.findAll { it.name.matches("^${KonanProject.DEFAULT_ARTIFACT_NAME}\\.[^.]+") }.isEmpty()
then: then:
res1.taskPaths(TaskOutcome.SUCCESS).containsAll(project.buildingTasks) res1.taskPaths(TaskOutcome.SUCCESS).containsAll(project.buildingTasks)
res2.taskPaths(TaskOutcome.SUCCESS).contains(":clean") res2.taskPaths(TaskOutcome.SUCCESS).contains(":clean")
artifactExistsAfterBuild artifactExistsAfterBuild
artifactDoesntNotExistAfterClean !artifactExistsAfterClean
} }
} }
@@ -46,7 +46,7 @@ class RegressionSpecification extends BaseKonanSpecification {
fun main(args: Array<String>) { fun main(args: Array<String>) {
foo() foo()
} }
""", KonanProject.PROGRAM) """, ArtifactType.PROGRAM)
} }
def result = project.createRunner().withArguments('build').buildAndFail() def result = project.createRunner().withArguments('build').buildAndFail()
@@ -3,13 +3,11 @@ package org.jetbrains.kotlin.gradle.plugin.test
import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.BuildResult
import org.gradle.testkit.runner.TaskOutcome import org.gradle.testkit.runner.TaskOutcome
import java.nio.file.Paths
class TaskSpecification extends BaseKonanSpecification { class TaskSpecification extends BaseKonanSpecification {
def 'Configs should allow user to add dependencies to them'() { def 'Configs should allow user to add dependencies to them'() {
when: when:
def project = KonanProject.createWithInterop(projectDirectory) def project = KonanProject.createWithInterop(projectDirectory, ArtifactType.LIBRARY)
project.buildFile.append(""" project.buildFile.append("""
task beforeInterop(type: DefaultTask) { doLast { println("Before Interop") } } task beforeInterop(type: DefaultTask) { doLast { println("Before Interop") } }
task beforeCompilation(type: DefaultTask) { doLast { println("Before compilation") } } task beforeCompilation(type: DefaultTask) { doLast { println("Before compilation") } }
@@ -27,18 +25,16 @@ class TaskSpecification extends BaseKonanSpecification {
def 'Compiler should print time measurements if measureTime flag is set'() { def 'Compiler should print time measurements if measureTime flag is set'() {
when: when:
def project = KonanProject.create(projectDirectory) def project = KonanProject.create(projectDirectory, ArtifactType.LIBRARY)
project.addSetting("measureTime", "true") project.addSetting("measureTime", "true")
def result = project.createRunner().withArguments('build').build() def result = project.createRunner().withArguments('build').build()
then: then:
result.output.findAll(~/FRONTEND:\s+\d+\s+msec/).size() == 1 result.output.findAll(~/FRONTEND:\s+\d+\s+msec/).size() == 1
result.output.findAll(~/BACKEND:\s+\d+\s+msec/).size() == 1 result.output.findAll(~/BACKEND:\s+\d+\s+msec/).size() == 1
result.output.findAll(~/LINK_STAGE:\s+\d+\s+msec/).size() == 1
} }
BuildResult failOnPropertyAccess(String property) { BuildResult failOnPropertyAccess(KonanProject project, String property) {
def project = KonanProject.createWithInterop(projectDirectory)
project.buildFile.append(""" project.buildFile.append("""
task testTask(type: DefaultTask) { task testTask(type: DefaultTask) {
doLast { doLast {
@@ -49,8 +45,7 @@ class TaskSpecification extends BaseKonanSpecification {
return project.createRunner().withArguments("testTask").buildAndFail() return project.createRunner().withArguments("testTask").buildAndFail()
} }
BuildResult failOnTaskAccess(String task) { BuildResult failOnTaskAccess(KonanProject project, String task) {
def project = KonanProject.createWithInterop(projectDirectory)
project.buildFile.append(""" project.buildFile.append("""
task testTask(type: DefaultTask) { task testTask(type: DefaultTask) {
dependsOn $task dependsOn $task