This commit adds the supporting testing infrastructure using the
`RuntimeHintsAgent`. Given that the agent is loaded by the JVM running
the test suite, we can then use it to record method invocations at
runtime and check whether the prepared `RuntimeHints` match the expected
behavior.
This commit contributes the `RuntimeHintsRecorder`. With this, we can
record relevant method invocations for a given lambda, focusing on a
specific part of the code behavior. This returns a
`RuntimeHintsInvocations` instance, which is an AssertJ assert provider.
From there, we can perform assertions on the recorded invocations and
check that a given collection of hints cover the reflection, resources
and proxies needs at runtime.
This also ships the `@EnabledIfRuntimeHintsAgent` opinionated
annotation: this applies the `RuntimeHintsAgentCondition` JUnit
extension that detects whether the `RuntimeHintsAgent` is loaded by the
current JVM. Tests annotated with this will be skipped if the agent is
not present. This annotation is also tagged with a JUnit `@Tag` to
gather such tests in a specific `"RuntimeHintsTests"` test suite.
In the Spring Framework build, we have chosen to isolate such tests and
not load the agent for the main test suite ("RuntimeHintsTests" tests
are excluded from the main suite). While the agent's intent is to be as
transparent as possible, there are security and access considerations
that could interefere with other tests.
With this approach, we can then create a separate test suite and run
agent tests in a dedicated JVM.
Note that projects using this infrastructure can choose to use the
condition by itself in a custom annotation.
Here is an example of this testing infrastructure:
```
@EnabledIfRuntimeHintsAgent
class MyTestCases {
@Test
void hintsForMethodsReflectionShouldMatch() {
RuntimeHints hints = new RuntimeHints();
hints.reflection().registerType(String.class,
hint -> hint.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS));
RuntimeHintsInvocations invocations = RuntimeHintsRecorder.record(() -> {
Method[] methods = String.class.getMethods();
});
assertThat(invocations).match(hints);
}
}
```
See gh-27981