def test_checkForNewBuilders(self):
        # Test that checkForNewBuilders() detects a new builder

        # The basic case, where no builders are added.
        builder_scanner = self._getScanner()
        self.assertEqual([], builder_scanner.checkForNewBuilders())

        # Add two builders and ensure they're returned.
        new_builders = ["scooby", "lassie"]
        factory = LaunchpadObjectFactory()
        for builder_name in new_builders:
            factory.makeBuilder(name=builder_name)
        self.assertEqual(new_builders, builder_scanner.checkForNewBuilders())
    def test_checkForNewBuilders(self):
        # Test that checkForNewBuilders() detects a new builder

        # The basic case, where no builders are added.
        builder_scanner = self._getScanner()
        self.assertEqual([], builder_scanner.checkForNewBuilders())

        # Add two builders and ensure they're returned.
        new_builders = ["scooby", "lassie"]
        factory = LaunchpadObjectFactory()
        for builder_name in new_builders:
            factory.makeBuilder(name=builder_name)
        self.assertEqual(
            new_builders, builder_scanner.checkForNewBuilders())
 def test_scan_with_nothing_to_dispatch(self):
     factory = LaunchpadObjectFactory()
     builder = factory.makeBuilder()
     self.patch(BuilderSlave, 'makeBuilderSlave', FakeMethod(OkSlave()))
     transaction.commit()
     scanner = self._getScanner(builder_name=builder.name)
     d = scanner.scan()
     return d.addCallback(self._checkNoDispatch, builder)
 def test_scan_with_nothing_to_dispatch(self):
     factory = LaunchpadObjectFactory()
     builder = factory.makeBuilder()
     self.patch(BuilderSlave, 'makeBuilderSlave', FakeMethod(OkSlave()))
     transaction.commit()
     scanner = self._getScanner(builder_name=builder.name)
     d = scanner.scan()
     return d.addCallback(self._checkNoDispatch, builder)
示例#5
0
class TestBuildNotifications(TrialTestCase):

    layer = LaunchpadZopelessLayer

    def setUp(self):
        super(TestBuildNotifications, self).setUp()
        from lp.testing.factory import LaunchpadObjectFactory
        self.factory = LaunchpadObjectFactory()

    def prepareBehavior(self, fake_successful_upload=False):
        self.queue_record = self.factory.makeSourcePackageRecipeBuildJob()
        build = self.queue_record.specific_job.build
        build.updateStatus(BuildStatus.FULLYBUILT)
        if fake_successful_upload:
            removeSecurityProxy(build).verifySuccessfulUpload = FakeMethod(
                result=True)
            # We overwrite the buildmaster root to use a temp directory.
            tempdir = tempfile.mkdtemp()
            self.addCleanup(shutil.rmtree, tempdir)
            self.upload_root = tempdir
            tmp_builddmaster_root = """
            [builddmaster]
            root: %s
            """ % self.upload_root
            config.push('tmp_builddmaster_root', tmp_builddmaster_root)
            self.addCleanup(config.pop, 'tmp_builddmaster_root')
        self.queue_record.builder = self.factory.makeBuilder()
        slave = WaitingSlave('BuildStatus.OK')
        return BuilderInteractor.getBuildBehavior(self.queue_record,
                                                  self.queue_record.builder,
                                                  slave)

    def assertDeferredNotifyCount(self, status, behavior, expected_count):
        d = behavior.handleStatus(self.queue_record, status, {'filemap': {}})

        def cb(result):
            self.assertEqual(expected_count, len(pop_notifications()))

        d.addCallback(cb)
        return d

    def test_handleStatus_PACKAGEFAIL(self):
        """Failing to build the package immediately sends a notification."""
        return self.assertDeferredNotifyCount("PACKAGEFAIL",
                                              self.prepareBehavior(), 1)

    def test_handleStatus_OK(self):
        """Building the source package does _not_ immediately send mail.

        (The archive uploader mail send one later.
        """
        return self.assertDeferredNotifyCount("OK", self.prepareBehavior(), 0)

    def test_handleStatus_OK_successful_upload(self):
        return self.assertDeferredNotifyCount("OK", self.prepareBehavior(True),
                                              0)
