Add check_output function and tests.

This commit is contained in:
Brad Warren 2017-08-10 14:17:13 -07:00
parent dfd1cceb9b
commit 5beaae3b65
2 changed files with 99 additions and 0 deletions

View file

@ -0,0 +1,49 @@
"""Utility functions for use in the Postfix installer."""
import logging
import subprocess
logger = logging.getLogger(__name__)
def check_output(*args, **kwargs):
"""Backported version of subprocess.check_output for Python 2.6+.
This is the same as subprocess.check_output from newer versions of
Python, except:
1. The return value is a string rather than a byte string. To
accomplish this, the caller cannot set the parameter
universal_newlines.
2. If the command exits with a nonzero status, output is not
included in the raised subprocess.CalledProcessError because
subprocess.CalledProcessError on Python 2.6 does not support this.
Instead, the failure including the output is logged.
:param tuple args: positional arguments for Popen
:param dict kwargs: keyword arguments for Popen
:returns: data printed to stdout
:rtype: str
"""
for keyword in ('stdout', 'universal_newlines',):
if keyword in kwargs:
raise ValueError(
keyword + ' argument not allowed, it will be overridden.')
kwargs['stdout'] = subprocess.PIPE
kwargs['universal_newlines'] = True
process = subprocess.Popen(*args, **kwargs)
output, unused_err = process.communicate()
retcode = process.poll()
if retcode:
cmd = kwargs.get('args')
if cmd is None:
cmd = args[0]
logger.debug(
"'%s' exited with %d. Output was:\n%s", cmd, retcode, output)
raise subprocess.CalledProcessError(retcode, cmd)
return output

View file

@ -0,0 +1,50 @@
"""Tests for certbot_postfix.util."""
import subprocess
import unittest
import mock
class CheckOutputTest(unittest.TestCase):
"""Tests for certbot_postfix.util.check_output."""
@classmethod
def _call(cls, *args, **kwargs):
from certbot_postfix.util import check_output
return check_output(*args, **kwargs)
@mock.patch('certbot_postfix.util.logger')
@mock.patch('certbot_postfix.util.subprocess.Popen')
def test_command_error(self, mock_popen, mock_logger):
command = 'foo'
retcode = 42
output = 'bar'
mock_popen().communicate.return_value = (output, '')
mock_popen().poll.return_value = 42
self.assertRaises(subprocess.CalledProcessError, self._call, command)
log_args = mock_logger.debug.call_args[0]
self.assertTrue(command in log_args)
self.assertTrue(retcode in log_args)
self.assertTrue(output in log_args)
@mock.patch('certbot_postfix.util.subprocess.Popen')
def test_success(self, mock_popen):
command = 'foo'
output = 'bar'
mock_popen().communicate.return_value = (output, '')
mock_popen().poll.return_value = 0
self.assertEqual(self._call(command), output)
def test_stdout_error(self):
self.assertRaises(ValueError, self._call, stdout=None)
def test_universal_newlines_error(self):
self.assertRaises(ValueError, self._call, universal_newlines=False)
if __name__ == '__main__': # pragma: no cover
unittest.main()