~avishai-ish-shalom/cloud-init/chef-refactor

« back to all changes in this revision

Viewing changes to tests/unittests/test_merging.py

support different and user-suppliable merging algorithms for cloud-config

This adds a very useful mechanism for merging cloud-config, allowing
the user to append to lists (ie, just add more 'run_cmd') or other
things.

See doc/merging.txt for more information, it is intended to be backwards
compatible by default.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
from mocker import MockerTestCase
2
 
 
3
 
from cloudinit import util
4
 
 
5
 
 
6
 
class TestMergeDict(MockerTestCase):
7
 
    def test_simple_merge(self):
8
 
        """Test simple non-conflict merge."""
9
 
        source = {"key1": "value1"}
10
 
        candidate = {"key2": "value2"}
11
 
        result = util.mergedict(source, candidate)
12
 
        self.assertEqual({"key1": "value1", "key2": "value2"}, result)
13
 
 
14
 
    def test_nested_merge(self):
15
 
        """Test nested merge."""
16
 
        source = {"key1": {"key1.1": "value1.1"}}
17
 
        candidate = {"key1": {"key1.2": "value1.2"}}
18
 
        result = util.mergedict(source, candidate)
19
 
        self.assertEqual(
20
 
            {"key1": {"key1.1": "value1.1", "key1.2": "value1.2"}}, result)
21
 
 
22
 
    def test_merge_does_not_override(self):
23
 
        """Test that candidate doesn't override source."""
24
 
        source = {"key1": "value1", "key2": "value2"}
25
 
        candidate = {"key1": "value2", "key2": "NEW VALUE"}
26
 
        result = util.mergedict(source, candidate)
27
 
        self.assertEqual(source, result)
28
 
 
29
 
    def test_empty_candidate(self):
30
 
        """Test empty candidate doesn't change source."""
31
 
        source = {"key": "value"}
32
 
        candidate = {}
33
 
        result = util.mergedict(source, candidate)
34
 
        self.assertEqual(source, result)
35
 
 
36
 
    def test_empty_source(self):
37
 
        """Test empty source is replaced by candidate."""
38
 
        source = {}
39
 
        candidate = {"key": "value"}
40
 
        result = util.mergedict(source, candidate)
41
 
        self.assertEqual(candidate, result)
42
 
 
43
 
    def test_non_dict_candidate(self):
44
 
        """Test non-dict candidate is discarded."""
45
 
        source = {"key": "value"}
46
 
        candidate = "not a dict"
47
 
        result = util.mergedict(source, candidate)
48
 
        self.assertEqual(source, result)
49
 
 
50
 
    def test_non_dict_source(self):
51
 
        """Test non-dict source is not modified with a dict candidate."""
52
 
        source = "not a dict"
53
 
        candidate = {"key": "value"}
54
 
        result = util.mergedict(source, candidate)
55
 
        self.assertEqual(source, result)
56
 
 
57
 
    def test_neither_dict(self):
58
 
        """Test if neither candidate or source is dict source wins."""
59
 
        source = "source"
60
 
        candidate = "candidate"
61
 
        result = util.mergedict(source, candidate)
62
 
        self.assertEqual(source, result)
 
1
from tests.unittests import helpers
 
2
 
 
3
from cloudinit import mergers
 
4
 
 
5
 
 
6
class TestSimpleRun(helpers.MockerTestCase):
 
7
    def test_basic_merge(self):
 
8
        source = {
 
9
            'Blah': ['blah2'],
 
10
            'Blah3': 'c',
 
11
        }
 
12
        merge_with = {
 
13
            'Blah2': ['blah3'],
 
14
            'Blah3': 'b',
 
15
            'Blah': ['123'],
 
16
        }
 
17
        # Basic merge should not do thing special
 
18
        merge_how = "list()+dict()+str()"
 
19
        merger_set = mergers.string_extract_mergers(merge_how)
 
20
        self.assertEquals(3, len(merger_set))
 
21
        merger = mergers.construct(merger_set)
 
22
        merged = merger.merge(source, merge_with)
 
23
        self.assertEquals(merged['Blah'], ['blah2'])
 
24
        self.assertEquals(merged['Blah2'], ['blah3'])
 
25
        self.assertEquals(merged['Blah3'], 'c')
 
26
 
 
27
    def test_dict_overwrite(self):
 
28
        source = {
 
29
            'Blah': ['blah2'],
 
30
        }
 
31
        merge_with = {
 
32
            'Blah': ['123'],
 
33
        }
 
