CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-junit-platform--junit-platform-engine

JUnit Platform Engine API - Core engine API for implementing custom test engines in the JUnit Platform ecosystem

Pending
Overview
Eval results
Files

hierarchical-engine.mddocs/

Hierarchical Engine Support

Framework for building hierarchical test engines with parallel execution, resource management, and structured execution contexts. This support package provides abstractions for engines that organize tests in tree structures with sophisticated execution control.

Capabilities

HierarchicalTestEngine

Abstract base class for implementing test engines with hierarchical test organization, providing execution orchestration and lifecycle management.

/**
 * Abstract base class for hierarchical test engines with tree-structured test organization.
 * @param C the execution context type extending EngineExecutionContext
 */
public abstract class HierarchicalTestEngine<C extends EngineExecutionContext> 
        implements TestEngine {
    
    /**
     * Final implementation of test execution using hierarchical framework.
     * @param request the execution request
     */
    @Override
    public final void execute(ExecutionRequest request);
    
    /**
     * Create the root execution context for test execution.
     * @param request the execution request
     * @return root execution context
     */
    protected abstract C createExecutionContext(ExecutionRequest request);
    
    /**
     * Create the executor service for managing test execution.
     * @param request the execution request
     * @return hierarchical test executor service
     */
    protected HierarchicalTestExecutorService createExecutorService(ExecutionRequest request);
    
    /**
     * Create the throwable collector factory for error handling.
     * @param request the execution request
     * @return throwable collector factory
     */
    protected ThrowableCollector.Factory createThrowableCollectorFactory(ExecutionRequest request);
}

Usage Example:

public class MyHierarchicalEngine extends HierarchicalTestEngine<MyExecutionContext> {
    
    @Override
    public String getId() {
        return "my-hierarchical-engine";
    }
    
    @Override
    public TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {
        // Discovery implementation
        return new MyEngineDescriptor(uniqueId);
    }
    
    @Override
    protected MyExecutionContext createExecutionContext(ExecutionRequest request) {
        return new MyExecutionContext(
            request.getConfigurationParameters(),
            request.getStore()
        );
    }
    
    @Override
    protected HierarchicalTestExecutorService createExecutorService(ExecutionRequest request) {
        boolean parallelExecution = request.getConfigurationParameters()
            .getBoolean("parallel.enabled").orElse(false);
            
        if (parallelExecution) {
            return new ForkJoinPoolHierarchicalTestExecutorService();
        } else {
            return new SameThreadHierarchicalTestExecutorService();
        }
    }
}

Node Interface

Core interface representing an individual node in the hierarchical test execution tree, with lifecycle methods and execution control.

/**
 * Interface for nodes in hierarchical test execution, providing lifecycle methods and execution control.
 * @param C the execution context type
 */
public interface Node<C extends EngineExecutionContext> {
    
    /**
     * Prepare execution context before test execution begins.
     * @param context the current execution context
     * @return updated execution context
     * @throws Exception if preparation fails
     */
    default C prepare(C context) throws Exception {
        return context;
    }
    
    /**
     * Determine if this node should be skipped during execution.
     * @param context the current execution context
     * @return skip result indicating whether to skip and reason
     * @throws Exception if skip determination fails
     */
    default SkipResult shouldBeSkipped(C context) throws Exception {
        return SkipResult.doNotSkip();
    }
    
    /**
     * Execute before behavior (setup) for this node.
     * @param context the current execution context
     * @return updated execution context
     * @throws Exception if before behavior fails
     */
    default C before(C context) throws Exception {
        return context;
    }
    
    /**
     * Execute the main behavior of this node.
     * @param context the current execution context
     * @param dynamicTestExecutor executor for registering dynamic tests
     * @return updated execution context
     * @throws Exception if execution fails
     */
    default C execute(C context, DynamicTestExecutor dynamicTestExecutor) throws Exception {
        return context;
    }
    
    /**
     * Execute after behavior (teardown) for this node.
     * @param context the current execution context
     * @throws Exception if after behavior fails
     */
    default void after(C context) throws Exception {
        // Default: no after behavior
    }
    
    /**
     * Clean up resources after execution completes.
     * @param context the current execution context
     * @throws Exception if cleanup fails
     */
    default void cleanUp(C context) throws Exception {
        // Default: no cleanup
    }
    
    /**
     * Wrap around the execution of child nodes.
     * @param context the current execution context
     * @param invocation the child execution invocation to wrap
     * @throws Exception if around behavior fails
     */
    default void around(C context, Invocation<C> invocation) throws Exception {
        invocation.proceed(context);
    }
    
    /**
     * Get exclusive resources required by this node.
     * @return set of exclusive resources
     */
    default Set<ExclusiveResource> getExclusiveResources() {
        return Collections.emptySet();
    }
    
