From 297af346a6506c35b161b44df03f684c81b3ee2b Mon Sep 17 00:00:00 2001 From: kotfu Date: Fri, 24 May 2019 12:21:56 -0600 Subject: Initializing history now detects plaintext or pickle format --- tests/test_history.py | 48 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/tests/test_history.py b/tests/test_history.py index 105dead9..554281c4 100644 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -5,6 +5,7 @@ Test history functions of cmd2 """ import tempfile import os +import pickle import sys import pytest @@ -121,6 +122,19 @@ def test_history_regex_search(hist): assert hist.regex_search('/i.*d/') == ['third'] assert hist.regex_search('s[a-z]+ond') == ['second'] +def test_history_max_length_zero(hist): + hist.truncate(0) + assert len(hist) == 0 + +def test_history_max_length_negative(hist): + hist.truncate(-1) + assert len(hist) == 0 + +def test_history_max_length(hist): + hist.truncate(2) + assert hist.get(1) == 'third' + assert hist.get(2) == 'fourth' + def test_base_history(base_app): run_cmd(base_app, 'help') run_cmd(base_app, 'shortcuts') @@ -399,7 +413,7 @@ def test_existing_history_file(hist_file, capsys): assert err == '' # Unregister the call to write_history_file that cmd2 did - atexit.unregister(readline.write_history_file) + ## TODO atexit.unregister(readline.write_history_file) # Remove created history file os.remove(hist_file) @@ -422,7 +436,7 @@ def test_new_history_file(hist_file, capsys): assert err == '' # Unregister the call to write_history_file that cmd2 did - atexit.unregister(readline.write_history_file) + ### TODO atexit.unregister(readline.write_history_file) # Remove created history file os.remove(hist_file) @@ -435,10 +449,28 @@ def test_bad_history_file_path(capsys, request): cmd2.Cmd(persistent_history_file=test_dir) _, err = capsys.readouterr() - if sys.platform == 'win32': - # pyreadline masks the read exception. Therefore the bad path error occurs when trying to write the file. - assert 'readline cannot write' in err - else: - # GNU readline raises an exception upon trying to read the directory as a file - assert 'readline cannot read' in err + assert 'can not write' in err + +def test_history_file_conversion_no_truncate_on_init(hist_file, capsys): + # test the code that converts a plain text history file to a pickle binary + # history file + + # first we need some plain text commands in the history file + with open(hist_file, 'w') as hfobj: + hfobj.write('help\n') + hfobj.write('alias\n') + hfobj.write('alias create s shortcuts\n') + + # Create a new cmd2 app + cmd2.Cmd(persistent_history_file=hist_file) + + # history should be initialized, but the file on disk should + # still be plain text + with open(hist_file, 'r') as hfobj: + histlist= hfobj.readlines() + assert len(histlist) == 3 + # history.get() is overridden to be one based, not zero based + assert histlist[0]== 'help\n' + assert histlist[1] == 'alias\n' + assert histlist[2] == 'alias create s shortcuts\n' -- cgit v1.2.1 From ba6c5ed5cc006b156093f977856afc03d6c12763 Mon Sep 17 00:00:00 2001 From: kotfu Date: Fri, 24 May 2019 17:34:15 -0600 Subject: Cleanup some history tests --- tests/test_history.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/tests/test_history.py b/tests/test_history.py index 554281c4..2a2d90d2 100644 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -398,8 +398,6 @@ def hist_file(): pass def test_existing_history_file(hist_file, capsys): - import atexit - import readline # Create the history file before making cmd2 app with open(hist_file, 'w'): @@ -416,11 +414,9 @@ def test_existing_history_file(hist_file, capsys): ## TODO atexit.unregister(readline.write_history_file) # Remove created history file - os.remove(hist_file) + #os.remove(hist_file) def test_new_history_file(hist_file, capsys): - import atexit - import readline # Remove any existing history file try: @@ -439,7 +435,7 @@ def test_new_history_file(hist_file, capsys): ### TODO atexit.unregister(readline.write_history_file) # Remove created history file - os.remove(hist_file) + #os.remove(hist_file) def test_bad_history_file_path(capsys, request): # Use a directory path as the history file @@ -449,7 +445,7 @@ def test_bad_history_file_path(capsys, request): cmd2.Cmd(persistent_history_file=test_dir) _, err = capsys.readouterr() - assert 'can not write' in err + assert 'is a directory' in err def test_history_file_conversion_no_truncate_on_init(hist_file, capsys): # test the code that converts a plain text history file to a pickle binary -- cgit v1.2.1 From 69308f455193d5b5f89a6edf7c7d3d052bdcd5ce Mon Sep 17 00:00:00 2001 From: kotfu Date: Fri, 24 May 2019 20:00:16 -0600 Subject: Refactor HistoryItem to not subclass str --- tests/test_history.py | 121 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 91 insertions(+), 30 deletions(-) (limited to 'tests') diff --git a/tests/test_history.py b/tests/test_history.py index 2a2d90d2..4553575a 100644 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -55,33 +55,82 @@ def test_exclude_from_history(base_app, monkeypatch): def hist(): from cmd2.parsing import Statement from cmd2.cmd2 import History, HistoryItem - h = History([HistoryItem(Statement('', raw='first')), - HistoryItem(Statement('', raw='second')), - HistoryItem(Statement('', raw='third')), - HistoryItem(Statement('', raw='fourth'))]) + h = History([HistoryItem(Statement('', raw='first'), 1), + HistoryItem(Statement('', raw='second'), 2), + HistoryItem(Statement('', raw='third'), 3), + HistoryItem(Statement('', raw='fourth'),4)]) return h def test_history_class_span(hist): for tryit in ['*', ':', '-', 'all', 'ALL']: assert hist.span(tryit) == hist - assert hist.span('3') == ['third'] - assert hist.span('-1') == ['fourth'] - - assert hist.span('2..') == ['second', 'third', 'fourth'] - assert hist.span('2:') == ['second', 'third', 'fourth'] - - assert hist.span('-2..') == ['third', 'fourth'] - assert hist.span('-2:') == ['third', 'fourth'] - - assert hist.span('1..3') == ['first', 'second', 'third'] - assert hist.span('1:3') == ['first', 'second', 'third'] - assert hist.span('2:-1') == ['second', 'third', 'fourth'] - assert hist.span('-3:4') == ['second', 'third','fourth'] - assert hist.span('-4:-2') == ['first', 'second', 'third'] - - assert hist.span(':-2') == ['first', 'second', 'third'] - assert hist.span('..-2') == ['first', 'second', 'third'] + assert hist.span('3')[0].statement.raw == 'third' + assert hist.span('-1')[0].statement.raw == 'fourth' + + span = hist.span('2..') + assert len(span) == 3 + assert span[0].statement.raw == 'second' + assert span[1].statement.raw == 'third' + assert span[2].statement.raw == 'fourth' + + span = hist.span('2:') + assert len(span) == 3 + assert span[0].statement.raw == 'second' + assert span[1].statement.raw == 'third' + assert span[2].statement.raw == 'fourth' + + span = hist.span('-2..') + assert len(span) == 2 + assert span[0].statement.raw == 'third' + assert span[1].statement.raw == 'fourth' + + span = hist.span('-2:') + assert len(span) == 2 + assert span[0].statement.raw == 'third' + assert span[1].statement.raw == 'fourth' + + span = hist.span('1..3') + assert len(span) == 3 + assert span[0].statement.raw == 'first' + assert span[1].statement.raw == 'second' + assert span[2].statement.raw == 'third' + + span = hist.span('1:3') + assert len(span) == 3 + assert span[0].statement.raw == 'first' + assert span[1].statement.raw == 'second' + assert span[2].statement.raw == 'third' + + span = hist.span('2:-1') + assert len(span) == 3 + assert span[0].statement.raw == 'second' + assert span[1].statement.raw == 'third' + assert span[2].statement.raw == 'fourth' + + span = hist.span('-3:4') + assert len(span) == 3 + assert span[0].statement.raw == 'second' + assert span[1].statement.raw == 'third' + assert span[2].statement.raw == 'fourth' + + span = hist.span('-4:-2') + assert len(span) == 3 + assert span[0].statement.raw == 'first' + assert span[1].statement.raw == 'second' + assert span[2].statement.raw == 'third' + + span = hist.span(':-2') + assert len(span) == 3 + assert span[0].statement.raw == 'first' + assert span[1].statement.raw == 'second' + assert span[2].statement.raw == 'third' + + span = hist.span('..-2') + assert len(span) == 3 + assert span[0].statement.raw == 'first' + assert span[1].statement.raw == 'second' + assert span[2].statement.raw == 'third' value_errors = ['fred', 'fred:joe', 'a..b', '2 ..', '1 : 3', '1:0', '0:3'] for tryit in value_errors: @@ -89,10 +138,10 @@ def test_history_class_span(hist): hist.span(tryit) def test_history_class_get(hist): - assert hist.get('1') == 'first' - assert hist.get(3) == 'third' + assert hist.get('1').statement.raw == 'first' + assert hist.get(3).statement.raw == 'third' assert hist.get('-2') == hist[-2] - assert hist.get(-1) == 'fourth' + assert hist.get(-1).statement.raw == 'fourth' with pytest.raises(IndexError): hist.get(0) @@ -115,12 +164,23 @@ def test_history_class_get(hist): hist.get(None) def test_history_str_search(hist): - assert hist.str_search('ir') == ['first', 'third'] - assert hist.str_search('rth') == ['fourth'] + items = hist.str_search('ir') + assert len(items) == 2 + assert items[0].statement.raw == 'first' + assert items[1].statement.raw == 'third' + + items = hist.str_search('rth') + assert len(items) == 1 + assert items[0].statement.raw == 'fourth' def test_history_regex_search(hist): - assert hist.regex_search('/i.*d/') == ['third'] - assert hist.regex_search('s[a-z]+ond') == ['second'] + items = hist.regex_search('/i.*d/') + assert len(items) == 1 + assert items[0].statement.raw == 'third' + + items = hist.regex_search('s[a-z]+ond') + assert len(items) == 1 + assert items[0].statement.raw == 'second' def test_history_max_length_zero(hist): hist.truncate(0) @@ -132,8 +192,9 @@ def test_history_max_length_negative(hist): def test_history_max_length(hist): hist.truncate(2) - assert hist.get(1) == 'third' - assert hist.get(2) == 'fourth' + assert len(hist) == 2 + assert hist.get(1).statement.raw == 'third' + assert hist.get(2).statement.raw == 'fourth' def test_base_history(base_app): run_cmd(base_app, 'help') -- cgit v1.2.1 From c13ff0f48b1e4652b6145a5b69ea2f439629afdf Mon Sep 17 00:00:00 2001 From: kotfu Date: Fri, 24 May 2019 23:03:22 -0600 Subject: Populate readline history from unpickled history --- tests/test_history.py | 68 +++++++++++++++++++++------------------------------ 1 file changed, 28 insertions(+), 40 deletions(-) (limited to 'tests') diff --git a/tests/test_history.py b/tests/test_history.py index 4553575a..2fdd772f 100644 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -458,46 +458,6 @@ def hist_file(): except FileNotFoundError: pass -def test_existing_history_file(hist_file, capsys): - - # Create the history file before making cmd2 app - with open(hist_file, 'w'): - pass - - # Create a new cmd2 app - cmd2.Cmd(persistent_history_file=hist_file) - _, err = capsys.readouterr() - - # Make sure there were no errors - assert err == '' - - # Unregister the call to write_history_file that cmd2 did - ## TODO atexit.unregister(readline.write_history_file) - - # Remove created history file - #os.remove(hist_file) - -def test_new_history_file(hist_file, capsys): - - # Remove any existing history file - try: - os.remove(hist_file) - except OSError: - pass - - # Create a new cmd2 app - cmd2.Cmd(persistent_history_file=hist_file) - _, err = capsys.readouterr() - - # Make sure there were no errors - assert err == '' - - # Unregister the call to write_history_file that cmd2 did - ### TODO atexit.unregister(readline.write_history_file) - - # Remove created history file - #os.remove(hist_file) - def test_bad_history_file_path(capsys, request): # Use a directory path as the history file test_dir = os.path.dirname(request.module.__file__) @@ -531,3 +491,31 @@ def test_history_file_conversion_no_truncate_on_init(hist_file, capsys): assert histlist[0]== 'help\n' assert histlist[1] == 'alias\n' assert histlist[2] == 'alias create s shortcuts\n' + +def test_history_populates_readline(hist_file): + # - create a cmd2 with persistent history + app = cmd2.Cmd(persistent_history_file=hist_file) + run_cmd(app, 'help') + run_cmd(app, 'shortcuts') + run_cmd(app, 'shortcuts') + run_cmd(app, 'alias') + + # call the private method which is registered to write history at exit + app._persist_history_on_exit() + # - create a new cmd2 with persistent history + app = cmd2.Cmd(persistent_history_file=hist_file) + + assert len(app.history) == 4 + assert app.history.get(1).statement.raw == 'help' + assert app.history.get(2).statement.raw == 'shortcuts' + assert app.history.get(3).statement.raw == 'shortcuts' + assert app.history.get(4).statement.raw == 'alias' + + # readline only adds a single entry for multiple sequential identical commands + # so we check to make sure that cmd2 populated the readline history + # using the same rules + from cmd2.rl_utils import readline + assert readline.get_current_history_length() == 3 + assert readline.get_history_item(1) == 'help' + assert readline.get_history_item(2) == 'shortcuts' + assert readline.get_history_item(3) == 'alias' -- cgit v1.2.1 From 7644204d3d9589e02ce6f39c16caf2c75cd364df Mon Sep 17 00:00:00 2001 From: kotfu Date: Sat, 25 May 2019 13:58:25 -0600 Subject: Resolve PR feedback --- tests/test_history.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/tests/test_history.py b/tests/test_history.py index 2fdd772f..3a52bda9 100644 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -459,14 +459,11 @@ def hist_file(): pass def test_bad_history_file_path(capsys, request): - # Use a directory path as the history file - test_dir = os.path.dirname(request.module.__file__) - - # Create a new cmd2 app - cmd2.Cmd(persistent_history_file=test_dir) - _, err = capsys.readouterr() - - assert 'is a directory' in err + with tempfile.TemporaryDirectory() as test_dir: + # Create a new cmd2 app + cmd2.Cmd(persistent_history_file=test_dir) + _, err = capsys.readouterr() + assert 'is a directory' in err def test_history_file_conversion_no_truncate_on_init(hist_file, capsys): # test the code that converts a plain text history file to a pickle binary -- cgit v1.2.1 From 9b0a3c9315a0c3599a0ad06f9ea607a83af85cdb Mon Sep 17 00:00:00 2001 From: kotfu Date: Sat, 25 May 2019 14:16:45 -0600 Subject: Try and fix an Appveyor permission problem --- tests/test_history.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/test_history.py b/tests/test_history.py index 3a52bda9..4ece9e0d 100644 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -460,8 +460,13 @@ def hist_file(): def test_bad_history_file_path(capsys, request): with tempfile.TemporaryDirectory() as test_dir: + # For appveyor, create a directory in our temp dir + # for some reason it seems that appveyor won't let us read + # the directory we created + safe_dir = os.path.join(test_dir, 'somedir') + os.mkdir(safe_dir) # Create a new cmd2 app - cmd2.Cmd(persistent_history_file=test_dir) + cmd2.Cmd(persistent_history_file=safe_dir) _, err = capsys.readouterr() assert 'is a directory' in err -- cgit v1.2.1 From 0ed74c7ece6d69197b4e46fc37c0f3a440316392 Mon Sep 17 00:00:00 2001 From: kotfu Date: Sat, 25 May 2019 14:32:36 -0600 Subject: Another try to fix the appveyor on windows permission issue --- tests/test_history.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/tests/test_history.py b/tests/test_history.py index 4ece9e0d..09b83279 100644 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -459,16 +459,18 @@ def hist_file(): pass def test_bad_history_file_path(capsys, request): - with tempfile.TemporaryDirectory() as test_dir: - # For appveyor, create a directory in our temp dir - # for some reason it seems that appveyor won't let us read - # the directory we created - safe_dir = os.path.join(test_dir, 'somedir') - os.mkdir(safe_dir) - # Create a new cmd2 app - cmd2.Cmd(persistent_history_file=safe_dir) - _, err = capsys.readouterr() - assert 'is a directory' in err + # can't use tempfile.TemporaryDirectory() as a context on Appveyor + # on windows. it causes a file locking issue which is reflected as + # a permission exception + test_dir = tempfile.mkdtemp() + # Create a new cmd2 app + cmd2.Cmd(persistent_history_file=test_dir) + _, err = capsys.readouterr() + assert 'is a directory' in err + try: + os.rmdir(test_dir) + except OSError: + pass def test_history_file_conversion_no_truncate_on_init(hist_file, capsys): # test the code that converts a plain text history file to a pickle binary -- cgit v1.2.1 From 5fe14a26eb9abe5a198eb38b07b3b7af4225802c Mon Sep 17 00:00:00 2001 From: kotfu Date: Sat, 25 May 2019 14:51:45 -0600 Subject: Manually check whether persistent_history_file is a directory --- tests/test_history.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/tests/test_history.py b/tests/test_history.py index 09b83279..3a52bda9 100644 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -459,18 +459,11 @@ def hist_file(): pass def test_bad_history_file_path(capsys, request): - # can't use tempfile.TemporaryDirectory() as a context on Appveyor - # on windows. it causes a file locking issue which is reflected as - # a permission exception - test_dir = tempfile.mkdtemp() - # Create a new cmd2 app - cmd2.Cmd(persistent_history_file=test_dir) - _, err = capsys.readouterr() - assert 'is a directory' in err - try: - os.rmdir(test_dir) - except OSError: - pass + with tempfile.TemporaryDirectory() as test_dir: + # Create a new cmd2 app + cmd2.Cmd(persistent_history_file=test_dir) + _, err = capsys.readouterr() + assert 'is a directory' in err def test_history_file_conversion_no_truncate_on_init(hist_file, capsys): # test the code that converts a plain text history file to a pickle binary -- cgit v1.2.1 From 1fe024f33574af02c93d4df0eb0ddb88e690305a Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 25 May 2019 17:49:20 -0400 Subject: Refactored implementation of HistoryItem.__str__ and added an explicit HistoryItem unit test --- tests/test_history.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/test_history.py b/tests/test_history.py index 3a52bda9..11133456 100644 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -5,8 +5,6 @@ Test history functions of cmd2 """ import tempfile import os -import pickle -import sys import pytest @@ -18,6 +16,8 @@ except ImportError: import cmd2 from .conftest import run_cmd, normalize, HELP_HISTORY +from cmd2.parsing import Statement +from cmd2.history import HistoryItem def test_base_help_history(base_app): @@ -50,17 +50,26 @@ def test_exclude_from_history(base_app, monkeypatch): expected = normalize(""" 1 help""") assert out == expected - @pytest.fixture def hist(): - from cmd2.parsing import Statement - from cmd2.cmd2 import History, HistoryItem + from cmd2.history import History h = History([HistoryItem(Statement('', raw='first'), 1), HistoryItem(Statement('', raw='second'), 2), HistoryItem(Statement('', raw='third'), 3), - HistoryItem(Statement('', raw='fourth'),4)]) + HistoryItem(Statement('', raw='fourth'), 4)]) return h +def test_history_item(): + raw = 'help' + stmt = Statement('', raw=raw) + index = 1 + hi = HistoryItem(stmt, index) + assert hi.statement == stmt + assert hi.idx == index + assert hi.statement.raw == raw + assert str(hi) == raw + + def test_history_class_span(hist): for tryit in ['*', ':', '-', 'all', 'ALL']: assert hist.span(tryit) == hist -- cgit v1.2.1 From f82c3653088f15bb33e2624fa0a3b5e9a6e74fb7 Mon Sep 17 00:00:00 2001 From: kotfu Date: Sat, 25 May 2019 16:35:23 -0600 Subject: Improve unit test coverage for history --- tests/test_history.py | 193 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 128 insertions(+), 65 deletions(-) (limited to 'tests') diff --git a/tests/test_history.py b/tests/test_history.py index 11133456..1956a574 100644 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -8,7 +8,8 @@ import os import pytest -# Python 3.5 had some regressions in the unitest.mock module, so use 3rd party mock if available +# Python 3.5 had some regressions in the unitest.mock module, so use +# 3rd party mock if available try: import mock except ImportError: @@ -16,60 +17,32 @@ except ImportError: import cmd2 from .conftest import run_cmd, normalize, HELP_HISTORY -from cmd2.parsing import Statement -from cmd2.history import HistoryItem -def test_base_help_history(base_app): - out, err = run_cmd(base_app, 'help history') - assert out == normalize(HELP_HISTORY) - -def test_exclude_from_history(base_app, monkeypatch): - # Set a fake editor just to make sure we have one. We aren't really going to call it due to the mock - base_app.editor = 'fooedit' - - # Mock out the subprocess.Popen call so we don't actually open an editor - m = mock.MagicMock(name='Popen') - monkeypatch.setattr("subprocess.Popen", m) - - # Run edit command - run_cmd(base_app, 'edit') - - # Run history command - run_cmd(base_app, 'history') - - # Verify that the history is empty - out, err = run_cmd(base_app, 'history') - assert out == [] - - # Now run a command which isn't excluded from the history - run_cmd(base_app, 'help') - - # And verify we have a history now ... - out, err = run_cmd(base_app, 'history') - expected = normalize(""" 1 help""") - assert out == expected +# +# readline tests +# +def test_readline_remove_history_item(base_app): + from cmd2.rl_utils import readline + assert readline.get_current_history_length() == 0 + readline.add_history('this is a test') + assert readline.get_current_history_length() == 1 + readline.remove_history_item(0) + assert readline.get_current_history_length() == 0 +# +# test History() class +# @pytest.fixture def hist(): - from cmd2.history import History + from cmd2.parsing import Statement + from cmd2.cmd2 import History, HistoryItem h = History([HistoryItem(Statement('', raw='first'), 1), HistoryItem(Statement('', raw='second'), 2), HistoryItem(Statement('', raw='third'), 3), - HistoryItem(Statement('', raw='fourth'), 4)]) + HistoryItem(Statement('', raw='fourth'),4)]) return h -def test_history_item(): - raw = 'help' - stmt = Statement('', raw=raw) - index = 1 - hi = HistoryItem(stmt, index) - assert hi.statement == stmt - assert hi.idx == index - assert hi.statement.raw == raw - assert str(hi) == raw - - def test_history_class_span(hist): for tryit in ['*', ':', '-', 'all', 'ALL']: assert hist.span(tryit) == hist @@ -205,6 +178,46 @@ def test_history_max_length(hist): assert hist.get(1).statement.raw == 'third' assert hist.get(2).statement.raw == 'fourth' +# +# test HistoryItem() +# +@pytest.fixture +def histitem(): + from cmd2.parsing import Statement + from cmd2.history import HistoryItem + statement = Statement('history', + raw='help history', + command='help', + arg_list=['history'], + ) + histitem = HistoryItem(statement, 1) + return histitem + +def test_history_item_instantiate(): + from cmd2.parsing import Statement + from cmd2.history import HistoryItem + statement = Statement('history', + raw='help history', + command='help', + arg_list=['history'], + ) + with pytest.raises(TypeError): + _ = HistoryItem() + with pytest.raises(TypeError): + _ = HistoryItem(idx=1) + with pytest.raises(TypeError): + _ = HistoryItem(statement=statement) + with pytest.raises(TypeError): + _ = HistoryItem(statement=statement, idx='hi') + +def test_history_item_properties(histitem): + assert histitem.raw == 'help history' + assert histitem.expanded == 'help history' + assert str(histitem) == 'help history' + +# +# test history command +# def test_base_history(base_app): run_cmd(base_app, 'help') run_cmd(base_app, 'shortcuts') @@ -283,7 +296,6 @@ def test_history_with_integer_argument(base_app): """) assert out == expected - def test_history_with_integer_span(base_app): run_cmd(base_app, 'help') run_cmd(base_app, 'shortcuts') @@ -441,21 +453,40 @@ def test_history_script_expanded(base_app): expected = ['alias create s shortcuts', 'shortcuts'] assert out == expected +def test_base_help_history(base_app): + out, err = run_cmd(base_app, 'help history') + assert out == normalize(HELP_HISTORY) -##### -# -# readline tests -# -##### -def test_readline_remove_history_item(base_app): - from cmd2.rl_utils import readline - assert readline.get_current_history_length() == 0 - readline.add_history('this is a test') - assert readline.get_current_history_length() == 1 - readline.remove_history_item(0) - assert readline.get_current_history_length() == 0 +def test_exclude_from_history(base_app, monkeypatch): + # Set a fake editor just to make sure we have one. We aren't + # really going to call it due to the mock + base_app.editor = 'fooedit' + # Mock out the subprocess.Popen call so we don't actually open an editor + m = mock.MagicMock(name='Popen') + monkeypatch.setattr("subprocess.Popen", m) + + # Run edit command + run_cmd(base_app, 'edit') + # Run history command + run_cmd(base_app, 'history') + + # Verify that the history is empty + out, err = run_cmd(base_app, 'history') + assert out == [] + + # Now run a command which isn't excluded from the history + run_cmd(base_app, 'help') + + # And verify we have a history now ... + out, err = run_cmd(base_app, 'history') + expected = normalize(""" 1 help""") + assert out == expected + +# +# test history initialization +# @pytest.fixture(scope="session") def hist_file(): fd, filename = tempfile.mkstemp(prefix='hist_file', suffix='.txt') @@ -467,16 +498,25 @@ def hist_file(): except FileNotFoundError: pass -def test_bad_history_file_path(capsys, request): +def test_history_file_is_directory(capsys): with tempfile.TemporaryDirectory() as test_dir: # Create a new cmd2 app cmd2.Cmd(persistent_history_file=test_dir) _, err = capsys.readouterr() assert 'is a directory' in err +def test_history_file_permission_error(mocker, capsys): + mock_open = mocker.patch('builtins.open') + mock_open.side_effect = PermissionError + + cmd2.Cmd(persistent_history_file='/tmp/doesntmatter') + out, err = capsys.readouterr() + assert not out + assert 'can not read' in err + def test_history_file_conversion_no_truncate_on_init(hist_file, capsys): - # test the code that converts a plain text history file to a pickle binary - # history file + # make sure we don't truncate the plain text history file on init + # it shouldn't get converted to pickle format until we save history # first we need some plain text commands in the history file with open(hist_file, 'w') as hfobj: @@ -505,12 +545,11 @@ def test_history_populates_readline(hist_file): run_cmd(app, 'shortcuts') run_cmd(app, 'shortcuts') run_cmd(app, 'alias') - # call the private method which is registered to write history at exit - app._persist_history_on_exit() - # - create a new cmd2 with persistent history - app = cmd2.Cmd(persistent_history_file=hist_file) + app._persist_history() + # see if history came back + app = cmd2.Cmd(persistent_history_file=hist_file) assert len(app.history) == 4 assert app.history.get(1).statement.raw == 'help' assert app.history.get(2).statement.raw == 'shortcuts' @@ -525,3 +564,27 @@ def test_history_populates_readline(hist_file): assert readline.get_history_item(1) == 'help' assert readline.get_history_item(2) == 'shortcuts' assert readline.get_history_item(3) == 'alias' + +# +# test cmd2's ability to write out history on exit +# we are testing the _persist_history_on_exit() method, and +# we assume that the atexit module will call this method +# properly +# +def test_persist_history_ensure_no_error_if_no_histfile(base_app, capsys): + # make sure if there is no persistent history file and someone + # calls the private method call that we don't get an error + base_app._persist_history() + out, err = capsys.readouterr() + assert not out + assert not err + +def test_persist_history_permission_error(hist_file, mocker, capsys): + app = cmd2.Cmd(persistent_history_file=hist_file) + run_cmd(app, 'help') + mock_open = mocker.patch('builtins.open') + mock_open.side_effect = PermissionError + app._persist_history() + out, err = capsys.readouterr() + assert not out + assert 'can not write' in err -- cgit v1.2.1 From 9dd00461b22085d3f16b4cc178a222c30fe95d11 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Mon, 27 May 2019 15:45:34 -0400 Subject: Add the -a/--all flag to the history command for showing all commands including those persisted from previous sessions Also: - History class has been modified to keep track of the session start index - History class span(), str_search(), and regex_search() methods now take an optional 2nd boolean parameter `include_persisted` which determines whether or not commands persisted from previous sessions should be included by default - If a start index is manually specified, then it automatically includes the full search - Updates unit tests --- tests/conftest.py | 3 +- tests/test_history.py | 78 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 77 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/conftest.py b/tests/conftest.py index 9d55eb4d..769e5a8f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -59,6 +59,7 @@ shortcuts List available shortcuts # Help text for the history command HELP_HISTORY = """Usage: history [-h] [-r | -e | -o FILE | -t TRANSCRIPT | -c] [-s] [-x] [-v] + [-a] [arg] View, run, edit, save, or clear previously entered commands @@ -88,7 +89,7 @@ formatting: macros expanded, instead of typed commands -v, --verbose display history and include expanded commands if they differ from the typed command - + -a, --all display all commands, including ones persisted from previous sessions """ # Output from the shortcuts command with default built-in shortcuts diff --git a/tests/test_history.py b/tests/test_history.py index 1956a574..5e01688c 100644 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -43,6 +43,19 @@ def hist(): HistoryItem(Statement('', raw='fourth'),4)]) return h +@pytest.fixture +def persisted_hist(): + from cmd2.parsing import Statement + from cmd2.cmd2 import History, HistoryItem + h = History([HistoryItem(Statement('', raw='first'), 1), + HistoryItem(Statement('', raw='second'), 2), + HistoryItem(Statement('', raw='third'), 3), + HistoryItem(Statement('', raw='fourth'),4)]) + h.start_session() + h.append(Statement('', raw='fifth')) + h.append(Statement('', raw='sixth')) + return h + def test_history_class_span(hist): for tryit in ['*', ':', '-', 'all', 'ALL']: assert hist.span(tryit) == hist @@ -119,6 +132,62 @@ def test_history_class_span(hist): with pytest.raises(ValueError): hist.span(tryit) +def test_persisted_history_span(persisted_hist): + for tryit in ['*', ':', '-', 'all', 'ALL']: + assert persisted_hist.span(tryit, include_persisted=True) == persisted_hist + assert persisted_hist.span(tryit, include_persisted=False) != persisted_hist + + assert persisted_hist.span('3')[0].statement.raw == 'third' + assert persisted_hist.span('-1')[0].statement.raw == 'sixth' + + span = persisted_hist.span('2..') + assert len(span) == 5 + assert span[0].statement.raw == 'second' + assert span[1].statement.raw == 'third' + assert span[2].statement.raw == 'fourth' + assert span[3].statement.raw == 'fifth' + assert span[4].statement.raw == 'sixth' + + span = persisted_hist.span('-2..') + assert len(span) == 2 + assert span[0].statement.raw == 'fifth' + assert span[1].statement.raw == 'sixth' + + span = persisted_hist.span('1..3') + assert len(span) == 3 + assert span[0].statement.raw == 'first' + assert span[1].statement.raw == 'second' + assert span[2].statement.raw == 'third' + + span = persisted_hist.span('2:-1') + assert len(span) == 5 + assert span[0].statement.raw == 'second' + assert span[1].statement.raw == 'third' + assert span[2].statement.raw == 'fourth' + assert span[3].statement.raw == 'fifth' + assert span[4].statement.raw == 'sixth' + + span = persisted_hist.span('-3:4') + assert len(span) == 1 + assert span[0].statement.raw == 'fourth' + + span = persisted_hist.span(':-2', include_persisted=True) + assert len(span) == 5 + assert span[0].statement.raw == 'first' + assert span[1].statement.raw == 'second' + assert span[2].statement.raw == 'third' + assert span[3].statement.raw == 'fourth' + assert span[4].statement.raw == 'fifth' + + span = persisted_hist.span(':-2', include_persisted=False) + assert len(span) == 1 + assert span[0].statement.raw == 'fifth' + + value_errors = ['fred', 'fred:joe', 'a..b', '2 ..', '1 : 3', '1:0', '0:3'] + for tryit in value_errors: + with pytest.raises(ValueError): + persisted_hist.span(tryit) + def test_history_class_get(hist): assert hist.get('1').statement.raw == 'first' assert hist.get(3).statement.raw == 'third' @@ -401,7 +470,8 @@ def test_history_verbose_with_other_options(base_app): options_to_test = ['-r', '-e', '-o file', '-t file', '-c', '-x'] for opt in options_to_test: out, err = run_cmd(base_app, 'history -v ' + opt) - assert len(out) == 3 + assert len(out) == 4 + assert out[0] == '-v can not be used with any other options' assert out[1].startswith('Usage:') def test_history_verbose(base_app): @@ -417,7 +487,8 @@ def test_history_script_with_invalid_options(base_app): options_to_test = ['-r', '-e', '-o file', '-t file', '-c'] for opt in options_to_test: out, err = run_cmd(base_app, 'history -s ' + opt) - assert len(out) == 3 + assert len(out) == 4 + assert out[0] == '-s and -x can not be used with -c, -r, -e, -o, or -t' assert out[1].startswith('Usage:') def test_history_script(base_app): @@ -432,7 +503,8 @@ def test_history_expanded_with_invalid_options(base_app): options_to_test = ['-r', '-e', '-o file', '-t file', '-c'] for opt in options_to_test: out, err = run_cmd(base_app, 'history -x ' + opt) - assert len(out) == 3 + assert len(out) == 4 + assert out[0] == '-s and -x can not be used with -c, -r, -e, -o, or -t' assert out[1].startswith('Usage:') def test_history_expanded(base_app): -- cgit v1.2.1 From 976ddc859311e640f3ae67af34299bbf1160358f Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Wed, 5 Jun 2019 10:56:01 -0400 Subject: Changed wrapping point of history's help text to match all other help text in cmd2 --- tests/conftest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/conftest.py b/tests/conftest.py index 769e5a8f..dc5c1ab1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -89,7 +89,8 @@ formatting: macros expanded, instead of typed commands -v, --verbose display history and include expanded commands if they differ from the typed command - -a, --all display all commands, including ones persisted from previous sessions + -a, --all display all commands, including ones persisted from + previous sessions """ # Output from the shortcuts command with default built-in shortcuts -- cgit v1.2.1