diff options
Diffstat (limited to 'conftest.py')
-rw-r--r-- | conftest.py | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/conftest.py b/conftest.py new file mode 100644 index 00000000..251bca59 --- /dev/null +++ b/conftest.py @@ -0,0 +1,72 @@ +from unittest import mock + +import pytest + +from cloudinit import subp + + +@pytest.yield_fixture(autouse=True) +def disable_subp_usage(request): + """ + Across all (pytest) tests, ensure that subp.subp is not invoked. + + Note that this can only catch invocations where the util module is imported + and ``subp.subp(...)`` is called. ``from cloudinit.subp mport subp`` + imports happen before the patching here (or the CiTestCase monkey-patching) + happens, so are left untouched. + + To allow a particular test method or class to use subp.subp you can set the + parameter passed to this fixture to False using pytest.mark.parametrize:: + + @pytest.mark.parametrize("disable_subp_usage", [False], indirect=True) + def test_whoami(self): + subp.subp(["whoami"]) + + To instead allow subp.subp usage for a specific command, you can set the + parameter passed to this fixture to that command: + + @pytest.mark.parametrize("disable_subp_usage", ["bash"], indirect=True) + def test_bash(self): + subp.subp(["bash"]) + + To specify multiple commands, set the parameter to a list (note the + double-layered list: we specify a single parameter that is itself a list): + + @pytest.mark.parametrize( + "disable_subp_usage", ["bash", "whoami"], indirect=True) + def test_several_things(self): + subp.subp(["bash"]) + subp.subp(["whoami"]) + + This fixture (roughly) mirrors the functionality of + CiTestCase.allowed_subp. N.B. While autouse fixtures do affect non-pytest + tests, CiTestCase's allowed_subp does take precedence (and we have + TestDisableSubpUsageInTestSubclass to confirm that). + """ + should_disable = getattr(request, "param", True) + if should_disable: + if not isinstance(should_disable, (list, str)): + def side_effect(args, *other_args, **kwargs): + raise AssertionError("Unexpectedly used subp.subp") + else: + # Look this up before our patch is in place, so we have access to + # the real implementation in side_effect + real_subp = subp.subp + + if isinstance(should_disable, str): + should_disable = [should_disable] + + def side_effect(args, *other_args, **kwargs): + cmd = args[0] + if cmd not in should_disable: + raise AssertionError( + "Unexpectedly used subp.subp to call {} (allowed:" + " {})".format(cmd, ",".join(should_disable)) + ) + return real_subp(args, *other_args, **kwargs) + + with mock.patch('cloudinit.subp.subp', autospec=True) as m_subp: + m_subp.side_effect = side_effect + yield + else: + yield |