~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/registry/browser/tests/test_distroseries.py

Merge db-devel.

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
from testtools.content_type import UTF8_TEXT
21
21
from testtools.matchers import (
22
22
    EndsWith,
 
23
    Equals,
23
24
    LessThan,
24
25
    Not,
25
26
    )
32
33
from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
33
34
from canonical.launchpad.testing.pages import find_tag_by_id
34
35
from canonical.launchpad.webapp.batching import BatchNavigator
 
36
from canonical.launchpad.webapp.interaction import get_current_principal
 
37
from canonical.launchpad.webapp.interfaces import BrowserNotificationLevel
35
38
from canonical.launchpad.webapp.publisher import canonical_url
36
39
from canonical.testing.layers import (
37
40
    DatabaseFunctionalLayer,
38
41
    LaunchpadFunctionalLayer,
39
42
    LaunchpadZopelessLayer,
40
43
    )
 
44
from lp.archivepublisher.debversion import Version
41
45
from lp.registry.browser.distroseries import (
42
46
    IGNORED,
43
47
    HIGHER_VERSION_THAN_PARENT,
62
66
    )
63
67
from lp.soyuz.interfaces.component import IComponentSet
64
68
from lp.soyuz.interfaces.distributionjob import (
 
69
    IDistroSeriesDifferenceJobSource,
65
70
    IInitialiseDistroSeriesJobSource,
66
71
    )
 
72
from lp.soyuz.interfaces.packagecopyjob import IPlainPackageCopyJobSource
67
73
from lp.soyuz.interfaces.sourcepackageformat import (
68
74
    ISourcePackageFormatSelectionSet,
69
75
    )
70
76
from lp.soyuz.model.archivepermission import ArchivePermission
 
77
from lp.soyuz.model import distroseriesdifferencejob
 
78
from lp.soyuz.model.packagecopyjob import specify_dsd_package
71
79
from lp.testing import (
 
80
    anonymous_logged_in,
72
81
    celebrity_logged_in,
73
82
    feature_flags,
74
83
    login_person,
76
85
    set_feature_flag,
77
86
    StormStatementRecorder,
78
87
    TestCaseWithFactory,
 
88
    with_celebrity_logged_in,
79
89
    )
 
90
from lp.testing.fakemethod import FakeMethod
80
91
from lp.testing.matchers import HasQueryCount
81
92
from lp.testing.views import create_initialized_view
82
93
 
83
94
 
84
95
def set_derived_series_ui_feature_flag(test_case):
85
96
    test_case.useFixture(FeatureFixture({
86
 
        u'soyuz.derived-series-ui.enabled': u'on',
 
97
        u'soyuz.derived_series_ui.enabled': u'on',
87
98
        }))
88
99
 
89
100
 
90
101
def set_derived_series_sync_feature_flag(test_case):
91
102
    test_case.useFixture(FeatureFixture({
92
103
        u'soyuz.derived_series_sync.enabled': u'on',
93
 
        u'soyuz.derived-series-ui.enabled': u'on',
 
104
        u'soyuz.derived_series_ui.enabled': u'on',
 
105
        }))
 
106
 
 
107
 
 
108
def set_derived_series_difference_jobs_feature_flag(test_case):
 
109
    test_case.useFixture(FeatureFixture({
 
110
        distroseriesdifferencejob.FEATURE_FLAG_ENABLE_MODULE: u'on',
94
111
        }))
95
112
 
96
113
 
107
124
 
108
125
    def _createDifferenceAndGetView(self, difference_type):
109
126
        # Helper function to create a valid DSD.
110
 
        distroseries = self.factory.makeDistroSeries(
111
 
            previous_series=self.factory.makeDistroSeries())
 
127
        dsp = self.factory.makeDistroSeriesParent()
112
128
        self.factory.makeDistroSeriesDifference(
113
 
            derived_series=distroseries, difference_type=difference_type)
114
 
        return create_initialized_view(distroseries, '+index')
 
129
            derived_series=dsp.derived_series,
 
130
            difference_type=difference_type)
 
131
        return create_initialized_view(dsp.derived_series, '+index')
115
132
 
116
133
    def test_num_differences(self):
117
134
        diff_type = DistroSeriesDifferenceType.DIFFERENT_VERSIONS
134
151
 
135
152
    layer = DatabaseFunctionalLayer
136
153
 
137
 
    def _setupDifferences(self, name, parent_name, nb_diff_versions,
 
154
    def _setupDifferences(self, name, parent_names, nb_diff_versions,
138
155
                          nb_diff_child, nb_diff_parent):
139
 
        # Helper to create DSD of the different types.
140
 
        derived_series = self.factory.makeDistroSeries(
141
 
            name=name,
142
 
            previous_series=self.factory.makeDistroSeries(name=parent_name))
 
156
        # Helper to create DSDs of the different types.
 
157
        derived_series = self.factory.makeDistroSeries(name=name)
143
158
        self.simple_user = self.factory.makePerson()
 
159
        # parent_names can be a list of parent names or a single name
 
160
        # for a single parent (e.g. ['parent1_name', 'parent2_name'] or
 
161
        # 'parent_name').
 
162
        # If multiple parents are created, the DSDs will be created with
 
163
        # the first one.
 
164
        if type(parent_names) == str:
 
165
            parent_names = [parent_names]
 
166
        dsps = []
 
167
        for parent_name in parent_names:
 
168
            parent_series = self.factory.makeDistroSeries(name=parent_name)
 
169
            dsps.append(self.factory.makeDistroSeriesParent(
 
170
                derived_series=derived_series, parent_series=parent_series))
 
171
        first_parent_series = dsps[0].parent_series
144
172
        for i in range(nb_diff_versions):
145
173
            diff_type = DistroSeriesDifferenceType.DIFFERENT_VERSIONS
146
174
            self.factory.makeDistroSeriesDifference(
147
175
                derived_series=derived_series,
148
 
                difference_type=diff_type)
 
176
                difference_type=diff_type,
 
177
                parent_series=first_parent_series)
149
178
        for i in range(nb_diff_child):
150
179
            diff_type = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
151
180
            self.factory.makeDistroSeriesDifference(
152
181
                derived_series=derived_series,
153
 
                difference_type=diff_type)
 
182
                difference_type=diff_type,
 
183
                parent_series=first_parent_series)
154
184
        for i in range(nb_diff_parent):
155
185
            diff_type = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
156
186
            self.factory.makeDistroSeriesDifference(
157
187
                derived_series=derived_series,
158
 
                difference_type=diff_type)
 
188
                difference_type=diff_type,
 
189
                parent_series=first_parent_series)
159
190
        return derived_series
160
191
 
161
192
    def test_differences_no_flag_no_portlet(self):
175
206
            html_content = view()
176
207
 
177
208
        self.assertEqual(
178
 
            None, getFeatureFlag('soyuz.derived-series-ui.enabled'))
 
209
            None, getFeatureFlag('soyuz.derived_series_ui.enabled'))
179
210
        self.assertThat(html_content, Not(portlet_header))
180
211
 
181
212
    def test_differences_portlet_all_differences(self):
212
243
 
213
244
        self.assertThat(html_content, portlet_display)
214
245
 
 
246
    def test_differences_portlet_all_differences_multiple_parents(self):
 
247
        # The difference portlet shows the differences with the multiple
 
248
        # parent series.
 
249
        set_derived_series_ui_feature_flag(self)
 
250
        derived_series = self._setupDifferences(
 
251
            'deri', ['sid1', 'sid2'], 0, 1, 0)
 
252
        portlet_display = soupmatchers.HTMLContains(
 
253
            soupmatchers.Tag(
 
254
                'Derivation portlet header', 'h2',
 
255
                text='Derived from 2 parents'),
 
256
            soupmatchers.Tag(
 
257
                'Parent diffs link', 'a',
 
258
                text=re.compile('\s*1 package only in a parent series\s*'),
 
259
                attrs={'href': re.compile('.*/\+missingpackages')}))
 
