summaryrefslogblamecommitdiff
path: root/lib/spack/spack/test/cmd/pkg.py
blob: 56a1f1f78676e855228a950b9e8885fe94e166e9 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                                                                         



                                                                         


             

             

                                                    
                    
                 
                 

                            
                   
                           




                                                       
                                                          


                                    
   
 

                                      


                                                                      
                               
                                           
                                                                           

                                                        




                                                                   
                                         
                   

                                           
                                                                            



                                                                                     

                                                      








                                                                                    

                                 








                                                                                                  
 
                                                     


                                
                               
                     
                                                   








                                                                         





                                                  
                                                     

 
                                    


                         
                                                                                          


                                           
                                                                                               

 
                                         
                                        


                                                     
 
                       
 

                                        
                                                                                
                
                                  
                                                                 

                                                                           

                                                     
                                    

 
                                                     
                                                     
                                      

                                                

                                                                                   
 

                                                                                   

                                                                  
                                         


                                                
                                                     
                                                     

                                                       
 

                                                      
 

                                                       

 
                                                     
                                      

                                                
 

                                               
 

                                              
 
                                             


                    
                                                     
                                        
                                                  

                    
                                                 

                    

                                                

 
                                                     
                                        
                                                  

                    
                                                                 

                    

                                                                 
 
                                                                 

                    

                                                                  
 

                                                                   
 

                                                
 

                                                               
 

                                                               
 

                                                               
 

                                                                
 

                                                                 


                                                     
                                       


                                                  
                                                                      
                                                     
                    












                                                     
                                                                 






                                             
                                    





































                                                                                   


                                                                                 
                                        
                                                                  

                                           
                                       
                                                           



                                                                                 
                                                                              
                              
                                  




                                                                             
# 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 re
import shutil

import pytest

from llnl.util.filesystem import mkdirp, working_dir

import spack.cmd.pkg
import spack.main
import spack.repo

#: new fake package template
pkg_template = """\
from spack.package import *

class {name}(Package):
    homepage = "http://www.example.com"
    url      = "http://www.example.com/test-1.0.tar.gz"

    version("1.0", md5="0123456789abcdef0123456789abcdef")

    def install(self, spec, prefix):
        pass
"""

abc = set(("pkg-a", "pkg-b", "pkg-c"))
abd = set(("pkg-a", "pkg-b", "pkg-d"))


# Force all tests to use a git repository *in* the mock packages repo.
@pytest.fixture(scope="module")
def mock_pkg_git_repo(git, tmpdir_factory):
    """Copy the builtin.mock repo and make a mutable git repo inside it."""
    tmproot = tmpdir_factory.mktemp("mock_pkg_git_repo")
    repo_path = tmproot.join("builtin.mock")

    shutil.copytree(spack.paths.mock_packages_path, str(repo_path))
    mock_repo = spack.repo.RepoPath(str(repo_path))
    mock_repo_packages = mock_repo.repos[0].packages_path

    with working_dir(mock_repo_packages):
        git("init")

        # initial commit with mock packages
        # the -f is necessary in case people ignore build-* in their ignores
        git("add", "-f", ".")
        git("config", "user.email", "testing@spack.io")
        git("config", "user.name", "Spack Testing")
        git("-c", "commit.gpgsign=false", "commit", "-m", "initial mock repo commit")

        # add commit with pkg-a, pkg-b, pkg-c packages
        mkdirp("pkg-a", "pkg-b", "pkg-c")
        with open("pkg-a/package.py", "w") as f:
            f.write(pkg_template.format(name="PkgA"))
        with open("pkg-b/package.py", "w") as f:
            f.write(pkg_template.format(name="PkgB"))
        with open("pkg-c/package.py", "w") as f:
            f.write(pkg_template.format(name="PkgC"))
        git("add", "pkg-a", "pkg-b", "pkg-c")
        git("-c", "commit.gpgsign=false", "commit", "-m", "add pkg-a, pkg-b, pkg-c")

        # remove pkg-c, add pkg-d
        with open("pkg-b/package.py", "a") as f:
            f.write("\n# change pkg-b")
        git("add", "pkg-b")
        mkdirp("pkg-d")
        with open("pkg-d/package.py", "w") as f:
            f.write(pkg_template.format(name="PkgD"))
        git("add", "pkg-d")
        git("rm", "-rf", "pkg-c")
        git("-c", "commit.gpgsign=false", "commit", "-m", "change pkg-b, remove pkg-c, add pkg-d")

    with spack.repo.use_repositories(str(repo_path)):
        yield mock_repo_packages


