0
# Hierarchical Engine Support
1
2
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.
3
4
## Capabilities
5
6
### HierarchicalTestEngine
7
8
Abstract base class for implementing test engines with hierarchical test organization, providing execution orchestration and lifecycle management.
9
10
```java { .api }
11
/**
12
* Abstract base class for hierarchical test engines with tree-structured test organization.
13
* @param C the execution context type extending EngineExecutionContext
14
*/
15
public abstract class HierarchicalTestEngine<C extends EngineExecutionContext>
16
implements TestEngine {
17
18
/**
19
* Final implementation of test execution using hierarchical framework.
20
* @param request the execution request
21
*/
22
@Override
23
public final void execute(ExecutionRequest request);
24
25
/**
26
* Create the root execution context for test execution.
27
* @param request the execution request
28
* @return root execution context
29
*/
30
protected abstract C createExecutionContext(ExecutionRequest request);
31
32
/**
33
* Create the executor service for managing test execution.
34
* @param request the execution request
35
* @return hierarchical test executor service
36
*/
37
protected HierarchicalTestExecutorService createExecutorService(ExecutionRequest request);
38
39
/**
40
* Create the throwable collector factory for error handling.
41
* @param request the execution request
42
* @return throwable collector factory
43
*/
44
protected ThrowableCollector.Factory createThrowableCollectorFactory(ExecutionRequest request);
45
}
46
```
47
48
**Usage Example:**
49
50
```java
51
public class MyHierarchicalEngine extends HierarchicalTestEngine<MyExecutionContext> {
52
53
@Override
54
public String getId() {
55
return "my-hierarchical-engine";
56
}
57
58
@Override
59
public TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {
60
// Discovery implementation
61
return new MyEngineDescriptor(uniqueId);
62
}
63
64
@Override
65
protected MyExecutionContext createExecutionContext(ExecutionRequest request) {
66
return new MyExecutionContext(
67
request.getConfigurationParameters(),
68
request.getStore()
69
);
70
}
71
72
@Override
73
protected HierarchicalTestExecutorService createExecutorService(ExecutionRequest request) {
74
boolean parallelExecution = request.getConfigurationParameters()
75
.getBoolean("parallel.enabled").orElse(false);
76
77
if (parallelExecution) {
78
return new ForkJoinPoolHierarchicalTestExecutorService();
79
} else {
80
return new SameThreadHierarchicalTestExecutorService();
81
}
82
}
83
}
84
```
85
86
### Node Interface
87
88
Core interface representing an individual node in the hierarchical test execution tree, with lifecycle methods and execution control.
89
90
```java { .api }
91
/**
92
* Interface for nodes in hierarchical test execution, providing lifecycle methods and execution control.
93
* @param C the execution context type
94
*/
95
public interface Node<C extends EngineExecutionContext> {
96
97
/**
98
* Prepare execution context before test execution begins.
99
* @param context the current execution context
100
* @return updated execution context
101
* @throws Exception if preparation fails
102
*/
103
default C prepare(C context) throws Exception {
104
return context;
105
}
106
107
/**
108
* Determine if this node should be skipped during execution.
109
* @param context the current execution context
110
* @return skip result indicating whether to skip and reason
111
* @throws Exception if skip determination fails
112
*/
113
default SkipResult shouldBeSkipped(C context) throws Exception {
114
return SkipResult.doNotSkip();
115
}
116
117
/**
118
* Execute before behavior (setup) for this node.
119
* @param context the current execution context
120
* @return updated execution context
121
* @throws Exception if before behavior fails
122
*/
123
default C before(C context) throws Exception {
124
return context;
125
}
126
127
/**
128
* Execute the main behavior of this node.
129
* @param context the current execution context
130
* @param dynamicTestExecutor executor for registering dynamic tests
131
* @return updated execution context
132
* @throws Exception if execution fails
133
*/
134
default C execute(C context, DynamicTestExecutor dynamicTestExecutor) throws Exception {
135
return context;
136
}
137
138
/**
139
* Execute after behavior (teardown) for this node.
140
* @param context the current execution context
141
* @throws Exception if after behavior fails
142
*/
143
default void after(C context) throws Exception {
144
// Default: no after behavior
145
}
146
147
/**
148
* Clean up resources after execution completes.
149
* @param context the current execution context
150
* @throws Exception if cleanup fails
151
*/
152
default void cleanUp(C context) throws Exception {
153
// Default: no cleanup
154
}
155
156
/**
157
* Wrap around the execution of child nodes.
158
* @param context the current execution context
159
* @param invocation the child execution invocation to wrap
160
* @throws Exception if around behavior fails
161
*/
162
default void around(C context, Invocation<C> invocation) throws Exception {
163
invocation.proceed(context);
164
}
165
166
/**
167
* Get exclusive resources required by this node.
168
* @return set of exclusive resources
169
*/
170
default Set<ExclusiveResource> getExclusiveResources() {
171
return Collections.emptySet();
172
}
173
174
/**
175
* Get the preferred execution mode for this node.
176
* @return execution mode (SAME_THREAD or CONCURRENT)
177
*/
178
default ExecutionMode getExecutionMode() {
179
return ExecutionMode.SAME_THREAD;
180
}
181
182
/**
183
* Result of skip determination.
184
*/
185
class SkipResult {
186
public static SkipResult skip(String reason);
187
public static SkipResult doNotSkip();
188
189
public boolean isSkipped();
190
public Optional<String> getReason();
191
}
192
193
/**
194
* Execution mode for nodes.
195
*/
196
enum ExecutionMode {
197
/** Execute in same thread as parent */
198
SAME_THREAD,
199
/** Execute concurrently with siblings */
200
CONCURRENT
201
}
202
203
/**
204
* Interface for registering dynamic tests during execution.
205
*/
206
interface DynamicTestExecutor {
207
void execute(TestDescriptor testDescriptor);
208
}
209
210
/**
211
* Represents an execution invocation that can be wrapped.
212
* @param C the execution context type
213
*/
214
interface Invocation<C> {
215
C proceed(C context) throws Exception;
216
}
217
}
218
```
219
220
**Usage Example:**
221
222
```java
223
public class MyTestNode implements Node<MyExecutionContext> {
224
private final Method testMethod;
225
private final Object testInstance;
226
227
@Override
228
public SkipResult shouldBeSkipped(MyExecutionContext context) throws Exception {
229
if (testMethod.isAnnotationPresent(Disabled.class)) {
230
return SkipResult.skip("Test is disabled");
231
}
232
return SkipResult.doNotSkip();
233
}
234
235
@Override
236
public MyExecutionContext before(MyExecutionContext context) throws Exception {
237
// Set up test instance
238
return context.withTestInstance(createTestInstance());
239
}
240
241
@Override
242
public MyExecutionContext execute(MyExecutionContext context,
243
DynamicTestExecutor dynamicTestExecutor) throws Exception {
244
// Execute test method
245
testMethod.invoke(context.getTestInstance());
246
247
// Register dynamic tests if needed
248
if (isDynamicTestContainer()) {
249
generateDynamicTests().forEach(dynamicTestExecutor::execute);
250
}
251
252
return context;
253
}
254
255
@Override
256
public void after(MyExecutionContext context) throws Exception {
257
// Clean up test instance resources
258
cleanupTestInstance(context.getTestInstance());
259
}
260
261
@Override
262
public Set<ExclusiveResource> getExclusiveResources() {
263
return Set.of(ExclusiveResource.SYSTEM_PROPERTIES);
264
}
265
266
@Override
267
public ExecutionMode getExecutionMode() {
268
return testMethod.isAnnotationPresent(Parallel.class)
269
? ExecutionMode.CONCURRENT
270
: ExecutionMode.SAME_THREAD;
271
}
272
}
273
```
274
275
### Execution Context
276
277
Base interface for execution contexts that carry state through the hierarchical execution process.
278
279
```java { .api }
280
/**
281
* Base interface for execution contexts in hierarchical test engines.
282
*/
283
public interface EngineExecutionContext {
284
/**
285
* Get the configuration parameters for execution.
286
* @return configuration parameters
287
*/
288
ConfigurationParameters getConfigurationParameters();
289
290
/**
291
* Get the engine execution listener.
292
* @return execution listener
293
*/
294
EngineExecutionListener getEngineExecutionListener();
295
296
/**
297
* Get the hierarchical store for this context level.
298
* @return namespaced hierarchical store
299
*/
300
NamespacedHierarchicalStore<Namespace> getStore();
301
}
302
```
303
304
### Executor Services
305
306
Services for managing the execution of hierarchical test structures with support for parallel and sequential execution.
307
308
```java { .api }
309
/**
310
* Service for executing hierarchical test structures.
311
*/
312
public interface HierarchicalTestExecutorService {
313
/**
314
* Submit a test task for execution.
315
* @param task the test task to execute
316
* @return future representing the task execution
317
*/
318
Future<?> submit(TestTask task);
319
320
/**
321
* Invoke all tasks and wait for completion.
322
* @param tasks the tasks to execute
323
* @throws InterruptedException if interrupted while waiting
324
*/
325
void invokeAll(Collection<? extends TestTask> tasks) throws InterruptedException;
326
327
/**
328
* Close the executor service and clean up resources.
329
*/
330
void close();
331
}
332
333
/**
334
* Same-thread implementation of hierarchical test executor service.
335
*/
336
public class SameThreadHierarchicalTestExecutorService implements HierarchicalTestExecutorService {
337
// Executes all tasks sequentially in the current thread
338
}
339
```
340
341
### Resource Management
342
343
System for managing exclusive resources and preventing conflicts between concurrent test execution.
344
345
```java { .api }
346
/**
347
* Represents an exclusive resource that tests may require.
348
*/
349
public class ExclusiveResource {
350
/**
351
* Create an exclusive resource with a key.
352
* @param key the resource key
353
* @return exclusive resource
354
*/
355
public static ExclusiveResource from(String key);
356
357
/**
358
* Get the resource key.
359
* @return resource key
360
*/
361
public String getKey();
362
363
// Predefined common resources
364
public static final ExclusiveResource SYSTEM_PROPERTIES;
365
public static final ExclusiveResource SYSTEM_OUT;
366
public static final ExclusiveResource SYSTEM_ERR;
367
public static final ExclusiveResource LOCALE;
368
public static final ExclusiveResource TIME_ZONE;
369
}
370
371
/**
372
* Interface for resource locks.
373
*/
374
public interface ResourceLock {
375
/**
376
* Acquire the lock.
377
* @throws InterruptedException if interrupted while acquiring
378
*/
379
void acquire() throws InterruptedException;
380
381
/**
382
* Release the lock.
383
*/
384
void release();
385
}
386
387
/**
388
* No-operation lock implementation.
389
*/
390
public class NopLock implements ResourceLock {
391
public static final NopLock INSTANCE = new NopLock();
392
393
@Override
394
public void acquire() { /* no-op */ }
395
396
@Override
397
public void release() { /* no-op */ }
398
}
399
400
/**
401
* Lock for a single exclusive resource.
402
*/
403
public class SingleLock implements ResourceLock {
404
public SingleLock(ExclusiveResource resource);
405
// Implementation details...
406
}
407
```
408
409
### Parallel Execution Configuration
410
411
Configuration system for controlling parallel test execution behavior.
412
413
```java { .api }
414
/**
415
* Configuration for parallel test execution.
416
*/
417
public interface ParallelExecutionConfiguration {
418
/**
419
* Get the parallelism level.
420
* @return number of parallel threads
421
*/
422
int getParallelism();
423
424
/**
425
* Get the minimum runnable count.
426
* @return minimum runnable tasks to maintain
427
*/
428
int getMinimumRunnable();
429
430
/**
431
* Get the maximum pool size.
432
* @return maximum thread pool size
433
*/
434
int getMaxPoolSize();
435
436
/**
437
* Get the core pool size.
438
* @return core thread pool size
439
*/
440
int getCorePoolSize();
441
442
/**
443
* Get the keep alive time in seconds.
444
* @return keep alive time for idle threads
445
*/
446
int getKeepAliveSeconds();
447
}
448
449
/**
450
* Strategy for creating parallel execution configuration.
451
*/
452
public interface ParallelExecutionConfigurationStrategy {
453
/**
454
* Create configuration from configuration parameters.
455
* @param configurationParameters the configuration parameters
456
* @return parallel execution configuration
457
*/
458
ParallelExecutionConfiguration createConfiguration(ConfigurationParameters configurationParameters);
459
}
460
461
/**
462
* Default implementation of parallel execution configuration.
463
*/
464
public class DefaultParallelExecutionConfiguration implements ParallelExecutionConfiguration {
465
public DefaultParallelExecutionConfiguration(ConfigurationParameters configurationParameters);
466
// Implementation details...
467
}
468
```
469
470
### Error Handling
471
472
Sophisticated error collection and handling for hierarchical test execution.
473
474
```java { .api }
475
/**
476
* Collector for gathering and managing throwables during test execution.
477
*/
478
public class ThrowableCollector {
479
/**
480
* Factory for creating throwable collectors.
481
*/
482
public interface Factory {
483
ThrowableCollector create();
484
}
485
486
/**
487
* Execute code and collect any throwables.
488
* @param executable the code to execute
489
*/
490
public void execute(Executable executable);
491
492
/**
493
* Add a throwable to the collection.
494
* @param throwable the throwable to add
495
*/
496
public void add(Throwable throwable);
497
498
/**
499
* Get the first throwable in the collection.
500
* @return optional first throwable
501
*/
502
public Optional<Throwable> getThrowable();
503
504
/**
505
* Check if any throwables have been collected.
506
* @return true if throwables exist
507
*/
508
public boolean isEmpty();
509
510
/**
511
* Assert that no throwables have been collected.
512
* @throws Exception if throwables exist
513
*/
514
public void assertEmpty() throws Exception;
515
516
/**
517
* Executable interface for code that may throw exceptions.
518
*/
519
@FunctionalInterface
520
public interface Executable {
521
void execute() throws Throwable;
522
}
523
}
524
525
/**
526
* OpenTest4J-aware throwable collector with enhanced assertion handling.
527
*/
528
public class OpenTest4JAwareThrowableCollector extends ThrowableCollector {
529
// Enhanced handling for OpenTest4J assertion failures
530
}
531
```
532
533
**Usage Example:**
534
535
```java
536
public class MyContainerNode implements Node<MyExecutionContext> {
537
538
@Override
539
public MyExecutionContext execute(MyExecutionContext context,
540
DynamicTestExecutor dynamicTestExecutor) throws Exception {
541
ThrowableCollector collector = context.getThrowableCollector();
542
543
// Execute multiple child operations, collecting errors
544
collector.execute(() -> setupDatabase());
545
collector.execute(() -> loadTestData());
546
collector.execute(() -> configureSystem());
547
548
// Assert that all setup completed successfully
549
collector.assertEmpty();
550
551
return context;
552
}
553
554
@Override
555
public Set<ExclusiveResource> getExclusiveResources() {
556
return Set.of(
557
ExclusiveResource.from("database"),
558
ExclusiveResource.SYSTEM_PROPERTIES
559
);
560
}
561
562
@Override
563
public ExecutionMode getExecutionMode() {
564
return ExecutionMode.CONCURRENT;
565
}
566
}
567
```