Skip to content

Commit c9bf35c

Browse files
committed
[GR-14051] gate unittests regressions
PullRequest: graalpython/417
2 parents 517b4bb + 5849686 commit c9bf35c

File tree

2 files changed

+80
-10
lines changed

2 files changed

+80
-10
lines changed

ci.jsonnet

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
overlay: "e08ca6c848943a04c5e46d54272ff927ff3d51f7",
2+
overlay: "d20cc2abdeb3cfb022e1a8035e40e350e5cfe5fc",
33

44
// ======================================================================================================
55
//

graalpython/com.oracle.graal.python.test/src/python_unittests.py

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -74,6 +74,9 @@
7474
PTRN_JAVA_EXCEPTION = re.compile(r'^(?P<exception>com\.oracle\.[^:]*):(?P<message>.*)')
7575
PTRN_MODULE_NOT_FOUND = re.compile(r'.*ModuleNotFound: \'(?P<module>.*)\'\..*', re.DOTALL)
7676
PTRN_IMPORT_ERROR = re.compile(r".*cannot import name \'(?P<module>.*)\'.*", re.DOTALL)
77+
PTRN_REMOTE_HOST = re.compile(r"(?P<user>\w+)@(?P<host>[\w.]+):(?P<path>.+)")
78+
PTRN_VALID_CSV_NAME = re.compile(r"unittests-\d{4}-\d{2}-\d{2}.csv")
79+
7780

7881
# ----------------------------------------------------------------------------------------------------------------------
7982
#
@@ -192,6 +195,27 @@ def _sorter(iterable):
192195
return unittests[:limit] if limit else unittests
193196

194197

198+
def get_remote_host(scp_path):
199+
match = re.match(PTRN_REMOTE_HOST, scp_path)
200+
return match.group('user'), match.group('host'), match.group('path')
201+
202+
203+
def ssh_ls(scp_path):
204+
user, host, path = get_remote_host(scp_path)
205+
cmd = ['ssh', '{}@{}'.format(user, host), 'ls', '-l', path]
206+
return map(lambda l: l.split()[-1], _run_cmd(cmd)[1].splitlines())
207+
208+
209+
def read_csv(path):
210+
rows = []
211+
with open(path, "r") as CSV_FILE:
212+
reader = csv.reader(CSV_FILE)
213+
headers = next(reader)[1:]
214+
for row in reader:
215+
rows.append(row)
216+
return rows
217+
218+
195219
# ----------------------------------------------------------------------------------------------------------------------
196220
#
197221
# result (output processing)
@@ -597,7 +621,7 @@ def format_val(row, k):
597621
elif k == Col.NUM_TESTS:
598622
_class = "text-dark"
599623
else:
600-
_class = "text-danger" if value < 0 else "text-muted"
624+
_class = "text-danger" if value and value < 0 else "text-muted"
601625
return '<span class="{} h6"><b>{}</b></span>'.format(_class, value)
602626

