← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~abentley/launchpad/user-blueprints-tests into lp:launchpad

 

Aaron Bentley has proposed merging lp:~abentley/launchpad/user-blueprints-tests into lp:launchpad.

Commit message:
Add tests for Person.specifications.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~abentley/launchpad/user-blueprints-tests/+merge/129944

= Summary =
Add tests for Person.specifications.

== Pre-implementation notes ==
None

== LOC Rationale ==
Part of private projects

== Implementation details ==
This branch adds tests for Person.specifications, so that a follow-on branch can re-write it to use Storm expressions.  Many of these tests were copied from the tests for Sprint.specifications.

== Tests ==
bin/test -t TestSpecifications test_person

== Demo and Q/A ==
None


= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/testing/factory.py
  lib/lp/registry/tests/test_person.py
-- 
https://code.launchpad.net/~abentley/launchpad/user-blueprints-tests/+merge/129944
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~abentley/launchpad/user-blueprints-tests into lp:launchpad.
=== modified file 'lib/lp/registry/tests/test_person.py'
--- lib/lp/registry/tests/test_person.py	2012-09-28 06:15:58 +0000
+++ lib/lp/registry/tests/test_person.py	2012-10-16 16:52:26 +0000
@@ -24,6 +24,14 @@
 from lp.answers.model.answercontact import AnswerContact
 from lp.app.enums import InformationType
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
+from lp.blueprints.enums import (
+    NewSpecificationDefinitionStatus,
+    SpecificationDefinitionStatus,
+    SpecificationImplementationStatus,
+    SpecificationPriority,
+    SpecificationFilter,
+    SpecificationSort,
+    )
 from lp.blueprints.model.specification import Specification
 from lp.bugs.interfaces.bugtasksearch import (
     get_person_bugtasks_search_params,
@@ -1550,3 +1558,208 @@
         # 10. One to get all distroseries of a bug's distro. (See comment on
         # getAssignedBugTasksDueBefore() to understand why it's needed)
         self.assertThat(recorder, HasQueryCount(Equals(12)))
+
+
+def list_result(sprint, filter=None, user=None):
+    result = sprint.specifications(user, SpecificationSort.DATE, filter=filter)
+    return list(result)
+
+
+class TestSpecifications(TestCaseWithFactory):
+
+    layer = DatabaseFunctionalLayer
+
+    def setUp(self):
+        super(TestSpecifications, self).setUp()
+        self.date_created = datetime.now(pytz.utc)
+
+    def makeSpec(self, owner=None, date_created=0, title=None,
+                 status=NewSpecificationDefinitionStatus.NEW,
+                 name=None, priority=None, information_type=None):
+        blueprint = self.factory.makeSpecification(
+            title=title, status=status, name=name, priority=priority,
+            information_type=information_type, owner=owner,
+            )
+        removeSecurityProxy(blueprint).datecreated = (
+            self.date_created + timedelta(date_created))
+        return blueprint
+
+    def test_specifications_quantity(self):
+        # Ensure the quantity controls the maximum number of entries.
+        owner = self.factory.makePerson()
+        for count in range(10):
+            self.factory.makeSpecification(owner=owner)
+        self.assertEqual(10, owner.specifications(None).count())
+        result = owner.specifications(None, quantity=None).count()
+        self.assertEqual(10, result)
+        self.assertEqual(8, owner.specifications(None, quantity=8).count())
+        self.assertEqual(10, owner.specifications(None, quantity=11).count())
+
+    def test_date_sort(self):
+        # Sort on date_created.
+        owner = self.factory.makePerson()
+        blueprint1 = self.makeSpec(owner, date_created=0)
+        blueprint2 = self.makeSpec(owner, date_created=-1)
+        blueprint3 = self.makeSpec(owner, date_created=1)
+        result = list_result(owner)
+        self.assertEqual([blueprint3, blueprint1, blueprint2], result)
+
+    def test_date_sort_id(self):
+        # date-sorting when no date varies uses object id.
+        owner = self.factory.makePerson()
+        blueprint1 = self.makeSpec(owner)
+        blueprint2 = self.makeSpec(owner)
+        blueprint3 = self.makeSpec(owner)
+        result = list_result(owner)
+        self.assertEqual([blueprint1, blueprint2, blueprint3], result)
+
+    def test_priority_sort(self):
+        # Sorting by priority works and is the default.
+        # When priority is supplied, status is ignored.
+        blueprint1 = self.makeSpec(priority=SpecificationPriority.UNDEFINED,
+                                   status=SpecificationDefinitionStatus.NEW)
+        owner = blueprint1.owner
+        blueprint2 = self.makeSpec(
+            owner, priority=SpecificationPriority.NOTFORUS,
+            status=SpecificationDefinitionStatus.APPROVED)
+        blueprint3 = self.makeSpec(
+            owner, priority=SpecificationPriority.LOW,
+            status=SpecificationDefinitionStatus.NEW)
+        result = owner.specifications(None)
+        self.assertEqual([blueprint3, blueprint1, blueprint2], list(result))
+        result = owner.specifications(None, sort=SpecificationSort.PRIORITY)
+        self.assertEqual([blueprint3, blueprint1, blueprint2], list(result))
+
+    def test_priority_sort_fallback_status(self):
+        # Sorting by priority falls back to defintion_status.
+        # When status is supplied, name is ignored.
+        blueprint1 = self.makeSpec(
+            status=SpecificationDefinitionStatus.NEW, name='a')
+        owner = blueprint1.owner
+        blueprint2 = self.makeSpec(
+            owner, status=SpecificationDefinitionStatus.APPROVED, name='c')
+        blueprint3 = self.makeSpec(
+            owner, status=SpecificationDefinitionStatus.DISCUSSION, name='b')
+        result = owner.specifications(None)
+        self.assertEqual([blueprint2, blueprint3, blueprint1], list(result))
+        result = owner.specifications(None, sort=SpecificationSort.PRIORITY)
+        self.assertEqual([blueprint2, blueprint3, blueprint1], list(result))
+
+    def test_priority_sort_fallback_name(self):
+        # Sorting by priority falls back to name
+        blueprint1 = self.makeSpec(name='b')
+        owner = blueprint1.owner
+        blueprint2 = self.makeSpec(owner, name='c')
+        blueprint3 = self.makeSpec(owner, name='a')
+        result = owner.specifications(None)
+        self.assertEqual([blueprint3, blueprint1, blueprint2], list(result))
+        result = owner.specifications(None, sort=SpecificationSort.PRIORITY)
+        self.assertEqual([blueprint3, blueprint1, blueprint2], list(result))
+
+    def test_ignore_inactive(self):
+        # Specs for inactive products are skipped.
+        product = self.factory.makeProduct()
+        with celebrity_logged_in('admin'):
+            product.active = False
+        spec = self.factory.makeSpecification(product=product)
+        self.assertNotIn(spec, spec.owner.specifications(None))
+
+    def test_include_distro(self):
+        # Specs for distributions are included.
+        distribution = self.factory.makeDistribution()
+        spec = self.factory.makeSpecification(distribution=distribution)
+        self.assertIn(spec, spec.owner.specifications(None))
+
+    def test_informational(self):
+        # INFORMATIONAL causes only informational specs to be shown.
+        enum = SpecificationImplementationStatus
+        informational = self.factory.makeSpecification(
+            implementation_status=enum.INFORMATIONAL)
+        owner = informational.owner
+        plain = self.factory.makeSpecification(owner=owner)
+        result = owner.specifications(None)
+        self.assertIn(informational, result)
+        self.assertIn(plain, result)
+        result = owner.specifications(
+            None, filter=[SpecificationFilter.INFORMATIONAL])
+        self.assertIn(informational, result)
+        self.assertNotIn(plain, result)
+
+    def test_completeness(self):
+        # If COMPLETE is specified, completed specs are listed.  If INCOMPLETE
+        # is specified or neither is specified, only incomplete specs are
+        # listed.
+        enum = SpecificationImplementationStatus
+        implemented = self.factory.makeSpecification(
+            implementation_status=enum.IMPLEMENTED)
+        owner = implemented.owner
+        non_implemented = self.factory.makeSpecification(owner=owner)
+        result = owner.specifications(
+            None, filter=[SpecificationFilter.COMPLETE])
+        self.assertIn(implemented, result)
+        self.assertNotIn(non_implemented, result)
+
+        result = owner.specifications(
+            None, filter=[SpecificationFilter.INCOMPLETE])
+        self.assertNotIn(implemented, result)
+        self.assertIn(non_implemented, result)
+        result = owner.specifications(
+            None)
+        self.assertNotIn(implemented, result)
+        self.assertIn(non_implemented, result)
+
+    def test_all(self):
+        # ALL causes both complete and incomplete to be listed.
+        enum = SpecificationImplementationStatus
+        implemented = self.factory.makeSpecification(
+            implementation_status=enum.IMPLEMENTED)
+        owner = implemented.owner
+        non_implemented = self.factory.makeSpecification(owner=owner)
+        result = owner.specifications(None, filter=[SpecificationFilter.ALL])
+        self.assertContentEqual([implemented, non_implemented], result)
+
+    def test_valid(self):
+        # VALID adjusts COMPLETE to exclude OBSOLETE and SUPERSEDED specs.
+        # (INCOMPLETE already excludes OBSOLETE and SUPERSEDED.)
+        i_enum = SpecificationImplementationStatus
+        d_enum = SpecificationDefinitionStatus
+        implemented = self.factory.makeSpecification(
+            implementation_status=i_enum.IMPLEMENTED)
+        owner = implemented.owner
+        self.factory.makeSpecification(owner=owner, status=d_enum.SUPERSEDED)
+        self.factory.makeSpecification(owner=owner, status=d_enum.OBSOLETE)
+        filter = [SpecificationFilter.VALID, SpecificationFilter.COMPLETE]
+        results = owner.specifications(None, filter=filter)
+        self.assertContentEqual([implemented], results)
+
+    def test_roles(self):
+        # If roles are specified, they control which specifications are shown.
+        # If no roles are specified, all roles are used.
+        created = self.factory.makeSpecification()
+        person = created.owner
+
+        def rlist(filter=None):
+            return list(person.specifications(None, filter=filter))
+        assigned = self.factory.makeSpecification(assignee=person)
+        drafting = self.factory.makeSpecification(drafter=person)
+        approving = self.factory.makeSpecification(approver=person)
+        subscribed = self.factory.makeSpecification()
+        subscribed.subscribe(person)
+        self.assertEqual([created, assigned, drafting, approving, subscribed],
+                         rlist([]))
+        self.assertEqual([created], rlist([SpecificationFilter.CREATOR]))
+        self.assertEqual([assigned], rlist([SpecificationFilter.ASSIGNEE]))
+        self.assertEqual([drafting], rlist([SpecificationFilter.DRAFTER]))
+        self.assertEqual([approving], rlist([SpecificationFilter.APPROVER]))
+        self.assertEqual([subscribed],
+                         rlist([SpecificationFilter.SUBSCRIBER]))
+
+    def test_text_search(self):
+        # Text searches work.
+        blueprint1 = self.makeSpec(title='abc')
+        owner = blueprint1.owner
+        blueprint2 = self.makeSpec(owner, title='def')
+        result = list_result(owner, ['abc'])
+        self.assertEqual([blueprint1], result)
+        result = list_result(owner, ['def'])
+        self.assertEqual([blueprint2], result)

=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py	2012-10-15 23:20:25 +0000
+++ lib/lp/testing/factory.py	2012-10-16 16:52:26 +0000
@@ -2129,7 +2129,8 @@
         if status.name not in status_names:
             # Set the closed status after the status has a sane initial state.
             naked_spec.definition_status = status
-        if status == SpecificationDefinitionStatus.OBSOLETE:
+        if status in (SpecificationDefinitionStatus.OBSOLETE,
+                      SpecificationDefinitionStatus.SUPERSEDED):
             # This is to satisfy a DB constraint of obsolete specs.
             naked_spec.completer = owner
             naked_spec.date_completed = datetime.now(pytz.UTC)


Follow ups