/* * Copyright 2015-2017 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 v1.0 which * accompanies this distribution and is available at * * http://www.eclipse.org/legal/epl-v10.html */ package org.junit.platform.engine.support.hierarchical; import static org.junit.platform.commons.meta.API.Usage.Experimental; import java.util.Optional; import org.junit.platform.commons.meta.API; import org.junit.platform.commons.util.ToStringBuilder; import org.junit.platform.engine.TestDescriptor; /** * A <em>node</em> within the execution hierarchy. * * @param <C> the type of {@code EngineExecutionContext} used by the * {@code HierarchicalTestEngine} * @since 1.0 * @see HierarchicalTestEngine */ @API(Experimental) public interface Node<C extends EngineExecutionContext> { /** * Prepare the supplied {@code context} prior to execution. * * <p>The default implementation returns the supplied {@code context} unmodified. */ default C prepare(C context) throws Exception { return context; } /** * Determine if the execution of the supplied {@code context} should be * <em>skipped</em>. * * <p>The default implementation returns {@link SkipResult#doNotSkip()}. */ default SkipResult shouldBeSkipped(C context) throws Exception { return SkipResult.doNotSkip(); } /** * Execute the <em>before</em> behavior of this node. * * <p>This method will be called once <em>before</em> {@linkplain #execute * execution} of this node. * * @param context the context to execute in * @return the new context to be used for children of this node * * @see #execute * @see #after */ default C before(C context) throws Exception { return context; } /** * Execute the <em>behavior</em> of this node. * * <p>Containers typically do not implement this method since the * {@link HierarchicalTestEngine} handles execution of their children. * * <p>The supplied {@code dynamicTestExecutor} may be used to submit * additional dynamic tests for immediate execution. * * @param context the context to execute in * @param dynamicTestExecutor the executor to submit dynamic tests to * @return the new context to be used for children of this node and for the * <em>after</em> behavior of the parent of this node, if any * * @see #before * @see #after */ default C execute(C context, DynamicTestExecutor dynamicTestExecutor) throws Exception { return context; } /** * Execute the <em>after</em> behavior of this node. * * <p>This method will be called once <em>after</em> {@linkplain #execute * execution} of this node. * * @param context the context to execute in * * @see #before * @see #execute */ default void after(C context) throws Exception { } /** * The result of determining whether the execution of a given {@code context} * should be <em>skipped</em>. * * @see Node#shouldBeSkipped(EngineExecutionContext) */ class SkipResult { private static final SkipResult alwaysExecuteSkipResult = new SkipResult(false, null); private final boolean skipped; private final Optional<String> reason; /** * Factory for creating <em>skipped</em> results. * * <p>A context that is skipped will be not be executed. * * @param reason the reason why the context should be skipped * @return a skipped {@code SkipResult} with the given reason */ public static SkipResult skip(String reason) { return new SkipResult(true, reason); } /** * Factory for creating <em>do not skip</em> results. * * <p>A context that is not skipped will be executed as normal. * * @return a <em>do not skip</em> {@code SkipResult} */ public static SkipResult doNotSkip() { return alwaysExecuteSkipResult; } private SkipResult(boolean skipped, String reason) { this.skipped = skipped; this.reason = Optional.ofNullable(reason); } /** * Whether execution of the context should be skipped. * * @return {@code true} if the execution should be skipped */ public boolean isSkipped() { return this.skipped; } /** * Get the reason why execution of the context should be skipped, * if available. */ public Optional<String> getReason() { return this.reason; } @Override public String toString() { // @formatter:off return new ToStringBuilder(this) .append("skipped", this.skipped) .append("reason", this.reason.orElse("<unknown>")) .toString(); // @formatter:on } } /** * Executor for additional, dynamic test descriptors discovered during * execution of a {@link Node}. * * <p>The test descriptors will be executed by the same * {@link HierarchicalTestExecutor} that executes the submitting node. * * <p>This interface is not intended to be implemented by clients. * * @see Node#execute(EngineExecutionContext, DynamicTestExecutor) * @see HierarchicalTestExecutor */ interface DynamicTestExecutor { /** * Submit a dynamic test descriptor for immediate execution. * * @param testDescriptor the test descriptor to be executed */ void execute(TestDescriptor testDescriptor); } }