/*
 * Copyright 2015-2018 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
 *
 * http://www.eclipse.org/legal/epl-v20.html
 */

package org.junit.jupiter.api;

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

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.apiguardian.api.API;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.junit.platform.commons.util.AnnotationUtils;
import org.junit.platform.commons.util.ClassUtils;

/**
 * @since 5.4
 */
@API(status = EXPERIMENTAL, since = "5.4")
public interface MethodOrderer {

	void orderMethods(List<? extends MethodDescriptor> methodDescriptors);

	/**
	 * Get the <em>default</em> {@link ExecutionMode} for the annotated class.
	 *
	 * <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.
	 *
	 * @return the default {@code ExecutionMode}; never {@code null}
	 */
	default ExecutionMode getDefaultExecutionMode() {
		return ExecutionMode.SAME_THREAD;
	}

	interface MethodDescriptor {

		Class<?> getTestClass();

		Method getTestMethod();

	}

	/**
	 * {@code MethodOrderer} that sorts methods alphanumerically based on their
	 * names using {@link String#compareTo(String)}.
	 *
	 * <p>If two methods have the same name, the combinations of their names and
	 * formal parameter lists will be used as a fallback for comparing them.
	 */
	class Alphanumeric implements MethodOrderer {

		@Override
		public void orderMethods(List<? extends MethodDescriptor> methodDescriptors) {
			methodDescriptors.sort(comparator);
		}

		private static final Comparator<MethodDescriptor> comparator = (descriptor1, descriptor2) -> {
			Method method1 = descriptor1.getTestMethod();
			Method method2 = descriptor2.getTestMethod();

			int result = method1.getName().compareTo(method2.getName());
			if (result != 0) {
				return result;
			}

			// else
			return signature(method1).compareTo(signature(method2));
		};

		private static String signature(Method method) {
			// We intentionally do not use ReflectionUtils.getFullyQualifiedMethodName()
			// here since we want to ignore the class name.
			return String.format("%s(%s)", method.getName(), ClassUtils.nullSafeToString(method.getParameterTypes()));
		}
	}

	/**
	 * {@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.
	 */
	class OrderAnnotation implements MethodOrderer {

		@Override
		public void orderMethods(List<? extends MethodDescriptor> methodDescriptors) {
			methodDescriptors.sort(comparator);
		}

		private static final Comparator<MethodDescriptor> comparator = (descriptor1, descriptor2) -> {
			return Integer.compare(getOrder(descriptor1), getOrder(descriptor2));
		};

		private static Integer getOrder(MethodDescriptor descriptor) {
			return AnnotationUtils.findAnnotation(descriptor.getTestMethod(), Order.class)//
					.map(Order::value)//
					.orElse(Integer.MAX_VALUE);
		}
	}

	/**
	 * {@code MethodOrderer} that orders methods randomly and allows for concurrent
	 * execution by default.
	 *
	 * @see #getDefaultExecutionMode()
	 */
	class Random implements MethodOrderer {

		@Override
		public void orderMethods(List<? extends MethodDescriptor> methodDescriptors) {
			Collections.shuffle(methodDescriptors);
		}

		/**
		 * Returns {@link ExecutionMode#CONCURRENT CONCURRENT} to allow concurrent
		 * execution of randomly ordered methods by default.
		 */
		@Override
		public ExecutionMode getDefaultExecutionMode() {
			return ExecutionMode.CONCURRENT;
		}
	}

}

@@ -1702,47 +1702,52 @@
 me, 
-the combin
+a %7B@code String%7D represent
 ation
-s
  of
+%0A%09 *
  their
- names and%0A%09 *
  for
@@ -1810,17 +1810,28 @@
 ring the
-m
+%0A%09 * methods
 .%0A%09 */%0A%09
@@ -2342,25 +2342,29 @@
 %09return 
-signature
+parameterList
 (method1
@@ -2375,25 +2375,29 @@
 mpareTo(
-signature
+parameterList
 (method2
@@ -2434,217 +2434,48 @@
 ing 
-signature(Method method) %7B%0A%09%09%09// We intentionally do not use ReflectionUtils.getFullyQualifiedMethodName()%0A%09%09%09// here since we want to ignore the class name.%0A%09%09%09return String.format(%22%25s(%25s)%22, method.getName(),
+parameterList(Method method) %7B%0A%09%09%09return
  Cla
@@ -2526,17 +2526,16 @@
 Types())
-)
 ;%0A%09%09%7D%0A%09%7D

Use method parameter list as fallback in Alphanumeric