Fix log-format reporter ignoring install errors (#25961)

When running `spack install --log-format junit|cdash ...`, install
errors were ignored. This made spack continue building dependents of
failed install, ignoring `--fail-fast`, and exit 0 at the end.
This commit is contained in:
Jordan Galby 2021-11-09 16:47:32 +01:00 committed by GitHub
parent 8a836213f7
commit 6e9c0a8155
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 48 additions and 31 deletions

View file

@ -151,6 +151,22 @@ def wrapper(instance, *args, **kwargs):
'installed_from_binary_cache': False 'installed_from_binary_cache': False
} }
# Append the package to the correct spec report. In some
# cases it may happen that a spec that is asked to be
# installed explicitly will also be installed as a
# dependency of another spec. In this case append to both
# spec reports.
for s in llnl.util.lang.dedupe([pkg.spec.root, pkg.spec]):
name = name_fmt.format(s.name, s.dag_hash(length=7))
try:
item = next((
x for x in self.specs
if x['name'] == name
))
item['packages'].append(package)
except StopIteration:
pass
start_time = time.time() start_time = time.time()
value = None value = None
try: try:
@ -170,6 +186,7 @@ def wrapper(instance, *args, **kwargs):
package['stdout'] = fetch_log(pkg, do_fn, self.dir) package['stdout'] = fetch_log(pkg, do_fn, self.dir)
package['stdout'] += package['message'] package['stdout'] += package['message']
package['exception'] = e.traceback package['exception'] = e.traceback
raise
except (Exception, BaseException) as e: except (Exception, BaseException) as e:
# Everything else is an error (the installation # Everything else is an error (the installation
@ -178,26 +195,11 @@ def wrapper(instance, *args, **kwargs):
package['stdout'] = fetch_log(pkg, do_fn, self.dir) package['stdout'] = fetch_log(pkg, do_fn, self.dir)
package['message'] = str(e) or 'Unknown error' package['message'] = str(e) or 'Unknown error'
package['exception'] = traceback.format_exc() package['exception'] = traceback.format_exc()
raise
finally: finally:
package['elapsed_time'] = time.time() - start_time package['elapsed_time'] = time.time() - start_time
# Append the package to the correct spec report. In some
# cases it may happen that a spec that is asked to be
# installed explicitly will also be installed as a
# dependency of another spec. In this case append to both
# spec reports.
for s in llnl.util.lang.dedupe([pkg.spec.root, pkg.spec]):
name = name_fmt.format(s.name, s.dag_hash(length=7))
try:
item = next((
x for x in self.specs
if x['name'] == name
))
item['packages'].append(package)
except StopIteration:
pass
return value return value
return wrapper return wrapper

View file

