How to share common shared folder with multiple test source sets in gradle - gradle

My test source structure is as follows:
test
testFunctional
testIntegration
testSupport
The testSupport folder contains common code to be shared among all test folders.
The definition of the source sets in gradle looks like below:
test {
java {
srcDirs = ['src/test/java', 'src/testSupport/java']
}
resources {
srcDirs = ['src/test/resources']
}
}
functionalTest {
java {
srcDirs = ['src/testFunctional/java', 'src/testSupport/java']
}
resources {
srcDirs = ['src/testFunctional/resources']
}
}
integrationTest {
java {
srcDirs = ['src/testIntegration/java', 'src/testSupport/java']
}
resources {
srcDirs = ['src/testIntegration/resources']
}
}
}
IntelliJ currently complains that "Duplicate content roots detected" referring to the fact that the same 'src/testSupport/java' folder is shared among multiple source sets.
Do you have a more elegant solution to achieve sharing code among test folder in gradle without going to a multi-module approach?

Related

How to force Gradle to pick up tests in `main`?

We have some of our tests in main (so they can be built into and run from a service). The latest Gradle defaults to not recognizing source in main as test code.
I thought I could use the Nebula Facets plugin but the following doesn't work around the problem, either:
facets {
functionalTest {
parentSourceSet = 'main'
includeInCheckLifecycle = false
}
}
If you really need to live with tests in src/main/java, then you do not need an extra source set.
Instead you need to configure a test task so that it uses the main source set classpath.
Here is an example to configure such a test task, using the Kotlin DSL and task configuration avoidance:
val mainTest = tasks.register<Test>("mainTest") {
useJUnitPlatform()
description = "Run tests from main"
group = "verification"
testClassesDirs = sourceSets["main"].output.classesDirs
classpath = sourceSets["main"].runtimeClasspath
}
tasks.named("check").configure {
dependsOn(mainTest)
}
The following is how to override srcDirs:
facets {
functionalTest {
includeInCheckLifecycle = false
}
}
sourceSets {
functionalTest {
java {
srcDirs = ["${projectDir}/src/main/java"]
}
resources {
srcDirs = ["${projectDir}/src/main/resources"]
}
}
}

How to include all src/test/resources/** AND src/main/java/**/*.html in the test sourceset in gradle?

I have the following and thought it was 'adding' to my sourceSet but actually just modified it..
sourceSets {
test {
resources {
srcDirs = ["src/main/java"]
includes = ["**/*.html"]
}
}
}
What I really want is both src/test/resources/** and the above as well. I don't want to exclude any files from src/test/resources though and the above is only including html from any directories I put there.
thanks,
Dean
The following will illustrate the technique using main (so it can be verified):
apply plugin: 'java'
sourceSets {
myExtra {
resources {
srcDirs "src/main/java"
includes = ["**/*.html"]
}
}
main {
resources {
source myExtra.resources
}
}
}
Proof of concept via the command-line:
bash$ ls src/main/java
abc.html
xyz.txt
bash$ ls src/main/resources/
def.html
ijk.txt
bash$ gradle clean jar
bash$ jar tf build/libs/myexample.jar
META-INF/
META-INF/MANIFEST.MF
abc.html
def.html
ijk.txt
In your case, change main to test. This answer was discovered via the Gradle doc for SourceDirectorySet. Interestingly, for 3.0, it contains a TODO:
TODO - configure includes/excludes for individual source dirs
which implies that this work-around (via this method) is probably necessary.
I got your point. I tried this and it worked . Please take a look into it:
sourceSets {
test {
resources {
srcDirs = ["src/main/java"]
includes = ["**/*.html"]
}
}
}
sourceSets.test.resources.srcDir 'src/test/resources'
Add these in build.gradle.
I was thinking whether or not to post this answer. So that if you are not satisfied with the previous answer, try the following hacky way (probably it will work with eclipse command):
apply plugin: 'java'
ConfigurableFileTree.metaClass.getAsSource = {
def fileTrees = delegate.asFileTrees
fileTrees.metaClass.getSrcDirTrees = {
return delegate as Set
}
fileTrees as SourceDirectorySet
}
sourceSets {
main {
resources {
srcDirs = [] // cleanup first
source fileTree('src/main/java').include('**/*.html').asSource
source fileTree('src/main/resources').asSource
}
}
}
Using srcDir may be what you want. Here's an example:
sourceSets {
main {
resources {
srcDir "src/main/resources"
exclude "file-to-be-excluded"
include "file-to-be-included"
srcDir "src/main/java"
include "**/*.html"
srcDir "image-folder-in-root"
include "**/*.png"
include "**/*.jpg"
exclude "**/*.xcf"
}
}
}
Not exactly what you asked for, but it could be helpful for someone who finds this question:
I only wanted to have the test resources next to the sources. So I only need to exclude the sources.
In your case, perhaps you could exclude those which you would mind getting in the JAR and/or classpath.
None of the other answers worked for me, and this did work:
sourceSets {
test {
resources {
srcDirs += "src/test/kotlin"
excludes = ["**/*.kt"]
}
}
}
Gradle 6.3.

How to specify output.classesDir for custom sourceSet in Gradle?

