Code refactoring and improvements for MuranoPL testing mini-framework

* Stack-traces of caught exceptions are no longer printed in Python 2.6
* No more warning on Exception.message being deprecated
* Loaded YAML files are cached instead of being re-read for each test
   greatly increasing performance without side effects because class-loader
   is still new on each test
* object_model helper objects can be used as a test method arguments
* Path to core library classes was relative to the test. That prevented grouping
   tests into sub-folders
* Preserve original stack-trace for Python exceptions thrown by the engine
* Ability to delete (clear) traces (produced by calls to trace(...) in MuranoPL code)
* Code refactoring

Change-Id: Ifab8460d3a188c6e05edd682a302e2a8a6dd0425
This commit is contained in:
Stan Lagun
2014-07-02 18:04:34 +04:00
parent 4245d3e219
commit 803b5f83e4
6 changed files with 66 additions and 31 deletions

View File

@@ -59,16 +59,17 @@ class MuranoClassLoader(object):
namespaces = data.get('Namespaces', {}) namespaces = data.get('Namespaces', {})
ns_resolver = namespace_resolver.NamespaceResolver(namespaces) ns_resolver = namespace_resolver.NamespaceResolver(namespaces)
class_parents = data.get('Extends') parent_class_names = data.get('Extends')
if class_parents: parent_classes = []
if not isinstance(class_parents, types.ListType): if parent_class_names:
class_parents = [class_parents] if not isinstance(parent_class_names, types.ListType):
for i, parent_name in enumerate(class_parents): parent_class_names = [parent_class_names]
for parent_name in parent_class_names:
full_name = ns_resolver.resolve_name(parent_name) full_name = ns_resolver.resolve_name(parent_name)
class_parents[i] = self.get_class(full_name) parent_classes.append(self.get_class(full_name))
type_obj = murano_class.MuranoClass(self, ns_resolver, name, type_obj = murano_class.MuranoClass(self, ns_resolver, name,
package, class_parents) package, parent_classes)
properties = data.get('Properties', {}) properties = data.get('Properties', {})
for property_name, property_spec in properties.iteritems(): for property_name, property_spec in properties.iteritems():

View File

@@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import sys
import murano.dsl.yaql_functions as yaql_functions import murano.dsl.yaql_functions as yaql_functions
@@ -55,8 +57,10 @@ class MuranoPlException(Exception):
exception_type.__name__)] exception_type.__name__)]
result = MuranoPlException( result = MuranoPlException(
names, exception.message, stacktrace) names, str(exception), stacktrace)
exc_type, exc_value, exc_traceback = sys.exc_info()
result.original_exception = exception result.original_exception = exception
result.original_traceback = exc_traceback
return result return result
def _format_name(self): def _format_name(self):

View File

@@ -25,10 +25,18 @@ class Object(object):
} }
self.data.update(kwargs) self.data.update(kwargs)
@property
def id(self):
return self.data['?']['id']
class Ref(object): class Ref(object):
def __init__(self, obj): def __init__(self, obj):
self.id = obj.data['?']['id'] self._id = obj.id
@property
def id(self):
return self._id
def build_model(root): def build_model(root):

View File