260
 
 
261
        with person_logged_in(self.simple_user):
 
262
            view = create_initialized_view(
 
263
                derived_series,
 
264
                '+index',
 
265
                principal=self.simple_user)
 
266
            # XXX rvb 2011-04-12 bug=758649: LaunchpadTestRequest overrides
 
267
            # self.features to NullFeatureController.
 
268
            view.request.features = get_relevant_feature_controller()
 
269
            html_text = view()
 
270
 
 
271
        self.assertThat(html_text, portlet_display)
 
272
 
215
273
    def test_differences_portlet_no_differences(self):
216
274
        # The difference portlet displays 'No differences' if there is no
217
275
        # differences with the parent.
354
412
 
355
413
    def test_is_derived_series_feature_enabled(self):
356
414
        # The feature is disabled by default, but can be enabled by setting
357
 
        # the soyuz.derived-series-ui.enabled flag.
 
415
        # the soyuz.derived_series_ui.enabled flag.
358
416
        distroseries = self.factory.makeDistroSeries()
359
417
        view = create_initialized_view(distroseries, "+initseries")
360
418
        with feature_flags():
361
419
            self.assertFalse(view.is_derived_series_feature_enabled)
362
420
        with feature_flags():
363
 
            set_feature_flag(u"soyuz.derived-series-ui.enabled", u"true")
 
421
            set_feature_flag(u"soyuz.derived_series_ui.enabled", u"true")
364
422
            self.assertTrue(view.is_derived_series_feature_enabled)
365
423
 
366
424
    def test_form_hidden_when_derived_series_feature_disabled(self):
382
440
        distroseries = self.factory.makeDistroSeries()
383
441
        view = create_initialized_view(distroseries, "+initseries")
384
442
        with feature_flags():
385
 
            set_feature_flag(u"soyuz.derived-series-ui.enabled", u"true")
 
443
            set_feature_flag(u"soyuz.derived_series_ui.enabled", u"true")
386
444
            root = html.fromstring(view())
387
445
            self.assertNotEqual(
388
446
                [], root.cssselect("#initseries-form-container"))
410
468
 
411
469
        self.assertThat(html_content, parent_packagesets)
412
470
 
 
471
    def _createChildAndParent(self):
 
472
        derived_series = self.factory.makeDistroSeries(name='derilucid')
 
473
        parent_series = self.factory.makeDistroSeries(name='lucid')
 
474
        self.factory.makeDistroSeriesParent(
 
475
            derived_series=derived_series, parent_series=parent_series)
 
476
        return (derived_series, parent_series)
 
477
 
 
478
    def _createChildAndParents(self, other_parent_series=None):
 
479
        derived_series, parent_series = self._createChildAndParent()
 
480
        self.factory.makeDistroSeriesParent(
 
481
            derived_series=derived_series, parent_series=other_parent_series)
 
482
        return (derived_series, parent_series)
 
483
 
413
484
 
414
485
class TestDistroSeriesLocalDifferences(
415
486
    DistroSeriesDifferenceMixin, TestCaseWithFactory):
427
498
        # Test that the page includes the filter form if differences
428
499
        # are present
429
500
        login_person(self.simple_user)
430
 
        derived_series = self.factory.makeDistroSeries(
431
 
            name='derilucid', previous_series=self.factory.makeDistroSeries(
432
 
                name='lucid'))
 
501
        derived_series, parent_series = self._createChildAndParent()
433
502
        self.factory.makeDistroSeriesDifference(
434
503
            derived_series=derived_series)
435
504
 
445
514
        # Test that the page doesn't includes the filter form if no
446
515
        # differences are present
447
516
        login_person(self.simple_user)
448
 
        derived_series = self.factory.makeDistroSeries(
449
 
            name='derilucid', previous_series=self.factory.makeDistroSeries(
450
 
                name='lucid'))
 
517
        derived_series, parent_series = self._createChildAndParent()
451
518
 
452
519
        view = create_initialized_view(
453
520
            derived_series, '+localpackagediffs', principal=self.simple_user)
463
530
        with celebrity_logged_in('admin'):
464
531
            ps = self.factory.makePackageset(
465
532
                packages=[ds_diff.source_package_name],
466
 
                distroseries=ds_diff.derived_series.previous_series)
 
533
                distroseries=ds_diff.parent_series)
467
534
 
468
535
        with person_logged_in(self.simple_user):
469
536
            view = create_initialized_view(
486
553
                self.factory.makePackageset(
487
554
                    name=name,
488
555
                    packages=[ds_diff.source_package_name],
489
 
                    distroseries=ds_diff.derived_series.previous_series)
 
556
                    distroseries=ds_diff.parent_series)
490
557
 
491
558
        with person_logged_in(self.simple_user):
492
559
            view = create_initialized_view(
493
560
                ds_diff.derived_series,
494
561
                '+localpackagediffs',
495
562
                principal=self.simple_user)
496
 
            html = view()
 
563
            html_content = view()
497
564
 
498
565
        packageset_text = re.compile(
499
566
            '\s*' + ', '.join(sorted(unsorted_names)))
500
567
        self._test_packagesets(
501
 
            html, packageset_text, 'parent-packagesets', 'Parent packagesets')
502
 
 
503
 
    def test_queries(self):
 
568
            html_content, packageset_text, 'parent-packagesets',
 
569
            'Parent packagesets')
 
570
 
 
571
 
 
572
class TestDistroSeriesLocalDiffPerformance(TestCaseWithFactory,
 
573
                                           DistroSeriesDifferenceMixin):
 
574
    """Test the distroseries +localpackagediffs page's performance."""
 
575
 
 
576
    layer = DatabaseFunctionalLayer
 
577
 
 
578
    def setUp(self):
 
579
        super(TestDistroSeriesLocalDiffPerformance,
 
580
             self).setUp('foo.bar@canonical.com')
 
581
        set_derived_series_ui_feature_flag(self)
 
582
        self.simple_user = self.factory.makePerson()
 
583
 
 
584
    def _assertQueryCount(self, derived_series):
504
585
        # With no DistroSeriesDifferences the query count should be low and
505
586
        # fairly static. However, with some DistroSeriesDifferences the query
506
587
        # count will be higher, but it should remain the same no matter how
507
588
        # many differences there are.
508
 
        derived_series = self.factory.makeDistroSeries(
509
 
            previous_series=self.factory.makeDistroSeries())
510
589
        ArchivePermission(
511
590
            archive=derived_series.main_archive, person=self.simple_user,
512
591
            component=getUtility(IComponentSet)["main"],
604
683
        self.addDetail(
605
684
            "statement-count-per-row-average",
606
685
            text_content(u"%.2f" % statement_count_per_row))
607
 
        # XXX: GavinPanella 2011-04-12 bug=760733: Reducing the query count
608
 
        # further needs work. Ideally this test would be along the lines of
609
 
        # recorder3.count == recorder2.count. 4 queries above the recorder2
610
 
        # count is 2 queries per difference which is not acceptable, but is
611
 
        # *far* better than without the changes introduced by landing this.
612
 
        compromise_statement_count = recorder2.count + 4
 
686
        # Query count is ~O(1) (i.e. not dependent of the number of
 
687
        # differences displayed).
613
688
        self.assertThat(
614
 
            recorder3, HasQueryCount(
615
 
                LessThan(compromise_statement_count + 1)))
616
 
 
617
 
 
618
 
class TestDistroSeriesLocalDifferencesZopeless(TestCaseWithFactory):
 
689
            recorder3, HasQueryCount(Equals(recorder2.count)))
 
690
 
 
691
    def test_queries_single_parent(self):
 
692
        dsp = self.factory.makeDistroSeriesParent()
 
693
        derived_series = dsp.derived_series
 
694
        self._assertQueryCount(derived_series)
 
695
 
 
696
    def test_queries_multiple_parents(self):
 
697
        dsp = self.factory.makeDistroSeriesParent()
 