class TestBuildNotifications(TrialTestCase):

    layer = LaunchpadZopelessLayer

    def setUp(self):
        super(TestBuildNotifications, self).setUp()
        from lp.testing.factory import LaunchpadObjectFactory

        self.factory = LaunchpadObjectFactory()

    def prepareBehavior(self, fake_successful_upload=False):
        self.queue_record = self.factory.makeSourcePackageRecipeBuildJob()
        build = self.queue_record.specific_job.build
        build.updateStatus(BuildStatus.FULLYBUILT)
        if fake_successful_upload:
            removeSecurityProxy(build).verifySuccessfulUpload = FakeMethod(result=True)
            # We overwrite the buildmaster root to use a temp directory.
            tempdir = tempfile.mkdtemp()
            self.addCleanup(shutil.rmtree, tempdir)
            self.upload_root = tempdir
            tmp_builddmaster_root = (
                """
            [builddmaster]
            root: %s
            """
                % self.upload_root
            )
            config.push("tmp_builddmaster_root", tmp_builddmaster_root)
            self.addCleanup(config.pop, "tmp_builddmaster_root")
        self.queue_record.builder = self.factory.makeBuilder()
        slave = WaitingSlave("BuildStatus.OK")
        return BuilderInteractor.getBuildBehavior(self.queue_record, self.queue_record.builder, slave)

    def assertDeferredNotifyCount(self, status, behavior, expected_count):
        d = behavior.handleStatus(self.queue_record, status, {"filemap": {}})

        def cb(result):
            self.assertEqual(expected_count, len(pop_notifications()))

        d.addCallback(cb)
        return d

    def test_handleStatus_PACKAGEFAIL(self):
        """Failing to build the package immediately sends a notification."""
        return self.assertDeferredNotifyCount("PACKAGEFAIL", self.prepareBehavior(), 1)

    def test_handleStatus_OK(self):
        """Building the source package does _not_ immediately send mail.

        (The archive uploader mail send one later.
        """
        return self.assertDeferredNotifyCount("OK", self.prepareBehavior(), 0)

    def test_handleStatus_OK_successful_upload(self):
        return self.assertDeferredNotifyCount("OK", self.prepareBehavior(True), 0)
