Skip to content

Script fixtures

provide.testkit.process.script_fixtures

Bash script testing fixtures and utilities.

Provides fixtures and utilities for testing bash scripts with subprocess execution, output capture, environment isolation, and workspace management.

Classes

ScriptExecutionContext

Context for executing bash scripts in isolation.

Provides a temporary workspace with environment variable control and automatic cleanup.

Attributes:

Name Type Description
workspace Path

Path to temporary workspace directory.

env dict[str, str]

Environment variables for script execution.

timeout int

Timeout in seconds for script execution.

Functions
create_directory
create_directory(name: str) -> Path

Create a directory in the workspace.

Parameters:

Name Type Description Default
name str

Directory name (can be nested path).

required

Returns:

Type Description
Path

Path to the created directory.

Source code in provide/testkit/process/script_fixtures.py
def create_directory(self, name: str) -> Path:
    """Create a directory in the workspace.

    Args:
        name: Directory name (can be nested path).

    Returns:
        Path to the created directory.
    """
    dir_path = self.workspace / name
    dir_path.mkdir(parents=True, exist_ok=True)
    return dir_path
create_file
create_file(name: str, content: str = '') -> Path

Create a file in the workspace.

Parameters:

Name Type Description Default
name str

File name (can include subdirectories).

required
content str

File content.

''

Returns:

Type Description
Path

Path to the created file.

Source code in provide/testkit/process/script_fixtures.py
def create_file(self, name: str, content: str = "") -> Path:
    """Create a file in the workspace.

    Args:
        name: File name (can include subdirectories).
        content: File content.

    Returns:
        Path to the created file.
    """
    file_path = self.workspace / name
    file_path.parent.mkdir(parents=True, exist_ok=True)
    file_path.write_text(content)
    return file_path
file_exists
file_exists(name: str) -> bool

Check if a file or directory exists in workspace.

Parameters:

Name Type Description Default
name str

File or directory name.

required

Returns:

Type Description
bool

True if path exists, False otherwise.

Source code in provide/testkit/process/script_fixtures.py
def file_exists(self, name: str) -> bool:
    """Check if a file or directory exists in workspace.

    Args:
        name: File or directory name.

    Returns:
        True if path exists, False otherwise.
    """
    return (self.workspace / name).exists()
init_git_repo
init_git_repo() -> None

Initialize a git repository in the workspace.

Raises:

Type Description
RuntimeError

If git is not available.

Source code in provide/testkit/process/script_fixtures.py
def init_git_repo(self) -> None:
    """Initialize a git repository in the workspace.

    Raises:
        RuntimeError: If git is not available.
    """
    if not shutil.which("git"):
        raise RuntimeError("git command not found")

    run(["git", "init"], cwd=self.workspace, check=True)
    run(["git", "config", "user.email", "[email protected]"], cwd=self.workspace, check=True)
    run(["git", "config", "user.name", "Test User"], cwd=self.workspace, check=True)
read_file
read_file(name: str) -> str

Read a file from the workspace.

Parameters:

Name Type Description Default
name str

File name.

required

Returns:

Type Description
str

File contents as string.

Raises:

Type Description
FileNotFoundError

If file does not exist.

Source code in provide/testkit/process/script_fixtures.py
def read_file(self, name: str) -> str:
    """Read a file from the workspace.

    Args:
        name: File name.

    Returns:
        File contents as string.

    Raises:
        FileNotFoundError: If file does not exist.
    """
    return (self.workspace / name).read_text()
run_command
run_command(
    command: str | list[str], check: bool = False
) -> ScriptResult

Execute a shell command in the isolated workspace.

Parameters:

Name Type Description Default
command str | list[str]

Shell command to execute (string or list).

required
check bool

If True, raise exception on non-zero exit code.

False

Returns:

Type Description
ScriptResult

ScriptResult with execution details.

Raises:

Type Description
CalledProcessError

If check=True and command fails.

TimeoutExpired

If execution exceeds timeout.

