package org.apereo.cas.util.junit;

import org.apereo.cas.util.SocketUtils;

import lombok.val;
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.platform.commons.support.AnnotationSupport;

import java.lang.reflect.AnnotatedElement;
import java.util.Arrays;

/**
 * This is {@link EnabledIfPortOpenCondition}.
 *
 * @author Timur Duehr
 * @since 6.1.0
 */
public class EnabledIfPortOpenCondition implements ExecutionCondition {
    private static final ConditionEvaluationResult ENABLED_BY_DEFAULT = ConditionEvaluationResult.enabled("@EnabledIfPortOpen is not present");

    private static ConditionEvaluationResult enableIfOpen(final EnabledIfPortOpen annotation, final AnnotatedElement element) {
        val ports = annotation.port();
        if (ports.length == 0) {
            throw new IllegalArgumentException("At least one port must be defined");
        }
        for (val port : ports) {
            if (port > 0 && SocketUtils.isTcpPortAvailable(port)) {
                return ConditionEvaluationResult.disabled(String.format("%s is disabled because %s is not in use", element, port));
            }
        }
        return ConditionEvaluationResult.enabled(
            String.format("%s is enabled because all ports (%s) is open.", element, Arrays.toString(ports)));
    }

    @Override
    public ConditionEvaluationResult evaluateExecutionCondition(final ExtensionContext extensionContext) {
        val element = extensionContext
            .getElement()
            .orElseThrow(IllegalStateException::new);
        return AnnotationSupport.findAnnotation(element, EnabledIfPortOpen.class)
            .map(annotation -> enableIfOpen(annotation, element))
            .orElse(ENABLED_BY_DEFAULT);
    }
}

@@ -714,16 +714,89 @@
 esent%22);
+%0A    private static final String IGNORE_PORT_CHECK = %22IGNORE_PORT_CHECK%22;
 %0A%0A    pr
@@ -1076,32 +1076,249 @@
 ed%22);%0A        %7D%0A
+        if (ignorePortCheck()) %7B%0A            return ConditionEvaluationResult.enabled(%0A                    String.format(%22%25s is enabled because %25s environment variable is set%22, element, IGNORE_PORT_CHECK));%0A        %7D%0A
         for (val
@@ -2113,16 +2113,16 @@
 ement))%0A
-
         
@@ -2159,11 +2159,376 @@
 );%0A    %7D
+%0A%0A    /**%0A     * The available port check doesn't always work on Windows so this provides a bypass mechanism.%0A     * @return true if IGNORE_PORT_CHECK environment variable equals true%0A     */%0A    private static boolean ignorePortCheck() %7B%0A        val ignorePortCheck = System.getenv(IGNORE_PORT_CHECK);%0A        return %22true%22.equalsIgnoreCase(ignorePortCheck);%0A    %7D
 %0A%7D%0A

Add a way to bypass unit test port available check (#4847)