Skip to content

Commit 91028e4

Browse files
committed
[GR-15049] Pkgtest: do not shortcut test output checking.
PullRequest: fastr/1999
2 parents 11760ca + 2fe2870 commit 91028e4

File tree

4 files changed

+165
-33
lines changed

4 files changed

+165
-33
lines changed

com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Getwd.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -30,6 +30,7 @@
3030
import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
3131
import com.oracle.truffle.r.runtime.builtins.RBuiltin;
3232
import com.oracle.truffle.r.runtime.data.RDataFactory;
33+
import com.oracle.truffle.r.runtime.data.RNull;
3334
import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
3435
import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
3536

@@ -42,6 +43,9 @@ public abstract class Getwd extends RBuiltinNode.Arg0 {
4243
@TruffleBoundary
4344
protected Object getwd() {
4445
String result = getwdNode.execute();
45-
return RDataFactory.createStringVector(result);
46+
if (result != null) {
47+
return RDataFactory.createStringVector(result);
48+
}
49+
return RNull.instance;
4650
}
4751
}

com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Setwd.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -35,6 +35,7 @@
3535
import com.oracle.truffle.r.runtime.RError;
3636
import com.oracle.truffle.r.runtime.Utils;
3737
import com.oracle.truffle.r.runtime.builtins.RBuiltin;
38+
import com.oracle.truffle.r.runtime.data.RNull;
3839
import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
3940
import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
4041

@@ -60,7 +61,7 @@ protected Object setwd(String path) {
6061
} else {
6162
String nwdAbs = getwdNode.execute();
6263
Utils.updateCurwd(nwdAbs);
63-
return owd;
64+
return owd != null ? owd : RNull.instance;
6465
}
6566
}
6667
}

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

Lines changed: 141 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -312,8 +312,7 @@ def __call__(self, data):
312312
if time_match:
313313
pkg_name = time_match.group(1)
314314
test_time = time_match.group(2)
315-
with open(join(_pkg_testdir('fastr', pkg_name), 'test_time'), 'w') as f:
316-
f.write(test_time)
315+
get_pkg_test_status(self.test_info, pkg_name).test_time = test_time
317316

318317

319318
class TestFileStatus:
@@ -322,7 +321,8 @@ class TestFileStatus:
322321
The latter means that the file had a .fail extension.
323322
'''
324323

325-
def __init__(self, status, abspath):
324+
def __init__(self, test_status, status, abspath):
325+
self.test_status = test_status
326326
self.status = status
327327
self.abspath = abspath
328328
if status == "OK":
@@ -335,6 +335,17 @@ def __init__(self, status, abspath):
335335
else:
336336
raise ValueError('Invalid test file status: %s (allowed: "OK", "FAILED")' % status)
337337

338+
def set_report(self, ok, skipped, failed):
339+
self.report = ok, skipped, failed
340+
341+
def get_report(self):
342+
if self.test_status.is_status_indeterminate():
343+
ok, skipped, failed = self.report
344+
return ok, 0, skipped + failed
345+
else:
346+
return self.report
347+
348+
338349

339350
class TestStatus:
340351
'''Records the test status of a package. status ends up as either "OK" or "FAILED",
@@ -346,17 +357,57 @@ class TestStatus:
346357
def __init__(self):
347358
self.status = "UNKNOWN"
348359
self.testfile_outputs = dict()
360+
self.test_time = 0.0
361+
362+
def set_status_indeterminate(self):
363+
self.status = "INDETERMINATE"
364+
self.test_time = -1.0
365+
366+
def is_status_indeterminate(self):
367+
if self.status == "INDETERMINATE":
368+
assert self.test_time == -1.0
369+
return True
370+
return False
371+
372+
def set_status_code(self, new_status):
373+
if self.status == "INDETERMINATE":
374+
assert self.test_time < 0.0
375+
elif self.status == "FAILED" and new_status == "INDETERMINATE":
376+
self.set_status_indeterminate()
377+
else:
378+
assert self.status == "OK" or self.status == "FAILED" or self.status == "UNKNOWN"
379+
assert new_status in ["OK", "FAILED", "INDETERMINATE"]
380+
if new_status == "INDETERMINATE":
381+
self.set_status_indeterminate()
382+
else:
383+
self.status = new_status
384+
385+
def __str__(self):
386+
return "Test Status:\n%s (time: %s s)" % (self.status, self.test_time)
349387

350388

351389
def _pkg_testdir(rvm, pkg_name):
352390
return join(get_fastr_repo_dir(), 'test.' + rvm, pkg_name)
353391

354392

393+
def get_pkg_test_status(test_info, pkg_name):
394+
'''
395+
Get the test status (class TestStatus) for a given package.
396+
It is created on demand if does not exist yet.
397+
'''
398+
test_status = test_info.get(pkg_name)
399+
if not test_status:
400+
test_status = TestStatus()
401+
test_info[pkg_name] = test_status
402+
return test_status
403+
404+
355405
def _get_test_outputs(rvm, pkg_name, test_info):
356406
pkg_testdir = _pkg_testdir(rvm, pkg_name)
407+
test_status = None
357408
for root, _, files in os.walk(pkg_testdir):
358-
if not test_info.has_key(pkg_name):
359-
test_info[pkg_name] = TestStatus()
409+
if not test_status:
410+
test_status = get_pkg_test_status(test_info, pkg_name)
360411
for f in files:
361412
ext = os.path.splitext(f)[1]
362413
# suppress .pdf's for now (we can't compare them)
@@ -374,7 +425,7 @@ def _get_test_outputs(rvm, pkg_name, test_info):
374425

