Skip to content

Commit fe79f81

Browse files
feat(e2e): use OR tag matching semantics with TAGS_TO_RUN when all are name filters (#8631)
1 parent 88021bd commit fe79f81

2 files changed

Lines changed: 32 additions & 6 deletions

File tree

e2e/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ TAGS_TO_RUN="name=Test_azurelinuxv2" ./e2e-local.sh
8989
go test -run Test_azurelinuxv2 -v -timeout 90m
9090
```
9191

92+
To run multiple specific scenarios by name, provide multiple `Name=` filters. When all filters are
93+
`Name=` filters, OR semantics are used automatically (matching any of the listed names):
94+
95+
```bash
96+
TAGS_TO_RUN="name=Test_azurelinuxv2,name=Test_ubuntu2204" ./e2e-local.sh
97+
```
98+
9299
### Debugging
93100

94101
Set `KEEP_VMSS=true` to retain bootstrapped VMs for debugging. Setting this will also have the VM's private SSH key

e2e/types.go

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ type Tags struct {
4141
// MatchesFilters checks if the Tags struct matches all given filters.
4242
// Filters are comma-separated "key=value" pairs (e.g., "gpu=true,os=x64").
4343
// Returns true if all filters match, false otherwise. Errors on invalid input.
44+
//
45+
// Special case: when ALL filters use the "Name" key (e.g., "Name=foo,Name=bar"),
46+
// OR semantics are used instead, matching if any name matches. This allows
47+
// selecting multiple scenarios by name with a single filter string.
4448
func (t Tags) MatchesFilters(filters string) (bool, error) {
4549
return t.matchFilters(filters, true)
4650
}
@@ -54,6 +58,8 @@ func (t Tags) MatchesAnyFilter(filters string) (bool, error) {
5458

5559
// matchFilters is a helper function used by both MatchesFilters and MatchesAnyFilter.
5660
// The 'all' parameter determines whether all filters must match (true) or just any filter (false).
61+
// When all filters target the "Name" field, OR semantics are applied regardless of the 'all'
62+
// parameter, since a scenario can only have one name and AND would never match multiple names.
5763
func (t Tags) matchFilters(filters string, all bool) (bool, error) {
5864
if filters == "" {
5965
return true, nil
@@ -62,6 +68,10 @@ func (t Tags) matchFilters(filters string, all bool) (bool, error) {
6268
v := reflect.ValueOf(t)
6369
filterPairs := strings.Split(filters, ",")
6470

71+
allNameFilters := true
72+
anyMatch := false
73+
allMatch := true
74+
6575
for _, pair := range filterPairs {
6676
kv := strings.SplitN(pair, "=", 2)
6777
if len(kv) != 2 {
@@ -71,6 +81,10 @@ func (t Tags) matchFilters(filters string, all bool) (bool, error) {
7181
key := strings.TrimSpace(kv[0])
7282
value := strings.TrimSpace(kv[1])
7383

84+
if !strings.EqualFold(key, "Name") {
85+
allNameFilters = false
86+
}
87+
7488
// Case-insensitive field lookup
7589
field := reflect.Value{}
7690
for i := 0; i < v.NumField(); i++ {
@@ -98,15 +112,20 @@ func (t Tags) matchFilters(filters string, all bool) (bool, error) {
98112
return false, fmt.Errorf("unsupported field type for %s", key)
99113
}
100114

101-
if all && !match {
102-
return false, nil
103-
}
104-
if !all && match {
105-
return true, nil
115+
if match {
116+
anyMatch = true
117+
} else {
118+
allMatch = false
106119
}
107120
}
108121

109-
return all, nil
122+
if allNameFilters {
123+
return anyMatch, nil
124+
}
125+
if all {
126+
return allMatch, nil
127+
}
128+
return anyMatch, nil
110129
}
111130

112131
// Scenario represents an AgentBaker E2E scenario.

0 commit comments

Comments
 (0)