root / kamaki / cli / argument / test.py @ c0fbf04c
History | View | Annotate | Download (15.2 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 mock import patch, call, MagicMock |
35 |
from unittest import TestCase |
36 |
from StringIO import StringIO |
37 |
from datetime import datetime |
38 |
#from itertools import product
|
39 |
|
40 |
from kamaki.cli import argument, errors |
41 |
from kamaki.cli.config import Config |
42 |
|
43 |
|
44 |
def assert_dicts_are_equal(test_case, d1, d2): |
45 |
for k, v in d1.items(): |
46 |
test_case.assertTrue(k in d2)
|
47 |
if isinstance(v, dict): |
48 |
test_case.assert_dicts_are_equal(v, d2[k]) |
49 |
else:
|
50 |
test_case.assertEqual(unicode(v), unicode(d2[k])) |
51 |
|
52 |
|
53 |
cnf_path = 'kamaki.cli.config.Config'
|
54 |
|
55 |
|
56 |
class Argument(TestCase): |
57 |
|
58 |
def test___init__(self): |
59 |
self.assertRaises(ValueError, argument.Argument, 'non-integer') |
60 |
self.assertRaises(AssertionError, argument.Argument, 1) |
61 |
self.assertRaises(AssertionError, argument.Argument, 0, 'noname') |
62 |
self.assertRaises(AssertionError, argument.Argument, 0, '--no name') |
63 |
self.assertRaises(AssertionError, argument.Argument, 0, ['-n', 'n m']) |
64 |
for arity, help, parsed_name, default in ( |
65 |
(0, 'help 0', '--zero', None), |
66 |
(1, 'help 1', ['--one', '-o'], 'lala'), |
67 |
(-1, 'help -1', ['--help', '--or', '--more'], 0), |
68 |
(0, 'help 0 again', ['--again', ], True)): |
69 |
a = argument.Argument(arity, help, parsed_name, default) |
70 |
if arity:
|
71 |
self.assertEqual(arity, a.arity)
|
72 |
self.assertEqual(help, a.help)
|
73 |
|
74 |
exp_name = parsed_name if (
|
75 |
isinstance(parsed_name, list)) else [parsed_name, ] |
76 |
self.assertEqual(exp_name, a.parsed_name)
|
77 |
|
78 |
exp_default = default or (None if arity else False) |
79 |
self.assertEqual(exp_default, a.default)
|
80 |
|
81 |
def test_value(self): |
82 |
a = argument.Argument(1, parsed_name='--value') |
83 |
for value in (None, '', 0, 0.1, -12, [1, 'a', 2.8], (3, 'lala'), 'pi'): |
84 |
a.value = value |
85 |
self.assertEqual(value, a.value)
|
86 |
|
87 |
def test_update_parser(self): |
88 |
for i, arity in enumerate((-1, 0, 1)): |
89 |
arp = argument.ArgumentParser() |
90 |
pname, aname = '--pname%s' % i, 'a_name_%s' % i |
91 |
a = argument.Argument(arity, 'args', pname, 42) |
92 |
a.update_parser(arp, aname) |
93 |
|
94 |
f = StringIO() |
95 |
arp.print_usage(file=f), f.seek(0)
|
96 |
usage, exp = f.readline(), '[%s%s]\n' % (
|
97 |
pname, (' %s' % aname.upper()) if arity else '') |
98 |
self.assertEqual(usage[-len(exp):], exp) |
99 |
del arp
|
100 |
|
101 |
|
102 |
class ConfigArgument(TestCase): |
103 |
|
104 |
def setUp(self): |
105 |
argument._config_arg = argument.ConfigArgument('Recovered Path')
|
106 |
|
107 |
def test_value(self): |
108 |
c = argument._config_arg |
109 |
self.assertEqual(c.value, None) |
110 |
exp = '/some/random/path'
|
111 |
c.value = exp |
112 |
self.assertTrue(isinstance(c.value, Config)) |
113 |
self.assertEqual(c.file_path, exp)
|
114 |
self.assertEqual(c.value.path, exp)
|
115 |
|
116 |
def test_get(self): |
117 |
c = argument._config_arg |
118 |
c.value = None
|
119 |
with patch('%s.get' % cnf_path, return_value='config') as get: |
120 |
self.assertEqual(c.value.get('global', 'config_cli'), 'config') |
121 |
self.assertEqual(get.mock_calls[-1], call('global', 'config_cli')) |
122 |
|
123 |
@patch('%s.keys' % cnf_path, return_value=( |
124 |
'image_cli', 'config_cli', 'history_cli', 'file')) |
125 |
def test_groups(self, keys): |
126 |
c = argument._config_arg |
127 |
c.value = None
|
128 |
cset = set(c.groups)
|
129 |
self.assertTrue(cset.issuperset(['image', 'config', 'history'])) |
130 |
self.assertEqual(keys.mock_calls[-1], call('global')) |
131 |
self.assertFalse('file' in cset) |
132 |
self.assertEqual(keys.mock_calls[-1], call('global')) |
133 |
|
134 |
@patch('%s.items' % cnf_path, return_value=( |
135 |
('image_cli', 'image'), ('file', 'pithos'), |
136 |
('config_cli', 'config'), ('history_cli', 'history'))) |
137 |
def test_cli_specs(self, items): |
138 |
c = argument._config_arg |
139 |
c.value = None
|
140 |
cset = set(c.cli_specs)
|
141 |
self.assertTrue(cset.issuperset([
|
142 |
('image', 'image'), ('config', 'config'), ('history', 'history')])) |
143 |
self.assertEqual(items.mock_calls[-1], call('global')) |
144 |
self.assertFalse(cset.issuperset([('file', 'pithos'), ])) |
145 |
self.assertEqual(items.mock_calls[-1], call('global')) |
146 |
|
147 |
def test_get_global(self): |
148 |
c = argument._config_arg |
149 |
c.value = None
|
150 |
for k, v in ( |
151 |
('config_cli', 'config'), |
152 |
('image_cli', 'image'), |
153 |
('history_cli', 'history')): |
154 |
with patch('%s.get_global' % cnf_path, return_value=v) as gg: |
155 |
self.assertEqual(c.get_global(k), v)
|
156 |
self.assertEqual(gg.mock_calls[-1], call(k)) |
157 |
|
158 |
def test_get_cloud(self): |
159 |
c = argument._config_arg |
160 |
c.value = None
|
161 |
with patch(
|
162 |
'%s.get_cloud' % cnf_path,
|
163 |
return_value='http://cloud') as get_cloud: |
164 |
self.assertTrue(len(c.get_cloud('mycloud', 'url')) > 0) |
165 |
self.assertEqual(get_cloud.mock_calls[-1], call('mycloud', 'url')) |
166 |
with patch(
|
167 |
'%s.get_cloud' % cnf_path,
|
168 |
side_effect=KeyError('no token')) as get_cloud: |
169 |
self.assertRaises(KeyError, c.get_cloud, 'mycloud', 'token') |
170 |
invalidcloud = 'PLEASE_DO_NOT_EVER_NAME_YOUR_CLOUD_LIKE_THIS111'
|
171 |
self.assertRaises(KeyError, c.get_cloud, invalidcloud, 'url') |
172 |
|
173 |
|
174 |
class RuntimeConfigArgument(TestCase): |
175 |
|
176 |
def setUp(self): |
177 |
argument._config_arg = argument.ConfigArgument('Recovered Path')
|
178 |
|
179 |
@patch('kamaki.cli.argument.Argument.__init__') |
180 |
def test___init__(self, arg): |
181 |
config, help, pname, default = 'config', 'help', 'pname', 'default' |
182 |
rca = argument.RuntimeConfigArgument(config, help, pname, default) |
183 |
self.assertTrue(isinstance(rca, argument.RuntimeConfigArgument)) |
184 |
self.assertEqual(rca._config_arg, config)
|
185 |
self.assertEqual(arg.mock_calls[-1], call(1, help, pname, default)) |
186 |
|
187 |
@patch('%s.override' % cnf_path) |
188 |
def test_value(self, override): |
189 |
config, help, pname, default = argument._config_arg, 'help', '-n', 'df' |
190 |
config.value = None
|
191 |
rca = argument.RuntimeConfigArgument(config, help, pname, default) |
192 |
self.assertEqual(rca.value, default)
|
193 |
|
194 |
for options in ('grp', 'grp.opt', 'k v', '=nokey', 2.8, None, 42, ''): |
195 |
self.assertRaises(TypeError, rca.value, options) |
196 |
|
197 |
for options in ('key=val', 'grp.key=val', 'dotted.opt.key=val'): |
198 |
rca.value = options |
199 |
option, sep, val = options.partition('=')
|
200 |
grp, sep, key = option.partition('.')
|
201 |
grp, key = (grp, key) if key else ('global', grp) |
202 |
self.assertEqual(override.mock_calls[-1], call(grp, key, val)) |
203 |
|
204 |
|
205 |
class FlagArgument(TestCase): |
206 |
|
207 |
@patch('kamaki.cli.argument.Argument.__init__') |
208 |
def test___init__(self, arg): |
209 |
help, pname, default = 'help', 'pname', 'default' |
210 |
fa = argument.FlagArgument(help, pname, default) |
211 |
self.assertTrue(isinstance(fa, argument.FlagArgument)) |
212 |
arg.assert_called_once(0, help, pname, default)
|
213 |
|
214 |
|
215 |
class ValueArgument(TestCase): |
216 |
|
217 |
@patch('kamaki.cli.argument.Argument.__init__') |
218 |
def test___init__(self, arg): |
219 |
help, pname, default = 'help', 'pname', 'default' |
220 |
fa = argument.ValueArgument(help, pname, default) |
221 |
self.assertTrue(isinstance(fa, argument.ValueArgument)) |
222 |
arg.assert_called_once(1, help, pname, default)
|
223 |
|
224 |
|
225 |
class IntArgument(TestCase): |
226 |
|
227 |
def test_value(self): |
228 |
ia = argument.IntArgument(parsed_name='--ia')
|
229 |
self.assertEqual(ia.value, None) |
230 |
for v in (1, 0, -1, 923455555555555555555555555555555): |
231 |
ia.value = v |
232 |
self.assertEqual(ia.value, v)
|
233 |
for v in ('1', '-1', 2.8): |
234 |
ia.value = v |
235 |
self.assertEqual(ia.value, int(v)) |
236 |
for v, err in ( |
237 |
('invalid', errors.CLIError),
|
238 |
(None, TypeError), (False, TypeError), ([1, 2, 3], TypeError)): |
239 |
try:
|
240 |
ia.value = v |
241 |
except Exception as e: |
242 |
self.assertTrue(isinstance(e, err)) |
243 |
|
244 |
|
245 |
class DateArgument(TestCase): |
246 |
|
247 |
def test_timestamp(self): |
248 |
da = argument.DateArgument(parsed_name='--date')
|
249 |
self.assertEqual(da.timestamp, None) |
250 |
date, format, exp = '24-10-1917', '%d-%m-%Y', -1646964000.0 |
251 |
da._value = argument.dtm.strptime(date, format) |
252 |
self.assertEqual(da.timestamp, exp)
|
253 |
|
254 |
def test_formated(self): |
255 |
da = argument.DateArgument(parsed_name='--date')
|
256 |
self.assertEqual(da.formated, None) |
257 |
date, format, exp = ( |
258 |
'24-10-1917', '%d-%m-%Y', 'Wed Oct 24 00:00:00 1917') |
259 |
da._value = argument.dtm.strptime(date, format) |
260 |
self.assertEqual(da.formated, exp)
|
261 |
|
262 |
@patch('kamaki.cli.argument.DateArgument.timestamp') |
263 |
@patch('kamaki.cli.argument.DateArgument.format_date') |
264 |
def test_value(self, format_date, timestamp): |
265 |
da = argument.DateArgument(parsed_name='--date')
|
266 |
self.assertTrue(isinstance(da.value, MagicMock)) |
267 |
da.value = 'Something'
|
268 |
format_date.assert_called_once(call('Something'))
|
269 |
|
270 |
def test_format_date(self): |
271 |
da = argument.DateArgument(parsed_name='--date')
|
272 |
for datestr, exp in ( |
273 |
('Wed Oct 24 01:02:03 1917', datetime(1917, 10, 24, 1, 2, 3)), |
274 |
('24-10-1917', datetime(1917, 10, 24)), |
275 |
('01:02:03 24-10-1917', datetime(1917, 10, 24, 1, 2, 3))): |
276 |
self.assertEqual(da.format_date(datestr), exp)
|
277 |
for datestr, err in ( |
278 |
('32-40-20134', errors.CLIError),
|
279 |
('Wednesday, 24 Oct 2017', errors.CLIError),
|
280 |
(None, TypeError), (0.8, TypeError)): |
281 |
self.assertRaises(err, da.format_date, datestr)
|
282 |
|
283 |
|
284 |
class VersionArgument(TestCase): |
285 |
|
286 |
def test_value(self): |
287 |
va = argument.VersionArgument(parsed_name='--version')
|
288 |
self.assertTrue(va, argument.VersionArgument)
|
289 |
va.value = 'some value'
|
290 |
self.assertEqual(va.value, 'some value') |
291 |
|
292 |
|
293 |
class KeyValueArgument(TestCase): |
294 |
|
295 |
@patch('kamaki.cli.argument.Argument.__init__') |
296 |
def test___init__(self, init): |
297 |
help, pname, default = 'help', 'pname', 'default' |
298 |
kva = argument.KeyValueArgument(help, pname, default) |
299 |
self.assertTrue(isinstance(kva, argument.KeyValueArgument)) |
300 |
self.assertEqual(init.mock_calls[-1], call(-1, help, pname, default)) |
301 |
|
302 |
def test_value(self): |
303 |
kva = argument.KeyValueArgument(parsed_name='--keyval')
|
304 |
self.assertEqual(kva.value, None) |
305 |
for kvpairs in ( |
306 |
'strval', 'key=val', 2.8, 42, None, |
307 |
('key', 'val'), ('key val'), ['=val', 'key=val'], |
308 |
['key1=val1', 'key2 val2'], ('key1 = val1', )): |
309 |
self.assertRaises(errors.CLIError, kva.value, kvpairs)
|
310 |
for kvpairs, exp in ( |
311 |
(('key=val', ), {'key': 'val'}), |
312 |
(['key1=val1', 'key2=val2'], {'key1': 'val1', 'key2': 'val2'}), |
313 |
( |
314 |
('k1=v1 v2', 'k3=', 'k 4=v4'), |
315 |
{'k1': 'v1 v2', 'k3': '', 'k 4': 'v4'}), |
316 |
(('k=v1', 'k=v2', 'k=v3'), {'k': 'v3'}) |
317 |
): |
318 |
kva.value = kvpairs |
319 |
assert_dicts_are_equal(self, kva.value, exp)
|
320 |
|
321 |
|
322 |
class ProgressBarArgument(TestCase): |
323 |
|
324 |
class PseudoBar(object): |
325 |
message = ''
|
326 |
suffix = ''
|
327 |
|
328 |
def start(): |
329 |
pass
|
330 |
|
331 |
@patch('kamaki.cli.argument.FlagArgument.__init__') |
332 |
def test___init__(self, init): |
333 |
help, pname, default = 'help', '--progress', 'default' |
334 |
pba = argument.ProgressBarArgument(help, pname, default) |
335 |
self.assertTrue(isinstance(pba, argument.ProgressBarArgument)) |
336 |
self.assertEqual(pba.suffix, '%(percent)d%%') |
337 |
init.assert_called_once(help, pname, default) |
338 |
|
339 |
def test_clone(self): |
340 |
pba = argument.ProgressBarArgument(parsed_name='--progress')
|
341 |
pba.value = None
|
342 |
pba_clone = pba.clone() |
343 |
self.assertTrue(isinstance(pba, argument.ProgressBarArgument)) |
344 |
self.assertTrue(isinstance(pba_clone, argument.ProgressBarArgument)) |
345 |
self.assertNotEqual(pba, pba_clone)
|
346 |
self.assertEqual(pba.parsed_name, pba_clone.parsed_name)
|
347 |
|
348 |
def test_get_generator(self): |
349 |
pba = argument.ProgressBarArgument(parsed_name='--progress')
|
350 |
pba.value = None
|
351 |
msg, msg_len = 'message', 40 |
352 |
with patch('kamaki.cli.argument.KamakiProgressBar.start') as start: |
353 |
pba.get_generator(msg, msg_len) |
354 |
self.assertTrue(isinstance(pba.bar, argument.KamakiProgressBar)) |
355 |
self.assertNotEqual(pba.bar.message, msg)
|
356 |
self.assertEqual(
|
357 |
pba.bar.message, '%s%s' % (msg, ' ' * (msg_len - len(msg)))) |
358 |
self.assertEqual(pba.bar.suffix, '%(percent)d%% - %(eta)ds') |
359 |
start.assert_called_once() |
360 |
|
361 |
def test_finish(self): |
362 |
pba = argument.ProgressBarArgument(parsed_name='--progress')
|
363 |
pba.value = None
|
364 |
self.assertEqual(pba.finish(), None) |
365 |
pba.bar = argument.KamakiProgressBar() |
366 |
with patch('kamaki.cli.argument.KamakiProgressBar.finish') as finish: |
367 |
pba.finish() |
368 |
finish.assert_called_once() |
369 |
|
370 |
|
371 |
if __name__ == '__main__': |
372 |
from sys import argv |
373 |
from kamaki.cli.test import runTestCase |
374 |
runTestCase(Argument, 'Argument', argv[1:]) |
375 |
runTestCase(ConfigArgument, 'ConfigArgument', argv[1:]) |
376 |
runTestCase(RuntimeConfigArgument, 'RuntimeConfigArgument', argv[1:]) |
377 |
runTestCase(FlagArgument, 'FlagArgument', argv[1:]) |
378 |
runTestCase(FlagArgument, 'ValueArgument', argv[1:]) |
379 |
runTestCase(IntArgument, 'IntArgument', argv[1:]) |
380 |
runTestCase(DateArgument, 'DateArgument', argv[1:]) |
381 |
runTestCase(VersionArgument, 'VersionArgument', argv[1:]) |
382 |
runTestCase(KeyValueArgument, 'KeyValueArgument', argv[1:]) |
383 |
runTestCase(ProgressBarArgument, 'ProgressBarArgument', argv[1:]) |