summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorTodd Gamblin <tgamblin@llnl.gov>2019-07-22 17:02:21 -0700
committerTodd Gamblin <tgamblin@llnl.gov>2019-07-22 23:01:45 -0700
commit3cb40e1c02186c1f662c4f28db0b1527fff24360 (patch)
treedc1b5a7405ec69c3eb0201cc98c486ba0ca3331c /lib
parent0b17bccdcefd2cad8b00508d8ddc64cf665003ad (diff)
downloadspack-3cb40e1c02186c1f662c4f28db0b1527fff24360.tar.gz
spack-3cb40e1c02186c1f662c4f28db0b1527fff24360.tar.bz2
spack-3cb40e1c02186c1f662c4f28db0b1527fff24360.tar.xz
spack-3cb40e1c02186c1f662c4f28db0b1527fff24360.zip
commands: add tests for all subcommands of `spack pkg`
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/spack/cmd/pkg.py63
-rw-r--r--lib/spack/spack/repo.py3
-rw-r--r--lib/spack/spack/test/cmd/pkg.py229
3 files changed, 271 insertions, 24 deletions
diff --git a/lib/spack/spack/cmd/pkg.py b/lib/spack/spack/cmd/pkg.py
index 0164f2e612..b81641febc 100644
--- a/lib/spack/spack/cmd/pkg.py
+++ b/lib/spack/spack/cmd/pkg.py
@@ -7,15 +7,15 @@ from __future__ import print_function
import os
import argparse
+import re
import llnl.util.tty as tty
from llnl.util.tty.colify import colify
-from llnl.util.filesystem import working_dir
+import spack.cmd
import spack.paths
import spack.repo
from spack.util.executable import which
-from spack.cmd import spack_is_git_repo
description = "query packages associated with particular git revisions"
section = "developer"
@@ -57,6 +57,10 @@ def setup_parser(subparser):
add_parser.add_argument(
'rev2', nargs='?', default='HEAD',
help="revision to compare to rev1 (default is HEAD)")
+ add_parser.add_argument(
+ '-t', '--type', action='store', default='C',
+ help="Types of changes to show (A: added, R: removed, "
+ "C: changed); default is 'C'")
rm_parser = sp.add_parser('removed', help=pkg_removed.__doc__)
rm_parser.add_argument(
@@ -84,20 +88,20 @@ def get_git():
def list_packages(rev):
- pkgpath = packages_path()
- relpath = pkgpath[len(spack.paths.prefix + os.path.sep):] + os.path.sep
-
git = get_git()
- with working_dir(spack.paths.prefix):
- output = git('ls-tree', '--full-tree', '--name-only', rev, relpath,
- output=str)
- return sorted(line[len(relpath):] for line in output.split('\n') if line)
+
+ # git ls-tree does not support ... merge-base syntax, so do it manually
+ if rev.endswith('...'):
+ ref = rev.replace('...', '')
+ rev = git('merge-base', ref, 'HEAD', output=str).strip()
+
+ output = git('ls-tree', '--name-only', rev, output=str)
+ return sorted(line for line in output.split('\n')
+ if line and not line.startswith('.'))
def pkg_add(args):
"""add a package to the git stage with `git add`"""
- pkgpath = packages_path()
-
for pkg_name in args.packages:
filename = spack.repo.path.filename_for_package_name(pkg_name)
if not os.path.isfile(filename):
@@ -105,8 +109,7 @@ def pkg_add(args):
pkg_name, filename)
git = get_git()
- with working_dir(spack.paths.prefix):
- git('-C', pkgpath, 'add', filename)
+ git('add', filename)
def pkg_list(args):
@@ -151,24 +154,38 @@ def pkg_added(args):
def pkg_changed(args):
"""show packages changed since a commit"""
- pkgpath = spack.repo.path.get_repo('builtin').packages_path
- rel_pkg_path = os.path.relpath(pkgpath, spack.paths.prefix)
+ lower_type = args.type.lower()
+ if not re.match('^[arc]*$', lower_type):
+ tty.die("Invald change type: '%s'." % args.type,
+ "Can contain only A (added), R (removed), or C (changed)")
+
+ removed, added = diff_packages(args.rev1, args.rev2)
git = get_git()
- paths = git('diff', '--name-only', args.rev1, args.rev2, pkgpath,
- output=str).strip().split('\n')
+ out = git('diff', '--relative', '--name-only', args.rev1, args.rev2,
+ output=str).strip()
- packages = set([])
- for path in paths:
- path = path.replace(rel_pkg_path + os.sep, '')
+ lines = [] if not out else re.split(r'\s+', out)
+ changed = set()
+ for path in lines:
pkg_name, _, _ = path.partition(os.sep)
- packages.add(pkg_name)
+ if pkg_name not in added and pkg_name not in removed:
+ changed.add(pkg_name)
+
+ packages = set()
+ if 'a' in lower_type:
+ packages |= added
+ if 'r' in lower_type:
+ packages |= removed
+ if 'c' in lower_type:
+ packages |= changed
- colify(sorted(packages))
+ if packages:
+ colify(sorted(packages))
def pkg(parser, args):
- if not spack_is_git_repo():
+ if not spack.cmd.spack_is_git_repo():
tty.die("This spack is not a git clone. Can't use 'spack pkg'")
action = {'add': pkg_add,
diff --git a/lib/spack/spack/repo.py b/lib/spack/spack/repo.py
index 1e593634b9..f9f733ef7f 100644
--- a/lib/spack/spack/repo.py
+++ b/lib/spack/spack/repo.py
@@ -150,7 +150,8 @@ class FastPackageChecker(Mapping):
pkg_dir = os.path.join(self.packages_path, pkg_name)
# Warn about invalid names that look like packages.
- if not valid_module_name(pkg_name):
+ if (not valid_module_name(pkg_name)
+ and not pkg_name.startswith('.')):
msg = 'Skipping package at {0}. '
msg += '"{1}" is not a valid Spack module name.'
tty.warn(msg.format(pkg_dir, pkg_name))
diff --git a/lib/spack/spack/test/cmd/pkg.py b/lib/spack/spack/test/cmd/pkg.py
new file mode 100644
index 0000000000..c9e8e74c4a
--- /dev/null
+++ b/lib/spack/spack/test/cmd/pkg.py
@@ -0,0 +1,229 @@
+# Copyright 2013-2019 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)
+
+from __future__ import print_function
+
+import pytest
+import re
+import shutil
+
+from llnl.util.filesystem import mkdirp, working_dir
+
+import spack.main
+import spack.cmd.pkg
+from spack.util.executable import which
+
+pytestmark = pytest.mark.skipif(not which('git'),
+ reason="spack pkg tests require git")
+
+#: new fake package template
+pkg_template = '''\
+from spack import *
+
+class {name}(Package):
+ homepage = "http://www.example.com"
+ url = "http://www.example.com/test-1.0.tar.gz"
+
+ version('1.0', '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(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
+
+ git = which('git', required=True)
+ with working_dir(mock_repo_packages):
+ git('init')
+
+ # initial commit with mock packages
+ git('add', '.')
+ git('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-c/package.py', 'w') as f:
+ f.write(pkg_template.format(name='PkgB'))
+ with open('pkg-b/package.py', 'w') as f:
+ f.write(pkg_template.format(name='PkgC'))
+ git('add', 'pkg-a', 'pkg-b', 'pkg-c')
+ git('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('commit', '-m', 'change pkg-b, remove pkg-c, add pkg-d')
+
+ with spack.repo.swap(mock_repo):
+ yield mock_repo_packages
+
+
+@pytest.fixture(scope='module')
+def mock_pkg_names():
+ repo = spack.repo.path.get_repo('builtin.mock')
+ names = set(name for name in repo.all_package_names()
+ 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.cmd.pkg.packages_path() ==
+ spack.repo.path.get_repo('builtin').packages_path)
+
+
+def test_mock_packages_path(mock_packages):
+ assert (spack.cmd.pkg.packages_path() ==
+ spack.repo.path.get_repo('builtin.mock').packages_path)
+
+
+def test_pkg_add(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')
+
+ git = which('git', required=True)
+ 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')
+
+ with pytest.raises(spack.main.SpackCommandError):
+ pkg('add', 'does-not-exist')
+
+
+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)
+
+
+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']
+
+
+def test_pkg_added(mock_pkg_git_repo):
+ out = split(pkg('added', 'HEAD^^', 'HEAD^'))
+ assert out == ['pkg-a', 'pkg-b', 'pkg-c']
+
+ out = split(pkg('added', 'HEAD^^', 'HEAD'))
+ assert out == ['pkg-a', 'pkg-b', 'pkg-d']
+
+ out = split(pkg('added', 'HEAD^', 'HEAD'))
+ assert out == ['pkg-d']
+
+ out = split(pkg('added', 'HEAD', 'HEAD'))
+ assert out == []
+
+
+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']
+
+
+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')