diff --git a/pyogp/lib/base/message_template.py b/pyogp/lib/base/message_template.py index b7968ec..ba52f2c 100644 --- a/pyogp/lib/base/message_template.py +++ b/pyogp/lib/base/message_template.py @@ -77,8 +77,8 @@ class MsgBlockData(object): def add_variable(self, var): self.variable_map[var.name] = var - def add_data(self, var_name, data, data_size): - self.get_variable(var_name).add_data(data, data_size) + def add_data(self, var_name, data, size): + self.get_variable(var_name).add_data(data, size) class MsgVariableData(object): """ Used as a Message Block variable that is being created that will be @@ -87,15 +87,14 @@ class MsgVariableData(object): self.name = name #data_size holds info whether or not the variable is of type #MVT_VARIABLE - self.data_size = 0 self.size = -1 self.type = tp self.data = None #how DO we add data? What format will it be in? - def add_data(self, data, data_size): + def add_data(self, data, size): self.data = data - self.size = data_size + self.size = size class MessageTemplateVariable(object): def __init__(self, name, tp, size): @@ -105,6 +104,7 @@ class MessageTemplateVariable(object): class MessageTemplateBlock(object): def __init__(self, name): + self.variables = [] self.variable_map = {} self.name = name self.block_type = None @@ -112,15 +112,17 @@ class MessageTemplateBlock(object): def add_variable(self, var): self.variable_map[var.name] = var + self.variables.append(var) def get_variables(self): - return self.variable_map.values() + return self.variables #self.variable_map.values() def get_variable(self, name): return self.variable_map[name] class MessageTemplate(object): def __init__(self, name): + self.blocks = [] self.block_map = {} #this is the function or object that will handle this type of message self.handler = None @@ -138,9 +140,10 @@ class MessageTemplate(object): def add_block(self, block): self.block_map[block.name] = block + self.blocks.append(block) def get_blocks(self): - return self.block_map.values() + return self.blocks #self.block_map.values() def get_block(self, name): return self.block_map[name] diff --git a/pyogp/lib/base/message_template_builder.py b/pyogp/lib/base/message_template_builder.py index b57a774..1cdbb60 100644 --- a/pyogp/lib/base/message_template_builder.py +++ b/pyogp/lib/base/message_template_builder.py @@ -24,12 +24,6 @@ class MessageTemplateBuilder(object): self.cur_block_name = '' self.packer = DataPacker() - - def get_current_message(self): - return self.current_msg - - def get_current_block(self): - return self.current_block def build_message(self): """ Builds the message by serializing the data. Creates a packet ready @@ -90,18 +84,19 @@ class MessageTemplateBuilder(object): bytes += 1 for block in block_list: - for variable in message_block.get_variables(): + for v in template_block.get_variables(): #message_block.get_variables(): + #this mapping has to occur to make sure the data is written in correct order + variable = message_block.get_variable(v.name) var_size = variable.size - + if var_size == -1: raise Exception('Variable ' + variable.name + ' in block ' + \ message_block.name + ' of message ' + message_data.name + \ ' wasn"t set prior to buildMessage call') - data_size = variable.data_size - #variable's data_size represents whether or not it is of the type - #MVT_VARIABLE. If it is positive, it is - if data_size > 0: + #if its a VARIABLE type, we have to write in the size of the data + if variable.type == MsgType.MVT_VARIABLE: + data_size = template_block.get_variable(variable.name).size if data_size == 1: block_buffer += struct.pack('>B', var_size) elif data_size == 2: @@ -136,11 +131,8 @@ class MessageTemplateBuilder(object): self.current_msg.add_block(block_data) def next_block(self, block_name): - #if it doesn't exist, create a new block (may be a VARIABLE or MULTIPLE type - #block if block_name not in self.current_template.block_map: #error: - print 'ERROR: template doesn"t have the block' return template_block = self.current_template.get_block(block_name) @@ -159,40 +151,64 @@ class MessageTemplateBuilder(object): return #although we may have a block already, there are some cases where we can have - #more than one block of the same name (when type is MULTIPLE or VARIABLE) + #more than one block of the same name (when type is MULTIPLE or VARIABLE), so we + #might have to create a whole new block else: - #make sure it isn't SINGLE - if self.template_block.type == MsgBlockType.MBT_SINGLE: - #error: can't have more than 1 block when its supposed to be 1 - print 'ERROR: can"t have more than 1 block when its supposed to be 1' - return + #make sure it isn't SINGLE and trying to create a new block + if template_block.type == MsgBlockType.MBT_SINGLE: + raise Exception('ERROR: can"t have more than 1 block when its supposed to be 1') - elif self.template_block.type == MsgBlockType.MBT_MULTIPLE and \ - self.template_block.block_number == block_data.block_number: - #error: we are about to exceed block total - print 'ERROR: we are about to exceed block total' - return + elif template_block.type == MsgBlockType.MBT_MULTIPLE and \ + template_block.block_number == block_data.block_number: + raise Exception('ERROR: we are about to exceed block total') block_data.block_number += 1 - self.current_block = MsgBlockData(block.name) + self.current_block = MsgBlockData(block_name) self.current_msg.add_block(self.current_block) self.cur_block_name = block_name - for variable in self.current_block.variables: + for variable in template_block.get_variables(): var_data = MsgVariableData(variable.name, variable.type) self.current_block.add_variable(var_data) def add_data(self, var_name, data, data_type): """ the data type is passed in to make sure that the programmer is aware of what type (and therefore size) of the data that is being passed in. """ - self.__check_size(var_name, data, data_type) - self.current_block.add_data(var_name, data, data_type) + if self.current_template == None: + raise Exception('Attempting to add data to a null message') - def __check_size(self, var_name, data, data_type): + if self.current_block == None: + raise Exception('Attempting to add data to a null block') + + template_variable = self.current_template.get_block(self.cur_block_name).get_variable(var_name) + if template_variable == None: + raise Exception('Variable is not in the block') + + #this should be the size of the actual data + size = sizeof(data_type) + + if data_type == MsgType.MVT_VARIABLE: + #if its a MVT_VARIABLE type of data, then size will be -1 for the type + #so the actual size we will have to get from the data itself + #HACK - this may cause a bug if the data type doesn't have len + size = len(data) + #template holds the max size the data can be + data_size = template_variable.size + #error check - size can't be larger than the bytes will hold + + self.current_block.add_data(var_name, data, size) + + else: + #size check can't be done on VARIABLE sized variables + if self.__check_size(var_name, data, size) == False: + raise Exception('Variable size isn"t the same as the template size') + + self.current_block.add_data(var_name, data, size) + + def __check_size(self, var_name, data, data_size): block = self.template_list[self.cur_msg_name].get_block(self.cur_block_name) - data_size = sizeof(data_type) size = block.get_variable(var_name).size if size != data_size: - #warning - #for now, exception - raise Exception('Variable size isn"t the same as the template size') + return False + + return True diff --git a/pyogp/lib/base/tests/test_template_builder.py b/pyogp/lib/base/tests/test_template_builder.py index 3f84015..18a50c9 100644 --- a/pyogp/lib/base/tests/test_template_builder.py +++ b/pyogp/lib/base/tests/test_template_builder.py @@ -1,5 +1,6 @@ #standard libraries import unittest, doctest +from uuid import UUID #local libraries from pyogp.lib.base.data import msg_tmpl @@ -8,7 +9,7 @@ from pyogp.lib.base.message_template_parser import MessageTemplateParser from pyogp.lib.base.message_template_builder import MessageTemplateBuilder from pyogp.lib.base.message_template_dict import TemplateDictionary from pyogp.lib.base.message_types import MsgType -from indra.base.lluuid import UUID +#from indra.base.lluuid import UUID class TestTemplateBuilder(unittest.TestCase): @@ -36,18 +37,18 @@ class TestTemplateBuilder(unittest.TestCase): except: assert True - assert builder.get_current_message() == None, "Created a message despite its non-existence" + assert builder.current_msg == None, "Created a message despite its non-existence" def test_create_message(self): builder = MessageTemplateBuilder(self.template_dict) - assert builder.get_current_message() == None, "Message exists before it was created" + assert builder.current_msg == None, "Message exists before it was created" builder.new_message('AddCircuitCode') - assert builder.get_current_message() != None, "Create message failed" + assert builder.current_msg != None, "Create message failed" def test_create_message_blocks(self): builder = MessageTemplateBuilder(self.template_dict) builder.new_message('AvatarTextureUpdate') - blocks = builder.get_current_message().block_map + blocks = builder.current_msg.block_map assert len(blocks) == 3, "Blocks not added to the message" try: t_block = blocks['AgentData'] @@ -70,10 +71,10 @@ class TestTemplateBuilder(unittest.TestCase): def test_next_block(self): builder = MessageTemplateBuilder(self.template_dict) builder.new_message('AvatarTextureUpdate') - assert builder.get_current_block() == None, "Message block exists before it was created" + assert builder.current_block == None, "Message block exists before it was created" builder.next_block('AgentData') - assert builder.get_current_block() != None, "Setting next block failed" - assert builder.get_current_block().name == 'AgentData', "Wrong block set" + assert builder.current_block != None, "Setting next block failed" + assert builder.current_block.name == 'AgentData', "Wrong block set" def test_next_block_fail(self): builder = MessageTemplateBuilder(self.template_dict) @@ -84,13 +85,88 @@ class TestTemplateBuilder(unittest.TestCase): assert False, "Using block AgentSocialIdeas that doesn't exist" except: assert True - assert builder.get_current_block() == None, "Set block without one existing" + assert builder.current_block == None, "Set block without one existing" + + def test_next_single(self): + builder = MessageTemplateBuilder(self.template_dict) + builder.new_message('TestMessage') + builder.next_block('TestBlock1') + builder.add_data('Test1', 0x00000001, MsgType.MVT_U32) + try: + builder.next_block('TestBlock1') + assert False, "Adding 2 blocks of the same type to a MBT_SINGLE block" + except: + assert True + + def test_nextmultiple_exceed(self): + builder = MessageTemplateBuilder(self.template_dict) + builder.new_message('TestMessage') + builder.next_block('TestBlock1') + builder.add_data('Test1', 0x00000001, MsgType.MVT_U32) + builder.next_block('NeighborBlock') + builder.add_data('Test0', 0x00000001, MsgType.MVT_U32) + builder.add_data('Test1', 0x00000001, MsgType.MVT_U32) + builder.add_data('Test2', 0x00000001, MsgType.MVT_U32) + builder.next_block('NeighborBlock') + builder.add_data('Test0', 0x00000001, MsgType.MVT_U32) + builder.add_data('Test1', 0x00000001, MsgType.MVT_U32) + builder.add_data('Test2', 0x00000001, MsgType.MVT_U32) + builder.next_block('NeighborBlock') + builder.add_data('Test0', 0x00000001, MsgType.MVT_U32) + builder.add_data('Test1', 0x00000001, MsgType.MVT_U32) + builder.add_data('Test2', 0x00000001, MsgType.MVT_U32) + builder.next_block('NeighborBlock') + builder.add_data('Test0', 0x00000001, MsgType.MVT_U32) + builder.add_data('Test1', 0x00000001, MsgType.MVT_U32) + builder.add_data('Test2', 0x00000001, MsgType.MVT_U32) + try: + builder.next_block('NeighborBlock') + assert False, "Allowed to add more than specified number of blocks" + except: + assert True + + def test_build_variable(self): + builder = MessageTemplateBuilder(self.template_dict) + builder.new_message('PacketAck') + builder.next_block('Packets') + builder.add_data('ID', 0x00000001, MsgType.MVT_U32) + builder.next_block('Packets') + builder.add_data('ID', 0x00000001, MsgType.MVT_U32) + msg, size = builder.build_message() + assert msg == '\xff\xff\xff\xfb' + '\x02' + \ + '\x00\x00\x00\x01\x00\x00\x00\x01', \ + "Building variable block failed" + + def test_build_empty_var(self): + builder = MessageTemplateBuilder(self.template_dict) + builder.new_message('PacketAck') + builder.next_block('Packets') + try: + msg, size = builder.build_message() + assert False, "Building with a variable that wasn't set" + except: + assert True + + def test_build_variable_var(self): + builder = MessageTemplateBuilder(self.template_dict) + builder.new_message('UpdateSimulator') + builder.next_block('SimulatorInfo') + builder.add_data('RegionID', UUID("550e8400-e29b-41d4-a716-446655440000"), MsgType.MVT_LLUUID) + builder.add_data('SimName', "Testing", MsgType.MVT_VARIABLE) + builder.add_data('EstateID', 0x00000001, MsgType.MVT_U32) + builder.add_data('SimAccess', 0x01, MsgType.MVT_U8) + msg, size = builder.build_message() + + assert_string = '\xff\xff\x00\x11' + UUID("550e8400-e29b-41d4-a716-446655440000").bytes + \ + '\x07' + 'Testing' + '\x00\x00\x00\x01' + '\x01' + assert msg == assert_string, "Building variable block failed" + def test_next_block_variables(self): builder = MessageTemplateBuilder(self.template_dict) builder.new_message('AvatarTextureUpdate') builder.next_block('AgentData') - variables = builder.get_current_block().variable_map + variables = builder.current_block.variable_map assert len(variables) == 2, "Variables not added to the block" try: @@ -115,7 +191,7 @@ class TestTemplateBuilder(unittest.TestCase): builder.next_block('AgentData') builder.add_data('TexturesChanged', True, MsgType.MVT_BOOL) #need a way to determine the right variable data is sent compared to the type - assert builder.get_current_block().variable_map['TexturesChanged'].data == True,\ + assert builder.current_block.variable_map['TexturesChanged'].data == True,\ "Data not set correctly" def test_add_lluuid(self): @@ -123,9 +199,9 @@ class TestTemplateBuilder(unittest.TestCase): builder.new_message('AvatarTextureUpdate') builder.next_block('AgentData') builder.add_data('AgentID', UUID("550e8400-e29b-41d4-a716-446655440000"), MsgType.MVT_LLUUID) - assert builder.get_current_block().variable_map['AgentID'].data == UUID("550e8400-e29b-41d4-a716-446655440000"),\ + assert builder.current_block.variable_map['AgentID'].data == UUID("550e8400-e29b-41d4-a716-446655440000"),\ "Data not set correctly" - #assert builder.get_current_block().variables['AgentID'].get_size() == ? + #assert builder.current_block.variables['AgentID'].get_size() == ? #test should go with the packer mostly def test_serialize_u8_fail(self): @@ -139,6 +215,65 @@ class TestTemplateBuilder(unittest.TestCase): except: assert True + def test_build_fail(self): + builder = MessageTemplateBuilder(self.template_dict) + msg = builder.build_message() + assert msg == None, "Got a message without calling new first" + + + def test_build_multiple_fail(self): + builder = MessageTemplateBuilder(self.template_dict) + builder.new_message('TestMessage') + builder.next_block('TestBlock1') + builder.add_data('Test1', 0x00000001, MsgType.MVT_U32) + builder.next_block('NeighborBlock') + builder.add_data('Test0', 0x00000001, MsgType.MVT_U32) + builder.add_data('Test1', 0x00000001, MsgType.MVT_U32) + builder.add_data('Test2', 0x00000001, MsgType.MVT_U32) + try: + msg, size = builder.build_message() + assert False, "Message block should be 1, where it should be 4" + except: + assert True + + def test_build_multiple(self): + builder = MessageTemplateBuilder(self.template_dict) + builder.new_message('TestMessage') + builder.next_block('TestBlock1') + builder.add_data('Test1', 0x00000001, MsgType.MVT_U32) + builder.next_block('NeighborBlock') + builder.add_data('Test0', 0x00000001, MsgType.MVT_U32) + builder.add_data('Test1', 0x00000001, MsgType.MVT_U32) + builder.add_data('Test2', 0x00000001, MsgType.MVT_U32) + builder.next_block('NeighborBlock') + builder.add_data('Test0', 0x00000001, MsgType.MVT_U32) + builder.add_data('Test1', 0x00000001, MsgType.MVT_U32) + builder.add_data('Test2', 0x00000001, MsgType.MVT_U32) + builder.next_block('NeighborBlock') + builder.add_data('Test0', 0x00000001, MsgType.MVT_U32) + builder.add_data('Test1', 0x00000001, MsgType.MVT_U32) + builder.add_data('Test2', 0x00000001, MsgType.MVT_U32) + builder.next_block('NeighborBlock') + builder.add_data('Test0', 0x00000001, MsgType.MVT_U32) + builder.add_data('Test1', 0x00000001, MsgType.MVT_U32) + builder.add_data('Test2', 0x00000001, MsgType.MVT_U32) + + msg = None + size = 0 + + try: + msg, size = builder.build_message() + + except Exception, e: + assert False, "Multiple blocks not working correctly" + + assert msg == '\xff\xff\x00\x01' + '\x00\x00\x00\x01' + \ + '\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01' + \ + '\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01' + \ + '\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01' + \ + '\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01', \ + 'TestMessage data not built correctly' + def test_serialize_u8(self): builder = MessageTemplateBuilder(self.template_dict) builder.new_message('CompletePingCheck')