def test_protobuf_copy(self): """ Copy to and back from a Protobuf type and ensure the original object is the same as the copy. :return: """ logging.info("Test copy to/from a protobuf type") message1 = Message1(field='Message-687', state=State.S2, tasks=[ Task(task_name="Task-1", task_id=1), Task(task_name="Task-2", task_id=2) ], strings=["A", "2", "c", "4", "D"], _double=3.142, _float=6.284, _int32=496, _int64=8128, _bool=True, _bytes=b'A Byte String') pb_message1 = PBMessage1() actual = DCCopy.deep_corresponding_copy(message1, pb_message1) try: actual.SerializeToString() except Exception: self.fail("Profbuf object invalid after copy as cannot serialize") message2 = Message1() final = DCCopy.deep_corresponding_copy(actual, message2) self.assertEqual(final, message1) return
def test_enum(self): logging.info("Test enum specifics") # Pass case src = Z(AnEnumInt.S1, AnEnumStr.S2) tgt = Z(AnEnumInt.S0, AnEnumStr.S0) tgt = DCCopy.deep_corresponding_copy(src, tgt) self.assertEqual(tgt, src) # Pass case reverse as it's valid to copy a str Enum over an Int Enum src = Z(AnEnumInt.S1, AnEnumStr.S2) tgt = Z(AnEnumStr.S0, AnEnumInt.S0) # Reverse int/str type Enum tgt = DCCopy.deep_corresponding_copy(src, tgt) self.assertEqual(tgt, src) return
def test_enum_to_value_type(self): # Pass case, target types align with value type src = Z(AnEnumInt.S2, AnEnumStr.S1) tgt = Z(int(3142), "3142") tgt = DCCopy.deep_corresponding_copy(src, tgt) self.assertEqual(tgt._a, src._a.value) self.assertEqual(tgt._b, src._b.value) # Fail case target type not same as value type src = Z(AnEnumInt.S2, AnEnumStr.S1) tgt = Z("3142", int(3142)) with self.assertRaises(TypeError): _ = DCCopy.deep_corresponding_copy(src=src, tgt=tgt) return
def test_simple(self): logging.info("Test simple base types and collections") scenarios = [[int(678), int(0), int(678)], [[1, 2], [6, 7], [1, 2]], [[1, 2], [6, 7, 8], [1, 2, 8]], [[1, 2, 3, 4], [6, 7, 8], [1, 2, 3, 4]], [dict(), dict(), dict()], [{ 1: 2 }, { 1: 3 }, { 1: 2 }], [{ 1: 2, 2: 3 }, { 1: 2, 2: 5, 3: 4 }, { 1: 2, 2: 3, 3: 4 }], [{ 1: [Z({ 1: 2, 2: 3 }, [3, 4, 5])], 3: "4", 4: True }, { 1: [Z({ 1: 3, 3: 4 }, [1, 4, 5, 4])], 3: "5", 4: False, 7: AnEnumInt.S1 }, { 1: [Z({ 1: 2, 2: 3, 3: 4 }, [3, 4, 5, 4])], 3: "4", 4: True, 7: AnEnumInt.S1 }], [ Z(Z(Z(Z(Z(1, 2), 3), 4), 5), 6), Z(Z(Z(Z(Z(9, 8), 7), 6), 5), 4), Z(Z(Z(Z(Z(1, 2), 3), 4), 5), 6) ], [[[1, 2], 3], [[4, 5], 6], [[1, 2], 3]], [[[[[[float(0)]]]]], [[[[[float(1)]]]]], [[[[[float(0)]]]]]]] for scenario in scenarios: src, tgt, expected = scenario self.assertEqual(expected, DCCopy.deep_corresponding_copy(src=src, tgt=tgt)) return
def test_enum_with_members(self): src = Z(AnEnumWithMembers.S0, AnEnumInt.S0) tgt = Z(AnEnumWithMembers.S1, AnEnumInt.S2) tgt = DCCopy.deep_corresponding_copy(src, tgt) self.assertEqual(tgt, src) self.assertEqual((1 * 2), src._a.func) return
def test_simple_obj(self): logging.info("Test simple, un-nested object") src = Z(A(1, 2, 3, 4), B(5, 6, 7, 8)) tgt = Z(A(10, 12, 13, 14), B(15, 16, 17, 18)) tgt = DCCopy.deep_corresponding_copy(src=src, tgt=tgt) self.assertEqual(src, tgt) return
def test_prune_fails(self): logging.info("Test prune failure cases") z1a = 1 z1b = 2 z2a = 3 z2b = 4 scenarios = [1, [1], bool, [bool], 3.142, [3.142], ['_b', 123]] for p in scenarios: z1 = Z(z1a, z1b) z2 = Z(z2a, z2b) with self.assertRaises(TypeError): _ = DCCopy.deep_corresponding_copy(src=z1, tgt=z2, prune=p) return
def serialize(self, src: object) -> bytes: """ Take the given object and serialise it using it's registered protobuf partner object :param src: The object to be serialised. The object type must have been registered with its protobuf partner. :return: """ obj_type = type(src).__name__ if obj_type not in self._transform_map: raise ValueError("Object of type {} has no registered serializer".format(obj_type)) tgt = DCCopy.deep_corresponding_copy(src=src, tgt=self._transform_map[obj_type][ProtoCopy._PROTOBUF_TYPE]()) return tgt.SerializeToString()
def test_fails(self): logging.info("Test failure cases for type mismatch") scenarios = [[int(0), float(0)], [AnEnumInt.S1, bool(True)], [AnEnumStr.S2, int(0)], [list(), dict()], [[int(0), float(0)], [int(0), int(0)]], [list(), Z(1, 2)], [Z(1.0, 2), Z(3, 4)], [Z([1, 2, 3], "Hi"), Z([1, 2.0], "Lo")]] for scenario in scenarios: src, tgt = scenario with self.assertRaises(TypeError): _ = DCCopy.deep_corresponding_copy(src=src, tgt=tgt) return
def test_prune(self): logging.info("Test prune sunshine cases") z1a = 1 z1b = 2 z2a = 3 z2b = 4 scenarios = [[None, z1a, z1b], [[None], z1a, z1b], ['', z1a, z1b], ['_a', z2a, z1b], [['a', 'a_', 'x', 'b', 'b_'], z1a, z1b], [['_a'], z2a, z1b], [['_a', '_a'], z2a, z1b]] for p, expected_a, expected_b in scenarios: z1 = Z(z1a, z1b) z2 = Z(z2a, z2b) actual = DCCopy.deep_corresponding_copy(src=z1, tgt=z2, prune=p) self.assertEqual(actual._a, expected_a) self.assertEqual(actual._b, expected_b) return
def test_fields_rev(self): logging.info( "Test simple object to object with only partial member field overlap B -> A" ) # B -> A src = B(4, 5.0, "6", AnEnumInt.S2) tgt = A(1, 2.0, "3", AnEnumInt.S0) tgt_unmod = A(1, 2.0, "3", AnEnumInt.S0) actual = DCCopy.deep_corresponding_copy(src, tgt) self.assertEqual(src._a, actual._a) # Corresponding & updated self.assertEqual( getattr(tgt_unmod, "_{}__b".format(type(tgt_unmod).__name__)), getattr(actual, "_{}__b".format( type(actual).__name__))) # .__b hidden & not updated self.assertEqual(tgt_unmod._ca, actual._ca) # Not corresponding & not updated self.assertEqual(src.d, actual.d) # Corresponding & updated self.assertEqual(tgt._ae, actual._ae) # Not Corresponding & const on __init__ return
def deserialize(self, serialized_src: bytes, target_type: Type) -> object: """ Take the given ByteString and deserialize it as the given type :param serialized_src: The required object as a ByteString :param target_type: The type of object to deserialize as. This must have been registered() with it't protobuf partner. :return: """ if serialized_src is None or len(serialized_src) == 0 or not isinstance(serialized_src, bytes): raise ValueError("serialized_src must be a non zero length ByteString") obj_type = target_type.__name__ if obj_type not in self._transform_map: raise ValueError("Object of type {} has no registered deserializer".format(target_type)) pbt = self._transform_map[obj_type][ProtoCopy._PROTOBUF_TYPE]() tgt = self._transform_map[obj_type][ProtoCopy._OBJECT_TYPE]() pbt.ParseFromString(serialized_src) tgt = DCCopy.deep_corresponding_copy(src=pbt, tgt=tgt) return tgt
def test_complex_nested(self): logging.info( "Test complex nested object, with base types, collections, Enum etc" ) actual = DCCopy.deep_corresponding_copy(X(), Y()) expected = X() target_unmodified = Y() # Top Level - with over lap self.assertEqual(expected._n, actual._n) self.assertEqual(expected._an_enum, actual._an_enum) # Top Level - no over lap or hidden self.assertEqual(target_unmodified._y, actual._y) # ._y not shared, should not be overridden self.assertEqual( getattr(target_unmodified, "_{}__hidden".format(type(target_unmodified).__name__)), getattr(actual, "_{}__hidden".format( type(actual).__name__))) # ._hidden should be same # Contained object self.assertEqual(expected._z._a, actual._z._a) self.assertEqual(expected._z._b, actual._z._b) # Inherited self.assertEqual(expected._base_s, actual._base_s) self.assertEqual(expected._base_b, actual._base_b) self.assertEqual(expected._base_n, actual._base_n) self.assertEqual(expected._base_i, actual._base_i) self.assertEqual(expected._base_f, actual._base_f) # Simple Lists self.assertEqual(target_unmodified._l_1, actual._l_1) self.assertEqual(expected._l_2, actual._l_2) self.assertEqual(expected._l_3, actual._l_3) self.assertEqual([.7, .9, .11, .12], actual._l_4) self.assertEqual(expected._l_5, actual._l_5) self.assertEqual([[1, 2, 3], .4, .13], actual._l_6) # Heterogeneous lists with objects self.assertEqual(expected._l_7, actual._l_7) self.assertEqual( [False, [Z(11, 13), Z(22, 23), Z(24, 25)]], actual._l_8) # Simple dictionaries self.assertEqual(expected._d_1, actual._d_1) self.assertEqual(expected._d_2, actual._d_2) # Complex dictionaries self.assertEqual(expected._d_3, actual._d_3) self.assertEqual(target_unmodified._d_4, actual._d_4) self.assertEqual( { '1': 1, '2': True, '3': 1.2, '4': ['a', 1, True, 'xray'], '5': Z(5, 6), '6': 'added' }, actual._d_5) return