Blame conftest.py

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)