2026-08-06 11:45:39 +02:00
|
|
|
"""Test di regressione sui file campione presenti nella cartella.
|
|
|
|
|
|
|
|
|
|
Il parser si appoggia alla struttura binaria del formato Creo: qualsiasi
|
|
|
|
|
modifica alle espressioni di ricerca va verificata contro file reali. Il test
|
|
|
|
|
si limita ai file effettivamente presenti, quindi resta eseguibile anche su
|
|
|
|
|
un archivio diverso.
|
|
|
|
|
|
|
|
|
|
python3 -m unittest test_regression -v
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import glob
|
|
|
|
|
import os
|
|
|
|
|
import unittest
|
|
|
|
|
|
2026-08-06 14:51:39 +02:00
|
|
|
from creoparams.extract import (extract, latest_versions, read_preview,
|
|
|
|
|
split_filename)
|
2026-08-06 11:45:39 +02:00
|
|
|
|
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
|
|
|
|
|
|
#: Parametri che ci aspettiamo di leggere da ogni .prt aziendale.
|
|
|
|
|
REQUIRED = ["CODICE", "DENOMINAZIONE", "REVISIONE", "STAMPIGLIATURA",
|
|
|
|
|
"MATERIALE", "DISEGNATORE", "DATA"]
|
|
|
|
|
|
|
|
|
|
#: Valori verificati manualmente. Vanno estesi man mano che si confrontano
|
|
|
|
|
#: altri file con Creo: e' questa la rete di sicurezza vera.
|
|
|
|
|
KNOWN = {
|
|
|
|
|
"9258400201.prt": {
|
|
|
|
|
"CODICE": "9258400201",
|
|
|
|
|
"DENOMINAZIONE": "Carter lato destro",
|
|
|
|
|
"MATERIALE": "ACC.INOX LAMIERA SP.4",
|
|
|
|
|
"DISEGNATORE": "Grilli",
|
|
|
|
|
"DATA": "9/02/2026",
|
|
|
|
|
},
|
|
|
|
|
"9258400205.prt": {
|
|
|
|
|
"CODICE": "9258400205",
|
|
|
|
|
"DENOMINAZIONE": "Tirante carter lateriali",
|
|
|
|
|
"MATERIALE": "AISI 304 TONDO D.12",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _parts():
|
|
|
|
|
paths = latest_versions(glob.glob(os.path.join(HERE, "*.prt*")))
|
|
|
|
|
return [p for p in paths if split_filename(os.path.basename(p))[1] == "prt"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestEstrazione(unittest.TestCase):
|
|
|
|
|
@classmethod
|
|
|
|
|
def setUpClass(cls):
|
|
|
|
|
cls.parts = _parts()
|
|
|
|
|
if not cls.parts:
|
|
|
|
|
raise unittest.SkipTest("nessun file .prt nella cartella")
|
|
|
|
|
cls.records = {
|
|
|
|
|
os.path.basename(p): extract(p) for p in cls.parts
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def test_header_riconosciuto(self):
|
|
|
|
|
for name, record in self.records.items():
|
|
|
|
|
with self.subTest(file=name):
|
|
|
|
|
self.assertEqual(record["model"]["kind"], "PART")
|
|
|
|
|
self.assertTrue(record["model"]["creo_version"])
|
|
|
|
|
self.assertGreater(record["extraction"]["sections"], 0)
|
|
|
|
|
|
|
|
|
|
def test_parametri_obbligatori_presenti(self):
|
|
|
|
|
for name, record in self.records.items():
|
|
|
|
|
for param in REQUIRED:
|
|
|
|
|
with self.subTest(file=name, param=param):
|
|
|
|
|
entry = record["parameters"].get(param)
|
|
|
|
|
self.assertIsNotNone(entry, f"{param} non trovato")
|
|
|
|
|
self.assertEqual(entry["status"], "found")
|
|
|
|
|
|
|
|
|
|
def test_confidenza_alta(self):
|
|
|
|
|
"""Ogni parametro obbligatorio deve essere confermato in due
|
|
|
|
|
rappresentazioni indipendenti del file."""
|
|
|
|
|
for name, record in self.records.items():
|
|
|
|
|
for param in REQUIRED:
|
|
|
|
|
with self.subTest(file=name, param=param):
|
|
|
|
|
self.assertGreaterEqual(
|
|
|
|
|
record["parameters"][param]["confidence"], 0.95
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_valori_noti(self):
|
|
|
|
|
for stem, expected in KNOWN.items():
|
|
|
|
|
matching = [
|
|
|
|
|
r for r in self.records.values()
|
|
|
|
|
if r["model"]["file_name"] == stem
|
|
|
|
|
]
|
|
|
|
|
if not matching:
|
|
|
|
|
continue
|
|
|
|
|
record = matching[0]
|
|
|
|
|
for param, value in expected.items():
|
|
|
|
|
with self.subTest(file=stem, param=param):
|
|
|
|
|
self.assertEqual(record["parameters"][param]["value"], value)
|
|
|
|
|
|
|
|
|
|
def test_codice_coerente_col_nome_file(self):
|
|
|
|
|
for name, record in self.records.items():
|
|
|
|
|
with self.subTest(file=name):
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
record["parameters"]["CODICE"]["value"],
|
|
|
|
|
record["model"]["name"],
|
|
|
|
|
)
|
|
|
|
|
|
2026-08-06 14:51:39 +02:00
|
|
|
def test_anteprima_presente_e_valida(self):
|
|
|
|
|
for path in self.parts:
|
|
|
|
|
name = os.path.basename(path)
|
|
|
|
|
with self.subTest(file=name):
|
|
|
|
|
shot = read_preview(path)
|
|
|
|
|
self.assertIsNotNone(shot, "anteprima non trovata")
|
|
|
|
|
self.assertTrue(shot.data.startswith(b"\xff\xd8\xff"))
|
|
|
|
|
self.assertTrue(shot.data.endswith(b"\xff\xd9"))
|
|
|
|
|
self.assertGreater(shot.width, 0)
|
|
|
|
|
self.assertGreater(shot.height, 0)
|
|
|
|
|
# Le dimensioni dichiarate devono corrispondere al record.
|
|
|
|
|
declared = self.records[name]["preview"]
|
|
|
|
|
self.assertTrue(declared["present"])
|
|
|
|
|
self.assertEqual(declared["width"], shot.width)
|
|
|
|
|
|
|
|
|
|
def test_una_sola_anteprima_per_file(self):
|
|
|
|
|
"""Se il rilevatore trovasse piu' candidati, starebbe scambiando dati
|
|
|
|
|
binari qualsiasi per immagini."""
|
|
|
|
|
from creoparams import preview
|
|
|
|
|
for path in self.parts:
|
|
|
|
|
with self.subTest(file=os.path.basename(path)):
|
|
|
|
|
with open(path, "rb") as handle:
|
|
|
|
|
self.assertEqual(len(preview.find_all(handle.read())), 1)
|
|
|
|
|
|
2026-08-06 11:45:39 +02:00
|
|
|
def test_parametri_feature_separati(self):
|
|
|
|
|
"""I parametri dei fori non devono finire tra quelli del modello."""
|
|
|
|
|
for name, record in self.records.items():
|
|
|
|
|
for param, entry in record["parameters"].items():
|
|
|
|
|
if param in ("SCREW_SIZE", "DRILL_DIAMETER", "CSINK_ANGLE"):
|
|
|
|
|
with self.subTest(file=name, param=param):
|
|
|
|
|
self.assertEqual(entry["owner"], "feature")
|
|
|
|
|
|
|
|
|
|
|
2026-08-06 14:51:39 +02:00
|
|
|
class TestDisegni(unittest.TestCase):
|
|
|
|
|
"""I .drw non hanno parametri di modello ma contengono la tavola."""
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def setUpClass(cls):
|
|
|
|
|
paths = latest_versions(glob.glob(os.path.join(HERE, "*.drw*")))
|
|
|
|
|
cls.drawings = [p for p in paths
|
|
|
|
|
if split_filename(os.path.basename(p))[1] == "drw"]
|
|
|
|
|
if not cls.drawings:
|
|
|
|
|
raise unittest.SkipTest("nessun file .drw nella cartella")
|
|
|
|
|
cls.records = {os.path.basename(p): extract(p) for p in cls.drawings}
|
|
|
|
|
|
|
|
|
|
def test_riconosciuti_come_disegni(self):
|
|
|
|
|
for name, record in self.records.items():
|
|
|
|
|
with self.subTest(file=name):
|
|
|
|
|
self.assertEqual(record["model"]["kind"], "DRAWING")
|
|
|
|
|
self.assertFalse(record["extraction"]["holds_model_parameters"])
|
|
|
|
|
|
|
|
|
|
def test_testi_estratti(self):
|
|
|
|
|
for name, record in self.records.items():
|
|
|
|
|
with self.subTest(file=name):
|
|
|
|
|
sheet = record["drawing"]
|
|
|
|
|
self.assertGreater(sheet["decompressed_bytes"], 10_000)
|
|
|
|
|
self.assertGreater(len(sheet["texts"]), 20)
|
|
|
|
|
|
|
|
|
|
def test_voci_del_cartiglio(self):
|
|
|
|
|
"""Il cartiglio aziendale deve comparire in ogni tavola."""
|
|
|
|
|
attese = {"CODICE", "DISEGNATO", "STAMPIGLIATURA", "SCALA"}
|
|
|
|
|
for name, record in self.records.items():
|
|
|
|
|
with self.subTest(file=name):
|
|
|
|
|
testi = set(record["drawing"]["texts"])
|
|
|
|
|
self.assertTrue(attese.issubset(testi),
|
|
|
|
|
f"mancano {attese - testi}")
|
|
|
|
|
|
|
|
|
|
def test_primitive_grafiche(self):
|
|
|
|
|
for name, record in self.records.items():
|
|
|
|
|
with self.subTest(file=name):
|
|
|
|
|
prims = record["drawing"]["primitives"]
|
|
|
|
|
self.assertIn("prim_text", prims)
|
|
|
|
|
self.assertIn("prim_line", prims)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestLzw(unittest.TestCase):
|
|
|
|
|
def test_round_trip_noto(self):
|
|
|
|
|
"""Verifica il decoder su uno stream prodotto da compress(1), se
|
|
|
|
|
disponibile nel sistema."""
|
|
|
|
|
import shutil
|
|
|
|
|
import subprocess
|
|
|
|
|
from creoparams import lzw
|
|
|
|
|
tool = shutil.which("compress") or shutil.which("gzip")
|
|
|
|
|
if not tool:
|
|
|
|
|
self.skipTest("nessun compressore disponibile")
|
|
|
|
|
if "gzip" in tool:
|
|
|
|
|
self.skipTest("gzip non produce il formato .Z")
|
|
|
|
|
payload = b"PGL " * 500 + bytes(range(256)) * 4
|
|
|
|
|
proc = subprocess.run([tool, "-c"], input=payload, capture_output=True)
|
|
|
|
|
out, _ = lzw.decompress(proc.stdout)
|
|
|
|
|
self.assertEqual(out, payload)
|
|
|
|
|
|
|
|
|
|
|
2026-08-06 11:45:39 +02:00
|
|
|
if __name__ == "__main__":
|
|
|
|
|
unittest.main()
|