Merge lp:~jml/launchpad/split-mail-and-summary into lp:launchpad

Proposed by Jonathan Lange
Status: Merged
Approved by: Robert Collins
Approved revision: no longer in the source branch.
Merged at revision: 11596
Proposed branch: lp:~jml/launchpad/split-mail-and-summary
Merge into: lp:launchpad
Diff against target: 551 lines (+259/-46)
3 files modified
lib/devscripts/ec2test/remote.py (+87/-15)
lib/devscripts/ec2test/tests/test_remote.py (+168/-30)
lib/lp/services/job/tests/test_runner.py (+4/-1)
To merge this branch: bzr merge lp:~jml/launchpad/split-mail-and-summary
Reviewer Review Type Date Requested Status
Robert Collins (community) Approve
Review via email: mp+36111@code.launchpad.net

Commit message

Improve the email sent by ec2 test.

Description of the change

Hello,

This branch changes the email sent by ec2 test. Rather than being a mere lump of stuff that we have collected as summary, it's a custom built message derived from the test results.

Examples:
  * http://paste.ubuntu.com/497557/
  * http://paste.ubuntu.com/497555/

Implementation-wise, the main trick is to change EC2Tester, the object responsible for running tests and gathering results, to send its TestResult object back to the logger, rather than a simple boolean. The Logger was then changed to use the new format_result method to compose the result email, rather than simply dumping the summary.

The details:
  * If the test child process exits with a non-zero code and we have no test results, we raise an error, since it's likely to be something weird.

  * Included in the diff are changes from a merge proposal that I should have applied before landing my previous branch. Mostly stylistic.

  * Some tests were incorrect. They used a dictionary to fake an email message, but had the subject stored under the wrong key ("Subject:" rather than "Subject").

I look forward to your review.

jml

To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

This is nice.

A few thoughts for now, or later, or never.

Perhaps testrepository would make this a lot smaller? Like tiny?

