# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. from __future__ import absolute_import, print_function, unicode_literals import unittest import mozunit import textwrap from taskgraph.util.templates import ( merge_to, merge, Templates, TemplatesException ) files = {} files['/fixtures/circular.yml'] = textwrap.dedent("""\ $inherits: from: 'circular_ref.yml' variables: woot: 'inherit' """) files['/fixtures/inherit.yml'] = textwrap.dedent("""\ $inherits: from: 'templates.yml' variables: woot: 'inherit' """) files['/fixtures/extend_child.yml'] = textwrap.dedent("""\ list: ['1', '2', '3'] was_list: ['1'] obj: level: 1 deeper: woot: 'bar' list: ['baz'] """) files['/fixtures/circular_ref.yml'] = textwrap.dedent("""\ $inherits: from: 'circular.yml' """) files['/fixtures/child_pass.yml'] = textwrap.dedent("""\ values: - {{a}} - {{b}} - {{c}} """) files['/fixtures/inherit_pass.yml'] = textwrap.dedent("""\ $inherits: from: 'child_pass.yml' variables: a: 'a' b: 'b' c: 'c' """) files['/fixtures/deep/2.yml'] = textwrap.dedent("""\ $inherits: from: deep/1.yml """) files['/fixtures/deep/3.yml'] = textwrap.dedent("""\ $inherits: from: deep/2.yml """) files['/fixtures/deep/4.yml'] = textwrap.dedent("""\ $inherits: from: deep/3.yml """) files['/fixtures/deep/1.yml'] = textwrap.dedent("""\ variable: {{value}} """) files['/fixtures/simple.yml'] = textwrap.dedent("""\ is_simple: true """) files['/fixtures/templates.yml'] = textwrap.dedent("""\ content: 'content' variable: '{{woot}}' """) files['/fixtures/extend_parent.yml'] = textwrap.dedent("""\ $inherits: from: 'extend_child.yml' list: ['4'] was_list: replaced: true obj: level: 2 from_parent: true deeper: list: ['bar'] """) class TemplatesTest(unittest.TestCase): def setUp(self): self.mocked_open = mozunit.MockedOpen(files) self.mocked_open.__enter__() self.subject = Templates('/fixtures') def tearDown(self): self.mocked_open.__exit__(None, None, None) def test_invalid_path(self): with self.assertRaisesRegexp(TemplatesException, 'must be a directory'): Templates('/zomg/not/a/dir') def test_no_templates(self): content = self.subject.load('simple.yml', {}) self.assertEquals(content, { 'is_simple': True }) def test_with_templates(self): content = self.subject.load('templates.yml', { 'woot': 'bar' }) self.assertEquals(content, { 'content': 'content', 'variable': 'bar' }) def test_inheritance(self): ''' The simple single pass inheritance case. ''' content = self.subject.load('inherit.yml', {}) self.assertEqual(content, { 'content': 'content', 'variable': 'inherit' }) def test_inheritance_implicat_pass(self): ''' Implicitly pass parameters from the child to the ancestor. ''' content = self.subject.load('inherit_pass.yml', { 'a': 'overriden' }) self.assertEqual(content, {'values': ['overriden', 'b', 'c']}) def test_inheritance_circular(self): ''' Circular reference handling. ''' with self.assertRaisesRegexp(TemplatesException, 'circular'): self.subject.load('circular.yml', {}) def test_deep_inheritance(self): content = self.subject.load('deep/4.yml', { 'value': 'myvalue' }) self.assertEqual(content, {'variable': 'myvalue'}) def test_inheritance_with_simple_extensions(self): content = self.subject.load('extend_parent.yml', {}) self.assertEquals(content, { 'list': ['1', '2', '3', '4'], 'obj': { 'from_parent': True, 'deeper': { 'woot': 'bar', 'list': ['baz', 'bar'] }, 'level': 2, }, 'was_list': {'replaced': True} }) class MergeTest(unittest.TestCase): def test_merge_to_dicts(self): source = {'a': 1, 'b': 2} dest = {'b': '20', 'c': 30} expected = { 'a': 1, # source only 'b': 2, # source overrides dest 'c': 30, # dest only } self.assertEqual(merge_to(source, dest), expected) self.assertEqual(dest, expected) def test_merge_to_lists(self): source = {'x': [3, 4]} dest = {'x': [1, 2]} expected = {'x': [1, 2, 3, 4]} # dest first self.assertEqual(merge_to(source, dest), expected) self.assertEqual(dest, expected) def test_merge_diff_types(self): source = {'x': [1, 2]} dest = {'x': 'abc'} expected = {'x': [1, 2]} # source wins self.assertEqual(merge_to(source, dest), expected) self.assertEqual(dest, expected) def test_merge(self): first = {'a': 1, 'b': 2, 'd': 11} second = {'b': 20, 'c': 30} third = {'c': 300, 'd': 400} expected = { 'a': 1, 'b': 20, 'c': 300, 'd': 400, } self.assertEqual(merge(first, second, third), expected) # inputs haven't changed.. self.assertEqual(first, {'a': 1, 'b': 2, 'd': 11}) self.assertEqual(second, {'b': 20, 'c': 30}) self.assertEqual(third, {'c': 300, 'd': 400}) if __name__ == '__main__': mozunit.main()