launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #04585
[Merge] lp:~rvb/launchpad/bug-820452 into lp:launchpad
Raphaël Victor Badin has proposed merging lp:~rvb/launchpad/bug-820452 into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #820452 in Launchpad itself: "Initializing a distroseries is too eager to check in-progress builds in the parent"
https://bugs.launchpad.net/launchpad/+bug/820452
For more details, see:
https://code.launchpad.net/~rvb/launchpad/bug-820452/+merge/71392
This branch improves the checks performed before we initialize a new distroseries. The checks are twofold: we make sure the parents' queue is empty and we also assert that there are no pending builds for the given parent series.
This branch:
- extends the checks to include the 3 pockets RELEASE, SECURITY and UPDATES.
- restricts the checks to only consider the packages that will be copied if the initialization is restricted to a selection of the parents's packagesets.
- changes the check performed on builds to only consider builds in the selected architectures (if no architectures a selected, this mean we will copy all the architectures from the parents).
- fixes a few lints.
- adds proper tests to make sure that the queue check only considers the items of the right status.
= Tests =
./bin/test -vvc test_initialize_distroseries test_failure_with_pending_builds_specific_arches
./bin/test -vvc test_initialize_distroseries test_check_success_with_pending_builds_in_other_arches
./bin/test -vvc test_initialize_distroseries test_failure_if_build_present_in_selected_packagesets
./bin/test -vvc test_initialize_distroseries test_check_success_if_build_present_in_non_selected_packagesets
./bin/test -vvc test_initialize_distroseries test_check_success_with_binary_queue_items_pockets
./bin/test -vvc test_initialize_distroseries test_failure_with_binary_queue_items_pockets
./bin/test -vvc test_initialize_distroseries test_failure_with_binary_queue_items_status
./bin/test -vvc test_initialize_distroseries test_check_success_with_binary_queue_items_status
./bin/test -vvc test_initialize_distroseries test_check_success_with_source_queue_items
./bin/test -vvc test_initialize_distroseries test_check_success_with_binary_queue_items_outside_packagesets
./bin/test -vvc test_initialize_distroseries test_failure_with_binary_queue_items_in_packagesets
= Q/A =
- Create a parent with 2 architectures 'a' and 'b'. Create pending builds for architecture 'a'. Initializing a series from this parent should only be possible when selecting only the architecture 'b'.
- Create builds for a package from the packageset 'p1' of the parent series. Initializing a series from this parent should be only possible when selecting only other packagesets than 'p1' for the initialization.
- Create an unapproved queue item for a package from the packageset 'p1' of the parent series. Initializing a series from this parent should be only possible when selecting only other packagesets than 'p1' for the initialization.
--
https://code.launchpad.net/~rvb/launchpad/bug-820452/+merge/71392
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~rvb/launchpad/bug-820452 into lp:launchpad.
=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg 2011-08-10 15:57:35 +0000
+++ database/schema/security.cfg 2011-08-12 16:32:27 +0000
@@ -998,6 +998,7 @@
public.packagesetsources = SELECT, INSERT
public.packageupload = SELECT
public.packageuploadsource = SELECT
+public.packageuploadbuild = SELECT
public.packaging = SELECT, INSERT
public.person = SELECT
public.processor = SELECT
=== modified file 'lib/lp/registry/interfaces/distroseries.py'
--- lib/lp/registry/interfaces/distroseries.py 2011-08-05 03:58:16 +0000
+++ lib/lp/registry/interfaces/distroseries.py 2011-08-12 16:32:27 +0000
@@ -541,7 +541,8 @@
:param created_since_date: If specified, only returns items uploaded
since the timestamp supplied.
:param archive: Filter results for this `IArchive`.
- :param pocket: Filter results by this `PackagePublishingPocket`.
+ :param pocket: Filter results by this `PackagePublishingPocket` or a
+ list of `PackagePublishingPocket`.
:param custom_type: Filter results by this
`PackageUploadCustomFormat`.
:param name: Filter results by this file name or package name.
=== modified file 'lib/lp/soyuz/interfaces/queue.py'
--- lib/lp/soyuz/interfaces/queue.py 2011-06-23 12:42:46 +0000
+++ lib/lp/soyuz/interfaces/queue.py 2011-08-12 16:32:27 +0000
@@ -619,6 +619,7 @@
name=None, version=None, exact_match=False):
"""Get package upload records for a series with optional filtering.
+ :param distroseries: the `IDistroSeries` to consider.
:param status: Filter results by this `PackageUploadStatus`, or list
of statuses.
:param created_since_date: If specified, only returns items uploaded
@@ -646,6 +647,20 @@
:return: a matching `IPackageUpload` object.
"""
+ def getBuildsForSources(self, distroseries, status=None, pockets=None,
+ names=None):
+ """Return binary package upload records for a series with option
+ filtering.
+
+ :param distroseries: the `IDistroSeries` to consider.
+ :param status: Filter results by this list of `PackageUploadStatus`s.
+ :param pockets: Filter results by this list of
+ `PackagePublishingPocket`s.
+ :param names: Filter results by this list of package names.
+
+ :return: A result set containing `IPackageUpload`s.
+ """
+
def getBuildByBuildIDs(build_ids):
"""Return `PackageUploadBuilds`s for the supplied build IDs."""
=== modified file 'lib/lp/soyuz/model/binarypackagebuild.py'
--- lib/lp/soyuz/model/binarypackagebuild.py 2011-05-27 21:12:25 +0000
+++ lib/lp/soyuz/model/binarypackagebuild.py 2011-08-12 16:32:27 +0000
@@ -570,7 +570,7 @@
# Analysis of previous build data shows that a build rate
# of 6 KB/second is realistic. Furthermore we have to add
# another minute for generic build overhead.
- estimate = int(package_size/6.0/60 + 1)
+ estimate = int(package_size / 6.0 / 60 + 1)
else:
# No historic build times and no package size available,
# assume a build time of 5 minutes.
@@ -862,10 +862,11 @@
:param tables: container to which to add joined tables.
:param status: optional build state for which to add a query clause if
present.
- :param name: optional source package release name for which to add a
+ :param name: optional source package release name (or list of source
+ package release names) for which to add a query clause if
+ present.
+ :param pocket: optional pocket (or list of pockets) for which to add a
query clause if present.
- :param pocket: optional pocket for which to add a query clause if
- present.
:param arch_tag: optional architecture tag for which to add a
query clause if present.
"""
@@ -883,7 +884,10 @@
# Add query clause that filters on pocket if the latter is provided.
if pocket:
- queries.append('PackageBuild.pocket=%s' % sqlvalues(pocket))
+ if not isinstance(pocket, list):
+ pocket = (pocket,)
+
+ queries.append('PackageBuild.pocket IN %s' % sqlvalues(pocket))
# Add query clause that filters on architecture tag if provided.
if arch_tag is not None:
@@ -896,12 +900,22 @@
# Add query clause that filters on source package release name if the
# latter is provided.
if name is not None:
- queries.append('''
- BinaryPackageBuild.source_package_release =
- SourcePackageRelease.id AND
- SourcePackageRelease.sourcepackagename = SourcePackageName.id
- AND SourcepackageName.name LIKE '%%' || %s || '%%'
- ''' % quote_like(name))
+ if not isinstance(name, list):
+ queries.append('''
+ BinaryPackageBuild.source_package_release =
+ SourcePackageRelease.id AND
+ SourcePackageRelease.sourcepackagename =
+ SourcePackageName.id
+ AND SourcepackageName.name LIKE '%%' || %s || '%%'
+ ''' % quote_like(name))
+ else:
+ queries.append('''
+ BinaryPackageBuild.source_package_release =
+ SourcePackageRelease.id AND
+ SourcePackageRelease.sourcepackagename =
+ SourcePackageName.id
+ AND SourcepackageName.name IN %s
+ ''' % sqlvalues(name))
tables.extend(['SourcePackageRelease', 'SourcePackageName'])
def getBuildsForBuilder(self, builder_id, status=None, name=None,
=== modified file 'lib/lp/soyuz/model/distroarchseries.py'
--- lib/lp/soyuz/model/distroarchseries.py 2011-01-24 21:16:30 +0000
+++ lib/lp/soyuz/model/distroarchseries.py 2011-08-12 16:32:27 +0000
@@ -53,9 +53,7 @@
IDistroArchSeriesSet,
IPocketChroot,
)
-from lp.soyuz.interfaces.publishing import (
- ICanPublishPackages,
- )
+from lp.soyuz.interfaces.publishing import ICanPublishPackages
from lp.soyuz.model.binarypackagename import BinaryPackageName
from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
from lp.soyuz.model.distroarchseriesbinarypackage import (
@@ -203,7 +201,7 @@
find_spec = (
BinaryPackageRelease,
BinaryPackageName,
- BinaryPackageName, # dummy value
+ BinaryPackageName, # dummy value
)
archives = self.distroseries.distribution.getArchiveIDList()
@@ -308,7 +306,7 @@
published = BinaryPackagePublishingHistory.select(
" AND ".join(queries),
- clauseTables = ['BinaryPackageRelease'],
+ clauseTables=['BinaryPackageRelease'],
orderBy=['-id'])
return shortlist(published)
@@ -383,8 +381,8 @@
used simply to keep trusted code DRY.
:param architectures: an iterable of architectures to process.
- :param arch_tag: an optional architecture tag with which to filter
- the results.
+ :param arch_tag: an optional architecture tag or a tag list with
+ which to filter the results.
:return: a list of the ids of the architectures matching arch_tag.
"""
# If arch_tag was not provided, just return the ids without
@@ -392,8 +390,10 @@
if arch_tag is None:
return [arch.id for arch in architectures]
else:
+ if not isinstance(arch_tag, list):
+ arch_tag = (arch_tag, )
return [arch.id for arch in architectures
- if arch_tag == arch.architecturetag]
+ if arch.architecturetag in arch_tag]
class PocketChroot(SQLBase):
@@ -409,4 +409,3 @@
notNull=True)
chroot = ForeignKey(dbName='chroot', foreignKey='LibraryFileAlias')
-
=== modified file 'lib/lp/soyuz/model/queue.py'
--- lib/lp/soyuz/model/queue.py 2011-07-25 13:43:24 +0000
+++ lib/lp/soyuz/model/queue.py 2011-08-12 16:32:27 +0000
@@ -1442,6 +1442,38 @@
return conflicts.one()
+ def getBuildsForSources(self, distroseries, status=None, pockets=None,
+ names=None):
+ """See `IPackageUploadSet`."""
+ # Avoiding circular imports.
+ from lp.registry.model.distroseries import DistroSeries
+ from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
+ from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
+
+ archives = distroseries.distribution.getArchiveIDList()
+ clauses = [
+ DistroSeries.id == PackageUpload.distroseriesID,
+ PackageUpload.archiveID.is_in(archives),
+ PackageUploadBuild.packageuploadID == PackageUpload.id,
+ ]
+
+ if status is not None:
+ clauses.append(PackageUpload.status.is_in(status))
+ if pockets is not None:
+ clauses.append(PackageUpload.pocket.is_in(pockets))
+ if names is not None:
+ clauses.extend([
+ BinaryPackageBuild.id == PackageUploadBuild.buildID,
+ BinaryPackageBuild.source_package_release ==
+ SourcePackageRelease.id,
+ SourcePackageRelease.sourcepackagename ==
+ SourcePackageName.id,
+ SourcePackageName.name.is_in(names),
+ ])
+
+ store = IMasterStore(PackageUpload)
+ return store.find(PackageUpload, *clauses)
+
def count(self, status=None, distroseries=None, pocket=None):
"""See `IPackageUploadSet`."""
clauses = []
=== modified file 'lib/lp/soyuz/scripts/initialize_distroseries.py'
--- lib/lp/soyuz/scripts/initialize_distroseries.py 2011-08-05 03:58:16 +0000
+++ lib/lp/soyuz/scripts/initialize_distroseries.py 2011-08-12 16:32:27 +0000
@@ -41,6 +41,7 @@
IPackagesetSet,
NoSuchPackageSet,
)
+from lp.soyuz.interfaces.queue import IPackageUploadSet
from lp.soyuz.model.packageset import Packageset
from lp.soyuz.scripts.packagecopier import do_copy
@@ -48,6 +49,13 @@
class InitializationError(Exception):
"""Raised when there is an exception during the initialization process."""
+# Pockets to consider when initializing the derived series from its parent(s).
+INIT_POCKETS = [
+ PackagePublishingPocket.RELEASE,
+ PackagePublishingPocket.SECURITY,
+ PackagePublishingPocket.UPDATES,
+ ]
+
class InitializeDistroSeries:
"""Copy in all of the parents distroseries's configuration. This
@@ -128,6 +136,7 @@
if self.parent_ids == []:
self.parents = (
self.distroseries.previous_series.getParentSeries())
+ self._create_source_names_by_parent()
def check(self):
if self.distroseries.isDerivedSeries():
@@ -148,6 +157,7 @@
self._checkBuilds(parent)
self._checkQueue(parent)
self._checkSeries()
+ return True
def _checkParents(self):
"""If self.first_derivation, the parents list cannot be empty."""
@@ -164,13 +174,21 @@
def _checkBuilds(self, parent):
"""Assert there are no pending builds for the given parent series.
- Only cares about the RELEASE pocket, which is the only one inherited
- via initializeFromParent method.
+ Only cares about the RELEASE, SECURITY and UPDATES pockets, which are
+ the only ones inherited via initializeFromParent method.
+ Restrict the check to the select architectures (if applicable).
+ Restrict the check to the selected packages if a limited set of
+ packagesets is used by the initialization.
"""
- # only the RELEASE pocket is inherited, so we only check
- # pending build records for it.
+ spns = self.source_names_by_parent.get(parent, None)
+ if spns is not None and len(spns) == 0:
+ # If no sources are selected in this parent, skip the check.
+ return
+
+ arch_tags = self.arches if self.arches is not () else None
pending_builds = parent.getBuildRecords(
- BuildStatus.NEEDSBUILD, pocket=PackagePublishingPocket.RELEASE)
+ BuildStatus.NEEDSBUILD, pocket=INIT_POCKETS,
+ arch_tag=arch_tags, name=spns)
if pending_builds.any():
raise InitializationError("Parent series has pending builds.")
@@ -178,18 +196,23 @@
def _checkQueue(self, parent):
"""Assert upload queue is empty on the given parent series.
- Only cares about the RELEASE pocket, which is the only one inherited
- via initializeFromParent method.
- """
- # only the RELEASE pocket is inherited, so we only check
- # queue items for it.
+ Only cares about the RELEASE, SECURITY and UPDATES pockets, which are
+ the only ones inherited via initializeFromParent method.
+ Restrict the check to the selected packages if a limited set of
+ packagesets is used by the initialization.
+ """
statuses = [
PackageUploadStatus.NEW,
PackageUploadStatus.ACCEPTED,
PackageUploadStatus.UNAPPROVED,
]
- items = parent.getPackageUploads(
- status=statuses, pocket=PackagePublishingPocket.RELEASE)
+ spns = self.source_names_by_parent.get(parent, None)
+ if spns is not None and len(spns) == 0:
+ # If no sources are selected in this parent, skip the check.
+ return
+
+ items = getUtility(IPackageUploadSet).getBuildsForSources(
+ parent, statuses, INIT_POCKETS, spns)
if not items.is_empty():
raise InitializationError(
"Parent series queues are not empty.")
@@ -373,6 +396,24 @@
distroseries=self.distroseries).is_empty()))
return case_1a or case_1b
+ def _create_source_names_by_parent(self):
+ """If only a subset of the packagesets was selected to be copied,
+ create a dict with the list of source names to be copied for each
+ parent.
+ """
+ source_names_by_parent = {}
+ if self.packagesets:
+ for parent in self.derivation_parents:
+ spns = []
+ # The overhead from looking up each packageset is
+ # mitigated by this usually running from a job.
+ for pkgsetid in self.packagesets:
+ pkgset = self._store.get(Packageset, int(pkgsetid))
+ if pkgset.distroseries == parent:
+ spns += list(pkgset.getSourcesIncluded())
+ source_names_by_parent[parent] = spns
+ self.source_names_by_parent = source_names_by_parent
+
def _copy_publishing_records(self, distroarchseries_lists):
"""Copy the publishing records from the parent arch series
to the given arch series in ourselves.
@@ -385,21 +426,13 @@
archive_set = getUtility(IArchiveSet)
for parent in self.derivation_parents:
- spns = []
- # The overhead from looking up each packageset is mitigated by
- # this usually running from a job.
- if self.packagesets:
- for pkgsetid in self.packagesets:
- pkgset = self._store.get(Packageset, int(pkgsetid))
- if pkgset.distroseries == parent:
- spns += list(pkgset.getSourcesIncluded())
-
+ spns = self.source_names_by_parent.get(parent, None)
+ if spns is not None and len(spns) == 0:
# Some packagesets where selected but not a single
# source from this parent: we skip the copy since
# calling copy with spns=[] would copy all the packagesets
# from this parent.
- if len(spns) == 0:
- continue
+ continue
distroarchseries_list = distroarchseries_lists[parent]
for archive in parent.distribution.all_distro_archives:
@@ -432,12 +465,8 @@
# There is only one available pocket in an unreleased
# series.
target_pocket = PackagePublishingPocket.RELEASE
- pockets_to_copy = (
- PackagePublishingPocket.RELEASE,
- PackagePublishingPocket.UPDATES,
- PackagePublishingPocket.SECURITY)
sources = archive.getPublishedSources(
- distroseries=parent, pocket=pockets_to_copy,
+ distroseries=parent, pocket=INIT_POCKETS,
name=spns)
# XXX: rvb 2011-06-23 bug=801112: do_copy is atomic (all
# or none of the sources will be copied). This might
=== modified file 'lib/lp/soyuz/scripts/tests/test_initialize_distroseries.py'
--- lib/lp/soyuz/scripts/tests/test_initialize_distroseries.py 2011-08-09 08:28:56 +0000
+++ lib/lp/soyuz/scripts/tests/test_initialize_distroseries.py 2011-08-12 16:32:27 +0000
@@ -26,6 +26,7 @@
from lp.services.features.testing import FeatureFixture
from lp.soyuz.enums import (
ArchivePurpose,
+ PackageUploadStatus,
SourcePackageFormat,
)
from lp.soyuz.interfaces.archivepermission import IArchivePermissionSet
@@ -58,24 +59,34 @@
# - setup/populate parents with packages;
# - initialize a child from parents.
- def setupParent(self, packages=None, format_selection=None,
- distribution=None,
- pocket=PackagePublishingPocket.RELEASE,
- ):
- parent = self.factory.makeDistroSeries(distribution)
- pf = getUtility(IProcessorFamilySet).getByName('x86')
+ def setupDas(self, parent, proc, arch_tag):
+ pf = getUtility(IProcessorFamilySet).getByName(proc)
parent_das = self.factory.makeDistroArchSeries(
distroseries=parent, processorfamily=pf,
- architecturetag='i386')
+ architecturetag=arch_tag)
lf = self.factory.makeLibraryFileAlias()
transaction.commit()
parent_das.addOrUpdateChroot(lf)
parent_das.supports_virtualized = True
+ return parent_das
+
+ def setupParent(self, parent=None, packages=None, format_selection=None,
+ distribution=None,
+ pocket=PackagePublishingPocket.RELEASE,
+ proc='x86', arch_tag='i386'
+ ):
+ if parent is None:
+ parent = self.factory.makeDistroSeries(distribution)
+ parent_das = self.setupDas(parent, proc, arch_tag)
parent.nominatedarchindep = parent_das
+ # Set a proper source package format if required.
if format_selection is None:
format_selection = SourcePackageFormat.FORMAT_1_0
- getUtility(ISourcePackageFormatSelectionSet).add(
+ spfss_utility = getUtility(ISourcePackageFormatSelectionSet)
+ existing_format_selection = spfss_utility.getBySeriesAndFormat(
parent, format_selection)
+ if existing_format_selection is None:
+ spfss_utility.add(parent, format_selection)
parent.backports_not_automatic = True
self._populate_parent(parent, parent_das, packages, pocket)
return parent, parent_das
@@ -163,17 +174,126 @@
# If the parent series has pending builds, and the child is a series
# of the same distribution (which means they share an archive), we
# can't initialize.
- self.parent, self.parent_das = self.setupParent()
- source = self.factory.makeSourcePackagePublishingHistory(
- distroseries=self.parent,
- pocket=PackagePublishingPocket.RELEASE)
- source.createMissingBuilds()
- child = self.factory.makeDistroSeries(
- distribution=self.parent.parent, previous_series=self.parent)
- ids = InitializeDistroSeries(child, [self.parent.id])
- self.assertRaisesWithContent(
- InitializationError, "Parent series has pending builds.",
- ids.check)
+ pockets = [
+ PackagePublishingPocket.RELEASE,
+ PackagePublishingPocket.SECURITY,
+ PackagePublishingPocket.UPDATES,
+ ]
+ for pocket in pockets:
+ self.parent, self.parent_das = self.setupParent()
+ source = self.factory.makeSourcePackagePublishingHistory(
+ distroseries=self.parent,
+ pocket=pocket)
+ source.createMissingBuilds()
+ child = self.factory.makeDistroSeries(
+ distribution=self.parent.parent, previous_series=self.parent)
+ ids = InitializeDistroSeries(child, [self.parent.id])
+ self.assertRaisesWithContent(
+ InitializationError, "Parent series has pending builds.",
+ ids.check)
+
+ def create2archParentAndSource(self, packages):
+ # Helper to create a parent series with 2 distroarchseries and
+ # a source.
+ parent, parent_das = self.setupParent(packages=packages)
+ unused, parent_das2 = self.setupParent(
+ parent=parent, proc='amd64', arch_tag='amd64',
+ packages=packages)
+ source = self.factory.makeSourcePackagePublishingHistory(
+ distroseries=parent,
+ pocket=PackagePublishingPocket.RELEASE)
+ return parent, parent_das, parent_das2, source
+
+ def test_failure_with_pending_builds_specific_arches(self):
+ # We only check for pending builds of the same architectures we're
+ # copying over from the parents. If a build is present in the
+ # architecture we're initializing with, IDS raises an error.
+ res = self.create2archParentAndSource(packages={'p1': '1.1'})
+ parent, parent_das, parent_das2, source = res
+ # Create builds for the architecture of parent_das2.
+ source.createMissingBuilds(architectures_available=[parent_das2])
+ # Initialize only with parent_das2's architecture.
+ child = self.factory.makeDistroSeries(
+ distribution=parent.distribution, previous_series=parent)
+ ids = InitializeDistroSeries(
+ child, arches=[parent_das2.architecturetag])
+
+ self.assertRaisesWithContent(
+ InitializationError, "Parent series has pending builds.",
+ ids.check)
+
+ def test_check_success_with_pending_builds_in_other_arches(self):
+ # We only check for pending builds of the same architectures we're
+ # copying over from the parents. If *no* build is present in the
+ # architecture we're initializing with, IDS will succeed.
+ res = self.create2archParentAndSource(packages={'p1': '1.1'})
+ parent, parent_das, parent_das2, source = res
+ # Create builds for the architecture of parent_das2.
+ source.createMissingBuilds(architectures_available=[parent_das2])
+ # Initialize only with parent_das's architecture.
+ child = self.factory.makeDistroSeries(
+ distribution=parent.distribution, previous_series=parent)
+ ids = InitializeDistroSeries(
+ child, arches=[parent_das.architecturetag])
+
+ # No error is raised because we're initializing only the architecture
+ # which has no pending builds in it.
+ self.assertTrue(ids.check())
+
+ def createPackageInPackageset(self, distroseries, package_name,
+ packageset_name, create_build=False):
+ # Helper method to create a package in a packageset in the given
+ # distroseries, optionaly creating the missing build for this source
+ # package.
+ spn = self.factory.getOrMakeSourcePackageName(package_name)
+ sourcepackagerelease = self.factory.makeSourcePackageRelease(
+ sourcepackagename=spn)
+ source = self.factory.makeSourcePackagePublishingHistory(
+ sourcepackagerelease=sourcepackagerelease,
+ distroseries=distroseries,
+ sourcepackagename=spn,
+ pocket=PackagePublishingPocket.RELEASE)
+ packageset = getUtility(IPackagesetSet).new(
+ packageset_name, packageset_name, distroseries.owner,
+ distroseries=distroseries)
+ packageset.addSources(package_name)
+ if create_build:
+ source.createMissingBuilds()
+ return source, packageset, sourcepackagerelease
+
+ def test_failure_if_build_present_in_selected_packagesets(self):
+ # Pending builds in a parent for source packages included in the
+ # packagesets selected for the copy will make the queue check fail.
+ parent, parent_das = self.setupParent()
+ p1, packageset1, unsed = self.createPackageInPackageset(
+ parent, u'p1', u'packageset1', True)
+ p2, packageset2, unsed = self.createPackageInPackageset(
+ parent, u'p2', u'packageset2', False)
+
+ child = self.factory.makeDistroSeries(
+ distribution=parent.distribution, previous_series=parent)
+ ids = InitializeDistroSeries(
+ child, packagesets=(str(packageset1.id),))
+
+ self.assertRaisesWithContent(
+ InitializationError, "Parent series has pending builds.",
+ ids.check)
+
+ def test_check_success_if_build_present_in_non_selected_packagesets(self):
+ # Pending builds in a parent for source packages not included in the
+ # packagesets selected for the copy won't make the queue check fail.
+ parent, parent_das = self.setupParent()
+ p1, packageset1, unused = self.createPackageInPackageset(
+ parent, u'p1', u'packageset1', True)
+ p2, packageset2, unused = self.createPackageInPackageset(
+ parent, u'p2', u'packageset2', False)
+
+ child = self.factory.makeDistroSeries(
+ distribution=parent.distribution, previous_series=parent)
+ ids = InitializeDistroSeries(
+ child, packagesets=(str(packageset2.id),))
+
+ self.assertTrue(ids.check())
def test_success_with_updates_packages(self):
# Initialization copies all the package from the UPDATES pocket.
@@ -203,15 +323,149 @@
self.assertDistroSeriesInitializedCorrectly(
child, self.parent, self.parent_das)
- def test_failure_with_queue_items(self):
- # If the parent series has items in its queues, such as NEW and
- # UNAPPROVED, we can't initialize.
- self.parent, self.parent_das = self.setupParent()
- self.parent.createQueueEntry(
- PackagePublishingPocket.RELEASE, self.parent.main_archive,
- 'foo.changes', 'bar')
- child = self.factory.makeDistroSeries()
- ids = InitializeDistroSeries(child, [self.parent.id])
+ def test_check_success_with_binary_queue_items_pockets(self):
+ # If the parent series has binary items in pockets PROPOSED or
+ # BACKPORTS, in its queues, we still can initialize because these
+ # pockets are not considered by the initialization process.
+ pockets = [
+ PackagePublishingPocket.PROPOSED,
+ PackagePublishingPocket.BACKPORTS,
+ ]
+ for pocket in pockets:
+ parent, parent_das = self.setupParent()
+ upload = parent.createQueueEntry(
+ pocket, parent.main_archive, 'foo.changes', 'bar')
+ # Create a binary package upload for this upload.
+ upload.addBuild(self.factory.makeBinaryPackageBuild())
+ child = self.factory.makeDistroSeries()
+ ids = InitializeDistroSeries(child, [parent.id])
+
+ self.assertTrue(ids.check())
+
+ def test_failure_with_binary_queue_items_pockets(self):
+ # If the parent series has binary items in pockets RELEASE,
+ # SECURITY or UPDATES in its queues, we can't initialize.
+ pockets = [
+ PackagePublishingPocket.RELEASE,
+ PackagePublishingPocket.SECURITY,
+ PackagePublishingPocket.UPDATES,
+ ]
+ for pocket in pockets:
+ parent, parent_das = self.setupParent()
+ upload = parent.createQueueEntry(
+ pocket, parent.main_archive, 'foo.changes', 'bar')
+ # Create a binary package upload for this upload.
+ upload.addBuild(self.factory.makeBinaryPackageBuild())
+ child = self.factory.makeDistroSeries()
+ ids = InitializeDistroSeries(child, [parent.id])
+
+ self.assertRaisesWithContent(
+ InitializationError, "Parent series queues are not empty.",
+ ids.check)
+
+ def test_failure_with_binary_queue_items_status(self):
+ # If the parent series has binary items with status NEW,
+ # ACCEPTED or UNAPPROVED we can't initialize.
+ statuses = [
+ PackageUploadStatus.NEW,
+ PackageUploadStatus.ACCEPTED,
+ PackageUploadStatus.UNAPPROVED,
+ ]
+ for status in statuses:
+ parent, parent_das = self.setupParent()
+ upload = self.factory.makePackageUpload(
+ distroseries=parent, status=status,
+ archive=parent.main_archive,
+ pocket=PackagePublishingPocket.RELEASE)
+ # Create a binary package upload for this upload.
+ upload.addBuild(self.factory.makeBinaryPackageBuild())
+ child = self.factory.makeDistroSeries()
+ ids = InitializeDistroSeries(child, [parent.id])
+
+ self.assertRaisesWithContent(
+ InitializationError, "Parent series queues are not empty.",
+ ids.check)
+
+ def test_check_success_with_binary_queue_items_status(self):
+ # If the parent series has binary items with status DONE or
+ # REJECTED we still can initialize.
+ statuses = [
+ PackageUploadStatus.DONE,
+ PackageUploadStatus.REJECTED,
+ ]
+ for status in statuses:
+ parent, parent_das = self.setupParent()
+ upload = self.factory.makePackageUpload(
+ distroseries=parent, status=status,
+ archive=parent.main_archive,
+ pocket=PackagePublishingPocket.RELEASE)
+ # Create a binary package upload for this upload.
+ upload.addBuild(self.factory.makeBinaryPackageBuild())
+ child = self.factory.makeDistroSeries()
+ ids = InitializeDistroSeries(child, [parent.id])
+
+ self.assertTrue(ids.check())
+
+ def test_check_success_with_source_queue_items(self):
+ # If the parent series has *source* items in its queues, we
+ # still can initialize.
+ parent, parent_das = self.setupParent()
+ upload = parent.createQueueEntry(
+ PackagePublishingPocket.RELEASE,
+ parent.main_archive, 'foo.changes', 'bar')
+ # Create a source package upload for this upload.
+ upload.addSource(self.factory.makeSourcePackageRelease())
+ child = self.factory.makeDistroSeries()
+ ids = InitializeDistroSeries(child, [parent.id])
+
+ self.assertTrue(ids.check())
+
+ def test_check_success_with_binary_queue_items_outside_packagesets(self):
+ # If the parent series has binary items in its queues not in the
+ # packagesets selected for the initialization, we still can
+ # initialize.
+ parent, parent_das = self.setupParent()
+ p1, packageset1, spr1 = self.createPackageInPackageset(
+ parent, u'p1', u'packageset1', False)
+ p2, packageset2, spr2 = self.createPackageInPackageset(
+ parent, u'p2', u'packageset2', False)
+
+ # Create a binary package upload for the package 'p2' inside
+ # packageset 'packageset2'.
+ upload = parent.createQueueEntry(
+ PackagePublishingPocket.RELEASE,
+ parent.main_archive, 'foo.changes', 'bar')
+ upload.addBuild(self.factory.makeBinaryPackageBuild(
+ distroarchseries=parent_das,
+ source_package_release=spr2))
+ child = self.factory.makeDistroSeries()
+ # Initialize with packageset1 only.
+ ids = InitializeDistroSeries(child, [parent.id],
+ child, packagesets=(str(packageset1.id),))
+
+ self.assertTrue(ids.check())
+
+ def test_failure_with_binary_queue_items_in_packagesets(self):
+ # If the parent series has binary items in its queues in the
+ # packagesets selected for the initialization, we can't
+ # initialize.
+ parent, parent_das = self.setupParent()
+ p1, packageset1, spr1 = self.createPackageInPackageset(
+ parent, u'p1', u'packageset1', False)
+
+ # Create a binary package upload for the package 'p2' inside
+ # packageset 'packageset2'.
+ upload = parent.createQueueEntry(
+ PackagePublishingPocket.RELEASE,
+ parent.main_archive, 'foo.changes', 'bar')
+ upload.addBuild(self.factory.makeBinaryPackageBuild(
+ distroarchseries=parent_das,
+ source_package_release=spr1))
+ child = self.factory.makeDistroSeries()
+ # Initialize with packageset1 only.
+ ids = InitializeDistroSeries(child, [parent.id],
+ child, packagesets=(str(packageset1.id),))
+
self.assertRaisesWithContent(
InitializationError, "Parent series queues are not empty.",
ids.check)
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2011-08-10 07:02:59 +0000
+++ lib/lp/testing/factory.py 2011-08-12 16:32:27 +0000
@@ -3415,13 +3415,18 @@
pocket, archive, changes_filename, changes_file_content,
signing_key=signing_key, package_copy_job=package_copy_job)
if status is not None:
- naked_package_upload = removeSecurityProxy(package_upload)
- status_changers = {
- PackageUploadStatus.DONE: naked_package_upload.setDone,
- PackageUploadStatus.ACCEPTED:
- naked_package_upload.setAccepted,
- }
- status_changers[status]()
+ if status is not PackageUploadStatus.NEW:
+ naked_package_upload = removeSecurityProxy(package_upload)
+ status_changers = {
+ PackageUploadStatus.UNAPPROVED:
+ naked_package_upload.setUnapproved,
+ PackageUploadStatus.REJECTED:
+ naked_package_upload.setRejected,
+ PackageUploadStatus.DONE: naked_package_upload.setDone,
+ PackageUploadStatus.ACCEPTED:
+ naked_package_upload.setAccepted,
+ }
+ status_changers[status]()
return package_upload
def makeSourcePackageUpload(self, distroseries=None,