Skip to content

Commit f8fa092

Browse files
committed
Do not shortcut checking; report 'test_time=-1.0' if 'INDETERMINATE'
1 parent c04b360 commit f8fa092

File tree

2 files changed

+156
-29
lines changed

2 files changed

+156
-29
lines changed

com.oracle.truffle.r.test.packages/pkgtest/__init__.py

Lines changed: 141 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -308,8 +308,7 @@ def __call__(self, data):
308308
if time_match:
309309
pkg_name = time_match.group(1)
310310
test_time = time_match.group(2)
311-
with open(join(_pkg_testdir('fastr', pkg_name), 'test_time'), 'w') as f:
312-
f.write(test_time)
311+
get_pkg_test_status(self.test_info, pkg_name).test_time = test_time
313312

314313

315314
class TestFileStatus:
@@ -318,7 +317,8 @@ class TestFileStatus:
318317
The latter means that the file had a .fail extension.
319318
'''
320319

321-
def __init__(self, status, abspath):
320+
def __init__(self, test_status, status, abspath):
321+
self.test_status = test_status
322322
self.status = status
323323
self.abspath = abspath
324324
if status == "OK":
@@ -331,6 +331,17 @@ def __init__(self, status, abspath):
331331
else:
332332
raise ValueError('Invalid test file status: %s (allowed: "OK", "FAILED")' % status)
333333

334+
def set_report(self, ok, skipped, failed):
335+
self.report = ok, skipped, failed
336+
337+
def get_report(self):
338+
if self.test_status.is_status_indeterminate():
339+
ok, skipped, failed = self.report
340+
return ok, 0, skipped + failed
341+
else:
342+
return self.report
343+
344+
334345

335346
class TestStatus:
336347
'''Records the test status of a package. status ends up as either "OK" or "FAILED",
@@ -342,17 +353,57 @@ class TestStatus:
342353
def __init__(self):
343354
self.status = "UNKNOWN"
344355
self.testfile_outputs = dict()
356+
self.test_time = 0.0
357+
358+
def set_status_indeterminate(self):
359+
self.status = "INDETERMINATE"
360+
self.test_time = -1.0
361+
362+
def is_status_indeterminate(self):
363+
if self.status == "INDETERMINATE":
364+
assert self.test_time == -1.0
365+
return True
366+
return False
367+
368+
def set_status_code(self, new_status):
369+
if self.status == "INDETERMINATE":
370+
assert self.test_time < 0.0
371+
elif self.status == "FAILED" and new_status == "INDETERMINATE":
372+
self.set_status_indeterminate()
373+
else:
374+
assert self.status == "OK" or self.status == "FAILED" or self.status == "UNKNOWN"
375+
assert new_status in ["OK", "FAILED", "INDETERMINATE"]
376+
if new_status == "INDETERMINATE":
377+
self.set_status_indeterminate()
378+
else:
379+
self.status = new_status
380+
381+
def __str__(self):
382+
return "Test Status:\n%s (time: %s s)" % (self.status, self.test_time)
345383

346384

347385
def _pkg_testdir(rvm, pkg_name):
348386
return join(get_fastr_repo_dir(), 'test.' + rvm, pkg_name)
349387

350388

389+
def get_pkg_test_status(test_info, pkg_name):
390+
'''
391+
Get the test status (class TestStatus) for a given package.
392+
It is created on demand if does not exist yet.
393+
'''
394+
test_status = test_info.get(pkg_name)
395+
if not test_status:
396+
test_status = TestStatus()
397+
test_info[pkg_name] = test_status
398+
return test_status
399+
400+
351401
def _get_test_outputs(rvm, pkg_name, test_info):
352402
pkg_testdir = _pkg_testdir(rvm, pkg_name)
403+
test_status = None
353404
for root, _, files in os.walk(pkg_testdir):
354-
if not test_info.has_key(pkg_name):
355-
test_info[pkg_name] = TestStatus()
405+
if not test_status:
406+
test_status = get_pkg_test_status(test_info, pkg_name)
356407
for f in files:
357408
ext = os.path.splitext(f)[1]
358409
# suppress .pdf's for now (we can't compare them)
@@ -370,7 +421,7 @@ def _get_test_outputs(rvm, pkg_name, test_info):
370421

371422
absfile = join(root, f)
372423
relfile = relpath(absfile, pkg_testdir)
373-
test_info[pkg_name].testfile_outputs[relfile] = TestFileStatus(status, absfile)
424+
test_status.testfile_outputs[relfile] = TestFileStatus(test_status, status, absfile)
374425

375426

376427
def _args_to_forward_to_gnur(args):
@@ -451,24 +502,27 @@ def _failed_outputs(outputs):
451502
# In addition to the similar comment for GNU R, this can happen
452503
# if, say, the JVM crashes (possible with native code packages)
453504
logging.info("{0}: FastR test had .fail outputs".format(pkg))
454-
fastr_test_status.status = "FAILED"
505+
fastr_test_status.set_status_code("FAILED")
455506

