def testBuildRetryWhenWorkerPrepareReturnFalseBecauseBuildStop(self): b = self.build step = FakeBuildStep() b.setStepFactories([FakeStepFactory(step)]) d = defer.Deferred() self.workerforbuilder.prepare = lambda _: d b.startBuild(FakeBuildStatus(), self.workerforbuilder) b.stopBuild('Cancel Build', RETRY) d.callback(False) self.assertEqual(b.results, RETRY) self.assertWorkerPreparationFailure('error while worker_prepare')
def testRunSuccessfulBuild(self): b = self.build step = Mock() step.return_value = step step.startStep.return_value = SUCCESS b.setStepFactories([FakeStepFactory(step)]) b.startBuild(FakeBuildStatus(), None, self.slavebuilder) self.assertEqual(b.result, SUCCESS) self.assert_(('startStep', (self.slavebuilder.slave.conn, ), {}) in step.method_calls)
def testRunSuccessfulBuild(self): b = self.build step = Mock() step.return_value = step step.startStep.return_value = SUCCESS b.setStepFactories([FakeStepFactory(step)]) b.startBuild(FakeBuildStatus(), self.workerforbuilder) self.assertEqual(b.results, SUCCESS) self.assertIn(('startStep', (self.workerforbuilder.worker.conn, ), {}), step.method_calls)
def run_fake_single_build(self, gsp, buildResult, expWarning=False): build = FakeBuildStatus(name="build") build.getProperty = self.TEST_PROPS.get gsp.buildFinished('dummyBuilder', build, buildResult) if expWarning: self.assertEqual([w['message'] for w in self.flushWarnings()], ['The Gerrit status callback uses the old ' 'way to communicate results. The outcome ' 'might be not what is expected.']) return defer.succeed(str({'name': 'dummyBuilder', 'result': buildResult}))
def test_createEmail_message_without_patch_and_log_contains_unicode(self): builds = [ FakeBuildStatus(name="build") ] msgdict = create_msgdict() mn = MailNotifier('*****@*****.**') d = mn.createEmail(msgdict, u'builder-n\u00E5me', u'project-n\u00E5me', SUCCESS, builds) @d.addCallback def callback(m): try: m.as_string() except UnicodeEncodeError: self.fail('Failed to call as_string() on email message.') return d
def test_createEmail_message_with_nonascii_patch(self): builds = [ FakeBuildStatus(name="build") ] msgdict = create_msgdict() patches = [ ['', '\x99\xaa', ''] ] logs = [ FakeLog('simple log') ] mn = MailNotifier('*****@*****.**', addLogs=True) d = mn.createEmail(msgdict, u'builder', u'pr', SUCCESS, builds, patches, logs) @d.addCallback def callback(m): txt = m.as_string() self.assertIn('application/octet-stream', txt) return d
def test_buildsetFinished_sends_email(self): fakeBuildMessage = Mock() mn = MailNotifier('*****@*****.**', buildSetSummary=True, mode="all", builders=["Builder"]) mn.buildMessage = fakeBuildMessage def fakeGetBuild(number): return build def fakeGetBuilder(buildername): if buildername == builder.name: return builder return None def fakeGetBuildRequests(self, bsid): return defer.succeed([{"buildername":"Builder", "brid":1}]) builder = Mock() builder.getBuild = fakeGetBuild builder.name = "Builder" build = FakeBuildStatus() build.result = FAILURE build.finished = True build.reason = "testReason" build.getBuilder.return_value = builder self.db = fakedb.FakeDBConnector(self) self.db.insertTestData([fakedb.SourceStampSet(id=127), fakedb.Buildset(id=99, sourcestampsetid=127, results=SUCCESS, reason="testReason"), fakedb.BuildRequest(id=11, buildsetid=99, buildername='Builder'), fakedb.Build(number=0, brid=11), ]) mn.parent = self self.status = Mock() mn.master_status = Mock() mn.master_status.getBuilder = fakeGetBuilder mn.buildMessageDict = Mock() mn.buildMessageDict.return_value = {"body":"body", "type":"text", "subject":"subject"} mn.buildsetFinished(99, FAILURE) fakeBuildMessage.assert_called_with("Buildset Complete: testReason", [build], SUCCESS)
def test_createEmail_extraHeaders_one_build(self): builds = [ FakeBuildStatus(name="build") ] builds[0].properties = properties.Properties() builds[0].setProperty('hhh','vvv') msgdict = create_msgdict() mn = MailNotifier('*****@*****.**', extraHeaders=dict(hhh='vvv')) # add some Unicode to detect encoding problems d = mn.createEmail(msgdict, u'builder-n\u00E5me', u'project-n\u00E5me', SUCCESS, builds) @d.addCallback def callback(m): txt = m.as_string() self.assertIn('hhh: vvv', txt) return d
def do_test_createEmail_cte(self, funnyChars, expEncoding): builds = [ FakeBuildStatus(name='build') ] msgdict = create_msgdict(funnyChars) mn = MailNotifier('*****@*****.**') d = mn.createEmail(msgdict, u'builder-name', u'project-name', SUCCESS, builds) @d.addCallback def callback(m): cte_lines = [ l for l in m.as_string().split("\n") if l.startswith('Content-Transfer-Encoding:') ] self.assertEqual(cte_lines, [ 'Content-Transfer-Encoding: %s' % expEncoding ], `m.as_string()`) return d
def test_createEmail_message_with_patch_and_log_containing_unicode(self): builds = [FakeBuildStatus(name="build")] msgdict = create_msgdict() patches = [['', u'\u00E5\u00E4\u00F6', '']] msg = u'Unicode log with non-ascii (\u00E5\u00E4\u00F6).' # add msg twice: as unicode and already encoded logs = [FakeLog(msg), FakeLog(msg.encode('utf-8'))] mn = MailNotifier('*****@*****.**', addLogs=True) m = mn.createEmail(msgdict, u'builder-n\u00E5me', u'project-n\u00E5me', SUCCESS, builds, patches, logs) try: m.as_string() except UnicodeEncodeError: self.fail('Failed to call as_string() on email message.')
def run_fake_single_build(self, buildResult, verifiedScore): gsp = GerritStatusPush('host.example.com', 'username', reviewCB=testReviewCB) self.run_prepare_gsp(gsp) fakeSCR = Mock() gsp.sendCodeReview = fakeSCR build = FakeBuildStatus(name="build") build.getProperty = lambda prop: self.TEST_PROPS.get(prop) gsp.buildFinished('dummyBuilder', build, buildResult) fakeSCR.assert_called_once_with(self.TEST_PROJECT, self.TEST_REVISION, str({'name': 'dummyBuilder', 'result': buildResult}), verifiedScore, 0)
def setUp(self): r = FakeRequest() self.request = r self.master = FakeMaster() self.master.maybeStartBuildsForSlave = lambda slave: True self.build = Build([r]) self.builder = Mock() self.builder.botmaster = self.master.botmaster self.build.setBuilder(self.builder) self.build_status = FakeBuildStatus() self.step_status = Mock() self.step_status.finished = None self.build_status.addStepWithName = lambda x, y: self.step_status
def testAlwaysRunStepStopBuild(self): """Test that steps marked with alwaysRun=True still get run even if the build is stopped.""" # Create a build with 2 steps, the first one will get interrupted, and # the second one is marked with alwaysRun=True b = self.build step1 = Mock() step1.return_value = step1 step1.alwaysRun = False step1.results = None step2 = Mock() step2.return_value = step2 step2.alwaysRun = True step2.results = None b.setStepFactories([ FakeStepFactory(step1), FakeStepFactory(step2), ]) def startStep1(*args, **kw): # Now interrupt the build b.stopBuild("stop it") return defer.succeed(SUCCESS) step1.startStep = startStep1 step1.stepDone.return_value = False step2Started = [False] def startStep2(*args, **kw): step2Started[0] = True return defer.succeed(SUCCESS) step2.startStep = startStep2 step1.stepDone.return_value = False d = b.startBuild(FakeBuildStatus(), self.workerforbuilder) def check(ign): self.assertEqual(b.results, CANCELLED) self.assertIn(('interrupt', ('stop it', ), {}), step1.method_calls) self.assertTrue(step2Started[0]) d.addCallback(check) return d
def testStopBuild(self): b = self.build step = FakeBuildStep() b.setStepFactories([FakeStepFactory(step)]) def startStep(*args, **kw): # Now interrupt the build b.stopBuild("stop it") return defer.Deferred() step.startStep = startStep b.startBuild(FakeBuildStatus(), self.workerforbuilder) self.assertEqual(b.results, CANCELLED) self.assertIn('stop it', step.interrupted)
def test_start_step_throws_exception(self): b = self.build step1 = FakeBuildStep() b.setStepFactories([ FakeStepFactory(step1), ]) def startStep(*args, **kw): raise TestException() step1.startStep = startStep yield b.startBuild(FakeBuildStatus(), self.workerforbuilder) self.assertEqual(b.results, EXCEPTION) self.flushLoggedErrors(TestException)
def setUp(self): self.props = {} r = FakeRequest() r.sources = [] r.sources.append(FakeSource()) r.sources[0].changes = [FakeChange()] r.sources[0].repository = "http://svn-repo-A" r.sources[0].codebase = "A" r.sources[0].branch = "develop" r.sources[0].revision = "12345" self.build = Build([r]) self.build.setStepFactories([]) self.builder = FakeBuilder( fakemaster.make_master(wantData=True, testcase=self)) self.build.setBuilder(self.builder) self.build.build_status = FakeBuildStatus() # record properties that will be set self.build.properties.setProperty = self.setProperty
def testAddStepsAfterLastStep(self): b = self.build steps = self.create_mock_steps(["a", "b", "c"]) def startStepB(*args, **kw): new_steps = self.create_mock_steps(["d", "e"]) b.addStepsAfterLastStep([FakeStepFactory(s) for s in new_steps]) return SUCCESS steps[1].startStep = startStepB b.setStepFactories([FakeStepFactory(s) for s in steps]) b.startBuild(FakeBuildStatus(), None, self.slavebuilder) self.assertEqual(b.results, SUCCESS) expected_names = ["a", "b", "c", "d", "e"] executed_names = [s.name for s in b.executedSteps] self.assertEqual(executed_names, expected_names)
def testStepNamesDuplicateAfterAdd(self): b = self.build steps = self.create_fake_steps(["a", "b", "c"]) def startStepB(*args, **kw): new_steps = self.create_fake_steps(["c", "c"]) b.addStepsAfterCurrentStep([FakeStepFactory(s) for s in new_steps]) return SUCCESS steps[1].startStep = startStepB b.setStepFactories([FakeStepFactory(s) for s in steps]) b.startBuild(FakeBuildStatus(), self.workerforbuilder) self.assertEqual(b.results, SUCCESS) expected_names = ["a", "b", "c_1", "c_2", "c"] executed_names = [s.name for s in b.executedSteps] self.assertEqual(executed_names, expected_names)
def testStopBuild(self): b = self.build step = Mock() step.return_value = step b.setStepFactories([FakeStepFactory(step)]) def startStep(*args, **kw): # Now interrupt the build b.stopBuild("stop it") return defer.Deferred() step.startStep = startStep b.startBuild(FakeBuildStatus(), None, self.slavebuilder) self.assertEqual(b.results, CANCELLED) self.assert_(('interrupt', ('stop it',), {}) in step.method_calls)
def setUp(self): class FakeBuildStatus(Mock): implements(interfaces.IProperties) r = FakeRequest() r.sources = [FakeSource()] r.sources[0].changes = [FakeChange()] r.sources[0].revision = "12345" self.master = fakemaster.make_master(wantData=True, testcase=self) self.slave = slave.FakeSlave(self.master) self.slave.attached(None) self.slavebuilder = Mock(name='slavebuilder') self.slavebuilder.slave = self.slave self.build = Build([r]) self.build.setStepFactories([]) self.builder = FakeBuilder( fakemaster.make_master(wantData=True, testcase=self)) self.build.setBuilder(self.builder) self.build_status = FakeBuildStatus() self.build.startBuild(self.build_status, None, self.slavebuilder)
def testAlwaysRunStepStopBuild(self): """Test that steps marked with alwaysRun=True still get run even if the build is stopped.""" # Create a build with 2 steps, the first one will get interrupted, and # the second one is marked with alwaysRun=True b = self.build step1 = FakeBuildStep() step1.alwaysRun = False step1.results = None step2 = FakeBuildStep() step2.alwaysRun = True step2.results = None b.setStepFactories([ FakeStepFactory(step1), FakeStepFactory(step2), ]) def startStep1(*args, **kw): # Now interrupt the build b.stopBuild("stop it") return defer.succeed(SUCCESS) step1.startStep = startStep1 step1.stepDone = lambda: False step2Started = [False] def startStep2(*args, **kw): step2Started[0] = True return defer.succeed(SUCCESS) step2.startStep = startStep2 step1.stepDone = lambda: False yield b.startBuild(FakeBuildStatus(), self.workerforbuilder) self.assertEqual(b.results, CANCELLED) self.assertIn('stop it', step1.interrupted) self.assertTrue(step2Started[0])
def testflushProperties(self): b = self.build class FakeBuildStatus(Mock): implements(interfaces.IProperties) b.build_status = FakeBuildStatus() class Properties(Mock): def asList(self): return [(u'p', 5, u'fake'), (u'p2', ['abc', 9], u'mock')] b.master.data.updates.setBuildProperty = Mock() b.build_status.getProperties.return_value = Properties() b.buildid = 42 result = 'SUCCESS' res = yield b._flushProperties(result) self.assertEquals(res, result) b.master.data.updates.setBuildProperty.assert_has_calls( [call(42, u'p', 5, u'fake'), call(42, u'p2', ['abc', 9], u'mock')])
def test_active_builds_metric_failure(self): """ The number of active builds is increased when a build starts and decreased when it finishes.. """ b = self.build b.setStepFactories([FailingStepFactory()]) observer = MetricLogObserver() observer.enable() self.addCleanup(observer.disable) def get_active_builds(): return observer.asDict()['counters'].get('active_builds', 0) self.assertEqual(get_active_builds(), 0) b.startBuild(FakeBuildStatus(), self.workerforbuilder) self.flushLoggedErrors(TestException) self.assertEqual(get_active_builds(), 0)
def setUp(self): class FakeProperties(Mock): implements(interfaces.IProperties) class FakeBuildStatus(Mock): pass r = FakeRequest() r.sources = [FakeSource()] r.sources[0].changes = [FakeChange()] r.sources[0].revision = "12345" self.master = fakemaster.make_master(wantData=True, testcase=self) self.worker = worker.FakeWorker(self.master) self.worker.attached(None) self.workerforbuilder = Mock(name='workerforbuilder') self.workerforbuilder.worker = self.worker self.build = Build([r]) self.build.setStepFactories([]) self.builder = FakeBuilder( fakemaster.make_master(wantData=True, testcase=self)) self.build.setBuilder(self.builder) self.properties = self.build.properties = FakeProperties() self.build_status = FakeBuildStatus() self.build._flushProperties = Mock() self.build.startBuild(self.build_status, self.workerforbuilder)
def testStopBuildWaitingForLocks_lostRemote(self): b = self.build l = WorkerLock('lock') lock_access = l.access('counting') l.access = lambda mode: lock_access real_lock = b.builder.botmaster.getLockByID(l) \ .getLock(self.workerforbuilder.worker) b.setLocks([lock_access]) step = Mock() step.return_value = step step.startStep.return_value = SUCCESS step.alwaysRun = False b.setStepFactories([FakeStepFactory(step)]) real_lock.claim(Mock(), l.access('counting')) def acquireLocks(res=None): retval = Build.acquireLocks(b, res) b.lostRemote() return retval b.acquireLocks = acquireLocks b.startBuild(FakeBuildStatus(), self.workerforbuilder) self.assertNotIn( ('startStep', (self.workerforbuilder.worker.conn, ), {}), step.method_calls) self.assertTrue(b.currentStep is None) self.assertEqual(b.results, RETRY) self.assertNotIn(('interrupt', ('stop it', ), {}), step.method_calls) self.build.build_status.setText.assert_called_with( ["retry", "lost", "connection"]) self.build.build_status.setResults.assert_called_with(RETRY)
def testBuildLocksAcquired(self): b = self.build lock = WorkerLock('lock') claimCount = [0] lock_access = lock.access('counting') lock.access = lambda mode: lock_access real_lock = b.builder.botmaster.getLockByID(lock) \ .getLockForWorker(self.workerforbuilder.worker.workername) def claim(owner, access): claimCount[0] += 1 return real_lock.old_claim(owner, access) real_lock.old_claim = real_lock.claim real_lock.claim = claim b.setLocks([lock_access]) step = FakeBuildStep() b.setStepFactories([FakeStepFactory(step)]) b.startBuild(FakeBuildStatus(), self.workerforbuilder) self.assertEqual(b.results, SUCCESS) self.assertEqual(claimCount[0], 1)
def run_fake_summary_build(self, gsp, buildResults, finalResult, resultText): buildpairs = [] i = 0 for i in xrange(len(buildResults)): buildResult = buildResults[i] builder = Mock() build = FakeBuildStatus() builder.getBuild.return_value = build builder.name = "Builder-%d" % i builder.getName.return_value = builder.name build.results = buildResult build.finished = True build.reason = "testReason" build.getBuilder.return_value = builder build.getResults.return_value = build.results build.getText.return_value = ['buildText'] build.getProperty = self.TEST_PROPS.get buildpairs.append((builder, build)) def fakeGetBuilder(buildername): # e.g. Builder-5 will be buildpairs[5][0] return buildpairs[int(buildername.split("-")[1])][0] gsp.master_status.getBuilder = fakeGetBuilder gsp.master_status.getURLForThing = Mock() gsp.master_status.getURLForThing.return_value = self.THING_URL gsp.master.db = fakedb.FakeDBConnector(gsp.master, self) fakedata = [ fakedb.Master(id=92), fakedb.Buildslave(id=13, name='sl'), fakedb.Buildset(id=99, results=finalResult, reason="testReason"), ] breqid = 1000 for (builder, build) in buildpairs: fakedata.append( fakedb.BuildRequest(id=breqid, buildsetid=99, buildername=builder.name)) fakedata.append( fakedb.Build(number=0, buildrequestid=breqid, masterid=92, buildslaveid=13)) breqid = breqid + 1 gsp.master.db.insertTestData(fakedata) d = gsp._buildsetComplete('buildset.99.complete', dict(bsid=99, result=SUCCESS)) @d.addCallback def check(_): info = [] for i in xrange(len(buildResults)): info.append({ 'name': "Builder-%d" % i, 'result': buildResults[i], 'resultText': resultText[i], 'text': 'buildText', 'url': self.THING_URL }) return str(info) return d
def do_test_sendToInterestedUsers(self, lookup=None, extraRecipients=[], sendToInterestedUsers=True, exp_called_with=None, exp_TO=None, exp_CC=None): from email.Message import Message m = Message() mn = MailNotifier(fromaddr='*****@*****.**', lookup=lookup, sendToInterestedUsers=sendToInterestedUsers, extraRecipients=extraRecipients) mn.sendMessage = Mock() def fakeGetBuild(number): return build def fakeGetBuilder(buildername): if buildername == builder.name: return builder return None def fakeGetBuildRequests(self, bsid): return defer.succeed([{"buildername":"Builder", "brid":1}]) builder = Mock() builder.getBuild = fakeGetBuild builder.name = "Builder" build = FakeBuildStatus(name="build") build.result = FAILURE build.finished = True build.reason = "testReason" build.builder = builder def fakeCreateEmail(msgdict, builderName, title, results, builds=None, patches=None, logs=None): # only concerned with m['To'] and m['CC'], which are added in # _got_recipients later return m mn.createEmail = fakeCreateEmail self.db = fakedb.FakeDBConnector(self) self.db.insertTestData([fakedb.SourceStampSet(id=1099), fakedb.Buildset(id=99, sourcestampsetid=1099, results=SUCCESS, reason="testReason"), fakedb.BuildRequest(id=11, buildsetid=99, buildername='Builder'), fakedb.Build(number=0, brid=11), fakedb.Change(changeid=9123), fakedb.ChangeUser(changeid=9123, uid=1), fakedb.User(uid=1, identifier="tdurden"), fakedb.UserInfo(uid=1, attr_type='svn', attr_data="tdurden"), fakedb.UserInfo(uid=1, attr_type='email', attr_data="*****@*****.**") ]) # fake sourcestamp with relevant user bits ss = Mock(name="sourcestamp") fake_change = Mock(name="change") fake_change.number = 9123 ss.changes = [fake_change] ss.patch, ss.addPatch = None, None def fakeGetSS(): return ss build.getSourceStamp = fakeGetSS def _getInterestedUsers(): # 'narrator' in this case is the owner, which tests the lookup return ["Big Bob <*****@*****.**>", "narrator"] build.getInterestedUsers = _getInterestedUsers def _getResponsibleUsers(): return ["Big Bob <*****@*****.**>"] build.getResponsibleUsers = _getResponsibleUsers mn.parent = self self.status = mn.master_status = mn.buildMessageDict = Mock() mn.master_status.getBuilder = fakeGetBuilder mn.buildMessageDict.return_value = {"body": "body", "type": "text"} mn.buildMessage(builder.name, [build], build.result) mn.sendMessage.assert_called_with(m, exp_called_with) self.assertEqual(m['To'], exp_TO) self.assertEqual(m['CC'], exp_CC)
def run_fake_build(self, notifier, info=None): notifier.master = fakemaster.make_master() notifier.master_status = notifier.master.status builders = [] builds = [] for i in [0, 1, 2]: builder = Mock() build = FakeBuildStatus() builder.getBuild.return_value = build builder.name = "Builder%d" % i build.results = SUCCESS build.finished = True build.reason = "testReason" build.getBuilder.return_value = builder builders.append(builder) builds.append(build) def fakeGetBuilder(buildername): return { "Builder0": builders[0], "Builder1": builders[1], "Builder2": builders[2] }[buildername] notifier.master_status.getBuilder = fakeGetBuilder notifier.master.db = fakedb.FakeDBConnector(notifier.master, self) notifier.master.db.insertTestData([ fakedb.Master(id=92), fakedb.Buildslave(id=13, name='sl'), fakedb.Buildset(id=99, results=SUCCESS, reason="testReason"), fakedb.BuildRequest(id=10, buildsetid=99, buildername="Builder0"), fakedb.Build(number=0, buildrequestid=10, masterid=92, buildslaveid=13), fakedb.BuildRequest(id=11, buildsetid=99, buildername="Builder1"), fakedb.Build(number=0, buildrequestid=11, masterid=92, buildslaveid=13), fakedb.BuildRequest(id=12, buildsetid=99, buildername="Builder2"), fakedb.Build(number=0, buildrequestid=12, masterid=92, buildslaveid=13) ]) if info is not None: info['bsid'] = 99 info['builds'] = builds d = notifier._buildsetComplete('buildset.99.complete', { 'bsid': 99, 'result': SUCCESS }) return d
def test_sendToInterestedUsers_two_builds(self): from email.Message import Message m = Message() mn = MailNotifier(fromaddr="*****@*****.**", lookup=None) mn.sendMessage = Mock() def fakeGetBuilder(buildername): if buildername == builder.name: return builder return None def fakeGetBuildRequests(self, bsid): return defer.succeed([{"buildername":"Builder", "brid":1}]) builder = Mock() builder.name = "Builder" build1 = FakeBuildStatus(name="build") build1.result = FAILURE build1.finished = True build1.reason = "testReason" build1.builder = builder build2 = FakeBuildStatus(name="build") build2.result = FAILURE build2.finished = True build2.reason = "testReason" build2.builder = builder def fakeCreateEmail(msgdict, builderName, title, results, builds=None, patches=None, logs=None): # only concerned with m['To'] and m['CC'], which are added in # _got_recipients later return m mn.createEmail = fakeCreateEmail self.db = fakedb.FakeDBConnector(self) self.db.insertTestData([fakedb.SourceStampSet(id=1099), fakedb.Buildset(id=99, sourcestampsetid=1099, results=SUCCESS, reason="testReason"), fakedb.BuildRequest(id=11, buildsetid=99, buildername='Builder'), fakedb.Build(number=0, brid=11), fakedb.Build(number=1, brid=11), fakedb.Change(changeid=9123), fakedb.Change(changeid=9124), fakedb.ChangeUser(changeid=9123, uid=1), fakedb.ChangeUser(changeid=9124, uid=2), fakedb.User(uid=1, identifier="tdurden"), fakedb.User(uid=2, identifier="user2"), fakedb.UserInfo(uid=1, attr_type='email', attr_data="*****@*****.**"), fakedb.UserInfo(uid=2, attr_type='email', attr_data="*****@*****.**") ]) def _getInterestedUsers(): # 'narrator' in this case is the owner, which tests the lookup return ["Big Bob <*****@*****.**>", "narrator"] build1.getInterestedUsers = _getInterestedUsers build2.getInterestedUsers = _getInterestedUsers def _getResponsibleUsers(): return ["Big Bob <*****@*****.**>"] build1.getResponsibleUsers = _getResponsibleUsers build2.getResponsibleUsers = _getResponsibleUsers # fake sourcestamp with relevant user bits ss1 = Mock(name="sourcestamp") fake_change1 = Mock(name="change") fake_change1.number = 9123 ss1.changes = [fake_change1] ss1.patch, ss1.addPatch = None, None ss2 = Mock(name="sourcestamp") fake_change2 = Mock(name="change") fake_change2.number = 9124 ss2.changes = [fake_change2] ss2.patch, ss1.addPatch = None, None def fakeGetSS(ss): return lambda: ss build1.getSourceStamp = fakeGetSS(ss1) build2.getSourceStamp = fakeGetSS(ss2) mn.parent = self self.status = mn.master_status = mn.buildMessageDict = Mock() mn.master_status.getBuilder = fakeGetBuilder mn.buildMessageDict.return_value = {"body": "body", "type": "text"} mn.buildMessage(builder.name, [build1, build2], build1.result) self.assertEqual(m['To'], "[email protected], [email protected]")