Skip to content

Commit

Permalink
Document method ordering APIs
Browse files Browse the repository at this point in the history
Issue: #13
  • Loading branch information
sbrannen committed Nov 9, 2018
1 parent cee914f commit 68b1e6f
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,70 @@
import org.junit.platform.commons.util.ClassUtils;

/**
* {@code MethodOrderer} defines the API for ordering the <em>test methods</em>
* in a given test class.
*
* <p>In this context, the term "test method" refers to any method annotated with
* {@code @Test}, {@code @RepeatedTest}, {@code @ParameterizedTest},
* {@code @TestFactory}, or {@code @TestTemplate}.
*
* <h4>Built-in Implementations</h4>
*
* <p>JUnit Jupiter provides the following built-in {@code MethodOrderer}
* implementations.
*
* <ul>
* <li>{@link Alphanumeric}</li>
* <li>{@link OrderAnnotation}</li>
* <li>{@link Random}</li>
* </ul>
*
* @since 5.4
* @see TestMethodOrder
* @see MethodOrdererContext
* @see #orderMethods(MethodOrdererContext)
*/
@API(status = EXPERIMENTAL, since = "5.4")
public interface MethodOrderer {

/**
* Order the methods encapsulated in the supplied {@link MethodOrdererContext}.
*
* <p>The methods to order or sort are made indirectly available via
* {@link MethodOrdererContext#getMethodDescriptors()}. Since this method
* has a {@code void} return type, the list of method descriptors must be
* modified directly.
*
* <p>For example, a simplified implementation of the {@link Random}
* {@code MethodOrderer} might look like the following.
*
* <pre class="code">
* public void orderMethods(MethodOrdererContext context) {
* Collections.shuffle(context.getMethodDescriptors());
* }
* </pre>
*
* @param context the {@code MethodOrdererContext} containing the
* {@link MethodDescriptor method descriptors} to order; never {@code null}
* @see #getDefaultExecutionMode()
*/
void orderMethods(MethodOrdererContext context);

/**
* Get the <em>default</em> {@link ExecutionMode} for the annotated class.
* Get the <em>default</em> {@link ExecutionMode} for the test class
* configured with this {@link MethodOrderer}.
*
* <p>Defaults to {@link ExecutionMode#SAME_THREAD SAME_THREAD}, since
* ordered methods are typically sorted in a fashion that would conflict
* with concurrent execution.
*
* <p>Can be overridden via an explicit
* {@link org.junit.jupiter.api.parallel.Execution @Execution} declaration.
* {@link org.junit.jupiter.api.parallel.Execution @Execution} declaration
* on the test class or in concrete implementations of the
* {@code MethodOrderer} API.
*
* @return the default {@code ExecutionMode}; never {@code null}
* @see #orderMethods(MethodOrdererContext)
*/
default ExecutionMode getDefaultExecutionMode() {
return ExecutionMode.SAME_THREAD;
Expand All @@ -52,12 +97,17 @@ default ExecutionMode getDefaultExecutionMode() {
* {@code MethodOrderer} that sorts methods alphanumerically based on their
* names using {@link String#compareTo(String)}.
*
* <p>If two methods have the same name, a {@code String} representation of
* <p>If two methods have the same name, {@code String} representations of
* their formal parameter lists will be used as a fallback for comparing the
* methods.
*/
class Alphanumeric implements MethodOrderer {

/**
* Sort the methods encapsulated in the supplied
* {@link MethodOrdererContext} alphanumerically based on their names
* and formal parameter lists.
*/
@Override
public void orderMethods(MethodOrdererContext context) {
context.getMethodDescriptors().sort(comparator);
Expand Down Expand Up @@ -85,11 +135,20 @@ private static String parameterList(Method method) {
* {@code MethodOrderer} that sorts methods based on the {@link Order @Order}
* annotation.
*
* <p>Any methods not annotated with {@code @Order} will appear at the end of
* the sorted list.
* <p>Any methods that are assigned the same order value will be sorted
* arbitrarily adjacent to each other.
*
* <p>Any methods not annotated with {@code @Order} will be assigned a default
* order value of {@link Integer#MAX_VALUE} which will effectively cause them to
* appear at the end of the sorted list.
*/
class OrderAnnotation implements MethodOrderer {

/**
* Sort the methods encapsulated in the supplied
* {@link MethodOrdererContext} based on the {@link Order @Order}
* annotation.
*/
@Override
public void orderMethods(MethodOrdererContext context) {
context.getMethodDescriptors().sort(comparator);
Expand All @@ -104,10 +163,24 @@ private static Integer getOrder(MethodDescriptor descriptor) {
}

/**
* {@code MethodOrderer} that orders methods randomly and allows for concurrent
* execution by default.
* {@code MethodOrderer} that orders methods pseudo-randomly and allows for
* concurrent execution by default.
*
* <h4>Custom Seed</h4>
*
* <p>By default, the random <em>seed</em> used for ordering methods is the
* value returned by {@link System#nanoTime()}. In order to produce repeatable
* builds, a custom seed may be specified via the
* {@link Random#RANDOM_SEED_PROPERTY_NAME junit.jupiter.execution.order.random.seed}
* <em>configuration parameter</em> which can be supplied via the
* {@code Launcher} API, build tools (e.g., Gradle and Maven), a JVM system
* property, or the JUnit Platform configuration file (i.e., a file named
* {@code junit-platform.properties} in the root of the class path). Consult
* the User Guide for further information.
*
* @see #getDefaultExecutionMode()
* @see Random#RANDOM_SEED_PROPERTY_NAME
* @see java.util.Random
*/
class Random implements MethodOrderer {

Expand All @@ -122,10 +195,16 @@ class Random implements MethodOrderer {
* <p>Supported values include any string that can be converted to a
* {@link Long} via {@link Long#valueOf(String)}.
*
* <p>If not specified, {@link System#nanoTime()} will be used.
* <p>If not specified or if the specified value cannot be converted to
* a {@code Long}, {@link System#nanoTime()} will be used as the random
* seed.
*/
public static final String RANDOM_SEED_PROPERTY_NAME = "junit.jupiter.execution.order.random.seed";

/**
* Order the methods encapsulated in the supplied
* {@link MethodOrdererContext} pseudo-randomly.
*/
@Override
public void orderMethods(MethodOrdererContext context) {
Long seed = null;
Expand All @@ -152,8 +231,10 @@ public void orderMethods(MethodOrdererContext context) {
}

/**
* Returns {@link ExecutionMode#CONCURRENT CONCURRENT} to allow concurrent
* execution of randomly ordered methods by default.
* Get the <em>default</em> {@link ExecutionMode} for the test class.
*
* @return {@link ExecutionMode#CONCURRENT CONCURRENT} to allow concurrent
* execution of randomly ordered methods by default
*/
@Override
public ExecutionMode getDefaultExecutionMode() {
Expand Down
23 changes: 21 additions & 2 deletions junit-jupiter-api/src/main/java/org/junit/jupiter/api/Order.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

package org.junit.jupiter.api;

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

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
Expand All @@ -19,16 +19,35 @@
import java.lang.annotation.Target;

import org.apiguardian.api.API;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;

/**
* {@code @Order} is a method-level annotation that is used to configure the
* {@linkplain #value order} in which the annotated method should be executed
* relative to other methods of the same category.
*
* <p>When used with the {@link OrderAnnotation} {@link MethodOrderer}, the
* category applies to <em>test methods</em>.
*
* <p>If {@code @Order} is not explicitly declared on a method, the default
* order value assigned to the method is {@link Integer#MAX_VALUE}.
*
* @since 5.4
* @see MethodOrderer.OrderAnnotation
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = STABLE, since = "5.4")
@API(status = EXPERIMENTAL, since = "5.4")
public @interface Order {

/**
* The order value for the annotated method.
*
* <p>Methods are ordered based on priority where a lower value has greater
* priority than a higher value. For example, {@link Integer#MAX_VALUE} has
* the lowest priority.
*/
int value();

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,44 @@
import org.apiguardian.api.API;

/**
* {@code @TestMethodOrder} is a type-level annotation that is used to configure
* a {@link #value MethodOrderer} for the <em>test methods</em> of the annotated
* test class or test interface.
*
* <p>In this context, the term "test method" refers to any method annotated with
* {@code @Test}, {@code @RepeatedTest}, {@code @ParameterizedTest},
* {@code @TestFactory}, or {@code @TestTemplate}.
*
* <p>If {@code @TestMethodOrder} is not explicitly declared on a test class,
* inherited from a parent class, or declared on a test interface implemented by
* a test class, test methods will be ordered using a default algorithm that is
* deterministic but intentionally nonobvious.
*
* <h4>Example Usage</h4>
*
* <p>The following demonstrates how to guarantee that test methods are executed
* in the order specified via the {@link Order @Order} annotation.
*
* <pre class="code">
* {@literal @}TestMethodOrder(MethodOrderer.OrderAnnotation.class)
* class OrderedTests {
*
* {@literal @}Test
* {@literal @}Order(1)
* void nullValues() {}
*
* {@literal @}Test
* {@literal @}Order(2)
* void emptyValues() {}
*
* {@literal @}Test
* {@literal @}Order(3)
* void validValues() {}
* }
* </pre>
*
* @since 5.4
* @see MethodOrderer
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
Expand All @@ -31,6 +68,14 @@
@API(status = EXPERIMENTAL, since = "5.4")
public @interface TestMethodOrder {

/**
* The {@link MethodOrderer} to use.
*
* @see MethodOrderer
* @see MethodOrderer.Alphanumeric
* @see MethodOrderer.OrderAnnotation
* @see MethodOrderer.Random
*/
Class<? extends MethodOrderer> value();

}

0 comments on commit 68b1e6f

Please sign in to comment.