summaryrefslogtreecommitdiff
path: root/lib/spack/spack/test/verification.py
blob: 0cc99a70b947fd04b2c5512ab483f0c4677a8897 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# Copyright 2013-2024 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 stat

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.not_on_windows("Tests fail on Win")


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["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()

    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"]


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 stat.S_ISDIR(data["mode"])
    assert all(x in data for x in ("mode", "owner", "group"))

    results = spack.verify.check_entry(dirent, data)
    assert not results.has_errors()

    data["mode"] = "garbage"

    results = spack.verify.check_entry(dirent, data)
    assert results.has_errors()
    assert dirent in results.errors
    assert results.errors[dirent] == ["mode"]


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 stat.S_ISREG(data["mode"])
    assert data["size"] == len(orig_str)
    assert all(x in data for x in ("owner", "group"))

    results = spack.verify.check_entry(file, data)
    assert not results.has_errors()

    data["mode"] = 0x99999

    results = spack.verify.check_entry(file, data)
    assert results.has_errors()
    assert file in results.errors
    assert results.errors[file] == ["mode"]

    with open(file, "w") as f:
        f.write(new_str)

    data["mode"] = os.stat(file).st_mode

    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)


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"]


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.STORE.layout.metadata_dir,
        spack.store.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"]


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.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.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"]