698
        derived_series = dsp.derived_series
 
699
        self.factory.makeDistroSeriesParent(
 
700
            derived_series=derived_series)
 
701
        self._assertQueryCount(derived_series)
 
702
 
 
703
 
 
704
class TestDistroSeriesLocalDifferencesZopeless(TestCaseWithFactory,
 
705
                                               DistroSeriesDifferenceMixin):
619
706
    """Test the distroseries +localpackagediffs view."""
620
707
 
621
 
    layer = LaunchpadZopelessLayer
 
708
    layer = LaunchpadFunctionalLayer
 
709
 
 
710
    def makePackageUpgrade(self, derived_series=None):
 
711
        """Create a `DistroSeriesDifference` for a package upgrade."""
 
712
        base_version = '1.%d' % self.factory.getUniqueInteger()
 
713
        versions = {
 
714
            'base': base_version,
 
715
            'parent': base_version + '-' + self.factory.getUniqueString(),
 
716
            'derived': base_version,
 
717
        }
 
718
        return self.factory.makeDistroSeriesDifference(
 
719
            derived_series=derived_series, versions=versions,
 
720
            set_base_version=True)
 
721
 
 
722
    def makeView(self, distroseries=None):
 
723
        """Create a +localpackagediffs view for `distroseries`."""
 
724
        if distroseries is None:
 
725
            distroseries = (
 
726
                self.factory.makeDistroSeriesParent().derived_series)
 
727
        # current_request=True causes the current interaction to end so we
 
728
        # must explicitly ask that the current principal be used for the
 
729
        # request.
 
730
        return create_initialized_view(
 
731
            distroseries, '+localpackagediffs',
 
732
            principal=get_current_principal(),
 
733
            current_request=True)
622
734
 
623
735
    def test_view_redirects_without_feature_flag(self):
624
 
        # If the feature flag soyuz.derived-series-ui.enabled is not set the
 
736
        # If the feature flag soyuz.derived_series_ui.enabled is not set the
625
737
        # view simply redirects to the derived series.
626
 
        derived_series = self.factory.makeDistroSeries(
627
 
            name='derilucid', previous_series=self.factory.makeDistroSeries(
628
 
                name='lucid'))
 
738
        derived_series, parent_series = self._createChildAndParent()
629
739
 
630
740
        self.assertIs(
631
 
            None, getFeatureFlag('soyuz.derived-series-ui.enabled'))
632
 
        view = create_initialized_view(
633
 
            derived_series, '+localpackagediffs')
 
741
            None, getFeatureFlag('soyuz.derived_series_ui.enabled'))
 
742
        view = self.makeView(derived_series)
634
743
 
635
744
        response = view.request.response
636
745
        self.assertEqual(302, response.getStatus())
639
748
 
640
749
    def test_label(self):
641
750
        # The view label includes the names of both series.
642
 
        derived_series = self.factory.makeDistroSeries(
643
 
            name='derilucid', previous_series=self.factory.makeDistroSeries(
644
 
                name='lucid'))
 
751
        derived_series, parent_series = self._createChildAndParent()
645
752
 
646
 
        view = create_initialized_view(
647
 
            derived_series, '+localpackagediffs')
 
753
        view = self.makeView(derived_series)
648
754
 
649
755
        self.assertEqual(
650
756
            "Source package differences between 'Derilucid' and "
651
757
            "parent series 'Lucid'",
652
758
            view.label)
653
759
 
 
760
    def test_label_multiple_parents(self):
 
761
        # If the series has multiple parents, the view label mentions
 
762
        # the generic term 'parent series'.
 
763
        derived_series, parent_series = self._createChildAndParents()
 
764
 
 
765
        view = create_initialized_view(
 
766
            derived_series, '+localpackagediffs')
 
767
 
 
768
        self.assertEqual(
 
769
            "Source package differences between 'Derilucid' and "
 
770
            "parent series",
 
771
            view.label)
 
772
 
654
773
    def test_batch_includes_needing_attention_only(self):
655
774
        # The differences attribute includes differences needing
656
775
        # attention only.
657
 
        derived_series = self.factory.makeDistroSeries(
658
 
            name='derilucid', previous_series=self.factory.makeDistroSeries(
659
 
                name='lucid'))
 
776
        derived_series, parent_series = self._createChildAndParent()
660
777
        current_difference = self.factory.makeDistroSeriesDifference(
661
778
            derived_series=derived_series)
662
779
        self.factory.makeDistroSeriesDifference(
663
780
            derived_series=derived_series,
664
781
            status=DistroSeriesDifferenceStatus.RESOLVED)
665
782
 
666
 
        view = create_initialized_view(
667
 
            derived_series, '+localpackagediffs')
 
783
        view = self.makeView(derived_series)
668
784
 
669
785
        self.assertContentEqual(
670
786
            [current_difference], view.cached_differences.batch)
671
787
 
672
788
    def test_batch_includes_different_versions_only(self):
673
789
        # The view contains differences of type DIFFERENT_VERSIONS only.
674
 
        derived_series = self.factory.makeDistroSeries(
675
 
            name='derilucid', previous_series=self.factory.makeDistroSeries(
676
 
                name='lucid'))
 
790
        derived_series, parent_series = self._createChildAndParent()
677
791
        different_versions_diff = self.factory.makeDistroSeriesDifference(
678
792
            derived_series=derived_series)
679
793
        self.factory.makeDistroSeriesDifference(
681
795
            difference_type=(
682
796
                DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES))
683
797
 
684
 
        view = create_initialized_view(
685
 
            derived_series, '+localpackagediffs')
 
798
        view = self.makeView(derived_series)
686
799
 
687
800
        self.assertContentEqual(
688
801
            [different_versions_diff], view.cached_differences.batch)
689
802
 
690
803
    def test_template_includes_help_link(self):
691
804
        # The help link for popup help is included.
692
 
        derived_series = self.factory.makeDistroSeries(
693
 
            name='derilucid', previous_series=self.factory.makeDistroSeries(
694
 
                name='lucid'))
695
 
 
 
805
        derived_series, parent_series = self._createChildAndParent()
696
806
        set_derived_series_ui_feature_flag(self)
697
 
        view = create_initialized_view(
698
 
            derived_series, '+localpackagediffs')
 
807
        view = self.makeView(derived_series)
699
808
 
700
809
        soup = BeautifulSoup(view())
