stage: make source_path available before stage is built
				
					
				
			- `stage.source_path` was previously overloaded; it returned `None` if it
  didn't exist and this was used by client code
  - we want to be able to know the `source_path` before it's created
- make stage.source_path available before it exists.
  - use a well-known stage source path name, `$stage_path/src` that is
    available when `Stage` is instantiated but does not exist until it's
    "expanded"
  - client code can now use the variable before the stage is created.
  - client code can test whether the tarball is expanded by using the new
    `stage.expanded` property instead of testing whether `source_path` is
    `None`
- add tests for the new source_path semantics
			
			
This commit is contained in:
		 Tamara Dahlgren
					Tamara Dahlgren
				
			
				
					committed by
					
						 Todd Gamblin
						Todd Gamblin
					
				
			
			
				
	
			
			
			 Todd Gamblin
						Todd Gamblin
					
				
			
						parent
						
							eb584d895b
						
					
				
				
					commit
					1842873f85
				
			| @@ -107,7 +107,7 @@ def location(parser, args): | ||||
|                     print(pkg.stage.path) | ||||
|  | ||||
|                 else:  # args.build_dir is the default. | ||||
|                     if not pkg.stage.source_path: | ||||
|                     if not pkg.stage.expanded: | ||||
|                         tty.die("Build directory does not exist yet. " | ||||
|                                 "Run this to create it:", | ||||
|                                 "spack stage " + " ".join(args.spec)) | ||||
|   | ||||
| @@ -60,6 +60,13 @@ def wrapper(self, *args, **kwargs): | ||||
|     return wrapper | ||||
|  | ||||
|  | ||||
| def _ensure_one_stage_entry(stage_path): | ||||
|     """Ensure there is only one stage entry in the stage path.""" | ||||
|     stage_entries = os.listdir(stage_path) | ||||
|     assert len(stage_entries) == 1 | ||||
|     return os.path.join(stage_path, stage_entries[0]) | ||||
|  | ||||
|  | ||||
| class FSMeta(type): | ||||
|     """This metaclass registers all fetch strategies in a list.""" | ||||
|     def __init__(cls, name, bases, dict): | ||||
| @@ -302,6 +309,7 @@ def fetch(self): | ||||
|             raise FailedDownloadError(self.url) | ||||
|  | ||||
|     @property | ||||
|     @_needs_stage | ||||
|     def archive_file(self): | ||||
|         """Path to the source archive within this stage directory.""" | ||||
|         return self.stage.archive_file | ||||
| @@ -313,7 +321,13 @@ def cachable(self): | ||||
|     @_needs_stage | ||||
|     def expand(self): | ||||
|         if not self.expand_archive: | ||||
|             tty.msg("Skipping expand step for %s" % self.archive_file) | ||||
|             tty.msg("Staging unexpanded archive %s in %s" % ( | ||||
|                     self.archive_file, self.stage.source_path)) | ||||
|             if not self.stage.expanded: | ||||
|                 mkdirp(self.stage.source_path) | ||||
|             dest = os.path.join(self.stage.source_path, | ||||
|                                 os.path.basename(self.archive_file)) | ||||
|             shutil.move(self.archive_file, dest) | ||||
|             return | ||||
|  | ||||
|         tty.msg("Staging archive: %s" % self.archive_file) | ||||
| @@ -326,6 +340,10 @@ def expand(self): | ||||
|         if not self.extension: | ||||
|             self.extension = extension(self.archive_file) | ||||
|  | ||||
|         if self.stage.expanded: | ||||
|             tty.debug('Source already staged to %s' % self.stage.source_path) | ||||
|             return | ||||
|  | ||||
|         decompress = decompressor_for(self.archive_file, self.extension) | ||||
|  | ||||
|         # Expand all tarballs in their own directory to contain | ||||
| @@ -337,26 +355,37 @@ def expand(self): | ||||
|         with working_dir(tarball_container): | ||||
|             decompress(self.archive_file) | ||||
|  | ||||
|         # Check for an exploding tarball, i.e. one that doesn't expand | ||||
|         # to a single directory.  If the tarball *didn't* explode, | ||||
|         # move contents up & remove the container directory. | ||||
|         # Check for an exploding tarball, i.e. one that doesn't expand to | ||||
|         # a single directory.  If the tarball *didn't* explode, move its | ||||
|         # contents to the staging source directory & remove the container | ||||
|         # directory.  If the tarball did explode, just rename the tarball | ||||
|         # directory to the staging source directory. | ||||
|         # | ||||
|         # NOTE: The tar program on Mac OS X will encode HFS metadata | ||||
|         # in hidden files, which can end up *alongside* a single | ||||
|         # top-level directory.  We ignore hidden files to accomodate | ||||
|         # these "semi-exploding" tarballs. | ||||
|         # NOTE: The tar program on Mac OS X will encode HFS metadata in | ||||
|         # hidden files, which can end up *alongside* a single top-level | ||||
|         # directory.  We initially ignore presence of hidden files to | ||||
|         # accomodate these "semi-exploding" tarballs but ensure the files | ||||
|         # are copied to the source directory. | ||||
|         files = os.listdir(tarball_container) | ||||
|         non_hidden = [f for f in files if not f.startswith('.')] | ||||
|         if len(non_hidden) == 1: | ||||
|             expanded_dir = os.path.join(tarball_container, non_hidden[0]) | ||||
|             if os.path.isdir(expanded_dir): | ||||
|             src = os.path.join(tarball_container, non_hidden[0]) | ||||
|             if os.path.isdir(src): | ||||
|                 shutil.move(src, self.stage.source_path) | ||||
|                 if len(files) > 1: | ||||
|                     files.remove(non_hidden[0]) | ||||
|                     for f in files: | ||||
|                     shutil.move(os.path.join(tarball_container, f), | ||||
|                                 os.path.join(self.stage.path, f)) | ||||
|                         src = os.path.join(tarball_container, f) | ||||
|                         dest = os.path.join(self.stage.path, f) | ||||
|                         shutil.move(src, dest) | ||||
|                 os.rmdir(tarball_container) | ||||
|             else: | ||||
|                 # This is a non-directory entry (e.g., a patch file) so simply | ||||
|                 # rename the tarball container to be the source path. | ||||
|                 shutil.move(tarball_container, self.stage.source_path) | ||||
|  | ||||
|         if not files: | ||||
|             os.rmdir(tarball_container) | ||||
|         else: | ||||
|             shutil.move(tarball_container, self.stage.source_path) | ||||
|  | ||||
|     def archive(self, destination): | ||||
|         """Just moves this archive to the destination.""" | ||||
| @@ -390,7 +419,7 @@ def reset(self): | ||||
|                 "Tried to reset URLFetchStrategy before fetching", | ||||
|                 "Failed on reset() for URL %s" % self.url) | ||||
|  | ||||
|         # Remove everythigng but the archive from the stage | ||||
|         # Remove everything but the archive from the stage | ||||
|         for filename in os.listdir(self.stage.path): | ||||
|             abspath = os.path.join(self.stage.path, filename) | ||||
|             if abspath != self.archive_file: | ||||
| @@ -549,6 +578,15 @@ def fetch(self): | ||||
|     def archive(self, destination): | ||||
|         super(GoFetchStrategy, self).archive(destination, exclude='.git') | ||||
|  | ||||
|     @_needs_stage | ||||
|     def expand(self): | ||||
|         tty.debug( | ||||
|             "Source fetched with %s is already expanded." % self.url_attr) | ||||
|  | ||||
|         # Move the directory to the well-known stage source path | ||||
|         repo_root = _ensure_one_stage_entry(self.stage.path) | ||||
|         shutil.move(repo_root, self.stage.source_path) | ||||
|  | ||||
|     @_needs_stage | ||||
|     def reset(self): | ||||
|         with working_dir(self.stage.source_path): | ||||
| @@ -634,8 +672,9 @@ def _repo_info(self): | ||||
|  | ||||
|         return '{0}{1}'.format(self.url, args) | ||||
|  | ||||
|     @_needs_stage | ||||
|     def fetch(self): | ||||
|         if self.stage.source_path: | ||||
|         if self.stage.expanded: | ||||
|             tty.msg("Already fetched {0}".format(self.stage.source_path)) | ||||
|             return | ||||
|  | ||||
| @@ -645,17 +684,16 @@ def fetch(self): | ||||
|         if self.commit: | ||||
|             # Need to do a regular clone and check out everything if | ||||
|             # they asked for a particular commit. | ||||
|             with working_dir(self.stage.path): | ||||
|                 if spack.config.get('config:debug'): | ||||
|                     git('clone', self.url) | ||||
|                 else: | ||||
|                     git('clone', '--quiet', self.url) | ||||
|             args = ['clone', self.url, self.stage.source_path] | ||||
|             if not spack.config.get('config:debug'): | ||||
|                 args.insert(1, '--quiet') | ||||
|             git(*args) | ||||
|  | ||||
|             with working_dir(self.stage.source_path): | ||||
|                 if spack.config.get('config:debug'): | ||||
|                     git('checkout', self.commit) | ||||
|                 else: | ||||
|                     git('checkout', '--quiet', self.commit) | ||||
|                 args = ['checkout', self.commit] | ||||
|                 if not spack.config.get('config:debug'): | ||||
|                     args.insert(1, '--quiet') | ||||
|                 git(*args) | ||||
|  | ||||
|         else: | ||||
|             # Can be more efficient if not checking out a specific commit. | ||||
| @@ -674,20 +712,20 @@ def fetch(self): | ||||
|             if self.git_version > ver('1.7.10'): | ||||
|                 args.append('--single-branch') | ||||
|  | ||||
|             with working_dir(self.stage.path): | ||||
|             cloned = False | ||||
|             # Yet more efficiency, only download a 1-commit deep tree | ||||
|             if self.git_version >= ver('1.7.1'): | ||||
|                 try: | ||||
|                         git(*(args + ['--depth', '1', self.url])) | ||||
|                     git(*(args + ['--depth', '1', self.url, | ||||
|                                   self.stage.source_path])) | ||||
|                     cloned = True | ||||
|                     except spack.error.SpackError: | ||||
|                 except spack.error.SpackError as e: | ||||
|                     # This will fail with the dumb HTTP transport | ||||
|                     # continue and try without depth, cleanup first | ||||
|                         pass | ||||
|                     tty.debug(e) | ||||
|  | ||||
|             if not cloned: | ||||
|                     args.append(self.url) | ||||
|                 args.extend([self.url, self.stage.source_path]) | ||||
|                 git(*args) | ||||
|  | ||||
|             with working_dir(self.stage.source_path): | ||||
| @@ -698,21 +736,22 @@ def fetch(self): | ||||
|                     # pull --tags returns a "special" error code of 1 in | ||||
|                     # older versions that we have to ignore. | ||||
|                     # see: https://github.com/git/git/commit/19d122b | ||||
|                         if spack.config.get('config:debug'): | ||||
|                             git('pull', '--tags', ignore_errors=1) | ||||
|                             git('checkout', self.tag) | ||||
|                         else: | ||||
|                             git('pull', '--quiet', '--tags', ignore_errors=1) | ||||
|                             git('checkout', '--quiet', self.tag) | ||||
|                     pull_args = ['pull', '--tags'] | ||||
|                     co_args = ['checkout', self.tag] | ||||
|                     if not spack.config.get('config:debug'): | ||||
|                         pull_args.insert(1, '--quiet') | ||||
|                         co_args.insert(1, '--quiet') | ||||
|  | ||||
|                     git(*pull_args, ignore_errors=1) | ||||
|                     git(*co_args) | ||||
|  | ||||
|         with working_dir(self.stage.source_path): | ||||
|         # Init submodules if the user asked for them. | ||||
|         if self.submodules: | ||||
|                 if spack.config.get('config:debug'): | ||||
|                     git('submodule', 'update', '--init', '--recursive') | ||||
|                 else: | ||||
|                     git('submodule', '--quiet', 'update', '--init', | ||||
|                         '--recursive') | ||||
|             with working_dir(self.stage.source_path): | ||||
|                 args = ['submodule', 'update', '--init', '--recursive'] | ||||
|                 if not spack.config.get('config:debug'): | ||||
|                     args.insert(1, '--quiet') | ||||
|                 git(*args) | ||||
|  | ||||
|     def archive(self, destination): | ||||
|         super(GitFetchStrategy, self).archive(destination, exclude='.git') | ||||
| @@ -720,12 +759,14 @@ def archive(self, destination): | ||||
|     @_needs_stage | ||||
|     def reset(self): | ||||
|         with working_dir(self.stage.source_path): | ||||
|             co_args = ['checkout', '.'] | ||||
|             clean_args = ['clean', '-f'] | ||||
|             if spack.config.get('config:debug'): | ||||
|                 self.git('checkout', '.') | ||||
|                 self.git('clean', '-f') | ||||
|             else: | ||||
|                 self.git('checkout', '--quiet', '.') | ||||
|                 self.git('clean', '--quiet', '-f') | ||||
|                 co_args.insert(1, '--quiet') | ||||
|                 clean_args.insert(1, '--quiet') | ||||
|  | ||||
|             self.git(*co_args) | ||||
|             self.git(*clean_args) | ||||
|  | ||||
|     def __str__(self): | ||||
|         return '[git] {0}'.format(self._repo_info()) | ||||
| @@ -778,7 +819,7 @@ def get_source_id(self): | ||||
|  | ||||
|     @_needs_stage | ||||
|     def fetch(self): | ||||
|         if self.stage.source_path: | ||||
|         if self.stage.expanded: | ||||
|             tty.msg("Already fetched %s" % self.stage.source_path) | ||||
|             return | ||||
|  | ||||
| @@ -787,9 +828,7 @@ def fetch(self): | ||||
|         args = ['checkout', '--force', '--quiet'] | ||||
|         if self.revision: | ||||
|             args += ['-r', self.revision] | ||||
|         args.append(self.url) | ||||
|  | ||||
|         with working_dir(self.stage.path): | ||||
|         args.extend([self.url, self.stage.source_path]) | ||||
|         self.svn(*args) | ||||
|  | ||||
|     def _remove_untracked_files(self): | ||||
| @@ -881,7 +920,7 @@ def get_source_id(self): | ||||
|  | ||||
|     @_needs_stage | ||||
|     def fetch(self): | ||||
|         if self.stage.source_path: | ||||
|         if self.stage.expanded: | ||||
|             tty.msg("Already fetched %s" % self.stage.source_path) | ||||
|             return | ||||
|  | ||||
| @@ -895,12 +934,10 @@ def fetch(self): | ||||
|         if not spack.config.get('config:verify_ssl'): | ||||
|             args.append('--insecure') | ||||
|  | ||||
|         args.append(self.url) | ||||
|  | ||||
|         if self.revision: | ||||
|             args.extend(['-r', self.revision]) | ||||
|  | ||||
|         with working_dir(self.stage.path): | ||||
|         args.extend([self.url, self.stage.source_path]) | ||||
|         self.hg(*args) | ||||
|  | ||||
|     def archive(self, destination): | ||||
|   | ||||
| @@ -519,12 +519,12 @@ def mock_archive(tmpdir_factory): | ||||
|     tar = spack.util.executable.which('tar', required=True) | ||||
|  | ||||
|     tmpdir = tmpdir_factory.mktemp('mock-archive-dir') | ||||
|     expanded_archive_basedir = 'mock-archive-repo' | ||||
|     tmpdir.ensure(expanded_archive_basedir, dir=True) | ||||
|     repodir = tmpdir.join(expanded_archive_basedir) | ||||
|     tmpdir.ensure(spack.stage._source_path_subdir, dir=True) | ||||
|     repodir = tmpdir.join(spack.stage._source_path_subdir) | ||||
|  | ||||
|     # Create the configure script | ||||
|     configure_path = str(tmpdir.join(expanded_archive_basedir, 'configure')) | ||||
|     configure_path = str(tmpdir.join(spack.stage._source_path_subdir, | ||||
|                                      'configure')) | ||||
|     with open(configure_path, 'w') as f: | ||||
|         f.write( | ||||
|             "#!/bin/sh\n" | ||||
| @@ -541,8 +541,8 @@ def mock_archive(tmpdir_factory): | ||||
|  | ||||
|     # Archive it | ||||
|     with tmpdir.as_cwd(): | ||||
|         archive_name = '{0}.tar.gz'.format(expanded_archive_basedir) | ||||
|         tar('-czf', archive_name, expanded_archive_basedir) | ||||
|         archive_name = '{0}.tar.gz'.format(spack.stage._source_path_subdir) | ||||
|         tar('-czf', archive_name, spack.stage._source_path_subdir) | ||||
|  | ||||
|     Archive = collections.namedtuple('Archive', | ||||
|                                      ['url', 'path', 'archive_file', | ||||
| @@ -554,7 +554,7 @@ def mock_archive(tmpdir_factory): | ||||
|         url=('file://' + archive_file), | ||||
|         archive_file=archive_file, | ||||
|         path=str(repodir), | ||||
|         expanded_archive_basedir=expanded_archive_basedir) | ||||
|         expanded_archive_basedir=spack.stage._source_path_subdir) | ||||
|  | ||||
|  | ||||
| @pytest.fixture(scope='session') | ||||
| @@ -565,9 +565,8 @@ def mock_git_repository(tmpdir_factory): | ||||
|     git = spack.util.executable.which('git', required=True) | ||||
|  | ||||
|     tmpdir = tmpdir_factory.mktemp('mock-git-repo-dir') | ||||
|     expanded_archive_basedir = 'mock-git-repo' | ||||
|     tmpdir.ensure(expanded_archive_basedir, dir=True) | ||||
|     repodir = tmpdir.join(expanded_archive_basedir) | ||||
|     tmpdir.ensure(spack.stage._source_path_subdir, dir=True) | ||||
|     repodir = tmpdir.join(spack.stage._source_path_subdir) | ||||
|  | ||||
|     # Initialize the repository | ||||
|     with repodir.as_cwd(): | ||||
| @@ -639,9 +638,8 @@ def mock_hg_repository(tmpdir_factory): | ||||
|     hg = spack.util.executable.which('hg', required=True) | ||||
|  | ||||
|     tmpdir = tmpdir_factory.mktemp('mock-hg-repo-dir') | ||||
|     expanded_archive_basedir = 'mock-hg-repo' | ||||
|     tmpdir.ensure(expanded_archive_basedir, dir=True) | ||||
|     repodir = tmpdir.join(expanded_archive_basedir) | ||||
|     tmpdir.ensure(spack.stage._source_path_subdir, dir=True) | ||||
|     repodir = tmpdir.join(spack.stage._source_path_subdir) | ||||
|  | ||||
|     get_rev = lambda: hg('id', '-i', output=str).strip() | ||||
|  | ||||
| @@ -685,9 +683,8 @@ def mock_svn_repository(tmpdir_factory): | ||||
|     svnadmin = spack.util.executable.which('svnadmin', required=True) | ||||
|  | ||||
|     tmpdir = tmpdir_factory.mktemp('mock-svn-stage') | ||||
|     expanded_archive_basedir = 'mock-svn-repo' | ||||
|     tmpdir.ensure(expanded_archive_basedir, dir=True) | ||||
|     repodir = tmpdir.join(expanded_archive_basedir) | ||||
|     tmpdir.ensure(spack.stage._source_path_subdir, dir=True) | ||||
|     repodir = tmpdir.join(spack.stage._source_path_subdir) | ||||
|     url = 'file://' + str(repodir) | ||||
|  | ||||
|     # Initialize the repository | ||||
|   | ||||
| @@ -155,7 +155,8 @@ def successful_fetch(_class): | ||||
|             pass | ||||
|  | ||||
|     def successful_expand(_class): | ||||
|         expanded_path = os.path.join(_class.stage.path, 'expanded-dir') | ||||
|         expanded_path = os.path.join(_class.stage.path, | ||||
|                                      spack.stage._source_path_subdir) | ||||
|         os.mkdir(expanded_path) | ||||
|         with open(os.path.join(expanded_path, 'test.patch'), 'w'): | ||||
|             pass | ||||
|   | ||||
| @@ -63,9 +63,9 @@ def test_url_patch(mock_stage, filename, sha256, archive_sha256): | ||||
|         # TODO: there is probably a better way to mock this. | ||||
|         stage.mirror_path = mock_stage  # don't disrupt the spack install | ||||
|  | ||||
|         # fake a source path | ||||
|         # Fake a source path and ensure the directory exists | ||||
|         with working_dir(stage.path): | ||||
|             mkdirp('spack-expanded-archive') | ||||
|             mkdirp(spack.stage._source_path_subdir) | ||||
|  | ||||
|         with working_dir(stage.source_path): | ||||
|             # write a file to be patched | ||||
|   | ||||
| @@ -6,6 +6,9 @@ | ||||
| """Test that the Stage class works correctly.""" | ||||
| import os | ||||
| import collections | ||||
| import shutil | ||||
| import tempfile | ||||
| import getpass | ||||
|  | ||||
| import pytest | ||||
|  | ||||
| @@ -18,27 +21,108 @@ | ||||
| from spack.resource import Resource | ||||
| from spack.stage import Stage, StageComposite, ResourceStage | ||||
|  | ||||
| # The following values are used for common fetch and stage mocking fixtures: | ||||
| _archive_base = 'test-files' | ||||
| _archive_fn = '%s.tar.gz' % _archive_base | ||||
| _extra_fn = 'extra.sh' | ||||
| _hidden_fn = '.hidden' | ||||
| _readme_fn = 'README.txt' | ||||
|  | ||||
| def check_expand_archive(stage, stage_name, mock_archive): | ||||
| _extra_contents = '#!/bin/sh\n' | ||||
| _hidden_contents = '' | ||||
| _readme_contents = 'hello world!\n' | ||||
|  | ||||
| # TODO: Replace the following with an enum once guarantee supported (or | ||||
| # include enum34 for python versions < 3.4. | ||||
| _include_readme = 1 | ||||
| _include_hidden = 2 | ||||
| _include_extra = 3 | ||||
|  | ||||
| # Mock fetch directories are expected to appear as follows: | ||||
| # | ||||
| # TMPDIR/ | ||||
| #     _archive_fn     archive_url = file:///path/to/_archive_fn | ||||
| # | ||||
| # Mock expanded stage directories are expected to have one of two forms, | ||||
| # depending on how the tarball expands.  Non-exploding tarballs are expected | ||||
| # to have the following structure: | ||||
| # | ||||
| # TMPDIR/                 temp stage dir | ||||
| #     src/                well-known stage source directory | ||||
| #         _readme_fn      Optional test_readme (contains _readme_contents) | ||||
| #     _hidden_fn          Optional hidden file (contains _hidden_contents) | ||||
| #     _archive_fn         archive_url = file:///path/to/_archive_fn | ||||
| # | ||||
| # while exploding tarball directories are expected to be structured as follows: | ||||
| # | ||||
| # TMPDIR/                 temp stage dir | ||||
| #     src/                well-known stage source directory | ||||
| #         archive_name/   archive dir | ||||
| #             _readme_fn  test_readme (contains _readme_contents) | ||||
| #         _extra_fn       test_extra file (contains _extra_contents) | ||||
| #     _archive_fn         archive_url = file:///path/to/_archive_fn | ||||
| # | ||||
|  | ||||
|  | ||||
| def check_expand_archive(stage, stage_name, expected_file_list): | ||||
|     """ | ||||
|     Ensure the expanded archive directory contains the expected structure and | ||||
|     files as described in the module-level comments above. | ||||
|     """ | ||||
|     stage_path = get_stage_path(stage, stage_name) | ||||
|     archive_name = 'test-files.tar.gz' | ||||
|     archive_dir = 'test-files' | ||||
|     assert archive_name in os.listdir(stage_path) | ||||
|     assert archive_dir in os.listdir(stage_path) | ||||
|     archive_dir = spack.stage._source_path_subdir | ||||
|  | ||||
|     assert os.path.join(stage_path, archive_dir) == stage.source_path | ||||
|     stage_contents = os.listdir(stage_path) | ||||
|     assert _archive_fn in stage_contents | ||||
|     assert archive_dir in stage_contents | ||||
|  | ||||
|     readme = os.path.join(stage_path, archive_dir, 'README.txt') | ||||
|     assert os.path.isfile(readme) | ||||
|     with open(readme) as file: | ||||
|         'hello world!\n' == file.read() | ||||
|     source_path = os.path.join(stage_path, archive_dir) | ||||
|     assert source_path == stage.source_path | ||||
|  | ||||
|     source_contents = os.listdir(source_path) | ||||
|  | ||||
|     for _include in expected_file_list: | ||||
|         if _include == _include_hidden: | ||||
|             # The hidden file represent the HFS metadata associated with Mac | ||||
|             # OS X tar files so is expected to be in the same directory as | ||||
|             # the archive directory. | ||||
|             assert _hidden_fn in stage_contents | ||||
|  | ||||
|             fn = os.path.join(stage_path, _hidden_fn) | ||||
|             contents = _hidden_contents | ||||
|  | ||||
|         elif _include == _include_readme: | ||||
|             # The standard README.txt file will be in the source directory if | ||||
|             # the tarball didn't explode; otherwise, it will be in the | ||||
|             # original archive subdirectory of it. | ||||
|             if _archive_base in source_contents: | ||||
|                 fn = os.path.join(source_path, _archive_base, _readme_fn) | ||||
|             else: | ||||
|                 fn = os.path.join(source_path, _readme_fn) | ||||
|             contents = _readme_contents | ||||
|  | ||||
|         elif _include == _include_extra: | ||||
|             assert _extra_fn in source_contents | ||||
|  | ||||
|             fn = os.path.join(source_path, _extra_fn) | ||||
|             contents = _extra_contents | ||||
|  | ||||
|         else: | ||||
|             assert False | ||||
|  | ||||
|         assert os.path.isfile(fn) | ||||
|         with open(fn) as _file: | ||||
|             _file.read() == contents | ||||
|  | ||||
|  | ||||
| def check_fetch(stage, stage_name): | ||||
|     archive_name = 'test-files.tar.gz' | ||||
|     """ | ||||
|     Ensure the fetch resulted in a properly placed archive file as described in | ||||
|     the module-level comments. | ||||
|     """ | ||||
|     stage_path = get_stage_path(stage, stage_name) | ||||
|     assert archive_name in os.listdir(stage_path) | ||||
|     assert os.path.join(stage_path, archive_name) == stage.fetcher.archive_file | ||||
|     assert _archive_fn in os.listdir(stage_path) | ||||
|     assert os.path.join(stage_path, _archive_fn) == stage.fetcher.archive_file | ||||
|  | ||||
|  | ||||
| def check_destroy(stage, stage_name): | ||||
| @@ -76,7 +160,7 @@ def check_setup(stage, stage_name, archive): | ||||
|  | ||||
|     else: | ||||
|         # Make sure the stage path is NOT a link for a non-tmp stage | ||||
|         assert os.path.islink(stage_path) | ||||
|         assert not os.path.islink(stage_path) | ||||
|  | ||||
|  | ||||
| def get_stage_path(stage, stage_name): | ||||
| @@ -93,71 +177,187 @@ def get_stage_path(stage, stage_name): | ||||
|         return stage.path | ||||
|  | ||||
|  | ||||
| @pytest.fixture() | ||||
| def tmpdir_for_stage(mock_archive): | ||||
|     """Uses a temporary directory for staging""" | ||||
|     current = spack.paths.stage_path | ||||
|     spack.config.set( | ||||
|         'config', | ||||
|         {'build_stage': [str(mock_archive.test_tmp_dir)]}, | ||||
| @pytest.fixture | ||||
| def no_tmp_root_stage(monkeypatch): | ||||
|     """ | ||||
|     Disable use of a temporary root for staging. | ||||
|  | ||||
|     Note that it can be important for other tests that the previous settings be | ||||
|     restored when the test case is over. | ||||
|     """ | ||||
|     monkeypatch.setattr(spack.stage, '_tmp_root', None) | ||||
|     monkeypatch.setattr(spack.stage, '_use_tmp_stage', False) | ||||
|     yield | ||||
|  | ||||
|  | ||||
| @pytest.fixture | ||||
| def non_user_path_for_stage(): | ||||
|     """ | ||||
|     Use a non-user path for staging. | ||||
|  | ||||
|     Note that it can be important for other tests that the previous settings be | ||||
|     restored when the test case is over. | ||||
|     """ | ||||
|     current = spack.config.get('config:build_stage') | ||||
|     spack.config.set('config', {'build_stage': ['/var/spack/non-path']}, | ||||
|                      scope='user') | ||||
|     yield | ||||
|     spack.config.set('config', {'build_stage': [current]}, scope='user') | ||||
|     spack.config.set('config', {'build_stage': current}, scope='user') | ||||
|  | ||||
|  | ||||
| @pytest.fixture() | ||||
| def mock_archive(tmpdir, monkeypatch): | ||||
|     """Creates a mock archive with the structure expected by the tests""" | ||||
|     # Mock up a stage area that looks like this: | ||||
|     # | ||||
|     # TMPDIR/                    test_files_dir | ||||
|     #     tmp/                   test_tmp_path (where stage should be) | ||||
|     #     test-files/            archive_dir_path | ||||
|     #         README.txt         test_readme (contains "hello world!\n") | ||||
|     #     test-files.tar.gz      archive_url = file:///path/to/this | ||||
|     # | ||||
|     test_tmp_path = tmpdir.join('tmp') | ||||
|     # set _test_tmp_path as the default test directory to use for stages. | ||||
|     spack.config.set( | ||||
|         'config', {'build_stage': [str(test_tmp_path)]}, scope='user') | ||||
| @pytest.fixture | ||||
| def stage_path_for_stage(): | ||||
|     """ | ||||
|     Use the basic stage_path for staging. | ||||
|  | ||||
|     archive_dir = tmpdir.join('test-files') | ||||
|     archive_name = 'test-files.tar.gz' | ||||
|     archive = tmpdir.join(archive_name) | ||||
|     archive_url = 'file://' + str(archive) | ||||
|     test_readme = archive_dir.join('README.txt') | ||||
|     archive_dir.ensure(dir=True) | ||||
|     test_tmp_path.ensure(dir=True) | ||||
|     test_readme.write('hello world!\n') | ||||
|     Note that it can be important for other tests that the previous settings be | ||||
|     restored when the test case is over. | ||||
|     """ | ||||
|     current = spack.config.get('config:build_stage') | ||||
|     spack.config.set('config', | ||||
|                      {'build_stage': spack.paths.stage_path}, scope='user') | ||||
|     yield | ||||
|     spack.config.set('config', {'build_stage': current}, scope='user') | ||||
|  | ||||
|     with tmpdir.as_cwd(): | ||||
|         tar = spack.util.executable.which('tar', required=True) | ||||
|         tar('czf', str(archive_name), 'test-files') | ||||
|  | ||||
|     # Make spack use the test environment for tmp stuff. | ||||
| @pytest.fixture | ||||
| def tmp_path_for_stage(tmpdir): | ||||
|     """ | ||||
|     Use a built-in, temporary, test directory for staging. | ||||
|  | ||||
|     Note that it can be important for other tests that the previous settings be | ||||
|     restored when the test case is over. | ||||
|     """ | ||||
|     current = spack.config.get('config:build_stage') | ||||
|     spack.config.set('config', {'build_stage': [str(tmpdir)]}, scope='user') | ||||
|     yield | ||||
|     spack.config.set('config', {'build_stage': current}, scope='user') | ||||
|  | ||||
|  | ||||
| @pytest.fixture | ||||
| def tmp_root_stage(monkeypatch): | ||||
|     """ | ||||
|     Enable use of a temporary root for staging. | ||||
|  | ||||
|     Note that it can be important for other tests that the previous settings be | ||||
|     restored when the test case is over. | ||||
|     """ | ||||
|     monkeypatch.setattr(spack.stage, '_tmp_root', None) | ||||
|     monkeypatch.setattr(spack.stage, '_use_tmp_stage', True) | ||||
|     yield | ||||
|  | ||||
|  | ||||
| @pytest.fixture | ||||
| def tmpdir_for_stage(mock_stage_archive): | ||||
|     """ | ||||
|     Use the mock_stage_archive's temporary directory for staging. | ||||
|  | ||||
|     Note that it can be important for other tests that the previous settings be | ||||
|     restored when the test case is over. | ||||
|     """ | ||||
|     archive = mock_stage_archive() | ||||
|     current = spack.config.get('config:build_stage') | ||||
|     spack.config.set( | ||||
|         'config', | ||||
|         {'build_stage': [str(archive.test_tmp_dir)]}, | ||||
|         scope='user') | ||||
|     yield | ||||
|     spack.config.set('config', {'build_stage': current}, scope='user') | ||||
|  | ||||
|  | ||||
| @pytest.fixture | ||||
| def tmp_build_stage_dir(tmpdir): | ||||
|     """Establish the temporary build_stage for the mock archive.""" | ||||
|     test_tmp_path = tmpdir.join('tmp') | ||||
|  | ||||
|     # Set test_tmp_path as the default test directory to use for stages. | ||||
|     current = spack.config.get('config:build_stage') | ||||
|     spack.config.set('config', | ||||
|                      {'build_stage': [str(test_tmp_path)]}, scope='user') | ||||
|  | ||||
|     yield (tmpdir, test_tmp_path) | ||||
|  | ||||
|     spack.config.set('config', {'build_stage': current}, scope='user') | ||||
|  | ||||
|  | ||||
| @pytest.fixture | ||||
| def mock_stage_archive(tmp_build_stage_dir, tmp_root_stage, request): | ||||
|     """ | ||||
|     Create the directories and files for the staged mock archive. | ||||
|  | ||||
|     Note that it can be important for other tests that the previous settings be | ||||
|     restored when the test case is over. | ||||
|     """ | ||||
|     # Mock up a stage area that looks like this: | ||||
|     # | ||||
|     # tmpdir/                test_files_dir | ||||
|     #     tmp/               test_tmp_path (where stage should be) | ||||
|     #     <_archive_base>/   archive_dir_path | ||||
|     #         <_readme_fn>   Optional test_readme (contains _readme_contents) | ||||
|     #     <_extra_fn>        Optional extra file (contains _extra_contents) | ||||
|     #     <_hidden_fn>       Optional hidden file (contains _hidden_contents) | ||||
|     #     <_archive_fn>      archive_url = file:///path/to/<_archive_fn> | ||||
|     # | ||||
|     def create_stage_archive(expected_file_list=[_include_readme]): | ||||
|         tmpdir, test_tmp_path = tmp_build_stage_dir | ||||
|         test_tmp_path.ensure(dir=True) | ||||
|  | ||||
|         # Create the archive directory and associated file | ||||
|         archive_dir = tmpdir.join(_archive_base) | ||||
|         archive = tmpdir.join(_archive_fn) | ||||
|         archive_url = 'file://' + str(archive) | ||||
|         archive_dir.ensure(dir=True) | ||||
|  | ||||
|         # Create the optional files as requested and make sure expanded | ||||
|         # archive peers are included. | ||||
|         tar_args = ['czf', str(_archive_fn), _archive_base] | ||||
|         for _include in expected_file_list: | ||||
|             if _include == _include_hidden: | ||||
|                 # The hidden file case stands in for the way Mac OS X tar files | ||||
|                 # represent HFS metadata.  Locate in the same directory as the | ||||
|                 # archive file. | ||||
|                 tar_args.append(_hidden_fn) | ||||
|                 fn, contents = (tmpdir.join(_hidden_fn), _hidden_contents) | ||||
|  | ||||
|             elif _include == _include_readme: | ||||
|                 # The usual README.txt file is contained in the archive dir. | ||||
|                 fn, contents = (archive_dir.join(_readme_fn), _readme_contents) | ||||
|  | ||||
|             elif _include == _include_extra: | ||||
|                 # The extra file stands in for exploding tar files so needs | ||||
|                 # to be in the same directory as the archive file. | ||||
|                 tar_args.append(_extra_fn) | ||||
|                 fn, contents = (tmpdir.join(_extra_fn), _extra_contents) | ||||
|             else: | ||||
|                 break | ||||
|  | ||||
|             fn.write(contents) | ||||
|  | ||||
|         # Create the archive file | ||||
|         with tmpdir.as_cwd(): | ||||
|             tar = spack.util.executable.which('tar', required=True) | ||||
|             tar(*tar_args) | ||||
|  | ||||
|         Archive = collections.namedtuple( | ||||
|             'Archive', ['url', 'tmpdir', 'test_tmp_dir', 'archive_dir'] | ||||
|         ) | ||||
|     yield Archive( | ||||
|         url=archive_url, | ||||
|         tmpdir=tmpdir, | ||||
|         test_tmp_dir=test_tmp_path, | ||||
|         archive_dir=archive_dir | ||||
|     ) | ||||
|         return Archive(url=archive_url, tmpdir=tmpdir, | ||||
|                        test_tmp_dir=test_tmp_path, archive_dir=archive_dir) | ||||
|  | ||||
|     return create_stage_archive | ||||
|  | ||||
|  | ||||
| @pytest.fixture() | ||||
| @pytest.fixture | ||||
| def mock_noexpand_resource(tmpdir): | ||||
|     """Set up a non-expandable resource in the tmpdir prior to staging.""" | ||||
|     test_resource = tmpdir.join('resource-no-expand.sh') | ||||
|     test_resource.write("an example resource") | ||||
|     return str(test_resource) | ||||
|  | ||||
|  | ||||
| @pytest.fixture() | ||||
| @pytest.fixture | ||||
| def mock_expand_resource(tmpdir): | ||||
|     """Sets up an expandable resource in tmpdir prior to staging.""" | ||||
|     resource_dir = tmpdir.join('resource-expand') | ||||
|     archive_name = 'resource.tar.gz' | ||||
|     archive = tmpdir.join(archive_name) | ||||
| @@ -165,10 +365,10 @@ def mock_expand_resource(tmpdir): | ||||
|     test_file = resource_dir.join('resource-file.txt') | ||||
|     resource_dir.ensure(dir=True) | ||||
|     test_file.write('test content\n') | ||||
|     current = tmpdir.chdir() | ||||
|  | ||||
|     with tmpdir.as_cwd(): | ||||
|         tar = spack.util.executable.which('tar', required=True) | ||||
|         tar('czf', str(archive_name), 'resource-expand') | ||||
|     current.chdir() | ||||
|  | ||||
|     MockResource = collections.namedtuple( | ||||
|         'MockResource', ['url', 'files']) | ||||
| @@ -176,11 +376,13 @@ def mock_expand_resource(tmpdir): | ||||
|     return MockResource(archive_url, ['resource-file.txt']) | ||||
|  | ||||
|  | ||||
| @pytest.fixture() | ||||
| @pytest.fixture | ||||
| def composite_stage_with_expanding_resource( | ||||
|         mock_archive, mock_expand_resource): | ||||
|         mock_stage_archive, mock_expand_resource): | ||||
|     """Sets up a composite for expanding resources prior to staging.""" | ||||
|     composite_stage = StageComposite() | ||||
|     root_stage = Stage(mock_archive.url) | ||||
|     archive = mock_stage_archive() | ||||
|     root_stage = Stage(archive.url) | ||||
|     composite_stage.append(root_stage) | ||||
|  | ||||
|     test_resource_fetcher = spack.fetch_strategy.from_kwargs( | ||||
| @@ -195,7 +397,7 @@ def composite_stage_with_expanding_resource( | ||||
|     return composite_stage, root_stage, resource_stage | ||||
|  | ||||
|  | ||||
| @pytest.fixture() | ||||
| @pytest.fixture | ||||
| def failing_search_fn(): | ||||
|     """Returns a search function that fails! Always!""" | ||||
|     def _mock(): | ||||
| @@ -203,7 +405,7 @@ def _mock(): | ||||
|     return _mock | ||||
|  | ||||
|  | ||||
| @pytest.fixture() | ||||
| @pytest.fixture | ||||
| def failing_fetch_strategy(): | ||||
|     """Returns a fetch strategy that fails.""" | ||||
|     class FailingFetchStrategy(spack.fetch_strategy.FetchStrategy): | ||||
| @@ -215,7 +417,7 @@ def fetch(self): | ||||
|     return FailingFetchStrategy() | ||||
|  | ||||
|  | ||||
| @pytest.fixture() | ||||
| @pytest.fixture | ||||
| def search_fn(): | ||||
|     """Returns a search function that always succeeds.""" | ||||
|     class _Mock(object): | ||||
| @@ -234,28 +436,32 @@ class TestStage(object): | ||||
|     stage_name = 'spack-test-stage' | ||||
|  | ||||
|     @pytest.mark.usefixtures('tmpdir_for_stage') | ||||
|     def test_setup_and_destroy_name_with_tmp(self, mock_archive): | ||||
|         with Stage(mock_archive.url, name=self.stage_name) as stage: | ||||
|             check_setup(stage, self.stage_name, mock_archive) | ||||
|     def test_setup_and_destroy_name_with_tmp(self, mock_stage_archive): | ||||
|         archive = mock_stage_archive() | ||||
|         with Stage(archive.url, name=self.stage_name) as stage: | ||||
|             check_setup(stage, self.stage_name, archive) | ||||
|         check_destroy(stage, self.stage_name) | ||||
|  | ||||
|     def test_setup_and_destroy_name_without_tmp(self, mock_archive): | ||||
|         with Stage(mock_archive.url, name=self.stage_name) as stage: | ||||
|             check_setup(stage, self.stage_name, mock_archive) | ||||
|     def test_setup_and_destroy_name_without_tmp(self, mock_stage_archive): | ||||
|         archive = mock_stage_archive() | ||||
|         with Stage(archive.url, name=self.stage_name) as stage: | ||||
|             check_setup(stage, self.stage_name, archive) | ||||
|         check_destroy(stage, self.stage_name) | ||||
|  | ||||
|     @pytest.mark.usefixtures('tmpdir_for_stage') | ||||
|     def test_setup_and_destroy_no_name_with_tmp(self, mock_archive): | ||||
|         with Stage(mock_archive.url) as stage: | ||||
|             check_setup(stage, None, mock_archive) | ||||
|     def test_setup_and_destroy_no_name_with_tmp(self, mock_stage_archive): | ||||
|         archive = mock_stage_archive() | ||||
|         with Stage(archive.url) as stage: | ||||
|             check_setup(stage, None, archive) | ||||
|         check_destroy(stage, None) | ||||
|  | ||||
|     @pytest.mark.disable_clean_stage_check | ||||
|     @pytest.mark.usefixtures('tmpdir_for_stage') | ||||
|     def test_composite_stage_with_noexpand_resource( | ||||
|             self, mock_archive, mock_noexpand_resource): | ||||
|             self, mock_stage_archive, mock_noexpand_resource): | ||||
|         archive = mock_stage_archive() | ||||
|         composite_stage = StageComposite() | ||||
|         root_stage = Stage(mock_archive.url) | ||||
|         root_stage = Stage(archive.url) | ||||
|         composite_stage.append(root_stage) | ||||
|  | ||||
|         resource_dst_name = 'resource-dst-name.sh' | ||||
| @@ -276,7 +482,7 @@ def test_composite_stage_with_noexpand_resource( | ||||
|     @pytest.mark.disable_clean_stage_check | ||||
|     @pytest.mark.usefixtures('tmpdir_for_stage') | ||||
|     def test_composite_stage_with_expand_resource( | ||||
|             self, mock_archive, mock_expand_resource, | ||||
|             self, mock_stage_archive, mock_expand_resource, | ||||
|             composite_stage_with_expanding_resource): | ||||
|  | ||||
|         composite_stage, root_stage, resource_stage = ( | ||||
| @@ -291,22 +497,26 @@ def test_composite_stage_with_expand_resource( | ||||
|                 root_stage.source_path, 'resource-dir', fname) | ||||
|             assert os.path.exists(file_path) | ||||
|  | ||||
|     def test_setup_and_destroy_no_name_without_tmp(self, mock_archive): | ||||
|         with Stage(mock_archive.url) as stage: | ||||
|             check_setup(stage, None, mock_archive) | ||||
|     def test_setup_and_destroy_no_name_without_tmp(self, mock_stage_archive): | ||||
|         archive = mock_stage_archive() | ||||
|         with Stage(archive.url) as stage: | ||||
|             check_setup(stage, None, archive) | ||||
|         check_destroy(stage, None) | ||||
|  | ||||
|     def test_fetch(self, mock_archive): | ||||
|         with Stage(mock_archive.url, name=self.stage_name) as stage: | ||||
|     @pytest.mark.parametrize('debug', [False, True]) | ||||
|     def test_fetch(self, mock_stage_archive, debug): | ||||
|         archive = mock_stage_archive() | ||||
|         with spack.config.override('config:debug', debug): | ||||
|             with Stage(archive.url, name=self.stage_name) as stage: | ||||
|                 stage.fetch() | ||||
|             check_setup(stage, self.stage_name, mock_archive) | ||||
|                 check_setup(stage, self.stage_name, archive) | ||||
|                 check_fetch(stage, self.stage_name) | ||||
|             check_destroy(stage, self.stage_name) | ||||
|  | ||||
|     def test_no_search_if_default_succeeds( | ||||
|             self, mock_archive, failing_search_fn): | ||||
|         stage = Stage(mock_archive.url, | ||||
|                       name=self.stage_name, | ||||
|             self, mock_stage_archive, failing_search_fn): | ||||
|         archive = mock_stage_archive() | ||||
|         stage = Stage(archive.url, name=self.stage_name, | ||||
|                       search_fn=failing_search_fn) | ||||
|         with stage: | ||||
|             stage.fetch() | ||||
| @@ -336,22 +546,49 @@ def test_search_if_default_fails(self, failing_fetch_strategy, search_fn): | ||||
|         check_destroy(stage, self.stage_name) | ||||
|         assert search_fn.performed_search | ||||
|  | ||||
|     def test_expand_archive(self, mock_archive): | ||||
|         with Stage(mock_archive.url, name=self.stage_name) as stage: | ||||
|     def test_ensure_one_stage_entry(self, mock_stage_archive): | ||||
|         archive = mock_stage_archive() | ||||
|         with Stage(archive.url, name=self.stage_name) as stage: | ||||
|             stage.fetch() | ||||
|             check_setup(stage, self.stage_name, mock_archive) | ||||
|             check_fetch(stage, self.stage_name) | ||||
|             stage.expand_archive() | ||||
|             check_expand_archive(stage, self.stage_name, mock_archive) | ||||
|             stage_path = get_stage_path(stage, self.stage_name) | ||||
|             spack.fetch_strategy._ensure_one_stage_entry(stage_path) | ||||
|         check_destroy(stage, self.stage_name) | ||||
|  | ||||
|     def test_restage(self, mock_archive): | ||||
|         with Stage(mock_archive.url, name=self.stage_name) as stage: | ||||
|     @pytest.mark.parametrize("expected_file_list", [ | ||||
|                              [], | ||||
|                              [_include_readme], | ||||
|                              [_include_extra, _include_readme], | ||||
|                              [_include_hidden, _include_readme]]) | ||||
|     def test_expand_archive(self, expected_file_list, mock_stage_archive): | ||||
|         archive = mock_stage_archive(expected_file_list) | ||||
|         with Stage(archive.url, name=self.stage_name) as stage: | ||||
|             stage.fetch() | ||||
|             check_setup(stage, self.stage_name, archive) | ||||
|             check_fetch(stage, self.stage_name) | ||||
|             stage.expand_archive() | ||||
|             check_expand_archive(stage, self.stage_name, expected_file_list) | ||||
|         check_destroy(stage, self.stage_name) | ||||
|  | ||||
|     def test_expand_archive_extra_expand(self, mock_stage_archive): | ||||
|         """Test expand with an extra expand after expand (i.e., no-op).""" | ||||
|         archive = mock_stage_archive() | ||||
|         with Stage(archive.url, name=self.stage_name) as stage: | ||||
|             stage.fetch() | ||||
|             check_setup(stage, self.stage_name, archive) | ||||
|             check_fetch(stage, self.stage_name) | ||||
|             stage.expand_archive() | ||||
|             stage.fetcher.expand() | ||||
|             check_expand_archive(stage, self.stage_name, [_include_readme]) | ||||
|         check_destroy(stage, self.stage_name) | ||||
|  | ||||
|     def test_restage(self, mock_stage_archive): | ||||
|         archive = mock_stage_archive() | ||||
|         with Stage(archive.url, name=self.stage_name) as stage: | ||||
|             stage.fetch() | ||||
|             stage.expand_archive() | ||||
|  | ||||
|             with working_dir(stage.source_path): | ||||
|                 check_expand_archive(stage, self.stage_name, mock_archive) | ||||
|                 check_expand_archive(stage, self.stage_name, [_include_readme]) | ||||
|  | ||||
|                 # Try to make a file in the old archive dir | ||||
|                 with open('foobar', 'w') as file: | ||||
| @@ -365,26 +602,29 @@ def test_restage(self, mock_archive): | ||||
|             assert 'foobar' not in os.listdir(stage.source_path) | ||||
|         check_destroy(stage, self.stage_name) | ||||
|  | ||||
|     def test_no_keep_without_exceptions(self, mock_archive): | ||||
|         stage = Stage(mock_archive.url, name=self.stage_name, keep=False) | ||||
|     def test_no_keep_without_exceptions(self, mock_stage_archive): | ||||
|         archive = mock_stage_archive() | ||||
|         stage = Stage(archive.url, name=self.stage_name, keep=False) | ||||
|         with stage: | ||||
|             pass | ||||
|         check_destroy(stage, self.stage_name) | ||||
|  | ||||
|     @pytest.mark.disable_clean_stage_check | ||||
|     def test_keep_without_exceptions(self, mock_archive): | ||||
|         stage = Stage(mock_archive.url, name=self.stage_name, keep=True) | ||||
|     def test_keep_without_exceptions(self, mock_stage_archive): | ||||
|         archive = mock_stage_archive() | ||||
|         stage = Stage(archive.url, name=self.stage_name, keep=True) | ||||
|         with stage: | ||||
|             pass | ||||
|         path = get_stage_path(stage, self.stage_name) | ||||
|         assert os.path.isdir(path) | ||||
|  | ||||
|     @pytest.mark.disable_clean_stage_check | ||||
|     def test_no_keep_with_exceptions(self, mock_archive): | ||||
|     def test_no_keep_with_exceptions(self, mock_stage_archive): | ||||
|         class ThisMustFailHere(Exception): | ||||
|             pass | ||||
|  | ||||
|         stage = Stage(mock_archive.url, name=self.stage_name, keep=False) | ||||
|         archive = mock_stage_archive() | ||||
|         stage = Stage(archive.url, name=self.stage_name, keep=False) | ||||
|         try: | ||||
|             with stage: | ||||
|                 raise ThisMustFailHere() | ||||
| @@ -394,11 +634,12 @@ class ThisMustFailHere(Exception): | ||||
|             assert os.path.isdir(path) | ||||
|  | ||||
|     @pytest.mark.disable_clean_stage_check | ||||
|     def test_keep_exceptions(self, mock_archive): | ||||
|     def test_keep_exceptions(self, mock_stage_archive): | ||||
|         class ThisMustFailHere(Exception): | ||||
|             pass | ||||
|  | ||||
|         stage = Stage(mock_archive.url, name=self.stage_name, keep=True) | ||||
|         archive = mock_stage_archive() | ||||
|         stage = Stage(archive.url, name=self.stage_name, keep=True) | ||||
|         try: | ||||
|             with stage: | ||||
|                 raise ThisMustFailHere() | ||||
| @@ -406,3 +647,55 @@ class ThisMustFailHere(Exception): | ||||
|         except ThisMustFailHere: | ||||
|             path = get_stage_path(stage, self.stage_name) | ||||
|             assert os.path.isdir(path) | ||||
|  | ||||
|     def test_source_path_available(self, mock_stage_archive): | ||||
|         """Ensure source path available but does not exist on instantiation.""" | ||||
|         archive = mock_stage_archive() | ||||
|         stage = Stage(archive.url, name=self.stage_name) | ||||
|  | ||||
|         source_path = stage.source_path | ||||
|         assert source_path | ||||
|         assert source_path.endswith(spack.stage._source_path_subdir) | ||||
|         assert not os.path.exists(source_path) | ||||
|  | ||||
|     def test_first_accessible_path_error(self): | ||||
|         """Test _first_accessible_path handling of an OSError.""" | ||||
|         with tempfile.NamedTemporaryFile() as _file: | ||||
|             assert spack.stage._first_accessible_path([_file.name]) is None | ||||
|  | ||||
|     def test_get_tmp_root_no_use(self, no_tmp_root_stage): | ||||
|         """Ensure not using tmp root results in no path.""" | ||||
|         assert spack.stage.get_tmp_root() is None | ||||
|  | ||||
|     def test_get_tmp_root_non_user_path(self, tmp_root_stage, | ||||
|                                         non_user_path_for_stage): | ||||
|         """Ensure build_stage of tmp root with non-user path includes user.""" | ||||
|         path = spack.stage.get_tmp_root() | ||||
|         assert path.endswith(os.path.join(getpass.getuser(), 'spack-stage')) | ||||
|  | ||||
|     def test_get_tmp_root_use(self, tmp_root_stage, tmp_path_for_stage): | ||||
|         """Ensure build_stage of tmp root provides has right ancestors.""" | ||||
|         path = spack.stage.get_tmp_root() | ||||
|         shutil.rmtree(path) | ||||
|         assert path.rfind('test_get_tmp_root_use') > 0 | ||||
|         assert path.endswith('spack-stage') | ||||
|  | ||||
|     def test_get_tmp_root_stage_path(self, tmp_root_stage, | ||||
|                                      stage_path_for_stage): | ||||
|         """ | ||||
|         Ensure build_stage of tmp root with stage_path means use local path. | ||||
|         """ | ||||
|         assert spack.stage.get_tmp_root() is None | ||||
|         assert not spack.stage._use_tmp_stage | ||||
|  | ||||
|     def test_stage_constructor_no_fetcher(self): | ||||
|         """Ensure Stage constructor with no URL or fetch strategy fails.""" | ||||
|         with pytest.raises(ValueError): | ||||
|             with Stage(None): | ||||
|                 pass | ||||
|  | ||||
|     def test_stage_constructor_with_path(self, tmpdir): | ||||
|         """Ensure Stage constructor with a path uses it.""" | ||||
|         testpath = str(tmpdir) | ||||
|         with Stage('file:///does-not-exist', path=testpath) as stage: | ||||
|             assert stage.path == testpath | ||||
|   | ||||
		Reference in New Issue
	
	Block a user