From 5cf62e69ce080348f06b43c3e79b2d6d30d25e1e Mon Sep 17 00:00:00 2001 From: Tamara Dahlgren <35777542+tldahlgren@users.noreply.github.com> Date: Thu, 13 Jun 2019 11:14:35 -0700 Subject: Always treat DIYStage as expanded (#11663) DIYStage, used to treat a user-managed directory as a staging area, should always be considered expanded (i.e. the source has been decompressed if it was stored in an archive). This also: * Adds checks to ensure that the path used to instantiate a DIYStage refers to an existing directory. * Adds tests to check the behavior of DIYStage (including behavior added here, but it was generally untested before). --- lib/spack/spack/stage.py | 17 ++++++++++++- lib/spack/spack/test/stage.py | 58 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/spack/spack/stage.py b/lib/spack/spack/stage.py index 34d19860fa..474fda3538 100644 --- a/lib/spack/spack/stage.py +++ b/lib/spack/spack/stage.py @@ -597,6 +597,12 @@ class DIYStage(object): """Simple class that allows any directory to be a spack stage.""" def __init__(self, path): + if path is None: + raise ValueError("Cannot construct DIYStage without a path.") + elif not os.path.isdir(path): + raise StagePathError("The stage path directory does not exist:", + path) + self.archive_file = None self.path = path self.source_path = path @@ -618,8 +624,13 @@ class DIYStage(object): def expand_archive(self): tty.msg("Using source directory: %s" % self.source_path) + @property + def expanded(self): + """Returns True since the source_path must exist.""" + return True + def restage(self): - tty.die("Cannot restage DIY stage.") + raise RestageError("Cannot restage a DIY stage.") def create(self): self.created = True @@ -650,6 +661,10 @@ class StageError(spack.error.SpackError): """"Superclass for all errors encountered during staging.""" +class StagePathError(StageError): + """"Error encountered with stage path.""" + + class RestageError(StageError): """"Error encountered during restaging.""" diff --git a/lib/spack/spack/test/stage.py b/lib/spack/spack/test/stage.py index ba0249f6eb..83d8056119 100644 --- a/lib/spack/spack/test/stage.py +++ b/lib/spack/spack/test/stage.py @@ -19,7 +19,7 @@ import spack.stage import spack.util.executable from spack.resource import Resource -from spack.stage import Stage, StageComposite, ResourceStage +from spack.stage import Stage, StageComposite, ResourceStage, DIYStage # The following values are used for common fetch and stage mocking fixtures: _archive_base = 'test-files' @@ -699,3 +699,59 @@ class TestStage(object): testpath = str(tmpdir) with Stage('file:///does-not-exist', path=testpath) as stage: assert stage.path == testpath + + def test_diystage_path_none(self): + """Ensure DIYStage for path=None behaves as expected.""" + with pytest.raises(ValueError): + DIYStage(None) + + def test_diystage_path_invalid(self): + """Ensure DIYStage for an invalid path behaves as expected.""" + with pytest.raises(spack.stage.StagePathError): + DIYStage('/path/does/not/exist') + + def test_diystage_path_valid(self, tmpdir): + """Ensure DIYStage for a valid path behaves as expected.""" + path = str(tmpdir) + stage = DIYStage(path) + assert stage.path == path + assert stage.source_path == path + + # Order doesn't really matter for DIYStage since they are + # basically NOOPs; however, call each since they are part + # of the normal stage usage and to ensure full test coverage. + stage.create() # Only sets the flag value + assert stage.created + + stage.cache_local() # Only outputs a message + stage.fetch() # Only outputs a message + stage.check() # Only outputs a message + stage.expand_archive() # Only outputs a message + + assert stage.expanded # The path/source_path does exist + + with pytest.raises(spack.stage.RestageError): + stage.restage() + + stage.destroy() # A no-op + assert stage.path == path # Ensure can still access attributes + assert os.path.exists(stage.source_path) # Ensure path still exists + + def test_diystage_preserve_file(self, tmpdir): + """Ensure DIYStage preserves an existing file.""" + # Write a file to the temporary directory + fn = tmpdir.join(_readme_fn) + fn.write(_readme_contents) + + # Instantiate the DIYStage and ensure the above file is unchanged. + path = str(tmpdir) + stage = DIYStage(path) + assert os.path.isdir(path) + assert os.path.isfile(str(fn)) + + stage.create() # Only sets the flag value + + readmefn = str(fn) + assert os.path.isfile(readmefn) + with open(readmefn) as _file: + _file.read() == _readme_contents -- cgit v1.2.3-60-g2f50