Split default test sourceset into more specific test sourcesets - java

i'm currently setting up a new project using java, gradle and spring boot. I'm struggling to create my planned test setup. My goal is to have separate test commands and directories located IN the test package.
What I have
I have looked into gradle sourceSets and was able to create two new sourceSets for unit and component tests. The problem I face is that I don't know if there is any way to defined the path of the sourcesets so they are inside the test directory.
src
-- main
-- test
-- unit
-- component
What I want
I just want the default test sourceSet to behave like a normal directory which itself contains my two testing sourceSets like this:
src
-- main
-- test
---- unit
---- component
Is this possible and also would this be 'against the convention' or something like that?
Any pointers are appreciated!

Basically, you should be able to set paths to your source sets any way you want to. Something like this should work:
sourceSets {
test {
java {
srcDirs = ['test/unit']
}
resources {
srcDirs = ['test/unit']
}
}
testComponent {
java {
srcDirs = ['test/component']
}
resources {
srcDirs = ['test/component']
}
}
}
Unfortunately I'm not sure if you can change the names of default source sets.
Keep in mind that you will have to provide more configuration for your new source set, e.g. create new Test task:
task testComponent(type: Test) {
testClassesDirs = sourceSets.testComponent.output
classpath += sourceSets.main.output
}
And also manage its dependencies separately or extend them from already existing configuration:
configurations {
testComponentCompile.extendsFrom(testCompile)
testComponentRuntime.extendsFrom(testRuntime)
}
As for the convention: it's best to stick to provided defaults, but if you have the need for different structure then it's also fine. Most common case for test separation is probably when you want split unit and integration test in order to be able to run them individually.

Related

How to define gradle tasks dependency in a task in custom gradle plugin

I'm writing a custom gradle plugin in which I want to have a bunch of common for several of my projects tasks and a sort of a 'main' task to control which of these tasks to turn on.
Regular tasks in the plugin are e.g.:
CopyDockerResourcesTask
CopyContainerFilesTask
PerformAnalysisTask
and the 'main' task is:
BaseProjectTask
so then in the project in build.gradle I'd like to be able to do this:
BaseProjectTask {
copyDockerResources = true
copyContainerFiles = true
performAnalysis = true
}
I want the default behaviour of the plugin to be to not to do anything, only add certain tasks if they are turned on in BaseProjectTask.
I wanted to achieve this with adding task dependency in #TaskAction method of BaseProjectTask:
class BaseProjectTask extends DefaultTask {
private final BaseProjectExtension extension
private final Project project
#Optional
#Input
Boolean copyContainerFiles = false
...
#Inject
BaseProjectTask(Project project, BaseProjectExtension extension) {
this.project = project
this.extension = extension
}
#TaskAction
def execute() {
if (copyContainerFiles) {
project.tasks.assemble.dependsOn(project.tasks.copyContainerFiles)
}
...
}
}
Creating task dependency, this line:
project.tasks.assemble.dependsOn(project.tasks.copyContainerFiles)
doesn't work.
Edit:
My current findings are that defining task dependency in #TaskAction is too late as this is execution phase. I could do it in the constructor (this way it works) but its too early as property copyContainerFiles isn't set yet.
Does anyone know a way of adding code in the task class that would be fired in the configuration phase? I think this is what I'm missing.
You need to configure task dependencies during the build configuration phase, as you surmised.
It's not possible to do it in the #TaskAction method. It's fundamental to the way Gradle works that it needs to know how tasks depend on each other before it starts executing the build. That allows Gradle to do some useful things, such as only executing the tasks that are not up to date, or working out what tasks will execute without actually executing them.
In general, tasks should not be aware of one another1.
When you are trying to do this in a plugin using values in a project extension, you must wait until after the project has evaluated so that the build script code executes first. You can do this with project.afterEvaluate()2.
So you can do the following (using Kotlin DSL3):
project.afterEvaluate {
tasks.register("baseTask") {
if (baseProjectExtension.copyDockerResources)
dependsOn(tasks.getByName("copyDockerResources"))
if (baseProjectExtension.copyContainerFiles)
dependsOn(tasks.getByName("copyContainerFiles"))
if (baseProjectExtension.performAnalysis)
dependsOn(tasks.getByName("performAnalysis"))
}
}
1See How to declare dependencies of a Gradle custom task?
2See https://docs.gradle.org/current/userguide/build_lifecycle.html#sec:project_evaluation
3What I am familiar with. Hopefully not too much trouble to convert to Groovy.

Extending/reusing the classpath file of the base image created using Jib?

