Skip to content

Commit

Permalink
Fail on error by default during test AOT processing
Browse files Browse the repository at this point in the history
Prior to this commit, if an error was encountered during build-time AOT
processing, the error was logged at WARN/DEBUG level, and processing
continued.

With this commit, test AOT processing now fails on error by default. In
addition, the `failOnError` mode can be disabled by setting the
`spring.test.aot.processing.failOnError` Spring/System property to
`false`.

Closes gh-30977
  • Loading branch information
sbrannen committed Aug 3, 2023
1 parent 1bfcaec commit 3e5aa8d
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 6 deletions.
5 changes: 5 additions & 0 deletions framework-docs/modules/ROOT/pages/appendix.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ for details.
{api-spring-framework}++/objenesis/SpringObjenesis.html#IGNORE_OBJENESIS_PROPERTY_NAME++[`SpringObjenesis`]
for details.

| `spring.test.aot.processing.failOnError`
| A boolean flag that controls whether errors encountered during AOT processing in the
_Spring TestContext Framework_ should result in an exception that fails the overall process.
See xref:testing/testcontext-framework/aot.adoc[Ahead of Time Support for Tests].

| `spring.test.constructor.autowire.mode`
| The default _test constructor autowire mode_ to use if `@TestConstructor` is not present
on a test class. See xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-testconstructor[Changing the default test constructor autowire mode].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,22 @@ following features.
use an AOT-optimized `ApplicationContext` that participates transparently with the
xref:testing/testcontext-framework/ctx-management/caching.adoc[context cache].

[WARNING]
[TIP]
====
By default, if an error is encountered during build-time AOT processing, an exception
will be thrown, and the overall process will fail immediately.
If you would prefer that build-time AOT processing continue after errors are encountered,
you can disable the `failOnError` mode which results in errors being logged at `WARN`
level or with greater detail at `DEBUG` level.
The `failOnError` mode can be disabled from the command line or a build script by setting
a JVM system property named `spring.test.aot.processing.failOnError` to `false`. As an
alternative, you can set the same property via the
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism.
====

[NOTE]
====
The `@ContextHierarchy` annotation is currently not supported in AOT mode.
====
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.context.aot.ApplicationContextAotGenerator;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.SpringProperties;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
Expand All @@ -56,6 +57,7 @@
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;

