Skip to content

Commit 28dea7a

Browse files
committed
Fix behavior when applying multiple patches to the same test
1 parent 5721daf commit 28dea7a

File tree

2 files changed

+77
-21
lines changed

2 files changed

+77
-21
lines changed

src/beku/kuttl.py

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def expand(self, template_dir: str, target_dir: str) -> None:
142142
test_source.build_destination()
143143

144144

145-
@dataclass(frozen=True)
145+
@dataclass(frozen=True, eq=True)
146146
class TestDimension:
147147
"""Test dimension."""
148148
name: str
@@ -172,25 +172,26 @@ class TestSuitePatchDimension:
172172
* None : no patch is applied.
173173
* first : the first element in a dimension sequence is retained. All others are discarded.
174174
* last : the last element in a dimension sequence is retained. All others are discarded.
175-
* <substring> : elements the contain the given <substring> are retained. All others are discarded.
175+
* <string> : set the dim value to <string> and ignore other values.
176176
"""
177177
name: Optional[str]
178178
expr: Optional[str]
179179

180-
def patch_dimensions(self, dims: List[TestDimension]) -> List[TestDimension]:
180+
def patch_dimensions(self, test_name: str, dims: List[TestDimension]) -> List[TestDimension]:
181181
""""Patch the given dimensions according to expr attribute."""
182182
result = []
183183
for dim in dims:
184184
if not self.name or self.name == dim.name:
185+
patched_values = dim.values
185186
if self.expr == 'last':
186187
patched_values = [dim.values[-1]]
187188
elif self.expr == 'first':
188189
patched_values = [dim.values[0]]
189-
elif self.expr is None:
190-
patched_values = dim.values
191-
else:
192-
patched_values = [
193-
v for v in dim.values if v.find(self.expr) != -1]
190+
elif self.expr:
191+
patched_values = [self.expr]
192+
193+
logging.debug(
194+
f"Patching dimension [{test_name}].[{dim.name}] value [{patched_values}]")
194195
result.append(TestDimension(
195196
name=dim.name, values=patched_values))
196197
else:
@@ -226,9 +227,10 @@ def patch_dimensions(self, test_name: str, dims: List[TestDimension]) -> List[Te
226227
f"Skipping patch for test {[self.test]} for test [{test_name}]")
227228

228229
if self._may_patch(test_name):
230+
logging.debug(f"Patching test [{test_name}]")
229231
result = {}
230232
for _pd in self.patches:
231-
for dim in _pd.patch_dimensions(dims):
233+
for dim in _pd.patch_dimensions(test_name, dims):
232234
result[dim.name] = dim
233235
return list(result.values())
234236
return dims
@@ -272,11 +274,23 @@ def select_tests(self, tests: List[TestDefinition]) -> List[TestDefinition]:
272274
return tests
273275

274276
def patch_dimensions(self, test_name: str, dims: List[TestDimension]) -> List[TestDimension]:
277+
"""Apply all patches sequentially in the order they are defined. Each patch uses the resulting
278+
dimensions of the previous patch
279+
280+
Arguments:
281+
test_name (str) : Name of the test to apply the patches.
282+
dims (list) : Initial dimensions as per test definition.
283+
284+
Return:
285+
The dimensions after applying all patches.
286+
"""
275287
if self.patches:
276-
result = []
277-
for patch in self.patches:
278-
result.extend(patch.patch_dimensions(test_name, dims))
279-
return result
288+
result = {p.name: p for p in self.patches[0].patch_dimensions(
289+
test_name, dims)}
290+
for patch in self.patches[1:]:
291+
result = {p.name: p for p in patch.patch_dimensions(
292+
test_name, list(result.values()))}
293+
return list(result.values())
280294
return dims
281295

282296

