~celebdor/charms/trusty/midonet-gateway/fake

« back to all changes in this revision

Viewing changes to unit_tests/midonet_helpers/test.py

  • Committer: Antoni Segura Puimedon
  • Date: 2015-12-21 01:14:08 UTC
  • Revision ID: toni@midokura.com-20151221011408-tdcv1pec67th7ckr
ultra bare bones charm for just pulling midonet-agent

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/env python
2
 
# vim: tabstop=4 shiftwidth=4 softtabstop=4 filetype=python
3
 
#
4
 
# Copyright (c) 2015 Canonical Ltd.
5
 
# Copyright (c) 2015 Midokura Sarl.
6
 
#
7
 
# Licensed under the Apache License, Version 2.0 (the "License"); you may
8
 
# not use this file except in compliance with the License. You may obtain
9
 
# a copy of the License at
10
 
#
11
 
# http://www.apache.org/licenses/LICENSE-2.0
12
 
#
13
 
# Unless required by applicable law or agreed to in writing, software
14
 
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15
 
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16
 
# License for the specific language governing permissions and limitations
17
 
# under the License.
18
 
 
19
 
# Taken from the neutron-api charm. It should be included in charm-helpers
20
 
# in the future. (It was under unit_tests/test_utils.py).
21
 
import contextlib
22
 
import io
23
 
import logging
24
 
import os
25
 
import unittest
26
 
 
27
 
import mock
28
 
import yaml
29
 
 
30
 
 
31
 
def load_config():
32
 
    """Get config options
33
 
 
34
 
    Walk backwords from __file__ looking for config.yaml, load and return the
35
 
    'options' section'
36
 
    """
37
 
    config = None
38
 
    directory = os.path.dirname(__file__)
39
 
    while config is None:
40
 
        if os.path.isfile(os.path.join(directory, 'config.yaml')):
41
 
            config = os.path.join(directory, 'config.yaml')
42
 
            break
43
 
        parent_dir = os.path.dirname(directory)
44
 
        if parent_dir == directory:
45
 
            break  # We reached the root of the fs
46
 
        directory = parent_dir
47
 
 
48
 
    if not config:
49
 
        logging.info('Could not find config.yaml in any parent directory '
50
 
                     'of %s. ' % __file__)
51
 
        return
52
 
 
53
 
    return yaml.safe_load(open(config).read())['options']
54
 
 
55
 
 
56
 
class ServiceConfig(dict):
57
 
    def __init__(self):
58
 
        self.implicit_save = False
59
 
        dict.__init__(self)
60
 
 
61
 
 
62
 
def get_default_config():
63
 
    """Get the default charm config
64
 
 
65
 
    Load default charm config from config.yaml return as a dict.
66
 
    If no default is set in config.yaml, its value is None.
67
 
    """
68
 
    default_config = ServiceConfig()
69
 
    config = load_config()
70
 
    if config is not None:
71
 
        for k, v in config.items():
72
 
            if 'default' in v:
73
 
                default_config[k] = v['default']
74
 
            else:
75
 
                default_config[k] = None
76
 
    return default_config
77
 
 
78
 
 
79
 
class CharmTestCase(unittest.TestCase):
80
 
 
81
 
    def setUp(self, obj, patches):
82
 
        super(CharmTestCase, self).setUp()
83
 
        self.patches = patches
84
 
        self.obj = obj
85
 
        self.test_config = TestConfig()
86
 
        self.test_relation = TestRelation()
87
 
        self.patch_all()
88
 
 
89
 
    def patch(self, method):
90
 
        _m = mock.patch.object(self.obj, method)
91
 
        _mock = _m.start()
92
 
        self.addCleanup(_m.stop)
93
 
        return _mock
94
 
 
95
 
    def patch_all(self):
96
 
        for method in self.patches:
97
 
            setattr(self, method, self.patch(method))
98
 
 
99
 
 
100
 
class TestConfig(object):
101
 
 
102
 
    def __init__(self):
103
 
        self.config = get_default_config()
104
 
 
105
 
    def get(self, attr=None):
106
 
        if not attr:
107
 
            return self.get_all()
108
 
        try:
109
 
            return self.config[attr]
