1 # Copyright 2013 GRNET S.A. All rights reserved.
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
7 # 1. Redistributions of source code must retain the above
8 # copyright notice, this list of conditions and the following
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.
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.
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.
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
41 class UtilsMethods(TestCase):
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])
49 self.assertEqual(unicode(v), unicode(d2[k]))
51 def test_guess_mime_type(self):
52 from kamaki.cli.utils import guess_mime_type
53 from mimetypes import guess_type
55 ('file.txt', 'file.png', 'file.zip', 'file.gz', None, 'X'),
57 ('an/encoding', None)):
58 filename, ctype, cencoding = args
60 exp_type, exp_enc = guess_type(filename)
62 guess_mime_type(*args),
63 (exp_type or ctype, exp_enc or cencoding))
65 self.assertRaises(AssertionError, guess_mime_type, *args)
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
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')
75 def test_print_dict(self):
76 from kamaki.cli.utils import print_dict, INDENT_TAB
78 self.assertRaises(AssertionError, print_dict, 'non-dict think')
79 self.assertRaises(AssertionError, print_dict, {}, indent=-10)
83 {'k1': 'v1', 'k2': 'v2'},
84 {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'},
85 {'k1': 'v1', 'k2': {'k1': 'v1', 'k2': 'v2'}, 'k3': 'v3'},
87 'k1': {'k1': 'v1', 'k2': 'v2'},
91 'k1': {'k1': 'v1', 'k2': 'v2'},
93 'k3': {'k1': 1, 'k2': [1, 2, 3]}},
98 'k3': {'k1': [(1, 2)]}},
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)
109 for i, (k, v) in enumerate(d.items()):
112 str_k = u' ' * indent
113 str_k += u'%s.' % (i + 1) if with_enumeration else u''
115 if isinstance(v, dict):
117 PD.mock_calls[pd_calls],
122 recursive_enumeration,
123 recursive_enumeration,
126 exp_calls += str_k + '\n'
127 elif isinstance(v, list) or isinstance(v, tuple):
129 PL.mock_calls[pl_calls],
134 recursive_enumeration,
135 recursive_enumeration,
138 exp_calls += str_k + '\n'
140 exp_calls += u'%s %s\n' % (str_k, v)
141 self.assertEqual(exp_calls, out.getvalue())
144 def test_print_list(self):
145 from kamaki.cli.utils import print_list, INDENT_TAB
147 self.assertRaises(AssertionError, print_list, 'non-list non-tuple')
148 self.assertRaises(AssertionError, print_list, {}, indent=-10)
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)
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):
169 exp_calls += str_v + '\n'
170 elif i and i < len(l):
173 PD.mock_calls[pd_calls],
178 INDENT_TAB if with_enumeration else 0),
179 recursive_enumeration,
180 recursive_enumeration,
183 elif isinstance(v, list) or isinstance(v, tuple):
185 exp_calls += str_v + '\n'
186 elif i and i < len(l):
189 PL.mock_calls[pl_calls],
194 recursive_enumeration,
195 recursive_enumeration,
198 elif ('%s' % v) in exclude:
201 exp_calls += u'%s%s\n' % (str_v, v)
202 self.assertEqual(out.getvalue(), exp_calls)
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
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', )),
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
222 print_items(*args, out=out)
224 if not (isinstance(items, dict) or isinstance(
225 items, list) or isinstance(items, tuple)):
227 self.assertEqual(out.getvalue(), '%s\n' % items)
229 for i, item in enumerate(items):
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)
239 bold.mock_calls[bold_counter], call(header))
240 self.assertEqual(out.read(5), 'bold\n')
243 PD.mock_calls[pd_counter],
244 call(item, indent=INDENT_TAB, out=out))
246 elif isinstance(item, list) or isinstance(item, tuple):
248 PL.mock_calls[pl_counter],
249 call(item, indent=INDENT_TAB, out=out))
252 exp_str = u' %s\n' % item
253 self.assertEqual(out.read(len(exp_str)), exp_str)
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)
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)
287 for size, (unit, factor) in product(
288 (0, 42, 3.14, 1023, 10000),
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))
297 def test_dict2file(self):
298 from kamaki.cli.utils import dict2file, INDENT_TAB
299 for d, depth in product((
301 {'k1': 'v1', 'k2': [1, 2, 3], 'k3': {'k': 'v'}},
305 'k1.3': {'k': 'v'}}}),
310 exp, exp_d, exp_l = '', [], []
311 with NamedTemporaryFile() as f:
312 for k, v in d.items():
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))
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)
326 self.assertEqual(f.read(), exp)
327 self.assertEqual(L2F.mock_calls, exp_l)
328 self.assertEqual(D2F.mock_calls, exp_d)
330 def test_list2file(self):
331 from kamaki.cli.utils import list2file, INDENT_TAB
332 for l, depth in product(
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'}}]),
339 with NamedTemporaryFile() as f:
340 exp, exp_d, exp_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))
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)
352 self.assertEqual(f.read(), exp)
353 self.assertEqual(L2F.mock_calls, exp_l)
354 self.assertEqual(D2F.mock_calls, exp_d)
356 def test__parse_with_regex(self):
357 from re import compile as r_compile
358 from kamaki.cli.utils import _parse_with_regex
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" !'),
370 '\'.*?\'|".*?"|^[\S]*$',
371 r'"([A-Za-z0-9_\./\\-]*)"',
374 r_parser = r_compile(args[1])
376 _parse_with_regex(*args),
377 (r_parser.split(args[0]), r_parser.findall(args[0])))
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']),
387 ('two" or" more"', ['two or', 'more"']),
388 ('Go "down \'deep " deeper \'bottom \' up" go\' up" !', [
389 'Go', "down 'deep ", 'deeper', 'bottom ',
391 ('Is "this" a \'parsed\' string?', [
392 'Is', 'this', 'a', 'parsed', 'string?'])):
393 self.assertEqual(split_input(line), expected)
395 def test_ask_user(self):
396 from kamaki.cli.utils import ask_user
397 msg = u'some question'
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)
405 self.assertTrue(ask_user(msg, ('n', ), out=out, user_in=user_in))
406 self.assertEqual(out.getvalue(), u'%s [n/<not n>]: ' % msg)
408 user_in = StringIO(unicode('N'))
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)
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)
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)
428 def test_filter_dicts_by_dict(self):
429 from kamaki.cli.utils import filter_dicts_by_dict
432 dict(k1='v1', k2='v2', k3='v3'),
434 dict(k2='v2', k3='v3'),
435 dict(k1='V1', k3='V3'),
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),
447 self.assertEqual(exp, filter_dicts_by_dict(l, f, em, cs))
450 if __name__ == '__main__':
452 from kamaki.cli.test import runTestCase
453 runTestCase(UtilsMethods, 'UtilsMethods', argv[1:])