Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aws
GitHub Repository: aws/aws-cli
Path: blob/develop/tests/unit/test_help.py
1566 views
1
# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License"). You
4
# may not use this file except in compliance with the License. A copy of
5
# the License is located at
6
#
7
# http://aws.amazon.com/apache2.0/
8
#
9
# or in the "license" file accompanying this file. This file is
10
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
# ANY KIND, either express or implied. See the License for the specific
12
# language governing permissions and limitations under the License.
13
from awscli.testutils import mock, unittest, skip_if_windows, FileCreator
14
import signal
15
import platform
16
import json
17
import sys
18
import os
19
20
from awscli.help import PosixHelpRenderer, ExecutableNotFoundError
21
from awscli.help import WindowsHelpRenderer, ProviderHelpCommand, HelpCommand
22
from awscli.help import TopicListerCommand, TopicHelpCommand
23
from awscli.argparser import HELP_BLURB
24
from awscli.compat import StringIO
25
26
27
class HelpSpyMixin(object):
28
def __init__(self):
29
self.exists_on_path = {}
30
self.popen_calls = []
31
self.mock_popen = mock.Mock()
32
33
def _exists_on_path(self, name):
34
return self.exists_on_path.get(name)
35
36
def _popen(self, *args, **kwargs):
37
self.popen_calls.append((args, kwargs))
38
return self.mock_popen
39
40
41
class FakePosixHelpRenderer(HelpSpyMixin, PosixHelpRenderer):
42
def __init__(self, output_stream=sys.stdout):
43
HelpSpyMixin.__init__(self)
44
PosixHelpRenderer.__init__(self, output_stream)
45
46
47
class FakeWindowsHelpRenderer(HelpSpyMixin, WindowsHelpRenderer):
48
def __init__(self, output_stream=sys.stdout):
49
HelpSpyMixin.__init__(self)
50
WindowsHelpRenderer.__init__(self, output_stream)
51
52
53
class TestHelpPager(unittest.TestCase):
54
55
def setUp(self):
56
self.environ = {}
57
self.environ_patch = mock.patch('os.environ', self.environ)
58
self.environ_patch.start()
59
self.renderer = PosixHelpRenderer()
60
61
def tearDown(self):
62
self.environ_patch.stop()
63
64
def test_no_env_vars(self):
65
self.assertEqual(self.renderer.get_pager_cmdline(),
66
self.renderer.PAGER.split())
67
68
def test_manpager(self):
69
pager_cmd = 'foobar'
70
os.environ['MANPAGER'] = pager_cmd
71
self.assertEqual(self.renderer.get_pager_cmdline(),
72
pager_cmd.split())
73
74
def test_pager(self):
75
pager_cmd = 'fiebaz'
76
os.environ['PAGER'] = pager_cmd
77
self.assertEqual(self.renderer.get_pager_cmdline(),
78
pager_cmd.split())
79
80
def test_both(self):
81
os.environ['MANPAGER'] = 'foobar'
82
os.environ['PAGER'] = 'fiebaz'
83
self.assertEqual(self.renderer.get_pager_cmdline(),
84
'foobar'.split())
85
86
def test_manpager_with_args(self):
87
pager_cmd = 'less -X'
88
os.environ['MANPAGER'] = pager_cmd
89
self.assertEqual(self.renderer.get_pager_cmdline(),
90
pager_cmd.split())
91
92
def test_pager_with_args(self):
93
pager_cmd = 'less -X --clearscreen'
94
os.environ['PAGER'] = pager_cmd
95
self.assertEqual(self.renderer.get_pager_cmdline(),
96
pager_cmd.split())
97
98
@skip_if_windows('Requires posix system.')
99
def test_no_groff_or_mandoc_exists(self):
100
renderer = FakePosixHelpRenderer()
101
renderer.exists_on_path['groff'] = False
102
renderer.exists_on_path['mandoc'] = False
103
expected_error = 'Could not find executable named "groff or mandoc"'
104
with self.assertRaisesRegex(ExecutableNotFoundError, expected_error):
105
renderer.render('foo')
106
107
@skip_if_windows('Requires POSIX system.')
108
def test_renderer_falls_back_to_mandoc(self):
109
stdout = StringIO()
110
renderer = FakePosixHelpRenderer(output_stream=stdout)
111
112
renderer.exists_on_path['groff'] = False
113
renderer.exists_on_path['mandoc'] = True
114
renderer.mock_popen.communicate.return_value = (b'foo', '')
115
renderer.render('foo')
116
self.assertEqual(stdout.getvalue(), 'foo\n')
117
118
@skip_if_windows('Requires POSIX system.')
119
def test_no_pager_exists(self):
120
fake_pager = 'foobar'
121
os.environ['MANPAGER'] = fake_pager
122
stdout = StringIO()
123
renderer = FakePosixHelpRenderer(output_stream=stdout)
124
renderer.exists_on_path[fake_pager] = False
125
126
renderer.exists_on_path['groff'] = True
127
renderer.mock_popen.communicate.return_value = (b'foo', '')
128
renderer.render('foo')
129
self.assertEqual(stdout.getvalue(), 'foo\n')
130
131
def test_shlex_split_for_pager_var(self):
132
pager_cmd = '/bin/sh -c "col -bx | vim -c \'set ft=man\' -"'
133
os.environ['PAGER'] = pager_cmd
134
self.assertEqual(self.renderer.get_pager_cmdline(),
135
['/bin/sh', '-c', "col -bx | vim -c 'set ft=man' -"])
136
137
def test_can_render_contents(self):
138
renderer = FakePosixHelpRenderer()
139
renderer.exists_on_path['groff'] = True
140
renderer.exists_on_path['less'] = True
141
renderer.mock_popen.communicate.return_value = ('rendered', '')
142
renderer.render('foo')
143
self.assertEqual(renderer.popen_calls[-1][0], (['less', '-R'],))
144
145
def test_can_page_output_on_windows(self):
146
renderer = FakeWindowsHelpRenderer()
147
renderer.mock_popen.communicate.return_value = ('rendered', '')
148
renderer.render('foo')
149
self.assertEqual(renderer.popen_calls[-1][0], (['more'],))
150
151
@skip_if_windows("Ctrl-C not valid on windows.")
152
def test_can_handle_ctrl_c(self):
153
class CtrlCRenderer(FakePosixHelpRenderer):
154
def _popen(self, *args, **kwargs):
155
if self._is_pager_call(args):
156
os.kill(os.getpid(), signal.SIGINT)
157
return self.mock_popen
158
159
def _is_pager_call(self, args):
160
return 'less' in args[0]
161
162
renderer = CtrlCRenderer()
163
renderer.mock_popen.communicate.return_value = ('send to pager', '')
164
renderer.exists_on_path['groff'] = True
165
renderer.exists_on_path['less'] = True
166
renderer.render('foo')
167
last_call = renderer.mock_popen.communicate.call_args_list[-1]
168
self.assertEqual(last_call, mock.call(input='send to pager'))
169
170
171
class TestHelpCommandBase(unittest.TestCase):
172
def setUp(self):
173
self.session = mock.Mock()
174
self.file_creator = FileCreator()
175
176
def tearDown(self):
177
self.file_creator.remove_all()
178
179
180
class TestHelpCommand(TestHelpCommandBase):
181
"""Test some of the deeper functionality of the HelpCommand
182
183
We do this by subclassing from HelpCommand and ensure it is behaving
184
as expected.
185
"""
186
def setUp(self):
187
super(TestHelpCommand, self).setUp()
188
self.doc_handler_mock = mock.Mock()
189
self.subcommand_mock = mock.Mock()
190
self.renderer = mock.Mock()
191
192
class SampleHelpCommand(HelpCommand):
193
EventHandlerClass = self.doc_handler_mock
194
195
@property
196
def subcommand_table(sample_help_cmd_self):
197
return {'mycommand': self.subcommand_mock}
198
199
self.cmd = SampleHelpCommand(self.session, None, None, None)
200
self.cmd.renderer = self.renderer
201
202
def test_subcommand_call(self):
203
self.cmd(['mycommand'], None)
204
self.subcommand_mock.assert_called_with([], None)
205
self.assertFalse(self.doc_handler_mock.called)
206
207
def test_regular_call(self):
208
self.cmd([], None)
209
self.assertFalse(self.subcommand_mock.called)
210
self.doc_handler_mock.assert_called_with(self.cmd)
211
self.assertTrue(self.renderer.render.called)
212
213
def test_invalid_subcommand(self):
214
with mock.patch('sys.stderr') as f:
215
with self.assertRaises(SystemExit):
216
self.cmd(['no-exist-command'], None)
217
# We should see the pointer to "aws help" in the error message.
218
error_message = ''.join(arg[0][0] for arg in f.write.call_args_list)
219
self.assertIn(HELP_BLURB, error_message)
220
221
222
class TestProviderHelpCommand(TestHelpCommandBase):
223
def setUp(self):
224
super(TestProviderHelpCommand, self).setUp()
225
self.session.provider = None
226
self.command_table = {}
227
self.arg_table = {}
228
self.description = None
229
self.synopsis = None
230
self.usage = None
231
232
# Create a temporary index file for ``aws help [command]`` to use.
233
self.tags_dict = {
234
'topic-name-1': {},
235
'topic-name-2': {}
236
}
237
json_index = self.file_creator.create_file('index.json', '')
238
with open(json_index, 'w') as f:
239
json.dump(self.tags_dict, f, indent=4, sort_keys=True)
240
self.json_patch = mock.patch(
241
'awscli.topictags.TopicTagDB.index_file', json_index)
242
self.json_patch.start()
243
244
self.cmd = ProviderHelpCommand(self.session, self.command_table,
245
self.arg_table, self.description,
246
self.synopsis, self.usage)
247
248
def tearDown(self):
249
self.json_patch.stop()
250
super(TestProviderHelpCommand, self).tearDown()
251
252
def test_related_items(self):
253
self.assertEqual(self.cmd.related_items, ['aws help topics'])
254
255
def test_subcommand_table(self):
256
subcommand_table = self.cmd.subcommand_table
257
258
self.assertEqual(len(subcommand_table), 3)
259
260
# Ensure there is a topics command
261
self.assertIn('topics', subcommand_table)
262
self.assertIsInstance(subcommand_table['topics'], TopicListerCommand)
263
264
# Ensure the topics are there as well
265
self.assertIn('topic-name-1', subcommand_table)
266
self.assertIsInstance(subcommand_table['topic-name-1'],
267
TopicHelpCommand)
268
self.assertEqual(subcommand_table['topic-name-1'].name, 'topic-name-1')
269
270
self.assertIn('topic-name-2', subcommand_table)
271
self.assertIsInstance(subcommand_table['topic-name-2'],
272
TopicHelpCommand)
273
self.assertEqual(subcommand_table['topic-name-2'].name,
274
'topic-name-2')
275
276
277
class TestTopicListerCommand(TestHelpCommandBase):
278
def setUp(self):
279
super(TestTopicListerCommand, self).setUp()
280
self.cmd = TopicListerCommand(self.session)
281
282
def test_event_class(self):
283
self.assertEqual(self.cmd.event_class, 'topics')
284
285
def test_name(self):
286
self.assertEqual(self.cmd.name, 'topics')
287
288
289
class TestTopicHelpCommand(TestHelpCommandBase):
290
def setUp(self):
291
super(TestTopicHelpCommand, self).setUp()
292
self.name = 'topic-name-1'
293
self.cmd = TopicHelpCommand(self.session, self.name)
294
295
def test_event_class(self):
296
self.assertEqual(self.cmd.event_class, 'topics.topic-name-1')
297
298
def test_name(self):
299
self.assertEqual(self.cmd.name, self.name)
300
301