Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@ Misc

+ `#14`_: drop support for Python 3.5.

.. _#14: https://github.com/RKrahl/auto-patch/pull/14
Bug fixes and minor changes
---------------------------

+ `#16`_, `#17`_: Review of the test framework.

.. _#14: https://github.com/RKrahl/auto-patch/pull/14
.. _#16: https://github.com/RKrahl/auto-patch/issues/16
.. _#17: https://github.com/RKrahl/auto-patch/pull/17


1.1.0 (2022-10-03)
Expand Down
100 changes: 58 additions & 42 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import argparse
from configparser import ConfigParser
import contextlib
import json
from multiprocessing import Process
import os
Expand All @@ -11,9 +12,61 @@

test_dir = Path(__file__).parent
script_dir = Path(os.environ['BUILD_SCRIPTS_DIR'])
auto_patch_path = script_dir / "auto-patch.py"

os.environ['AUTO_PATCH_CFG'] = "auto-patch.cfg"

def get_zypper_argument_parser():
parser = argparse.ArgumentParser()
parser.add_argument('--quiet', action='store_true')
parser.add_argument('--non-interactive', action='store_true')
parser.add_argument('subcmd')
parser.add_argument('--skip-interactive', action='store_true')
return parser

zypper_arg_parser = get_zypper_argument_parser()

class mock_subprocess_run:
"""A mock replacement for subprocess.run.
It fakes all invocations of the zypper binary.
"""
def __init__(self, results):
self.results_iter = iter(results)

def __call__(self, cmd, stdout=None, **kwargs):
zypp_res = next(self.results_iter)
assert Path(cmd[0]).name == "zypper"
args = zypper_arg_parser.parse_args(args=cmd[1:])
assert args.subcmd == zypp_res.cmd
stdout.write(zypp_res.stdout)
return subprocess.CompletedProcess(cmd, zypp_res.returncode,
stderr=zypp_res.stderr)

class mock_smtp(contextlib.AbstractContextManager):
"""A mock replacement for smtplib.SMTP.
Instead of sending the mail, it pickles it to a well known file.
"""
def __init__(self, host='', **kwargs):
self.host = host

def __exit__(self, *args):
pass

def send_message(self, msg, **kwargs):
with open("report.pickle", "wb") as f:
pickle.dump(self.host, f)
pickle.dump(msg, f)

def invoke_auto_patch(zypper_results):
"""Patch the current Python intepreter and execute auto-patch.py.
This function is supposed to be the target of a Process.
"""
import subprocess
import smtplib
subprocess.run = mock_subprocess_run(zypper_results)
smtplib.SMTP = mock_smtp
with auto_patch_path.open("rt") as script:
exec(script.read(), dict(__name__="__main__"))

class ZypperResult:
"""Represent the result of one mock zypper call in AutoPatchCaller.
Expand All @@ -32,16 +85,15 @@ class AutoPatchCaller:
relations to the outside world for the script.
"""

auto_patch_path = script_dir / "auto-patch.py"
_zypper_result_data = None

@classmethod
def _get_zypper_result_data(cls):
if not cls._zypper_result_data:
try:
return cls._zypper_result_data
except AttributeError:
datafile = test_dir / "zypper-result-data.json"
with datafile.open("rt") as f:
cls._zypper_result_data = json.load(f)
return cls._zypper_result_data
return cls._zypper_result_data

@classmethod
def get_caller(cls, case, config=None):
Expand All @@ -62,46 +114,10 @@ def _create_config(self, config):

def __init__(self, zypper_results, config=None):
self._create_config(config)
parser = argparse.ArgumentParser()
parser.add_argument('--quiet', action='store_true')
parser.add_argument('--non-interactive', action='store_true')
parser.add_argument('subcmd')
parser.add_argument('--skip-interactive', action='store_true')
self.zypper_arg_parser = parser
self.zypper_results = zypper_results
self.results_iter = iter(self.zypper_results)

def _mock_subprocess_run(self, cmd, stdout=None, **kwargs):
zypp_res = next(self.results_iter)
assert Path(cmd[0]).name == "zypper"
args = self.zypper_arg_parser.parse_args(args=cmd[1:])
assert args.subcmd == zypp_res.cmd
stdout.write(zypp_res.stdout)
return subprocess.CompletedProcess(cmd, zypp_res.returncode,
stderr=zypp_res.stderr)

class _mock_smtp:
def __init__(self, host='', **kwargs):
self.host = host
def __enter__(self):
return self
def __exit__(self, *args):
pass
def send_message(self, msg, **kwargs):
with open("report.pickle", "wb") as f:
pickle.dump(self.host, f)
pickle.dump(msg, f)

def _patch_and_call(self):
import subprocess
import smtplib
subprocess.run = self._mock_subprocess_run
smtplib.SMTP = self._mock_smtp
with self.auto_patch_path.open("rt") as script:
exec(script.read(), dict(__name__="__main__"))

def run(self, exitcode=0):
p = Process(target=self._patch_and_call)
p = Process(target=invoke_auto_patch, args=(self.zypper_results,))
p.start()
p.join()
assert p.exitcode == exitcode
Expand Down