Perhaps Fixtures would make some of the test machinery a little cleaner. (we can use them in LP).

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/devscripts/ec2test/remote.py'
--- lib/devscripts/ec2test/remote.py 2010-08-27 14:27:22 +0000
+++ lib/devscripts/ec2test/remote.py 2010-09-22 13:36:00 +0000
@@ -28,6 +28,7 @@
28import optparse28import optparse
29import os29import os
30import pickle30import pickle
31from StringIO import StringIO
31import subprocess32import subprocess
32import sys33import sys
33import tempfile34import tempfile
@@ -49,6 +50,15 @@
49import subunit50import subunit
5051
5152
53class NonZeroExitCode(Exception):
54 """Raised when the child process exits with a non-zero exit code."""
55
56 def __init__(self, retcode):
57 super(NonZeroExitCode, self).__init__(
58 'Test process died with exit code %r, but no tests failed.'
59 % (retcode,))
60
61
52class SummaryResult(unittest.TestResult):62class SummaryResult(unittest.TestResult):
53 """Test result object used to generate the summary."""63 """Test result object used to generate the summary."""
5464
@@ -271,12 +281,16 @@
271 self._logger.prepare()281 self._logger.prepare()
272 try:282 try:
273 popen = self._spawn_test_process()283 popen = self._spawn_test_process()
274 self._gather_test_output(popen.stdout, self._logger)284 result = self._gather_test_output(popen.stdout, self._logger)
275 exit_status = popen.wait()285 retcode = popen.wait()
286 # The process could have an error not indicated by an actual test
287 # result nor by a raised exception
288 if result.wasSuccessful() and retcode:
289 raise NonZeroExitCode(retcode)
276 except:290 except:
277 self._logger.error_in_testrunner(sys.exc_info())291 self._logger.error_in_testrunner(sys.exc_info())
278 else:292 else:
279 self._logger.got_result(not exit_status)293 self._logger.got_result(result)
280294
281 def _gather_test_output(self, input_stream, logger):295 def _gather_test_output(self, input_stream, logger):
282 """Write the testrunner output to the logs."""296 """Write the testrunner output to the logs."""
@@ -287,6 +301,7 @@
287 subunit_server.lineReceived(line)301 subunit_server.lineReceived(line)
288 logger.got_line(line)302 logger.got_line(line)
289 summary_stream.flush()303 summary_stream.flush()
304 return result
290305
291306
292class Request:307class Request:
@@ -327,6 +342,57 @@
327 """Actually send 'message'."""342 """Actually send 'message'."""
328 self._smtp_connection.send_email(message)343 self._smtp_connection.send_email(message)
329344
345 def _format_test_list(self, header, tests):
346 if not tests:
347 return []
348 tests = [' ' + test.id() for test, error in tests]
349 return [header, '-' * len(header)] + tests + ['']
350
351 def format_result(self, result, start_time, end_time):
352 duration = end_time - start_time
353 output = [
354 'Tests started at approximately %s' % start_time,
355 ]
356 source = self.get_source_details()
357 if source:
358 output.append('Source: %s r%s' % source)
359 target = self.get_target_details()
360 if target:
361 output.append('Target: %s r%s' % target)
362 output.extend([
363 '',
364 '%s tests run in %s, %s failures, %s errors' % (
365 result.testsRun, duration, len(result.failures),
366 len(result.errors)),
367 '',
368 ])
369
370 bad_tests = (
371 self._format_test_list('Failing tests', result.failures) +
372 self._format_test_list('Tests with errors', result.errors))
373 output.extend(bad_tests)
374
375 if bad_tests:
376 full_error_stream = StringIO()
377 copy_result = SummaryResult(full_error_stream)
378 for test, error in result.failures:
379 full_error_stream.write(
380 copy_result._formatError('FAILURE', test, error))
381 for test, error in result.errors:
382 full_error_stream.write(
383 copy_result._formatError('ERROR', test, error))
384 output.append(full_error_stream.getvalue())
385
386 subject = self._get_pqm_subject()
387 if subject:
388 if result.wasSuccessful():
389 output.append('SUBMITTED TO PQM:')
390 else:
391 output.append('**NOT** submitted to PQM:')
392 output.extend([subject, ''])
393 output.extend(['(See the attached file for the complete log)', ''])
394 return '\n'.join(output)
395
330 def get_target_details(self):396 def get_target_details(self):
331 """Return (branch_url, revno) for trunk."""397 """Return (branch_url, revno) for trunk."""
332 branch = bzrlib.branch.Branch.open(self._local_branch_path)398 branch = bzrlib.branch.Branch.open(self._local_branch_path)
@@ -449,12 +515,15 @@
449 continue515 continue
450 yield name, branch.get_parent(), branch.revno()516 yield name, branch.get_parent(), branch.revno()
451517
518 def _get_pqm_subject(self):
519 if not self._pqm_message:
520 return
521 return self._pqm_message.get('Subject')
522
452 def submit_to_pqm(self, successful):523 def submit_to_pqm(self, successful):
453 """Submit this request to PQM, if successful & configured to do so."""524 """Submit this request to PQM, if successful & configured to do so."""
454 if not self._pqm_message:525 subject = self._get_pqm_subject()
455 return526 if subject and successful:
456 subject = self._pqm_message.get('Subject')
457 if successful:
458 self._send_email(self._pqm_message)527 self._send_email(self._pqm_message)
459 return subject528 return subject
460529
@@ -491,6 +560,9 @@
491 self._index_filename = index_filename560 self._index_filename = index_filename
492 self._request = request561 self._request = request
493 self._echo_to_stdout = echo_to_stdout562 self._echo_to_stdout = echo_to_stdout
563 # Actually set by prepare(), but setting to a dummy value to make
564 # testing easier.
565 self._start_time = datetime.datetime.utcnow()
494566
495 @classmethod567 @classmethod
496 def make_in_directory(cls, www_dir, request, echo_to_stdout):568 def make_in_directory(cls, www_dir, request, echo_to_stdout):
@@ -558,16 +630,16 @@
558 if e.errno == errno.ENOENT:630 if e.errno == errno.ENOENT:
559 return ''631 return ''
560632
561 def got_result(self, successful):633 def got_result(self, result):
562 """The tests are done and the results are known."""634 """The tests are done and the results are known."""
635 self._end_time = datetime.datetime.utcnow()
636 successful = result.wasSuccessful()
563 self._handle_pqm_submission(successful)637 self._handle_pqm_submission(successful)
564 if self._request.wants_email:638 if self._request.wants_email:
565 self._write_to_filename(639 email_text = self._request.format_result(
566 self._summary_filename,640 result, self._start_time, self._end_time)
567 '\n(See the attached file for the complete log)\n')
568 summary = self.get_summary_contents()
569 full_log_gz = gzip_data(self.get_full_log_contents())641 full_log_gz = gzip_data(self.get_full_log_contents())
570 self._request.send_report_email(successful, summary, full_log_gz)642 self._request.send_report_email(successful, email_text, full_log_gz)
571643
572 def _handle_pqm_submission(self, successful):644 def _handle_pqm_submission(self, successful):
573 subject = self._request.submit_to_pqm(successful)645 subject = self._request.submit_to_pqm(successful)
@@ -614,9 +686,9 @@
614 return self._write_to_filename(686 return self._write_to_filename(
615 self._index_filename, textwrap.dedent(html))687 self._index_filename, textwrap.dedent(html))
616688
689 self._start_time = datetime.datetime.utcnow()
617 msg = 'Tests started at approximately %(now)s UTC' % {690 msg = 'Tests started at approximately %(now)s UTC' % {
618 'now': datetime.datetime.utcnow().strftime(691 'now': self._start_time.strftime('%a, %d %b %Y %H:%M:%S')}
619 '%a, %d %b %Y %H:%M:%S')}
620 add_to_html('''\692 add_to_html('''\
621 <html>693 <html>
622 <head>694 <head>
623695
=== modified file 'lib/devscripts/ec2test/tests/test_remote.py'
--- lib/devscripts/ec2test/tests/test_remote.py 2010-08-27 14:27:22 +0000
+++ lib/devscripts/ec2test/tests/test_remote.py 2010-09-22 13:36:00 +0000
@@ -5,6 +5,8 @@
55
6__metaclass__ = type6__metaclass__ = type
77
8from datetime import datetime, timedelta
9import doctest
8from email.mime.application import MIMEApplication10from email.mime.application import MIMEApplication
9from email.mime.text import MIMEText11from email.mime.text import MIMEText
10import gzip12import gzip
@@ -21,9 +23,10 @@
21from bzrlib.config import GlobalConfig23from bzrlib.config import GlobalConfig
22from bzrlib.tests import TestCaseWithTransport24from bzrlib.tests import TestCaseWithTransport
2325
24from testtools import TestCase26from testtools import TestCase, TestResult
25from testtools.content import Content27from testtools.content import Content
26from testtools.content_type import ContentType28from testtools.content_type import ContentType
29from testtools.matchers import DocTestMatches
2730
28from devscripts.ec2test.remote import (31from devscripts.ec2test.remote import (
29 EC2Runner,32 EC2Runner,
@@ -75,7 +78,8 @@
75 """Make a request to test, specifying only things we care about.78 """Make a request to test, specifying only things we care about.
7679
77 Note that the returned request object will not ever send email, but80 Note that the returned request object will not ever send email, but
78 will instead log "sent" emails to `request.emails_sent`.81 will instead append "sent" emails to the list provided here as
82 'emails_sent'.
79 """83 """
80 if trunk is None:84 if trunk is None:
81 trunk = self.make_trunk()85 trunk = self.make_trunk()
@@ -109,8 +113,6 @@
109 def make_tester(self, logger=None, test_directory=None, test_options=()):113 def make_tester(self, logger=None, test_directory=None, test_options=()):
110 if not logger:114 if not logger:
111 logger = self.make_logger()115 logger = self.make_logger()
112 if not test_directory:
113 test_directory = 'unspecified-test-directory'
114 return LaunchpadTester(logger, test_directory, test_options)116 return LaunchpadTester(logger, test_directory, test_options)
115117
116 def make_logger(self, request=None, echo_to_stdout=False):118 def make_logger(self, request=None, echo_to_stdout=False):
@@ -472,14 +474,14 @@
472 def test_submit_to_pqm_unsuccessful(self):474 def test_submit_to_pqm_unsuccessful(self):
473 # submit_to_pqm returns the subject of the PQM mail even if it's475 # submit_to_pqm returns the subject of the PQM mail even if it's
474 # handling a failed test run.476 # handling a failed test run.
475 message = {'Subject:': 'My PQM message'}477 message = {'Subject': 'My PQM message'}
476 req = self.make_request(pqm_message=message)478 req = self.make_request(pqm_message=message)
477 subject = req.submit_to_pqm(successful=False)479 subject = req.submit_to_pqm(successful=False)
478 self.assertIs(message.get('Subject'), subject)480 self.assertIs(message.get('Subject'), subject)
479481
480 def test_submit_to_pqm_unsuccessful_no_email(self):482 def test_submit_to_pqm_unsuccessful_no_email(self):
481 # submit_to_pqm doesn't send any email if the run was unsuccessful.483 # submit_to_pqm doesn't send any email if the run was unsuccessful.
482 message = {'Subject:': 'My PQM message'}484 message = {'Subject': 'My PQM message'}
483 log = []485 log = []
484 req = self.make_request(pqm_message=message, emails_sent=log)486 req = self.make_request(pqm_message=message, emails_sent=log)
485 req.submit_to_pqm(successful=False)487 req.submit_to_pqm(successful=False)
@@ -487,7 +489,7 @@
487489
488 def test_submit_to_pqm_successful(self):490 def test_submit_to_pqm_successful(self):
489 # submit_to_pqm returns the subject of the PQM mail.491 # submit_to_pqm returns the subject of the PQM mail.
490 message = {'Subject:': 'My PQM message'}492 message = {'Subject': 'My PQM message'}
491 log = []493 log = []
492 req = self.make_request(pqm_message=message, emails_sent=log)494 req = self.make_request(pqm_message=message, emails_sent=log)
493 subject = req.submit_to_pqm(successful=True)495 subject = req.submit_to_pqm(successful=True)
@@ -558,6 +560,124 @@
558 self.assertEqual(560 self.assertEqual(
559 expected_part.get_payload(), observed_part.get_payload())561 expected_part.get_payload(), observed_part.get_payload())
560562
563 def test_format_result_success(self):
564 class SomeTest(TestCase):
565 def test_a(self):
566 pass
567 def test_b(self):
568 pass
569 def test_c(self):
570 pass
571 test = unittest.TestSuite(map(SomeTest, ['test_' + x for x in 'abc']))
572 result = TestResult()
573 test.run(result)
574 tree = self.make_trunk()
575 # Fake a merge, giving silly revision ids.
576 tree.add_pending_merge('foo', 'bar')
577 req = self.make_request(
578 branch_url='https://example.com/bzr/thing', revno=42, trunk=tree)
579 source_branch, source_revno = req.get_source_details()
580 target_branch, target_revno = req.get_target_details()
581 start_time = datetime.utcnow()
582 end_time = start_time + timedelta(hours=1)
583 data = {
584 'source_branch': source_branch,
585 'source_revno': source_revno,
586 'target_branch': target_branch,
587 'target_revno': target_revno,
588 'start_time': str(start_time),
589 'duration': str(end_time - start_time),
590 'num_tests': result.testsRun,
591 'num_failures': len(result.failures),
592 'num_errors': len(result.errors),
593 }
594 result_text = req.format_result(result, start_time, end_time)
595 self.assertThat(
596 result_text, DocTestMatches("""\
597Tests started at approximately %(start_time)s
598Source: %(source_branch)s r%(source_revno)s
599Target: %(target_branch)s r%(target_revno)s
600<BLANKLINE>
601%(num_tests)s tests run in %(duration)s, %(num_failures)s failures, %(num_errors)s errors
602<BLANKLINE>
603(See the attached file for the complete log)
604""" % data, doctest.REPORT_NDIFF | doctest.ELLIPSIS))
605
606 def test_format_result_with_errors(self):
607 class SomeTest(TestCase):
608 def test_ok(self):
609 pass
610 def test_fail(self):
611 self.fail("oh no")
612 def test_error(self):
613 1/0
614 fail_test = SomeTest('test_fail')
615 error_test = SomeTest('test_error')
616 test = unittest.TestSuite(
617 [fail_test, error_test, SomeTest('test_ok')])
618 result = TestResult()
619 test.run(result)
620 tree = self.make_trunk()
621 # Fake a merge, giving silly revision ids.
622 tree.add_pending_merge('foo', 'bar')
623 req = self.make_request(
624 branch_url='https://example.com/bzr/thing', revno=42, trunk=tree)
625 source_branch, source_revno = req.get_source_details()
626 target_branch, target_revno = req.get_target_details()
627 start_time = datetime.utcnow()
628 end_time = start_time + timedelta(hours=1)
629 data = {
630 'source_branch': source_branch,
631 'source_revno': source_revno,
632 'target_branch': target_branch,
633 'target_revno': target_revno,
634 'start_time': str(start_time),
635 'duration': str(end_time - start_time),
636 'fail_id': fail_test.id(),
637 'error_id': error_test.id(),
638 'num_tests': result.testsRun,
639 'num_failures': len(result.failures),
640 'num_errors': len(result.errors),
641 }
642 result_text = req.format_result(result, start_time, end_time)
643 self.assertThat(
644 result_text, DocTestMatches("""\
645Tests started at approximately %(start_time)s
646Source: %(source_branch)s r%(source_revno)s
647Target: %(target_branch)s r%(target_revno)s
648<BLANKLINE>
649%(num_tests)s tests run in %(duration)s, %(num_failures)s failures, %(num_errors)s errors
650<BLANKLINE>
651Failing tests
652-------------
653 %(fail_id)s
654<BLANKLINE>
655Tests with errors
656-----------------
657 %(error_id)s
658<BLANKLINE>
659======================================================================
660FAILURE: test_fail...
661----------------------------------------------------------------------
662Text attachment: traceback
663------------
664Traceback (most recent call last):
665...
666------------
667<BLANKLINE>
668======================================================================
669ERROR: test_error...
670----------------------------------------------------------------------
671Text attachment: traceback
672------------
673Traceback (most recent call last):
674...
675------------
676<BLANKLINE>
677<BLANKLINE>
678(See the attached file for the complete log)
679""" % data, doctest.REPORT_NDIFF | doctest.ELLIPSIS))
680
561681
562class TestWebTestLogger(TestCaseWithTransport, RequestHelpers):682class TestWebTestLogger(TestCaseWithTransport, RequestHelpers):
563683
@@ -672,11 +792,13 @@
672 False, "who-cares.pid", False, smtp_connection, emails=emails)792 False, "who-cares.pid", False, smtp_connection, emails=emails)
673793
674 def test_run(self):794 def test_run(self):
795 # EC2Runner.run() runs the given function, passing through whatever
796 # arguments and keyword arguments it has been given.
675 calls = []797 calls = []
676 runner = self.make_ec2runner()798 runner = self.make_ec2runner()
677 runner.run(799 def function(*args, **kwargs):
678 "boring test method",800 calls.append((args, kwargs))
679 lambda *a, **kw: calls.append((a, kw)), "foo", "bar", baz="qux")801 runner.run("boring test method", function, "foo", "bar", baz="qux")
680 self.assertEqual([(("foo", "bar"), {'baz': 'qux'})], calls)802 self.assertEqual([(("foo", "bar"), {'baz': 'qux'})], calls)
681803
682 def test_email_on_failure_no_emails(self):804 def test_email_on_failure_no_emails(self):
@@ -688,7 +810,7 @@
688 self.assertEqual([], log)810 self.assertEqual([], log)
689811
690 def test_email_on_failure_some_emails(self):812 def test_email_on_failure_some_emails(self):
691 # If no emails are specified, then no email is sent on failure.813 # If emails *are* specified, then an email is sent on failure.
692 log = []814 log = []
693 runner = self.make_ec2runner(815 runner = self.make_ec2runner(
694 email_log=log, emails=["foo@example.com"])816 email_log=log, emails=["foo@example.com"])
@@ -701,6 +823,7 @@
701 self.assertIn('ZeroDivisionError', str(message))823 self.assertIn('ZeroDivisionError', str(message))
702824
703 def test_email_with_launchpad_tester_failure(self):825 def test_email_with_launchpad_tester_failure(self):
826 # LaunchpadTester sends email on catastrophic failure.
704 email_log = []827 email_log = []
705 to_emails = ['foo@example.com']828 to_emails = ['foo@example.com']
706 request = self.make_request(emails=to_emails, emails_sent=email_log)829 request = self.make_request(emails=to_emails, emails_sent=email_log)
@@ -767,18 +890,37 @@
767 def get_body_text(self, email):890 def get_body_text(self, email):
768 return email.get_payload()[0].get_payload()891 return email.get_payload()[0].get_payload()
769892
893 def make_empty_result(self):
894 return TestResult()
895
896 def make_successful_result(self):
897 result = self.make_empty_result()
898 result.startTest(self)
899 result.stopTest(self)
900 return result
901
902 def make_failing_result(self):
903 result = self.make_empty_result()
904 result.startTest(self)
905 try:
906 1/0
907 except ZeroDivisionError:
908 result.addError(self, sys.exc_info())
909 result.stopTest(self)
910 return result
911
770 def test_success_no_emails(self):912 def test_success_no_emails(self):
771 log = []913 log = []
772 request = self.make_request(emails=[], emails_sent=log)914 request = self.make_request(emails=[], emails_sent=log)
773 logger = self.make_logger(request=request)915 logger = self.make_logger(request=request)
774 logger.got_result(True)916 logger.got_result(self.make_successful_result())
775 self.assertEqual([], log)917 self.assertEqual([], log)
776918
777 def test_failure_no_emails(self):919 def test_failure_no_emails(self):
778 log = []920 log = []
779 request = self.make_request(emails=[], emails_sent=log)921 request = self.make_request(emails=[], emails_sent=log)
780 logger = self.make_logger(request=request)922 logger = self.make_logger(request=request)
781 logger.got_result(False)923 logger.got_result(self.make_failing_result())
782 self.assertEqual([], log)924 self.assertEqual([], log)
783925
784 def test_submits_to_pqm_on_success(self):926 def test_submits_to_pqm_on_success(self):
@@ -787,7 +929,7 @@
787 request = self.make_request(929 request = self.make_request(
788 emails=[], pqm_message=message, emails_sent=log)930 emails=[], pqm_message=message, emails_sent=log)
789 logger = self.make_logger(request=request)931 logger = self.make_logger(request=request)
790 logger.got_result(True)932 logger.got_result(self.make_successful_result())
791 self.assertEqual([message], log)933 self.assertEqual([message], log)
792934
793 def test_records_pqm_submission_in_email(self):935 def test_records_pqm_submission_in_email(self):
@@ -796,20 +938,20 @@
796 request = self.make_request(938 request = self.make_request(
797 emails=['foo@example.com'], pqm_message=message, emails_sent=log)939 emails=['foo@example.com'], pqm_message=message, emails_sent=log)
798 logger = self.make_logger(request=request)940 logger = self.make_logger(request=request)
799 logger.got_result(True)941 logger.got_result(self.make_successful_result())
800 [pqm_message, user_message] = log942 [pqm_message, user_message] = log
801 self.assertEqual(message, pqm_message)943 self.assertEqual(message, pqm_message)
802 self.assertIn(944 self.assertIn(
803 'SUBMITTED TO PQM:\n%s' % (message['Subject'],),945 'SUBMITTED TO PQM:\n%s' % (message['Subject'],),
804 self.get_body_text(user_message))946 self.get_body_text(user_message))
805947
806 def test_doesnt_submit_to_pqm_no_failure(self):948 def test_doesnt_submit_to_pqm_on_failure(self):
807 log = []949 log = []
808 message = {'Subject': 'foo'}950 message = {'Subject': 'foo'}
809 request = self.make_request(951 request = self.make_request(
810 emails=[], pqm_message=message, emails_sent=log)952 emails=[], pqm_message=message, emails_sent=log)
811 logger = self.make_logger(request=request)953 logger = self.make_logger(request=request)
812 logger.got_result(False)954 logger.got_result(self.make_failing_result())
813 self.assertEqual([], log)955 self.assertEqual([], log)
814956
815 def test_records_non_pqm_submission_in_email(self):957 def test_records_non_pqm_submission_in_email(self):
@@ -818,7 +960,7 @@
818 request = self.make_request(960 request = self.make_request(
819 emails=['foo@example.com'], pqm_message=message, emails_sent=log)961 emails=['foo@example.com'], pqm_message=message, emails_sent=log)
820 logger = self.make_logger(request=request)962 logger = self.make_logger(request=request)
821 logger.got_result(False)963 logger.got_result(self.make_failing_result())
822 [user_message] = log964 [user_message] = log
823 self.assertIn(965 self.assertIn(
824 '**NOT** submitted to PQM:\n%s' % (message['Subject'],),966 '**NOT** submitted to PQM:\n%s' % (message['Subject'],),
@@ -829,29 +971,25 @@
829 request = self.make_request(971 request = self.make_request(
830 emails=['foo@example.com'], emails_sent=log)972 emails=['foo@example.com'], emails_sent=log)
831 logger = self.make_logger(request=request)973 logger = self.make_logger(request=request)
832 logger.got_result(False)974 logger.got_result(self.make_failing_result())
833 [user_message] = log975 [user_message] = log
834 self.assertIn(976 self.assertIn(
835 '(See the attached file for the complete log)\n',977 '(See the attached file for the complete log)\n',
836 self.get_body_text(user_message))978 self.get_body_text(user_message))
837979
838 def test_email_body_is_summary(self):980 def test_email_body_is_format_result(self):
839 # The body of the email sent to the user is the summary file.981 # The body of the email sent to the user is the summary file.
840 # XXX: JonathanLange 2010-08-17: We actually want to change this
841 # behaviour so that the summary log stays roughly the same as it is
842 # now and can vary independently from the contents of the sent
843 # email. We probably just want the contents of the email to be a list
844 # of failing tests.
845 log = []982 log = []
846 request = self.make_request(983 request = self.make_request(
847 emails=['foo@example.com'], emails_sent=log)984 emails=['foo@example.com'], emails_sent=log)
848 logger = self.make_logger(request=request)985 logger = self.make_logger(request=request)
849 logger.get_summary_stream().write('bar\nbaz\nqux\n')986 result = self.make_failing_result()
850 logger.got_result(False)987 logger.got_result(result)
851 [user_message] = log988 [user_message] = log
852 self.assertEqual(989 self.assertEqual(
853 'bar\nbaz\nqux\n\n(See the attached file for the complete log)\n',990 request.format_result(
854 self.get_body_text(user_message))991 result, logger._start_time, logger._end_time),
992 self.get_body_text(user_message).decode('quoted-printable'))
855993
856 def test_gzip_of_full_log_attached(self):994 def test_gzip_of_full_log_attached(self):
857 # The full log is attached to the email.995 # The full log is attached to the email.
@@ -861,7 +999,7 @@
861 logger = self.make_logger(request=request)999 logger = self.make_logger(request=request)
862 logger.got_line("output from test process\n")1000 logger.got_line("output from test process\n")
863 logger.got_line("more output\n")1001 logger.got_line("more output\n")
864 logger.got_result(False)1002 logger.got_result(self.make_successful_result())
865 [user_message] = log1003 [user_message] = log
866 [body, attachment] = user_message.get_payload()1004 [body, attachment] = user_message.get_payload()
867 self.assertEqual('application/x-gzip', attachment['Content-Type'])1005 self.assertEqual('application/x-gzip', attachment['Content-Type'])
8681006
=== modified file 'lib/lp/services/job/tests/test_runner.py'
--- lib/lp/services/job/tests/test_runner.py 2010-09-15 22:09:12 +0000
+++ lib/lp/services/job/tests/test_runner.py 2010-09-22 13:36:00 +0000
@@ -216,7 +216,10 @@
216 # aborted, it is None.216 # aborted, it is None.
217 self.assertIs(None, job.job.log)217 self.assertIs(None, job.job.log)
218218
219 def test_runAll_mails_oopses(self):219 # XXX michaeln 2010-09-22 bug=644984
220 # This is failing sometimes, but has not yet been reproduced
221 # locally.
222 def _disabled_test_runAll_mails_oopses(self):
220 """Email interested parties about OOPses."""223 """Email interested parties about OOPses."""
221 job_1, job_2 = self.makeTwoJobs()224 job_1, job_2 = self.makeTwoJobs()
222 def raiseError():225 def raiseError():