Skip to content

Commit

Permalink
add test for Java modules compatibility of JUnit 5 support
Browse files Browse the repository at this point in the history
Unfortunately it is not trivial to test that the final artifacts work in a Java modules project. To keep the effort low we just add a test that catches the most probable error: The dependencies change and suddenly two artifacts comprising the JUnit 5 support export the same package. To simplify this even further we can just assume that each JUnit 5 artifact exports exactly one package and by asserting that this is exactly the expected package we effectively prevent collisions. If we use automatic module names we have to assume that artifacts will export all of their packages on the module path.

Signed-off-by: Peter Gafert <peter.gafert@tngtech.com>
  • Loading branch information
codecholeric committed Apr 22, 2022
1 parent ba452bf commit ff3bc43
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 68 deletions.
6 changes: 5 additions & 1 deletion archunit-junit/junit5/api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,8 @@ def configureDependencies = { deps ->
dep.scope.text() != 'compile' || !(dep.artifactId.text() in ['archunit'])
}
}
this.with project(':archunit-junit').configureJUnitArchive(configureDependencies)
this.with project(':archunit-junit').configureJUnitArchive(configureDependencies)

singlePackageExport {
exportedPackage = 'com.tngtech.archunit.junit'
}
4 changes: 4 additions & 0 deletions archunit-junit/junit5/engine-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,7 @@ def configureDependencies = { deps ->
}
}
this.with project(':archunit-junit').configureJUnitArchive(configureDependencies)

singlePackageExport {
exportedPackage = 'com.tngtech.archunit.junit.engine_api'
}
4 changes: 4 additions & 0 deletions archunit-junit/junit5/engine/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,7 @@ def configureDependencies = { deps ->
}
}
this.with project(':archunit-junit').configureJUnitArchive(configureDependencies)

