use epics_macros_rs::EpicsRecord;
#[derive(EpicsRecord)]
#[record(type = "asyn")]
pub struct AsynRecord {
#[field(type = "String")]
pub port: String,
#[field(type = "String")]
pub tfil: String,
}
impl Default for AsynRecord {
fn default() -> Self {
Self {
port: String::new(),
tfil: "Unknown".to_string(),
}
}
}
#[cfg(test)]
mod tests {
use super::AsynRecord;
use crate::server::record::RecordInstance;
use crate::types::{DbFieldType, EpicsValue};
fn instance() -> RecordInstance {
RecordInstance::new("ASYN:TEST".to_string(), AsynRecord::default())
}
#[test]
fn menu_field_stores_ordinal_and_serves_as_enum() {
let mut inst = instance();
assert_eq!(inst.declared_field_type("TMOD"), Some(DbFieldType::Enum));
assert_eq!(inst.resolve_field("TMOD"), Some(EpicsValue::Enum(0)));
inst.put_common_field("TMOD", EpicsValue::String("Read".into()))
.expect("caput asyn.TMOD Read must resolve against menu(asynTMOD)");
assert_eq!(inst.resolve_field("TMOD"), Some(EpicsValue::Enum(2)));
}
#[test]
fn connection_and_trace_menu_fields_serve_as_enum() {
let inst = instance();
assert_eq!(inst.declared_field_type("CNCT"), Some(DbFieldType::Enum));
assert_eq!(inst.resolve_field("CNCT"), Some(EpicsValue::Enum(0)));
assert_eq!(inst.declared_field_type("TIB2"), Some(DbFieldType::Enum));
assert_eq!(inst.resolve_field("TIB2"), Some(EpicsValue::Enum(0)));
}
#[test]
fn string_field_truncates_to_field_size() {
let mut inst = instance();
let long = "X".repeat(45); inst.put_common_field("DRVINFO", EpicsValue::String(long.as_str().into()))
.expect("caput asyn.DRVINFO <45 chars> must be accepted");
match inst.resolve_field("DRVINFO") {
Some(EpicsValue::String(s)) => {
assert_eq!(s.as_bytes().len(), 39, "size(40) caps stored bytes at 39");
}
other => panic!("DRVINFO should read back a String, got {other:?}"),
}
}
#[test]
fn numeric_fields_round_trip_and_serve_initial() {
let mut inst = instance();
assert_eq!(inst.resolve_field("TMOT"), Some(EpicsValue::Double(1.0)));
inst.put_common_field("NRRD", EpicsValue::String("42".into()))
.expect("caput asyn.NRRD 42 must be accepted");
assert_eq!(inst.resolve_field("NRRD"), Some(EpicsValue::Long(42)));
assert_eq!(inst.resolve_field("OMAX"), Some(EpicsValue::Long(80)));
assert_eq!(inst.resolve_field("NOWT"), Some(EpicsValue::Long(80)));
}
#[test]
fn modeled_port_round_trips() {
let mut inst = instance();
assert_eq!(inst.declared_field_type("PORT"), Some(DbFieldType::String));
inst.record
.put_field("PORT", EpicsValue::String("L0".into()))
.expect("PORT is modeled and writable");
assert_eq!(
inst.resolve_field("PORT"),
Some(EpicsValue::String("L0".into()))
);
}
#[test]
fn tfil_serves_unknown_with_no_port() {
let inst = instance();
assert_eq!(inst.declared_field_type("TFIL"), Some(DbFieldType::String));
assert_eq!(
inst.resolve_field("TFIL"),
Some(EpicsValue::String("Unknown".into())),
"C init_record pass 0 sets tfil=\"Unknown\"; the .dbd cannot express it"
);
}
}