456507
# Now for each successful GNU R output we compare content (assuming FastR didn't fail)
457508
for gnur_test_output_relpath, gnur_testfile_status in gnur_outputs.iteritems():
458509
# Can't compare if either GNUR or FastR failed
459510
if gnur_testfile_status.status == "FAILED":
460-
fastr_test_status.status = "INDETERMINATE"
461-
break
511+
fastr_test_status.set_status_code("INDETERMINATE")
512+
continue
462513

463514
if not gnur_test_output_relpath in fastr_outputs:
464515
# FastR crashed on this test
465-
fastr_test_status.status = "FAILED"
516+
fastr_test_status.set_status_code("FAILED")
466517
logging.info("{0}: FastR is missing output file: {1}".format(pkg, gnur_test_output_relpath))
467-
break
518+
continue
468519

469520
fastr_testfile_status = fastr_outputs[gnur_test_output_relpath]
470521
if fastr_testfile_status.status == "FAILED":
471-
break
522+
# Don't do fuzzy-compare.
523+
# It may only be fuzzy-compare because if we would have a test framework, the status would not be
524+
# "FAILED" since a test framework cannot produce ".fail" output files.
525+
continue
472526

473527
gnur_content = None
474528
with open(gnur_testfile_status.abspath) as f:
@@ -499,18 +553,18 @@ def _failed_outputs(outputs):
499553

500554
if fastr_invalid_numbers or total_fastr > total_gnur:
501555
# If FastR's numbers are invalid or GnuR ran fewer tests than FastR, we cannot trust the FastR numbers
502-
fastr_testfile_status.report = 0, gnur_skipped, gnur_ok + gnur_failed
503-
fastr_test_status.status = "FAILED"
556+
fastr_testfile_status.set_report(0, gnur_skipped, gnur_ok + gnur_failed)
557+
fastr_test_status.set_status_code("FAILED")
504558
fastr_testfile_status.status = "FAILED"
505559
elif total_fastr < total_gnur:
506560
# If FastR ran fewer tests than GnuR, we complement the missing ones as failing
507-
fastr_testfile_status.report = ok, skipped, failed + (total_gnur - total_fastr)
508-
fastr_test_status.status = "FAILED"
561+
fastr_testfile_status.set_report(ok, skipped, failed + (total_gnur - total_fastr))
562+
fastr_test_status.set_status_code("FAILED")
509563
fastr_testfile_status.status = "FAILED"
510564
else:
511565
# The total numbers are equal, so we are fine.
512566
fastr_testfile_status.status = "OK"
513-
fastr_testfile_status.report = ok, skipped, failed
567+
fastr_testfile_status.set_report(ok, skipped, failed)
514568
else:
515569
result, n_tests_passed, n_tests_failed = fuzzy_compare(gnur_content, fastr_content,
516570
gnur_testfile_status.abspath,
@@ -519,23 +573,21 @@ def _failed_outputs(outputs):
519573
dump_preprocessed=get_opts().dump_preprocessed)
520574
if result == -1:
521575
logging.info("{0}: content malformed: {1}".format(pkg, gnur_test_output_relpath))
522-
fastr_test_status.status = "INDETERMINATE"
576+
fastr_test_status.set_status_code("INDETERMINATE")
523577
# we don't know how many tests are in there, so consider the whole file to be one big skipped test
524-
fastr_testfile_status.report = 0, 1, 0
525-
# break
578+
fastr_testfile_status.set_report(0, 1, 0)
526579
elif result != 0:
527-
fastr_test_status.status = "FAILED"
580+
fastr_test_status.set_status_code("FAILED")
528581
fastr_testfile_status.status = "FAILED"
529-
fastr_testfile_status.report = n_tests_passed, 0, n_tests_failed
582+
fastr_testfile_status.set_report(n_tests_passed, 0, n_tests_failed)
530583
logging.info("{0}: FastR output mismatch: {1}".format(pkg, gnur_test_output_relpath))
531-
# break
532584
else:
533585
fastr_testfile_status.status = "OK"
534-
fastr_testfile_status.report = n_tests_passed, 0, n_tests_failed
586+
fastr_testfile_status.set_report(n_tests_passed, 0, n_tests_failed)
535587

536588
# we started out as UNKNOWN
537589
if not (fastr_test_status.status == "INDETERMINATE" or fastr_test_status.status == "FAILED"):
538-
fastr_test_status.status = "OK"
590+
fastr_test_status.set_status_code("OK")
539591

540592
# write out a file with the test status for each output (that exists)
541593
with open(join(_pkg_testdir('fastr', pkg), 'testfile_status'), 'w') as f:
@@ -546,21 +598,25 @@ def _failed_outputs(outputs):
546598
test_output_file = join(_pkg_testdir('fastr', pkg), relpath)
547599

