mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-06 01:53:56 -06:00
feat: hold wiki commands and steps to the code
This commit is contained in:
1 parent
8936861a0c
commit
7f6af11bbd
1 file changed
+184
@@ -860,5 +860,189 @@ class RetroArchWindowsSystemFolder(unittest.TestCase):
|
|||||||
self.assertEqual(folders["macOS"], "`~/Documents/RetroArch/system/`")
|
self.assertEqual(folders["macOS"], "`~/Documents/RetroArch/system/`")
|
||||||
|
|
||||||
|
|
||||||
|
# A command a page gives: `python scripts/X.py ...` or `python -m scripts.x.y ...`,
|
||||||
|
# up to a comment, a pipe or the end of the line. A placeholder module
|
||||||
|
# (`myplatform_scraper`) is what a guide asks the reader to write.
|
||||||
|
_COMMAND = re.compile(
|
||||||
|
r"python3? (?:(scripts/[\w/]+\.py)|-m (scripts(?:\.\w+)+)(?![.*\w]))((?:[ \t]+[^\s#|&;`]+)*)"
|
||||||
|
)
|
||||||
|
_OPTION = re.compile(r"(?<![\w-])--[a-z0-9][a-z0-9-]*")
|
||||||
|
_PLACEHOLDER = ("myplatform",)
|
||||||
|
|
||||||
|
|
||||||
|
def _commands(text: str):
|
||||||
|
for number, line in enumerate(text.splitlines(), 1):
|
||||||
|
for match in _COMMAND.finditer(line):
|
||||||
|
target = match.group(1) or match.group(2)
|
||||||
|
if any(word in target for word in _PLACEHOLDER):
|
||||||
|
continue
|
||||||
|
yield number, target, bool(match.group(2)), match.group(3).split()
|
||||||
|
|
||||||
|
|
||||||
|
class CitedScriptsExist(unittest.TestCase):
|
||||||
|
"""The wiki names scripts by path. A script renamed or removed leaves a
|
||||||
|
command that fails, and the page says nothing until a reader runs it."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def missing(text: str) -> list[str]:
|
||||||
|
found = []
|
||||||
|
for number, target, module, _args in _commands(text):
|
||||||
|
path = ROOT / (target.replace(".", "/") + ".py" if module else target)
|
||||||
|
if not path.is_file():
|
||||||
|
found.append(f"{number}: {target}")
|
||||||
|
found += [
|
||||||
|
f"{number}: {name}"
|
||||||
|
for number, line in enumerate(text.splitlines(), 1)
|
||||||
|
for name in re.findall(r"(?<![\w./-])scripts/[\w/]+\.py", line)
|
||||||
|
if not (ROOT / name).is_file()
|
||||||
|
]
|
||||||
|
return found
|
||||||
|
|
||||||
|
def test_every_cited_script_exists(self):
|
||||||
|
for page in sorted(WIKI.glob("*.md")):
|
||||||
|
with self.subTest(page=page.name):
|
||||||
|
self.assertEqual(self.missing(page.read_text(encoding="utf-8")), [])
|
||||||
|
|
||||||
|
def test_a_removed_script_is_named(self):
|
||||||
|
self.assertEqual(self.missing("python scripts/no_such_tool.py --all\n"),
|
||||||
|
["1: scripts/no_such_tool.py", "1: scripts/no_such_tool.py"])
|
||||||
|
|
||||||
|
|
||||||
|
class CitedOptionsExist(unittest.TestCase):
|
||||||
|
"""Every option a command on a wiki page passes is one the script's own
|
||||||
|
parser lists, read from its --help (a subcommand's when the command names
|
||||||
|
one). A renamed flag otherwise survives on the page as a command that
|
||||||
|
exits 2."""
|
||||||
|
|
||||||
|
_helps: dict[tuple[str, str], str] = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def help_text(cls, target: str, module: bool, sub: str = "") -> str:
|
||||||
|
key = (target, sub)
|
||||||
|
if key not in cls._helps:
|
||||||
|
import subprocess # noqa: PLC0415
|
||||||
|
|
||||||
|
argv = [sys.executable, *(["-m", target] if module else [target])]
|
||||||
|
proc = subprocess.run(
|
||||||
|
[*argv, *([sub] if sub else []), "--help"],
|
||||||
|
cwd=ROOT, capture_output=True, text=True, timeout=120,
|
||||||
|
)
|
||||||
|
cls._helps[key] = proc.stdout if proc.returncode == 0 else ""
|
||||||
|
return cls._helps[key]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def subcommands(cls, target: str, module: bool) -> list[str]:
|
||||||
|
usage = cls.help_text(target, module).split("\n\n", 1)[0]
|
||||||
|
choice = re.search(r"\{([\w,-]+)\}", usage)
|
||||||
|
return choice.group(1).split(",") if choice else []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def unknown(cls, text: str) -> list[str]:
|
||||||
|
found = []
|
||||||
|
for number, target, module, args in _commands(text):
|
||||||
|
subs = cls.subcommands(target, module)
|
||||||
|
sub = args[0] if args and args[0] in subs else ""
|
||||||
|
known = set(_OPTION.findall(cls.help_text(target, module, sub)))
|
||||||
|
if not sub:
|
||||||
|
for other in subs:
|
||||||
|
if not other.startswith("_"):
|
||||||
|
known |= set(_OPTION.findall(cls.help_text(target, module, other)))
|
||||||
|
if not known:
|
||||||
|
found.append(f"{number}: {target} prints no --help")
|
||||||
|
continue
|
||||||
|
found += [
|
||||||
|
f"{number}: {target} {sub} {option}".replace(" ", " ")
|
||||||
|
for arg in args
|
||||||
|
for option in _OPTION.findall(arg.split("=", 1)[0])
|
||||||
|
if arg.startswith(option) and option not in known
|
||||||
|
]
|
||||||
|
return found
|
||||||
|
|
||||||
|
def test_every_cited_option_is_in_the_parser(self):
|
||||||
|
for page in sorted(WIKI.glob("*.md")):
|
||||||
|
with self.subTest(page=page.name):
|
||||||
|
self.assertEqual(self.unknown(page.read_text(encoding="utf-8")), [])
|
||||||
|
|
||||||
|
def test_an_unknown_option_is_named(self):
|
||||||
|
self.assertEqual(
|
||||||
|
self.unknown("python scripts/campaign.py status --budget 6h\n"),
|
||||||
|
["1: scripts/campaign.py status --budget"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PipelineStepsMatchTheRun(unittest.TestCase):
|
||||||
|
"""The step numbers the pages give are the ones pipeline.py prints. The
|
||||||
|
testing guide called the integrity check "step 6/8" in a run of 24 steps
|
||||||
|
numbered by label, and the tools table can lose or keep a step the run
|
||||||
|
no longer has."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def labels() -> list[str]:
|
||||||
|
import pipeline # noqa: PLC0415
|
||||||
|
|
||||||
|
return [pipeline.step_number(key) for key in pipeline.STEP_LABELS]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def cited(text: str) -> list[str]:
|
||||||
|
"""Step numbers a sentence attributes to the pipeline."""
|
||||||
|
found = []
|
||||||
|
for match in re.finditer(
|
||||||
|
r"(?i)pipeline steps? ((?:\d[a-z0-9]*(?:/\d+)?(?:, | and | or )?)+)"
|
||||||
|
r"|steps? (\d[a-z0-9]*(?:/\d+)?) of the pipeline",
|
||||||
|
text,
|
||||||
|
):
|
||||||
|
found += re.findall(r"\d[a-z0-9]*(?:/\d+)?", match.group(1) or match.group(2))
|
||||||
|
return found
|
||||||
|
|
||||||
|
def test_the_table_lists_every_step_in_order(self):
|
||||||
|
section = _section(_page("tools.md"), "Pipeline")
|
||||||
|
table = re.findall(r"^\| (\d[a-z0-9]*) \|", section, re.M)
|
||||||
|
self.assertEqual(table, self.labels())
|
||||||
|
|
||||||
|
def test_steps_named_in_the_pipeline_section_exist(self):
|
||||||
|
section = _section(_page("tools.md"), "Pipeline")
|
||||||
|
labels = set(self.labels())
|
||||||
|
named = [
|
||||||
|
step
|
||||||
|
for clause in re.findall(r"(?i)\bsteps? ((?:\(?\d[a-z0-9]*\)?(?:, | and |, and )?)+)", section)
|
||||||
|
for step in re.findall(r"\d[a-z0-9]*", clause)
|
||||||
|
]
|
||||||
|
named += [step for group in re.findall(r"\(((?:\d[a-z0-9]*, )+\d[a-z0-9]*)\)", section)
|
||||||
|
for step in group.split(", ")]
|
||||||
|
self.assertTrue(named)
|
||||||
|
self.assertEqual(sorted(set(named) - labels), [])
|
||||||
|
|
||||||
|
def test_every_page_cites_a_step_the_run_has(self):
|
||||||
|
labels = set(self.labels())
|
||||||
|
for page in sorted(WIKI.glob("*.md")):
|
||||||
|
with self.subTest(page=page.name):
|
||||||
|
self.assertEqual(
|
||||||
|
[step for step in self.cited(page.read_text(encoding="utf-8"))
|
||||||
|
if step not in labels], [])
|
||||||
|
|
||||||
|
def test_a_fraction_of_steps_is_refused(self):
|
||||||
|
self.assertEqual(self.cited("Integrated as pipeline step 6/8 (runs after"), ["6/8"])
|
||||||
|
self.assertEqual(self.cited("pipeline steps 3a and 4d"), ["3a", "4d"])
|
||||||
|
|
||||||
|
|
||||||
|
class SiteOutputIsTheStarlightProject(unittest.TestCase):
|
||||||
|
"""generate_site.py writes into website/ since the move to Starlight,
|
||||||
|
while tools.md still gave `docs/` as the default of --docs-dir and as a
|
||||||
|
path the pipeline holds, and the testing guide measured pages in `docs/`."""
|
||||||
|
|
||||||
|
PAGES = ("tools.md", "testing-guide.md", "release-process.md",
|
||||||
|
"reading-reports.md", "measurements.md")
|
||||||
|
BARE_DOCS = re.compile(r"(?<![\w/.-])docs/(?!\w)")
|
||||||
|
|
||||||
|
def test_no_page_names_a_docs_folder(self):
|
||||||
|
for name in self.PAGES:
|
||||||
|
with self.subTest(page=name):
|
||||||
|
self.assertEqual(self.BARE_DOCS.findall(_page(name)), [])
|
||||||
|
|
||||||
|
def test_the_old_wording_is_caught(self):
|
||||||
|
self.assertTrue(self.BARE_DOCS.search("| `--docs-dir` (output, `docs/` by default) |"))
|
||||||
|
self.assertFalse(self.BARE_DOCS.search("`website/src/content/docs/`"))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
Reference in new issue
Block a user