test: aggiunge suite completa — unit, integration ed e2e (42 test)
- pytest.ini: configura testpaths, marker slow, output verboso - tests/conftest.py: fixture condivise (device, small_data, pinn_model) - tests/test_config.py: sanità parametri fisici e numerici, CFL, _pde_scale - tests/test_model.py: HeatPINN.forward e heat_pinn_loss (shape, finiti, zero-weight analytici per IC e BC, scaling dei pesi) - tests/test_engine_data.py: set_seed, _get_device, prepare_data (shape, bounds, device consistency, determinismo) - tests/test_integration_pinn.py: pipeline dati→modello→loss→backward - tests/test_e2e.py: FDM completo, visualizer FDM/PINN con tmp_path, training breve (2 test @slow) - requirements.txt: aggiunge pytest>=7.0.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import pytest
|
||||
import numpy as np
|
||||
import torch
|
||||
import config
|
||||
|
||||
|
||||
# ── FDM ───────────────────────────────────────────────────────────────────────
|
||||
|
||||
def test_fdm_full_run():
|
||||
"""Il solver FDM produce un campo di temperatura fisicamente corretto."""
|
||||
from fdm.solver import solve
|
||||
T, x, t = solve()
|
||||
|
||||
assert T.shape == (config.NX, config.NT)
|
||||
assert np.isfinite(T).all()
|
||||
assert T[:, -1].mean() > config.T0 # la sorgente ha scaldato il dominio
|
||||
assert T.max() > config.T0 # picco sopra la IC
|
||||
assert T.min() >= config.T0 - 1e-6 # nessun raffreddamento sotto T0 (no sorgenti fredde)
|
||||
|
||||
|
||||
def test_fdm_visualizer_creates_html(tmp_path, monkeypatch):
|
||||
"""Il visualizer FDM scrive almeno un file HTML senza errori."""
|
||||
import fdm.visualizer as fdm_vis
|
||||
monkeypatch.setattr(fdm_vis, 'BASE_DIR', str(tmp_path))
|
||||
|
||||
from fdm.solver import solve
|
||||
T, x, t = solve()
|
||||
fdm_vis.visualize_fdm(T, x, t)
|
||||
|
||||
html_files = list(tmp_path.rglob('*.html'))
|
||||
assert len(html_files) >= 1, "Nessun file HTML generato dal visualizer FDM"
|
||||
|
||||
|
||||
def test_pinn_visualizer_creates_html(tmp_path, monkeypatch):
|
||||
"""Il visualizer PINN scrive i tre file HTML senza errori."""
|
||||
import visualizer as pinn_vis
|
||||
monkeypatch.setattr(pinn_vis, 'BASE_DIR', str(tmp_path))
|
||||
|
||||
from fdm.solver import solve as fdm_solve
|
||||
T_fdm, _, _ = fdm_solve()
|
||||
|
||||
nx, nt = 20, 20
|
||||
x_vals = np.linspace(0, config.L, nx)
|
||||
t_vals = np.linspace(0, config.T_END, nt)
|
||||
T_pred = np.full((nx, nt), config.T_AMB) # predizione costante (dummy)
|
||||
|
||||
pinn_vis.visualize_heat_field(T_pred, x_vals, t_vals, T_fdm)
|
||||
|
||||
html_files = list(tmp_path.rglob('*.html'))
|
||||
assert len(html_files) == 3, f"Attesi 3 HTML, trovati {len(html_files)}"
|
||||
|
||||
|
||||
# ── PINN training (lento) ─────────────────────────────────────────────────────
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_pinn_training_saves_checkpoint(tmp_path, monkeypatch):
|
||||
"""Training per 30 epoche: il checkpoint viene salvato."""
|
||||
import engine
|
||||
save_path = str(tmp_path / 'model.pth')
|
||||
monkeypatch.setattr(engine, 'MODEL_SAVE_PATH', save_path)
|
||||
monkeypatch.setattr(engine, 'MODELS_DIR', str(tmp_path))
|
||||
|
||||
from engine import prepare_data, train_model
|
||||
data = prepare_data(N_f=300, N_ic=100, N_bc=100)
|
||||
train_model(data, epochs=30, patience=30)
|
||||
|
||||
assert os.path.exists(save_path)
|
||||
ckpt = torch.load(save_path, map_location='cpu', weights_only=True)
|
||||
assert 'state_dict' in ckpt
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_pinn_evaluate_after_training(tmp_path, monkeypatch):
|
||||
"""evaluate_model gira senza errori dopo un training minimo."""
|
||||
import engine
|
||||
save_path = str(tmp_path / 'model.pth')
|
||||
monkeypatch.setattr(engine, 'MODEL_SAVE_PATH', save_path)
|
||||
monkeypatch.setattr(engine, 'MODELS_DIR', str(tmp_path))
|
||||
|
||||
from engine import prepare_data, train_model, evaluate_model
|
||||
data = prepare_data(N_f=300, N_ic=100, N_bc=100)
|
||||
train_model(data, epochs=30, patience=30)
|
||||
evaluate_model(data)
|
||||
Reference in New Issue
Block a user