Mercurial > bitten > bitten-test
changeset 626:73ed8c171063
Simplify email notification code by removing BuildInfo class
author | mgood |
---|---|
date | Sun, 09 Aug 2009 08:47:19 +0000 |
parents | 98675686ec4d |
children | 0a9f2af1f711 |
files | bitten/notify.py bitten/templates/bitten_notify_email.txt bitten/tests/notify.py |
diffstat | 3 files changed, 93 insertions(+), 157 deletions(-) [+] |
line wrap: on
line diff
--- a/bitten/notify.py +++ b/bitten/notify.py @@ -30,14 +30,14 @@ self.log.debug('Initializing BittenNotify plugin') def notify(self, build=None): - self.log.info('BittenNotify invoked for build %r' % build) - self.log.debug('build status: %s' % build.status) + self.log.info('BittenNotify invoked for build %r', build) + self.log.debug('build status: %s', build.status) if not self._should_notify(build): return - self.log.info('Sending notification for build %r' % build) + self.log.info('Sending notification for build %r', build) try: - email = BittenNotifyEmail(self.env) - email.notify(BuildInfo(self.env, build)) + email = BuildNotifyEmail(self.env) + email.notify(build) except Exception, e: self.log.exception("Failure sending notification for build " "%s: %s", build.id, e) @@ -78,99 +78,77 @@ return [] -class BuildInfo(dict): - """Wraps a Build instance and exposes properties conveniently""" - - readable_states = {Build.SUCCESS:'Successful', Build.FAILURE:'Failed'} - - def __init__(self, env, build): - dict.__init__(self) - self.build = build - self.env = env - self['project_name'] = self.env.project_name - self['id'] = self.build.id - self['status'] = self.readable_states[self.build.status] - self['link'] = self.env.abs_href.build(self.build.config, - self.build.id) - self['config'] = self.build.config - self['slave'] = self.build.slave - self['changeset'] = self.build.rev - self['changesetlink'] = self.env.abs_href.changeset(self.build.rev) - self['author'] = self.get_author(build) - self['errors'] = self.get_errors(build) - self['faillog'] = self.get_faillog(build) - - def get_author(self, build): - if build and build.rev: - changeset = self.env.get_repository().get_changeset(build.rev) - return changeset.author - - def get_failed_steps(self, build): - build_steps = BuildStep.select(self.env, - build=build.id, - status=BuildStep.FAILURE) - return build_steps - - def get_errors(self, build): - errors = '' - for step in self.get_failed_steps(build): - errors += ', '.join(['%s: %s' % (step.name, error) \ - for error in step.errors]) - return errors - - def get_faillog(self, build): - faillog = '' - for step in self.get_failed_steps(build): - build_logs = BuildLog.select(self.env, - build=build.id, - step=step.name) - for log in build_logs: - faillog += '\n'.join(['%5s: %s' % (level, msg) \ - for level, msg in log.messages]) - return faillog - - def __getattr__(self, attr): - return dict.__getitem__(self,attr) - - def __repr__(self): - repr = '' - for k, v in self.items(): - repr += '%s: %s\n' % (k, v) - return repr - - def __str__(self): - return self.repr() - - -class BittenNotifyEmail(NotifyEmail): +class BuildNotifyEmail(NotifyEmail): """Notification of failed builds.""" + readable_states = { + Build.SUCCESS: 'Successful', + Build.FAILURE: 'Failed', + } template_name = 'bitten_notify_email.txt' from_email = 'bitten@localhost' def __init__(self, env): NotifyEmail.__init__(self, env) - def notify(self, build_info): - self.build_info = build_info - self.data = self.build_info - subject = '[%s Build] %s [%s] %s' % (self.build_info.status, - self.env.project_name, - self.build_info.changeset, - self.build_info.config) - stream = self.template.generate(**self.data) - body = stream.render('text') - self.env.log.debug('notification: %s' % body ) - NotifyEmail.notify(self, self.build_info.id, subject) + def notify(self, build): + self.build = build + self.data.update(self.template_data()) + subject = '[%s Build] %s [%s] %s' % (self.readable_states[build.status], + self.env.project_name, + self.build.rev, + self.build.config) + NotifyEmail.notify(self, self.build.id, subject) def get_recipients(self, resid): - to = [self.build_info.author] + to = [self.get_author()] cc = [] return (to, cc) - def send(self, torcpts, ccrcpts, mime_headers={}): + def send(self, torcpts, ccrcpts): mime_headers = { - 'X-Trac-Build-ID': str(self.build_info.id), - 'X-Trac-Build-URL': self.build_info.link, + 'X-Trac-Build-ID': str(self.build.id), + 'X-Trac-Build-URL': self.build_link(), } NotifyEmail.send(self, torcpts, ccrcpts, mime_headers) + + def build_link(self): + return self.env.abs_href.build(self.build.config, self.build.id) + + def template_data(self): + failed_steps = BuildStep.select(self.env, build=self.build.id, + status=BuildStep.FAILURE) + change = self.get_changeset() + return { + 'build': { + 'id': self.build.id, + 'status': self.readable_states[self.build.status], + 'link': self.build_link(), + 'config': self.build.config, + 'slave': self.build.slave, + 'failed_steps': [{ + 'name': step.name, + 'description': step.description, + 'errors': step.errors, + 'log_messages': self.get_all_log_messages_for_step(step), + } for step in failed_steps], + }, + 'change': { + 'rev': change.rev, + 'link': self.env.abs_href.changeset(change.rev), + 'author': change.author, + }, + } + + def get_all_log_messages_for_step(self, step): + messages = [] + for log in BuildLog.select(self.env, build=self.build.id, + step=step.name): + messages.extend(log.messages) + return messages + + def get_changeset(self): + return self.env.get_repository().get_changeset(self.build.rev) + + def get_author(self): + return self.get_changeset().author
--- a/bitten/templates/bitten_notify_email.txt +++ b/bitten/templates/bitten_notify_email.txt @@ -1,15 +1,26 @@ -${status} build of ${project_name} [${changeset}] +$build.status build of $project.name [$change.rev] --------------------------------------------------------------------- - Changeset: ${changeset} - <${changesetlink}> - Committed by: ${author} + Changeset: $change.rev - <$change.link> + Committed by: $change.author - Build Configuration: ${config} - Build Slave: ${slave} - Build Number: ${id} - <${link}> + Build Configuration: $build.config + Build Slave: $build.slave + Build Number: $build.id - <${build.link}> +#if build.failed_steps - Failed Steps: ${errors} - Failure Log: + Failures: +#for step in build.failed_steps + Step: $step.name + Errors: ${', '.join(step.errors)} + Log: + #for lvl, msg in step.log_messages + [${lvl.upper().ljust(8)}] $msg + #end +#end +#end -${faillog} - +-- +Build URL: <$build.link> +$project.name <${project.url or abs_href()}> +$project.descr
--- a/bitten/tests/notify.py +++ b/bitten/tests/notify.py @@ -12,13 +12,13 @@ from trac.test import EnvironmentStub, Mock from trac.web.session import DetachedSession from bitten.model import schema, Build, BuildStep, BuildLog -from bitten.notify import BittenNotify, BittenNotifyEmail, BuildInfo +from bitten.notify import BittenNotify, BuildNotifyEmail class BittenNotifyBaseTest(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(enable=['trac.*', 'bitten.notify.*']) - repos = Mock(get_changeset=lambda rev: Mock(author='author')) + repos = Mock(get_changeset=lambda rev: Mock(author='author', rev=rev)) self.env.get_repository = lambda authname=None: repos db = self.env.get_db_cnx() @@ -62,52 +62,7 @@ self.env.config.set(option.section, option.name, value) -class BuildInfoTest(BittenNotifyBaseTest): - """unit tests for BuildInfo class""" - - def setUp(self): - BittenNotifyBaseTest.setUp(self) - #fixture - self.failed_build = Build(self.env, - config = 'config', - slave = 'slave', - rev = 10, - status = Build.FAILURE) - self.failed_build.id = 1 - self.successful_build = Build(self.env, status = Build.SUCCESS) - self.successful_build.id = 2 - step = BuildStep(self.env, - build = 1, - name = 'test', - status = BuildStep.FAILURE) - step.errors = ['msg'] - step.insert() - log = BuildLog(self.env, build = 1, step = 'test') - log.messages = [('info','msg')] - log.insert() - - def test_exposed_properties(self): - build_info = BuildInfo(self.env, self.failed_build) - self.assertEquals(self.failed_build.id, build_info.id) - self.assertEquals('Failed', build_info.status) - self.assertEquals('http://example.org/trac.cgi/build/config/1', - build_info.link) - self.assertEquals('config', build_info.config) - self.assertEquals('slave', build_info.slave) - self.assertEquals('10', build_info.changeset) - self.assertEquals('http://example.org/trac.cgi/changeset/10', - build_info.changesetlink) - self.assertEquals('author', build_info.author) - self.assertEquals('test: msg', build_info.errors) - self.assertEquals(' info: msg', build_info.faillog) - - def test_exposed_properties_on_successful_build(self): - build_info = BuildInfo(self.env, self.successful_build) - self.assertEquals(self.successful_build.id, build_info.id) - self.assertEquals('Successful', build_info.status) - - -class BittenNotifyEmailTest(BittenNotifyBaseTest): +class BuildNotifyEmailTest(BittenNotifyBaseTest): """unit tests for BittenNotifyEmail class""" def setUp(self): BittenNotifyBaseTest.setUp(self) @@ -117,32 +72,24 @@ self.notifications_sent_to = to def noop(*args, **kw): pass - self.email = Mock(BittenNotifyEmail, self.env, + self.email = Mock(BuildNotifyEmail, self.env, begin_send=noop, finish_send=noop, send=send) - self.build_info = BuildInfo(self.env, - Build(self.env, status=Build.SUCCESS, rev=123)) + self.build = Build(self.env, status=Build.SUCCESS, rev=123) def test_notification_is_sent_to_author(self): - self.email.notify(self.build_info) + self.email.notify(self.build) self.assertTrue('author' in self.notifications_sent_to, 'Recipient list should contain the author') - def add_known_user(self, username, name, email): - session = DetachedSession(self.env, username) - if name is not None: - session['name'] = name - if email is not None: - session['email'] = email - session.save() + # TODO functional tests of generated mails def suite(): suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(BittenNotifyTest,'test')) - suite.addTest(unittest.makeSuite(BuildInfoTest,'test')) - suite.addTest(unittest.makeSuite(BittenNotifyEmailTest,'test')) + suite.addTest(unittest.makeSuite(BittenNotifyTest, 'test')) + suite.addTest(unittest.makeSuite(BuildNotifyEmailTest, 'test')) return suite if __name__ == '__main__':