Using getProject() with Gradle Configuration Cache - gradle

Gradle documentation states that using the getProject() method in the #TaskAction method of a Task class should not be used if you want compatibility with Gradle Configuration Cache. The question I have is that, suppose you have something like this:
public abstract class AbstractMyTask extends DefaultTask {
#Internal
protected abstract DirectoryProperty getRootDirectory();
protected AbstractMyTask() {
getRootDirectory().convention(getProject().getRootProject().getLayout().getProjectDirectory());
}
}
The general intent of the code snippet is to have a Directory property representing the root project directory (ie. a safe replacement for getProject().getRootDir()), and it seems like the getProject() call in the constructor would be okay. I'd like some sober second thought on whether that is the case.

Related

Testing Gradle task that uses javaexec()

Suppose I have a custom Gradle task that uses ExecOperations.javaexec() in its code:
public class MyTask extends DefaultTask {
private final ExecOperations execOps
#Inject
public MyTask(ExecOperations execOps) {
this.execOps = execOps;
}
#TaskAction
public void run() {
ExecResult result = execOps.javaexec(spec -> {
// classpath is set to some jar with a main class
spec.classpath(...);
spec.args(...);
});
// Do more stuff with result
}
}
The issue I have is that I am not sure how I can create a Spock test fixture for it to validate that the jar is being run with the correct arguments without actually running the jar itself. (The jar makes remote calls, for example, which I want to avoid).
It seems like the only plausible way forward is to potentially find a way to expose the ExecOperations for testing and possibly replace it with a stub, along with turning the Action<JavaExecSpec> from a mere lambda to something more substantial. Any ideas would be appreciated.

How do I implement a gradle plugin which depends on an extension being configured?

For context, I'm attempting to write a plugin which will generate source code (Java classes), and it exposes multiple tasks (for different purposes). These tasks rely on information given by the user via extensions. Without this user defined information, these tasks wouldn't know what to do, and so they shouldn't be run at all if the extension hasn't been defined in build.gradle by the user.
One key thing to note is that, since I'm generating source code, I'm making the JavaCompile task depend on each of these tasks. However, I only want to define this relationship if the extension has been configured in build.gradle. How do I achieve this?
The following is a simple example for one task.
The MyPlugin class which is the implementation class:
public class MyPlugin implements Plugin<Project> {
// define extensions
MyPluginConfig myPluginConfig = project.getExtensions().create("myPluginConfig", MyPluginConfig.class);
((ExtensionAware) myPluginConfig).getExtensions().create("fileConfig", FileConfig.class);
// register task
TaskProvider<MyPluginTask> taskProvider = project.getTasks().register("myPluginTask", MyPluginTask.class, task -> {
task.getFilesConfigs().set(myPluginConfig.getFileConfigs());
});
// ensure JavaCompile task depends on this plugin's task
project.getTasks().withType(JavaCompile.class).configureEach(task -> task.dependsOn(taskProvider));
}
MyPluginConfig is an extension class which just contains a list of FileConfig:
public abstract class MyPluginConfig {
public abstract ListProperty<FileConfig> getFileConfigs();
}
FileConfig is an extension class (only used within MyPluginConfig) which contains the actual information the task may use to generate Java classes:
public abstract class FileConfig {
public abstract Property<String> getX();
public abstract Property<String> getY();
public abstract Property<String> getZ();
}
And finally, MyPluginTask uses the information from these extensions to generate the Java classes:
public abstract class MyPluginTask {
#Input
public abstract ListProperty<FileConfig> getFileConfigs();
#TaskAction
public void execute() {
System.out.println("Executing task...");
}
}
In build.gradle, a user can now use this plugin like so:
plugins {
id 'com.example.my-plugin' version '0.0.1'
}
/*
myPluginConfig {
fileConfigs = [
fileConfig {
x = 'x'
y = 'y'
z = 'z'
},
fileConfig {
x = '2x'
y = '2y'
z = '2z'
},
]
}
*/
Notice that I actually commented out the myPluginConfig configuration above. As such, I don't want my task (i.e, MyPluginTask#execute) to run at all. With the way I've implemented it so far, it will run because of the dependency I created between it and the JavaCompile task.
An easy way to work around this would be to check if MyPluginTask#getFileConfigs returns an empty list. However, this doesn't feel "clean"? It seems a bit fragile since the business logic within the task could change in the future. Is there an official or proper way to do what I'm trying to achieve?

Error if the [AssemblyInitialize] already exists in the test project with Specflow

I've updated Specflow from the 3.0.225 to the 3.1.62 and I received the error Tests_Integration_MSTestAssemblyHooks: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly.
The reason is obviously that I'd had the [AssemblyInitialize] attribute in my project already. How can I fix it?
The reason is that Specflow generates another file in the background which has the AssemblyInitialize/AssemblyCleanup hooks defined. In order to fix that one should use the hooks provided by Specflow, namely BeforeTestRun/AfterTestRun. Like this:
[Binding] // add the Binding attribute on the class with the assembly level hooks
public abstract class SeleniumTest
{
// it used to be [AssemblyInitialize]
[BeforeTestRun]
public static void AssemblyInitialize(/* note there is no TestContext parameter anymore */)
{
// ...
}
// it used to be [AssemblyCleanup]
[AfterTestRun]
public static void AssemblyCleanup()
{
// ...
}
}

Private method visibility confusion

I am confused with Groovy method visibility in the context of my Gradle build.
For some tests in my project, I have to first start a server.
For this I created a custom task class that extends Gradle's Test like so:
class TestWithServer extends Test {
TestWithServer() {
super()
beforeTest {
startServer()
}
}
private void startServer() {
println('placeholder')
}
}
But if I try to run such a task, I get an error:
Could not find method startServer() for arguments [] on task ':testWithServer' of type TestWithServer.
I found that when I change the visibility of startServer() to the default (public), the task runs fine.
How come I can't use the private method from within its own class?
It is not the same class, because Gradle adds some magic to the task types. Just add println this.class into the beforeTest closure to see the name of the actual class (something like TestWithServer_Decorated). This additional magic also explains why the error message contains the task name and how the class knows about being a task (type) at all. Since the decorated class is a subclass of your class you can use the protected modifier to encapsulate your method.

Use #Timed for inheritanced functions

We use a abstract classes for services like this pseudocode
abstract class AbstractApiService {
#Timed(value="get", useClassPrefix=true)
def get(Long id) {
... returns sth ....
}
#Timed(value="create", useClassPrefix=true)
def create(Map params) {
... returns sth ....
}
}
There are beans which inherit AbstractApiService and serve features like creating, deleting, updating entities like
class UserAccountService extends AbstractApiService {
... code ....
}
I would like to get metrics for each call a function from child classes like UserAccountService, but Prometheus sends events with full parent class prefix.
App is based on Grails 3.3.8
build.gradle:
compile 'com.moelholm:prometheus-spring-boot-starter:1.0.2'
compile 'io.dropwizard.metrics:metrics-core:4.0.0-alpha2'
compile 'io.dropwizard.metrics:metrics-jvm:4.0.0-alpha2'
compile 'org.grails.plugins:dropwizard-metrics:1.0.0.M2'
Unfortunately, I believe you will have to define the #Timed annotation for each method you want tracked. I don't think that the annotation code will spin up a separate metric for each concrete class.
I created a custom manual solution
https://gist.github.com/michmzr/1e03534bc5fb6df89065f6964acf9c71

Resources