commands: add tests for all subcommands of spack pkg
				
					
				
			This commit is contained in:
		| @@ -7,15 +7,15 @@ | |||||||
|  |  | ||||||
| import os | import os | ||||||
| import argparse | import argparse | ||||||
|  | import re | ||||||
|  |  | ||||||
| import llnl.util.tty as tty | import llnl.util.tty as tty | ||||||
| from llnl.util.tty.colify import colify | from llnl.util.tty.colify import colify | ||||||
| from llnl.util.filesystem import working_dir |  | ||||||
|  |  | ||||||
|  | import spack.cmd | ||||||
| import spack.paths | import spack.paths | ||||||
| import spack.repo | import spack.repo | ||||||
| from spack.util.executable import which | from spack.util.executable import which | ||||||
| from spack.cmd import spack_is_git_repo |  | ||||||
|  |  | ||||||
| description = "query packages associated with particular git revisions" | description = "query packages associated with particular git revisions" | ||||||
| section = "developer" | section = "developer" | ||||||
| @@ -57,6 +57,10 @@ def setup_parser(subparser): | |||||||
|     add_parser.add_argument( |     add_parser.add_argument( | ||||||
|         'rev2', nargs='?', default='HEAD', |         'rev2', nargs='?', default='HEAD', | ||||||
|         help="revision to compare to rev1 (default is 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 = sp.add_parser('removed', help=pkg_removed.__doc__) | ||||||
|     rm_parser.add_argument( |     rm_parser.add_argument( | ||||||
| @@ -84,20 +88,20 @@ def get_git(): | |||||||
|  |  | ||||||
|  |  | ||||||
| def list_packages(rev): | def list_packages(rev): | ||||||
|     pkgpath = packages_path() |  | ||||||
|     relpath = pkgpath[len(spack.paths.prefix + os.path.sep):] + os.path.sep |  | ||||||
|  |  | ||||||
|     git = get_git() |     git = get_git() | ||||||
|     with working_dir(spack.paths.prefix): |  | ||||||
|         output = git('ls-tree', '--full-tree', '--name-only', rev, relpath, |     # git ls-tree does not support ... merge-base syntax, so do it manually | ||||||
|                      output=str) |     if rev.endswith('...'): | ||||||
|     return sorted(line[len(relpath):] for line in output.split('\n') if line) |         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): | def pkg_add(args): | ||||||
|     """add a package to the git stage with `git add`""" |     """add a package to the git stage with `git add`""" | ||||||
|     pkgpath = packages_path() |  | ||||||
|  |  | ||||||
|     for pkg_name in args.packages: |     for pkg_name in args.packages: | ||||||
|         filename = spack.repo.path.filename_for_package_name(pkg_name) |         filename = spack.repo.path.filename_for_package_name(pkg_name) | ||||||
|         if not os.path.isfile(filename): |         if not os.path.isfile(filename): | ||||||
| @@ -105,8 +109,7 @@ def pkg_add(args): | |||||||
|                     pkg_name, filename) |                     pkg_name, filename) | ||||||
|  |  | ||||||
|         git = get_git() |         git = get_git() | ||||||
|         with working_dir(spack.paths.prefix): |         git('add', filename) | ||||||
|             git('-C', pkgpath, 'add', filename) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def pkg_list(args): | def pkg_list(args): | ||||||
| @@ -151,24 +154,38 @@ def pkg_added(args): | |||||||
|  |  | ||||||
| def pkg_changed(args): | def pkg_changed(args): | ||||||
|     """show packages changed since a commit""" |     """show packages changed since a commit""" | ||||||
|     pkgpath = spack.repo.path.get_repo('builtin').packages_path |     lower_type = args.type.lower() | ||||||
|     rel_pkg_path = os.path.relpath(pkgpath, spack.paths.prefix) |     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() |     git = get_git() | ||||||
|     paths = git('diff', '--name-only', args.rev1, args.rev2, pkgpath, |     out = git('diff', '--relative', '--name-only', args.rev1, args.rev2, | ||||||
|                 output=str).strip().split('\n') |               output=str).strip() | ||||||
|  |  | ||||||
|     packages = set([]) |     lines = [] if not out else re.split(r'\s+', out) | ||||||
|     for path in paths: |     changed = set() | ||||||
|         path = path.replace(rel_pkg_path + os.sep, '') |     for path in lines: | ||||||
|         pkg_name, _, _ = path.partition(os.sep) |         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 | ||||||
|  |  | ||||||
|  |     if packages: | ||||||
|         colify(sorted(packages)) |         colify(sorted(packages)) | ||||||
|  |  | ||||||
|  |  | ||||||
| def pkg(parser, args): | 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'") |         tty.die("This spack is not a git clone. Can't use 'spack pkg'") | ||||||
|  |  | ||||||
|     action = {'add': pkg_add, |     action = {'add': pkg_add, | ||||||
|   | |||||||
| @@ -150,7 +150,8 @@ def _create_new_cache(self): | |||||||
|             pkg_dir = os.path.join(self.packages_path, pkg_name) |             pkg_dir = os.path.join(self.packages_path, pkg_name) | ||||||
|  |  | ||||||
|             # Warn about invalid names that look like packages. |             # 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 = 'Skipping package at {0}. ' | ||||||
|                 msg += '"{1}" is not a valid Spack module name.' |                 msg += '"{1}" is not a valid Spack module name.' | ||||||
|                 tty.warn(msg.format(pkg_dir, pkg_name)) |                 tty.warn(msg.format(pkg_dir, pkg_name)) | ||||||
|   | |||||||
							
								
								
									
										229
									
								
								lib/spack/spack/test/cmd/pkg.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								lib/spack/spack/test/cmd/pkg.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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') | ||||||
		Reference in New Issue
	
	Block a user
	 Todd Gamblin
					Todd Gamblin