import static org.springframework.aot.hint.MemberCategory.INVOKE_DECLARED_CONSTRUCTORS;
import static org.springframework.aot.hint.MemberCategory.INVOKE_PUBLIC_METHODS;
Expand All @@ -70,8 +72,26 @@
*/
public class TestContextAotGenerator {

/**
* JVM system property used to set the {@code failOnError} flag: {@value}.
* <p>The {@code failOnError} flag controls whether errors encountered during
* AOT processing in the <em>Spring TestContext Framework</em> should result
* in an exception that fails the overall process.
* <p>Defaults to {@code true}.
* <p>Supported values include {@code true} or {@code false}, ignoring case.
* For example, the default may be changed to {@code false} by supplying
* the following JVM system property via the command line.
* <pre style="code">-Dspring.test.aot.processing.failOnError=false</pre>
* <p>May alternatively be configured via the
* {@link org.springframework.core.SpringProperties SpringProperties}
* mechanism.
* @since 6.1
*/
public static final String FAIL_ON_ERROR_PROPERTY_NAME = "spring.test.aot.processing.failOnError";

private static final Log logger = LogFactory.getLog(TestContextAotGenerator.class);


private final ApplicationContextAotGenerator aotGenerator = new ApplicationContextAotGenerator();

private final AotServices<TestRuntimeHintsRegistrar> testRuntimeHintsRegistrars;
Expand All @@ -85,13 +105,14 @@ public class TestContextAotGenerator {

private final RuntimeHints runtimeHints;

private final boolean failOnError;
final boolean failOnError;


/**
* Create a new {@link TestContextAotGenerator} that uses the supplied
* {@link GeneratedFiles}.
* @param generatedFiles the {@code GeneratedFiles} to use
* @see #TestContextAotGenerator(GeneratedFiles, RuntimeHints)
*/
public TestContextAotGenerator(GeneratedFiles generatedFiles) {
this(generatedFiles, new RuntimeHints());
Expand All @@ -100,11 +121,15 @@ public TestContextAotGenerator(GeneratedFiles generatedFiles) {
/**
* Create a new {@link TestContextAotGenerator} that uses the supplied
* {@link GeneratedFiles} and {@link RuntimeHints}.
* <p>This constructor looks up the value of the {@code failOnError} flag via
* the {@value #FAIL_ON_ERROR_PROPERTY_NAME} property, defaulting to
* {@code true} if the property is not set.
* @param generatedFiles the {@code GeneratedFiles} to use
* @param runtimeHints the {@code RuntimeHints} to use
* @see #TestContextAotGenerator(GeneratedFiles, RuntimeHints, boolean)
*/
public TestContextAotGenerator(GeneratedFiles generatedFiles, RuntimeHints runtimeHints) {
this(generatedFiles, runtimeHints, false);
this(generatedFiles, runtimeHints, getFailOnErrorFlag());
}

/**
Expand All @@ -114,9 +139,9 @@ public TestContextAotGenerator(GeneratedFiles generatedFiles, RuntimeHints runti
* @param runtimeHints the {@code RuntimeHints} to use
* @param failOnError {@code true} if errors encountered during AOT processing
* should result in an exception that fails the overall process
* @since 6.0.12
* @since 6.1
*/
TestContextAotGenerator(GeneratedFiles generatedFiles, RuntimeHints runtimeHints, boolean failOnError) {
public TestContextAotGenerator(GeneratedFiles generatedFiles, RuntimeHints runtimeHints, boolean failOnError) {
this.testRuntimeHintsRegistrars = AotServices.factories().load(TestRuntimeHintsRegistrar.class);
this.generatedFiles = generatedFiles;
this.runtimeHints = runtimeHints;
Expand Down Expand Up @@ -368,4 +393,12 @@ private void registerDeclaredConstructors(Class<?> type) {
this.runtimeHints.reflection().registerType(type, INVOKE_DECLARED_CONSTRUCTORS);
}

private static boolean getFailOnErrorFlag() {
String failOnError = SpringProperties.getProperty(FAIL_ON_ERROR_PROPERTY_NAME);
if (StringUtils.hasText(failOnError)) {
return Boolean.parseBoolean(failOnError.trim());
}
return true;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ void endToEndTests() {

// AOT BUILD-TIME: PROCESSING
InMemoryGeneratedFiles generatedFiles = new InMemoryGeneratedFiles();
TestContextAotGenerator generator = new TestContextAotGenerator(generatedFiles, new RuntimeHints(), true);
TestContextAotGenerator generator = new TestContextAotGenerator(generatedFiles, new RuntimeHints());
generator.processAheadOfTime(testClasses);

List<String> sourceFiles = generatedFiles.getGeneratedFiles(Kind.SOURCE).keySet().stream().toList();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.test.context.aot;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import org.springframework.core.SpringProperties;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.context.aot.TestContextAotGenerator.FAIL_ON_ERROR_PROPERTY_NAME;

/**
* Unit tests for {@link TestContextAotGenerator}.
*
* @author Sam Brannen
* @since 6.1
*/
class TestContextAotGeneratorUnitTests {

@BeforeEach
@AfterEach
void resetFlag() {
SpringProperties.setProperty(FAIL_ON_ERROR_PROPERTY_NAME, null);
}

@Test
void failOnErrorEnabledByDefault() {
assertThat(createGenerator().failOnError).isTrue();
}

@ParameterizedTest
@ValueSource(strings = {"true", " True\t"})
void failOnErrorEnabledViaSpringProperty(String value) {
SpringProperties.setProperty(FAIL_ON_ERROR_PROPERTY_NAME, value);
assertThat(createGenerator().failOnError).isTrue();
}

@ParameterizedTest
@ValueSource(strings = {"false", " False\t", "x"})
void failOnErrorDisabledViaSpringProperty(String value) {
SpringProperties.setProperty(FAIL_ON_ERROR_PROPERTY_NAME, value);
assertThat(createGenerator().failOnError).isFalse();
}


private static TestContextAotGenerator createGenerator() {
return new TestContextAotGenerator(null);
}

}

0 comments on commit 3e5aa8d

Please sign in to comment.