Beispiel #1
0
    def reconfigServiceBuilders(self, new_config):

        timer = metrics.Timer("BotMaster.reconfigServiceBuilders")
        timer.start()

        # arrange builders by name
        old_by_name = dict([(b.name, b)
                            for b in list(self)
                            if isinstance(b, Builder)])
        old_set = set(old_by_name)
        new_by_name = dict([(bc.name, bc)
                            for bc in new_config.builders])
        new_set = set(new_by_name)

        # calculate new builders, by name, and removed builders
        removed_names, added_names = util.diffSets(old_set, new_set)

        if removed_names or added_names:
            log.msg("adding %d new builders, removing %d" %
                    (len(added_names), len(removed_names)))

            for n in removed_names:
                builder = old_by_name[n]

                del self.builders[n]
                builder.master = None
                builder.botmaster = None

                # pylint: disable=cell-var-from-loop
                yield defer.maybeDeferred(lambda:
                                          builder.disownServiceParent())

            for n in added_names:
                builder = Builder(n)
                self.builders[n] = builder

                builder.botmaster = self
                builder.master = self.master
                yield builder.setServiceParent(self)

        self.builderNames = list(self.builders)

        yield self.master.data.updates.updateBuilderList(
            self.master.masterid,
            [util.ascii2unicode(n) for n in self.builderNames])

        metrics.MetricCountEvent.log("num_builders",
                                     len(self.builders), absolute=True)

        timer.stop()
Beispiel #2
0
    def reconfigServiceBuilders(self, new_config):

        timer = metrics.Timer("BotMaster.reconfigServiceBuilders")
        timer.start()

        # arrange builders by name
        old_by_name = {b.name: b
                       for b in list(self)
                       if isinstance(b, Builder)}
        old_set = set(old_by_name)
        new_by_name = {bc.name: bc
                       for bc in new_config.builders}
        new_set = set(new_by_name)

        # calculate new builders, by name, and removed builders
        removed_names, added_names = util.diffSets(old_set, new_set)

        if removed_names or added_names:
            log.msg("adding %d new builders, removing %d" %
                    (len(added_names), len(removed_names)))

            for n in removed_names:
                builder = old_by_name[n]

                del self.builders[n]
                builder.master = None
                builder.botmaster = None

                # pylint: disable=cell-var-from-loop
                yield defer.maybeDeferred(lambda:
                                          builder.disownServiceParent())

            for n in added_names:
                builder = Builder(n)
                self.builders[n] = builder

                builder.botmaster = self
                builder.master = self.master
                yield builder.setServiceParent(self)

        self.builderNames = list(self.builders)

        yield self.master.data.updates.updateBuilderList(
            self.master.masterid,
            [util.bytes2unicode(n) for n in self.builderNames])

        metrics.MetricCountEvent.log("num_builders",
                                     len(self.builders), absolute=True)

        timer.stop()
Beispiel #3
0
    def reconfigServiceBuilders(self, new_config):

        timer = metrics.Timer("BotMaster.reconfigServiceBuilders")
        timer.start()

        # arrange builders by name
        old_by_name = dict([ (b.name, b)
                            for b in list(self)
                            if isinstance(b, Builder) ])
        old_set = set(old_by_name.iterkeys())
        new_by_name = dict([ (bc.name, bc)
                            for bc in new_config.builders ])
        new_set = set(new_by_name.iterkeys())

        # calculate new builders, by name, and removed builders
        removed_names, added_names = util.diffSets(old_set, new_set)

        if removed_names or added_names:
            log.msg("adding %d new builders, removing %d" %
                    (len(added_names), len(removed_names)))

            for n in removed_names:
                builder = old_by_name[n]

                del self.builders[n]
                builder.master = None
                builder.botmaster = None

                wfd = defer.waitForDeferred(
                    defer.maybeDeferred(lambda :
                        builder.disownServiceParent()))
                yield wfd
                wfd.getResult()

            for n in added_names:
                builder = Builder(n)
                self.builders[n] = builder

                builder.botmaster = self
                builder.master = self.master
                builder.setServiceParent(self)

        self.builderNames = self.builders.keys()

        metrics.MetricCountEvent.log("num_builders",
                len(self.builders), absolute=True)

        timer.stop()
Beispiel #4
0
 def collapse_fn(master, builder, brdict1, brdict2):
     if not claimed:
         yield self.master.data.updates.claimBuildRequests([20])
         claimed.append(20)
     res = yield Builder._defaultCollapseRequestFn(
         master, builder, brdict1, brdict2)
     return res
Beispiel #5
0
def makeBuildStep(basedir, step_class=BuildStep, **kwargs):
    bss = setupBuildStepStatus(basedir)

    ss = SourceStamp()
    setup = {"name": "builder1", "slavename": "bot1", "builddir": "builddir", "factory": None}
    b0 = Builder(setup, bss.getBuild().getBuilder())
    b0.botmaster = FakeBotMaster()
    br = BuildRequest("reason", ss, "test_builder")
    b = Build([br])
    b.setBuilder(b0)
    s = step_class(**kwargs)
    s.setBuild(b)
    s.setStepStatus(bss)
    b.build_status = bss.getBuild()
    b.setupProperties()
    s.slaveVersion = fake_slaveVersion
    return s