I created a base image for a Java application using Jib which I want to extend using Jib.
(The Java application provides extensibility by loading additional Jars from the classpath)
In the extending gradle project, I did this:
jib {
....
container {
entrypoint = 'INHERIT'
}
...
}
It allowed me to reuse the entrypoint and args attributes added in the base image but I also want to extend/reuse the base classpath file.
As Jib creates /app/jib-classpath-file in the extending gradle project, the base layer /app/jib-classpath-file is not visible ( I would assume).
To workaround the issue, I added this in extending container configuration block.
extraClasspath = ['/app/libs/*']
Is there an idiomatic way of achieving this in Jib? One option I was thinking is to specify unique classpath files in base and extending projects and
use them like this in the Java command line:
java -cp #/app/jib-BASE-classpath-file #/app/jib-EXTENDED-classpath-file, but I am not finding the option of specifying the classpath file.
What is the recommended way? Thanks
Not a total solution but this shows how to remove the jvm args layer from the child image build altogether.
//build.gradle.kts
buildscript {
dependencies {
classpath("com.google.cloud.tools:jib-layer-filter-extension-gradle:0.1.0")
}
}
...
jib {
...
pluginExtensions {
pluginExtension {
implementation = "com.google.cloud.tools.jib.gradle.extension.layerfilter.JibLayerFilterExtension"
configuration (Action<com.google.cloud.tools.jib.gradle.extension.layerfilter.Configuration> {
filters { filter { glob ="**/jib-*-file" } }
})
}
}
}
This filters out files added in any given layer of a given jib build. If all the files from a layer are filtered out as in this case then the layer is never added to the image.
Sadly this does not let you see the contents of the java-classpath-file in the base image, which you would need to be able to extend it.
References:
https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin#jib-extensions
https://github.com/GoogleContainerTools/jib-extensions/tree/master/first-party/jib-layer-filter-extension-gradle
https://github.com/GoogleContainerTools/jib-extensions

Writing custom Lombok Annotation handlers

I want to write custom Lombok Annotation handlers. I know http://notatube.blogspot.de/2010/12/project-lombok-creating-custom.html. But the current lombok jar file does not contain many .class files, but files named .SCL.lombok instead.
I found, the .SCL.lombok files are the .class files, the build script of Lombok does rename them while generating the jar file, and the ShadowClassLoader is capable of loading these classes -- and the acronym SCL seems to come from this. It seems the reason for this is just to "Avoid contaminating the namespace of any project using an SCL-based jar. Autocompleters in IDEs will NOT suggest anything other than actual public API."
I was only able to compile my custom handler by
unpacking the contents of the lombok.jar
renaming the .SCL.lombok files to .class
adding the resulting directory to the compile classpath
In addition, to be able to use my custom handler, I needed to create a new fat jar containing both the lombok classes and my custom handler. The custom lombok class loader essentially prevents adding custom handlers in other multiple jars.
Is this the only way to extend Lombok? Or am I missing something?
I am using the following buildscript
apply plugin: 'java'
repositories {
jcenter()
}
configurations {
lombok
compileOnly
}
def unpackedAndRenamedLombokDir = file("$buildDir/lombok")
task unpackAndRenameLombok {
inputs.files configurations.lombok
outputs.dir unpackedAndRenamedLombokDir
doFirst {
mkdir unpackedAndRenamedLombokDir
delete unpackedAndRenamedLombokDir.listFiles()
}
doLast {
copy {
from zipTree(configurations.lombok.singleFile)
into unpackedAndRenamedLombokDir
rename "(.*)[.]SCL[.]lombok", '$1.class'
}
}
}
sourceSets {
main {
compileClasspath += configurations.compileOnly
output.dir(unpackedAndRenamedLombokDir, builtBy: unpackAndRenameLombok)
}
}
tasks.compileJava {
dependsOn unpackAndRenameLombok
}
dependencies {
compile files("${System.properties['java.home']}/../lib/tools.jar")
compile "org.eclipse.jdt:org.eclipse.jdt.core:3.10.0"
compile 'javax.inject:javax.inject:1'
lombok 'org.projectlombok:lombok:1.16.6'
compileOnly files(unpackedAndRenamedLombokDir)
}
In the meantime Reinier Zwitserloot created a new git-branch sclExpansionUpdate, that contains an updated version of the ShadowClassLoader:
ShadowClassLoader is now friendlier to trying to extend lombok.
Your (separate) jar/dir should have a file named
META-INF/ShadowClassLoader. This file should contain the string
'lombok'. If you have that, any classes in that jar/dir will be loaded
in the same space as lombok classes. You can also rename the class
files to .SCL.lombok to avoid other loaders from finding them.
I guess this did not yet make it into the main branch because it certainly has not been tested that much - I just tried it out for myself and it contains a little bug that prevents loading the required META-INF/services from extensions. To fix it you should replace two method calls to partOfShadow with inOwnBase:
[... line 443]
Enumeration<URL> sec = super.getResources(name);
while (sec.hasMoreElements()) {
URL item = sec.nextElement();
if (!inOwnBase(item, name)) vector.add(item); // <<-- HERE
}
if (altName != null) {
Enumeration<URL> tern = super.getResources(altName);
while (tern.hasMoreElements()) {
URL item = tern.nextElement();
if (!inOwnBase(item, altName)) vector.add(item); // <<-- AND HERE
}
}
I tested it with the above fix and it seems to work fine (not tested much though).
On a side note: with this new extension mechanism, it is now finally also possible to have the extensions annotation handlers and annotations in a different namespace than "lombok" - nice!
Using the input from this question and from the other answer (by Balder), we managed to put together a custom Lombok annotation handler: Symbok. Feel free to use that as a sample for writing your own.
BTW, instead of writing a custom Lombok handler, you could also implement a javac plugin instead -- it might be simpler.

