You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This document describes the business logic of test filtering in Testo.
7
+
This document describes the internal logic of the filtering plugin: the algorithm, pipeline stages, and criteria combination. If you just need to filter tests when running — see the [CLI reference](../cli-reference.md).
Testo provides a flexible filtering system that operates in multiple stages to progressively narrow down the test set. Filtering can be controlled programmatically via the <class>\Testo\Common\Filter</class> class or automatically from CLI arguments.
14
-
15
-
## Filter Class
16
-
17
-
The <class>\Testo\Common\Filter</class> class is an immutable DTO containing test filtering criteria:
13
+
Testo provides a flexible filtering system that operates in multiple stages to progressively narrow down the test set. Filtering can be controlled programmatically via the <class>\Testo\Filter</class> class or automatically from CLI arguments.
<short>Immutable DTO containing test filtering criteria.</short>
17
+
<paramname="$suites">Test suite names to filter by.</param>
18
+
<paramname="$names">Class, method, or function names. Formats: `ClassName::methodName`, `Namespace\ClassName`, fragment `methodName`. Optional DataProvider indices via colon: `name:providerIndex:datasetIndex`.</param>
19
+
<paramname="$paths">File or directory paths. Supports glob patterns: `*`, `?`, `[abc]`.</param>
20
+
<paramname="$type">Test type: `test`, `inline`, `bench`, or other custom type. If not specified — all types are run.</param>
21
+
<example>
19
22
```php
20
23
$filter = new Filter(
21
24
suites: ['Unit', 'Integration'],
@@ -24,48 +27,31 @@ $filter = new Filter(
24
27
type: 'test',
25
28
);
26
29
```
30
+
</example>
31
+
</signature>
27
32
28
-
### Properties
29
-
30
-
**`testSuites`**: `list<non-empty-string>`
31
-
- Test suite names to filter by
32
-
- Used in Stage 1 to determine which configuration scopes to load
33
-
34
-
**`names`**: `list<non-empty-string>`
35
-
- Class, method, or function names to filter by
36
-
- Supports three formats:
37
-
- Method: `ClassName::methodName` or `Namespace\ClassName::methodName`
38
-
- FQN: `Namespace\ClassName` or `Namespace\functionName`
39
-
- Fragment: `methodName`, `functionName`, or `ShortClassName`
- Possible values: `test` (regular tests), `inline` (inline tests), `bench` (benchmarks), or other custom types
53
-
- If not specified — all test types are run
54
-
- Middleware bound to a specific type won't enter the pipeline if the type doesn't match
35
+
**Via CLI options** — when creating via `Application::createFromInput()`, the plugin automatically creates <class>\Testo\Filter</class> from command options: `--filter`, `--path`, `--suite`, `--type`:
The <class>\Testo\Common\Filter</class> object can be passed to `Application::run()`:
44
+
**Via container** — register the <class>\Testo\Filter</class> object directly:
59
45
60
46
```php
61
-
$app = Application::createFromInput(/* ... */);
47
+
$app = Application::createFromConfig($config);
62
48
63
-
$filter = new Filter(
49
+
$app->getContainer()->set(Filter::class, new Filter(
64
50
suites: ['Unit'],
65
51
names: ['UserTest'],
66
-
);
52
+
));
67
53
68
-
$result = $app->run($filter);
54
+
$result = $app->run();
69
55
```
70
56
71
57
When running from CLI, the <class>\Testo\Common\Filter</class> is populated automatically from command arguments via `Filter::fromScope()`.
@@ -103,7 +89,7 @@ $filter = new Filter(
103
89
104
90
## Name Filter Behavior
105
91
106
-
The behavior of name filtering is implemented in `FilterInterceptor` and depends on the name format:
92
+
The behavior of name filtering is implemented in <class>\Testo\Filter\Internal\FilterInterceptor</class> and depends on the name format:
107
93
108
94
### Method Format (`ClassName::methodName`)
109
95
@@ -145,28 +131,30 @@ $filter = new Filter(names: ['testLogin']);
145
131
// Result: All classes with testLogin method, each with only that method
146
132
```
147
133
148
-
### DataProvider Indices
134
+
### Narrowing by DataProvider and DataSet
149
135
150
-
When tests use data providers, names can include provider and dataset indices using colon separator. These indices become available to the data provider module.
136
+
After the name, you can narrow down to a specific DataProvider via colon, and further to a specific DataSet within it via another colon.
151
137
152
138
**Format:**`name:providerIndex:datasetIndex`
153
139
154
-
- Indices are 0-based integers, independent of dataset labels
155
-
-`datasetIndex` is optional - omit to pass only provider index
156
-
- Works with all name formats (Method, FQN, Fragment)
140
+
- The format maps to <class>\Testo\Filter\DataPointer</class> and is passed to the data provider module.
141
+
- "Provider" here means any attribute that spawns a separate test: <attr>\Testo\Data\DataProvider</attr>, <attr>\Testo\Data\DataSet</attr>, <attr>\Testo\Inline\TestInline</attr>, <attr>\Testo\Bench\Bench</attr>, etc.
142
+
- Indices are 0-based, independent of dataset labels.
143
+
-`datasetIndex` is optional — you can specify only the provider.
144
+
- Works with all name formats (method, FQN, fragment).
157
145
158
146
**Examples:**
159
147
```php
160
-
// Pass provider #0 index
148
+
// First provider
161
149
$filter = new Filter(names: ['UserTest::testLogin:0']);
162
150
163
-
// Pass provider #0 and dataset #1 indices
151
+
// First provider, second dataset
164
152
$filter = new Filter(names: ['UserTest::testLogin:0:1']);
165
153
166
-
// Pass provider #1 and dataset #3 indices, matching any test named 'testAuth'
154
+
// Second provider, fourth dataset — for any test named testAuth
167
155
$filter = new Filter(names: ['testAuth:1:3']);
168
156
169
-
// Pass provider #0 index for entire UserTest class
157
+
// First provider — for entire UserTest class
170
158
$filter = new Filter(names: ['UserTest:0']);
171
159
```
172
160
@@ -227,7 +215,7 @@ Filtering operates in five stages:
227
215
228
216
## Pattern Matching
229
217
230
-
`FilterInterceptor` uses whole-word boundary matching with regex:
218
+
<class>\Testo\Filter\Internal\FilterInterceptor</class> uses whole-word boundary matching with regex:
231
219
232
220
```php
233
221
private static function has(string $needle, string $haystack): bool
0 commit comments