Skip to content

Commit

Permalink
Support arbitrary Java versions with JRE conditions
Browse files Browse the repository at this point in the history
This commit introduces support for arbitrary Java versions in the JRE
enum, @⁠EnabledOnJre, @⁠DisabledOnJre, @⁠EnabledForJreRange, and
@⁠DisabledForJreRange.

Closes: junit-team#3930
Closes: junit-team#3931
  • Loading branch information
sbrannen committed Feb 2, 2025
1 parent 0ba92b4 commit d197f5a
Show file tree
Hide file tree
Showing 22 changed files with 1,036 additions and 160 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

import static org.junit.jupiter.api.condition.JRE.JAVA_10;
import static org.junit.jupiter.api.condition.JRE.JAVA_11;
import static org.junit.jupiter.api.condition.JRE.JAVA_17;
import static org.junit.jupiter.api.condition.JRE.JAVA_18;
import static org.junit.jupiter.api.condition.JRE.JAVA_8;
import static org.junit.jupiter.api.condition.JRE.JAVA_9;
import static org.junit.jupiter.api.condition.OS.LINUX;
Expand All @@ -23,6 +25,7 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledForJreRange;
import org.junit.jupiter.api.condition.DisabledIf;
Expand All @@ -38,6 +41,7 @@
import org.junit.jupiter.api.condition.EnabledInNativeImage;
import org.junit.jupiter.api.condition.EnabledOnJre;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.JRE;

