~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# Copyright 2010-2011 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

"""Test initializing a distroseries using
IDistroSeries.initDerivedDistroSeries."""

__metaclass__ = type

import transaction
from zope.component import getUtility
from zope.security.interfaces import Unauthorized
from zope.security.proxy import removeSecurityProxy

from lp.registry.interfaces.distroseries import DerivationError
from lp.services.features.testing import FeatureFixture
from lp.soyuz.interfaces.distributionjob import (
    IInitializeDistroSeriesJobSource,
    )
from lp.soyuz.model.distroseriesdifferencejob import (
    FEATURE_FLAG_ENABLE_MODULE,
    )
from lp.soyuz.scripts.tests.test_initialize_distroseries import (
    InitializationHelperTestCase,
    )
from lp.testing import (
    ANONYMOUS,
    login,
    login_person,
    TestCaseWithFactory,
    )
from lp.testing.fakemethod import FakeMethod
from lp.testing.layers import (
    LaunchpadFunctionalLayer,
    LaunchpadZopelessLayer,
    )


class TestDeriveDistroSeries(TestCaseWithFactory):

    layer = LaunchpadFunctionalLayer

    def setUp(self):
        super(TestDeriveDistroSeries, self).setUp()
        self.parent = self.factory.makeDistroSeries()
        arch = self.factory.makeDistroArchSeries(distroseries=self.parent)
        removeSecurityProxy(self.parent).nominatedarchindep = arch
        self.child = self.factory.makeDistroSeries()
        removeSecurityProxy(self.child).driver = self.factory.makePerson()
        login_person(self.child.driver)

    def test_no_permission_to_call(self):
        login(ANONYMOUS)
        self.assertRaises(
            Unauthorized, getattr, self.child, "initDerivedDistroSeries")

    def test_parent_is_not_set(self):
        # When the series already has a parent series, it means that the
        # distroseries has already been derived, and it is forbidden to
        # derive more than once.
        self.factory.makeDistroSeriesParent(
            derived_series=self.child, parent_series=self.parent)
        self.assertRaisesWithContent(
            DerivationError,
            ("DistroSeries {self.child.name} already has parent "
             "series.".format(self=self)),
            self.child.initDerivedDistroSeries, self.child.driver,
            [self.parent.id])

    def test_init_creates_new_job(self):
        self.child.initDerivedDistroSeries(
            self.child.driver, [self.parent.id])
        [job] = list(
            getUtility(IInitializeDistroSeriesJobSource).iterReady())
        self.assertEqual(job.distroseries, self.child)


class TestDeriveDistroSeriesMultipleParents(InitializationHelperTestCase):

    layer = LaunchpadZopelessLayer

    def setUpParents(self, packages1, packages2):
        parent1, unused = self.setupParent(packages=packages1)
        parent2, unused = self.setupParent(packages=packages2)
        return parent1, parent2

    def assertBinPackagesAndVersions(self, series, pack_versions):
        # Helper to assert that series contains the required binaries
        # pack_version should be of the form [(packagname1, version1), ...]
        # e.g. [(u'p1', u'0.1-1'), (u'p2', u'2.1')])
        pub_sources = series.main_archive.getPublishedSources(
            distroseries=series)
        binaries = sorted(
            [(p.getBuiltBinaries()[0].binarypackagerelease.sourcepackagename,
              p.getBuiltBinaries()[0].binarypackagerelease.version)
                 for p in pub_sources])

        self.assertEquals(pack_versions, binaries)

    def test_multiple_parents_binary_packages(self):
        # An initialization from many parents (using the package copier)
        # can happen using the same the db user the job will use
        # ('initializedistroseries').
        parent1, parent2 = self.setUpParents(
            packages1={'p1': '0.1-1'}, packages2={'p2': '2.1'})
        child = self.factory.makeDistroSeries()
        transaction.commit()
        self.layer.switchDbUser('initializedistroseries')

        child = self._fullInitialize(
            [parent1, parent2], child=child)
        self.assertBinPackagesAndVersions(
            child,
            [(u'p1', u'0.1-1'), (u'p2', u'2.1')])

    def test_multiple_parents_dsd_flag_on(self):
        # An initialization can happen if the flag for distroseries
        # difference creation is on.
        self.useFixture(FeatureFixture({FEATURE_FLAG_ENABLE_MODULE: u'on'}))
        parent1, parent2 = self.setUpParents(
            packages1={'p1': '0.1-1'}, packages2={'p2': '2.1'})
        child = self.factory.makeDistroSeries()
        transaction.commit()
        self.layer.switchDbUser('initializedistroseries')

        child = self._fullInitialize(
            [parent1, parent2], child=child)
        # Make sure the initialization was successful.
        self.assertBinPackagesAndVersions(
            child,
            [(u'p1', u'0.1-1'), (u'p2', u'2.1')])
        # Switch back to launchpad_main to be able to cleanup the
        # feature flags.
        self.layer.switchDbUser('launchpad_main')

    def test_multiple_parents_do_not_close_bugs(self):
        # The initialization does not close the bugs on the copied
        # publications (and thus does not try to access the bug table).
        self.useFixture(FeatureFixture({FEATURE_FLAG_ENABLE_MODULE: u'on'}))
        parent1, parent2 = self.setUpParents(
            packages1={'p1': '0.1-1'}, packages2={'p2': '2.1'})
        child = self.factory.makeDistroSeries()
        transaction.commit()
        self.layer.switchDbUser('initializedistroseries')

        # Patch close_bugs_for_sourcepublication to be able to record if
        # the method has been called.
        fakeCloseBugs = FakeMethod()
        from lp.soyuz.scripts import packagecopier as packagecopier_module
        self.patch(
            packagecopier_module,
            'close_bugs_for_sourcepublication',
            fakeCloseBugs)

        child = self._fullInitialize(
            [parent1, parent2], child=child)
        # Make sure the initialization was successful.
        self.assertBinPackagesAndVersions(
            child,
            [(u'p1', u'0.1-1'), (u'p2', u'2.1')])
        # Assert that close_bugs_for_sourcepublication has not been
        # called.
        self.assertEqual(
            0,
            fakeCloseBugs.call_count)
        # Switch back to launchpad_main to be able to cleanup the
        # feature flags.
        self.layer.switchDbUser('launchpad_main')

    def test_packageset_check_performed(self):
        # Packagesets passed to initDerivedDistroSeries are passed down
        # to InitializeDistroSeries to check for any pending builds.
        parent, parent_das = self.setupParent()
        # Create packageset p1 with a build.
        p1, packageset1, unsed = self.createPackageInPackageset(
            parent, u'p1', u'packageset1', True)
        # Create packageset p2 without a build.
        p2, packageset2, unsed = self.createPackageInPackageset(
            parent, u'p2', u'packageset2', False)
        child = self.factory.makeDistroSeries(
            distribution=parent.distribution, previous_series=parent)

        # Packageset p2 has no build so no exception should be raised.
        child.initDerivedDistroSeries(
            child.driver, [parent.id], (), None, (str(packageset2.id),))

    def test_arch_check_performed(self):
        # Architectures passed to initDerivedDistroSeries are passed down
        # to InitializeDistroSeries to check for any pending builds.
        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_das])
        child = self.factory.makeDistroSeries(
            distribution=parent.distribution, previous_series=parent)

        # Initialize only with parent_das2's architecture. The build is
        # in the other architecture so no exception should be raised.
        child.initDerivedDistroSeries(
            child.driver, [parent.id], (parent_das2.architecturetag, ))