From cfb1eb66a5ccf36e56cf69ddd5421bba0a84ff23 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Thu, 29 Oct 2026 13:32:23 +0100 Subject: [PATCH] Test: errori di configurazione e CSV, domini.toml non validi, exit code CLI, varianti di report e grafici Co-Authored-By: Claude Sonnet 5.5 --- tests/test_cli.py | 21 +++++++++++++++++++++ tests/test_config_dati.py | 30 ++++++++++++++++++++++++++++++ tests/test_domini.py | 15 +++++++++++++++ tests/test_fit.py | 9 +++++++++ tests/test_report.py | 29 +++++++++++++++++++++++++++++ 5 files changed, 104 insertions(+) diff --git a/tests/test_cli.py b/tests/test_cli.py index c351e09..8c2d6a6 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -43,3 +43,24 @@ def test_cli_errori(tmp_path): with pytest.raises(SystemExit) as e: cli.main(["--config", "/non/esiste.toml", "domini", "x.csv"]) assert e.value.code == 3 + + +def test_cli_domini_non_validi_exit_6(tmp_path): + csv = csv_sintetico(tmp_path) + toml = tmp_path / "d.toml" + toml.write_text('[[dominio]]\ntipo="riposo"\nt0_s=0\nt1_s=10\n[[dominio]]\ntipo="raffreddamento"\nt0_s=20\nt1_s=30\n') + with pytest.raises(SystemExit) as e: + cli.main(["--config", str(RADICE / "config.toml"), "--output", str(tmp_path / "out"), "analizza", str(csv), "--domini", str(toml)]) + assert e.value.code == 6 + with pytest.raises(SystemExit) as e: + cli.main(["--config", str(RADICE / "config.toml"), "--output", str(tmp_path / "out"), "analizza", str(csv), + "--domini", str(tmp_path / "manca.toml")]) + assert e.value.code == 6 + + +def test_cli_csv_non_valido_exit_4(tmp_path): + csv = tmp_path / "vuoto.csv" + csv.write_text("t_ms,T_C,dT_C\n") + with pytest.raises(SystemExit) as e: + cli.main(["--config", str(RADICE / "config.toml"), "--output", str(tmp_path / "out"), "domini", str(csv)]) + assert e.value.code == 4 diff --git a/tests/test_config_dati.py b/tests/test_config_dati.py index d76f8a9..c2255de 100644 --- a/tests/test_config_dati.py +++ b/tests/test_config_dati.py @@ -44,3 +44,33 @@ def test_csv_tempo_non_crescente(cfg, tmp_path): (tmp_path / "d.csv").write_text("t_ms,T_C,dT_C\n200,20,0\n100,20,0\n") with pytest.raises(dati.ErroreDati, match="crescente"): dati.leggi_csv(tmp_path / "d.csv", cfg) + + +def test_toml_non_valido(tmp_path): + (tmp_path / "c.toml").write_text("[dati\nrotto") + with pytest.raises(config.ErroreConfig, match="TOML non valido"): + config.leggi(tmp_path / "c.toml") + + +@pytest.mark.parametrize(("vecchio", "nuovo", "messaggio"), [ + ('unita_tempo = "ms"', 'unita_tempo = "min"', "unita_tempo"), + ('serie_riferimento = "T_C"', 'serie_riferimento = "X"', "serie_riferimento"), +]) +def test_valori_non_ammessi(tmp_path, vecchio, nuovo, messaggio): + testo = (RADICE / "config.toml").read_text() + assert vecchio in testo + (tmp_path / "c.toml").write_text(testo.replace(vecchio, nuovo, 1)) + with pytest.raises(config.ErroreConfig, match=messaggio): + config.leggi(tmp_path / "c.toml") + + +def test_csv_senza_righe_di_dati(cfg, tmp_path): + (tmp_path / "d.csv").write_text("t_ms,T_C,dT_C\n") + with pytest.raises(dati.ErroreDati, match="vuoti o non numerici"): + dati.leggi_csv(tmp_path / "d.csv", cfg) + + +def test_csv_con_valori_non_numerici(cfg, tmp_path): + (tmp_path / "d.csv").write_text("t_ms,T_C,dT_C\n100,abc,0.1\n250,20.1,0.2\n") + with pytest.raises(dati.ErroreDati, match="vuoti o non numerici"): + dati.leggi_csv(tmp_path / "d.csv", cfg) diff --git a/tests/test_domini.py b/tests/test_domini.py index 5193d07..042ece7 100644 --- a/tests/test_domini.py +++ b/tests/test_domini.py @@ -94,3 +94,18 @@ def test_salita_complessiva_sotto_soglia_non_e_un_gradino(cfg): T[101] += 8.0 T[102:] += 16.0 # 16 °C in due campioni: sotto la soglia di 20 assert tipi(t, {"T_C": T, "dT_C": T - 23.0}, cfg) == ["riposo"] + + +def test_domini_toml_vuoto_rifiutato(sintetico, tmp_path): + t, _ = sintetico + (tmp_path / "d.toml").write_text("# nessun dominio\n") + with pytest.raises(ValueError, match="senza"): + domini.leggi_toml(tmp_path / "d.toml", t) + + +def test_domini_toml_intervallo_vuoto_rifiutato(sintetico, tmp_path): + t, _ = sintetico + (tmp_path / "d.toml").write_text('[[dominio]]\ntipo="riposo"\nt0_s=0\nt1_s=0\n' + '[[dominio]]\ntipo="raffreddamento"\nt0_s=0\nt1_s=30\n') + with pytest.raises(ValueError, match="vuoto"): + domini.leggi_toml(tmp_path / "d.toml", t) diff --git a/tests/test_fit.py b/tests/test_fit.py index 0a17b2b..2a97425 100644 --- a/tests/test_fit.py +++ b/tests/test_fit.py @@ -180,3 +180,12 @@ def test_salto_con_tratto_adiacente_indeterminato_usa_la_misura_corretta(cfg): assert g["stima"] == "misura corretta" assert abs(g["delta"] - ampiezza) <= abs(g["delta_misurato"] - ampiezza) + 1e-9 assert g["delta"] == pytest.approx(ampiezza, rel=0.03) + + +def test_dati_non_finiti_non_convergono_senza_eccezioni(cfg): + t = np.arange(20) * 0.15 + y = 25.0 + np.exp(-t / 5.0) + y[3] = np.nan + for nome in MODELLI: + r = adatta(t, y, nome, 0.1, cfg) + assert r["convergenza"] is False and r["modello"] == nome diff --git a/tests/test_report.py b/tests/test_report.py index e012ef3..609032d 100644 --- a/tests/test_report.py +++ b/tests/test_report.py @@ -72,3 +72,32 @@ def test_senza_nan(): 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