Skip to content

Commit fc1408f

Browse files
committed
Configure RuntimeHintsAgent in test suite
This commit configures the `RuntimeHintsAgent` in the Spring Framework test suite. Instead of applying the agent to the entire test suite, and possibly interfering with other tests, this commit adds a new custom Gradle plugin that does the following: * create a new test task named `"runtimeHintsTest"` * run this task with the runtime hints java agent * only execute tests tagged with `"RuntimeHintsTests"` See gh-27981
1 parent 444a9bd commit fc1408f

File tree

4 files changed

+150
-0
lines changed

4 files changed

+150
-0
lines changed

buildSrc/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,40 @@ current working version with. You can generate the reports for all modules or a
3333
```
3434

3535
The reports are located under `build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/`.
36+
37+
38+
### RuntimeHints Java Agent
39+
40+
The `spring-core-test` project module contributes the `RuntimeHintsAgent` Java agent.
41+
42+
The `RuntimeHintsAgentPlugin` Gradle plugin creates a dedicated `"runtimeHintsTest"` test task for each project.
43+
This task will detect and execute [tests tagged](https://junit.org/junit5/docs/current/user-guide/#running-tests-build-gradle)
44+
with the `"RuntimeHintsTests"` [JUnit tag](https://junit.org/junit5/docs/current/user-guide/#running-tests-tags).
45+
In the Spring Framework test suite, those are usually annotated with the `@EnabledIfRuntimeHintsAgent` annotation.
46+
47+
By default, the agent will instrument all classes located in the `"org.springframework"` package, as they are loaded.
48+
The `RuntimeHintsAgentExtension` allows to customize this using a DSL:
49+
50+
```groovy
51+
// this applies the `RuntimeHintsAgentPlugin` to the project
52+
plugins {
53+
id 'org.springframework.build.runtimehints-agent'
54+
}
55+
56+
// You can configure the agent to include and exclude packages from the instrumentation process.
57+
runtimeHintsAgent {
58+
includedPackages = ["org.springframework", "io.spring"]
59+
excludedPackages = ["org.example"]
60+
}
61+
62+
dependencies {
63+
// to use the test infrastructure, the project should also depend on the "spring-core-test" module
64+
testImplementation(project(":spring-core-test"))
65+
}
66+
```
67+
68+
With this configuration, `./gradlew runtimeHintsTest` will run all tests instrumented by this java agent.
69+
The global `./gradlew check` task depends on `runtimeHintsTest`.
70+
71+
NOTE: the "spring-core-test" module doesn't shade "spring-core" by design, so the agent should never instrument
72+
code that doesn't have "spring-core" on its classpath.

buildSrc/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,9 @@ gradlePlugin {
2525
id = "org.springframework.build.optional-dependencies"
2626
implementationClass = "org.springframework.build.optional.OptionalDependenciesPlugin"
2727
}
28+
runtimeHintsAgentPlugin {
29+
id = "org.springframework.build.runtimehints-agent"
30+
implementationClass = "org.springframework.build.hint.RuntimeHintsAgentPlugin"
31+
}
2832
}
2933
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.build.hint;
18+
19+
import java.util.Collections;
20+
21+
import org.gradle.api.model.ObjectFactory;
22+
import org.gradle.api.provider.SetProperty;
23+
24+
/**
25+
* Entry point to the DSL extension for the {@link RuntimeHintsAgentPlugin} Gradle plugin.
26+
* @author Brian Clozel
27+
*/
28+
public class RuntimeHintsAgentExtension {
29+
30+
private final SetProperty<String> includedPackages;
31+
32+
private final SetProperty<String> excludedPackages;
33+
34+
public RuntimeHintsAgentExtension(ObjectFactory objectFactory) {
35+
this.includedPackages = objectFactory.setProperty(String.class).convention(Collections.singleton("org.springframework"));
36+
this.excludedPackages = objectFactory.setProperty(String.class).convention(Collections.emptySet());
37+
}
38+
39+
public SetProperty<String> getIncludedPackages() {
40+
return this.includedPackages;
41+
}
42+
43+
public SetProperty<String> getExcludedPackages() {
44+
return this.excludedPackages;
45+
}
46+
47+
String asJavaAgentArgument() {
48+
StringBuilder builder = new StringBuilder();
49+
this.includedPackages.get().forEach(packageName -> builder.append('+').append(packageName).append(','));
50+
this.excludedPackages.get().forEach(packageName -> builder.append('-').append(packageName).append(','));
51+
return builder.toString();
52+
}
53+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.build.hint;
18+
19+
import org.gradle.api.Plugin;
20+
import org.gradle.api.Project;
21+
import org.gradle.api.plugins.JavaPlugin;
22+
import org.gradle.api.tasks.bundling.Jar;
23+
import org.gradle.api.tasks.testing.Test;
24+
25+
/**
26+
* {@link Plugin} that configures the {@code RuntimeHints} Java agent to test tasks.
27+
*
28+
* @author Brian Clozel
29+
*/
30+
public class RuntimeHintsAgentPlugin implements Plugin<Project> {
31+
32+
public static final String RUNTIMEHINTS_TEST_TASK = "runtimeHintsTest";
33+
private static final String EXTENSION_NAME = "runtimeHintsAgent";
34+
35+
36+
@Override
37+
public void apply(Project project) {
38+
39+
project.getPlugins().withType(JavaPlugin.class, javaPlugin -> {
40+
RuntimeHintsAgentExtension agentExtension = project.getExtensions().create(EXTENSION_NAME,
41+
RuntimeHintsAgentExtension.class, project.getObjects());
42+
Test agentTest = project.getTasks().create(RUNTIMEHINTS_TEST_TASK, Test.class, test -> {
43+
test.useJUnitPlatform(options -> {
44+
options.includeTags("RuntimeHintsTests");
45+
});
46+
test.include("**/*Tests.class", "**/*Test.class");
47+
test.systemProperty("java.awt.headless", "true");
48+
});
49+
project.afterEvaluate(p -> {
50+
Jar jar = project.getRootProject().project("spring-core-test").getTasks().withType(Jar.class).named("jar").get();
51+
agentTest.jvmArgs("-javaagent:" + jar.getArchiveFile().get().getAsFile() + "=" + agentExtension.asJavaAgentArgument());
52+
});
53+
project.getTasks().getByName("check", task -> task.dependsOn(agentTest));
54+
});
55+
}
56+
}

0 commit comments

Comments
 (0)