def test_attach(self): parent = Node() child = parent.attach(Node()) # check for the parent being set self.assertIs(child._node_parent, parent) # check for the child being in the relevant direct child sets for key in getmro(type(child)): self.assertIn(child, parent._node_children[key]) # check that _detach is called if the node already had a parent new_parent = Node() with patch("tgm.sys.node.Node._detach") as mock: new_parent.attach(child) mock.assert_called_once_with(child) self.assertIs(child._node_parent, new_parent) # check for _add_index_key being called for everything in the # child's index parent = Node() child = Node() with patch("tgm.sys.node.Node._add_index_key") as mock: parent.attach(child) for key, node_set in child._node_index.items(): if node_set: mock.assert_any_call(key, child) # the return value should be the child parent = Node() child = Node() self.assertIs(parent.attach(child), child)
def test_remove_index_key(self): class Key: pass world = Node() level = world.attach(Node()) player = level.attach(Node()) enemy = level.attach(Node()) player._add_index_key(Key, player) enemy._add_index_key(Key, enemy) world._add_index_key(Key, world) # check remove key with sibling having key # index removal should stop at the immediate parent player._remove_index_key(Key, player) self.assertEqual(enemy._node_index[Key], {enemy}) self.assertEqual(player._node_index[Key], set()) self.assertEqual(level._node_index[Key], {enemy}) self.assertEqual(world._node_index[Key], {level, world}) # check removing key propagation # world has the key directly, so it should remain enemy._remove_index_key(Key, enemy) self.assertEqual(enemy._node_index[Key], set()) self.assertEqual(player._node_index[Key], set()) self.assertEqual(level._node_index[Key], set()) self.assertEqual(world._node_index[Key], {world}) # ensure that removing the key from world works too world._remove_index_key(Key, world) self.assertEqual(world._node_index[Key], set())
def test_parent(self): # get direct parent parent = Node() child = parent.attach(Node()) self.assertIs(child.parent(), parent) self.assertIs(child.parent(Node), parent) # get specific parent full query class Level(Node): pass game = Node() level = game.attach(Level()) layer = level.attach(Node()) player = layer.attach(Node()) with patch("tgm.sys.query.Query.test", lambda _, obj: isinstance(obj, Level)): self.assertIs(player.parent(Query()), level) # get specific parent key only self.assertIs(player.parent(Level), level) # check no valid parent with patch("tgm.sys.query.Query.test", lambda _, obj: False): with self.assertRaises(ValueError): player.parent(Query())
def test_child_deletion(self): """Test that the children of objects are deleted when they are destroyed. """ parent = Node(None) child = ref(Node(parent)) parent.destroy() gc.collect() self.assertIsNone(child())
def test_parent_changed(self): """Test if objects have the correct parent set after having it changed. """ old_parent = Node(None) new_parent = Node(None) child = Node(old_parent) child.set_parent(new_parent) self.assertIs(child.parent(), new_parent)
def test_optimal_key(self): world = Node() for i in range(10): node = DummyNodeA() if (i % 2) == 0: node.attach(DummyNodeB()) world.attach(node) query = Query(DummyNodeA).child_matches(Query(DummyNodeB)) self.assertEqual(DummyNodeB, query._optimal_key(world))
def test_find(self): node = Node() child = node.attach(Node()) def trim(current_node): return False with patch("tgm.sys.query.Query.find_in") as mock: # check call with query list(node.find(Query())) mock.assert_called_once_with(node) with patch("tgm.sys.node._find_fast") as mock: # check call with key list(node.find(Node)) mock.assert_called_once_with(child, Node) # check trimming full query with patch("tgm.sys.query.Query.trim") as mock: node.find(Query(), trim) mock.assert_called_once_with(trim) # check trimming key only with patch("tgm.sys.node._find_fast_trim") as mock: list(node.find(Node, trim)) mock.assert_called_once_with(child, Node, trim)
def test_children(self): node = Node() # check finding a child child = node.attach(Node()) self.assertEqual(list(node.children(Node)), [child]) # check full query call with patch("tgm.sys.query.Query.find_on") as mock: list(node.children(Query())) mock.assert_called_once_with(node)
def test_init(self): # check that all the base classes have been added as keys with patch("tgm.sys.node.Node._add_index_key") as mock: node = Node() for key in getmro(type(node)): mock.assert_any_call(key, node) # check that everything in _get_instantiation_calls calls get called call_mocks = [Mock(), Mock()] with patch("tgm.sys.node._get_instantiation_calls", lambda _: call_mocks): node = Node() for mock in call_mocks: mock.assert_called_once_with(node)
def test_find_on(self): world = Node() for i in range(10): world.attach(DummyNodeA()) # test basic find by key results = list(Query(Node).find_on(world)) self.assertEqual(10, len(results)) # ensure non-nested selection for i, node in enumerate(results): node.attach(DummyNodeB()) node.angry = (i % 2) == 0 results = list(Query(Node).find_on(world)) self.assertEqual(10, len(results))
def test_destroy(self): # check that all every child has destroy called node = Node() children_mocks = [Mock(), Mock()] with patch("tgm.sys.node.Node.children", lambda _, _2: children_mocks): node.destroy() for mock in children_mocks: mock.destroy.assert_called_once_with() # check that the parent's _detach has been called parent = Node() child = parent.attach(Node()) with patch("tgm.sys.node.Node._detach") as mock: child.destroy() mock.assert_called_once_with(child)
def setUp(self): # every enemy has a collider (and only enemies) # world has an enemy directly as well as two layers # layer 1 has an enemy and a player # layer 2 has two enemies self.world = Node() self.enemy_world = self.world.attach(self.Enemy()) self.enemy_world.attach(self.Collider()) self.layer1 = self.world.attach(Node()) self.layer1.attach(self.Player()) self.enemy_layer1 = self.layer1.attach(self.Enemy()) self.enemy_layer1.attach(self.Collider()) self.layer2 = self.world.attach(Node()) self.enemy1_layer2 = self.layer2.attach(self.Enemy()) self.enemy1_layer2.attach(self.Collider()) self.enemy2_layer2 = self.layer2.attach(self.Enemy()) self.enemy2_layer2.attach(self.Collider()) self.enemies = { self.enemy_world, self.enemy_layer1, self.enemy1_layer2, self.enemy2_layer2 }
def test_combine(self): # combining a real query with a dummy query query = Query() self.assertIs(query.combine(DummyQuery()), query) self.assertIs(DummyQuery().combine(query), query) # picks optimal key self.assertIs(Query(Node).combine(Query(DummyNodeA))._key, DummyNodeA) # combines conditions true_query = Query(condition=lambda _: True) false_query = Query(condition=lambda _: False) query1 = true_query.combine(false_query) query2 = false_query.combine(true_query) self.assertFalse(query1._condition(Node())) self.assertFalse(query2._condition(Node())) # combines trim conditions query1 = (Query(trim=lambda _: True) .combine(Query(trim=lambda _: False))) query2 = (Query(trim=lambda _: False) .combine(Query(trim=lambda _: False))) self.assertTrue(query1._trim(Node())) self.assertFalse(query2._trim(Node())) # combines sibling query by adding a condition query = Query(DummyNodeA).combine(Query(DummyNodeB)) # one sibling is not being checked by the query self.assertTrue( query._condition(DummyNodeA()) ^ query._condition(DummyNodeB()) ) # DummyNodeAB is both a DummyNodeA and a DummyNodeB self.assertTrue(query._condition(DummyNodeAB())) # ensure child and parent queries are combined def keyed_mock(): return Mock(_key=object) query1 = Query(child_query=keyed_mock(), parent_query=keyed_mock()) query2 = Query(child_query=keyed_mock(), parent_query=keyed_mock()) query1.combine(query2) query1._parent_query.combine.assert_called_once_with( query2._parent_query ) query1._child_query.combine.assert_called_once_with( query2._child_query )
def test_find_in(self): world = Node() for i in range(10): world.attach(DummyNodeA()) # test basic find by key results = list(Query(Node).find_in(world)) self.assertEqual(10, len(results)) # test nested selection for i, node in enumerate(results): node.attach(DummyNodeB()) node.angry = (i % 2) == 0 results = list(Query(Node).find_in(world)) self.assertEqual(20, len(results)) # test trim query = Query(Node).trim( lambda node: hasattr(node, "angry") and node.angry ) results = list(query.find_in(world)) self.assertEqual(10, len(results))
def test_add_index_key(self): class Key: pass world = Node() level = world.attach(Node()) player = level.attach(Node()) enemy = level.attach(Node()) # check single child with key player._add_index_key(Key, player) self.assertEqual(enemy._node_index[Key], set()) self.assertEqual(player._node_index[Key], {player}) self.assertEqual(level._node_index[Key], {player}) self.assertEqual(world._node_index[Key], {level}) # check two children with key enemy._add_index_key(Key, enemy) self.assertEqual(enemy._node_index[Key], {enemy}) self.assertEqual(player._node_index[Key], {player}) self.assertEqual(level._node_index[Key], {player, enemy}) self.assertEqual(world._node_index[Key], {level})
def test_children_with(self): node = Node() # check finding a child child = node.attach(Node()) child.attach(Node()) self.assertEqual(list(node.children_with(Node)), [child]) # check full query call with patch("tgm.sys.query.Query.find_on") as mock: list(node.children(Query())) mock.assert_called_once_with(node)
def test_detach(self): parent = Node() child = parent.attach(Node()) parent._detach(child) # check for the child being removed from the relevant direct child sets for key in getmro(type(child)): self.assertNotIn(child, parent._node_children[key]) # check for _remove_index_key being called for everything in the # child's index parent = Node() child = parent.attach(Node()) with patch("tgm.sys.node.Node._remove_index_key") as mock: parent._detach(child) for key, node_set in child._node_index.items(): if node_set: mock.assert_any_call(key, child)
def test_find_with(self): node = Node() node.attach(Node()) def trim(current_node): return False # check call with query with patch("tgm.sys.query.Query.find_in") as mock: list(node.find_with(Query())) mock.assert_called_once_with(node) # check call with key with patch("tgm.sys.node._find_with_fast") as mock: list(node.find_with(Node)) mock.assert_called_once_with(node, Node) # check call with key with patch("tgm.sys.node._find_with_fast_trim") as mock: list(node.find_with(Node, trim)) mock.assert_called_once_with(node, Node, trim)
def test_test(self): world = Node() for i in range(10): world.attach(DummyNodeA()) b = DummyNodeB() b.furious = True world.attach(b) # test for key self.assertEqual(10, len(list(Query(DummyNodeA).find_on(world)))) # test for condition query = Query(Node).filter( lambda node: not isinstance(node, DummyNodeB) ) self.assertEqual(10, len(list(query.find_on(world)))) # test for trim query = Query(Node).filter(lambda node: not node.furious)
def test_parent(self): """Test if objects have the correct parent set upon instantiation.""" parent = Node(None) child = Node(parent) self.assertIs(child.parent(), parent)
def test_get_with(self): # check that the child is returned parent = Node() child = parent.attach(Node()) child.attach(Node()) self.assertIs(parent.get_with(Node), child)
class TestFindFast(TestCase): class Enemy(Node): pass class Player(Node): pass class Collider(Node): pass def setUp(self): # every enemy has a collider (and only enemies) # world has an enemy directly as well as two layers # layer 1 has an enemy and a player # layer 2 has two enemies self.world = Node() self.enemy_world = self.world.attach(self.Enemy()) self.enemy_world.attach(self.Collider()) self.layer1 = self.world.attach(Node()) self.layer1.attach(self.Player()) self.enemy_layer1 = self.layer1.attach(self.Enemy()) self.enemy_layer1.attach(self.Collider()) self.layer2 = self.world.attach(Node()) self.enemy1_layer2 = self.layer2.attach(self.Enemy()) self.enemy1_layer2.attach(self.Collider()) self.enemy2_layer2 = self.layer2.attach(self.Enemy()) self.enemy2_layer2.attach(self.Collider()) self.enemies = { self.enemy_world, self.enemy_layer1, self.enemy1_layer2, self.enemy2_layer2 } def test_find_fast(self): # find disperse objects self.assertEqual(set(_find_fast(self.world, self.Enemy)), self.enemies) # find root object self.assertIn(self.world, set(_find_fast(self.world, Node))) def test_find_fast_trim(self): def no_trim(node): return False def layer2_trim(node): return node is self.layer2 # find disperse objects self.assertEqual( set(_find_fast_trim(self.world, self.Enemy, no_trim)), self.enemies ) # find root object self.assertIn( self.world, set(_find_fast_trim(self.world, Node, no_trim)) ) # find disperse objects with trim self.assertEqual( set(_find_fast_trim(self.world, self.Enemy, layer2_trim)), self.enemies - {self.enemy1_layer2, self.enemy2_layer2} ) def test_find_with_fast(self): # find disperse objects self.assertEqual( set(_find_with_fast(self.world, self.Collider)), self.enemies ) # do not find root object self.assertNotIn(self.world, set(_find_with_fast(self.world, Node))) def test_find_with_fast_trim(self): def no_trim(node): return False def layer2_trim(node): return node is self.layer2 # find disperse objects self.assertEqual( set(_find_with_fast_trim(self.world, self.Collider, no_trim)), self.enemies ) # do not find root object self.assertNotIn( self.world, set(_find_with_fast_trim(self.world, Node, no_trim)) ) # find disperse objects with trim self.assertEqual( set(_find_with_fast_trim(self.world, self.Collider, layer2_trim)), self.enemies - {self.enemy1_layer2, self.enemy2_layer2} )
class TestFindFast(TestCase): class Enemy(Node): pass class Player(Node): pass class Collider(Node): pass def setUp(self): # every enemy has a collider (and only enemies) # world has an enemy directly as well as two layers # layer 1 has an enemy and a player # layer 2 has two enemies self.world = Node() self.enemy_world = self.world.attach(self.Enemy()) self.enemy_world.attach(self.Collider()) self.layer1 = self.world.attach(Node()) self.layer1.attach(self.Player()) self.enemy_layer1 = self.layer1.attach(self.Enemy()) self.enemy_layer1.attach(self.Collider()) self.layer2 = self.world.attach(Node()) self.enemy1_layer2 = self.layer2.attach(self.Enemy()) self.enemy1_layer2.attach(self.Collider()) self.enemy2_layer2 = self.layer2.attach(self.Enemy()) self.enemy2_layer2.attach(self.Collider()) self.enemies = { self.enemy_world, self.enemy_layer1, self.enemy1_layer2, self.enemy2_layer2 } def test_find_fast(self): # find disperse objects self.assertEqual(set(_find_fast(self.world, self.Enemy)), self.enemies) # find root object self.assertIn(self.world, set(_find_fast(self.world, Node))) def test_find_fast_trim(self): def no_trim(node): return False def layer2_trim(node): return node is self.layer2 # find disperse objects self.assertEqual(set(_find_fast_trim(self.world, self.Enemy, no_trim)), self.enemies) # find root object self.assertIn(self.world, set(_find_fast_trim(self.world, Node, no_trim))) # find disperse objects with trim self.assertEqual( set(_find_fast_trim(self.world, self.Enemy, layer2_trim)), self.enemies - {self.enemy1_layer2, self.enemy2_layer2}) def test_find_with_fast(self): # find disperse objects self.assertEqual(set(_find_with_fast(self.world, self.Collider)), self.enemies) # do not find root object self.assertNotIn(self.world, set(_find_with_fast(self.world, Node))) def test_find_with_fast_trim(self): def no_trim(node): return False def layer2_trim(node): return node is self.layer2 # find disperse objects self.assertEqual( set(_find_with_fast_trim(self.world, self.Collider, no_trim)), self.enemies) # do not find root object self.assertNotIn(self.world, set(_find_with_fast_trim(self.world, Node, no_trim))) # find disperse objects with trim self.assertEqual( set(_find_with_fast_trim(self.world, self.Collider, layer2_trim)), self.enemies - {self.enemy1_layer2, self.enemy2_layer2})
def test_parent_has_child(self): """Test that objects are in their parent's children set.""" parent = Node(None) child = Node(parent) self.assertIn(child, parent.children(Node))
def test_test(self): self.assertTrue(DummyQuery().test(Node()))
def test_matches(self): node = Node() with patch("tgm.sys.query.Query.test") as mock: node.matches(Query()) mock.assert_called_once_with(node)
def test_no_parent(self): """Test that game objects with no parent return None for their parent. """ obj = Node(None) self.assertIs(obj.parent(), None)
def test_query_slice(self): query = _query_slice(slice("hates_life", True)) node = Node() node.hates_life = True self.assertTrue(query.test(node))
def test_optimal_key(self): self.assertIs(DummyQuery()._optimal_key(Node()), object)