Android Studio with Java Library Module Unable to load test resources - gradle

I have an Android Studio Project with a Java library inner module that has tests with test resources. Yet when i run the tests I am not able to retrieve the artifacts. This seems to work fine in a pure java gradle project (in eclipse at least).
meaning For java plugin:
src/main/java
src/main/test
src/test/java
src/test/resources
Under the resources directory i have a crt file that i want to load in my junit test. When using any command i have come across it returns null for the resource. Yet I have confirmed the resources are in the build folder.
Some things I Tried:
getClass().getClassLoader().getResourceAsStream("cert_format_der.crt").read(); // NPE
getClass().getClassLoader().getResourceAsStream("/cert_format_der.crt").read(); // NPE
getClass().getClassLoader().getSystemResourceAsStream("/cert_format_der.crt").read(); // NPE
Thanks

Turns out this seems to be a bug with Intellij and how Gradle not setting the resource directory for the test sourcesets correctly.
Adding This to the build.gradle for the module Fixes it:
sourceSets {
test {
output.resourcesDir = output.classesDir
}
}

Instead of diverging the resources to the class output directory − as suggested by nibbuen − it is also possible not to touch the output directory and explicitly add it as a dependency.
dependencies {
runtime files(sourceSets.test.output.resourcesDir)
}

Related

Duplicate generated class gradle output (build/...) vs intellij output (out/...)

