Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,6 @@ target/
# pyenv
.python-version

# Visual Studio Code
.vs/

23 changes: 12 additions & 11 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -129,17 +129,18 @@ processes with real ``subprocess``, or use
.. code-block:: python

def test_real_process(fp):
command = ["python", "-c", "pass"]
with pytest.raises(fp.exceptions.ProcessNotRegisteredError):
# this will fail, as "ls" command is not registered
subprocess.call("ls")
# this will fail, as the command is not registered
subprocess.call(command)

fp.pass_command("ls")
fp.pass_command(command)
# now it should be fine
assert subprocess.call("ls") == 0
assert subprocess.call(command) == 0

# allow all commands to be called by real subprocess
fp.allow_unregistered(True)
assert subprocess.call(["ls", "-l"]) == 0
assert subprocess.call(command) == 0


Differing results
Expand Down Expand Up @@ -272,12 +273,12 @@ if the subprocess command will be called with a string argument.

def test_non_exact_matching(fp):
# define a command that will take any number of arguments
fp.register(["ls", fp.any()])
assert subprocess.check_call("ls -lah") == 0
fp.register(["python", fp.any()])
assert subprocess.check_call(["python", "-c", "pass"]) == 0

# `fake_subprocess.any()` is OK even with no arguments
fp.register(["ls", fp.any()])
assert subprocess.check_call("ls") == 0
fp.register(["python", fp.any()])
assert subprocess.check_call(["python"]) == 0

# but it can force a minimum amount of arguments
fp.register(["cp", fp.any(min=2)])
Expand Down Expand Up @@ -310,8 +311,8 @@ the same name, regardless of the location. This is accomplished with

def test_any_matching_program(fp):
# define a command that can come from anywhere
fp.register([fp.program("ls")])
assert subprocess.check_call("/bin/ls") == 0
fp.register([fp.program("python")])
assert subprocess.check_call(sys.executable) == 0


Check if process was called
Expand Down
2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[pytest]
junit_family=legacy
asyncio_default_fixture_loop_scope = function
filterwarnings =
error
ignore::pytest.PytestUnraisableExceptionWarning
ignore::pytest.PytestDeprecationWarning
8 changes: 7 additions & 1 deletion pytest_subprocess/fake_popen.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class FakePopen:

stdout: Optional[BUFFER] = None
stderr: Optional[BUFFER] = None
stdin: Optional[BUFFER] = None
returncode: Optional[int] = None
text_mode: bool = False
pid: int = 0
Expand Down Expand Up @@ -168,6 +169,11 @@ def configure(self, **kwargs: Optional[Dict]) -> None:
"""Setup the FakePopen instance based on a real Popen arguments."""
self.__kwargs = self.safe_copy(kwargs)
self.__universal_newlines = kwargs.get("universal_newlines", None)

stdin = kwargs.get("stdin")
if stdin == subprocess.PIPE:
self.stdin = self._get_empty_buffer(False)

text = kwargs.get("text", None)
encoding = kwargs.get("encoding", None)
errors = kwargs.get("errors", None)
Expand Down Expand Up @@ -288,7 +294,7 @@ def _write_to_buffer(self, data: OPTIONAL_TEXT_OR_ITERABLE, buffer: IO) -> None:
)
if isinstance(data, (list, tuple)):
buffer.writelines([data_type(line + "\n") for line in data])
else:
elif data is not None:
buffer.write(data_type(data))

def _convert(self, input: Union[str, bytes]) -> Union[str, bytes]:
Expand Down
4 changes: 3 additions & 1 deletion tests/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ def get_code_blocks(file_path):
with file_path.open() as file_handle:
content = file_handle.read()

code_blocks = publish_doctree(content).findall(condition=is_code_block)
code_blocks = publish_doctree(
content, settings_overrides={"report_level": 5}
).findall(condition=is_code_block)
return [block.astext() for block in code_blocks]


Expand Down
75 changes: 71 additions & 4 deletions tests/test_subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import sys
import time
from pathlib import Path
from tempfile import NamedTemporaryFile

import pytest

Expand Down Expand Up @@ -922,12 +923,14 @@ def test_encoding(fp, fake, argument):
if fake:
fp.register(["whoami"], stdout=username)

output = subprocess.check_output(
["whoami"], **{argument: values.get(argument)}
).strip()
output = (
subprocess.check_output(["whoami"], **{argument: values.get(argument)})
.strip()
.lower()
)

assert isinstance(output, str)
assert output.endswith(username)
assert output.endswith(username.lower())


@pytest.mark.parametrize("command", ["ls -lah", ["ls", "-lah"]])
Expand Down Expand Up @@ -1280,3 +1283,67 @@ def spawn_process() -> subprocess.Popen[str]:
proc.wait()

assert proc.stdout.read() == "Stdout line 1\nStdout line 2\n"


def test_stdin_pipe(fp):
"""
Test that stdin is a writable buffer when using subprocess.PIPE.
"""
fp.register(["my-command"])

process = subprocess.Popen(
["my-command"],
stdin=subprocess.PIPE,
)

assert process.stdin is not None
assert process.stdin.writable()

# We can write to the buffer.
process.stdin.write(b"some data")
process.stdin.flush()

# The data can be read back from the buffer for inspection.
process.stdin.seek(0)
assert process.stdin.read() == b"some data"

# After closing, it should raise a ValueError.
process.stdin.close()
with pytest.raises(ValueError):
process.stdin.write(b"more data")


def test_stdout_stderr_as_file_bug(fp):
"""
Test that no TypeError is raised when stdout/stderr is a file
and the stream is not registered.

From GitHub #144
"""
# register process with stdout but no stderr
fp.register(
["test-no-stderr"],
stdout="test",
)
# register process with stderr but no stdout
fp.register(
["test-no-stdout"],
stderr="test",
)
# register process with no streams
fp.register(
["test-no-streams"],
)

with NamedTemporaryFile("wb") as f:
# test with stderr not registered
p = subprocess.Popen("test-no-stderr", stdout=f.file, stderr=f.file)
p.wait()

# test with stdout not registered
p = subprocess.Popen("test-no-stdout", stdout=f.file, stderr=f.file)
p.wait()

# test with no streams registered
p = subprocess.Popen("test-no-streams", stdout=f.file, stderr=f.file)
p.wait()
Loading