class ConditionalTestExecutionDemo {

Expand Down Expand Up @@ -99,6 +103,16 @@ void notOnNewMacs() {
}
// end::user_guide_architecture[]

@Test
@EnabledOnJre(value = { JAVA_17, JAVA_18 }, versions = { 20, 21 })
void onJava17or18or20or21() {
}

@Test
@EnabledOnJre(versions = 21)
void onlyOnJava21() {
}

// tag::user_guide_jre[]
@Test
@EnabledOnJre(JAVA_8)
Expand All @@ -124,6 +138,45 @@ void fromJava9toCurrentJavaFeatureNumber() {
// ...
}

@Test
@EnabledForJreRange(minVersion = 10)
void fromJava10toCurrentJavaFeatureNumber() {
// ...
}

@Test
@EnabledForJreRange(minVersion = 25)
void fromJava25toCurrentJavaFeatureNumber() {
// ...
}

@Disabled("DEMO: intended to fail")
@Test
@EnabledForJreRange(minVersion = 99, max = JRE.JAVA_17)
void fromJava99toJava17() {
// ...
}

@Disabled("DEMO: intended to fail")
@Test
@EnabledForJreRange(min = JAVA_11, minVersion = 10)
void competingJreAndMinVersions() {
// ...
}

@Disabled("DEMO: intended to fail")
@Test
@EnabledForJreRange(max = JAVA_11, maxVersion = 10)
void competingJreAndMaxVersions() {
// ...
}

@Test
@EnabledForJreRange(minVersion = 10, maxVersion = 25)
void fromJava17to25() {
// ...
}

@Test
@EnabledForJreRange(max = JAVA_11)
void fromJava8To11() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

package org.junit.jupiter.api.condition;

import static org.apiguardian.api.API.Status.EXPERIMENTAL;
import static org.apiguardian.api.API.Status.STABLE;

import java.lang.annotation.Documented;
Expand All @@ -24,7 +25,10 @@
/**
* {@code @DisabledForJreRange} is used to signal that the annotated test class
* or test method is <em>disabled</em> for a specific range of Java Runtime
* Environment (JRE) versions from {@link #min} to {@link #max}.
* Environment (JRE) versions.
*
* <p>Version ranges can be specified as {@link JRE} enum constants via {@link #min}
* and {@link #max} or as integers via {@link #minVersion} and {@link #maxVersion}.
*
* <p>When applied at the class level, all test methods within that class will
* be disabled on the same specified JRE versions.
Expand Down Expand Up @@ -82,28 +86,74 @@
public @interface DisabledForJreRange {

/**
* Java Runtime Environment version which is used as the lower boundary
* for the version range that determines if the annotated class or method
* should be disabled.
* Java Runtime Environment version which is used as the lower boundary for
* the version range that determines if the annotated class or method should
* be disabled, specified as a {@link JRE} enum constant.
*
* <p>If a {@code JRE} enum constant does not exist for a particular JRE
* version, you can specify the minimum version via {@link #minVersion()}
* instead.
*
* <p>Defaults to {@link JRE#JAVA_8 JAVA_8}, as this is the lowest
* supported JRE version.
* <p>Defaults to {@link JRE#UNDEFINED UNDEFINED}, which will be interpreted
* as {@link JRE#JAVA_8 JAVA_8} if the {@link #minVersion()} is not set.
*
* @see JRE
* @see #minVersion()
*/
JRE min() default JRE.JAVA_8;
JRE min() default JRE.UNDEFINED;

/**
* Java Runtime Environment version which is used as the upper boundary
* for the version range that determines if the annotated class or method
* should be disabled.
* Java Runtime Environment version which is used as the upper boundary for
* the version range that determines if the annotated class or method should
* be disabled, specified as a {@link JRE} enum constant.
*
* <p>Defaults to {@link JRE#OTHER OTHER}, as this will always be the highest
* possible version.
* <p>If a {@code JRE} enum constant does not exist for a particular JRE
* version, you can specify the maximum version via {@link #maxVersion()}
* instead.
*
* <p>Defaults to {@link JRE#UNDEFINED UNDEFINED}, which will be interpreted
* as {@link JRE#OTHER OTHER} if the {@link #maxVersion()} is not set.
*
* @see JRE
* @see #maxVersion()
*/
JRE max() default JRE.UNDEFINED;

/**
* Java Runtime Environment version which is used as the lower boundary for
* the version range that determines if the annotated class or method should
* be disabled, specified as an integer.
*
* <p>If a {@code JRE} enum constant exists for the particular JRE version,
* you can specify the minimum version via {@link #min()} instead.
*
* <p>Defaults to {@code -1} to signal that {@link #min()} should be used instead.
*
* @since 5.12
* @see #min()
* @see JRE#featureVersion()
* @see Runtime.Version#feature()
*/
@API(status = EXPERIMENTAL, since = "5.12")
int minVersion() default -1;

/**
* Java Runtime Environment version which is used as the upper boundary for
* the version range that determines if the annotated class or method should
* be disabled, specified as an integer.
*
* <p>If a {@code JRE} enum constant exists for the particular JRE version,
* you can specify the maximum version via {@link #max()} instead.
*
* <p>Defaults to {@code -1} to signal that {@link #max()} should be used instead.
*
* @since 5.12
* @see #max()
* @see JRE#featureVersion()
* @see Runtime.Version#feature()
*/
JRE max() default JRE.OTHER;
@API(status = EXPERIMENTAL, since = "5.12")
int maxVersion() default -1;

/**
* Custom reason to provide if the test or container is disabled.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import static org.junit.jupiter.api.condition.EnabledOnJreCondition.ENABLED_ON_CURRENT_JRE;

import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.platform.commons.util.Preconditions;

/**
* {@link ExecutionCondition} for {@link DisabledForJreRange @DisabledForJreRange}.
Expand All @@ -31,15 +30,8 @@ class DisabledForJreRangeCondition extends BooleanExecutionCondition<DisabledFor

@Override
boolean isEnabled(DisabledForJreRange annotation) {
JRE min = annotation.min();
JRE max = annotation.max();

Preconditions.condition((min != JRE.JAVA_8 || max != JRE.OTHER),
"You must declare a non-default value for min or max in @DisabledForJreRange");
Preconditions.condition(max.compareTo(min) >= 0,
"@DisabledForJreRange.min must be less than or equal to @DisabledForJreRange.max");

return !JRE.isCurrentVersionWithinRange(min, max);
return !EnabledForJreRangeCondition.isCurrentVersionWithinRange("DisabledForJreRange", annotation.min(),
annotation.max(), annotation.minVersion(), annotation.maxVersion());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

package org.junit.jupiter.api.condition;

import static org.apiguardian.api.API.Status.EXPERIMENTAL;
import static org.apiguardian.api.API.Status.STABLE;

import java.lang.annotation.Documented;
Expand All @@ -24,7 +25,10 @@
/**
* {@code @DisabledOnJre} is used to signal that the annotated test class or
* test method is <em>disabled</em> on one or more specified Java Runtime
* Environment (JRE) {@linkplain #value versions}.
* Environment (JRE) versions.
*
* <p>Versions can be specified as {@link JRE} enum constants via {@link #value()}
* or as integers via {@link #versions()}.
*
* <p>When applied at the class level, all test methods within that class
* will be disabled on the same specified JRE versions.
Expand Down Expand Up @@ -82,12 +86,31 @@
public @interface DisabledOnJre {

/**
* Java Runtime Environment versions on which the annotated class or
* method should be disabled.
* Java Runtime Environment versions on which the annotated class or method
* should be disabled, specified as {@link JRE} enum constants.
*
* <p>If a {@code JRE} enum constant does not exist for a particular JRE
* version, you can specify the version via {@link #versions()} instead.
*
* @see JRE
* @see #versions()
*/
JRE[] value() default {};

/**
* Java Runtime Environment versions on which the annotated class or method
* should be disabled, specified as integers.
*
* <p>If a {@code JRE} enum constant exists for a particular JRE version, you
* can specify the version via {@link #value()} instead.
*
* @since 5.12
* @see #value()
* @see JRE#featureVersion()
* @see Runtime.Version#feature()
*/
JRE[] value();
@API(status = EXPERIMENTAL, since = "5.12")
int[] versions() default {};

/**
* Custom reason to provide if the test or container is disabled.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,17 @@ class DisabledOnJreCondition extends BooleanExecutionCondition<DisabledOnJre> {

@Override
boolean isEnabled(DisabledOnJre annotation) {
JRE[] versions = annotation.value();
Preconditions.condition(versions.length > 0, "You must declare at least one JRE in @DisabledOnJre");
return Arrays.stream(versions).noneMatch(JRE::isCurrentVersion);
JRE[] jres = annotation.value();
int[] featureVersions = annotation.versions();
Preconditions.condition(jres.length > 0 || featureVersions.length > 0,
"You must declare at least one JRE or version in @DisabledOnJre");
return Arrays.stream(jres).map(this::validateJre).noneMatch(JRE::isCurrentVersion)
&& Arrays.stream(featureVersions).noneMatch(JRE::isCurrentVersion);
}

private final JRE validateJre(JRE jre) {
Preconditions.condition(jre != JRE.UNDEFINED, "JRE.UNDEFINED is not supported in @DisabledOnJre");
return jre;
}

}
Loading

0 comments on commit d197f5a

Please sign in to comment.