From 87ca041d50a3f17bdfda1593244000ffb4110c3a Mon Sep 17 00:00:00 2001 From: Harry Borkhuis Date: Fri, 25 May 2012 14:41:56 +0200 Subject: [PATCH] Trigger step composes a set sourcestamps for the Triggerable scheduler. Scheduler merges this set with its configuration (codebases) and creates new buildset. --- master/buildbot/process/build.py | 6 +- master/buildbot/schedulers/triggerable.py | 42 +--- master/buildbot/sourcestamp.py | 9 +- master/buildbot/steps/trigger.py | 96 +++++--- .../test/unit/test_schedulers_triggerable.py | 178 ++++---------- .../buildbot/test/unit/test_steps_trigger.py | 230 ++++++++++++------ 6 files changed, 290 insertions(+), 271 deletions(-) diff --git a/master/buildbot/process/build.py b/master/buildbot/process/build.py index fb16596b063..ad55e7d6874 100644 --- a/master/buildbot/process/build.py +++ b/master/buildbot/process/build.py @@ -102,7 +102,11 @@ def getSourceStampSetId(self): return self.sources[0].sourcestampsetid else: return None - + + def getAllSourceStamps(self): + for s in self.sources: + yield s + def allChanges(self): for s in self.sources: for c in s.changes: diff --git a/master/buildbot/schedulers/triggerable.py b/master/buildbot/schedulers/triggerable.py index 5f3715b1579..52d2ba08739 100644 --- a/master/buildbot/schedulers/triggerable.py +++ b/master/buildbot/schedulers/triggerable.py @@ -30,10 +30,8 @@ def __init__(self, name, builderNames, properties={}, **kwargs): self._bsc_subscription = None self.reason = "Triggerable(%s)" % name - def trigger(self, ss_setid, sourcestamps = None, got_revision = None, - set_props=None): - """Trigger this scheduler with the given sourcestampset ID, optionally - a set of sourcestamps and a dictionary with got_revision entries. + def trigger(self, sourcestamps = None, set_props=None): + """Trigger this scheduler with the optional given list of sourcestamps Returns a deferred that will fire when the buildset is finished.""" # properties for this buildset are composed of our own properties, # potentially overridden by anything from the triggering build @@ -45,11 +43,7 @@ def trigger(self, ss_setid, sourcestamps = None, got_revision = None, # note that this does not use the buildset subscriptions mechanism, as # the duration of interest to the caller is bounded by the lifetime of # this process. - if ss_setid or sourcestamps or got_revision: - d = self._addBuildsetForTrigger(self.reason, ss_setid, sourcestamps, - got_revision, props) - else: - d = self.addBuildsetForLatest(reason=self.reason, properties=props) + d = self._addBuildsetForTrigger(self.reason, sourcestamps, props) def setup_waiter((bsid,brids)): d = defer.Deferred() self._waiters[bsid] = (d, brids) @@ -74,42 +68,20 @@ def stopService(self): return base.BaseScheduler.stopService(self) @defer.inlineCallbacks - def _addBuildsetForTrigger(self, reason, setid, sourcestamps, - got_revision, properties): + def _addBuildsetForTrigger(self, reason, sourcestamps, properties): - def createLookup(input_list, key): - output_dict = {} - for item in input_list: - output_dict[item[key]] = item - return output_dict - - if got_revision is None: - got_revision = {} if sourcestamps is None: - sourcestamps = [] - - # Create lookup for sourcestamps that exist in database - existing_lookup = {} - if setid: - existing_list = yield self.master.db.sourcestamps.getSourceStamps(setid) - existing_lookup = createLookup(existing_list, 'codebase') - - # Create lookup for sourcestamps that where passed - passed_lookup = createLookup(sourcestamps, 'codebase') + sourcestamps = {} # Define new setid for this set of triggering sourcestamps new_setid = yield self.master.db.sourcestampsets.addSourceStampSet() - # Merge codebases with the passed setid, sourcestamps and got_revision + # Merge codebases with the passed list of sourcestamps # This results in a new sourcestamp for each codebase for codebase in self.codebases: ss = self.codebases[codebase].copy() # apply info from setid - ss.update(existing_lookup.get(codebase,{})) - # apply info from got_revision - ss['revision'] = got_revision.get(codebase, ss.get('revision', None)) - # apply info from passed sourcestamp - ss.update(passed_lookup.get(codebase,{})) + ss.update(sourcestamps.get(codebase,{})) # at least repository must be set, this is normaly forced except when # codebases is not explicitly set in configuration file. diff --git a/master/buildbot/sourcestamp.py b/master/buildbot/sourcestamp.py index 8e88fb991e0..59a447699ee 100644 --- a/master/buildbot/sourcestamp.py +++ b/master/buildbot/sourcestamp.py @@ -248,12 +248,19 @@ def getText(self): text.append("[patch]") return text - def asDict(self): + def asDict(self, includePatch = False): result = {} # Constant result['revision'] = self.revision + # TODO(maruel): Make the patch content a suburl. result['hasPatch'] = self.patch is not None + if self.patch and includePatch: + result['patch_level'] = self.patch[0] + result['patch_body'] = self.patch[1] + result['patch_author'] = self.patch_info[0] + result['patch_comment'] = self.patch_info[1] + result['branch'] = self.branch result['changes'] = [c.asDict() for c in getattr(self, 'changes', [])] result['project'] = self.project diff --git a/master/buildbot/steps/trigger.py b/master/buildbot/steps/trigger.py index 128f07b7289..4b15c83cbe0 100644 --- a/master/buildbot/steps/trigger.py +++ b/master/buildbot/steps/trigger.py @@ -79,64 +79,95 @@ def end(self, result): self.ended = True return self.finished(result) - @defer.inlineCallbacks - def start(self): + # Create the properties that are used for the trigger + def createTriggerProperties(self): properties = self.build.getProperties() # make a new properties object from a dict rendered by the old # properties object - props_to_set = Properties() - props_to_set.update(self.set_properties, "Trigger") + trigger_properties = Properties() + trigger_properties.update(self.set_properties, "Trigger") for p in self.copy_properties: if p not in properties: continue - props_to_set.setProperty(p, properties[p], + trigger_properties.setProperty(p, properties[p], "%s (in triggering build)" % properties.getPropertySource(p)) + return trigger_properties - self.running = True - - # (is there an easier way to find the BuildMaster?) + # Get all scheduler instances that were configured + # A tuple of (triggerables, invalidnames) is returned + def getSchedulers(self): all_schedulers = self.build.builder.botmaster.parent.allSchedulers() all_schedulers = dict([(sch.name, sch) for sch in all_schedulers]) - unknown_schedulers = [] + invalid_schedulers = [] triggered_schedulers = [] - # don't fire any schedulers if we discover an unknown one for scheduler in self.schedulerNames: scheduler = scheduler if all_schedulers.has_key(scheduler): sch = all_schedulers[scheduler] if isinstance(sch, Triggerable): - triggered_schedulers.append(scheduler) + triggered_schedulers.append(sch) else: - unknown_schedulers.append(scheduler) + invalid_schedulers.append(scheduler) else: - unknown_schedulers.append(scheduler) - - if unknown_schedulers: - self.step_status.setText(['no scheduler:'] + unknown_schedulers) - self.end(FAILURE) - return - - master = self.build.builder.botmaster.parent # seriously?! - sourceStamps = self.sourceStamps - got = None - if self.alwaysUseLatest: - ss_setid = None - else: - ss_setid = self.build.getSourceStampSetId() - if self.updateSourceStamp: + invalid_schedulers.append(scheduler) + + return (triggered_schedulers, invalid_schedulers) + + def prepareSourcestampListForTrigger(self): + # start with the sourcestamps from current build + ss_for_trigger = {} + objs_from_build = self.build.getAllSourceStamps() + for ss in objs_from_build: + ss_for_trigger[ss.codebase] = ss.asDict(includePatch = True) + if self.alwaysUseLatest: + # Reset revision so latest version will be requested from vcs + ss_for_trigger[ss.codebase]['revision'] = None + + # overrule revision in sourcestamps with got revision + if self.updateSourceStamp and not self.alwaysUseLatest: + properties = self.build.getProperties() got = properties.getProperty('got_revision') # be sure property is always a dictionary if got and not isinstance(got, dict): got = {'': got} + for codebase in ss_for_trigger: + if codebase in got: + ss_for_trigger[codebase]['revision'] = got[codebase] + + # update sourcestamps from build with passed set of fixed sourcestamps + # or add fixed sourcestamp to the dictionary + for ss in self.sourceStamps: + codebase = ss.get('codebase','') + if codebase in ss_for_trigger: + ss_for_trigger[codebase].update(ss) + else: + ss_for_trigger[codebase] = ss + + return ss_for_trigger + + @defer.inlineCallbacks + def start(self): + # Get all triggerable schedulers and check if there are invalid schedules + (triggered_schedulers, invalid_schedulers) = self.getSchedulers() + if invalid_schedulers: + self.step_status.setText(['not valid scheduler:'] + invalid_schedulers) + self.end(FAILURE) + return + + self.running = True + + props_to_set = self.createTriggerProperties() + + ss_for_trigger = self.prepareSourcestampListForTrigger() dl = [] - for scheduler in triggered_schedulers: - sch = all_schedulers[scheduler] - dl.append(sch.trigger(ss_setid, set_props=props_to_set, - got_revision=got, sourcestamps=sourceStamps)) - self.step_status.setText(['triggered'] + triggered_schedulers) + triggered_names = [] + for sch in triggered_schedulers: + dl.append(sch.trigger(ss_for_trigger, set_props=props_to_set)) + triggered_names.append(sch.name) + self.step_status.setText(['triggered'] + triggered_names) if self.waitForFinish: rclist = yield defer.DeferredList(dl, consumeErrors=1) @@ -172,6 +203,7 @@ def start(self): result = SUCCESS if brids: + master = self.build.builder.botmaster.parent def add_links(res): # reverse the dictionary lookup for brid to builder name brid_to_bn = dict((_brid,_bn) for _bn,_brid in brids.iteritems()) diff --git a/master/buildbot/test/unit/test_schedulers_triggerable.py b/master/buildbot/test/unit/test_schedulers_triggerable.py index 07051bb74e4..e7ee2a9c0d5 100644 --- a/master/buildbot/test/unit/test_schedulers_triggerable.py +++ b/master/buildbot/test/unit/test_schedulers_triggerable.py @@ -46,12 +46,6 @@ def makeScheduler(self, **kwargs): def test_trigger(self): sched = self.makeScheduler(codebases = {'cb':{'repository':'r'}}) - self.db.insertTestData([ - fakedb.SourceStampSet(id=100), - fakedb.SourceStamp(id=91, sourcestampsetid=100, revision='myrev', branch='br', - project='p', repository='r', codebase='cb'), - ]) - # no subscription should be in place yet callbacks = self.master.getSubscriptionCallbacks() self.assertEqual(callbacks['buildset_completion'], None) @@ -59,7 +53,12 @@ def test_trigger(self): # trigger the scheduler, exercising properties while we're at it set_props = properties.Properties() set_props.setProperty('pr', 'op', 'test') - d = sched.trigger(100, set_props=set_props) + ss = {'revision':'myrev', + 'branch':'br', + 'project':'p', + 'repository':'r', + 'codebase':'cb' } + d = sched.trigger({'cb': ss}, set_props=set_props) bsid = self.db.buildsets.assertBuildset('?', dict(external_idstring=None, @@ -68,10 +67,10 @@ def test_trigger(self): ('scheduler', ('n', 'Scheduler')), ], reason='Triggerable(n)', - sourcestampsetid=101), + sourcestampsetid=100), {'cb': dict(branch='br', project='p', repository='r', - codebase='cb', revision='myrev', sourcestampsetid=101) + codebase='cb', revision='myrev', sourcestampsetid=100) }) # set up a boolean so that we can know when the deferred fires @@ -106,41 +105,45 @@ def fired((result, brids)): def test_trigger_overlapping(self): sched = self.makeScheduler(codebases = {'cb':{'repository':'r'}}) - self.db.insertTestData([ - fakedb.SourceStampSet(id=100), - fakedb.SourceStampSet(id=101), - fakedb.SourceStamp(id=91, sourcestampsetid=100, revision='myrev1', - branch='br', project='p', repository='r', codebase='cb'), - fakedb.SourceStamp(id=92, sourcestampsetid=101, revision='myrev2', - branch='br', project='p', repository='r', codebase='cb'), - ]) # no subscription should be in place yet callbacks = self.master.getSubscriptionCallbacks() self.assertEqual(callbacks['buildset_completion'], None) + # define sourcestamp + ss = {'revision':'myrev1', + 'branch':'br', + 'project':'p', + 'repository':'r', + 'codebase':'cb' } # trigger the scheduler the first time - d = sched.trigger(100) + d = sched.trigger({'cb':ss}) bsid1 = self.db.buildsets.assertBuildset('?', dict(external_idstring=None, properties=[('scheduler', ('n', 'Scheduler'))], reason='Triggerable(n)', - sourcestampsetid=102), + sourcestampsetid=100), {'cb': dict(branch='br', project='p', repository='r', codebase='cb', - revision='myrev1', sourcestampsetid=102)}) + revision='myrev1', sourcestampsetid=100)}) d.addCallback(lambda (res, brids) : self.assertEqual(res, 11) and self.assertEqual(brids, self.db.buildsets.allBuildRequests(bsid1))) + # define sourcestamp + ss = {'revision':'myrev2', + 'branch':'br', + 'project':'p', + 'repository':'r', + 'codebase':'cb' } # and the second time - d = sched.trigger(101) + d = sched.trigger({'cb':ss}) bsid2 = self.db.buildsets.assertBuildset(bsid1+1, # assumes bsid's are sequential dict(external_idstring=None, properties=[('scheduler', ('n', 'Scheduler'))], - reason='Triggerable(n)', sourcestampsetid=103), + reason='Triggerable(n)', sourcestampsetid=101), {'cb': dict(branch='br', project='p', repository='r', codebase='cb', - revision='myrev2', sourcestampsetid=103)}) + revision='myrev2', sourcestampsetid=101)}) d.addCallback(lambda (res, brids) : self.assertEqual(res, 22) and self.assertEqual(brids, self.db.buildsets.allBuildRequests(bsid2))) @@ -159,127 +162,52 @@ def test_trigger_overlapping(self): callbacks = self.master.getSubscriptionCallbacks() self.assertEqual(callbacks['buildset_completion'], None) - def test_trigger_with_fixed_sourcestamps(self): - # Test a scheduler with 4 repositories. - # Trigger the scheduler with a build containing repositories 1 and 2 - # and pass a set of fixed sourcestamps with repositories 1 and 3 + def test_trigger_with_unknown_sourcestamp(self): + # Test a scheduler with 2 repositories. + # Trigger the scheduler with a sourcestamp that is unknown to the scheduler # Expected Result: - # sourcestamp 1 for repository 1 based on fixed sourcestamp - # sourcestamp 2 for repository 2 based on sourcestamp from build - # sourcestamp 3 for repository 3 based on fixed sourcestamp - # sourcestamp 4 for repository 4 based on configured sourcestamp + # sourcestamp 1 for repository 1 based on configured sourcestamp + # sourcestamp 2 for repository 2 based on configured sourcestamp sched = self.makeScheduler( - codebases = {'cb':{'repository':'r', 'branch': 'branchX'}, - 'cb2':{'repository':'r2', 'branch': 'branchX'}, - 'cb3':{'repository':'r3', 'branch': 'branchX'}, - 'cb4':{'repository':'r4', 'branch': 'branchX'},}) - - self.db.insertTestData([ - fakedb.SourceStampSet(id=100), - fakedb.SourceStamp(id=91, sourcestampsetid=100, revision='myrev1', - branch='br', project='p', repository='r', codebase='cb'), - fakedb.SourceStamp(id=92, sourcestampsetid=100, revision='myrev2', - branch='br', project='p', repository='r2', codebase='cb2'), - ]) + codebases = {'cb':{'repository':'r', 'branch': 'branchX'}, + 'cb2':{'repository':'r2', 'branch': 'branchX'},}) - ss1 = {'repository': 'r', 'codebase': 'cb', 'revision': 'fixrev1', - 'branch': 'default', 'project': 'p' } - ss2 = {'repository': 'r3', 'codebase': 'cb3', 'revision': 'fixrev3', + ss = {'repository': 'r3', 'codebase': 'cb3', 'revision': 'fixrev3', 'branch': 'default', 'project': 'p' } - d = sched.trigger(100, sourcestamps = [ss1, ss2]) + d = sched.trigger(sourcestamps = {'cb3': ss}) self.db.buildsets.assertBuildset('?', dict(external_idstring=None, properties=[('scheduler', ('n', 'Scheduler'))], reason='Triggerable(n)', - sourcestampsetid=101), + sourcestampsetid=100), {'cb': - dict(branch='default', project='p', repository='r', codebase='cb', - revision='fixrev1', sourcestampsetid=101), + dict(branch='branchX', project='', repository='r', codebase='cb', + revision=None, sourcestampsetid=100), 'cb2': - dict(branch='br', project='p', repository='r2', codebase='cb2', - revision='myrev2', sourcestampsetid=101), - 'cb3': - dict(branch='default', project='p', repository='r3', codebase='cb3', - revision='fixrev3', sourcestampsetid=101), - 'cb4': - dict(branch='branchX', project='', repository='r4', codebase='cb4', - revision=None, sourcestampsetid=101)}) + dict(branch='branchX', project='', repository='r2', codebase='cb2', + revision=None, sourcestampsetid=100),}) - def test_trigger_with_got_revision(self): - # Test a scheduler with 2 repositories. - # Trigger the scheduler with a build containing repositories 1 and 2 - # and pass a set of got revision values for repository 1 and 2 + def test_trigger_without_sourcestamps(self): + # Test a scheduler with 2 repositories. + # Trigger the scheduler without a sourcestamp # Expected Result: - # sourcestamp 1 for repo 1 based on sourcestamp from build, other revision - # sourcestamp 2 for repo 2 based on sourcestamp from build, other revision + # sourcestamp 1 for repository 1 based on configured sourcestamp + # sourcestamp 2 for repository 2 based on configured sourcestamp sched = self.makeScheduler( - codebases = {'cb':{'repository':'r', 'branch': 'branchX'}, - 'cb2':{'repository':'r2', 'branch': 'branchX'}}) - - self.db.insertTestData([ - fakedb.SourceStampSet(id=100), - fakedb.SourceStamp(id=91, sourcestampsetid=100, revision='myrev1', - branch='br', project='p', repository='r', codebase='cb'), - fakedb.SourceStamp(id=92, sourcestampsetid=100, revision='myrev2', - branch='br', project='p', repository='r2', codebase='cb2'), - ]) - - got_revision = {'cb': 'gotrevision1a', } - d = sched.trigger(100, got_revision = got_revision) + codebases = {'cb':{'repository':'r', 'branch': 'branchX'}, + 'cb2':{'repository':'r2', 'branch': 'branchX'},}) - self.db.buildsets.assertBuildset('?', - dict(external_idstring=None, - properties=[('scheduler', ('n', 'Scheduler'))], - reason='Triggerable(n)', - sourcestampsetid=101), - {'cb': - dict(branch='br', project='p', repository='r', codebase='cb', - revision='gotrevision1a', sourcestampsetid=101), - 'cb2': - dict(branch='br', project='p', repository='r2', codebase='cb2', - revision='myrev2', sourcestampsetid=101),}) - - def test_trigger_with_fixed_sourcestamps_and_got_revision(self): - # Test a scheduler with 3 repositories. - # Trigger the scheduler with a build containing repositories 1 and 2 - # and pass a set of got revision values for repository 1 and 2 - # and pass a set of 2 fixed sourcestamps with repository 1 and 3 - # Expected Result: - # sourcestamp 1 for repo 1 based on fixed sourcestamp (ignore got_revision) - # sourcestamp 2 for repo 2 based on sourcestamp from build, got revision - # sourcestamp 3 for repo 3 based on fixed sourcestamp - sched = self.makeScheduler( - codebases = {'cb':{'repository':'r', 'branch': 'branchX'}, - 'cb2':{'repository':'r2', 'branch': 'branchX'}, - 'cb3':{'repository':'r3', 'branch': 'branchX'},}) - - self.db.insertTestData([ - fakedb.SourceStampSet(id=100), - fakedb.SourceStamp(id=91, sourcestampsetid=100, revision='myrev1', - branch='br', project='p', repository='r', codebase='cb'), - fakedb.SourceStamp(id=92, sourcestampsetid=100, revision='myrev2', - branch='br', project='p', repository='r2', codebase='cb2'), - ]) - - ss1 = {'repository': 'r', 'codebase': 'cb', 'revision': 'fixrev1', - 'branch': 'default', 'project': 'p' } - ss2 = {'repository': 'r3', 'codebase': 'cb3', 'revision': 'fixrev3', - 'branch': 'default', 'project': 'p' } - got_revision = {'cb': 'gotrevision1a', 'cb2': 'gotrevision2a', } - d = sched.trigger(100, sourcestamps = [ss1, ss2], got_revision = got_revision) + d = sched.trigger(sourcestamps = None) self.db.buildsets.assertBuildset('?', dict(external_idstring=None, properties=[('scheduler', ('n', 'Scheduler'))], reason='Triggerable(n)', - sourcestampsetid=101), + sourcestampsetid=100), {'cb': - dict(branch='default', project='p', repository='r', codebase='cb', - revision='fixrev1', sourcestampsetid=101), + dict(branch='branchX', project='', repository='r', codebase='cb', + revision=None, sourcestampsetid=100), 'cb2': - dict(branch='br', project='p', repository='r2', codebase='cb2', - revision='gotrevision2a', sourcestampsetid=101), - 'cb3': - dict(branch='default', project='p', repository='r3', codebase='cb3', - revision='fixrev3', sourcestampsetid=101),}) + dict(branch='branchX', project='', repository='r2', codebase='cb2', + revision=None, sourcestampsetid=100),}) \ No newline at end of file diff --git a/master/buildbot/test/unit/test_steps_trigger.py b/master/buildbot/test/unit/test_steps_trigger.py index 05c1daedf35..463edc3c3f5 100644 --- a/master/buildbot/test/unit/test_steps_trigger.py +++ b/master/buildbot/test/unit/test_steps_trigger.py @@ -32,10 +32,8 @@ class FakeTriggerable(triggerable.Triggerable): brids = {} exception = False - def trigger(self, sourcestampsetid, got_revision = {}, - sourcestamps = None, set_props=None): - self.triggered_with = (sourcestampsetid, got_revision,sourcestamps, - set_props.properties) + def trigger(self, sourcestamps = None, set_props=None): + self.triggered_with = (sourcestamps, set_props.properties) d = defer.Deferred() if self.exception: reactor.callLater(0, d.errback, RuntimeError('oh noes')) @@ -47,15 +45,10 @@ def trigger(self, sourcestampsetid, got_revision = {}, class FakeSourceStamp(object): def __init__(self, _ssid, _setid, **kwargs): - self._ssid = _ssid - self._setid = _setid self.__dict__.update(kwargs) - def getAbsoluteSourceStamp(self, rev): - return FakeSourceStamp(self._ssid + 1000, self._setid + 1000, revision=rev) - - def getSourceStampSetId(self, master): - return defer.succeed(self._setid) + def asDict(self, includePatch = True): + return self.__dict__.copy() # Magic numbers that relate brid to other build settings BRID_TO_BSID = lambda brid: brid+2000 @@ -66,9 +59,6 @@ class TestTrigger(steps.BuildStepMixin, unittest.TestCase): THIS_SSID = 6 THIS_SS_SETID = 66 - ABS_SS_SETID = THIS_SS_SETID + 1000 - NEW_SSID = 987 - NEW_SS_SETID = 800 def setUp(self): return self.setUpBuildStep() @@ -77,6 +67,11 @@ def tearDown(self): return self.tearDownBuildStep() def setupStep(self, *args, **kwargs): + sourcestamps = [] + if 'sourcestampsInBuild' in kwargs: + sourcestamps = kwargs['sourcestampsInBuild'] + del kwargs['sourcestampsInBuild'] + steps.BuildStepMixin.setupStep(self, *args, **kwargs) # This step reaches deeply into a number of parts of Buildbot. That @@ -103,7 +98,7 @@ def allSchedulers(): 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"), @@ -111,26 +106,10 @@ def allSchedulers(): make_fake_build(22), ]) - # the build has a getSourceStamp method - def getSourceStamp(codebase): - return FakeSourceStamp(self.THIS_SSID, self.THIS_SS_SETID) - self.build.getSourceStamp = getSourceStamp - - # the db has a addSourceStamp method - self.addSourceStamp_kwargs = None - def addSourceStamp(**kwargs): - self.addSourceStamp_kwargs = kwargs - return defer.succeed(self.NEW_SSID) - m.db.sourcestamps.addSourceStamp = addSourceStamp - - # the db has a addSourceStampSet method - def addSourceStampSet(): - return defer.succeed(self.NEW_SS_SETID) - m.db.sourcestampsets.addSourceStampSet = addSourceStampSet - - def getSourceStampSetId(): - return self.THIS_SS_SETID - self.build.getSourceStampSetId = getSourceStampSetId + def getAllSourceStamps(): + return self.build_sources + self.build.getAllSourceStamps = getAllSourceStamps + self.build_sources = sourcestamps self.exp_add_sourcestamp = None self.exp_a_trigger = None @@ -180,11 +159,11 @@ def expectAddedSourceStamp(self, **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 ) + 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... + #returns the *args and **kwargs that will be called on addURL... # which is just addURL('label', 'url') return ( (label(name,num), url(name,num)) , {} ) @@ -224,13 +203,13 @@ def test_sourceStamp_and_alwaysUseLatest(self): def test_sourceStamps_and_alwaysUseLatest(self): self.assertRaises(config.ConfigErrors, lambda : trigger.Trigger(schedulerNames=['c'], - sourceStamps=[dict(x=1), dict(x=2)], + sourceStamps=[dict(x=1), dict(x=2)], alwaysUseLatest=True)) def test_simple(self): - self.setupStep(trigger.Trigger(schedulerNames=['a'])) + self.setupStep(trigger.Trigger(schedulerNames=['a'], sourceStamps = {})) self.expectOutcome(result=SUCCESS, status_text=['triggered', 'a']) - self.expectTriggeredWith(a=(self.THIS_SS_SETID, None, [], {})) + self.expectTriggeredWith(a=({}, {})) return self.runStep() def test_simple_failure(self): @@ -239,7 +218,7 @@ def test_simple_failure(self): # not waitForFinish, so trigger step succeeds even though the build # didn't fail self.expectOutcome(result=SUCCESS, status_text=['triggered', 'a']) - self.expectTriggeredWith(a=(self.THIS_SS_SETID, None, [], {})) + self.expectTriggeredWith(a=({}, {})) return self.runStep() @compat.usesFlushLoggedErrors @@ -247,7 +226,7 @@ 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=(self.THIS_SS_SETID, None, [], {})) + self.expectTriggeredWith(a=( {}, {})) d = self.runStep() def flush(_): self.assertEqual(len(self.flushLoggedErrors(RuntimeError)), 1) @@ -256,77 +235,174 @@ def flush(_): def test_bogus_scheduler(self): self.setupStep(trigger.Trigger(schedulerNames=['a', 'x'])) - self.expectOutcome(result=FAILURE, status_text=['no scheduler:', '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)) + updateSourceStamp=True), + sourcestampsInBuild = [FakeSourceStamp(self.THIS_SSID, + self.THIS_SS_SETID, + codebase='', + repository='x', + revision=11111) + ]) self.properties.setProperty('got_revision', 23456, 'Source') self.expectOutcome(result=SUCCESS, status_text=['triggered', 'a']) - self.expectTriggeredWith(a=(self.THIS_SS_SETID, {'':23456}, [], {})) + self.expectTriggeredWith(a=({'':{'codebase':'', + 'repository': 'x', + 'revision': 23456} + }, {})) + return self.runStep() + + def test_not_updateSourceStamp(self): + self.setupStep(trigger.Trigger(schedulerNames=['a'], + updateSourceStamp=False), + sourcestampsInBuild = [FakeSourceStamp(self.THIS_SSID, + self.THIS_SS_SETID, + codebase='', + repository='x', + revision=11111) + ]) + self.properties.setProperty('got_revision', 23456, 'Source') + self.expectOutcome(result=SUCCESS, status_text=['triggered', 'a']) + self.expectTriggeredWith(a=({'':{'codebase':'', + 'repository': 'x', + 'revision': 11111} + }, {})) return self.runStep() def test_updateSourceStamp_multiple_repositories(self): self.setupStep(trigger.Trigger(schedulerNames=['a'], - updateSourceStamp=True)) - self.properties.setProperty('got_revision', + updateSourceStamp=True), + sourcestampsInBuild = [ + FakeSourceStamp(self.THIS_SSID, + self.THIS_SS_SETID, + codebase='cb1', + revision='12345'), + FakeSourceStamp(self.THIS_SSID, + self.THIS_SS_SETID, + codebase='cb2', + revision='12345') + ]) + self.properties.setProperty('got_revision', {'cb1': 23456, 'cb2': 34567}, 'Source') self.expectOutcome(result=SUCCESS, status_text=['triggered', 'a']) - self.expectTriggeredWith(a=(self.THIS_SS_SETID, - {'cb1': 23456, 'cb2': 34567}, [], {})) + self.expectTriggeredWith(a=({'cb1': {'codebase':'cb1', + 'revision':23456}, + 'cb2': {'codebase':'cb2', + 'revision':34567} + }, {})) return self.runStep() - def test_updateSourceStamp_no_prop(self): + + def test_updateSourceStamp_prop_false(self): self.setupStep(trigger.Trigger(schedulerNames=['a'], - updateSourceStamp=True)) + updateSourceStamp=properties.Property('usess')), + sourcestampsInBuild = [FakeSourceStamp(self.THIS_SSID, + self.THIS_SS_SETID, + codebase='', + repository='x', + revision=11111) + ]) + self.properties.setProperty('got_revision', 23456, 'Source') + self.properties.setProperty('usess', False, 'me') self.expectOutcome(result=SUCCESS, status_text=['triggered', 'a']) - self.expectTriggeredWith(a=(self.THIS_SS_SETID, None, [], {})) + # didn't use got_revision + self.expectTriggeredWith(a=({'': { 'codebase':'', + 'repository': 'x', + 'revision': 11111 + }}, {})) return self.runStep() - def test_updateSourceStamp_prop(self): + def test_updateSourceStamp_prop_true(self): self.setupStep(trigger.Trigger(schedulerNames=['a'], - updateSourceStamp=properties.Property('usess'))) + updateSourceStamp=properties.Property('usess')), + sourcestampsInBuild = [FakeSourceStamp(self.THIS_SSID, + self.THIS_SS_SETID, + codebase='', + repository='x', + revision=11111) + ]) self.properties.setProperty('got_revision', 23456, 'Source') - self.properties.setProperty('usess', False, 'me') + self.properties.setProperty('usess', True, 'me') self.expectOutcome(result=SUCCESS, status_text=['triggered', 'a']) # didn't use got_revision - self.expectTriggeredWith(a=(self.THIS_SS_SETID, None, [], {})) + self.expectTriggeredWith(a=({'': { 'codebase':'', + 'repository': 'x', + 'revision': 23456 + }}, {})) return self.runStep() def test_alwaysUseLatest(self): self.setupStep(trigger.Trigger(schedulerNames=['b'], - alwaysUseLatest=True)) + alwaysUseLatest=True), + sourcestampsInBuild = [FakeSourceStamp(self.THIS_SSID, + self.THIS_SS_SETID, + codebase='', + repository='x', + revision=11111) + ]) self.expectOutcome(result=SUCCESS, status_text=['triggered', 'b']) # Do not pass setid - self.expectTriggeredWith(b=(None, None, [], {})) + self.expectTriggeredWith(b=({'': { 'codebase':'', + 'repository': 'x', + 'revision': None} + }, {})) return self.runStep() - def test_alwaysUseLatest_prop(self): + def test_alwaysUseLatest_prop_false(self): self.setupStep(trigger.Trigger(schedulerNames=['b'], - alwaysUseLatest=properties.Property('aul'))) + alwaysUseLatest=properties.Property('aul')), + sourcestampsInBuild = [FakeSourceStamp(self.THIS_SSID, + self.THIS_SS_SETID, + 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=(self.THIS_SS_SETID, None, [], {})) + self.expectTriggeredWith(b=({'': { 'codebase':'', + 'repository': 'x', + 'revision': 11111} + }, {})) + return self.runStep() + + def test_alwaysUseLatest_prop_true(self): + self.setupStep(trigger.Trigger(schedulerNames=['b'], + alwaysUseLatest=properties.Property('aul')), + sourcestampsInBuild = [FakeSourceStamp(self.THIS_SSID, + self.THIS_SS_SETID, + 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=({'': { 'codebase':'', + 'repository': 'x', + 'revision': None} + }, {})) return self.runStep() def test_sourceStamp(self): ss = dict(revision=9876, branch='dev') - self.setupStep(trigger.Trigger(schedulerNames=['b'], + self.setupStep(trigger.Trigger(schedulerNames=['b'], sourceStamp=ss)) self.expectOutcome(result=SUCCESS, status_text=['triggered', 'b']) - self.expectTriggeredWith(b=(self.THIS_SS_SETID, None, [ss], {})) + self.expectTriggeredWith(b=({'': ss}, {})) return self.runStep() def test_set_of_sourceStamps(self): - ss1 = dict(repository='r1', revision=9876, branch='dev') - ss2 = dict(repository='r2', revision=5432, branch='dev') - self.setupStep(trigger.Trigger(schedulerNames=['b'], + 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=(self.THIS_SS_SETID, None, [ss1,ss2], {})) + self.expectTriggeredWith(b=({'cb1':ss1, 'cb2':ss2}, {})) return self.runStep() def test_sourceStamp_prop(self): @@ -336,7 +412,7 @@ def test_sourceStamp_prop(self): self.properties.setProperty('rev', 602, 'me') expected_ss = dict(revision=602, branch='dev') self.expectOutcome(result=SUCCESS, status_text=['triggered', 'b']) - self.expectTriggeredWith(b=(self.THIS_SS_SETID, None, [expected_ss], {})) + self.expectTriggeredWith(b=({'': expected_ss}, {})) return self.runStep() def test_waitForFinish(self): @@ -344,8 +420,8 @@ def test_waitForFinish(self): waitForFinish=True)) self.expectOutcome(result=SUCCESS, status_text=['triggered', 'a', 'b']) self.expectTriggeredWith( - a=(self.THIS_SS_SETID, None, [], {}), - b=(self.THIS_SS_SETID, None, [], {})) + a=({}, {}), + b=({}, {})) self.expectTriggeredLinks('a','b') return self.runStep(expect_waitForFinish=True) @@ -354,7 +430,7 @@ def test_waitForFinish_failure(self): waitForFinish=True)) self.scheduler_a.result = FAILURE self.expectOutcome(result=FAILURE, status_text=['triggered', 'a']) - self.expectTriggeredWith(a=(self.THIS_SS_SETID, None, [], {})) + self.expectTriggeredWith(a=({}, {})) self.expectTriggeredLinks('a') return self.runStep(expect_waitForFinish=True) @@ -366,8 +442,8 @@ def test_waitForFinish_exception(self): self.expectOutcome(result=EXCEPTION, status_text=['triggered', 'a', 'b']) self.expectTriggeredWith( - a=(self.THIS_SS_SETID, None, [], {}), - b=(self.THIS_SS_SETID, None, [], {})) + a=({}, {}), + b=({}, {})) self.expectTriggeredLinks('a') # b doesnt return a brid d = self.runStep(expect_waitForFinish=True) def flush(_): @@ -379,7 +455,7 @@ 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=(self.THIS_SS_SETID, None, [], + self.expectTriggeredWith(a=({}, dict(x=(1, 'Trigger'), y=(2, 'Trigger')))) return self.runStep() @@ -388,7 +464,7 @@ def test_set_properties_prop(self): 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=(self.THIS_SS_SETID, None, [], + self.expectTriggeredWith(a=({}, dict(x=('xxx', 'Trigger'), y=(2, 'Trigger')))) return self.runStep() @@ -399,7 +475,7 @@ def test_copy_properties(self): self.properties.setProperty('b', 'B', 'BB') self.properties.setProperty('c', 'C', 'CC') self.expectOutcome(result=SUCCESS, status_text=['triggered', 'a']) - self.expectTriggeredWith(a=(self.THIS_SS_SETID, None, [], + self.expectTriggeredWith(a=({}, dict(a=('A', 'AA (in triggering build)'), b=('B', 'BB (in triggering build)')))) return self.runStep() @@ -408,7 +484,7 @@ def test_interrupt(self): self.setupStep(trigger.Trigger(schedulerNames=['a'], waitForFinish=True)) self.expectOutcome(result=EXCEPTION, status_text=['interrupted']) - self.expectTriggeredWith(a=(self.THIS_SS_SETID, None, [], {})) + self.expectTriggeredWith(a=({}, {})) d = self.runStep(expect_waitForFinish=True) # interrupt before the callLater representing the Triggerable