Beispiel #6
0
def makeBuildStep(basedir, step_class=BuildStep, **kwargs):
    bss = setupBuildStepStatus(basedir)

    ss = SourceStamp()
    setup = {'name': "builder1", "slavename": "bot1",
             'builddir': "builddir", 'slavebuilddir': "slavebuilddir", 'factory': None}
    b0 = Builder(setup, bss.getBuild().getBuilder())
    b0.botmaster = FakeBotMaster()
    br = BuildRequest("reason", ss, 'test_builder')
    b = Build([br])
    b.setBuilder(b0)
    s = step_class(**kwargs)
    s.setBuild(b)
    s.setStepStatus(bss)
    b.build_status = bss.getBuild()
    b.setupProperties()
    s.slaveVersion = fake_slaveVersion
    s.step_status.setText(s.describe(False))
    return s
def makeBuildStep(basedir, step_class=BuildStep, **kwargs):
    bss = setupBuildStepStatus(basedir)

    ss = SourceStamp()
    setup = {
        'name': "builder1",
        "slavename": "bot1",
        'builddir': "builddir",
        'factory': None
    }
    b0 = Builder(setup, bss.getBuild().getBuilder())
    b0.botmaster = FakeBotMaster()
    br = BuildRequest("reason", ss)
    b = Build([br])
    b.setBuilder(b0)
    s = step_class(**kwargs)
    s.setBuild(b)
    s.setStepStatus(bss)
    b.build_status = bss.getBuild()
    b.setupProperties()
    s.slaveVersion = fake_slaveVersion
    return s
Beispiel #8
0
    def reconfigServiceBuilders(self, new_config):

        timer = metrics.Timer("BotMaster.reconfigServiceBuilders")
        timer.start()

        # arrange builders by name
        old_by_name = dict([(b.name, b) for b in list(self)
                            if isinstance(b, Builder)])
        old_set = set(old_by_name.iterkeys())
        new_by_name = dict([(bc.name, bc) for bc in new_config.builders])
        new_set = set(new_by_name.iterkeys())

        # calculate new builders, by name, and removed builders
        removed_names, added_names = util.diffSets(old_set, new_set)

        if removed_names or added_names:
            log.msg("adding %d new builders, removing %d" %
                    (len(added_names), len(removed_names)))

            for n in removed_names:
                builder = old_by_name[n]

                del self.builders[n]
                builder.master = None
                builder.botmaster = None

                yield defer.maybeDeferred(
                    lambda: builder.disownServiceParent())

            for n in added_names:
                builder = Builder(n)
                self.builders[n] = builder

                builder.botmaster = self
                builder.master = self.master
                builder.setServiceParent(self)

        self.builderNames = self.builders.keys()

        metrics.MetricCountEvent.log("num_builders",
                                     len(self.builders),
                                     absolute=True)

        # remove unclaimed builds if the builder has been removed from configuration
        if len(self.master.config.projects) > 1:
            queued_builds = yield self.master.db.buildrequests.getBuildRequestInQueue(
                sorted=True)
            # TODO: if we are running in multimaster mode with multiple instance of katana we need
            # to check for the project key as well
            removed_builders = [
                b for b in queued_builds if b["buildername"] not in new_set
            ]
            self.removeQueuedBuilds(removed_builders)

        timer.stop()
Beispiel #9
0
    def makeBuilder(self, klass):
        dummy_factory = klass([Dummy()])
        builder = {
            'name': 'b1',
            'slavenames': ['s1'],
            'builddir': 'b1',
            'slavebuilddir': 'b1',
            'factory': dummy_factory,
        }
        builder_status = mock.Mock()

        builder = Builder(builder, builder_status)
        builder.running = True
        builder.db = self.dbc
        builder.master_name = 'test_master'
        builder.master_incarnation = '12345'
        builder.botmaster = mock.Mock()
        builder.botmaster.shouldMergeRequests.return_value = True
        return builder
Beispiel #10
0
    def reconfigServiceBuilders(self, new_config):

        timer = metrics.Timer("BotMaster.reconfigServiceBuilders")
        timer.start()

        # arrange builders by name
        old_by_name = dict([(b.name, b) for b in list(self)
                            if isinstance(b, Builder)])
        old_set = set(old_by_name.iterkeys())
        new_by_name = dict([(bc.name, bc) for bc in new_config.builders])
        new_set = set(new_by_name.iterkeys())

        # calculate new builders, by name, and removed builders
        removed_names, added_names = util.diffSets(old_set, new_set)

        if removed_names or added_names:
            log.msg("adding %d new builders, removing %d" %
                    (len(added_names), len(removed_names)))

            for n in removed_names:
                builder = old_by_name[n]

                del self.builders[n]
                builder.master = None
                builder.botmaster = None

                wfd = defer.waitForDeferred(
                    defer.maybeDeferred(lambda: builder.disownServiceParent()))
                yield wfd
                wfd.getResult()

            for n in added_names:
                builder = Builder(n)
                self.builders[n] = builder

                builder.botmaster = self
                builder.master = self.master
                builder.setServiceParent(self)

        self.builderNames = self.builders.keys()

        metrics.MetricCountEvent.log("num_builders",
                                     len(self.builders),
                                     absolute=True)

        timer.stop()