Where is the 'main' method of 'sourceSets' defined?

I'm going through some simple Gradle examples, and trying to get my head around the syntax. According to Gradle syntax, there must be a method called 'main' somewhere on 'sourceSets' (which is a SourceSetContainer) that takes a Closure. I figured that I would be able to find it by browsing around the Gradle javadocs, but I can't find it. Can someone point me to where 'main' is defined in this example?
apply plugin: 'java'
sourceSets {
main {
java {
srcDirs = ['src']
}
}
}
sourceSets is a container of named source sets. The java plugin adds a source set named main (and another named test) to that container. As such, there is no physical method or property called main. sourceSets.main { ... } could also be written as sourceSets.getByName("main") { ... }.

gwt-test-utils does not find my entry point class

I am trying to get gwt-test-utils to work. I set up the project in the following way:
src/main/java : all the java source code
src/test/java : the test source code
src/test/resources : resource files for the tests
I am building my project with gradle and eclipse. Gradle uses these directories correctly by default and I added all three of them as source directories to Eclipse.
I have successfully built and run the project and was able to execute some plain old JUnit tests as well as a GWTTestCase, so I think I set up the project and its dependencies correctly.
Now I wanted to use gwt-test-utils for some more advanced integration tests. To do so I did the following:
Add the gwt-test-utils and gwt-test-utils-csv to my dependencies
gwtTestUtilsVersion = '0.45'
testCompile group:'com.googlecode.gwt-test-utils', name:'gwt-test-utils', version:gwtTestUtilsVersion
testCompile group:'com.googlecode.gwt-test-utils', name:'gwt-test-utils-csv', version:gwtTestUtilsVersion
Add a gwt-test-utils.properties file to the directory src/test/resources/META-INF with the following content:
path/to/my/module = gwt-module
Added a class that extends GwtCsvTest to a package in the src/test/java directory. It is modeled after the second example in HowToWriteCsvScenario from the gwt-test-utils project wiki, replacing occurrence of their example classes with mine. It looks like this
#CsvDirectory(value = "gwtTests")
public class LoginLogoutTest extends GwtCsvTest
{
#Mock
private MainServiceAsync mainService;
private AppController appController = new AppController();
#CsvMethod
public void initApp()
{
appController.onModuleLoad();
}
#Before
public void setup()
{
GwtFinder.registerNodeFinder("myApp", new NodeObjectFinder()
{
#Override
public Object find(Node node)
{
return csvRunner.getNodeValue(appController, node);
}
});
GwtFinder.registerNodeFinder("loginView", new NodeObjectFinder()
{
#Override
public Object find(Node node)
{
return csvRunner.getNodeValue(appController.getRootPresenter().getCurrentlyActiveSubPresenters().iterator().next().getView(), node);
}
});
addGwtCreateHandler(createRemoteServiceCreateHandler());
}
}
added a csv-file for configuring the test to src/test/resources/gwtTests with the following content
start
initApp
assertExist;/loginView/emailTextBox
I tried executing it via the Eclipse's Run As > JUnit Test and indirectly via gradle build (which executes all the test cases, not just this one). Both lead to the same error:
ERROR GwtTreeLogger Unable to find type 'myPackage.client.AppController'
ERROR GwtTreeLogger Hint: Check that the type name 'myPackage.client.AppController' is really what you meant
ERROR GwtTreeLogger Hint: Check that your classpath includes all required source roots
The AppController class is the entry-point configured in the module I configured in gwt-test-utils.properties, which makes me think that configuration works correctly and the rest of the setup (dependencies and all) work as well.
In an earlier version I used the same file as a subclass of GWTTestCase and created an AppController instance in the same way. That worked, so I'm pretty sure the class path is setup correctly to include it as well. I also tried changing it back to the previous version just now and it still works.
I have no clue why the class is not found. Is there anything gwt-test-utils does differently which means I need to specifically set the class path for it? Otherwise it should just work, since both gradle and eclipse know about all the relevant source folders and dependencies.

Categories

Resources