701
810
        help_links = soup.findAll(
704
813
 
705
814
    def test_diff_row_includes_last_comment_only(self):
706
815
        # The most recent comment is rendered for each difference.
707
 
        derived_series = self.factory.makeDistroSeries(
708
 
            name='derilucid', previous_series=self.factory.makeDistroSeries(
709
 
                name='lucid'))
 
816
        derived_series, parent_series = self._createChildAndParent()
710
817
        difference = self.factory.makeDistroSeriesDifference(
711
818
            derived_series=derived_series)
712
 
        difference.addComment(difference.owner, "Earlier comment")
713
 
        difference.addComment(difference.owner, "Latest comment")
 
819
        with person_logged_in(derived_series.owner):
 
820
            difference.addComment(difference.owner, "Earlier comment")
 
821
            difference.addComment(difference.owner, "Latest comment")
714
822
 
715
823
        set_derived_series_ui_feature_flag(self)
716
 
        view = create_initialized_view(
717
 
            derived_series, '+localpackagediffs')
 
824
        view = self.makeView(derived_series)
718
825
 
719
826
        # Find all the rows within the body of the table
720
827
        # listing the differences.
728
835
 
729
836
    def test_diff_row_links_to_extra_details(self):
730
837
        # The source package name links to the difference details.
731
 
        derived_series = self.factory.makeDistroSeries(
732
 
            name='derilucid', previous_series=self.factory.makeDistroSeries(
733
 
                name='lucid'))
 
838
        derived_series, parent_series = self._createChildAndParent()
734
839
        difference = self.factory.makeDistroSeriesDifference(
735
840
            derived_series=derived_series)
736
841
 
737
842
        set_derived_series_ui_feature_flag(self)
738
 
        view = create_initialized_view(
739
 
            derived_series, '+localpackagediffs')
 
843
        view = self.makeView(derived_series)
740
844
        soup = BeautifulSoup(view())
741
845
        diff_table = soup.find('table', {'class': 'listing'})
742
846
        row = diff_table.tbody.findAll('tr')[0]
743
847
 
744
 
        href = canonical_url(difference).replace('http://launchpad.dev', '')
745
 
        links = row.findAll('a', href=href)
 
848
        links = row.findAll('a', href=canonical_url(difference))
746
849
        self.assertEqual(1, len(links))
747
850
        self.assertEqual(difference.source_package_name.name, links[0].string)
748
851
 
 
852
    def test_multiple_parents_display(self):
 
853
        package_name = 'package-1'
 
854
        other_parent_series = self.factory.makeDistroSeries(name='other')
 
855
        derived_series, parent_series = self._createChildAndParents(
 
856
            other_parent_series=other_parent_series)
 
857
        versions = {
 
858
            'base': u'1.0',
 
859
            'derived': u'1.0derived1',
 
860
            'parent': u'1.0-1',
 
861
        }
 
862
 
 
863
        self.factory.makeDistroSeriesDifference(
 
864
            versions=versions,
 
865
            parent_series=other_parent_series,
 
866
            source_package_name_str=package_name,
 
867
            derived_series=derived_series)
 
868
        self.factory.makeDistroSeriesDifference(
 
869
            versions=versions,
 
870
            parent_series=parent_series,
 
871
            source_package_name_str=package_name,
 
872
            derived_series=derived_series)
 
873
        set_derived_series_ui_feature_flag(self)
 
874
        view = create_initialized_view(
 
875
            derived_series, '+localpackagediffs')
 
876
        multiple_parents_matches = soupmatchers.HTMLContains(
 
877
            soupmatchers.Tag(
 
878
                "Parent table header", 'th',
 
879
                text=re.compile("\s*Parent\s")),
 
880
            soupmatchers.Tag(
 
881
                "Parent version table header", 'th',
 
882
                text=re.compile("\s*Parent version\s*")),
 
883
            soupmatchers.Tag(
 
884
                "Parent name", 'a',
 
885
                attrs={'class': 'parent-name'},
 
886
                text=re.compile("\s*Other\s*")),
 
887
             )
 
888
        self.assertThat(view.render(), multiple_parents_matches)
 
889
 
749
890
    def test_diff_row_shows_version_attached(self):
750
891
        # The +localpackagediffs page shows the version attached to the
751
892
        # DSD and not the last published version (bug=745776).
752
893
        package_name = 'package-1'
753
 
        derived_series = self.factory.makeDistroSeries(
754
 
            name='derilucid', previous_series=self.factory.makeDistroSeries(
755
 
                name='lucid'))
 
894
        derived_series, parent_series = self._createChildAndParent()
756
895
        versions = {
757
896
            'base': u'1.0',
758
897
            'derived': u'1.0derived1',
774
913
            version=new_version)
775
914
 
776
915
        set_derived_series_ui_feature_flag(self)
777
 
        view = create_initialized_view(
778
 
            derived_series, '+localpackagediffs')
 
916
        view = self.makeView(derived_series)
779
917
        soup = BeautifulSoup(view())
780
918
        diff_table = soup.find('table', {'class': 'listing'})
781
919
        row = diff_table.tbody.tr
797
935
        # The +localpackagediffs page shows only the version (no link)
798
936
        # if we fail to fetch the published version.
799
937
        package_name = 'package-1'
800
 
        derived_series = self.factory.makeDistroSeries(
801
 
            name='derilucid', previous_series=self.factory.makeDistroSeries(
802
 
                name='lucid'))
 
938
        derived_series, parent_series = self._createChildAndParent()
803
939
        versions = {
804
940
            'base': u'1.0',
805
941
            'derived': u'1.0derived1',
812
948
            derived_series=derived_series)
813
949
 
814
950
        # Delete the publications.
815
 
        difference.source_pub.status = PackagePublishingStatus.DELETED
816
 
        difference.parent_source_pub.status = PackagePublishingStatus.DELETED
 
951
        with celebrity_logged_in("admin"):
 
952
            difference.source_pub.status = (
 
953
                PackagePublishingStatus.DELETED)
 
954
            difference.parent_source_pub.status = (
 
955
                PackagePublishingStatus.DELETED)
817
956
        # Flush out the changes and invalidate caches (esp. property caches).
818
957
        flush_database_caches()
819
958
 
820
959
        set_derived_series_ui_feature_flag(self)
821
 
        view = create_initialized_view(
822
 
            derived_series, '+localpackagediffs')
 
960
        view = self.makeView(derived_series)
823
961
        soup = BeautifulSoup(view())
824
962
        diff_table = soup.find('table', {'class': 'listing'})
825
963
        row = diff_table.tbody.tr
836
974
        self.assertEqual(versions['derived'], derived_span[0].string.strip())
837
975
        self.assertEqual(versions['parent'], parent_span[0].string.strip())
838
976
 
839
 
 
840
 
class TestDistroSeriesLocalDifferencesFunctional(TestCaseWithFactory):
 
977
    def test_getUpgrades_shows_updates_in_parent(self):
 
978
        # The view's getUpgrades methods lists packages that can be
 
979
        # trivially upgraded: changed in the parent, not changed in the
 
980
        # derived series, but present in both.
 
981
        dsd = self.makePackageUpgrade()
 
982
        view = self.makeView(dsd.derived_series)
 
983
        self.assertContentEqual([dsd], view.getUpgrades())
 
984
 
 
985
    def enableDerivedSeriesSyncFeature(self):
 
986
        self.useFixture(
 
987
            FeatureFixture(
 
988
                {u'soyuz.derived_series_sync.enabled': u'on'}))
 
989
 
 
990
    @with_celebrity_logged_in("admin")
 
991
    def test_upgrades_offered_only_with_feature_flag(self):
 
992
        # The "Upgrade Packages" button will only be shown when a specific
 
993
        # feature flag is enabled.
 
994
        view = self.makeView()
 
995
        self.makePackageUpgrade(view.context)
 
996
        self.assertFalse(view.canUpgrade())
 
997
        self.enableDerivedSeriesSyncFeature()
 
998
        self.assertTrue(view.canUpgrade())
 
999
 
 
1000
    def test_upgrades_are_offered_if_appropriate(self):
 
1001
        # The "Upgrade Packages" button will only be shown to privileged
 
1002
        # users.
 
1003
        self.enableDerivedSeriesSyncFeature()
 
1004
        dsd = self.makePackageUpgrade()
 
1005
        view = self.makeView(dsd.derived_series)
 
1006
        with celebrity_logged_in("admin"):
 
1007
            self.assertTrue(view.canUpgrade())
 
1008
        with person_logged_in(self.factory.makePerson()):
 
1009
            self.assertFalse(view.canUpgrade())
 
1010
        with anonymous_logged_in():
 
1011
            self.assertFalse(view.canUpgrade())
 
1012
 
 
1013
    @with_celebrity_logged_in("admin")
 
1014
    def test_upgrades_offered_only_if_available(self):
 
1015
        # If there are no upgrades, the "Upgrade Packages" button won't
 
1016
        # be shown.
 
1017
        self.enableDerivedSeriesSyncFeature()
 
1018
        view = self.makeView()
 
1019
        self.assertFalse(view.canUpgrade())
 
1020
        self.makePackageUpgrade(view.context)
 
1021
        self.assertTrue(view.canUpgrade())
 
1022
 
 
1023
    @with_celebrity_logged_in("admin")
 
1024
    def test_upgrades_not_offered_after_feature_freeze(self):
 
1025
        # There won't be an "Upgrade Packages" button once feature
 
1026
        # freeze has occurred.  Mass updates would not make sense after
 
1027
        # that point.
 
1028
        self.enableDerivedSeriesSyncFeature()
 
1029
        upgradeable = {}
 
1030
        for status in SeriesStatus.items:
 
1031
            dsd = self.makePackageUpgrade()
 
1032
            dsd.derived_series.status = status
 
1033
            view = self.makeView(dsd.derived_series)
 
1034
            upgradeable[status] = view.canUpgrade()
 
1035
        expected = {
 
1036
            SeriesStatus.FUTURE: True,
 
1037
            SeriesStatus.EXPERIMENTAL: True,
 
1038
            SeriesStatus.DEVELOPMENT: True,
 
1039
            SeriesStatus.FROZEN: False,
 
1040
            SeriesStatus.CURRENT: False,
 
1041
            SeriesStatus.SUPPORTED: False,
 
1042
            SeriesStatus.OBSOLETE: False,
 
1043
        }
 
1044
        self.assertEqual(expected, upgradeable)
 
1045
 
 
1046
    def test_upgrade_creates_sync_jobs(self):
 
1047
        # requestUpgrades generates PackageCopyJobs for the upgrades
 
1048
        # that need doing.
 
1049
        dsd = self.makePackageUpgrade()
 
1050
        series = dsd.derived_series
 
1051
        view = self.makeView(series)
 
1052
        view.requestUpgrades()
 
1053
        job_source = getUtility(IPlainPackageCopyJobSource)
 
1054
        jobs = list(
 
1055
            job_source.getActiveJobs(series.distribution.main_archive))
 
1056
        self.assertEquals(1, len(jobs))
 
1057
        job = jobs[0]
 
1058
        self.assertEquals(series, job.target_distroseries)
 
1059
        source_package_info = list(job.source_packages)
 
1060
        self.assertEquals(1, len(source_package_info))
 
1061
        self.assertEqual(
 
1062
            (dsd.source_package_name.name, dsd.parent_source_version),
 
1063
            source_package_info[0][:2])
 
1064
 
 
1065
    def test_upgrade_gives_feedback(self):
 
1066
        # requestUpgrades doesn't instantly perform package upgrades,
 
1067
        # but it shows the user a notice that the upgrades have been
 
1068
        # requested.
 
1069
        dsd = self.makePackageUpgrade()
 
1070
        view = self.makeView(dsd.derived_series)
 
1071
        view.requestUpgrades()
 
1072
        expected = {
 
1073
            "level": BrowserNotificationLevel.INFO,
 
1074
            "message":
 
1075
                ("Upgrades of {0.displayname} packages have been "
 
1076
                 "requested. Please give Launchpad some time to "
 
1077
                 "complete these.").format(dsd.derived_series),
 
1078
            }
 
1079
        observed = map(vars, view.request.response.notifications)
 
1080
        self.assertEqual([expected], observed)
 
1081
 
 
1082
    def test_requestUpgrade_is_efficient(self):
 
1083
        # A single web request may need to schedule large numbers of
 
1084
        # package upgrades.  It must do so without issuing large numbers
 
1085
        # of database queries.
 
1086
        derived_series, parent_series = self._createChildAndParent()
 
1087
        # Take a baseline measure of queries.
 
1088
        self.makePackageUpgrade(derived_series=derived_series)
 
1089
        flush_database_caches()
 
1090
        with StormStatementRecorder() as recorder1:
 
1091
            self.makeView(derived_series).requestUpgrades()
 
1092
        self.assertThat(recorder1, HasQueryCount(LessThan(10)))
 
1093
        # The query count does not increase with more differences.
 
1094
        for index in xrange(3):
 
1095
            self.makePackageUpgrade(derived_series=derived_series)
 
1096
        flush_database_caches()
 
1097
        with StormStatementRecorder() as recorder2:
 
1098
            self.makeView(derived_series).requestUpgrades()
 
1099
        self.assertThat(recorder2, HasQueryCount(Equals(recorder1.count)))
 
1100
 
 
1101
 
 
1102
class TestDistroSeriesLocalDifferencesFunctional(TestCaseWithFactory,
 
1103
                                                 DistroSeriesDifferenceMixin):
841
1104
 
842
1105
    layer = LaunchpadFunctionalLayer
843
1106
 
 
1107
    def makeDSDJob(self, dsd):
 
1108
        """Create a `DistroSeriesDifferenceJob` to update `dsd`."""
 
1109
        job_source = getUtility(IDistroSeriesDifferenceJobSource)
 
1110
        jobs = job_source.createForPackagePublication(
 
1111
            dsd.derived_series, dsd.source_package_name,
 
1112
            PackagePublishingPocket.RELEASE)
 
1113
        return jobs[0]
 
1114
 
844
1115
    def test_higher_radio_mentions_parent(self):
 
1116
        # The user is shown an option to display only the blacklisted
 
1117
        # package with a higer version than in the parent.
845
1118
        set_derived_series_ui_feature_flag(self)
846
 
        previous_series = self.factory.makeDistroSeries(
847
 
            name='lucid', displayname='Lucid')
848
 
        derived_series = self.factory.makeDistroSeries(
849
 
            name='derilucid', previous_series=previous_series)
 
1119
        derived_series, parent_series = self._createChildAndParent()
850
1120
        self.factory.makeDistroSeriesDifference(
851
1121
            derived_series=derived_series,
852
1122
            source_package_name_str="my-src-package")
863
1133
            )
