Skip to content

Commit

Permalink
Additional test coverage for Ivy; introduce variant derivation rule
Browse files Browse the repository at this point in the history
  • Loading branch information
DPUkyle committed Mar 23, 2021
1 parent 95ba811 commit cd1a263
Show file tree
Hide file tree
Showing 11 changed files with 930 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class PegasusPluginIntegrationTest extends Specification {
.withProjectDir(tempDir.root)
.withPluginClasspath()
.withArguments('mainDataTemplateJar')
.forwardOutput()
//.forwardOutput()
.build()

then:
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,258 @@ class PegasusPluginLegacyIvyPublishIntegrationTest extends Specification {
assertZipContains(childProjectDataTemplateArtifact, 'pegasus/com/linkedin/child/Photo.pdl')
}

/**
* Regression test illustrating how to consume software components published using the legacy Ivy format.
*
* <p>By requesting a named <b>capability</b> instead of a specific configuration name, we can consume pegasus
* artifacts in a forward-compatible manner.
*
* Note that, in order to derive information about the capabilities of a software component, we must augment
* the consumer logic with a ComponentMetadataRule.
*
* <p>See <a href="https://docs.gradle.org/6.8.3/userguide/feature_variants.html">Modeling feature variants and optional dependencies</a>
* and <a href="https://docs.gradle.org/6.8.3/userguide/feature_variants.html#sec::consuming_feature_variants">Consuming Feature Variants</a>
* for more information about capabilities.
*/
def 'publishes with legacy ivies but derives capabilities from dataTemplate configurations'() {
given:
def gradlePropertiesFile = grandparentProject.newFile('gradle.properties')
gradlePropertiesFile << '''
|group=com.linkedin.pegasus-grandparent-demo
|version=1.0.0
|'''.stripMargin()

def settingsFile = grandparentProject.newFile('settings.gradle')
settingsFile << "rootProject.name = 'grandparent'"

grandparentProject.newFile('build.gradle') << """
|plugins {
| id 'pegasus'
|}
|
|repositories {
| mavenCentral()
|}
|
|dependencies {
| dataTemplateCompile files(${System.getProperty('integTest.dataTemplateCompileDependencies')})
| pegasusPlugin files(${System.getProperty('integTest.pegasusPluginDependencies')})
|}
|
|//legacy publishing configuration
|tasks.withType(Upload) {
| repositories {
| ivy { url '$localIvyRepo' }
| }
|}""".stripMargin()

// Create a simple pdl schema, borrowed from restli-example-api
def schemaFilename = 'LatLong.pdl'
def grandparentPegasusDir = grandparentProject.newFolder('src', 'main', 'pegasus', 'com', 'linkedin', 'grandparent')
def grandparentPdlFile = new File("$grandparentPegasusDir.path$File.separator$schemaFilename")
grandparentPdlFile << '''namespace com.linkedin.grandparent
|
|record LatLong {
| latitude: optional float
| longitude: optional float
|}'''.stripMargin()

when:
def grandparentRunner = GradleRunner.create()
.withProjectDir(grandparentProject.root)
.withPluginClasspath()
.withArguments('uploadDataTemplate', 'uploadTestDataTemplate', 'uploadAvroSchema', 'uploadTestAvroSchema', 'uploadArchives', '-is')
//.forwardOutput()
//.withDebug(true)

def grandparentResult = grandparentRunner.build()

then:
grandparentResult.task(':compileMainGeneratedDataTemplateJava').outcome == TaskOutcome.SUCCESS
grandparentResult.task(':uploadDataTemplate').outcome == TaskOutcome.SUCCESS
grandparentResult.task(':uploadArchives').outcome == TaskOutcome.SUCCESS

def grandparentProjectIvyDescriptor = new File(localIvyRepo.path, 'com.linkedin.pegasus-grandparent-demo/grandparent/1.0.0/ivy-1.0.0.xml')
grandparentProjectIvyDescriptor.exists()
def grandparentProjectIvyDescriptorContents = grandparentProjectIvyDescriptor.text
def expectedGrandparentContents = new File(Thread.currentThread().contextClassLoader.getResource('ivy/legacyWithVariantDerivation/expectedGrandparentIvyDescriptorContents.txt').toURI()).text
grandparentProjectIvyDescriptorContents.contains expectedGrandparentContents

def grandparentProjectPrimaryArtifact = new File(localIvyRepo.path, 'com.linkedin.pegasus-grandparent-demo/grandparent/1.0.0/grandparent-1.0.0.jar')
grandparentProjectPrimaryArtifact.exists()
def grandparentProjectDataTemplateArtifact = new File(localIvyRepo.path, 'com.linkedin.pegasus-grandparent-demo/grandparent/1.0.0/grandparent-data-template-1.0.0.jar')
grandparentProjectDataTemplateArtifact.exists()

assertZipContains(grandparentProjectDataTemplateArtifact, 'com/linkedin/grandparent/LatLong.class')
assertZipContains(grandparentProjectDataTemplateArtifact, 'pegasus/com/linkedin/grandparent/LatLong.pdl')

when: 'a parent project consumes the grandparent project data-template jar'

gradlePropertiesFile = parentProject.newFile('gradle.properties')
gradlePropertiesFile << '''
|group=com.linkedin.pegasus-parent-demo
|version=1.0.0
|'''.stripMargin()

settingsFile = parentProject.newFile('settings.gradle')
settingsFile << "rootProject.name = 'parent'"

parentProject.newFile('build.gradle') << """
|plugins {
| id 'pegasus'
|}
|
|repositories {
| ivy { url '$localIvyRepo' }
| mavenCentral()
|}
|
|dependencies {
| dataTemplateCompile files(${System.getProperty('integTest.dataTemplateCompileDependencies')})
| pegasusPlugin files(${System.getProperty('integTest.pegasusPluginDependencies')})
|
| dataModel ('com.linkedin.pegasus-grandparent-demo:grandparent:1.0.0') {
| capabilities {
| requireCapability('com.linkedin.pegasus-grandparent-demo:grandparent-data-template:1.0.0')
| }
| }
| components.all(com.linkedin.pegasus.gradle.rules.PegasusIvyVariantDerivationRule)
|}
|
|//legacy publishing configuration
|tasks.withType(Upload) {
| repositories {
| ivy { url '$localIvyRepo' }
| }
|}""".stripMargin()

// Create a simple pdl schema which references a grandparent type
schemaFilename = 'EXIF.pdl'
def parentPegasusDir = parentProject.newFolder('src', 'main', 'pegasus', 'com', 'linkedin', 'parent')
def parentPdlFile = new File("$parentPegasusDir.path$File.separator$schemaFilename")
parentPdlFile << '''namespace com.linkedin.parent
|
|import com.linkedin.grandparent.LatLong
|
|record EXIF {
| isFlash: optional boolean = true
| location: optional LatLong
|}'''.stripMargin()

def parentRunner = GradleRunner.create()
.withProjectDir(parentProject.root)
.withPluginClasspath()
.withArguments('uploadDataTemplate', 'uploadTestDataTemplate', 'uploadAvroSchema', 'uploadTestAvroSchema', 'uploadArchives', '-is')
//.forwardOutput()
//.withDebug(true)

def parentResult = parentRunner.build()

then:
parentResult.task(':compileMainGeneratedDataTemplateJava').outcome == TaskOutcome.SUCCESS
parentResult.task(':uploadDataTemplate').outcome == TaskOutcome.SUCCESS
parentResult.task(':uploadArchives').outcome == TaskOutcome.SUCCESS

def parentProjectIvyDescriptor = new File(localIvyRepo.path, 'com.linkedin.pegasus-parent-demo/parent/1.0.0/ivy-1.0.0.xml')
parentProjectIvyDescriptor.exists()
def parentProjectIvyDescriptorContents = parentProjectIvyDescriptor.text
def expectedParentContents = new File(Thread.currentThread().contextClassLoader.getResource('ivy/legacyWithVariantDerivation/expectedParentIvyDescriptorContents.txt').toURI()).text
parentProjectIvyDescriptorContents.contains expectedParentContents

def parentProjectPrimaryArtifact = new File(localIvyRepo.path, 'com.linkedin.pegasus-parent-demo/parent/1.0.0/parent-1.0.0.jar')
parentProjectPrimaryArtifact.exists()
def parentProjectDataTemplateArtifact = new File(localIvyRepo.path, 'com.linkedin.pegasus-parent-demo/parent/1.0.0/parent-data-template-1.0.0.jar')
parentProjectDataTemplateArtifact.exists()

assertZipContains(parentProjectDataTemplateArtifact, 'com/linkedin/parent/EXIF.class')
assertZipContains(parentProjectDataTemplateArtifact, 'pegasus/com/linkedin/parent/EXIF.pdl')

when: 'a child project transitively consumes the grandparent project data-template jar'

gradlePropertiesFile = childProject.newFile('gradle.properties')
gradlePropertiesFile << '''
|group=com.linkedin.pegasus-child-demo
|version=1.0.0
|'''.stripMargin()

settingsFile = childProject.newFile('settings.gradle')
settingsFile << "rootProject.name = 'child'"

childProject.newFile('build.gradle') << """
|plugins {
| id 'pegasus'
|}
|
|repositories {
| ivy { url '$localIvyRepo' }
| mavenCentral()
|}
|
|dependencies {
| dataTemplateCompile files(${System.getProperty('integTest.dataTemplateCompileDependencies')})
| pegasusPlugin files(${System.getProperty('integTest.pegasusPluginDependencies')})
|
| dataModel ('com.linkedin.pegasus-parent-demo:parent:1.0.0') {
| capabilities {
| requireCapability('com.linkedin.pegasus-parent-demo:parent-data-template:1.0.0')
| }
| }
| components.all(com.linkedin.pegasus.gradle.rules.PegasusIvyVariantDerivationRule)
|}
|
|//legacy publishing configuration
|tasks.withType(Upload) {
| repositories {
| ivy { url '$localIvyRepo' }
| }
|}""".stripMargin()

// Create a simple pdl schema which references parent and grandparent types
schemaFilename = 'Photo.pdl'
def childPegasusDir = childProject.newFolder('src', 'main', 'pegasus', 'com', 'linkedin', 'child')
def childPdlFile = new File("$childPegasusDir.path$File.separator$schemaFilename")
childPdlFile << '''namespace com.linkedin.child
|
|import com.linkedin.grandparent.LatLong
|import com.linkedin.parent.EXIF
|
|record Photo {
| id: long
| urn: string
| title: string
| exif: EXIF
| backupLocation: optional LatLong
|}'''.stripMargin()

def childRunner = GradleRunner.create()
.withProjectDir(childProject.root)
.withPluginClasspath()
.withArguments('uploadDataTemplate', 'uploadTestDataTemplate', 'uploadAvroSchema', 'uploadTestAvroSchema', 'uploadArchives', '-is')
//.forwardOutput()
//.withDebug(true)

def childResult = childRunner.build()

then:
childResult.task(':compileMainGeneratedDataTemplateJava').outcome == TaskOutcome.SUCCESS
childResult.task(':uploadDataTemplate').outcome == TaskOutcome.SUCCESS
childResult.task(':uploadArchives').outcome == TaskOutcome.SUCCESS

def childProjectIvyDescriptor = new File(localIvyRepo.path, 'com.linkedin.pegasus-child-demo/child/1.0.0/ivy-1.0.0.xml')
childProjectIvyDescriptor.exists()
def childProjectIvyDescriptorContents = childProjectIvyDescriptor.text
def expectedChildContents = new File(Thread.currentThread().contextClassLoader.getResource('ivy/legacyWithVariantDerivation/expectedChildIvyDescriptorContents.txt').toURI()).text
childProjectIvyDescriptorContents.contains expectedChildContents

def childProjectPrimaryArtifact = new File(localIvyRepo.path, 'com.linkedin.pegasus-child-demo/child/1.0.0/child-1.0.0.jar')
childProjectPrimaryArtifact.exists()
def childProjectDataTemplateArtifact = new File(localIvyRepo.path, 'com.linkedin.pegasus-child-demo/child/1.0.0/child-data-template-1.0.0.jar')
childProjectDataTemplateArtifact.exists()

assertZipContains(childProjectDataTemplateArtifact, 'com/linkedin/child/Photo.class')
assertZipContains(childProjectDataTemplateArtifact, 'pegasus/com/linkedin/child/Photo.pdl')
}

private static boolean assertZipContains(File zip, String path) {
return new ZipFile(zip).getEntry(path)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<configurations>
<conf name="annotationProcessor" visibility="private"/>
<conf name="apiElements" visibility="private" extends="runtime"/>
<conf name="archives" visibility="public"/>
<conf name="avroSchema" visibility="public"/>
<conf name="avroSchemaGenerator" visibility="private"/>
<conf name="compile" visibility="private" extends="dataModel,dataTemplateCompile"/>
<conf name="compileClasspath" visibility="private" extends="compileOnly,implementation"/>
<conf name="compileOnly" visibility="private"/>
<conf name="dataModel" visibility="public"/>
<conf name="dataTemplate" visibility="public" extends="dataModel,dataTemplateCompile"/>
<conf name="dataTemplateCompile" visibility="private"/>
<conf name="dataTemplateGenerator" visibility="private"/>
<conf name="default" visibility="public" extends="runtimeElements"/>
<conf name="generatedJavadoc" visibility="public"/>
<conf name="generatedSources" visibility="public"/>
<conf name="implementation" visibility="private" extends="compile"/>
<conf name="mainGeneratedDataTemplateAnnotationProcessor" visibility="private"/>
<conf name="mainGeneratedDataTemplateCompile" visibility="private"/>
<conf name="mainGeneratedDataTemplateCompileClasspath" visibility="private" extends="mainGeneratedDataTemplateCompileOnly,mainGeneratedDataTemplateImplementation"/>
<conf name="mainGeneratedDataTemplateCompileOnly" visibility="private"/>
<conf name="mainGeneratedDataTemplateImplementation" visibility="private" extends="mainGeneratedDataTemplateCompile"/>
<conf name="mainGeneratedDataTemplateRuntime" visibility="private" extends="mainGeneratedDataTemplateCompile"/>
<conf name="mainGeneratedDataTemplateRuntimeClasspath" visibility="private" extends="mainGeneratedDataTemplateImplementation,mainGeneratedDataTemplateRuntime,mainGeneratedDataTemplateRuntimeOnly"/>
<conf name="mainGeneratedDataTemplateRuntimeOnly" visibility="private"/>
<conf name="pegasusPlugin" visibility="public"/>
<conf name="restClient" visibility="public" extends="dataTemplate,restClientCompile"/>
<conf name="restClientCompile" visibility="private"/>
<conf name="restModel" visibility="public"/>
<conf name="restTools" visibility="private"/>
<conf name="runtime" visibility="private" extends="compile"/>
<conf name="runtimeClasspath" visibility="private" extends="implementation,runtime,runtimeOnly"/>
<conf name="runtimeElements" visibility="private" extends="implementation,runtime,runtimeOnly"/>
<conf name="runtimeOnly" visibility="private"/>
<conf name="schemaAnnotationHandler" visibility="public"/>
<conf name="testAnnotationProcessor" visibility="private"/>
<conf name="testAvroSchema" visibility="public" extends="avroSchema"/>
<conf name="testCompile" visibility="private" extends="compile,dataTemplateCompile,testDataModel"/>
<conf name="testCompileClasspath" visibility="private" extends="testCompileOnly,testImplementation"/>
<conf name="testCompileOnly" visibility="private"/>
<conf name="testDataModel" visibility="public" extends="dataModel"/>
<conf name="testDataTemplate" visibility="public" extends="dataTemplate,testDataModel"/>
<conf name="testGeneratedDataTemplateAnnotationProcessor" visibility="private"/>
<conf name="testGeneratedDataTemplateCompile" visibility="private"/>
<conf name="testGeneratedDataTemplateCompileClasspath" visibility="private" extends="testGeneratedDataTemplateCompileOnly,testGeneratedDataTemplateImplementation"/>
<conf name="testGeneratedDataTemplateCompileOnly" visibility="private"/>
<conf name="testGeneratedDataTemplateImplementation" visibility="private" extends="testGeneratedDataTemplateCompile"/>
<conf name="testGeneratedDataTemplateRuntime" visibility="private" extends="testGeneratedDataTemplateCompile"/>
<conf name="testGeneratedDataTemplateRuntimeClasspath" visibility="private" extends="testGeneratedDataTemplateImplementation,testGeneratedDataTemplateRuntime,testGeneratedDataTemplateRuntimeOnly"/>
<conf name="testGeneratedDataTemplateRuntimeOnly" visibility="private"/>
<conf name="testGeneratedJavadoc" visibility="public" extends="generatedJavadoc"/>
<conf name="testGeneratedSources" visibility="public" extends="generatedSources"/>
<conf name="testImplementation" visibility="private" extends="implementation,testCompile"/>
<conf name="testRestClient" visibility="public" extends="restClient,testDataTemplate"/>
<conf name="testRestModel" visibility="public" extends="restModel"/>
<conf name="testRuntime" visibility="private" extends="runtime,testCompile"/>
<conf name="testRuntimeClasspath" visibility="private" extends="testImplementation,testRuntime,testRuntimeOnly"/>
<conf name="testRuntimeOnly" visibility="private" extends="runtimeOnly"/>
</configurations>
<publications>
<artifact name="child" type="jar" ext="jar" conf="apiElements,archives,runtime,runtimeElements"/>
<artifact name="child-avro-schema" type="jar" ext="jar" conf="avroSchema"/>
<artifact name="child-data-template" type="jar" ext="jar" conf="dataTemplate"/>
<artifact name="child" type="jar" ext="jar" conf="generatedJavadoc" m:classifier="javadoc"/>
<artifact name="child" type="jar" ext="jar" conf="generatedSources" m:classifier="sources"/>
<artifact name="child-test-avro-schema" type="jar" ext="jar" conf="testAvroSchema"/>
<artifact name="child-test-data-template" type="jar" ext="jar" conf="testDataTemplate"/>
</publications>
<dependencies>
<dependency org="com.linkedin.pegasus-parent-demo" name="parent" rev="1.0.0" conf="dataModel-&gt;default"/>
<dependency org="com.google.code.findbugs" name="jsr305" rev="3.0.2" conf="dataTemplateCompile-&gt;default"/>
<dependency org="org.slf4j" name="slf4j-simple" rev="1.7.2" conf="pegasusPlugin-&gt;default"/>
</dependencies>
Loading

0 comments on commit cd1a263

Please sign in to comment.