org.gradle.language.java.JvmApiSpecIntegrationTest.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 6.9.1 API redistribution.
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.language.java
import org.gradle.integtests.fixtures.jvm.JvmSourceFile
import org.gradle.integtests.fixtures.jvm.TestJvmComponent
import org.gradle.integtests.language.AbstractJvmLanguageIntegrationTest
import org.gradle.language.fixtures.TestJavaComponent
import org.gradle.test.fixtures.file.TestFile
import org.gradle.util.Requires
import org.gradle.util.TestPrecondition
import spock.lang.Issue
@Requires(TestPrecondition.JDK8_OR_EARLIER)
class JvmApiSpecIntegrationTest extends AbstractJvmLanguageIntegrationTest {
TestJvmComponent app = new TestJavaComponent()
def "should succeed when public api specification is empty"() {
when:
buildFile << """
model {
components {
myLib(JvmLibrarySpec) {
api {
}
}
}
}
"""
then:
succeeds "assemble"
}
def "should succeed when public api specification is fully configured"() {
when:
buildFile << """
model {
components {
myLib(JvmLibrarySpec) {
api {
exports 'com.example.p1'
exports 'com.example.p2'
}
}
}
}
"""
then:
succeeds "assemble"
}
@Issue('GRADLE-3483')
def "can scan Annotations for public API"() {
when:
buildFile << """
repositories {
mavenCentral()
}
model {
components {
myLib(JvmLibrarySpec) {
api {
exports 'com.example.p1'
}
}
}
}
"""
app.sources.add(new JvmSourceFile("com/example/p1", "MyCustomAnnotation.java", """
package com.example.p1;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomAnnotation {
String[] names();
}
""".stripIndent()))
app.sources.add(new JvmSourceFile("com/example/p1", "ExampleApi.java",
"""
package com.example.p1;
public class ExampleApi {
@MyCustomAnnotation(names = {"Hello"})
public String field;
public void canRun() { }
}
""".stripIndent()))
app.sources*.writeToDir(file("src/myLib/java"))
then:
succeeds "assemble"
}
def "should succeed when public api exports an unnamed package"() {
when:
buildFile << """
model {
components {
myLib(JvmLibrarySpec) {
api {
exports ''
}
}
}
}
"""
then:
succeeds "assemble"
}
def "should fail when public api exports an invalid package name"() {
when:
buildFile << """
model {
components {
myLib(JvmLibrarySpec) {
api {
exports 'com.example.p-1'
}
}
}
}
"""
then:
fails "assemble"
failure.assertHasCause("Invalid public API specification: 'com.example.p-1' is not a valid package name")
}
def "should fail when public api exports the same package more than once"() {
when:
buildFile << """
model {
components {
myLib(JvmLibrarySpec) {
api {
exports 'com.example.p1'
exports 'com.example.p1'
}
}
}
}
"""
then:
fails "assemble"
failure.assertHasCause("Invalid public API specification: package 'com.example.p1' has already been exported")
}
def "api jar should contain only classes declared in packages exported in api spec"() {
when:
addNonApiClasses()
app.writeResources(file("src/myLib/resources"))
and:
buildFile << """
model {
components {
myLib(JvmLibrarySpec) {
api {
exports 'compile.test'
}
}
}
}
"""
then:
succeeds "assemble"
and:
def allClasses = app.sources*.classFile.fullPath
def resources = app.resources*.fullPath
def apiClassesOnly = (allClasses - resources -
["non_api/pkg/InternalPerson.class", "compile/test/internal/Util.class"])
resources.size() > 0
jarFile("build/jars/myLib/jar/myLib.jar").hasDescendants((allClasses + resources) as String[])
jarFile("build/jars/myLib/jar/api/myLib.jar").hasDescendants(apiClassesOnly as String[])
}
def "api jar should not be rebuilt when resource class changes"() {
when:
addNonApiClasses()
List resources = app.writeResources(file("src/myLib/resources"))
and:
buildFile << """
model {
components {
myLib(JvmLibrarySpec) {
api {
exports 'compile.test'
}
}
}
}
"""
then:
succeeds "assemble"
when:
resources.each {
it << "More content"
}
then:
succeeds "assemble"
and:
skipped(':myLibApiJar')
}
def "api jar should be rebuilt if API spec adds a new exported package"() {
when:
addNonApiClasses()
and:
buildFile << """
model {
components {
myLib(JvmLibrarySpec) {
api {
exports 'compile.test'
}
}
}
}
"""
then:
succeeds "assemble"
and:
def allClasses = app.sources*.classFile.fullPath
def apiClassesOnly = (allClasses -
["non_api/pkg/InternalPerson.class", "compile/test/internal/Util.class"])
jarFile("build/jars/myLib/jar/myLib.jar").hasDescendants((allClasses) as String[])
jarFile("build/jars/myLib/jar/api/myLib.jar").hasDescendants(apiClassesOnly as String[])
when:
buildFile << """
model {
components {
myLib {
api {
exports 'compile.test.internal'
}
}
}
}
"""
allClasses = app.sources*.classFile.fullPath
apiClassesOnly = (allClasses -
["non_api/pkg/InternalPerson.class"])
then:
succeeds 'assemble'
and:
skipped ':compileMyLibJarMyLibJava'
skipped ':createMyLibJar'
executedAndNotSkipped(':myLibApiJar')
jarFile("build/jars/myLib/jar/myLib.jar").hasDescendants((allClasses) as String[])
jarFile("build/jars/myLib/jar/api/myLib.jar").hasDescendants(apiClassesOnly as String[])
}
def "api jar should be rebuilt if API spec removes an exported package"() {
when:
addNonApiClasses()
and:
buildFile << """
model {
components {
myLib(JvmLibrarySpec) {
api {
exports 'compile.test'
exports 'compile.test.internal'
}
}
}
}
"""
then:
succeeds "assemble"
and:
def allClasses = app.sources*.classFile.fullPath
def apiClassesOnly = (allClasses -
["non_api/pkg/InternalPerson.class"])
jarFile("build/jars/myLib/jar/myLib.jar").hasDescendants((allClasses) as String[])
jarFile("build/jars/myLib/jar/api/myLib.jar").hasDescendants(apiClassesOnly as String[])
when:
buildFile.text = buildFile.text.replace(/exports 'compile.test.internal'/,'')
allClasses = app.sources*.classFile.fullPath
apiClassesOnly = (allClasses -
["non_api/pkg/InternalPerson.class", "compile/test/internal/Util.class"])
then:
succeeds 'assemble'
and:
skipped ':compileMyLibJarMyLibJava'
skipped ':createMyLibJar'
executedAndNotSkipped(':myLibApiJar')
jarFile("build/jars/myLib/jar/myLib.jar").hasDescendants((allClasses) as String[])
jarFile("build/jars/myLib/jar/api/myLib.jar").hasDescendants(apiClassesOnly as String[])
}
def "api jar should be empty if specification matches no package found in runtime jar"() {
when:
addNonApiClasses()
and:
buildFile << """
model {
components {
myLib(JvmLibrarySpec) {
api {
exports 'arbitrary.pkg'
}
}
}
}
"""
then:
succeeds "assemble"
executedAndNotSkipped(':myLibApiJar')
def allClasses = app.sources*.classFile.fullPath
def apiClassesOnly = []
jarFile("build/jars/myLib/jar/myLib.jar").hasDescendants((allClasses) as String[])
jarFile("build/jars/myLib/jar/api/myLib.jar").hasDescendants(apiClassesOnly as String[])
}
def "api jar should include all library packages when no api specification is declared"() {
when:
addNonApiClasses()
and:
buildFile << """
model {
components {
myLib(JvmLibrarySpec) {
}
}
}
"""
then:
succeeds "assemble"
and:
def allClasses = app.sources*.classFile.fullPath as String[]
jarFile("build/jars/myLib/jar/myLib.jar").hasDescendants(allClasses)
jarFile("build/jars/myLib/jar/api/myLib.jar").hasDescendants(allClasses)
}
def "api jar should include all library packages api specification is declared empty"() {
when:
addNonApiClasses()
and:
buildFile << """
model {
components {
myLib(JvmLibrarySpec) {
api {
}
}
}
}
"""
then:
succeeds "assemble"
and:
def allClasses = app.sources*.classFile.fullPath as String[]
jarFile("build/jars/myLib/jar/myLib.jar").hasDescendants(allClasses)
jarFile("build/jars/myLib/jar/api/myLib.jar").hasDescendants(allClasses)
}
def "api jar should be built for each variant and contain only api classes"() {
when:
addNonApiClasses()
and:
buildFile << """
model {
components {
myLib(JvmLibrarySpec) {
api {
exports 'compile.test'
}
targetPlatform 'java5'
targetPlatform 'java6'
}
}
}
"""
then:
succeeds "assemble"
and:
def allClasses = app.sources*.classFile.fullPath as String[]
def apiClassesOnly = (allClasses -
["non_api/pkg/InternalPerson.class", "compile/test/internal/Util.class"]) as String[];
jarFile("build/jars/myLib/java5Jar/myLib.jar").hasDescendants(allClasses)
jarFile("build/jars/myLib/java5Jar/api/myLib.jar").hasDescendants(apiClassesOnly)
jarFile("build/jars/myLib/java6Jar/myLib.jar").hasDescendants(allClasses)
jarFile("build/jars/myLib/java6Jar/api/myLib.jar").hasDescendants(apiClassesOnly)
}
def "building api jar should trigger compilation of classes"() {
when:
addNonApiClasses()
and:
buildFile << """
model {
components {
myLib(JvmLibrarySpec) {
api {
exports 'compile.test'
}
}
}
}
"""
then:
succeeds "myLibJar"
and:
def allClasses = app.sources*.classFile.fullPath as String[]
def apiClassesOnly = (allClasses -
["non_api/pkg/InternalPerson.class", "compile/test/internal/Util.class"]) as String[];
jarFile("build/jars/myLib/jar/api/myLib.jar").hasDescendants(apiClassesOnly)
jarFile("build/jars/myLib/jar/myLib.jar").hasDescendants(allClasses)
}
def "should support configuring exported packages from rule method"() {
when:
addNonApiClasses()
and:
buildFile << '''
class Rules extends RuleSource {
@Mutate
void specifyMyLibApi(@Path("components.myLib") JvmLibrarySpec myLib) {
myLib.getApi().exports("compile.test")
}
}
apply type: Rules
model {
components {
myLib(JvmLibrarySpec) {
}
}
}
'''
then:
succeeds "myLibJar"
and:
def allClasses = app.sources*.classFile.fullPath as String[]
def apiClassesOnly = (allClasses -
["non_api/pkg/InternalPerson.class", "compile/test/internal/Util.class"]) as String[];
jarFile("build/jars/myLib/jar/api/myLib.jar").hasDescendants(apiClassesOnly)
jarFile("build/jars/myLib/jar/myLib.jar").hasDescendants(allClasses)
}
def "should support chained configuration of library api exports"() {
when:
addNonApiClasses()
and:
buildFile << """
model {
components {
myLib(JvmLibrarySpec) {
api.exports('compile.test')
}
}
}
"""
then:
succeeds "myLibJar"
and:
def allClasses = app.sources*.classFile.fullPath as String[]
def apiClassesOnly = (allClasses -
["non_api/pkg/InternalPerson.class", "compile/test/internal/Util.class"]) as String[];
jarFile("build/jars/myLib/jar/api/myLib.jar").hasDescendants(apiClassesOnly)
jarFile("build/jars/myLib/jar/myLib.jar").hasDescendants(allClasses)
}
def addNonApiClasses() {
app.sources.add(new JvmSourceFile("non_api/pkg", "InternalPerson.java", '''
package non_api.pkg;
public class InternalPerson {
}
'''
))
app.sources.add(new JvmSourceFile("compile/test/internal", "Util.java", '''
package compile.test.internal;
public class Util {
}
'''
))
app.sources*.writeToDir(file("src/myLib/java"))
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy