Skip to content

Deprecate unsupported JRE enum constants and update related conditions #4516

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: develop/6.x
Choose a base branch
from

Conversation

marcphilipp
Copy link
Member

  • Remove unnecessary reflection in JRE enum
  • Deprecate JRE.JAVA_8...JAVA_16
  • Forbid pre-17 JREs in execution condition annotations

Overview


I hereby agree to the terms of the JUnit Contributor License Agreement.


Definition of Done

@marcphilipp marcphilipp self-assigned this May 6, 2025
@marcphilipp marcphilipp force-pushed the marc/jre-enum-on-17 branch from a6eb2b1 to f263410 Compare May 6, 2025 14:34
@marcphilipp marcphilipp marked this pull request as ready for review May 6, 2025 14:35
@marcphilipp marcphilipp force-pushed the marc/jre-enum-on-17 branch from f263410 to 71c3d64 Compare May 6, 2025 14:38
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generated class now looks like this:

/*
 * Copyright 2015-2025 the original author or authors.
 *
 * All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License v2.0 which
 * accompanies this distribution and is available at
 *
 * https://www.eclipse.org/legal/epl-v20.html
 */

package org.junit.jupiter.api.condition;

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

import org.apiguardian.api.API;

/**
 * Enumeration of Java Runtime Environment (JRE) versions.
 *
 * <p>If the current JRE version can be detected but is not one of the predefined
 * constants in this enum, {@link #OTHER} will be considered to be the
 * {@linkplain #isCurrentVersion current JRE version}. If the current JRE version
 * cannot be detected &mdash; for example, if the {@code java.version} JVM system
 * property is undefined &mdash; {@link #UNDEFINED} will be considered to be the
 * current JRE version.
 *
 * @since 5.1
 * @see #JAVA_8
 * @see #JAVA_9
 * @see #JAVA_10
 * @see #JAVA_11
 * @see #JAVA_12
 * @see #JAVA_13
 * @see #JAVA_14
 * @see #JAVA_15
 * @see #JAVA_16
 * @see #JAVA_17
 * @see #JAVA_18
 * @see #JAVA_19
 * @see #JAVA_20
 * @see #JAVA_21
 * @see #JAVA_22
 * @see #JAVA_23
 * @see #JAVA_24
 * @see #JAVA_25
 * @see #OTHER
 * @see EnabledOnJre
 * @see DisabledOnJre
 * @see EnabledForJreRange
 * @see DisabledForJreRange
 */
@API(status = STABLE, since = "5.1")
public enum JRE {

	/**
	 * An undefined JRE version.
	 *
	 * <p>This constant is used by JUnit as a default configuration value but is
	 * not intended to be used by users.
	 *
	 * <p>This constant returns {@code -1} for its {@linkplain #version() version}.
	 *
	 * @since 5.12
	 */
	@API(status = EXPERIMENTAL, since = "5.12")
	UNDEFINED(-1),

	/**
	 * Java 8.
	 *
	 * @deprecated No longer supported at runtime; please use {@link #JAVA_17} or later
	 */
	@API(status = DEPRECATED, since = "6.0") //
	@Deprecated(since = "6.0", forRemoval = true)
	JAVA_8(8),

	/**
	 * Java 9.
	 *
	 * @deprecated No longer supported at runtime; please use {@link #JAVA_17} or later
	 */
	@API(status = DEPRECATED, since = "6.0") //
	@Deprecated(since = "6.0", forRemoval = true)
	JAVA_9(9),

	/**
	 * Java 10.
	 *
	 * @deprecated No longer supported at runtime; please use {@link #JAVA_17} or later
	 */
	@API(status = DEPRECATED, since = "6.0") //
	@Deprecated(since = "6.0", forRemoval = true)
	JAVA_10(10),

	/**
	 * Java 11.
	 *
	 * @deprecated No longer supported at runtime; please use {@link #JAVA_17} or later
	 */
	@API(status = DEPRECATED, since = "6.0") //
	@Deprecated(since = "6.0", forRemoval = true)
	JAVA_11(11),

	/**
	 * Java 12.
	 *
	 * @since 5.4
	 * @deprecated No longer supported at runtime; please use {@link #JAVA_17} or later
	 */
	@API(status = DEPRECATED, since = "6.0") //
	@Deprecated(since = "6.0", forRemoval = true)
	JAVA_12(12),

	/**
	 * Java 13.
	 *
	 * @since 5.4
	 * @deprecated No longer supported at runtime; please use {@link #JAVA_17} or later
	 */
	@API(status = DEPRECATED, since = "6.0") //
	@Deprecated(since = "6.0", forRemoval = true)
	JAVA_13(13),

	/**
	 * Java 14.
	 *
	 * @since 5.5
	 * @deprecated No longer supported at runtime; please use {@link #JAVA_17} or later
	 */
	@API(status = DEPRECATED, since = "6.0") //
	@Deprecated(since = "6.0", forRemoval = true)
	JAVA_14(14),

	/**
	 * Java 15.
	 *
	 * @since 5.6
	 * @deprecated No longer supported at runtime; please use {@link #JAVA_17} or later
	 */
	@API(status = DEPRECATED, since = "6.0") //
	@Deprecated(since = "6.0", forRemoval = true)
	JAVA_15(15),

	/**
	 * Java 16.
	 *
	 * @since 5.7
	 * @deprecated No longer supported at runtime; please use {@link #JAVA_17} or later
	 */
	@API(status = DEPRECATED, since = "6.0") //
	@Deprecated(since = "6.0", forRemoval = true)
	JAVA_16(16),

	/**
	 * Java 17.
	 *
	 * @since 5.7.1
	 */
	@API(status = STABLE, since = "5.7.1")
	JAVA_17(17),

	/**
	 * Java 18.
	 *
	 * @since 5.8.1
	 */
	@API(status = STABLE, since = "5.8.1")
	JAVA_18(18),

	/**
	 * Java 19.
	 *
	 * @since 5.9
	 */
	@API(status = STABLE, since = "5.9")
	JAVA_19(19),

	/**
	 * Java 20.
	 *
	 * @since 5.9
	 */
	@API(status = STABLE, since = "5.9")
	JAVA_20(20),

	/**
	 * Java 21.
	 *
	 * @since 5.9.2
	 */
	@API(status = STABLE, since = "5.9.2")
	JAVA_21(21),

	/**
	 * Java 22.
	 *
	 * @since 5.10
	 */
	@API(status = STABLE, since = "5.10")
	JAVA_22(22),

	/**
	 * Java 23.
	 *
	 * @since 5.11
	 */
	@API(status = STABLE, since = "5.11")
	JAVA_23(23),

	/**
	 * Java 24.
	 *
	 * @since 5.11
	 */
	@API(status = STABLE, since = "5.11")
	JAVA_24(24),

	/**
	 * Java 25.
	 *
	 * @since 5.11.4
	 */
	@API(status = STABLE, since = "5.11.4")
	JAVA_25(25),

	/**
	 * A JRE version other than {@link #JAVA_8}, {@link #JAVA_9},
	 * {@link #JAVA_10}, {@link #JAVA_11}, {@link #JAVA_12},
	 * {@link #JAVA_13}, {@link #JAVA_14}, {@link #JAVA_15},
	 * {@link #JAVA_16}, {@link #JAVA_17}, {@link #JAVA_18},
	 * {@link #JAVA_19}, {@link #JAVA_20}, {@link #JAVA_21},
	 * {@link #JAVA_22}, {@link #JAVA_23}, {@link #JAVA_24},
	 * or {@link #JAVA_25}.
	 *
	 * <p>This constant returns {@link Integer#MAX_VALUE} for its
	 * {@linkplain #version() version}. To retrieve the actual version number,
	 * use {@link #currentVersionNumber()}.
	 */
	OTHER(Integer.MAX_VALUE);

	static final int UNDEFINED_VERSION = -1;

	static final int MINIMUM_VERSION = 17;

	private static final int CURRENT_VERSION = Runtime.version().feature();

	private final int version;

	JRE(int version) {
		this.version = version;
	}

	/**
	 * Get the version of <em>this</em> {@code JRE}.
	 *
	 * <p>If this {@code JRE} is {@link #UNDEFINED}, this method returns
	 * {@code -1}. If this {@code JRE} is {@link #OTHER}, this method returns
	 * {@link Integer#MAX_VALUE}.
	 *
	 * @return the version of this {@code JRE}
	 * @since 5.12
	 * @see Runtime.Version#feature()
	 * @see #currentVersionNumber()
	 */
	@API(status = EXPERIMENTAL, since = "5.12")
	public int version() {
		return this.version;
	}

