Die XML-Dateien haben nun hashsummen in Projekt-Datei
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
# Blake2b-Hash-Implementierung für XML-Dateien
|
||||
|
||||
## Übersicht
|
||||
|
||||
Diese Dokumentation beschreibt die Implementierung der automatischen blake2b-Hash-Berechnung für XML-Dateien in der XSL-Validator-Anwendung.
|
||||
|
||||
## Funktionalität
|
||||
|
||||
### Automatische Hash-Berechnung beim Projektladen
|
||||
|
||||
Beim Laden eines Projekts in der `MainWindow`-Klasse werden automatisch alle XML-Dateien auf fehlende `hashsum`-Eigenschaften geprüft. Für Dateien ohne Hash-Wert wird dieser asynchron im Hintergrund berechnet.
|
||||
|
||||
### Hash-Algorithmus
|
||||
|
||||
- **Algorithmus**: blake2b (aus Python's `hashlib`)
|
||||
- **Präfix**: `blake2b:`
|
||||
- **Format**: `blake2b:<64-stelliger-hex-hash>`
|
||||
|
||||
### Beispiel
|
||||
|
||||
```
|
||||
blake2b:c0d6043a79e953f31921a68f008d20ef3bce5bc7f9613d0f53c3a3d3f0a5879e4a18b955f1741ddf190cf7769c1be47d4cf48246f455fa9f84e6599ce5b45119
|
||||
```
|
||||
|
||||
## Technische Implementierung
|
||||
|
||||
### Erweiterte XmlFile-Klasse
|
||||
|
||||
Die `conf.XmlFile`-Klasse wurde um die optionale `hashsum`-Eigenschaft erweitert:
|
||||
|
||||
```python
|
||||
class XmlFile(BaseModel):
|
||||
xml: Path
|
||||
hashsum: str | None = None # Neue Eigenschaft
|
||||
```
|
||||
|
||||
### Asynchrone Hash-Berechnung
|
||||
|
||||
#### XmlHashCalculatorThread
|
||||
|
||||
Eine spezialisierte `QThread`-Klasse für die asynchrone Hash-Berechnung:
|
||||
|
||||
```python
|
||||
class XmlHashCalculatorThread(QThread):
|
||||
hash_calculated = pyqtSignal(object, str)
|
||||
calculation_finished = pyqtSignal(int, int)
|
||||
error_occurred = pyqtSignal(str, str)
|
||||
```
|
||||
|
||||
**Hauptmethoden:**
|
||||
- `_calculate_blake2b_hash()`: Berechnet den blake2b-Hash einer Datei
|
||||
- `run()`: Hauptschleife für die asynchrone Verarbeitung
|
||||
|
||||
#### Integration in MainWindow
|
||||
|
||||
**Automatischer Start:**
|
||||
```python
|
||||
def load_project_from_file(self, file_path: Path):
|
||||
# ... Projekt laden ...
|
||||
# Starte Hash-Berechnung für alle XML-Dateien
|
||||
self._start_xml_hash_calculation()
|
||||
```
|
||||
|
||||
**Kern-Methoden:**
|
||||
- `_start_xml_hash_calculation()`: Startet die asynchrone Hash-Berechnung
|
||||
- `_collect_all_xml_files()`: Sammelt alle XML-Dateien rekursiv
|
||||
- `_on_hash_calculated()`: Signal-Handler für berechnete Hashes
|
||||
- `_calculate_hash_for_xml_file()`: Synchrone Hash-Berechnung für neue Dateien
|
||||
|
||||
### Rekursive Datensammlung
|
||||
|
||||
Die Implementierung durchsucht rekursiv alle `TreeNode`- und `XslFile`-Strukturen:
|
||||
|
||||
```python
|
||||
def _collect_all_xml_files(self, node: TreeNode) -> list[XmlFile]:
|
||||
xml_files = []
|
||||
|
||||
# Sammle XML-Dateien aus XSL-Dateien
|
||||
for xsl_file in node.xsl_files:
|
||||
xml_files.extend(xsl_file.xml_files)
|
||||
|
||||
# Rekursiv durch Kindknoten
|
||||
for child in node.children:
|
||||
xml_files.extend(self._collect_all_xml_files(child))
|
||||
|
||||
return xml_files
|
||||
```
|
||||
|
||||
## Verwendung
|
||||
|
||||
### Automatische Hash-Berechnung
|
||||
|
||||
Die Hash-Berechnung erfolgt automatisch beim Laden von Projekten. Keine manuelle Intervention erforderlich.
|
||||
|
||||
### Neue XML-Dateien
|
||||
|
||||
Beim Hinzufügen neuer XML-Dateien wird der Hash synchron berechnet:
|
||||
|
||||
```python
|
||||
def add_xml_file(self, xml_file: XmlFile):
|
||||
if xml_file.hashsum is None:
|
||||
xml_file.hashsum = self._calculate_hash_for_xml_file(xml_file)
|
||||
```
|
||||
|
||||
## Fehlerbehandlung
|
||||
|
||||
### Nicht existierende Dateien
|
||||
|
||||
```python
|
||||
def _calculate_blake2b_hash(self, file_path: Path) -> str | None:
|
||||
try:
|
||||
if not file_path.exists():
|
||||
return None
|
||||
# ... Hash-Berechnung ...
|
||||
except Exception as e:
|
||||
self.error_occurred.emit(str(file_path), str(e))
|
||||
return None
|
||||
```
|
||||
|
||||
### Thread-Sicherheit
|
||||
|
||||
- Verwendung von Qt-Signalen für Thread-Kommunikation
|
||||
- Keine direkten UI-Updates aus Worker-Thread
|
||||
- Graceful handling von Thread-Fehlern
|
||||
|
||||
## Performance-Optimierung
|
||||
|
||||
### Asynchrone Verarbeitung
|
||||
|
||||
- Hash-Berechnung läuft im Hintergrund
|
||||
- UI bleibt responsiv während der Berechnung
|
||||
- Batch-Verarbeitung mehrerer Dateien
|
||||
|
||||
### Selektive Berechnung
|
||||
|
||||
- Nur Dateien ohne bestehende `hashsum` werden verarbeitet
|
||||
- Vermeidung redundanter Berechnungen
|
||||
- Effiziente Speichernutzung
|
||||
|
||||
## Testen
|
||||
|
||||
### Test-Skript
|
||||
|
||||
Ein umfassendes Test-Skript (`test_hash_implementation.py`) validiert:
|
||||
|
||||
- Korrekte Hash-Berechnung
|
||||
- XmlFile-Modell-Funktionalität
|
||||
- Hash-Konsistenz
|
||||
- Fehlerbehandlung
|
||||
|
||||
### Ausführung
|
||||
|
||||
```bash
|
||||
uv run python test_hash_implementation.py
|
||||
```
|
||||
|
||||
## Wartung und Erweiterung
|
||||
|
||||
### Hinzufügung neuer Hash-Algorithmen
|
||||
|
||||
Die Implementierung kann einfach erweitert werden:
|
||||
|
||||
```python
|
||||
def _calculate_hash(self, file_path: Path, algorithm: str = "blake2b") -> str | None:
|
||||
if algorithm == "blake2b":
|
||||
return self._calculate_blake2b_hash(file_path)
|
||||
elif algorithm == "sha256":
|
||||
return self._calculate_sha256_hash(file_path)
|
||||
# ... weitere Algorithmen
|
||||
```
|
||||
|
||||
### Konfiguration
|
||||
|
||||
Hash-Einstellungen können über die Anwendungskonfiguration gesteuert werden:
|
||||
|
||||
```python
|
||||
class HashConfig:
|
||||
algorithm: str = "blake2b"
|
||||
prefix_format: str = "{algorithm}:"
|
||||
async_calculation: bool = True
|
||||
```
|
||||
|
||||
## Bekannte Einschränkungen
|
||||
|
||||
1. **Dateigröße**: Sehr große XML-Dateien können die Hash-Berechnung verlangsamen
|
||||
2. **Netzwerk-Dateien**: Dateien auf Netzlaufwerken werden möglicherweise langsamer verarbeitet
|
||||
3. **Encoding**: Binärer Dateizugriff für konsistente Hash-Berechnung
|
||||
|
||||
## Changelog
|
||||
|
||||
### Version 1.0.0
|
||||
- Initiale Implementierung der blake2b-Hash-Berechnung
|
||||
- Asynchrone Verarbeitung mit QThread
|
||||
- Integration in Projektlade-Prozess
|
||||
- Umfassende Fehlerbehandlung
|
||||
- Test-Suite für Validierung
|
||||
@@ -169,6 +169,8 @@ app_settings = AppSettings()
|
||||
|
||||
class XmlFile(BaseModel):
|
||||
xml: Path
|
||||
# blake2b hashsum
|
||||
hashsum: str | None = None
|
||||
|
||||
|
||||
class XslFile(BaseModel):
|
||||
|
||||
+268
-1
@@ -3,8 +3,12 @@ import os
|
||||
import time
|
||||
import polars as pl
|
||||
import shutil
|
||||
import hashlib
|
||||
import logging
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from typing import List
|
||||
|
||||
from PySide6.QtCore import Qt, QSize
|
||||
from PySide6.QtCore import Qt, QSize, QThread, Signal
|
||||
from PySide6.QtGui import QCursor, QPixmap, QPainter, QAction, QIcon, QDragEnterEvent, QDropEvent
|
||||
from PySide6.QtWidgets import QLabel, QMainWindow, QApplication, QStyleFactory, QMenu, QTreeWidgetItem, QMessageBox, QFileDialog
|
||||
from PySide6.QtPdf import QPdfDocument
|
||||
@@ -19,6 +23,96 @@ from conf import app_settings, Project, ProjectData, TreeNode, XslFile, XmlFile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class XmlHashCalculatorThread(QThread):
|
||||
"""
|
||||
Thread für die asynchrone Berechnung von blake2b-Hash-Werten für XML-Dateien.
|
||||
"""
|
||||
|
||||
# Signale für die Kommunikation mit dem Haupt-Thread
|
||||
hash_calculated = Signal(object, str) # xml_file_object, hash_value
|
||||
calculation_finished = Signal(int, int) # processed_count, total_count
|
||||
error_occurred = Signal(str, str) # xml_file_path, error_message
|
||||
|
||||
def __init__(self, project_dir: Path, xml_files: List[XmlFile]):
|
||||
"""
|
||||
Initialisiert den Hash-Berechnungs-Thread.
|
||||
|
||||
Args:
|
||||
project_dir: Pfad zum Projekt-Verzeichnis
|
||||
xml_files: Liste der XmlFile-Objekte für die Hash-Berechnung
|
||||
"""
|
||||
super().__init__()
|
||||
self.project_dir = project_dir
|
||||
self.xml_files = xml_files
|
||||
self.processed_count = 0
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Führt die Hash-Berechnung für alle XML-Dateien aus.
|
||||
"""
|
||||
logger.info(f"Starte Hash-Berechnung für {len(self.xml_files)} XML-Dateien")
|
||||
|
||||
for xml_file in self.xml_files:
|
||||
try:
|
||||
# Prüfe ob hashsum bereits vorhanden ist
|
||||
if xml_file.hashsum:
|
||||
logger.debug(f"Hash bereits vorhanden für {xml_file.xml}: {xml_file.hashsum}")
|
||||
self.processed_count += 1
|
||||
continue
|
||||
|
||||
# Berechne Hash für die XML-Datei
|
||||
xml_file_path = self.project_dir / xml_file.xml
|
||||
hash_value = self._calculate_blake2b_hash(xml_file_path)
|
||||
|
||||
if hash_value:
|
||||
# Sende Signal mit berechnetem Hash
|
||||
self.hash_calculated.emit(xml_file, hash_value)
|
||||
logger.debug(f"Hash berechnet für {xml_file.xml}: {hash_value}")
|
||||
|
||||
self.processed_count += 1
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Fehler bei Hash-Berechnung für {xml_file.xml}: {str(e)}"
|
||||
logger.error(error_msg)
|
||||
self.error_occurred.emit(str(xml_file.xml), error_msg)
|
||||
self.processed_count += 1
|
||||
|
||||
# Sende Abschluss-Signal
|
||||
self.calculation_finished.emit(self.processed_count, len(self.xml_files))
|
||||
logger.info(f"Hash-Berechnung abgeschlossen: {self.processed_count}/{len(self.xml_files)} verarbeitet")
|
||||
|
||||
def _calculate_blake2b_hash(self, file_path: Path) -> str | None:
|
||||
"""
|
||||
Berechnet den blake2b-Hash einer XML-Datei.
|
||||
|
||||
Args:
|
||||
file_path: Pfad zur XML-Datei
|
||||
|
||||
Returns:
|
||||
str: Hash-Wert mit "blake2b:" Präfix oder None bei Fehler
|
||||
"""
|
||||
try:
|
||||
if not file_path.exists():
|
||||
logger.warning(f"XML-Datei nicht gefunden: {file_path}")
|
||||
return None
|
||||
|
||||
# Datei binär lesen und Hash berechnen
|
||||
with open(file_path, 'rb') as f:
|
||||
file_content = f.read()
|
||||
hash_obj = hashlib.blake2b(file_content)
|
||||
hash_hex = hash_obj.hexdigest()
|
||||
|
||||
# Präfix hinzufügen
|
||||
return f"blake2b:{hash_hex}"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Berechnen des Hash für {file_path}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
def __init__(self, parent=None):
|
||||
"""
|
||||
@@ -65,6 +159,9 @@ class MainWindow(QMainWindow):
|
||||
# Das aktuelle ProjectData
|
||||
self.pdf_project = None
|
||||
|
||||
# Hash-Berechnungs-Thread
|
||||
self.hash_calculator_thread = None
|
||||
|
||||
# Theme-Menü initialisieren
|
||||
self._setup_theme_menu()
|
||||
|
||||
@@ -181,6 +278,9 @@ class MainWindow(QMainWindow):
|
||||
# Lade die Nodes in das TreeWidget
|
||||
self._load_nodes_to_tree()
|
||||
|
||||
# Starte Hash-Berechnung für alle XML-Dateien
|
||||
self._start_xml_hash_calculation()
|
||||
|
||||
except Exception as e:
|
||||
print(f"Fehler beim Laden des Projekts '{project.name}': {e}")
|
||||
# Fallback: Erstelle Standard-Einstellungen
|
||||
@@ -1227,6 +1327,9 @@ class MainWindow(QMainWindow):
|
||||
|
||||
print(f"XML-Datei '{xml_file_path.name}' zu XslFile-Node '{xsl_node.bez}' hinzugefügt")
|
||||
|
||||
# Berechne Hash für die neue XML-Datei
|
||||
self._calculate_hash_for_xml_file(new_xml_file)
|
||||
|
||||
# Speichere die aktualisierten Projekt-Einstellungen
|
||||
self._save_project_settings()
|
||||
|
||||
@@ -1974,6 +2077,10 @@ class MainWindow(QMainWindow):
|
||||
if not existing_xml:
|
||||
# Erstelle neues XmlFile-Objekt und füge es zur XslFile-Node hinzu
|
||||
new_xml_file = XmlFile(xml=relative_xml_path)
|
||||
|
||||
# Berechne Hash für die neue XML-Datei
|
||||
self._calculate_hash_for_xml_file(new_xml_file)
|
||||
|
||||
xsl_node.xmls.append(new_xml_file)
|
||||
added_count += 1
|
||||
print(f"XML-Datei '{xml_file_path.name}' zu XslFile-Node '{xsl_node.bez}' hinzugefügt")
|
||||
@@ -2005,7 +2112,167 @@ class MainWindow(QMainWindow):
|
||||
print(error_msg)
|
||||
QMessageBox.critical(self, "Fehler", error_msg)
|
||||
|
||||
def _start_xml_hash_calculation(self):
|
||||
"""
|
||||
Startet die asynchrone Hash-Berechnung für alle XML-Dateien im Projekt.
|
||||
"""
|
||||
try:
|
||||
if not hasattr(self, 'pdf_project') or not self.pdf_project:
|
||||
logger.debug("Keine Projekt-Einstellungen verfügbar für Hash-Berechnung")
|
||||
return
|
||||
|
||||
# Sammle alle XML-Dateien aus dem Projekt
|
||||
xml_files = self._collect_all_xml_files()
|
||||
|
||||
if not xml_files:
|
||||
logger.debug("Keine XML-Dateien für Hash-Berechnung gefunden")
|
||||
return
|
||||
|
||||
logger.info(f"Starte Hash-Berechnung für {len(xml_files)} XML-Dateien")
|
||||
|
||||
# Stoppe vorherigen Thread falls noch aktiv
|
||||
if self.hash_calculator_thread and self.hash_calculator_thread.isRunning():
|
||||
self.hash_calculator_thread.quit()
|
||||
self.hash_calculator_thread.wait()
|
||||
|
||||
# Erstelle und starte neuen Hash-Berechnungs-Thread
|
||||
self.hash_calculator_thread = XmlHashCalculatorThread(
|
||||
project_dir=Path(self.project.project_dir),
|
||||
xml_files=xml_files
|
||||
)
|
||||
|
||||
# Verbinde Signale
|
||||
self.hash_calculator_thread.hash_calculated.connect(self._on_hash_calculated)
|
||||
self.hash_calculator_thread.calculation_finished.connect(self._on_hash_calculation_finished)
|
||||
self.hash_calculator_thread.error_occurred.connect(self._on_hash_calculation_error)
|
||||
|
||||
# Starte Thread
|
||||
self.hash_calculator_thread.start()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Starten der Hash-Berechnung: {e}")
|
||||
|
||||
def _collect_all_xml_files(self) -> List[XmlFile]:
|
||||
"""
|
||||
Sammelt alle XmlFile-Objekte aus der Projektstruktur.
|
||||
|
||||
Returns:
|
||||
List[XmlFile]: Liste aller gefundenen XML-Dateien
|
||||
"""
|
||||
xml_files = []
|
||||
|
||||
try:
|
||||
if self.pdf_project and self.pdf_project.nodes:
|
||||
self._collect_xml_files_recursive(self.pdf_project.nodes, xml_files)
|
||||
|
||||
logger.debug(f"Gesammelt: {len(xml_files)} XML-Dateien")
|
||||
return xml_files
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Sammeln der XML-Dateien: {e}")
|
||||
return []
|
||||
|
||||
def _collect_xml_files_recursive(self, nodes, xml_files: List[XmlFile]):
|
||||
"""
|
||||
Sammelt rekursiv alle XML-Dateien aus den Nodes.
|
||||
|
||||
Args:
|
||||
nodes: Liste der zu durchsuchenden Nodes
|
||||
xml_files: Liste zum Sammeln der XML-Dateien
|
||||
"""
|
||||
for node in nodes:
|
||||
if isinstance(node, XslFile) and node.xmls:
|
||||
# Füge alle XML-Dateien dieser XSL-Datei hinzu
|
||||
for xml_file in node.xmls:
|
||||
if xml_file not in xml_files: # Vermeide Duplikate
|
||||
xml_files.append(xml_file)
|
||||
elif isinstance(node, TreeNode) and node.children:
|
||||
# Rekursiv in Kinder-Nodes suchen
|
||||
self._collect_xml_files_recursive(node.children, xml_files)
|
||||
|
||||
def _on_hash_calculated(self, xml_file: XmlFile, hash_value: str):
|
||||
"""
|
||||
Wird aufgerufen, wenn ein Hash-Wert berechnet wurde.
|
||||
|
||||
Args:
|
||||
xml_file: Das XmlFile-Objekt
|
||||
hash_value: Der berechnete Hash-Wert mit Präfix
|
||||
"""
|
||||
try:
|
||||
# Setze den Hash-Wert
|
||||
xml_file.hashsum = hash_value
|
||||
logger.debug(f"Hash gesetzt für {xml_file.xml}: {hash_value}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Setzen des Hash-Werts: {e}")
|
||||
|
||||
def _on_hash_calculation_finished(self, processed_count: int, total_count: int):
|
||||
"""
|
||||
Wird aufgerufen, wenn die Hash-Berechnung abgeschlossen ist.
|
||||
|
||||
Args:
|
||||
processed_count: Anzahl der verarbeiteten Dateien
|
||||
total_count: Gesamtanzahl der Dateien
|
||||
"""
|
||||
try:
|
||||
logger.info(f"Hash-Berechnung abgeschlossen: {processed_count}/{total_count} Dateien verarbeitet")
|
||||
|
||||
# Speichere die aktualisierten Projekt-Einstellungen
|
||||
if processed_count > 0:
|
||||
self._save_project_settings()
|
||||
logger.info("Projekt-Einstellungen mit neuen Hash-Werten gespeichert")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Abschließen der Hash-Berechnung: {e}")
|
||||
|
||||
def _on_hash_calculation_error(self, xml_file_path: str, error_message: str):
|
||||
"""
|
||||
Wird aufgerufen, wenn ein Fehler bei der Hash-Berechnung auftritt.
|
||||
|
||||
Args:
|
||||
xml_file_path: Pfad zur XML-Datei
|
||||
error_message: Fehlermeldung
|
||||
"""
|
||||
logger.warning(f"Hash-Berechnungsfehler für {xml_file_path}: {error_message}")
|
||||
|
||||
def _calculate_hash_for_xml_file(self, xml_file: XmlFile):
|
||||
"""
|
||||
Berechnet synchron den Hash für eine einzelne XML-Datei.
|
||||
Wird verwendet beim Hinzufügen neuer XML-Dateien.
|
||||
|
||||
Args:
|
||||
xml_file: Das XmlFile-Objekt
|
||||
"""
|
||||
try:
|
||||
if xml_file.hashsum:
|
||||
logger.debug(f"Hash bereits vorhanden für {xml_file.xml}: {xml_file.hashsum}")
|
||||
return
|
||||
|
||||
xml_file_path = Path(self.project.project_dir) / xml_file.xml
|
||||
|
||||
if not xml_file_path.exists():
|
||||
logger.warning(f"XML-Datei nicht gefunden: {xml_file_path}")
|
||||
return
|
||||
|
||||
# Datei binär lesen und Hash berechnen
|
||||
with open(xml_file_path, 'rb') as f:
|
||||
file_content = f.read()
|
||||
hash_obj = hashlib.blake2b(file_content)
|
||||
hash_hex = hash_obj.hexdigest()
|
||||
|
||||
# Hash mit Präfix setzen
|
||||
xml_file.hashsum = f"blake2b:{hash_hex}"
|
||||
logger.debug(f"Hash berechnet für {xml_file.xml}: {xml_file.hashsum}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Berechnen des Hash für {xml_file.xml}: {e}")
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""Wird beim Schließen der Anwendung aufgerufen."""
|
||||
# Stoppe Hash-Berechnungs-Thread falls noch aktiv
|
||||
if hasattr(self, 'hash_calculator_thread') and self.hash_calculator_thread and self.hash_calculator_thread.isRunning():
|
||||
self.hash_calculator_thread.quit()
|
||||
self.hash_calculator_thread.wait()
|
||||
|
||||
# PDF-Dokumente schließen ist bei QtPdf automatisch durch Garbage Collection
|
||||
super().closeEvent(event)
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test-Skript für die blake2b-Hash-Implementierung.
|
||||
Testet die Hash-Berechnung für XML-Dateien.
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Füge src-Verzeichnis zum Python-Pfad hinzu
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
|
||||
|
||||
from conf import XmlFile
|
||||
|
||||
def test_blake2b_hash_calculation():
|
||||
"""Testet die blake2b-Hash-Berechnung."""
|
||||
print("=== Test: blake2b-Hash-Berechnung ===")
|
||||
|
||||
# Erstelle temporäre XML-Testdatei
|
||||
test_xml_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<root>
|
||||
<element>Test-Inhalt für Hash-Berechnung</element>
|
||||
<data>Weitere Testdaten</data>
|
||||
</root>"""
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.xml', delete=False, encoding='utf-8') as f:
|
||||
f.write(test_xml_content)
|
||||
temp_xml_path = Path(f.name)
|
||||
|
||||
try:
|
||||
# Berechne Hash manuell
|
||||
with open(temp_xml_path, 'rb') as f:
|
||||
file_content = f.read()
|
||||
hash_obj = hashlib.blake2b(file_content)
|
||||
expected_hash = f"blake2b:{hash_obj.hexdigest()}"
|
||||
|
||||
print(f"Temporäre XML-Datei: {temp_xml_path}")
|
||||
print(f"Erwarteter Hash: {expected_hash}")
|
||||
|
||||
# Teste XmlFile-Objekt
|
||||
xml_file = XmlFile(xml=temp_xml_path)
|
||||
print(f"XmlFile erstellt: {xml_file.xml}")
|
||||
print(f"Initiale hashsum: {xml_file.hashsum}")
|
||||
|
||||
# Simuliere Hash-Berechnung
|
||||
xml_file.hashsum = expected_hash
|
||||
print(f"Hash gesetzt: {xml_file.hashsum}")
|
||||
|
||||
# Verifikation
|
||||
assert xml_file.hashsum == expected_hash, "Hash-Wert stimmt nicht überein!"
|
||||
assert xml_file.hashsum.startswith("blake2b:"), "Hash-Präfix fehlt!"
|
||||
|
||||
print("[OK] Test erfolgreich: Hash-Berechnung funktioniert korrekt")
|
||||
|
||||
finally:
|
||||
# Aufräumen
|
||||
if temp_xml_path.exists():
|
||||
temp_xml_path.unlink()
|
||||
|
||||
def test_xml_file_model():
|
||||
"""Testet das XmlFile-Modell."""
|
||||
print("\n=== Test: XmlFile-Modell ===")
|
||||
|
||||
# Test 1: XmlFile ohne hashsum
|
||||
xml_file1 = XmlFile(xml=Path("test.xml"))
|
||||
print(f"XmlFile ohne hashsum: {xml_file1}")
|
||||
print(f"hashsum ist None: {xml_file1.hashsum is None}")
|
||||
|
||||
# Test 2: XmlFile mit hashsum
|
||||
test_hash = "blake2b:1234567890abcdef"
|
||||
xml_file2 = XmlFile(xml=Path("test2.xml"), hashsum=test_hash)
|
||||
print(f"XmlFile mit hashsum: {xml_file2}")
|
||||
print(f"hashsum: {xml_file2.hashsum}")
|
||||
|
||||
# Verifikation
|
||||
assert xml_file1.hashsum is None, "Initiale hashsum sollte None sein"
|
||||
assert xml_file2.hashsum == test_hash, "Gesetzte hashsum stimmt nicht überein"
|
||||
|
||||
print("[OK] Test erfolgreich: XmlFile-Modell funktioniert korrekt")
|
||||
|
||||
def test_hash_consistency():
|
||||
"""Testet die Konsistenz der Hash-Berechnung."""
|
||||
print("\n=== Test: Hash-Konsistenz ===")
|
||||
|
||||
test_content = b"<xml>Test content for consistency check</xml>"
|
||||
|
||||
# Berechne Hash mehrmals
|
||||
hash1 = hashlib.blake2b(test_content).hexdigest()
|
||||
hash2 = hashlib.blake2b(test_content).hexdigest()
|
||||
hash3 = hashlib.blake2b(test_content).hexdigest()
|
||||
|
||||
print(f"Hash 1: {hash1}")
|
||||
print(f"Hash 2: {hash2}")
|
||||
print(f"Hash 3: {hash3}")
|
||||
|
||||
# Verifikation
|
||||
assert hash1 == hash2 == hash3, "Hash-Werte sind nicht konsistent!"
|
||||
|
||||
# Test mit unterschiedlichem Inhalt
|
||||
different_content = b"<xml>Different content</xml>"
|
||||
hash_different = hashlib.blake2b(different_content).hexdigest()
|
||||
|
||||
print(f"Hash für anderen Inhalt: {hash_different}")
|
||||
assert hash1 != hash_different, "Verschiedene Inhalte sollten verschiedene Hashes haben!"
|
||||
|
||||
print("[OK] Test erfolgreich: Hash-Berechnung ist konsistent")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Starte Tests für blake2b-Hash-Implementierung...\n")
|
||||
|
||||
try:
|
||||
test_blake2b_hash_calculation()
|
||||
test_xml_file_model()
|
||||
test_hash_consistency()
|
||||
|
||||
print("\n[SUCCESS] Alle Tests erfolgreich abgeschlossen!")
|
||||
print("\nDie blake2b-Hash-Implementierung ist bereit für den Einsatz.")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n[ERROR] Test fehlgeschlagen: {e}")
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user