Files
deb-murano/murano/tests/test_engine.py
Stan Lagun 2fdb1904ea Maintain virtual MuranoPL stack trace
Adds information to executing MuranoPL context that allows
to get currently executed method and instruction with its
location within source YAML file.

All this information can be collected to one stack trace using
stackTrace() function and string-formatted using formatStackTrace()

Change-Id: Ic0aa4e0b2a87d71f76945feb749ecfb0f5a0d299
Implements: blueprint muranopl-stack-traces
2014-06-12 01:40:11 +04:00

515 lines
19 KiB
Python

# 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 re
import mock
import yaql
import murano.dsl.exceptions as exceptions
import murano.dsl.helpers as helpers
import murano.dsl.murano_class as murano_class
import murano.dsl.murano_object as murano_object
import murano.dsl.namespace_resolver as ns_resolver
import murano.dsl.typespec as typespec
import murano.dsl.yaql_expression as yaql_expression
from murano.tests import base
ROOT_CLASS = 'io.murano.Object'
class TestNamespaceResolving(base.MuranoTestCase):
def setUp(self):
super(TestNamespaceResolving, self).setUp()
def test_fails_w_empty_name(self):
resolver = ns_resolver.NamespaceResolver({'=': 'com.example.murano'})
self.assertRaises(ValueError, resolver.resolve_name, None)
def test_fails_w_unknown_prefix(self):
resolver = ns_resolver.NamespaceResolver({'=': 'com.example.murano'})
name = 'unknown_prefix:example.murano'
self.assertRaises(KeyError, resolver.resolve_name, name)
def test_fails_w_prefix_wo_name(self):
resolver = ns_resolver.NamespaceResolver({'=': 'com.example.murano'})
name = 'sys:'
self.assertRaises(NameError, resolver.resolve_name, name)
def test_fails_w_excessive_prefix(self):
ns = {'sys': 'com.example.murano.system'}
resolver = ns_resolver.NamespaceResolver(ns)
invalid_name = 'sys:excessive_ns:muranoResource'
self.assertRaises(NameError, resolver.resolve_name, invalid_name)
def test_cuts_empty_prefix(self):
resolver = ns_resolver.NamespaceResolver({'=': 'com.example.murano'})
# name without prefix delimiter
name = 'some.arbitrary.name'
resolved_name = resolver.resolve_name(':' + name)
self.assertEqual(name, resolved_name)
def test_resolves_specified_ns_prefix(self):
ns = {'sys': 'com.example.murano.system'}
resolver = ns_resolver.NamespaceResolver(ns)
short_name, full_name = 'sys:File', 'com.example.murano.system.File'
resolved_name = resolver.resolve_name(short_name)
self.assertEqual(full_name, resolved_name)
def test_resolves_current_ns(self):
resolver = ns_resolver.NamespaceResolver({'=': 'com.example.murano'})
short_name, full_name = 'Resource', 'com.example.murano.Resource'
resolved_name = resolver.resolve_name(short_name)
self.assertEqual(full_name, resolved_name)
def test_resolves_explicit_base(self):
resolver = ns_resolver.NamespaceResolver({'=': 'com.example.murano'})
resolved_name = resolver.resolve_name('Resource', relative='com.base')
self.assertEqual('com.base.Resource', resolved_name)
def test_resolves_explicit_base_w_empty_namespaces(self):
resolver = ns_resolver.NamespaceResolver({})
resolved_name = resolver.resolve_name('File', 'com.base')
self.assertEqual('com.base.File', resolved_name)
def test_resolves_w_empty_namespaces(self):
resolver = ns_resolver.NamespaceResolver({})
resolved_name = resolver.resolve_name('Resource')
self.assertEqual('Resource', resolved_name)
class Bunch(object):
def __init__(self, **kwargs):
super(Bunch, self).__init__()
for key, value in kwargs.iteritems():
setattr(self, key, value)
class TestClassesManipulation(base.MuranoTestCase):
resolver = mock.Mock(resolve_name=lambda name: name)
def setUp(self):
super(TestClassesManipulation, self).setUp()
def test_class_name(self):
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
self.assertEqual(ROOT_CLASS, cls.name)
def test_class_namespace_resolver(self):
resolver = ns_resolver.NamespaceResolver({})
cls = murano_class.MuranoClass(None, resolver, ROOT_CLASS, None)
self.assertEqual(resolver, cls.namespace_resolver)
def test_root_class_has_no_parents(self):
root_class = murano_class.MuranoClass(
None, self.resolver, ROOT_CLASS, ['You should not see me!'])
self.assertEqual([], root_class.parents)
def test_non_root_class_resolves_parents(self):
root_cls = murano_class.MuranoClass(None, self.resolver,
ROOT_CLASS, None)
class_loader = mock.Mock(get_class=lambda name: root_cls)
desc_cl1 = murano_class.MuranoClass(class_loader, self.resolver,
'Obj', None)
desc_cl2 = murano_class.MuranoClass(
class_loader, self.resolver, 'Obj', None, [root_cls])
self.assertEqual([root_cls], desc_cl1.parents)
self.assertEqual([root_cls], desc_cl2.parents)
def test_class_initial_properties(self):
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
self.assertEqual([], cls.properties)
def test_fails_add_incompatible_property_to_class(self):
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
kwargs = {'name': 'sampleProperty', 'property_typespec': {}}
self.assertRaises(TypeError, cls.add_property, **kwargs)
def test_add_property_to_class(self):
self.skipTest("FIXME!")
prop = typespec.PropertySpec({'Default': 1}, self.resolver)
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
cls.add_property('firstPrime', prop)
class_properties = cls.properties
class_property = cls.get_property('firstPrime')
self.assertEqual(['firstPrime'], class_properties)
self.assertEqual(prop, class_property)
def test_class_property_search(self):
self.skipTest("FIXME!")
void_prop = typespec.PropertySpec({'Default': 'Void'}, self.resolver)
mother_prop = typespec.PropertySpec({'Default': 'Mother'},
self.resolver)
father_prop = typespec.PropertySpec({'Default': 'Father'},
self.resolver)
child_prop = typespec.PropertySpec({'Default': 'Child'},
self.resolver)
root = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
mother = murano_class.MuranoClass(None, self.resolver,
'Mother', [root])
father = murano_class.MuranoClass(None, self.resolver,
'Father', [root])
child = murano_class.MuranoClass(
None, self.resolver, 'Child', [mother, father])
root.add_property('Void', void_prop)
mother.add_property('Mother', mother_prop)
father.add_property('Father', father_prop)
child.add_property('Child', child_prop)
self.assertEqual(child_prop, child.find_property('Child'))
self.assertEqual(father_prop, child.find_property('Father'))
self.assertEqual(mother_prop, child.find_property('Mother'))
self.assertEqual(void_prop, child.find_property('Void'))
def test_class_is_compatible(self):
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
descendant_cls = murano_class.MuranoClass(
None, self.resolver, 'DescendantCls', None, [cls])
obj = mock.Mock(spec=murano_object.MuranoObject)
descendant_obj = mock.Mock(spec=murano_object.MuranoObject)
obj.type = cls
descendant_obj.type = descendant_cls
descendant_obj.parents = [obj]
self.assertTrue(cls.is_compatible(obj))
self.assertTrue(cls.is_compatible(descendant_obj))
self.assertFalse(descendant_cls.is_compatible(obj))
def test_new_method_calls_initialize(self):
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
cls.object_class = mock.Mock()
with mock.patch('inspect.getargspec') as spec_mock:
spec_mock.return_value = Bunch(args=())
obj = cls.new(None, None, None, {})
self.assertTrue(obj.initialize.called)
def test_new_method_not_calls_initialize(self):
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
cls.object_class = mock.Mock()
obj = cls.new(None, None, None)
self.assertFalse(obj.initialize.called)
class TestObjectsManipulation(base.MuranoTestCase):
def setUp(self):
super(TestObjectsManipulation, self).setUp()
self.resolver = mock.Mock(resolve_name=lambda name: name)
self.cls = mock.Mock()
self.cls.name = ROOT_CLASS
self.cls.parents = []
def test_object_valid_type_instantiation(self):
obj = murano_object.MuranoObject(self.cls, None, None, None)
self.assertEqual(self.cls, obj.type)
def test_object_own_properties_initialization(self):
# TODO: there should be test for initializing first non-dependent
# object properties, then the dependent ones (given as
# YAQL-expressions)
pass
def test_object_parent_properties_initialization(self):
root = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
cls = murano_class.MuranoClass(None, self.resolver,
'SomeClass', None, [root])
root.new = mock.Mock()
init_kwargs = {'theArg': 0}
obj = murano_object.MuranoObject(cls, None, None, None)
expected_calls = [mock.call().initialize(**init_kwargs)]
obj.initialize(**init_kwargs)
# each object should also initialize his parent objects
self.assertEqual(expected_calls, root.new.mock_calls[1:])
def test_object_id(self):
_id = 'some_id'
patch_at = 'murano.dsl.helpers.generate_id'
obj = murano_object.MuranoObject(self.cls, None, None, None,
object_id=_id)
with mock.patch(patch_at) as gen_id_mock:
gen_id_mock.return_value = _id
obj1 = murano_object.MuranoObject(self.cls, None, None, None)
self.assertEqual(_id, obj.object_id)
self.assertEqual(_id, obj1.object_id)
def test_parent_obj(self):
parent = mock.Mock()
obj = murano_object.MuranoObject(self.cls, parent, None, None)
self.assertEqual(parent, obj.parent)
def test_fails_internal_property_access(self):
self.skipTest("FIXME!")
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
cls.add_property('__hidden',
typespec.PropertySpec({'Default': 10}, self.resolver))
obj = murano_object.MuranoObject(cls, None, None, None)
self.assertRaises(AttributeError, lambda: obj.__hidden)
def test_proper_property_access(self):
self.skipTest("FIXME!")
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
cls.add_property('someProperty',
typespec.PropertySpec({'Default': 0}, self.resolver))
obj = cls.new(None, None, None, {})
self.assertEqual(0, obj.someProperty)
def test_parent_class_property_access(self):
self.skipTest("FIXME!")
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
child_cls = murano_class.MuranoClass(None, self.resolver,
'Child', [cls])
cls.add_property('anotherProperty',
typespec.PropertySpec({'Default': 0}, self.resolver))
obj = child_cls.new(None, None, None, {})
self.assertEqual(0, obj.anotherProperty)
def test_fails_on_parents_property_collision(self):
self.skipTest("FIXME!")
root = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
mother = murano_class.MuranoClass(None, self.resolver,
'Mother', [root])
father = murano_class.MuranoClass(None, self.resolver,
'Father', [root])
child = murano_class.MuranoClass(
None, self.resolver, 'Child', [mother, father])
mother.add_property(
'conflictProp',
typespec.PropertySpec({'Default': 0}, self.resolver))
father.add_property(
'conflictProp',
typespec.PropertySpec({'Default': 0}, self.resolver))
obj = child.new(None, None, None, {})
self.assertRaises(LookupError, lambda: obj.conflictProp)
def test_fails_setting_undeclared_property(self):
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
obj = cls.new(None, None, None, {})
self.assertRaises(AttributeError, obj.set_property, 'newOne', 10)
def test_set_undeclared_property_as_internal(self):
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
obj = cls.new(None, None, None, {})
obj.cast = mock.Mock(return_value=obj)
prop_value = 10
obj.set_property('internalProp', prop_value, caller_class=cls)
resolved_value = obj.get_property('internalProp', caller_class=cls)
self.assertEqual(prop_value, resolved_value)
def test_fails_forbidden_set_property(self):
self.skipTest("FIXME!")
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
cls.add_property('someProperty',
typespec.PropertySpec({'Default': 0}, self.resolver))
cls.is_compatible = mock.Mock(return_value=False)
obj = cls.new(None, None, None, {})
self.assertRaises(exceptions.NoWriteAccess, obj.set_property,
'someProperty', 10, caller_class=cls)
def test_set_property(self):
self.skipTest("FIXME!")
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
cls.add_property('someProperty',
typespec.PropertySpec({'Default': 0}, self.resolver))
obj = cls.new(None, None, None, {})
with mock.patch('yaql.context.Context'):
with mock.patch('murano.engine.helpers') as helpers_mock:
helpers_mock.evaluate = lambda val, ctx, _: val
obj.set_property('someProperty', 10)
self.assertEqual(10, obj.someProperty)
def test_set_parent_property(self):
self.skipTest("FIXME!")
root = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
cls = murano_class.MuranoClass(None, self.resolver,
'SomeClass', [root])
root.add_property('rootProperty',
typespec.PropertySpec({'Default': 0}, self.resolver))
obj = cls.new(None, None, None, {})
with mock.patch('murano.engine.helpers') as helpers_mock:
with mock.patch('yaql.context.Context'):
helpers_mock.evaluate = lambda val, ctx, _: val
obj.set_property('rootProperty', 20)
self.assertEqual(20, obj.rootProperty)
def test_object_up_cast(self):
self.skipTest("FIXME!")
root = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
root_alt = murano_class.MuranoClass(None, self.resolver, 'RootAlt', [])
cls = murano_class.MuranoClass(
None, self.resolver, 'SomeClass', [root, root_alt])
root_obj = root.new(None, None, None)
cls_obj = cls.new(None, None, None)
root_obj_casted2root = root_obj.cast(root)
cls_obj_casted2root = cls_obj.cast(root)
cls_obj_casted2root_alt = cls_obj.cast(root_alt)
self.assertEqual(root_obj, root_obj_casted2root)
# each object creates an _internal_ parent objects hierarchy,
# so direct comparison of objects is not possible
self.assertEqual(root, cls_obj_casted2root.type)
self.assertEqual(root_alt, cls_obj_casted2root_alt.type)
def test_fails_object_down_cast(self):
root = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
cls = murano_class.MuranoClass(
None, self.resolver, 'SomeClass', None, [root])
root_obj = root.new(None, None, None)
self.assertRaises(TypeError, root_obj.cast, cls)
class TestHelperFunctions(base.MuranoTestCase):
def setUp(self):
super(TestHelperFunctions, self).setUp()
def test_generate_id(self):
generated_id = helpers.generate_id()
self.assertTrue(re.match(r'[a-z0-9]{32}', generated_id))
def test_evaluate(self):
yaql_value = mock.Mock(spec=yaql_expression.YaqlExpression,
evaluate=lambda context: 'atom')
complex_value = {yaql_value: ['some', (1, yaql_value), lambda: 'hi!'],
'sample': [yaql_value, xrange(5)]}
complex_literal = {'atom': ['some', (1, 'atom'), 'hi!'],
'sample': ['atom', [0, 1, 2, 3, 4]]}
# tuple(evaluate(list)) transformation adds + 1
complex_literal_depth = 3 + 1
context = yaql.create_context(False)
evaluated_value = helpers.evaluate(yaql_value, context, 1)
non_evaluated_value = helpers.evaluate(yaql_value, context, 0)
evaluated_complex_value = helpers.evaluate(complex_value, context)
non_evaluated_complex_value = helpers.evaluate(
complex_value, context, complex_literal_depth)
self.assertEqual('atom', evaluated_value)
self.assertNotEqual('atom', non_evaluated_value)
self.assertEqual(complex_literal, evaluated_complex_value)
self.assertNotEqual(complex_literal, non_evaluated_complex_value)
def test_needs_evaluation(self):
testee = helpers.needs_evaluation
parsed_expr = yaql.parse("string")
yaql_expr = yaql_expression.YaqlExpression("string")
self.assertTrue(testee(parsed_expr))
self.assertTrue(testee(yaql_expr))
self.assertTrue(testee({yaql_expr: 1}))
self.assertTrue(testee({'label': yaql_expr}))
self.assertTrue(testee([yaql_expr]))
class TestYaqlExpression(base.MuranoTestCase):
def setUp(self):
super(TestYaqlExpression, self).setUp()
def test_expression(self):
yaql_expr = yaql_expression.YaqlExpression('string')
self.assertEqual('string', yaql_expr.expression)
def test_evaluate_calls(self):
string = 'string'
expected_calls = [mock.call(string),
mock.call().evaluate(context=None)]
with mock.patch('yaql.parse') as mock_parse:
yaql_expr = yaql_expression.YaqlExpression(string)
yaql_expr.evaluate()
self.assertEqual(expected_calls, mock_parse.mock_calls)
def test_match_returns(self):
expr = yaql_expression.YaqlExpression('string')
with mock.patch('yaql.parse'):
self.assertTrue(expr.match('$some'))
self.assertTrue(expr.match('$.someMore'))
with mock.patch('yaql.parse') as parse_mock:
parse_mock.side_effect = yaql.exceptions.YaqlGrammarException
self.assertFalse(expr.match(''))
with mock.patch('yaql.parse') as parse_mock:
parse_mock.side_effect = yaql.exceptions.YaqlLexicalException
self.assertFalse(expr.match(''))