""" This module is used to define shared pytest fixtures. Because this module is placed under tests, all fixtures defined here can be used by all test cases below tests """ import os import shutil import pytest def stage_file(dirname, filename, content=None): # we cannot use a os.path.join because we want to support # multi-parts in the filename if dirname.endswith('/') and filename.startswith('/'): pathname = dirname + filename[1:] elif filename.startswith('/') or dirname.endswith('/'): pathname = dirname + filename else: pathname = dirname + '/' + filename print('Staging file: ', pathname) os.makedirs(os.path.dirname(pathname), exist_ok=True) if content is None: content = pathname with open(pathname, 'w') as ff: ff.write(content) @pytest.fixture def stage_fs(): """ This fixture can be used to stage a complete file system below a given root. This is a fixture factory and each test function can call stage_fs with the root of the file system to stage and with a configuration. The entire root_fs will be deleted when the fixture terminates unless skip_clean=True Example of files_config: { 'file1.yaml': 'any content', 'folder1': { 'file_with_arbitrary_content.yaml': None, 'empty_file.txt': '', 'nested_empty_folder: {} } 'folder1/file2.txt': None } if '/tmp/pico' is passed as fs_root, this fixture will stage the following files: /tmp/pico/file1.yaml (containing 'any content') /tmp/pico/folder1/file_with_arbitrary_content.yaml (containing arbitary text) /tmp/pico/folder1/empty_file.txt (empty) /tmp/pico/folder1/nested_empty_folder/ (empty directory) /tmp/pico/folder1/file2.txt (any content) To use this fixture, simply add "stage_fs" as argument to your test function, then call stage_fs() with arguments described in below _stage_fs function. Also see the unit test code (test_fixtures.py) """ saved_fs_root = [] def _stage_fs(fs_root, files_config, skip_clean=False): """ fs_root: pathname of the root under which all all the files defined in files_config must be staged files_config: a dict of file content reflecting the desired staged file system skip_clean: set to True if you do not want the staged directory to be cleaned on exit (for troubleshooting only) """ if not saved_fs_root: if skip_clean: # for troubleshooting, it is possible to preserve # the stage_fs directory after the test finishes saved_fs_root.append(None) else: saved_fs_root.append(fs_root) # remove the stage_fs root directory at start # so we're sure we start with a clean directory shutil.rmtree(fs_root, ignore_errors=True) os.makedirs(fs_root, exist_ok=True) for file, content in files_config.items(): if isinstance(content, dict): # remove any "/" at start if file[0] == '/': file = file[1:] new_fs_root = os.path.join(fs_root, file) os.makedirs(new_fs_root, exist_ok=True) _stage_fs(new_fs_root, content) else: stage_file(fs_root, file, content) yield _stage_fs if saved_fs_root: if saved_fs_root[0]: # remove the stage_fs directory when the fixture terminates shutil.rmtree(saved_fs_root[0], ignore_errors=True)