My build uses source code from two projects: ProjectA and ProjectB, and produces JAR with classes and resources from ProjectB. I defined custom sourceSet mainProjectB which is supposed to have output in a separate directory:
sourceSets {
mainProjectB {
output.classesDir = "$buildDir/build/classes/projectB"
output.resourcesDir = "$buildDir/build/resources/projectB"
java { srcDirs = ['src/main/java']}
resources { srcDirs = ['src/main/resources']}
}
mainProjectA {
java { srcDirs = [
'../projectA/src/main/java'
]}
resources { srcDirs = [
'../projectA/src/main/resources'
]}
}
test {
java {
srcDirs = [
'../projectA/src/test/java',
'src/test/java'
]}
resources {
srcDirs = [
'../projectA/src/test/resources',
'src/test/resources'
]}
}
}
compileJava {
source sourceSets.mainProjectB.allJava
source sourceSets.mainProjectA.allJava
}
processResources {
from sourceSets.mainProjectB.resources
from sourceSets.mainProjectA.resources
}
jar {
from sourceSets.mainProjectB.output.classesDir
from sourceSets.mainProjectB.output.resourcesDir
}
Problem: custom sourceSet mainProjectB ingores specified output directories.
The directories "$buildDir/build/classes/projectB" and "$buildDir/build/resources/projectB" are not created, and as a consequence, JAR includes files from both projects (instead of ProjectB).
UPDATE:
Projects A and B have circular dependencies. That is why they have to share source code.
I would consider to use subprojects and project to achieve your goal - gradel docs . With the following approach you can get any kind of jar file depending on your build :
group 'CoreProject'
version '1.0-SNAPSHOT'
subprojects {
apply plugin: 'java'
repositories {
mavenCentral()
}
}
project (':projectA') {
}
project (':projectB') {
def generatedResources = "$buildDir"
//in case you want resources and classes to be written to custom location where
//redefined paths are relative to projectB root folder
sourceSets {
main {
output.classesDir = 'build/classes/projectB'
output.resourcesDir = 'build/resources/projectB'
}
}
dependencies {
compile project(':projectA')
}
jar {
manifest.mainAttributes(
'Main-Class': "ProjectBClass"
)
}
//To create fat Jar that will contain classes and resources from all dependencies
task fatJar(type: Jar) {
manifest.from jar.manifest
classifier = 'all'
from {
configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) }
} {
exclude "ProjectAResource" //if want to exclude resources from projectA
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
}
with jar
}
}
If you run jar task of projectB following jars will be created, each including only its own classes and resources : projectA/build/libs/projectA.jar , projectB/build/libs/projectB.jar('JAR with classes and resources from ProjectB' as you asked in your comment)
If you run farJar task of projectB the following jar file will be created that includes classes and resources from both projects and you can exclude any files patterns from projectA and projectB projects to create any final jar you like : projectB/build/libs/projectB-all.jar
Here is a screenshot of projects folders structure I created to mimic your scenario(as I understood it):
P.S. Also make sure none of the folders projectB/build and projectA/build are locked by any process and remove those handles if any, as otherwise Gradle will fail to run.

How do I replace the default source folders for gradle?

I am new to Gradle and I have a source code location different than what Gradle expects.
Gradle expects to find the production source code under src/main/java and your test source code under src/main/resources. How do I configure Gradle to a different source code?
You have to add few lines to build.gradle:
To replace the default source folders, you will want to use srcDirs instead, which takes an array of the path.
sourceSets {
main.java.srcDirs = ['src/java']
main.resources.srcDirs = ['src/resources']
}
Another way of doing it is:
sourceSets {
main {
java {
srcDir 'src/java'
}
resources {
srcDir 'src/resources'
}
}
}
The same thing is applicable to test folder too.

How to keep Java code and Junit tests together building with Gradle

I have a project in which the main source and the test cases for that source are kept in the same package/directory. Each test class is the name of the class which it is testing with "Test" appended on the end. So if I have a Foo.java there will be a FooTest.java right next to it.
My question is, how do I build this project with Gradle? I'd still like to keep the class files separate, i.e. a folder for main classes and a folder for test classes.
This should do the trick:
sourceSets {
main {
java {
srcDirs = ["some/path"]
exclude "**/*Test.java"
}
}
test {
java {
srcDirs = ["some/path"]
include "**/*Test.java"
}
}
}
For reference, here is the code I used to try to get around the Eclipse plugin's classpath issue. Using this in combination with Peter's answer above seems to work.
// The following ensures that Eclipse uses only one src directory
eclipse {
classpath {
file {
//closure executed after .classpath content is loaded from existing file
//and after gradle build information is merged
whenMerged { classpath ->
classpath.entries.removeAll { entry -> entry.kind == 'src'}
def srcEntry = new org.gradle.plugins.ide.eclipse.model.SourceFolder('src', null)
srcEntry.dir = file("$projectDir/src")
classpath.entries.add( srcEntry )
}
}
}
}
this work for me:
eclipse {
classpath {
file {
withXml {
process(it.asNode())
}
}
}
}
def process(node) {
if (node.attribute('path') == 'src/test/java' || node.attribute('path') == 'src/test/resources')
node.attributes().put('output', "build/test-classes")
else
node.children().each {
process(it)
}}

Resources