diff --git a/flake8_koles/checker.py b/flake8_koles/checker.py index ee866aa..3bbdb70 100644 --- a/flake8_koles/checker.py +++ b/flake8_koles/checker.py @@ -18,7 +18,7 @@ class KolesChecker: name = 'flake8-koles' options = optparse.Values() - SWEAR_DATA_DIR = '/data/swear_list' + SWEAR_DATA_DIR = 'data/swear_list' version = __version__ def __init__(self, tree: ast.Module, filename: str) -> None: @@ -47,11 +47,14 @@ def add_options(cls, parser: OptionManager) -> None: parse_from_config=True, action='store_true' ) + + lang_choices = cls._get_lang_choices() parser.add_option( '--lang', default='english', parse_from_config=True, - comma_separated_list=True + comma_separated_list=True, + choices=lang_choices ) @classmethod @@ -59,18 +62,6 @@ def parse_options(cls, options: optparse.Values) -> None: """Get parser options from flake8.""" cls.options = options - def _check_row(self, string: str) -> List[Tuple[int, str]]: - """Return a list containing bad words and their positions.""" - if self._pattern == '': - return [] - - regex = re.compile(f'(?=({self._pattern}))', flags=re.IGNORECASE) - - return [ - (match.start(), match.group(1)) - for match in regex.finditer(string) - ] - def _get_bad_words(self) -> Set[str]: """Get a set of bad words.""" data = self._get_swears_data() @@ -99,12 +90,6 @@ def _get_file_content(self) -> List[str]: else: return readlines(self.filename) - def _censor_word(self, word: str) -> str: - """Replace all letters but first with `*` if censor_msg option is True.""" - if self.options.censor_msg: - return word[0] + '*' * (len(word) - 1) - return word - def _get_filename_errors(self) -> Generator[Tuple[int, int, str, type], None, None]: """Get filename errors if exist.""" filename_errors = self._check_row(os.path.basename(self.filename)) @@ -134,3 +119,30 @@ def _get_content_errors( ) for column, word in errors ) + + def _check_row(self, string: str) -> List[Tuple[int, str]]: + """Return a list containing bad words and their positions.""" + if self._pattern == '': + return [] + + regex = re.compile(f'(?=({self._pattern}))', flags=re.IGNORECASE) + + return [ + (match.start(), match.group(1)) + for match in regex.finditer(string) + ] + + def _censor_word(self, word: str) -> str: + """Replace all letters but first with `*` if censor_msg option is True.""" + if self.options.censor_msg: + return word[0] + '*' * (len(word) - 1) + return word + + @classmethod + def _get_lang_choices(cls) -> List[str]: + """Get language choices by removing .dat from language filenames.""" + return [ + lang_file.replace('.dat', '') + for lang_file in + pkg_resources.resource_listdir(__name__, cls.SWEAR_DATA_DIR) + ] diff --git a/tests/test_checker.py b/tests/test_checker.py index 6b11c9d..ae5c1cc 100644 --- a/tests/test_checker.py +++ b/tests/test_checker.py @@ -8,6 +8,62 @@ from flake8_koles.checker import KolesChecker +@mock.patch('flake8_koles.checker.readlines') +@mock.patch('flake8_koles.checker.KolesChecker._get_bad_words') +def test_run( + mock_get_bad_words, + mock_readlines +): + """Test that flake interface returns appropriate error messages.""" + mock_get_bad_words.return_value = ['very', 'bad', 'words'] + mock_readlines.return_value = ['Test very', 'nice', 'and bad words'] + koles_checker = KolesChecker(tree='test_tree', filename='test_filename') + koles_checker.options = Mock(censor_msg=True) + result = [*koles_checker.run()] + + assert result == [ + (1, 5, 'KOL001 Bad language found: v***', KolesChecker), + (3, 4, 'KOL001 Bad language found: b**', KolesChecker), + (3, 8, 'KOL001 Bad language found: w****', KolesChecker)] + + +def test_add_options(koles_checker): + """Test that all options are added to the parser.""" + option_manager = OptionManager() + koles_checker.add_options(option_manager) + + assert repr(option_manager.options) == repr( + [ + Option( + long_option_name='--ignore-shorties', + default=0, + type='int', + parse_from_config=True + ), + Option( + long_option_name='--censor-msg', + default=0, + parse_from_config=True, + action='store_true' + ), + Option( + long_option_name='--lang', + default='english', + parse_from_config=True, + comma_separated_list=True + ) + ] + ) + + +def test_parse_options(koles_checker): + """Test that options are correctly assigned to the class.""" + test_options = {'kick_it': True} + koles_checker.parse_options(test_options) + + assert koles_checker.options == test_options + + @pytest.mark.parametrize( "ignore_shorties, expected_result, get_swears_data_value", ( @@ -59,7 +115,7 @@ def test_get_bad_words( ), ) @mock.patch('flake8_koles.checker.pkg_resources.resource_string') -def test_get_swear_data( +def test_get_swears_data( mock_resource_string, lang, resource_string_value, @@ -76,64 +132,6 @@ def test_get_swear_data( assert result == expected_result -@pytest.mark.parametrize( - 'pattern, string, expected_result', - ( - # Case 1: Multiple overlapping patterns - ('abcd|ab|abc|cd', 'abcdab', [(0, 'abcd'), (2, 'cd'), (4, 'ab')]), - # Case 2: Single non-overlapping pattern - ('ab', 'abcdab', [(0, 'ab'), (4, 'ab')]), - # Case 3: Empty string - ('(?=(ab))', '', []), - # Case 4: Empty pattern - ('', 'abcdab', []), - # Case 6: Empty string and pattern - ('', '', []), - # Case 7: Uppercase string - ('abcd|ab|abc|cd', 'ABCDAB', [(0, 'ABCD'), (2, 'CD'), (4, 'AB')]), - ), -) -def test_check_row( - pattern, - string, - expected_result, - koles_checker -): - """Test that check_string returns appropriate value for given pattern and string.""" - koles_checker._pattern = pattern - result = koles_checker._check_row(string) - - assert [*result] == expected_result - - -@pytest.mark.parametrize( - 'word, censor_msg, expected_result', - ( - ('Mike D', True, 'M*****'), - ('Mike D', False, 'Mike D'), - ('MCA', True, 'M**'), - ('MCA', False, 'MCA'), - ('Ad-Rock', True, 'A******'), - ('Ad-Rock', False, 'Ad-Rock'), - ), -) -def test_censor_word( - word, - censor_msg, - expected_result, - koles_checker -): - """Test censor_word. - - Test that the function returns proper set of bad words - depending on ignore-shorties option. - """ - koles_checker.options = Mock(censor_msg=censor_msg) - result = koles_checker._censor_word(word) - - assert result == expected_result - - @pytest.mark.parametrize( 'filename,', ( @@ -261,56 +259,80 @@ def test_get_content_errors( assert result == expected_result -@mock.patch('flake8_koles.checker.readlines') -@mock.patch('flake8_koles.checker.KolesChecker._get_bad_words') -def test_run( - mock_get_bad_words, - mock_readlines +@pytest.mark.parametrize( + 'pattern, string, expected_result', + ( + # Case 1: Multiple overlapping patterns + ('abcd|ab|abc|cd', 'abcdab', [(0, 'abcd'), (2, 'cd'), (4, 'ab')]), + # Case 2: Single non-overlapping pattern + ('ab', 'abcdab', [(0, 'ab'), (4, 'ab')]), + # Case 3: Empty string + ('(?=(ab))', '', []), + # Case 4: Empty pattern + ('', 'abcdab', []), + # Case 6: Empty string and pattern + ('', '', []), + # Case 7: Uppercase string + ('abcd|ab|abc|cd', 'ABCDAB', [(0, 'ABCD'), (2, 'CD'), (4, 'AB')]), + ), +) +def test_check_row( + pattern, + string, + expected_result, + koles_checker ): - """Test that flake interface returns appropriate error messages.""" - mock_get_bad_words.return_value = ['very', 'bad', 'words'] - mock_readlines.return_value = ['Test very', 'nice', 'and bad words'] - koles_checker = KolesChecker(tree='test_tree', filename='test_filename') - koles_checker.options = Mock(censor_msg=True) - result = [*koles_checker.run()] - assert result == [ - (1, 5, 'KOL001 Bad language found: v***', KolesChecker), - (3, 4, 'KOL001 Bad language found: b**', KolesChecker), - (3, 8, 'KOL001 Bad language found: w****', KolesChecker)] + """Test that check_string returns appropriate value for given pattern and string.""" + koles_checker._pattern = pattern + result = koles_checker._check_row(string) + assert [*result] == expected_result -def test_add_options(koles_checker): - """Test that all options are added to the parser.""" - option_manager = OptionManager() - koles_checker.add_options(option_manager) - assert repr(option_manager.options) == repr( - [ - Option( - long_option_name='--ignore-shorties', - default=0, - type='int', - parse_from_config=True - ), - Option( - long_option_name='--censor-msg', - default=0, - parse_from_config=True, - action='store_true' - ), - Option( - long_option_name='--lang', - default='english', - parse_from_config=True, - comma_separated_list=True - ) - ] - ) +@pytest.mark.parametrize( + 'word, censor_msg, expected_result', + ( + ('Mike D', True, 'M*****'), + ('Mike D', False, 'Mike D'), + ('MCA', True, 'M**'), + ('MCA', False, 'MCA'), + ('Ad-Rock', True, 'A******'), + ('Ad-Rock', False, 'Ad-Rock'), + ), +) +def test_censor_word( + word, + censor_msg, + expected_result, + koles_checker +): + """Test censor_word. + Test that the function returns proper set of bad words + depending on ignore-shorties option. + """ + koles_checker.options = Mock(censor_msg=censor_msg) + result = koles_checker._censor_word(word) -def test_parse_options(koles_checker): - """Test that options are correctly assigned to the class.""" - test_options = {'kick_it': True} - koles_checker.parse_options(test_options) + assert result == expected_result - assert koles_checker.options == test_options + +@pytest.mark.parametrize( + 'listdir_value, expected_result', + ( + # Case: empty language dir + ([], []), + (['english.dat'], ['english']), + (['english.dat', 'polish.dat'], ['english', 'polish']), + ), +) +@mock.patch('flake8_koles.checker.pkg_resources.resource_listdir') +def test_get_lang_choices( + listdir_mock, + listdir_value, + expected_result, + koles_checker): + """Test that proper language names are fetched from the resources.""" + listdir_mock.return_value = listdir_value + + assert koles_checker._get_lang_choices() == expected_result