    /**
     * Get the preferred execution mode for this node.
     * @return execution mode (SAME_THREAD or CONCURRENT)
     */
    default ExecutionMode getExecutionMode() {
        return ExecutionMode.SAME_THREAD;
    }
    
    /**
     * Result of skip determination.
     */
    class SkipResult {
        public static SkipResult skip(String reason);
        public static SkipResult doNotSkip();
        
        public boolean isSkipped();
        public Optional<String> getReason();
    }
    
    /**
     * Execution mode for nodes.
     */
    enum ExecutionMode {
        /** Execute in same thread as parent */
        SAME_THREAD,
        /** Execute concurrently with siblings */
        CONCURRENT
    }
    
    /**
     * Interface for registering dynamic tests during execution.
     */
    interface DynamicTestExecutor {
        void execute(TestDescriptor testDescriptor);
    }
    
    /**
     * Represents an execution invocation that can be wrapped.
     * @param C the execution context type
     */
    interface Invocation<C> {
        C proceed(C context) throws Exception;
    }
}

Usage Example:

public class MyTestNode implements Node<MyExecutionContext> {
    private final Method testMethod;
    private final Object testInstance;
    
    @Override
    public SkipResult shouldBeSkipped(MyExecutionContext context) throws Exception {
        if (testMethod.isAnnotationPresent(Disabled.class)) {
            return SkipResult.skip("Test is disabled");
        }
        return SkipResult.doNotSkip();
    }
    
    @Override
    public MyExecutionContext before(MyExecutionContext context) throws Exception {
        // Set up test instance
        return context.withTestInstance(createTestInstance());
    }
    
    @Override
    public MyExecutionContext execute(MyExecutionContext context, 
                                    DynamicTestExecutor dynamicTestExecutor) throws Exception {
        // Execute test method
        testMethod.invoke(context.getTestInstance());
        
        // Register dynamic tests if needed
        if (isDynamicTestContainer()) {
            generateDynamicTests().forEach(dynamicTestExecutor::execute);
        }
        
        return context;
    }
    
    @Override
    public void after(MyExecutionContext context) throws Exception {
        // Clean up test instance resources
        cleanupTestInstance(context.getTestInstance());
    }
    
    @Override
    public Set<ExclusiveResource> getExclusiveResources() {
        return Set.of(ExclusiveResource.SYSTEM_PROPERTIES);
    }
    
    @Override
    public ExecutionMode getExecutionMode() {
        return testMethod.isAnnotationPresent(Parallel.class) 
            ? ExecutionMode.CONCURRENT 
            : ExecutionMode.SAME_THREAD;
    }
}

Execution Context

Base interface for execution contexts that carry state through the hierarchical execution process.

/**
 * Base interface for execution contexts in hierarchical test engines.
 */
public interface EngineExecutionContext {
    /**
     * Get the configuration parameters for execution.
     * @return configuration parameters
     */
    ConfigurationParameters getConfigurationParameters();
    
    /**
     * Get the engine execution listener.
     * @return execution listener
     */
    EngineExecutionListener getEngineExecutionListener();
    
    /**
     * Get the hierarchical store for this context level.
     * @return namespaced hierarchical store
     */
    NamespacedHierarchicalStore<Namespace> getStore();
}

Executor Services

Services for managing the execution of hierarchical test structures with support for parallel and sequential execution.

/**
 * Service for executing hierarchical test structures.
 */
public interface HierarchicalTestExecutorService {
    /**
     * Submit a test task for execution.
     * @param task the test task to execute
     * @return future representing the task execution
     */
    Future<?> submit(TestTask task);
    
    /**
     * Invoke all tasks and wait for completion.
     * @param tasks the tasks to execute
     * @throws InterruptedException if interrupted while waiting
     */
    void invokeAll(Collection<? extends TestTask> tasks) throws InterruptedException;
    
    /**
     * Close the executor service and clean up resources.
     */
    void close();
}

/**
 * Same-thread implementation of hierarchical test executor service.
 */
public class SameThreadHierarchicalTestExecutorService implements HierarchicalTestExecutorService {
    // Executes all tasks sequentially in the current thread
}

Resource Management

System for managing exclusive resources and preventing conflicts between concurrent test execution.

/**
 * Represents an exclusive resource that tests may require.
 */
public class ExclusiveResource {
    /**
     * Create an exclusive resource with a key.
     * @param key the resource key
     * @return exclusive resource
     */
    public static ExclusiveResource from(String key);
    
    /**
     * Get the resource key.
     * @return resource key
     */
    public String getKey();
    
