Skip to content

Commit 41ef690

Browse files
committed
[AIENG-196] Add the new command for the early flake detection
1 parent e636393 commit 41ef690

File tree

8 files changed

+138
-0
lines changed

8 files changed

+138
-0
lines changed

launchable/__main__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from .commands.compare import compare
1313
from .commands.inspect import inspect
1414
from .commands.record import record
15+
from .commands.retry import retry
1516
from .commands.split_subset import split_subset
1617
from .commands.stats import stats
1718
from .commands.subset import subset
@@ -91,6 +92,7 @@ def main(ctx, log_level, plugin_dir, dry_run, skip_cert_verification):
9192
main.add_command(inspect)
9293
main.add_command(stats)
9394
main.add_command(compare)
95+
main.add_command(retry)
9496

9597
if __name__ == '__main__':
9698
main()
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import click
2+
3+
from launchable.utils.click import GroupWithAlias
4+
5+
from .flake_detection import flake_detection
6+
7+
8+
@click.group(cls=GroupWithAlias)
9+
def retry():
10+
pass
11+
12+
13+
retry.add_command(flake_detection, 'flake-detection')
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import os
2+
import sys
3+
4+
import click
5+
6+
from launchable.app import Application
7+
from launchable.commands.helper import find_or_create_session
8+
from launchable.commands.test_path_writer import TestPathWriter
9+
from launchable.utils.click import ignorable_error
10+
from launchable.utils.env_keys import REPORT_ERROR_KEY
11+
from launchable.utils.launchable_client import LaunchableClient
12+
from launchable.utils.tracking import Tracking, TrackingClient
13+
14+
from ...utils.commands import Command
15+
16+
17+
@click.group(help="Early flake detection")
18+
@click.option(
19+
'--session',
20+
'session',
21+
help='In the format builds/<build-name>/test_sessions/<test-session-id>',
22+
type=str,
23+
required=True
24+
)
25+
@click.option(
26+
'--confidence',
27+
help='Confidence level for flake detection',
28+
type=click.Choice(['low', 'medium', 'high'], case_sensitive=False),
29+
required=True,
30+
)
31+
@click.pass_context
32+
def flake_detection(ctx, confidence, session):
33+
tracking_client = TrackingClient(Command.FLAKE_DETECTION, app=ctx.obj)
34+
client = LaunchableClient(app=ctx.obj, tracking_client=tracking_client, test_runner=ctx.invoked_subcommand)
35+
session_id = None
36+
try:
37+
session_id = find_or_create_session(
38+
context=ctx,
39+
session=session,
40+
build_name=None,
41+
tracking_client=tracking_client
42+
)
43+
except click.UsageError as e:
44+
click.echo(click.style(str(e), fg="red"), err=True)
45+
sys.exit(1)
46+
except Exception as e:
47+
tracking_client.send_error_event(
48+
event_name=Tracking.ErrorEvent.INTERNAL_CLI_ERROR,
49+
stack_trace=str(e),
50+
)
51+
if os.getenv(REPORT_ERROR_KEY):
52+
raise e
53+
else:
54+
click.echo(ignorable_error(e), err=True)
55+
if session_id is None:
56+
return
57+
58+
class FlakeDetection(TestPathWriter):
59+
def __init__(self, app: Application):
60+
super(FlakeDetection, self).__init__(app=app)
61+
62+
def run(self):
63+
test_paths = []
64+
try:
65+
res = client.request(
66+
"get",
67+
"retry/flake-detection",
68+
params={
69+
"confidence": confidence.upper(),
70+
"session-id": os.path.basename(session_id),
71+
"test-runner": ctx.invoked_subcommand})
72+
res.raise_for_status()
73+
test_paths = res.json().get("testPaths", [])
74+
except Exception as e:
75+
tracking_client.send_error_event(
76+
event_name=Tracking.ErrorEvent.INTERNAL_CLI_ERROR,
77+
stack_trace=str(e),
78+
)
79+
if os.getenv(REPORT_ERROR_KEY):
80+
raise e
81+
else:
82+
click.echo(ignorable_error(e), err=True)
83+
if test_paths:
84+
self.print(test_paths)
85+
86+
ctx.obj = FlakeDetection(app=ctx.obj)

launchable/test_runners/bazel.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ def subset(client):
3333
split_subset = launchable.CommonSplitSubsetImpls(__name__,
3434
formatter=lambda x: x[0]['name'] + ":" + x[1]['name']).split_subset()
3535

36+
launchable.CommonFlakeDetectionImpls(__name__, formatter=lambda x: x[0]['name'] + ":" + x[1]['name']).flake_detection()
37+
3638

3739
@click.argument('workspace', required=True)
3840
@click.option('--build-event-json', 'build_event_json_files', help="set file path generated by --build_event_json_file",

launchable/test_runners/file.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,5 @@ def find_filename():
5252

5353

5454
split_subset = launchable.CommonSplitSubsetImpls(__name__).split_subset()
55+
56+
launchable.CommonFlakeDetectionImpls(__name__).flake_detection()

launchable/test_runners/launchable.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
import click
77

88
from launchable.commands.record.tests import tests as record_tests_cmd
9+
from launchable.commands.retry.flake_detection import flake_detection as flake_detection_cmd
910
from launchable.commands.split_subset import split_subset as split_subset_cmd
1011
from launchable.commands.subset import subset as subset_cmd
12+
from launchable.testpath import unparse_test_path
1113

1214

1315
def cmdname(m):
@@ -43,6 +45,10 @@ def subset(f):
4345
record.tests = lambda f: wrap(f, record_tests_cmd)
4446

4547

48+
def flake_detection(f):
49+
return wrap(f, flake_detection_cmd)
50+
51+
4652
def split_subset(f):
4753
return wrap(f, split_subset_cmd)
4854

@@ -165,3 +171,27 @@ def split_subset(client):
165171
client.run()
166172

167173
return wrap(split_subset, split_subset_cmd, self.cmdname)
174+
175+
176+
class CommonFlakeDetectionImpls:
177+
def __init__(
178+
self,
179+
module_name,
180+
formatter=unparse_test_path,
181+
seperator="\n",
182+
):
183+
self.cmdname = cmdname(module_name)
184+
self._formatter = formatter
185+
self._separator = seperator
186+
187+
def flake_detection(self):
188+
def flake_detection(client):
189+
if self._formatter:
190+
client.formatter = self._formatter
191+
192+
if self._separator:
193+
client.separator = self._separator
194+
195+
client.run()
196+
197+
return wrap(flake_detection, flake_detection_cmd, self.cmdname)

launchable/test_runners/raw.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ def subset(client, test_path_file):
4747

4848
split_subset = launchable.CommonSplitSubsetImpls(__name__, formatter=unparse_test_path, seperator='\n').split_subset()
4949

50+
launchable.CommonFlakeDetectionImpls(__name__).flake_detection()
51+
5052

5153
@click.argument('test_result_files', required=True, type=click.Path(exists=True), nargs=-1)
5254
@launchable.record.tests

launchable/utils/commands.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class Command(Enum):
88
RECORD_SESSION = 'RECORD_SESSION'
99
SUBSET = 'SUBSET'
1010
COMMIT = 'COMMIT'
11+
FLAKE_DETECTION = 'FLAKE_DETECTION'
1112

1213
def display_name(self):
1314
return self.value.lower().replace('_', ' ')

0 commit comments

Comments
 (0)