@@ -13,6 +13,7 @@
# under the License. # under the License.
import sys
import types import types
from murano.dsl import dsl_exception from murano.dsl import dsl_exception
@@ -29,15 +30,13 @@ class Runner(object):
self._runner = runner self._runner = runner
if isinstance(obj, types.StringTypes): if isinstance(obj, types.StringTypes):
self._object_id = obj self._object_id = obj
elif isinstance(obj, object_model.Object): elif isinstance(obj, (object_model.Object, object_model.Ref)):
self._object_id = obj.data['?']['id'] self._object_id = obj.id
elif isinstance(obj, murano_object.MuranoObject): elif isinstance(obj, murano_object.MuranoObject):
self._object_id = obj.object_id self._object_id = obj.object_id
elif isinstance(obj, object_model.Ref):
self._object_id = obj.id
else: else:
raise ValueError( raise ValueError(
'obj should be object ID string, MuranoObject or one of ' 'obj must be object ID string, MuranoObject or one of '
'object_model helper classes (Object, Ref)') 'object_model helper classes (Object, Ref)')
self._preserve_exception = False self._preserve_exception = False
@@ -58,32 +57,46 @@ class Runner(object):
self.executor = executor.MuranoDslExecutor( self.executor = executor.MuranoDslExecutor(
class_loader, environment.Environment()) class_loader, environment.Environment())
self.root = self.executor.load(model) self._root = self.executor.load(model)
def _execute(self, name, object_id, *args, **kwargs): def _execute(self, name, object_id, *args, **kwargs):
obj = self.executor.object_store.get(object_id) obj = self.executor.object_store.get(object_id)
try: try:
return obj.type.invoke( final_args = []
name, self.executor, obj, for arg in args:
tuple(list(args) + kwargs.items())) if isinstance(arg, object_model.Object):
arg = object_model.build_model(arg)
final_args.append(arg)
for name, arg in kwargs.iteritems():
if isinstance(arg, object_model.Object):
arg = object_model.build_model(arg)
final_args.append({name: arg})
return obj.type.invoke(name, self.executor, obj, tuple(final_args))
except dsl_exception.MuranoPlException as e: except dsl_exception.MuranoPlException as e:
if not self.preserve_exception: if not self.preserve_exception:
original_exception = getattr(e, 'original_exception', None) original_exception = getattr(e, 'original_exception', None)
if not isinstance(original_exception, if original_exception and not isinstance(
dsl_exception.MuranoPlException): original_exception, dsl_exception.MuranoPlException):
raise original_exception exc_traceback = getattr(
e, 'original_traceback', None) or sys.exc_info()[2]
raise type(original_exception), original_exception, \
exc_traceback
raise raise
def __getattr__(self, item): def __getattr__(self, item):
if item.startswith('test'): if item.startswith('test'):
return getattr(Runner.DslObjectWrapper(self.root, self), item) return getattr(Runner.DslObjectWrapper(self._root, self), item)
def on(self, obj): def on(self, obj):
return Runner.DslObjectWrapper(obj, self) return Runner.DslObjectWrapper(obj, self)
@property @property
def model(self): def root(self):
return results_serializer.serialize(self.root, self.executor) return self._root
@property
def serialized_model(self):
return results_serializer.serialize(self._root, self.executor)
@property @property
def preserve_exception(self): def preserve_exception(self):

View File

@@ -28,14 +28,17 @@ class DslTestCase(base.MuranoTestCase):
super(DslTestCase, self).setUp() super(DslTestCase, self).setUp()
directory = os.path.join(os.path.dirname( directory = os.path.join(os.path.dirname(
inspect.getfile(self.__class__)), 'meta') inspect.getfile(self.__class__)), 'meta')
root_meta_directory = os.path.join(
os.path.dirname(__file__), '../../../../meta')
sys_class_loader = test_class_loader.TestClassLoader( sys_class_loader = test_class_loader.TestClassLoader(
os.path.join(directory, '../../../../meta/io.murano/Classes'), os.path.join(root_meta_directory, 'io.murano/Classes'),
'murano.io') 'murano.io')
self._class_loader = test_class_loader.TestClassLoader( self._class_loader = test_class_loader.TestClassLoader(
directory, 'tests', sys_class_loader) directory, 'tests', sys_class_loader)
self.register_function( self.register_function(
lambda data: self._traces.append(data()), 'trace') lambda data: self._traces.append(data()), 'trace')
self._traces = [] self._traces = []
eventlet.debug.hub_exceptions(False)
def new_runner(self, model): def new_runner(self, model):
return runner.Runner(model, self.class_loader) return runner.Runner(model, self.class_loader)
@@ -44,13 +47,13 @@ class DslTestCase(base.MuranoTestCase):
def traces(self): def traces(self):
return self._traces return self._traces
@traces.deleter
def traces(self):
self._traces = []
@property @property
def class_loader(self): def class_loader(self):
return self._class_loader return self._class_loader
def register_function(self, func, name): def register_function(self, func, name):
self.class_loader.register_function(func, name) self.class_loader.register_function(func, name)
@classmethod
def setUpClass(cls):
eventlet.debug.hub_exceptions(False)

View File

@@ -26,12 +26,18 @@ from murano.engine import yaql_yaml_loader
class TestClassLoader(class_loader.MuranoClassLoader): class TestClassLoader(class_loader.MuranoClassLoader):
_classes_cache = {}
def __init__(self, directory, package_name, parent_loader=None): def __init__(self, directory, package_name, parent_loader=None):
self._classes = {}
self._package = murano_package.MuranoPackage() self._package = murano_package.MuranoPackage()
self._package.name = package_name self._package.name = package_name
self._parent = parent_loader self._parent = parent_loader
self._build_index(directory) if directory in TestClassLoader._classes_cache:
self._classes = TestClassLoader._classes_cache[directory]
else:
self._classes = {}
self._build_index(directory)
TestClassLoader._classes_cache[directory] = self._classes
self._functions = {} self._functions = {}
super(TestClassLoader, self).__init__() super(TestClassLoader, self).__init__()