    // Predefined common resources
    public static final ExclusiveResource SYSTEM_PROPERTIES;
    public static final ExclusiveResource SYSTEM_OUT;
    public static final ExclusiveResource SYSTEM_ERR;
    public static final ExclusiveResource LOCALE;
    public static final ExclusiveResource TIME_ZONE;
}

/**
 * Interface for resource locks.
 */
public interface ResourceLock {
    /**
     * Acquire the lock.
     * @throws InterruptedException if interrupted while acquiring
     */
    void acquire() throws InterruptedException;
    
    /**
     * Release the lock.
     */
    void release();
}

/**
 * No-operation lock implementation.
 */
public class NopLock implements ResourceLock {
    public static final NopLock INSTANCE = new NopLock();
    
    @Override
    public void acquire() { /* no-op */ }
    
    @Override
    public void release() { /* no-op */ }
}

/**
 * Lock for a single exclusive resource.
 */
public class SingleLock implements ResourceLock {
    public SingleLock(ExclusiveResource resource);
    // Implementation details...
}

Parallel Execution Configuration

Configuration system for controlling parallel test execution behavior.

/**
 * Configuration for parallel test execution.
 */
public interface ParallelExecutionConfiguration {
    /**
     * Get the parallelism level.
     * @return number of parallel threads
     */
    int getParallelism();
    
    /**
     * Get the minimum runnable count.
     * @return minimum runnable tasks to maintain
     */
    int getMinimumRunnable();
    
    /**
     * Get the maximum pool size.
     * @return maximum thread pool size
     */
    int getMaxPoolSize();
    
    /**
     * Get the core pool size.
     * @return core thread pool size
     */
    int getCorePoolSize();
    
    /**
     * Get the keep alive time in seconds.
     * @return keep alive time for idle threads
     */
    int getKeepAliveSeconds();
}

/**
 * Strategy for creating parallel execution configuration.
 */
public interface ParallelExecutionConfigurationStrategy {
    /**
     * Create configuration from configuration parameters.
     * @param configurationParameters the configuration parameters
     * @return parallel execution configuration
     */
    ParallelExecutionConfiguration createConfiguration(ConfigurationParameters configurationParameters);
}

/**
 * Default implementation of parallel execution configuration.
 */
public class DefaultParallelExecutionConfiguration implements ParallelExecutionConfiguration {
    public DefaultParallelExecutionConfiguration(ConfigurationParameters configurationParameters);
    // Implementation details...
}

Error Handling

Sophisticated error collection and handling for hierarchical test execution.

/**
 * Collector for gathering and managing throwables during test execution.
 */
public class ThrowableCollector {
    /**
     * Factory for creating throwable collectors.
     */
    public interface Factory {
        ThrowableCollector create();
    }
    
    /**
     * Execute code and collect any throwables.
     * @param executable the code to execute
     */
    public void execute(Executable executable);
    
    /**
     * Add a throwable to the collection.
     * @param throwable the throwable to add
     */
    public void add(Throwable throwable);
    
    /**
     * Get the first throwable in the collection.
     * @return optional first throwable
     */
    public Optional<Throwable> getThrowable();
    
    /**
     * Check if any throwables have been collected.
     * @return true if throwables exist
     */
    public boolean isEmpty();
    
    /**
     * Assert that no throwables have been collected.
     * @throws Exception if throwables exist
     */
    public void assertEmpty() throws Exception;
    
    /**
     * Executable interface for code that may throw exceptions.
     */
    @FunctionalInterface
    public interface Executable {
        void execute() throws Throwable;
    }
}

/**
 * OpenTest4J-aware throwable collector with enhanced assertion handling.
 */
public class OpenTest4JAwareThrowableCollector extends ThrowableCollector {
    // Enhanced handling for OpenTest4J assertion failures
}

Usage Example:

public class MyContainerNode implements Node<MyExecutionContext> {
    
    @Override
    public MyExecutionContext execute(MyExecutionContext context, 
                                    DynamicTestExecutor dynamicTestExecutor) throws Exception {
        ThrowableCollector collector = context.getThrowableCollector();
        
        // Execute multiple child operations, collecting errors
        collector.execute(() -> setupDatabase());
        collector.execute(() -> loadTestData());
        collector.execute(() -> configureSystem());
        
        // Assert that all setup completed successfully
        collector.assertEmpty();
        
        return context;
    }
    
    @Override
    public Set<ExclusiveResource> getExclusiveResources() {
        return Set.of(
            ExclusiveResource.from("database"),
            ExclusiveResource.SYSTEM_PROPERTIES
        );
    }
    
    @Override
    public ExecutionMode getExecutionMode() {
        return ExecutionMode.CONCURRENT;
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-junit-platform--junit-platform-engine

docs

config-storage.md

engine-implementation.md

hierarchical-engine.md

index.md

test-descriptors.md

test-discovery.md

test-execution.md

tile.json