864
1134
        self.assertThat(view.render(), radio_option_matches)
865
1135
 
 
1136
    def test_higher_radio_mentions_parents(self):
 
1137
        set_derived_series_ui_feature_flag(self)
 
1138
        derived_series, parent_series = self._createChildAndParents()
 
1139
        self.factory.makeDistroSeriesDifference(
 
1140
            derived_series=derived_series,
 
1141
            source_package_name_str="my-src-package")
 
1142
        view = create_initialized_view(
 
1143
            derived_series,
 
1144
            '+localpackagediffs')
 
1145
 
 
1146
        radio_title = \
 
1147
            " Ignored packages with a higher version than in parent"
 
1148
        radio_option_matches = soupmatchers.HTMLContains(
 
1149
            soupmatchers.Tag(
 
1150
                "radio displays parent's name", 'label',
 
1151
                text=radio_title),
 
1152
            )
 
1153
        self.assertThat(view.render(), radio_option_matches)
 
1154
 
866
1155
    def _set_source_selection(self, series):
867
1156
        # Set up source package format selection so that copying will
868
1157
        # work with the default dsc_format used in
873
1162
    def test_batch_filtered(self):
874
1163
        # The name_filter parameter allows filtering of packages by name.
875
1164
        set_derived_series_ui_feature_flag(self)
876
 
        derived_series = self.factory.makeDistroSeries(
877
 
            name='derilucid', previous_series=self.factory.makeDistroSeries(
878
 
                name='lucid'))
 
1165
        derived_series, parent_series = self._createChildAndParent()
879
1166
        diff1 = self.factory.makeDistroSeriesDifference(
880
1167
            derived_series=derived_series,
881
1168
            source_package_name_str="my-src-package")
899
1186
    def test_batch_non_blacklisted(self):
900
1187
        # The default filter is all non blacklisted differences.
901
1188
        set_derived_series_ui_feature_flag(self)
902
 
        derived_series = self.factory.makeDistroSeries(
903
 
            name='derilucid', previous_series=self.factory.makeDistroSeries(
904
 
                name='lucid'))
 
1189
        derived_series, parent_series = self._createChildAndParent()
905
1190
        diff1 = self.factory.makeDistroSeriesDifference(
906
1191
            derived_series=derived_series,
907
1192
            source_package_name_str="my-src-package")
929
1214
        # field.package_type parameter allows to list only
930
1215
        # blacklisted differences.
931
1216
        set_derived_series_ui_feature_flag(self)
932
 
        derived_series = self.factory.makeDistroSeries(
933
 
            name='derilucid', previous_series=self.factory.makeDistroSeries(
934
 
                name='lucid'))
 
