summaryrefslogblamecommitdiff
path: root/lib/spack/spack/test/cmd/diff.py
blob: 9a901b9cbc5cb4a4edf2620399d813274b976209 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
                                                                         










                                                                         
                                                
 


                                                

 








































































































































                                                                                                  
                                                                            

                                                             

                                                           


                                                                  

                                 


                                                                  


                                                                        
                                                 

                                                      

                                                                    

                                                   

                                                                     

 
                                                             

                                                                              
                           

                                            
                                    


                                                     
                            
                                                     
                                                    

                              

                                                               

                               

                                
 

                                         


                                                                                



                                                                                     









                                                                           
                           


                   

                                                                        
                                 


                                                     
                                              

                                                                      
                                                                         

                               

                                                                         
                                              


                                                                                         

                               

                                                                        
 

                                                                         
# 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)

import pytest

import spack.cmd.diff
import spack.config
import spack.main
import spack.store
import spack.util.spack_json as sjson
from spack.test.conftest import create_test_repo

install_cmd = spack.main.SpackCommand("install")
diff_cmd = spack.main.SpackCommand("diff")
find_cmd = spack.main.SpackCommand("find")


_p1 = (
    "p1",
    """\
class P1(Package):
    version("1.0")

    variant("p1var", default=True)
    variant("usev1", default=True)

    depends_on("p2")
    depends_on("v1", when="+usev1")
""",
)


_p2 = (
    "p2",
    """\
class P2(Package):
    version("1.0")

    variant("p2var", default=True)

    depends_on("p3")
""",
)


_p3 = (
    "p3",
    """\
class P3(Package):
    version("1.0")

    variant("p3var", default=True)
""",
)

_i1 = (
    "i1",
    """\
class I1(Package):
    version("1.0")

    provides("v1")

    variant("i1var", default=True)

    depends_on("p3")
    depends_on("p4")
""",
)

_i2 = (
    "i2",
    """\
class I2(Package):
    version("1.0")

    provides("v1")

    variant("i2var", default=True)

    depends_on("p3")
    depends_on("p4")
""",
)


_p4 = (
    "p4",
    """\
class P4(Package):
    version("1.0")

    variant("p4var", default=True)
""",
)


# Note that the hash of p1 will differ depending on the variant chosen
# we probably always want to omit that from diffs
@pytest.fixture
def _create_test_repo(tmpdir, mutable_config):
    """
    p1____
    |     \
    p2     v1
    | ____/ |
    p3      p4

    i1 and i2 provide v1 (and both have the same dependencies)

    All packages have an associated variant
    """
    yield create_test_repo(tmpdir, [_p1, _p2, _p3, _i1, _i2, _p4])


@pytest.fixture
def test_repo(_create_test_repo, monkeypatch, mock_stage):
    with spack.repo.use_repositories(_create_test_repo) as mock_repo_path:
        yield mock_repo_path


def test_diff_ignore(test_repo):
    specA = spack.spec.Spec("p1+usev1").concretized()
    specB = spack.spec.Spec("p1~usev1").concretized()

    c1 = spack.cmd.diff.compare_specs(specA, specB, to_string=False)

    def match(function, name, args):
        limit = len(args)
        return function.name == name and list(args[:limit]) == list(function.args[:limit])

    def find(function_list, name, args):
        return any(match(f, name, args) for f in function_list)

    assert find(c1["a_not_b"], "node_os", ["p4"])

    c2 = spack.cmd.diff.compare_specs(specA, specB, ignore_packages=["v1"], to_string=False)

    assert not find(c2["a_not_b"], "node_os", ["p4"])
    assert find(c2["intersect"], "node_os", ["p3"])

    # Check ignoring changes on multiple packages

    specA = spack.spec.Spec("p1+usev1 ^p3+p3var").concretized()
    specA = spack.spec.Spec("p1~usev1 ^p3~p3var").concretized()

    c3 = spack.cmd.diff.compare_specs(specA, specB, to_string=False)
    assert find(c3["a_not_b"], "variant_value", ["p3", "p3var"])

    c4 = spack.cmd.diff.compare_specs(specA, specB, ignore_packages=["v1", "p3"], to_string=False)
    assert not find(c4["a_not_b"], "node_os", ["p4"])
    assert not find(c4["a_not_b"], "variant_value", ["p3"])