@pytest.fixture(scope="module")
def mock_pkg_names():
    repo = spack.repo.PATH.get_repo("builtin.mock")

    # Be sure to include virtual packages since packages with stand-alone
    # tests may inherit additional tests from the virtuals they provide,
    # such as packages that implement `mpi`.
    names = set(
        name
        for name in repo.all_package_names(include_virtuals=True)
        if not name.startswith("pkg-")
    )
    return names


def split(output):
    """Split command line output into an array."""
    output = output.strip()
    return re.split(r"\s+", output) if output else []


pkg = spack.main.SpackCommand("pkg")


def test_packages_path():
    assert spack.repo.packages_path() == spack.repo.PATH.get_repo("builtin").packages_path


def test_mock_packages_path(mock_packages):
    assert spack.repo.packages_path() == spack.repo.PATH.get_repo("builtin.mock").packages_path


def test_pkg_add(git, mock_pkg_git_repo):
    with working_dir(mock_pkg_git_repo):
        mkdirp("pkg-e")
        with open("pkg-e/package.py", "w") as f:
            f.write(pkg_template.format(name="PkgE"))

    pkg("add", "pkg-e")

    with working_dir(mock_pkg_git_repo):
        try:
            assert "A  pkg-e/package.py" in git("status", "--short", output=str)
        finally:
            shutil.rmtree("pkg-e")
            # Removing a package mid-run disrupts Spack's caching
            if spack.repo.PATH.repos[0]._fast_package_checker:
                spack.repo.PATH.repos[0]._fast_package_checker.invalidate()

    with pytest.raises(spack.main.SpackCommandError):
        pkg("add", "does-not-exist")


@pytest.mark.not_on_windows("stdout format conflict")
def test_pkg_list(mock_pkg_git_repo, mock_pkg_names):
    out = split(pkg("list", "HEAD^^"))
    assert sorted(mock_pkg_names) == sorted(out)

    out = split(pkg("list", "HEAD^"))
    assert sorted(mock_pkg_names.union(["pkg-a", "pkg-b", "pkg-c"])) == sorted(out)

    out = split(pkg("list", "HEAD"))
    assert sorted(mock_pkg_names.union(["pkg-a", "pkg-b", "pkg-d"])) == sorted(out)

    # test with three dots to make sure pkg calls `git merge-base`
    out = split(pkg("list", "HEAD^^..."))
    assert sorted(mock_pkg_names) == sorted(out)


@pytest.mark.not_on_windows("stdout format conflict")
def test_pkg_diff(mock_pkg_git_repo, mock_pkg_names):
    out = split(pkg("diff", "HEAD^^", "HEAD^"))
    assert out == ["HEAD^:", "pkg-a", "pkg-b", "pkg-c"]

    out = split(pkg("diff", "HEAD^^", "HEAD"))
    assert out == ["HEAD:", "pkg-a", "pkg-b", "pkg-d"]

    out = split(pkg("diff", "HEAD^", "HEAD"))
    assert out == ["HEAD^:", "pkg-c", "HEAD:", "pkg-d"]


@pytest.mark.not_on_windows("stdout format conflict")
def test_pkg_added(mock_pkg_git_repo):
    out = split(pkg("added", "HEAD^^", "HEAD^"))
    assert ["pkg-a", "pkg-b", "pkg-c"] == out

    out = split(pkg("added", "HEAD^^", "HEAD"))
    assert ["pkg-a", "pkg-b", "pkg-d"] == out

    out = split(pkg("added", "HEAD^", "HEAD"))
    assert ["pkg-d"] == out

    out = split(pkg("added", "HEAD", "HEAD"))
    assert out == []


@pytest.mark.not_on_windows("stdout format conflict")
def test_pkg_removed(mock_pkg_git_repo):
    out = split(pkg("removed", "HEAD^^", "HEAD^"))
    assert out == []

    out = split(pkg("removed", "HEAD^^", "HEAD"))
    assert out == []

    out = split(pkg("removed", "HEAD^", "HEAD"))
    assert out == ["pkg-c"]


