81063213f9
控制器与两个对话框共用 CalibrationService.read_identity,解析失败与缺 SN 分开上报; 本轮改动文件统一 LF,SN 必填回归已锁定。 定向 73 项、校准 UI 8 项与 lint 通过;未跑全量门禁。
131 lines
5.1 KiB
Python
131 lines
5.1 KiB
Python
"""Acceptance for the calibration service's rules about *whose* a file is.
|
|
|
|
The window used to own these: reading a coefficient file's claimed identity and deciding
|
|
whether it obviously belongs to the device on the bench. They are pure rules about files,
|
|
so they now live with the rest of the calibration file work — and can be tested without
|
|
building a window. The end-to-end apply path is still covered by ``test_ui_smoke``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from orisys_app_shared.force.calibration_io import (
|
|
CalSample,
|
|
CalSessionStore,
|
|
build_export_model,
|
|
)
|
|
from orisys_studio.calibration_service import CalibrationService
|
|
|
|
|
|
def _calibration_file(directory: Path, *, sensor_type: str, config_ref: str) -> Path:
|
|
"""A coefficient document that names the device it was measured on."""
|
|
store = CalSessionStore()
|
|
store.aggregate("fn", CalSample(weight_n=0.0, raw_mean=0.0, n=10))
|
|
store.aggregate("fn", CalSample(weight_n=5.0, raw_mean=500.0, n=10))
|
|
model = build_export_model({key: store.channel(key) for key in ("fn", "fx", "fy")})
|
|
document = model.to_dict()
|
|
document["identity"] = {"sensor_type": sensor_type, "sn": "SN-A"}
|
|
document["pipeline"] = {"config_ref": config_ref}
|
|
path = directory / f"{sensor_type}_calibration.json"
|
|
path.write_text(json.dumps(document), encoding="utf-8")
|
|
return path
|
|
|
|
|
|
def test_the_identity_names_the_sensor_and_the_configuration(tmp_path):
|
|
path = _calibration_file(tmp_path, sensor_type="box", config_ref="D:/lab/a/sensor.json")
|
|
|
|
identity = CalibrationService.identity_of(path)
|
|
assert identity["sensor_type"] == "box"
|
|
assert identity["sn"] == "SN-A"
|
|
assert identity["config_ref"] == "D:/lab/a/sensor.json"
|
|
|
|
|
|
def test_a_file_that_cannot_be_read_claims_nothing(tmp_path):
|
|
"""No identity must mean "no mismatch claim", not "mismatch"."""
|
|
broken = tmp_path / "not-a-calibration.json"
|
|
broken.write_text("this is not a coefficient document\n", encoding="utf-8")
|
|
|
|
assert CalibrationService.identity_of(broken) == {}
|
|
assert CalibrationService.target_mismatch(
|
|
broken, sensor_type="finger", config_ref="D:/lab/b/sensor.json"
|
|
) == []
|
|
|
|
|
|
def test_another_devices_calibration_is_reported_with_its_reasons(tmp_path):
|
|
path = _calibration_file(tmp_path, sensor_type="box", config_ref="D:/lab/a/sensor.json")
|
|
|
|
reasons = CalibrationService.target_mismatch(
|
|
path, sensor_type="finger", config_ref="D:/lab/b/sensor.json"
|
|
)
|
|
assert any("sensor_type" in reason for reason in reasons), reasons
|
|
assert any("config_ref" in reason for reason in reasons), reasons
|
|
|
|
# The same device and configuration is not a mismatch.
|
|
assert (
|
|
CalibrationService.target_mismatch(
|
|
path, sensor_type="box", config_ref="D:/lab/a/sensor.json"
|
|
)
|
|
== []
|
|
)
|
|
|
|
|
|
def test_the_same_configuration_named_differently_is_not_a_mismatch(tmp_path):
|
|
"""A path is compared as a path: the apply path must not cry wolf over spelling."""
|
|
assert CalibrationService.same_path("", "D:/lab/a/sensor.json") is True
|
|
assert CalibrationService.same_path("D:/lab/a/sensor.json", "") is True
|
|
assert CalibrationService.same_path("D:/lab/a/sensor.json", "D:/lab/./a/sensor.json")
|
|
|
|
# ...but it does have to be a path: a non-path value simply differs.
|
|
assert CalibrationService.same_path("D:/lab/a/sensor.json", "not a path\x00") is False
|
|
|
|
|
|
# --------------------------------------------------- one parse, both answers
|
|
|
|
|
|
def test_read_identity_returns_the_identity_and_no_error(tmp_path):
|
|
"""The strict parse answers with the identity *and* says it succeeded."""
|
|
path = _calibration_file(tmp_path, sensor_type="box", config_ref="D:/lab/a/sensor.json")
|
|
|
|
identity, error = CalibrationService.read_identity(path)
|
|
|
|
assert error == ""
|
|
assert identity == {
|
|
"sensor_type": "box",
|
|
"sn": "SN-A",
|
|
"config_ref": "D:/lab/a/sensor.json",
|
|
}
|
|
# The old name keeps its behaviour and is a thin wrapper over the one parse.
|
|
assert CalibrationService.identity_of(path) == identity
|
|
|
|
|
|
def test_read_identity_keeps_the_parsers_reason_when_the_file_cannot_be_read(tmp_path):
|
|
broken = tmp_path / "broken.json"
|
|
broken.write_text(
|
|
json.dumps({"schema_version": 2, "unit": "N", "channels": {"fn": "bogus"}}),
|
|
encoding="utf-8",
|
|
)
|
|
|
|
identity, error = CalibrationService.read_identity(broken)
|
|
|
|
assert identity == {}
|
|
assert error
|
|
assert "\n" not in error, "the reason must stay one line for the status bar"
|
|
assert CalibrationService.identity_of(broken) == {}
|
|
|
|
|
|
def test_read_identity_distinguishes_a_parsed_document_without_an_sn(tmp_path):
|
|
"""A readable document that simply names no device is not a parse failure."""
|
|
path = _calibration_file(tmp_path, sensor_type="box", config_ref="D:/lab/a/sensor.json")
|
|
document = json.loads(path.read_text(encoding="utf-8"))
|
|
document.pop("identity")
|
|
document.pop("pipeline")
|
|
path.write_text(json.dumps(document), encoding="utf-8")
|
|
|
|
identity, error = CalibrationService.read_identity(path)
|
|
|
|
assert error == ""
|
|
assert identity["sn"] == ""
|
|
assert identity["sensor_type"] == ""
|