From 2eac1d33ee0a824e6e0079609a75f18f982d7cee Mon Sep 17 00:00:00 2001 From: Ilya Matveev Date: Thu, 19 Oct 2017 19:29:25 +0300 Subject: [PATCH] gradle-plugin: Improve test harness --- .../plugin/test/DefaultSpecification.groovy | 2 +- .../test/IncrementalSpecification.groovy | 46 +++++------ .../gradle/plugin/test/KonanProject.groovy | 82 ++++++++++++------- .../plugin/test/PathSpecification.groovy | 29 +++---- .../test/RegressionSpecification.groovy | 2 +- .../plugin/test/TaskSpecification.groovy | 13 +-- 6 files changed, 88 insertions(+), 86 deletions(-) diff --git a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/DefaultSpecification.groovy b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/DefaultSpecification.groovy index f5ccaa0efa1..15341ec132f 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/DefaultSpecification.groovy +++ b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/DefaultSpecification.groovy @@ -11,7 +11,7 @@ class DefaultSpecification extends BaseKonanSpecification { plugins { id 'konan' } konanArtifacts { interop('stdio') - program('main') + library('main') } """.stripIndent()) it.generateDefFile("stdio.def", "") diff --git a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/IncrementalSpecification.groovy b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/IncrementalSpecification.groovy index 726e73a64f7..33fc3414b19 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/IncrementalSpecification.groovy +++ b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/IncrementalSpecification.groovy @@ -15,8 +15,8 @@ class IncrementalSpecification extends BaseKonanSpecification { return new Tuple(project, firstResult, secondResult) } - Tuple buildTwice(Closure change) { - return buildTwice(KonanProject.createWithInterop(projectDirectory), change) + Tuple buildTwice(ArtifactType mainArtifactType = ArtifactType.LIBRARY, Closure change) { + return buildTwice(KonanProject.createWithInterop(projectDirectory, mainArtifactType), change) } Boolean noRecompilationHappened(KonanProject project, BuildResult firstResult, BuildResult secondResult) { @@ -74,14 +74,6 @@ class IncrementalSpecification extends BaseKonanSpecification { 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") def 'Parameter changes should cause only recompilaton'() { when: @@ -98,24 +90,22 @@ class IncrementalSpecification extends BaseKonanSpecification { "baseDir" | "'build/new/outputDir'" "enableOptimizations" | "true" "linkerOpts" | "'--help'" - "languageVersion" | "'1.2'" - "apiVersion" | "'1.0'" "enableAssertions" | "true" "enableDebug" | "true" - "outputName" | "'foo'" + "artifactName" | "'foo'" "extraOpts" | "'--time'" "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: - def project = KonanProject.createWithInterop(projectDirectory) { KonanProject it -> + def project = KonanProject.createWithInterop(projectDirectory, ArtifactType.LIBRARY) { KonanProject it -> it.generateSrcFile(["src", "foo", "kotlin"], 'bar.kt', """ fun main(args: Array) { println("Hello!") } """.stripIndent()) } 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: @@ -124,12 +114,12 @@ class IncrementalSpecification extends BaseKonanSpecification { def 'Library change for a compilation task should cause only recompilation'() { 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.buildFile.append(""" konanArtifacts { library('lib') { - inputFiles fileTree('src/lib/kotlin') + srcFiles fileTree('src/lib/kotlin') } } """.stripIndent()) @@ -142,14 +132,14 @@ class IncrementalSpecification extends BaseKonanSpecification { onlyRecompilationHappened(*results) } - def 'Library changes should cause only recompilaton'() { + def 'Native library change for a compilation task should cause only recompilaton'() { 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.buildFile.append(""" konanArtifacts { bitcode('lib') { - inputFiles fileTree('src/lib/kotlin') + srcFiles fileTree('src/lib/kotlin') } } """.stripIndent()) @@ -176,7 +166,7 @@ class IncrementalSpecification extends BaseKonanSpecification { where: parameter | value - "pkg" | "'org.sample'" + "packageName" | "'org.sample'" "compilerOpts" | "'-g'" "linkerOpts" | "'--help'" "includeDirs" | "'src'" @@ -186,7 +176,7 @@ class IncrementalSpecification extends BaseKonanSpecification { def 'defFile change for an interop task should cause recompilation and interop reprocessing'() { when: - def project = KonanProject.createWithInterop(projectDirectory) + def project = KonanProject.createWithInterop(projectDirectory, ArtifactType.LIBRARY) def defFile = project.generateDefFile("foo.def", "#some content") def results = buildTwice(project) { KonanProject it -> 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'() { when: - def project = KonanProject.createWithInterop(projectDirectory) + def project = KonanProject.createWithInterop(projectDirectory, ArtifactType.LIBRARY) def header = project.generateSrcFile('header.h', "#define CONST 1") def results = buildTwice(project) { KonanProject it -> 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'() { 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.buildFile.append(""" konanArtifacts { bitcode('lib') { - inputFiles fileTree('src/lib/kotlin') + srcFiles fileTree('src/lib/kotlin') } } """.stripIndent()) @@ -231,7 +221,7 @@ class IncrementalSpecification extends BaseKonanSpecification { def 'konan version change should cause recompilation and interop reprocessing'() { 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") } def results = buildTwice(project) { KonanProject it -> @@ -243,5 +233,7 @@ class IncrementalSpecification extends BaseKonanSpecification { recompilationAndInteropProcessingHappened(*results) } + // TODO: Add incremental tests for the 'libraries' block. + //endregion } diff --git a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/KonanProject.groovy b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/KonanProject.groovy index 92e70989b2d..a34fc5f1d77 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/KonanProject.groovy +++ b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/KonanProject.groovy @@ -8,6 +8,17 @@ import java.nio.file.Files import java.nio.file.Path 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 { static String DEFAULT_ARTIFACT_NAME = 'main' @@ -37,12 +48,6 @@ class KonanProject { List getBuildingTasks() { return compilationTasks + interopTasks } List 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 = """ fun main(args: Array) { println(42) @@ -133,10 +138,10 @@ class KonanProject { */ File generateBuildFile() { def result = generateBuildFile(""" - plugins { id 'konan' } - - konanTargets = [${targets.collect { "'$it'" }.join(", ")}] - """.stripIndent() + |plugins { id 'konan' } + | + |konan.targets = [${targets.collect { "'$it'" }.join(", ")}] + """.stripMargin() ) compilationTasks = [":compileKonan", ":build"] return result @@ -233,68 +238,83 @@ class KonanProject { } /** 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) } - String defaultInteropTask(String target = HOST) { + static String defaultInteropTask(String target = HOST) { return compilationTask(DEFAULT_INTEROP_NAME, target) } /** 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()}" } - String defaultCompilationConfig() { + static String defaultCompilationConfig() { return artifactConfig(DEFAULT_ARTIFACT_NAME) } - String defaultInteropConfig() { + static String defaultInteropConfig() { return artifactConfig(DEFAULT_INTEROP_NAME) } - String artifactConfig(String artifactName) { + static String artifactConfig(String 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() - if (type == INTEROP) { + buildFile.append("konanArtifacts { $type('$name') }\n") + if (type == ArtifactType.INTEROP) { defFiles += generateDefFile("${name}.def", content) interopTasks += newTasks } 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 } - buildFile.append("konanArtifacts { $type('$name') }\n") + } /** Creates a project with default build and source files. */ - static KonanProject create(File projectDir, List targets = [HOST]) { + static KonanProject create(File projectDir, + ArtifactType artifactType = ArtifactType.PROGRAM, + List targets = [HOST]) { 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. */ - static KonanProject create(File projectDir, List targets = [HOST], Closure config) { - def result = create(projectDir, targets) + static KonanProject create(File projectDir, + ArtifactType artifactType = ArtifactType.PROGRAM, + List targets = [HOST], + Closure config) { + def result = create(projectDir, artifactType, targets) config(result) return result } - static KonanProject createWithInterop(File projectDir, List targets = [HOST]) { - return create(projectDir, targets) { KonanProject p -> - p.addCompilerArtifact(DEFAULT_INTEROP_NAME, DEFAULT_DEF_CONTENT, INTEROP) + static KonanProject createWithInterop(File projectDir, + ArtifactType mainArtifactType = ArtifactType.PROGRAM, + List targets = [HOST]) { + return create(projectDir, mainArtifactType, targets) { KonanProject p -> + p.addCompilerArtifact(DEFAULT_INTEROP_NAME, DEFAULT_DEF_CONTENT, ArtifactType.INTEROP) p.addLibraryToArtifact() } } - static KonanProject createWithInterop(File projectDir, List targets = [HOST], Closure config) { - def result = createWithInterop(projectDir, targets) + static KonanProject createWithInterop(File projectDir, + ArtifactType mainArtifactType = ArtifactType.PROGRAM, + List targets = [HOST], + Closure config) { + def result = createWithInterop(projectDir, mainArtifactType, targets) config(result) return result } diff --git a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/PathSpecification.groovy b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/PathSpecification.groovy index 50ef3e246e8..83103b774dc 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/PathSpecification.groovy +++ b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/PathSpecification.groovy @@ -1,8 +1,6 @@ package org.jetbrains.kotlin.gradle.plugin.test import org.gradle.testkit.runner.TaskOutcome -import org.jetbrains.kotlin.konan.target.CompilerOutputKind -import org.jetbrains.kotlin.konan.target.TargetManager class PathSpecification extends BaseKonanSpecification { @@ -13,10 +11,9 @@ class PathSpecification extends BaseKonanSpecification { def 'Plugin should create all necessary directories'() { when: def project = KonanProject.createWithInterop(projectDirectory) - project.addCompilerArtifact("lib", "fun foo() {}", KonanProject.LIBRARY) - project.addCompilerArtifact("bit", "fun bar() {}", KonanProject.BITCODE) - println(project.buildFile.text) - def result = project.createRunner().withArguments('build').build() + project.addCompilerArtifact("lib", "fun foo() {}", ArtifactType.LIBRARY) + project.addCompilerArtifact("bit", "fun bar() {}", ArtifactType.BITCODE) + project.createRunner().withArguments('build').build() then: 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'() { when: 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() then: @@ -41,7 +38,7 @@ class PathSpecification extends BaseKonanSpecification { def 'Plugin should stop building if the stub generator classpath is empty'() { when: 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() then: @@ -51,26 +48,24 @@ class PathSpecification extends BaseKonanSpecification { def 'Plugin should remove custom output directories'() { when: 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) } def res1 = project.createRunner().withArguments("build").build() def artifactExistsAfterBuild = customOutputDir.toPath() - .resolve("${KonanProject.HOST}").toFile() - .listFiles() - .findAll { it.name.matches("^${KonanProject.DEFAULT_ARTIFACT_NAME}\\.[^.]+") }.size() > 0 + .resolve("${KonanProject.HOST}/${KonanProject.DEFAULT_ARTIFACT_NAME}.klib").toFile() + .exists() def res2 = project.createRunner().withArguments("clean").build() - def artifactDoesntNotExistAfterClean = customOutputDir.toPath() - .resolve("${KonanProject.HOST}").toFile() - .listFiles() - .findAll { it.name.matches("^${KonanProject.DEFAULT_ARTIFACT_NAME}\\.[^.]+") }.isEmpty() + def artifactExistsAfterClean = customOutputDir.toPath() + .resolve("${KonanProject.HOST}/${KonanProject.DEFAULT_ARTIFACT_NAME}.klib").toFile() + .exists() then: res1.taskPaths(TaskOutcome.SUCCESS).containsAll(project.buildingTasks) res2.taskPaths(TaskOutcome.SUCCESS).contains(":clean") artifactExistsAfterBuild - artifactDoesntNotExistAfterClean + !artifactExistsAfterClean } } diff --git a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/RegressionSpecification.groovy b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/RegressionSpecification.groovy index d81e541fab4..b083fad68e7 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/RegressionSpecification.groovy +++ b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/RegressionSpecification.groovy @@ -46,7 +46,7 @@ class RegressionSpecification extends BaseKonanSpecification { fun main(args: Array) { foo() } - """, KonanProject.PROGRAM) + """, ArtifactType.PROGRAM) } def result = project.createRunner().withArguments('build').buildAndFail() diff --git a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/TaskSpecification.groovy b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/TaskSpecification.groovy index 5561c2c6e15..ebdbc1d8255 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/TaskSpecification.groovy +++ b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/TaskSpecification.groovy @@ -3,13 +3,11 @@ package org.jetbrains.kotlin.gradle.plugin.test import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.TaskOutcome -import java.nio.file.Paths - class TaskSpecification extends BaseKonanSpecification { def 'Configs should allow user to add dependencies to them'() { when: - def project = KonanProject.createWithInterop(projectDirectory) + def project = KonanProject.createWithInterop(projectDirectory, ArtifactType.LIBRARY) project.buildFile.append(""" task beforeInterop(type: DefaultTask) { doLast { println("Before Interop") } } 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'() { when: - def project = KonanProject.create(projectDirectory) + def project = KonanProject.create(projectDirectory, ArtifactType.LIBRARY) project.addSetting("measureTime", "true") def result = project.createRunner().withArguments('build').build() then: result.output.findAll(~/FRONTEND:\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) { - def project = KonanProject.createWithInterop(projectDirectory) + BuildResult failOnPropertyAccess(KonanProject project, String property) { project.buildFile.append(""" task testTask(type: DefaultTask) { doLast { @@ -49,8 +45,7 @@ class TaskSpecification extends BaseKonanSpecification { return project.createRunner().withArguments("testTask").buildAndFail() } - BuildResult failOnTaskAccess(String task) { - def project = KonanProject.createWithInterop(projectDirectory) + BuildResult failOnTaskAccess(KonanProject project, String task) { project.buildFile.append(""" task testTask(type: DefaultTask) { dependsOn $task