Merge "MuranoPL testing mini-framework"

This commit is contained in:
Jenkins 2014-06-25 11:47:41 +00:00 committed by Gerrit Code Review
commit 55abdae728
10 changed files with 378 additions and 1 deletions

View File

@ -54,8 +54,10 @@ class MuranoPlException(Exception):
names = ['{0}.{1}'.format(exception_type.__module__,
exception_type.__name__)]
return MuranoPlException(
result = MuranoPlException(
names, exception.message, stacktrace)
result.original_exception = exception
return result
def _format_name(self):
if not self._names:

View File

View File

View File

@ -0,0 +1,45 @@
# Copyright (c) 2014 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from murano.dsl import helpers
class Object(object):
def __init__(self, name, **kwargs):
self.data = {
'?': {
'type': name,
'id': helpers.generate_id()
}
}
self.data.update(kwargs)
class Ref(object):
def __init__(self, obj):
self.id = obj.data['?']['id']
def build_model(root):
if isinstance(root, dict):
for key, value in root.items():
root[key] = build_model(value)
elif isinstance(root, list):
for i in range(len(root)):
root[i] = build_model(root[i])
elif isinstance(root, Object):
return build_model(root.data)
elif isinstance(root, Ref):
return root.id
return root

View File

@ -0,0 +1,94 @@
# Copyright (c) 2014 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import types
from murano.dsl import dsl_exception
from murano.dsl import executor
from murano.dsl import murano_object
from murano.dsl import results_serializer
from murano.engine import environment
from murano.tests.dsl.foundation import object_model
class Runner(object):
class DslObjectWrapper(object):
def __init__(self, obj, runner):
self._runner = runner
if isinstance(obj, types.StringTypes):
self._object_id = obj
elif isinstance(obj, object_model.Object):
self._object_id = obj.data['?']['id']
elif isinstance(obj, murano_object.MuranoObject):
self._object_id = obj.object_id
elif isinstance(obj, object_model.Ref):
self._object_id = obj.id
else:
raise ValueError(
'obj should be object ID string, MuranoObject or one of '
'object_model helper classes (Object, Ref)')
self._preserve_exception = False
def __getattr__(self, item):
def call(*args, **kwargs):
return self._runner._execute(
item, self._object_id, *args, **kwargs)
if item.startswith('test'):
return call
def __init__(self, model, class_loader):
if isinstance(model, types.StringTypes):
model = object_model.Object(model)
if not isinstance(model, dict):
model = object_model.build_model(model)
if 'Objects' not in model:
model = {'Objects': model}
self.executor = executor.MuranoDslExecutor(
class_loader, environment.Environment())
self.root = self.executor.load(model)
def _execute(self, name, object_id, *args, **kwargs):
obj = self.executor.object_store.get(object_id)
try:
return obj.type.invoke(
name, self.executor, obj,
tuple(list(args) + kwargs.items()))
except dsl_exception.MuranoPlException as e:
if not self.preserve_exception:
original_exception = getattr(e, 'original_exception', None)
if not isinstance(original_exception,
dsl_exception.MuranoPlException):
raise original_exception
raise
def __getattr__(self, item):
if item.startswith('test'):
return getattr(Runner.DslObjectWrapper(self.root, self), item)
def on(self, obj):
return Runner.DslObjectWrapper(obj, self)
@property
def model(self):
return results_serializer.serialize(self.root, self.executor)
@property
def preserve_exception(self):
return self._preserve_exception
@preserve_exception.setter
def preserve_exception(self, value):
self._preserve_exception = value

View File

@ -0,0 +1,56 @@
# Copyright (c) 2014 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import inspect
import os.path
import eventlet.debug
from murano.tests import base
from murano.tests.dsl.foundation import runner
from murano.tests.dsl.foundation import test_class_loader
class DslTestCase(base.MuranoTestCase):
def setUp(self):
super(DslTestCase, self).setUp()
directory = os.path.join(os.path.dirname(
inspect.getfile(self.__class__)), 'meta')
sys_class_loader = test_class_loader.TestClassLoader(
os.path.join(directory, '../../../../meta/io.murano/Classes'),
'murano.io')
self._class_loader = test_class_loader.TestClassLoader(
directory, 'tests', sys_class_loader)
self.register_function(
lambda data: self._traces.append(data()), 'trace')
self._traces = []
def new_runner(self, model):
return runner.Runner(model, self.class_loader)
@property
def traces(self):
return self._traces
@property
def class_loader(self):
return self._class_loader
def register_function(self, func, name):
self.class_loader.register_function(func, name)
@classmethod
def setUpClass(cls):
eventlet.debug.hub_exceptions(False)

View File

@ -0,0 +1,85 @@
# Copyright (c) 2014 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import glob
import os.path
import yaml
from murano.dsl import class_loader
from murano.dsl import exceptions
from murano.dsl import murano_package
from murano.dsl import namespace_resolver
from murano.engine.system import yaql_functions
from murano.engine import yaql_yaml_loader
class TestClassLoader(class_loader.MuranoClassLoader):
def __init__(self, directory, package_name, parent_loader=None):
self._classes = {}
self._package = murano_package.MuranoPackage()
self._package.name = package_name
self._parent = parent_loader
self._build_index(directory)
self._functions = {}
super(TestClassLoader, self).__init__()
def find_package_name(self, class_name):
if class_name in self._classes:
return self._package.name
if self._parent:
return self._parent.find_package_name(class_name)
return None
def load_package(self, class_name):
return self._package
def load_definition(self, name):
try:
return self._classes[name]
except KeyError:
if self._parent:
return self._parent.load_definition(name)
raise exceptions.NoClassFound(name)
def _build_index(self, directory):
yamls = glob.glob(os.path.join(directory, '*.yaml'))
for class_def_file in yamls:
self._load_class(class_def_file)
def _load_class(self, class_def_file):
with open(class_def_file) as stream:
data = yaml.load(stream, yaql_yaml_loader.YaqlYamlLoader)
if 'Name' not in data:
return
for name, method in (data.get('Methods') or data.get(
'Workflow') or {}).iteritems():
if name.startswith('test'):
method['Usage'] = 'Action'
ns = namespace_resolver.NamespaceResolver(data.get('Namespaces', {}))
class_name = ns.resolve_name(data['Name'])
self._classes[class_name] = data
def create_root_context(self):
context = super(TestClassLoader, self).create_root_context()
yaql_functions.register(context)
for name, func in self._functions.iteritems():
context.register_function(func, name)
return context
def register_function(self, func, name):
self._functions[name] = func

View File

@ -0,0 +1,29 @@
Name: SampleClass1
Properties:
stringProperty:
Contract: $.string().notNull()
classProperty:
Contract: $.class(SampleClass2).notNull()
Workflow:
testTrace:
Arguments:
- intArg:
Contract: $.int().notNull()
Body:
- trace($intArg)
- trace($.stringProperty)
- trace($.classProperty.class2Property)
testException:
Body:
- raiseException()
testReturn:
Arguments:
- intArg:
Contract: $.int().notNull()
Body:
Return: $intArg

View File

@ -0,0 +1,6 @@
Name: SampleClass2
Properties:
class2Property:
Contract: $.string().notNull()

View File

@ -0,0 +1,60 @@
# Copyright (c) 2014 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from murano.dsl import dsl_exception
from murano.tests.dsl.foundation import object_model as om
from murano.tests.dsl.foundation import test_case
class TestExecution(test_case.DslTestCase):
def _load(self):
return self.new_runner(
om.Object('SampleClass1', stringProperty='STRING',
classProperty=om.Object(
'SampleClass2', class2Property='ANOTHER_STRING')))
def test_load(self):
self._load()
def test_not_load(self):
def try_load():
self.new_runner(om.Object('SampleClass1'))
self.assertRaises(TypeError, try_load)
def test_trace(self):
runner = self._load()
self.assertEqual(self.traces, [])
runner.testTrace(123)
self.assertEqual(self.traces, [123, 'STRING', 'ANOTHER_STRING'])
runner.testTrace(321)
self.assertEqual(self.traces, [
123, 'STRING', 'ANOTHER_STRING',
321, 'STRING', 'ANOTHER_STRING'])
def test_exception(self):
class CustomException(Exception):
pass
def raise_exception():
raise CustomException()
self.register_function(raise_exception, 'raiseException')
runner = self._load()
self.assertRaises(CustomException, runner.testException)
runner.preserve_exception = True
self.assertRaises(dsl_exception.MuranoPlException,
runner.testException)
def test_return(self):
self.assertEqual(3, self._load().testReturn(3))