From da84764e97ead919bee7fb02d469c25a6b03ab63 Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Wed, 1 Oct 2014 23:32:36 -0700 Subject: [PATCH] Add test case for git fetching. --- lib/spack/spack/fetch_strategy.py | 15 +- lib/spack/spack/package.py | 21 ++- lib/spack/spack/packages.py | 21 ++- lib/spack/spack/test/__init__.py | 3 +- lib/spack/spack/test/git_fetch.py | 175 ++++++++++++++++++++ lib/spack/spack/test/install.py | 5 - var/spack/mock_packages/git-test/package.py | 10 ++ 7 files changed, 232 insertions(+), 18 deletions(-) create mode 100644 lib/spack/spack/test/git_fetch.py create mode 100644 var/spack/mock_packages/git-test/package.py diff --git a/lib/spack/spack/fetch_strategy.py b/lib/spack/spack/fetch_strategy.py index cba0ace6d3..328f45c3d6 100644 --- a/lib/spack/spack/fetch_strategy.py +++ b/lib/spack/spack/fetch_strategy.py @@ -225,15 +225,14 @@ def __init__(self, name, *rev_types, **kwargs): "%s requires %s argument." % (self.__class__, name)) # Ensure that there's only one of the rev_types - if sum((k in kwargs for k in rev_types)) > 1: + if sum(k in kwargs for k in rev_types) > 1: raise FetchStrategyError( "Supply only one of %s to fetch with %s." % ( comma_or(rev_types), name)) # Set attributes for each rev type. for rt in rev_types: - setattr(self, rt, getattr(kwargs, rt, None)) - + setattr(self, rt, kwargs.get(rt, None)) def check(self): assert(self.stage) @@ -301,7 +300,14 @@ def fetch(self): tty.msg("Already fetched %s." % self.stage.source_path) return - tty.msg("Trying to clone git repository: %s" % self.url) + args = [] + if self.commit: + args.append('at commit %s' % self.commit) + elif self.tag: + args.append('at tag %s' % self.branch) + elif self.branch: + args.append('on branch %s' % self.branch) + tty.msg("Trying to clone git repository:", self.url, *args) if self.commit: # Need to do a regular clone and check out everything if @@ -460,4 +466,3 @@ def __init__(self, msg, long_msg): class InvalidArgsError(FetchStrategyError): def __init__(self, msg, long_msg): super(InvalidArgsError, self).__init__(msg, long_msg) - diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py index e9fca9ec49..c6e1fd90ef 100644 --- a/lib/spack/spack/package.py +++ b/lib/spack/spack/package.py @@ -372,10 +372,8 @@ def ensure_has_dict(attr_name): if not hasattr(self, 'url'): self.url = None - # Set up a fetch strategy for this package. - self.fetcher = None - if self.spec.concrete: - self.fetcher = fs.from_args(self.versions[self.version], self) + # Init fetch strategy to None + self._fetcher = None # Set a default list URL (place to find available versions) if not hasattr(self, 'list_url'): @@ -458,6 +456,21 @@ def stage(self): return self._stage + @property + def fetcher(self): + if not self.spec.concrete: + raise ValueError("Can only get a fetcher for a concrete package.") + + if not self._fetcher: + self._fetcher = fs.from_args(self.versions[self.version], self) + return self._fetcher + + + @fetcher.setter + def fetcher(self, f): + self._fetcher = f + + def mirror_path(self): """Get path to this package's archive in a mirror.""" filename = "%s-%s." % (self.name, self.version) diff --git a/lib/spack/spack/packages.py b/lib/spack/spack/packages.py index 72f9403a64..047d82a93a 100644 --- a/lib/spack/spack/packages.py +++ b/lib/spack/spack/packages.py @@ -47,10 +47,10 @@ def _autospec(function): """Decorator that automatically converts the argument of a single-arg function to a Spec.""" - def converter(self, spec_like): + def converter(self, spec_like, **kwargs): if not isinstance(spec_like, spack.spec.Spec): spec_like = spack.spec.Spec(spec_like) - return function(self, spec_like) + return function(self, spec_like, **kwargs) return converter @@ -63,10 +63,14 @@ def __init__(self, root): @_autospec - def get(self, spec): + def get(self, spec, **kwargs): if spec.virtual: raise UnknownPackageError(spec.name) + if kwargs.get('new', False): + if spec in self.instances: + del self.instances[spec] + if not spec in self.instances: package_class = self.get_class_for_package_name(spec.name) try: @@ -77,6 +81,17 @@ def get(self, spec): return self.instances[spec] + @_autospec + def delete(self, spec): + """Force a package to be recreated.""" + del self.instances[spec] + + + def purge(self): + """Clear entire package instance cache.""" + self.instances.clear() + + @_autospec def get_installed(self, spec): """Get all the installed specs that satisfy the provided spec constraint.""" diff --git a/lib/spack/spack/test/__init__.py b/lib/spack/spack/test/__init__.py index 8ddc7f227d..b00f9a31ce 100644 --- a/lib/spack/spack/test/__init__.py +++ b/lib/spack/spack/test/__init__.py @@ -47,7 +47,8 @@ 'package_sanity', 'config', 'directory_layout', - 'python_version'] + 'python_version', + 'git_fetch'] def list_tests(): diff --git a/lib/spack/spack/test/git_fetch.py b/lib/spack/spack/test/git_fetch.py new file mode 100644 index 0000000000..c7c11621f9 --- /dev/null +++ b/lib/spack/spack/test/git_fetch.py @@ -0,0 +1,175 @@ +############################################################################## +# Copyright (c) 2013, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://scalability-llnl.github.io/spack +# Please also see the LICENSE file for our notice and the LGPL. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License (as published by +# the Free Software Foundation) version 2.1 dated February 1999. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and +# conditions of the GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +############################################################################## +import os +import unittest +import shutil +import tempfile +from contextlib import closing + +from llnl.util.filesystem import * + +import spack +from spack.version import ver +from spack.stage import Stage +from spack.util.executable import which +from spack.test.mock_packages_test import * + +test_repo_path = 'test-repo' +test_file_name = 'test-file.txt' + +test_branch = 'test-branch' +test_branch_file_name = 'branch-test-file' + +test_tag_branch = 'test-tag-branch' +test_tag = 'test-tag' +test_tag_file_name = 'tag-test-file' + +untracked = 'foobarbaz' + +git = which('git', required=True) + +def rev_hash(rev): + return git('rev-parse', rev, return_output=True).strip() + + +class GitFetchTest(MockPackagesTest): + """Tests fetching from a dummy git repository.""" + + def setUp(self): + """Create a git repository with master and two other branches, + and one tag, so that we can experiment on it.""" + super(GitFetchTest, self).setUp() + self.stage = Stage('fetch-test') + + self.repo_path = join_path(self.stage.path, test_repo_path) + mkdirp(self.repo_path) + + self.test_file = join_path(self.repo_path, test_file_name) + touch(self.test_file) + + with working_dir(self.repo_path): + git('init') + git('add', self.test_file) + git('commit', '-m', 'testing') + + git('branch', test_branch) + git('branch', test_tag_branch) + + git('checkout', test_branch) + touch(test_branch_file_name) + git('add', test_branch_file_name) + git('commit', '-m' 'branch test') + + git('checkout', test_tag_branch) + touch(test_tag_file_name) + git('add', test_tag_file_name) + git('commit', '-m' 'tag test') + git('tag', test_tag) + + git('checkout', 'master') + + self.commit = rev_hash(test_tag) + + spec = Spec('git-test') + spec.concretize() + self.pkg = spack.db.get(spec, new=True) + + + def tearDown(self): + """Destroy the stage space used by this test.""" + super(GitFetchTest, self).tearDown() + + if self.stage is not None: + self.stage.destroy() + + self.pkg.do_clean_dist() + + + def assert_rev(self, rev): + """Check that the current git revision is equal to the supplied rev.""" + self.assertEqual(rev_hash('HEAD'), rev_hash(rev)) + + + def try_fetch(self, rev, test_file, args): + """Tries to: + 1. Fetch the repo using a fetch strategy constructed with + supplied args. + 2. Check if the test_file is in the checked out repository. + 3. Assert that the repository is at the revision supplied. + 4. Add and remove some files, then reset the repo, and + ensure it's all there again. + """ + self.pkg.versions[ver('git')] = args + + self.pkg.do_stage() + self.assert_rev(rev) + + file_path = join_path(self.pkg.stage.source_path, test_file) + self.assertTrue(os.path.isdir(self.pkg.stage.source_path)) + self.assertTrue(os.path.isfile(file_path)) + + os.unlink(file_path) + self.assertFalse(os.path.isfile(file_path)) + + touch(untracked) + self.assertTrue(os.path.isfile(untracked)) + self.pkg.do_clean_work() + self.assertFalse(os.path.isfile(untracked)) + + self.assertTrue(os.path.isdir(self.pkg.stage.source_path)) + self.assertTrue(os.path.isfile(file_path)) + + self.assert_rev(rev) + + + def test_fetch_master(self): + """Test a default git checkout with no commit or tag specified.""" + self.try_fetch('master', test_file_name, { + 'git' : self.repo_path + }) + + + def test_fetch_branch(self): + """Test fetching a branch.""" + self.try_fetch(test_branch, test_branch_file_name, { + 'git' : self.repo_path, + 'branch' : test_branch + }) + + + def test_fetch_tag(self): + """Test fetching a tag.""" + self.try_fetch(test_tag, test_tag_file_name, { + 'git' : self.repo_path, + 'tag' : test_tag + }) + + + def test_fetch_commit(self): + """Test fetching a particular commit.""" + self.try_fetch(self.commit, test_tag_file_name, { + 'git' : self.repo_path, + 'commit' : self.commit + }) diff --git a/lib/spack/spack/test/install.py b/lib/spack/spack/test/install.py index 896f19ac01..0d53bb45c7 100644 --- a/lib/spack/spack/test/install.py +++ b/lib/spack/spack/test/install.py @@ -101,13 +101,8 @@ def test_install_and_uninstall(self): self.assertTrue(spec.concrete) # Get the package - print - print "======== GETTING PACKAGE ========" pkg = spack.db.get(spec) - print "======== GOT PACKAGE ========" - print - # Fake the URL for the package so it downloads from a file. archive_path = join_path(self.stage.path, archive_name) pkg.fetcher = URLFetchStrategy('file://' + archive_path) diff --git a/var/spack/mock_packages/git-test/package.py b/var/spack/mock_packages/git-test/package.py new file mode 100644 index 0000000000..689185463c --- /dev/null +++ b/var/spack/mock_packages/git-test/package.py @@ -0,0 +1,10 @@ +from spack import * + +class GitTest(Package): + """Mock package that uses git for fetching.""" + homepage = "http://www.git-fetch-example.com" + + version('git', git='to-be-filled-in-by-test') + + def install(self, spec, prefix): + pass