diff --git a/src/test-jdk17/java/com/fasterxml/jackson/databind/failing/RecordUpdate3079FailingTest.java b/src/test-jdk17/java/com/fasterxml/jackson/databind/failing/RecordUpdate3079FailingTest.java deleted file mode 100644 index de24fe331a..0000000000 --- a/src/test-jdk17/java/com/fasterxml/jackson/databind/failing/RecordUpdate3079FailingTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.fasterxml.jackson.databind.failing; - -import java.util.Collections; - -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.records.RecordUpdate3079Test; -import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; - -import static org.junit.jupiter.api.Assertions.*; - -// 01-Dec-2022, tatu: Alas, fails on JDK 17 -// see related passing test in RecordUpdate3079Test -public class RecordUpdate3079FailingTest extends DatabindTestUtil -{ - private final ObjectMapper MAPPER = newJsonMapper(); - - // [databind#3079]: Should be able to Record value directly - @Test - public void testDirectRecordUpdate() throws Exception - { - RecordUpdate3079Test.IdNameRecord orig = new RecordUpdate3079Test.IdNameRecord(123, "Bob"); - RecordUpdate3079Test.IdNameRecord result = MAPPER.updateValue(orig, - Collections.singletonMap("id", 137)); - assertNotNull(result); - assertEquals(137, result.id()); - assertEquals("Bob", result.name()); - assertNotSame(orig, result); - } -} diff --git a/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordUpdate3079Test.java b/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordUpdate3079Test.java index 68647cddbe..e96ff2219d 100644 --- a/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordUpdate3079Test.java +++ b/src/test-jdk17/java/com/fasterxml/jackson/databind/records/RecordUpdate3079Test.java @@ -2,8 +2,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; +import com.fasterxml.jackson.databind.testutil.Failing; import org.junit.jupiter.api.Test; +import java.util.Collections; + import static org.junit.jupiter.api.Assertions.*; @@ -36,4 +39,17 @@ public void testRecordAsPropertyUpdate() throws Exception assertSame(orig, result); assertNotSame(origRecord, result.value); } + + @Failing // 01-Dec-2022, tatu: Alas, fails on JDK 17 + // [databind#3079]: Should be able to Record value directly + @Test + public void testDirectRecordUpdate() throws Exception { + RecordUpdate3079Test.IdNameRecord orig = new RecordUpdate3079Test.IdNameRecord(123, "Bob"); + RecordUpdate3079Test.IdNameRecord result = MAPPER.updateValue(orig, + Collections.singletonMap("id", 137)); + assertNotNull(result); + assertEquals(137, result.id()); + assertEquals("Bob", result.name()); + assertNotSame(orig, result); + } } diff --git a/src/test/java/com/fasterxml/jackson/databind/testutil/Failing.java b/src/test/java/com/fasterxml/jackson/databind/testutil/Failing.java new file mode 100644 index 0000000000..ab79e277cf --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/testutil/Failing.java @@ -0,0 +1,89 @@ +package com.fasterxml.jackson.databind.testutil; + +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.TestExecutionExceptionHandler; +import org.opentest4j.TestAbortedException; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Method; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@ExtendWith(Failing.FailingExtension.class) // Comment this out if you want to temporarily let the tests actually fail +public @interface Failing { + + /** + * For when a test is failing only when running on specific Java version - e.g. Java 8 & Java 11 - but passing + * for other versions, then: + *

+     * {@code @}Failing(javaVersion = { "1.8", "11" }
+     * {@code @}Test
+     *  public void test...() { ... }
+     * 
+ */ + String[] javaVersion() default {}; + + class FailingExtension implements BeforeEachCallback, TestExecutionExceptionHandler, AfterEachCallback { + + private static final String SHOULD_FAIL = "should fail"; + + @Override + public void beforeEach(ExtensionContext context) { + getStore(context).put(SHOULD_FAIL, matchesFailingJavaVersion(context)); + } + + @Override + public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { + boolean shouldFail = getStore(context).get(SHOULD_FAIL, boolean.class); + if (!shouldFail) { + // the test threw exception even though it's not expected to fail for this test method, let the + // it fail with this exception so maintainers can investigate + throw throwable; + } + + // Instead of swallowing the exception and silently passing, better to mark the test as "aborted" + throw new TestAbortedException("Not implemented/fixed yet", throwable); + } + + @Override + public void afterEach(ExtensionContext context) { + boolean shouldFail = getStore(context).get(SHOULD_FAIL, boolean.class); + boolean testFailed = context.getExecutionException().isPresent(); + + if (shouldFail && !testFailed) { + throw new RuntimeException("Test that has been failing is now passing!"); + } + } + + private ExtensionContext.Store getStore(ExtensionContext context) { + return context.getStore(ExtensionContext.Namespace.create(getClass(), context.getRequiredTestMethod())); + } + + private boolean matchesFailingJavaVersion(ExtensionContext context) { + Method method = context.getTestMethod() + // should not happen because @Failing can only be annotated on test methods! + .orElseThrow(() -> new IllegalArgumentException("Cannot get test method!")); + + String currentJavaVersion = System.getProperty("java.version"); + String[] failingJavaVersions = method.getAnnotation(Failing.class).javaVersion(); + + if (failingJavaVersions.length == 0) { // failing for all Java versions + return true; + } + for (String version : failingJavaVersions) { + if (currentJavaVersion.startsWith(version)) { + return true; + } + } + return false; // not expected to fail for the current Java version + } + } +}