110
 
        except KeyError:
111
 
            return None
112
 
 
113
 
    def get_all(self):
114
 
        return self.config
115
 
 
116
 
    def set(self, attr, value):
117
 
        """Allows to set even non-original values to match hookenv.Config."""
118
 
        self.config[attr] = value
119
 
 
120
 
 
121
 
class TestRelation(object):
122
 
 
123
 
    def __init__(self, relation_data={}):
124
 
        self.relation_data = relation_data
125
 
 
126
 
    def set(self, relation_data):
127
 
        self.relation_data = relation_data
128
 
 
129
 
    def get(self, attribute=None, unit=None, rid=None):
130
 
        if attribute is None:
131
 
            return self.relation_data
132
 
        elif attribute in self.relation_data:
133
 
            return self.relation_data[attribute]
134
 
        return None
135
 
 
136
 
 
137
 
class FakeRelation(object):
138
 
    """A fake relation class.
139
 
 
140
 
    Lets tests specify simple relation data
141
 
    for a default relation + unit (foo:0, foo/0, set in setUp()), eg:
142
 
 
143
 
        rel = {
144
 
            'private-address': 'foo',
145
 
            'password': 'passwd',
146
 
        }
147
 
        relation = FakeRelation(rel)
148
 
        self.relation_get.side_effect = relation.get
149
 
        passwd = self.relation_get('password')
150
 
 
151
 
    or more complex relations meant to be addressed by explicit relation id
152
 
    + unit id combos:
153
 
 
154
 
        rel = {
155
 
            'mysql:0': {
156
 
                'mysql/0': {
157
 
                    'private-address': 'foo',
158
 
                    'password': 'passwd',
159
 
                }
160
 
            }
161
 
        }
162
 
        relation = FakeRelation(rel)
163
 
        self.relation_get.side_affect = relation.get
164
 
        passwd = self.relation_get('password', rid='mysql:0', unit='mysql/0')
165
 
    """
166
 
    def __init__(self, relation_data):
167
 
        self.relation_data = relation_data
168
 
 
169
 
    def get(self, attr=None, unit=None, rid=None):
170
 
        if not rid or rid == 'foo:0':
171
 
            if attr is None:
172
 
                return self.relation_data
173
 
            elif attr in self.relation_data:
174
 
                return self.relation_data[attr]
175
 
            return None
176
 
        else:
177
 
            if rid not in self.relation_data:
178
 
                return None
179
 
            try:
180
 
                relation = self.relation_data[rid][unit]
181
 
            except KeyError:
182
 
                return None
183
 
            if attr is None:
184
 
                return relation
185
 
            elif attr in relation:
186
 
                return relation[attr]
187
 
            return None
188
 
 
189
 
    def relation_ids(self, relation=None):
190
 
        if relation is None:
191
 
            return self.relation_data.keys()
192
 
        rids = []
193
 
        for key in self.relation_data.keys():
194
 
            if key.startswith(relation) and key[len(relation)] == ':':
195
 
                rids.append(key)
196
 
        return rids
197
 
 
198
 
    def related_units(self, relid=None):
199
 
        try:
200
 
            return self.relation_data[relid].keys()
201
 
        except KeyError:
202
 
            return []
203
 
 
204
 
    def relation_units(self, relation_id):
205
 
        if relation_id not in self.relation_data:
206
 
            return None
207
 
        return self.relation_data[relation_id].keys()
208
 
 
209
 
 
210
 
@contextlib.contextmanager
211
 
def patch_open():
212
 
    """Mock open()
213
 
 
214
 
    Mocking allows to mock both open() itself and the file that is being
215
 
    yielded.
216
 
 
217
 
    Yields the mock for "open" and "file", respectively.
218
 
    """
219
 
    mock_open = mock.MagicMock(spec=open)
220
 
    mock_file = mock.MagicMock(spec=io.FileIO)
221
 
 
222
 
    @contextlib.contextmanager
223
 
    def stub_open(*args, **kwargs):
224
 
        mock_open(*args, **kwargs)
225
 
        yield mock_file
226
 
 
227
 
    with mock.patch('__builtin__.open', stub_open):
228
 
        yield mock_open, mock_file