Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / utils / test.py @ fa9c0c38

History | View | Annotate | Download (20.4 kB)

1
# Copyright 2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from unittest import TestCase
35
from tempfile import NamedTemporaryFile
36
from mock import patch, call
37
from itertools import product
38
from io import StringIO
39

    
40

    
41
class UtilsMethods(TestCase):
42

    
43
    def assert_dicts_are_equal(self, d1, d2):
44
        for k, v in d1.items():
45
            self.assertTrue(k in d2)
46
            if isinstance(v, dict):
47
                self.assert_dicts_are_equal(v, d2[k])
48
            else:
49
                self.assertEqual(unicode(v), unicode(d2[k]))
50

    
51
    def test_guess_mime_type(self):
52
        from kamaki.cli.utils import guess_mime_type
53
        from mimetypes import guess_type
54
        for args in product(
55
                ('file.txt', 'file.png', 'file.zip', 'file.gz', None, 'X'),
56
                ('a type', None),
57
                ('an/encoding', None)):
58
            filename, ctype, cencoding = args
59
            if filename:
60
                exp_type, exp_enc = guess_type(filename)
61
                self.assertEqual(
62
                    guess_mime_type(*args),
63
                    (exp_type or ctype, exp_enc or cencoding))
64
            else:
65
                self.assertRaises(AssertionError, guess_mime_type, *args)
66

    
67
    @patch('kamaki.cli.utils.dumps', return_value='(dumps output)')
68
    def test_print_json(self, JD):
69
        from kamaki.cli.utils import print_json, INDENT_TAB
70
        out = StringIO()
71
        print_json('some data', out)
72
        JD.assert_called_once_with('some data', indent=INDENT_TAB)
73
        self.assertEqual(out.getvalue(), u'(dumps output)\n')
74

    
75
    def test_print_dict(self):
76
        from kamaki.cli.utils import print_dict, INDENT_TAB
77
        out = StringIO()
78
        self.assertRaises(AssertionError, print_dict, 'non-dict think')
79
        self.assertRaises(AssertionError, print_dict, {}, indent=-10)
