diff options
Diffstat (limited to 'tests/unit/test_file_processor.py')
| -rw-r--r-- | tests/unit/test_file_processor.py | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/tests/unit/test_file_processor.py b/tests/unit/test_file_processor.py new file mode 100644 index 0000000..db17c2e --- /dev/null +++ b/tests/unit/test_file_processor.py @@ -0,0 +1,293 @@ +"""Tests for the FileProcessor class.""" +import ast +import optparse +import tokenize + +from flake8 import processor + +import mock +import pytest + + +def options_from(**kwargs): + """Generate a Values instances with our kwargs.""" + kwargs.setdefault('hang_closing', True) + kwargs.setdefault('max_line_length', 79) + kwargs.setdefault('verbose', False) + return optparse.Values(kwargs) + + +def test_read_lines_splits_lines(): + """Verify that read_lines splits the lines of the file.""" + file_processor = processor.FileProcessor(__file__, options_from()) + lines = file_processor.lines + assert len(lines) > 5 + assert '"""Tests for the FileProcessor class."""\n' in lines + + +@pytest.mark.parametrize('first_line', [ + '\xEF\xBB\xBF"""Module docstring."""\n', + u'\uFEFF"""Module docstring."""\n', +]) +def test_strip_utf_bom(first_line): + r"""Verify that we strip '\xEF\xBB\xBF' from the first line.""" + lines = [first_line] + file_processor = processor.FileProcessor('-', options_from(), lines[:]) + assert file_processor.lines != lines + assert file_processor.lines[0] == '"""Module docstring."""\n' + + +@pytest.mark.parametrize('lines, expected', [ + (['\xEF\xBB\xBF"""Module docstring."""\n'], False), + ([u'\uFEFF"""Module docstring."""\n'], False), + (['#!/usr/bin/python', '# flake8 is great', 'a = 1'], False), + (['#!/usr/bin/python', '# flake8: noqa', 'a = 1'], True), + (['# flake8: noqa', '#!/usr/bin/python', 'a = 1'], True), + (['#!/usr/bin/python', 'a = 1', '# flake8: noqa'], True), +]) +def test_should_ignore_file(lines, expected): + """Verify that we ignore a file if told to.""" + file_processor = processor.FileProcessor('-', options_from(), lines) + assert file_processor.should_ignore_file() is expected + + +@mock.patch('flake8.utils.stdin_get_value') +def test_read_lines_from_stdin(stdin_get_value): + """Verify that we use our own utility function to retrieve stdin.""" + stdin_value = mock.Mock() + stdin_value.splitlines.return_value = [] + stdin_get_value.return_value = stdin_value + file_processor = processor.FileProcessor('-', options_from()) + stdin_get_value.assert_called_once_with() + stdin_value.splitlines.assert_called_once_with(True) + + +@mock.patch('flake8.utils.stdin_get_value') +def test_read_lines_sets_filename_attribute(stdin_get_value): + """Verify that we update the filename attribute.""" + stdin_value = mock.Mock() + stdin_value.splitlines.return_value = [] + stdin_get_value.return_value = stdin_value + file_processor = processor.FileProcessor('-', options_from()) + assert file_processor.filename == 'stdin' + + +def test_line_for(): + """Verify we grab the correct line from the cached lines.""" + file_processor = processor.FileProcessor('-', options_from(), lines=[ + 'Line 1', + 'Line 2', + 'Line 3', + ]) + + for i in range(1, 4): + assert file_processor.line_for(i) == 'Line {0}'.format(i) + + +def test_next_line(): + """Verify we update the file_processor state for each new line.""" + file_processor = processor.FileProcessor('-', options_from(), lines=[ + 'Line 1', + 'Line 2', + 'Line 3', + ]) + + for i in range(1, 4): + assert file_processor.next_line() == 'Line {}'.format(i) + assert file_processor.line_number == i + + +@pytest.mark.parametrize('error_code, line, expected_indent_char', [ + ('E101', '\t\ta = 1', '\t'), + ('E101', ' a = 1', ' '), + ('W101', 'frobulate()', None), + ('F821', 'class FizBuz:', None), +]) +def test_check_physical_error(error_code, line, expected_indent_char): + """Verify we update the indet char for the appropriate error code.""" + file_processor = processor.FileProcessor('-', options_from(), lines=[ + 'Line 1', + ]) + + file_processor.check_physical_error(error_code, line) + assert file_processor.indent_char == expected_indent_char + + +@pytest.mark.parametrize('params, args, expected_kwargs', [ + (['blank_before', 'blank_lines'], None, {'blank_before': 0, + 'blank_lines': 0}), + (['noqa', 'fake'], {'fake': 'foo'}, {'noqa': False, 'fake': 'foo'}), + (['blank_before', 'blank_lines', 'noqa'], + {'blank_before': 10, 'blank_lines': 5, 'noqa': True}, + {'blank_before': 10, 'blank_lines': 5, 'noqa': True}), + ([], {'fake': 'foo'}, {'fake': 'foo'}), +]) +def test_keyword_arguments_for(params, args, expected_kwargs): + """Verify the keyword args are generated properly.""" + file_processor = processor.FileProcessor('-', options_from(), lines=[ + 'Line 1', + ]) + kwargs_for = file_processor.keyword_arguments_for + + assert kwargs_for(params, args) == expected_kwargs + + +def test_keyword_arguments_for_does_not_handle_attribute_errors(): + """Verify we re-raise AttributeErrors.""" + file_processor = processor.FileProcessor('-', options_from(), lines=[ + 'Line 1', + ]) + + with pytest.raises(AttributeError): + file_processor.keyword_arguments_for(['fake']) + + +@pytest.mark.parametrize('unsplit_line, expected_lines', [ + ('line', []), + ('line 1\n', ['line 1']), + ('line 1\nline 2\n', ['line 1', 'line 2']), + ('line 1\n\nline 2\n', ['line 1', '', 'line 2']), +]) +def test_split_line(unsplit_line, expected_lines): + """Verify the token line spliting.""" + file_processor = processor.FileProcessor('-', options_from(), lines=[ + 'Line 1', + ]) + + actual_lines = list(file_processor.split_line((1, unsplit_line))) + assert expected_lines == actual_lines + + assert len(actual_lines) == file_processor.line_number + + +def test_build_ast(): + """Verify the logic for how we build an AST for plugins.""" + file_processor = processor.FileProcessor('-', options_from(), lines=[ + 'a = 1\n' + ]) + + module = file_processor.build_ast() + assert isinstance(module, ast.Module) + + +def test_next_logical_line_updates_the_previous_logical_line(): + """Verify that we update our tracking of the previous logical line.""" + file_processor = processor.FileProcessor('-', options_from(), lines=[ + 'a = 1\n' + ]) + + file_processor.indent_level = 1 + file_processor.logical_line = 'a = 1' + assert file_processor.previous_logical == '' + assert file_processor.previous_indent_level is 0 + + file_processor.next_logical_line() + assert file_processor.previous_logical == 'a = 1' + assert file_processor.previous_indent_level == 1 + + +def test_visited_new_blank_line(): + """Verify we update the number of blank lines seen.""" + file_processor = processor.FileProcessor('-', options_from(), lines=[ + 'a = 1\n' + ]) + + assert file_processor.blank_lines == 0 + file_processor.visited_new_blank_line() + assert file_processor.blank_lines == 1 + + +def test_inside_multiline(): + """Verify we update the line number and reset multiline.""" + file_processor = processor.FileProcessor('-', options_from(), lines=[ + 'a = 1\n' + ]) + + assert file_processor.multiline is False + assert file_processor.line_number == 0 + with file_processor.inside_multiline(10): + assert file_processor.multiline is True + assert file_processor.line_number == 10 + + assert file_processor.multiline is False + + +@pytest.mark.parametrize('string, expected', [ + ('""', '""'), + ("''", "''"), + ('"a"', '"x"'), + ("'a'", "'x'"), + ('"x"', '"x"'), + ("'x'", "'x'"), + ('"abcdef"', '"xxxxxx"'), + ("'abcdef'", "'xxxxxx'"), + ('""""""', '""""""'), + ("''''''", "''''''"), + ('"""a"""', '"""x"""'), + ("'''a'''", "'''x'''"), + ('"""x"""', '"""x"""'), + ("'''x'''", "'''x'''"), + ('"""abcdef"""', '"""xxxxxx"""'), + ("'''abcdef'''", "'''xxxxxx'''"), + ('"""xxxxxx"""', '"""xxxxxx"""'), + ("'''xxxxxx'''", "'''xxxxxx'''"), +]) +def test_mutate_string(string, expected): + """Verify we appropriately mutate the string to sanitize it.""" + actual = processor.mutate_string(string) + assert expected == actual + + +@pytest.mark.parametrize('string, expected', [ + (' ', 4), + (' ', 6), + ('\t', 8), + ('\t\t', 16), + (' \t', 8), + (' \t', 16), +]) +def test_expand_indent(string, expected): + """Verify we correctly measure the amount of indentation.""" + actual = processor.expand_indent(string) + assert expected == actual + + +@pytest.mark.parametrize('token, log_string', [ + [(tokenize.COMMENT, '# this is a comment', + (1, 0), # (start_row, start_column) + (1, 19), # (end_ro, end_column) + '# this is a comment',), + "l.1\t[:19]\tCOMMENT\t'# this is a comment'"], + [(tokenize.COMMENT, '# this is a comment', + (1, 5), # (start_row, start_column) + (1, 19), # (end_ro, end_column) + '# this is a comment',), + "l.1\t[5:19]\tCOMMENT\t'# this is a comment'"], + [(tokenize.COMMENT, '# this is a comment', + (1, 0), # (start_row, start_column) + (2, 19), # (end_ro, end_column) + '# this is a comment',), + "l.1\tl.2\tCOMMENT\t'# this is a comment'"], +]) +def test_log_token(token, log_string): + """Verify we use the log object passed in.""" + LOG = mock.Mock() + processor.log_token(LOG, token) + LOG.log.assert_called_once_with( + 5, # flake8._EXTRA_VERBOSE + log_string, + ) + + +@pytest.mark.parametrize('current_count, token_text, expected', [ + (None, '(', 1), + (None, '[', 1), + (None, '{', 1), + (1, ')', 0), + (1, ']', 0), + (1, '}', 0), + (10, '+', 10), +]) +def test_count_parentheses(current_count, token_text, expected): + """Verify our arithmetic is correct.""" + assert processor.count_parentheses(current_count, token_text) == expected |
