mirror of
https://github.com/unclebob/dryer.git
synced 2026-11-06 18:33:49 +00:00
Add a multi-language duplicate finder that writes .metrics/dry.edn.
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
from pathlib import Path
|
||||
|
||||
from dryer.cli import main, parse_args, run
|
||||
from dryer.discover import language_of
|
||||
|
||||
|
||||
def write_source(root: Path, name: str, text: str) -> None:
|
||||
path = root / name
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(text, encoding="utf-8")
|
||||
|
||||
|
||||
def test_language_detection():
|
||||
assert language_of("src/app/core.clj") == "clojure"
|
||||
assert language_of("src/app/core.cljd") == "clojure"
|
||||
assert language_of("Widget.java") == "java"
|
||||
assert language_of("board.go") == "go"
|
||||
assert language_of("ui/view.tsx") == "typescript"
|
||||
assert language_of("src/lib.rs") == "rust"
|
||||
assert language_of("src/dryer/cli.py") == "python"
|
||||
assert language_of("types.d.ts") is None
|
||||
assert language_of("notes.md") is None
|
||||
|
||||
|
||||
def test_help_does_not_scan(capsys):
|
||||
assert run(["--help"]) == 0
|
||||
out = capsys.readouterr().out
|
||||
assert "Usage: dryer" in out
|
||||
assert "--threshold" in out
|
||||
|
||||
|
||||
def test_unknown_format(capsys):
|
||||
assert run(["--format", "csv"]) == 2
|
||||
err = capsys.readouterr().err
|
||||
assert err == "Unknown format: csv\n"
|
||||
|
||||
|
||||
def test_unknown_option(capsys):
|
||||
assert run(["--nope"]) == 1
|
||||
assert "Unknown option: --nope" in capsys.readouterr().err
|
||||
|
||||
|
||||
def test_parse_defaults_and_flags():
|
||||
options = parse_args(
|
||||
["--threshold", "0.9", "--min-lines", "5", "--min-nodes", "30", "--edn", "spec"]
|
||||
)
|
||||
assert options.threshold == 0.9
|
||||
assert options.min_lines == 5
|
||||
assert options.min_nodes == 30
|
||||
assert options.format == "edn"
|
||||
assert options.positionals == ["spec"]
|
||||
|
||||
|
||||
def test_project_report_and_snapshot(tmp_path, capsys):
|
||||
write_source(
|
||||
tmp_path,
|
||||
"src/left.py",
|
||||
"def alpha(xs):\n ys = filter(xs, odd)\n zs = map(ys, inc)\n return list(zs)\n",
|
||||
)
|
||||
write_source(
|
||||
tmp_path,
|
||||
"src/right.py",
|
||||
"def beta(items):\n kept = filter(items, even)\n out = map(kept, dec)\n return list(out)\n",
|
||||
)
|
||||
write_source(
|
||||
tmp_path,
|
||||
"tests/test_left.py",
|
||||
"def alpha(xs):\n ys = filter(xs, odd)\n zs = map(ys, inc)\n return list(zs)\n",
|
||||
)
|
||||
code = run(["--root", str(tmp_path), "--min-lines", "3", "--min-nodes", "1"])
|
||||
captured = capsys.readouterr()
|
||||
assert code == 0
|
||||
assert "DUPLICATE score=1.00" in captured.out
|
||||
assert "src/left.py:1-" in captured.out
|
||||
assert "src/right.py:1-" in captured.out
|
||||
assert "test_left.py" not in captured.out
|
||||
text = (tmp_path / ".metrics" / "dry.edn").read_text(encoding="utf-8")
|
||||
assert ':language "python"' in text
|
||||
assert ':file "src/left.py"' in text
|
||||
assert "Wrote" in captured.err
|
||||
|
||||
|
||||
def test_edn_stdout(tmp_path, capsys):
|
||||
write_source(tmp_path, "one.py", "def alpha(xs):\n return xs\n")
|
||||
assert run(["--root", str(tmp_path), "--edn", "--min-lines", "1", "--min-nodes", "1"]) == 0
|
||||
out = capsys.readouterr().out
|
||||
assert out == "{:candidates []}\n"
|
||||
|
||||
|
||||
def test_empty_project(tmp_path, capsys):
|
||||
assert run(["--root", str(tmp_path)]) == 0
|
||||
assert capsys.readouterr().out == "No source files to analyze.\n"
|
||||
|
||||
|
||||
def test_path_filter(tmp_path, capsys):
|
||||
body_a = "def alpha(xs):\n ys = filter(xs, odd)\n zs = map(ys, inc)\n return list(zs)\n"
|
||||
body_b = "def beta(items):\n kept = filter(items, even)\n out = map(kept, dec)\n return list(out)\n"
|
||||
write_source(tmp_path, "src/board/a.py", body_a)
|
||||
write_source(tmp_path, "src/board/b.py", body_b)
|
||||
write_source(tmp_path, "src/other/a.py", body_a)
|
||||
write_source(tmp_path, "src/other/b.py", body_b)
|
||||
assert run(["--root", str(tmp_path), "--min-lines", "3", "--min-nodes", "1", "board"]) == 0
|
||||
out = capsys.readouterr().out
|
||||
assert "src/board/a.py" in out
|
||||
assert "src/other/a.py" not in out
|
||||
|
||||
|
||||
def test_explicit_test_file_is_included(tmp_path, capsys):
|
||||
body = "def alpha(xs):\n ys = filter(xs, odd)\n zs = map(ys, inc)\n return list(zs)\n"
|
||||
other = "def beta(items):\n kept = filter(items, even)\n out = map(kept, dec)\n return list(out)\n"
|
||||
write_source(tmp_path, "src/a.py", body)
|
||||
write_source(tmp_path, "tests/test_a.py", other)
|
||||
assert (
|
||||
run(
|
||||
[
|
||||
"--root",
|
||||
str(tmp_path),
|
||||
"--min-lines",
|
||||
"3",
|
||||
"--min-nodes",
|
||||
"1",
|
||||
"src/a.py",
|
||||
"tests/test_a.py",
|
||||
]
|
||||
)
|
||||
== 0
|
||||
)
|
||||
out = capsys.readouterr().out
|
||||
assert "src/a.py" in out
|
||||
assert "tests/test_a.py" in out
|
||||
|
||||
|
||||
def test_main_exits(monkeypatch):
|
||||
codes = []
|
||||
monkeypatch.setattr("dryer.cli.sys.exit", lambda code: codes.append(code))
|
||||
main(["--help"])
|
||||
assert codes == [0]
|
||||
Reference in New Issue
Block a user