class KnowledgeUpdateBehaviour(BehaviourBase): ''' Behaviour that updates knowledge after it is activated ''' def __init__(self, name, pattern, new_tuple, knowledge_base_name=KnowledgeBase.DEFAULT_NAME, **kwargs): """ :param name: behaviour name :param pattern: pattern match that will be updated :param new_tuple: new tuple value that will replaced the matched tuples :param knowledge_base_name: name of the knowledge base that is used :param kwargs: further general BehaviourBase arguments """ super(KnowledgeUpdateBehaviour, self) \ .__init__(name=name, **kwargs) self.pattern = pattern self.new_tuple = new_tuple self._kb_client = KnowledgeBaseClient( knowledge_base_name=knowledge_base_name) def start(self): rhbplog.logdebug("Updating knowledge '%s' to '%s'", self.pattern, self.new_tuple) self._kb_client.update(self.pattern, self.new_tuple)
class TestKnowledgeBaseSensor(unittest.TestCase): def __init__(self, *args, **kwargs): super(TestKnowledgeBaseSensor, self).__init__(*args, **kwargs) self.__knowledge_base_address = KnowledgeBase.DEFAULT_NAME + "KnowledgeBaseSensorTestSuite" # prevent influence of previous tests self.__message_prefix = 'TestKnowledgeBaseSensor' + str(time.time()) self.start_kb_node() rospy.init_node('knowledge_sensor_test_node', log_level=rospy.DEBUG) self.__client = KnowledgeBaseClient( knowledge_base_name=self.__knowledge_base_address) def start_kb_node(self): """ start the knowledge base node """ package = 'knowledge_base' executable = 'knowledge_base_node.py' node = roslaunch.core.Node(package=package, node_type=executable, name=self.__knowledge_base_address, output='screen') launch = roslaunch.scriptapi.ROSLaunch() launch.start() self._kb_process = launch.launch(node) rospy.sleep(1) def test_basic(self): """ Tests sensor output, if the fact is added at runtime (and did not exist before) """ sensor = KnowledgeSensor( pattern=(self.__message_prefix, 'test_basic', 'pos', '*', '*'), knowledge_base_name=self.__knowledge_base_address) rospy.sleep(0.1) sensor.sync() self.assertFalse(sensor.value) update_stamp = sensor._value_cache.update_time self.__client.push( (self.__message_prefix, 'test_basic', 'pos', '42', '0')) rospy.sleep(0.1) while update_stamp == sensor._value_cache.update_time: rospy.sleep(0.1) sensor.sync() self.assertTrue(sensor.value) def test_remove(self): """ Tests sensor output , if the fact is removed """ test_tuple = (self.__message_prefix, 'test_remove', 'pos', '42', '0') sensor = KnowledgeSensor( pattern=(self.__message_prefix, 'test_remove', 'pos', '*', '*'), knowledge_base_name=self.__knowledge_base_address) rospy.sleep(0.1) update_stamp = sensor._value_cache.update_time self.__client.push(test_tuple) rospy.sleep(0.1) while update_stamp == sensor._value_cache.update_time: rospy.sleep(0.1) sensor.sync() self.assertTrue(sensor.value) update_stamp = sensor._value_cache.update_time removed_facts = self.__client.pop(test_tuple) self.assertIsNotNone(removed_facts, "No fact removed") while update_stamp == sensor._value_cache.update_time: rospy.sleep(0.1) sensor.sync() self.assertFalse(sensor.value) def test_knowledge_fact_int_sensor(self): """ Test KnowledgeFactIntSensor """ initial_value = 1337 sensor_pattern = (self.__message_prefix, 'test_knowledge_fact_int_sensor', 'number', '*') sensor = KnowledgeFactNumberSensor( pattern=sensor_pattern, initial_value=initial_value, knowledge_base_name=self.__knowledge_base_address) rospy.sleep(0.1) sensor.sync() self.assertEquals(sensor.value, initial_value) test_value = 42 update_stamp = sensor._value_cache.update_time # regular operation self.__client.push( (self.__message_prefix, 'test_knowledge_fact_int_sensor', 'number', str(test_value))) rospy.sleep(0.1) while update_stamp == sensor._value_cache.update_time: rospy.sleep(0.1) sensor.sync() self.assertEquals(sensor.value, test_value) update_stamp = sensor._value_cache.update_time # illegal operation with non integer value new_tuple = (self.__message_prefix, 'test_knowledge_fact_int_sensor', 'number', "NO_NUMBER") self.assertEquals( self.__client.update(pattern=sensor_pattern, new=new_tuple), True) rospy.sleep(0.1) while update_stamp > sensor.value_update_time: rospy.sleep(0.5) sensor.sync() rospy.loginfo(sensor.value) self.assertEquals(sensor.value, initial_value) def test_knowledge_fact_count_sensor(self): """ Test KnowledgeFactCountSensor """ initial_value = 0 sensor_pattern = (self.__message_prefix, 'test_knowledge_fact_count_sensor', 'test', '*') sensor = KnowledgeFactCountSensor( pattern=sensor_pattern, knowledge_base_name=self.__knowledge_base_address, initial_value=initial_value) rospy.sleep(0.1) sensor.sync() self.assertEquals(sensor.value, initial_value) update_stamp = sensor._value_cache.update_time # regular operation self.__client.push((self.__message_prefix, 'test_knowledge_fact_count_sensor', 'test', 'a')) rospy.sleep(0.1) while update_stamp == sensor._value_cache.update_time: rospy.sleep(0.1) sensor.sync() self.assertEquals(sensor.value, 1) update_stamp = sensor._value_cache.update_time # regular operation self.__client.push((self.__message_prefix, 'test_knowledge_fact_count_sensor', 'test', 'b')) rospy.sleep(0.1) while update_stamp == sensor._value_cache.update_time: rospy.sleep(0.1) sensor.sync() self.assertEquals(sensor.value, 2) update_stamp = sensor._value_cache.update_time # regular operation self.__client.pop((self.__message_prefix, 'test_knowledge_fact_count_sensor', 'test', 'b')) rospy.sleep(0.1) while update_stamp == sensor._value_cache.update_time: rospy.sleep(0.1) sensor.sync() self.assertEquals(sensor.value, 1)
class TupleSpaceTestSuite(unittest.TestCase): def __init__(self, *args, **kwargs): super(TupleSpaceTestSuite, self).__init__(*args, **kwargs) def setUp(self): # prevent influence of previous tests self.__message_prefix = 'TupleSpaceTestSuite' + str(time.time()) self.__knowledge_base_address = KnowledgeBase.DEFAULT_NAME #start KnowledgeBase package = 'knowledge_base' executable = 'knowledge_base_node.py' node = roslaunch.core.Node(package=package, node_type=executable, name=self.__knowledge_base_address) launch = roslaunch.scriptapi.ROSLaunch() launch.start() self._kb_process = launch.launch(node) rospy.init_node('TupleSpaceTestSuite', log_level=rospy.DEBUG) self.__client = KnowledgeBaseClient(self.__knowledge_base_address) def tearDown(self): self._kb_process.stop() def __wait_for_tuple(self, wait_for_it): """ waits, until the requested tuple is contained in knowledge_base :param wait_for_it: tuple """ while not self.__client.exists(wait_for_it): rospy.sleep(0.1) def __add_multiple_facts(self, *facts): for t in facts: self.__client.push(t) self.__wait_for_tuple(facts[len(facts) - 1]) def __test_list_equality(self, l1, l2): error_message = 'Excepted "{0}", but is {1}'.format(str(l1), str(l2)) self.assertEqual(len(l1), len(l2)) for i1 in l1: self.assertTrue(i1 in l2, error_message) for i2 in l2: self.assertTrue(i2 in l1, error_message) def test_exists_for_non_existing(self): test_tuple = (self.__message_prefix, 'test_exists_for_non_existing', '0', '0') self.assertFalse(self.__client.exists(test_tuple)) def test_simple_adding(self): test_tuple = (self.__message_prefix, 'test_simple_adding', '0', '0') self.__client.push(test_tuple) self.assertTrue(self.__client.exists(test_tuple)) def test_peek(self): test_tuple = (self.__message_prefix, 'test_peek', '0', '0') self.__client.push(test_tuple) self.assertEqual(test_tuple, self.__client.peek(test_tuple)) def test_pop(self): test_tuple = (self.__message_prefix, 'test_pop', '0', '0') self.__client.push(test_tuple) self.assertTrue(self.__client.exists(test_tuple)) removed = self.__client.pop(test_tuple) self.assertEqual(1, len(removed)) self.assertEqual(test_tuple, removed[0]) def test_remove_of_non_existing(self): tuples = [] tuples.append( (self.__message_prefix, 'test_remove_of_non_existing', '0', '1')) tuples.append( (self.__message_prefix, 'test_remove_of_non_existing', '2', '5')) self.__add_multiple_facts(*tuples) self.__check_content( (self.__message_prefix, 'test_remove_of_non_existing', '*', '*'), *tuples) removed = self.__client.pop( (self.__message_prefix, 'test_remove_of_non_existing', '4', '*')) self.assertEqual(0, len(removed)) self.__check_content( (self.__message_prefix, 'test_remove_of_non_existing', '*', '*'), *tuples) def test_multiple_pop(self): uninteresting_content = [] uninteresting_content.append( (self.__message_prefix, 'test_multiple_pop', 'boring', '1')) uninteresting_content.append( (self.__message_prefix, 'test_multiple_pop', 'boring', '5')) to_remove = [] to_remove.append( (self.__message_prefix, 'test_multiple_pop', 'interesting', '1')) to_remove.append( (self.__message_prefix, 'test_multiple_pop', 'interesting', '5')) all = [] all.extend(uninteresting_content) all.extend(to_remove) self.__add_multiple_facts(*all) self.__check_content( (self.__message_prefix, 'test_multiple_pop', '*', '*'), *tuple(all)) removed = self.__client.pop( (self.__message_prefix, 'test_multiple_pop', 'interesting', '*')) self.__test_list_equality(removed, to_remove) self.__check_content( (self.__message_prefix, 'test_multiple_pop', '*', '*'), *tuple(uninteresting_content)) def test_placeholder(self): test_tuple = (self.__message_prefix, 'test_placeholder', '0', '0') pattern = (self.__message_prefix, 'test_placeholder', '*', '*') self.__client.push(test_tuple) self.assertEqual(test_tuple, self.__client.peek(pattern)) def test_all(self): """ test service for find all matching facts """ tuples = [] tuples.append((self.__message_prefix, 'test_all', 'pos', '0', '0')) tuples.append((self.__message_prefix, 'test_all', 'pos', '1', '0')) tuples.append((self.__message_prefix, 'test_all', 'pos', '1', '-4')) self.__add_multiple_facts(*tuples) self.__check_content( (self.__message_prefix, 'test_all', 'pos', '*', '*'), *tuple(tuples)) def __check_content(self, pattern, *expected): """ ensures, that exact the expected content is contained in the knowledge base :param pattern: filter for deciding, which contained facts of the kb should used :param expected: expected content of the kb """ knowledge_base_content = self.__client.all(pattern) self.assertEqual( len(expected), len(knowledge_base_content), ' Expected: ' + str(expected) + ' but is ' + str(knowledge_base_content)) for fact in expected: self.assertTrue( fact in knowledge_base_content, ' Expected: ' + str(expected) + ' but is ' + str(knowledge_base_content)) def test_prevent_multiple_adding(self): """ tests that no duplicates are contained in knowledge base """ test_tuple = (self.__message_prefix, 'test_prevent_multiple_adding', '0', '0') self.__client.push(test_tuple) self.__client.push(test_tuple) service_name = self.__knowledge_base_address + KnowledgeBase.ALL_SERVICE_NAME_POSTFIX rospy.wait_for_service(service_name) all_service = rospy.ServiceProxy(service_name, All) all_response = all_service(test_tuple) self.assertEqual(1, len(all_response.found)) def __check_mocks_empty(self, update_mocks): """ Asserts, that all given mocks has no remaining messages. All expected messages must be removed before method call :param update_mocks (list of UpdateSubscriberMock) """ for update_mock in update_mocks: self.assertTrue( len(update_mock.add_msg) == 0, 'Following mock has an unexpected add message received: ' + update_mock.name) self.assertTrue( len(update_mock.remove_msg) == 0, 'Following mock has an unexpected remove message received: ' + update_mock.name) self.assertTrue( len(update_mock.update_msg) == 0, 'Following mock has an unexpected update message received: ' + update_mock.name) def test_update_non_existing(self): """ Tries to update a fact, which is not in the kb """ prefix = self.__message_prefix + '_test_update_non_existing' new_fact = (prefix, 'new', '1') not_influenced = (prefix, 'not_influenced', '1') self.__client.push(not_influenced) informer_added = UpdateSubscriberMock(mock_name='added', client=self.__client, pattern=(prefix, 'new', '*')) informer_removed = UpdateSubscriberMock(mock_name='removed', client=self.__client, pattern=(prefix, 'unsuccess', '*')) informer_updated = UpdateSubscriberMock(mock_name='updated', client=self.__client, pattern=(prefix, '*', '*')) informer_not_influenced = UpdateSubscriberMock( mock_name='not_influended', client=self.__client, pattern=(prefix, 'not_influenced', '*')) all_informers = [ informer_added, informer_removed, informer_updated, informer_not_influenced ] self.assertFalse( self.__client.update((self.__message_prefix, 'unsuccess', '1'), new_fact, push_without_existing=False), 'Old fact does not exist, but update was successfull') rospy.sleep(0.1) self.__check_mocks_empty(all_informers) self.__check_content((prefix, '*', '*'), not_influenced) self.assertTrue( self.__client.update((self.__message_prefix, 'not_existing', '1'), new_fact, push_without_existing=True), 'Old fact does not exists and update was not successfull') rospy.sleep(0.1) self.assertEqual( 1, len(informer_added.add_msg), 'Error at receiving add message: ' + str(len(informer_added.add_msg))) self.assertEqual(new_fact, tuple(informer_added.add_msg[0].content)) informer_added.add_msg = [] self.assertEqual( 1, len(informer_updated.add_msg), 'Error at receiving add message: ' + str(len(informer_updated.add_msg))) self.assertEqual(new_fact, tuple(informer_updated.add_msg[0].content)) informer_updated.add_msg = [] self.__check_mocks_empty(all_informers) self.__check_content((prefix, '*', '*'), not_influenced, new_fact) def test_update_basic(self): """ Tests simple update of an existing fact """ prefix = self.__message_prefix + '_test_update_basic' old_fact = (prefix, 'old', '2') new_fact = (prefix, 'new', '1') not_influenced = (prefix, 'not_influenced', '1') self.__client.push(old_fact) self.__client.push(not_influenced) self.__check_content((prefix, '*', '*'), old_fact, not_influenced) informer_added = UpdateSubscriberMock(mock_name='added', client=self.__client, pattern=(prefix, 'new', '*')) informer_removed = UpdateSubscriberMock(mock_name='removed', client=self.__client, pattern=(prefix, 'old', '*')) informer_updated = UpdateSubscriberMock(mock_name='updated', client=self.__client, pattern=(prefix, '*', '*')) informer_second_target = UpdateSubscriberMock( mock_name='seccond', client=self.__client, pattern=(prefix, 'seccond_target', '*')) informer_not_influenced = UpdateSubscriberMock( mock_name='not_influended', client=self.__client, pattern=(prefix, 'not_influenced', '*')) all_informers = [ informer_added, informer_removed, informer_updated, informer_second_target, informer_not_influenced ] self.assertTrue(self.__client.update(old_fact, new_fact), 'First update failed') rospy.sleep(0.1) self.assertEqual( 1, len(informer_added.add_msg), 'Error at receiving add message: ' + str(len(informer_added.add_msg))) self.assertEqual(new_fact, tuple(informer_added.add_msg[0].content)) informer_added.add_msg = [] self.assertEqual( 1, len(informer_removed.remove_msg), 'Error at receiving remove message: ' + str(len(informer_removed.remove_msg))) self.assertEqual(old_fact, tuple(informer_removed.remove_msg[0].fact)) informer_removed.remove_msg = [] self.assertEqual( 1, len(informer_updated.update_msg), 'Error at receiving update message: ' + str(len(informer_updated.update_msg))) self.assertEqual(1, len(informer_updated.update_msg[0].removed)) self.assertEqual( old_fact, tuple(informer_updated.update_msg[0].removed[0].content)) self.assertEqual(new_fact, tuple(informer_updated.update_msg[0].new)) informer_updated.update_msg = [] self.__check_mocks_empty(all_informers) self.__check_content((prefix, '*', '*'), new_fact, not_influenced) def test_update_existing_target(self): """ Tests update of an existing fact to a fact, which is already contained in the kb """ prefix = self.__message_prefix + '_test_update_existing_target' old_fact = (prefix, 'old', '2') new_fact = (prefix, 'new', '1') not_influenced = (prefix, 'not_influenced', '1') self.__add_multiple_facts(old_fact, new_fact, not_influenced) self.__check_content((prefix, '*', '*'), old_fact, new_fact, not_influenced) informer_added = UpdateSubscriberMock(mock_name='added', client=self.__client, pattern=(prefix, 'new', '*')) informer_removed = UpdateSubscriberMock(mock_name='removed', client=self.__client, pattern=(prefix, 'old', '*')) informer_updated = UpdateSubscriberMock(mock_name='updated', client=self.__client, pattern=(prefix, '*', '*')) informer_second_target = UpdateSubscriberMock( mock_name='seccond', client=self.__client, pattern=(prefix, 'seccond_target', '*')) informer_not_influenced = UpdateSubscriberMock( mock_name='not_influended', client=self.__client, pattern=(prefix, 'not_influenced', '*')) all_informers = [ informer_added, informer_removed, informer_updated, informer_second_target, informer_not_influenced ] self.assertTrue(self.__client.update(old_fact, new_fact), 'update failed') rospy.sleep(0.1) self.assertEqual( 1, len(informer_removed.remove_msg), 'Error at receiving remove message: ' + str(len(informer_removed.remove_msg))) self.assertEqual(old_fact, tuple(informer_removed.remove_msg[0].fact)) informer_removed.remove_msg = [] self.assertEqual( 1, len(informer_updated.remove_msg), 'Error at receiving second remove message: ' + str(len(informer_removed.remove_msg))) self.assertEqual(old_fact, tuple(informer_updated.remove_msg[0].fact)) informer_updated.remove_msg = [] self.__check_mocks_empty(all_informers) self.__check_content((prefix, '*', '*'), new_fact, not_influenced) def test_replace_several_facts(self): prefix = self.__message_prefix + '_test_replace_several_facts' old_fact_1 = (prefix, 'old', '2') old_fact_2 = (prefix, 'old', '3') not_influenced = (prefix, 'not_influenced', '1') self.__add_multiple_facts(old_fact_1, old_fact_2, not_influenced) self.__check_content((prefix, '*', '*'), old_fact_1, old_fact_2, not_influenced) new_fact = (prefix, 'new', '1') informer_added = UpdateSubscriberMock(mock_name='added', client=self.__client, pattern=(prefix, 'new', '*')) informer_removed = UpdateSubscriberMock(mock_name='removed', client=self.__client, pattern=(prefix, 'old', '*')) informer_updated = UpdateSubscriberMock(mock_name='updated', client=self.__client, pattern=(prefix, '*', '*')) informer_not_influenced = UpdateSubscriberMock( mock_name='not_influended', client=self.__client, pattern=(prefix, 'not_influenced', '*')) all_informers = [ informer_added, informer_removed, informer_updated, informer_not_influenced ] self.assertTrue(self.__client.update((prefix, 'old', '*'), new_fact), 'update failed') rospy.sleep(0.1) self.__check_content((prefix, '*', '*'), new_fact, not_influenced) self.assertEqual( 1, len(informer_added.add_msg), 'Error at receiving add message: ' + str(len(informer_added.add_msg))) self.assertEqual(new_fact, tuple(informer_added.add_msg[0].content)) informer_added.add_msg = [] self.assertEqual( 2, len(informer_removed.remove_msg), 'Error at receiving remove messages: ' + str(len(informer_removed.remove_msg))) removed_facts = [old_fact_1, old_fact_2] for remove_msg in informer_removed.remove_msg: self.assertTrue( tuple(remove_msg.fact) in removed_facts, str(remove_msg.fact) + ' not in ' + str(removed_facts)) removed_facts.remove(tuple(remove_msg.fact)) informer_removed.remove_msg = [] self.assertEqual( 1, len(informer_updated.update_msg), 'Error at receiving update message: ' + str(len(informer_updated.update_msg))) self.assertEqual(tuple(informer_updated.update_msg[0].new), new_fact) removed_facts = [old_fact_1, old_fact_2] for removed_fact in informer_updated.update_msg[0].removed: self.assertTrue( tuple(removed_fact.content) in removed_facts, str(tuple(removed_fact.content)) + ' not in ' + str(removed_facts)) removed_facts.remove(tuple(removed_fact.content)) informer_updated.update_msg = [] self.__check_mocks_empty(all_informers)
class UpdateHandlerTestSuite(unittest.TestCase): def __init__(self, *args, **kwargs): super(UpdateHandlerTestSuite, self).__init__(*args, **kwargs) def setUp(self): self.__knowledge_base_address = KnowledgeBase.DEFAULT_NAME # prevent influence of previous tests self.__message_prefix = 'UpdateHandlerTestSuite' + str(time.time()) # start KnowledgeBase self.start_kb_node() rospy.init_node('UpdateHandlerTestSuite', log_level=rospy.DEBUG) self.__client = KnowledgeBaseClient(self.__knowledge_base_address) def start_kb_node(self): """ start the knowledge base node """ package = 'knowledge_base' executable = 'knowledge_base_node.py' node = roslaunch.core.Node(package=package, node_type=executable, name=self.__knowledge_base_address) launch = roslaunch.scriptapi.ROSLaunch() launch.start() self._kb_process = launch.launch(node) def tearDown(self): self._kb_process.stop() def test_simple_adding(self): """ Tests basic case of adding a tuple """ test_tuple = (self.__message_prefix, 'test_simple_adding', '0', '0') cache = KnowledgeBaseFactCache(pattern=test_tuple) self.assertFalse(cache.does_fact_exists(), 'Tuple is not added, but is indicated as present') self.__client.push(test_tuple) rospy.sleep(0.1) self.assertTrue(cache.does_fact_exists(), 'Tuple was added, but is not indicated as present') def test_remove(self): test_tuple = (self.__message_prefix, 'test_remove', '0', '0') cache = KnowledgeBaseFactCache(pattern=test_tuple) self.assertFalse(cache.does_fact_exists(), 'Tuple is not added, but is indicated as present') self.__client.push(test_tuple) rospy.sleep(0.1) self.assertTrue(cache.does_fact_exists(), 'Tuple was added, but is not indicated as present') self.__client.pop(test_tuple) rospy.sleep(0.1) self.assertFalse( cache.does_fact_exists(), 'Tuple was removed, but is still indicated as present') def test_multiple_caches_for_same_pattern(self): test_tuple = (self.__message_prefix, 'test_multiple_caches_for_same_pattern', '0', '0') cache1 = KnowledgeBaseFactCache(pattern=test_tuple) cache2 = KnowledgeBaseFactCache(pattern=test_tuple) self.assertFalse(cache1.does_fact_exists(), 'Tuple is not added, but is indicated as present') self.assertFalse(cache2.does_fact_exists(), 'Tuple is not added, but is indicated as present') self.__client.push(test_tuple) rospy.sleep(0.1) self.assertTrue(cache1.does_fact_exists(), 'Tuple was added, but is not indicated as present') self.assertTrue(cache2.does_fact_exists(), 'Tuple was added, but is not indicated as present') def test_middle_placeholder(self): """ Test updating an existing fact """ prefix = self.__message_prefix + '_test_middle_placeholder' first_fact = (prefix, 'updated', 'a', '1') second_fact = (prefix, 'updated', 'b', '1') self.__client.push(first_fact) self.__client.push(second_fact) cache = KnowledgeBaseFactCache(pattern=(prefix, 'updated', '*', '1')) rospy.sleep(0.1) current = cache.get_all_matching_facts() self.assertEqual(2, len(current)) self.assertTrue(first_fact in current, 'fact not found') self.assertTrue(second_fact in current, 'fact not found') def test_update_existing(self): """ Test updating an existing fact """ prefix = self.__message_prefix + '_test_update_empty' updated_old = (prefix, 'updated', '1') self.__client.push(updated_old) cache = KnowledgeBaseFactCache(pattern=(prefix, '*', '*')) updated_new = (prefix, 'updated', '1') self.__client.update((prefix, '*', '*'), updated_new) rospy.sleep(0.1) current = cache.get_all_matching_facts() self.assertEqual(1, len(current)) self.assertTrue(updated_new in current, 'Update not noticed') def test_update(self): prefix = self.__message_prefix + '_test_update' updated_old = (prefix, 'updated', '1') not_influenced = (prefix, 'not_influenced', '1') self.__client.push(updated_old) self.__client.push(not_influenced) cache = KnowledgeBaseFactCache(pattern=(prefix, '*', '*')) updated_new = (prefix, 'updated', '0') self.__client.update(updated_old, updated_new) rospy.sleep(0.1) current = cache.get_all_matching_facts() self.assertEqual(2, len(current)) self.assertTrue(updated_new in current, 'Update not noticed') self.assertTrue(not_influenced in current, 'NotInfluenced was influenced') def test_multiple_updates(self): prefix = self.__message_prefix + '_test_multiple_updates' updated_old_1 = (prefix, 'fact_1', '1') updated_old_2 = (prefix, 'fact_2', '2') self.__client.push(updated_old_1) self.__client.push(updated_old_2) cache = KnowledgeBaseFactCache(pattern=(prefix, '*', '*')) updated_new_1 = (prefix, 'fact_1', '3') self.__client.update(updated_old_1, updated_new_1) updated_new_2 = (prefix, 'fact_2', '4') self.__client.update(updated_old_2, updated_new_2) rospy.sleep(0.1) current = cache.get_all_matching_facts() self.assertEqual(2, len(current)) self.assertTrue(updated_new_1 in current, 'Update not noticed') self.assertTrue(updated_new_2 in current, 'Update not noticed') def __check_content(self, fact_cache, *expected_facts): content = fact_cache.get_all_matching_facts() error_message = 'Excepted "{0}", but is {1}'.format( str(expected_facts), str(content)) self.assertEqual(len(content), len(expected_facts), error_message) for expected_fact in expected_facts: self.assertTrue(expected_fact in content, error_message) for existing_fact in content: self.assertTrue(existing_fact in expected_facts, error_message) def test_update_replacing_several_facts(self): prefix = self.__message_prefix + 'test_update_replacing_several_facts' updated_old_1 = (prefix, 'toUpdate', '1') updated_old_2 = (prefix, 'toUpdate', '2') not_influenced = (prefix, 'not_influenced', '1') cache = KnowledgeBaseFactCache(pattern=(prefix, '*', '*')) rospy.sleep(0.1) self.__client.push(updated_old_1) self.__client.push(updated_old_2) self.__client.push(not_influenced) rospy.sleep(0.1) self.__check_content(cache, updated_old_1, updated_old_2, not_influenced) new_fact = (prefix, 'updated', '3') self.__client.update((prefix, 'toUpdate', '*'), new_fact) rospy.sleep(0.1) self.__check_content(cache, not_influenced, new_fact)
class FactTableModel(QAbstractTableModel): """ Simple QT table model for Knowledge Base facts """ def __init__(self, kb_name, parent=None, *args): super(FactTableModel, self).__init__(parent) self.facts = None self._kb_name = kb_name self._kb_client = KnowledgeBaseClient( knowledge_base_name=self._kb_name, timeout=0.5) def update(self, new_facts): """ Use the method to update the fact/data base :param new_facts: new list of fact tuples """ if new_facts: new_facts.sort() self.layoutAboutToBeChanged.emit() self.facts = new_facts self.layoutChanged.emit() def clear(self): """ Clear current fact base """ self.update(None) def rowCount(self, parent=QModelIndex()): if self.facts: return len(self.facts) else: return 0 def columnCount(self, parent=QModelIndex()): if self.facts: max_column = 0 for fact in self.facts: max_column = max(max_column, len(fact)) return max_column else: return 0 def data(self, index, role=Qt.DisplayRole): if role == Qt.DisplayRole or role == Qt.EditRole: i = index.row() j = index.column() if j < len(self.facts[i]): return '{0}'.format(self.facts[i][j]) else: return QVariant() else: return QVariant() def setData(self, index, value, role): """ Update remote knowledge base data """ try: pattern = copy.deepcopy(self.facts[index.row()]) current_fact = list(pattern) if index.column() >= len(current_fact): for i in range(0, index.column() - len(current_fact) + 1): current_fact.append("") current_fact[index.column()] = str(value) rospy.logdebug("Changing knowledge: \n" + str(pattern) + "\n --to-- \n" + str(current_fact)) return self._kb_client.update(pattern=pattern, new=current_fact) except Exception: rospy.logerr(traceback.format_exc()) return False def flags(self, index): return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable @property def kb_name(self): return self._kb_name @kb_name.setter def kb_name(self, value): if self._kb_name != value: self._kb_name = value self._kb_client = KnowledgeBaseClient(knowledge_base_name=value, timeout=0.5)
class UpdateHandlerTestSuite(unittest.TestCase): def __init__(self, *args, **kwargs): super(UpdateHandlerTestSuite, self).__init__(*args, **kwargs) def setUp(self): self.__knowledge_base_address = KnowledgeBase.DEFAULT_NAME + "UpdateHandlerTestSuite" # prevent influence of previous tests self.__message_prefix = 'UpdateHandlerTestSuite' + str(time.time()) # start KnowledgeBase self.start_kb_node() rospy.init_node('UpdateHandlerTestSuite', log_level=rospy.DEBUG) self.__client = KnowledgeBaseClient(self.__knowledge_base_address) def start_kb_node(self): """ start the knowledge base node """ package = 'knowledge_base' executable = 'knowledge_base_node.py' node = roslaunch.core.Node(package=package, node_type=executable, name=self.__knowledge_base_address, output='screen') launch = roslaunch.scriptapi.ROSLaunch() launch.start() self._kb_process = launch.launch(node) def tearDown(self): self._kb_process.stop() def test_simple_adding(self): """ Tests basic case of adding a tuple """ test_tuple = (self.__message_prefix, 'test_simple_adding', '0', '0') cache = KnowledgeBaseFactCache( pattern=test_tuple, knowledge_base_name=self.__knowledge_base_address) self.assertFalse(cache.does_fact_exists(), 'Tuple is not added, but is indicated as present') self.__client.push(test_tuple) rospy.sleep(0.1) self.assertTrue(cache.does_fact_exists(), 'Tuple was added, but is not indicated as present') def test_remove(self): test_tuple = (self.__message_prefix, 'test_remove', '0', '0') cache = KnowledgeBaseFactCache( pattern=test_tuple, knowledge_base_name=self.__knowledge_base_address) self.assertFalse(cache.does_fact_exists(), 'Tuple is not added, but is indicated as present') self.__client.push(test_tuple) rospy.sleep(0.1) self.assertTrue(cache.does_fact_exists(), 'Tuple was added, but is not indicated as present') self.__client.pop(test_tuple) rospy.sleep(0.1) self.assertFalse( cache.does_fact_exists(), 'Tuple was removed, but is still indicated as present') def test_multiple_caches_for_same_pattern(self): test_tuple = (self.__message_prefix, 'test_multiple_caches_for_same_pattern', '0', '0') cache1 = KnowledgeBaseFactCache( pattern=test_tuple, knowledge_base_name=self.__knowledge_base_address) cache2 = KnowledgeBaseFactCache( pattern=test_tuple, knowledge_base_name=self.__knowledge_base_address) self.assertFalse(cache1.does_fact_exists(), 'Tuple is not added, but is indicated as present') self.assertFalse(cache2.does_fact_exists(), 'Tuple is not added, but is indicated as present') self.__client.push(test_tuple) rospy.sleep(0.1) self.assertTrue(cache1.does_fact_exists(), 'Tuple was added, but is not indicated as present') self.assertTrue(cache2.does_fact_exists(), 'Tuple was added, but is not indicated as present') def test_middle_placeholder(self): """ Test updating an existing fact """ prefix = self.__message_prefix + '_test_middle_placeholder' first_fact = (prefix, 'updated', 'a', '1') second_fact = (prefix, 'updated', 'b', '1') self.__client.push(first_fact) self.__client.push(second_fact) pattern = (prefix, 'updated', '*', '1') cache = KnowledgeBaseFactCache( pattern=pattern, knowledge_base_name=self.__knowledge_base_address) rospy.sleep(0.1) current = cache.get_all_matching_facts() self.assertEqual(2, len(current)) self.assertTrue(first_fact in current, 'fact not found') self.assertTrue(second_fact in current, 'fact not found') def test_update_existing(self): """ Test updating a fact that is already stored. """ prefix = self.__message_prefix + '_test_update_existing' updated_old = (prefix, 'updated', '1') self.assertTrue(self.__client.push(updated_old)) rospy.sleep(0.1) cache = KnowledgeBaseFactCache( pattern=(prefix, '*', '*'), knowledge_base_name=self.__knowledge_base_address) rospy.sleep(0.1) updated_new = (prefix, 'updated', '1') self.assertTrue(self.__client.update((prefix, '*', '*'), updated_new)) rospy.sleep( 0.5 ) # don' t wait for updates because we will not get one, as nothing changed current = cache.get_all_matching_facts() self.assertEqual(1, len(current)) self.assertTrue(updated_new in current, 'Update corrupted') def test_update(self): prefix = self.__message_prefix + '_test_update' updated_old = (prefix, 'updated', '1') not_influenced = (prefix, 'not_influenced', '1') self.__client.push(updated_old) self.__client.push(not_influenced) cache = KnowledgeBaseFactCache( pattern=(prefix, '*', '*'), knowledge_base_name=self.__knowledge_base_address) update_stamp = cache.update_time updated_new = (prefix, 'updated', '0') self.__client.update(updated_old, updated_new) while update_stamp == cache.update_time: rospy.sleep(0.5) current = cache.get_all_matching_facts() self.assertEqual(2, len(current)) self.assertTrue(updated_new in current, 'Update not noticed') self.assertTrue(not_influenced in current, 'NotInfluenced was influenced') def test_update_non_existing(self): prefix = self.__message_prefix + 'test_update_non_existing' updated_not_existing = (prefix, 'updated', '1') cache = KnowledgeBaseFactCache( pattern=(prefix, '*', '*'), knowledge_base_name=self.__knowledge_base_address) update_stamp = cache.update_time updated_new = (prefix, 'updated', '0') self.__client.update(updated_not_existing, updated_new) while update_stamp == cache.update_time: rospy.sleep(0.5) current = cache.get_all_matching_facts() self.assertEqual(1, len(current)) self.assertTrue(updated_new in current, 'Update not noticed') def test_multiple_updates(self): prefix = self.__message_prefix + '_test_multiple_updates' updated_old_1 = (prefix, 'fact_1', '1') updated_old_2 = (prefix, 'fact_2', '2') self.__client.push(updated_old_1) self.__client.push(updated_old_2) cache = KnowledgeBaseFactCache( pattern=(prefix, '*', '*'), knowledge_base_name=self.__knowledge_base_address) updated_new_1 = (prefix, 'fact_1', '1*') self.__client.update(updated_old_1, updated_new_1) updated_new_2 = (prefix, 'fact_2', '2*') update_stamp = cache.update_time self.__client.update(updated_old_2, updated_new_2) rospy.sleep(0.5) while update_stamp == cache.update_time: rospy.sleep(0.5) current = cache.get_all_matching_facts() self.assertEqual(2, len(current)) self.assertTrue(updated_new_1 in current, 'Update of updated_new_1 not noticed') self.assertTrue(updated_new_2 in current, 'Update of updated_new_2 not noticed') def test_update_with_partly_matching_patterns(self): """ test an update that replaces several existing facts with a single new one or adds a new fact as a result of an update """ prefix = self.__message_prefix + 'test_update_reduction' updated_old_1 = (prefix, 'fact_1', '1') updated_old_2 = (prefix, 'fact_2', '1') self.__client.push(updated_old_1) self.__client.push(updated_old_2) cache_all = KnowledgeBaseFactCache( pattern=(prefix, '*', '*'), knowledge_base_name=self.__knowledge_base_address) cache_specific_1 = KnowledgeBaseFactCache( pattern=(prefix, 'fact_1', '*'), knowledge_base_name=self.__knowledge_base_address) cache_specific_2 = KnowledgeBaseFactCache( pattern=(prefix, 'fact_2', '*'), knowledge_base_name=self.__knowledge_base_address) cache_specific_3 = KnowledgeBaseFactCache( pattern=(prefix, 'fact_3', '*'), knowledge_base_name=self.__knowledge_base_address) current_all = cache_all.get_all_matching_facts() current_specific_1 = cache_specific_1.get_all_matching_facts() current_specific_2 = cache_specific_2.get_all_matching_facts() current_specific_3 = cache_specific_3.get_all_matching_facts() self.assertEqual(2, len(current_all)) self.assertEqual(1, len(current_specific_1)) self.assertEqual(1, len(current_specific_2)) self.assertEqual(0, len(current_specific_3)) updated_new = (prefix, 'fact_3', '1') update_stamp1 = cache_all.update_time update_stamp2 = cache_specific_1.update_time update_stamp3 = cache_specific_2.update_time update_stamp4 = cache_specific_3.update_time self.__client.update((prefix, '*', '1'), updated_new) # wait until the caches are updated rospy.sleep(0.5) while update_stamp1 == cache_all.update_time or \ update_stamp2 == cache_specific_1.update_time or update_stamp3 == cache_specific_2.update_time \ or update_stamp4 == cache_specific_3.update_time: rospy.sleep(0.1) current_all = cache_all.get_all_matching_facts() current_specific_1 = cache_specific_1.get_all_matching_facts() current_specific_2 = cache_specific_2.get_all_matching_facts() current_specific_3 = cache_specific_3.get_all_matching_facts() self.assertEqual(1, len(current_all)) self.assertEqual(0, len(current_specific_1)) self.assertEqual(0, len(current_specific_2)) self.assertEqual(1, len(current_specific_3)) self.assertTrue(updated_new in current_all, 'Update general cache of not noticed') self.assertTrue(updated_new not in current_specific_1, 'Delete update in specific cache 1 not noticed') self.assertTrue(updated_new not in current_specific_2, 'Delete update in specific cache 2 not noticed') self.assertTrue(updated_new in current_specific_3, 'Add update in specific cache 3 not noticed') def test_all_pattern_with_update(self): prefix = self.__message_prefix + '_test_multiple_updates' updated_old_1 = (prefix, 'fact_1', '1') self.__client.push(updated_old_1) all_pattern = () cache = KnowledgeBaseFactCache( pattern=all_pattern, knowledge_base_name=self.__knowledge_base_address) update_stamp = cache.update_time updated_new_1 = (prefix, 'fact_1', '3') self.__client.update(updated_old_1, updated_new_1) while update_stamp == cache.update_time: rospy.sleep(0.1) current = cache.get_all_matching_facts() self.assertEqual(1, len(current)) self.assertTrue(updated_new_1 in current, 'Update not noticed') def __check_content(self, fact_cache, *expected_facts): content = fact_cache.get_all_matching_facts() error_message = 'Excepted "{0}", but is {1}'.format( str(expected_facts), str(content)) self.assertEqual(len(content), len(expected_facts), error_message) for expected_fact in expected_facts: self.assertTrue(expected_fact in content, error_message) for existing_fact in content: self.assertTrue(existing_fact in expected_facts, error_message) def test_update_replacing_several_facts(self): prefix = self.__message_prefix + 'test_update_replacing_several_facts' updated_old_1 = (prefix, 'toUpdate', '1') updated_old_2 = (prefix, 'toUpdate', '2') not_influenced = (prefix, 'not_influenced', '1') cache = KnowledgeBaseFactCache( pattern=(prefix, '*', '*'), knowledge_base_name=self.__knowledge_base_address) rospy.sleep(0.1) self.__client.push(updated_old_1) self.__client.push(updated_old_2) self.__client.push(not_influenced) rospy.sleep(0.1) self.__check_content(cache, updated_old_1, updated_old_2, not_influenced) new_fact = (prefix, 'updated', '3') self.__client.update((prefix, 'toUpdate', '*'), new_fact) rospy.sleep(0.1) self.__check_content(cache, not_influenced, new_fact) def test_mass_updates(self): """ Here we test many updates in a short time period """ prefix = self.__message_prefix + 'test_mass_updates' number_of_entries = 500 # fill KB for i in range(number_of_entries): updated_old_1 = (prefix, 'fact', str(i)) self.__client.push(updated_old_1) cache = KnowledgeBaseFactCache( pattern=(prefix, 'fact_new', '*'), knowledge_base_name=self.__knowledge_base_address) current = cache.get_all_matching_facts() self.assertEqual(0, len(current)) rospy.sleep(0.5) for i in range(number_of_entries): updated_old_1 = (prefix, 'fact', str(i)) updated_new_1 = (prefix, 'fact_new', str(i)) last_fact = updated_new_1 self.__client.update(updated_old_1, updated_new_1) rospy.sleep(0.5) while last_fact != cache.last_updated_fact: rospy.sleep(0.5) current = cache.get_all_matching_facts() # test if we received all tuples for i in range(number_of_entries): updated_new_1 = (prefix, 'fact_new', str(i)) self.assertTrue( updated_new_1 in current, 'Update of "' + str(updated_new_1) + '" not noticed') def test_sub_fact_matching(self): prefix = self.__message_prefix + '_test_multiple_updates' fact_1 = (prefix, 'fact_1', '1', 'a') self.__client.push(fact_1) fact_2 = (prefix, 'fact_2', '2', 'a') self.__client.push(fact_2) fact_3 = (prefix, 'fact_3', '3', 'b') self.__client.push(fact_3) all_pattern = () cache = KnowledgeBaseFactCache( pattern=all_pattern, knowledge_base_name=self.__knowledge_base_address) # initial check to see if we have a correct setup current = cache.get_all_matching_facts() self.assertEqual(3, len(current)) sub_fact_pattern = (prefix, 'fact_2', '2', 'a') does_exist = cache.does_sub_fact_exist(pattern=sub_fact_pattern) self.assertTrue(does_exist) first_sub_fact = cache.get_matching_sub_fact(pattern=sub_fact_pattern) self.assertEqual(first_sub_fact, fact_2) # test with sub placeholders sub_fact_pattern = (prefix, '*', '*', 'a') does_exist = cache.does_sub_fact_exist(pattern=sub_fact_pattern) self.assertTrue(does_exist) first_sub_fact = cache.get_matching_sub_fact(pattern=sub_fact_pattern) self.assertEqual(first_sub_fact, fact_1) all_sub_facts = cache.get_all_matching_sub_facts( pattern=sub_fact_pattern) self.assertEqual(2, len(all_sub_facts)) self.assertTrue(fact_1 in all_sub_facts, "fact_1 not found in sub facts") self.assertTrue(fact_2 in all_sub_facts, "fact_2 not found in sub facts") # invalid pattern sub_fact_pattern = (prefix, 'fact_unknown', '2', 'a') does_exist = cache.does_sub_fact_exist(pattern=sub_fact_pattern) self.assertFalse(does_exist)