summaryrefslogtreecommitdiff
path: root/lib/spack/spack/test/spec_dag.py
blob: bdb64fee802f1cec9419c5c536e60e86408bcbfb (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
"""
These tests check validation of dummy packages.  You can find the dummy
packages directories that these tests use in:

    spack/lib/spack/spack/test/mock_packages

Each test validates conditions with the packages in those directories.
"""
import unittest

import spack
import spack.package
import spack.packages as packages

from spack.util.lang import new_path, list_modules
from spack.spec import Spec

mock_packages_path = new_path(spack.module_path, 'test', 'mock_packages')


def set_pkg_dep(pkg, spec):
    """Alters dependence information for a pacakge.
       Use this to mock up constraints.
    """
    spec = Spec(spec)
    packages.get(pkg).dependencies[spec.name] = spec


class ValidationTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        # Use a different packages directory for these tests.  We want to use
        # mocked up packages that don't interfere with the real ones.
        cls.real_packages_path = spack.packages_path
        spack.packages_path = mock_packages_path

        # First time through, record original relationships bt/w packages
        cls.original_deps = {}
        for name in list_modules(mock_packages_path):
            pkg = packages.get(name)
            cls.original_deps[name] = [
                spec for spec in pkg.dependencies.values()]


    @classmethod
    def restore(cls):
        # each time through restore original dependencies & constraints
        for pkg_name, deps in cls.original_deps.iteritems():
            packages.get(pkg_name).dependencies.clear()
            for dep in deps:
                set_pkg_dep(pkg_name, dep)

    @classmethod
    def tearDownClass(cls):
        """Restore the real packages path after any test."""
        cls.restore()
        spack.packages_path = cls.real_packages_path


    def setUp(self):
        """Before each test, restore deps between packages to original state."""
        ValidationTest.restore()


    def test_conflicting_package_constraints(self):
        set_pkg_dep('mpileaks', 'mpich@1.0')
        set_pkg_dep('callpath', 'mpich@2.0')

        spec = Spec('mpileaks ^mpich ^callpath ^dyninst ^libelf ^libdwarf')
        self.assertRaises(spack.package.InvalidPackageDependencyError,
                          spec.package.validate_dependencies)


    def test_conflicting_spec_constraints(self):
        mpileaks = Spec('mpileaks ^mpich ^callpath ^dyninst ^libelf ^libdwarf')
        try:
            mpileaks.package.validate_dependencies()
        except spack.package.InvalidPackageDependencyError, e:
            self.fail("validate_dependencies raised an exception: %s", e.message)

        # Normalize then add conflicting constraints to the DAG (this is an
        # extremely unlikely scenario, but we test for it anyway)
        mpileaks.normalize()
        mpileaks.dependencies['mpich'] = Spec('mpich@1.0')
        mpileaks.dependencies['callpath'].dependencies['mpich'] = Spec('mpich@2.0')

        self.assertRaises(spack.spec.InconsistentSpecError, mpileaks.flatten)


    def test_unsatisfiable_version(self):
        set_pkg_dep('mpileaks', 'mpich@1.0')
        spec = Spec('mpileaks ^mpich@2.0 ^callpath ^dyninst ^libelf ^libdwarf')
        self.assertRaises(spack.spec.UnsatisfiableVersionSpecError, spec.normalize)


    def test_unsatisfiable_compiler(self):
        set_pkg_dep('mpileaks', 'mpich%gcc')
        spec = Spec('mpileaks ^mpich%intel ^callpath ^dyninst ^libelf ^libdwarf')
        self.assertRaises(spack.spec.UnsatisfiableCompilerSpecError, spec.normalize)


    def test_unsatisfiable_compiler_version(self):
        set_pkg_dep('mpileaks', 'mpich%gcc@4.6')
        spec = Spec('mpileaks ^mpich%gcc@4.5 ^callpath ^dyninst ^libelf ^libdwarf')
        self.assertRaises(spack.spec.UnsatisfiableCompilerSpecError, spec.normalize)


    def test_unsatisfiable_variant(self):
        set_pkg_dep('mpileaks', 'mpich+debug')
        spec = Spec('mpileaks ^mpich~debug ^callpath ^dyninst ^libelf ^libdwarf')
        self.assertRaises(spack.spec.UnsatisfiableVariantSpecError, spec.normalize)


    def test_unsatisfiable_architecture(self):
        set_pkg_dep('mpileaks', 'mpich=bgqos_0')
        spec = Spec('mpileaks ^mpich=sles_10_ppc64 ^callpath ^dyninst ^libelf ^libdwarf')
        self.assertRaises(spack.spec.UnsatisfiableArchitectureSpecError, spec.normalize)


    def test_invalid_dep(self):
        spec = Spec('libelf ^mpich')
        self.assertRaises(spack.spec.InvalidDependencyException, spec.normalize)

        spec = Spec('libelf ^libdwarf')
        self.assertRaises(spack.spec.InvalidDependencyException, spec.normalize)

        spec = Spec('mpich ^dyninst ^libelf')
        self.assertRaises(spack.spec.InvalidDependencyException, spec.normalize)


    def test_equal(self):
        spec = Spec('mpileaks ^callpath ^libelf ^libdwarf')
        self.assertNotEqual(spec, Spec(
            'mpileaks', Spec('callpath',
                             Spec('libdwarf',
                                  Spec('libelf')))))
        self.assertNotEqual(spec, Spec(
            'mpileaks', Spec('callpath',
                             Spec('libelf',
                                  Spec('libdwarf')))))

        self.assertEqual(spec, Spec(
            'mpileaks', Spec('callpath'), Spec('libdwarf'), Spec('libelf')))

        self.assertEqual(spec, Spec(
            'mpileaks', Spec('libelf'), Spec('libdwarf'), Spec('callpath')))


    def test_normalize_mpileaks(self):
        spec = Spec('mpileaks ^mpich ^callpath ^dyninst ^libelf@1.8.11 ^libdwarf')

        expected_flat = Spec(
            'mpileaks', Spec('mpich'), Spec('callpath'), Spec('dyninst'),
            Spec('libelf@1.8.11'), Spec('libdwarf'))

        mpich = Spec('mpich')
        libelf = Spec('libelf@1.8.11')
        expected_normalized = Spec(
            'mpileaks',
            Spec('callpath',
                 Spec('dyninst', Spec('libdwarf', libelf),
                      libelf),
                 mpich), mpich)

        expected_non_dag = Spec(
            'mpileaks',
            Spec('callpath',
                 Spec('dyninst', Spec('libdwarf', Spec('libelf@1.8.11')),
                      Spec('libelf@1.8.11')),
                 mpich), Spec('mpich'))

        self.assertEqual(expected_normalized, expected_non_dag)

        self.assertEqual(str(expected_normalized), str(expected_non_dag))
        self.assertEqual(str(spec), str(expected_non_dag))
        self.assertEqual(str(expected_normalized), str(spec))

        self.assertEqual(spec, expected_flat)
        self.assertNotEqual(spec, expected_normalized)
        self.assertNotEqual(spec, expected_non_dag)

        spec.normalize()

        self.assertNotEqual(spec, expected_flat)
        self.assertEqual(spec, expected_normalized)
        self.assertEqual(spec, expected_non_dag)