X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=test%2Fremote_test.py;h=21913b6c959b38802d9043a8d4dc8d0023917143;hb=92e3082199d10add866894e86a9762d79a3536c4;hp=16595002583d885dbbbd5b74827a5fa4c25cc491;hpb=b1be2a0bb8d9a72bf0b868befaf5e966b9c26edb;p=vpp.git diff --git a/test/remote_test.py b/test/remote_test.py index 16595002583..21913b6c959 100644 --- a/test/remote_test.py +++ b/test/remote_test.py @@ -1,11 +1,16 @@ #!/usr/bin/env python +import inspect import os import unittest -import inspect from multiprocessing import Process, Pipe -from pickle import dumps, PicklingError +from pickle import dumps + +import six +from six import moves + from framework import VppTestCase +from enum import Enum class SerializableClassCopy(object): @@ -38,14 +43,16 @@ class RemoteClassAttr(object): def __getattr__(self, attr): if attr[0] == '_': - raise AttributeError + if not (attr.startswith('__') and attr.endswith('__')): + raise AttributeError self._path.append(attr) return self def __setattr__(self, attr, val): if attr[0] == '_': - super(RemoteClassAttr, self).__setattr__(attr, val) - return + if not (attr.startswith('__') and attr.endswith('__')): + super(RemoteClassAttr, self).__setattr__(attr, val) + return self._path.append(attr) self._remote._remote_exec(RemoteClass.SETATTR, self.path_to_str(), True, value=val) @@ -99,7 +106,7 @@ class RemoteClass(Process): self._pipe = Pipe() # pipe for input/output arguments def __repr__(self): - return repr(RemoteClassAttr(self, None)) + return moves.reprlib.repr(RemoteClassAttr(self, None)) def __str__(self): return str(RemoteClassAttr(self, None)) @@ -109,15 +116,17 @@ class RemoteClass(Process): def __getattr__(self, attr): if attr[0] == '_' or not self.is_alive(): - if hasattr(super(RemoteClass, self), '__getattr__'): - return super(RemoteClass, self).__getattr__(attr) - raise AttributeError + if not (attr.startswith('__') and attr.endswith('__')): + if hasattr(super(RemoteClass, self), '__getattr__'): + return super(RemoteClass, self).__getattr__(attr) + raise AttributeError return RemoteClassAttr(self, attr) def __setattr__(self, attr, val): if attr[0] == '_' or not self.is_alive(): - super(RemoteClass, self).__setattr__(attr, val) - return + if not (attr.startswith('__') and attr.endswith('__')): + super(RemoteClass, self).__setattr__(attr, val) + return setattr(RemoteClassAttr(self, None), attr, val) def _remote_exec(self, op, path=None, ret=True, *args, **kwargs): @@ -131,7 +140,7 @@ class RemoteClass(Process): isinstance(val, RemoteClassAttr): mutable_args[i] = val.get_remote_value() args = tuple(mutable_args) - for key, val in kwargs.iteritems(): + for key, val in six.iteritems(kwargs): if isinstance(val, RemoteClass) or \ isinstance(val, RemoteClassAttr): kwargs[key] = val.get_remote_value() @@ -191,7 +200,7 @@ class RemoteClass(Process): def _get_local_repr(self, path): try: obj = self._get_local_object(path) - return repr(obj) + return moves.reprlib.repr(obj) except AttributeError: return None @@ -217,11 +226,27 @@ class RemoteClass(Process): """ if self._serializable(obj): return obj # already serializable + copy = SerializableClassCopy() + + """ + Dictionaries can hold complex values, so we split keys and values into + separate lists and serialize them individually. + """ + if (type(obj) is dict): + copy.type = type(obj) + copy.k_list = list() + copy.v_list = list() + for k, v in obj.items(): + copy.k_list.append(self._make_serializable(k)) + copy.v_list.append(self._make_serializable(v)) + return copy + # copy at least serializable attributes and properties for name, member in inspect.getmembers(obj): if name[0] == '_': # skip private members - continue + if not (name.startswith('__') and name.endswith('__')): + continue if callable(member) and not isinstance(member, property): continue if not self._serializable(member): @@ -241,10 +266,18 @@ class RemoteClass(Process): if type(obj) is tuple: rv = tuple(rv) return rv + elif (isinstance(obj, Enum)): + return obj.value else: return self._make_obj_serializable(obj) def _deserialize_obj(self, obj): + if (hasattr(obj, 'type')): + if obj.type is dict: + _obj = dict() + for k, v in zip(obj.k_list, obj.v_list): + _obj[self._deserialize(k)] = self._deserialize(v) + return _obj return obj def _deserialize(self, obj): @@ -320,7 +353,7 @@ class RemoteVppTestCase(VppTestCase): @classmethod def setUpClass(cls): - # fork new process before clinet connects to VPP + # fork new process before client connects to VPP cls.remote_test = RemoteClass(RemoteVppTestCase) # start remote process @@ -347,12 +380,14 @@ class RemoteVppTestCase(VppTestCase): def __init__(self): super(RemoteVppTestCase, self).__init__("emptyTest") + # Note: __del__ is a 'Finalizer" not a 'Destructor'. + # https://docs.python.org/3/reference/datamodel.html#object.__del__ def __del__(self): if hasattr(self, "vpp"): - cls.vpp.poll() - if cls.vpp.returncode is None: - cls.vpp.terminate() - cls.vpp.communicate() + self.vpp.poll() + if self.vpp.returncode is None: + self.vpp.terminate() + self.vpp.communicate() @classmethod def setUpClass(cls, tempdir): @@ -366,6 +401,10 @@ class RemoteVppTestCase(VppTestCase): super(RemoteVppTestCase, cls).setUpClass() os.environ = orig_env + @classmethod + def tearDownClass(cls): + super(RemoteVppTestCase, cls).tearDownClass() + @unittest.skip("Empty test") def emptyTest(self): """ Do nothing """