@@ -354,8 +368,10 @@ def _resolve_effective_test_suites(
354368
):
355369
effective_test_suites = []
356370
for suite in suites:
371+
logging.debug(f"Resolving effective test suite [{suite.name}]")
357372
test_cases = []
358373
for test in suite.select_tests(tests):
374+
logging.debug(f"Selected test [{suite.name}].[{test.name}]")
359375
used_dims = [d for d in dims if d.name in test.dimensions]
360376
effective_dimensions = suite.patch_dimensions(
361377
test.name, used_dims)
@@ -364,8 +380,8 @@ def _resolve_effective_test_suites(
364380
]
365381
test_cases.extend(
366382
[TestCase(name=test.name, values=dict(tc_dim)) for tc_dim in product(*expanded_test_dims)])
367-
effective_test_suites.append(EffectiveTestSuite(
368-
name=suite.name, test_cases=test_cases))
383+
ets = EffectiveTestSuite(name=suite.name, test_cases=test_cases)
384+
effective_test_suites.append(ets)
369385
return effective_test_suites
370386

371387

src/beku/test/test_render_from_stream.py

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,9 @@ def test_patch_the_same_test_twice(self):
137137
ets = renderer_from_stream(fixture)
138138
expected = EffectiveTestSuite(name='two-patches-on-the-same-test',
139139
test_cases=[TestCase(name='smoke',
140-
values={'druid': '26.0.0-stackable0.0.0-dev'})])
141-
self.assertEqual(expected, ets[0], "Last patch wins")
140+
values={'druid': '24.0.0-stackable0.0.0-dev'})])
141+
self.assertEqual(
142+
expected, ets[0], "First patch select 24.0.0 and the second has only one value to choose from")
142143

143144
def test_patch_str_expression(self):
144145
fixture = textwrap.dedent("""
@@ -154,17 +155,17 @@ def test_patch_str_expression(self):
154155
dimensions:
155156
- druid
156157
suites:
157-
- name: select-25.0.0
158+
- name: select-27.0.0
158159
patch:
159160
- test: smoke
160161
dimensions:
161162
- name: druid
162-
expr: "25"
163+
expr: "27.0.0"
163164
""")
164165
ets = renderer_from_stream(fixture)
165-
expected = EffectiveTestSuite(name='select-25.0.0',
166+
expected = EffectiveTestSuite(name='select-27.0.0',
166167
test_cases=[TestCase(name='smoke',
167-
values={'druid': '25.0.0-stackable0.0.0-dev'})])
168+
values={'druid': '27.0.0'})])
168169
self.assertEqual(expected, ets[0], "String expression works")
169170

170171
def test_resolve_explicit_select(self):
@@ -250,6 +251,45 @@ def test_resolve_select_all(self):
250251
self.assertEqual(["smoke", "resources"], list(dict.fromkeys([tc.name for tc in ets[0].test_cases])),
251252
"Two test definitions were selected.")
252253

254+
def test_multiple_patches(self):
255+
fixture = textwrap.dedent("""
256+
---
257+
dimensions:
258+
- name: druid
259+
values:
260+
- 24.0.0-stackable0.0.0-dev
261+
- 26.0.0-stackable0.0.0-dev
262+
- name: zookeeper
263+
values:
264+
- 3.7.0-stackable0.0.0-dev
265+
- 3.8.0-stackable0.0.0-dev
266+
- name: openshift
267+
values:
268+
- "false"
269+
tests:
270+
- name: smoke
271+
dimensions:
272+
- druid
273+
- zookeeper
274+
- openshift
275+
suites:
276+
- name: openshift
277+
patch:
278+
- dimensions:
279+
- expr: last
280+
- dimensions:
281+
- name: openshift
282+
expr: "true"
283+
""")
284+
ets = renderer_from_stream(fixture)
285+
expected = EffectiveTestSuite(name='openshift',
286+
test_cases=[TestCase(name='smoke',
287+
values={'druid': '26.0.0-stackable0.0.0-dev',
288+
'openshift': 'true',
289+
'zookeeper': '3.8.0-stackable0.0.0-dev'})])
290+
291+
self.assertEqual(expected, ets[0])
292+
253293

254294
if __name__ == '__main__':
255295
unittest.main()

0 commit comments

Comments
 (0)