1"""Tests for the BaseFormatter object.""" 2import argparse 3from unittest import mock 4 5import pytest 6 7from flake8 import style_guide 8from flake8.formatting import base 9 10 11def options(**kwargs): 12 """Create an argparse.Namespace instance.""" 13 kwargs.setdefault("output_file", None) 14 kwargs.setdefault("tee", False) 15 return argparse.Namespace(**kwargs) 16 17 18@pytest.mark.parametrize("filename", [None, "out.txt"]) 19def test_start(filename): 20 """Verify we open a new file in the start method.""" 21 mock_open = mock.mock_open() 22 formatter = base.BaseFormatter(options(output_file=filename)) 23 with mock.patch("flake8.formatting.base.open", mock_open): 24 formatter.start() 25 26 if filename is None: 27 assert mock_open.called is False 28 else: 29 mock_open.assert_called_once_with(filename, "a") 30 31 32def test_stop(): 33 """Verify we close open file objects.""" 34 filemock = mock.Mock() 35 formatter = base.BaseFormatter(options()) 36 formatter.output_fd = filemock 37 formatter.stop() 38 39 filemock.close.assert_called_once_with() 40 assert formatter.output_fd is None 41 42 43def test_format_needs_to_be_implemented(): 44 """Ensure BaseFormatter#format raises a NotImplementedError.""" 45 formatter = base.BaseFormatter(options()) 46 with pytest.raises(NotImplementedError): 47 formatter.format( 48 style_guide.Violation("A000", "file.py", 1, 1, "error text", None) 49 ) 50 51 52def test_show_source_returns_nothing_when_not_showing_source(): 53 """Ensure we return nothing when users want nothing.""" 54 formatter = base.BaseFormatter(options(show_source=False)) 55 assert ( 56 formatter.show_source( 57 style_guide.Violation( 58 "A000", "file.py", 1, 1, "error text", "line" 59 ) 60 ) 61 == "" 62 ) 63 64 65def test_show_source_returns_nothing_when_there_is_source(): 66 """Ensure we return nothing when there is no line.""" 67 formatter = base.BaseFormatter(options(show_source=True)) 68 assert ( 69 formatter.show_source( 70 style_guide.Violation("A000", "file.py", 1, 1, "error text", None) 71 ) 72 == "" 73 ) 74 75 76@pytest.mark.parametrize( 77 ("line1", "line2", "column"), 78 [ 79 ( 80 "x=1\n", 81 " ^", 82 2, 83 ), 84 ( 85 " x=(1\n +2)\n", 86 " ^", 87 5, 88 ), 89 ( 90 "\tx\t=\ty\n", 91 "\t \t \t^", 92 6, 93 ), 94 ], 95) 96def test_show_source_updates_physical_line_appropriately(line1, line2, column): 97 """Ensure the error column is appropriately indicated.""" 98 formatter = base.BaseFormatter(options(show_source=True)) 99 error = style_guide.Violation("A000", "file.py", 1, column, "error", line1) 100 output = formatter.show_source(error) 101 assert output == line1 + line2 102 103 104@pytest.mark.parametrize("tee", [False, True]) 105def test_write_uses_an_output_file(tee, capsys): 106 """Verify that we use the output file when it's present.""" 107 line = "Something to write" 108 source = "source" 109 filemock = mock.Mock() 110 111 formatter = base.BaseFormatter(options(tee=tee)) 112 formatter.output_fd = filemock 113 114 formatter.write(line, source) 115 if tee: 116 assert capsys.readouterr().out == f"{line}\n{source}\n" 117 else: 118 assert capsys.readouterr().out == "" 119 120 assert filemock.write.called is True 121 assert filemock.write.call_count == 2 122 assert filemock.write.mock_calls == [ 123 mock.call(line + formatter.newline), 124 mock.call(source + formatter.newline), 125 ] 126 127 128def test_write_produces_stdout(capsys): 129 """Verify that we write to stdout without an output file.""" 130 line = "Something to write" 131 source = "source" 132 133 formatter = base.BaseFormatter(options()) 134 formatter.write(line, source) 135 136 assert capsys.readouterr().out == f"{line}\n{source}\n" 137 138 139class AfterInitFormatter(base.BaseFormatter): 140 """Subclass for testing after_init.""" 141 142 def after_init(self): 143 """Define method to verify operation.""" 144 self.post_initialized = True 145 146 147def test_after_init_is_always_called(): 148 """Verify after_init is called.""" 149 formatter = AfterInitFormatter(options()) 150 assert formatter.post_initialized is True 151 152 153class FormatFormatter(base.BaseFormatter): 154 """Subclass for testing format.""" 155 156 def format(self, error): 157 """Define method to verify operation.""" 158 return repr(error) 159 160 161def test_handle_formats_the_error(): 162 """Verify that a formatter will call format from handle.""" 163 formatter = FormatFormatter(options(show_source=False)) 164 filemock = formatter.output_fd = mock.Mock() 165 error = style_guide.Violation( 166 code="A001", 167 filename="example.py", 168 line_number=1, 169 column_number=1, 170 text="Fake error", 171 physical_line="a = 1", 172 ) 173 174 formatter.handle(error) 175 176 filemock.write.assert_called_once_with(repr(error) + "\n") 177