class TestHandleStatusMixin:
    """Tests for `IPackageBuild`s handleStatus method.

    This should be run with a Trial TestCase.
    """

    layer = LaunchpadZopelessLayer

    def makeBuild(self):
        """Allow classes to override the build with which the test runs."""
        raise NotImplementedError

    def setUp(self):
        super(TestHandleStatusMixin, self).setUp()
        self.factory = LaunchpadObjectFactory()
        self.build = self.makeBuild()
        # For the moment, we require a builder for the build so that
        # handleStatus_OK can get a reference to the slave.
        self.builder = self.factory.makeBuilder()
        self.build.buildqueue_record.markAsBuilding(self.builder)
        self.slave = WaitingSlave('BuildStatus.OK')
        self.slave.valid_file_hashes.append('test_file_hash')
        self.interactor = BuilderInteractor()
        self.behaviour = self.interactor.getBuildBehaviour(
            self.build.buildqueue_record, self.builder, self.slave)

        # We overwrite the buildmaster root to use a temp directory.
        tempdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, tempdir)
        self.upload_root = tempdir
        tmp_builddmaster_root = """
        [builddmaster]
        root: %s
        """ % self.upload_root
        config.push('tmp_builddmaster_root', tmp_builddmaster_root)

        # We stub out our builds getUploaderCommand() method so
        # we can check whether it was called as well as
        # verifySuccessfulUpload().
        removeSecurityProxy(
            self.build).verifySuccessfulUpload = FakeMethod(result=True)

    def assertResultCount(self, count, result):
        self.assertEqual(
            1, len(os.listdir(os.path.join(self.upload_root, result))))

    @defer.inlineCallbacks
    def test_handleStatus_OK_normal_file(self):
        # A filemap with plain filenames should not cause a problem.
        # The call to handleStatus will attempt to get the file from
        # the slave resulting in a URL error in this test case.
        with dbuser(config.builddmaster.dbuser):
            yield self.behaviour.handleStatus(
                self.build.buildqueue_record, 'OK',
                {'filemap': {
                    'myfile.py': 'test_file_hash'
                }})
        self.assertEqual(BuildStatus.UPLOADING, self.build.status)
        self.assertResultCount(1, "incoming")

    @defer.inlineCallbacks
    def test_handleStatus_OK_absolute_filepath(self):
        # A filemap that tries to write to files outside of the upload
        # directory will not be collected.
        with ExpectedException(
                BuildDaemonError,
                "Build returned a file named u'/tmp/myfile.py'."):
            with dbuser(config.builddmaster.dbuser):
                yield self.behaviour.handleStatus(
                    self.build.buildqueue_record, 'OK',
                    {'filemap': {
                        '/tmp/myfile.py': 'test_file_hash'
                    }})

    @defer.inlineCallbacks
    def test_handleStatus_OK_relative_filepath(self):
        # A filemap that tries to write to files outside of
        # the upload directory will not be collected.
        with ExpectedException(BuildDaemonError,
                               "Build returned a file named u'../myfile.py'."):
            with dbuser(config.builddmaster.dbuser):
                yield self.behaviour.handleStatus(
                    self.build.buildqueue_record, 'OK',
                    {'filemap': {
                        '../myfile.py': 'test_file_hash'
                    }})

    @defer.inlineCallbacks
    def test_handleStatus_OK_sets_build_log(self):
        # The build log is set during handleStatus.
        self.assertEqual(None, self.build.log)
        with dbuser(config.builddmaster.dbuser):
            yield self.behaviour.handleStatus(
                self.build.buildqueue_record, 'OK',
                {'filemap': {
                    'myfile.py': 'test_file_hash'
                }})
        self.assertNotEqual(None, self.build.log)

    @defer.inlineCallbacks
    def _test_handleStatus_notifies(self, status):
        # An email notification is sent for a given build status if
        # notifications are allowed for that status.
        expected_notification = (
            status in self.behaviour.ALLOWED_STATUS_NOTIFICATIONS)

        with dbuser(config.builddmaster.dbuser):
            yield self.behaviour.handleStatus(self.build.buildqueue_record,
                                              status, {})

        if expected_notification:
            self.assertNotEqual(0, len(pop_notifications()),
                                "Notifications received")
        else:
            self.assertEqual(0, len(pop_notifications()),
                             "Notifications received")

    def test_handleStatus_DEPFAIL_notifies(self):
        return self._test_handleStatus_notifies("DEPFAIL")

    def test_handleStatus_CHROOTFAIL_notifies(self):
        return self._test_handleStatus_notifies("CHROOTFAIL")

    def test_handleStatus_PACKAGEFAIL_notifies(self):
        return self._test_handleStatus_notifies("PACKAGEFAIL")

    @defer.inlineCallbacks
    def test_handleStatus_ABORTED_cancels_cancelling(self):
        with dbuser(config.builddmaster.dbuser):
            self.build.updateStatus(BuildStatus.CANCELLING)
            yield self.behaviour.handleStatus(self.build.buildqueue_record,
                                              "ABORTED", {})
        self.assertEqual(0, len(pop_notifications()), "Notifications received")
        self.assertEqual(BuildStatus.CANCELLED, self.build.status)

    @defer.inlineCallbacks
    def test_handleStatus_ABORTED_illegal_when_building(self):
        self.builder.vm_host = "fake_vm_host"
        self.behaviour = self.interactor.getBuildBehaviour(
            self.build.buildqueue_record, self.builder, self.slave)
        with dbuser(config.builddmaster.dbuser):
            self.build.updateStatus(BuildStatus.BUILDING)
            with ExpectedException(
                    BuildDaemonError,
                    "Build returned unexpected status: u'ABORTED'"):
                yield self.behaviour.handleStatus(self.build.buildqueue_record,
                                                  "ABORTED", {})

    @defer.inlineCallbacks
    def test_handleStatus_ABORTED_cancelling_sets_build_log(self):
        # If a build is intentionally cancelled, the build log is set.
        self.assertEqual(None, self.build.log)
        with dbuser(config.builddmaster.dbuser):
            self.build.updateStatus(BuildStatus.CANCELLING)
            yield self.behaviour.handleStatus(self.build.buildqueue_record,
                                              "ABORTED", {})
        self.assertNotEqual(None, self.build.log)

    @defer.inlineCallbacks
    def test_date_finished_set(self):
        # The date finished is updated during handleStatus_OK.
        self.assertEqual(None, self.build.date_finished)
        with dbuser(config.builddmaster.dbuser):
            yield self.behaviour.handleStatus(
                self.build.buildqueue_record, 'OK',
                {'filemap': {
                    'myfile.py': 'test_file_hash'
                }})
        self.assertNotEqual(None, self.build.date_finished)

    @defer.inlineCallbacks
    def test_givenback_collection(self):
        with ExpectedException(
                BuildDaemonError,
                "Build returned unexpected status: u'GIVENBACK'"):
            with dbuser(config.builddmaster.dbuser):
                yield self.behaviour.handleStatus(self.build.buildqueue_record,
                                                  "GIVENBACK", {})

    @defer.inlineCallbacks
    def test_builderfail_collection(self):
        with ExpectedException(
                BuildDaemonError,
                "Build returned unexpected status: u'BUILDERFAIL'"):
            with dbuser(config.builddmaster.dbuser):
                yield self.behaviour.handleStatus(self.build.buildqueue_record,
                                                  "BUILDERFAIL", {})

    @defer.inlineCallbacks
    def test_invalid_status_collection(self):
        with ExpectedException(BuildDaemonError,
                               "Build returned unexpected status: u'BORKED'"):
            with dbuser(config.builddmaster.dbuser):
                yield self.behaviour.handleStatus(self.build.buildqueue_record,
                                                  "BORKED", {})
