# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
"""Tests for the `spack.verify` module"""
import os
import shutil
import sys
import pytest
import llnl.util.filesystem as fs
from llnl.util.symlink import symlink
import spack.spec
import spack.store
import spack.util.spack_json as sjson
import spack.verify
pytestmark = pytest.mark.skipif(sys.platform == 'win32',
reason='Tests fail on Win')
[docs]def test_link_manifest_entry(tmpdir):
# Test that symlinks are properly checked against the manifest.
# Test that the appropriate errors are generated when the check fails.
file = str(tmpdir.join('file'))
open(file, 'a').close()
link = str(tmpdir.join('link'))
os.symlink(file, link)
data = spack.verify.create_manifest_entry(link)
assert data['type'] == 'link'
assert data['dest'] == file
assert all(x in data for x in ('mode', 'owner', 'group'))
results = spack.verify.check_entry(link, data)
assert not results.has_errors()
data['type'] = 'garbage'
results = spack.verify.check_entry(link, data)
assert results.has_errors()
assert link in results.errors
assert results.errors[link] == ['type']
data['type'] = 'link'
file2 = str(tmpdir.join('file2'))
open(file2, 'a').close()
os.remove(link)
os.symlink(file2, link)
results = spack.verify.check_entry(link, data)
assert results.has_errors()
assert link in results.errors
assert results.errors[link] == ['link']
[docs]def test_dir_manifest_entry(tmpdir):
# Test that directories are properly checked against the manifest.
# Test that the appropriate errors are generated when the check fails.
dirent = str(tmpdir.join('dir'))
fs.mkdirp(dirent)
data = spack.verify.create_manifest_entry(dirent)
assert data['type'] == 'dir'
assert all(x in data for x in ('mode', 'owner', 'group'))
results = spack.verify.check_entry(dirent, data)
assert not results.has_errors()
data['type'] = 'garbage'
results = spack.verify.check_entry(dirent, data)
assert results.has_errors()
assert dirent in results.errors
assert results.errors[dirent] == ['type']
[docs]def test_file_manifest_entry(tmpdir):
# Test that files are properly checked against the manifest.
# Test that the appropriate errors are generated when the check fails.
orig_str = 'This is a file'
new_str = 'The file has changed'
file = str(tmpdir.join('dir'))
with open(file, 'w') as f:
f.write(orig_str)
data = spack.verify.create_manifest_entry(file)
assert data['type'] == 'file'
assert data['size'] == len(orig_str)
assert all(x in data for x in ('mode', 'owner', 'group'))
results = spack.verify.check_entry(file, data)
assert not results.has_errors()
data['type'] = 'garbage'
results = spack.verify.check_entry(file, data)
assert results.has_errors()
assert file in results.errors
assert results.errors[file] == ['type']
data['type'] = 'file'
with open(file, 'w') as f:
f.write(new_str)
results = spack.verify.check_entry(file, data)
expected = ['size', 'hash']
mtime = os.stat(file).st_mtime
if mtime != data['time']:
expected.append('mtime')
assert results.has_errors()
assert file in results.errors
assert sorted(results.errors[file]) == sorted(expected)
[docs]def test_check_chmod_manifest_entry(tmpdir):
# Check that the verification properly identifies errors for files whose
# permissions have been modified.
file = str(tmpdir.join('dir'))
with open(file, 'w') as f:
f.write('This is a file')
data = spack.verify.create_manifest_entry(file)
os.chmod(file, data['mode'] - 1)
results = spack.verify.check_entry(file, data)
assert results.has_errors()
assert file in results.errors
assert results.errors[file] == ['mode']
[docs]def test_check_prefix_manifest(tmpdir):
# Test the verification of an entire prefix and its contents
prefix_path = tmpdir.join('prefix')
prefix = str(prefix_path)
spec = spack.spec.Spec('libelf')
spec._mark_concrete()
spec.prefix = prefix
results = spack.verify.check_spec_manifest(spec)
assert results.has_errors()
assert prefix in results.errors
assert results.errors[prefix] == ['manifest missing']
metadata_dir = str(prefix_path.join('.spack'))
bin_dir = str(prefix_path.join('bin'))
other_dir = str(prefix_path.join('other'))
for d in (metadata_dir, bin_dir, other_dir):
fs.mkdirp(d)
file = os.path.join(other_dir, 'file')
with open(file, 'w') as f:
f.write("I'm a little file short and stout")
link = os.path.join(bin_dir, 'run')
symlink(file, link)
spack.verify.write_manifest(spec)
results = spack.verify.check_spec_manifest(spec)
assert not results.has_errors()
os.remove(link)
malware = os.path.join(metadata_dir, 'hiddenmalware')
with open(malware, 'w') as f:
f.write("Foul evil deeds")
results = spack.verify.check_spec_manifest(spec)
assert results.has_errors()
assert all(x in results.errors for x in (malware, link))
assert len(results.errors) == 2
assert results.errors[link] == ['deleted']
assert results.errors[malware] == ['added']
manifest_file = os.path.join(spec.prefix,
spack.store.layout.metadata_dir,
spack.store.layout.manifest_file_name)
with open(manifest_file, 'w') as f:
f.write("{This) string is not proper json")
results = spack.verify.check_spec_manifest(spec)
assert results.has_errors()
assert results.errors[spec.prefix] == ['manifest corrupted']
[docs]def test_single_file_verification(tmpdir):
# Test the API to verify a single file, including finding the package
# to which it belongs
filedir = os.path.join(str(tmpdir), 'a', 'b', 'c', 'd')
filepath = os.path.join(filedir, 'file')
metadir = os.path.join(str(tmpdir), spack.store.layout.metadata_dir)
fs.mkdirp(filedir)
fs.mkdirp(metadir)
with open(filepath, 'w') as f:
f.write("I'm a file")
data = spack.verify.create_manifest_entry(filepath)
manifest_file = os.path.join(metadir,
spack.store.layout.manifest_file_name)
with open(manifest_file, 'w') as f:
sjson.dump({filepath: data}, f)
results = spack.verify.check_file_manifest(filepath)
assert not results.has_errors()
os.utime(filepath, (0, 0))
with open(filepath, 'w') as f:
f.write("I changed.")
results = spack.verify.check_file_manifest(filepath)
expected = ['hash']
mtime = os.stat(filepath).st_mtime
if mtime != data['time']:
expected.append('mtime')
assert results.has_errors()
assert filepath in results.errors
assert sorted(results.errors[filepath]) == sorted(expected)
shutil.rmtree(metadir)
results = spack.verify.check_file_manifest(filepath)
assert results.has_errors()
assert results.errors[filepath] == ['not owned by any package']