1
+ #
1
2
# coding=utf-8
2
3
"""
3
4
Cmd2 testing for argument parsing
4
5
"""
6
+ import readline
7
+
5
8
import pytest
9
+ from unittest import mock
6
10
7
11
from cmd2 import cmd2
8
12
import cmd2_submenu
9
- from . conftest import run_cmd , StdOut , normalize
13
+ from conftest import run_cmd , StdOut , normalize
10
14
11
15
12
16
class SecondLevelB (cmd2 .Cmd ):
13
17
"""To be used as a second level command class. """
14
18
15
19
def __init__ (self , * args , ** kwargs ):
16
- super ().__init__ (self , * args , ** kwargs )
20
+ super ().__init__ (* args , ** kwargs )
17
21
self .prompt = '2ndLevel B '
18
22
19
23
def do_get_top_level_attr (self , line ):
@@ -27,7 +31,7 @@ class SecondLevel(cmd2.Cmd):
27
31
"""To be used as a second level command class. """
28
32
29
33
def __init__ (self , * args , ** kwargs ):
30
- super ().__init__ (self , * args , ** kwargs )
34
+ super ().__init__ (* args , ** kwargs )
31
35
self .prompt = '2ndLevel '
32
36
self .top_level_attr = None
33
37
@@ -67,7 +71,7 @@ class SubmenuApp(cmd2.Cmd):
67
71
"""To be used as the main / top level command class that will contain other submenus."""
68
72
69
73
def __init__ (self , * args , ** kwargs ):
70
- super ().__init__ (self , * args , ** kwargs )
74
+ super ().__init__ (* args , ** kwargs )
71
75
self .prompt = 'TopLevel '
72
76
self .top_level_attr = 123456789
73
77
@@ -103,7 +107,6 @@ def secondlevel_app_b():
103
107
app .stdout = StdOut ()
104
108
return app
105
109
106
-
107
110
def run_submenu_cmd (app , second_level_app , cmd ):
108
111
""" Clear StdOut buffers, run the command, extract the buffer contents."""
109
112
app .stdout .clear ()
@@ -115,15 +118,52 @@ def run_submenu_cmd(app, second_level_app, cmd):
115
118
second_level_app .stdout .clear ()
116
119
return normalize (out1 ), normalize (out2 )
117
120
121
+ def complete_tester (text , line , begidx , endidx , app ):
122
+ """
123
+ This is a convenience function to test cmd2.complete() since
124
+ in a unit test environment there is no actual console readline
125
+ is monitoring. Therefore we use mock to provide readline data
126
+ to complete().
127
+
128
+ :param text: str - the string prefix we are attempting to match
129
+ :param line: str - the current input line with leading whitespace removed
130
+ :param begidx: int - the beginning index of the prefix text
131
+ :param endidx: int - the ending index of the prefix text
132
+ :param app: the cmd2 app that will run completions
133
+ :return: The first matched string or None if there are no matches
134
+ Matches are stored in app.completion_matches
135
+ These matches also have been sorted by complete()
136
+ """
137
+ def get_line ():
138
+ return line
139
+
140
+ def get_begidx ():
141
+ return begidx
142
+
143
+ def get_endidx ():
144
+ return endidx
145
+
146
+ first_match = None
147
+ with mock .patch .object (readline , 'get_line_buffer' , get_line ):
148
+ with mock .patch .object (readline , 'get_begidx' , get_begidx ):
149
+ with mock .patch .object (readline , 'get_endidx' , get_endidx ):
150
+ # Run the readline tab-completion function with readline mocks in place
151
+ first_match = app .complete (text , 0 )
152
+
153
+ return first_match
118
154
155
+ ######
156
+ #
157
+ # test submenu functionality
158
+ #
159
+ ######
119
160
def test_submenu_say_from_top_level (submenu_app ):
120
161
line = 'testing'
121
162
out1 , out2 = run_submenu_cmd (submenu_app , second_level_cmd , 'say ' + line )
122
163
assert len (out1 ) == 1
123
164
assert len (out2 ) == 0
124
165
assert out1 [0 ] == "You called a command in TopLevel with {!r}." .format (line )
125
166
126
-
127
167
def test_submenu_second_say_from_top_level (submenu_app ):
128
168
line = 'testing'
129
169
out1 , out2 = run_submenu_cmd (submenu_app , second_level_cmd , 'second say ' + line )
@@ -135,13 +175,11 @@ def test_submenu_second_say_from_top_level(submenu_app):
135
175
assert len (out2 ) == 1
136
176
assert out2 [0 ] == "You called a command in SecondLevel with {!r}." .format (line )
137
177
138
-
139
178
def test_submenu_say_from_second_level (secondlevel_app ):
140
179
line = 'testing'
141
180
out = run_cmd (secondlevel_app , 'say ' + line )
142
181
assert out == ["You called a command in SecondLevel with '%s'." % line ]
143
182
144
-
145
183
def test_submenu_help_second_say_from_top_level (submenu_app ):
146
184
out1 , out2 = run_submenu_cmd (submenu_app , second_level_cmd , 'help second say' )
147
185
# No output expected from the top level
@@ -150,29 +188,24 @@ def test_submenu_help_second_say_from_top_level(submenu_app):
150
188
# Output expected from the second level
151
189
assert out2 == ["This is a second level menu. Options are qwe, asd, zxc" ]
152
190
153
-
154
191
def test_submenu_help_say_from_second_level (secondlevel_app ):
155
192
out = run_cmd (secondlevel_app , 'help say' )
156
193
assert out == ["This is a second level menu. Options are qwe, asd, zxc" ]
157
194
158
-
159
195
def test_submenu_help_second (submenu_app ):
160
196
out1 , out2 = run_submenu_cmd (submenu_app , second_level_cmd , 'help second' )
161
197
out3 = run_cmd (second_level_cmd , 'help' )
162
198
assert out2 == out3
163
199
164
-
165
200
def test_submenu_from_top_help_second_say (submenu_app ):
166
201
out1 , out2 = run_submenu_cmd (submenu_app , second_level_cmd , 'help second say' )
167
202
out3 = run_cmd (second_level_cmd , 'help say' )
168
203
assert out2 == out3
169
204
170
-
171
205
def test_submenu_shared_attribute (submenu_app ):
172
206
out1 , out2 = run_submenu_cmd (submenu_app , second_level_cmd , 'second get_top_level_attr' )
173
207
assert out2 == [str (submenu_app .top_level_attr )]
174
208
175
-
176
209
def test_submenu_shared_attribute_preserve (submenu_app ):
177
210
out1 , out2 = run_submenu_cmd (submenu_app , second_level_b_cmd , 'secondb get_top_level_attr' )
178
211
assert out2 == [str (submenu_app .top_level_attr )]
@@ -184,127 +217,80 @@ def test_submenu_shared_attribute_preserve(submenu_app):
184
217
185
218
######
186
219
#
187
- # from test_completion.py
220
+ # test completion in submenus
188
221
#
189
222
######
190
223
191
- ####################################################
192
-
193
-
194
- class SecondLevel (cmd2 .Cmd ):
195
- """To be used as a second level command class. """
196
-
197
- def __init__ (self , * args , ** kwargs ):
198
- super ().__init__ (self , * args , ** kwargs )
199
- self .prompt = '2ndLevel '
200
-
201
- def do_foo (self , line ):
202
- self .poutput ("You called a command in SecondLevel with '%s'. " % line )
203
-
204
- def help_foo (self ):
205
- self .poutput ("This is a second level menu. Options are qwe, asd, zxc" )
206
-
207
- def complete_foo (self , text , line , begidx , endidx ):
208
- return [s for s in ['qwe' , 'asd' , 'zxc' ] if s .startswith (text )]
209
-
210
-
211
- second_level_cmd = SecondLevel ()
212
-
213
-
214
- @cmd2_submenu .AddSubmenu (second_level_cmd ,
215
- command = 'second' ,
216
- require_predefined_shares = False )
217
- class SubmenuApp (cmd2 .Cmd ):
218
- """To be used as the main / top level command class that will contain other submenus."""
219
-
220
- def __init__ (self , * args , ** kwargs ):
221
- super ().__init__ (self , * args , ** kwargs )
222
- self .prompt = 'TopLevel '
223
-
224
-
225
- @pytest .fixture
226
- def sb_app ():
227
- app = SubmenuApp ()
228
- return app
229
-
230
-
231
- def test_cmd2_submenu_completion_single_end (sb_app ):
232
- text = 'f'
224
+ def test_cmd2_submenu_completion_single_end (submenu_app ):
225
+ text = 'sa'
233
226
line = 'second {}' .format (text )
234
227
endidx = len (line )
235
228
begidx = endidx - len (text )
236
229
237
- first_match = complete_tester (text , line , begidx , endidx , sb_app )
230
+ first_match = complete_tester (text , line , begidx , endidx , submenu_app )
238
231
239
232
# It is at end of line, so extra space is present
240
- assert first_match is not None and sb_app .completion_matches == ['foo ' ]
241
-
233
+ assert first_match is not None and submenu_app .completion_matches == ['say ' ]
242
234
243
- def test_cmd2_submenu_completion_multiple (sb_app ):
235
+ def test_cmd2_submenu_completion_multiple (submenu_app ):
244
236
text = 'e'
245
237
line = 'second {}' .format (text )
246
238
endidx = len (line )
247
239
begidx = endidx - len (text )
248
240
249
241
expected = ['edit' , 'eof' , 'eos' ]
250
- first_match = complete_tester (text , line , begidx , endidx , sb_app )
242
+ first_match = complete_tester (text , line , begidx , endidx , submenu_app )
251
243
252
- assert first_match is not None and sb_app .completion_matches == expected
244
+ assert first_match is not None and submenu_app .completion_matches == expected
253
245
254
-
255
- def test_cmd2_submenu_completion_nomatch (sb_app ):
246
+ def test_cmd2_submenu_completion_nomatch (submenu_app ):
256
247
text = 'z'
257
248
line = 'second {}' .format (text )
258
249
endidx = len (line )
259
250
begidx = endidx - len (text )
260
251
261
- first_match = complete_tester (text , line , begidx , endidx , sb_app )
252
+ first_match = complete_tester (text , line , begidx , endidx , submenu_app )
262
253
assert first_match is None
263
254
264
-
265
- def test_cmd2_submenu_completion_after_submenu_match (sb_app ):
255
+ def test_cmd2_submenu_completion_after_submenu_match (submenu_app ):
266
256
text = 'a'
267
- line = 'second foo {}' .format (text )
257
+ line = 'second say {}' .format (text )
268
258
endidx = len (line )
269
259
begidx = endidx - len (text )
270
260
271
- first_match = complete_tester (text , line , begidx , endidx , sb_app )
272
- assert first_match is not None and sb_app .completion_matches == ['asd ' ]
273
-
261
+ first_match = complete_tester (text , line , begidx , endidx , submenu_app )
262
+ assert first_match is not None and submenu_app .completion_matches == ['asd ' ]
274
263
275
- def test_cmd2_submenu_completion_after_submenu_nomatch (sb_app ):
264
+ def test_cmd2_submenu_completion_after_submenu_nomatch (submenu_app ):
276
265
text = 'b'
277
- line = 'second foo {}' .format (text )
266
+ line = 'second say {}' .format (text )
278
267
endidx = len (line )
279
268
begidx = endidx - len (text )
280
269
281
- first_match = complete_tester (text , line , begidx , endidx , sb_app )
270
+ first_match = complete_tester (text , line , begidx , endidx , submenu_app )
282
271
assert first_match is None
283
272
284
-
285
- def test_cmd2_help_submenu_completion_multiple (sb_app ):
273
+ def test_cmd2_help_submenu_completion_multiple (submenu_app ):
286
274
text = 'p'
287
275
line = 'help second {}' .format (text )
288
276
endidx = len (line )
289
277
begidx = endidx - len (text )
290
278
291
- matches = sorted (sb_app .complete_help (text , line , begidx , endidx ))
279
+ matches = sorted (submenu_app .complete_help (text , line , begidx , endidx ))
292
280
assert matches == ['py' , 'pyscript' ]
293
281
294
-
295
- def test_cmd2_help_submenu_completion_nomatch (sb_app ):
282
+ def test_cmd2_help_submenu_completion_nomatch (submenu_app ):
296
283
text = 'fake'
297
284
line = 'help second {}' .format (text )
298
285
endidx = len (line )
299
286
begidx = endidx - len (text )
300
- assert sb_app .complete_help (text , line , begidx , endidx ) == []
301
-
287
+ assert submenu_app .complete_help (text , line , begidx , endidx ) == []
302
288
303
- def test_cmd2_help_submenu_completion_subcommands (sb_app ):
289
+ def test_cmd2_help_submenu_completion_subcommands (submenu_app ):
304
290
text = 'p'
305
291
line = 'help second {}' .format (text )
306
292
endidx = len (line )
307
293
begidx = endidx - len (text )
308
294
309
- matches = sorted (sb_app .complete_help (text , line , begidx , endidx ))
295
+ matches = sorted (submenu_app .complete_help (text , line , begidx , endidx ))
310
296
assert matches == ['py' , 'pyscript' ]
0 commit comments