	/**
	 * @return {@code true} if <em>this</em> {@code JRE} is known to be the
	 * Java Runtime Environment version for the currently executing JVM or if
	 * the version is {@link #OTHER} or {@link #UNDEFINED}
	 *
	 * @see #currentJre()
	 * @see #currentVersionNumber()
	 */
	public boolean isCurrentVersion() {
		return this == currentJre();
	}

	/**
	 * @return the {@link JRE} for the currently executing JVM, potentially
	 * {@link #OTHER} or {@link #UNDEFINED}
	 *
	 * @since 5.7
	 * @see #currentVersionNumber()
	 * @deprecated in favor of {@link #currentJre()}
	 */
	@API(status = DEPRECATED, since = "5.12")
	@Deprecated
	public static JRE currentVersion() {
		return currentJre();
	}

	/**
	 * @return the {@link JRE} for the currently executing JVM, potentially
	 * {@link #OTHER} or {@link #UNDEFINED}
	 *
	 * @since 5.12
	 * @see #currentVersionNumber()
	 */
	@API(status = STABLE, since = "5.12")
	public static JRE currentJre() {
		return switch (CURRENT_VERSION) {
			case UNDEFINED_VERSION -> UNDEFINED;
			case 8 -> JAVA_8;
			case 9 -> JAVA_9;
			case 10 -> JAVA_10;
			case 11 -> JAVA_11;
			case 12 -> JAVA_12;
			case 13 -> JAVA_13;
			case 14 -> JAVA_14;
			case 15 -> JAVA_15;
			case 16 -> JAVA_16;
			case 17 -> JAVA_17;
			case 18 -> JAVA_18;
			case 19 -> JAVA_19;
			case 20 -> JAVA_20;
			case 21 -> JAVA_21;
			case 22 -> JAVA_22;
			case 23 -> JAVA_23;
			case 24 -> JAVA_24;
			case 25 -> JAVA_25;
			default -> OTHER;
		};
	}

	/**
	 * @return the version number for the currently executing JVM, or {@code -1}
	 * if the current JVM version could not be determined
	 *
	 * @since 5.12
	 * @see Runtime.Version#feature()
	 * @see #currentJre()
	 */
	@API(status = EXPERIMENTAL, since = "5.12")
	public static int currentVersionNumber() {
		return CURRENT_VERSION;
	}

	/**
	 * @return {@code true} if the supplied version number is known to be the
	 * Java Runtime Environment version for the currently executing JVM or if
	 * the supplied version number is {@code -1} and the current JVM version
	 * could not be determined
	 *
	 * @since 5.12
	 * @see Runtime.Version#feature()
	 */
	@API(status = EXPERIMENTAL, since = "5.12")
	public static boolean isCurrentVersion(int version) {
		return version == CURRENT_VERSION;
	}

	static boolean isCurrentVersionWithinRange(int min, int max) {
		return CURRENT_VERSION >= min && CURRENT_VERSION <= max;
	}

}

})//
Arrays.stream(versions).peek(version -> Preconditions.condition(version >= JRE.MINIMUM_VERSION,
() -> String.format("Version [%d] in @%s must be greater than or equal to %d", version,
this.annotationName, JRE.MINIMUM_VERSION)))//
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for a little background...

We once discussed avoiding the use of peek() for validation as a policy; however, since we always invoke distinct() (a terminal operator) on this stream within the same method, I suppose it's fine to switch to peek(). 😉

@@ -71,8 +71,8 @@ protected final boolean isCurrentVersionWithinRange(JRE minJre, JRE maxJre, int
Preconditions.condition((min != JRE.MINIMUM_VERSION || max != Integer.MAX_VALUE),
() -> "You must declare a non-default value for the minimum or maximum value in @" + this.annotationName);
Preconditions.condition(min >= JRE.MINIMUM_VERSION,
() -> String.format("@%s's minimum value [%d] must greater than or equal to %d", this.annotationName, min,
JRE.MINIMUM_VERSION));
() -> String.format("@%s's minimum value [%d] must be greater than or equal to %d", this.annotationName,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops. Thanks for adding the missing word. 👍

Though, it looks like we didn't have a test to catch that -- yes/no? 🤔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants