411 lines
13 KiB
Python
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()
|