def test_diff_cmd(install_mockery, mock_fetch, mock_archive, mock_packages):
    """Test that we can install two packages and diff them"""

    specA = spack.spec.Spec("mpileaks").concretized()
    specB = spack.spec.Spec("mpileaks+debug").concretized()

    # Specs should be the same as themselves
    c = spack.cmd.diff.compare_specs(specA, specA, to_string=True)
    assert len(c["a_not_b"]) == 0
    assert len(c["b_not_a"]) == 0

    # Calculate the comparison (c)
    c = spack.cmd.diff.compare_specs(specA, specB, to_string=True)

    # these particular diffs should have the same length b/c thre aren't
    # any node differences -- just value differences.
    assert len(c["a_not_b"]) == len(c["b_not_a"])

    # ensure that variant diffs are in here the result
    assert ["variant_value", "mpileaks debug False"] in c["a_not_b"]
    assert ["variant_value", "mpileaks debug True"] in c["b_not_a"]

    # ensure that hash diffs are in here the result
    assert ["hash", "mpileaks %s" % specA.dag_hash()] in c["a_not_b"]
    assert ["hash", "mpileaks %s" % specB.dag_hash()] in c["b_not_a"]


@pytest.mark.not_on_windows("Not supported on Windows (yet)")
def test_load_first(install_mockery, mock_fetch, mock_archive, mock_packages):
    """Test with and without the --first option"""
    install_cmd("mpileaks")

    # Only one version of mpileaks will work
    diff_cmd("mpileaks", "mpileaks")

    # 2 specs are required for a diff
    with pytest.raises(spack.main.SpackCommandError):
        diff_cmd("mpileaks")
    with pytest.raises(spack.main.SpackCommandError):
        diff_cmd("mpileaks", "mpileaks", "mpileaks")

    # Ensure they are the same
    assert "No differences" in diff_cmd("mpileaks", "mpileaks")
    output = diff_cmd("--json", "mpileaks", "mpileaks")
    result = sjson.load(output)

    assert not result["a_not_b"]
    assert not result["b_not_a"]

    assert "mpileaks" in result["a_name"]
    assert "mpileaks" in result["b_name"]

    # spot check attributes in the intersection to ensure they describe the spec
    assert "intersect" in result
    assert all(
        ["node", dep] in result["intersect"]
        for dep in ("mpileaks", "callpath", "dyninst", "libelf", "libdwarf", "mpich")
    )
    assert all(
        len([diff for diff in result["intersect"] if diff[0] == attr]) == 6
        for attr in (
            "version",
            "node_target",
            "node_platform",
            "node_os",
            "node_compiler",
            "node_compiler_version",
            "node",
            "package_hash",
            "hash",
        )
    )

    # After we install another version, it should ask us to disambiguate
    install_cmd("mpileaks+debug")

    # There are two versions of mpileaks
    with pytest.raises(spack.main.SpackCommandError):
        diff_cmd("mpileaks", "mpileaks+debug")

    # But if we tell it to use the first, it won't try to disambiguate
    assert "variant" in diff_cmd("--first", "mpileaks", "mpileaks+debug")

    # This matches them exactly
    debug_hash = find_cmd("--format", "{hash}", "mpileaks+debug").strip()
    no_debug_hashes = find_cmd("--format", "{hash}", "mpileaks~debug")
    no_debug_hash = no_debug_hashes.split()[0]
    output = diff_cmd(
        "--json", "mpileaks/{0}".format(debug_hash), "mpileaks/{0}".format(no_debug_hash)
    )
    result = sjson.load(output)

    assert ["hash", "mpileaks %s" % debug_hash] in result["a_not_b"]
    assert ["variant_value", "mpileaks debug True"] in result["a_not_b"]

    assert ["hash", "mpileaks %s" % no_debug_hash] in result["b_not_a"]
    assert ["variant_value", "mpileaks debug False"] in result["b_not_a"]