548600
if os.path.exists(test_output_file):
549-
ok, skipped, failed = fastr_testfile_status.report
601+
ok, skipped, failed = fastr_testfile_status.get_report()
550602
f.write("{0} {1} {2} {3}\n".format(relpath, ok, skipped, failed))
551603
elif fastr_testfile_status.status == "FAILED":
552604
# In case of status == "FAILED", also try suffix ".fail" because we just do not know if the test
553605
# failed and finished or just never finished.
554606
relpath_fail = fastr_relpath + ".fail"
555607
test_output_file_fail = join(_pkg_testdir('fastr', pkg), relpath_fail)
556608
if os.path.exists(test_output_file_fail):
557-
ok, skipped, failed = fastr_testfile_status.report
609+
ok, skipped, failed = fastr_testfile_status.get_report()
558610
f.write("{0} {1} {2} {3}\n".format(relpath_fail, ok, skipped, failed))
559611
else:
560612
logging.info("File {0} or {1} does not exist".format(test_output_file, test_output_file_fail))
561613
else:
562614
logging.info("File {0} does not exist".format(test_output_file))
563615

616+
617+
with open(join(_pkg_testdir('fastr', pkg), 'test_time'), 'w') as f:
618+
f.write(str(fastr_test_status.test_time))
619+
564620
logging.info('END checking ' + pkg)
565621

566622

@@ -668,6 +724,64 @@ def installpkgs(args, **kwargs):
668724
return _installpkgs(rargs)
669725

670726

727+
def pkgtest_check(args):
728+
'''
729+
This function allows to do only the checking part on an existing test output
730+
(i.e. 'test.fastr' and 'test.gnur' directories).
731+
It will try to re-create
732+
:param args:
733+
:return:
734+
'''
735+
parser = argparse.ArgumentParser(prog="pkgtest", description='FastR package testing.')
736+
parser.add_argument('--fastr-home', metavar='FASTR_HOME', dest="fastr_home", type=str, default=None,
737+
required=True, help='The FastR standalone repo home directory (required).')
738+
parser.add_argument('-v', '--verbose', dest="verbose", action="store_const", const=1, default=0,
739+
help='Do verbose logging.')
740+
parser.add_argument('-V', '--very-verbose', dest="verbose", action="store_const", const=2,
741+
help='Do verbose logging.')
742+
parser.add_argument('--dump-preprocessed', dest="dump_preprocessed", action="store_true",
743+
help='Dump processed output files where replacement filters have been applied.')
744+
parser.add_argument('pkg_name', metavar="PKG_NAME",
745+
help='Package name for checking.')
746+
747+
import util
748+
_opts = parser.parse_args(args=args, namespace=util.get_opts())
749+
750+
log_format = '%(message)s'
751+
if _opts.verbose == 1:
752+
log_level = logging.DEBUG
753+
elif _opts.verbose == 2:
754+
log_level = VERY_VERBOSE
755+
else:
756+
log_level = logging.INFO
757+
logging.basicConfig(level=log_level, format=log_format)
758+
759+
# also log to console
760+
console_handler = logging.StreamHandler(stream=sys.stdout)
761+
console_handler.setLevel(log_level)
762+
console_handler.setFormatter(logging.Formatter(log_format))
763+
logging.getLogger("").addHandler(console_handler)
764+
765+
# if not :
766+
# print("Missing required argument 'pkg_name'")
767+
# return 1
768+
769+
pkg_name = _opts.pkg_name
770+
fastr_testdir = _pkg_testdir("fastr", pkg_name)
771+
if not os.path.isdir(fastr_testdir):
772+
print("test directory '%s' does not exist" % fastr_testdir)
773+
return 1
774+
775+
gnur_testdir = _pkg_testdir("gnur", pkg_name)
776+
if not os.path.isdir(gnur_testdir):
777+
print("test directory '%s' does not exist" % gnur_testdir)
778+
return 1
779+
780+
fastr_test_info = dict()
781+
_get_test_outputs("fastr", pkg_name, fastr_test_info)
782+
return _set_test_status(fastr_test_info)
783+
784+
671785
def pkgtest_cmp(args):
672786
gnur_filename = args[0]
673787
fastr_filename = args[1]

com.oracle.truffle.r.test.packages/pkgtest/__main__.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@
2121
# questions.
2222
#
2323
import sys
24-
from . import pkgtest
24+
from . import pkgtest, pkgtest_cmp, pkgtest_check
2525

26-
pkgtest(sys.argv)
26+
rc = 1
27+
if sys.argv:
28+
# argv[0] will be the script/executable
29+
command = sys.argv[1]
30+
31+
if command == "compare":
32+
rc = pkgtest_cmp(sys.argv[2:])
33+
elif command == "check":
34+
rc = pkgtest_check(sys.argv[2:])
35+
else:
36+
rc = pkgtest(sys.argv)
37+
38+
# ensure we propagate the exit code
39+
sys.exit(rc)

0 commit comments

Comments
 (0)