375426
absfile = join(root, f)
376427
relfile = relpath(absfile, pkg_testdir)
377-
test_info[pkg_name].testfile_outputs[relfile] = TestFileStatus(status, absfile)
428+
test_status.testfile_outputs[relfile] = TestFileStatus(test_status, status, absfile)
378429

379430

380431
def _args_to_forward_to_gnur(args):
@@ -455,24 +506,27 @@ def _failed_outputs(outputs):
455506
# In addition to the similar comment for GNU R, this can happen
456507
# if, say, the JVM crashes (possible with native code packages)
457508
logging.info("{0}: FastR test had .fail outputs".format(pkg))
458-
fastr_test_status.status = "FAILED"
509+
fastr_test_status.set_status_code("FAILED")
459510

460511
# Now for each successful GNU R output we compare content (assuming FastR didn't fail)
461512
for gnur_test_output_relpath, gnur_testfile_status in gnur_outputs.iteritems():
462513
# Can't compare if either GNUR or FastR failed
463514
if gnur_testfile_status.status == "FAILED":
464-
fastr_test_status.status = "INDETERMINATE"
465-
break
515+
fastr_test_status.set_status_code("INDETERMINATE")
516+
continue
466517

467518
if not gnur_test_output_relpath in fastr_outputs:
468519
# FastR crashed on this test
469-
fastr_test_status.status = "FAILED"
520+
fastr_test_status.set_status_code("FAILED")
470521
logging.info("{0}: FastR is missing output file: {1}".format(pkg, gnur_test_output_relpath))
471-
break
522+
continue
472523

473524
fastr_testfile_status = fastr_outputs[gnur_test_output_relpath]
474525
if fastr_testfile_status.status == "FAILED":
475-
break
526+
# Don't do fuzzy-compare.
527+
# It may only be fuzzy-compare because if we would have a test framework, the status would not be
528+
# "FAILED" since a test framework cannot produce ".fail" output files.
529+
continue
476530

477531
gnur_content = None
478532
with open(gnur_testfile_status.abspath) as f:
@@ -503,18 +557,18 @@ def _failed_outputs(outputs):
503557

504558
if fastr_invalid_numbers or total_fastr > total_gnur:
505559
# If FastR's numbers are invalid or GnuR ran fewer tests than FastR, we cannot trust the FastR numbers
506-
fastr_testfile_status.report = 0, gnur_skipped, gnur_ok + gnur_failed
507-
fastr_test_status.status = "FAILED"
560+
fastr_testfile_status.set_report(0, gnur_skipped, gnur_ok + gnur_failed)
561+
fastr_test_status.set_status_code("FAILED")
508562
fastr_testfile_status.status = "FAILED"
509563
elif total_fastr < total_gnur:
510564
# If FastR ran fewer tests than GnuR, we complement the missing ones as failing
511-
fastr_testfile_status.report = ok, skipped, failed + (total_gnur - total_fastr)
512-
fastr_test_status.status = "FAILED"
565+
fastr_testfile_status.set_report(ok, skipped, failed + (total_gnur - total_fastr))
566+
fastr_test_status.set_status_code("FAILED")
513567
fastr_testfile_status.status = "FAILED"
514568
else:
515569
# The total numbers are equal, so we are fine.
516570
fastr_testfile_status.status = "OK"
517-
fastr_testfile_status.report = ok, skipped, failed
571+
fastr_testfile_status.set_report(ok, skipped, failed)
518572
else:
519573
result, n_tests_passed, n_tests_failed = fuzzy_compare(gnur_content, fastr_content,
520574
gnur_testfile_status.abspath,
@@ -523,23 +577,21 @@ def _failed_outputs(outputs):
523577
dump_preprocessed=get_opts().dump_preprocessed)
524578
if result == -1:
525579
logging.info("{0}: content malformed: {1}".format(pkg, gnur_test_output_relpath))
526-
fastr_test_status.status = "INDETERMINATE"
580+
fastr_test_status.set_status_code("INDETERMINATE")
527581
# we don't know how many tests are in there, so consider the whole file to be one big skipped test
528-
fastr_testfile_status.report = 0, 1, 0
529-
# break
582+
fastr_testfile_status.set_report(0, 1, 0)
530583
elif result != 0:
531-
fastr_test_status.status = "FAILED"
584+
fastr_test_status.set_status_code("FAILED")
532585
fastr_testfile_status.status = "FAILED"
533-
fastr_testfile_status.report = n_tests_passed, 0, n_tests_failed
586+
fastr_testfile_status.set_report(n_tests_passed, 0, n_tests_failed)
534587
logging.info("{0}: FastR output mismatch: {1}".format(pkg, gnur_test_output_relpath))
535-
# break
536588
else:
537589
fastr_testfile_status.status = "OK"
538-
fastr_testfile_status.report = n_tests_passed, 0, n_tests_failed
590+
fastr_testfile_status.set_report(n_tests_passed, 0, n_tests_failed)
539591

540592
# we started out as UNKNOWN
541593
if not (fastr_test_status.status == "INDETERMINATE" or fastr_test_status.status == "FAILED"):
542-
fastr_test_status.status = "OK"
594+
fastr_test_status.set_status_code("OK")
543595

544596
# write out a file with the test status for each output (that exists)
545597
with open(join(_pkg_testdir('fastr', pkg), 'testfile_status'), 'w') as f:
@@ -550,21 +602,25 @@ def _failed_outputs(outputs):
550602
test_output_file = join(_pkg_testdir('fastr', pkg), relpath)
551603

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

620+
621+
with open(join(_pkg_testdir('fastr', pkg), 'test_time'), 'w') as f:
622+
f.write(str(fastr_test_status.test_time))
623+
568624
logging.info('END checking ' + pkg)
569625

570626

@@ -672,6 +728,64 @@ def installpkgs(args, **kwargs):
672728
return _installpkgs(rargs)
673729

674730

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