603627
_tbody = '\n'.join([
@@ -694,6 +718,10 @@ def main(prog, args):
694718
parser.add_argument("-o", "--only_tests", help="Run only these unittests (comma sep values).", default=None)
695719
parser.add_argument("-s", "--skip_tests", help="Run all unittests except (comma sep values)."
696720
"the only_tets option takes precedence", default=None)
721+
parser.add_argument("-r", "--regression_running_tests", help="Regression threshold for running tests.", type=float,
722+
default=None)
723+
parser.add_argument("-g", "--gate", help="Run in gate mode (Skip cpython runs; Do not upload results; "
724+
"Detect regressions).", action="store_true")
697725
parser.add_argument("path", help="Path to store the csv output and logs to.", nargs='?', default=None)
698726

699727
global flags
@@ -708,6 +736,12 @@ def main(prog, args):
708736
else:
709737
log("[INFO] results will not be saved remotely")
710738

739+
if flags.gate:
740+
log("[INFO] running in gate mode")
741+
if not flags.regression_running_tests:
742+
log("[WARNING] --regression_running_tests not set while in gate mode. "
743+
"Regression detection will not be performed")
744+
711745
def _fmt(t):
712746
t = t.strip()
713747
return os.path.join(flags.tests_path, t if t.endswith(".py") else t + ".py")
@@ -720,15 +754,22 @@ def _fmt(t):
720754
unittests = get_unittests(flags.tests_path, limit=flags.limit, skip_tests=skip_tests)
721755

722756
# get cpython stats
723-
log(HR)
724-
log("[INFO] get cpython stats")
725-
cpy_results = run_unittests(unittests, 60 * 5, with_cpython=True)
726-
cpy_stats = process_output('\n'.join(cpy_results))[-1]
757+
if not flags.gate:
758+
log(HR)
759+
log("[INFO] get cpython stats")
760+
cpy_results = run_unittests(unittests, 60 * 5, with_cpython=True)
761+
cpy_stats = process_output('\n'.join(cpy_results))[-1]
762+
# handle the timeout
763+
timeout = flags.timeout if flags.timeout else None
764+
else:
765+
cpy_stats = None
766+
# handle the timeout
767+
timeout = flags.timeout if flags.timeout else 60 * 5 # 5 minutes if no value specified (in gate mode only)
727768

728769
# get graalpython stats
729770
log(HR)
730771
log("[INFO] get graalpython stats")
731-
results = run_unittests(unittests, flags.timeout, with_cpython=False)
772+
results = run_unittests(unittests, timeout, with_cpython=False)
732773
txt_report_path = file_name(TXT_RESULTS_NAME, current_date)
733774
output = save_as_txt(txt_report_path, results)
734775

@@ -749,15 +790,44 @@ def _fmt(t):
749790
log("[JAVA ISSUES] \n{}", pformat(dict(java_issues)))
750791

751792
html_report_path = file_name(HTML_RESULTS_NAME, current_date)
752-
save_as_html(html_report_path, rows, totals, missing_modules, cannot_import_modules, java_issues, current_date)
793+
if not flags.gate:
794+
save_as_html(html_report_path, rows, totals, missing_modules, cannot_import_modules, java_issues, current_date)
753795

754-
if flags.path:
796+
if not flags.gate and flags.path:
755797
log("[SAVE] saving results to {} ... ", flags.path)
756798
scp(txt_report_path, flags.path)
757799
scp(csv_report_path, flags.path)
758800
scp(html_report_path, flags.path)
759801

802+
gate_failed = False
803+
if flags.gate and flags.regression_running_tests:
804+
log("[REGRESSION] detecting regression, acceptance threshold = {}%".format(
805+
flags.regression_running_tests * 100))
806+
csv_files = list(filter(lambda entry: True if PTRN_VALID_CSV_NAME.match(entry) else False, ssh_ls(flags.path)))
807+
last_csv = csv_files[-1]
808+
# log('\n'.join(csv_files))
809+
# read the remote csv and extract stats
810+
log("[REGRESSION] comparing against: {}".format(last_csv))
811+
scp('{}/{}'.format(flags.path, last_csv), '.', destination_name=last_csv)
812+
rows = read_csv(last_csv)
813+
prev_totals = {
814+
Col.NUM_TESTS: int(rows[-1][1]),
815+
Col.NUM_FAILS: int(rows[-1][2]),
816+
Col.NUM_ERRORS: int(rows[-1][3]),
817+
Col.NUM_SKIPPED: int(rows[-1][4]),
818+
Col.NUM_PASSES: int(rows[-1][5]),
819+
}
820+
print(prev_totals)
821+
if float(totals[Col.NUM_TESTS]) < float(prev_totals[Col.NUM_TESTS]) * (1.0 - flags.regression_running_tests):
822+
log("[REGRESSION] REGRESSION DETECTED, passed {} tests vs {} from {}".format(
823+
totals[Col.NUM_TESTS], prev_totals[Col.NUM_TESTS], last_csv))
824+
gate_failed = True
825+
else:
826+
log("[REGRESSION] no regression detected")
827+
760828
log("[DONE]")
829+
if flags.gate and gate_failed:
830+
exit(1)
761831

762832

763833
if __name__ == "__main__":

0 commit comments

Comments
 (0)