Skip to content

Commit b269134

Browse files
khluusimonsays1980
authored andcommitted
[release] Accommodate multiple filters with same attribute (#57577)
and allow a test to match with one of filters in the same attribute instead of saying it's mismatch if any of the filter fail Signed-off-by: kevin <kevin@anyscale.com>
1 parent 2ff9d77 commit b269134

File tree

3 files changed

+72
-37
lines changed

3 files changed

+72
-37
lines changed

release/ray_release/buildkite/filter.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def _unflattened_lookup(lookup: Dict, flat_key: str, delimiter: str = "/") -> An
2222
def filter_tests(
2323
test_collection: List[Test],
2424
frequency: Frequency,
25-
test_filters: Optional[Dict[str, str]] = None,
25+
test_filters: Optional[Dict[str, list]] = None,
2626
prefer_smoke_tests: bool = False,
2727
run_jailed_tests: bool = False,
2828
run_unstable_tests: bool = False,
@@ -38,19 +38,28 @@ def filter_tests(
3838
if test.is_kuberay() and get_global_config()["kuberay_disabled"]:
3939
continue
4040

41-
# Check if any test attributes match filters
41+
# Check if test attributes match filters
42+
# Logic: OR within same attribute, AND across different attributes
4243
if test_filters:
43-
for attr, value in test_filters.items():
44-
# Only prefix filter doesn't use regex
45-
if attr == "prefix":
46-
if not test.get_name().startswith(value):
47-
attr_mismatch = True
48-
break
49-
else: # Match filters using regex
50-
attr_value = _unflattened_lookup(test, attr) or ""
51-
if not re.match(value, attr_value):
52-
attr_mismatch = True
53-
break
44+
for attr, values in test_filters.items():
45+
# Check if at least one value matches for this attribute (OR logic)
46+
attr_matched = False
47+
for value in values:
48+
# Only prefix filter doesn't use regex
49+
if attr == "prefix":
50+
if test.get_name().startswith(value):
51+
attr_matched = True
52+
break
53+
else: # Match filters using regex
54+
attr_value = _unflattened_lookup(test, attr) or ""
55+
if re.match(value, attr_value):
56+
attr_matched = True
57+
break
58+
59+
# If none of the values matched for this attribute, skip this test
60+
if not attr_matched:
61+
attr_mismatch = True
62+
break
5463
if attr_mismatch:
5564
continue
5665

release/ray_release/buildkite/settings.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def get_priority(priority_str: str) -> Priority:
6363
return priority_str_to_enum[priority_str]
6464

6565

66-
def get_test_filters(filters_str: str) -> Dict[str, str]:
66+
def get_test_filters(filters_str: str) -> Dict[str, list]:
6767
if not filters_str:
6868
return {}
6969

@@ -77,7 +77,10 @@ def get_test_filters(filters_str: str) -> Dict[str, str]:
7777
raise ReleaseTestConfigError(
7878
f"Invalid test filter: {line}. " "Should be of the form attr:value"
7979
)
80-
test_filters[parts[0]] = parts[1]
80+
# Support multiple values for the same attribute (OR logic)
81+
if parts[0] not in test_filters:
82+
test_filters[parts[0]] = []
83+
test_filters[parts[0]].append(parts[1])
8184
return test_filters
8285

8386

release/ray_release/tests/test_buildkite.py

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,20 @@ def testGetTestAttrRegexFilters(self):
114114
self.assertDictEqual(test_filters, {})
115115

116116
test_filters = get_test_filters("name:xxx")
117-
self.assertDictEqual(test_filters, {"name": "xxx"})
117+
self.assertDictEqual(test_filters, {"name": ["xxx"]})
118118

119119
test_filters = get_test_filters("name:xxx\n")
120-
self.assertDictEqual(test_filters, {"name": "xxx"})
120+
self.assertDictEqual(test_filters, {"name": ["xxx"]})
121121

122122
test_filters = get_test_filters("name:xxx\n\nteam:yyy")
123-
self.assertDictEqual(test_filters, {"name": "xxx", "team": "yyy"})
123+
self.assertDictEqual(test_filters, {"name": ["xxx"], "team": ["yyy"]})
124124

125125
test_filters = get_test_filters("name:xxx\n \nteam:yyy\n")
126-
self.assertDictEqual(test_filters, {"name": "xxx", "team": "yyy"})
126+
self.assertDictEqual(test_filters, {"name": ["xxx"], "team": ["yyy"]})
127+
128+
# Test multiple filters with same attribute (OR logic)
129+
test_filters = get_test_filters("name:xxx\nname:yyy")
130+
self.assertDictEqual(test_filters, {"name": ["xxx", "yyy"]})
127131

128132
with self.assertRaises(ReleaseTestConfigError):
129133
get_test_filters("xxx")
@@ -172,8 +176,8 @@ def testSettingsOverrideEnv(self):
172176
self.assertDictEqual(
173177
updated_settings["test_filters"],
174178
{
175-
"name": "xxx",
176-
"team": "yyy",
179+
"name": ["xxx"],
180+
"team": ["yyy"],
177181
},
178182
)
179183

@@ -192,7 +196,7 @@ def testSettingsOverrideEnv(self):
192196
{
193197
"frequency": Frequency.NIGHTLY,
194198
"prefer_smoke_tests": False,
195-
"test_filters": {"name": "name_filter"},
199+
"test_filters": {"name": ["name_filter"]},
196200
"ray_test_repo": "https://github.com/user/ray.git",
197201
"ray_test_branch": "sub/branch",
198202
"priority": Priority.MANUAL,
@@ -207,7 +211,7 @@ def testSettingsOverrideEnv(self):
207211
{
208212
"frequency": Frequency.ANY,
209213
"prefer_smoke_tests": True,
210-
"test_filters": {"name": "name_filter"},
214+
"test_filters": {"name": ["name_filter"]},
211215
"ray_test_repo": "https://github.com/user/ray.git",
212216
"ray_test_branch": "sub/branch",
213217
"priority": Priority.MANUAL,
@@ -335,8 +339,8 @@ def testSettingsOverrideBuildkite(self):
335339
self.assertDictEqual(
336340
updated_settings["test_filters"],
337341
{
338-
"name": "xxx",
339-
"group": "yyy",
342+
"name": ["xxx"],
343+
"group": ["yyy"],
340344
},
341345
)
342346

@@ -354,7 +358,7 @@ def testSettingsOverrideBuildkite(self):
354358
{
355359
"frequency": Frequency.NIGHTLY,
356360
"prefer_smoke_tests": False,
357-
"test_filters": {"name": "name_filter"},
361+
"test_filters": {"name": ["name_filter"]},
358362
"ray_test_repo": "https://github.com/user/ray.git",
359363
"ray_test_branch": "sub/branch",
360364
"priority": Priority.MANUAL,
@@ -370,7 +374,7 @@ def testSettingsOverrideBuildkite(self):
370374
{
371375
"frequency": Frequency.ANY,
372376
"prefer_smoke_tests": True,
373-
"test_filters": {"name": "name_filter"},
377+
"test_filters": {"name": ["name_filter"]},
374378
"ray_test_repo": "https://github.com/user/ray.git",
375379
"ray_test_branch": "sub/branch",
376380
"priority": Priority.MANUAL,
@@ -432,7 +436,7 @@ def testFilterTests(self, *args):
432436

433437
# Test filter by prefix alone
434438
filtered = self._filter_names(
435-
tests, frequency=Frequency.ANY, test_filters={"prefix": "test"}
439+
tests, frequency=Frequency.ANY, test_filters={"prefix": ["test"]}
436440
)
437441
self.assertSequenceEqual(
438442
filtered,
@@ -448,7 +452,7 @@ def testFilterTests(self, *args):
448452
filtered = self._filter_names(
449453
tests,
450454
frequency=Frequency.NIGHTLY,
451-
test_filters={"prefix": "test", "name": "other.*"},
455+
test_filters={"prefix": ["test"], "name": ["other.*"]},
452456
)
453457
self.assertSequenceEqual(
454458
filtered,
@@ -522,7 +526,7 @@ def testFilterTests(self, *args):
522526
filtered = self._filter_names(
523527
tests,
524528
frequency=Frequency.NIGHTLY,
525-
test_filters={"name": "other.*"},
529+
test_filters={"name": ["other.*"]},
526530
)
527531
self.assertSequenceEqual(
528532
filtered,
@@ -534,7 +538,7 @@ def testFilterTests(self, *args):
534538
filtered = self._filter_names(
535539
tests,
536540
frequency=Frequency.NIGHTLY,
537-
test_filters={"name": "test.*"},
541+
test_filters={"name": ["test.*"]},
538542
)
539543
self.assertSequenceEqual(
540544
filtered,
@@ -547,7 +551,7 @@ def testFilterTests(self, *args):
547551
)
548552

549553
filtered = self._filter_names(
550-
tests, frequency=Frequency.NIGHTLY, test_filters={"name": "test"}
554+
tests, frequency=Frequency.NIGHTLY, test_filters={"name": ["test"]}
551555
)
552556
self.assertSequenceEqual(
553557
filtered,
@@ -562,22 +566,41 @@ def testFilterTests(self, *args):
562566
filtered = self._filter_names(
563567
tests,
564568
frequency=Frequency.NIGHTLY,
565-
test_filters={"name": "test.*", "team": "team_1"},
569+
test_filters={"name": ["test.*"], "team": ["team_1"]},
566570
)
567571
self.assertSequenceEqual(filtered, [("test_1", False)])
568572

569573
filtered = self._filter_names(
570574
tests,
571575
frequency=Frequency.NIGHTLY,
572-
test_filters={"name": "test_1|test_2"},
576+
test_filters={"name": ["test_1|test_2"]},
573577
)
574578
self.assertSequenceEqual(filtered, [("test_1", False), ("test_2", True)])
575579

580+
# Test OR logic within same attribute
581+
filtered = self._filter_names(
582+
tests,
583+
frequency=Frequency.NIGHTLY,
584+
test_filters={"name": ["^test_1$", "^test_3$"]},
585+
)
586+
self.assertSequenceEqual(filtered, [("test_1", False), ("test_3", False)])
587+
588+
# Test OR logic with AND across attributes
589+
filtered = self._filter_names(
590+
tests,
591+
frequency=Frequency.NIGHTLY,
592+
test_filters={
593+
"name": ["^test_1$", "^test_3$"],
594+
"team": ["team_1", "team_2"],
595+
},
596+
)
597+
self.assertSequenceEqual(filtered, [("test_1", False), ("test_3", False)])
598+
576599
# Filter by nested properties
577600
filtered = self._filter_names(
578601
tests,
579602
frequency=Frequency.ANY,
580-
test_filters={"run/type": "job"},
603+
test_filters={"run/type": ["job"]},
581604
)
582605
self.assertSequenceEqual(
583606
filtered, [("test_1", False), ("other_2", False), ("test_4.kuberay", False)]
@@ -586,14 +609,14 @@ def testFilterTests(self, *args):
586609
filtered = self._filter_names(
587610
tests,
588611
frequency=Frequency.ANY,
589-
test_filters={"run/type": "client"},
612+
test_filters={"run/type": ["client"]},
590613
)
591614
self.assertSequenceEqual(filtered, [("test_2", False)])
592615

593616
filtered = self._filter_names(
594617
tests,
595618
frequency=Frequency.ANY,
596-
test_filters={"run/invalid": "xxx"},
619+
test_filters={"run/invalid": ["xxx"]},
597620
)
598621
self.assertSequenceEqual(filtered, [])
599622

0 commit comments

Comments
 (0)