#!/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()