launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #21791
[Merge] lp:~cjwatson/launchpad-buildd/fake-backend into lp:launchpad-buildd
Colin Watson has proposed merging lp:~cjwatson/launchpad-buildd/fake-backend into lp:launchpad-buildd with lp:~cjwatson/launchpad-buildd/refactor-fakeslave-waitingfiles as a prerequisite.
Commit message:
Add a FakeBackend, for use in tests of Operation subclass logic.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad-buildd/fake-backend/+merge/328621
I'd previously had the tests for individual operations spell out what the chroot backend should be doing at the level of subprocess calls and similar. This required a lot of repetition of the tests of the chroot backend itself, and was going to require even more repetitions once we have a LXD backend. Having a backend implementation that meets the contract of a Backend while doing nothing except remembering how it was called means that the operation tests can instead just be unit tests of the operation logic.
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad-buildd/fake-backend into lp:launchpad-buildd.
=== modified file 'lpbuildd/target/backend.py'
--- lpbuildd/target/backend.py 2017-08-05 00:17:59 +0000
+++ lpbuildd/target/backend.py 2017-08-05 00:17:59 +0000
@@ -27,6 +27,10 @@
if name == "chroot":
from lpbuildd.target.chroot import Chroot
backend_factory = Chroot
+ elif name == "fake":
+ # Only for use in tests.
+ from lpbuildd.tests.fakeslave import FakeBackend
+ backend_factory = FakeBackend
else:
raise KeyError("Unknown backend: %s" % name)
return backend_factory(build_id, series=series, arch=arch)
=== modified file 'lpbuildd/target/kill_processes.py'
--- lpbuildd/target/kill_processes.py 2017-08-05 00:17:59 +0000
+++ lpbuildd/target/kill_processes.py 2017-08-05 00:17:59 +0000
@@ -21,3 +21,4 @@
logger.info(
"Scanning for processes to kill in build %s", self.args.build_id)
self.backend.kill_processes()
+ return 0
=== modified file 'lpbuildd/target/operation.py'
--- lpbuildd/target/operation.py 2017-08-05 00:17:59 +0000
+++ lpbuildd/target/operation.py 2017-08-05 00:17:59 +0000
@@ -29,7 +29,7 @@
def make_parser(self):
parser = ArgumentParser(description=self.description)
parser.add_argument(
- "--backend", choices=["chroot", "lxd"],
+ "--backend", choices=["chroot", "lxd", "fake"],
help="use this type of backend")
parser.add_argument(
"--series", metavar="SERIES", help="operate on series SERIES")
=== modified file 'lpbuildd/target/tests/test_add_trusted_keys.py'
--- lpbuildd/target/tests/test_add_trusted_keys.py 2017-08-05 00:17:59 +0000
+++ lpbuildd/target/tests/test_add_trusted_keys.py 2017-08-05 00:17:59 +0000
@@ -5,10 +5,6 @@
import io
-from fixtures import (
- EnvironmentVariable,
- MockPatchObject,
- )
from testtools import TestCase
from lpbuildd.target.add_trusted_keys import AddTrustedKeys
@@ -17,19 +13,12 @@
class TestAddTrustedKeys(TestCase):
def test_add_trusted_keys(self):
- self.useFixture(EnvironmentVariable("HOME", "/expected/home"))
- args = ["--backend=chroot", "--series=xenial", "--arch=amd64", "1"]
+ args = ["--backend=fake", "--series=xenial", "--arch=amd64", "1"]
input_file = io.BytesIO()
add_trusted_keys = AddTrustedKeys(args=args, input_file=input_file)
- # XXX cjwatson 2017-07-29: With a newer version of fixtures we could
- # mock this at the subprocess level instead, but at the moment doing
- # that wouldn't allow us to test stdin.
- mock_backend_run = self.useFixture(
- MockPatchObject(add_trusted_keys.backend, "run")).mock
- add_trusted_keys.run()
-
- self.assertEqual(2, len(mock_backend_run.mock_calls))
- mock_backend_run.assert_has_calls([
+ self.assertEqual(0, add_trusted_keys.run())
+ expected_run = [
((["apt-key", "add", "-"],), {"stdin": input_file}),
((["apt-key", "list"],), {}),
- ])
+ ]
+ self.assertEqual(expected_run, add_trusted_keys.backend.run.calls)
=== modified file 'lpbuildd/target/tests/test_create.py'
--- lpbuildd/target/tests/test_create.py 2017-08-05 00:17:59 +0000
+++ lpbuildd/target/tests/test_create.py 2017-08-05 00:17:59 +0000
@@ -3,8 +3,6 @@
__metaclass__ = type
-from fixtures import EnvironmentVariable
-from systemfixtures import FakeProcesses
from testtools import TestCase
from lpbuildd.target.create import Create
@@ -13,18 +11,10 @@
class TestCreate(TestCase):
def test_succeeds(self):
- self.useFixture(EnvironmentVariable("HOME", "/expected/home"))
- processes_fixture = self.useFixture(FakeProcesses())
- processes_fixture.add(lambda _: {}, name="sudo")
args = [
- "--backend=chroot", "--series=xenial", "--arch=amd64", "1",
+ "--backend=fake", "--series=xenial", "--arch=amd64", "1",
"/path/to/tarball"]
- Create(args=args).run()
-
- expected_args = [
- ["sudo", "tar", "-C", "/expected/home/build-1",
- "-xf", "/path/to/tarball"],
- ]
+ create = Create(args=args)
+ self.assertEqual(0, create.run())
self.assertEqual(
- expected_args,
- [proc._args["args"] for proc in processes_fixture.procs])
+ [(("/path/to/tarball",), {})], create.backend.create.calls)
=== modified file 'lpbuildd/target/tests/test_kill_processes.py'
--- lpbuildd/target/tests/test_kill_processes.py 2017-08-05 00:17:59 +0000
+++ lpbuildd/target/tests/test_kill_processes.py 2017-08-05 00:17:59 +0000
@@ -3,32 +3,16 @@
__metaclass__ = type
-import os.path
-import signal
-
-from fixtures import EnvironmentVariable
-from systemfixtures import FakeFilesystem
from testtools import TestCase
-from testtools.matchers import DirContains
from lpbuildd.target.kill_processes import KillProcesses
-from lpbuildd.target.tests.testfixtures import KillFixture
class TestKillProcesses(TestCase):
def test_succeeds(self):
- self.useFixture(EnvironmentVariable("HOME", "/expected/home"))
- fs_fixture = self.useFixture(FakeFilesystem())
- fs_fixture.add("/expected")
- os.makedirs("/expected/home/build-1/chroot-autobuild")
- fs_fixture.add("/proc")
- os.mkdir("/proc")
- os.mkdir("/proc/10")
- os.symlink("/expected/home/build-1/chroot-autobuild", "/proc/10/root")
- kill_fixture = self.useFixture(KillFixture())
- args = ["--backend=chroot", "--series=xenial", "--arch=amd64", "1"]
- KillProcesses(args=args).run()
-
- self.assertEqual([(10, signal.SIGKILL)], kill_fixture.kills)
- self.assertThat("/proc", DirContains([]))
+ args = ["--backend=fake", "--series=xenial", "--arch=amd64", "1"]
+ kill_processes = KillProcesses(args=args)
+ self.assertEqual(0, kill_processes.run())
+ self.assertEqual(
+ [((), {})], kill_processes.backend.kill_processes.calls)
=== modified file 'lpbuildd/target/tests/test_override_sources_list.py'
--- lpbuildd/target/tests/test_override_sources_list.py 2017-08-05 00:17:59 +0000
+++ lpbuildd/target/tests/test_override_sources_list.py 2017-08-05 00:17:59 +0000
@@ -5,45 +5,24 @@
from textwrap import dedent
-from fixtures import (
- EnvironmentVariable,
- MockPatchObject,
- )
from testtools import TestCase
from lpbuildd.target.override_sources_list import OverrideSourcesList
-from lpbuildd.tests.fakeslave import FakeMethod
-
-
-class MockCopyIn(FakeMethod):
-
- def __init__(self, *args, **kwargs):
- super(MockCopyIn, self).__init__(*args, **kwargs)
- self.source_bytes = None
-
- def __call__(self, source_path, *args, **kwargs):
- with open(source_path, "rb") as source:
- self.source_bytes = source.read()
- return super(MockCopyIn, self).__call__(source_path, *args, **kwargs)
class TestOverrideSourcesList(TestCase):
def test_succeeds(self):
- self.useFixture(EnvironmentVariable("HOME", "/expected/home"))
args = [
- "--backend=chroot", "--series=xenial", "--arch=amd64", "1",
+ "--backend=fake", "--series=xenial", "--arch=amd64", "1",
"deb http://archive.ubuntu.com/ubuntu xenial main",
"deb http://ppa.launchpad.net/launchpad/ppa/ubuntu xenial main",
]
override_sources_list = OverrideSourcesList(args=args)
- mock_copy_in = self.useFixture(MockPatchObject(
- override_sources_list.backend, "copy_in", new=MockCopyIn())).mock
- override_sources_list.run()
-
- self.assertEqual(dedent("""\
- deb http://archive.ubuntu.com/ubuntu xenial main
- deb http://ppa.launchpad.net/launchpad/ppa/ubuntu xenial main
- """).encode("UTF-8"), mock_copy_in.source_bytes)
- self.assertEqual(
- ("/etc/apt/sources.list",), mock_copy_in.extract_args()[0][1:])
+ self.assertEqual(0, override_sources_list.run())
+ self.assertEqual({
+ "/etc/apt/sources.list": dedent("""\
+ deb http://archive.ubuntu.com/ubuntu xenial main
+ deb http://ppa.launchpad.net/launchpad/ppa/ubuntu xenial main
+ """).encode("UTF-8"),
+ }, override_sources_list.backend.copied_in)
=== modified file 'lpbuildd/target/tests/test_remove.py'
--- lpbuildd/target/tests/test_remove.py 2017-08-05 00:17:59 +0000
+++ lpbuildd/target/tests/test_remove.py 2017-08-05 00:17:59 +0000
@@ -3,8 +3,6 @@
__metaclass__ = type
-from fixtures import EnvironmentVariable
-from systemfixtures import FakeProcesses
from testtools import TestCase
from lpbuildd.target.remove import Remove
@@ -13,13 +11,7 @@
class TestRemove(TestCase):
def test_succeeds(self):
- self.useFixture(EnvironmentVariable("HOME", "/expected/home"))
- processes_fixture = self.useFixture(FakeProcesses())
- processes_fixture.add(lambda _: {}, name="sudo")
- args = ["--backend=chroot", "--series=xenial", "--arch=amd64", "1"]
- Remove(args=args).run()
-
- expected_args = [["sudo", "rm", "-rf", "/expected/home/build-1"]]
- self.assertEqual(
- expected_args,
- [proc._args["args"] for proc in processes_fixture.procs])
+ args = ["--backend=fake", "--series=xenial", "--arch=amd64", "1"]
+ remove = Remove(args=args)
+ self.assertEqual(0, remove.run())
+ self.assertEqual([((), {})], remove.backend.remove.calls)
=== modified file 'lpbuildd/target/tests/test_start.py'
--- lpbuildd/target/tests/test_start.py 2017-08-05 00:17:59 +0000
+++ lpbuildd/target/tests/test_start.py 2017-08-05 00:17:59 +0000
@@ -3,13 +3,6 @@
__metaclass__ = type
-import os.path
-
-from fixtures import EnvironmentVariable
-from systemfixtures import (
- FakeFilesystem,
- FakeProcesses,
- )
from testtools import TestCase
from lpbuildd.target.start import Start
@@ -18,22 +11,7 @@
class TestStart(TestCase):
def test_succeeds(self):
- self.useFixture(EnvironmentVariable("HOME", "/expected/home"))
- processes_fixture = self.useFixture(FakeProcesses())
- processes_fixture.add(lambda _: {}, name="sudo")
- fs_fixture = self.useFixture(FakeFilesystem())
- fs_fixture.add("/etc")
- os.mkdir("/etc")
- for etc_name in ("hosts", "hostname", "resolv.conf.real"):
- with open(os.path.join("/etc", etc_name), "w") as etc_file:
- etc_file.write("%s\n" % etc_name)
- os.chmod(os.path.join("/etc", etc_name), 0o644)
- os.symlink("resolv.conf.real", "/etc/resolv.conf")
- args = ["--backend=chroot", "--series=xenial", "--arch=amd64", "1"]
- Start(args=args).run()
-
- # Tested in more detail in lpbuildd.target.tests.test_chroot.
- self.assertIn(
- ["sudo", "mount", "-t", "proc", "none",
- "/expected/home/build-1/chroot-autobuild/proc"],
- [proc._args["args"] for proc in processes_fixture.procs])
+ args = ["--backend=fake", "--series=xenial", "--arch=amd64", "1"]
+ start = Start(args=args)
+ self.assertEqual(0, start.run())
+ self.assertEqual([((), {})], start.backend.start.calls)
=== modified file 'lpbuildd/target/tests/test_stop.py'
--- lpbuildd/target/tests/test_stop.py 2017-08-05 00:17:59 +0000
+++ lpbuildd/target/tests/test_stop.py 2017-08-05 00:17:59 +0000
@@ -3,36 +3,33 @@
__metaclass__ = type
-import os.path
+from textwrap import dedent
-from fixtures import EnvironmentVariable
-from systemfixtures import (
- FakeFilesystem,
- FakeProcesses,
- )
+from fixtures import FakeLogger
from testtools import TestCase
+from testtools.matchers import StartsWith
+from lpbuildd.target.backend import BackendException
from lpbuildd.target.stop import Stop
-from lpbuildd.target.tests.testfixtures import SudoUmount
class TestStop(TestCase):
def test_succeeds(self):
- self.useFixture(EnvironmentVariable("HOME", "/expected/home"))
- processes_fixture = self.useFixture(FakeProcesses())
- processes_fixture.add(SudoUmount(), name="sudo")
- fs_fixture = self.useFixture(FakeFilesystem())
- fs_fixture.add("/proc")
- os.mkdir("/proc")
- with open("/proc/mounts", "w") as mounts_file:
- mounts_file.write(
- "none {chroot}/proc proc rw,relatime 0 0".format(
- chroot="/expected/home/build-1/chroot-autobuild"))
- args = ["--backend=chroot", "--series=xenial", "--arch=amd64", "1"]
- Stop(args=args).run()
+ args = ["--backend=fake", "--series=xenial", "--arch=amd64", "1"]
+ stop = Stop(args=args)
+ self.assertEqual(0, stop.run())
+ self.assertEqual([((), {})], stop.backend.stop.calls)
- # Tested in more detail in lpbuildd.target.tests.test_chroot.
- self.assertIn(
- ["sudo", "umount", "/expected/home/build-1/chroot-autobuild/proc"],
- [proc._args["args"] for proc in processes_fixture.procs])
+ def test_fails(self):
+ logger = self.useFixture(FakeLogger())
+ args = ["--backend=fake", "--series=xenial", "--arch=amd64", "1"]
+ stop = Stop(args=args)
+ stop.backend.stop.failure = BackendException
+ self.assertEqual(1, stop.run())
+ self.assertEqual([((), {})], stop.backend.stop.calls)
+ self.assertThat(logger.output, StartsWith(dedent("""\
+ Stopping target for build 1
+ Failed to stop target
+ Traceback (most recent call last):
+ """)))
=== modified file 'lpbuildd/target/tests/test_update.py'
--- lpbuildd/target/tests/test_update.py 2017-08-05 00:17:59 +0000
+++ lpbuildd/target/tests/test_update.py 2017-08-05 00:17:59 +0000
@@ -3,84 +3,79 @@
__metaclass__ = type
+import subprocess
import time
-from fixtures import (
- EnvironmentVariable,
- FakeLogger,
- )
-from systemfixtures import (
- FakeProcesses,
- FakeTime,
- )
+from fixtures import FakeLogger
+from systemfixtures import FakeTime
from testtools import TestCase
+from testtools.matchers import (
+ ContainsDict,
+ Equals,
+ MatchesDict,
+ MatchesListwise,
+ )
from lpbuildd.target.update import Update
+from lpbuildd.tests.fakeslave import FakeMethod
+
+
+class RanAptGet(MatchesListwise):
+
+ def __init__(self, args_list):
+ super(RanAptGet, self).__init__([
+ MatchesListwise([
+ Equals((["/usr/bin/apt-get"] + args,)),
+ ContainsDict({
+ "env": MatchesDict({
+ "LANG": Equals("C"),
+ "DEBIAN_FRONTEND": Equals("noninteractive"),
+ "TTY": Equals("unknown"),
+ }),
+ }),
+ ]) for args in args_list
+ ])
class TestUpdate(TestCase):
def test_succeeds(self):
- self.useFixture(EnvironmentVariable("HOME", "/expected/home"))
- processes_fixture = self.useFixture(FakeProcesses())
- processes_fixture.add(lambda _: {}, name="sudo")
self.useFixture(FakeTime())
start_time = time.time()
- args = ["--backend=chroot", "--series=xenial", "--arch=amd64", "1"]
- Update(args=args).run()
+ args = ["--backend=fake", "--series=xenial", "--arch=amd64", "1"]
+ update = Update(args=args)
+ self.assertEqual(0, update.run())
- apt_get_args = [
- "sudo", "/usr/sbin/chroot",
- "/expected/home/build-1/chroot-autobuild",
- "linux64", "env",
- "LANG=C",
- "DEBIAN_FRONTEND=noninteractive",
- "TTY=unknown",
- "/usr/bin/apt-get",
- ]
expected_args = [
- apt_get_args + ["-uy", "update"],
- apt_get_args + [
- "-o", "DPkg::Options::=--force-confold", "-uy", "--purge",
- "dist-upgrade",
- ],
+ ["-uy", "update"],
+ ["-o", "DPkg::Options::=--force-confold", "-uy", "--purge",
+ "dist-upgrade"],
]
- self.assertEqual(
- expected_args,
- [proc._args["args"] for proc in processes_fixture.procs])
+ self.assertThat(update.backend.run.calls, RanAptGet(expected_args))
self.assertEqual(start_time, time.time())
def test_first_run_fails(self):
+ class FailFirstTime(FakeMethod):
+ def __call__(self, run_args, *args, **kwargs):
+ super(FailFirstTime, self).__call__(run_args, *args, **kwargs)
+ if len(self.calls) == 1:
+ raise subprocess.CalledProcessError(1, run_args)
+
logger = self.useFixture(FakeLogger())
- self.useFixture(EnvironmentVariable("HOME", "/expected/home"))
- processes_fixture = self.useFixture(FakeProcesses())
- apt_get_proc_infos = iter([{"returncode": 1}, {}, {}])
- processes_fixture.add(lambda _: next(apt_get_proc_infos), name="sudo")
self.useFixture(FakeTime())
start_time = time.time()
- args = ["--backend=chroot", "--series=xenial", "--arch=amd64", "1"]
- Update(args=args).run()
+ args = ["--backend=fake", "--series=xenial", "--arch=amd64", "1"]
+ update = Update(args=args)
+ update.backend.run = FailFirstTime()
+ self.assertEqual(0, update.run())
- apt_get_args = [
- "sudo", "/usr/sbin/chroot",
- "/expected/home/build-1/chroot-autobuild",
- "linux64", "env",
- "LANG=C",
- "DEBIAN_FRONTEND=noninteractive",
- "TTY=unknown",
- "/usr/bin/apt-get",
- ]
expected_args = [
- apt_get_args + ["-uy", "update"],
- apt_get_args + ["-uy", "update"],
- apt_get_args + [
- "-o", "DPkg::Options::=--force-confold", "-uy", "--purge",
- "dist-upgrade",
- ],
+ ["-uy", "update"],
+ ["-uy", "update"],
+ ["-o", "DPkg::Options::=--force-confold", "-uy", "--purge",
+ "dist-upgrade"],
]
- self.assertEqual(
- expected_args,
- [proc._args["args"] for proc in processes_fixture.procs])
+ self.assertThat(update.backend.run.calls, RanAptGet(expected_args))
self.assertEqual(
"Updating target for build 1\n"
"Waiting 15 seconds and trying again ...\n",
=== modified file 'lpbuildd/tests/fakeslave.py'
--- lpbuildd/tests/fakeslave.py 2017-08-05 00:17:59 +0000
+++ lpbuildd/tests/fakeslave.py 2017-08-05 00:17:59 +0000
@@ -11,6 +11,8 @@
import os
import shutil
+from lpbuildd.target.backend import Backend
+
class FakeMethod:
"""Catch any function or method call, and record the fact.
@@ -102,3 +104,21 @@
def getArch(self):
return 'i386'
+
+
+class FakeBackend(Backend):
+
+ def __init__(self, *args, **kwargs):
+ super(FakeBackend, self).__init__(*args, **kwargs)
+ fake_methods = (
+ "create", "start",
+ "run",
+ "kill_processes", "stop", "remove",
+ )
+ for fake_method in fake_methods:
+ setattr(self, fake_method, FakeMethod())
+ self.copied_in = {}
+
+ def copy_in(self, source_path, target_path):
+ with open(source_path, "rb") as source:
+ self.copied_in[target_path] = source.read()