class TestHandleStatusMixin:
    """Tests for `IPackageBuild`s handleStatus method.

    This should be run with a Trial TestCase.
    """

    layer = LaunchpadZopelessLayer

    def makeBuild(self):
        """Allow classes to override the build with which the test runs."""
        raise NotImplementedError

    def setUp(self):
        super(TestHandleStatusMixin, self).setUp()
        self.factory = LaunchpadObjectFactory()
        self.build = self.makeBuild()
        # For the moment, we require a builder for the build so that
        # handleStatus_OK can get a reference to the slave.
        self.builder = self.factory.makeBuilder()
        self.build.buildqueue_record.markAsBuilding(self.builder)
        self.slave = WaitingSlave('BuildStatus.OK')
        self.slave.valid_file_hashes.append('test_file_hash')
        self.interactor = BuilderInteractor()
        self.behavior = self.interactor.getBuildBehavior(
            self.build.buildqueue_record, self.builder, self.slave)

        # We overwrite the buildmaster root to use a temp directory.
        tempdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, tempdir)
        self.upload_root = tempdir
        tmp_builddmaster_root = """
        [builddmaster]
        root: %s
        """ % self.upload_root
        config.push('tmp_builddmaster_root', tmp_builddmaster_root)

        # We stub out our builds getUploaderCommand() method so
        # we can check whether it was called as well as
        # verifySuccessfulUpload().
        removeSecurityProxy(self.build).verifySuccessfulUpload = FakeMethod(
            result=True)

    def assertResultCount(self, count, result):
        self.assertEquals(
            1, len(os.listdir(os.path.join(self.upload_root, result))))

    def test_handleStatus_OK_normal_file(self):
        # A filemap with plain filenames should not cause a problem.
        # The call to handleStatus will attempt to get the file from
        # the slave resulting in a URL error in this test case.
        def got_status(ignored):
            self.assertEqual(BuildStatus.UPLOADING, self.build.status)
            self.assertResultCount(1, "incoming")

        d = self.behavior.handleStatus(
            self.build.buildqueue_record, 'OK',
            {'filemap': {'myfile.py': 'test_file_hash'}})
        return d.addCallback(got_status)

    def test_handleStatus_OK_absolute_filepath(self):
        # A filemap that tries to write to files outside of
        # the upload directory will result in a failed upload.
        def got_status(ignored):
            self.assertEqual(BuildStatus.FAILEDTOUPLOAD, self.build.status)
            self.assertResultCount(0, "failed")
            self.assertIdentical(None, self.build.buildqueue_record)

        d = self.behavior.handleStatus(
            self.build.buildqueue_record, 'OK',
            {'filemap': {'/tmp/myfile.py': 'test_file_hash'}})
        return d.addCallback(got_status)

    def test_handleStatus_OK_relative_filepath(self):
        # A filemap that tries to write to files outside of
        # the upload directory will result in a failed upload.
        def got_status(ignored):
            self.assertEqual(BuildStatus.FAILEDTOUPLOAD, self.build.status)
            self.assertResultCount(0, "failed")

        d = self.behavior.handleStatus(
            self.build.buildqueue_record, 'OK',
            {'filemap': {'../myfile.py': 'test_file_hash'}})
        return d.addCallback(got_status)

    def test_handleStatus_OK_sets_build_log(self):
        # The build log is set during handleStatus.
        self.assertEqual(None, self.build.log)
        d = self.behavior.handleStatus(
            self.build.buildqueue_record, 'OK',
            {'filemap': {'myfile.py': 'test_file_hash'}})

        def got_status(ignored):
            self.assertNotEqual(None, self.build.log)

        return d.addCallback(got_status)

    def _test_handleStatus_notifies(self, status):
        # An email notification is sent for a given build status if
        # notifications are allowed for that status.

        expected_notification = (
            status in self.behavior.ALLOWED_STATUS_NOTIFICATIONS)

        def got_status(ignored):
            if expected_notification:
                self.failIf(
                    len(pop_notifications()) == 0,
                    "No notifications received")
            else:
                self.failIf(
                    len(pop_notifications()) > 0,
                    "Notifications received")

        d = self.behavior.handleStatus(
            self.build.buildqueue_record, status, {})
        return d.addCallback(got_status)

    def test_handleStatus_DEPFAIL_notifies(self):
        return self._test_handleStatus_notifies("DEPFAIL")

    def test_handleStatus_CHROOTFAIL_notifies(self):
        return self._test_handleStatus_notifies("CHROOTFAIL")

    def test_handleStatus_PACKAGEFAIL_notifies(self):
        return self._test_handleStatus_notifies("PACKAGEFAIL")

    def test_handleStatus_ABORTED_cancels_cancelling(self):
        self.build.updateStatus(BuildStatus.CANCELLING)

        def got_status(ignored):
            self.assertEqual(
                0, len(pop_notifications()), "Notifications received")
            self.assertEqual(BuildStatus.CANCELLED, self.build.status)

        d = self.behavior.handleStatus(
            self.build.buildqueue_record, "ABORTED", {})
        return d.addCallback(got_status)

    def test_handleStatus_ABORTED_recovers_building(self):
        self.builder.vm_host = "fake_vm_host"
        self.behavior = self.interactor.getBuildBehavior(
            self.build.buildqueue_record, self.builder, self.slave)
        self.build.updateStatus(BuildStatus.BUILDING)

        def got_status(ignored):
            self.assertEqual(
                0, len(pop_notifications()), "Notifications received")
            self.assertEqual(BuildStatus.NEEDSBUILD, self.build.status)
            self.assertEqual(1, self.builder.failure_count)
            self.assertEqual(1, self.build.failure_count)
            self.assertIn("clean", self.slave.call_log)

        d = self.behavior.handleStatus(
            self.build.buildqueue_record, "ABORTED", {})
        return d.addCallback(got_status)

    @defer.inlineCallbacks
    def test_handleStatus_ABORTED_cancelling_sets_build_log(self):
        # If a build is intentionally cancelled, the build log is set.
        self.assertEqual(None, self.build.log)
        self.build.updateStatus(BuildStatus.CANCELLING)
        yield self.behavior.handleStatus(
            self.build.buildqueue_record, "ABORTED", {})
        self.assertNotEqual(None, self.build.log)

    def test_date_finished_set(self):
        # The date finished is updated during handleStatus_OK.
        self.assertEqual(None, self.build.date_finished)
        d = self.behavior.handleStatus(
            self.build.buildqueue_record, 'OK',
            {'filemap': {'myfile.py': 'test_file_hash'}})

        def got_status(ignored):
            self.assertNotEqual(None, self.build.date_finished)

        return d.addCallback(got_status)