← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~stevenk/launchpad/bpb-currentcomponent-assertion-part-5 into lp:launchpad

 

Steve Kowalik has proposed merging lp:~stevenk/launchpad/bpb-currentcomponent-assertion-part-5 into lp:launchpad with lp:~stevenk/launchpad/bpb-currentcomponent-assertion-part-4 as a prerequisite.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~stevenk/launchpad/bpb-currentcomponent-assertion-part-5/+merge/46852

Building on the work in https://code.launchpad.net/~stevenk/launchpad/bpb-currentcomponent-assertion-part-4/+merge/46535, this branch completely kills build-notification.txt moving it into unit tests.

I would like to apologise for the length of this branch, but I can't win if I'm deleting 634 lines of doctest.
-- 
https://code.launchpad.net/~stevenk/launchpad/bpb-currentcomponent-assertion-part-5/+merge/46852
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~stevenk/launchpad/bpb-currentcomponent-assertion-part-5 into lp:launchpad.
=== removed file 'lib/lp/soyuz/doc/build-notification.txt'
--- lib/lp/soyuz/doc/build-notification.txt	2011-01-12 23:07:40 +0000
+++ lib/lp/soyuz/doc/build-notification.txt	1970-01-01 00:00:00 +0000
@@ -1,629 +0,0 @@
-=========================
-Build Notification System
-=========================
-
-IBinaryPackageBuild instance implements the 'notify' method which sends
-a status report about the current record to the
-ISourcePackageRelease.creator and the package uploader's preferred email
-address.
-
-We rely on get_contact_email_addresses() to DTRT, either use the
-Team.contactaddress of the Launchpad Buildd Celebrity or the
-preferredemail of its members.
-
-The report is based on the emailtemplate/build-notification.txt.
-
-All build states are supported by the current implementation, however
-only failures should be reported this time.
-
-  >>> from lp.soyuz.interfaces.binarypackagebuild import (
-  ...     IBinaryPackageBuildSet)
-  >>> from lp.testing.mail_helpers import pop_notifications
-  >>> buildset = getUtility(IBinaryPackageBuildSet)
-
-To avoid consequences of modified sampledata, specially if we add
-more builds for test we are going to pick a specific build record
-in a known state to perform the tests.
-
-Notification for a FAILEDTOBUILD (failed) build record:
-
-  >>> failed_candidate = buildset.getByBuildID(9)
-  >>> failed_candidate.source_package_release.name
-  u'pmount'
-  >>> failed_candidate.status.name
-  'FAILEDTOBUILD'
-  >>> failed_candidate.source_package_release.creator.name
-  u'mark'
-
-  >>> failed_candidate.notify()
-
-Check how many email the system have sent:
-
-  >>> notifications = pop_notifications()
-  >>> len(notifications)
-  3
-
-The members of launchpad-buildd-admins are notified:
-
-  >>> notifications.pop(0)['To']
-  'celso.providelo@xxxxxxxxxxxxx'
-
-  >>> notifications.pop(0)['To']
-  'foo.bar@xxxxxxxxxxxxx'
-
-The maintainer is notified:
-
-  >>> build_notification = notifications.pop(0)
-  >>> build_notification['To']
-  'mark@xxxxxxxxxxx'
-
-Checking subject and extra headers. Build notifications have a set of
-special headers to help filtering on users' mailboxes, they are:
-
- * X-Creator-Recipient: the creator email address which can be
-   notified or not according a configuration parameter.
- * X-Launchpad-Build-{State, Component, Arch}: build record attributes
- * X-Launchpad-PPA: omitted if it's not a PPA build-notification,
-   contains the PPA owner.name otherwise.
-
-  >>> def dump_build_notification(notification):
-  ...     clean_subject = notification['Subject'].replace(
-  ...         '\n','').replace('\t', ' ')
-  ...     build_state = notification['X-Launchpad-Build-State']
-  ...     build_component = notification['X-Launchpad-Build-Component']
-  ...     build_arch = notification['X-Launchpad-Build-Arch']
-  ...     ppa_header = 'X-Launchpad-PPA' in notification.keys()
-  ...     print 'Subject:', clean_subject
-  ...     print 'X-Creator-Recipient:', notification['X-Creator-Recipient']
-  ...     print 'X-Launchpad-Build-State:', build_state
-  ...     print 'X-Launchpad-Build-Component:', build_component
-  ...     print 'X-Launchpad-Build-Arch:', build_arch
-  ...     if ppa_header:
-  ...         print 'X-Launchpad-PPA:', notification['X-Launchpad-PPA']
-  ...     else:
-  ...         print 'X-Launchpad-PPA: omitted'
-
-  >>> dump_build_notification(build_notification)
-  Subject: [Build #9] i386 build of pmount 0.1-1 in ubuntu warty RELEASE
-  X-Creator-Recipient: mark@xxxxxxxxxxx
-  X-Launchpad-Build-State: FAILEDTOBUILD
-  X-Launchpad-Build-Component: main
-  X-Launchpad-Build-Arch: i386
-  X-Launchpad-PPA: omitted
-
-Let's check the notification content.  It should contain:
-
- * The source package name
- * The source package version
- * The build architecture
- * The build state
- * The duration of the build
- * A link to the build log
- * A link to the build page
- * A link to the source page
-
-This same basic content applies to all types of notification.
-
-  >>> notification_body = build_notification.get_payload(decode=True)
-  >>> print notification_body #doctest: -NORMALIZE_WHITESPACE
-  <BLANKLINE>
-   * Source Package: pmount
-   * Version: 0.1-1
-   * Architecture: i386
-   * Archive: ubuntu primary archive
-   * Component: main
-   * State: Failed to build
-   * Duration: three minutes
-   * Build Log: http://launchpad.dev/ubuntu/+source/pmount/0.1-1/+buildjob/9/+files/netapplet-1.0.0.tar.gz
-   * Builder: http://launchpad.dev/builders/bob
-   * Source: http://launchpad.dev/ubuntu/+source/pmount/0.1-1
-  <BLANKLINE>
-  <BLANKLINE>
-  <BLANKLINE>
-  If you want further information about this situation, feel free to
-  contact a member of the Launchpad Buildd Administrators team.
-  <BLANKLINE>
-  --
-  i386 build of pmount 0.1-1 in ubuntu warty RELEASE
-  http://launchpad.dev/ubuntu/+source/pmount/0.1-1/+buildjob/9
-  <BLANKLINE>
-
-Notification for a pending (NEEDSBUILD) build record:
-
-  >>> pending_candidate = buildset.getByBuildID(11)
-  >>> pending_candidate.source_package_release.name
-  u'alsa-utils'
-  >>> pending_candidate.status.name
-  'NEEDSBUILD'
-  >>> pending_candidate.source_package_release.creator.name
-  u'mark'
-
-  >>> pending_candidate.notify()
-
-Check how many email the system have sent:
-
-  >>> notifications = pop_notifications()
-  >>> len(notifications)
-  3
-
-The members of launchpad-buildd-admins are notified:
-
-  >>> notifications.pop(0)['To']
-  'celso.providelo@xxxxxxxxxxxxx'
-
-  >>> notifications.pop(0)['To']
-  'foo.bar@xxxxxxxxxxxxx'
-
-The maintainer is notified:
-
-  >>> build_notification = notifications.pop(0)
-  >>> build_notification['To']
-  'mark@xxxxxxxxxxx'
-
-Checking subject and extra headers:
-
-  >>> dump_build_notification(build_notification)
-  Subject: [Build #11] i386 build of alsa-utils 1.0.9a-4ubuntu1 in ubuntu hoary RELEASE
-  X-Creator-Recipient: mark@xxxxxxxxxxx
-  X-Launchpad-Build-State: NEEDSBUILD
-  X-Launchpad-Build-Component: main
-  X-Launchpad-Build-Arch: i386
-  X-Launchpad-PPA: omitted
-
-Check the notification content:
-
-  >>> notification_body = build_notification.get_payload()
-  >>> print notification_body #doctest: -NORMALIZE_WHITESPACE
-  <BLANKLINE>
-   * Source Package: alsa-utils
-   * Version: 1.0.9a-4ubuntu1
-   * Architecture: i386
-   * Archive: ubuntu primary archive
-   * Component: main
-   * State: Needs building
-   * Duration: not available
-   * Build Log: not available
-   * Builder: not available
-   * Source: http://launchpad.dev/ubuntu/+source/alsa-utils/1.0.9a-4ubuntu1
-  <BLANKLINE>
-  <BLANKLINE>
-  <BLANKLINE>
-  If you want further information about this situation, feel free to
-  contact a member of the Launchpad Buildd Administrators team.
-  <BLANKLINE>
-  --
-  i386 build of alsa-utils 1.0.9a-4ubuntu1 in ubuntu hoary RELEASE
-  http://launchpad.dev/ubuntu/+source/alsa-utils/1.0.9a-4ubuntu1/+buildjob/11
-  <BLANKLINE>
-
-Notification for a BUILDING build record:
-
-  >>> building_candidate = buildset.getByBuildID(8)
-  >>> building_candidate.source_package_release.name
-  u'mozilla-firefox'
-  >>> building_candidate.status.name
-  'BUILDING'
-  >>> building_candidate.source_package_release.creator.name
-  u'mark'
-
-  >>> building_candidate.notify()
-
-Check how many email the system have sent:
-
-  >>> notifications = pop_notifications()
-  >>> len(notifications)
-  3
-
-The members of launchpad-buildd-admins are notified:
-
-  >>> notifications.pop(0)['To']
-  'celso.providelo@xxxxxxxxxxxxx'
-
-  >>> notifications.pop(0)['To']
-  'foo.bar@xxxxxxxxxxxxx'
-
-The maintainer is notified:
-
-  >>> build_notification = notifications.pop(0)
-  >>> build_notification['To']
-  'mark@xxxxxxxxxxx'
-
-Checking subject and extra headers:
-
-  >>> dump_build_notification(build_notification)
-  Subject: [Build #8] i386 build of mozilla-firefox 0.9 in ubuntu hoary RELEASE
-  X-Creator-Recipient: mark@xxxxxxxxxxx
-  X-Launchpad-Build-State: BUILDING
-  X-Launchpad-Build-Component: main
-  X-Launchpad-Build-Arch: i386
-  X-Launchpad-PPA: omitted
-
-Check the notification content:
-
-  >>> notification_body = build_notification.get_payload()
-  >>> print notification_body #doctest: -NORMALIZE_WHITESPACE
-  <BLANKLINE>
-   * Source Package: mozilla-firefox
-   * Version: 0.9
-   * Architecture: i386
-   * Archive: ubuntu primary archive
-   * Component: main
-   * State: Currently building
-   * Duration: not finished
-   * Build Log: see builder page
-   * Builder: http://launchpad.dev/builders/bob
-   * Source: http://launchpad.dev/ubuntu/+source/mozilla-firefox/0.9
-  <BLANKLINE>
-  <BLANKLINE>
-  <BLANKLINE>
-  If you want further information about this situation, feel free to
-  contact a member of the Launchpad Buildd Administrators team.
-  <BLANKLINE>
-  --
-  i386 build of mozilla-firefox 0.9 in ubuntu hoary RELEASE
-  http://launchpad.dev/ubuntu/+source/mozilla-firefox/0.9/+buildjob/8
-  <BLANKLINE>
-
-Notification for a SUPERSEDED build record:
-
-  >>> superseded_candidate = buildset.getByBuildID(15)
-  >>> superseded_candidate.source_package_release.name
-  u'at'
-  >>> superseded_candidate.status.name
-  'SUPERSEDED'
-  >>> superseded_candidate.source_package_release.creator.name
-  u'mark'
-
-  >>> superseded_candidate.notify()
-
-Check how many email the system have sent:
-
-  >>> notifications = pop_notifications()
-  >>> len(notifications)
-  3
-
-The members of launchpad-buildd-admins are notified:
-
-  >>> notifications.pop(0)['To']
-  'celso.providelo@xxxxxxxxxxxxx'
-
-  >>> notifications.pop(0)['To']
-  'foo.bar@xxxxxxxxxxxxx'
-
-The maintainer is notified:
-
-  >>> build_notification = notifications.pop(0)
-  >>> build_notification['To']
-  'mark@xxxxxxxxxxx'
-
-Checking subject and extra headers:
-
-  >>> dump_build_notification(build_notification)
-  Subject: [Build #15] i386 build of at 0.00 in ubuntu warty RELEASE
-  X-Creator-Recipient: mark@xxxxxxxxxxx
-  X-Launchpad-Build-State: SUPERSEDED
-  X-Launchpad-Build-Component: main
-  X-Launchpad-Build-Arch: i386
-  X-Launchpad-PPA: omitted
-
-Check the notification content:
-
-  >>> notification_body = build_notification.get_payload()
-  >>> print notification_body #doctest: -NORMALIZE_WHITESPACE
-  <BLANKLINE>
-   * Source Package: at
-   * Version: 0.00
-   * Architecture: i386
-   * Archive: ubuntu primary archive
-   * Component: main
-   * State: Build for superseded Source
-   * Duration: not available
-   * Build Log: not available
-   * Builder: not available
-   * Source: http://launchpad.dev/ubuntu/+source/at/0.00
-  <BLANKLINE>
-  <BLANKLINE>
-  <BLANKLINE>
-  If you want further information about this situation, feel free to
-  contact a member of the Launchpad Buildd Administrators team.
-  <BLANKLINE>
-  --
-  i386 build of at 0.00 in ubuntu warty RELEASE
-  http://launchpad.dev/ubuntu/+source/at/0.00/+buildjob/15
-  <BLANKLINE>
-
-Check if the 'build_notification' configuration option really suppress
-notification as promised:
-
-  >>> from canonical.config import config
-  >>> send_build_notification = """
-  ...     [builddmaster]
-  ...     send_build_notification: False
-  ...     """
-  >>> config.push('send_build_notification', send_build_notification)
-  >>> superseeded_candidate = buildset.getByBuildID(15)
-  >>> superseeded_candidate.notify()
-
-  >>> notifications = pop_notifications()
-  >>> len(notifications)
-  0
-
-  >>> config_data = config.pop('send_build_notification')
-
-Check if 'notify_owner' config option really suppress email to
-the SPR owner, but still sending it to the default recipient:
-
-  >>> notify_owner = """
-  ...     [builddmaster]
-  ...     send_build_notification: True
-  ...     notify_owner: False
-  ...     """
-  >>> config.push('notify_owner', notify_owner)
-  >>> superseded_candidate = buildset.getByBuildID(15)
-
-  >>> superseded_candidate.notify()
-
-Check how many email the system have sent:
-
-  >>> notifications = pop_notifications()
-  >>> len(notifications)
-  2
-
-Only the members of launchpad-buildd-admins are notified:
-
-  >>> notifications.pop(0)['To']
-  'celso.providelo@xxxxxxxxxxxxx'
-
-  >>> notifications.pop(0)['To']
-  'foo.bar@xxxxxxxxxxxxx'
-
-Checking subject and extra headers:
-
-  >>> dump_build_notification(build_notification)
-  Subject: [Build #15] i386 build of at 0.00 in ubuntu warty RELEASE
-  X-Creator-Recipient: mark@xxxxxxxxxxx
-  X-Launchpad-Build-State: SUPERSEDED
-  X-Launchpad-Build-Component: main
-  X-Launchpad-Build-Arch: i386
-  X-Launchpad-PPA: omitted
-
-Just to keep the config sane, as it was before:
-
-  >>> config_data = config.pop('notify_owner')
-
-
-PPA build notifications
-=======================
-
-As for the normal (main archive) build candidates we are also able
-send notification about current (changed) PPA build record status.
-
-PPA build notifications should be as much as possible similar to those
-in the main archive, the important differences are:
-
- * We do not include 'buildd-admins' celebrity in its recipients,
-   instead we include the Archive owner;
-
- * We try to be explicit in the subject and in the body of the message
-   that it is about a PPA build candidate;
-
- * We don't have a 'source package' page for PPA sources.
-
- * We only warn the uploader (person specified in 'Changed-By'
-   changesfile) and the signer (person who owns the GPG key used to
-   sign the package) if the package was not copied.  Additionally,
-   the Changed-By person is only notified if they are the PPA owner or
-   they are in the team owning the PPA (to avoid spamming the innocent
-   when PPA users upload unchanged source packages).
-
-  >>> from lp.registry.interfaces.person import IPersonSet
-  >>> cprov = getUtility(IPersonSet).getByName('cprov')
-
-  >>> from lp.buildmaster.enums import BuildStatus
-  >>> failed_candidate = cprov.archive.getBuildRecords(
-  ...     build_state=BuildStatus.FAILEDTOBUILD, name='cdrkit')[0]
-
-  >>> failed_candidate.notify()
-
-Was this package copied?
-
-    >>> (failed_candidate.archive !=
-    ... failed_candidate.source_package_release.upload_archive)
-    True
-
-Since the package *was* copied, only the owner of the destination
-archive will get notified.
-
-  >>> notifications = pop_notifications()
-  >>> len(notifications)
-  1
-
-Checking the message contents:
-
-  >>> build_notification = notifications.pop(0)
-  >>> notification_body = build_notification.get_payload(decode=True)
-  >>> print notification_body #doctest: -NORMALIZE_WHITESPACE
-  <BLANKLINE>
-   * Source Package: cdrkit
-   * Version: 1.0
-   * Architecture: i386
-   * Archive: cprov PPA
-   * Component: main
-   * State: Failed to build
-   * Duration: a minute
-   * Build Log: http://launchpad.dev/~cprov/+archive/ppa/+buildjob/26/+files/netapplet-1.0.0.tar.gz
-   * Builder: http://launchpad.dev/builders/bob
-   * Source: not available
-  ...
-
-The notification went to the PPA owner.
-
-  >>> build_notification['To']
-  'celso.providelo@xxxxxxxxxxxxx'
-
-Checking subject and extra headers:
-
-  >>> dump_build_notification(build_notification)
-  Subject: [Build #26] i386 build of cdrkit 1.0 in ubuntu breezy-autotest RELEASE (cprov PPA)
-  X-Creator-Recipient: mark@xxxxxxxxxxx
-  X-Launchpad-Build-State: FAILEDTOBUILD
-  X-Launchpad-Build-Component: main
-  X-Launchpad-Build-Arch: i386
-  X-Launchpad-PPA: cprov
-
-In order to test build notifications for sources that were not copied,
-we will change the 'failed_candidate' source, setting its
-'upload_archive' to the same archive was built, Celso's PPA.
-
-  >>> from canonical.database.sqlbase import commit
-  >>> from canonical.testing.layers import LaunchpadZopelessLayer
-
-  >>> commit()
-  >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-
-  >>> from zope.security.proxy import removeSecurityProxy
-  >>> naked_candidate = removeSecurityProxy(failed_candidate)
-  >>> naked_sourcepackagerelease = naked_candidate.source_package_release
-  >>> naked_sourcepackagerelease.upload_archive = failed_candidate.archive
-
-We also make 'Foo Bar' the source signer.
-
-  >>> from lp.registry.interfaces.gpg import IGPGKeySet
-  >>> gpgkey = getUtility(IGPGKeySet).get(1)
-  >>> print gpgkey.owner.name
-  name16
-  >>> naked_sourcepackagerelease.dscsigningkey = gpgkey
-
-  >>> commit()
-  >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
-
-Since 'mark' is the 'creator' it makes the failed-to-build source
-look like a 'sponsored' upload.
-
-  >>> print failed_candidate.source_package_release.creator.name
-  mark
-
-Finally we issue the notification.
-
-  >>> failed_candidate.notify()
-
-As expected for a 'not-copied and sponsored' PPA upload, only two
-people involved with the source are notified.
-
- * 'cprov' who owns the PPA where the source is being built;
- * 'name16' who signed the source upload;
-
-However, 'mark' who was listed as the creator of the source revision is
-not notified because he is not in the PPA team or its owner.
-
-  >>> notifications = pop_notifications()
-  >>> len(notifications)
-  2
-
-  >>> for notification in notifications:
-  ...     print notification['To']
-  celso.providelo@xxxxxxxxxxxxx
-  foo.bar@xxxxxxxxxxxxx
-
-If the failed build candidate is for a PPA where mark is a member,
-he will be notified.
-
-  >>> commit()
-  >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-  >>> team = factory.makeTeam(owner=cprov, email="team@xxxxxxxxxxx")
-  >>> mark = failed_candidate.source_package_release.creator
-  >>> ignored = team.addMember(mark, mark)
-  >>> discard = pop_notifications() # Discard team join email.
-  >>> team_ppa = factory.makeArchive(owner=team)
-  >>> naked_candidate = removeSecurityProxy(failed_candidate)
-  >>> naked_candidate.archive = team_ppa
-  >>> naked_candidate.source_package_release.upload_archive = team_ppa
-  >>> commit()
-  >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
-
-  >>> failed_candidate.notify()
-
-This notification will be sent to the package uploader
-(foo.bar@xxxxxxxxxxxxx), the package creator (mark@xxxxxxx) and the archive
-team's contact address (team@xxxxxxxxxxx).
-
-  >>> notifications = pop_notifications()
-  >>> len(notifications)
-  3
-
-  >>> for notification in notifications:
-  ...     print notification['To']
-  foo.bar@xxxxxxxxxxxxx
-  mark@xxxxxxxxxxx
-  team@xxxxxxxxxxx
-
-
-Named PPA notifications
-=======================
-
-Named PPA build notifications contain a specific reference to the PPA
-in context. We will override the testing build to be in the context of
-a just created named-ppa for Celso.
-
-  >>> commit()
-  >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-
-  >>> from lp.soyuz.enums import ArchivePurpose
-  >>> from lp.soyuz.interfaces.archive import IArchiveSet
-  >>> named_ppa = getUtility(IArchiveSet).new(
-  ...     owner=cprov, name='testing', purpose=ArchivePurpose.PPA)
-
-  >>> naked_candidate = removeSecurityProxy(failed_candidate)
-  >>> naked_candidate.archive = named_ppa
-
-  >>> commit()
-  >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
-
-As described before, copied sources result in one build notification
-only, for the archive owner.
-
-  >>> failed_candidate.notify()
-
-  >>> notifications = pop_notifications()
-  >>> for notification in notifications:
-  ...     print notification['To']
-  celso.providelo@xxxxxxxxxxxxx
-
-The build-notification subject and headers correctly point
-to the named-ppa, 'cprov-testing'.
-
-  >>> build_notification = notifications.pop(0)
-  >>> dump_build_notification(build_notification)
-  Subject: [Build #26] i386 build of cdrkit 1.0 in ubuntu breezy-autotest RELEASE (cprov-testing PPA)
-  X-Creator-Recipient: mark@xxxxxxxxxxx
-  X-Launchpad-Build-State: FAILEDTOBUILD
-  X-Launchpad-Build-Component: main
-  X-Launchpad-Build-Arch: i386
-  X-Launchpad-PPA: cprov-testing
-
-The build notification body contains a reference to the named PPA, and
-the correct links to it the build in files in its context.
-
-  >>> notification_body = build_notification.get_payload(decode=True)
-  >>> print notification_body #doctest: -NORMALIZE_WHITESPACE
-  <BLANKLINE>
-   * Source Package: cdrkit
-   * Version: 1.0
-   * Architecture: i386
-   * Archive: cprov-testing PPA
-   * Component: main
-   * State: Failed to build
-   * Duration: a minute
-   * Build Log: http://launchpad.dev/~cprov/+archive/testing/+buildjob/26/+files/netapplet-1.0.0.tar.gz
-   * Builder: http://launchpad.dev/builders/bob
-   * Source: not available
-  <BLANKLINE>
-  <BLANKLINE>
-  <BLANKLINE>
-  If you want further information about this situation, feel free to
-  contact a member of the Launchpad Buildd Administrators team.
-  <BLANKLINE>
-  --
-  i386 build of cdrkit 1.0 in ubuntu breezy-autotest RELEASE
-  http://launchpad.dev/~cprov/+archive/testing/+buildjob/26
-  <BLANKLINE>

=== added file 'lib/lp/soyuz/tests/test_build_notify.py'
--- lib/lp/soyuz/tests/test_build_notify.py	1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/tests/test_build_notify.py	2011-01-19 22:55:51 +0000
@@ -0,0 +1,304 @@
+# Copyright 2011 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+__metaclass__ = type
+
+from datetime import (
+    datetime,
+    timedelta,
+    )
+import pytz
+from textwrap import dedent
+from zope.component import getUtility
+
+from canonical.launchpad.webapp import canonical_url
+from canonical.testing.layers import LaunchpadFunctionalLayer
+from lp.app.browser.tales import DurationFormatterAPI
+from lp.archivepublisher.utils import get_ppa_reference
+from lp.buildmaster.enums import BuildStatus
+from lp.registry.interfaces.person import IPersonSet
+from lp.soyuz.enums import ArchivePurpose
+from lp.soyuz.interfaces.publishing import PackagePublishingPocket
+from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
+from lp.testing import (
+    person_logged_in,
+    TestCaseWithFactory,
+    )
+from lp.testing.mail_helpers import pop_notifications
+from lp.testing.sampledata import ADMIN_EMAIL
+
+
+class TestBuildNotify(TestCaseWithFactory):
+
+    layer = LaunchpadFunctionalLayer
+
+    def setUp(self):
+        super(TestBuildNotify, self).setUp()
+        self.admin = getUtility(IPersonSet).getByEmail(ADMIN_EMAIL)
+        # Create all of the items we need to create builds
+        self.pf = self.factory.makeProcessorFamily()
+        pf_proc = self.pf.addProcessor(self.factory.getUniqueString(), '', '')
+        self.distroseries = self.factory.makeDistroSeries()
+        self.das = self.factory.makeDistroArchSeries(
+            distroseries=self.distroseries, processorfamily=self.pf,
+            supports_virtualized=True)
+        self.creator = self.factory.makePerson(email='test@xxxxxxxxxxx')
+        self.archive = self.factory.makeArchive(
+            distribution=self.distroseries.distribution,
+            purpose=ArchivePurpose.PRIMARY)
+        self.ppa = self.factory.makeArchive()
+        with person_logged_in(self.admin):
+            self.publisher = SoyuzTestPublisher()
+            self.publisher.prepareBreezyAutotest()
+            self.distroseries.nominatedarchindep = self.das
+            self.publisher.addFakeChroots(distroseries=self.distroseries)
+            self.builder = self.factory.makeBuilder(processor=pf_proc)
+        self.builds = []
+
+    def create_builds(self, archive):
+        for status in BuildStatus.items:
+            spph = self.publisher.getPubSource(
+                sourcename=self.factory.getUniqueString(),
+                version="%s.%s" % (
+                    self.factory.getUniqueInteger(), status.value),
+                distroseries=self.distroseries, architecturehintlist='any',
+                creator=self.creator, archive=archive)
+            [build] = spph.createMissingBuilds()
+            with person_logged_in(self.admin):
+                build.status = status
+                build.builder = self.builder
+                if status != BuildStatus.BUILDING:
+                    build.buildqueue_record.destroySelf()
+                else:
+                    build.buildqueue_record.builder = self.builder
+                build.date_started = datetime.now(pytz.UTC)
+                build.date_finished = build.date_started + timedelta(
+                    minutes=5 * (status.value + 1))
+            self.builds.append(build)
+
+    def _assert_mail_is_correct(self, build, notification, ppa=False):
+        self.assertEquals('test@xxxxxxxxxxx',
+            notification['X-Creator-Recipient'])
+        self.assertEquals(
+            self.das.architecturetag, notification['X-Launchpad-Build-Arch'])
+        self.assertEquals(
+            'main', notification['X-Launchpad-Build-Component'])
+        if ppa is True:
+            self.assertEquals(
+                get_ppa_reference(self.ppa), notification['X-Launchpad-PPA'])
+        body = notification.get_payload(decode=True)
+        duration = DurationFormatterAPI(build.duration).approximateduration()
+        build_log = 'None'
+        if ppa is True:
+            archive = '%s PPA' % get_ppa_reference(build.archive)
+            source = 'not available'
+        else:
+            archive = '%s primary archive' % (
+                self.distroseries.distribution.name)
+            source = canonical_url(build.distributionsourcepackagerelease)
+        builder = canonical_url(build.builder)
+        if build.status == BuildStatus.BUILDING:
+            duration = 'not finished'
+            build_log = 'see builder page'
+        elif (
+            build.status == BuildStatus.SUPERSEDED or 
+            build.status == BuildStatus.NEEDSBUILD):
+            duration = 'not available'
+            build_log = 'not available'
+            builder = 'not available'
+        elif build.status == BuildStatus.UPLOADING:
+            duration = 'uploading'
+            build_log = 'see builder page'
+            builder = 'not available'
+        expected_body = dedent("""
+         * Source Package: %s
+         * Version: %s
+         * Architecture: %s
+         * Archive: %s
+         * Component: main
+         * State: %s
+         * Duration: %s
+         * Build Log: %s
+         * Builder: %s
+         * Source: %s
+
+
+
+        If you want further information about this situation, feel free to
+        contact a member of the Launchpad Buildd Administrators team.
+
+        --
+        %s
+        %s
+        """ % (
+            build.source_package_release.sourcepackagename.name,
+            build.source_package_release.version, self.das.architecturetag,
+            archive, build.status.title, duration, build_log, builder,
+            source, build.title, canonical_url(build)))
+        self.assertEquals(expected_body, body)
+
+    def test_notify_buildd_admins(self):
+        # A build will cause an e-mail to be sent out to the buildd-admins,
+        # for primary archive builds.
+        self.create_builds(self.archive)
+        build = self.builds[BuildStatus.FAILEDTOBUILD.value]
+        build.notify()
+        notifications = pop_notifications()
+        # There are 2 buildd-admins in the sample data, as well as the creator
+        self.assertEquals(3, len(notifications))
+
+    def test_ppa_does_not_notify_buildd_admins(self):
+        # A build for a PPA does not notify the buildd admins.
+        self.create_builds(self.ppa)
+        build = self.builds[BuildStatus.FAILEDTOBUILD.value]
+        build.notify()
+        notifications = pop_notifications()
+        # An e-mail is sent to the archive owner, as well as the creator
+        self.assertEquals(2, len(notifications))
+
+    def test_notify_failed_to_build(self):
+        # An e-mail is sent to the source package creator on build failures.
+        self.create_builds(self.archive)
+        build = self.builds[BuildStatus.FAILEDTOBUILD.value]
+        build.notify()
+        notification = pop_notifications()[1]
+        self._assert_mail_is_correct(build, notification)
+
+    def test_notify_failed_to_build_ppa(self):
+        # An e-mail is sent to the source package creator on build failures.
+        self.create_builds(archive=self.ppa)
+        build = self.builds[BuildStatus.FAILEDTOBUILD.value]
+        build.notify()
+        notification = pop_notifications()[1]
+        self._assert_mail_is_correct(build, notification, ppa=True)
+
+    def test_notify_needs_building(self):
+        # We can notify the creator when the build is needing to be built.
+        self.create_builds(self.archive)
+        build = self.builds[BuildStatus.NEEDSBUILD.value]
+        build.notify()
+        notification = pop_notifications()[1]
+        self._assert_mail_is_correct(build, notification)
+
+    def test_notify_needs_building_ppa(self):
+        # We can notify the creator when the build is needing to be built.
+        self.create_builds(self.ppa)
+        build = self.builds[BuildStatus.NEEDSBUILD.value]
+        build.notify()
+        notification = pop_notifications()[1]
+        self._assert_mail_is_correct(build, notification, ppa=True)
+
+    def test_notify_successfully_built(self):
+        # We can notify the creator when the build is sucessful.
+        self.create_builds(self.archive)
+        build = self.builds[BuildStatus.FULLYBUILT.value]
+        build.notify()
+        notification = pop_notifications()[1]
+        self._assert_mail_is_correct(build, notification)
+
+    def test_notify_successfully_built_ppa(self):
+        # We can notify the creator when the build is sucessful.
+        self.create_builds(self.ppa)
+        build = self.builds[BuildStatus.FULLYBUILT.value]
+        build.notify()
+        notification = pop_notifications()[1]
+        self._assert_mail_is_correct(build, notification, ppa=True)
+
+    def test_notify_dependency_wait(self):
+        # We can notify the creator when the build can't find a dependency.
+        self.create_builds(self.archive)
+        build = self.builds[BuildStatus.MANUALDEPWAIT.value]
+        build.notify()
+        notification = pop_notifications()[1]
+        self._assert_mail_is_correct(build, notification)
+
+    def test_notify_dependency_wait_ppa(self):
+        # We can notify the creator when the build can't find a dependency.
+        self.create_builds(self.ppa)
+        build = self.builds[BuildStatus.MANUALDEPWAIT.value]
+        build.notify()
+        notification = pop_notifications()[1]
+        self._assert_mail_is_correct(build, notification, ppa=True)
+
+    def test_notify_chroot_problem(self):
+        # We can notify the creator when the builder the build attempted to
+        # be built on has an internal problem.
+        self.create_builds(self.archive)
+        build = self.builds[BuildStatus.CHROOTWAIT.value]
+        build.notify()
+        notification = pop_notifications()[1]
+        self._assert_mail_is_correct(build, notification)
+
+    def test_notify_chroot_problem_ppa(self):
+        # We can notify the creator when the builder the build attempted to
+        # be built on has an internal problem.
+        self.create_builds(self.ppa)
+        build = self.builds[BuildStatus.CHROOTWAIT.value]
+        build.notify()
+        notification = pop_notifications()[1]
+        self._assert_mail_is_correct(build, notification, ppa=True)
+
+    def test_notify_build_for_superseded_source(self):
+        # We can notify the creator when the source package had a newer
+        # version uploaded before this build had a chance to be dispatched.
+        self.create_builds(self.archive)
+        build = self.builds[BuildStatus.SUPERSEDED.value]
+        build.notify()
+        notification = pop_notifications()[1]
+        self._assert_mail_is_correct(build, notification)
+
+    def test_notify_build_for_superseded_source_ppa(self):
+        # We can notify the creator when the source package had a newer
+        # version uploaded before this build had a chance to be dispatched.
+        self.create_builds(self.ppa)
+        build = self.builds[BuildStatus.SUPERSEDED.value]
+        build.notify()
+        notification = pop_notifications()[1]
+        self._assert_mail_is_correct(build, notification, ppa=True)
+
+    def test_notify_currently_building(self):
+        # We can notify the creator when the build is currently building.
+        self.create_builds(self.archive)
+        build = self.builds[BuildStatus.BUILDING.value]
+        build.notify()
+        notification = pop_notifications()[1]
+        self._assert_mail_is_correct(build, notification)
+
+    def test_notify_currently_building_ppa(self):
+        # We can notify the creator when the build is currently building.
+        self.create_builds(self.ppa)
+        build = self.builds[BuildStatus.BUILDING.value]
+        build.notify()
+        notification = pop_notifications()[1]
+        self._assert_mail_is_correct(build, notification, ppa=True)
+
+    def test_notify_uploading_build(self):
+        # We can notify the creator when the build has completed, and binary
+        # packages are being uploaded by the builder.
+        self.create_builds(self.archive)
+        build = self.builds[BuildStatus.UPLOADING.value]
+        build.notify()
+        notification = pop_notifications()[1]
+        self._assert_mail_is_correct(build, notification)
+
+    def test_notify_uploading_build_ppa(self):
+        # We can notify the creator when the build has completed, and binary
+        # packages are being uploaded by the builder.
+        self.create_builds(self.ppa)
+        build = self.builds[BuildStatus.UPLOADING.value]
+        build.notify()
+        notification = pop_notifications()[1]
+        self._assert_mail_is_correct(build, notification, ppa=True)
+
+    def test_copied_into_ppa_does_not_spam(self):
+        # When a package is copied into a PPA, we don't send mail to the
+        # original creator of the source package.
+        self.create_builds(self.archive)
+        build = self.builds[BuildStatus.FULLYBUILT.value]
+        spph = build.current_source_publication
+        ppa_spph = spph.copyTo(
+            self.distroseries, PackagePublishingPocket.RELEASE, self.ppa)
+        [ppa_build] = ppa_spph.createMissingBuilds()
+        ppa_build.notify()
+        notifications = pop_notifications()
+        self.assertEquals(1, len(notifications))

=== modified file 'lib/lp/soyuz/tests/test_doc.py'
--- lib/lp/soyuz/tests/test_doc.py	2011-01-19 22:54:51 +0000
+++ lib/lp/soyuz/tests/test_doc.py	2011-01-19 22:55:51 +0000
@@ -141,11 +141,6 @@
 
 
 special = {
-    'build-notification.txt': LayeredDocFileSuite(
-        '../doc/build-notification.txt',
-        setUp=builddmasterSetUp,
-        layer=LaunchpadZopelessLayer,
-        ),
     'buildd-scoring.txt': LayeredDocFileSuite(
         '../doc/buildd-scoring.txt',
         setUp=builddmasterSetUp,


Follow ups