✨ feat(tsl-codegen): support file and directory inputs
This commit is contained in:
@@ -9,6 +9,11 @@ import unittest
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
yaml = None
|
||||
|
||||
SCRIPT = Path(__file__).parents[1] / "scripts" / "generate.py"
|
||||
REPO_ROOT = Path(__file__).resolve().parents[3]
|
||||
|
||||
@@ -50,7 +55,17 @@ class DocGenCliTest(unittest.TestCase):
|
||||
|
||||
def run_cli(self, *args, env=None):
|
||||
return subprocess.run(
|
||||
[sys.executable, str(SCRIPT), str(self.input), *args],
|
||||
[sys.executable, str(SCRIPT), "--file", str(self.input), *args],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
cwd=self.root,
|
||||
env=env,
|
||||
)
|
||||
|
||||
def run_raw_cli(self, *args, env=None):
|
||||
return subprocess.run(
|
||||
[sys.executable, str(SCRIPT), *map(str, args)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
@@ -64,6 +79,29 @@ class DocGenCliTest(unittest.TestCase):
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
def write_recording(self, path, relative, module="项目 / 批量"):
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"module": module,
|
||||
"path": relative,
|
||||
"declarations": [
|
||||
{
|
||||
"kind": "function",
|
||||
"name": "demo",
|
||||
"signature": "demo()",
|
||||
"desc": "示例函数。",
|
||||
"returns": "nil",
|
||||
}
|
||||
],
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
return path
|
||||
|
||||
def generated(self, relative, scope="project"):
|
||||
return (
|
||||
self.root
|
||||
@@ -120,6 +158,232 @@ class DocGenCliTest(unittest.TestCase):
|
||||
output.read_text(encoding="utf-8").startswith("# 项目 / 示例\n")
|
||||
)
|
||||
|
||||
def test_file_option_generates_one_recording(self):
|
||||
result = self.run_raw_cli("--file", self.input)
|
||||
|
||||
self.assertEqual(0, result.returncode, result.stderr)
|
||||
self.assertTrue(self.generated("base/my_functions").is_file())
|
||||
|
||||
def test_legacy_positional_input_remains_supported(self):
|
||||
result = self.run_raw_cli(self.input)
|
||||
|
||||
self.assertEqual(0, result.returncode, result.stderr)
|
||||
self.assertTrue(self.generated("base/my_functions").is_file())
|
||||
|
||||
def test_help_marks_legacy_input_as_deprecated(self):
|
||||
result = self.run_raw_cli("--help")
|
||||
|
||||
self.assertEqual(0, result.returncode, result.stderr)
|
||||
self.assertIn("--file INPUT_FILE", result.stdout)
|
||||
self.assertIn("--dir INPUT_DIR", result.stdout)
|
||||
self.assertIn("已废弃,请使用 --file", result.stdout)
|
||||
|
||||
def test_invalid_format_error_is_fully_chinese(self):
|
||||
result = self.run_raw_cli("--file", self.input, "--format", "xml")
|
||||
|
||||
self.assertEqual(2, result.returncode)
|
||||
self.assertIn(
|
||||
"参数 --format: 取值无效:'xml'(可选值:'json', 'yaml')",
|
||||
result.stderr,
|
||||
)
|
||||
self.assertNotIn("choose from", result.stderr)
|
||||
|
||||
def test_exactly_one_input_mode_is_required(self):
|
||||
cases = (
|
||||
(),
|
||||
("--file", self.input, "--dir", self.root),
|
||||
(self.input, "--file", self.input),
|
||||
)
|
||||
for args in cases:
|
||||
with self.subTest(args=args):
|
||||
result = self.run_raw_cli(*args)
|
||||
|
||||
self.assertEqual(2, result.returncode)
|
||||
self.assertIn(
|
||||
"必须且只能指定一种输入方式:INPUT_FILE、--file 或 --dir",
|
||||
result.stderr,
|
||||
)
|
||||
|
||||
def test_dir_processes_only_direct_json_files_in_name_order(self):
|
||||
input_dir = self.root / "recordings"
|
||||
second = self.write_recording(input_dir / "b.json", "base/b")
|
||||
first = self.write_recording(input_dir / "a.json", "base/a")
|
||||
self.write_recording(input_dir / "nested" / "c.json", "base/c")
|
||||
(input_dir / "notes.txt").write_text("ignore", encoding="utf-8")
|
||||
|
||||
result = self.run_raw_cli("--dir", input_dir, "--format", "json")
|
||||
|
||||
self.assertEqual(0, result.returncode, result.stderr)
|
||||
self.assertTrue(self.generated("base/a").is_file())
|
||||
self.assertTrue(self.generated("base/b").is_file())
|
||||
self.assertFalse(self.generated("base/c").exists())
|
||||
self.assertLess(
|
||||
result.stderr.index(str(first)), result.stderr.index(str(second))
|
||||
)
|
||||
|
||||
def test_dir_format_filters_out_other_supported_extensions(self):
|
||||
input_dir = self.root / "recordings"
|
||||
self.write_recording(input_dir / "only.json", "base/only")
|
||||
(input_dir / "ignored.yaml").write_text(": invalid", encoding="utf-8")
|
||||
|
||||
result = self.run_raw_cli("--dir", input_dir, "--format", "json")
|
||||
|
||||
self.assertEqual(0, result.returncode, result.stderr)
|
||||
self.assertTrue(self.generated("base/only").is_file())
|
||||
self.assertNotIn("ignored.yaml", result.stderr)
|
||||
|
||||
@unittest.skipUnless(yaml is not None, "pyyaml is not installed")
|
||||
def test_dir_without_format_processes_json_yaml_and_yml(self):
|
||||
input_dir = self.root / "recordings"
|
||||
self.write_recording(input_dir / "first.json", "base/first")
|
||||
for name, relative in (
|
||||
("second.yaml", "base/second"),
|
||||
("third.yml", "base/third"),
|
||||
):
|
||||
data = {
|
||||
"module": "项目 / 批量",
|
||||
"path": relative,
|
||||
"declarations": [
|
||||
{
|
||||
"kind": "function",
|
||||
"name": "demo",
|
||||
"signature": "demo()",
|
||||
"desc": "示例函数。",
|
||||
"returns": "nil",
|
||||
}
|
||||
],
|
||||
}
|
||||
(input_dir / name).write_text(
|
||||
yaml.safe_dump(data, allow_unicode=True, sort_keys=False),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
result = self.run_raw_cli("--dir", input_dir)
|
||||
|
||||
self.assertEqual(0, result.returncode, result.stderr)
|
||||
for relative in ("base/first", "base/second", "base/third"):
|
||||
self.assertTrue(self.generated(relative).is_file())
|
||||
|
||||
def test_dir_without_matching_files_reports_directory_and_extensions(self):
|
||||
input_dir = self.root / "recordings"
|
||||
input_dir.mkdir()
|
||||
(input_dir / "notes.txt").write_text("ignore", encoding="utf-8")
|
||||
self.write_recording(input_dir / "nested" / "hidden.json", "base/hidden")
|
||||
|
||||
result = self.run_raw_cli("--dir", input_dir)
|
||||
|
||||
self.assertEqual(1, result.returncode)
|
||||
self.assertIn(str(input_dir), result.stderr)
|
||||
self.assertIn("未找到符合条件的直属录入文件", result.stderr)
|
||||
self.assertIn(".json、.yaml、.yml", result.stderr)
|
||||
|
||||
def test_dir_scan_error_reports_directory_and_specific_reason(self):
|
||||
module = load_script()
|
||||
input_dir = self.root / "recordings"
|
||||
input_dir.mkdir()
|
||||
|
||||
with mock.patch.object(
|
||||
module.Path, "iterdir", side_effect=PermissionError("拒绝访问")
|
||||
):
|
||||
with self.assertRaises(module.GenerationError) as caught:
|
||||
module.gather_directory_inputs(input_dir, None)
|
||||
|
||||
message = str(caught.exception)
|
||||
self.assertIn(f"读取输入目录失败:{input_dir}", message)
|
||||
self.assertIn("拒绝访问", message)
|
||||
|
||||
def test_dir_collects_all_file_errors_before_writing_any_markdown(self):
|
||||
input_dir = self.root / "recordings"
|
||||
self.write_recording(input_dir / "a-valid.json", "base/valid")
|
||||
(input_dir / "b-invalid-json.json").write_text("{", encoding="utf-8")
|
||||
(input_dir / "c-invalid-data.json").write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"module": "项目 / 错误",
|
||||
"path": "base/invalid_data",
|
||||
"declarations": [],
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
existing = self.generated("base/valid")
|
||||
existing.parent.mkdir(parents=True, exist_ok=True)
|
||||
existing.write_text("原内容\n", encoding="utf-8")
|
||||
|
||||
result = self.run_raw_cli("--dir", input_dir, "--format", "json")
|
||||
|
||||
self.assertEqual(1, result.returncode)
|
||||
self.assertIn(str(input_dir / "b-invalid-json.json"), result.stderr)
|
||||
self.assertIn("JSON 格式错误", result.stderr)
|
||||
self.assertIn(str(input_dir / "c-invalid-data.json"), result.stderr)
|
||||
self.assertIn("录入数据校验失败", result.stderr)
|
||||
self.assertIn("declarations 必须是非空列表", result.stderr)
|
||||
self.assertIn("批量生成已中止", result.stderr)
|
||||
self.assertIn("未写入任何 Markdown 文件", result.stderr)
|
||||
self.assertEqual("原内容\n", existing.read_text(encoding="utf-8"))
|
||||
|
||||
def test_dir_rejects_output_collisions_before_writing(self):
|
||||
input_dir = self.root / "recordings"
|
||||
first = self.write_recording(input_dir / "a.json", "base/collision")
|
||||
second = self.write_recording(input_dir / "b.json", "base/collision")
|
||||
output = self.generated("base/collision")
|
||||
|
||||
result = self.run_raw_cli("--dir", input_dir, "--format", "json")
|
||||
|
||||
self.assertEqual(1, result.returncode)
|
||||
self.assertIn("输出目标冲突", result.stderr)
|
||||
self.assertIn(str(first), result.stderr)
|
||||
self.assertIn(str(second), result.stderr)
|
||||
self.assertIn(str(output), result.stderr)
|
||||
self.assertIn("未写入任何 Markdown 文件", result.stderr)
|
||||
self.assertFalse(output.exists())
|
||||
|
||||
def test_single_file_json_error_reports_file_and_position_in_chinese(self):
|
||||
self.input.write_text("{", encoding="utf-8")
|
||||
|
||||
result = self.run_raw_cli("--file", self.input)
|
||||
|
||||
self.assertEqual(1, result.returncode)
|
||||
self.assertIn(f"错误:{self.input}:JSON 格式错误", result.stderr)
|
||||
self.assertIn("第 1 行", result.stderr)
|
||||
self.assertIn("第 2 列", result.stderr)
|
||||
|
||||
def test_windows_path_separator_writes_nested_markdown(self):
|
||||
data = json.loads(self.input.read_text(encoding="utf-8"))
|
||||
data["path"] = r"base\windows_example"
|
||||
self.write_input(data)
|
||||
|
||||
result = self.run_raw_cli("--file", self.input)
|
||||
|
||||
self.assertEqual(0, result.returncode, result.stderr)
|
||||
self.assertTrue(self.generated("base/windows_example").is_file())
|
||||
|
||||
def test_invalid_paths_report_input_file_and_specific_reason(self):
|
||||
cases = (
|
||||
(r"C:\base\example", "不允许 Windows 盘符路径"),
|
||||
(r"\\server\share\example", "不允许 UNC 路径"),
|
||||
(r"\base\example", "不能以斜杠或反斜杠开头"),
|
||||
(r"base\..\example", "不允许跳转到父目录"),
|
||||
(r"base\example.md", "不能包含 .md 等文件扩展名"),
|
||||
(".", "必须指向具体文档"),
|
||||
)
|
||||
for path, reason in cases:
|
||||
with self.subTest(path=path):
|
||||
data = json.loads(self.input.read_text(encoding="utf-8"))
|
||||
data["path"] = path
|
||||
self.write_input(data)
|
||||
|
||||
result = self.run_raw_cli("--file", self.input)
|
||||
|
||||
self.assertEqual(1, result.returncode, result.stderr)
|
||||
self.assertIn(
|
||||
f"错误:{self.input}:输出路径无效:",
|
||||
result.stderr,
|
||||
)
|
||||
self.assertIn(reason, result.stderr)
|
||||
self.assertNotIn("Traceback", result.stderr)
|
||||
|
||||
def test_custom_scope_changes_first_destination_directory(self):
|
||||
result = self.run_cli("--scope", "my-project")
|
||||
output = (
|
||||
@@ -138,7 +402,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
def test_output_option_is_rejected(self):
|
||||
result = self.run_cli("--output", str(self.root / "out.md"))
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("unrecognized arguments: --output", result.stderr)
|
||||
self.assertIn("无法识别的参数: --output", result.stderr)
|
||||
|
||||
def test_generated_markdown_is_formatted_by_repo_prettier(self):
|
||||
self.input.write_text(
|
||||
@@ -304,54 +568,54 @@ class DocGenCliTest(unittest.TestCase):
|
||||
"tags": ["组件", "示例"],
|
||||
"bases": ["BaseWidget"],
|
||||
"members": [
|
||||
{
|
||||
"kind": "method",
|
||||
"name": "Close",
|
||||
"visibility": "public",
|
||||
"binding": "instance",
|
||||
"signature": "Close()",
|
||||
"desc": "关闭组件。",
|
||||
},
|
||||
{
|
||||
"kind": "method",
|
||||
"name": "Create",
|
||||
"visibility": "protected",
|
||||
"binding": "class",
|
||||
"signature": "Create(name)",
|
||||
"desc": "创建组件。",
|
||||
"params": [
|
||||
{
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"desc": "组件名称",
|
||||
}
|
||||
],
|
||||
"returns": "Widget",
|
||||
"modifiers": ["overload"],
|
||||
},
|
||||
{
|
||||
"kind": "property",
|
||||
"name": "Title",
|
||||
"visibility": "public",
|
||||
"desc": "组件标题。",
|
||||
"type": "string",
|
||||
"access": "readwrite",
|
||||
},
|
||||
{
|
||||
"kind": "field",
|
||||
"name": "Count",
|
||||
"visibility": "protected",
|
||||
"desc": "组件数量。",
|
||||
"type": "integer",
|
||||
"static": True,
|
||||
},
|
||||
{
|
||||
"kind": "constant",
|
||||
"name": "DefaultName",
|
||||
"visibility": "public",
|
||||
"desc": "默认名称。",
|
||||
"value": "'widget'",
|
||||
},
|
||||
{
|
||||
"kind": "method",
|
||||
"name": "Close",
|
||||
"visibility": "public",
|
||||
"binding": "instance",
|
||||
"signature": "Close()",
|
||||
"desc": "关闭组件。",
|
||||
},
|
||||
{
|
||||
"kind": "method",
|
||||
"name": "Create",
|
||||
"visibility": "protected",
|
||||
"binding": "class",
|
||||
"signature": "Create(name)",
|
||||
"desc": "创建组件。",
|
||||
"params": [
|
||||
{
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"desc": "组件名称",
|
||||
}
|
||||
],
|
||||
"returns": "Widget",
|
||||
"modifiers": ["overload"],
|
||||
},
|
||||
{
|
||||
"kind": "property",
|
||||
"name": "Title",
|
||||
"visibility": "public",
|
||||
"desc": "组件标题。",
|
||||
"type": "string",
|
||||
"access": "readwrite",
|
||||
},
|
||||
{
|
||||
"kind": "field",
|
||||
"name": "Count",
|
||||
"visibility": "protected",
|
||||
"desc": "组件数量。",
|
||||
"type": "integer",
|
||||
"static": True,
|
||||
},
|
||||
{
|
||||
"kind": "constant",
|
||||
"name": "DefaultName",
|
||||
"visibility": "public",
|
||||
"desc": "默认名称。",
|
||||
"value": "'widget'",
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
@@ -428,49 +692,49 @@ class DocGenCliTest(unittest.TestCase):
|
||||
"name": "DemoUnit",
|
||||
"desc": "提供文档能力。",
|
||||
"members": [
|
||||
{
|
||||
"kind": "constant",
|
||||
"name": "DefaultSize",
|
||||
"desc": "默认大小。",
|
||||
"value": 100,
|
||||
},
|
||||
{
|
||||
"kind": "variable",
|
||||
"name": "CurrentDocument",
|
||||
"desc": "当前文档。",
|
||||
"type": "Document",
|
||||
},
|
||||
{
|
||||
"kind": "function",
|
||||
"name": "OpenDocument",
|
||||
"signature": "OpenDocument(path)",
|
||||
"desc": "打开文档。",
|
||||
"params": [
|
||||
{
|
||||
"name": "path",
|
||||
"type": "string",
|
||||
"desc": "文档路径",
|
||||
}
|
||||
],
|
||||
"returns": "Document",
|
||||
},
|
||||
{
|
||||
"kind": "class",
|
||||
"name": "Document",
|
||||
"desc": "文档对象。",
|
||||
"bases": ["BaseDocument"],
|
||||
"members": [
|
||||
{
|
||||
"kind": "method",
|
||||
"name": "Save",
|
||||
"visibility": "public",
|
||||
"binding": "instance",
|
||||
"signature": "Save()",
|
||||
"desc": "保存文档。",
|
||||
"returns": "boolean",
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"kind": "constant",
|
||||
"name": "DefaultSize",
|
||||
"desc": "默认大小。",
|
||||
"value": 100,
|
||||
},
|
||||
{
|
||||
"kind": "variable",
|
||||
"name": "CurrentDocument",
|
||||
"desc": "当前文档。",
|
||||
"type": "Document",
|
||||
},
|
||||
{
|
||||
"kind": "function",
|
||||
"name": "OpenDocument",
|
||||
"signature": "OpenDocument(path)",
|
||||
"desc": "打开文档。",
|
||||
"params": [
|
||||
{
|
||||
"name": "path",
|
||||
"type": "string",
|
||||
"desc": "文档路径",
|
||||
}
|
||||
],
|
||||
"returns": "Document",
|
||||
},
|
||||
{
|
||||
"kind": "class",
|
||||
"name": "Document",
|
||||
"desc": "文档对象。",
|
||||
"bases": ["BaseDocument"],
|
||||
"members": [
|
||||
{
|
||||
"kind": "method",
|
||||
"name": "Save",
|
||||
"visibility": "public",
|
||||
"binding": "instance",
|
||||
"signature": "Save()",
|
||||
"desc": "保存文档。",
|
||||
"returns": "boolean",
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
@@ -579,7 +843,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
result = self.run_cli()
|
||||
|
||||
self.assertEqual(1, result.returncode)
|
||||
self.assertIn("missing 'returns'", result.stderr)
|
||||
self.assertIn("缺少 returns", result.stderr)
|
||||
|
||||
def test_examples_list_renders_independent_fences_and_output_comments(self):
|
||||
self.write_input(
|
||||
@@ -625,7 +889,9 @@ class DocGenCliTest(unittest.TestCase):
|
||||
output = self.root / "existing.md"
|
||||
output.write_text("原内容\n", encoding="utf-8")
|
||||
|
||||
with mock.patch.object(module.os, "replace", side_effect=OSError("replace failed")):
|
||||
with mock.patch.object(
|
||||
module.os, "replace", side_effect=OSError("replace failed")
|
||||
):
|
||||
with self.assertRaisesRegex(OSError, "replace failed"):
|
||||
module.atomic_write(output, "新内容\n")
|
||||
|
||||
@@ -634,9 +900,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
|
||||
def test_legacy_root_keys_are_rejected_without_overwrite(self):
|
||||
legacy_values = {
|
||||
"functions": [
|
||||
{"signature": "Old()", "desc": "旧函数。", "returns": "nil"}
|
||||
],
|
||||
"functions": [{"signature": "Old()", "desc": "旧函数。", "returns": "nil"}],
|
||||
"class": {"name": "Old", "desc": "旧类。", "members": []},
|
||||
"unit": {"name": "Old", "desc": "旧接口。", "members": []},
|
||||
}
|
||||
@@ -651,9 +915,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
}
|
||||
)
|
||||
|
||||
self.assert_rejected_without_overwrite(
|
||||
relative, f"unknown field(s): {key}"
|
||||
)
|
||||
self.assert_rejected_without_overwrite(relative, f"存在未知字段:{key}")
|
||||
|
||||
def test_unknown_class_member_kind_is_rejected_without_overwrite(self):
|
||||
self.write_class_member_input(
|
||||
@@ -666,9 +928,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
"base/unknown_kind",
|
||||
)
|
||||
|
||||
self.assert_rejected_without_overwrite(
|
||||
"base/unknown_kind", "unknown kind 'event'"
|
||||
)
|
||||
self.assert_rejected_without_overwrite("base/unknown_kind", "未知 kind:event")
|
||||
|
||||
def test_method_static_field_is_rejected_without_overwrite(self):
|
||||
self.write_class_member_input(
|
||||
@@ -685,7 +945,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
)
|
||||
|
||||
self.assert_rejected_without_overwrite(
|
||||
"base/method_static", "unknown field(s): static"
|
||||
"base/method_static", "存在未知字段:static"
|
||||
)
|
||||
|
||||
def test_private_class_member_is_rejected_without_overwrite(self):
|
||||
@@ -701,7 +961,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
)
|
||||
|
||||
self.assert_rejected_without_overwrite(
|
||||
"base/private_member", "visibility must be public or protected"
|
||||
"base/private_member", "visibility 只能是 public 或 protected"
|
||||
)
|
||||
|
||||
def test_property_empty_type_is_treated_as_omitted(self):
|
||||
@@ -757,7 +1017,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
)
|
||||
|
||||
self.assert_rejected_without_overwrite(
|
||||
"base/field_type", "members[0]: type: must be non-empty"
|
||||
"base/field_type", "members[0]: type:不能为空"
|
||||
)
|
||||
|
||||
def test_unit_variable_missing_type_is_rejected_without_overwrite(self):
|
||||
@@ -783,7 +1043,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
)
|
||||
|
||||
self.assert_rejected_without_overwrite(
|
||||
"base/variable_type", "members[0]: type: must be non-empty"
|
||||
"base/variable_type", "members[0]: type:不能为空"
|
||||
)
|
||||
|
||||
def test_invalid_method_binding_is_rejected_without_overwrite(self):
|
||||
@@ -799,9 +1059,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
"base/invalid_binding",
|
||||
)
|
||||
|
||||
self.assert_rejected_without_overwrite(
|
||||
"base/invalid_binding", "invalid binding"
|
||||
)
|
||||
self.assert_rejected_without_overwrite("base/invalid_binding", "binding 无效")
|
||||
|
||||
def test_invalid_property_access_is_rejected_without_overwrite(self):
|
||||
self.write_class_member_input(
|
||||
@@ -816,9 +1074,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
"base/invalid_access",
|
||||
)
|
||||
|
||||
self.assert_rejected_without_overwrite(
|
||||
"base/invalid_access", "invalid access"
|
||||
)
|
||||
self.assert_rejected_without_overwrite("base/invalid_access", "access 无效")
|
||||
|
||||
def test_invalid_method_modifier_is_rejected_without_overwrite(self):
|
||||
self.write_class_member_input(
|
||||
@@ -834,9 +1090,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
"base/invalid_modifier",
|
||||
)
|
||||
|
||||
self.assert_rejected_without_overwrite(
|
||||
"base/invalid_modifier", "invalid modifier"
|
||||
)
|
||||
self.assert_rejected_without_overwrite("base/invalid_modifier", "无效 modifier")
|
||||
|
||||
def test_property_examples_are_rejected_without_overwrite(self):
|
||||
self.write_class_member_input(
|
||||
@@ -858,7 +1112,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
)
|
||||
|
||||
self.assert_rejected_without_overwrite(
|
||||
"base/property_examples", "unknown field(s): examples"
|
||||
"base/property_examples", "存在未知字段:examples"
|
||||
)
|
||||
|
||||
def test_unit_rejects_implementation_data_without_overwrite(self):
|
||||
@@ -879,7 +1133,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
)
|
||||
|
||||
self.assert_rejected_without_overwrite(
|
||||
"base/unit_implementation", "unknown field(s): implementation"
|
||||
"base/unit_implementation", "存在未知字段:implementation"
|
||||
)
|
||||
|
||||
def test_empty_declarations_are_rejected_without_overwrite(self):
|
||||
@@ -892,7 +1146,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
)
|
||||
|
||||
self.assert_rejected_without_overwrite(
|
||||
"base/missing_branch", "declarations must be a non-empty list"
|
||||
"base/missing_branch", "declarations 必须是非空列表"
|
||||
)
|
||||
|
||||
def test_unknown_declaration_kind_is_rejected_without_overwrite(self):
|
||||
@@ -905,7 +1159,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
)
|
||||
|
||||
self.assert_rejected_without_overwrite(
|
||||
"base/unknown_declaration", "unknown kind 'procedure'"
|
||||
"base/unknown_declaration", "未知 kind:procedure"
|
||||
)
|
||||
|
||||
def test_top_level_function_requires_name(self):
|
||||
@@ -946,7 +1200,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
)
|
||||
|
||||
self.assert_rejected_without_overwrite(
|
||||
"base/function_name_mismatch", "name and signature differ"
|
||||
"base/function_name_mismatch", "name 与 signature 中的名称不一致"
|
||||
)
|
||||
|
||||
def test_duplicate_function_signature_is_rejected(self):
|
||||
@@ -974,7 +1228,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
)
|
||||
|
||||
self.assert_rejected_without_overwrite(
|
||||
"base/duplicate_function", "duplicate function signature"
|
||||
"base/duplicate_function", "function signature 重复"
|
||||
)
|
||||
|
||||
def test_duplicate_class_and_unit_names_are_rejected(self):
|
||||
@@ -1019,9 +1273,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
}
|
||||
)
|
||||
|
||||
self.assert_rejected_without_overwrite(
|
||||
relative, f"duplicate {kind} name"
|
||||
)
|
||||
self.assert_rejected_without_overwrite(relative, f"{kind} 名称重复")
|
||||
|
||||
def test_function_overloads_and_cross_kind_same_name_are_allowed(self):
|
||||
self.write_input(
|
||||
@@ -1034,9 +1286,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
"name": "Open",
|
||||
"signature": "Open(path)",
|
||||
"desc": "按路径打开。",
|
||||
"params": [
|
||||
{"name": "path", "type": "string", "desc": "路径"}
|
||||
],
|
||||
"params": [{"name": "path", "type": "string", "desc": "路径"}],
|
||||
"returns": "nil",
|
||||
},
|
||||
{
|
||||
@@ -1044,9 +1294,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
"name": "Open",
|
||||
"signature": "Open(mode)",
|
||||
"desc": "按模式打开。",
|
||||
"params": [
|
||||
{"name": "mode", "type": "integer", "desc": "模式"}
|
||||
],
|
||||
"params": [{"name": "mode", "type": "integer", "desc": "模式"}],
|
||||
"returns": "nil",
|
||||
},
|
||||
{
|
||||
@@ -1097,9 +1345,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
"base/missing_value",
|
||||
)
|
||||
|
||||
self.assert_rejected_without_overwrite(
|
||||
"base/missing_value", "missing 'value'"
|
||||
)
|
||||
self.assert_rejected_without_overwrite("base/missing_value", "缺少 value")
|
||||
|
||||
def test_class_missing_description_is_rejected_without_overwrite(self):
|
||||
self.write_input(
|
||||
@@ -1119,7 +1365,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
|
||||
self.assert_rejected_without_overwrite(
|
||||
"base/missing_class_desc",
|
||||
"declarations[0]: desc: must be non-empty",
|
||||
"declarations[0]: desc:不能为空",
|
||||
)
|
||||
|
||||
def test_method_name_and_parameter_order_must_match_signature(self):
|
||||
@@ -1150,7 +1396,7 @@ class DocGenCliTest(unittest.TestCase):
|
||||
relative = f"base/mismatch_{name}"
|
||||
self.write_class_member_input(member, relative)
|
||||
|
||||
expected = "differ" if name == "name" else "signature order"
|
||||
expected = "名称不一致" if name == "name" else "参数顺序一致"
|
||||
self.assert_rejected_without_overwrite(relative, expected)
|
||||
|
||||
def test_instance_field_and_static_constant_render_distinct_headings(self):
|
||||
@@ -1164,21 +1410,21 @@ class DocGenCliTest(unittest.TestCase):
|
||||
"name": "Bindings",
|
||||
"desc": "绑定示例。",
|
||||
"members": [
|
||||
{
|
||||
"kind": "field",
|
||||
"name": "Name",
|
||||
"visibility": "public",
|
||||
"desc": "名称。",
|
||||
"type": "string",
|
||||
},
|
||||
{
|
||||
"kind": "constant",
|
||||
"name": "Maximum",
|
||||
"visibility": "protected",
|
||||
"desc": "最大值。",
|
||||
"value": 10,
|
||||
"static": True,
|
||||
},
|
||||
{
|
||||
"kind": "field",
|
||||
"name": "Name",
|
||||
"visibility": "public",
|
||||
"desc": "名称。",
|
||||
"type": "string",
|
||||
},
|
||||
{
|
||||
"kind": "constant",
|
||||
"name": "Maximum",
|
||||
"visibility": "protected",
|
||||
"desc": "最大值。",
|
||||
"value": 10,
|
||||
"static": True,
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user