|
Packit Service |
751c4a |
import os
|
|
Packit Service |
751c4a |
from unittest import mock
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
import pytest
|
|
Packit Service |
751c4a |
import httpretty as _httpretty
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
from cloudinit import helpers, subp
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
class _FixtureUtils:
|
|
Packit Service |
751c4a |
"""A namespace for fixture helper functions, used by fixture_utils.
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
These helper functions are all defined as staticmethods so they are
|
|
Packit Service |
751c4a |
effectively functions; they are defined in a class only to give us a
|
|
Packit Service |
751c4a |
namespace so calling them can look like
|
|
Packit Service |
751c4a |
``fixture_utils.fixture_util_function()`` in test code.
|
|
Packit Service |
751c4a |
"""
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
@staticmethod
|
|
Packit Service |
751c4a |
def closest_marker_args_or(request, marker_name: str, default):
|
|
Packit Service |
751c4a |
"""Get the args for closest ``marker_name`` or return ``default``
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
:param request:
|
|
Packit Service |
751c4a |
A pytest request, as passed to a fixture.
|
|
Packit Service |
751c4a |
:param marker_name:
|
|
Packit Service |
751c4a |
The name of the marker to look for
|
|
Packit Service |
751c4a |
:param default:
|
|
Packit Service |
751c4a |
The value to return if ``marker_name`` is not found.
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
:return:
|
|
Packit Service |
751c4a |
The args for the closest ``marker_name`` marker, or ``default``
|
|
Packit Service |
751c4a |
if no such marker is found.
|
|
Packit Service |
751c4a |
"""
|
|
Packit Service |
751c4a |
try:
|
|
Packit Service |
751c4a |
marker = request.node.get_closest_marker(marker_name)
|
|
Packit Service |
751c4a |
except AttributeError:
|
|
Packit Service |
751c4a |
# Older versions of pytest don't have the new API
|
|
Packit Service |
751c4a |
marker = request.node.get_marker(marker_name)
|
|
Packit Service |
751c4a |
if marker is not None:
|
|
Packit Service |
751c4a |
return marker.args
|
|
Packit Service |
751c4a |
return default
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
@staticmethod
|
|
Packit Service |
751c4a |
def closest_marker_first_arg_or(request, marker_name: str, default):
|
|
Packit Service |
751c4a |
"""Get the first arg for closest ``marker_name`` or return ``default``
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
This is a convenience wrapper around closest_marker_args_or, see there
|
|
Packit Service |
751c4a |
for full details.
|
|
Packit Service |
751c4a |
"""
|
|
Packit Service |
751c4a |
result = _FixtureUtils.closest_marker_args_or(
|
|
Packit Service |
751c4a |
request, marker_name, [default]
|
|
Packit Service |
751c4a |
)
|
|
Packit Service |
751c4a |
if not result:
|
|
Packit Service |
751c4a |
raise TypeError(
|
|
Packit Service |
751c4a |
"Missing expected argument to {} marker".format(marker_name)
|
|
Packit Service |
751c4a |
)
|
|
Packit Service |
751c4a |
return result[0]
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
@pytest.yield_fixture(autouse=True)
|
|
Packit Service |
751c4a |
def disable_subp_usage(request, fixture_utils):
|
|
Packit Service |
751c4a |
"""
|
|
Packit Service |
751c4a |
Across all (pytest) tests, ensure that subp.subp is not invoked.
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
Note that this can only catch invocations where the util module is imported
|
|
Packit Service |
751c4a |
and ``subp.subp(...)`` is called. ``from cloudinit.subp mport subp``
|
|
Packit Service |
751c4a |
imports happen before the patching here (or the CiTestCase monkey-patching)
|
|
Packit Service |
751c4a |
happens, so are left untouched.
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
To allow a particular test method or class to use subp.subp you can mark it
|
|
Packit Service |
751c4a |
as such::
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
@pytest.mark.allow_all_subp
|
|
Packit Service |
751c4a |
def test_whoami(self):
|
|
Packit Service |
751c4a |
subp.subp(["whoami"])
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
To instead allow subp.subp usage for a specific command, you can use the
|
|
Packit Service |
751c4a |
``allow_subp_for`` mark::
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
@pytest.mark.allow_subp_for("bash")
|
|
Packit Service |
751c4a |
def test_bash(self):
|
|
Packit Service |
751c4a |
subp.subp(["bash"])
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
You can pass multiple commands as values; they will all be permitted::
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
@pytest.mark.allow_subp_for("bash", "whoami")
|
|
Packit Service |
751c4a |
def test_several_things(self):
|
|
Packit Service |
751c4a |
subp.subp(["bash"])
|
|
Packit Service |
751c4a |
subp.subp(["whoami"])
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
This fixture (roughly) mirrors the functionality of
|
|
Packit Service |
751c4a |
CiTestCase.allowed_subp. N.B. While autouse fixtures do affect non-pytest
|
|
Packit Service |
751c4a |
tests, CiTestCase's allowed_subp does take precedence (and we have
|
|
Packit Service |
751c4a |
TestDisableSubpUsageInTestSubclass to confirm that).
|
|
Packit Service |
751c4a |
"""
|
|
Packit Service |
751c4a |
allow_subp_for = fixture_utils.closest_marker_args_or(
|
|
Packit Service |
751c4a |
request, "allow_subp_for", None
|
|
Packit Service |
751c4a |
)
|
|
Packit Service |
751c4a |
# Because the mark doesn't take arguments, `allow_all_subp` will be set to
|
|
Packit Service |
751c4a |
# [] if the marker is present, so explicit None checks are required
|
|
Packit Service |
751c4a |
allow_all_subp = fixture_utils.closest_marker_args_or(
|
|
Packit Service |
751c4a |
request, "allow_all_subp", None
|
|
Packit Service |
751c4a |
)
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
if allow_all_subp is not None and allow_subp_for is None:
|
|
Packit Service |
751c4a |
# Only allow_all_subp specified, don't mock subp.subp
|
|
Packit Service |
751c4a |
yield
|
|
Packit Service |
751c4a |
return
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
if allow_all_subp is None and allow_subp_for is None:
|
|
Packit Service |
751c4a |
# No marks, default behaviour; disallow all subp.subp usage
|
|
Packit Service |
751c4a |
def side_effect(args, *other_args, **kwargs):
|
|
Packit Service |
751c4a |
raise AssertionError("Unexpectedly used subp.subp")
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
elif allow_all_subp is not None and allow_subp_for is not None:
|
|
Packit Service |
751c4a |
# Both marks, ambiguous request; raise an exception on all subp usage
|
|
Packit Service |
751c4a |
def side_effect(args, *other_args, **kwargs):
|
|
Packit Service |
751c4a |
raise AssertionError(
|
|
Packit Service |
751c4a |
"Test marked both allow_all_subp and allow_subp_for: resolve"
|
|
Packit Service |
751c4a |
" this either by modifying your test code, or by modifying"
|
|
Packit Service |
751c4a |
" disable_subp_usage to handle precedence."
|
|
Packit Service |
751c4a |
)
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
# Look this up before our patch is in place, so we have access to
|
|
Packit Service |
751c4a |
# the real implementation in side_effect
|
|
Packit Service |
751c4a |
real_subp = subp.subp
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
def side_effect(args, *other_args, **kwargs):
|
|
Packit Service |
751c4a |
cmd = args[0]
|
|
Packit Service |
751c4a |
if cmd not in allow_subp_for:
|
|
Packit Service |
751c4a |
raise AssertionError(
|
|
Packit Service |
751c4a |
"Unexpectedly used subp.subp to call {} (allowed:"
|
|
Packit Service |
751c4a |
" {})".format(cmd, ",".join(allow_subp_for))
|
|
Packit Service |
751c4a |
)
|
|
Packit Service |
751c4a |
return real_subp(args, *other_args, **kwargs)
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
with mock.patch("cloudinit.subp.subp", autospec=True) as m_subp:
|
|
Packit Service |
751c4a |
m_subp.side_effect = side_effect
|
|
Packit Service |
751c4a |
yield
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
@pytest.fixture(scope="session")
|
|
Packit Service |
751c4a |
def fixture_utils():
|
|
Packit Service |
751c4a |
"""Return a namespace containing fixture utility functions.
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
See :py:class:`_FixtureUtils` for further details."""
|
|
Packit Service |
751c4a |
return _FixtureUtils
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
@pytest.yield_fixture
|
|
Packit Service |
751c4a |
def httpretty():
|
|
Packit Service |
751c4a |
"""
|
|
Packit Service |
751c4a |
Enable HTTPretty for duration of the testcase, resetting before and after.
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
This will also ensure allow_net_connect is set to False, and temporarily
|
|
Packit Service |
751c4a |
unset http_proxy in os.environ if present (to work around
|
|
Packit Service |
751c4a |
https://github.com/gabrielfalcao/HTTPretty/issues/122).
|
|
Packit Service |
751c4a |
"""
|
|
Packit Service |
751c4a |
restore_proxy = os.environ.pop("http_proxy", None)
|
|
Packit Service |
751c4a |
_httpretty.HTTPretty.allow_net_connect = False
|
|
Packit Service |
751c4a |
_httpretty.reset()
|
|
Packit Service |
751c4a |
_httpretty.enable()
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
yield _httpretty
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
_httpretty.disable()
|
|
Packit Service |
751c4a |
_httpretty.reset()
|
|
Packit Service |
751c4a |
if restore_proxy is not None:
|
|
Packit Service |
751c4a |
os.environ["http_proxy"] = restore_proxy
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
@pytest.fixture
|
|
Packit Service |
751c4a |
def paths(tmpdir):
|
|
Packit Service |
751c4a |
"""
|
|
Packit Service |
751c4a |
Return a helpers.Paths object configured to use a tmpdir.
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
(This uses the builtin tmpdir fixture.)
|
|
Packit Service |
751c4a |
"""
|
|
Packit Service |
751c4a |
dirs = {
|
|
Packit Service |
751c4a |
"cloud_dir": tmpdir.mkdir("cloud_dir").strpath,
|
|
Packit Service |
751c4a |
"run_dir": tmpdir.mkdir("run_dir").strpath,
|
|
Packit Service |
751c4a |
}
|
|
Packit Service |
751c4a |
return helpers.Paths(dirs)
|