Skip to content

Commit 05c2d5c

Browse files
author
Ryosuke Yabuki
authored
Merge pull request #565 from launchableinc/aw/split-subset-zis
Add --output-exclusion-rules support to split-subset
2 parents 390ef29 + 36080e3 commit 05c2d5c

File tree

5 files changed

+113
-15
lines changed

5 files changed

+113
-15
lines changed

launchable/commands/split_subset.py

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,24 @@ def split_subset(
102102
class SplitSubset(TestPathWriter):
103103
def __init__(self, dry_run: bool = False):
104104
super(SplitSubset, self).__init__(dry_run=dry_run)
105+
self.rest = rest
106+
self.output_handler = self._default_output_handler
107+
self.exclusion_output_handler = self._default_exclusion_output_handler
105108
self.split_by_groups_output_handler = self._default_split_by_groups_output_handler
106109
self.split_by_groups_exclusion_output_handler = self._default_split_by_groups_exclusion_output_handler
107110
self.is_split_by_groups_with_rest = is_split_by_groups_with_rest
108111
self.split_by_groups_output_dir = split_by_groups_output_dir
112+
self.is_output_exclusion_rules = is_output_exclusion_rules
113+
114+
def _default_output_handler(self, output: List[TestPath], rests: List[TestPath]):
115+
if rest:
116+
self.write_file(rest, rests)
117+
118+
if output:
119+
self.print(output)
120+
121+
def _default_exclusion_output_handler(self, subset: List[TestPath], rest: List[TestPath]):
122+
self.output_handler(rest, subset)
109123

110124
def _default_split_by_groups_output_handler(self, group_name: str, subset: List[TestPath], rests: List[TestPath]):
111125
if is_split_by_groups_with_rest:
@@ -148,8 +162,8 @@ def split_by_bin(self):
148162
)
149163
return
150164

151-
output = []
152-
rests = []
165+
output_subset = []
166+
output_rests = []
153167
is_observation = False
154168

155169
try:
@@ -230,24 +244,24 @@ def split_by_bin(self):
230244
res = client.request("POST", "{}/slice".format(subset_id), payload=payload)
231245
res.raise_for_status()
232246

233-
output = res.json().get("testPaths", [])
234-
rests = res.json().get("rest", [])
247+
output_subset = res.json().get("testPaths", [])
248+
output_rests = res.json().get("rest", [])
235249
is_observation = res.json().get("isObservation", False)
236250

237-
if len(output) == 0:
251+
if len(output_subset) == 0:
238252
click.echo(click.style(
239-
"Error: no tests found in this subset id.", 'yellow'), err=True)
253+
"Error: no tests found for this subset id.", 'yellow'), err=True)
240254
return
241255

242256
if is_observation:
243-
output = output + rests
244-
if rest:
245-
if len(rests) == 0:
246-
rests.append(output[0])
257+
output_subset = output_subset + output_rests
258+
output_rests = []
247259

248-
self.write_file(rest, rests)
260+
if is_output_exclusion_rules:
261+
self.exclusion_output_handler(output_subset, output_rests)
262+
else:
263+
self.output_handler(output_subset, output_rests)
249264

250-
self.print(output)
251265
except Exception as e:
252266
if os.getenv(REPORT_ERROR_KEY):
253267
raise e