Source code in provide/testkit/process/script_fixtures.py
def run_command(
    self,
    command: str | list[str],
    check: bool = False,
) -> ScriptResult:
    """Execute a shell command in the isolated workspace.

    Args:
        command: Shell command to execute (string or list).
        check: If True, raise exception on non-zero exit code.

    Returns:
        ScriptResult with execution details.

    Raises:
        subprocess.CalledProcessError: If check=True and command fails.
        subprocess.TimeoutExpired: If execution exceeds timeout.
    """
    import time

    start_time = time.time()

    try:
        result = run(
            command,
            cwd=self.workspace,
            env=self.env if self.env else None,
            timeout=self.timeout,
            check=check,
            shell=isinstance(command, str),
        )
        duration = time.time() - start_time

        return ScriptResult(
            returncode=result.returncode,
            stdout=result.stdout,
            stderr=result.stderr,
            command=command,
            cwd=self.workspace,
            duration=duration,
        )
    except subprocess.CalledProcessError as e:
        duration = time.time() - start_time
        return ScriptResult(
            returncode=e.returncode,
            stdout=e.stdout or "",
            stderr=e.stderr or "",
            command=command,
            cwd=self.workspace,
            duration=duration,
        )
run_script
run_script(
    script_path: Path | str,
    args: list[str] | None = None,
    check: bool = False,
) -> ScriptResult

Execute a bash script in the isolated workspace.

Parameters:

Name Type Description Default
script_path Path | str

Path to the bash script to execute.

required
args list[str] | None

Optional arguments to pass to the script.

None
check bool

If True, raise exception on non-zero exit code.

False

Returns:

Type Description
ScriptResult

ScriptResult with execution details.

Raises:

Type Description
CalledProcessError

If check=True and script fails.

TimeoutExpired

If execution exceeds timeout.

Source code in provide/testkit/process/script_fixtures.py
def run_script(
    self,
    script_path: Path | str,
    args: list[str] | None = None,
    check: bool = False,
) -> ScriptResult:
    """Execute a bash script in the isolated workspace.

    Args:
        script_path: Path to the bash script to execute.
        args: Optional arguments to pass to the script.
        check: If True, raise exception on non-zero exit code.

    Returns:
        ScriptResult with execution details.

    Raises:
        subprocess.CalledProcessError: If check=True and script fails.
        subprocess.TimeoutExpired: If execution exceeds timeout.
    """
    script_path = Path(script_path)
    if not script_path.exists():
        raise FileNotFoundError(f"Script not found: {script_path}")

    command = ["bash", str(script_path)]
    if args:
        command.extend(args)

    import time

    start_time = time.time()

    try:
        result = run(
            command,
            cwd=self.workspace,
            env=self.env if self.env else None,
            timeout=self.timeout,
            check=check,
        )
        duration = time.time() - start_time

        return ScriptResult(
            returncode=result.returncode,
            stdout=result.stdout,
            stderr=result.stderr,
            command=command,
            cwd=self.workspace,
            duration=duration,
        )
    except subprocess.CalledProcessError as e:
        duration = time.time() - start_time
        return ScriptResult(
            returncode=e.returncode,
            stdout=e.stdout or "",
            stderr=e.stderr or "",
            command=command,
            cwd=self.workspace,
            duration=duration,
        )

ScriptResult

Result of a bash script execution.

Attributes:

Name Type Description
returncode int

Exit code from the script.

stdout str

Standard output as string.

stderr str

Standard error as string.

command str | list[str]

Command that was executed.

cwd Path

Working directory where script was executed.

duration float

Execution duration in seconds.

Attributes
failed property
failed: bool

Check if script failed (non-zero exit code).

Returns:

Type Description
bool

True if returncode is non-zero, False otherwise.

success property
success: bool

Check if script succeeded (exit code 0).

Returns:

Type Description
bool

True if returncode is 0, False otherwise.

Functions

bash_script_runner

bash_script_runner(
    isolated_workspace: Path,
) -> Callable[
    [Path | str, list[str] | None, dict[str, str] | None],
    ScriptResult,
]

Create a bash script runner fixture.

Returns:

Type Description
Callable[[Path | str, list[str] | None, dict[str, str] | None], ScriptResult]

Function that executes bash scripts and returns results.

Example

def test_my_script(bash_script_runner): result = bash_script_runner("path/to/script.sh", args=["--flag"]) assert result.success assert "expected output" in result.stdout

Source code in provide/testkit/process/script_fixtures.py
@pytest.fixture
def bash_script_runner(
    isolated_workspace: Path,
) -> Callable[[Path | str, list[str] | None, dict[str, str] | None], ScriptResult]:
    """Create a bash script runner fixture.

    Returns:
        Function that executes bash scripts and returns results.

    Example:
        def test_my_script(bash_script_runner):
            result = bash_script_runner("path/to/script.sh", args=["--flag"])
            assert result.success
            assert "expected output" in result.stdout
    """

    def _run_script(
        script_path: Path | str,
        args: list[str] | None = None,
        env: dict[str, str] | None = None,
        timeout: int = 60,
    ) -> ScriptResult:
        """Execute a bash script.

        Args:
            script_path: Path to bash script.
            args: Optional script arguments.
            env: Optional environment variables.
            timeout: Timeout in seconds.

        Returns:
            ScriptResult with execution details.
        """
        context = ScriptExecutionContext(
            workspace=isolated_workspace,
            env=env or {},
            timeout=timeout,
        )
        return context.run_script(script_path, args=args)

    return _run_script

