diff --git a/.coveragerc b/.coveragerc index 998ef4d..b49c15d 100644 --- a/.coveragerc +++ b/.coveragerc @@ -10,6 +10,6 @@ omit = */imaginary/pyparsing.py [paths] -source = +paths = src/ */site-packages/ diff --git a/.coveragerc-examplegame b/.coveragerc-examplegame new file mode 100644 index 0000000..59d3d5e --- /dev/null +++ b/.coveragerc-examplegame @@ -0,0 +1,11 @@ +[run] +branch = True +parallel = True + +source_pkgs = + examplegame + +[paths] +source_paths = + ExampleGame/ + */site-packages/ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 262e76b..632c12c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,23 +61,48 @@ jobs: with: python-version: "${{ matrix.python-version }}" - - name: "Install Python packages" + - name: "Install Imaginary and dependencies" run: | pip install --upgrade pip setuptools coverage - pip install . ./ExampleGame - pip list + pip install . - - name: "Run Tests" + - name: "Dump Environment Info" + uses: "twisted/python-info-action@v1.0.1" + + - name: "Run Imaginary Tests" run: | python -m coverage run -m twisted.trial imaginary + # Combine the Imaginary coverage results in a .coverage data file. + # Like the `coverage run` above, use the default .coveragerc. coverage combine - # --ignore-errors is required here because the path resolution can't - # --figure out the right place to find Twisted dropin source files. - # --Without --ignore-errors the report fails when it fails to read - # --these sources. - coverage report --show-missing --ignore-errors + + - name: "Install ExampleGame and dependencies" + run: | + pip install ./ExampleGame + + - name: "Dump Environment Info" + uses: "twisted/python-info-action@v1.0.1" + + - name: "Run ExampleGame Tests" + run: | + python -m coverage run --rcfile .coveragerc-examplegame -m twisted.trial examplegame + # Combine the ExampleGame coverage results into the existing + # .coverage data file. Make sure to use the ExampleGame .coveragerc + # so paths are resolved correctly. + coverage combine --rcfile .coveragerc-examplegame --append - name: "Upload Coverage Results" run: | + # For fun, I guess, dump a text report to CI output. These aren't + # too much fun to read but perhaps it's useful sometimes if the CI + # reporting service is misbehaving. + # + # --ignore-errors is required here because the path resolution can't + # figure out the right place to find Twisted dropin source files. + # Without --ignore-errors the report fails when it fails to read + # these sources. + coverage report --show-missing --ignore-errors + + # Okay actually upload it. pip install coveralls COVERALLS_REPO_TOKEN=1oyBvUgwFpx0lZfqAXiqM8LPmgKk2Y7OL coveralls diff --git a/doc/examples/example_world.py b/doc/examples/example_world.py index 3f7ca58..4ffc83a 100644 --- a/doc/examples/example_world.py +++ b/doc/examples/example_world.py @@ -5,6 +5,9 @@ from imaginary.objects import Thing, Container, Exit from imaginary.garments import createShirt, createPants from imaginary.iimaginary import IClothing, IClothingWearer +from imaginary.manipulation import ( + Manipulator, +) from examplegame.squeaky import Squeaker @@ -23,6 +26,10 @@ def room(name, description): origin = room("The Beginning", "Everything here looks fresh and new.") world = ImaginaryWorld(store=store, origin=origin) protagonist = world.create("An Example Player") + # Allow the protagonist to use the mildly privileged "manipulator" + # actions. + Manipulator.createFor(protagonist) + shirt = createShirt(store=store, name="shirt", location=world.origin) pants = createPants(store=store, name="pants", location=world.origin) middle = room( diff --git a/src/imaginary/objects.py b/src/imaginary/objects.py index 3c76f60..aeb73f7 100644 --- a/src/imaginary/objects.py +++ b/src/imaginary/objects.py @@ -293,6 +293,9 @@ def moveTo(self, where, arrivalEventFactory=None): .capitalizeConcept(), " won't fit inside itself."])) + if not self.portable: + raise eimaginary.CannotMove(self, where) + oldLocation = self.location for restriction in self.powerupsFor(iimaginary.IMovementRestriction): restriction.movementImminent(self, where) @@ -300,8 +303,6 @@ def moveTo(self, where, arrivalEventFactory=None): events.DepartureEvent(oldLocation, self).broadcast() if where is not None: where = iimaginary.IContainer(where) - if oldLocation is not None and not self.portable: - raise eimaginary.CannotMove(self, where) where.add(self) if arrivalEventFactory is not None: arrivalEventFactory(self).broadcast() diff --git a/src/imaginary/test/test_actions.py b/src/imaginary/test/test_actions.py index a866349..ffd2006 100644 --- a/src/imaginary/test/test_actions.py +++ b/src/imaginary/test/test_actions.py @@ -327,6 +327,24 @@ def testTakeImmoveable(self): ["Test Player regards an immoveable thoughtfully."], ) + def testTakeNonContainingImmoveable(self): + """ + A L{Thing} with C{portable} set to C{False} cannot be taken even if the + taker is not contained by it. it. + """ + immoveable = objects.Thing( + store=self.store, + name=u"immoveable", + portable=False, + ) + objects.Container.createFor(immoveable) + objects.Exit.link(self.location, immoveable, u'north') + + self._test( + "take immoveable", + ["An immoveable cannot be taken."], + ["Test Player regards an immoveable thoughtfully."], + ) def testDrop(self): self._test( diff --git a/src/imaginary/test/test_objects.py b/src/imaginary/test/test_objects.py index 7628dad..9d412b0 100644 --- a/src/imaginary/test/test_objects.py +++ b/src/imaginary/test/test_objects.py @@ -101,11 +101,14 @@ def testNonPortable(self): Test that the C{portable} flag is respected and prevents movement between locations. """ - obj = objects.Thing(store=self.store, name=u"mountain") - obj.portable = False room = objects.Thing(store=self.store, name=u"place") objects.Container.createFor(room, capacity=1000) - obj.moveTo(room) + obj = objects.Thing( + store=self.store, + name=u"mountain", + portable=False, + location=room, + ) elsewhere = objects.Thing(store=self.store, name=u"different place") container = objects.Container.createFor(elsewhere, capacity=1000) self.assertRaises(eimaginary.CannotMove, obj.moveTo, elsewhere)