Skip to content

Commit b2f2da6

Browse files
author
AlgorithmAlchemy
committed
chore(build): cleanup code and add new unit test
1 parent 2eaac83 commit b2f2da6

File tree

3 files changed

+178
-13
lines changed

3 files changed

+178
-13
lines changed

flake8_no_emoji/checker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,4 @@ def run(self) -> Generator[Tuple[int, int, str, Type["NoEmojiChecker"]], None, N
6767
continue
6868

6969
yield lineno, match.start(), self._error_tmpl, type(self)
70-
break # stop after 1 match
70+
break # stop after 1 match

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ readme = "README.md"
1212
requires-python = ">=3.8"
1313
license = { text = "MIT" }
1414
authors = [
15-
{ name = "AlgorithmAlchemy" }
15+
{ name = "AlgorithmAlchemy" }
1616
]
1717
keywords = ["flake8", "plugin", "emoji", "linter", "pre-commit", "unicode", "code style", "static analysis", "code quality", "no-emoji-in-code"]
1818
classifiers = [
@@ -33,8 +33,8 @@ classifiers = [
3333
]
3434

3535
dependencies = [
36-
"regex",
37-
"emoji>=2.0.0"
36+
"regex",
37+
"emoji>=2.0.0"
3838
]
3939

4040
[project.entry-points."flake8.extension"]

tests/test_checker.py

Lines changed: 174 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,20 @@
77
from flake8_no_emoji.checker import NoEmojiChecker
88

99

10-
def run_checker_on_content(content, ignore_emoji_types=None, only_emoji_types=None):
11-
"""Write content to temp file, set options, run checker, return results."""
12-
fd, path = tempfile.mkstemp(suffix=".py", text=True)
10+
def run_checker_on_content(content, ignore_emoji_types=None, only_emoji_types=None, filename=None):
11+
"""
12+
Write content to a temp file (if filename not provided), set options, run checker, return results.
13+
Allows passing a real file path for special cases like unreadable or binary files.
14+
"""
15+
if filename is None:
16+
fd, path = tempfile.mkstemp(suffix=".py", text=True)
17+
else:
18+
path = filename
19+
1320
try:
14-
with os.fdopen(fd, "w", encoding="utf-8") as f:
15-
f.write(content)
21+
if filename is None:
22+
with os.fdopen(fd, "w", encoding="utf-8") as f:
23+
f.write(content)
1624

1725
opts = SimpleNamespace(
1826
ignore_emoji_types=(ignore_emoji_types or ""),
@@ -24,10 +32,11 @@ def run_checker_on_content(content, ignore_emoji_types=None, only_emoji_types=No
2432
checker._only_categories = NoEmojiChecker._only_categories
2533
return list(checker.run())
2634
finally:
27-
try:
28-
os.unlink(path)
29-
except OSError:
30-
pass
35+
if filename is None:
36+
try:
37+
os.unlink(path)
38+
except OSError:
39+
pass
3140

3241

3342
def test_detect_any_emoji_by_default():
@@ -181,3 +190,159 @@ def test_multiple_lines_with_only_emojis():
181190
content = "😀\n🐶\n\n🛸\n👩‍💻"
182191
results = run_checker_on_content(content)
183192
assert len(results) == 5, "Each line with single emoji should be detected"
193+
194+
195+
def test_empty_file():
196+
results = run_checker_on_content("")
197+
assert results == [], "Empty file should produce no results"
198+
199+
200+
def test_empty_comment():
201+
results = run_checker_on_content("# ")
202+
assert results == [], "Empty comment should produce no results"
203+
204+
205+
def test_unreadable_file(tmp_path):
206+
path = tmp_path / "unreadable.py"
207+
path.write_text("x = '😀'")
208+
os.chmod(path, 0) # remove permissions
209+
results = run_checker_on_content("", filename=str(path))
210+
assert results == [], "Unreadable file should produce no results"
211+
os.chmod(path, 0o644)
212+
213+
214+
def test_binary_file(tmp_path):
215+
path = tmp_path / "binary.bin"
216+
path.write_bytes(b"\x00\x01\x02\x03\x04")
217+
results = run_checker_on_content("", filename=str(path))
218+
assert results == [], "Binary file should produce no results"
219+
220+
221+
def test_disable_all_checks():
222+
# simulate ignoring all categories
223+
results = run_checker_on_content("x = '😀🐶⭐'",
224+
ignore_emoji_types="PEOPLE,NATURE,FOOD,ACTIVITY,TRAVEL,OBJECTS,SYMBOLS,FLAGS,OTHER")
225+
assert results == [], "All emoji checks disabled should produce no results"
226+
227+
228+
def test_non_english_with_noqa():
229+
content = "# Привет 🌸 # noqa"
230+
results = run_checker_on_content(content)
231+
assert results == [], "Line with # noqa should skip emoji check"
232+
233+
234+
def test_comment_with_multiple_noqa():
235+
content = "# 🚀 test # noqa something else # noqa"
236+
results = run_checker_on_content(content)
237+
assert results == [], "Multiple # noqa should skip check"
238+
239+
240+
def test_string_with_noqa():
241+
content = "x = '😀' # noqa"
242+
results = run_checker_on_content(content)
243+
assert results == [], "String with # noqa should skip check"
244+
245+
246+
def test_emoji_in_multiline_string():
247+
content = '''x = """Line 1
248+
🚀 Line 2
249+
Line 3 🐶"""'''
250+
results = run_checker_on_content(content)
251+
assert len(results) == 2, "Should detect emoji in multiline strings"
252+
253+
254+
def test_emoji_in_docstring():
255+
content = '''"""
256+
This is a docstring with emoji 🌸
257+
"""'''
258+
results = run_checker_on_content(content)
259+
assert len(results) == 1, "Should detect emoji in docstrings"
260+
261+
262+
def test_emoji_in_variable_name():
263+
content = "😀 = 5"
264+
results = run_checker_on_content(content)
265+
assert results, "Should detect emoji in variable names"
266+
267+
268+
def test_emoji_in_function_name():
269+
content = "def 🐶():\n pass"
270+
results = run_checker_on_content(content)
271+
assert results, "Should detect emoji in function names"
272+
273+
274+
def test_multiple_noqa_in_code():
275+
content = "# noqa 🚀\n😀 # noqa"
276+
results = run_checker_on_content(content)
277+
assert results == [], "Multiple # noqa should skip all checks"
278+
279+
280+
def test_only_category_with_no_matches():
281+
results = run_checker_on_content("x = '😀🐶'", only_emoji_types="FLAGS")
282+
assert results == [], "No matches for only category should yield no results"
283+
284+
285+
def test_ignore_category_with_all_matches():
286+
results = run_checker_on_content("x = '😀🐶⭐'", ignore_emoji_types="PEOPLE,NATURE,SYMBOLS")
287+
assert results == [], "Ignoring all categories with matches should yield no results"
288+
289+
290+
def test_unicode_non_emoji_characters():
291+
content = "x = '© ™ ∑ √'"
292+
results = run_checker_on_content(content)
293+
assert results == [], "Unicode symbols that are not emojis should not be detected"
294+
295+
296+
def test_emoji_with_combining_characters():
297+
content = "x = '🇺🇸‍👩‍🚀'"
298+
results = run_checker_on_content(content)
299+
assert results, "Emoji with combining characters should be detected"
300+
301+
302+
def test_long_file_with_sparse_emojis(tmp_path):
303+
path = tmp_path / "long_file.py"
304+
lines = ["print('line {}')\n".format(i) for i in range(1000)]
305+
lines[500] = "print('🚀')\n"
306+
path.write_text("".join(lines), encoding="utf-8")
307+
results = run_checker_on_content("", filename=str(path))
308+
assert results, "Long file with sparse emoji should detect at least one"
309+
310+
311+
def test_noqa_with_different_cases():
312+
content = "# NoQA 🚀\n😀 # NoQa"
313+
results = run_checker_on_content(content)
314+
assert results == [], "NoQA in different cases should still skip checks"
315+
316+
317+
def test_multiple_emojis_with_noqa():
318+
content = "x = '😀🐶⭐' # noqa"
319+
results = run_checker_on_content(content)
320+
assert results == [], "Line with multiple emojis and noqa should skip all checks"
321+
322+
323+
def test_comment_only_with_emoji():
324+
content = "# 🚀"
325+
results = run_checker_on_content(content)
326+
assert results, "Emoji in comment should be detected"
327+
328+
329+
def test_comment_with_emoji_and_text():
330+
content = "# This is 🚀 a comment"
331+
results = run_checker_on_content(content)
332+
assert results, "Emoji in comment with text should be detected"
333+
334+
335+
def test_long_line_with_multiple_emojis():
336+
content = "x = '😀🐶⭐🛸👩‍💻🏳️‍🌈' * 100"
337+
results = run_checker_on_content(content)
338+
assert len(results) == 1, "Only first emoji per line should be reported even for long lines"
339+
340+
341+
def test_ignore_case_category_names():
342+
results = run_checker_on_content("x = '😀'", ignore_emoji_types="people")
343+
assert results == [], "Category ignoring should be case insensitive"
344+
345+
346+
def test_only_case_category_names():
347+
results = run_checker_on_content("x = '🐶'", only_emoji_types="nature")
348+
assert results, "Category only check should be case insensitive"

0 commit comments

Comments
 (0)