singlePackageExport {
exportedPackage = 'com.tngtech.archunit.junit.internal'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import java.time.LocalDateTime
import java.util.jar.JarFile
import java.util.jar.Manifest

def checkThirdParty = { JarFile jarFile ->
assert jarFile.getEntry('com/tngtech/archunit/thirdparty/org/objectweb/asm/ClassVisitor.class') != null: 'ASM is missing from 3rd party'
assert jarFile.getEntry('com/tngtech/archunit/thirdparty/org/objectweb/asm/asm.license') != null: 'ASM license is missing from 3rd party'
assert jarFile.getEntry('com/tngtech/archunit/thirdparty/com/google/common/collect/ImmutableSet.class') != null: 'Guava is missing from 3rd party'
}

def checkNoThirdParty = { JarFile jarFile ->
assert jarFile.getEntry('com/tngtech/archunit/thirdparty') == null: 'There exists a third party folder'
}

def checkNoIllegalJarEntries = { JarFile jarFile ->
def illegalEntries = Collections.list(jarFile.entries()).findAll {
!it.name.startsWith('com/tngtech/archunit/') &&
it.name != 'com/' &&
it.name != 'com/tngtech/' &&
!it.name.startsWith('META-INF/')
}
assert illegalEntries.empty: """
|There are invalid entries contained inside of release artifact ${new File(jarFile.name).name}:
|-> ${illegalEntries.join("${System.lineSeparator()}-> ")}""".stripMargin().trim()
}

def checkManifest = { Manifest manifest ->
println "Verifying correct Manifest of ${project.name}"

def checkAttributes = { expected ->
expected.each { key, value ->
assert manifest.mainAttributes.getValue(key) == value
}
}
checkAttributes([
'Specification-Title' : "ArchUnit - Module '${project.name}'",
'Specification-Version' : version,
'Specification-Vendor' : 'TNG Technology Consulting GmbH',
'Implementation-Title' : "com.tngtech.${project.name.replace('-', '.')}",
'Implementation-Version': version,
'Implementation-Vendor' : 'TNG Technology Consulting GmbH',
'Issue-Tracker' : 'https://github.com/TNG/ArchUnit/issues',
'Documentation-URL' : 'https://github.com/TNG/ArchUnit',
'Copyright' : "${LocalDateTime.now().year} TNG Technology Consulting GmbH",
'License' : 'The Apache Software License, Version 2.0',
'Automatic-Module-Name' : "com.tngtech.${project.name.replaceFirst('-', '.').replaceFirst('-', '.').replace('-', '')}"
])
}

ext.checkArtifactContent = { JarFile jarFile ->
checkManifest(jarFile.manifest)
if (project.repackaging.repackagesAsm) {
println "Artifact ${project.name} is configured to repackage 3rd party libs -> checking existence of 3rd party package..."
checkThirdParty(jarFile)
} else {
println "Artifact ${project.name} is configured to not repackage 3rd party libs -> checking absense of 3rd party package..."
checkNoThirdParty(jarFile)
}
checkNoIllegalJarEntries(jarFile)
}

task checkArtifact {
dependsOn(build)

doLast {
def jarFile = new JarFile(jar.archiveFile.get().getAsFile())
checkArtifactContent(jarFile)
}
}
build.finalizedBy(checkArtifact)

abstract class SinglePackageExportExtension {
String exportedPackage
}

ext.singlePackageExport = extensions.create('singlePackageExport', SinglePackageExportExtension)

task checkExportedPackage {
dependsOn(build)
doLast {
def exportedPackage = project.singlePackageExport?.exportedPackage
if (exportedPackage) {
def jarFile = new JarFile(jar.archiveFile.get().getAsFile())
def packageEntries = Collections.list(jarFile.entries())
.findAll { it.name.endsWith('.class') }
.collect { it.name.replaceAll('/[^/]*$', '') }
.collect { it.replace('/', '.') }
.unique()
assert packageEntries == [exportedPackage]: "Project is configured to export only single package $exportedPackage"
}
}
}
build.finalizedBy(checkExportedPackage)
Original file line number Diff line number Diff line change
@@ -1,75 +1,10 @@
import java.nio.file.Files
import java.time.LocalDateTime
import java.util.jar.JarFile
import java.util.jar.Manifest

def checkThirdParty = { JarFile jarFile ->
assert jarFile.getEntry('com/tngtech/archunit/thirdparty/org/objectweb/asm/ClassVisitor.class') != null: 'ASM is missing from 3rd party'
assert jarFile.getEntry('com/tngtech/archunit/thirdparty/org/objectweb/asm/asm.license') != null: 'ASM license is missing from 3rd party'
assert jarFile.getEntry('com/tngtech/archunit/thirdparty/com/google/common/collect/ImmutableSet.class') != null: 'Guava is missing from 3rd party'
plugins {
id 'archunit.java-artifact-check-conventions'
}

def checkNoThirdParty = { JarFile jarFile ->
assert jarFile.getEntry('com/tngtech/archunit/thirdparty') == null: 'There exists a third party folder'
}

def checkNoIllegalJarEntries = { JarFile jarFile ->
def illegalEntries = Collections.list(jarFile.entries()).findAll {
!it.name.startsWith('com/tngtech/archunit/') &&
it.name != 'com/' &&
it.name != 'com/tngtech/' &&
!it.name.startsWith('META-INF/')
}
assert illegalEntries.empty: """
|There are invalid entries contained inside of release artifact ${new File(jarFile.name).name}:
|-> ${illegalEntries.join("${System.lineSeparator()}-> ")}""".stripMargin().trim()
}

def checkManifest = { Manifest manifest ->
println "Verifying correct Manifest of ${project.name}"

def checkAttributes = { expected ->
expected.each { key, value ->
assert manifest.mainAttributes.getValue(key) == value
}
}
checkAttributes([
'Specification-Title' : "ArchUnit - Module '${project.name}'",
'Specification-Version' : version,
'Specification-Vendor' : 'TNG Technology Consulting GmbH',
'Implementation-Title' : "com.tngtech.${project.name.replace('-', '.')}",
'Implementation-Version': version,
'Implementation-Vendor' : 'TNG Technology Consulting GmbH',
'Issue-Tracker' : 'https://github.com/TNG/ArchUnit/issues',
'Documentation-URL' : 'https://github.com/TNG/ArchUnit',
'Copyright' : "${LocalDateTime.now().year} TNG Technology Consulting GmbH",
'License' : 'The Apache Software License, Version 2.0',
'Automatic-Module-Name' : "com.tngtech.${project.name.replaceFirst('-', '.').replaceFirst('-', '.').replace('-', '')}"
])
}

def checkArtifactContent = { JarFile jarFile ->
checkManifest(jarFile.manifest)
if (project.repackaging.repackagesAsm) {
println "Artifact ${project.name} is configured to repackage 3rd party libs -> checking existence of 3rd party package..."
checkThirdParty(jarFile)
} else {
println "Artifact ${project.name} is configured to not repackage 3rd party libs -> checking absense of 3rd party package..."
checkNoThirdParty(jarFile)
}
checkNoIllegalJarEntries(jarFile)
}

task checkArtifact {
dependsOn(build)

doLast {
def jarFile = new JarFile(jar.archiveFile.get().getAsFile())
checkArtifactContent(jarFile)
}
}
build.finalizedBy(checkArtifact)

task checkUploadedArtifacts {
doLast {
def tngRepoId = project.findProperty('tngRepoId') ?: rootProject.closeSonatypeStagingRepository.stagingRepositoryId.get()
Expand Down

0 comments on commit ff3bc43

Please sign in to comment.