@pytest.mark.not_on_windows("stdout format conflict")
def test_pkg_changed(mock_pkg_git_repo):
    out = split(pkg("changed", "HEAD^^", "HEAD^"))
    assert out == []

    out = split(pkg("changed", "--type", "c", "HEAD^^", "HEAD^"))
    assert out == []

    out = split(pkg("changed", "--type", "a", "HEAD^^", "HEAD^"))
    assert out == ["pkg-a", "pkg-b", "pkg-c"]

    out = split(pkg("changed", "--type", "r", "HEAD^^", "HEAD^"))
    assert out == []

    out = split(pkg("changed", "--type", "ar", "HEAD^^", "HEAD^"))
    assert out == ["pkg-a", "pkg-b", "pkg-c"]

    out = split(pkg("changed", "--type", "arc", "HEAD^^", "HEAD^"))
    assert out == ["pkg-a", "pkg-b", "pkg-c"]

    out = split(pkg("changed", "HEAD^", "HEAD"))
    assert out == ["pkg-b"]

    out = split(pkg("changed", "--type", "c", "HEAD^", "HEAD"))
    assert out == ["pkg-b"]

    out = split(pkg("changed", "--type", "a", "HEAD^", "HEAD"))
    assert out == ["pkg-d"]

    out = split(pkg("changed", "--type", "r", "HEAD^", "HEAD"))
    assert out == ["pkg-c"]

    out = split(pkg("changed", "--type", "ar", "HEAD^", "HEAD"))
    assert out == ["pkg-c", "pkg-d"]

    out = split(pkg("changed", "--type", "arc", "HEAD^", "HEAD"))
    assert out == ["pkg-b", "pkg-c", "pkg-d"]

    # invalid type argument
    with pytest.raises(spack.main.SpackCommandError):
        pkg("changed", "--type", "foo")


def test_pkg_fails_when_not_git_repo(monkeypatch):
    monkeypatch.setattr(spack.cmd, "spack_is_git_repo", lambda: False)
    with pytest.raises(spack.main.SpackCommandError):
        pkg("added")


def test_pkg_source_requires_one_arg(mock_packages):
    with pytest.raises(spack.main.SpackCommandError):
        pkg("source", "a", "b")

    with pytest.raises(spack.main.SpackCommandError):
        pkg("source", "--canonical", "a", "b")


def test_pkg_source(mock_packages):
    fake_source = pkg("source", "fake")

    fake_file = spack.repo.PATH.filename_for_package_name("fake")
    with open(fake_file) as f:
        contents = f.read()
        assert fake_source == contents


def test_pkg_canonical_source(mock_packages):
    source = pkg("source", "multimethod")
    assert '@when("@2.0")' in source
    assert "Check that multimethods work with boolean values" in source

    canonical_1 = pkg("source", "--canonical", "multimethod@1.0")
    assert "@when" not in canonical_1
    assert "should_not_be_reached by diamond inheritance test" not in canonical_1
    assert "return 'base@1.0'" in canonical_1
    assert "return 'base@2.0'" not in canonical_1
    assert "return 'first_parent'" not in canonical_1
    assert "'should_not_be_reached by diamond inheritance test'" not in canonical_1

    canonical_2 = pkg("source", "--canonical", "multimethod@2.0")
    assert "@when" not in canonical_2
    assert "return 'base@1.0'" not in canonical_2
    assert "return 'base@2.0'" in canonical_2
    assert "return 'first_parent'" in canonical_2
    assert "'should_not_be_reached by diamond inheritance test'" not in canonical_2

    canonical_3 = pkg("source", "--canonical", "multimethod@3.0")
    assert "@when" not in canonical_3
    assert "return 'base@1.0'" not in canonical_3
    assert "return 'base@2.0'" not in canonical_3
    assert "return 'first_parent'" not in canonical_3
    assert "'should_not_be_reached by diamond inheritance test'" not in canonical_3

    canonical_4 = pkg("source", "--canonical", "multimethod@4.0")
    assert "@when" not in canonical_4
    assert "return 'base@1.0'" not in canonical_4
    assert "return 'base@2.0'" not in canonical_4
    assert "return 'first_parent'" not in canonical_4
    assert "'should_not_be_reached by diamond inheritance test'" in canonical_4


def test_pkg_hash(mock_packages):
    output = pkg("hash", "a", "b").strip().split()
    assert len(output) == 2 and all(len(elt) == 32 for elt in output)

    output = pkg("hash", "multimethod").strip().split()
    assert len(output) == 1 and all(len(elt) == 32 for elt in output)


@pytest.mark.skipif(not spack.cmd.pkg.get_grep(), reason="grep is not installed")
def test_pkg_grep(mock_packages, capfd):
    # only splice-* mock packages have the string "splice" in them
    pkg("grep", "-l", "splice", output=str)
    output, _ = capfd.readouterr()
    assert output.strip() == "\n".join(
        spack.repo.PATH.get_pkg_class(name).module.__file__
        for name in ["splice-a", "splice-h", "splice-t", "splice-vh", "splice-z"]
    )

    # ensure that this string isn't fouhnd
    pkg("grep", "abcdefghijklmnopqrstuvwxyz", output=str, fail_on_error=False)
    assert pkg.returncode == 1
    output, _ = capfd.readouterr()
    assert output.strip() == ""

    # ensure that we return > 1 for an error
    pkg("grep", "--foobarbaz-not-an-option", output=str, fail_on_error=False)
    assert pkg.returncode == 2