1217
        derived_series, parent_series = self._createChildAndParent()
935
1218
        blacklisted_diff = self.factory.makeDistroSeriesDifference(
936
1219
            derived_series=derived_series,
937
1220
            status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT)
953
1236
        # field.package_type parameter allows to list only
954
1237
        # blacklisted differences with a child's version higher than parent's.
955
1238
        set_derived_series_ui_feature_flag(self)
956
 
        derived_series = self.factory.makeDistroSeries(
957
 
            name='derilucid', previous_series=self.factory.makeDistroSeries(
958
 
                name='lucid'))
 
1239
        derived_series, parent_series = self._createChildAndParent()
959
1240
        blacklisted_diff_higher = self.factory.makeDistroSeriesDifference(
960
1241
            derived_series=derived_series,
961
1242
            status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT,
983
1264
        # Test that we can search for differences that we marked
984
1265
        # resolved.
985
1266
        set_derived_series_ui_feature_flag(self)
986
 
        derived_series = self.factory.makeDistroSeries(
987
 
            name='derilucid', previous_series=self.factory.makeDistroSeries(
988
 
                name='lucid'))
 
1267
        derived_series, parent_series = self._createChildAndParent()
989
1268
 
990
1269
        self.factory.makeDistroSeriesDifference(
991
1270
            derived_series=derived_series,
1009
1288
                  difference_type=None, distribution=None):
1010
1289
        # Helper to create a derived series with fixed names and proper
1011
1290
        # source package format selection along with a DSD.
1012
 
        previous_series = self.factory.makeDistroSeries(name='warty')
 
1291
        parent_series = self.factory.makeDistroSeries(name='warty')
1013
1292
        if distribution == None:
1014
1293
            distribution = self.factory.makeDistribution('deribuntu')
1015
1294
        derived_series = self.factory.makeDistroSeries(
1016
1295
            distribution=distribution,
1017
 
            name='derilucid', previous_series=previous_series)
 
1296
            name='derilucid')
 
1297
        self.factory.makeDistroSeriesParent(
 
1298
            derived_series=derived_series, parent_series=parent_series)
1018
1299
        self._set_source_selection(derived_series)
1019
 
        self.factory.makeDistroSeriesDifference(
 
1300
        diff = self.factory.makeDistroSeriesDifference(
1020
1301
            source_package_name_str=src_name,
1021
1302
            derived_series=derived_series, versions=versions,
1022
1303
            difference_type=difference_type)
1023
1304
        sourcepackagename = self.factory.getOrMakeSourcePackageName(
1024
1305
            src_name)
1025
1306
        set_derived_series_ui_feature_flag(self)
1026
 
        return derived_series, previous_series, sourcepackagename
 
1307
        return derived_series, parent_series, sourcepackagename, str(diff.id)
1027
1308
 
1028
1309
    def test_canPerformSync_anon(self):
1029
1310
        # Anonymous users cannot sync packages.
1081
1362
 
1082
1363
            self.assertFalse(view.canPerformSync())
1083
1364
 
 
1365
    def test_hasPendingDSDUpdate_returns_False_if_no_pending_update(self):
 
1366
        dsd = self.factory.makeDistroSeriesDifference()
 
1367
        view = create_initialized_view(
 
1368
            dsd.derived_series, '+localpackagediffs')
 
1369
        self.assertFalse(view.hasPendingDSDUpdate(dsd))
 
1370
 
 
1371
    def test_hasPendingDSDUpdate_returns_True_if_pending_update(self):
 
1372
        set_derived_series_difference_jobs_feature_flag(self)
 
1373
        dsd = self.factory.makeDistroSeriesDifference()
 
1374
        self.makeDSDJob(dsd)
 
1375
        view = create_initialized_view(
 
1376
            dsd.derived_series, '+localpackagediffs')
 
1377
        self.assertTrue(view.hasPendingDSDUpdate(dsd))
 
1378
 
 
1379
    def test_hasPendingSync_returns_False_if_no_pending_sync(self):
 
1380
        dsd = self.factory.makeDistroSeriesDifference()
 
1381
        view = create_initialized_view(
 
1382
            dsd.derived_series, '+localpackagediffs')
 
1383
        self.assertFalse(view.hasPendingSync(dsd))
 
1384
 
 
1385
    def test_hasPendingSync_returns_True_if_pending_sync(self):
 
1386
        dsd = self.factory.makeDistroSeriesDifference()
 
1387
        view = create_initialized_view(
 
1388
            dsd.derived_series, '+localpackagediffs')
 
1389
        view.pending_syncs = {specify_dsd_package(dsd): object()}
 
1390
        self.assertTrue(view.hasPendingSync(dsd))
 
1391
 
 
1392
    def test_isNewerThanParent_compares_versions_not_strings(self):
 
1393
        # isNewerThanParent compares Debian-style version numbers, not
 
1394
        # raw version strings.  So it's possible for a child version to
 
1395
        # be considered newer than the corresponding parent version even
 
1396
        # though a string comparison goes the other way.
 
1397
        versions = dict(base='1.0', parent='1.1c', derived='1.10')
 
1398
        dsd = self.factory.makeDistroSeriesDifference(versions=versions)
 
1399
        view = create_initialized_view(
 
1400
            dsd.derived_series, '+localpackagediffs')
 
1401
 
 
1402
        # Assumption for the test: the child version is greater than the
 
1403
        # parent version, but a string comparison puts them the other
 
1404
        # way around.
 
1405
        self.assertFalse(versions['parent'] < versions['derived'])
 
1406
        self.assertTrue(
 
1407
            Version(versions['parent']) < Version(versions['derived']))
 
1408
 
 
1409
        # isNewerThanParent is not fooled by the misleading string
 
1410
        # comparison.
 
1411
        self.assertTrue(view.isNewerThanParent(dsd))
 
1412
 
 
1413
    def test_isNewerThanParent_is_False_for_parent_update(self):
 
1414
        dsd = self.factory.makeDistroSeriesDifference(
 
1415
            versions=dict(base='1.0', parent='1.1', derived='1.0'))
 
1416
        view = create_initialized_view(
 
1417
            dsd.derived_series, '+localpackagediffs')
 
1418
        self.assertFalse(view.isNewerThanParent(dsd))
 
1419
 
 
1420
    def test_isNewerThanParent_is_False_for_equivalent_updates(self):
 
1421
        # Some non-identical version numbers compare as "equal."  If the
 
1422
        # child and parent versions compare as equal, the child version
 
1423
        # is not considered newer.
 
1424
        dsd = self.factory.makeDistroSeriesDifference(
 
1425
            versions=dict(base='1.0', parent='1.1', derived='1.1'))
 
1426
        view = create_initialized_view(
 
1427
            dsd.derived_series, '+localpackagediffs')
 
1428
        self.assertFalse(view.isNewerThanParent(dsd))
 
1429
 
 
1430
    def test_isNewerThanParent_is_True_for_child_update(self):
 
1431
        dsd = self.factory.makeDistroSeriesDifference(
 
1432
            versions=dict(base='1.0', parent='1.0', derived='1.1'))
 
1433
        view = create_initialized_view(
 
1434
            dsd.derived_series, '+localpackagediffs')
 
1435
        self.assertTrue(view.isNewerThanParent(dsd))
 
1436
 
 
1437
    def test_canRequestSync_returns_False_if_pending_sync(self):
 
1438
        dsd = self.factory.makeDistroSeriesDifference()
 
1439
        view = create_initialized_view(
 
1440
            dsd.derived_series, '+localpackagediffs')
 
1441
        view.pending_syncs = {specify_dsd_package(dsd): object()}
 
1442
        self.assertFalse(view.canRequestSync(dsd))
 
1443
 
 
1444
    def test_canRequestSync_returns_False_if_child_is_newer(self):
 
1445
        dsd = self.factory.makeDistroSeriesDifference(
 
1446
            versions=dict(base='1.0', parent='1.0', derived='1.1'))
 
1447
        view = create_initialized_view(
 
1448
            dsd.derived_series, '+localpackagediffs')
 
1449
        self.assertFalse(view.canRequestSync(dsd))
 
1450
 
 
1451
    def test_canRequestSync_returns_True_if_sync_makes_sense(self):
 
1452
        dsd = self.factory.makeDistroSeriesDifference()
 
1453
        view = create_initialized_view(
 
1454
            dsd.derived_series, '+localpackagediffs')
 
1455
        self.assertTrue(view.canRequestSync(dsd))
 
1456
 
 
1457
    def test_canRequestSync_ignores_DSDJobs(self):
 
1458
        dsd = self.factory.makeDistroSeriesDifference()
 
1459
        view = create_initialized_view(
 
1460
            dsd.derived_series, '+localpackagediffs')
 
1461
        view.hasPendingDSDUpdate = FakeMethod(result=True)
 
1462
        self.assertTrue(view.canRequestSync(dsd))
 
1463
 
 
1464
    def test_describeJobs_returns_None_if_no_jobs(self):
 
1465
        dsd = self.factory.makeDistroSeriesDifference()
 
1466
        view = create_initialized_view(
 
1467
            dsd.derived_series, '+localpackagediffs')
 
1468
        self.assertIs(None, view.describeJobs(dsd))
 
1469
 
 
1470
    def test_describeJobs_reports_pending_update(self):
 
1471
        dsd = self.factory.makeDistroSeriesDifference()
 
1472
        view = create_initialized_view(
 
1473
            dsd.derived_series, '+localpackagediffs')
 
1474
        view.hasPendingDSDUpdate = FakeMethod(result=True)
 
1475
        view.hasPendingSync = FakeMethod(result=False)
 
1476
        self.assertEqual("updating&hellip;", view.describeJobs(dsd))
 
1477
 
 
1478
    def test_describeJobs_reports_pending_sync(self):
 
1479
        dsd = self.factory.makeDistroSeriesDifference()
 
1480
        view = create_initialized_view(
 
1481
            dsd.derived_series, '+localpackagediffs')
 
1482
        view.hasPendingDSDUpdate = FakeMethod(result=False)
 
1483
        view.hasPendingSync = FakeMethod(result=True)
 
1484
        self.assertEqual("synchronizing&hellip;", view.describeJobs(dsd))
 
1485
 
 
1486
    def test_describeJobs_reports_pending_sync_and_update(self):
 
1487
        dsd = self.factory.makeDistroSeriesDifference()
 
1488
        view = create_initialized_view(
 
1489
            dsd.derived_series, '+localpackagediffs')
 
1490
        view.hasPendingDSDUpdate = FakeMethod(result=True)
 
1491
        view.hasPendingSync = FakeMethod(result=True)
 
1492
        self.assertEqual(
 
1493
            "updating and synchronizing&hellip;", view.describeJobs(dsd))
 
1494
 
1084
1495
    def _syncAndGetView(self, derived_series, person, sync_differences,
1085
1496
                        difference_type=None, view_name='+localpackagediffs'):
1086
1497
        # A helper to get the POST'ed sync view.
1106
1517
 
1107
1518
    def test_sync_error_invalid_selection(self):
1108
1519
        # An error is raised when an invalid difference is selected.
1109
 
        derived_series = self._setUpDSD('my-src-name')[0]
 
1520
        derived_series, unused, unused2, diff_id = self._setUpDSD(
 
1521
            'my-src-name')
1110
1522
        person = self._setUpPersonWithPerm(derived_series)
 
1523
        another_id = str(int(diff_id) + 1)
1111
1524
        set_derived_series_sync_feature_flag(self)
1112
1525
        view = self._syncAndGetView(
1113
 
            derived_series, person, ['some-other-name'])
 
1526
            derived_series, person, [another_id])
