104 lines
4.7 KiB
Python
104 lines
4.7 KiB
Python
"""Forma del report.md e dei grafici su un CSV sintetico (nessun file esterno)."""
|
||
|
||
import numpy as np
|
||
from conftest import scrivi_csv, segnale
|
||
|
||
from fitseg import cli
|
||
|
||
|
||
def esegui(tmp_path, gradini):
|
||
t, serie = segnale(gradini=gradini, durata=80.0)
|
||
csv = tmp_path / "sint.csv"
|
||
scrivi_csv(csv, t, serie)
|
||
cli.main(["--output", str(tmp_path / "out"), "analizza", str(csv)])
|
||
return (tmp_path / "out/sint/03_report/report.md").read_text(), tmp_path / "out/sint/02_sovrapposizione"
|
||
|
||
|
||
def test_dominio_indeterminato_senza_ripetizioni_e_con_il_minimo(tmp_path):
|
||
report, _ = esegui(tmp_path, [(20.0, 80.0), (20.5, 60.0)]) # tratto intermedio di 1-2 campioni
|
||
assert "indeterminato: indeterminato" not in report and "Indeterminato** — indeterminato" not in report
|
||
assert "meno del minimo 4 (`n_min_punti_parametri_fissi`)" in report
|
||
|
||
|
||
def test_costante_senza_r2_e_senza_zero_negativo(tmp_path):
|
||
report, _ = esegui(tmp_path, [(20.0, 80.0)])
|
||
assert "-0.0000" not in report
|
||
dettaglio = report.split("### Dominio 1")[1].split("### Dominio 2")[0]
|
||
assert "`costante`" in dettaglio and "R^2" not in dettaglio # R² non ha senso per un modello costante
|
||
assert "R^2 = " in report # ma c'è per l'esponenziale
|
||
|
||
|
||
def test_intervallo_del_dominio_include_l_ultimo_campione(tmp_path):
|
||
report, _ = esegui(tmp_path, [(20.0, 80.0)])
|
||
assert r"\le t \le" in report and r"\le t <" not in report
|
||
|
||
|
||
def test_salto_stimato_dai_modelli_nel_report(tmp_path):
|
||
report, _ = esegui(tmp_path, [(20.0, 80.0)])
|
||
assert "stimato dai modelli adiacenti (misurato sui campioni:" in report
|
||
|
||
|
||
def test_motivo_con_un_solo_modello_senza_parsimonia(tmp_path):
|
||
report, _ = esegui(tmp_path, [(20.0, 80.0)])
|
||
finale = report.split("### Dominio 3")[1]
|
||
assert "1/1 modelli validi" in finale and "meno parametri" not in finale
|
||
|
||
|
||
def test_grafico_indeterminato_senza_pannello_residui(tmp_path):
|
||
_, cartella = esegui(tmp_path, [(20.0, 80.0), (20.5, 60.0)])
|
||
from PIL import Image # matplotlib dipende già da Pillow
|
||
larghezza, altezza = Image.open(cartella / "dominio3_T_C.png").size
|
||
assert altezza / larghezza < 0.55 # un solo pannello (9×4.2), non due (9×6)
|
||
larghezza, altezza = Image.open(cartella / "dominio7_T_C.png").size if (cartella / "dominio7_T_C.png").exists() else (1, 1)
|
||
assert np.isfinite(altezza / larghezza)
|
||
|
||
|
||
def test_risultati_json_e_json_valido_anche_con_domini_indeterminati(tmp_path):
|
||
import json
|
||
esegui(tmp_path, [(20.0, 80.0), (20.5, 60.0)]) # tratto intermedio con meno di 4 campioni: σ non stimabile
|
||
testo = (tmp_path / "out/sint/03_report/risultati.json").read_text()
|
||
|
||
def rifiuta(costante):
|
||
raise ValueError(f"costante non ammessa in JSON: {costante}")
|
||
dati = json.loads(testo, parse_constant=rifiuta) # NaN / Infinity farebbero fallire
|
||
assert any(v["sigma"] is None for r in dati for v in r["serie"].values())
|
||
|
||
|
||
def test_senza_nan():
|
||
from fitseg.uscita import _senza_nan
|
||
assert _senza_nan({"a": float("nan"), "b": [1.0, float("inf")], "c": {"d": 2}}) == {"a": None, "b": [1.0, None], "c": {"d": 2}}
|
||
|
||
|
||
def test_report_mostra_la_scala_robusta_dei_residui(tmp_path):
|
||
report, _ = esegui(tmp_path, [(20.0, 80.0)])
|
||
assert r"\hat\sigma_\text{res}" in report and "1.4826" in report
|
||
|
||
|
||
def test_equazione_lineare_con_segno():
|
||
from fitseg.uscita import _eq_numerica
|
||
fit = {"modello": "lineare", "parametri": {"a": 20.0, "b": -0.5}}
|
||
assert _eq_numerica(fit) == r"y = 20 - 0.5\,t'"
|
||
assert _eq_numerica({"modello": "lineare", "parametri": {"a": 20.0, "b": 0.25}}) == r"y = 20 + 0.25\,t'"
|
||
|
||
|
||
def test_salto_solo_misurato_nel_report():
|
||
from fitseg.uscita import _salto
|
||
g = {"n": 3, "durata_s": 0.3, "stima": "misura", "delta": 70.0, "delta_misurato": 70.0}
|
||
testo = _salto({"gradino": g})
|
||
assert "misurato sui campioni" in testo and "modelli adiacenti non disponibili" in testo and testo.count("$") % 2 == 0
|
||
|
||
|
||
def test_grafici_con_campioni_esclusi_e_modelli_alternativi(tmp_path):
|
||
# riposo con deriva (costante, lineare ed esponenziale validi → curve tratteggiate) e scarta_dopo_salto_n > 0 (campioni grigi)
|
||
from conftest import RADICE
|
||
testo = (RADICE / "config.toml").read_text().replace("scarta_dopo_salto_n = 0", "scarta_dopo_salto_n = 2")
|
||
(tmp_path / "c.toml").write_text(testo)
|
||
t, serie = segnale(gradini=[(40.0, 70.0)], durata=80.0, rumore=0.3)
|
||
deriva = 0.02 * t
|
||
serie = {"T_C": serie["T_C"] + deriva, "dT_C": serie["dT_C"] + deriva}
|
||
csv = tmp_path / "sint.csv"
|
||
scrivi_csv(csv, t, serie)
|
||
cli.main(["--config", str(tmp_path / "c.toml"), "--output", str(tmp_path / "out"), "analizza", str(csv)])
|
||
cartella = tmp_path / "out/sint/02_sovrapposizione"
|
||
assert (cartella / "dominio1_T_C.png").stat().st_size > 0 and (cartella / "dominio3_T_C.png").stat().st_size > 0
|