summaryrefslogtreecommitdiff
path: root/lib/spack/spack/test/multimethod.py
blob: 76e06ab3a7fa240e8b5073da3e561b4e6b84982e (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
# Copyright 2013-2023 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)

"""Test for multi_method dispatch."""

import pytest

import spack.platforms
import spack.repo
import spack.spec
from spack.multimethod import NoSuchMethodError

pytestmark = [
    pytest.mark.usefixtures("mock_packages", "config"),
    pytest.mark.only_clingo("The original concretizer cannot concretize most of the specs"),
    pytest.mark.not_on_windows("Not running on windows"),
]


@pytest.fixture(scope="module", params=["multimethod", "multimethod-inheritor"])
def pkg_name(request):
    """Make tests run on both multimethod and multimethod-inheritor.

    This means we test all of our @when methods on a class that uses them
    directly, AND on a class that inherits them.
    """
    return request.param


def test_no_version_match(pkg_name):
    spec = spack.spec.Spec(pkg_name + "@2.0").concretized()
    with pytest.raises(NoSuchMethodError):
        spec.package.no_version_2()


@pytest.mark.parametrize(
    "constraint_str,method_name,expected_result",
    [
        # Only one version match these constraints
        ("@1.0", "no_version_2", 1),
        ("@3.0", "no_version_2", 3),
        ("@4.0", "no_version_2", 4),
        # These constraints overlap, in which case the first match wins
        ("@2.0", "version_overlap", 1),
        ("@5.0", "version_overlap", 2),
        # These constraints are on the version of a virtual dependency
        ("^mpich@3.0.4", "mpi_version", 3),
        ("^mpich2@1.2", "mpi_version", 2),
        ("^mpich@1.0", "mpi_version", 1),
        # Undefined mpi versions
        ("^mpich@=0.4", "mpi_version", 1),
        ("^mpich@=1.4", "mpi_version", 1),
        # Constraints on compilers with a default
        ("%gcc", "has_a_default", "gcc"),
        ("%clang", "has_a_default", "clang"),
        ("%apple-clang os=elcapitan", "has_a_default", "default"),
        # Constraints on dependencies
        ("^zmpi", "different_by_dep", "zmpi"),
        ("^mpich", "different_by_dep", "mpich"),
        # Constraints on virtual dependencies
        ("^mpich2", "different_by_virtual_dep", 2),
        ("^mpich@1.0", "different_by_virtual_dep", 1),
        # Multimethod with base classes
        ("@1", "base_method", "base_method"),
        # Boolean
        ("", "boolean_true_first", "True"),
        ("", "boolean_false_first", "True"),
    ],
)
def test_multimethod_calls(pkg_name, constraint_str, method_name, expected_result):
    s = spack.spec.Spec(pkg_name + constraint_str).concretized()
    msg = "Method {0} from {1} is giving a wrong result".format(method_name, s)
    assert getattr(s.package, method_name)() == expected_result, msg


def test_target_match(pkg_name):
    platform = spack.platforms.host()
    targets = list(platform.targets.values())
    for target in targets[:-1]:
        s = spack.spec.Spec(pkg_name + " target=" + target.name).concretized()
        assert s.package.different_by_target() == target.name

    s = spack.spec.Spec(pkg_name + " target=" + targets[-1].name).concretized()
    if len(targets) == 1:
        assert s.package.different_by_target() == targets[-1].name
    else:
        with pytest.raises(NoSuchMethodError):
            s.package.different_by_target()


@pytest.mark.parametrize(
    "spec_str,method_name,expected_result",
    [
        # This is overridden in the second case
        ("multimethod@3", "base_method", "multimethod"),
        ("multimethod-inheritor@3", "base_method", "multimethod-inheritor"),
        # Here we have a mix of inherited and overridden methods
        ("multimethod-inheritor@1.0", "inherited_and_overridden", "inheritor@1.0"),
        ("multimethod-inheritor@2.0", "inherited_and_overridden", "base@2.0"),
        ("multimethod@1.0", "inherited_and_overridden", "base@1.0"),
        ("multimethod@2.0", "inherited_and_overridden", "base@2.0"),
        # Diamond-like inheritance (even though the MRO linearize everything)
        ("multimethod-diamond@1.0", "diamond_inheritance", "base_package"),
        ("multimethod-base@=1.0", "diamond_inheritance", "base_package"),
        ("multimethod-diamond@2.0", "diamond_inheritance", "first_parent"),
        ("multimethod-inheritor@2.0", "diamond_inheritance", "first_parent"),
        ("multimethod-diamond@=3.0", "diamond_inheritance", "second_parent"),
        ("multimethod-diamond-parent@=3.0", "diamond_inheritance", "second_parent"),
        ("multimethod-diamond@4.0", "diamond_inheritance", "subclass"),
    ],
)
def test_multimethod_calls_and_inheritance(spec_str, method_name, expected_result):
    s = spack.spec.Spec(spec_str).concretized()
    assert getattr(s.package, method_name)() == expected_result