@ -393,9 +393,14 @@ def test_junit_output_with_failures(tmpdir, exc_typename, msg):
'--log-format=junit', '--log-file=test.xml', '--log-format=junit', '--log-file=test.xml',
'raiser', 'raiser',
'exc_type={0}'.format(exc_typename), 'exc_type={0}'.format(exc_typename),
'msg="{0}"'.format(msg) 'msg="{0}"'.format(msg),
fail_on_error=False,
) )
assert isinstance(install.error, spack.build_environment.ChildError)
assert install.error.name == exc_typename
assert install.error.pkg.name == 'raiser'
files = tmpdir.listdir() files = tmpdir.listdir()
filename = tmpdir.join('test.xml') filename = tmpdir.join('test.xml')
assert filename in files assert filename in files
@ -407,18 +412,22 @@ def test_junit_output_with_failures(tmpdir, exc_typename, msg):
assert 'failures="1"' in content assert 'failures="1"' in content
assert 'errors="0"' in content assert 'errors="0"' in content
# Nothing should have succeeded
assert 'tests="0"' not in content
assert 'failures="0"' not in content
# We want to have both stdout and stderr # We want to have both stdout and stderr
assert '<system-out>' in content assert '<system-out>' in content
assert msg in content assert msg in content
@pytest.mark.disable_clean_stage_check @pytest.mark.disable_clean_stage_check
@pytest.mark.parametrize('exc_typename,msg', [ @pytest.mark.parametrize('exc_typename,expected_exc,msg', [
('RuntimeError', 'something weird happened'), ('RuntimeError', spack.installer.InstallError, 'something weird happened'),
('KeyboardInterrupt', 'Ctrl-C strikes again') ('KeyboardInterrupt', KeyboardInterrupt, 'Ctrl-C strikes again')
]) ])
def test_junit_output_with_errors( def test_junit_output_with_errors(
exc_typename, msg, exc_typename, expected_exc, msg,
mock_packages, mock_archive, mock_fetch, install_mockery, mock_packages, mock_archive, mock_fetch, install_mockery,
config, tmpdir, monkeypatch): config, tmpdir, monkeypatch):
@ -429,11 +438,11 @@ def just_throw(*args, **kwargs):
monkeypatch.setattr(spack.installer.PackageInstaller, '_install_task', monkeypatch.setattr(spack.installer.PackageInstaller, '_install_task',
just_throw) just_throw)
# TODO: Why does junit output capture appear to swallow the exception
# TODO: as evidenced by the two failing packages getting tagged as
# TODO: installed?
with tmpdir.as_cwd(): with tmpdir.as_cwd():
install('--log-format=junit', '--log-file=test.xml', 'libdwarf') install('--log-format=junit', '--log-file=test.xml', 'libdwarf',
fail_on_error=False)
assert isinstance(install.error, expected_exc)
files = tmpdir.listdir() files = tmpdir.listdir()
filename = tmpdir.join('test.xml') filename = tmpdir.join('test.xml')
@ -441,10 +450,14 @@ def just_throw(*args, **kwargs):
content = filename.open().read() content = filename.open().read()
# Count failures and errors correctly: libdwarf _and_ libelf # Only libelf error is reported (through libdwarf root spec). libdwarf
assert 'tests="2"' in content # install is skipped and it is not an error.
assert 'tests="1"' in content
assert 'failures="0"' in content assert 'failures="0"' in content
assert 'errors="2"' in content assert 'errors="1"' in content
# Nothing should have succeeded
assert 'errors="0"' not in content
# We want to have both stdout and stderr # We want to have both stdout and stderr
assert '<system-out>' in content assert '<system-out>' in content
@ -877,7 +890,7 @@ def test_install_help_cdash(capsys):
@pytest.mark.disable_clean_stage_check @pytest.mark.disable_clean_stage_check
def test_cdash_auth_token(tmpdir, install_mockery, capfd): def test_cdash_auth_token(tmpdir, mock_fetch, install_mockery, capfd):
# capfd interferes with Spack's capturing # capfd interferes with Spack's capturing
with tmpdir.as_cwd(): with tmpdir.as_cwd():
with capfd.disabled(): with capfd.disabled():

View file

@ -133,7 +133,8 @@ def test_junit_output_with_failures(tmpdir, mock_test_stage, pkg_name, msgs):
with tmpdir.as_cwd(): with tmpdir.as_cwd():
spack_test('run', spack_test('run',
'--log-format=junit', '--log-file=test.xml', '--log-format=junit', '--log-file=test.xml',
pkg_name) pkg_name,
fail_on_error=False)
files = tmpdir.listdir() files = tmpdir.listdir()
filename = tmpdir.join('test.xml') filename = tmpdir.join('test.xml')
@ -160,7 +161,8 @@ def test_cdash_output_test_error(
spack_test('run', spack_test('run',
'--log-format=cdash', '--log-format=cdash',
'--log-file=cdash_reports', '--log-file=cdash_reports',
'test-error') 'test-error',
fail_on_error=False)
report_dir = tmpdir.join('cdash_reports') report_dir = tmpdir.join('cdash_reports')
print(tmpdir.listdir()) print(tmpdir.listdir())
assert report_dir in tmpdir.listdir() assert report_dir in tmpdir.listdir()