Add scratch module roots to test configuration (#19477)
fixes #19476 Module file content is written to file in a temporary location and read back to be analyzed by unit tests. The approach to patch "open" and write to a StringIO in memory has been abandoned, since over time other operations insisting on the filesystem have been added to the module file generator.
This commit is contained in:
		 Massimiliano Culpo
					Massimiliano Culpo
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						GitHub
					
				
			
						parent
						
							d6f19eeed2
						
					
				
				
					commit
					4ec404dfc0
				
			| @@ -425,7 +425,17 @@ def configuration_dir(tmpdir_factory, linux_os): | ||||
|     # Create temporary 'defaults', 'site' and 'user' folders | ||||
|     tmpdir.ensure('user', dir=True) | ||||
| 
 | ||||
|     # Slightly modify compilers.yaml to look like Linux | ||||
|     # Slightly modify config.yaml and compilers.yaml | ||||
|     config_yaml = test_config.join('config.yaml') | ||||
|     modules_root = tmpdir_factory.mktemp('share') | ||||
|     tcl_root = modules_root.ensure('modules', dir=True) | ||||
|     lmod_root = modules_root.ensure('lmod', dir=True) | ||||
|     content = ''.join(config_yaml.read()).format( | ||||
|         str(tcl_root), str(lmod_root) | ||||
|     ) | ||||
|     t = tmpdir.join('site', 'config.yaml') | ||||
|     t.write(content) | ||||
| 
 | ||||
|     compilers_yaml = test_config.join('compilers.yaml') | ||||
|     content = ''.join(compilers_yaml.read()).format(linux_os) | ||||
|     t = tmpdir.join('site', 'compilers.yaml') | ||||
|   | ||||
| @@ -14,4 +14,5 @@ config: | ||||
|   checksum: true | ||||
|   dirty: false | ||||
|   module_roots: | ||||
|     tcl:    $spack/share/spack/modules | ||||
|     tcl: {0} | ||||
|     lmod: {1} | ||||
|   | ||||
| @@ -2,16 +2,9 @@ | ||||
| # Spack Project Developers. See the top-level COPYRIGHT file for details. | ||||
| # | ||||
| # SPDX-License-Identifier: (Apache-2.0 OR MIT) | ||||
| 
 | ||||
| import os.path | ||||
| import collections | ||||
| import contextlib | ||||
| import inspect | ||||
| 
 | ||||
| import ruamel.yaml as yaml | ||||
| import pytest | ||||
| from six import StringIO | ||||
| 
 | ||||
| import spack.config | ||||
| import spack.paths | ||||
| import spack.spec | ||||
| import spack.modules.common | ||||
| @@ -19,34 +12,7 @@ | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture() | ||||
| def file_registry(): | ||||
|     """Fake filesystem for modulefiles test""" | ||||
|     return collections.defaultdict(StringIO) | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture() | ||||
| def filename_dict(file_registry, monkeypatch): | ||||
|     """Returns a fake open that writes on a StringIO instance instead | ||||
|     of disk. | ||||
|     """ | ||||
|     @contextlib.contextmanager | ||||
|     def _mock(filename, mode): | ||||
|         if not mode == 'w': | ||||
|             raise RuntimeError('opening mode must be "w" [stringio_open]') | ||||
| 
 | ||||
|         try: | ||||
|             yield file_registry[filename] | ||||
|         finally: | ||||
|             handle = file_registry[filename] | ||||
|             file_registry[filename] = handle.getvalue() | ||||
|             handle.close() | ||||
|     # Patch 'open' in the appropriate module | ||||
|     monkeypatch.setattr(spack.modules.common, 'open', _mock, raising=False) | ||||
|     return file_registry | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture() | ||||
| def modulefile_content(filename_dict, request): | ||||
| def modulefile_content(request): | ||||
|     """Returns a function that generates the content of a module file | ||||
|     as a list of lines. | ||||
|     """ | ||||
| @@ -58,59 +24,21 @@ def _impl(spec_str): | ||||
|         spec = spack.spec.Spec(spec_str) | ||||
|         spec.concretize() | ||||
|         generator = writer_cls(spec) | ||||
|         generator.write() | ||||
|         generator.write(overwrite=True) | ||||
| 
 | ||||
|         # Get its filename | ||||
|         filename = generator.layout.filename | ||||
| 
 | ||||
|         # Retrieve the content | ||||
|         content = filename_dict[filename].split('\n') | ||||
|         with open(filename) as f: | ||||
|             content = f.readlines() | ||||
|             content = ''.join(content).split('\n') | ||||
|         generator.remove() | ||||
|         return content | ||||
| 
 | ||||
|     return _impl | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture() | ||||
| def patch_configuration(monkeypatch, request): | ||||
|     """Reads a configuration file from the mock ones prepared for tests | ||||
|     and monkeypatches the right classes to hook it in. | ||||
|     """ | ||||
|     # Class of the module file writer | ||||
|     writer_cls = getattr(request.module, 'writer_cls') | ||||
|     # Module where the module file writer is defined | ||||
|     writer_mod = inspect.getmodule(writer_cls) | ||||
|     # Key for specific settings relative to this module type | ||||
|     writer_key = str(writer_mod.__name__).split('.')[-1] | ||||
|     # Root folder for configuration | ||||
|     root_for_conf = os.path.join( | ||||
|         spack.paths.test_path, 'data', 'modules', writer_key | ||||
|     ) | ||||
| 
 | ||||
|     def _impl(filename): | ||||
| 
 | ||||
|         file = os.path.join(root_for_conf, filename + '.yaml') | ||||
|         with open(file) as f: | ||||
|             configuration = yaml.load(f) | ||||
| 
 | ||||
|         monkeypatch.setattr( | ||||
|             spack.modules.common, | ||||
|             'configuration', | ||||
|             configuration | ||||
|         ) | ||||
|         monkeypatch.setattr( | ||||
|             writer_mod, | ||||
|             'configuration', | ||||
|             configuration[writer_key] | ||||
|         ) | ||||
|         monkeypatch.setattr( | ||||
|             writer_mod, | ||||
|             'configuration_registry', | ||||
|             {} | ||||
|         ) | ||||
|     return _impl | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture() | ||||
| def update_template_dirs(config, monkeypatch): | ||||
|     """Mocks the template directories for tests""" | ||||
|   | ||||
| @@ -133,8 +133,10 @@ def test_blacklist(self, modulefile_content, module_configuration): | ||||
|         assert len([x for x in content if 'is-loaded' in x]) == 1 | ||||
|         assert len([x for x in content if 'module load ' in x]) == 1 | ||||
| 
 | ||||
|         # Returns a StringIO instead of a string as no module file was written | ||||
|         with pytest.raises(AttributeError): | ||||
|         # Catch "Exception" to avoid using FileNotFoundError on Python 3 | ||||
|         # and IOError on Python 2 or common bases like EnvironmentError | ||||
|         # which are not officially documented | ||||
|         with pytest.raises(Exception): | ||||
|             modulefile_content('callpath arch=x86-linux') | ||||
| 
 | ||||
|         content = modulefile_content('zmpi arch=x86-linux') | ||||
|   | ||||
		Reference in New Issue
	
	Block a user