Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / utils / test.py @ 12be2bd2

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
                        if header:
238
                            self.assertEqual(
239
                                bold.mock_calls[bold_counter], call(header))
240
                            self.assertEqual(out.read(5), 'bold\n')
241
                            bold_counter += 1
242
                        self.assertEqual(
243
                            PD.mock_calls[pd_counter],
244
                            call(item, indent=INDENT_TAB, out=out))
245
                        pd_counter += 1
246
                    elif isinstance(item, list) or isinstance(item, tuple):
247
                        self.assertEqual(
248
                            PL.mock_calls[pl_counter],
249
                            call(item, indent=INDENT_TAB, out=out))
250
                        pl_counter += 1
251
                    else:
252
                        exp_str = u' %s\n' % item
253
                        self.assertEqual(out.read(len(exp_str)), exp_str)
254

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

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

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

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

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

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

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

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

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

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

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

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

    
449

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