88
99import pytest
1010
11- # Python 3.5 had some regressions in the unitest.mock module, so use 3rd party mock if available
11+ # Python 3.5 had some regressions in the unitest.mock module, so use
12+ # 3rd party mock if available
1213try :
1314 import mock
1415except ImportError :
1516 from unittest import mock
1617
1718import cmd2
1819from .conftest import run_cmd , normalize , HELP_HISTORY
19- from cmd2 .parsing import Statement
20- from cmd2 .history import HistoryItem
2120
2221
23- def test_base_help_history (base_app ):
24- out , err = run_cmd (base_app , 'help history' )
25- assert out == normalize (HELP_HISTORY )
26-
27- def test_exclude_from_history (base_app , monkeypatch ):
28- # Set a fake editor just to make sure we have one. We aren't really going to call it due to the mock
29- base_app .editor = 'fooedit'
30-
31- # Mock out the subprocess.Popen call so we don't actually open an editor
32- m = mock .MagicMock (name = 'Popen' )
33- monkeypatch .setattr ("subprocess.Popen" , m )
34-
35- # Run edit command
36- run_cmd (base_app , 'edit' )
37-
38- # Run history command
39- run_cmd (base_app , 'history' )
40-
41- # Verify that the history is empty
42- out , err = run_cmd (base_app , 'history' )
43- assert out == []
44-
45- # Now run a command which isn't excluded from the history
46- run_cmd (base_app , 'help' )
47-
48- # And verify we have a history now ...
49- out , err = run_cmd (base_app , 'history' )
50- expected = normalize (""" 1 help""" )
51- assert out == expected
22+ #
23+ # readline tests
24+ #
25+ def test_readline_remove_history_item (base_app ):
26+ from cmd2 .rl_utils import readline
27+ assert readline .get_current_history_length () == 0
28+ readline .add_history ('this is a test' )
29+ assert readline .get_current_history_length () == 1
30+ readline .remove_history_item (0 )
31+ assert readline .get_current_history_length () == 0
5232
33+ #
34+ # test History() class
35+ #
5336@pytest .fixture
5437def hist ():
55- from cmd2 .history import History
38+ from cmd2 .parsing import Statement
39+ from cmd2 .cmd2 import History , HistoryItem
5640 h = History ([HistoryItem (Statement ('' , raw = 'first' ), 1 ),
5741 HistoryItem (Statement ('' , raw = 'second' ), 2 ),
5842 HistoryItem (Statement ('' , raw = 'third' ), 3 ),
59- HistoryItem (Statement ('' , raw = 'fourth' ), 4 )])
43+ HistoryItem (Statement ('' , raw = 'fourth' ),4 )])
6044 return h
6145
62- def test_history_item ():
63- raw = 'help'
64- stmt = Statement ('' , raw = raw )
65- index = 1
66- hi = HistoryItem (stmt , index )
67- assert hi .statement == stmt
68- assert hi .idx == index
69- assert hi .statement .raw == raw
70- assert str (hi ) == raw
71-
72-
7346def test_history_class_span (hist ):
7447 for tryit in ['*' , ':' , '-' , 'all' , 'ALL' ]:
7548 assert hist .span (tryit ) == hist
@@ -205,6 +178,46 @@ def test_history_max_length(hist):
205178 assert hist .get (1 ).statement .raw == 'third'
206179 assert hist .get (2 ).statement .raw == 'fourth'
207180
181+ #
182+ # test HistoryItem()
183+ #
184+ @pytest .fixture
185+ def histitem ():
186+ from cmd2 .parsing import Statement
187+ from cmd2 .history import HistoryItem
188+ statement = Statement ('history' ,
189+ raw = 'help history' ,
190+ command = 'help' ,
191+ arg_list = ['history' ],
192+ )
193+ histitem = HistoryItem (statement , 1 )
194+ return histitem
195+
196+ def test_history_item_instantiate ():
197+ from cmd2 .parsing import Statement
198+ from cmd2 .history import HistoryItem
199+ statement = Statement ('history' ,
200+ raw = 'help history' ,
201+ command = 'help' ,
202+ arg_list = ['history' ],
203+ )
204+ with pytest .raises (TypeError ):
205+ _ = HistoryItem ()
206+ with pytest .raises (TypeError ):
207+ _ = HistoryItem (idx = 1 )
208+ with pytest .raises (TypeError ):
209+ _ = HistoryItem (statement = statement )
210+ with pytest .raises (TypeError ):
211+ _ = HistoryItem (statement = statement , idx = 'hi' )
212+
213+ def test_history_item_properties (histitem ):
214+ assert histitem .raw == 'help history'
215+ assert histitem .expanded == 'help history'
216+ assert str (histitem ) == 'help history'
217+
218+ #
219+ # test history command
220+ #
208221def test_base_history (base_app ):
209222 run_cmd (base_app , 'help' )
210223 run_cmd (base_app , 'shortcuts' )
@@ -283,7 +296,6 @@ def test_history_with_integer_argument(base_app):
283296""" )
284297 assert out == expected
285298
286-
287299def test_history_with_integer_span (base_app ):
288300 run_cmd (base_app , 'help' )
289301 run_cmd (base_app , 'shortcuts' )
@@ -441,21 +453,40 @@ def test_history_script_expanded(base_app):
441453 expected = ['alias create s shortcuts' , 'shortcuts' ]
442454 assert out == expected
443455
456+ def test_base_help_history (base_app ):
457+ out , err = run_cmd (base_app , 'help history' )
458+ assert out == normalize (HELP_HISTORY )
444459
445- #####
446- #
447- # readline tests
448- #
449- #####
450- def test_readline_remove_history_item (base_app ):
451- from cmd2 .rl_utils import readline
452- assert readline .get_current_history_length () == 0
453- readline .add_history ('this is a test' )
454- assert readline .get_current_history_length () == 1
455- readline .remove_history_item (0 )
456- assert readline .get_current_history_length () == 0
460+ def test_exclude_from_history (base_app , monkeypatch ):
461+ # Set a fake editor just to make sure we have one. We aren't
462+ # really going to call it due to the mock
463+ base_app .editor = 'fooedit'
457464
465+ # Mock out the subprocess.Popen call so we don't actually open an editor
466+ m = mock .MagicMock (name = 'Popen' )
467+ monkeypatch .setattr ("subprocess.Popen" , m )
468+
469+ # Run edit command
470+ run_cmd (base_app , 'edit' )
458471
472+ # Run history command
473+ run_cmd (base_app , 'history' )
474+
475+ # Verify that the history is empty
476+ out , err = run_cmd (base_app , 'history' )
477+ assert out == []
478+
479+ # Now run a command which isn't excluded from the history
480+ run_cmd (base_app , 'help' )
481+
482+ # And verify we have a history now ...
483+ out , err = run_cmd (base_app , 'history' )
484+ expected = normalize (""" 1 help""" )
485+ assert out == expected
486+
487+ #
488+ # test history initialization
489+ #
459490@pytest .fixture (scope = "session" )
460491def hist_file ():
461492 fd , filename = tempfile .mkstemp (prefix = 'hist_file' , suffix = '.txt' )
@@ -467,16 +498,25 @@ def hist_file():
467498 except FileNotFoundError :
468499 pass
469500
470- def test_bad_history_file_path (capsys , request ):
501+ def test_history_file_is_directory (capsys ):
471502 with tempfile .TemporaryDirectory () as test_dir :
472503 # Create a new cmd2 app
473504 cmd2 .Cmd (persistent_history_file = test_dir )
474505 _ , err = capsys .readouterr ()
475506 assert 'is a directory' in err
476507
508+ def test_history_file_permission_error (mocker , capsys ):
509+ mock_open = mocker .patch ('builtins.open' )
510+ mock_open .side_effect = PermissionError
511+
512+ cmd2 .Cmd (persistent_history_file = '/tmp/doesntmatter' )
513+ out , err = capsys .readouterr ()
514+ assert not out
515+ assert 'can not read' in err
516+
477517def test_history_file_conversion_no_truncate_on_init (hist_file , capsys ):
478- # test the code that converts a plain text history file to a pickle binary
479- # history file
518+ # make sure we don't truncate the plain text history file on init
519+ # it shouldn't get converted to pickle format until we save history
480520
481521 # first we need some plain text commands in the history file
482522 with open (hist_file , 'w' ) as hfobj :
@@ -505,12 +545,11 @@ def test_history_populates_readline(hist_file):
505545 run_cmd (app , 'shortcuts' )
506546 run_cmd (app , 'shortcuts' )
507547 run_cmd (app , 'alias' )
508-
509548 # call the private method which is registered to write history at exit
510- app ._persist_history_on_exit ()
511- # - create a new cmd2 with persistent history
512- app = cmd2 .Cmd (persistent_history_file = hist_file )
549+ app ._persist_history ()
513550
551+ # see if history came back
552+ app = cmd2 .Cmd (persistent_history_file = hist_file )
514553 assert len (app .history ) == 4
515554 assert app .history .get (1 ).statement .raw == 'help'
516555 assert app .history .get (2 ).statement .raw == 'shortcuts'
@@ -525,3 +564,27 @@ def test_history_populates_readline(hist_file):
525564 assert readline .get_history_item (1 ) == 'help'
526565 assert readline .get_history_item (2 ) == 'shortcuts'
527566 assert readline .get_history_item (3 ) == 'alias'
567+
568+ #
569+ # test cmd2's ability to write out history on exit
570+ # we are testing the _persist_history_on_exit() method, and
571+ # we assume that the atexit module will call this method
572+ # properly
573+ #
574+ def test_persist_history_ensure_no_error_if_no_histfile (base_app , capsys ):
575+ # make sure if there is no persistent history file and someone
576+ # calls the private method call that we don't get an error
577+ base_app ._persist_history ()
578+ out , err = capsys .readouterr ()
579+ assert not out
580+ assert not err
581+
582+ def test_persist_history_permission_error (hist_file , mocker , capsys ):
583+ app = cmd2 .Cmd (persistent_history_file = hist_file )
584+ run_cmd (app , 'help' )
585+ mock_open = mocker .patch ('builtins.open' )
586+ mock_open .side_effect = PermissionError
587+ app ._persist_history ()
588+ out , err = capsys .readouterr ()
589+ assert not out
590+ assert 'can not write' in err
0 commit comments