Beispiel #11
0
    def makeBuilder(self, klass):
        dummy_factory = klass([Dummy()])
        builder = {
                'name': 'b1',
                'slavenames': ['s1'],
                'builddir': 'b1',
                'slavebuilddir': 'b1',
                'factory': dummy_factory,
                }
        builder_status = mock.Mock()

        builder = Builder(builder, builder_status)
        builder.running = True
        builder.db = self.dbc
        builder.master_name = 'test_master'
        builder.master_incarnation = '12345'
        builder.botmaster = mock.Mock()
        builder.botmaster.shouldMergeRequests.return_value = True
        return builder
    def makeBuilder(self, klass):
        dummy_factory = klass([Dummy()])
        builder = {
            "name": "b1",
            "slavenames": ["s1"],
            "builddir": "b1",
            "slavebuilddir": "b1",
            "factory": dummy_factory,
        }
        builder_status = mock.Mock()

        builder = Builder(builder, builder_status)
        builder.running = True
        builder.db = self.dbc
        builder.master_name = "test_master"
        builder.master_incarnation = "12345"
        builder.botmaster = mock.Mock()
        builder.botmaster.shouldMergeRequests.return_value = True
        return builder
Beispiel #13
0
    def loadConfig_Builders(self, newBuilderData):
        somethingChanged = False
        newList = {}
        newBuilderNames = []
        allBuilders = self.botmaster.builders.copy()
        for data in newBuilderData:
            name = data['name']
            newList[name] = data
            newBuilderNames.append(name)

        # identify all that were removed
        for oldname in self.botmaster.getBuildernames():
            if oldname not in newList:
                log.msg("removing old builder %s" % oldname)
                del allBuilders[oldname]
                somethingChanged = True
                # announce the change
                self.status.builderRemoved(oldname)

        # everything in newList is either unchanged, changed, or new
        for name, data in newList.items():
            old = self.botmaster.builders.get(name)
            basedir = data['builddir'] # used on both master and slave
            #name, slave, builddir, factory = data
            if not old: # new
                # category added after 0.6.2
                category = data.get('category', None)
                log.msg("adding new builder %s for category %s" %
                        (name, category))
                statusbag = self.status.builderAdded(name, basedir, category)
                builder = Builder(data, statusbag)
                allBuilders[name] = builder
                somethingChanged = True
            elif old.compareToSetup(data):
                # changed: try to minimize the disruption and only modify the
                # pieces that really changed
                diffs = old.compareToSetup(data)
                log.msg("updating builder %s: %s" % (name, "\n".join(diffs)))

                statusbag = old.builder_status
                statusbag.saveYourself() # seems like a good idea
                # TODO: if the basedir was changed, we probably need to make
                # a new statusbag
                new_builder = Builder(data, statusbag)
                new_builder.consumeTheSoulOfYourPredecessor(old)
                # that migrates any retained slavebuilders too

                # point out that the builder was updated. On the Waterfall,
                # this will appear just after any currently-running builds.
                statusbag.addPointEvent(["config", "updated"])

                allBuilders[name] = new_builder
                somethingChanged = True
            else:
                # unchanged: leave it alone
                log.msg("builder %s is unchanged" % name)
                pass

        if somethingChanged:
            sortedAllBuilders = [allBuilders[name] for name in newBuilderNames]
            d = self.botmaster.setBuilders(sortedAllBuilders)
            return d
        return None
    def loadConfig_Builders(self, newBuilderData):
        somethingChanged = False
        newList = {}
        newBuilderNames = []
        allBuilders = self.botmaster.builders.copy()
        for data in newBuilderData:
            name = data['name']
            newList[name] = data
            newBuilderNames.append(name)

        # identify all that were removed
        for oldname in self.botmaster.getBuildernames():
            if oldname not in newList:
                log.msg("removing old builder %s" % oldname)
                del allBuilders[oldname]
                somethingChanged = True
                # announce the change
                self.status.builderRemoved(oldname)

        # everything in newList is either unchanged, changed, or new
        for name, data in newList.items():
            old = self.botmaster.builders.get(name)
            basedir = data['builddir']  # used on both master and slave
            #name, slave, builddir, factory = data
            if not old:  # new
                # category added after 0.6.2
                category = data.get('category', None)
                log.msg("adding new builder %s for category %s" %
                        (name, category))
                statusbag = self.status.builderAdded(name, basedir, category)
                builder = Builder(data, statusbag)
                allBuilders[name] = builder
                somethingChanged = True
            elif old.compareToSetup(data):
                # changed: try to minimize the disruption and only modify the
                # pieces that really changed
                diffs = old.compareToSetup(data)
                log.msg("updating builder %s: %s" % (name, "\n".join(diffs)))

                statusbag = old.builder_status
                statusbag.saveYourself()  # seems like a good idea
                # TODO: if the basedir was changed, we probably need to make
                # a new statusbag
                new_builder = Builder(data, statusbag)
                new_builder.consumeTheSoulOfYourPredecessor(old)
                # that migrates any retained slavebuilders too

                # point out that the builder was updated. On the Waterfall,
                # this will appear just after any currently-running builds.
                statusbag.addPointEvent(["config", "updated"])

                allBuilders[name] = new_builder
                somethingChanged = True
            else:
                # unchanged: leave it alone
                log.msg("builder %s is unchanged" % name)
                pass

        if somethingChanged:
            sortedAllBuilders = [allBuilders[name] for name in newBuilderNames]
            d = self.botmaster.setBuilders(sortedAllBuilders)
            return d
        return None
