Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,158 @@
|
||||
"""Check shipped skill links, current library signatures and candidate projects."""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SKILL = ROOT / "skills"
|
||||
COMPILER = None
|
||||
|
||||
|
||||
def check_tutorial_sources(skill):
|
||||
"""Keep tutorial excerpts tied to the complete projects checked by Rust."""
|
||||
projects = [
|
||||
path.resolve()
|
||||
for path in [skill / "assets/template", *(skill / "assets/examples").iterdir()]
|
||||
]
|
||||
for document in sorted((skill / "references").glob("*.md")):
|
||||
content = document.read_text()
|
||||
blocks = list(re.finditer(
|
||||
r"^```dever[ \t]*\n(.*?)^```[ \t]*$", content, re.MULTILINE | re.DOTALL
|
||||
))
|
||||
if len(blocks) != len(re.findall(r"^```dever\b", content, re.MULTILINE)):
|
||||
raise ValueError(f"{document.name}: unclosed or unsupported dever fence")
|
||||
for block in blocks:
|
||||
binding = re.search(
|
||||
r"源码:\[[^\]\n]+\]\(([^)\n]+)\)\n\n\Z", content[:block.start()]
|
||||
)
|
||||
if binding is None:
|
||||
raise ValueError(f"{document.name}: dever block needs a source link")
|
||||
source = (document.parent / binding.group(1)).resolve()
|
||||
owners = [root for root in projects if source.is_relative_to(root)]
|
||||
if len(owners) != 1 or not source.name.endswith(".dever"):
|
||||
raise ValueError(f"{document.name}: source must belong to a checked skill project")
|
||||
relative = source.relative_to(owners[0])
|
||||
if relative.parts[0] not in ("module", "test") or not source.is_file():
|
||||
raise ValueError(f"{document.name}: source must be a module or test file")
|
||||
excerpt = block.group(1)
|
||||
code = source.read_text()
|
||||
if not excerpt.strip() or ("\n" + code).count("\n" + excerpt) != 1:
|
||||
raise ValueError(f"{document.name}: excerpt differs from its source or is ambiguous")
|
||||
|
||||
|
||||
class SkillExamples(unittest.TestCase):
|
||||
def test_all_shipped_projects_pass_the_candidate_checker(self):
|
||||
if COMPILER is None:
|
||||
self.skipTest("pass --compiler <candidate-dever> to check shipped projects")
|
||||
projects = [SKILL / "assets/template", *sorted((SKILL / "assets/examples").iterdir())]
|
||||
for project in projects:
|
||||
with self.subTest(project=project.name):
|
||||
result = subprocess.run(
|
||||
[str(COMPILER), "check", str(project)],
|
||||
capture_output=True, text=True, timeout=30,
|
||||
)
|
||||
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
||||
|
||||
def test_project_templates_do_not_pin_the_compiler(self):
|
||||
for path in (SKILL / "assets").rglob("setting.json"):
|
||||
with self.subTest(path=path):
|
||||
settings = json.loads(path.read_text())
|
||||
self.assertNotIn("version", settings.get("dever", {}))
|
||||
|
||||
def test_relocated_skill_links_and_release_file_limits(self):
|
||||
with tempfile.TemporaryDirectory(prefix="dever-skill-") as directory:
|
||||
installed = Path(directory) / "dever-language"
|
||||
shutil.copytree(SKILL, installed)
|
||||
files = [path for path in installed.rglob("*") if path.is_file()]
|
||||
self.assertLessEqual(len(files), 256)
|
||||
self.assertLessEqual(sum(path.stat().st_size for path in files), 8 * 1024 * 1024)
|
||||
self.assertTrue((installed / "SKILL.md").is_file())
|
||||
for path in files:
|
||||
self.assertFalse(path.is_symlink())
|
||||
self.assertLessEqual(path.stat().st_size, 1024 * 1024)
|
||||
if path.suffix != ".md":
|
||||
continue
|
||||
for target in re.findall(r"\]\(([^)]+)\)", path.read_text()):
|
||||
if "://" in target or target.startswith("#"):
|
||||
continue
|
||||
resolved = (path.parent / target.split("#", 1)[0]).resolve()
|
||||
self.assertTrue(resolved.is_relative_to(installed), (path, target))
|
||||
self.assertTrue(resolved.exists(), (path, target))
|
||||
|
||||
def test_tutorial_code_matches_relocated_checked_sources(self):
|
||||
with tempfile.TemporaryDirectory(prefix="dever-tutorial-") as directory:
|
||||
installed = Path(directory) / "dever-language"
|
||||
shutil.copytree(SKILL, installed)
|
||||
check_tutorial_sources(installed)
|
||||
|
||||
def test_tutorial_source_drift_and_unbound_code_are_rejected(self):
|
||||
with tempfile.TemporaryDirectory(prefix="dever-tutorial-drift-") as directory:
|
||||
installed = Path(directory) / "dever-language"
|
||||
shutil.copytree(SKILL, installed)
|
||||
document = installed / "references/language.md"
|
||||
original = document.read_text()
|
||||
source = installed / "assets/examples/collections/test/content/article/clauses.dever"
|
||||
original_source = source.read_text()
|
||||
mutations = {
|
||||
"unbound": original.replace("源码:", "完整示例:", 1),
|
||||
"changed_code": original.replace('"fail"', '"failed"', 1),
|
||||
"unclosed": original + "\n```dever\nunclosed() () {}\n",
|
||||
"outside_project": original.replace(
|
||||
"../assets/template/module/hello/greeting/app.dever", "../SKILL.md", 1
|
||||
),
|
||||
}
|
||||
for name, changed in mutations.items():
|
||||
with self.subTest(name=name):
|
||||
document.write_text(changed)
|
||||
with self.assertRaises(ValueError):
|
||||
check_tutorial_sources(installed)
|
||||
document.write_text(original)
|
||||
source.write_text(original_source.replace('"fail"', '"failed"'))
|
||||
with self.assertRaisesRegex(ValueError, "excerpt differs"):
|
||||
check_tutorial_sources(installed)
|
||||
source.write_text(original_source + "\n" + original_source)
|
||||
with self.assertRaisesRegex(ValueError, "ambiguous"):
|
||||
check_tutorial_sources(installed)
|
||||
|
||||
def test_signature_index_matches_public_library_declarations(self):
|
||||
project = ROOT / "rust/src/compiler/project/mod.rs"
|
||||
libraries = {}
|
||||
for package, relative in re.findall(r'\(\s*"([^"]+)",\s*"lib/[^"]+",\s*include_str!\("([^"]+)"\)', project.read_text()):
|
||||
source = (project.parent / relative).read_text()
|
||||
libraries[package] = libraries.get(package, "") + "\n" + source
|
||||
index = (SKILL / "references/signatures.md").read_text()
|
||||
blocks = re.findall(r"```signatures\n(.*?)```", index, re.DOTALL)
|
||||
self.assertTrue(blocks, "the shipped index must contain concrete signatures")
|
||||
seen = set()
|
||||
for signature in "\n".join(blocks).splitlines():
|
||||
if not signature:
|
||||
continue
|
||||
qualified = signature.split("(", 1)[0]
|
||||
package, function = qualified.rsplit(".", 1)
|
||||
self.assertIn(package, libraries, "signature package must be embedded by this candidate")
|
||||
self.assertNotIn(qualified, seen)
|
||||
seen.add(qualified)
|
||||
declaration = re.search(
|
||||
rf"^public ({re.escape(function)}\s*\([^{{]*?)\s*\{{",
|
||||
libraries[package],
|
||||
re.MULTILINE,
|
||||
)
|
||||
self.assertIsNotNone(declaration, qualified)
|
||||
expected = package + "." + declaration.group(1)
|
||||
self.assertEqual(re.sub(r"\s", "", signature), re.sub(r"\s", "", expected), qualified)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(add_help=False)
|
||||
parser.add_argument("--compiler", type=Path)
|
||||
options, remaining = parser.parse_known_args()
|
||||
COMPILER = options.compiler.resolve() if options.compiler else None
|
||||
unittest.main(argv=[sys.argv[0], *remaining])
|
||||
Reference in new issue
Block a user