File indexing completed on 2026-08-16 08:16:23
0001
0002 """Self-tests for check_deprecated_docs.py.
0003
0004 Guards the doc-block association heuristic (the only non-trivial part): walking
0005 up from a ``[[deprecated]]`` past declaration-prefix lines to the Doxygen block,
0006 and stopping at statement boundaries so an unrelated comment above is not
0007 mis-attributed.
0008
0009 Run directly (``python3 CI/public_api/test_check_deprecated_docs.py``); non-zero exit on
0010 failure. Also collectable by pytest.
0011 """
0012
0013 from __future__ import annotations
0014
0015 import sys
0016 import tempfile
0017 from pathlib import Path
0018
0019 HERE = Path(__file__).resolve().parent
0020 sys.path.insert(0, str(HERE))
0021
0022 import check_deprecated_docs as cdd
0023
0024
0025 def _reasons(text: str) -> list[str]:
0026 """Run the checker over a single synthetic header, return violation reasons."""
0027 with tempfile.TemporaryDirectory() as d:
0028 repo = Path(d)
0029 hdr = repo / "Core" / "include" / "Acts" / "T.hpp"
0030 hdr.parent.mkdir(parents=True)
0031 hdr.write_text(text)
0032 vios = cdd.find_violations(repo, ["Core/include"])
0033 return [v["reason"] for v in vios]
0034
0035
0036
0037
0038
0039 def test_paired_simple_block():
0040 text = (
0041 "/// Does a thing.\n"
0042 "/// @deprecated Use bar() instead\n"
0043 '[[deprecated("Use bar() instead")]] void foo();\n'
0044 )
0045 assert _reasons(text) == [], "an adjacent @deprecated block should pair"
0046
0047
0048 def test_template_line_between_doc_and_attribute():
0049 text = (
0050 "/// Does a thing.\n"
0051 "/// @deprecated gone soon\n"
0052 "template <typename T>\n"
0053 '[[deprecated("gone soon")]] void foo();\n'
0054 )
0055 assert _reasons(text) == [], "template head between block and attr must be skipped"
0056
0057
0058 def test_backslash_command_form_accepted():
0059 text = "/// \\deprecated old\n[[deprecated]] void foo();\n"
0060 assert _reasons(text) == []
0061
0062
0063 def test_doc_block_without_command_is_violation():
0064 text = "/// Does a thing.\n[[deprecated]] void foo();\n"
0065 assert _reasons(text) == ["no-deprecated-tag"]
0066
0067
0068 def test_no_doc_block_is_violation():
0069 text = "int x = 0;\n\n[[deprecated]] void foo();\n"
0070 assert _reasons(text) == ["no-doc-block"]
0071
0072
0073 def test_previous_statement_not_mistaken_for_doc():
0074
0075 text = (
0076 "/// @deprecated old one\n"
0077 '[[deprecated("old one")]] void a();\n'
0078 "\n"
0079 "[[deprecated]] void b();\n"
0080 )
0081 assert _reasons(text) == ["no-doc-block"], "b() is undocumented"
0082
0083
0084 def test_gnu_deprecated_attribute_recognised():
0085 text = "/// no tag here\n[[gnu::deprecated]] void foo();\n"
0086 assert _reasons(text) == ["no-deprecated-tag"]
0087
0088
0089 def test_copydoc_block_with_explicit_command_pairs():
0090 text = (
0091 "/// @copydoc Base::foo() const\n"
0092 "/// @deprecated Use the 2D overload instead\n"
0093 '[[deprecated("Use the 2D overload instead")]] void foo() const;\n'
0094 )
0095 assert _reasons(text) == []
0096
0097
0098 def _main() -> int:
0099 failures = 0
0100 for name, fn in sorted(globals().items()):
0101 if name.startswith("test_") and callable(fn):
0102 try:
0103 fn()
0104 print(f"ok {name}")
0105 except AssertionError as e:
0106 failures += 1
0107 print(f"FAIL {name}: {e}")
0108 print(f"\n{failures} failure(s)")
0109 return 1 if failures else 0
0110
0111
0112 if __name__ == "__main__":
0113 sys.exit(_main())