1# (c) 2016, Adrian Likins <alikins@redhat.com>
2#
3# This file is part of Ansible
4#
5# Ansible is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# Ansible is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
17
18# Make coding more python3-ish
19from __future__ import (absolute_import, division, print_function)
20__metaclass__ = type
21
22from units.compat import unittest
23
24from ansible.errors import AnsibleParserError
25from ansible.module_utils.six import string_types
26from ansible.playbook.attribute import FieldAttribute
27from ansible.template import Templar
28from ansible.playbook import base
29from ansible.utils.unsafe_proxy import AnsibleUnsafeBytes, AnsibleUnsafeText
30
31from units.mock.loader import DictDataLoader
32
33
34class TestBase(unittest.TestCase):
35    ClassUnderTest = base.Base
36
37    def setUp(self):
38        self.assorted_vars = {'var_2_key': 'var_2_value',
39                              'var_1_key': 'var_1_value',
40                              'a_list': ['a_list_1', 'a_list_2'],
41                              'a_dict': {'a_dict_key': 'a_dict_value'},
42                              'a_set': set(['set_1', 'set_2']),
43                              'a_int': 42,
44                              'a_float': 37.371,
45                              'a_bool': True,
46                              'a_none': None,
47                              }
48        self.b = self.ClassUnderTest()
49
50    def _base_validate(self, ds):
51        bsc = self.ClassUnderTest()
52        parent = ExampleParentBaseSubClass()
53        bsc._parent = parent
54        bsc._dep_chain = [parent]
55        parent._dep_chain = None
56        bsc.load_data(ds)
57        fake_loader = DictDataLoader({})
58        templar = Templar(loader=fake_loader)
59        bsc.post_validate(templar)
60        return bsc
61
62    def test(self):
63        self.assertIsInstance(self.b, base.Base)
64        self.assertIsInstance(self.b, self.ClassUnderTest)
65
66    # dump me doesnt return anything or change anything so not much to assert
67    def test_dump_me_empty(self):
68        self.b.dump_me()
69
70    def test_dump_me(self):
71        ds = {'environment': [],
72              'vars': {'var_2_key': 'var_2_value',
73                       'var_1_key': 'var_1_value'}}
74        b = self._base_validate(ds)
75        b.dump_me()
76
77    def _assert_copy(self, orig, copy):
78        self.assertIsInstance(copy, self.ClassUnderTest)
79        self.assertIsInstance(copy, base.Base)
80        self.assertEquals(len(orig._valid_attrs),
81                          len(copy._valid_attrs))
82
83        sentinel = 'Empty DS'
84        self.assertEquals(getattr(orig, '_ds', sentinel),
85                          getattr(copy, '_ds', sentinel))
86
87    def test_copy_empty(self):
88        copy = self.b.copy()
89        self._assert_copy(self.b, copy)
90
91    def test_copy_with_vars(self):
92        ds = {'vars': self.assorted_vars}
93        b = self._base_validate(ds)
94
95        copy = b.copy()
96        self._assert_copy(b, copy)
97
98    def test_serialize(self):
99        ds = {}
100        ds = {'environment': [],
101              'vars': self.assorted_vars
102              }
103        b = self._base_validate(ds)
104        ret = b.serialize()
105        self.assertIsInstance(ret, dict)
106
107    def test_deserialize(self):
108        data = {}
109
110        d = self.ClassUnderTest()
111        d.deserialize(data)
112        self.assertIn('run_once', d._attributes)
113        self.assertIn('check_mode', d._attributes)
114
115        data = {'no_log': False,
116                'remote_user': None,
117                'vars': self.assorted_vars,
118                'environment': [],
119                'run_once': False,
120                'connection': None,
121                'ignore_errors': False,
122                'port': 22,
123                'a_sentinel_with_an_unlikely_name': ['sure, a list']}
124
125        d = self.ClassUnderTest()
126        d.deserialize(data)
127        self.assertNotIn('a_sentinel_with_an_unlikely_name', d._attributes)
128        self.assertIn('run_once', d._attributes)
129        self.assertIn('check_mode', d._attributes)
130
131    def test_serialize_then_deserialize(self):
132        ds = {'environment': [],
133              'vars': self.assorted_vars}
134        b = self._base_validate(ds)
135        copy = b.copy()
136        ret = b.serialize()
137        b.deserialize(ret)
138        c = self.ClassUnderTest()
139        c.deserialize(ret)
140        # TODO: not a great test, but coverage...
141        self.maxDiff = None
142        self.assertDictEqual(b.serialize(), copy.serialize())
143        self.assertDictEqual(c.serialize(), copy.serialize())
144
145    def test_post_validate_empty(self):
146        fake_loader = DictDataLoader({})
147        templar = Templar(loader=fake_loader)
148        ret = self.b.post_validate(templar)
149        self.assertIsNone(ret)
150
151    def test_get_ds_none(self):
152        ds = self.b.get_ds()
153        self.assertIsNone(ds)
154
155    def test_load_data_ds_is_none(self):
156        self.assertRaises(AssertionError, self.b.load_data, None)
157
158    def test_load_data_invalid_attr(self):
159        ds = {'not_a_valid_attr': [],
160              'other': None}
161
162        self.assertRaises(AnsibleParserError, self.b.load_data, ds)
163
164    def test_load_data_invalid_attr_type(self):
165        ds = {'environment': True}
166
167        # environment is supposed to be  a list. This
168        # seems like it shouldn't work?
169        ret = self.b.load_data(ds)
170        self.assertEquals(True, ret._attributes['environment'])
171
172    def test_post_validate(self):
173        ds = {'environment': [],
174              'port': 443}
175        b = self._base_validate(ds)
176        self.assertEquals(b.port, 443)
177        self.assertEquals(b.environment, [])
178
179    def test_post_validate_invalid_attr_types(self):
180        ds = {'environment': [],
181              'port': 'some_port'}
182        b = self._base_validate(ds)
183        self.assertEquals(b.port, 'some_port')
184
185    def test_squash(self):
186        data = self.b.serialize()
187        self.b.squash()
188        squashed_data = self.b.serialize()
189        # TODO: assert something
190        self.assertFalse(data['squashed'])
191        self.assertTrue(squashed_data['squashed'])
192
193    def test_vars(self):
194        # vars as a dict.
195        ds = {'environment': [],
196              'vars': {'var_2_key': 'var_2_value',
197                       'var_1_key': 'var_1_value'}}
198        b = self._base_validate(ds)
199        self.assertEquals(b.vars['var_1_key'], 'var_1_value')
200
201    def test_vars_list_of_dicts(self):
202        ds = {'environment': [],
203              'vars': [{'var_2_key': 'var_2_value'},
204                       {'var_1_key': 'var_1_value'}]
205              }
206        b = self._base_validate(ds)
207        self.assertEquals(b.vars['var_1_key'], 'var_1_value')
208
209    def test_vars_not_dict_or_list(self):
210        ds = {'environment': [],
211              'vars': 'I am a string, not a dict or a list of dicts'}
212        self.assertRaises(AnsibleParserError, self.b.load_data, ds)
213
214    def test_vars_not_valid_identifier(self):
215        ds = {'environment': [],
216              'vars': [{'var_2_key': 'var_2_value'},
217                       {'1an-invalid identifer': 'var_1_value'}]
218              }
219        self.assertRaises(AnsibleParserError, self.b.load_data, ds)
220
221    def test_vars_is_list_but_not_of_dicts(self):
222        ds = {'environment': [],
223              'vars': ['foo', 'bar', 'this is a string not a dict']
224              }
225        self.assertRaises(AnsibleParserError, self.b.load_data, ds)
226
227    def test_vars_is_none(self):
228        # If vars is None, we should get a empty dict back
229        ds = {'environment': [],
230              'vars': None
231              }
232        b = self._base_validate(ds)
233        self.assertEquals(b.vars, {})
234
235    def test_validate_empty(self):
236        self.b.validate()
237        self.assertTrue(self.b._validated)
238
239    def test_getters(self):
240        # not sure why these exist, but here are tests anyway
241        loader = self.b.get_loader()
242        variable_manager = self.b.get_variable_manager()
243        self.assertEquals(loader, self.b._loader)
244        self.assertEquals(variable_manager, self.b._variable_manager)
245
246
247class TestExtendValue(unittest.TestCase):
248    # _extend_value could be a module or staticmethod but since its
249    # not, the test is here.
250    def test_extend_value_list_newlist(self):
251        b = base.Base()
252        value_list = ['first', 'second']
253        new_value_list = ['new_first', 'new_second']
254        ret = b._extend_value(value_list, new_value_list)
255        self.assertEquals(value_list + new_value_list, ret)
256
257    def test_extend_value_list_newlist_prepend(self):
258        b = base.Base()
259        value_list = ['first', 'second']
260        new_value_list = ['new_first', 'new_second']
261        ret_prepend = b._extend_value(value_list, new_value_list, prepend=True)
262        self.assertEquals(new_value_list + value_list, ret_prepend)
263
264    def test_extend_value_newlist_list(self):
265        b = base.Base()
266        value_list = ['first', 'second']
267        new_value_list = ['new_first', 'new_second']
268        ret = b._extend_value(new_value_list, value_list)
269        self.assertEquals(new_value_list + value_list, ret)
270
271    def test_extend_value_newlist_list_prepend(self):
272        b = base.Base()
273        value_list = ['first', 'second']
274        new_value_list = ['new_first', 'new_second']
275        ret = b._extend_value(new_value_list, value_list, prepend=True)
276        self.assertEquals(value_list + new_value_list, ret)
277
278    def test_extend_value_string_newlist(self):
279        b = base.Base()
280        some_string = 'some string'
281        new_value_list = ['new_first', 'new_second']
282        ret = b._extend_value(some_string, new_value_list)
283        self.assertEquals([some_string] + new_value_list, ret)
284
285    def test_extend_value_string_newstring(self):
286        b = base.Base()
287        some_string = 'some string'
288        new_value_string = 'this is the new values'
289        ret = b._extend_value(some_string, new_value_string)
290        self.assertEquals([some_string, new_value_string], ret)
291
292    def test_extend_value_list_newstring(self):
293        b = base.Base()
294        value_list = ['first', 'second']
295        new_value_string = 'this is the new values'
296        ret = b._extend_value(value_list, new_value_string)
297        self.assertEquals(value_list + [new_value_string], ret)
298
299    def test_extend_value_none_none(self):
300        b = base.Base()
301        ret = b._extend_value(None, None)
302        self.assertEquals(len(ret), 0)
303        self.assertFalse(ret)
304
305    def test_extend_value_none_list(self):
306        b = base.Base()
307        ret = b._extend_value(None, ['foo'])
308        self.assertEquals(ret, ['foo'])
309
310
311class ExampleException(Exception):
312    pass
313
314
315# naming fails me...
316class ExampleParentBaseSubClass(base.Base):
317    _test_attr_parent_string = FieldAttribute(isa='string', default='A string attr for a class that may be a parent for testing')
318
319    def __init__(self):
320
321        super(ExampleParentBaseSubClass, self).__init__()
322        self._dep_chain = None
323
324    def get_dep_chain(self):
325        return self._dep_chain
326
327
328class ExampleSubClass(base.Base):
329    _test_attr_blip = FieldAttribute(isa='string', default='example sub class test_attr_blip',
330                                     inherit=False,
331                                     always_post_validate=True)
332
333    def __init__(self):
334        super(ExampleSubClass, self).__init__()
335
336    def get_dep_chain(self):
337        if self._parent:
338            return self._parent.get_dep_chain()
339        else:
340            return None
341
342
343class BaseSubClass(base.Base):
344    _name = FieldAttribute(isa='string', default='', always_post_validate=True)
345    _test_attr_bool = FieldAttribute(isa='bool', always_post_validate=True)
346    _test_attr_int = FieldAttribute(isa='int', always_post_validate=True)
347    _test_attr_float = FieldAttribute(isa='float', default=3.14159, always_post_validate=True)
348    _test_attr_list = FieldAttribute(isa='list', listof=string_types, always_post_validate=True)
349    _test_attr_list_no_listof = FieldAttribute(isa='list', always_post_validate=True)
350    _test_attr_list_required = FieldAttribute(isa='list', listof=string_types, required=True,
351                                              default=list, always_post_validate=True)
352    _test_attr_string = FieldAttribute(isa='string', default='the_test_attr_string_default_value')
353    _test_attr_string_required = FieldAttribute(isa='string', required=True,
354                                                default='the_test_attr_string_default_value')
355    _test_attr_percent = FieldAttribute(isa='percent', always_post_validate=True)
356    _test_attr_set = FieldAttribute(isa='set', default=set, always_post_validate=True)
357    _test_attr_dict = FieldAttribute(isa='dict', default=lambda: {'a_key': 'a_value'}, always_post_validate=True)
358    _test_attr_class = FieldAttribute(isa='class', class_type=ExampleSubClass)
359    _test_attr_class_post_validate = FieldAttribute(isa='class', class_type=ExampleSubClass,
360                                                    always_post_validate=True)
361    _test_attr_unknown_isa = FieldAttribute(isa='not_a_real_isa', always_post_validate=True)
362    _test_attr_example = FieldAttribute(isa='string', default='the_default',
363                                        always_post_validate=True)
364    _test_attr_none = FieldAttribute(isa='string', always_post_validate=True)
365    _test_attr_preprocess = FieldAttribute(isa='string', default='the default for preprocess')
366    _test_attr_method = FieldAttribute(isa='string', default='some attr with a getter',
367                                       always_post_validate=True)
368    _test_attr_method_missing = FieldAttribute(isa='string', default='some attr with a missing getter',
369                                               always_post_validate=True)
370
371    def _get_attr_test_attr_method(self):
372        return 'foo bar'
373
374    def _validate_test_attr_example(self, attr, name, value):
375        if not isinstance(value, str):
376            raise ExampleException('_test_attr_example is not a string: %s type=%s' % (value, type(value)))
377
378    def _post_validate_test_attr_example(self, attr, value, templar):
379        after_template_value = templar.template(value)
380        return after_template_value
381
382    def _post_validate_test_attr_none(self, attr, value, templar):
383        return None
384
385    def _get_parent_attribute(self, attr, extend=False, prepend=False):
386        value = None
387        try:
388            value = self._attributes[attr]
389            if self._parent and (value is None or extend):
390                parent_value = getattr(self._parent, attr, None)
391                if extend:
392                    value = self._extend_value(value, parent_value, prepend)
393                else:
394                    value = parent_value
395        except KeyError:
396            pass
397
398        return value
399
400
401# terrible name, but it is a TestBase subclass for testing subclasses of Base
402class TestBaseSubClass(TestBase):
403    ClassUnderTest = BaseSubClass
404
405    def _base_validate(self, ds):
406        ds['test_attr_list_required'] = []
407        return super(TestBaseSubClass, self)._base_validate(ds)
408
409    def test_attr_bool(self):
410        ds = {'test_attr_bool': True}
411        bsc = self._base_validate(ds)
412        self.assertEquals(bsc.test_attr_bool, True)
413
414    def test_attr_int(self):
415        MOST_RANDOM_NUMBER = 37
416        ds = {'test_attr_int': MOST_RANDOM_NUMBER}
417        bsc = self._base_validate(ds)
418        self.assertEquals(bsc.test_attr_int, MOST_RANDOM_NUMBER)
419
420    def test_attr_int_del(self):
421        MOST_RANDOM_NUMBER = 37
422        ds = {'test_attr_int': MOST_RANDOM_NUMBER}
423        bsc = self._base_validate(ds)
424        del bsc.test_attr_int
425        self.assertNotIn('test_attr_int', bsc._attributes)
426
427    def test_attr_float(self):
428        roughly_pi = 4.0
429        ds = {'test_attr_float': roughly_pi}
430        bsc = self._base_validate(ds)
431        self.assertEquals(bsc.test_attr_float, roughly_pi)
432
433    def test_attr_percent(self):
434        percentage = '90%'
435        percentage_float = 90.0
436        ds = {'test_attr_percent': percentage}
437        bsc = self._base_validate(ds)
438        self.assertEquals(bsc.test_attr_percent, percentage_float)
439
440    # This method works hard and gives it its all and everything it's got. It doesn't
441    # leave anything on the field. It deserves to pass. It has earned it.
442    def test_attr_percent_110_percent(self):
443        percentage = '110.11%'
444        percentage_float = 110.11
445        ds = {'test_attr_percent': percentage}
446        bsc = self._base_validate(ds)
447        self.assertEquals(bsc.test_attr_percent, percentage_float)
448
449    # This method is just here for the paycheck.
450    def test_attr_percent_60_no_percent_sign(self):
451        percentage = '60'
452        percentage_float = 60.0
453        ds = {'test_attr_percent': percentage}
454        bsc = self._base_validate(ds)
455        self.assertEquals(bsc.test_attr_percent, percentage_float)
456
457    def test_attr_set(self):
458        test_set = set(['first_string_in_set', 'second_string_in_set'])
459        ds = {'test_attr_set': test_set}
460        bsc = self._base_validate(ds)
461        self.assertEquals(bsc.test_attr_set, test_set)
462
463    def test_attr_set_string(self):
464        test_data = ['something', 'other']
465        test_value = ','.join(test_data)
466        ds = {'test_attr_set': test_value}
467        bsc = self._base_validate(ds)
468        self.assertEquals(bsc.test_attr_set, set(test_data))
469
470    def test_attr_set_not_string_or_list(self):
471        test_value = 37.1
472        ds = {'test_attr_set': test_value}
473        bsc = self._base_validate(ds)
474        self.assertEquals(bsc.test_attr_set, set([test_value]))
475
476    def test_attr_dict(self):
477        test_dict = {'a_different_key': 'a_different_value'}
478        ds = {'test_attr_dict': test_dict}
479        bsc = self._base_validate(ds)
480        self.assertEquals(bsc.test_attr_dict, test_dict)
481
482    def test_attr_dict_string(self):
483        test_value = 'just_some_random_string'
484        ds = {'test_attr_dict': test_value}
485        self.assertRaisesRegexp(AnsibleParserError, 'is not a dictionary', self._base_validate, ds)
486
487    def test_attr_class(self):
488        esc = ExampleSubClass()
489        ds = {'test_attr_class': esc}
490        bsc = self._base_validate(ds)
491        self.assertIs(bsc.test_attr_class, esc)
492
493    def test_attr_class_wrong_type(self):
494        not_a_esc = ExampleSubClass
495        ds = {'test_attr_class': not_a_esc}
496        bsc = self._base_validate(ds)
497        self.assertIs(bsc.test_attr_class, not_a_esc)
498
499    def test_attr_class_post_validate(self):
500        esc = ExampleSubClass()
501        ds = {'test_attr_class_post_validate': esc}
502        bsc = self._base_validate(ds)
503        self.assertIs(bsc.test_attr_class_post_validate, esc)
504
505    def test_attr_class_post_validate_class_not_instance(self):
506        not_a_esc = ExampleSubClass
507        ds = {'test_attr_class_post_validate': not_a_esc}
508        self.assertRaisesRegexp(AnsibleParserError, 'is not a valid.*got a.*Meta.*instead',
509                                self._base_validate, ds)
510
511    def test_attr_class_post_validate_wrong_class(self):
512        not_a_esc = 37
513        ds = {'test_attr_class_post_validate': not_a_esc}
514        self.assertRaisesRegexp(AnsibleParserError, 'is not a valid.*got a.*int.*instead',
515                                self._base_validate, ds)
516
517    def test_attr_remote_user(self):
518        ds = {'remote_user': 'testuser'}
519        bsc = self._base_validate(ds)
520        # TODO: attemp to verify we called parent gettters etc
521        self.assertEquals(bsc.remote_user, 'testuser')
522
523    def test_attr_example_undefined(self):
524        ds = {'test_attr_example': '{{ some_var_that_shouldnt_exist_to_test_omit }}'}
525        exc_regex_str = 'test_attr_example.*has an invalid value, which includes an undefined variable.*some_var_that_shouldnt*'
526        self.assertRaises(AnsibleParserError)
527
528    def test_attr_name_undefined(self):
529        ds = {'name': '{{ some_var_that_shouldnt_exist_to_test_omit }}'}
530        bsc = self._base_validate(ds)
531        # the attribute 'name' is special cases in post_validate
532        self.assertEquals(bsc.name, '{{ some_var_that_shouldnt_exist_to_test_omit }}')
533
534    def test_subclass_validate_method(self):
535        ds = {'test_attr_list': ['string_list_item_1', 'string_list_item_2'],
536              'test_attr_example': 'the_test_attr_example_value_string'}
537        # Not throwing an exception here is the test
538        bsc = self._base_validate(ds)
539        self.assertEquals(bsc.test_attr_example, 'the_test_attr_example_value_string')
540
541    def test_subclass_validate_method_invalid(self):
542        ds = {'test_attr_example': [None]}
543        self.assertRaises(ExampleException, self._base_validate, ds)
544
545    def test_attr_none(self):
546        ds = {'test_attr_none': 'foo'}
547        bsc = self._base_validate(ds)
548        self.assertEquals(bsc.test_attr_none, None)
549
550    def test_attr_string(self):
551        the_string_value = "the new test_attr_string_value"
552        ds = {'test_attr_string': the_string_value}
553        bsc = self._base_validate(ds)
554        self.assertEquals(bsc.test_attr_string, the_string_value)
555
556    def test_attr_string_invalid_list(self):
557        ds = {'test_attr_string': ['The new test_attr_string', 'value, however in a list']}
558        self.assertRaises(AnsibleParserError, self._base_validate, ds)
559
560    def test_attr_string_required(self):
561        the_string_value = "the new test_attr_string_required_value"
562        ds = {'test_attr_string_required': the_string_value}
563        bsc = self._base_validate(ds)
564        self.assertEquals(bsc.test_attr_string_required, the_string_value)
565
566    def test_attr_list_invalid(self):
567        ds = {'test_attr_list': {}}
568        self.assertRaises(AnsibleParserError, self._base_validate, ds)
569
570    def test_attr_list(self):
571        string_list = ['foo', 'bar']
572        ds = {'test_attr_list': string_list}
573        bsc = self._base_validate(ds)
574        self.assertEquals(string_list, bsc._attributes['test_attr_list'])
575
576    def test_attr_list_none(self):
577        ds = {'test_attr_list': None}
578        bsc = self._base_validate(ds)
579        self.assertEquals(None, bsc._attributes['test_attr_list'])
580
581    def test_attr_list_no_listof(self):
582        test_list = ['foo', 'bar', 123]
583        ds = {'test_attr_list_no_listof': test_list}
584        bsc = self._base_validate(ds)
585        self.assertEquals(test_list, bsc._attributes['test_attr_list_no_listof'])
586
587    def test_attr_list_required(self):
588        string_list = ['foo', 'bar']
589        ds = {'test_attr_list_required': string_list}
590        bsc = self.ClassUnderTest()
591        bsc.load_data(ds)
592        fake_loader = DictDataLoader({})
593        templar = Templar(loader=fake_loader)
594        bsc.post_validate(templar)
595        self.assertEquals(string_list, bsc._attributes['test_attr_list_required'])
596
597    def test_attr_list_required_empty_string(self):
598        string_list = [""]
599        ds = {'test_attr_list_required': string_list}
600        bsc = self.ClassUnderTest()
601        bsc.load_data(ds)
602        fake_loader = DictDataLoader({})
603        templar = Templar(loader=fake_loader)
604        self.assertRaisesRegexp(AnsibleParserError, 'cannot have empty values',
605                                bsc.post_validate, templar)
606
607    def test_attr_unknown(self):
608        a_list = ['some string']
609        ds = {'test_attr_unknown_isa': a_list}
610        bsc = self._base_validate(ds)
611        self.assertEquals(bsc.test_attr_unknown_isa, a_list)
612
613    def test_attr_method(self):
614        ds = {'test_attr_method': 'value from the ds'}
615        bsc = self._base_validate(ds)
616        # The value returned by the subclasses _get_attr_test_attr_method
617        self.assertEquals(bsc.test_attr_method, 'foo bar')
618
619    def test_attr_method_missing(self):
620        a_string = 'The value set from the ds'
621        ds = {'test_attr_method_missing': a_string}
622        bsc = self._base_validate(ds)
623        self.assertEquals(bsc.test_attr_method_missing, a_string)
624
625    def test_get_validated_value_string_rewrap_unsafe(self):
626        attribute = FieldAttribute(isa='string')
627        value = AnsibleUnsafeText(u'bar')
628        templar = Templar(None)
629        bsc = self.ClassUnderTest()
630        result = bsc.get_validated_value('foo', attribute, value, templar)
631        self.assertIsInstance(result, AnsibleUnsafeText)
632        self.assertEquals(result, AnsibleUnsafeText(u'bar'))
633