diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index a0852a8f..8d14fdb1 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -27,7 +27,8 @@ jobs: needs: build strategy: matrix: - tests: [ProtobufJavaPluginTest, ProtobufKotlinDslCopySpecTest, ProtobufKotlinDslPluginTest, ProtobufAndroidPluginTest, ProtobufAndroidPluginKotlinTest, AndroidProjectDetectionTest] + tests: [ProtobufJavaPluginTest, ProtobufKotlinDslCopySpecTest, ProtobufKotlinDslPluginTest, ProtobufAndroidPluginTest, ProtobufAndroidPluginKotlinTest, AndroidProjectDetectionTest, IDESupportTest] + fail-fast: false runs-on: ubuntu-latest timeout-minutes: 30 steps: diff --git a/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy index 6550e002..814c7cf4 100644 --- a/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy @@ -263,6 +263,14 @@ class ProtobufPlugin implements Plugin { } postConfigure.add { + project.plugins.withId("eclipse") { + // This is required because the intellij/eclipse plugin does not allow adding source directories + // that do not exist. The intellij/eclipse config files should be valid from the start. + generateProtoTask.get().getOutputSourceDirectories().each { File outputDir -> + outputDir.mkdirs() + } + } + project.plugins.withId("idea") { boolean isTest = Utils.isTest(sourceSet.name) protoSrcDirSet.srcDirs.each { File protoDir -> diff --git a/src/test/groovy/com/google/protobuf/gradle/IDESupportTest.groovy b/src/test/groovy/com/google/protobuf/gradle/IDESupportTest.groovy new file mode 100644 index 00000000..489d3faa --- /dev/null +++ b/src/test/groovy/com/google/protobuf/gradle/IDESupportTest.groovy @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2017, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.protobuf.gradle + +import com.google.common.collect.ImmutableSet +import groovy.transform.CompileDynamic +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import spock.lang.Specification +import spock.lang.Unroll + +/** + * Unit tests to check ide metadata generation + */ +@CompileDynamic +class IDESupportTest extends Specification { + // Current supported version is Gradle 5+. + private static final List GRADLE_VERSIONS = ["5.6", "6.0", "6.7.1", "7.4.2"] + + @Unroll + void "testProject proto and generated output directories should be added to intellij [gradle #gradleVersion]"() { + given: "project from testProject" + File projectDir = ProtobufPluginTestHelper.projectBuilder('testIdea') + .copyDirs('testProjectBase', 'testProject') + .build() + + when: "idea is invoked" + BuildResult result = GradleRunner.create() + .withProjectDir(projectDir) + .withArguments('idea') + .withPluginClasspath() + .withGradleVersion(gradleVersion) + .build() + + then: "it succeed" + result.task(":idea").outcome == TaskOutcome.SUCCESS + Node imlRoot = new XmlParser().parse(projectDir.toPath().resolve("testProject.iml").toFile()) + Collection rootMgr = imlRoot.component.findAll { it.'@name' == 'NewModuleRootManager' } + assert rootMgr.size() == 1 + assert rootMgr.content.sourceFolder.size() == 1 + + Set sourceDir = [] as Set + Set testSourceDir = [] as Set + Set generatedDirs = [] as Set + rootMgr.content.sourceFolder[0].each { + if (Boolean.parseBoolean(it.@isTestSource)) { + testSourceDir.add(it.@url) + } else { + sourceDir.add(it.@url) + } + if (Boolean.parseBoolean(it.@generated)) { + generatedDirs.add(it.@url) + } + } + + Set expectedSourceDir = ImmutableSet.builder() + .add('file://$MODULE_DIR$/src/main/java') + .add('file://$MODULE_DIR$/src/grpc/proto') + .add('file://$MODULE_DIR$/src/main/proto') + .add('file://$MODULE_DIR$/build/extracted-include-protos/grpc') + .add('file://$MODULE_DIR$/build/extracted-protos/main') + .add('file://$MODULE_DIR$/build/extracted-include-protos/main') + .add('file://$MODULE_DIR$/build/extracted-protos/grpc') + .add('file://$MODULE_DIR$/build/generated/source/proto/grpc/java') + .add('file://$MODULE_DIR$/build/generated/source/proto/grpc/grpc_output') + .add('file://$MODULE_DIR$/build/generated/source/proto/main/java') + .build() + Set expectedTestSourceDir = ImmutableSet.builder() + .add('file://$MODULE_DIR$/src/test/java') + .add('file://$MODULE_DIR$/src/test/proto') + .add('file://$MODULE_DIR$/build/extracted-protos/test') + .add('file://$MODULE_DIR$/build/extracted-include-protos/test') + .add('file://$MODULE_DIR$/build/generated/source/proto/test/java') + .build() + Set expectedGeneratedDirs = [ + 'file://$MODULE_DIR$/build/extracted-include-protos/grpc', + 'file://$MODULE_DIR$/build/extracted-protos/main', + 'file://$MODULE_DIR$/build/extracted-include-protos/main', + 'file://$MODULE_DIR$/build/extracted-protos/grpc', + 'file://$MODULE_DIR$/build/generated/source/proto/grpc/java', + 'file://$MODULE_DIR$/build/generated/source/proto/grpc/grpc_output', + 'file://$MODULE_DIR$/build/generated/source/proto/main/java', + 'file://$MODULE_DIR$/build/extracted-protos/test', + 'file://$MODULE_DIR$/build/extracted-include-protos/test', + 'file://$MODULE_DIR$/build/generated/source/proto/test/java', + ] + assert Objects.equals(expectedSourceDir, sourceDir) + assert Objects.equals(expectedTestSourceDir, testSourceDir) + Objects.equals(expectedGeneratedDirs, generatedDirs) + + where: + gradleVersion << GRADLE_VERSIONS + } + + @Unroll + void "testProject proto and generated output directories should be added to Eclipse [gradle #gradleVersion]"() { + given: "project from testProject" + File projectDir = ProtobufPluginTestHelper.projectBuilder('testEclipse') + .copyDirs('testProjectBase', 'testProject') + .build() + + when: "eclipse is invoked" + BuildResult result = GradleRunner.create() + .withProjectDir(projectDir) + .withArguments('eclipse') + .withPluginClasspath() + .withGradleVersion(gradleVersion) + .build() + + then: "it succeed" + result.task(":eclipse").outcome == TaskOutcome.SUCCESS + Node classpathFile = new XmlParser().parse(projectDir.toPath().resolve(".classpath").toFile()) + Collection srcEntries = classpathFile.classpathentry.findAll { it.'@kind' == 'src' } + assert srcEntries.size() == 6 + + Set sourceDir = [] as Set + srcEntries.each { + String path = it.@path + sourceDir.add(path) + if (path.startsWith("build/generated/source/proto")) { + if (path.contains("test")) { + // test source path has one more attribute: ["test"="true"] + assert it.attributes.attribute.size() == 3 + } else { + assert it.attributes.attribute.size() == 2 + } + } + } + + Set expectedSourceDir = ImmutableSet.builder() + .add('src/main/java') + .add('src/test/java') + .add('build/generated/source/proto/grpc/java') + .add('build/generated/source/proto/grpc/grpc_output') + .add('build/generated/source/proto/main/java') + .add('build/generated/source/proto/test/java') + .build() + assert Objects.equals(expectedSourceDir, sourceDir) + + where: + gradleVersion << GRADLE_VERSIONS + } +} diff --git a/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy b/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy index 7f6e60d4..58c72b59 100644 --- a/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy +++ b/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy @@ -1,6 +1,5 @@ package com.google.protobuf.gradle -import com.google.common.collect.ImmutableSet import groovy.transform.CompileDynamic import org.gradle.api.Project import org.gradle.testfixtures.ProjectBuilder @@ -339,81 +338,6 @@ class ProtobufJavaPluginTest extends Specification { gradleVersion << GRADLE_VERSIONS } - @Unroll - void "testProject proto and generated output directories should be added to intellij [gradle #gradleVersion]"() { - given: "project from testProject" - File projectDir = ProtobufPluginTestHelper.projectBuilder('testIdea') - .copyDirs('testProjectBase', 'testProject') - .build() - - when: "idea is invoked" - BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) - .withArguments('idea') - .withPluginClasspath() - .withGradleVersion(gradleVersion) - .build() - - then: "it succeed" - result.task(":idea").outcome == TaskOutcome.SUCCESS - Node imlRoot = new XmlParser().parse(projectDir.toPath().resolve("testProject.iml").toFile()) - Collection rootMgr = imlRoot.component.findAll { it.'@name' == 'NewModuleRootManager' } - assert rootMgr.size() == 1 - assert rootMgr.content.sourceFolder.size() == 1 - - Set sourceDir = [] as Set - Set testSourceDir = [] as Set - Set generatedDirs = [] as Set - rootMgr.content.sourceFolder[0].each { - if (Boolean.parseBoolean(it.@isTestSource)) { - testSourceDir.add(it.@url) - } else { - sourceDir.add(it.@url) - } - if (Boolean.parseBoolean(it.@generated)) { - generatedDirs.add(it.@url) - } - } - - Set expectedSourceDir = ImmutableSet.builder() - .add('file://$MODULE_DIR$/src/main/java') - .add('file://$MODULE_DIR$/src/grpc/proto') - .add('file://$MODULE_DIR$/src/main/proto') - .add('file://$MODULE_DIR$/build/extracted-include-protos/grpc') - .add('file://$MODULE_DIR$/build/extracted-protos/main') - .add('file://$MODULE_DIR$/build/extracted-include-protos/main') - .add('file://$MODULE_DIR$/build/extracted-protos/grpc') - .add('file://$MODULE_DIR$/build/generated/source/proto/grpc/java') - .add('file://$MODULE_DIR$/build/generated/source/proto/grpc/grpc_output') - .add('file://$MODULE_DIR$/build/generated/source/proto/main/java') - .build() - Set expectedTestSourceDir = ImmutableSet.builder() - .add('file://$MODULE_DIR$/src/test/java') - .add('file://$MODULE_DIR$/src/test/proto') - .add('file://$MODULE_DIR$/build/extracted-protos/test') - .add('file://$MODULE_DIR$/build/extracted-include-protos/test') - .add('file://$MODULE_DIR$/build/generated/source/proto/test/java') - .build() - Set expectedGeneratedDirs = [ - 'file://$MODULE_DIR$/build/extracted-include-protos/grpc', - 'file://$MODULE_DIR$/build/extracted-protos/main', - 'file://$MODULE_DIR$/build/extracted-include-protos/main', - 'file://$MODULE_DIR$/build/extracted-protos/grpc', - 'file://$MODULE_DIR$/build/generated/source/proto/grpc/java', - 'file://$MODULE_DIR$/build/generated/source/proto/grpc/grpc_output', - 'file://$MODULE_DIR$/build/generated/source/proto/main/java', - 'file://$MODULE_DIR$/build/extracted-protos/test', - 'file://$MODULE_DIR$/build/extracted-include-protos/test', - 'file://$MODULE_DIR$/build/generated/source/proto/test/java', - ] - assert Objects.equals(expectedSourceDir, sourceDir) - assert Objects.equals(expectedTestSourceDir, testSourceDir) - Objects.equals(expectedGeneratedDirs, generatedDirs) - - where: - gradleVersion << GRADLE_VERSIONS - } - @Unroll void "test proto generation is not up-to-date on dependency changes [gradle #gradleVersion]"() { given: "project from testProject" diff --git a/testProject/build.gradle b/testProject/build.gradle index 62ec63be..a44e7fee 100644 --- a/testProject/build.gradle +++ b/testProject/build.gradle @@ -3,6 +3,7 @@ plugins { id 'java' id 'idea' + id 'eclipse' id 'com.google.protobuf' } apply from: 'build_base.gradle'