Stop using print in certbot.main module. (#5121)

* Stop using print in `certbot.main` module.

* Update certbot.main.plugins_cmd` function.

* Update test methods `test_plugins_no_args`,
`test_plugins_no_args_unprivileged`, `test_plugins_init` and
`test_plugins_prepare` in `cerbot.tests.MainTest` class.

Addresses #3720.

* certbot: Add `patch_get_utility_with_stdout` function.

* Add functions `certbot.tests.util.patch_get_utility_with_stdout`
  and `certbot.tests.util._create_get_utility_mock_with_stdout`.

* certbot: tests: Update tests in MainTest.

* Update methods `test_plugins_no_args`,
`test_plugins_no_args_unprivileged`, and `test_plugins_init`,
`test_plugins_prepare` to use `patch_get_utility_with_stdout`.

* certbot: tests: Update _create_get_utility_mock_with_stdout.

* Update certbot.tests.util._create_get_utility_mock_with_stdout
  function. The mock function for all IDisplay methods, except
  `notification` method, calls _write_msg and _assert_valid_call.

* certbot: tests: Update `patch_get_utility_with_stdout`

* Update doc string.
* Argument stdout's default value is None now.

* certbot: tests: Update util._create_get_utility_mock_with_stdout.
This commit is contained in:
r5d
2017-09-25 18:42:31 -07:00
committed by Brad Warren
parent 8b7d6c4ea3
commit 85deca588f
3 changed files with 83 additions and 13 deletions
+7 -4
View File
@@ -1,5 +1,6 @@
"""Certbot main entry point."""
from __future__ import print_function
import functools
import logging.handlers
import os
import sys
@@ -492,7 +493,7 @@ def install(config, plugins):
_install_cert(config, le_client, domains)
def plugins_cmd(config, plugins): # TODO: Use IDisplay rather than print
def plugins_cmd(config, plugins):
"""List server software plugins."""
logger.debug("Expected interfaces: %s", config.ifaces)
@@ -500,8 +501,10 @@ def plugins_cmd(config, plugins): # TODO: Use IDisplay rather than print
filtered = plugins.visible().ifaces(ifaces)
logger.debug("Filtered plugins: %r", filtered)
notify = functools.partial(zope.component.getUtility(
interfaces.IDisplay).notification, pause=False)
if not config.init and not config.prepare:
print(str(filtered))
notify(str(filtered))
return
filtered.init(config)
@@ -509,13 +512,13 @@ def plugins_cmd(config, plugins): # TODO: Use IDisplay rather than print
logger.debug("Verified plugins: %r", verified)
if not config.prepare:
print(str(verified))
notify(str(verified))
return
verified.prepare()
available = verified.available()
logger.debug("Prepared plugins: %s", available)
print(str(available))
notify(str(available))
def rollback(config, plugins):
+21 -9
View File
@@ -539,7 +539,10 @@ class MainTest(test_util.ConfigTestCase): # pylint: disable=too-many-public-met
ifaces = []
plugins = mock_disco.PluginsRegistry.find_all()
_, stdout, _, _ = self._call(['plugins'])
stdout = six.StringIO()
with test_util.patch_get_utility_with_stdout(stdout=stdout):
_, stdout, _, _ = self._call(['plugins'], stdout)
plugins.visible.assert_called_once_with()
plugins.visible().ifaces.assert_called_once_with(ifaces)
filtered = plugins.visible().ifaces()
@@ -556,14 +559,16 @@ class MainTest(test_util.ConfigTestCase): # pylint: disable=too-many-public-met
_, _, _, _ = directory, mode, uid, strict
raise errors.Error()
stdout = six.StringIO()
with mock.patch('certbot.util.set_up_core_dir') as mock_set_up_core_dir:
mock_set_up_core_dir.side_effect = throw_error
with test_util.patch_get_utility_with_stdout(stdout=stdout):
mock_set_up_core_dir.side_effect = throw_error
_, stdout, _, _ = self._call(['plugins'], stdout)
_, stdout, _, _ = self._call(['plugins'])
plugins.visible.assert_called_once_with()
plugins.visible().ifaces.assert_called_once_with(ifaces)
filtered = plugins.visible().ifaces()
self.assertEqual(stdout.getvalue().strip(), str(filtered))
plugins.visible.assert_called_once_with()
plugins.visible().ifaces.assert_called_once_with(ifaces)
filtered = plugins.visible().ifaces()
self.assertEqual(stdout.getvalue().strip(), str(filtered))
@mock.patch('certbot.main.plugins_disco')
@mock.patch('certbot.main.cli.HelpfulArgumentParser.determine_help_topics')
@@ -571,7 +576,10 @@ class MainTest(test_util.ConfigTestCase): # pylint: disable=too-many-public-met
ifaces = []
plugins = mock_disco.PluginsRegistry.find_all()
_, stdout, _, _ = self._call(['plugins', '--init'])
stdout = six.StringIO()
with test_util.patch_get_utility_with_stdout(stdout=stdout):
_, stdout, _, _ = self._call(['plugins', '--init'], stdout)
plugins.visible.assert_called_once_with()
plugins.visible().ifaces.assert_called_once_with(ifaces)
filtered = plugins.visible().ifaces()
@@ -585,7 +593,11 @@ class MainTest(test_util.ConfigTestCase): # pylint: disable=too-many-public-met
def test_plugins_prepare(self, _det, mock_disco):
ifaces = []
plugins = mock_disco.PluginsRegistry.find_all()
_, stdout, _, _ = self._call(['plugins', '--init', '--prepare'])
stdout = six.StringIO()
with test_util.patch_get_utility_with_stdout(stdout=stdout):
_, stdout, _, _ = self._call(['plugins', '--init', '--prepare'], stdout)
plugins.visible.assert_called_once_with()
plugins.visible().ifaces.assert_called_once_with(ifaces)
filtered = plugins.visible().ifaces()
+55
View File
@@ -14,6 +14,7 @@ from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
import mock
import OpenSSL
import six
from six.moves import reload_module # pylint: disable=import-error
from acme import jose
@@ -169,6 +170,30 @@ def patch_get_utility(target='zope.component.getUtility'):
return mock.patch(target, new_callable=_create_get_utility_mock)
def patch_get_utility_with_stdout(target='zope.component.getUtility',
stdout=None):
"""Patch zope.component.getUtility to use a special mock IDisplay.
The mock IDisplay works like a regular mock object, except it also
also asserts that methods are called with valid arguments.
The `message` argument passed to the IDisplay methods is passed to
stdout's write method.
:param str target: path to patch
:param object stdout: object to write standard output to; it is
expected to have a `write` method
:returns: mock zope.component.getUtility
:rtype: mock.MagicMock
"""
stdout = stdout if stdout else six.StringIO()
freezable_mock = _create_get_utility_mock_with_stdout(stdout)
return mock.patch(target, new=freezable_mock)
class FreezableMock(object):
"""Mock object with the ability to freeze attributes.
@@ -249,6 +274,36 @@ def _create_get_utility_mock():
return FreezableMock(frozen=True, return_value=display)
def _create_get_utility_mock_with_stdout(stdout):
def _write_msg(message, *unused_args, **unused_kwargs):
"""Write to message to stdout.
"""
if message:
stdout.write(message)
def mock_method(*args, **kwargs):
"""
Mock function for IDisplay methods.
"""
_assert_valid_call(args, kwargs)
_write_msg(*args, **kwargs)
display = FreezableMock()
for name in interfaces.IDisplay.names(): # pylint: disable=no-member
if name == 'notification':
frozen_mock = FreezableMock(frozen=True,
func=_write_msg)
setattr(display, name, frozen_mock)
else:
frozen_mock = FreezableMock(frozen=True,
func=mock_method)
setattr(display, name, frozen_mock)
display.freeze()
return FreezableMock(frozen=True, return_value=display)
def _assert_valid_call(*args, **kwargs):
assert_args = [args[0] if args else kwargs['message']]