I'm using intellij (2019.1.1) for a java gradle (5.4.1) project and use lombok (1.18.6) for autogenerating code. Intellij puts generated sources under out/production/classes/generated/... and gradle puts them under build/generated/sources/...
This is fine, and I like that intellij keeps it's own build artifacts separate from gradles, however, intellij seems to look in both directories when running projects and it complains about the duplicate generated classes.
What is the best practice for using intellij with gradle and autogenerated sources? Do you:
tell intellij to output to the same directory as gradle (this
could lead to odd behaviour if a process outside of intellij updates
a file under build/)
tell intellij to perform all tasks with
gradle (i hear this is slower than intellij's make)
tell intellij
to simply ignore the 'build' directory (how do you even do this? and
why does intellij even care about 'build/' when it knows it outputs
to 'out/')
UPDATE: to clarify the situation, the issue is NOT with lombok autogenerated code, it is with hibernate-jpamodelgen. The problem remains the same (duplicate generated sources) but I want to clarify the it is the sources generated by jpamodelgen and not lombok.
UPDATE 2: I have tried the following configuration in an attempt to tell intellij where the generated sources live and also to tell intellij to ignore the build directory. Sadly, this did not work (still get duplicate class error on the generated source files).
apply plugin: 'idea'
idea {
module {
sourceDirs += file('out/production/classes/generated')
generatedSourceDirs += file('out/production/classes/generated')
excludeDirs += file('build')
}
}
UPDATE 3:
Tried the advice from M.Riccuiti and deleted build/, out/, .idea/, .gradle/ and reimported the gradle project but intellij is still seeing the generated sources in the build/ directory.
Here is an approach that finally worked for me. The trick is to notice that when gradle generates the classes, it puts them in:
build\generated\sources\annotationProcessor\java\main\com...
but intellij has the production sources directory set to "generated" in this case, the sources go to:
build\generated\sources\annotationProcessor\java\main\generated\com...
if you compile with gradle first and then use idea, you get both of them, which causes the problem!
To solve this, replace "generated" and "generated_test" in the intellij annotation processors "production sources directory " and "test sources directory " configuration with just a "/" this makes both gradle and intellij generate the sources in the SAME directory, overwriting each other as needed. Also make sure that the "store generated sources relative to" is set to "module content root" and REBUILD the application to clear out any other sources.
The solution I proposed in previous comment was working fine in IDEA 2018.3.x but after upgrading to IDEA 2019.1 I again got this duplicate class exception...
Below is a working solution to make this work with Gradle 5.x (tested with 5.4) and IDEA 2019.1 , to implement your solution #3 which I think is the best option (do not mix gradle & idea generated output directories, and do not delegate IDEA action do Gradle )
The key point is to use excludeDirs property from idea.module extension to make IDEA ignore generated sources managed by Gradle under build/generated/sources/...
ext {
// path to Gradle generated main sources directory
gradleGeneratedMainSourcesDir = "$buildDir/generated/sources/annotationProcessor/java/main/"
// path to Gradle generated test sources directory
gradleGeneratedTestSourcesDir = "$buildDir/generated/sources/annotationProcessor/java/test/"
// path to IDEA generated sources directory
ideaGeneratedSourcesDir = "$projectDir/out/production/classes/generated"
}
idea {
module {
// exclude main & test sources generated by Gradle from project source directories
excludeDirs += file(gradleGeneratedMainSourcesDir)
excludeDirs += file(gradleGeneratedTestSourcesDir)
// include generated sources directory managed by IDEA
sourceDirs += file(ideaGeneratedSourcesDir)
generatedSourceDirs += file(ideaGeneratedSourcesDir)
}
}
See complete sample project based on this configuration here : https://github.com/mricciuti/sample-springboot-gradle-idea
You can enter to IntelliJ Settings (Preferences):
Preferences | Build, Execution, Deployment | Build Tools | Gradle | Runner
Then you tick the checkbox Delegate IDE build/run action to Gradle
Finally, you clean and build again. The issues will be resolved.

Gradle Idea Plugin Respect Gradle Configuration

The simple question;
Is there a way to make Intellij respect gradle configuration?
The details;
I created a new sourceSets called integrationTest
sourceSets {
integrationTest{
java.srcDir 'src/integration/java'
resources.srcDir 'src/integration/resources'
}
}
I added the new source set to Intellij testSourceDirs
idea {
module {
testSourceDirs += file('src/integration/java')
}
}
Everything works fine from Gradle perspective locally and on CI.
Now I don't want Intellij to create out directory which duplicates build dir I want Intellij to respect and follow gradle configuration as follows:
Source directory main/java should be compiled to classes/java/main
Source directory test/java should be compiled to classes/java/test
Source directory integrationTest/java should be compiled to classes/java/integrationTest
Resource directory main/resources should be compiled to resources/main
Resource directory test/resources should be compiled to resources/test
Resource directory integrationTest/resources should be compiled to `resources/integrationTest
The only solution I found so for (here) is to have
All source directories (in my case I have only one at main/java) and resource directories goes to classes/java/main.
All test directories (in my case I have two at test/java and integrationTest/java) and resource direcotries (in my case I have two at test/resources and integrationTest/resources) goes to classes/java/test; and this is so wrong I need now to pay attention to file names and location not to override each other.
Gradle Version: 4.5
Intellij Version: 2017.3
Update Jan 2021 This is not relevant anymore. It is working fine with newer Intellij. I personally tested with latest version 2020.3.2 Community edition.

Building a project with Groovy extension methods in it

I'm trying to build a simple Groovy project which contains a package with a Groovy extension method.
When I try to compile the project the classes using the extension method can't find it. The descriptor and the class are picked up correctly by the IDE.
I guess the problem is that the extension method is needed at compile time by which it probably hasn't been compiled yet.
I've tried creating different sourceSets but with no success.
dependencies {
compile 'org.codehaus.groovy:groovy:2.4.7'
compile 'org.membrane-soa:service-proxy-core:4.2.2'
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
extensionCompile 'org.codehaus.groovy:groovy:2.4.7'
}
sourceSets {
extension {
groovy {
include '**/ClosureExtension.groovy'
}
}
main {
groovy {
compileClasspath += extension.output
}
}
}
The extension method lives in src/main/groovy/com/predic8/membrane/dsl/extension and the rest of the project in src/main/groovy/com/predic8/membrane/dsl/.
P.S.: The project classes are all annotated with #CompileStatic.
One approach: separate the extension code into its own project, and produce a jar from that project. Then, in another project, the DSL code can use that jar and specify the extension jar as a dependency.
This would make a huge answer, so I mocked it up and placed it on GitHub here (with all due credit to mrhaki's post). There are two projects: extension and dsl. Assuming you understand multi-project builds in Gradle, the essence is this line from dsl/build.gradle:
compileGroovy.dependsOn tasks.getByPath(':extension:jar')

Spring Tools Suite and Gradle - Setup to use correct resources from inside STS

I have a Spring Boot Gradle project setup in Spring Tools Suite (3.7.2 RELEASE) with the following source folders:
- src/integration-test/java
- src/integration-test/resources
- src/main/java
- src/main/resources
- src/test/java
- src/test/resources`
Whenever I run the application or unit tests from within STS, I see that STS is using the resources found under src/integration-test/resources.
I see a duplicate resource warning in STS for files which exist in all 3 resource source folders. For example, I have an application.properties in all 3 source folders and I see following:
The resource is a duplicate of src/integration-test/resources/application.properties and was not copied to the output folder
If I run the application as a JAR or unit tests/integration tests from the command line (via gradle build), everything seems to use the correct resources. This makes me believe it is a problem with how STS/Eclipse is handling gradle.
Does anybody know of how I can configure STS to use the correct resource source folders when using gradle?
I think my problem may be related to (or the same as?) Spring Boot incorrectly loads test configuration when running from eclipse+gradle, https://issuetracker.springsource.com/browse/STS-3882, https://issues.gradle.org/browse/GRADLE-1777
I also tried the solution found here, but that seems to only fix Maven builds:
Spring Tool Suite finds spring-boot integration test configuration and does not start main application
I think my problem may be related to...
Yes, it is related but in my opinion not the same. That problem is caused by the runtime classpath being incorrect. This problem is an error coming from the eclipse project builder so it is a compile-time issue.
The problems are closely related though. Depending on your point of view, you could say they are the same (incorrect mixing of test and compile-time classpaths).
Here, specifically, the problem is that the eclipse builder tries to copy all the resources it finds in source folders to the project's single output folder. Each source folder has a 'application.properties'. The builder warns that it could not copy some of them because one would overwrite the other.
I think there may be a solution for this problem. But it is a solution that really should come from Gradle + ( BuildShip | STS Gradle Tooling) than from you.
It is possible in Eclipse to configure each source-folder individually to target a specific outputfolder. Maven + M2E are doing this correcty, but Gradle + (BuildsShip | STS Gradle Tooling) combdos do not.
For example this is what maven puts into the eclipse .classpath file when it configures a test resources folder:
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
Notice how it explicitly sets the output folder for that entry (to something different from the project's default output folder).
You may be able to address the problem yourself by modifying the .classpath for a gradle project in a similar way. Either by doing it manually or from your build.gradle.
I'm not sure this is worth it however as you will then likely still get hit by the runtime classpath issue (since these folders will still be added to your runtime classpath, your runtime classpath will end-up with two appication.properties resources, one which will 'shadow' the other. See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=482315)
I would say, the right thing to do is add a comment to the issue I linked, and hope they fix it soon as there is only so much you can do yourself by hacking the build.gradle file to modify the .classpath (this can not solve the runtime classpath issue, but in order to solve the runtime classpath issue, they would have to configure source folders to target individual output folder similar to what m2e does).
I would add this as a comment to #Kris's answer but it's too long.
I have solved the runtime classpath issue by adding the code below to my build.gradle file. The code generates an Eclipse launch configuration for the Spring Boot application class and includes only the runtime classpath (i.e. no test JARs).
My project uses the Gradle 'eclipse' plugin to generate the Eclipse project files (which I then import into Eclipse). Running the eclipseClasspath Gradle target will generate the launch file in the project's root directory.
def mainClassName = "com.example.MyApplication"
task eclipseApplicationLaunch {
group "IDE"
description "Generate an Eclipse launch configuration file for the Spring Boot application class"
}
eclipseApplicationLaunch << {
def writer = new FileWriter("${mainClassName.substring(mainClassName.lastIndexOf(".")+1)}.launch")
def xml = new groovy.xml.MarkupBuilder(writer)
xml.doubleQuotes = true
xml.launchConfiguration(type: "org.eclipse.jdt.launching.localJavaApplication") {
listAttribute(key:"org.eclipse.debug.core.MAPPED_RESOURCE_PATHS") {
listEntry(value:"/${project.name}/src/main/java/${mainClassName.replace(".","/")}.java")
}
listAttribute(key:"org.eclipse.debug.core.MAPPED_RESOURCE_TYPES") {
listEntry(value:"1")
}
listAttribute(key:"org.eclipse.jdt.launching.CLASSPATH") {
listEntry(value:"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r\n<runtimeClasspathEntry containerPath=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/\" javaProject=\"${project.name}\" path=\"1\" type=\"4\"/>\r\n")
listEntry(value:"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r\n<runtimeClasspathEntry path=\"3\" projectName=\"${project.name}\" type=\"1\"/>\r\n")
configurations.runtime.resolvedConfiguration.resolvedArtifacts.each { artifact ->
def filePath = artifact.file.canonicalPath.replace("\\","/")
listEntry(value:"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r\n<runtimeClasspathEntry externalArchive=\"${filePath}\" path=\"3\" type=\"2\"/>\r\n")
}
}
booleanAttribute(key:"org.eclipse.jdt.launching.DEFAULT_CLASSPATH", value:"false")
stringAttribute(key:"org.eclipse.jdt.launching.MAIN_TYPE", value:"${mainClassName}")
stringAttribute(key:"org.eclipse.jdt.launching.PROGRAM_ARGUMENTS", value:"--spring.profiles.active=local --spring.config.location=conf/")
stringAttribute(key:"org.eclipse.jdt.launching.PROJECT_ATTR", value:"${project.name}")
stringAttribute(key:"org.eclipse.jdt.launching.VM_ARGUMENTS", value:"-Djava.net.preferIPv4Stack=true")
}
writer.close()
}
eclipseClasspath.dependsOn eclipseApplicationLaunch
I haven't modified the Eclipse .classpath file as per Kris' suggestion. Instead, I have added #Profile("test") to my test application class and #ActiveProfiles("test") to my test classes.

How to compile single class dependency in gradle outside the main project

I have a gradle project that contains only Selenium/TestNG test classes. They are executed against a deployed war application. All works fine and now I'm adding a java utility that will query the test base and print list of tests that belong to a given TestNG group. The utility should be compiled and executed separate from the main project, as users may want to query the test base before test execution.
I added the following to build.gradle:
task listgroups(dependsOn:'buildUtil' ) <<{
ant.java(classname: 'util.TestGroupScanner', fork: true,
classpath: "src/test/java")
}
task buildUtil {
compile {
source = "src/test/java/util"
}
}
However, when calling listgroups task, I'm getting the following error:
C:\console-bg1>g listgroups
FAILURE: Build failed with an exception.
(...)
* What went wrong:
A problem occurred evaluating root project 'console-bg1'.
> Could not find method compile() for arguments [build_4emu7duna2isgubc1k8uts8k9
8$_run_closure6_closure11#d210ab] on root project 'console-bg1'.
I'm not sure how to resolve this issue and needless to say, haven't found an answer online so far. Any pointers appreciated.
The problem is in the buildUtil task, as the error suggests. The buildUtil declares a compile closure, but such closure does not exist for the default task.
Let me try to clarify what your setup is. The util.TestGroupScanner source is in the src/test/java/util directory, which you want to compile separately from other source (presumably src/main/java and src/test/java). The buildUtil task is supposed to compile sources in src/test/java/util, and the listgroups task executes the scanner utility on sources src/test/java folder.
In this case, I'd suggest you declare a new source set for your utility sources, like this:
sourceSets {
util {
java {
srcDir 'src/test/java/util'
}
}
}
This will automatically create a compile task called compileUtilJava for you, that will compile those sources. I also think you'll want to include utility classes in the classpath when executing your tool, which can be retrieved by sourceSets.util.output.classesDir. So now your listgroups task will look like:
task listgroups(dependsOn: 'compileUtilJava' ) <<{
ant.java(classname: 'util.TestGroupScanner', fork: true,
classpath: "src/test/java:" + sourceSets.util.output.classesDir)
}
One thing I have noticed about your setup, is that src/test/java/util source folder is nested under src/test/java. Gradle will assume src/test/java to be the default folder for your project test, and will therefore automatically include it, and all of its children when running tests. Since you want to keep your utility folder separate from the default setup, I would recommend you put it in src/testutil/java, to avoid any clashes. If you do, don't forget to update the sourceSets setup above with the correct source path.
To solve this with gradle, I suggest to create a specific sourceset for your util class and add a task of type JavaExec that executes this class for printing your testng groups. Have a look at the following snippet:
apply plugin:'java'
...
...
configurations{
testUtilCompile.extendsFrom testCompile
}
...
...
sourceSets{
testUtil{
java {
srcDir "src/test/java"
include "util/**"
}
}
}
task printGroups(type:JavaExec){
main = "util.TestGroupScanner"
classpath = sourceSets.testUtil.runtimeClasspath
}
regards,
René

Resources