272 lines
14 KiB
Python
272 lines
14 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
from scripts import check_prohibited_names
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
CHECKER = ROOT / "scripts" / "check_prohibited_names.py"
|
|
FIXTURES = ROOT / "scripts" / "tests" / "fixtures" / "prohibited_names"
|
|
|
|
|
|
def run_git(repository: Path, *args: str) -> None:
|
|
subprocess.run(["git", *args], cwd=repository, check=True, stdout=subprocess.DEVNULL)
|
|
|
|
|
|
class ProhibitedNamesTests(unittest.TestCase):
|
|
def test_names_do_not_match_inside_unrelated_words(self) -> None:
|
|
self.assertFalse(check_prohibited_names.prohibited("spotty network; tty72; netcatty_adapter"))
|
|
self.assertTrue(check_prohibited_names.prohibited("Use Otty or TTY-7"))
|
|
|
|
def test_ordinary_competitor_comparison_is_rejected(self) -> None:
|
|
text = (FIXTURES / "ordinary-comparison.md").read_text(encoding="utf-8")
|
|
self.assertTrue(check_prohibited_names.prohibited_source_line("comparison.md", text))
|
|
|
|
def test_inline_legal_attribution_is_allowed_but_comparison_is_not(self) -> None:
|
|
text = (FIXTURES / "legal-notice.txt").read_text(encoding="utf-8")
|
|
self.assertFalse(check_prohibited_names.prohibited_source_line("source.rs", text))
|
|
self.assertTrue(
|
|
check_prohibited_names.prohibited_source_line("README.md", "Compare with Alacritty.")
|
|
)
|
|
self.assertTrue(
|
|
check_prohibited_names.prohibited_source_line(
|
|
"source.rs", "Copyright (c) Alacritty contributors; compare with Ghostty."
|
|
)
|
|
)
|
|
|
|
def test_protocol_identifiers_are_allowed(self) -> None:
|
|
text = (FIXTURES / "protocol.rs").read_text(encoding="utf-8")
|
|
self.assertFalse(check_prohibited_names.prohibited_source_line("src/protocol.rs", text))
|
|
|
|
def test_github_cli_text_is_not_a_competitor_name(self) -> None:
|
|
command = (FIXTURES / "github-cli.sh").read_text(encoding="utf-8")
|
|
documentation = (FIXTURES / "github-cli-docs.sh").read_text(encoding="utf-8")
|
|
self.assertFalse(check_prohibited_names.prohibited_source_line("check.sh", command))
|
|
self.assertFalse(check_prohibited_names.prohibited_source_line("check.sh", documentation))
|
|
|
|
def test_real_cargo_git_dependency_is_allowed_but_readme_reference_is_not(self) -> None:
|
|
dependency = 'gpui = { git = "https://github.com/ghostty/ghostty", rev = "deadbeef" }'
|
|
self.assertFalse(check_prohibited_names.prohibited_source_line("Cargo.toml", dependency))
|
|
self.assertTrue(check_prohibited_names.prohibited_source_line("README.md", dependency))
|
|
|
|
def test_staged_hook_configuration_is_not_exempt(self) -> None:
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
repository = Path(directory)
|
|
run_git(repository, "init", "-q")
|
|
(repository / ".pre-commit-config.yaml").write_text(
|
|
"# Compare with Ghostty.\nrepos: []\n", encoding="utf-8"
|
|
)
|
|
run_git(repository, "add", ".pre-commit-config.yaml")
|
|
result = subprocess.run(
|
|
[sys.executable, str(CHECKER), "staged"], cwd=repository,
|
|
text=True, capture_output=True,
|
|
)
|
|
self.assertEqual(result.returncode, 1, result.stderr)
|
|
self.assertIn(".pre-commit-config.yaml", result.stderr)
|
|
|
|
def test_theme_format_message_does_not_exempt_an_appended_comparison(self) -> None:
|
|
message = (
|
|
"unsupported theme format; use Pebrel JSON, Windows Terminal JSON, "
|
|
"Kitty, Ghostty, WezTerm or Alacritty"
|
|
)
|
|
self.assertFalse(check_prohibited_names.prohibited_source_line("src/theme.rs", message))
|
|
self.assertTrue(check_prohibited_names.prohibited_source_line(
|
|
"src/theme.rs", message + "; copied from Ghostty"
|
|
))
|
|
|
|
def test_range_scans_added_text_and_new_commit_messages(self) -> None:
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
repository = Path(directory)
|
|
run_git(repository, "init", "-q")
|
|
run_git(repository, "config", "user.name", "CI test")
|
|
run_git(repository, "config", "user.email", "ci@example.invalid")
|
|
(repository / "README.md").write_text("safe\n", encoding="utf-8")
|
|
run_git(repository, "add", "README.md")
|
|
run_git(repository, "commit", "-qm", "baseline")
|
|
base = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=repository, text=True).strip()
|
|
|
|
(repository / "comparison.md").write_text("Compare with Ghostty.\n", encoding="utf-8")
|
|
run_git(repository, "add", "comparison.md")
|
|
run_git(repository, "commit", "-qm", "ordinary comparison")
|
|
head = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=repository, text=True).strip()
|
|
result = subprocess.run(
|
|
[sys.executable, str(CHECKER), "range", "--base", base, "--head", head],
|
|
cwd=repository,
|
|
text=True,
|
|
capture_output=True,
|
|
env={**os.environ, "PYTHONUTF8": "1"},
|
|
)
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertIn("Ghostty", result.stderr)
|
|
|
|
def test_range_scans_a_commit_message_even_when_text_is_legal(self) -> None:
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
repository = Path(directory)
|
|
run_git(repository, "init", "-q")
|
|
run_git(repository, "config", "user.name", "CI test")
|
|
run_git(repository, "config", "user.email", "ci@example.invalid")
|
|
(repository / "README.md").write_text("safe\n", encoding="utf-8")
|
|
run_git(repository, "add", "README.md")
|
|
run_git(repository, "commit", "-qm", "baseline")
|
|
base = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=repository, text=True).strip()
|
|
|
|
(repository / "README.md").write_text("still safe\n", encoding="utf-8")
|
|
run_git(repository, "add", "README.md")
|
|
run_git(repository, "commit", "-qm", "Compare with Ghostty")
|
|
head = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=repository, text=True).strip()
|
|
result = subprocess.run(
|
|
[sys.executable, str(CHECKER), "range", "--base", base, "--head", head],
|
|
cwd=repository,
|
|
text=True,
|
|
capture_output=True,
|
|
)
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertIn("commit-message", result.stderr)
|
|
|
|
def test_range_uses_merge_base_and_keeps_added_then_deleted_text(self) -> None:
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
repository = Path(directory)
|
|
run_git(repository, "init", "-q")
|
|
run_git(repository, "config", "user.name", "CI test")
|
|
run_git(repository, "config", "user.email", "ci@example.invalid")
|
|
(repository / "README.md").write_text("Historical Ghostty attribution\n", encoding="utf-8")
|
|
run_git(repository, "add", "README.md")
|
|
run_git(repository, "commit", "-qm", "baseline")
|
|
base = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=repository, text=True).strip()
|
|
|
|
run_git(repository, "branch", "main")
|
|
run_git(repository, "switch", "main")
|
|
(repository / "README.md").write_text("safe on main\n", encoding="utf-8")
|
|
run_git(repository, "add", "README.md")
|
|
run_git(repository, "commit", "-qm", "advance main")
|
|
|
|
run_git(repository, "switch", "-c", "feature", base)
|
|
(repository / "README.md").write_text("safe on feature\n", encoding="utf-8")
|
|
run_git(repository, "add", "README.md")
|
|
run_git(repository, "commit", "-qm", "advance feature")
|
|
clean_head = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=repository, text=True).strip()
|
|
clean = subprocess.run(
|
|
[sys.executable, str(CHECKER), "range", "--base", "main", "--head", clean_head],
|
|
cwd=repository,
|
|
text=True,
|
|
capture_output=True,
|
|
)
|
|
self.assertEqual(clean.returncode, 0, clean.stderr)
|
|
|
|
(repository / "temporary.md").write_text("Ghostty\n", encoding="utf-8")
|
|
run_git(repository, "add", "temporary.md")
|
|
run_git(repository, "commit", "-qm", "temporary comparison")
|
|
(repository / "temporary.md").unlink()
|
|
run_git(repository, "add", "-u")
|
|
run_git(repository, "commit", "-qm", "remove temporary comparison")
|
|
head = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=repository, text=True).strip()
|
|
result = subprocess.run(
|
|
[sys.executable, str(CHECKER), "range", "--base", base, "--head", head],
|
|
cwd=repository,
|
|
text=True,
|
|
capture_output=True,
|
|
)
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertIn("temporary.md", result.stderr)
|
|
|
|
unrelated_tree = subprocess.check_output(["git", "write-tree"], cwd=repository, text=True).strip()
|
|
unrelated = subprocess.check_output(
|
|
["git", "commit-tree", unrelated_tree, "-m", "unrelated"],
|
|
cwd=repository,
|
|
text=True,
|
|
).strip()
|
|
unrelated_result = subprocess.run(
|
|
[sys.executable, str(CHECKER), "range", "--base", unrelated, "--head", clean_head],
|
|
cwd=repository,
|
|
text=True,
|
|
capture_output=True,
|
|
)
|
|
self.assertNotEqual(unrelated_result.returncode, 0)
|
|
self.assertIn("cannot scan prohibited-name range", unrelated_result.stderr)
|
|
|
|
def test_range_scans_merge_resolution_without_replaying_second_parent(self) -> None:
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
repository = Path(directory)
|
|
run_git(repository, "init", "-q")
|
|
run_git(repository, "config", "user.name", "CI test")
|
|
run_git(repository, "config", "user.email", "ci@example.invalid")
|
|
(repository / "shared.md").write_text("base\n", encoding="utf-8")
|
|
run_git(repository, "add", "shared.md")
|
|
run_git(repository, "commit", "-qm", "baseline")
|
|
run_git(repository, "branch", "main")
|
|
|
|
run_git(repository, "switch", "-c", "feature")
|
|
(repository / "shared.md").write_text("feature\n", encoding="utf-8")
|
|
run_git(repository, "add", "shared.md")
|
|
run_git(repository, "commit", "-qm", "feature change")
|
|
feature_parent = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=repository, text=True).strip()
|
|
run_git(repository, "switch", "main")
|
|
(repository / "shared.md").write_text("main Ghostty\n", encoding="utf-8")
|
|
run_git(repository, "add", "shared.md")
|
|
run_git(repository, "commit", "-qm", "main change")
|
|
main_head = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=repository, text=True).strip()
|
|
run_git(repository, "switch", "feature")
|
|
merge = subprocess.run(
|
|
["git", "merge", "--no-ff", "main", "-m", "merge main"],
|
|
cwd=repository,
|
|
text=True,
|
|
capture_output=True,
|
|
)
|
|
self.assertNotEqual(merge.returncode, 0, merge.stdout + merge.stderr)
|
|
(repository / "shared.md").write_text("resolved safe\n", encoding="utf-8")
|
|
run_git(repository, "add", "shared.md")
|
|
run_git(repository, "commit", "-qm", "resolve safe merge")
|
|
safe_head = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=repository, text=True).strip()
|
|
|
|
safe_result = subprocess.run(
|
|
[sys.executable, str(CHECKER), "range", "--base", main_head, "--head", safe_head],
|
|
cwd=repository,
|
|
text=True,
|
|
capture_output=True,
|
|
)
|
|
self.assertEqual(safe_result.returncode, 0, safe_result.stderr)
|
|
|
|
run_git(repository, "switch", "-c", "feature-bad", feature_parent)
|
|
merge_bad = subprocess.run(
|
|
["git", "merge", "--no-ff", "main", "-m", "merge main with bad resolution"],
|
|
cwd=repository,
|
|
text=True,
|
|
capture_output=True,
|
|
)
|
|
self.assertNotEqual(merge_bad.returncode, 0, merge_bad.stdout + merge_bad.stderr)
|
|
(repository / "shared.md").write_text("resolved Ghostty\n", encoding="utf-8")
|
|
run_git(repository, "add", "shared.md")
|
|
run_git(repository, "commit", "-qm", "resolve bad merge")
|
|
bad_head = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=repository, text=True).strip()
|
|
bad_result = subprocess.run(
|
|
[sys.executable, str(CHECKER), "range", "--base", main_head, "--head", bad_head],
|
|
cwd=repository,
|
|
text=True,
|
|
capture_output=True,
|
|
)
|
|
self.assertNotEqual(bad_result.returncode, 0)
|
|
self.assertIn("shared.md", bad_result.stderr)
|
|
|
|
def test_range_fails_closed_for_an_unknown_base(self) -> None:
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
repository = Path(directory)
|
|
run_git(repository, "init", "-q")
|
|
result = subprocess.run(
|
|
[sys.executable, str(CHECKER), "range", "--base", "missing", "--head", "HEAD"],
|
|
cwd=repository,
|
|
text=True,
|
|
capture_output=True,
|
|
)
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertIn("cannot scan prohibited-name range", result.stderr)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|