launchable/test_runners/dotnet.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,40 @@ def exclusion_output_handler(subset_tests: List[TestPath], rest_tests: List[Test
5353
client.run()
5454

5555

56+
@launchable.split_subset
57+
def split_subset(client):
58+
# ref: https://github.com/Microsoft/vstest-docs/blob/main/docs/filter.md
59+
separator = "|"
60+
prefix = "FullyQualifiedName="
61+
62+
if client.is_output_exclusion_rules:
63+
separator = "&"
64+
prefix = "FullyQualifiedName!="
65+
66+
def formatter(test_path: TestPath):
67+
paths = []
68+
69+
for path in test_path:
70+
t = path.get("type", "")
71+
if t == 'Assembly':
72+
continue
73+
paths.append(path.get("name", ""))
74+
75+
return prefix + ".".join(paths)
76+
77+
def exclusion_output_handler(subset_tests: List[TestPath], rest_tests: List[TestPath]):
78+
if client.rest:
79+
with open(client.rest, "w+", encoding="utf-8") as fp:
80+
fp.write(client.separator.join(formatter(t) for t in subset_tests))
81+
82+
click.echo(client.separator.join(formatter(t) for t in rest_tests))
83+
84+
client.separator = separator
85+
client.formatter = formatter
86+
client.exclusion_output_handler = exclusion_output_handler
87+
client.run()
88+
89+
5690
@click.argument('files', required=True, nargs=-1)
5791
@launchable.record.tests
5892
def record_tests(client, files):

tests/commands/test_split_subset.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def test_split_subset_with_observation_mode(self):
6565

6666
self.assertEqual(result.exit_code, 0)
6767
self.assertEqual(result.stdout, "test_1.py\ntest_3.py\ntest_5.py\n")
68-
self.assertEqual(observation_mode_rest.read().decode(), os.linesep.join(["test_5.py"]))
68+
self.assertEqual(observation_mode_rest.read().decode(), "")
6969
observation_mode_rest.close()
7070
os.unlink(observation_mode_rest.name)
7171

tests/test_runners/test_dotnet.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from launchable.utils.http_client import get_base_url
1010
from tests.cli_test_case import CliTestCase
11+
from tests.helper import ignore_warnings
1112

1213

1314
class DotnetTest(CliTestCase):
@@ -93,6 +94,55 @@ def test_subset(self):
9394
output = "FullyQualifiedName!=rocket_car_dotnet.ExampleTest.TestAdd&FullyQualifiedName!=rocket_car_dotnet.ExampleTest.TestDiv\n" # noqa: E501
9495
self.assertEqual(result.output, output)
9596

97+
@ignore_warnings
98+
@responses.activate
99+
@mock.patch.dict(os.environ, {"LAUNCHABLE_TOKEN": CliTestCase.launchable_token})
100+
def test_split_subset(self):
101+
responses.replace(
102+
responses.POST, "{}/intake/organizations/{}/workspaces/{}/subset/456/slice".format(
103+
get_base_url(), self.organization, self.workspace), json={
104+
"testPaths": [
105+
[
106+
{"type": "Assembly", "name": "rocket-car-dotnet.dll"},
107+
{"type": "TestSuite", "name": "rocket_car_dotnet"},
108+
{"type": "TestSuite", "name": "ExampleTest"},
109+
{"type": "TestCase", "name": "TestSub"},
110+
],
111+
],
112+
"rest": [
113+
[
114+
{"type": "Assembly", "name": "rocket-car-dotnet.dll"},
115+
{"type": "TestSuite", "name": "rocket_car_dotnet"},
116+
{"type": "TestSuite", "name": "ExampleTest"},
117+
{"type": "TestCase", "name": "TestAdd"},
118+
],
119+
],
120+
'subsettingId': 456,
121+
'summary': {
122+
'subset': {
123+
'duration': 8, 'candidates': 1, 'rate': 50,
124+
},
125+
'rest': {
126+
'duration': 7, 'candidates': 1, 'rate': 50,
127+
},
128+
},
129+
},
130+
status=200)
131+
132+
result = self.cli('split-subset', '--subset-id', 'subset/456',
133+
'--bin', '1/2', 'dotnet')
134+
135+
self.assertEqual(result.exit_code, 0)
136+
137+
output = "FullyQualifiedName=rocket_car_dotnet.ExampleTest.TestSub\n" # noqa: E501
138+
self.assertEqual(result.output, output)
139+
140+
result = self.cli('split-subset', '--subset-id', 'subset/456',
141+
'--bin', '1/2', '--output-exclusion-rules', 'dotnet')
142+
self.assertEqual(result.exit_code, 0)
143+
output = "FullyQualifiedName!=rocket_car_dotnet.ExampleTest.TestAdd\n" # noqa: E501
144+
self.assertEqual(result.output, output)
145+
96146
@responses.activate
97147
@mock.patch.dict(os.environ, {"LAUNCHABLE_TOKEN": CliTestCase.launchable_token})
98148
def test_record_tests(self):
@@ -102,4 +152,4 @@ def test_record_tests(self):
102152

103153
payload = json.loads(gzip.decompress(responses.calls[1].request.body).decode())
104154
expected = self.load_json_from_file(self.test_files_dir.joinpath("record_test_result.json"))
105-
self.assert_json_orderless_equal(payload, expected)
155+
self.assert_json_orderless_equal(payload, expected)

tests/test_runners/test_minitest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,6 @@ def test_split_subset(self):
134134
self.assertEqual(result.exit_code, 0)
135135
output = Path(self.test_files_dir, "test", "example_test.rb")
136136
self.assertEqual(str(output), result.output.rstrip("\n"))
137-
self.assertEqual(rest.read().decode().rstrip("\n"), str(output))
137+
self.assertEqual(rest.read().decode().rstrip("\n"), "")
138138
rest.close()
139139
os.unlink(rest.name)

0 commit comments

Comments
 (0)