field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

test_maintainability_ratchet.py (6308B)


      1 from __future__ import annotations
      2 
      3 import json
      4 import sys
      5 import tempfile
      6 import unittest
      7 from pathlib import Path
      8 
      9 SCRIPTS = Path(__file__).resolve().parent
     10 if str(SCRIPTS) not in sys.path:
     11     sys.path.insert(0, str(SCRIPTS))
     12 
     13 import maintainability_ratchet as ratchet  # noqa: E402
     14 
     15 
     16 class MaintainabilityRatchetTests(unittest.TestCase):
     17     def _repository(self) -> tuple[tempfile.TemporaryDirectory[str], Path]:
     18         temporary = tempfile.TemporaryDirectory()
     19         root = Path(temporary.name)
     20         (root / "Tera").mkdir()
     21         (root / "TeraTests").mkdir()
     22         (root / "TeraUITests").mkdir()
     23         (root / "scripts").mkdir()
     24         (root / "test-fixtures").mkdir()
     25         (root / "Tera/App.swift").write_text("struct App {}\n", encoding="utf-8")
     26         (root / "scripts/check.py").write_text(
     27             "def check():\n    return True\n", encoding="utf-8"
     28         )
     29         self._write_baseline(root)
     30         return temporary, root
     31 
     32     def _write_baseline(self, root: Path, **overrides: object) -> None:
     33         baseline: dict[str, object] = {
     34             "schema": "radroots.ios.maintainability-baseline.v1",
     35             "schema_version": 1,
     36             "source_revision": "c63002bcc4d3f6656e93aabe4fca6bd771376629",
     37             "thresholds": {
     38                 "swift_file_lines": 600,
     39                 "python_file_lines": 800,
     40                 "python_function_complexity": 10,
     41             },
     42             "swift_file_exception": [],
     43             "python_file_exception": [],
     44             "python_complexity_exception": [],
     45             "bounded_module": ["Tera/App.swift", "scripts/check.py"],
     46         }
     47         baseline.update(overrides)
     48         (root / ratchet.BASELINE_PATH).write_text(
     49             json.dumps(baseline, sort_keys=True), encoding="utf-8"
     50         )
     51 
     52     def test_current_repository_satisfies_ratchet(self) -> None:
     53         ratchet.verify(SCRIPTS.parent)
     54 
     55     def test_new_oversized_swift_file_is_rejected(self) -> None:
     56         temporary, root = self._repository()
     57         with temporary:
     58             (root / "Tera/Large.swift").write_text("x\n" * 601, encoding="utf-8")
     59             with self.assertRaisesRegex(ratchet.MaintainabilityError, "inventory"):
     60                 ratchet.verify(root)
     61 
     62     def test_exception_cannot_grow_or_become_stale(self) -> None:
     63         temporary, root = self._repository()
     64         with temporary:
     65             path = root / "Tera/App.swift"
     66             path.write_text("x\n" * 602, encoding="utf-8")
     67             exception = [{"path": "Tera/App.swift", "maximum_lines": 601}]
     68             self._write_baseline(root, swift_file_exception=exception)
     69             with self.assertRaisesRegex(ratchet.MaintainabilityError, "regressed"):
     70                 ratchet.verify(root)
     71             path.write_text("struct App {}\n", encoding="utf-8")
     72             with self.assertRaisesRegex(ratchet.MaintainabilityError, "inventory"):
     73                 ratchet.verify(root)
     74 
     75     def test_new_oversized_python_file_is_rejected(self) -> None:
     76         temporary, root = self._repository()
     77         with temporary:
     78             (root / "scripts/large.py").write_text("x = 1\n" * 801, encoding="utf-8")
     79             with self.assertRaisesRegex(ratchet.MaintainabilityError, "inventory"):
     80                 ratchet.verify(root)
     81 
     82     def test_new_complex_python_function_is_rejected(self) -> None:
     83         temporary, root = self._repository()
     84         with temporary:
     85             branches = "".join(
     86                 f"    if value == {index}:\n        return {index}\n"
     87                 for index in range(10)
     88             )
     89             (root / "scripts/complex.py").write_text(
     90                 "def complex(value):\n" + branches + "    return -1\n",
     91                 encoding="utf-8",
     92             )
     93             with self.assertRaisesRegex(ratchet.MaintainabilityError, "inventory"):
     94                 ratchet.verify(root)
     95 
     96     def test_baseline_identity_threshold_and_order_are_closed(self) -> None:
     97         temporary, root = self._repository()
     98         with temporary:
     99             self._write_baseline(root, source_revision="f" * 40)
    100             with self.assertRaisesRegex(ratchet.MaintainabilityError, "identity"):
    101                 ratchet.verify(root)
    102             self._write_baseline(
    103                 root,
    104                 thresholds={
    105                     "swift_file_lines": 601,
    106                     "python_file_lines": 800,
    107                     "python_function_complexity": 10,
    108                 },
    109             )
    110             with self.assertRaisesRegex(ratchet.MaintainabilityError, "threshold"):
    111                 ratchet.verify(root)
    112             self._write_baseline(
    113                 root,
    114                 bounded_module=["scripts/check.py", "Tera/App.swift"],
    115             )
    116             with self.assertRaisesRegex(ratchet.MaintainabilityError, "inventory"):
    117                 ratchet.verify(root)
    118 
    119     def test_bounded_module_must_exist_and_remain_small(self) -> None:
    120         temporary, root = self._repository()
    121         with temporary:
    122             self._write_baseline(root, bounded_module=["Tera/Missing.swift"])
    123             with self.assertRaisesRegex(ratchet.MaintainabilityError, "absent"):
    124                 ratchet.verify(root)
    125             (root / "Tera/App.swift").write_text("x\n" * 601, encoding="utf-8")
    126             self._write_baseline(
    127                 root,
    128                 swift_file_exception=[{"path": "Tera/App.swift", "maximum_lines": 601}],
    129             )
    130             with self.assertRaisesRegex(ratchet.MaintainabilityError, "bounded"):
    131                 ratchet.verify(root)
    132 
    133     def test_exception_rows_are_exact_unique_and_ordered(self) -> None:
    134         invalid = [
    135             [
    136                 {"path": "b", "maximum_lines": 601},
    137                 {"path": "a", "maximum_lines": 601},
    138             ],
    139             [
    140                 {"path": "a", "maximum_lines": 601},
    141                 {"path": "a", "maximum_lines": 602},
    142             ],
    143             [{"path": "a", "maximum_lines": 601, "extra": True}],
    144         ]
    145         for rows in invalid:
    146             with self.subTest(rows=rows):
    147                 with self.assertRaises(ratchet.MaintainabilityError):
    148                     ratchet._closed_exception_map(
    149                         rows, key_name="path", ceiling_name="maximum_lines"
    150                     )
    151 
    152 
    153 if __name__ == "__main__":
    154     unittest.main()