diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/README.md | 63 | ||||
| -rw-r--r-- | tests/run_tests.py | 12 | ||||
| -rw-r--r-- | tests/test_parser.py | 20 | ||||
| -rw-r--r-- | tests/test_processor.py | 22 | ||||
| -rw-r--r-- | tests/test_pyo3.py | 4 | ||||
| -rw-r--r-- | tests/test_renderers.py | 25 | ||||
| -rw-r--r-- | tests/test_rule_extractor.py | 17 | ||||
| -rw-r--r-- | tests/test_rust_core.py | 99 |
8 files changed, 106 insertions, 156 deletions
diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index 97a8028..0000000 --- a/tests/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# ConventionalRP 测试套件 - -本目录包含 ConventionalRP SDK 的所有单元测试。 - -## 测试文件 - -- `test_parser.py` - Parser 解析器测试 -- `test_processor.py` - Processor 处理器测试 -- `test_rule_extractor.py` - RuleExtractor 规则提取器测试 -- `test_renderers.py` - 渲染器测试(HTML/JSON/Markdown) -- `test_pyo3.py` - PyO3 Rust 扩展测试 - -## 运行测试 - -### 运行所有测试 - -```bash -python tests/run_tests.py -``` - -### 运行单个测试文件 - -```bash -python -m unittest tests/test_parser.py -python -m unittest tests/test_processor.py -``` - -### 运行特定测试类 - -```bash -python -m unittest tests.test_parser.TestParser -``` - -### 运行特定测试方法 - -```bash -python -m unittest tests.test_parser.TestParser.test_load_rules_success -``` - -## 测试覆盖率 - -要查看测试覆盖率,请安装 `coverage` 并运行: - -```bash -pip install coverage -coverage run -m unittest discover -s tests -p "test_*.py" -coverage report -coverage html # 生成 HTML 报告 -``` - -## 测试数据 - -测试使用临时文件来模拟规则文件和日志文件,测试完成后会自动清理。 - -## 添加新测试 - -创建新的测试文件时,请遵循以下约定: - -1. 文件名以 `test_` 开头 -2. 测试类继承自 `unittest.TestCase` -3. 测试方法以 `test_` 开头 -4. 使用 `setUp()` 和 `tearDown()` 方法管理测试状态 -5. 添加清晰的文档字符串说明测试目的 diff --git a/tests/run_tests.py b/tests/run_tests.py index 4cfc2d4..b776062 100644 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -1,34 +1,22 @@ -#!/usr/bin/env python3 -""" -测试套件运行器 -运行所有单元测试 -""" - import sys import unittest from pathlib import Path -# 添加 src 目录到路径 src_path = Path(__file__).parent.parent / "src" sys.path.insert(0, str(src_path)) def run_all_tests(): - """运行所有测试""" - # 创建测试加载器 loader = unittest.TestLoader() - # 从当前目录加载所有测试 suite = loader.discover( start_dir=Path(__file__).parent, pattern='test_*.py' ) - # 运行测试 runner = unittest.TextTestRunner(verbosity=2) result = runner.run(suite) - # 返回结果 return 0 if result.wasSuccessful() else 1 diff --git a/tests/test_parser.py b/tests/test_parser.py index 595d0b4..1d41a1e 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -1,8 +1,3 @@ -#!/usr/bin/env python3 -""" -Parser 模块单元测试 -""" - import unittest import tempfile from pathlib import Path @@ -10,13 +5,9 @@ from conventionalrp.core.parser import Parser class TestParser(unittest.TestCase): - """Parser 类的单元测试""" - def setUp(self): - """设置测试环境""" self.parser = Parser() - # 创建临时规则文件 self.temp_rules = tempfile.NamedTemporaryFile( mode='w', suffix='.json5', @@ -56,7 +47,6 @@ class TestParser(unittest.TestCase): }''') self.temp_rules.close() - # 创建临时日志文件 self.temp_log = tempfile.NamedTemporaryFile( mode='w', suffix='.txt', @@ -70,30 +60,25 @@ class TestParser(unittest.TestCase): self.temp_log.close() def tearDown(self): - """清理测试环境""" Path(self.temp_rules.name).unlink(missing_ok=True) Path(self.temp_log.name).unlink(missing_ok=True) def test_load_rules_success(self): - """测试成功加载规则文件""" self.parser.load_rules(self.temp_rules.name) self.assertIn("metadata", self.parser.rules) self.assertIn("content", self.parser.rules) def test_load_rules_file_not_found(self): - """测试加载不存在的规则文件""" with self.assertRaises(FileNotFoundError): self.parser.load_rules("nonexistent_file.json5") def test_parse_log_success(self): - """测试成功解析日志""" self.parser.load_rules(self.temp_rules.name) result = self.parser.parse_log(self.temp_log.name) self.assertIsInstance(result, list) self.assertGreater(len(result), 0) - # 检查第一条记录 first_entry = result[0] self.assertIn("timestamp", first_entry) self.assertIn("speaker", first_entry) @@ -101,13 +86,11 @@ class TestParser(unittest.TestCase): self.assertEqual(first_entry["speaker"], "艾莉娅") def test_parse_log_file_not_found(self): - """测试解析不存在的日志文件""" self.parser.load_rules(self.temp_rules.name) with self.assertRaises(FileNotFoundError): self.parser.parse_log("nonexistent_log.txt") def test_match_metadata(self): - """测试元数据匹配""" self.parser.load_rules(self.temp_rules.name) line = "[2025-10-24 14:30:01] <艾莉娅> 测试内容" result = self.parser._match_metadata(line) @@ -118,7 +101,6 @@ class TestParser(unittest.TestCase): self.assertEqual(result["speaker"], "艾莉娅") def test_parse_line_content_dialogue(self): - """测试解析对话内容""" self.parser.load_rules(self.temp_rules.name) line = "「这是一段对话」" result = self.parser._parse_line_content(line) @@ -128,12 +110,10 @@ class TestParser(unittest.TestCase): self.assertEqual(result[0]["type"], "dialogue") def test_parse_line_content_dice_roll(self): - """测试解析骰子投掷""" self.parser.load_rules(self.temp_rules.name) line = "检定结果: [d20 = 18]" result = self.parser._parse_line_content(line) - # 应该包含文本和骰子投掷 dice_tokens = [t for t in result if t["type"] == "dice_roll"] self.assertGreater(len(dice_tokens), 0) diff --git a/tests/test_processor.py b/tests/test_processor.py index c08fc52..adaf2db 100644 --- a/tests/test_processor.py +++ b/tests/test_processor.py @@ -1,17 +1,9 @@ -#!/usr/bin/env python3 -""" -Processor 模块单元测试 -""" - import unittest from conventionalrp.core.processor import Processor class TestProcessor(unittest.TestCase): - """Processor 类的单元测试""" - def setUp(self): - """设置测试环境""" self.processor = Processor() self.sample_tokens = [ { @@ -33,30 +25,26 @@ class TestProcessor(unittest.TestCase): ] def test_init_without_rules(self): - """测试无规则初始化""" processor = Processor() self.assertEqual(processor.rules, {}) def test_init_with_rules(self): - """测试带规则初始化""" rules = {"test_rule": "value"} processor = Processor(rules) self.assertEqual(processor.rules, rules) def test_process_tokens(self): - """测试处理 token 列表""" result = self.processor.process_tokens(self.sample_tokens) self.assertIsInstance(result, list) self.assertEqual(len(result), len(self.sample_tokens)) - # 检查处理标记 + # Check processing marks for token in result: if "timestamp" in token: self.assertTrue(token.get("processed")) def test_apply_rules(self): - """测试应用规则到单个 token""" token = self.sample_tokens[0] result = self.processor.apply_rules(token) @@ -65,7 +53,6 @@ class TestProcessor(unittest.TestCase): self.assertTrue(result.get("processed")) def test_generate_json_output(self): - """测试生成 JSON 输出""" output = self.processor.generate_json_output(self.sample_tokens) self.assertIsInstance(output, str) @@ -73,7 +60,6 @@ class TestProcessor(unittest.TestCase): self.assertIn("speaker", output) def test_generate_html_output(self): - """测试生成 HTML 输出""" output = self.processor.generate_html_output(self.sample_tokens) self.assertIsInstance(output, str) @@ -81,29 +67,25 @@ class TestProcessor(unittest.TestCase): self.assertIn("</html>", output) def test_generate_markdown_output(self): - """测试生成 Markdown 输出""" output = self.processor.generate_markdown_output(self.sample_tokens) self.assertIsInstance(output, str) self.assertIn("-", output) def test_generate_output_json(self): - """测试生成输出 - JSON 格式""" output = self.processor.generate_output(self.sample_tokens, "json") self.assertIsInstance(output, str) def test_generate_output_html(self): - """测试生成输出 - HTML 格式""" + output = self.processor.generate_output(self.sample_tokens, "html") output = self.processor.generate_output(self.sample_tokens, "html") self.assertIsInstance(output, str) def test_generate_output_markdown(self): - """测试生成输出 - Markdown 格式""" output = self.processor.generate_output(self.sample_tokens, "markdown") self.assertIsInstance(output, str) def test_generate_output_unsupported_format(self): - """测试生成输出 - 不支持的格式""" with self.assertRaises(ValueError) as context: self.processor.generate_output(self.sample_tokens, "pdf") diff --git a/tests/test_pyo3.py b/tests/test_pyo3.py deleted file mode 100644 index 9668519..0000000 --- a/tests/test_pyo3.py +++ /dev/null @@ -1,4 +0,0 @@ -from conventionalrp._core import sum_as_string - -if __name__ == "__main__": - print(sum_as_string(1, 2)) diff --git a/tests/test_renderers.py b/tests/test_renderers.py index 13e4540..8095550 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -1,8 +1,3 @@ -#!/usr/bin/env python3 -""" -Renderers 模块单元测试 -""" - import unittest import json from conventionalrp.renderers.html_renderer import HTMLRenderer @@ -11,10 +6,7 @@ from conventionalrp.renderers.markdown_renderer import MarkdownRenderer class TestRenderers(unittest.TestCase): - """测试所有渲染器""" - def setUp(self): - """设置测试数据""" self.sample_data = [ { "type": "metadata", @@ -40,7 +32,6 @@ class TestRenderers(unittest.TestCase): } def test_html_renderer_basic(self): - """测试 HTML 渲染器基本功能""" renderer = HTMLRenderer() output = renderer.render(self.sample_data) @@ -50,51 +41,45 @@ class TestRenderers(unittest.TestCase): self.assertIn("<title>", output) def test_html_renderer_set_style(self): - """测试 HTML 渲染器设置样式""" renderer = HTMLRenderer() renderer.set_style("custom_style") - # 当前实现为占位符,仅测试不抛出异常 + # now style is set, just ensure no exceptions self.assertIsNotNone(renderer) def test_json_renderer_basic(self): - """测试 JSON 渲染器基本功能""" renderer = JSONRenderer() output = renderer.render(self.sample_data) self.assertIsInstance(output, str) - # 验证输出是有效的 JSON + # Output should be valid JSON parsed = json.loads(output) self.assertIsInstance(parsed, list) self.assertEqual(len(parsed), len(self.sample_data)) def test_json_renderer_unicode(self): - """测试 JSON 渲染器处理 Unicode""" renderer = JSONRenderer() output = renderer.render(self.sample_data) - # 应该保留中文字符 + # should preserve Chinese characters self.assertIn("艾莉娅", output) self.assertIn("测试", output) def test_markdown_renderer_basic(self): - """测试 Markdown 渲染器基本功能""" renderer = MarkdownRenderer() output = renderer.render(self.dict_data) self.assertIsInstance(output, str) - self.assertIn("##", output) # 应该有标题标记 - self.assertIn("测试标题", output) + self.assertIn("##", output) + self.assertIn("test content", output) def test_markdown_renderer_set_style(self): - """测试 Markdown 渲染器设置样式""" renderer = MarkdownRenderer() style = {"heading_level": 2} renderer.set_style(style) self.assertEqual(renderer.style, style) def test_all_renderers_empty_data(self): - """测试所有渲染器处理空数据""" empty_data = [] html_renderer = HTMLRenderer() diff --git a/tests/test_rule_extractor.py b/tests/test_rule_extractor.py index 6c4d585..2c2815c 100644 --- a/tests/test_rule_extractor.py +++ b/tests/test_rule_extractor.py @@ -1,8 +1,3 @@ -#!/usr/bin/env python3 -""" -RuleExtractor 模块单元测试 -""" - import unittest import tempfile import json5 @@ -11,11 +6,7 @@ from conventionalrp.extractors.rule_extractor import RuleExtractor class TestRuleExtractor(unittest.TestCase): - """RuleExtractor 类的单元测试""" - def setUp(self): - """设置测试环境""" - # 创建临时规则文件 self.temp_rules = tempfile.NamedTemporaryFile( mode='w', suffix='.json5', @@ -30,23 +21,19 @@ class TestRuleExtractor(unittest.TestCase): self.temp_rules.close() def tearDown(self): - """清理测试环境""" Path(self.temp_rules.name).unlink(missing_ok=True) def test_init_without_file(self): - """测试不带配置文件的初始化""" extractor = RuleExtractor() self.assertEqual(extractor.rules, {}) self.assertIsNone(extractor.config_file) def test_init_with_file(self): - """测试带配置文件的初始化""" extractor = RuleExtractor(self.temp_rules.name) self.assertIsNotNone(extractor.rules) self.assertIn("test_rule", extractor.rules) def test_load_rules_from_file_success(self): - """测试成功加载规则文件""" extractor = RuleExtractor() rules = extractor.load_rules_from_file(self.temp_rules.name) @@ -55,13 +42,11 @@ class TestRuleExtractor(unittest.TestCase): self.assertEqual(rules["test_rule"], "test_value") def test_load_rules_from_file_not_found(self): - """测试加载不存在的文件""" extractor = RuleExtractor() with self.assertRaises(FileNotFoundError): extractor.load_rules_from_file("nonexistent.json5") def test_load_rules_empty_file(self): - """测试加载空文件""" empty_file = tempfile.NamedTemporaryFile( mode='w', suffix='.json5', @@ -79,7 +64,6 @@ class TestRuleExtractor(unittest.TestCase): Path(empty_file.name).unlink(missing_ok=True) def test_load_rules_method(self): - """测试 load_rules 方法""" extractor = RuleExtractor() rules = extractor.load_rules(self.temp_rules.name) @@ -87,7 +71,6 @@ class TestRuleExtractor(unittest.TestCase): self.assertEqual(extractor.rules, rules) def test_extract_method(self): - """测试 extract 方法""" extractor = RuleExtractor(self.temp_rules.name) extracted = extractor.extract() diff --git a/tests/test_rust_core.py b/tests/test_rust_core.py new file mode 100644 index 0000000..1848e68 --- /dev/null +++ b/tests/test_rust_core.py @@ -0,0 +1,99 @@ +import sys +from pathlib import Path + +project_root = Path(__file__).parent.parent +sys.path.insert(0, str(project_root / "src")) + +from conventionalrp import _core + +def test_token(): + token = _core.Token("dialogue", "「你好世界」") + token.add_metadata("speaker", "艾莉娅") + token.add_metadata("timestamp", "2025-10-24 14:30:01") + + print(f"Token: {token}") + print(f"Type: {token.token_type}") + print(f"Content: {token.content}") + print(f"Speaker: {token.get_metadata('speaker')}") + print(f"Dict: {token.to_dict()}") + + +def test_regex_rule(): + rule = _core.RegexRule(r"\[d(\d+)\s*=\s*(\d+)\]", "dice_roll", 90) + + text = "检定结果: [d20 = 18]" + print(f"Text: {text}") + print(f"Matches: {rule.matches(text)}") + print(f"Extracted: {rule.extract(text)}") + print(f"Find all: {rule.find_all(text)}") + + +def test_text_parser(): + parser = _core.TextParser() + + parser.add_rule(r"\[d(\d+)\s*=\s*(\d+)\]", "dice_roll", 90) + parser.add_rule(r"「(.+?)」", "dialogue", 80) + parser.add_rule(r"\*\*(.+?)\*\*", "action", 70) + + print(f"规则数量: {parser.rule_count()}") + + line = "艾莉娅说「我要投掷」然后 **投掷骰子** 结果是 [d20 = 18]" + print(f"\n解析文本: {line}") + result = parser.parse_line(line) + print(f"解析结果: {result}") + + lines = [ + "「你好」", + "**挥剑** [d20 = 15]", + "普通文本" + ] + results = parser.parse_lines(lines) + for i, line_result in enumerate(results): + print(f" 行 {i+1}: {line_result}") + + +def test_fast_matcher(): + patterns = ["骰子", "投掷", "检定"] + matcher = _core.FastMatcher(patterns) + + text = "艾莉娅进行投掷骰子检定" + print(f"Text: {text}") + print(f"Contains any: {matcher.contains_any(text)}") + print(f"Find matches: {matcher.find_matches(text)}") + print(f"Count matches: {matcher.count_matches(text)}") + + +def benchmark_parser(): + import time + + parser = _core.TextParser() + parser.add_rule(r"\[d(\d+)\s*=\s*(\d+)\]", "dice_roll", 90) + parser.add_rule(r"「(.+?)」", "dialogue", 80) + parser.add_rule(r"\*\*(.+?)\*\*", "action", 70) + + test_lines = [ + "「你好」**挥剑** [d20 = 15]" for _ in range(1000) + ] + + start = time.time() + results = parser.parse_lines(test_lines) + elapsed = time.time() - start + + print(f"Process {len(test_lines)} lines") + print(f"time: {elapsed:.4f} s") + print(f"speed: {len(test_lines)/elapsed:.0f} lines/s") + + +if __name__ == "__main__": + try: + test_token() + test_regex_rule() + test_text_parser() + test_fast_matcher() + benchmark_parser() + + except Exception as e: + print(f"\nTest failed: {e}") + import traceback + traceback.print_exc() + sys.exit(1) |