git_workspace

git_workspace() -> Generator[Path, None, None]

Create a temporary workspace with git initialized.

Yields:

Type Description
Path

Path to git-initialized workspace directory.

Source code in provide/testkit/process/script_fixtures.py
@pytest.fixture
def git_workspace() -> Generator[Path, None, None]:
    """Create a temporary workspace with git initialized.

    Yields:
        Path to git-initialized workspace directory.
    """
    with temp_dir(prefix="git_test_") as workspace:
        if not shutil.which("git"):
            pytest.skip("git command not available")

        run(["git", "init"], cwd=workspace, check=True)
        run(["git", "config", "user.email", "[email protected]"], cwd=workspace, check=True)
        run(["git", "config", "user.name", "Test User"], cwd=workspace, check=True)

        yield workspace

isolated_workspace

isolated_workspace() -> Generator[Path, None, None]

Create an isolated temporary workspace directory.

Yields:

Type Description
Path

Path to temporary workspace directory.

Source code in provide/testkit/process/script_fixtures.py
@pytest.fixture
def isolated_workspace() -> Generator[Path, None, None]:
    """Create an isolated temporary workspace directory.

    Yields:
        Path to temporary workspace directory.
    """
    with temp_dir(prefix="workspace_test_") as workspace:
        yield workspace

mock_git_repo

mock_git_repo() -> Callable[[Path, str], Path]

Create a factory for mock git repositories.

Returns:

Type Description
Callable[[Path, str], Path]

Function that creates a mock git repo with commits.

Example

def test_git_clone(mock_git_repo, isolated_workspace): repo = mock_git_repo(isolated_workspace, "test-repo") # repo is a path to a git repository with initial commit

Source code in provide/testkit/process/script_fixtures.py
@pytest.fixture
def mock_git_repo() -> Callable[[Path, str], Path]:
    """Create a factory for mock git repositories.

    Returns:
        Function that creates a mock git repo with commits.

    Example:
        def test_git_clone(mock_git_repo, isolated_workspace):
            repo = mock_git_repo(isolated_workspace, "test-repo")
            # repo is a path to a git repository with initial commit
    """

    def _create_repo(base_dir: Path, repo_name: str, add_commits: bool = True) -> Path:
        """Create a mock git repository.

        Args:
            base_dir: Directory to create repo in.
            repo_name: Name of the repository.
            add_commits: If True, add an initial commit.

        Returns:
            Path to the created git repository.
        """
        if not shutil.which("git"):
            pytest.skip("git command not available")

        repo_path = base_dir / repo_name
        repo_path.mkdir(parents=True, exist_ok=True)

        run(["git", "init"], cwd=repo_path, check=True)
        run(["git", "config", "user.email", "[email protected]"], cwd=repo_path, check=True)
        run(["git", "config", "user.name", "Test User"], cwd=repo_path, check=True)

        if add_commits:
            readme = repo_path / "README.md"
            readme.write_text(f"# {repo_name}\n\nTest repository.")
            run(["git", "add", "README.md"], cwd=repo_path, check=True)
            run(["git", "commit", "-m", "Initial commit"], cwd=repo_path, check=True)

        return repo_path

    return _create_repo

script_execution_context

script_execution_context() -> (
    Generator[ScriptExecutionContext, None, None]
)

Create an isolated script execution context.

Provides a temporary workspace directory with utilities for running bash scripts in isolation.

Yields:

Type Description
ScriptExecutionContext

ScriptExecutionContext with temporary workspace.

Source code in provide/testkit/process/script_fixtures.py
@pytest.fixture
def script_execution_context() -> Generator[ScriptExecutionContext, None, None]:
    """Create an isolated script execution context.

    Provides a temporary workspace directory with utilities
    for running bash scripts in isolation.

    Yields:
        ScriptExecutionContext with temporary workspace.
    """
    with temp_dir(prefix="script_test_") as workspace:
        yield ScriptExecutionContext(workspace=workspace)