1114
1527
 
1115
1528
        self.assertEqual(2, len(view.errors))
1116
1529
        self.assertEqual(
1121
1534
    def test_sync_error_no_perm_dest_archive(self):
1122
1535
        # A user without upload rights on the destination archive cannot
1123
1536
        # sync packages.
1124
 
        derived_series = self._setUpDSD('my-src-name')[0]
 
1537
        derived_series, unused, unused2, diff_id = self._setUpDSD(
 
1538
            'my-src-name')
1125
1539
        person = self._setUpPersonWithPerm(derived_series)
1126
1540
        set_derived_series_sync_feature_flag(self)
1127
1541
        view = self._syncAndGetView(
1128
 
            derived_series, person, ['my-src-name'])
 
1542
            derived_series, person, [diff_id])
1129
1543
 
1130
1544
        self.assertEqual(1, len(view.errors))
1131
1545
        self.assertTrue(
1143
1557
    def test_sync_success_perm_component(self):
1144
1558
        # A user with upload rights on the destination component
1145
1559
        # can sync packages.
1146
 
        derived_series, previous_series, sourcepackagename = self._setUpDSD(
 
1560
        derived_series, parent_series, sp_name, diff_id = self._setUpDSD(
1147
1561
            'my-src-name')
1148
1562
        person, _ = self.makePersonWithComponentPermission(
1149
1563
            derived_series.main_archive,
1150
1564
            derived_series.getSourcePackage(
1151
 
                sourcepackagename).latest_published_component)
 
1565
                sp_name).latest_published_component)
1152
1566
        view = self._syncAndGetView(
1153
 
            derived_series, person, ['my-src-name'])
 
1567
            derived_series, person, [diff_id])
1154
1568
 
1155
1569
        self.assertEqual(0, len(view.errors))
1156
1570
 
1157
1571
    def test_sync_error_no_perm_component(self):
1158
1572
        # A user without upload rights on the destination component
1159
1573
        # will get an error when he syncs packages to this component.
1160
 
        derived_series, previous_series, sourcepackagename = self._setUpDSD(
 
1574
        derived_series, parent_series, unused, diff_id = self._setUpDSD(
1161
1575
            'my-src-name')
1162
1576
        person, another_component = self.makePersonWithComponentPermission(
1163
1577
            derived_series.main_archive)
1164
1578
        set_derived_series_sync_feature_flag(self)
1165
1579
        view = self._syncAndGetView(
1166
 
            derived_series, person, ['my-src-name'])
 
1580
            derived_series, person, [diff_id])
1167
1581
 
1168
1582
        self.assertEqual(1, len(view.errors))
1169
1583
        self.assertTrue(
1203
1617
            'derived': '1.0derived1',
1204
1618
            'parent': '1.0-1',
1205
1619
        }
1206
 
        derived_series, previous_series, sourcepackagename = self._setUpDSD(
 
1620
        derived_series, parent_series, sp_name, diff_id = self._setUpDSD(
1207
1621
            'my-src-name', versions=versions)
1208
1622
 
1209
1623
        # Setup a user with upload rights.
1210
1624
        person = self.factory.makePerson()
1211
1625
        removeSecurityProxy(derived_series.main_archive).newPackageUploader(
1212
 
            person, sourcepackagename)
 
1626
            person, sp_name)
1213
1627
 
1214
1628
        # The inital state is that 1.0-1 is not in the derived series.
1215
1629
        pubs = derived_series.main_archive.getPublishedSources(
1220
1634
        # Now, sync the source from the parent using the form.
1221
1635
        set_derived_series_sync_feature_flag(self)
1222
1636
        view = self._syncAndGetView(
1223
 
            derived_series, person, ['my-src-name'])
 
1637
            derived_series, person, [diff_id])
1224
1638
 
1225
1639
        # The parent's version should now be in the derived series and
1226
1640
        # the notifications displayed:
1235
1649
            'parent': '1.0-1',
1236
1650
        }
1237
1651
        missing = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
