From d81c981dbf982bff6eec0484f63b441a7c634613 Mon Sep 17 00:00:00 2001 From: Michael Lazar Date: Thu, 23 Jun 2016 22:50:12 -0700 Subject: [PATCH] Updated tests. Updated log format. --- rtv/__main__.py | 5 +++- rtv/terminal.py | 23 +++++++++------ tests/conftest.py | 4 ++- tests/test_submission.py | 6 ++-- tests/test_subreddit.py | 4 +-- tests/test_terminal.py | 60 ++++++++++++++++++++++++++++++++++++---- 6 files changed, 82 insertions(+), 20 deletions(-) diff --git a/rtv/__main__.py b/rtv/__main__.py index 490f427..5c9c4c5 100644 --- a/rtv/__main__.py +++ b/rtv/__main__.py @@ -74,7 +74,10 @@ def main(): # if args[0] != "header:": # _http_logger.info(' '.join(args)) # client.print = print_to_file - logging.basicConfig(level=logging.DEBUG, filename=config['log']) + logging.basicConfig( + level=logging.DEBUG, + filename=config['log'], + format='%(asctime)s:%(levelname)s:%(filename)s:%(lineno)d:%(message)s') else: # Add an empty handler so the logger doesn't complain logging.root.addHandler(logging.NullHandler()) diff --git a/rtv/terminal.py b/rtv/terminal.py index 70bc2f0..ccad825 100644 --- a/rtv/terminal.py +++ b/rtv/terminal.py @@ -385,19 +385,26 @@ class Terminal(object): @contextmanager def open_editor(self, data=''): """ - Open a temporary file using the system's default editor. + Open a file for editing using the system's default editor. - The data string will be written to the file before opening. This - function will block until the editor has closed. At that point the file - will be read and and lines starting with '#' will be stripped. If no - errors occur, the file will be deleted when the context manager closes. + After the file has been altered, the text will be read back and lines + starting with '#' will be stripped. If an error occurs inside of the + context manager, the file will be preserved. Otherwise, the file will + be deleted when the context manager closes. + + Params: + data (str): If provided, text will be written to the file before + opening it with the editor. + + Returns: + text (str): The text that the user entered into the editor. """ filename = 'rtv_{:%Y%m%d_%H%M%S}.txt'.format(datetime.now()) filepath = os.path.join(tempfile.gettempdir(), filename) with codecs.open(filepath, 'w', 'utf-8') as fp: - fp.write(self.clean(data)) + fp.write(data) _logger.info('File created: {}'.format(filepath)) editor = os.getenv('RTV_EDITOR') or os.getenv('EDITOR') or 'nano' @@ -426,8 +433,8 @@ class Terminal(object): # If no errors occurred, try to remove the file try: os.remove(filepath) - except OSError as e: - _logger.exception(e) + except OSError: + _logger.warning('Could not delete: {}'.format(filepath)) else: _logger.info('File deleted: {}'.format(filepath)) diff --git a/tests/conftest.py b/tests/conftest.py index 6632c31..02ee9ee 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -27,7 +27,9 @@ except ImportError: patch = partial(mock.patch, autospec=True) # Turn on logging, but disable vcr from spamming -logging.basicConfig(level=logging.DEBUG) +logging.basicConfig( + level=logging.DEBUG, + format='%(asctime)s:%(levelname)s:%(filename)s:%(lineno)d:%(message)s') for name in ['vcr.matchers', 'vcr.stubs']: logging.getLogger(name).disabled = True diff --git a/tests/test_submission.py b/tests/test_submission.py index 57d6922..e224bd7 100644 --- a/tests/test_submission.py +++ b/tests/test_submission.py @@ -178,7 +178,7 @@ def test_submission_comment(submission_page, terminal, refresh_token): with mock.patch('praw.objects.Submission.add_comment') as add_comment, \ mock.patch.object(terminal, 'open_editor') as open_editor, \ mock.patch('time.sleep'): - open_editor.return_value = 'comment text' + open_editor.return_value.__enter__.return_value = 'comment text' submission_page.controller.trigger('c') assert open_editor.called @@ -233,7 +233,7 @@ def test_submission_edit(submission_page, terminal, refresh_token): with mock.patch('praw.objects.Submission.edit') as edit, \ mock.patch.object(terminal, 'open_editor') as open_editor, \ mock.patch('time.sleep'): - open_editor.return_value = 'submission text' + open_editor.return_value.__enter__.return_value = 'submission text' submission_page.controller.trigger('e') assert open_editor.called @@ -249,7 +249,7 @@ def test_submission_edit(submission_page, terminal, refresh_token): with mock.patch('praw.objects.Comment.edit') as edit, \ mock.patch.object(terminal, 'open_editor') as open_editor, \ mock.patch('time.sleep'): - open_editor.return_value = 'comment text' + open_editor.return_value.__enter__.return_value = 'comment text' submission_page.controller.trigger('e') assert open_editor.called diff --git a/tests/test_subreddit.py b/tests/test_subreddit.py index 81b7730..dbc9b3e 100644 --- a/tests/test_subreddit.py +++ b/tests/test_subreddit.py @@ -148,7 +148,7 @@ def test_subreddit_post(subreddit_page, terminal, reddit, refresh_token): # Post a submission with a title but with no body subreddit_page.refresh_content(name='python') with mock.patch.object(terminal, 'open_editor'): - terminal.open_editor.return_value = 'title' + terminal.open_editor.return_value.__enter__.return_value = 'title' subreddit_page.controller.trigger('c') text = 'Canceled'.encode('utf-8') terminal.stdscr.subwin.addstr.assert_called_with(1, 1, text) @@ -160,7 +160,7 @@ def test_subreddit_post(subreddit_page, terminal, reddit, refresh_token): mock.patch.object(reddit, 'submit'), \ mock.patch('rtv.page.Page.loop') as loop, \ mock.patch('time.sleep'): - terminal.open_editor.return_value = 'test\ncontent' + terminal.open_editor.return_value.__enter__.return_value = 'test\ncont' reddit.submit.return_value = submission subreddit_page.controller.trigger('c') assert reddit.submit.called diff --git a/tests/test_terminal.py b/tests/test_terminal.py index de6a07f..ef3808b 100644 --- a/tests/test_terminal.py +++ b/tests/test_terminal.py @@ -10,6 +10,7 @@ import pytest from rtv.docs import HELP, COMMENT_EDIT_FILE from rtv.objects import Color +from rtv.exceptions import TemporaryFileError try: from unittest import mock @@ -269,7 +270,10 @@ def test_prompt_y_or_n(terminal, stdscr): assert curses.flash.called -def test_open_editor(terminal): +@pytest.mark.parametrize('ascii', [True, False]) +def test_open_editor(terminal, ascii): + + terminal.ascii = ascii comment = COMMENT_EDIT_FILE.format(content='#| This is a comment! ❤') data = {'filename': None} @@ -284,11 +288,57 @@ def test_open_editor(terminal): with mock.patch('subprocess.Popen', autospec=True) as Popen: Popen.side_effect = side_effect - reply_text = terminal.open_editor(comment) - assert reply_text == 'This is an amended comment! ❤' + with terminal.open_editor(comment) as reply_text: + assert reply_text == 'This is an amended comment! ❤' + assert os.path.isfile(data['filename']) + assert curses.endwin.called + assert curses.doupdate.called assert not os.path.isfile(data['filename']) - assert curses.endwin.called - assert curses.doupdate.called + + +def test_open_editor_error(terminal): + + with mock.patch('subprocess.Popen', autospec=True) as Popen, \ + mock.patch.object(terminal, 'show_notification'): + + # Invalid editor + Popen.side_effect = OSError + with terminal.open_editor('hello') as text: + assert text == 'hello' + assert 'Could not open' in terminal.show_notification.call_args[0][0] + + data = {'filename': None} + + def side_effect(args): + data['filename'] = args[1] + return mock.Mock() + + # Temporary File Errors don't delete the file + Popen.side_effect = side_effect + with terminal.open_editor('test'): + assert os.path.isfile(data['filename']) + raise TemporaryFileError() + assert os.path.isfile(data['filename']) + os.remove(data['filename']) + + # Other Exceptions don't delete the file *and* are propagated + Popen.side_effect = side_effect + with pytest.raises(ValueError): + with terminal.open_editor('test'): + assert os.path.isfile(data['filename']) + raise ValueError() + assert os.path.isfile(data['filename']) + os.remove(data['filename']) + + # Gracefully handle the case when we can't remove the file + with mock.patch.object(os, 'remove'): + os.remove.side_effect = OSError + with terminal.open_editor(): + pass + assert os.remove.called + + assert os.path.isfile(data['filename']) + os.remove(data['filename']) def test_open_browser(terminal):