class TestTrigger(steps.BuildStepMixin, unittest.TestCase):

    def setUp(self):
        return self.setUpBuildStep()

    def tearDown(self):
        return self.tearDownBuildStep()

    def setupStep(self, step, sourcestampsInBuild=None, gotRevisionsInBuild=None, *args, **kwargs):
        sourcestamps = sourcestampsInBuild or []
        got_revisions = gotRevisionsInBuild or {}

        steps.BuildStepMixin.setupStep(self, step, *args, **kwargs)

        # This step reaches deeply into a number of parts of Buildbot.  That
        # should be fixed!

        # set up a buildmaster that knows about two fake schedulers, a and b
        m = fakemaster.make_master()
        self.build.builder.botmaster = m.botmaster
        m.db = fakedb.FakeDBConnector(self)
        m.status = master.Status(m)
        m.config.buildbotURL = "baseurl/"

        self.scheduler_a = a = FakeTriggerable(name='a')
        self.scheduler_b = b = FakeTriggerable(name='b')
        def allSchedulers():
            return [ a, b ]
        m.allSchedulers = allSchedulers

        a.brids = {'A': 11}
        b.brids = {'B': 22}

        make_fake_br = lambda brid, name: fakedb.BuildRequest(id=brid,
                                                              buildsetid=BRID_TO_BSID(brid),
                                                              buildername=name)
        make_fake_build = lambda brid: fakedb.Build(brid=brid,
                                                    id=BRID_TO_BID(brid),
                                                    number=BRID_TO_BUILD_NUMBER(brid))

        m.db.insertTestData([
               make_fake_br(11, "A"),
               make_fake_br(22, "B"),
               make_fake_build(11),
               make_fake_build(22),
        ])

        def getAllSourceStamps():
            return sourcestamps
        self.build.getAllSourceStamps = getAllSourceStamps
        def getAllGotRevisions():
            return got_revisions
        self.build.build_status.getAllGotRevisions = getAllGotRevisions

        request = Mock()
        request.id = 1

        self.build.requests = [request]

        self.exp_add_sourcestamp = None
        self.exp_a_trigger = None
        self.exp_b_trigger = None
        self.exp_added_urls = []

    def runStep(self, expect_waitForFinish=False):
        d = steps.BuildStepMixin.runStep(self)

        if expect_waitForFinish:
            # the build doesn't finish until after a callLater, so this has the
            # effect of checking whether the deferred has been fired already;
            # it should not have been!
            early = []
            d.addCallback(early.append)
            self.assertEqual(early, [])

        def check(_):
            self.assertEqual(self.scheduler_a.triggered_with,
                             self.exp_a_trigger)
            self.assertEqual(self.scheduler_b.triggered_with,
                             self.exp_b_trigger)
            self.assertEqual(self.step_status.addURL.call_args_list,
                             self.exp_added_urls)

            if self.exp_add_sourcestamp:
                self.assertEqual(self.addSourceStamp_kwargs,
                                self.exp_add_sourcestamp)
        d.addCallback(check)

        # pause runStep's completion until after any other callLater's are done
        def wait(_):
            d = defer.Deferred()
            reactor.callLater(0, d.callback, None)
            return d
        d.addCallback(wait)

        return d

    def expectTriggeredWith(self, a=None, b=None):
        self.exp_a_trigger = a
        self.exp_b_trigger = b

    def expectAddedSourceStamp(self, **kwargs):
        self.exp_add_sourcestamp = kwargs

    def expectTriggeredLinks(self, *args):
        def get_args(sch, name):
            label = lambda name, num: "%s #%d" % (name, num)
            url = lambda name, num: "baseurl/builders/%s/builds/%d" % (name, num )

            num = BRID_TO_BUILD_NUMBER(sch.brids[name])

            #returns the *args and **kwargs that will be called on addURL...
            #   which is just addURL('label', 'url')
            return ( (label(name,num), url(name,num)) , {} )

        if 'a' in args:
            self.exp_added_urls.append(get_args(self.scheduler_a, 'A'))
        if 'b' in args:
            self.exp_added_urls.append(get_args(self.scheduler_b, 'B'))


    # tests

    def test_no_schedulerNames(self):
        self.assertRaises(config.ConfigErrors, lambda :
                trigger.Trigger())

    def test_sourceStamp_and_updateSourceStamp(self):
        self.assertRaises(config.ConfigErrors, lambda :
                trigger.Trigger(schedulerNames=['c'],
                    sourceStamp=dict(x=1), updateSourceStamp=True))

    def test_sourceStamps_and_updateSourceStamp(self):
        self.assertRaises(config.ConfigErrors, lambda :
                trigger.Trigger(schedulerNames=['c'],
                    sourceStamps=[dict(x=1), dict(x=2)],
                    updateSourceStamp=True))

    def test_updateSourceStamp_and_alwaysUseLatest(self):
        self.assertRaises(config.ConfigErrors, lambda :
                trigger.Trigger(schedulerNames=['c'],
                    updateSourceStamp=True, alwaysUseLatest=True))

    def test_sourceStamp_and_alwaysUseLatest(self):
        self.assertRaises(config.ConfigErrors, lambda :
                trigger.Trigger(schedulerNames=['c'],
                    sourceStamp=dict(x=1), alwaysUseLatest=True))

    def test_sourceStamps_and_alwaysUseLatest(self):
        self.assertRaises(config.ConfigErrors, lambda :
                trigger.Trigger(schedulerNames=['c'],
                    sourceStamps=[dict(x=1), dict(x=2)],
                    alwaysUseLatest=True))

    def test_simple(self):
        self.setupStep(trigger.Trigger(schedulerNames=['a'], sourceStamps = {}))
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'a'"])
        self.expectTriggeredWith(a=({}, {'stepname': ('Trigger', 'Trigger')}, 1))
        return self.runStep()

    def test_simple_failure(self):
        self.setupStep(trigger.Trigger(schedulerNames=['a']))
        self.scheduler_a.result = FAILURE
        # not waitForFinish, so trigger step succeeds even though the build
        # didn't fail
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'a'"])
        self.expectTriggeredWith(a=({}, {'stepname': ('Trigger', 'Trigger')}, 1))
        return self.runStep()

    @compat.usesFlushLoggedErrors
    def test_simple_exception(self):
        self.setupStep(trigger.Trigger(schedulerNames=['a']))
        self.scheduler_a.exception = True
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'a'"])
        self.expectTriggeredWith(a=({}, {'stepname': ('Trigger', 'Trigger')}, 1))
        d = self.runStep()
        def flush(_):
            self.assertEqual(len(self.flushLoggedErrors(RuntimeError)), 1)
        d.addCallback(flush)
        return d

    def test_bogus_scheduler(self):
        self.setupStep(trigger.Trigger(schedulerNames=['a', 'x']))
        self.expectOutcome(result=FAILURE, status_text=['not valid scheduler:', 'x'])
        self.expectTriggeredWith(a=None) # a is not triggered!
        return self.runStep()

    def test_updateSourceStamp(self):
        self.setupStep(trigger.Trigger(schedulerNames=['a'],
                                       updateSourceStamp=True),
                       sourcestampsInBuild = [FakeSourceStamp(codebase='',
                                                              repository='x',
                                                              revision=11111)
                                              ],
                       gotRevisionsInBuild = {'': 23456},
                       )
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'a'"])
        self.expectTriggeredWith(a=({'':{'codebase':'',
                                         'repository': 'x',
                                         'revision': 23456}
                                    }, {'stepname': ('Trigger', 'Trigger')}, 1))
        return self.runStep()

    def test_updateSourceStamp_no_got_revision(self):
        self.setupStep(trigger.Trigger(schedulerNames=['a'],
                                       updateSourceStamp=True),
                       sourcestampsInBuild = [FakeSourceStamp(codebase='',
                                                              repository='x',
                                                              revision=11111)
                                              ])
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'a'"])
        self.expectTriggeredWith(a=({'':{'codebase':'',
                                         'repository': 'x',
                                         'revision': 11111} # uses old revision
                                    }, {'stepname': ('Trigger', 'Trigger')}, 1))
        return self.runStep()

    def test_not_updateSourceStamp(self):
        self.setupStep(trigger.Trigger(schedulerNames=['a'],
                                       updateSourceStamp=False),
                       sourcestampsInBuild = [FakeSourceStamp(codebase='',
                                                              repository='x',
                                                              revision=11111)
                                              ],
                       gotRevisionsInBuild = {'': 23456},
                       )
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'a'"])
        self.expectTriggeredWith(a=({'':{'codebase':'',
                                          'repository': 'x',
                                          'revision': 11111}
                                    }, {'stepname': ('Trigger', 'Trigger')}, 1))
        return self.runStep()

    def test_updateSourceStamp_multiple_repositories(self):
        self.setupStep(trigger.Trigger(schedulerNames=['a'],
                                       updateSourceStamp=True),
                       sourcestampsInBuild = [
                                              FakeSourceStamp(codebase='cb1',
                                                              revision='12345'),
                                              FakeSourceStamp(codebase='cb2',
                                                              revision='12345')
                                              ],
                       gotRevisionsInBuild = {'cb1': 23456, 'cb2': 34567},
                       )
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'a'"])
        self.expectTriggeredWith(a=({'cb1': {'codebase':'cb1',
                                             'revision':23456},
                                     'cb2': {'codebase':'cb2',
                                             'revision':34567}
                                    }, {'stepname': ('Trigger', 'Trigger')}, 1))
        return self.runStep()

    def test_updateSourceStamp_prop_false(self):
        self.setupStep(trigger.Trigger(schedulerNames=['a'],
                               updateSourceStamp=properties.Property('usess')),
                       sourcestampsInBuild = [FakeSourceStamp(codebase='',
                                                              repository='x',
                                                              revision=11111)
                                             ],
                       gotRevisionsInBuild = {'': 23456},
                       )
        self.properties.setProperty('usess', False, 'me')
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'a'"])
        # didn't use got_revision
        self.expectTriggeredWith(a=({'': { 'codebase':'',
                                           'repository': 'x',
                                           'revision': 11111
                                    }}, {'stepname': ('Trigger', 'Trigger')}, 1))
        return self.runStep()

    def test_updateSourceStamp_prop_true(self):
        self.setupStep(trigger.Trigger(schedulerNames=['a'],
                               updateSourceStamp=properties.Property('usess')),
                       sourcestampsInBuild = [FakeSourceStamp(codebase='',
                                                              repository='x',
                                                              revision=11111)
                                             ],
                       gotRevisionsInBuild = {'': 23456},
                       )
        self.properties.setProperty('usess', True, 'me')
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'a'"])
        # didn't use got_revision
        self.expectTriggeredWith(a=({'': { 'codebase':'',
                                           'repository': 'x',
                                           'revision': 23456
                                    }}, {'stepname': ('Trigger', 'Trigger')}, 1))
        return self.runStep()

    def test_alwaysUseLatest(self):
        self.setupStep(trigger.Trigger(schedulerNames=['b'],
                                       alwaysUseLatest=True),
                       sourcestampsInBuild = [FakeSourceStamp(codebase='',
                                                              repository='x',
                                                              revision=11111)
                                             ])
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'b'"])
        # Do not pass setid
        self.expectTriggeredWith(b=({}, {'stepname': ('Trigger', 'Trigger')}, 1))
        return self.runStep()

    def test_alwaysUseLatest_prop_false(self):
        self.setupStep(trigger.Trigger(schedulerNames=['b'],
                                       alwaysUseLatest=properties.Property('aul')),
                       sourcestampsInBuild = [FakeSourceStamp(codebase='',
                                                              repository='x',
                                                              revision=11111)
                                              ])
        self.properties.setProperty('aul', False, 'me')
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'b'"])
        # didn't use latest
        self.expectTriggeredWith(b=({'': { 'codebase':'',
                                           'repository': 'x',
                                           'revision': 11111}
                                    }, {'stepname': ('Trigger', 'Trigger')}, 1))
        return self.runStep()

    def test_alwaysUseLatest_prop_true(self):
        self.setupStep(trigger.Trigger(schedulerNames=['b'],
                                       alwaysUseLatest=properties.Property('aul')),
                       sourcestampsInBuild = [FakeSourceStamp(codebase='',
                                                              repository='x',
                                                              revision=11111)
                                              ])
        self.properties.setProperty('aul', True, 'me')
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'b'"])
        # didn't use latest
        self.expectTriggeredWith(b=({}, {'stepname': ('Trigger', 'Trigger')}, 1))
        return self.runStep()

    def test_sourceStamp(self):
        ss = dict(revision=9876, branch='dev')
        self.setupStep(trigger.Trigger(schedulerNames=['b'],
            sourceStamp=ss))
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'b'"])
        self.expectTriggeredWith(b=({'': ss}, {'stepname': ('Trigger', 'Trigger')}, 1))
        return self.runStep()

    def test_set_of_sourceStamps(self):
        ss1 = dict(codebase='cb1', repository='r1', revision=9876, branch='dev')
        ss2 = dict(codebase='cb2',repository='r2', revision=5432, branch='dev')
        self.setupStep(trigger.Trigger(schedulerNames=['b'],
            sourceStamps=[ss1,ss2]))
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'b'"])
        self.expectTriggeredWith(b=({'cb1':ss1, 'cb2':ss2}, {'stepname': ('Trigger', 'Trigger')}, 1))
        return self.runStep()

    def test_set_of_sourceStamps_override_build(self):
        ss1 = dict(codebase='cb1', repository='r1', revision=9876, branch='dev')
        ss2 = dict(codebase='cb2',repository='r2', revision=5432, branch='dev')
        ss3 = FakeSourceStamp(codebase='cb3', repository='r3', revision=1234, branch='dev')
        ss4 = FakeSourceStamp(codebase='cb4',repository='r4', revision=2345, branch='dev')
        self.setupStep(trigger.Trigger(schedulerNames=['b'],
            sourceStamps=[ss1,ss2]), sourcestampsInBuild=[ss3, ss4])
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'b'"])
        self.expectTriggeredWith(b=({'cb1':ss1, 'cb2':ss2}, {'stepname': ('Trigger', 'Trigger')}, 1))
        return self.runStep()


    def test_sourceStamp_prop(self):
        self.setupStep(trigger.Trigger(schedulerNames=['b'],
            sourceStamp=dict(revision=properties.Property('rev'),
                branch='dev')))
        self.properties.setProperty('rev', 602, 'me')
        expected_ss = dict(revision=602, branch='dev')
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'b'"])
        self.expectTriggeredWith(b=({'': expected_ss}, {'stepname': ('Trigger', 'Trigger')}, 1))
        return self.runStep()

    def test_waitForFinish(self):
        self.setupStep(trigger.Trigger(schedulerNames=['a', 'b'],
            waitForFinish=True))
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'a'", "'b'"])
        self.expectTriggeredWith(
            a=({}, {'stepname': ('Trigger', 'Trigger')}, 1),
            b=({}, {'stepname': ('Trigger', 'Trigger')}, 1))
        self.expectTriggeredLinks('a','b')
        return self.runStep(expect_waitForFinish=True)

    def test_waitForFinish_failure(self):
        self.setupStep(trigger.Trigger(schedulerNames=['a'],
            waitForFinish=True))
        self.scheduler_a.result = FAILURE
        self.expectOutcome(result=DEPENDENCY_FAILURE, status_text=['Dependency failed to build.'])
        self.expectTriggeredWith(a=({}, {'stepname': ('Trigger', 'Trigger')}, 1))
        self.expectTriggeredLinks('a')
        return self.runStep(expect_waitForFinish=True)

    @compat.usesFlushLoggedErrors
    def test_waitForFinish_exception(self):
        self.setupStep(trigger.Trigger(schedulerNames=['a', 'b'],
            waitForFinish=True))
        self.scheduler_b.exception = True
        self.expectOutcome(result=DEPENDENCY_FAILURE,
                        status_text=['Dependency failed to build.'])
        self.expectTriggeredWith(
            a=({}, {'stepname': ('Trigger', 'Trigger')}, 1),
            b=({}, {'stepname': ('Trigger', 'Trigger')}, 1))
        self.expectTriggeredLinks('a') # b doesn't return a brid
        d = self.runStep(expect_waitForFinish=True)
        def flush(_):
            self.assertEqual(len(self.flushLoggedErrors(RuntimeError)), 1)
        d.addCallback(flush)
        return d

    def test_set_properties(self):
        self.setupStep(trigger.Trigger(schedulerNames=['a'],
                set_properties=dict(x=1, y=2)))
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'a'"])
        self.expectTriggeredWith(a=({},
                                dict(stepname=('Trigger', 'Trigger'),
                                     x=(1, 'Trigger'), y=(2, 'Trigger')), 1))
        return self.runStep()

    def test_set_properties_prop(self):
        self.setupStep(trigger.Trigger(schedulerNames=['a'],
                set_properties=dict(x=properties.Property('X'), y=2)))
        self.properties.setProperty('X', 'xxx', 'here')
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'a'"])
        self.expectTriggeredWith(a=({},
                                    dict(stepname=('Trigger', 'Trigger'),
                                         x=('xxx', 'Trigger'),
                                         y=(2, 'Trigger')), 1))
        return self.runStep()

    def test_copy_properties(self):
        self.setupStep(trigger.Trigger(schedulerNames=['a'],
                copy_properties=['a', 'b']))
        self.properties.setProperty('a', 'A', 'AA')
        self.properties.setProperty('b', 'B', 'BB')
        self.properties.setProperty('c', 'C', 'CC')
        self.expectOutcome(result=SUCCESS, status_text=['Triggered:', "'a'"])
        self.expectTriggeredWith(a=({},
                            dict(a=('A', 'Trigger'),
                                 b=('B', 'Trigger'),
                                 stepname=('Trigger', 'Trigger')), 1))
        return self.runStep()

    def test_interrupt(self):
        self.setupStep(trigger.Trigger(schedulerNames=['a'],
            waitForFinish=True))
        self.expectOutcome(result=INTERRUPTED, status_text=['Trigger', '(build was interrupted)'])
        self.expectTriggeredWith(a=({}, {'stepname': ('Trigger', 'Trigger')}, 1))
        d = self.runStep(expect_waitForFinish=True)

        # interrupt before the callLater representing the Triggerable
        # schedulers completes
        self.step.interrupt(failure.Failure(RuntimeError('oh noes')))

        return d

    @defer.inlineCallbacks
    def test_interruptConnectionLostShouldRetry(self):
        self.master = fakemaster.FakeMaster()
        self.master.maybeStartBuildsForSlave = lambda slave: True

        self.master.db = fakedb.FakeDBConnector(self)

        rows = [fakedb.SourceStampSet(id=1),
                fakedb.SourceStamp(id=1, sourcestampsetid=1, codebase='c', branch="az", repository="xz", revision="ww"),
                fakedb.Buildset(id=1, reason='because', sourcestampsetid=1),
                fakedb.BuildRequest(id=1, buildsetid=1, buildername="bldr", submitted_at=130000)]

        yield self.master.db.insertTestData(rows)

        self.master.status = master.Status(self.master)
        self.master.config.buildbotURL = "baseurl/"

        self.scheduler_a = a = FakeTriggerable(name='a')
        self.scheduler_b = b = FakeTriggerable(name='b')
        def allSchedulers():
            return [a, b]
        self.master.allSchedulers = allSchedulers

        self.factory = factory.BuildFactory()
        self.step = trigger.Trigger(schedulerNames=['a'], waitForFinish=True)
        self.step.addCompleteLog = lambda x,y: True


        self.factory.addStep(self.step)
        config_args = dict(name="bldr", slavename="slv", builddir="bdir",
                     slavebuilddir="sbdir", project='default', factory=self.factory)
        builder_config = config.BuilderConfig(**config_args)

        self.bldr = Builder(builder_config.name, _addServices=False)
        self.bldr.master = self.master
        self.bldr.botmaster = self.master.botmaster

        mastercfg = config.MasterConfig()
        mastercfg.builders = [ builder_config ]

        self.bldr.startService()
        yield self.bldr.reconfigService(mastercfg)

        def newBuild(buildrequests):
            self.build = Build(buildrequests)
            self.build.setStepFactories([fakebuild.FakeStepFactory(self.step)])
            return self.build

        self.bldr.config.factory.newBuild = newBuild

        self.bldr.notifyRequestsRemoved = lambda x: True

        sb = Mock(spec=['isAvailable'], name='test-slave-1')
        sb.name = 'test-slave-1'
        sb.isAvailable.return_value = 1
        sb.slave = Mock()
        sb.slave.updateStatusBuildFinished = lambda result, build: result
        sb.slave.properties = properties.Properties()
        sb.prepare = lambda x, y: True
        sb.ping = lambda timeout: True
        sb.buildStarted = lambda: True
        sb.buildFinished = lambda _: False
        sb.setSlaveIdle = lambda: False
        sb.remote = Mock()
        self.bldr.slaves.append(sb)

        self.assertEqual(self.master.db.buildrequests.claims, {})
        from buildbot.process import buildrequestdistributor
        self.brd = buildrequestdistributor.BuildRequestDistributor(self.master.botmaster)
        self.brd.startService()

        yield self.brd._maybeStartBuildsOnBuilder(self.bldr)

        self.assertEqual(self.master.db.buildrequests.claims[1].brid, 1)
        self.build.build_status.saveYourself = lambda: True
        self.build.currentStep.start()
        self.build.lostRemote()
        self.assertEqual(self.master.db.buildrequests.claims, {})

        if self.brd.running:
            self.brd.stopService()

    @defer.inlineCallbacks
    def test_TriggerStepMultiMasterMode(self):
        m = fakemaster.FakeMaster()
        m.maybeStartBuildsForSlave = lambda slave: True
        m.status = master.Status(m)
        m.config.buildbotURL = "baseurl/"
        m.config.multiMaster = True
        m.db = fakedb.FakeDBConnector(self)

        scheduler_a = FakeTriggerable(name='a')

        m.allSchedulers = lambda: [scheduler_a]

        def trigger_sch(sourcestamps = None, set_props=None, triggeredbybrid=None, reason=None):
            rows = [ fakedb.MasterConfig(id=1,  buildbotURL="build-master-01/", objectid=1),
                     fakedb.MasterConfig(id=2, buildbotURL="build-master-02/", objectid=2),
                     fakedb.SourceStampSet(id=1),
                     fakedb.SourceStamp(id=1, sourcestampsetid=1, codebase='c',
                                        branch="az", repository="xz", revision="ww"),
                     fakedb.Buildset(id=1, reason='because', sourcestampsetid=1),
                     fakedb.BuildRequest(id=1, buildsetid=1, buildername="builder1", submitted_at=130000),
                     fakedb.BuildRequest(id=2, buildsetid=1, buildername="builder2", submitted_at=130000),
                     fakedb.BuildRequestClaim(brid=1, objectid=2, claimed_at=130000),
                     fakedb.BuildRequestClaim(brid=2, objectid=1, claimed_at=130000),
                     fakedb.Build(id=1, number=1, brid=1),
                     fakedb.Build(id=2, number=1, brid=2)]

            d = m.db.insertTestData(rows)
            d.addCallback(lambda _: (SUCCESS,  {'builder1': 1L, 'builder2': 2L}))
            return d

        scheduler_a.trigger = trigger_sch
        self.step = trigger.Trigger(schedulerNames=['a'], waitForFinish=True)
        self.step.addCompleteLog = lambda x,y: True
        self.step.step_status = Mock()
        self.step.step_status.getLogs = lambda: []
        self.expected_urls = []
        self.step.step_status.addURL = lambda text, path, results=None: \
            self.expected_urls.append({'text': text, 'path': path})
        self.step.build = fakebuild.FakeBuild()
        self.step.build.builder.botmaster = m.botmaster

        self.step.build.getAllSourceStamps = lambda: []
        self.step.build.build_status.getAllGotRevisions = lambda: {}
        request = Mock()
        request.id = 1
        self.step.build.requests = [request]

        self.remote = Mock(name="SlaveBuilder(remote)")
        yield self.step.startStep(self.remote)

        self.assertEquals(len(self.expected_urls), 2)
        self.assertTrue({'text': 'builder1 #1', 'path': 'build-master-02/builders/builder1/builds/1'}
                         in self.expected_urls)
        self.assertTrue({'text': 'builder1 #1', 'path': 'build-master-02/builders/builder1/builds/1'}
                        in self.expected_urls)
    def test_interruptConnectionLostShouldRetry(self):
        self.master = fakemaster.FakeMaster()
        self.master.maybeStartBuildsForSlave = lambda slave: True

        self.master.db = fakedb.FakeDBConnector(self)

        rows = [fakedb.SourceStampSet(id=1),
                fakedb.SourceStamp(id=1, sourcestampsetid=1, codebase='c', branch="az", repository="xz", revision="ww"),
                fakedb.Buildset(id=1, reason='because', sourcestampsetid=1),
                fakedb.BuildRequest(id=1, buildsetid=1, buildername="bldr", submitted_at=130000)]

        yield self.master.db.insertTestData(rows)

        self.master.status = master.Status(self.master)
        self.master.config.buildbotURL = "baseurl/"

        self.scheduler_a = a = FakeTriggerable(name='a')
        self.scheduler_b = b = FakeTriggerable(name='b')
        def allSchedulers():
            return [a, b]
        self.master.allSchedulers = allSchedulers

        self.factory = factory.BuildFactory()
        self.step = trigger.Trigger(schedulerNames=['a'], waitForFinish=True)
        self.step.addCompleteLog = lambda x,y: True


        self.factory.addStep(self.step)
        config_args = dict(name="bldr", slavename="slv", builddir="bdir",
                     slavebuilddir="sbdir", project='default', factory=self.factory)
        builder_config = config.BuilderConfig(**config_args)

        self.bldr = Builder(builder_config.name, _addServices=False)
        self.bldr.master = self.master
        self.bldr.botmaster = self.master.botmaster

        mastercfg = config.MasterConfig()
        mastercfg.builders = [ builder_config ]

        self.bldr.startService()
        yield self.bldr.reconfigService(mastercfg)

        def newBuild(buildrequests):
            self.build = Build(buildrequests)
            self.build.setStepFactories([fakebuild.FakeStepFactory(self.step)])
            return self.build

        self.bldr.config.factory.newBuild = newBuild

        self.bldr.notifyRequestsRemoved = lambda x: True

        sb = Mock(spec=['isAvailable'], name='test-slave-1')
        sb.name = 'test-slave-1'
        sb.isAvailable.return_value = 1
        sb.slave = Mock()
        sb.slave.updateStatusBuildFinished = lambda result, build: result
        sb.slave.properties = properties.Properties()
        sb.prepare = lambda x, y: True
        sb.ping = lambda timeout: True
        sb.buildStarted = lambda: True
        sb.buildFinished = lambda _: False
        sb.setSlaveIdle = lambda: False
        sb.remote = Mock()
        self.bldr.slaves.append(sb)

        self.assertEqual(self.master.db.buildrequests.claims, {})
        from buildbot.process import buildrequestdistributor
        self.brd = buildrequestdistributor.BuildRequestDistributor(self.master.botmaster)
        self.brd.startService()

        yield self.brd._maybeStartBuildsOnBuilder(self.bldr)

        self.assertEqual(self.master.db.buildrequests.claims[1].brid, 1)
        self.build.build_status.saveYourself = lambda: True
        self.build.currentStep.start()
        self.build.lostRemote()
        self.assertEqual(self.master.db.buildrequests.claims, {})

        if self.brd.running:
            self.brd.stopService()