80
        for args in product(
81
                (
82
                    {'k1': 'v1'},
83
                    {'k1': 'v1', 'k2': 'v2'},
84
                    {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'},
85
                    {'k1': 'v1', 'k2': {'k1': 'v1', 'k2': 'v2'}, 'k3': 'v3'},
86
                    {
87
                        'k1': {'k1': 'v1', 'k2': 'v2'},
88
                        'k2': [1, 2, 3],
89
                        'k3': 'v3'},
90
                    {
91
                        'k1': {'k1': 'v1', 'k2': 'v2'},
92
                        'k2': 42,
93
                        'k3': {'k1': 1, 'k2': [1, 2, 3]}},
94
                    {
95
                        'k1': {
96
                            'k1': 'v1',
97
                            'k2': [1, 2, 3],
98
                            'k3': {'k1': [(1, 2)]}},
99
                        'k2': (3, 4, 5),
100
                        'k3': {'k1': 1, 'k2': [1, 2, 3]}}),
101
                (tuple(), ('k1', ), ('k1', 'k2')),
102
                (0, 1, 2, 9), (False, True), (False, True)):
103
            d, exclude, indent, with_enumeration, recursive_enumeration = args
104
            with patch('kamaki.cli.utils.print_dict') as PD:
105
                with patch('kamaki.cli.utils.print_list') as PL:
106
                    pd_calls, pl_calls = 0, 0
107
                    print_dict(*args, out=out)
108
                    exp_calls = u''
109
                    for i, (k, v) in enumerate(d.items()):
110
                        if k in exclude:
111
                            continue
112
                        str_k = u' ' * indent
113
                        str_k += u'%s.' % (i + 1) if with_enumeration else u''
114
                        str_k += u'%s:' % k
115
                        if isinstance(v, dict):
116
                            self.assertEqual(
117
                                PD.mock_calls[pd_calls],
118
                                call(
119
                                    v,
120
                                    exclude,
121
                                    indent + INDENT_TAB,
122
                                    recursive_enumeration,
123
                                    recursive_enumeration,
124
                                    out))
125
                            pd_calls += 1
126
                            exp_calls += str_k + '\n'
127
                        elif isinstance(v, list) or isinstance(v, tuple):
128
                            self.assertEqual(
129
                                PL.mock_calls[pl_calls],
130
                                call(
131
                                    v,
132
                                    exclude,
133
                                    indent + INDENT_TAB,
134
                                    recursive_enumeration,
135
                                    recursive_enumeration,
136
                                    out))
137
                            pl_calls += 1
138
                            exp_calls += str_k + '\n'
139
                        else:
140
                            exp_calls += u'%s %s\n' % (str_k, v)
141
                    self.assertEqual(exp_calls, out.getvalue())
142
                    out = StringIO()
143

    
144
    def test_print_list(self):
145
        from kamaki.cli.utils import print_list, INDENT_TAB
146
        out = StringIO()
147
        self.assertRaises(AssertionError, print_list, 'non-list non-tuple')
148
        self.assertRaises(AssertionError, print_list, {}, indent=-10)
149
        for args in product(
150
                (
151
                    ['v1', ],
152
                    ('v2', 'v3'),
153
                    [1, '2', 'v3'],
154
                    ({'k1': 'v1'}, 2, 'v3'),
155
                    [(1, 2), 'v2', [(3, 4), {'k3': [5, 6], 'k4': 7}]]),
156
                (tuple(), ('v1', ), ('v1', 1), ('v1', 'k3')),
157
                (0, 1, 2, 9), (False, True), (False, True)):
158
            l, exclude, indent, with_enumeration, recursive_enumeration = args
159
            with patch('kamaki.cli.utils.print_dict') as PD:
160
                with patch('kamaki.cli.utils.print_list') as PL:
161
                    pd_calls, pl_calls = 0, 0
162
                    print_list(*args, out=out)
163
                    exp_calls = ''
164
                    for i, v in enumerate(l):
165
                        str_v = u' ' * indent
166
                        str_v += u'%s.' % (i + 1) if with_enumeration else u''
167
                        if isinstance(v, dict):
168
                            if with_enumeration:
169
                                exp_calls += str_v + '\n'
170
                            elif i and i < len(l):
171
                                exp_calls += u'\n'
172
                            self.assertEqual(
173
                                PD.mock_calls[pd_calls],
174
                                call(
175
                                    v,
176
                                    exclude,
177
                                    indent + (
178
                                        INDENT_TAB if with_enumeration else 0),
179
                                    recursive_enumeration,
180
                                    recursive_enumeration,
181
                                    out))
182
                            pd_calls += 1
183
                        elif isinstance(v, list) or isinstance(v, tuple):
184
                            if with_enumeration:
185
                                exp_calls += str_v + '\n'
186
                            elif i and i < len(l):
187
                                exp_calls += u'\n'
188
                            self.assertEqual(
189
                                PL.mock_calls[pl_calls],
190
                                call(
191
                                    v,
192
                                    exclude,
193
                                    indent + INDENT_TAB,
194
                                    recursive_enumeration,
195
                                    recursive_enumeration,
196
                                    out))
197
                            pl_calls += 1
198
                        elif ('%s' % v) in exclude:
199
                            continue
200
                        else:
201
                            exp_calls += u'%s%s\n' % (str_v, v)
202
                    self.assertEqual(out.getvalue(), exp_calls)
203
                    out = StringIO()
204

    
205
    @patch('kamaki.cli.utils.print_dict')
206
    @patch('kamaki.cli.utils.print_list')
207
    @patch('kamaki.cli.utils.bold', return_value='bold')
208
    def test_print_items(self, bold, PL, PD):
209
        from kamaki.cli.utils import print_items, INDENT_TAB
210
        for args in product(
211
                (
212
                    42, None, 'simple outputs',
213
                    [1, 2, 3], {1: 1, 2: 2}, (3, 4),
214
                    ({'k': 1, 'id': 2}, [5, 6, 7], (8, 9), '10')),
215
                (('id', 'name'), ('something', 2), ('lala', )),
216
                (False, True),
217
                (False, True)):
218
            items, title, with_enumeration, with_redundancy = args
219
            pl_counter, pd_counter = len(PL.mock_calls), len(PD.mock_calls)
220
            bold_counter, out_counter = len(bold.mock_calls), 0
221
            out = StringIO()
222
            print_items(*args, out=out)
223
            out.seek(0)
224
            if not (isinstance(items, dict) or isinstance(
225
                    items, list) or isinstance(items, tuple)):
226
                if items:
227
                    self.assertEqual(out.getvalue(), '%s\n' % items)
228
            else:
229
                for i, item in enumerate(items):
230
                    if with_enumeration:
231
                        exp_str = '%s. ' % (i + 1)
232
                        self.assertEqual(out.read(len(exp_str)), exp_str)
233
                    if isinstance(item, dict):
234
                        title = sorted(set(title).intersection(item))
235
                        pick = item.get if with_redundancy else item.pop
236
                        header = ' '.join('%s' % pick(key) for key in title)
237
                        self.assertEqual(
238
                            bold.mock_calls[bold_counter], call(header))
239
                        self.assertEqual(out.read(5), 'bold\n')
240
                        self.assertEqual(
241
                            PD.mock_calls[pd_counter],
242
                            call(item, indent=INDENT_TAB, out=out))
243
                        pd_counter += 1
244
                        bold_counter += 1
245
                    elif isinstance(item, list) or isinstance(item, tuple):
246
                        self.assertEqual(
247
                            PL.mock_calls[pl_counter],
248
                            call(item, indent=INDENT_TAB, out=out))
249
                        pl_counter += 1
250
                    else:
251
                        exp_str = u' %s\n' % item
252
                        self.assertEqual(out.read(len(exp_str)), exp_str)
253

    
254
    def test_format_size(self):
255
        from kamaki.cli.utils import format_size
256
        from kamaki.cli import CLIError
257
        for v in ('wrong', {1: '1', 2: '2'}, ('tuples', 'not OK'), [1, 2]):
258
            self.assertRaises(CLIError, format_size, v)
259
        for step, B, K, M, G, T in (
260
                (1000, 'B', 'KB', 'MB', 'GB', 'TB'),
261
                (1024, 'B', 'KiB', 'MiB', 'GiB', 'TiB')):
262
            Ki, Mi, Gi = step, step * step, step * step * step
263
            for before, after in (
264
                    (0, '0' + B), (512, '512' + B), (
265
                        Ki - 1, '%s%s' % (step - 1, B)),
266
                    (Ki, '1' + K), (42 * Ki, '42' + K), (
267
                        Mi - 1, '%s.99%s' % (step - 1, K)),
268
                    (Mi, '1' + M), (42 * Mi, '42' + M), (
269
                        Ki * Mi - 1, '%s.99%s' % (step - 1, M)),
270
                    (Gi, '1' + G), (42 * Gi, '42' + G), (
271
                        Mi * Mi - 1, '%s.99%s' % (step - 1, G)),
272
                    (Mi * Mi, '1' + T), (42 * Mi * Mi, '42' + T), (
273
                        Mi * Gi - 1, '%s.99%s' % (step - 1, T)), (
274
                        42 * Mi * Gi, '%s%s' % (42 * Ki, T))):
275
                self.assertEqual(format_size(before, step == 1000), after)
276

    
277
    def test_to_bytes(self):
278
        from kamaki.cli.utils import to_bytes
279
        for v in ('wrong', 'KABUM', 'kbps', 'kibps'):
280
            self.assertRaises(ValueError, to_bytes, v, 'B')
281
            self.assertRaises(ValueError, to_bytes, 42, v)
282
        for v in ([1, 2, 3], ('kb', 'mb'), {'kb': 1, 'byte': 2}):
283
            self.assertRaises(TypeError, to_bytes, v, 'B')
284
            self.assertRaises(AttributeError, to_bytes, 42, v)
285
        kl, ki = 1000, 1024
286
        for size, (unit, factor) in product(
287
                (0, 42, 3.14, 1023, 10000),
288
                (
289
                    ('B', 1), ('b', 1),
290
                    ('KB', kl), ('KiB', ki),
291
                    ('mb', kl * kl), ('mIb', ki * ki),
292
                    ('gB', kl * kl * kl), ('GIB', ki * ki * ki),
293
                    ('TB', kl * kl * kl * kl), ('tiB', ki * ki * ki * ki))):
294
            self.assertEqual(to_bytes(size, unit), int(size * factor))
295

    
296
    def test_dict2file(self):
297
        from kamaki.cli.utils import dict2file, INDENT_TAB
298
        for d, depth in product((
299
                    {'k': 42},
300
                    {'k1': 'v1', 'k2': [1, 2, 3], 'k3': {'k': 'v'}},
301
                    {'k1': {
302
                        'k1.1': 'v1.1',
303
                        'k1.2': [1, 2, 3],
304
                        'k1.3': {'k': 'v'}}}),
305
                (-42, 0, 42)):
306
            exp = ''
307
            exp_d = []
308
            exp_l = []
309
            exp, exp_d, exp_l = '', [], []
310
            with NamedTemporaryFile() as f:
311
                for k, v in d.items():
312
                    sfx = '\n'
313
                    if isinstance(v, dict):
314
                        exp_d.append(call(v, f, depth + 1))
315
                    elif isinstance(v, tuple) or isinstance(v, list):
316
                        exp_l.append(call(v, f, depth + 1))
317
                    else:
318
                        sfx = '%s\n' % v
319
                    exp += '%s%s: %s' % (
320
                        ' ' * (depth * INDENT_TAB), k, sfx)
321
                with patch('kamaki.cli.utils.dict2file') as D2F:
322
                    with patch('kamaki.cli.utils.list2file') as L2F:
323
                        dict2file(d, f, depth)
324
                        f.seek(0)
325
                        self.assertEqual(f.read(), exp)
326
                        self.assertEqual(L2F.mock_calls, exp_l)
327
                        self.assertEqual(D2F.mock_calls, exp_d)
328

    
329
    def test_list2file(self):
330
        from kamaki.cli.utils import list2file, INDENT_TAB
331
        for l, depth in product(
332
                (
333
                    (1, 2, 3),
334
                    [1, 2, 3],
335
                    ('v', [1, 2, 3], (1, 2, 3), {'1': 1, 2: '2', 3: 3}),
336
                    ['v', {'k1': 'v1', 'k2': [1, 2, 3], 'k3': {1: '1'}}]),
337
                (-42, 0, 42)):
338
            with NamedTemporaryFile() as f:
339
                exp, exp_d, exp_l = '', [], []
340
                for v in l:
341
                    if isinstance(v, dict):
342
                        exp_d.append(call(v, f, depth + 1))
343
                    elif isinstance(v, list) or isinstance(v, tuple):
344
                        exp_l.append(call(v, f, depth + 1))
345
                    else:
346
                        exp += '%s%s\n' % (' ' * INDENT_TAB * depth, v)
347
                with patch('kamaki.cli.utils.dict2file') as D2F:
348
                    with patch('kamaki.cli.utils.list2file') as L2F:
349
                        list2file(l, f, depth)
350
                        f.seek(0)
351
                        self.assertEqual(f.read(), exp)
352
                        self.assertEqual(L2F.mock_calls, exp_l)
353
                        self.assertEqual(D2F.mock_calls, exp_d)
354

    
355
    def test__parse_with_regex(self):
356
        from re import compile as r_compile
357
        from kamaki.cli.utils import _parse_with_regex
358
        for args in product(
359
                (
360
                    'this is a line',
361
                    'this_is_also_a_line',
362
                    'This "text" is quoted',
363
                    'This "quoted" "text" is more "complicated"',
364
                    'Is this \'quoted\' text "double \'quoted\' or not?"',
365
                    '"What \'about\' the" oposite?',
366
                    ' Try with a " single double quote',
367
                    'Go "down \'deep " deeper \'bottom \' up" go\' up" !'),
368
                (
369
                    '\'.*?\'|".*?"|^[\S]*$',
370
                    r'"([A-Za-z0-9_\./\\-]*)"',
371
                    r'\"(.+?)\"',
372
                    '\\^a\\.\\*\\$')):
373
            r_parser = r_compile(args[1])
374
            self.assertEqual(
375
                _parse_with_regex(*args),
376
                (r_parser.split(args[0]), r_parser.findall(args[0])))
377

    
378
    def test_split_input(self):
379
        from kamaki.cli.utils import split_input
380
        for line, expected in (
381
                ('set key="v1"', ['set', 'key=v1']),
382
                ('unparsable', ['unparsable']),
383
                ('"parsable"', ['parsable']),
384
                ('"parse" out', ['parse', 'out']),
385
                ('"one', ['"one']),
386
                ('two" or" more"', ['two or', 'more"']),
387
                ('Go "down \'deep " deeper \'bottom \' up" go\' up" !', [
388
                    'Go', "down 'deep ", 'deeper', 'bottom ',
389
                    'up go\' up', '!']),
390
                ('Is "this" a \'parsed\' string?', [
391
                    'Is', 'this', 'a', 'parsed', 'string?'])):
392
            self.assertEqual(split_input(line), expected)
393

    
394
    def test_ask_user(self):
395
        from kamaki.cli.utils import ask_user
396
        msg = u'some question'
397
        out = StringIO()
398
        user_in = StringIO(u'n')
399
        self.assertFalse(ask_user(msg, out=out, user_in=user_in))
400
        self.assertEqual(out.getvalue(), u'%s [y/N]: ' % msg)
401

    
402
        user_in.seek(0)
403
        out.seek(0)
404
        self.assertTrue(ask_user(msg, ('n', ), out=out, user_in=user_in))
405
        self.assertEqual(out.getvalue(), u'%s [n/<not n>]: ' % msg)
406

    
407
        user_in = StringIO(unicode('N'))
408
        out.seek(0)
409
        self.assertTrue(ask_user(msg, ('r', 'N'), out=out, user_in=user_in))
410
        self.assertEqual(out.getvalue(), u'%s [r, N/<not r, N>]: ' % msg)
411

    
412
    def test_remove_from_items(self):
413
        from kamaki.cli.utils import remove_from_items
414
        for v in ('wrong', [1, 2, 3], [{}, 2, {}]):
415
            self.assertRaises(AssertionError, remove_from_items, v, 'none')
416
        d = dict(k1=1, k2=dict(k2=2, k3=3), k3=3, k4=4)
417
        for k in (d.keys() + ['kN']):
418
            tmp1, tmp2 = dict(d), dict(d)
419
            remove_from_items([tmp1, ], k)
420
            tmp1.pop(k, None)
421
            self.assert_dicts_are_equal(tmp1, tmp2)
422
        for k in (d.keys() + ['kN']):
423
            tmp1, tmp2 = dict(d), dict(d)
424
            remove_from_items([tmp1, tmp2], k)
425
            self.assert_dicts_are_equal(tmp1, tmp2)
426

    
427
    def test_filter_dicts_by_dict(self):
428
        from kamaki.cli.utils import filter_dicts_by_dict
429

    
430
        dlist = [
431
            dict(k1='v1', k2='v2', k3='v3'),
432
            dict(k1='v1'),
433
            dict(k2='v2', k3='v3'),
434
            dict(k1='V1', k3='V3'),
435
            dict()]
436
        for l, f, em, cs, exp in (
437
                (dlist, dlist[2], True, False, dlist[0:1] + dlist[2:3]),
438
                (dlist, dlist[1], True, False, dlist[0:2] + dlist[3:4]),
439
                (dlist, dlist[1], True, True, dlist[0:2]),
440
                (dlist, {'k3': 'v'}, True, False, []),
441
                (dlist, {'k3': 'v'}, False, False, dlist[0:1] + dlist[2:4]),
442
                (dlist, {'k3': 'v'}, False, True, dlist[0:1] + dlist[2:3]),
443
                (dlist, {'k3': 'v'}, True, True, []),
444
                (dlist, dlist[4], True, False, dlist),
445
                ):
446
            self.assertEqual(exp, filter_dicts_by_dict(l, f, em, cs))
447

    
448

    
449
if __name__ == '__main__':
450
    from sys import argv
451
    from kamaki.cli.test import runTestCase
452
    runTestCase(UtilsMethods, 'UtilsMethods', argv[1:])