Blame conftest.py

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