Calc/test_suite.py

411 lines
13 KiB
Python

#!/usr/bin/env python3
"""
Suite de tests unitarios para Calculadora MAV - CAS Híbrido
"""
import unittest
import sys
import os
from pathlib import Path
# Agregar directorio actual al path para importar módulos
sys.path.insert(0, str(Path(__file__).parent))
import sympy
from bracket_parser import BracketParser, EquationDetector
from hybrid_base_types import (
HybridHex, HybridBin, HybridDec, HybridIP4, HybridChr,
Hex, Bin, Dec, IP4, Chr
)
from hybrid_evaluation_engine import HybridEvaluationEngine, EvaluationResult
class TestBracketParser(unittest.TestCase):
"""Tests para el bracket parser"""
def setUp(self):
self.parser = BracketParser()
def test_bracket_transformation(self):
"""Test transformación de sintaxis con corchetes"""
test_cases = [
("Hex[FF]", 'Hex("FF")'),
("IP4[192.168.1.1/24]", 'IP4("192.168.1.1/24")'),
("Bin[1010]", 'Bin("1010")'),
("Dec[10.5]", 'Dec("10.5")'),
("Chr[A]", 'Chr("A")'),
]
for input_expr, expected in test_cases:
with self.subTest(input_expr=input_expr):
result, info = self.parser.parse_line(input_expr)
self.assertEqual(result, expected)
self.assertEqual(info, "bracket_transform")
def test_solve_shortcut(self):
"""Test transformación de atajo solve"""
test_cases = [
("x=?", "solve(x)"),
("variable_name=?", "solve(variable_name)"),
("a=?", "solve(a)"),
]
for input_expr, expected in test_cases:
with self.subTest(input_expr=input_expr):
result, info = self.parser.parse_line(input_expr)
self.assertEqual(result, expected)
self.assertEqual(info, "solve_shortcut")
def test_equation_detection(self):
"""Test detección de ecuaciones standalone"""
equation_cases = [
"x + 2 = 5",
"3*a + b = 10",
"x**2 == 4",
"y > 5",
]
for equation in equation_cases:
with self.subTest(equation=equation):
result, info = self.parser.parse_line(equation)
self.assertEqual(info, "equation")
self.assertTrue(result.startswith('_add_equation('))
def test_non_equation_cases(self):
"""Test casos que NO deben detectarse como ecuaciones"""
non_equation_cases = [
"result = solve(x + 2, x)", # Asignación Python
"2 + 3", # Expresión simple
"sin(pi/2)", # Función
]
for expr in non_equation_cases:
with self.subTest(expr=expr):
result, info = self.parser.parse_line(expr)
self.assertNotEqual(info, "equation")
def test_comments(self):
"""Test manejo de comentarios"""
comment_cases = [
"# Esto es un comentario",
" # Comentario con espacios",
"", # Línea vacía
]
for comment in comment_cases:
with self.subTest(comment=comment):
result, info = self.parser.parse_line(comment)
self.assertEqual(info, "comment")
class TestHybridBaseTypes(unittest.TestCase):
"""Tests para las clases base híbridas"""
def test_hybrid_hex(self):
"""Test clase HybridHex"""
# Creación desde string
h1 = Hex("FF")
self.assertEqual(h1.value, 255)
self.assertEqual(str(h1), "0xFF")
# Creación desde entero
h2 = Hex(255)
self.assertEqual(h2.value, 255)
self.assertEqual(str(h2), "0xFF")
# Verificar que es instancia de SymPy Basic
self.assertIsInstance(h1, sympy.Basic)
# Test conversión decimal
self.assertEqual(h1.__dec__(), 255)
def test_hybrid_bin(self):
"""Test clase HybridBin"""
# Creación desde string binario
b1 = Bin("1010")
self.assertEqual(b1.value, 10)
self.assertEqual(str(b1), "0b1010")
# Creación desde entero
b2 = Bin(10)
self.assertEqual(b2.value, 10)
self.assertEqual(str(b2), "0b1010")
# Verificar SymPy Basic
self.assertIsInstance(b1, sympy.Basic)
def test_hybrid_ip4(self):
"""Test clase HybridIP4"""
# IP con CIDR
ip1 = IP4("192.168.1.100/24")
self.assertEqual(ip1.ip_address, "192.168.1.100")
self.assertEqual(ip1.prefix, 24)
self.assertEqual(str(ip1), "192.168.1.100/24")
# IP sin máscara
ip2 = IP4("10.0.0.1")
self.assertEqual(ip2.ip_address, "10.0.0.1")
self.assertIsNone(ip2.prefix)
# Verificar SymPy Basic
self.assertIsInstance(ip1, sympy.Basic)
# Test métodos especializados
network = ip1.NetworkAddress()
self.assertEqual(str(network), "192.168.1.0/24")
broadcast = ip1.BroadcastAddress()
self.assertEqual(str(broadcast), "192.168.1.255/24")
nodes = ip1.Nodes()
self.assertEqual(nodes, 254) # 2^8 - 2
def test_hybrid_chr(self):
"""Test clase HybridChr"""
# Carácter único
c1 = Chr("A")
self.assertEqual(c1.value, 65)
self.assertEqual(str(c1), "A")
# String múltiple
c2 = Chr("Hello")
self.assertEqual(c2.value, [72, 101, 108, 108, 111])
self.assertEqual(str(c2), "Hello")
# Verificar SymPy Basic
self.assertIsInstance(c1, sympy.Basic)
def test_hybrid_dec(self):
"""Test clase HybridDec"""
# Desde string decimal
d1 = Dec("10.5")
self.assertEqual(d1.value, 10.5)
self.assertEqual(str(d1), "10.5")
# Desde entero
d2 = Dec(10)
self.assertEqual(d2.value, 10.0)
self.assertEqual(str(d2), "10")
# Verificar SymPy Basic
self.assertIsInstance(d1, sympy.Basic)
class TestHybridEvaluationEngine(unittest.TestCase):
"""Tests para el motor de evaluación híbrida"""
def setUp(self):
self.engine = HybridEvaluationEngine()
def test_basic_expressions(self):
"""Test expresiones básicas"""
test_cases = [
("2 + 3", 5),
("10 * 2", 20),
("15 / 3", 5),
]
for expr, expected in test_cases:
with self.subTest(expr=expr):
result = self.engine.evaluate_line(expr)
self.assertFalse(result.is_error)
self.assertEqual(result.result, expected)
def test_sympy_functions(self):
"""Test funciones de SymPy"""
# Test sin con pi/2
result = self.engine.evaluate_line("sin(pi/2)")
self.assertFalse(result.is_error)
self.assertEqual(result.result, 1)
# Test diferenciación
result = self.engine.evaluate_line("diff(x**2, x)")
self.assertFalse(result.is_error)
self.assertEqual(str(result.result), "2*x")
def test_bracket_syntax(self):
"""Test sintaxis con corchetes"""
# Test Hex
result = self.engine.evaluate_line("Hex[FF]")
self.assertFalse(result.is_error)
self.assertIsInstance(result.result, Hex)
self.assertEqual(result.result.value, 255)
# Test IP4
result = self.engine.evaluate_line("IP4[192.168.1.1/24]")
self.assertFalse(result.is_error)
self.assertIsInstance(result.result, IP4)
self.assertEqual(result.result.ip_address, "192.168.1.1")
def test_equation_handling(self):
"""Test manejo de ecuaciones"""
# Agregar ecuación
result = self.engine.evaluate_line("x + 2 = 5")
self.assertFalse(result.is_error)
self.assertEqual(result.result_type, "equation_added")
# Verificar que la ecuación se agregó
self.assertEqual(len(self.engine.equations), 1)
def test_variable_creation(self):
"""Test creación automática de variables"""
# Usar variable no definida
result = self.engine.evaluate_line("x + y")
self.assertFalse(result.is_error)
# Verificar que las variables se crearon como símbolos
self.assertIn("x", self.engine.symbol_table)
self.assertIn("y", self.engine.symbol_table)
self.assertIsInstance(self.engine.symbol_table["x"], sympy.Symbol)
def test_solve_shortcut(self):
"""Test atajo de solve"""
# Agregar ecuación
self.engine.evaluate_line("x + 2 = 5")
# Usar atajo solve
result = self.engine.evaluate_line("x=?")
self.assertFalse(result.is_error)
# Verificar que x se resolvió
self.assertIn("x", self.engine.symbol_table)
def test_error_handling(self):
"""Test manejo de errores"""
# División por cero
result = self.engine.evaluate_line("1/0")
self.assertTrue(result.is_error)
# Sintaxis inválida
result = self.engine.evaluate_line("2 +")
self.assertTrue(result.is_error)
def test_clear_operations(self):
"""Test operaciones de limpieza"""
# Agregar datos
self.engine.evaluate_line("x = 5")
self.engine.evaluate_line("y + 2 = 7")
# Verificar que hay datos
self.assertTrue(len(self.engine.symbol_table) > 0)
self.assertTrue(len(self.engine.equations) > 0)
# Limpiar variables
self.engine.clear_variables()
self.assertEqual(len(self.engine.symbol_table), 0)
# Limpiar ecuaciones
self.engine.clear_equations()
self.assertEqual(len(self.engine.equations), 0)
class TestIntegration(unittest.TestCase):
"""Tests de integración del sistema completo"""
def setUp(self):
self.engine = HybridEvaluationEngine()
def test_specialized_class_methods(self):
"""Test métodos de clases especializadas"""
# IP4 NetworkAddress
result = self.engine.evaluate_line("IP4[192.168.1.100/24].NetworkAddress[]")
self.assertFalse(result.is_error)
self.assertIsInstance(result.result, IP4)
self.assertEqual(str(result.result), "192.168.1.0/24")
def test_mixed_operations(self):
"""Test operaciones mixtas"""
# Hex + entero
result = self.engine.evaluate_line("Hex[FF] + 1")
self.assertFalse(result.is_error)
# El resultado depende de cómo implementemos las operaciones
def test_equation_solving_workflow(self):
"""Test flujo completo de resolución de ecuaciones"""
# Agregar ecuaciones
self.engine.evaluate_line("x + y = 10")
self.engine.evaluate_line("x - y = 2")
# Resolver sistema
try:
solutions = self.engine.solve_system()
self.assertIsInstance(solutions, dict)
# Verificar que x = 6, y = 4
self.assertEqual(solutions.get("x"), 6)
self.assertEqual(solutions.get("y"), 4)
except Exception as e:
self.fail(f"Solve system failed: {e}")
def test_sympy_integration(self):
"""Test integración con SymPy"""
# Diferenciación de expresión con variables
result = self.engine.evaluate_line("diff(x**3 + 2*x**2 + x, x)")
self.assertFalse(result.is_error)
expected = "3*x**2 + 4*x + 1"
self.assertEqual(str(result.result), expected)
# Integración
result = self.engine.evaluate_line("integrate(2*x, x)")
self.assertFalse(result.is_error)
self.assertEqual(str(result.result), "x**2")
def run_all_tests():
"""Ejecuta todos los tests"""
print("=== Ejecutando Suite de Tests ===\n")
# Crear suite de tests
loader = unittest.TestLoader()
suite = unittest.TestSuite()
# Agregar test classes
test_classes = [
TestBracketParser,
TestHybridBaseTypes,
TestHybridEvaluationEngine,
TestIntegration
]
for test_class in test_classes:
tests = loader.loadTestsFromTestCase(test_class)
suite.addTests(tests)
# Ejecutar tests
runner = unittest.TextTestRunner(verbosity=2)
result = runner.run(suite)
# Mostrar resumen
print(f"\n=== Resumen ===")
print(f"Tests ejecutados: {result.testsRun}")
print(f"Errores: {len(result.errors)}")
print(f"Fallos: {len(result.failures)}")
if result.errors:
print("\nErrores:")
for test, error in result.errors:
print(f" {test}: {error}")
if result.failures:
print("\nFallos:")
for test, failure in result.failures:
print(f" {test}: {failure}")
success = len(result.errors) == 0 and len(result.failures) == 0
print(f"\n{'✅ Todos los tests pasaron' if success else '❌ Algunos tests fallaron'}")
return success
def main():
"""Función principal"""
if len(sys.argv) > 1 and sys.argv[1] == "--verbose":
# Ejecutar tests individuales con más detalle
unittest.main(verbosity=2)
else:
# Ejecutar suite completa
success = run_all_tests()
sys.exit(0 if success else 1)
if __name__ == "__main__":
main()