0
# Test Descriptors
1
2
Hierarchical test descriptor system for representing discovered tests and containers with metadata, sources, and relationships. Test descriptors form a tree structure that represents the organization of tests within an engine.
3
4
## Capabilities
5
6
### TestDescriptor Interface
7
8
Core interface representing a single test or container in the test hierarchy, providing metadata, child management, and source information.
9
10
```java { .api }
11
/**
12
* Mutable descriptor for a test or container within the test hierarchy.
13
*/
14
public interface TestDescriptor {
15
/**
16
* Get the unique identifier for this descriptor.
17
* @return unique ID within the engine's namespace
18
*/
19
UniqueId getUniqueId();
20
21
/**
22
* Get the display name for this descriptor.
23
* @return human-readable name for display purposes
24
*/
25
String getDisplayName();
26
27
/**
28
* Get the set of tags associated with this descriptor.
29
* @return unmodifiable set of test tags
30
*/
31
Set<TestTag> getTags();
32
33
/**
34
* Get the source location of this test or container.
35
* @return optional test source (class, method, file, etc.)
36
*/
37
Optional<TestSource> getSource();
38
39
/**
40
* Get the parent descriptor of this descriptor.
41
* @return optional parent descriptor
42
*/
43
Optional<TestDescriptor> getParent();
44
45
/**
46
* Set the parent descriptor of this descriptor.
47
* @param parent the parent descriptor
48
*/
49
void setParent(TestDescriptor parent);
50
51
/**
52
* Get all direct child descriptors.
53
* @return unmodifiable set of child descriptors
54
*/
55
Set<? extends TestDescriptor> getChildren();
56
57
/**
58
* Add a child descriptor to this descriptor.
59
* @param child the child descriptor to add
60
*/
61
void addChild(TestDescriptor child);
62
63
/**
64
* Remove a child descriptor from this descriptor.
65
* @param child the child descriptor to remove
66
*/
67
void removeChild(TestDescriptor child);
68
69
/**
70
* Remove this descriptor from its parent.
71
*/
72
void removeFromHierarchy();
73
74
/**
75
* Get the type of this descriptor.
76
* @return descriptor type (CONTAINER, TEST, or CONTAINER_AND_TEST)
77
*/
78
Type getType();
79
80
/**
81
* Find the first descendant matching the given predicate.
82
* @param predicate the matching predicate
83
* @return optional matching descendant
84
*/
85
Optional<? extends TestDescriptor> findByUniqueId(UniqueId uniqueId);
86
87
/**
88
* Accept a visitor for traversing the descriptor hierarchy.
89
* @param visitor the visitor to accept
90
*/
91
void accept(Visitor visitor);
92
93
/**
94
* Check if this descriptor contains any executable tests.
95
* @param testDescriptor the descriptor to check
96
* @return true if contains tests, false otherwise
97
*/
98
static boolean containsTests(TestDescriptor testDescriptor);
99
100
/**
101
* Type of test descriptor.
102
*/
103
enum Type {
104
/** Contains other tests or containers but is not executable itself */
105
CONTAINER,
106
/** Executable test that does not contain other tests */
107
TEST,
108
/** Both container and executable test */
109
CONTAINER_AND_TEST
110
}
111
112
/**
113
* Visitor interface for traversing test descriptor hierarchies.
114
*/
115
interface Visitor {
116
/**
117
* Visit a test descriptor.
118
* @param descriptor the descriptor to visit
119
* @param remove function to remove the descriptor from its parent
120
*/
121
void visit(TestDescriptor descriptor, Runnable remove);
122
}
123
}
124
```
125
126
**Usage Example:**
127
128
```java
129
// Create hierarchical test structure
130
TestDescriptor engineDescriptor = new EngineDescriptor(
131
UniqueId.forEngine("my-engine"), "My Test Engine"
132
);
133
134
// Add class container
135
UniqueId classId = engineDescriptor.getUniqueId().append("class", "MyTestClass");
136
TestDescriptor classDescriptor = new MyClassDescriptor(classId, MyTestClass.class);
137
engineDescriptor.addChild(classDescriptor);
138
139
// Add test methods
140
UniqueId methodId = classId.append("method", "testSomething");
141
TestDescriptor methodDescriptor = new MyMethodDescriptor(methodId, testMethod);
142
classDescriptor.addChild(methodDescriptor);
143
144
// Navigate hierarchy
145
Optional<TestDescriptor> found = engineDescriptor.findByUniqueId(methodId);
146
Set<? extends TestDescriptor> children = classDescriptor.getChildren();
147
```
148
149
### AbstractTestDescriptor
150
151
Abstract base implementation providing common functionality for test descriptors, including hierarchy management and visitor support.
152
153
```java { .api }
154
/**
155
* Abstract base implementation of the TestDescriptor interface.
156
*/
157
public abstract class AbstractTestDescriptor implements TestDescriptor {
158
/**
159
* Constructor for creating test descriptors.
160
* @param uniqueId unique identifier for this descriptor
161
* @param displayName display name for this descriptor
162
*/
163
protected AbstractTestDescriptor(UniqueId uniqueId, String displayName);
164
165
/**
166
* Constructor with optional source.
167
* @param uniqueId unique identifier for this descriptor
168
* @param displayName display name for this descriptor
169
* @param source optional test source
170
*/
171
protected AbstractTestDescriptor(UniqueId uniqueId, String displayName, TestSource source);
172
173
// Implements all TestDescriptor methods with sensible defaults
174
175
/**
176
* Add a tag to this descriptor.
177
* @param tag the tag to add
178
*/
179
protected void addTag(TestTag tag);
180
181
/**
182
* Set the source for this descriptor.
183
* @param source the test source
184
*/
185
protected void setSource(TestSource source);
186
}
187
```
188
189
### EngineDescriptor
190
191
Specialized test descriptor implementation for test engine root descriptors.
192
193
```java { .api }
194
/**
195
* TestDescriptor implementation for test engine root descriptors.
196
*/
197
public class EngineDescriptor extends AbstractTestDescriptor {
198
/**
199
* Create an engine descriptor.
200
* @param uniqueId unique ID for the engine (should be engine root)
201
* @param displayName display name for the engine
202
*/
203
public EngineDescriptor(UniqueId uniqueId, String displayName);
204
205
/**
206
* Engine descriptors are always containers.
207
* @return Type.CONTAINER
208
*/
209
@Override
210
public final Type getType();
211
}
212
```
213
214
### TestSource Implementations
215
216
Various implementations of the TestSource interface for different types of test origins.
217
218
```java { .api }
219
/**
220
* Test source for Java classes.
221
*/
222
public class ClassSource implements TestSource {
223
public static ClassSource from(Class<?> javaClass);
224
public static ClassSource from(String className);
225
public Class<?> getJavaClass();
226
public String getClassName();
227
}
228
229
/**
230
* Test source for Java methods.
231
*/
232
public class MethodSource implements TestSource {
233
public static MethodSource from(Method javaMethod);
234
public static MethodSource from(Class<?> javaClass, Method javaMethod);
235
public static MethodSource from(Class<?> javaClass, String methodName);
236
public static MethodSource from(String className, String methodName);
237
public static MethodSource from(String className, String methodName, String methodParameterTypes);
238
239
public Class<?> getJavaClass();
240
public String getClassName();
241
public Method getJavaMethod();
242
public String getMethodName();
243
public String getMethodParameterTypes();
244
}
245
246
/**
247
* Test source for files.
248
*/
249
public class FileSource implements TestSource {
250
public static FileSource from(File file);
251
public static FileSource from(File file, FilePosition position);
252
public File getFile();
253
public Optional<FilePosition> getPosition();
254
}
255
256
/**
257
* Test source for directories.
258
*/
259
public class DirectorySource implements TestSource {
260
public static DirectorySource from(File directory);
261
public File getDirectory();
262
}
263
264
/**
265
* Test source for packages.
266
*/
267
public class PackageSource implements TestSource {
268
public static PackageSource from(String packageName);
269
public String getPackageName();
270
}
271
272
/**
273
* Test source for URIs.
274
*/
275
public class UriSource implements TestSource {
276
public static UriSource from(URI uri);
277
public URI getUri();
278
}
279
280
/**
281
* Test source for classpath resources.
282
*/
283
public class ClasspathResourceSource implements TestSource {
284
public static ClasspathResourceSource from(String classpathResourceName);
285
public static ClasspathResourceSource from(String classpathResourceName, FilePosition position);
286
public String getClasspathResourceName();
287
public Optional<FilePosition> getPosition();
288
}
289
290
/**
291
* Composite test source combining multiple sources.
292
*/
293
public class CompositeTestSource implements TestSource {
294
public static CompositeTestSource from(TestSource... sources);
295
public List<TestSource> getSources();
296
}
297
```
298
299
**Usage Example:**
300
301
```java
302
// Create various test sources
303
TestSource classSource = ClassSource.from(MyTestClass.class);
304
TestSource methodSource = MethodSource.from(MyTestClass.class, "testMethod");
305
TestSource fileSource = FileSource.from(new File("MyTest.java"));
306
307
// Use in test descriptors
308
TestDescriptor classDescriptor = new MyClassDescriptor(
309
uniqueId, "MyTestClass", classSource
310
);
311
312
TestDescriptor methodDescriptor = new MyMethodDescriptor(
313
methodId, "testMethod", methodSource
314
);
315
```
316
317
### Visitor Pattern Support
318
319
Built-in support for traversing test descriptor hierarchies using the visitor pattern.
320
321
```java { .api }
322
/**
323
* Composite visitor that combines multiple visitors.
324
*/
325
public class CompositeTestDescriptorVisitor implements TestDescriptor.Visitor {
326
/**
327
* Create a composite visitor from multiple visitors.
328
* @param visitors the visitors to combine
329
* @return composite visitor
330
*/
331
public static CompositeTestDescriptorVisitor of(TestDescriptor.Visitor... visitors);
332
333
@Override
334
public void visit(TestDescriptor descriptor, Runnable remove);
335
}
336
```
337
338
**Usage Example:**
339
340
```java
341
// Traverse descriptor hierarchy
342
engineDescriptor.accept(new TestDescriptor.Visitor() {
343
@Override
344
public void visit(TestDescriptor descriptor, Runnable remove) {
345
System.out.println("Visiting: " + descriptor.getDisplayName());
346
347
// Conditionally remove descriptors
348
if (shouldRemove(descriptor)) {
349
remove.run();
350
}
351
}
352
});
353
354
// Using composite visitor
355
TestDescriptor.Visitor loggingVisitor = (descriptor, remove) ->
356
System.out.println("Processing: " + descriptor.getDisplayName());
357
358
TestDescriptor.Visitor filteringVisitor = (descriptor, remove) -> {
359
if (descriptor.getTags().contains(TestTag.create("disabled"))) {
360
remove.run();
361
}
362
};
363
364
CompositeTestDescriptorVisitor composite = CompositeTestDescriptorVisitor.of(
365
loggingVisitor, filteringVisitor
366
);
367
368
engineDescriptor.accept(composite);
369
```
370
371
### Hierarchy Navigation
372
373
Utility methods for navigating and querying test descriptor hierarchies.
374
375
**Usage Example:**
376
377
```java
378
// Check if descriptor contains executable tests
379
if (TestDescriptor.containsTests(containerDescriptor)) {
380
// Process container with tests
381
}
382
383
// Find specific descriptors
384
Optional<TestDescriptor> specificTest = engineDescriptor.findByUniqueId(
385
UniqueId.parse("[engine:my-engine]/[class:MyClass]/[method:testMethod]")
386
);
387
388
// Get all leaf test descriptors
389
List<TestDescriptor> allTests = new ArrayList<>();
390
engineDescriptor.accept((descriptor, remove) -> {
391
if (descriptor.getType() == TestDescriptor.Type.TEST &&
392
descriptor.getChildren().isEmpty()) {
393
allTests.add(descriptor);
394
}
395
});
396
397
// Count tests and containers
398
long testCount = allTests.size();
399
long containerCount = getAllDescriptors(engineDescriptor).stream()
400
.filter(d -> d.getType() == TestDescriptor.Type.CONTAINER)
401
.count();
402
```