1238
 
        derived_series, previous_series, sourcepackagename = self._setUpDSD(
 
1652
        derived_series, parent_series, unused, diff_id = self._setUpDSD(
1239
1653
            'my-src-name', difference_type=missing, versions=versions)
1240
1654
        person, another_component = self.makePersonWithComponentPermission(
1241
1655
            derived_series.main_archive)
1242
1656
        set_derived_series_sync_feature_flag(self)
1243
1657
        view = self._syncAndGetView(
1244
 
            derived_series, person, ['my-src-name'],
 
1658
            derived_series, person, [diff_id],
1245
1659
            view_name='+missingpackages')
1246
1660
 
1247
1661
        self.assertPackageCopied(
1248
1662
            derived_series, 'my-src-name', versions['parent'], view)
1249
1663
 
1250
 
 
1251
1664
    def test_sync_in_released_series_in_updates(self):
1252
1665
        # If the destination series is released, the sync packages end
1253
1666
        # up in the updates pocket.
1254
1667
        versions = {
1255
1668
            'parent': '1.0-1',
1256
1669
            }
1257
 
        derived_series, previous_series, sourcepackagename = self._setUpDSD(
 
1670
        derived_series, parent_series, sp_name, diff_id = self._setUpDSD(
1258
1671
            'my-src-name', versions=versions)
1259
1672
        # Update destination series status to current and update
1260
1673
        # daterelease.
1265
1678
        set_derived_series_sync_feature_flag(self)
1266
1679
        person = self.factory.makePerson()
1267
1680
        removeSecurityProxy(derived_series.main_archive).newPackageUploader(
1268
 
            person, sourcepackagename)
 
1681
            person, sp_name)
1269
1682
        self._syncAndGetView(
1270
 
            derived_series, person, ['my-src-name'])
1271
 
        parent_pub = previous_series.main_archive.getPublishedSources(
 
1683
            derived_series, person, [diff_id])
 
1684
        parent_pub = parent_series.main_archive.getPublishedSources(
1272
1685
            name='my-src-name', version=versions['parent'],
1273
 
            distroseries=previous_series).one()
 
1686
            distroseries=parent_series).one()
1274
1687
        pub = derived_series.main_archive.getPublishedSources(
1275
1688
            name='my-src-name', version=versions['parent'],
1276
1689
            distroseries=derived_series).one()
1302
1715
    def test_missingpackages_differences(self):
1303
1716
        # The view fetches the differences with type
1304
1717
        # MISSING_FROM_DERIVED_SERIES.
1305
 
        derived_series = self.factory.makeDistroSeries(
1306
 
            previous_series=self.factory.makeDistroSeries())
 
1718
        dsp = self.factory.makeDistroSeriesParent()
 
1719
        derived_series = dsp.derived_series
1307
1720
 
1308
1721
        missing_type = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
1309
1722
        # Missing blacklisted diff.
1326
1739
    def test_missingpackages_differences_empty(self):
1327
1740
        # The view is empty if there is no differences with type
1328
1741
        # MISSING_FROM_DERIVED_SERIES.
1329
 
        derived_series = self.factory.makeDistroSeries(
1330
 
            previous_series=self.factory.makeDistroSeries())
 
1742
        dsp = self.factory.makeDistroSeriesParent()
 
1743
        derived_series = dsp.derived_series
1331
1744
 
1332
1745
        not_missing_type = DistroSeriesDifferenceType.DIFFERENT_VERSIONS
1333
1746
 
1343
1756
        self.assertContentEqual(
1344
1757
            [], view.cached_differences.batch)
1345
1758
 
1346
 
 
1347
 
class DistroSeriesMissingPackagesPageTestCase(DistroSeriesDifferenceMixin,
1348
 
                                              TestCaseWithFactory):
 
1759
    def test_isNewerThanParent_is_False_if_missing_from_child(self):
 
1760
        # If a package is missing from the child series,
 
1761
        # isNewerThanParent returns False.
 
1762
        missing_type = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
 
1763
        dsd = self.factory.makeDistroSeriesDifference(
 
1764
            difference_type=missing_type)
 
1765
        view = create_initialized_view(dsd.derived_series, '+missingpackages')
 
1766
        self.assertFalse(view.isNewerThanParent(dsd))
 
1767
 
 
1768
 
 
1769
class DistroSeriesMissingPackagesPageTestCase(TestCaseWithFactory,
 
1770
                                              DistroSeriesDifferenceMixin):
1349
1771
    """Test the distroseries +missingpackages page."""
1350
1772
 
1351
1773
    layer = DatabaseFunctionalLayer
1365
1787
        with celebrity_logged_in('admin'):
1366
1788
            ps = self.factory.makePackageset(
1367
1789
                packages=[self.ds_diff.source_package_name],
1368
 
                distroseries=self.ds_diff.derived_series.previous_series)
 
1790
                distroseries=self.ds_diff.parent_series)
1369
1791
 
1370
1792
        with person_logged_in(self.simple_user):
1371
1793
            view = create_initialized_view(
1372
1794
                self.ds_diff.derived_series,
1373
1795
                '+missingpackages',
1374
1796
                principal=self.simple_user)
1375
 
            html = view()
 
1797
            html_content = view()
1376
1798
 
1377
1799
        packageset_text = re.compile('\s*' + ps.name)
1378
1800
        self._test_packagesets(
1379
 
            html, packageset_text, 'parent-packagesets', 'Parent packagesets')
1380
 
 
1381
 
 
1382
 
class DistroSerieUniquePackageDiffsTestCase(TestCaseWithFactory):
 
1801
            html_content, packageset_text, 'parent-packagesets',
 
1802
            'Parent packagesets')
 
1803
 
 
1804
 
 
1805
class DistroSerieUniquePackageDiffsTestCase(TestCaseWithFactory,
 
1806
                                            DistroSeriesDifferenceMixin):
1383
1807
    """Test the distroseries +uniquepackages view."""
1384
1808
 
1385
1809
    layer = LaunchpadZopelessLayer
1387
1811
    def test_uniquepackages_differences(self):
1388
1812
        # The view fetches the differences with type
1389
1813
        # UNIQUE_TO_DERIVED_SERIES.
1390
 
        derived_series = self.factory.makeDistroSeries(
1391
 
            name='derilucid', previous_series=self.factory.makeDistroSeries(
1392
 
                name='lucid'))
 
1814
        derived_series, parent_series = self._createChildAndParent()
1393
1815
 
1394
1816
        missing_type = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
1395
1817
        # Missing blacklisted diff.
1412
1834
    def test_uniquepackages_differences_empty(self):
1413
1835
        # The view is empty if there is no differences with type
1414
1836
        # UNIQUE_TO_DERIVED_SERIES.
1415
 
        derived_series = self.factory.makeDistroSeries(
1416
 
            previous_series=self.factory.makeDistroSeries())
 
1837
        derived_series, parent_series = self._createChildAndParent()
1417
1838
 
1418
1839
        not_missing_type = DistroSeriesDifferenceType.DIFFERENT_VERSIONS
1419
1840
 
1429
1850
        self.assertContentEqual(
1430
1851
            [], view.cached_differences.batch)
1431
1852
 
1432
 
 
1433
 
class DistroSeriesUniquePackagesPageTestCase(DistroSeriesDifferenceMixin,
1434
 
                                             TestCaseWithFactory):
 
1853
    def test_isNewerThanParent_is_True_if_unique_to_child(self):
 
1854
        unique_to_child = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
 
1855
        dsd = self.factory.makeDistroSeriesDifference(
 
1856
            difference_type=unique_to_child)
 
1857
        view = create_initialized_view(
 
1858
            dsd.derived_series, '+localpackagediffs')
 
1859
        self.assertTrue(view.isNewerThanParent(dsd))
 
1860
 
 
1861
 
 
1862
class DistroSeriesUniquePackagesPageTestCase(TestCaseWithFactory,
 
1863
                                             DistroSeriesDifferenceMixin):
1435
1864
    """Test the distroseries +uniquepackages page."""
1436
1865
 
1437
1866
    layer = DatabaseFunctionalLayer