blob: fbe46338b5b6c022662fba26e639cc4560f75efa [file]
#!/usr/bin/env fuchsia-vendored-python
# Copyright 2026 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import sys
import unittest
from pathlib import Path
# Add parent directory to path so we can import commit_msg_checker
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
import commit_msg_checker
class TestCommitMessageChecker(unittest.TestCase):
def test_valid_message(self) -> None:
msg = (
"Add commit message checker\n\n"
"This change implements a commit message checker.\n\n"
"Bug: 12345\n"
"Test: Added unit tests.\n"
)
findings = commit_msg_checker.check_commit_message(msg)
self.assertEqual(findings, [])
def test_subject_line_at_limit_for_warning(self) -> None:
subject = "A" * 65
msg = f"{subject}\n\nBug: 1\nTest: 1"
findings = commit_msg_checker.check_commit_message(msg)
self.assertEqual(findings, [])
def test_subject_line_just_above_limit_for_warning(self) -> None:
long_subject = "A" * 66
msg = f"{long_subject}\n\nBug: 1\nTest: 1"
findings = commit_msg_checker.check_commit_message(msg)
self.assertTrue(
any(
"Subject line exceeds 65 characters" in f["message"]
for f in findings
)
)
def test_subject_line_too_long_exempt_revert(self) -> None:
long_subject = 'Revert "Subject that is extremely long and exceeds the typical 65 character limit normally"'
msg = f"{long_subject}\n\nBug: 1\nTest: 1"
findings = commit_msg_checker.check_commit_message(msg)
self.assertEqual(findings, [])
def test_subject_line_too_long_exempt_reland(self) -> None:
long_subject = 'Reland "Subject that is extremely long and exceeds the typical 65 character limit normally"'
msg = f"{long_subject}\n\nBug: 1\nTest: 1"
findings = commit_msg_checker.check_commit_message(msg)
self.assertEqual(findings, [])
def test_subject_line_too_long_exempt_autosquash(self) -> None:
for prefix in ("fixup!", "squash!", "amend!", "fixup! fixup!"):
with self.subTest(prefix=prefix):
long_subject = f"{prefix} Subject that is extremely long and exceeds the typical 65 character limit"
msg = f"{long_subject}\n\nBug: 1\nTest: 1"
findings = commit_msg_checker.check_commit_message(msg)
self.assertEqual(findings, [])
def test_body_line_at_limit(self) -> None:
body = "A" * 72
msg = f"Subject line\n\n{body}\n\nBug: 1\nTest: 1"
findings = commit_msg_checker.check_commit_message(msg)
self.assertEqual(findings, [])
def test_body_line_just_above_limit(self) -> None:
long_body = "A" * 73
msg = f"Subject line\n\n{long_body}\n\nBug: 1\nTest: 1"
findings = commit_msg_checker.check_commit_message(msg)
self.assertTrue(
any(
"Line 3 exceeds 72 characters (73 chars). Wrap body text at 72 characters."
in f["message"]
for f in findings
)
)
def test_body_line_too_long_ignores_urls(self) -> None:
long_url = "https://fuchsia.dev/" + "a" * 80
msg = f"Subject line\n\nSee {long_url} for details.\n\nBug: 1\nTest: 1"
findings = commit_msg_checker.check_commit_message(msg)
self.assertEqual(findings, [])
def test_long_footer_lines_exempt(self) -> None:
# Footers whose values are labels or commands are indexed by Gerrit
# as single lines and must not be wrapped, so they are exempt.
label = "//src/connectivity/wlan/drivers/lib/fidl-bridge/cpp/test/fidl"
msg = (
"Subject line\n\n"
"Body line.\n\n"
"Bug: 1\n"
f"Test: fx bazel build --config=fuchsia_platform @{label}\n"
f"Bazel-Migration-Target: {label}\n"
)
findings = commit_msg_checker.check_commit_message(msg)
self.assertEqual(findings, [])
def test_long_no_ifttt_lines_exempt(self) -> None:
# Gerrit's IfThisThenAnalyzer requires `NO_IFTTT=<reason>` (with '=')
# and matches it anywhere in the commit message. Long justifications or
# file paths should not trigger body line-length warnings.
long_reason = (
"NO_IFTTT=Visibility-only change in "
"//build/config/rust/lints/BUILD.bazel that does not affect paired files."
)
self.assertGreater(len(long_reason), 72)
for msg in (
# In a body paragraph
f"Subject line\n\n{long_reason}\n\nBug: 1\nTest: 1\n",
# In the footer block
f"Subject line\n\nBody line.\n\nBug: 1\nTest: 1\n{long_reason}\n",
):
with self.subTest(msg=msg):
findings = commit_msg_checker.check_commit_message(msg)
self.assertEqual(findings, [])
def test_long_no_ifttt_invalid_variants_not_exempt(self) -> None:
# Only exact `NO_IFTTT=` at the start of a line is recognized by
# Gerrit's IfThisThenAnalyzer; colon, lowercase, spaced '=', or
# mid-line occurrences are not exempt from line wrapping.
suffix = "a" * 80
for line in (
f"NO_IFTTT: {suffix}",
f"no_ifttt={suffix}",
f"NO_IFTTT = {suffix}",
f"See NO_IFTTT={suffix}",
):
with self.subTest(line=line):
msg = f"Subject line\n\n{line}\n\nBug: 1\nTest: 1\n"
findings = commit_msg_checker.check_commit_message(msg)
self.assertEqual(len(findings), 1)
self.assertIn(
"Line 3 exceeds 72 characters", findings[0]["message"]
)
def test_quoted_revert_long_line_exempt(self) -> None:
msg = (
'Revert "Subject"\n\n' "> " + "a" * 80 + "\n\n" "Bug: 1\n" "Test: 1"
)
findings = commit_msg_checker.check_commit_message(msg)
self.assertEqual(findings, [])
def test_comment_long_line_exempt(self) -> None:
msg = (
"Subject line\n\n"
"Body line.\n\n"
"# " + "a" * 80 + "\n\n"
"Bug: 1\n"
"Test: 1\n"
)
findings = commit_msg_checker.check_commit_message(msg)
self.assertEqual(findings, [])
def test_revert_message_quoted_change_id_allowed(self) -> None:
msg = (
'Revert "Some commit"\n\n'
"This reverts commit 12345.\n\n"
"> Change-Id: Ioldchangeid\n"
"> Bug: 123\n\n"
"Bug: 123\n"
"Test: Reverting patch\n"
"Change-Id: I1234567890123456789012345678901234567890\n"
)
findings = commit_msg_checker.check_commit_message(msg)
self.assertEqual(findings, [])
def test_multiple_change_id_errors(self) -> None:
msg = (
"Subject line\n\n"
"Bug: 123\n"
"Test: Unit test\n"
"Change-Id: I1111111111111111111111111111111111111111\n"
"Change-Id: I2222222222222222222222222222222222222222\n"
)
findings = commit_msg_checker.check_commit_message(msg)
self.assertTrue(
any(
f["level"] == "error"
and "Multiple 'Change-Id:' footers" in f["message"]
for f in findings
)
)
def test_agent_metadata_single_allowed(self) -> None:
msg = (
"Subject line\n\n"
"Bug: 123\n"
"Test: Unit test\n"
"TAG: agy\n"
"CONV: 6344ad16-d9ce-483d-9b98-affe2f24feac\n"
)
findings = commit_msg_checker.check_commit_message(msg)
self.assertEqual(findings, [])
def test_agent_metadata_different_tags_allowed(self) -> None:
msg = (
"Subject line\n\n"
"Bug: 123\n"
"Test: Unit test\n"
"TAG: agy\n"
"TAG: refactor\n"
"CONV: 6344ad16-first\n"
"CONV: 6344ad16-second\n"
)
findings = commit_msg_checker.check_commit_message(msg)
self.assertEqual(findings, [])
def test_agent_metadata_duplicate_warns(self) -> None:
msg = (
"Subject line\n\n"
"Bug: 123\n"
"Test: Unit test\n"
"TAG=agy\n"
"TAG: agy\n"
"CONV=6344ad16-same\n"
"CONV: 6344ad16-same\n"
)
findings = commit_msg_checker.check_commit_message(msg)
self.assertTrue(
any(
"Duplicate 'TAG' metadata line" in f["message"]
for f in findings
)
)
self.assertTrue(
any(
"Duplicate 'CONV' metadata line" in f["message"]
for f in findings
)
)
if __name__ == "__main__":
unittest.main()