Skip to content

Commit

Permalink
Merge pull request #2935 from asottile/capsysbinary
Browse files Browse the repository at this point in the history
Add capsysbinary fixture
  • Loading branch information
RonnyPfannschmidt committed Nov 17, 2017
2 parents 6161bcf + 219b758 commit ca1f4bc
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 10 deletions.
33 changes: 28 additions & 5 deletions _pytest/capture.py
Expand Up @@ -180,7 +180,7 @@ def suspend_capture_item(self, item, when, in_=False):
item.add_report_section(when, "stderr", err)


capture_fixtures = {'capfd', 'capfdbinary', 'capsys'}
capture_fixtures = {'capfd', 'capfdbinary', 'capsys', 'capsysbinary'}


def _ensure_only_one_capture_fixture(request, name):
Expand All @@ -207,6 +207,22 @@ def capsys(request):
yield fixture


@pytest.fixture
def capsysbinary(request):
"""Enable capturing of writes to sys.stdout/sys.stderr and make
captured output available via ``capsys.readouterr()`` method calls
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``bytes``
objects.
"""
_ensure_only_one_capture_fixture(request, 'capsysbinary')
# Currently, the implementation uses the python3 specific `.buffer`
# property of CaptureIO.
if sys.version_info < (3,):
raise request.raiseerror('capsysbinary is only supported on python 3')
with _install_capture_fixture_on_item(request, SysCaptureBinary) as fixture:
yield fixture


@pytest.fixture
def capfd(request):
"""Enable capturing of writes to file descriptors 1 and 2 and make
Expand Down Expand Up @@ -506,10 +522,9 @@ def start(self):
setattr(sys, self.name, self.tmpfile)

def snap(self):
f = self.tmpfile
res = f.getvalue()
f.truncate(0)
f.seek(0)
res = self.tmpfile.getvalue()
self.tmpfile.seek(0)
self.tmpfile.truncate()
return res

def done(self):
Expand All @@ -528,6 +543,14 @@ def writeorg(self, data):
self._old.flush()


class SysCaptureBinary(SysCapture):
def snap(self):
res = self.tmpfile.buffer.getvalue()
self.tmpfile.seek(0)
self.tmpfile.truncate()
return res


class DontReadFromInput:
"""Temporary stub class. Ideally when stdin is accessed, the
capturing should be turned off, with possibly all data captured
Expand Down
2 changes: 2 additions & 0 deletions changelog/2934.feature
@@ -0,0 +1,2 @@
Add ``capsysbinary`` a version of ``capsys`` which returns bytes from
``readouterr()``.
17 changes: 13 additions & 4 deletions doc/en/capture.rst
Expand Up @@ -85,9 +85,9 @@ of the failing function and hide the other one::
Accessing captured output from a test function
---------------------------------------------------

The ``capsys``, ``capfd``, and ``capfdbinary`` fixtures allow access to
stdout/stderr output created during test execution. Here is an example test
function that performs some output related checks:
The ``capsys``, ``capsysbinary``, ``capfd``, and ``capfdbinary`` fixtures
allow access to stdout/stderr output created during test execution. Here is
an example test function that performs some output related checks:

.. code-block:: python
Expand Down Expand Up @@ -115,11 +115,20 @@ same interface but allows to also capture output from
libraries or subprocesses that directly write to operating
system level output streams (FD1 and FD2).

.. versionadded:: 3.3

If the code under test writes non-textual data, you can capture this using
the ``capsysbinary`` fixture which instead returns ``bytes`` from
the ``readouterr`` method. The ``capfsysbinary`` fixture is currently only
available in python 3.


.. versionadded:: 3.3

If the code under test writes non-textual data, you can capture this using
the ``capfdbinary`` fixture which instead returns ``bytes`` from
the ``readouterr`` method.
the ``readouterr`` method. The ``capfdbinary`` fixture operates on the
filedescriptor level.


.. versionadded:: 3.0
Expand Down
34 changes: 33 additions & 1 deletion testing/test_capture.py
Expand Up @@ -470,6 +470,38 @@ def test_hello(capfdbinary):
""")
reprec.assertoutcome(passed=1)

@pytest.mark.skipif(
sys.version_info < (3,),
reason='only have capsysbinary in python 3',
)
def test_capsysbinary(self, testdir):
reprec = testdir.inline_runsource("""
def test_hello(capsysbinary):
import sys
# some likely un-decodable bytes
sys.stdout.buffer.write(b'\\xfe\\x98\\x20')
out, err = capsysbinary.readouterr()
assert out == b'\\xfe\\x98\\x20'
assert err == b''
""")
reprec.assertoutcome(passed=1)

@pytest.mark.skipif(
sys.version_info >= (3,),
reason='only have capsysbinary in python 3',
)
def test_capsysbinary_forbidden_in_python2(self, testdir):
testdir.makepyfile("""
def test_hello(capsysbinary):
pass
""")
result = testdir.runpytest()
result.stdout.fnmatch_lines([
"*test_hello*",
"*capsysbinary is only supported on python 3*",
"*1 error in*",
])

def test_partial_setup_failure(self, testdir):
p = testdir.makepyfile("""
def test_hello(capsys, missingarg):
Expand Down Expand Up @@ -1233,7 +1265,7 @@ def test_capattr():
reprec.assertoutcome(passed=1)


def test_pickling_and_unpickling_enocded_file():
def test_pickling_and_unpickling_encoded_file():
# See https://bitbucket.org/pytest-dev/pytest/pull-request/194
# pickle.loads() raises infinite recursion if
# EncodedFile.__getattr__ is not implemented properly
Expand Down

0 comments on commit ca1f4bc

Please sign in to comment.