#!/usr/bin/env python3
"""
Test específico para demostrar casos donde el hash detecta cambios
que el método tradicional (tiempo + tamaño) podría perder.
"""
import os
import json
import hashlib
import tempfile
import time
def calculate_file_hash(filepath):
"""Calcula el hash SHA256 de un archivo."""
try:
sha256_hash = hashlib.sha256()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
sha256_hash.update(chunk)
return sha256_hash.hexdigest()
except Exception as e:
print(f"Error calculando hash de {filepath}: {e}")
return None
def test_subtle_changes():
"""Demuestra detección de cambios sutiles que mantienen el mismo tamaño."""
print("=== Test: Cambios Sutiles con Mismo Tamaño ===\n")
with tempfile.TemporaryDirectory() as temp_dir:
xml_file = os.path.join(temp_dir, "test.xml")
# Crear contenido original de exactamente el mismo tamaño
original = """
MotorControl_01
Active
"""
# Crear contenido modificado del mismo tamaño (cambiar solo algunos caracteres)
modified = """
MotorControl_02
Active
"""
# Verificar que tienen el mismo tamaño
assert len(original) == len(
modified
), "Los contenidos deben tener el mismo tamaño"
# Paso 1: Crear archivo original
with open(xml_file, "w", encoding="utf-8") as f:
f.write(original)
original_hash = calculate_file_hash(xml_file)
original_size = os.path.getsize(xml_file)
original_mtime = os.path.getmtime(xml_file)
print(f"1. Archivo original:")
print(f" Contenido: MotorControl_01")
print(f" Hash: {original_hash[:16]}...")
print(f" Tamaño: {original_size} bytes")
# Simular JSON con metadatos
json_data = {
"source_xml_hash": original_hash,
"source_xml_size": original_size,
"source_xml_mod_time": original_mtime,
}
# Paso 2: Esperar un poco y modificar archivo manteniendo tamaño
time.sleep(0.1) # Pequeña pausa
with open(xml_file, "w", encoding="utf-8") as f:
f.write(modified)
# Restaurar tiempo de modificación original
os.utime(xml_file, (original_mtime, original_mtime))
new_hash = calculate_file_hash(xml_file)
new_size = os.path.getsize(xml_file)
new_mtime = os.path.getmtime(xml_file)
print(f"\n2. Archivo modificado:")
print(f" Contenido: MotorControl_02")
print(f" Hash: {new_hash[:16]}...")
print(f" Tamaño: {new_size} bytes")
print(
f" Tiempo: {'Igual' if abs(new_mtime - original_mtime) < 0.001 else 'Diferente'}"
)
# Paso 3: Comparar métodos de detección
print(f"\n3. Detección de cambios:")
# Método tradicional
time_match = abs(new_mtime - json_data["source_xml_mod_time"]) < 0.001
size_match = new_size == json_data["source_xml_size"]
traditional_detects_change = not (time_match and size_match)
# Método por hash
hash_detects_change = new_hash != json_data["source_xml_hash"]
print(f" Método tradicional:")
print(f" Tiempo coincide: {time_match}")
print(f" Tamaño coincide: {size_match}")
print(f" Detecta cambio: {traditional_detects_change}")
print(f" Método por hash:")
print(f" Hash coincide: {new_hash == json_data['source_xml_hash']}")
print(f" Detecta cambio: {hash_detects_change}")
# Resultado
print(f"\n4. Resultado:")
if not traditional_detects_change and hash_detects_change:
print(f" 🎯 HASH DETECTÓ CAMBIO que método tradicional PERDIÓ")
print(f" ✅ Sin hash: archivo NO se regeneraría (ERROR)")
print(f" ✅ Con hash: archivo SÍ se regenerará (CORRECTO)")
elif traditional_detects_change and hash_detects_change:
print(f" ✅ Ambos métodos detectaron el cambio correctamente")
else:
print(
f" ℹ️ Resultado: tradicional={traditional_detects_change}, hash={hash_detects_change}"
)
return not traditional_detects_change and hash_detects_change
def test_same_size_different_content():
"""Test con contenidos completamente diferentes pero mismo tamaño."""
print("\n" + "=" * 50)
print("=== Test: Contenido Diferente, Mismo Tamaño ===\n")
with tempfile.TemporaryDirectory() as temp_dir:
xml_file = os.path.join(temp_dir, "test.xml")
# Contenidos de igual longitud pero diferentes
content1 = "- AAAAAAAAAA
" # 33 chars
content2 = "- BBBBBBBBBB
" # 33 chars
assert len(content1) == len(content2), "Deben tener igual tamaño"
# Crear archivo con contenido 1
with open(xml_file, "w", encoding="utf-8") as f:
f.write(content1)
hash1 = calculate_file_hash(xml_file)
size1 = os.path.getsize(xml_file)
mtime1 = os.path.getmtime(xml_file)
# Cambiar a contenido 2 manteniendo tiempo
time.sleep(0.1)
with open(xml_file, "w", encoding="utf-8") as f:
f.write(content2)
os.utime(xml_file, (mtime1, mtime1))
hash2 = calculate_file_hash(xml_file)
size2 = os.path.getsize(xml_file)
mtime2 = os.path.getmtime(xml_file)
print(f"Contenido 1: {content1[:20]}...")
print(f"Contenido 2: {content2[:20]}...")
print(f"Tamaños: {size1} = {size2} ({'✓' if size1 == size2 else '✗'})")
print(f"Tiempos: {'iguales' if abs(mtime2 - mtime1) < 0.001 else 'diferentes'}")
print(f"Hashes: {'iguales' if hash1 == hash2 else 'diferentes'}")
would_skip_traditional = (size1 == size2) and (abs(mtime2 - mtime1) < 0.001)
would_skip_hash = hash1 == hash2
print(
f"\nSin hash: {'SALTARÍA procesamiento' if would_skip_traditional else 'procesaría'}"
)
print(
f"Con hash: {'saltaría procesamiento' if would_skip_hash else 'PROCESARÍA'}"
)
if would_skip_traditional and not would_skip_hash:
print("🎯 El hash evita saltar un archivo que SÍ cambió")
return True
return False
if __name__ == "__main__":
print("Ejecutando tests de detección de cambios por hash...\n")
test1_result = test_subtle_changes()
test2_result = test_same_size_different_content()
print("\n" + "=" * 50)
print("=== RESUMEN ===")
print(
f"Test 1 (cambios sutiles): {'HASH ÚTIL' if test1_result else 'ambos métodos iguales'}"
)
print(
f"Test 2 (mismo tamaño): {'HASH ÚTIL' if test2_result else 'ambos métodos iguales'}"
)
if test1_result or test2_result:
print("\n✅ El hash SHA256 mejora la detección de cambios")
print("✅ Evita regeneraciones innecesarias Y perdida de cambios reales")
else:
print("\n✓ Ambos métodos funcionaron igual en estos tests")