summaryrefslogtreecommitdiff
path: root/lib/spack/spack/test/cmd/uninstall.py
blob: 857f1ae35dbe29b9228ef6eadfcab1c560ee70df (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
# 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)

import pytest

import llnl.util.tty as tty

import spack.store
from spack.main import SpackCommand, SpackCommandError

uninstall = SpackCommand('uninstall')
install = SpackCommand('install')


class MockArgs(object):

    def __init__(self, packages, all=False, force=False, dependents=False):
        self.packages = packages
        self.all = all
        self.force = force
        self.dependents = dependents
        self.yes_to_all = True


@pytest.mark.db
def test_multiple_matches(mutable_database):
    """Test unable to uninstall when multiple matches."""
    with pytest.raises(SpackCommandError):
        uninstall('-y', 'mpileaks')


@pytest.mark.db
def test_installed_dependents(mutable_database):
    """Test can't uninstall when there are installed dependents."""
    with pytest.raises(SpackCommandError):
        uninstall('-y', 'libelf')


@pytest.mark.db
def test_recursive_uninstall(mutable_database):
    """Test recursive uninstall."""
    uninstall('-y', '-a', '--dependents', 'callpath')

    all_specs = spack.store.layout.all_specs()
    assert len(all_specs) == 9
    # query specs with multiple configurations
    mpileaks_specs = [s for s in all_specs if s.satisfies('mpileaks')]
    callpath_specs = [s for s in all_specs if s.satisfies('callpath')]
    mpi_specs = [s for s in all_specs if s.satisfies('mpi')]

    assert len(mpileaks_specs) == 0
    assert len(callpath_specs) == 0
    assert len(mpi_specs) == 3


@pytest.mark.db
@pytest.mark.regression('3690')
@pytest.mark.parametrize('constraint,expected_number_of_specs', [
    ('dyninst', 8), ('libelf', 6)
])
def test_uninstall_spec_with_multiple_roots(
        constraint, expected_number_of_specs, mutable_database
):
    uninstall('-y', '-a', '--dependents', constraint)

    all_specs = spack.store.layout.all_specs()
    assert len(all_specs) == expected_number_of_specs


@pytest.mark.db
@pytest.mark.parametrize('constraint,expected_number_of_specs', [
    ('dyninst', 14), ('libelf', 14)
])
def test_force_uninstall_spec_with_ref_count_not_zero(
        constraint, expected_number_of_specs, mutable_database
):
    uninstall('-f', '-y', constraint)

    all_specs = spack.store.layout.all_specs()
    assert len(all_specs) == expected_number_of_specs


@pytest.mark.db
def test_force_uninstall_and_reinstall_by_hash(mutable_database):
    """Test forced uninstall and reinstall of old specs."""
    # this is the spec to be removed
    callpath_spec = spack.store.db.query_one('callpath ^mpich')
    dag_hash = callpath_spec.dag_hash()

    # ensure can look up by hash and that it's a dependent of mpileaks
    def validate_callpath_spec(installed):
        assert installed is True or installed is False

        specs = spack.store.db.get_by_hash(dag_hash, installed=installed)
        assert len(specs) == 1 and specs[0] == callpath_spec

        specs = spack.store.db.get_by_hash(dag_hash[:7], installed=installed)
        assert len(specs) == 1 and specs[0] == callpath_spec

        specs = spack.store.db.get_by_hash(dag_hash, installed=any)
        assert len(specs) == 1 and specs[0] == callpath_spec

        specs = spack.store.db.get_by_hash(dag_hash[:7], installed=any)
        assert len(specs) == 1 and specs[0] == callpath_spec

        specs = spack.store.db.get_by_hash(dag_hash, installed=not installed)
        assert specs is None

        specs = spack.store.db.get_by_hash(dag_hash[:7],
                                           installed=not installed)
        assert specs is None

        mpileaks_spec = spack.store.db.query_one('mpileaks ^mpich')
        assert callpath_spec in mpileaks_spec

        spec = spack.store.db.query_one('callpath ^mpich', installed=installed)
        assert spec == callpath_spec

        spec = spack.store.db.query_one('callpath ^mpich', installed=any)
        assert spec == callpath_spec

        spec = spack.store.db.query_one('callpath ^mpich',
                                        installed=not installed)
        assert spec is None

    validate_callpath_spec(True)

    uninstall('-y', '-f', 'callpath ^mpich')

    # ensure that you can still look up by hash and see deps, EVEN though
    # the callpath spec is missing.
    validate_callpath_spec(False)

    # BUT, make sure that the removed callpath spec is not in queries
    def db_specs():
        all_specs = spack.store.layout.all_specs()
        return (
            all_specs,
            [s for s in all_specs if s.satisfies('mpileaks')],
            [s for s in all_specs if s.satisfies('callpath')],
            [s for s in all_specs if s.satisfies('mpi')]
        )
    all_specs, mpileaks_specs, callpath_specs, mpi_specs = db_specs()
    total_specs = len(all_specs)
    assert total_specs == 14
    assert len(mpileaks_specs) == 3
    assert len(callpath_specs) == 2
    assert len(mpi_specs) == 3

    # Now, REINSTALL the spec and make sure everything still holds
    install('--fake', '/%s' % dag_hash[:7])

    validate_callpath_spec(True)

    all_specs, mpileaks_specs, callpath_specs, mpi_specs = db_specs()
    assert len(all_specs) == total_specs + 1   # back to total_specs+1
    assert len(mpileaks_specs) == 3
    assert len(callpath_specs) == 3  # back to 3
    assert len(mpi_specs) == 3


@pytest.mark.db
@pytest.mark.regression('15773')
def test_in_memory_consistency_when_uninstalling(
        mutable_database, monkeypatch
):
    """Test that uninstalling doesn't raise warnings"""
    def _warn(*args, **kwargs):
        raise RuntimeError('a warning was triggered!')
    monkeypatch.setattr(tty, 'warn', _warn)
    # Now try to uninstall and check this doesn't trigger warnings
    uninstall('-y', '-a')