Sikuwa first commit
Some checks are pending
CI / Test (Python 3.10 on macos-latest) (push) Waiting to run
CI / Test (Python 3.11 on macos-latest) (push) Waiting to run
CI / Test (Python 3.12 on macos-latest) (push) Waiting to run
CI / Test (Python 3.8 on macos-latest) (push) Waiting to run
CI / Test (Python 3.9 on macos-latest) (push) Waiting to run
CI / Test (Python 3.10 on ubuntu-latest) (push) Waiting to run
CI / Test (Python 3.11 on ubuntu-latest) (push) Waiting to run
CI / Test (Python 3.12 on ubuntu-latest) (push) Waiting to run
CI / Test (Python 3.8 on ubuntu-latest) (push) Waiting to run
CI / Test (Python 3.9 on ubuntu-latest) (push) Waiting to run
CI / Test (Python 3.10 on windows-latest) (push) Waiting to run
CI / Test (Python 3.11 on windows-latest) (push) Waiting to run
CI / Test (Python 3.12 on windows-latest) (push) Waiting to run
CI / Test (Python 3.8 on windows-latest) (push) Waiting to run
CI / Test (Python 3.9 on windows-latest) (push) Waiting to run
CI / Lint (push) Waiting to run
CI / Release (push) Blocked by required conditions
Documentation / Build Documentation (push) Waiting to run
Some checks are pending
CI / Test (Python 3.10 on macos-latest) (push) Waiting to run
CI / Test (Python 3.11 on macos-latest) (push) Waiting to run
CI / Test (Python 3.12 on macos-latest) (push) Waiting to run
CI / Test (Python 3.8 on macos-latest) (push) Waiting to run
CI / Test (Python 3.9 on macos-latest) (push) Waiting to run
CI / Test (Python 3.10 on ubuntu-latest) (push) Waiting to run
CI / Test (Python 3.11 on ubuntu-latest) (push) Waiting to run
CI / Test (Python 3.12 on ubuntu-latest) (push) Waiting to run
CI / Test (Python 3.8 on ubuntu-latest) (push) Waiting to run
CI / Test (Python 3.9 on ubuntu-latest) (push) Waiting to run
CI / Test (Python 3.10 on windows-latest) (push) Waiting to run
CI / Test (Python 3.11 on windows-latest) (push) Waiting to run
CI / Test (Python 3.12 on windows-latest) (push) Waiting to run
CI / Test (Python 3.8 on windows-latest) (push) Waiting to run
CI / Test (Python 3.9 on windows-latest) (push) Waiting to run
CI / Lint (push) Waiting to run
CI / Release (push) Blocked by required conditions
Documentation / Build Documentation (push) Waiting to run
This commit is contained in:
2
incremental/tests/__init__.py
Normal file
2
incremental/tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# sikuwa/incremental/tests/__init__.py
|
||||
"""减量编译测试包"""
|
||||
360
incremental/tests/test_incremental.py
Normal file
360
incremental/tests/test_incremental.py
Normal file
@@ -0,0 +1,360 @@
|
||||
# sikuwa/incremental/tests/test_incremental.py
|
||||
"""
|
||||
减量编译系统测试
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
# 添加父目录到路径
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
||||
|
||||
from incremental.core import (
|
||||
IncrementalCompiler,
|
||||
CompilationUnit,
|
||||
Snapshot,
|
||||
ChangeDetector,
|
||||
CompilationCache,
|
||||
UnitType,
|
||||
UnitState
|
||||
)
|
||||
from incremental.analyzer import PythonAnalyzer, BlockType
|
||||
|
||||
|
||||
class TestPythonAnalyzer(unittest.TestCase):
|
||||
"""测试 Python 分析器"""
|
||||
|
||||
def setUp(self):
|
||||
self.analyzer = PythonAnalyzer()
|
||||
|
||||
def test_analyze_function(self):
|
||||
"""测试函数分析"""
|
||||
code = '''
|
||||
def hello(name):
|
||||
"""Say hello"""
|
||||
print(f"Hello, {name}!")
|
||||
'''
|
||||
blocks = self.analyzer.analyze(code, "test.py")
|
||||
|
||||
# 应该检测到函数块
|
||||
func_blocks = [b for b in blocks if b.type == BlockType.FUNCTION]
|
||||
self.assertEqual(len(func_blocks), 1)
|
||||
self.assertEqual(func_blocks[0].name, "hello")
|
||||
|
||||
def test_analyze_class(self):
|
||||
"""测试类分析"""
|
||||
code = '''
|
||||
class MyClass:
|
||||
def __init__(self):
|
||||
self.value = 0
|
||||
|
||||
def increment(self):
|
||||
self.value += 1
|
||||
'''
|
||||
blocks = self.analyzer.analyze(code, "test.py")
|
||||
|
||||
# 应该检测到类块
|
||||
class_blocks = [b for b in blocks if b.type == BlockType.CLASS]
|
||||
self.assertEqual(len(class_blocks), 1)
|
||||
self.assertEqual(class_blocks[0].name, "MyClass")
|
||||
|
||||
def test_analyze_import(self):
|
||||
"""测试导入分析"""
|
||||
code = '''
|
||||
import os
|
||||
from sys import path
|
||||
from pathlib import Path
|
||||
'''
|
||||
blocks = self.analyzer.analyze(code, "test.py")
|
||||
|
||||
import_blocks = [b for b in blocks if b.type == BlockType.IMPORT]
|
||||
self.assertEqual(len(import_blocks), 3)
|
||||
|
||||
def test_dependency_extraction(self):
|
||||
"""测试依赖提取"""
|
||||
code = '''
|
||||
def outer():
|
||||
def inner():
|
||||
return x
|
||||
return inner()
|
||||
'''
|
||||
blocks = self.analyzer.analyze(code, "test.py")
|
||||
func_blocks = [b for b in blocks if b.type == BlockType.FUNCTION]
|
||||
|
||||
# outer 函数应该依赖 x
|
||||
self.assertEqual(len(func_blocks), 1)
|
||||
self.assertIn('x', func_blocks[0].references)
|
||||
|
||||
|
||||
class TestChangeDetector(unittest.TestCase):
|
||||
"""测试变更检测器"""
|
||||
|
||||
def setUp(self):
|
||||
self.detector = ChangeDetector()
|
||||
|
||||
def test_detect_addition(self):
|
||||
"""测试新增检测"""
|
||||
old = Snapshot()
|
||||
old.units = {}
|
||||
|
||||
new_unit = CompilationUnit(
|
||||
id="u1", content="def foo(): pass",
|
||||
start_line=1, end_line=1, file_path="test.py"
|
||||
)
|
||||
new_unit.compute_hash()
|
||||
|
||||
new = Snapshot()
|
||||
new.units = {"u1": new_unit}
|
||||
|
||||
changes = self.detector.detect_changes(old, new)
|
||||
|
||||
self.assertEqual(len(changes), 1)
|
||||
self.assertEqual(changes[0].unit_id, "u1")
|
||||
self.assertEqual(changes[0].change_type, UnitState.ADDED)
|
||||
|
||||
def test_detect_modification(self):
|
||||
"""测试修改检测"""
|
||||
old_unit = CompilationUnit(
|
||||
id="u1", content="def foo(): pass",
|
||||
start_line=1, end_line=1, file_path="test.py"
|
||||
)
|
||||
old_unit.compute_hash()
|
||||
|
||||
old = Snapshot()
|
||||
old.units = {"u1": old_unit}
|
||||
|
||||
new_unit = CompilationUnit(
|
||||
id="u1", content="def foo(): return 1",
|
||||
start_line=1, end_line=1, file_path="test.py"
|
||||
)
|
||||
new_unit.compute_hash()
|
||||
|
||||
new = Snapshot()
|
||||
new.units = {"u1": new_unit}
|
||||
|
||||
changes = self.detector.detect_changes(old, new)
|
||||
|
||||
self.assertEqual(len(changes), 1)
|
||||
self.assertEqual(changes[0].unit_id, "u1")
|
||||
self.assertEqual(changes[0].change_type, UnitState.MODIFIED)
|
||||
|
||||
def test_detect_deletion(self):
|
||||
"""测试删除检测"""
|
||||
old_unit = CompilationUnit(
|
||||
id="u1", content="def foo(): pass",
|
||||
start_line=1, end_line=1, file_path="test.py"
|
||||
)
|
||||
old_unit.compute_hash()
|
||||
|
||||
old = Snapshot()
|
||||
old.units = {"u1": old_unit}
|
||||
|
||||
new = Snapshot()
|
||||
new.units = {}
|
||||
|
||||
changes = self.detector.detect_changes(old, new)
|
||||
|
||||
self.assertEqual(len(changes), 1)
|
||||
self.assertEqual(changes[0].change_type, UnitState.DELETED)
|
||||
|
||||
|
||||
class TestCompilationCache(unittest.TestCase):
|
||||
"""测试编译缓存"""
|
||||
|
||||
def setUp(self):
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.cache = CompilationCache(self.temp_dir)
|
||||
|
||||
def tearDown(self):
|
||||
import shutil
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
def test_put_get(self):
|
||||
"""测试缓存存取"""
|
||||
self.cache.put("key1", "value1", "hash1")
|
||||
result = self.cache.get("key1")
|
||||
self.assertEqual(result, "value1")
|
||||
|
||||
def test_get_nonexistent(self):
|
||||
"""测试获取不存在的键"""
|
||||
result = self.cache.get("nonexistent")
|
||||
self.assertEqual(result, "") # 返回空字符串
|
||||
|
||||
def test_persistence(self):
|
||||
"""测试持久化"""
|
||||
self.cache.put("key1", "value1", "hash1")
|
||||
self.cache.save()
|
||||
|
||||
# 创建新缓存实例
|
||||
cache2 = CompilationCache(self.temp_dir)
|
||||
result = cache2.get("key1")
|
||||
self.assertEqual(result, "value1")
|
||||
|
||||
|
||||
class TestIncrementalCompiler(unittest.TestCase):
|
||||
"""测试减量编译器"""
|
||||
|
||||
def setUp(self):
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.compiler = IncrementalCompiler(self.temp_dir)
|
||||
|
||||
# 设置简单的编译器(返回大写代码)
|
||||
self.compiler.set_compiler(lambda unit: unit.content.upper())
|
||||
|
||||
def tearDown(self):
|
||||
import shutil
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
def test_initial_compile(self):
|
||||
"""测试初始编译"""
|
||||
code = '''
|
||||
def hello():
|
||||
print("Hello")
|
||||
|
||||
def world():
|
||||
print("World")
|
||||
'''
|
||||
self.compiler.analyze_source("test.py", code)
|
||||
changes = self.compiler.update_source("test.py", code)
|
||||
|
||||
# 首次编译,所有单元都应该是新的
|
||||
self.assertGreater(len(changes), 0)
|
||||
|
||||
# 编译
|
||||
outputs = self.compiler.compile_all_pending()
|
||||
self.assertGreater(len(outputs), 0)
|
||||
|
||||
def test_incremental_compile(self):
|
||||
"""测试增量编译"""
|
||||
# 初始代码
|
||||
code1 = '''
|
||||
def hello():
|
||||
print("Hello")
|
||||
|
||||
def world():
|
||||
print("World")
|
||||
'''
|
||||
self.compiler.analyze_source("test.py", code1)
|
||||
self.compiler.update_source("test.py", code1)
|
||||
outputs1 = self.compiler.compile_all_pending()
|
||||
|
||||
# 修改一个函数
|
||||
code2 = '''
|
||||
def hello():
|
||||
print("Hello Modified")
|
||||
|
||||
def world():
|
||||
print("World")
|
||||
'''
|
||||
changes = self.compiler.update_source("test.py", code2)
|
||||
|
||||
# 应该有变更
|
||||
self.assertGreater(len(changes), 0)
|
||||
|
||||
# 再次编译
|
||||
outputs2 = self.compiler.compile_all_pending()
|
||||
|
||||
# 验证有输出
|
||||
self.assertGreater(len(outputs1) + len(outputs2), 0)
|
||||
|
||||
def test_dependency_propagation(self):
|
||||
"""测试依赖传播"""
|
||||
code = '''
|
||||
x = 10
|
||||
|
||||
def get_x():
|
||||
return x
|
||||
|
||||
def double_x():
|
||||
return get_x() * 2
|
||||
'''
|
||||
self.compiler.analyze_source("test.py", code)
|
||||
self.compiler.update_source("test.py", code)
|
||||
self.compiler.compile_all_pending()
|
||||
|
||||
# 修改 x 的值
|
||||
code2 = '''
|
||||
x = 20
|
||||
|
||||
def get_x():
|
||||
return x
|
||||
|
||||
def double_x():
|
||||
return get_x() * 2
|
||||
'''
|
||||
changes = self.compiler.update_source("test.py", code2)
|
||||
|
||||
# 应该检测到变更(x 变了,依赖它的也应该被标记)
|
||||
self.assertGreater(len(changes), 0)
|
||||
|
||||
def test_combined_output(self):
|
||||
"""测试合并输出"""
|
||||
code = '''
|
||||
import os
|
||||
|
||||
def hello():
|
||||
print("Hello")
|
||||
|
||||
def world():
|
||||
print("World")
|
||||
'''
|
||||
self.compiler.analyze_source("test.py", code)
|
||||
self.compiler.update_source("test.py", code)
|
||||
self.compiler.compile_all_pending()
|
||||
|
||||
combined = self.compiler.get_combined_output("test.py")
|
||||
|
||||
# 合并输出应该包含所有编译产物
|
||||
self.assertGreater(len(combined), 0)
|
||||
|
||||
|
||||
class TestBlockBoundary(unittest.TestCase):
|
||||
"""测试边界触发器"""
|
||||
|
||||
def setUp(self):
|
||||
self.analyzer = PythonAnalyzer()
|
||||
|
||||
def test_class_contains_methods(self):
|
||||
"""测试类包含其方法"""
|
||||
code = '''
|
||||
class MyClass:
|
||||
def method1(self):
|
||||
pass
|
||||
|
||||
def method2(self):
|
||||
pass
|
||||
'''
|
||||
blocks = self.analyzer.analyze(code, "test.py")
|
||||
|
||||
class_blocks = [b for b in blocks if b.type == BlockType.CLASS]
|
||||
self.assertEqual(len(class_blocks), 1)
|
||||
|
||||
# 类块应该包含整个类定义
|
||||
class_block = class_blocks[0]
|
||||
self.assertIn("method1", class_block.content)
|
||||
self.assertIn("method2", class_block.content)
|
||||
|
||||
|
||||
def run_tests():
|
||||
"""运行所有测试"""
|
||||
loader = unittest.TestLoader()
|
||||
suite = unittest.TestSuite()
|
||||
|
||||
suite.addTests(loader.loadTestsFromTestCase(TestPythonAnalyzer))
|
||||
suite.addTests(loader.loadTestsFromTestCase(TestChangeDetector))
|
||||
suite.addTests(loader.loadTestsFromTestCase(TestCompilationCache))
|
||||
suite.addTests(loader.loadTestsFromTestCase(TestIncrementalCompiler))
|
||||
suite.addTests(loader.loadTestsFromTestCase(TestBlockBoundary))
|
||||
|
||||
runner = unittest.TextTestRunner(verbosity=2)
|
||||
result = runner.run(suite)
|
||||
|
||||
return result.wasSuccessful()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
success = run_tests()
|
||||
sys.exit(0 if success else 1)
|
||||
Reference in New Issue
Block a user