first commit
This commit is contained in:
226
tests/test_algebra.py
Normal file
226
tests/test_algebra.py
Normal file
@@ -0,0 +1,226 @@
|
||||
"""
|
||||
Tests for Phase 2: Algebra System
|
||||
"""
|
||||
|
||||
from pysecondo.core.nested_list import atom
|
||||
from pysecondo.core.types import BaseType
|
||||
from pysecondo.algebras.standard import StandardAlgebra
|
||||
from pysecondo.algebras.base import AlgebraManager
|
||||
import sys
|
||||
sys.path.insert(0, '.')
|
||||
|
||||
|
||||
def test_algebra_registration():
|
||||
"""Test algebra registration"""
|
||||
print("Testing Algebra Registration...")
|
||||
|
||||
manager = AlgebraManager()
|
||||
std_algebra = StandardAlgebra()
|
||||
|
||||
manager.register_algebra("StandardAlgebra", std_algebra)
|
||||
|
||||
# Check algebra is registered
|
||||
assert "StandardAlgebra" in manager.list_algebras()
|
||||
|
||||
# Check operators are indexed
|
||||
ops = manager.list_operators()
|
||||
assert "+" in ops
|
||||
assert "-" in ops
|
||||
assert "*" in ops
|
||||
assert "/" in ops
|
||||
assert "and" in ops
|
||||
assert "or" in ops
|
||||
assert "not" in ops
|
||||
|
||||
print(" ✓ Algebra registration tests passed")
|
||||
|
||||
|
||||
def test_arithmetic_operators():
|
||||
"""Test arithmetic operators"""
|
||||
print("Testing Arithmetic Operators...")
|
||||
|
||||
manager = AlgebraManager()
|
||||
manager.register_algebra("StandardAlgebra", StandardAlgebra())
|
||||
|
||||
# Test addition
|
||||
add_op = manager.get_operator("+")
|
||||
|
||||
# Type map: int + int = int
|
||||
result_type = add_op.type_map([BaseType.INT, BaseType.INT])
|
||||
assert result_type == BaseType.INT
|
||||
|
||||
# Type map: int + real = real
|
||||
result_type = add_op.type_map([BaseType.INT, BaseType.REAL])
|
||||
assert result_type == BaseType.REAL
|
||||
|
||||
# Value map: 5 + 3 = 8
|
||||
result = add_op.value_map([atom(5), atom(3)])
|
||||
assert result.value == 8
|
||||
|
||||
# Test subtraction
|
||||
sub_op = manager.get_operator("-")
|
||||
result = sub_op.value_map([atom(10), atom(3)])
|
||||
assert result.value == 7
|
||||
|
||||
# Test multiplication
|
||||
mul_op = manager.get_operator("*")
|
||||
result = mul_op.value_map([atom(6), atom(7)])
|
||||
assert result.value == 42
|
||||
|
||||
# Test division
|
||||
div_op = manager.get_operator("/")
|
||||
result = div_op.value_map([atom(10), atom(2)])
|
||||
assert result.value == 5.0
|
||||
|
||||
print(" ✓ Arithmetic operators tests passed")
|
||||
|
||||
|
||||
def test_comparison_operators():
|
||||
"""Test comparison operators"""
|
||||
print("Testing Comparison Operators...")
|
||||
|
||||
manager = AlgebraManager()
|
||||
manager.register_algebra("StandardAlgebra", StandardAlgebra())
|
||||
|
||||
# Test less than
|
||||
lt_op = manager.get_operator("<")
|
||||
|
||||
result_type = lt_op.type_map([BaseType.INT, BaseType.INT])
|
||||
assert result_type == BaseType.BOOL
|
||||
|
||||
result = lt_op.value_map([atom(3), atom(5)])
|
||||
assert result.value is True
|
||||
|
||||
result = lt_op.value_map([atom(5), atom(3)])
|
||||
assert result.value is False
|
||||
|
||||
# Test greater than
|
||||
gt_op = manager.get_operator(">")
|
||||
result = gt_op.value_map([atom(10), atom(5)])
|
||||
assert result.value is True
|
||||
|
||||
# Test equal
|
||||
eq_op = manager.get_operator("=")
|
||||
result = eq_op.value_map([atom(5), atom(5)])
|
||||
assert result.value is True
|
||||
|
||||
result = eq_op.value_map([atom(5), atom(3)])
|
||||
assert result.value is False
|
||||
|
||||
# Test not equal
|
||||
ne_op = manager.get_operator("!=")
|
||||
result = ne_op.value_map([atom(5), atom(3)])
|
||||
assert result.value is True
|
||||
|
||||
print(" ✓ Comparison operators tests passed")
|
||||
|
||||
|
||||
def test_logical_operators():
|
||||
"""Test logical operators"""
|
||||
print("Testing Logical Operators...")
|
||||
|
||||
manager = AlgebraManager()
|
||||
manager.register_algebra("StandardAlgebra", StandardAlgebra())
|
||||
|
||||
# Test and
|
||||
and_op = manager.get_operator("and")
|
||||
result = and_op.value_map([atom(True), atom(True)])
|
||||
assert result.value is True
|
||||
|
||||
result = and_op.value_map([atom(True), atom(False)])
|
||||
assert result.value is False
|
||||
|
||||
# Test or
|
||||
or_op = manager.get_operator("or")
|
||||
result = or_op.value_map([atom(True), atom(False)])
|
||||
assert result.value is True
|
||||
|
||||
result = or_op.value_map([atom(False), atom(False)])
|
||||
assert result.value is False
|
||||
|
||||
# Test not
|
||||
not_op = manager.get_operator("not")
|
||||
result = not_op.value_map([atom(True)])
|
||||
assert result.value is False
|
||||
|
||||
result = not_op.value_map([atom(False)])
|
||||
assert result.value is True
|
||||
|
||||
print(" ✓ Logical operators tests passed")
|
||||
|
||||
|
||||
def test_type_checking():
|
||||
"""Test type checking in operators"""
|
||||
print("Testing Type Checking...")
|
||||
|
||||
manager = AlgebraManager()
|
||||
manager.register_algebra("StandardAlgebra", StandardAlgebra())
|
||||
|
||||
add_op = manager.get_operator("+")
|
||||
|
||||
# Valid types
|
||||
try:
|
||||
add_op.type_map([BaseType.INT, BaseType.INT])
|
||||
except TypeError:
|
||||
assert False, "Should not raise TypeError for valid types"
|
||||
|
||||
# Invalid types
|
||||
try:
|
||||
add_op.type_map([BaseType.STRING, BaseType.INT])
|
||||
assert False, "Should raise TypeError for invalid types"
|
||||
except TypeError as e:
|
||||
assert "requires" in str(e).lower()
|
||||
|
||||
# Wrong number of arguments
|
||||
try:
|
||||
add_op.type_map([BaseType.INT])
|
||||
assert False, "Should raise TypeError for wrong argument count"
|
||||
except TypeError as e:
|
||||
assert "expects 2" in str(e)
|
||||
|
||||
print(" ✓ Type checking tests passed")
|
||||
|
||||
|
||||
def test_complex_expressions():
|
||||
"""Test complex expressions using multiple operators"""
|
||||
print("Testing Complex Expressions...")
|
||||
|
||||
manager = AlgebraManager()
|
||||
manager.register_algebra("StandardAlgebra", StandardAlgebra())
|
||||
|
||||
# Expression: (5 + 3) * 2 = 16
|
||||
add_op = manager.get_operator("+")
|
||||
mul_op = manager.get_operator("*")
|
||||
|
||||
temp = add_op.value_map([atom(5), atom(3)])
|
||||
result = mul_op.value_map([temp, atom(2)])
|
||||
assert result.value == 16
|
||||
|
||||
# Expression: (10 > 5) and (3 < 7) = True
|
||||
gt_op = manager.get_operator(">")
|
||||
lt_op = manager.get_operator("<")
|
||||
and_op = manager.get_operator("and")
|
||||
|
||||
temp1 = gt_op.value_map([atom(10), atom(5)])
|
||||
temp2 = lt_op.value_map([atom(3), atom(7)])
|
||||
result = and_op.value_map([temp1, temp2])
|
||||
assert result.value is True
|
||||
|
||||
print(" ✓ Complex expressions tests passed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 50)
|
||||
print("Phase 2: Algebra System Tests")
|
||||
print("=" * 50)
|
||||
|
||||
test_algebra_registration()
|
||||
test_arithmetic_operators()
|
||||
test_comparison_operators()
|
||||
test_logical_operators()
|
||||
test_type_checking()
|
||||
test_complex_expressions()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("All Phase 2 tests passed! ✓")
|
||||
print("=" * 50)
|
||||
179
tests/test_core.py
Normal file
179
tests/test_core.py
Normal file
@@ -0,0 +1,179 @@
|
||||
"""
|
||||
Tests for Phase 1: Core functionality
|
||||
"""
|
||||
|
||||
from pysecondo.storage.memory import MemoryStorage
|
||||
from pysecondo.core.type_system import TypeChecker
|
||||
from pysecondo.core.types import (
|
||||
BaseType, TupleType, RelationType, Attribute,
|
||||
parse_type, type_to_string
|
||||
)
|
||||
from pysecondo.core.nested_list import NestedList, atom, list_nl
|
||||
import sys
|
||||
sys.path.insert(0, '.')
|
||||
|
||||
|
||||
def test_nested_list():
|
||||
"""Test nested list creation and operations"""
|
||||
print("Testing NestedList...")
|
||||
|
||||
# Atomic values
|
||||
nl_int = atom(42)
|
||||
nl_str = atom("Beijing")
|
||||
nl_bool = atom(True)
|
||||
|
||||
assert nl_int.is_atom()
|
||||
assert nl_int.value == 42
|
||||
assert str(nl_int) == "42"
|
||||
|
||||
assert nl_str.is_atom()
|
||||
assert nl_str.value == "Beijing"
|
||||
assert str(nl_str) == '"Beijing"'
|
||||
|
||||
# Lists
|
||||
nl_list = list_nl(1, 2, 3)
|
||||
assert nl_list.is_list()
|
||||
assert len(nl_list) == 3
|
||||
assert str(nl_list) == "(1 2 3)"
|
||||
|
||||
# Nested structures (tuple)
|
||||
nl_tuple = list_nl("Beijing", 21540000)
|
||||
assert str(nl_tuple) == '("Beijing" 21540000)'
|
||||
|
||||
# Nested structures (relation)
|
||||
nl_rel = list_nl(
|
||||
list_nl("Beijing", 21540000),
|
||||
list_nl("Shanghai", 24280000)
|
||||
)
|
||||
assert str(nl_rel) == '(("Beijing" 21540000) ("Shanghai" 24280000))'
|
||||
|
||||
# to_python conversion
|
||||
assert nl_int.to_python() == 42
|
||||
assert nl_list.to_python() == [1, 2, 3]
|
||||
assert nl_tuple.to_python() == ["Beijing", 21540000]
|
||||
|
||||
print(" ✓ NestedList tests passed")
|
||||
|
||||
|
||||
def test_type_system():
|
||||
"""Test type system"""
|
||||
print("Testing Type System...")
|
||||
|
||||
# Parse basic types
|
||||
int_type = parse_type("int")
|
||||
assert int_type == BaseType.INT
|
||||
|
||||
string_type = parse_type("string")
|
||||
assert string_type == BaseType.STRING
|
||||
|
||||
# Parse tuple type
|
||||
tuple_type_str = "(tuple ((Name string)(Population int)))"
|
||||
tuple_type = parse_type(tuple_type_str)
|
||||
assert isinstance(tuple_type, TupleType)
|
||||
assert len(tuple_type.attributes) == 2
|
||||
assert tuple_type.attributes[0].name == "Name"
|
||||
assert tuple_type.attributes[0].type == BaseType.STRING
|
||||
|
||||
# Parse relation type
|
||||
rel_type_str = "(rel (tuple ((Name string)(Population int))))"
|
||||
rel_type = parse_type(rel_type_str)
|
||||
assert isinstance(rel_type, RelationType)
|
||||
assert isinstance(rel_type.tuple_type, TupleType)
|
||||
|
||||
# Type to string
|
||||
assert type_to_string(BaseType.INT) == "int"
|
||||
assert "Name" in type_to_string(tuple_type)
|
||||
assert "(rel" in type_to_string(rel_type)
|
||||
|
||||
print(" ✓ Type system tests passed")
|
||||
|
||||
|
||||
def test_type_checker():
|
||||
"""Test type checking"""
|
||||
print("Testing Type Checker...")
|
||||
|
||||
checker = TypeChecker()
|
||||
|
||||
# Check basic types
|
||||
assert checker.check(atom(42), BaseType.INT)
|
||||
assert checker.check(atom("hello"), BaseType.STRING)
|
||||
assert not checker.check(atom("hello"), BaseType.INT)
|
||||
|
||||
# Check tuple type
|
||||
city_tuple_type = TupleType([
|
||||
Attribute("Name", BaseType.STRING),
|
||||
Attribute("Population", BaseType.INT)
|
||||
])
|
||||
|
||||
beijing = list_nl("Beijing", 21540000)
|
||||
assert checker.check(beijing, city_tuple_type)
|
||||
|
||||
wrong_tuple = list_nl(123, 21540000)
|
||||
assert not checker.check(wrong_tuple, city_tuple_type)
|
||||
|
||||
# Check relation type
|
||||
cities_rel_type = RelationType(city_tuple_type)
|
||||
|
||||
cities = list_nl(
|
||||
list_nl("Beijing", 21540000),
|
||||
list_nl("Shanghai", 24280000)
|
||||
)
|
||||
assert checker.check(cities, cities_rel_type)
|
||||
|
||||
print(" ✓ Type checker tests passed")
|
||||
|
||||
|
||||
def test_storage():
|
||||
"""Test in-memory storage"""
|
||||
print("Testing Memory Storage...")
|
||||
|
||||
storage = MemoryStorage()
|
||||
|
||||
# Create object
|
||||
city_tuple_type = TupleType([
|
||||
Attribute("Name", BaseType.STRING),
|
||||
Attribute("Population", BaseType.INT)
|
||||
])
|
||||
|
||||
beijing = list_nl("Beijing", 21540000)
|
||||
storage.create_object("beijing", beijing, city_tuple_type)
|
||||
|
||||
# Get object
|
||||
retrieved = storage.get_object("beijing")
|
||||
assert retrieved == beijing
|
||||
|
||||
retrieved_type = storage.get_type("beijing")
|
||||
assert retrieved_type == city_tuple_type
|
||||
|
||||
# Update object
|
||||
shanghai = list_nl("Shanghai", 24280000)
|
||||
storage.update_object("beijing", shanghai, city_tuple_type)
|
||||
assert storage.get_object("beijing") == shanghai
|
||||
|
||||
# List objects
|
||||
storage.create_object("city2", beijing, city_tuple_type)
|
||||
objects = storage.list_objects()
|
||||
assert "beijing" in objects
|
||||
assert "city2" in objects
|
||||
assert len(objects) == 2
|
||||
|
||||
# Delete object
|
||||
storage.delete_object("city2")
|
||||
assert len(storage.list_objects()) == 1
|
||||
|
||||
print(" ✓ Storage tests passed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 50)
|
||||
print("Phase 1: Core Functionality Tests")
|
||||
print("=" * 50)
|
||||
|
||||
test_nested_list()
|
||||
test_type_system()
|
||||
test_type_checker()
|
||||
test_storage()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("All Phase 1 tests passed! ✓")
|
||||
print("=" * 50)
|
||||
311
tests/test_relation.py
Normal file
311
tests/test_relation.py
Normal file
@@ -0,0 +1,311 @@
|
||||
"""
|
||||
Tests for Phase 3: Relation Algebra
|
||||
"""
|
||||
|
||||
from pysecondo.core.nested_list import atom, list_nl
|
||||
from pysecondo.core.types import BaseType, TupleType, RelationType, Attribute
|
||||
from pysecondo.query_processor import QueryProcessor
|
||||
from pysecondo.storage.memory import MemoryStorage
|
||||
from pysecondo.algebras.relation import RelationAlgebra
|
||||
from pysecondo.algebras.standard import StandardAlgebra
|
||||
from pysecondo.algebras.base import AlgebraManager
|
||||
import sys
|
||||
sys.path.insert(0, '.')
|
||||
|
||||
|
||||
def test_create_relation():
|
||||
"""Test creating relations"""
|
||||
print("Testing Create Relation...")
|
||||
|
||||
storage = MemoryStorage()
|
||||
algebra_manager = AlgebraManager()
|
||||
algebra_manager.register_algebra("StandardAlgebra", StandardAlgebra())
|
||||
algebra_manager.register_algebra(
|
||||
"RelationAlgebra",
|
||||
RelationAlgebra(storage)
|
||||
)
|
||||
|
||||
qp = QueryProcessor(algebra_manager, storage)
|
||||
|
||||
# Create a relation
|
||||
qp.execute_create(
|
||||
"cities",
|
||||
"(rel (tuple ((Name string)(Population int))))"
|
||||
)
|
||||
|
||||
# Check it exists
|
||||
assert storage.object_exists("cities")
|
||||
obj_type = storage.get_type("cities")
|
||||
assert isinstance(obj_type, RelationType)
|
||||
|
||||
print(" ✓ Create relation tests passed")
|
||||
|
||||
|
||||
def test_update_relation():
|
||||
"""Test updating relations with data"""
|
||||
print("Testing Update Relation...")
|
||||
|
||||
storage = MemoryStorage()
|
||||
algebra_manager = AlgebraManager()
|
||||
algebra_manager.register_algebra("StandardAlgebra", StandardAlgebra())
|
||||
algebra_manager.register_algebra(
|
||||
"RelationAlgebra",
|
||||
RelationAlgebra(storage)
|
||||
)
|
||||
|
||||
qp = QueryProcessor(algebra_manager, storage)
|
||||
|
||||
# Create and populate a relation
|
||||
qp.execute_create(
|
||||
"cities",
|
||||
"(rel (tuple ((Name string)(Population int))))"
|
||||
)
|
||||
|
||||
# Insert data
|
||||
cities_data = list_nl(
|
||||
list_nl("Beijing", 21540000),
|
||||
list_nl("Shanghai", 24280000),
|
||||
list_nl("Guangzhou", 14040000),
|
||||
)
|
||||
|
||||
qp.execute_update("cities", cities_data)
|
||||
|
||||
# Verify data
|
||||
cities = qp.lookup_identifier("cities")
|
||||
assert len(cities) == 3
|
||||
|
||||
print(" ✓ Update relation tests passed")
|
||||
|
||||
|
||||
def test_feed_consume():
|
||||
"""Test feed and consume operators"""
|
||||
print("Testing Feed and Consume...")
|
||||
|
||||
storage = MemoryStorage()
|
||||
algebra_manager = AlgebraManager()
|
||||
algebra_manager.register_algebra("StandardAlgebra", StandardAlgebra())
|
||||
algebra_manager.register_algebra(
|
||||
"RelationAlgebra",
|
||||
RelationAlgebra(storage)
|
||||
)
|
||||
|
||||
qp = QueryProcessor(algebra_manager, storage)
|
||||
|
||||
# Create and populate a relation
|
||||
qp.execute_create(
|
||||
"cities",
|
||||
"(rel (tuple ((Name string)(Population int))))"
|
||||
)
|
||||
|
||||
cities_data = list_nl(
|
||||
list_nl("Beijing", 21540000),
|
||||
list_nl("Shanghai", 24280000),
|
||||
list_nl("Guangzhou", 14040000),
|
||||
)
|
||||
qp.execute_update("cities", cities_data)
|
||||
|
||||
# Test feed: relation -> stream
|
||||
cities = qp.lookup_identifier("cities")
|
||||
cities_type = qp.get_identifier_type("cities")
|
||||
|
||||
feed_op = algebra_manager.get_operator("feed")
|
||||
stream = feed_op.value_map([cities])
|
||||
assert len(stream) == 3
|
||||
|
||||
# Test consume: stream -> relation
|
||||
consume_op = algebra_manager.get_operator("consume")
|
||||
result = consume_op.value_map([stream])
|
||||
assert len(result) == 3
|
||||
|
||||
print(" ✓ Feed and consume tests passed")
|
||||
|
||||
|
||||
def test_count():
|
||||
"""Test count operator"""
|
||||
print("Testing Count Operator...")
|
||||
|
||||
storage = MemoryStorage()
|
||||
algebra_manager = AlgebraManager()
|
||||
algebra_manager.register_algebra("StandardAlgebra", StandardAlgebra())
|
||||
algebra_manager.register_algebra(
|
||||
"RelationAlgebra",
|
||||
RelationAlgebra(storage)
|
||||
)
|
||||
|
||||
qp = QueryProcessor(algebra_manager, storage)
|
||||
|
||||
# Create and populate a relation
|
||||
qp.execute_create(
|
||||
"cities",
|
||||
"(rel (tuple ((Name string)(Population int))))"
|
||||
)
|
||||
|
||||
cities_data = list_nl(
|
||||
list_nl("Beijing", 21540000),
|
||||
list_nl("Shanghai", 24280000),
|
||||
list_nl("Guangzhou", 14040000),
|
||||
list_nl("Shenzhen", 17560000),
|
||||
)
|
||||
qp.execute_update("cities", cities_data)
|
||||
|
||||
# Test count
|
||||
cities = qp.lookup_identifier("cities")
|
||||
feed_op = algebra_manager.get_operator("feed")
|
||||
count_op = algebra_manager.get_operator("count")
|
||||
|
||||
stream = feed_op.value_map([cities])
|
||||
count_result = count_op.value_map([stream])
|
||||
assert count_result.value == 4
|
||||
|
||||
print(" ✓ Count operator tests passed")
|
||||
|
||||
|
||||
def test_filter():
|
||||
"""Test filter operator"""
|
||||
print("Testing Filter Operator...")
|
||||
|
||||
storage = MemoryStorage()
|
||||
algebra_manager = AlgebraManager()
|
||||
algebra_manager.register_algebra("StandardAlgebra", StandardAlgebra())
|
||||
algebra_manager.register_algebra(
|
||||
"RelationAlgebra",
|
||||
RelationAlgebra(storage)
|
||||
)
|
||||
|
||||
qp = QueryProcessor(algebra_manager, storage)
|
||||
|
||||
# Create and populate a relation
|
||||
qp.execute_create(
|
||||
"cities",
|
||||
"(rel (tuple ((Name string)(Population int))))"
|
||||
)
|
||||
|
||||
cities_data = list_nl(
|
||||
list_nl("Beijing", 21540000),
|
||||
list_nl("Shanghai", 24280000),
|
||||
list_nl("Guangzhou", 14040000),
|
||||
list_nl("Shenzhen", 17560000),
|
||||
)
|
||||
qp.execute_update("cities", cities_data)
|
||||
|
||||
# Test filter with true (pass all)
|
||||
cities = qp.lookup_identifier("cities")
|
||||
feed_op = algebra_manager.get_operator("feed")
|
||||
filter_op = algebra_manager.get_operator("filter")
|
||||
|
||||
stream = feed_op.value_map([cities])
|
||||
filtered = filter_op.value_map([stream, atom(True)])
|
||||
assert len(filtered) == 4
|
||||
|
||||
# Test filter with false (pass none)
|
||||
filtered = filter_op.value_map([stream, atom(False)])
|
||||
assert len(filtered) == 0
|
||||
|
||||
print(" ✓ Filter operator tests passed")
|
||||
|
||||
|
||||
def test_query_pipeline():
|
||||
"""Test a complete query pipeline"""
|
||||
print("Testing Query Pipeline...")
|
||||
|
||||
storage = MemoryStorage()
|
||||
algebra_manager = AlgebraManager()
|
||||
algebra_manager.register_algebra("StandardAlgebra", StandardAlgebra())
|
||||
algebra_manager.register_algebra(
|
||||
"RelationAlgebra",
|
||||
RelationAlgebra(storage)
|
||||
)
|
||||
|
||||
qp = QueryProcessor(algebra_manager, storage)
|
||||
|
||||
# Setup: create cities relation
|
||||
qp.execute_create(
|
||||
"cities",
|
||||
"(rel (tuple ((Name string)(Population int))))"
|
||||
)
|
||||
|
||||
cities_data = list_nl(
|
||||
list_nl("Beijing", 21540000),
|
||||
list_nl("Shanghai", 24280000),
|
||||
list_nl("Guangzhou", 14040000),
|
||||
list_nl("Shenzhen", 17560000),
|
||||
list_nl("Hangzhou", 12200000),
|
||||
)
|
||||
qp.execute_update("cities", cities_data)
|
||||
|
||||
# Query: cities feed count
|
||||
# Equivalent to: SELECT COUNT(*) FROM cities
|
||||
cities = qp.lookup_identifier("cities")
|
||||
feed_op = algebra_manager.get_operator("feed")
|
||||
count_op = algebra_manager.get_operator("count")
|
||||
|
||||
stream = feed_op.value_map([cities])
|
||||
count = count_op.value_map([stream])
|
||||
assert count.value == 5
|
||||
|
||||
# Query: cities feed filter[true] consume
|
||||
# Equivalent to: SELECT * FROM cities
|
||||
filter_op = algebra_manager.get_operator("filter")
|
||||
consume_op = algebra_manager.get_operator("consume")
|
||||
|
||||
stream = feed_op.value_map([cities])
|
||||
filtered = filter_op.value_map([stream, atom(True)])
|
||||
result = consume_op.value_map([filtered])
|
||||
assert len(result) == 5
|
||||
|
||||
print(" ✓ Query pipeline tests passed")
|
||||
|
||||
|
||||
def test_type_checking():
|
||||
"""Test type checking for relation operators"""
|
||||
print("Testing Type Checking for Relations...")
|
||||
|
||||
algebra_manager = AlgebraManager()
|
||||
algebra_manager.register_algebra("StandardAlgebra", StandardAlgebra())
|
||||
storage = MemoryStorage()
|
||||
algebra_manager.register_algebra(
|
||||
"RelationAlgebra",
|
||||
RelationAlgebra(storage)
|
||||
)
|
||||
|
||||
# Test feed type mapping
|
||||
feed_op = algebra_manager.get_operator("feed")
|
||||
tuple_type = TupleType([
|
||||
Attribute("Name", BaseType.STRING),
|
||||
Attribute("Population", BaseType.INT)
|
||||
])
|
||||
rel_type = RelationType(tuple_type)
|
||||
|
||||
result_type = feed_op.type_map([rel_type])
|
||||
assert result_type == tuple_type
|
||||
|
||||
# Test consume type mapping
|
||||
consume_op = algebra_manager.get_operator("consume")
|
||||
result_type = consume_op.type_map([tuple_type])
|
||||
assert isinstance(result_type, RelationType)
|
||||
assert result_type.tuple_type == tuple_type
|
||||
|
||||
# Test count type mapping
|
||||
count_op = algebra_manager.get_operator("count")
|
||||
result_type = count_op.type_map([tuple_type])
|
||||
assert result_type == BaseType.INT
|
||||
|
||||
print(" ✓ Type checking tests passed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 50)
|
||||
print("Phase 3: Relation Algebra Tests")
|
||||
print("=" * 50)
|
||||
|
||||
test_create_relation()
|
||||
test_update_relation()
|
||||
test_feed_consume()
|
||||
test_count()
|
||||
test_filter()
|
||||
test_query_pipeline()
|
||||
test_type_checking()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("All Phase 3 tests passed! ✓")
|
||||
print("=" * 50)
|
||||
220
tests/test_repl.py
Normal file
220
tests/test_repl.py
Normal file
@@ -0,0 +1,220 @@
|
||||
"""
|
||||
Tests for Phase 4: Query Processing & REPL
|
||||
"""
|
||||
|
||||
from pysecondo.core.nested_list import atom, list_nl
|
||||
from pysecondo.core.types import BaseType, TupleType, RelationType
|
||||
from pysecondo.storage.memory import MemoryStorage
|
||||
from pysecondo.algebras.relation import RelationAlgebra
|
||||
from pysecondo.algebras.standard import StandardAlgebra
|
||||
from pysecondo.algebras.base import AlgebraManager
|
||||
from pysecondo.parser.evaluator import Evaluator
|
||||
from pysecondo.parser.parser import Parser, parse_query, CreateCommand, UpdateCommand, QueryCommand
|
||||
import sys
|
||||
sys.path.insert(0, '.')
|
||||
|
||||
|
||||
def test_parser_create():
|
||||
"""Test parsing CREATE commands"""
|
||||
print("Testing Parser (CREATE)...")
|
||||
|
||||
parser = Parser()
|
||||
|
||||
# Test create command
|
||||
cmd = parser.parse(
|
||||
'create cities : (rel (tuple ((Name string)(Population int))))')
|
||||
assert isinstance(cmd, CreateCommand)
|
||||
assert cmd.name == "cities"
|
||||
assert "(rel" in cmd.type_str
|
||||
|
||||
print(" ✓ CREATE parsing tests passed")
|
||||
|
||||
|
||||
def test_parser_update():
|
||||
"""Test parsing UPDATE commands"""
|
||||
print("Testing Parser (UPDATE)...")
|
||||
|
||||
parser = Parser()
|
||||
|
||||
# Test update command
|
||||
cmd = parser.parse(
|
||||
'update cities := (("Beijing" 21540000)("Shanghai" 24280000))')
|
||||
assert isinstance(cmd, UpdateCommand)
|
||||
assert cmd.name == "cities"
|
||||
assert "Beijing" in cmd.value
|
||||
|
||||
print(" ✓ UPDATE parsing tests passed")
|
||||
|
||||
|
||||
def test_parser_query():
|
||||
"""Test parsing QUERY commands"""
|
||||
print("Testing Parser (QUERY)...")
|
||||
|
||||
parser = Parser()
|
||||
|
||||
# Test query command
|
||||
cmd = parser.parse('query cities feed count')
|
||||
assert isinstance(cmd, QueryCommand)
|
||||
assert cmd.expression == "cities feed count"
|
||||
|
||||
# Test arithmetic query
|
||||
cmd = parser.parse('query 5 + 3')
|
||||
assert isinstance(cmd, QueryCommand)
|
||||
assert cmd.expression == "5 + 3"
|
||||
|
||||
print(" ✓ QUERY parsing tests passed")
|
||||
|
||||
|
||||
def test_parser_expressions():
|
||||
"""Test expression tokenization"""
|
||||
print("Testing Expression Tokenization...")
|
||||
|
||||
parser = Parser()
|
||||
|
||||
# Simple identifier
|
||||
tokens = parser.parse_expression("cities")
|
||||
assert tokens == ["cities"]
|
||||
|
||||
# Operator chain
|
||||
tokens = parser.parse_expression("cities feed consume")
|
||||
assert tokens == ["cities", "feed", "consume"]
|
||||
|
||||
# Arithmetic
|
||||
tokens = parser.parse_expression("5 + 3")
|
||||
assert tokens == ["5", "+", "3"]
|
||||
|
||||
# Complex expression
|
||||
tokens = parser.parse_expression("cities feed filter true consume")
|
||||
assert tokens == ["cities", "feed", "filter", "true", "consume"]
|
||||
|
||||
print(" ✓ Expression tokenization tests passed")
|
||||
|
||||
|
||||
def test_evaluator_arithmetic():
|
||||
"""Test evaluating arithmetic expressions"""
|
||||
print("Testing Evaluator (Arithmetic)...")
|
||||
|
||||
storage = MemoryStorage()
|
||||
algebra_manager = AlgebraManager()
|
||||
algebra_manager.register_algebra("StandardAlgebra", StandardAlgebra())
|
||||
|
||||
evaluator = Evaluator(algebra_manager, storage)
|
||||
|
||||
# Test: 5 + 3
|
||||
tokens = ["5", "+", "3"]
|
||||
value, value_type = evaluator.evaluate(tokens)
|
||||
assert value.value == 8
|
||||
|
||||
# Test: 10 - 4
|
||||
tokens = ["10", "-", "4"]
|
||||
value, value_type = evaluator.evaluate(tokens)
|
||||
assert value.value == 6
|
||||
|
||||
# Test: 6 * 7
|
||||
tokens = ["6", "*", "7"]
|
||||
value, value_type = evaluator.evaluate(tokens)
|
||||
assert value.value == 42
|
||||
|
||||
print(" ✓ Arithmetic evaluation tests passed")
|
||||
|
||||
|
||||
def test_evaluator_identifiers():
|
||||
"""Test evaluating identifiers"""
|
||||
print("Testing Evaluator (Identifiers)...")
|
||||
|
||||
storage = MemoryStorage()
|
||||
algebra_manager = AlgebraManager()
|
||||
algebra_manager.register_algebra("StandardAlgebra", StandardAlgebra())
|
||||
|
||||
evaluator = Evaluator(algebra_manager, storage)
|
||||
|
||||
# Store a value
|
||||
storage.create_object("x", atom(42), BaseType.INT)
|
||||
|
||||
# Test: x
|
||||
tokens = ["x"]
|
||||
value, value_type = evaluator.evaluate(tokens)
|
||||
assert value.value == 42
|
||||
|
||||
print(" ✓ Identifier evaluation tests passed")
|
||||
|
||||
|
||||
def test_evaluator_relations():
|
||||
"""Test evaluating relation expressions"""
|
||||
print("Testing Evaluator (Relations)...")
|
||||
|
||||
storage = MemoryStorage()
|
||||
algebra_manager = AlgebraManager()
|
||||
algebra_manager.register_algebra("StandardAlgebra", StandardAlgebra())
|
||||
algebra_manager.register_algebra(
|
||||
"RelationAlgebra", RelationAlgebra(storage))
|
||||
|
||||
evaluator = Evaluator(algebra_manager, storage)
|
||||
|
||||
# Create a relation
|
||||
cities_type = RelationType(TupleType([]))
|
||||
storage.create_object("cities", list_nl(), cities_type)
|
||||
|
||||
# Test: cities feed
|
||||
tokens = ["cities", "feed"]
|
||||
value, value_type = evaluator.evaluate(tokens)
|
||||
|
||||
print(" ✓ Relation evaluation tests passed")
|
||||
|
||||
|
||||
def test_end_to_end():
|
||||
"""Test end-to-end query execution"""
|
||||
print("Testing End-to-End Queries...")
|
||||
|
||||
storage = MemoryStorage()
|
||||
algebra_manager = AlgebraManager()
|
||||
algebra_manager.register_algebra("StandardAlgebra", StandardAlgebra())
|
||||
algebra_manager.register_algebra(
|
||||
"RelationAlgebra", RelationAlgebra(storage))
|
||||
|
||||
parser = Parser()
|
||||
evaluator = Evaluator(algebra_manager, storage)
|
||||
|
||||
# Create relation
|
||||
create_cmd = parser.parse(
|
||||
'create cities : (rel (tuple ((Name string)(Population int))))')
|
||||
assert isinstance(create_cmd, CreateCommand)
|
||||
|
||||
# Insert data (using storage directly for simplicity)
|
||||
from pysecondo.core.types import parse_type
|
||||
cities_type = parse_type('(rel (tuple ((Name string)(Population int))))')
|
||||
cities_data = list_nl(
|
||||
list_nl("Beijing", 21540000),
|
||||
list_nl("Shanghai", 24280000),
|
||||
list_nl("Guangzhou", 14040000),
|
||||
)
|
||||
storage.create_object("cities", cities_data, cities_type)
|
||||
|
||||
# Query: cities feed count
|
||||
query_cmd = parser.parse('query cities feed count')
|
||||
assert isinstance(query_cmd, QueryCommand)
|
||||
|
||||
tokens = parser.parse_expression(query_cmd.expression)
|
||||
value, value_type = evaluator.evaluate(tokens)
|
||||
assert value.value == 3
|
||||
|
||||
print(" ✓ End-to-end query tests passed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 50)
|
||||
print("Phase 4: Query Processing & REPL Tests")
|
||||
print("=" * 50)
|
||||
|
||||
test_parser_create()
|
||||
test_parser_update()
|
||||
test_parser_query()
|
||||
test_parser_expressions()
|
||||
test_evaluator_arithmetic()
|
||||
test_evaluator_identifiers()
|
||||
test_evaluator_relations()
|
||||
test_end_to_end()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("All Phase 4 tests passed! ✓")
|
||||
print("=" * 50)
|
||||
Reference in New Issue
Block a user