- Added ExpectedToFailUntilFixed marker and rule

Ref.: https://github.com/VolmitSoftware/Iris/issues/1251
This commit is contained in:
CeeVeeR
2026-10-02 05:12:37 +02:00
parent 789644b93f
commit 72eff9b775
3 changed files with 135 additions and 0 deletions
@@ -0,0 +1,81 @@
package art.arcane.iris.testsupport;
import org.junit.AssumptionViolatedException;
import org.junit.Test;
import org.junit.function.ThrowingRunnable;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import java.lang.annotation.Annotation;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
public class ExpectedToFailUntilFixedRuleTest {
private static final String KNOWN_BUG = "known bug";
private static final String BUG_STILL_PRESENT = "bug still present";
private static final String REMOVAL_REMINDER = "remove @ExpectedToFailUntilFixed: " + KNOWN_BUG;
private static final Statement PASSING = new Statement() {
@Override
public void evaluate() {
}
};
private final ExpectedToFailUntilFixedRule rule = new ExpectedToFailUntilFixedRule();
private final AssertionError bugStillPresent = new AssertionError(BUG_STILL_PRESENT);
private final IllegalStateException crash = new IllegalStateException(BUG_STILL_PRESENT);
@Test
public void doesAMarkedTestThatStillFailsGetReportedAsSkippedWithItsFailure() {
assertSame(bugStillPresent, assertThrows(AssumptionViolatedException.class, runMarked(failingWith(bugStillPresent))).getCause());
}
@Test
public void doesAMarkedTestThatPassesFailWithARemovalReminder() {
assertTrue(messageOfAssertionFailure(runMarked(PASSING)).contains(REMOVAL_REMINDER));
}
@Test
public void doesACrashInAMarkedTestStillFail() {
assertSame(crash, assertThrows(IllegalStateException.class, runMarked(failingWith(crash))));
}
@Test
public void doesAnUnmarkedFailingTestStillFail() {
assertSame(bugStillPresent, assertThrows(AssertionError.class, runUnmarked(failingWith(bugStillPresent))));
}
@ExpectedToFailUntilFixed(KNOWN_BUG)
private void markedTestTemplate() {
}
private ThrowingRunnable runMarked(Statement test) {
return () -> rule.apply(test, describe(markerOfTemplate())).evaluate();
}
private ThrowingRunnable runUnmarked(Statement test) {
return () -> rule.apply(test, describe()).evaluate();
}
private ExpectedToFailUntilFixed markerOfTemplate() throws NoSuchMethodException {
return getClass().getDeclaredMethod("markedTestTemplate").getAnnotation(ExpectedToFailUntilFixed.class);
}
private static String messageOfAssertionFailure(ThrowingRunnable test) {
return assertThrows(AssertionError.class, test).getMessage();
}
private static Description describe(Annotation... annotations) {
return Description.createTestDescription(ExpectedToFailUntilFixedRuleTest.class, "example", annotations);
}
private static Statement failingWith(Throwable failure) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
throw failure;
}
};
}
}
@@ -0,0 +1,12 @@
package art.arcane.iris.testsupport;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExpectedToFailUntilFixed {
String value();
}
@@ -0,0 +1,42 @@
package art.arcane.iris.testsupport;
import org.junit.AssumptionViolatedException;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
public final class ExpectedToFailUntilFixedRule implements TestRule {
@Override
public Statement apply(Statement test, Description description) {
ExpectedToFailUntilFixed marker = markerOf(description);
return marker == null ? test : new ExpectedFailure(test, marker.value());
}
private static ExpectedToFailUntilFixed markerOf(Description description) {
return description.getAnnotation(ExpectedToFailUntilFixed.class);
}
private static final class ExpectedFailure extends Statement {
private final Statement test;
private final String reason;
private ExpectedFailure(Statement test, String reason) {
this.test = test;
this.reason = reason;
}
@Override
public void evaluate() throws Throwable {
try {
test.evaluate();
} catch (AssertionError stillFailing) {
throw reportAsSkipped(stillFailing);
}
throw new AssertionError("Passes now, remove @ExpectedToFailUntilFixed: " + reason);
}
private AssumptionViolatedException reportAsSkipped(AssertionError stillFailing) {
return new AssumptionViolatedException("Still failing until fixed: " + reason, stillFailing);
}
}
}