34
        # Now lets try a dict overwrite
 
35
        merge_how = "list()+dict(overwrite)+str()"
 
36
        merger_set = mergers.string_extract_mergers(merge_how)
 
37
        self.assertEquals(3, len(merger_set))
 
38
        merger = mergers.construct(merger_set)
 
39
        merged = merger.merge(source, merge_with)
 
40
        self.assertEquals(merged['Blah'], ['123'])
 
41
 
 
42
    def test_string_append(self):
 
43
        source = {
 
44
            'Blah': 'blah2',
 
45
        }
 
46
        merge_with = {
 
47
            'Blah': '345',
 
48
        }
 
49
        merge_how = "list()+dict()+str(append)"
 
50
        merger_set = mergers.string_extract_mergers(merge_how)
 
51
        self.assertEquals(3, len(merger_set))
 
52
        merger = mergers.construct(merger_set)
 
53
        merged = merger.merge(source, merge_with)
 
54
        self.assertEquals(merged['Blah'], 'blah2345')
 
55
 
 
56
    def test_list_extend(self):
 
57
        source = ['abc']
 
58
        merge_with = ['123']
 
59
        merge_how = "list(extend)+dict()+str()"
 
60
        merger_set = mergers.string_extract_mergers(merge_how)
 
61
        self.assertEquals(3, len(merger_set))
 
62
        merger = mergers.construct(merger_set)
 
63
        merged = merger.merge(source, merge_with)
 
64
        self.assertEquals(merged, ['abc', '123'])
 
65
 
 
66
    def test_deep_merge(self):
 
67
        source = {
 
68
            'a': [1, 'b', 2],
 
69
            'b': 'blahblah',
 
70
            'c': {
 
71
                'e': [1, 2, 3],
 
72
                'f': 'bigblobof',
 
73
                'iamadict': {
 
74
                    'ok': 'ok',
 
75
                }
 
76
            },
 
77
            'run': [
 
78
                'runme',
 
79
                'runme2',
 
80
            ],
 
81
            'runmereally': [
 
82
                'e', ['a'], 'd',
 
83
            ],
 
84
        }
 
85
        merge_with = {
 
86
            'a': ['e', 'f', 'g'],
 
87
            'b': 'more',
 
88
            'c': {
 
89
                'a': 'b',
 
90
                'f': 'stuff',
 
91
            },
 
92
            'run': [
 
93
                'morecmd',
 
94
                'moremoremore',
 
95
            ],
 
96
            'runmereally': [
 
97
                'blah', ['b'], 'e',
 
98
            ],
 
99
        }
 
100
        merge_how = "list(extend)+dict()+str(append)"
 
101
        merger_set = mergers.string_extract_mergers(merge_how)
 
102
        self.assertEquals(3, len(merger_set))
 
103
        merger = mergers.construct(merger_set)
 
104
        merged = merger.merge(source, merge_with)
 
105
        self.assertEquals(merged['a'], [1, 'b', 2, 'e', 'f', 'g'])
 
106
        self.assertEquals(merged['b'], 'blahblahmore')
 
107
        self.assertEquals(merged['c']['f'], 'bigblobofstuff')
 
108
        self.assertEquals(merged['run'], ['runme', 'runme2', 'morecmd',
 
109
                                          'moremoremore'])
 
110
        self.assertEquals(merged['runmereally'], ['e', ['a'], 'd', 'blah',
 
111
                                                  ['b'], 'e'])
 
112
 
 
113
    def test_dict_overwrite_layered(self):
 
114
        source = {
 
115
            'Blah3': {
 
116
                'f': '3',
 
117
                'g': {
 
118
                    'a': 'b',
 
119
                }
 
120
            }
 
121
        }
 
122
        merge_with = {
 
123
            'Blah3': {
 
124
                'e': '2',
 
125
                'g': {
 
126
                    'e': 'f',
 
127
                }
 
128
            }
 
129
        }
 
130
        merge_how = "list()+dict()+str()"
 
131
        merger_set = mergers.string_extract_mergers(merge_how)
 
132
        self.assertEquals(3, len(merger_set))
 
133
        merger = mergers.construct(merger_set)
 
134
        merged = merger.merge(source, merge_with)
 
135
        self.assertEquals(merged['Blah3'], {
 
136
                'e': '2',
 
137
                'f': '3',
 
138
                'g': {
 
139
                    'a': 'b',
 
140
                    'e': 'f',
 
141
                }
 
142
        })