Files
rich-print/tests/test_detect.py

236 lines
8.0 KiB
Python

"""Tests for file type detection."""
from __future__ import annotations
from pathlib import Path
import pytest
from rp.detect import EXTENSION_MAP, _FILENAME_MAP, detect_type
class TestExplicitType:
"""Priority 1: explicit_type always wins."""
def test_explicit_overrides_everything(self) -> None:
assert detect_type(path="foo.py", content="...", explicit_type="json") == "json"
def test_explicit_with_none_path_and_content(self) -> None:
assert detect_type(explicit_type="rust") == "rust"
def test_explicit_empty_string(self) -> None:
# Empty string is falsy but not None, so it should still win.
assert detect_type(explicit_type="") == ""
class TestFilenameMap:
"""Priority 2: special filenames like Dockerfile."""
def test_dockerfile(self) -> None:
assert detect_type(path="Dockerfile") == "docker"
def test_dockerfile_with_path(self) -> None:
assert detect_type(path="/some/dir/Dockerfile") == "docker"
def test_makefile(self) -> None:
assert detect_type(path="Makefile") == "make"
def test_makefile_with_path(self) -> None:
assert detect_type(path="src/Makefile") == "make"
class TestExtensionMap:
"""Priority 3: file extension lookup."""
def test_python(self) -> None:
assert detect_type(path="script.py") == "python"
def test_python_case_insensitive(self) -> None:
assert detect_type(path="SCRIPT.PY") == "python"
def test_json_extension(self) -> None:
assert detect_type(path="data.json") == "json"
def test_markdown(self) -> None:
assert detect_type(path="README.md") == "markdown"
def test_rust(self) -> None:
assert detect_type(path="main.rs") == "rust"
def test_go(self) -> None:
assert detect_type(path="main.go") == "go"
def test_unknown_extension(self) -> None:
assert detect_type(path="file.xyz") == "text"
def test_no_extension(self) -> None:
assert detect_type(path="Makefile") == "make"
def test_double_extension(self) -> None:
assert detect_type(path="archive.tar.gz") == "text"
def test_hidden_file(self) -> None:
assert detect_type(path=".bashrc") == "bash"
def test_zshrc(self) -> None:
assert detect_type(path=".zshrc") == "bash"
def test_profile(self) -> None:
assert detect_type(path=".profile") == "bash"
@pytest.mark.parametrize(
("suffix", "expected"),
sorted(EXTENSION_MAP.items()),
)
def test_all_known_extensions_detect(self, suffix: str, expected: str) -> None:
assert detect_type(path=f"example{suffix}") == expected
class TestShebangDetection:
"""Priority 4: detect common scripts from shebangs."""
def test_env_python3_shebang(self) -> None:
assert detect_type(content="#!/usr/bin/env python3\nprint('hi')\n") == "python"
def test_env_python3_with_assignment(self) -> None:
assert (
detect_type(content="#!/usr/bin/env FOO=1 python3\nprint('hi')\n")
== "python"
)
def test_env_ignore_environment_before_program(self) -> None:
assert detect_type(content="#!/usr/bin/env -i bash\necho hi\n") == "bash"
def test_env_split_string_shebang(self) -> None:
assert (
detect_type(content="#!/usr/bin/env -S python3 -u\nprint('hi')\n")
== "python"
)
def test_env_compact_split_string_shebang(self) -> None:
assert (
detect_type(content="#!/usr/bin/env -Spython3 -u\nprint('hi')\n")
== "python"
)
def test_env_quoted_split_string_shebang(self) -> None:
assert (
detect_type(
content='#!/usr/bin/env --split-string="python3 -u"\nprint("hi")\n'
)
== "python"
)
def test_env_long_option_with_equals(self) -> None:
assert (
detect_type(content="#!/usr/bin/env --ignore-signal=TERM bash\necho hi\n")
== "bash"
)
def test_direct_python_shebang(self) -> None:
assert detect_type(content="#!/usr/bin/python3\nprint('hi')\n") == "python"
def test_bash_shebang(self) -> None:
assert detect_type(content="#!/bin/bash\necho hi\n") == "bash"
def test_sh_shebang(self) -> None:
assert detect_type(content="#!/bin/sh\necho hi\n") == "bash"
def test_zsh_shebang(self) -> None:
assert detect_type(content="#!/bin/zsh\necho hi\n") == "bash"
def test_extension_wins_over_shebang(self) -> None:
assert (
detect_type(path="script.py", content="#!/bin/bash\necho hi\n") == "python"
)
def test_filename_wins_over_shebang(self) -> None:
assert detect_type(path=".bashrc", content="#!/usr/bin/env python3\n") == "bash"
def test_unrecognized_shebang_falls_through(self) -> None:
assert detect_type(content="#!/usr/bin/env ruby\nputs 'hi'\n") == "text"
class TestContentJsonDetection:
"""Priority 5: detect JSON from content when path gives no clue."""
def test_json_object(self, sample_json: str) -> None:
assert detect_type(content=sample_json) == "json"
def test_json_array(self) -> None:
assert detect_type(content="[1, 2, 3]") == "json"
def test_json_with_whitespace(self) -> None:
assert detect_type(content=' \n {"a": 1} \n ') == "json"
def test_invalid_json_braces(self) -> None:
assert detect_type(content="{invalid json}") == "text"
def test_non_json_content(self) -> None:
assert detect_type(content="hello world") == "text"
def test_empty_content(self) -> None:
assert detect_type(content="") == "text"
def test_content_with_valid_path_takes_priority(self) -> None:
assert detect_type(path="script.py", content='{"a": 1}') == "python"
def test_shebang_takes_priority_over_json(self) -> None:
assert detect_type(content='#!/usr/bin/env python3\n{"a": 1}\n') == "python"
class TestGuessContent:
"""Priority 6: optional heuristic content guessing."""
def test_guess_content_disabled_by_default(self) -> None:
diff = "--- a/file\n+++ b/file\n@@ -1 +1 @@\n-old\n+new\n"
assert detect_type(content=diff) == "text"
def test_guess_content_detects_diff(self) -> None:
diff = "--- a/file\n+++ b/file\n@@ -1 +1 @@\n-old\n+new\n"
assert detect_type(content=diff, guess_content=True) == "diff"
def test_guess_content_rejects_low_confidence_false_positive(self) -> None:
python_snippet = "def f():\n return 1\n"
assert detect_type(content=python_snippet, guess_content=True) == "text"
def test_json_still_wins_over_guess_content(self, sample_json: str) -> None:
assert detect_type(content=sample_json, guess_content=True) == "json"
class TestFallback:
"""Priority 7: fallback to 'text'."""
def test_no_args(self) -> None:
assert detect_type() == "text"
def test_none_everything(self) -> None:
assert detect_type(path=None, content=None, explicit_type=None) == "text"
class TestExtensionMapCompleteness:
"""Verify EXTENSION_MAP entries are valid."""
def test_all_extensions_are_strings(self) -> None:
for ext, lexer in EXTENSION_MAP.items():
assert isinstance(ext, str), f"Key {ext!r} is not str"
assert isinstance(lexer, str), f"Value for {ext!r} is not str"
def test_all_extensions_start_with_dot(self) -> None:
for ext in EXTENSION_MAP:
assert ext.startswith("."), f"Extension {ext!r} doesn't start with '.'"
def test_all_filenames_in_filename_map(self) -> None:
for name in _FILENAME_MAP:
assert isinstance(name, str)
def test_filename_map_matches_path_name_lookup(self, tmp_file) -> None:
for name, expected in _FILENAME_MAP.items():
path = tmp_file("content", name=name)
assert detect_type(path=str(path)) == expected
def test_path_stringified_from_pathlib(tmp_file) -> None:
path = tmp_file('print("hi")\n', suffix=".py")
assert detect_type(path=str(Path(path))) == "python"