Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GameObjectCollection.add: Behave correctly with inheritance #241

Merged
merged 6 commits into from
Apr 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions ppb/scenes.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ def add(self, game_object: Hashable, tags: Iterable[Hashable]=()) -> None:
if isinstance(tags, (str, bytes)):
raise TypeError("You passed a string instead of an iterable, this probably isn't what you intended.\n\nTry making it a tuple.")
self.all.add(game_object)
self.kinds[type(game_object)].add(game_object)

for kind in type(game_object).mro():
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a tricky thing: mro here is going to include BaseSprite, Sprite (the ABC), and object. Are you intending for that to happen or would you rather explicitly exclude those?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't think of any reasons to exclude them (except maybe object?), and there are no documented exceptions about what are valid kinds.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The question is: what's the difference between self.all and self.kinds[object] under this new functionality?

self.kinds[kind].add(game_object)
for tag in tags:
self.tags[tag].add(game_object)

Expand Down Expand Up @@ -91,7 +93,8 @@ def remove(self, game_object: Hashable) -> None:
container.remove(myObject)
"""
self.all.remove(game_object)
self.kinds[type(game_object)].remove(game_object)
for kind in type(game_object).mro():
self.kinds[kind].remove(game_object)
for s in self.tags.values():
s.discard(game_object)

Expand Down Expand Up @@ -120,6 +123,14 @@ def __contains__(self, item: Hashable) -> bool:
def __iter__(self) -> Iterator:
return (x for x in self.game_objects)

@property
def kinds(self):
return self.game_objects.kinds

@property
def tags(self):
return self.game_objects.tags

@property
def main_camera(self) -> Camera:
return next(self.game_objects.get(tag="main_camera"))
Expand Down
63 changes: 30 additions & 33 deletions tests/test_scenes.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ class TestPlayer:
pass


class TestSubclassPlayer(TestPlayer):
pass


class TestSprite:
pass

Expand All @@ -26,9 +30,15 @@ def containers():
yield BaseScene(Mock())


@fixture()
def player():
return TestPlayer()
def players():
yield TestPlayer()
yield TestSubclassPlayer()


def players_and_containers():
pathunstrom marked this conversation as resolved.
Show resolved Hide resolved
for player in players():
for container in containers():
yield player, container


@fixture()
Expand All @@ -42,7 +52,7 @@ def scene():
return BaseScene(engine)


@mark.parametrize("container", containers())
@mark.parametrize("player, container", players_and_containers())
def test_add_methods(container, player, enemies):
container.add(player)
for group, sprite in zip(("red", "blue"), enemies):
Expand All @@ -52,7 +62,7 @@ def test_add_methods(container, player, enemies):
assert enemy in container


@mark.parametrize("container", containers())
@mark.parametrize("player, container", players_and_containers())
def test_get_methods(container, player, enemies):

sprite = TestSprite()
Expand All @@ -61,43 +71,26 @@ def test_get_methods(container, player, enemies):
container.add(enemies[1], ["red"])
container.add(sprite)

enemy_set = set(container.get(kind=TestEnemy))
assert len(enemy_set) == 2
for enemy in enemies:
assert enemy in enemy_set

player_set = set(container.get(kind=TestPlayer))
assert len(player_set) == 1
assert player in player_set
assert set(container.get(kind=TestEnemy)) == set(enemies)
assert set(container.get(kind=TestPlayer)) == {player}
assert set(container.get(kind=TestSprite)) == {sprite}

sprite_set = set(container.get(kind=TestSprite))
assert len(sprite_set) == 1
assert sprite in sprite_set
assert set(container.get(tag="red")) == {player, enemies[1]}

red_set = set(container.get(tag="red"))
assert len(red_set) == 2
assert player in red_set
assert enemies[1] in red_set
assert enemies[0] not in red_set

null_set = set(container.get(tag="this doesn't exist"))
assert len(null_set) == 0
assert player not in null_set
assert enemies[0] not in null_set
assert enemies[1] not in null_set
assert set(container.get(tag="this doesn't exist")) == set()

with raises(TypeError):
container.get()


@mark.parametrize("container", containers())
@mark.parametrize("player, container", players_and_containers())
def test_get_with_string_tags(container, player):
"""Test that addings a string instead of an array-like throws."""
with raises(TypeError):
container.add(player, "player")


@mark.parametrize("container", containers())
@mark.parametrize("player, container", players_and_containers())
def test_remove_methods(container, player, enemies):
container.add(player, ["test"])
container.add(enemies[0], ["test"])
Expand All @@ -109,15 +102,19 @@ def test_remove_methods(container, player, enemies):
container.remove(player)

assert player not in container
assert player not in container.get(kind=TestPlayer)
assert player not in container.get(tag="test")
for kind in container.kinds:
assert player not in container.get(kind=kind)
for tag in container.tags:
assert player not in container.get(tag=tag)

assert enemies[0] in container
assert enemies[0] in container.get(tag="test")
assert enemies[1] in container


@mark.parametrize("container", [GameObjectCollection()])
def test_collection_methods(container, player, enemies):
@mark.parametrize("player", players())
def test_collection_methods(player, enemies):
container = GameObjectCollection()
container.add(player)
container.add(enemies[0])

Expand Down