1+ #
12# coding=utf-8
23"""
34Cmd2 testing for argument parsing
45"""
6+ import readline
7+
58import pytest
9+ from unittest import mock
610
711from cmd2 import cmd2
812import cmd2_submenu
9- from . conftest import run_cmd , StdOut , normalize
13+ from conftest import run_cmd , StdOut , normalize
1014
1115
1216class SecondLevelB (cmd2 .Cmd ):
1317 """To be used as a second level command class. """
1418
1519 def __init__ (self , * args , ** kwargs ):
16- super ().__init__ (self , * args , ** kwargs )
20+ super ().__init__ (* args , ** kwargs )
1721 self .prompt = '2ndLevel B '
1822
1923 def do_get_top_level_attr (self , line ):
@@ -27,7 +31,7 @@ class SecondLevel(cmd2.Cmd):
2731 """To be used as a second level command class. """
2832
2933 def __init__ (self , * args , ** kwargs ):
30- super ().__init__ (self , * args , ** kwargs )
34+ super ().__init__ (* args , ** kwargs )
3135 self .prompt = '2ndLevel '
3236 self .top_level_attr = None
3337
@@ -67,7 +71,7 @@ class SubmenuApp(cmd2.Cmd):
6771 """To be used as the main / top level command class that will contain other submenus."""
6872
6973 def __init__ (self , * args , ** kwargs ):
70- super ().__init__ (self , * args , ** kwargs )
74+ super ().__init__ (* args , ** kwargs )
7175 self .prompt = 'TopLevel '
7276 self .top_level_attr = 123456789
7377
@@ -103,7 +107,6 @@ def secondlevel_app_b():
103107 app .stdout = StdOut ()
104108 return app
105109
106-
107110def run_submenu_cmd (app , second_level_app , cmd ):
108111 """ Clear StdOut buffers, run the command, extract the buffer contents."""
109112 app .stdout .clear ()
@@ -115,15 +118,52 @@ def run_submenu_cmd(app, second_level_app, cmd):
115118 second_level_app .stdout .clear ()
116119 return normalize (out1 ), normalize (out2 )
117120
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
118154
155+ ######
156+ #
157+ # test submenu functionality
158+ #
159+ ######
119160def test_submenu_say_from_top_level (submenu_app ):
120161 line = 'testing'
121162 out1 , out2 = run_submenu_cmd (submenu_app , second_level_cmd , 'say ' + line )
122163 assert len (out1 ) == 1
123164 assert len (out2 ) == 0
124165 assert out1 [0 ] == "You called a command in TopLevel with {!r}." .format (line )
125166
126-
127167def test_submenu_second_say_from_top_level (submenu_app ):
128168 line = 'testing'
129169 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):
135175 assert len (out2 ) == 1
136176 assert out2 [0 ] == "You called a command in SecondLevel with {!r}." .format (line )
137177
138-
139178def test_submenu_say_from_second_level (secondlevel_app ):
140179 line = 'testing'
141180 out = run_cmd (secondlevel_app , 'say ' + line )
142181 assert out == ["You called a command in SecondLevel with '%s'." % line ]
143182
144-
145183def test_submenu_help_second_say_from_top_level (submenu_app ):
146184 out1 , out2 = run_submenu_cmd (submenu_app , second_level_cmd , 'help second say' )
147185 # No output expected from the top level
@@ -150,29 +188,24 @@ def test_submenu_help_second_say_from_top_level(submenu_app):
150188 # Output expected from the second level
151189 assert out2 == ["This is a second level menu. Options are qwe, asd, zxc" ]
152190
153-
154191def test_submenu_help_say_from_second_level (secondlevel_app ):
155192 out = run_cmd (secondlevel_app , 'help say' )
156193 assert out == ["This is a second level menu. Options are qwe, asd, zxc" ]
157194
158-
159195def test_submenu_help_second (submenu_app ):
160196 out1 , out2 = run_submenu_cmd (submenu_app , second_level_cmd , 'help second' )
161197 out3 = run_cmd (second_level_cmd , 'help' )
162198 assert out2 == out3
163199
164-
165200def test_submenu_from_top_help_second_say (submenu_app ):
166201 out1 , out2 = run_submenu_cmd (submenu_app , second_level_cmd , 'help second say' )
167202 out3 = run_cmd (second_level_cmd , 'help say' )
168203 assert out2 == out3
169204
170-
171205def test_submenu_shared_attribute (submenu_app ):
172206 out1 , out2 = run_submenu_cmd (submenu_app , second_level_cmd , 'second get_top_level_attr' )
173207 assert out2 == [str (submenu_app .top_level_attr )]
174208
175-
176209def test_submenu_shared_attribute_preserve (submenu_app ):
177210 out1 , out2 = run_submenu_cmd (submenu_app , second_level_b_cmd , 'secondb get_top_level_attr' )
178211 assert out2 == [str (submenu_app .top_level_attr )]
@@ -184,127 +217,80 @@ def test_submenu_shared_attribute_preserve(submenu_app):
184217
185218######
186219#
187- # from test_completion.py
220+ # test completion in submenus
188221#
189222######
190223
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'
233226 line = 'second {}' .format (text )
234227 endidx = len (line )
235228 begidx = endidx - len (text )
236229
237- first_match = complete_tester (text , line , begidx , endidx , sb_app )
230+ first_match = complete_tester (text , line , begidx , endidx , submenu_app )
238231
239232 # 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 ' ]
242234
243- def test_cmd2_submenu_completion_multiple (sb_app ):
235+ def test_cmd2_submenu_completion_multiple (submenu_app ):
244236 text = 'e'
245237 line = 'second {}' .format (text )
246238 endidx = len (line )
247239 begidx = endidx - len (text )
248240
249241 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 )
251243
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
253245
254-
255- def test_cmd2_submenu_completion_nomatch (sb_app ):
246+ def test_cmd2_submenu_completion_nomatch (submenu_app ):
256247 text = 'z'
257248 line = 'second {}' .format (text )
258249 endidx = len (line )
259250 begidx = endidx - len (text )
260251
261- first_match = complete_tester (text , line , begidx , endidx , sb_app )
252+ first_match = complete_tester (text , line , begidx , endidx , submenu_app )
262253 assert first_match is None
263254
264-
265- def test_cmd2_submenu_completion_after_submenu_match (sb_app ):
255+ def test_cmd2_submenu_completion_after_submenu_match (submenu_app ):
266256 text = 'a'
267- line = 'second foo {}' .format (text )
257+ line = 'second say {}' .format (text )
268258 endidx = len (line )
269259 begidx = endidx - len (text )
270260
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 ' ]
274263
275- def test_cmd2_submenu_completion_after_submenu_nomatch (sb_app ):
264+ def test_cmd2_submenu_completion_after_submenu_nomatch (submenu_app ):
276265 text = 'b'
277- line = 'second foo {}' .format (text )
266+ line = 'second say {}' .format (text )
278267 endidx = len (line )
279268 begidx = endidx - len (text )
280269
281- first_match = complete_tester (text , line , begidx , endidx , sb_app )
270+ first_match = complete_tester (text , line , begidx , endidx , submenu_app )
282271 assert first_match is None
283272
284-
285- def test_cmd2_help_submenu_completion_multiple (sb_app ):
273+ def test_cmd2_help_submenu_completion_multiple (submenu_app ):
286274 text = 'p'
287275 line = 'help second {}' .format (text )
288276 endidx = len (line )
289277 begidx = endidx - len (text )
290278
291- matches = sorted (sb_app .complete_help (text , line , begidx , endidx ))
279+ matches = sorted (submenu_app .complete_help (text , line , begidx , endidx ))
292280 assert matches == ['py' , 'pyscript' ]
293281
294-
295- def test_cmd2_help_submenu_completion_nomatch (sb_app ):
282+ def test_cmd2_help_submenu_completion_nomatch (submenu_app ):
296283 text = 'fake'
297284 line = 'help second {}' .format (text )
298285 endidx = len (line )
299286 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 ) == []
302288
303- def test_cmd2_help_submenu_completion_subcommands (sb_app ):
289+ def test_cmd2_help_submenu_completion_subcommands (submenu_app ):
304290 text = 'p'
305291 line = 'help second {}' .format (text )
306292 endidx = len (line )
307293 begidx = endidx - len (text )
308294
309- matches = sorted (sb_app .complete_help (text , line , begidx , endidx ))
295+ matches = sorted (submenu_app .complete_help (text , line , begidx , endidx ))
310296 assert matches == ['py' , 'pyscript' ]
0 commit comments