class TestLittleBig(unittest.TestCase): def setUp(self): self.rng = SimpleRNG(time.time()) data = StringIO(LITTLE_BIG_PROTO_SPEC) ppp = StringProtoSpecParser(data) # data should be file-like self.str_obj_model = ppp.parse() # object model from string serialization self.proto_name = self.str_obj_model.name # the dotted name of the protocol def tearDown(self): pass # utility functions ############################################# def lil_big_msg_values(self): values = [] # XXX these MUST be kept in sync with littleBigTest.py values.append(self.rng.next_boolean()) # vBoolReqField values.append(self.rng.next_int16()) # vEnumReqField values.append(self.rng.next_int32()) # vuInt32ReqField values.append(self.rng.next_int32()) # vuInt64ReqField values.append(self.rng.next_int64()) # vsInt32ReqField values.append(self.rng.next_int64()) # vsInt64ReqField # #vuInt32ReqField # #vuInt64ReqField values.append(self.rng.next_int32()) # fsInt32ReqField values.append(self.rng.next_int32()) # fuInt32ReqField values.append(self.rng.next_real()) # fFloatReqField values.append(self.rng.next_int64()) # fsInt64ReqField values.append(self.rng.next_int64()) # fuInt64ReqField values.append(self.rng.next_real()) # fDoubleReqField values.append(self.rng.next_file_name(16)) # lStringReqField rnd_len = 16 + self.rng.next_int16(49) byte_buf = bytearray(rnd_len) self.rng.next_bytes(byte_buf) values.append(bytes(byte_buf)) # lBytesReqField b128_buf = bytearray(16) self.rng.next_bytes(b128_buf) values.append(bytes(b128_buf)) # fBytes16ReqField b160_buf = bytearray(20) self.rng.next_bytes(b160_buf) values.append(bytes(b160_buf)) # fBytes20ReqField b256_buf = bytearray(32) self.rng.next_bytes(b256_buf) values.append(bytes(b256_buf)) # fBytes32ReqField return values # actual unit tests ############################################# def check_field_impl_against_spec( self, proto_name, msg_name, field_spec, value): self.assertIsNotNone(field_spec) dotted_name = "%s.%s" % (proto_name, msg_name) cls = make_field_class(dotted_name, field_spec) if '__dict__' in dir(cls): print('\nGENERATED FieldImpl CLASS DICTIONARY') for exc in list(cls.__dict__.keys()): print("%-20s %s" % (exc, cls.__dict__[exc])) self.assertIsNotNone(cls) file = cls(value) self.assertIsNotNone(file) # class attributes -------------------------------- self.assertEqual(field_spec.name, file.name) self.assertEqual(field_spec.field_type_ndx, file.field_type) self.assertEqual(field_spec.quantifier, file.quantifier) self.assertEqual(field_spec.field_nbr, file.field_nbr) self.assertIsNone(file.default) # not an elegant test # instance attribute ------------------------------ self.assertEqual(value, file.value) # with slots enabled, this is never seen ---------- # because __dict__ is not in the list of valid # attributes for f if '__dict__' in dir(file): print('\nGENERATED FieldImpl INSTANCE DICTIONARY') for item in list(file.__dict__.keys()): print("%-20s %s" % (item, file.__dict__[item])) # GEEP def test_field_impl(self): msg_spec = self.str_obj_model.msgs[0] # the fields in this imaginary logEntry values = self.lil_big_msg_values() for i in range(len(msg_spec)): print( "\nDEBUG: field %u ------------------------------------------------------" % i) field_spec = msg_spec[i] self.check_field_impl_against_spec( self.proto_name, msg_spec.name, field_spec, values[i]) def test_caching(self): self.assertTrue(isinstance(self.str_obj_model, M.ProtoSpec)) # XXX A HACK WHILE WE CHANGE INTERFACE ------------ msg_spec = self.str_obj_model.msgs[0] name = msg_spec.name cls0 = make_msg_class(self.str_obj_model, name) # DEBUG print("Constructed Clz0 name is '%s'" % cls0.name) # END self.assertEqual(name, cls0.name) cls1 = make_msg_class(self.str_obj_model, name) self.assertEqual(name, cls1.name) # END HACK ---------------------------------------- # we cache classe, so the two should be the same self.assertEqual(id(cls0), id(cls1)) # chan = Channel(BUFSIZE) values = self.lil_big_msg_values() lil_big_msg0 = cls0(values) lil_big_msg1 = cls0(values) # we don't cache instances, so these will differ self.assertNotEqual(id(lil_big_msg0), id(lil_big_msg1)) field_spec = msg_spec[0] dotted_name = "%s.%s" % (self.proto_name, msg_spec.name) f0cls = make_field_class(dotted_name, field_spec) f1cls = make_field_class(dotted_name, field_spec) self.assertEqual(id(f0cls), id(f1cls)) def test_little_big(self): self.assertIsNotNone(self.str_obj_model) self.assertTrue(isinstance(self.str_obj_model, M.ProtoSpec)) self.assertEqual('org.xlattice.fieldz.test.littleBigProto', self.str_obj_model.name) self.assertEqual(0, len(self.str_obj_model.enums)) self.assertEqual(1, len(self.str_obj_model.msgs)) self.assertEqual(0, len(self.str_obj_model.seqs)) msg_spec = self.str_obj_model.msgs[0] # Create a channel ------------------------------------------ # its buffer will be used for both serializing the instance # data and, by deserializing it, for creating a second instance. chan = Channel(BUFSIZE) buf = chan.buffer self.assertEqual(BUFSIZE, len(buf)) # create the LittleBigMsg class ------------------------------ little_big_msg_cls = make_msg_class(self.str_obj_model, msg_spec.name) # ------------------------------------------------------------- # XXX the following fails because field 2 is seen as a property # instead of a list if False: # DEBUGGING print('\nLittleBigMsg CLASS DICTIONARY') for (ndx, key) in enumerate(little_big_msg_cls.__dict__.keys()): print( "%3u: %-20s %s" % (ndx, key, little_big_msg_cls.__dict__[key])) # ------------------------------------------------------------- # create a message instance --------------------------------- values = self.lil_big_msg_values() # quasi-random values lil_big_msg = little_big_msg_cls(values) # __setattr__ in MetaMsg raises exception on any attempt # to add new attributes. This works at the class level but # NOT at the instance level # if True: try: lil_big_msg.foo = 42 self.fail( "ERROR: attempt to assign new instance attribute succeeded") except AttributeError as a_exc: # DEBUG print( "ATTR ERROR ATTEMPTING TO SET lilBigMsg.foo: " + str(a_exc)) # END pass if '__dict__' in dir(lil_big_msg): print('\nlilBigMsg INSTANCE DICTIONARY') for exc in list(lil_big_msg.__dict__.keys()): print("%-20s %s" % (exc, lil_big_msg.__dict__[exc])) # lilBigMsg.name is a property try: lil_big_msg.name = 'boo' self.fail("ERROR: attempt to change message name succeeded") except AttributeError: pass self.assertEqual(msg_spec.name, lil_big_msg.name) # we don't have any nested enums or messages self.assertEqual(0, len(lil_big_msg.enums)) self.assertEqual(0, len(lil_big_msg.msgs)) self.assertEqual(17, len(lil_big_msg.field_classes)) # number of fields in instance self.assertEqual(17, len(lil_big_msg)) for i in range(len(lil_big_msg)): self.assertEqual(values[i], lil_big_msg[i].value) # serialize the object to the channel ----------------------- print("\nDEBUG: PHASE A ######################################") nnn = lil_big_msg.write_stand_alone(chan) old_position = chan.position chan.flip() self.assertEqual(old_position, chan.limit) self.assertEqual(0, chan.position) # deserialize the channel, making a clone of the message ---- (read_back, nn2) = little_big_msg_cls.read( chan, self.str_obj_model) # sOM is protoSpec self.assertIsNotNone(read_back) self.assertEqual(nnn, nn2) # verify that the messages are identical -------------------- self.assertTrue(lil_big_msg.__eq__(read_back)) print("\nDEBUG: PHASE B ######################################") # produce another message from the same values -------------- lil_big_msg2 = little_big_msg_cls(values) chan2 = Channel(BUFSIZE) nnn = lil_big_msg2.write_stand_alone(chan2) chan2.flip() (copy2, nn3) = little_big_msg_cls.read(chan2, self.str_obj_model) self.assertIsNotNone(copy2) self.assertEqual(nnn, nn3) self.assertTrue(lil_big_msg.__eq__(copy2)) self.assertTrue(lil_big_msg2.__eq__(copy2)) # test clear() chan2.position = 97 chan2.limit = 107 chan2.clear() self.assertEqual(0, chan2.limit) self.assertEqual(0, chan2.position)
class TestFieldImpl(unittest.TestCase): def setUp(self): self.rng = SimpleRNG(time.time()) # data = StringIO(ZOGGERY_PROTO_SPEC) # p = StringProtoSpecParser(data) # data should be file-like # self.str_obj_model = p.parse() # object model from string serialization # self.proto_name = self.str_obj_model.name # the dotted name of the # protocol def tearDown(self): pass # utility functions ############################################# def make_registries(self, protocol): node_reg = R.NodeReg() proto_reg = R.ProtoReg(protocol, node_reg) msg_reg = R.MsgReg(proto_reg) return (node_reg, proto_reg, msg_reg) def le_msg_values(self): """ returns a list """ timestamp = int(time.time()) node_id = [0] * 20 key = [0] * 20 length = self.rng.next_int32(256 * 256) # let's have some random bytes self.rng.next_bytes(node_id) self.rng.next_bytes(key) by_ = 'who is responsible' path = '/home/jdd/tarballs/something.tar.gz' return [timestamp, node_id, key, length, by_, path] def lil_big_msg_values(self): """ This returns a list of random-ish values in order by field type so that values[_F_FLOAT], for example, is a random float value. """ values = [] # 2016-03-30 This is NOT in sync with littleBigTest.py, # because I have added a None for lMsg at _L_MSG values.append(self.rng.next_boolean()) # vBoolReqField 0 values.append(self.rng.next_int16()) # vEnumReqField 1 values.append(self.rng.next_int32()) # vInt32ReqField 2 values.append(self.rng.next_int64()) # vInt64ReqField 3 values.append(self.rng.next_int32()) # vuInt32ReqField 4 values.append(self.rng.next_int32()) # vuInt64ReqField 5 values.append(self.rng.next_int64()) # vsInt32ReqField 6 values.append(self.rng.next_int64()) # vsInt64ReqField 7 values.append(self.rng.next_int32()) # fsInt32ReqField 8 values.append(self.rng.next_int32()) # fuInt32ReqField 9 values.append(self.rng.next_real()) # fFloatReqField 10 values.append(self.rng.next_int64()) # fsInt64ReqField 11 values.append(self.rng.next_int64()) # fuInt64ReqField 12 values.append(self.rng.next_real()) # fDoubleReqField 13 # lStringReqField 14 values.append(self.rng.next_file_name(16)) rnd_len = 16 + self.rng.next_int16(49) byte_buf = bytearray(rnd_len) self.rng.next_bytes(byte_buf) values.append(bytes(byte_buf)) # lBytesReqField 15 values.append(None) # <-------- for lMsg 16 b128_buf = bytearray(16) self.rng.next_bytes(b128_buf) values.append(bytes(b128_buf)) # fBytes16ReqField 17 b160_buf = bytearray(20) self.rng.next_bytes(b160_buf) values.append(bytes(b160_buf)) # fBytes20ReqField 18 b256_buf = bytearray(32) self.rng.next_bytes(b256_buf) values.append(bytes(b256_buf)) # fBytes32ReqField 19 return values # actual unit tests ############################################# def check_field_impl_against_spec(self, proto_name, msg_name, # not actually tested field_spec, value): # significant for tests self.assertIsNotNone(field_spec) dotted_name = "%s.%s" % (proto_name, msg_name) cls = make_field_class(dotted_name, field_spec) # a class if '__dict__' in dir(cls): print('\nGENERATED FieldImpl CLASS DICTIONARY') for exc in list(cls.__dict__.keys()): print(" %-20s %s" % (exc, cls.__dict__[exc])) self.assertIsNotNone(cls) file = cls(value) # an instance self.assertIsNotNone(file) self.assertTrue(isinstance(file, cls)) # instance attributes ----------------------------- # we verify that the properties work correctly self.assertEqual(field_spec.name, file._name) self.assertEqual(field_spec.field_type_ndx, file.field_type) self.assertEqual(field_spec.quantifier, file.quantifier) self.assertEqual(field_spec.field_nbr, file.field_nbr) self.assertIsNone(file.default) # not an elegant test # instance attribute ------------------------------ # we can read back the value assigned to the instance self.assertEqual(value, file.value) # with slots enabled, this is never seen ---------- # because __dict__ is not in the list of valid # attributes for f if '__dict__' in dir(file): print('\nGENERATED FieldImpl INSTANCE DICTIONARY') for item in list(file.__dict__.keys()): print("%-20s %s" % (item, file.__dict__[item])) def test_field_impl(self): node_reg, proto_reg, msg_reg = self.make_registries( PROTOCOL_UNDER_TEST) values = self.lil_big_msg_values() # DEBUG print("testFieldImpl: there are %d values" % len(values)) # END # There are 18 values corresponding to the 18 field types; # _L_MSG should be skipped for tstamp in range(FieldTypes.F_BYTES32 + 1): # DEBUG print("testFieldImpl: t = %d" % tstamp) # END if tstamp == FieldTypes.L_MSG: continue # default quantifier is Q_REQ_, default is None field_name = 'field%d' % tstamp field_spec = M.FieldSpec( msg_reg, field_name, tstamp, field_nbr=tstamp + 100) self.check_field_impl_against_spec( PROTOCOL_UNDER_TEST, MSG_UNDER_TEST, field_spec, values[tstamp]) # TEST FIELD SPEC ----------------------------------------------- def do_field_spec_test(self, name, field_type, quantifier=M.Q_REQUIRED, field_nbr=0, default=None): node_reg, proto_reg, msg_reg = self.make_registries( PROTOCOL_UNDER_TEST) # XXX Defaults are ignored for now. file = M.FieldSpec( msg_reg, name, field_type, quantifier, field_nbr, default) self.assertEqual(name, file.name) self.assertEqual(field_type, file.field_type_ndx) self.assertEqual(quantifier, file.quantifier) self.assertEqual(field_nbr, file.field_nbr) if default is not None: self.assertEqual(default, file.default) expected_repr = "%s %s%s @%d \n" % ( name, file.field_type_name, M.q_name(quantifier), field_nbr) # DEFAULTS NOT SUPPORTED self.assertEqual(expected_repr, file.__repr__()) def test_quantifiers(self): q_name = M.q_name self.assertEqual('', q_name(M.Q_REQUIRED)) self.assertEqual('?', q_name(M.Q_OPTIONAL)) self.assertEqual('*', q_name(M.Q_STAR)) self.assertEqual('+', q_name(M.Q_PLUS)) def test_field_spec(self): # default is not implemented yet self.do_field_spec_test('foo', FieldTypes.V_UINT32, M.Q_REQUIRED, 9) self.do_field_spec_test('bar', FieldTypes.V_SINT32, M.Q_STAR, 17) self.do_field_spec_test( 'node_id', FieldTypes.F_BYTES20, M.Q_OPTIONAL, 92) self.do_field_spec_test('tix', FieldTypes.V_BOOL, M.Q_PLUS, 147)
class TestOptionz(unittest.TestCase): """ Test the basic Optionz classes. """ def setUp(self): self.rng = SimpleRNG(time.time()) def tearDown(self): pass # utility functions ############################################# # actual unit tests ############################################# def test_bare_optionz(self): """ Create an Optionz instance, check for expected attibutes. """ my_optz = Z('fred') self.assertEqual(my_optz.name, 'fred') self.assertEqual(my_optz.desc, None) self.assertEqual(my_optz.epilog, None) self.assertEqual(len(my_optz), 0) my_optz = Z('frank', 'frivolous', 'fabulous') self.assertEqual(my_optz.name, 'frank') self.assertEqual(my_optz.desc, 'frivolous') self.assertEqual(my_optz.epilog, 'fabulous') self.assertEqual(len(my_optz), 0) def test_z_option(self): """ Populate an Optionz object, check for expected attr. """ z_name = self.rng.next_file_name(8) z_desc = self.rng.next_file_name(64) z_epilog = self.rng.next_file_name(64) my_optz = Z(z_name, z_desc, z_epilog) self.assertEqual(my_optz.name, z_name) self.assertEqual(my_optz.desc, z_desc) self.assertEqual(my_optz.epilog, z_epilog) self.assertEqual(len(my_optz), 0) # booleans -------------------------------------------------- b_dflt_val = True b_desc = "I'm small" bool_opt = BoolOption('bO', default=b_dflt_val, desc=b_desc) self.assertEqual(bool_opt.name, 'bO') self.assertEqual(bool_opt.default, b_dflt_val) self.assertEqual(bool_opt.desc, b_desc) # name valType default desc b_check = my_optz.add_option('bO', ValType.BOOL, b_dflt_val, b_desc) self.assertEqual(len(my_optz), 1) self.assertEqual(bool_opt, b_check) # choice lists ---------------------------------------------- # NOTE We should probably require that list elements be of # compatible types. For the moment we just assume that elements # are all strings. # succeeds if default in list of choices ---------- my_size = 2 + self.rng.next_int16(4) # so in [2..5] choice = self.rng.next_file_name(8) choices = [choice] while len(choices) < my_size: if choice not in choices: choices.append(choice) choice = self.rng.next_file_name(8) c_dflt_val = choices[self.rng.next_int16(my_size)] c_desc = 'a list' choice_opt = ChoiceOption('cO', choices, c_dflt_val, c_desc) self.assertEqual(choice_opt.name, 'cO') self.assertEqual(choice_opt.choices, choices) self.assertEqual(choice_opt.default, c_dflt_val) self.assertEqual(choice_opt.desc, "a list") # fails if default is NOT in list of choices ------ my_size = 2 + self.rng.next_int16(4) # so in [2..5] choice = self.rng.next_file_name(8) b_choices = [choice] while len(b_choices) < my_size: if choice not in b_choices: b_choices.append(choice) choice = self.rng.next_file_name(8) dflt_val = self.rng.next_file_name(8) while dflt_val in choices: dflt_val = self.rng.next_file_name(8) try: ChoiceOption('bC', choices, default=dflt_val, desc="a list") self.fail('added default value not in list of choices') except BaseException: pass c_check = my_optz.add_choice_option('cO', choices, c_dflt_val, c_desc) self.assertEqual(len(my_optz), 2) self.assertEqual(choice_opt, c_check) # floats ---------------------------------------------------- f_dflt_val = self.rng.next_real() f_desc = 'bubbly' float_opt = FloatOption('fO', default=f_dflt_val, desc=f_desc) self.assertEqual(float_opt.name, 'fO') self.assertEqual(float_opt.default, f_dflt_val) self.assertEqual(float_opt.desc, f_desc) # name valType default desc f_check = my_optz.add_option('fO', ValType.FLOAT, f_dflt_val, f_desc) self.assertEqual(len(my_optz), 3) self.assertEqual(float_opt, f_check) # ints ------------------------------------------------------ i_dflt_val = self.rng.next_int32() i_desc = 'discrete' int_opt = IntOption('iO', default=i_dflt_val, desc=i_desc) self.assertEqual(int_opt.name, 'iO') self.assertEqual(int_opt.default, i_dflt_val) self.assertEqual(int_opt.desc, i_desc) # name valType default desc i_check = my_optz.add_option('iO', ValType.INT, i_dflt_val, i_desc) self.assertEqual(len(my_optz), 4) self.assertEqual(int_opt, i_check) # lists ----------------------------------------------------- size_val = self.rng.next_int16() # select polarity of size randomly if self.rng.next_boolean(): size_val = - size_val l_desc = "chunky" list_opt = ListOption('lO', default=size_val, desc=l_desc) self.assertEqual(list_opt.name, 'lO') self.assertEqual(list_opt.default, size_val) self.assertEqual(list_opt.size, size_val) self.assertEqual(list_opt.desc, l_desc) zero_val = 0 var_list_opt = ListOption('zO', default=zero_val, desc="skinny") self.assertEqual(var_list_opt.name, 'zO') self.assertEqual(var_list_opt.default, zero_val) self.assertEqual(var_list_opt.desc, "skinny") # name valType default desc l_check = my_optz.add_option('lO', ValType.LIST, size_val, l_desc) self.assertEqual(len(my_optz), 5) self.assertEqual(list_opt, l_check) # strings --------------------------------------------------- s_dflt_val = self.rng.next_file_name(12) s_desc = "wiggly" str_opt = StrOption('sO', default=s_dflt_val, desc=s_desc) self.assertEqual(str_opt.name, 'sO') self.assertEqual(str_opt.default, s_dflt_val) self.assertEqual(str_opt.desc, s_desc) # name valType default desc s_check = my_optz.add_option('sO', ValType.STR, s_dflt_val, s_desc) self.assertEqual(len(my_optz), 6) self.assertEqual(str_opt, s_check)