use ridl_backend_rust::{generate, generate_face};
#[path = "support/ir.rs"]
mod ir;
fn dense(source: &str) -> String {
source.chars().filter(|c| !c.is_whitespace()).collect()
}
fn module(source: &str, name: &str) -> String {
let header = format!("pub mod {name} {{");
let start = source.find(&header).unwrap_or_else(|| {
panic!("generated source has no `{header}`:\n{source}");
});
let rest = &source[start..];
let end = rest[1..]
.find("\npub mod ")
.map_or(rest.len(), |index| index + 1);
rest[..end].to_string()
}
fn between(source: &str, from: &str, to: &str) -> String {
let start = source
.find(from)
.unwrap_or_else(|| panic!("generated source has no `{from}`"));
let rest = &source[start..];
let end = rest
.find(to)
.unwrap_or_else(|| panic!("generated source has no `{to}` after `{from}`"));
rest[..end].to_string()
}
fn face() -> String {
let package = ir::compile_fixture("interaction_face.ridl");
generate_face(&package).expect("generate_face").rust_source
}
#[test]
fn each_interface_gets_its_own_face_module() {
let source = face();
assert!(source.contains("pub mod cabin {"), "cabin face module");
assert!(source.contains("pub mod horn {"), "horn face module");
}
#[test]
fn the_emitter_writes_no_inner_attribute() {
assert!(
!face().contains("#!["),
"generated source has an inner attribute"
);
}
#[test]
fn the_client_is_bound_by_every_port_its_interface_needs() {
let d = dense(&module(&face(), "cabin"));
assert!(
d.contains(
"pubstructClient<P:::ridl_rt::port::SignalReader+::ridl_rt::port::EventSource\
+::ridl_rt::port::Caller"
),
"Cabin client port bounds",
);
assert!(d.contains("port:P,"), "the client holds its port by value");
assert!(
!d.contains("Client<'a,"),
"the client carries no lifetime parameter"
);
}
#[test]
fn a_signal_only_interface_gets_a_client_bound_only_by_signal_reader() {
let horn = module(&face(), "horn");
let d = dense(&horn);
assert!(
d.contains("pubstructClient<P:::ridl_rt::port::SignalReader"),
"Horn client is bound by SignalReader",
);
assert!(
!d.contains("Caller"),
"no Caller bound on a signal-only client"
);
assert!(
!d.contains("EventSource"),
"no EventSource bound on a signal-only client",
);
assert!(
!d.contains("EventSink"),
"no EventSink bound on a signal-only publisher",
);
assert!(
d.contains("pubstructPublisher<W:::ridl_rt::port::SignalWriter>"),
"a signal-only interface still gets a publisher, bound by SignalWriter alone",
);
assert!(
!d.contains("Handler"),
"a signal-only interface gets no dispatch",
);
assert!(
!d.contains("pubtraitProvider"),
"a signal-only interface gets no Provider trait",
);
}
#[test]
fn the_client_reads_a_signal_through_the_signal_reader_port() {
let d = dense(&module(&face(), "cabin"));
assert!(
d.contains("pubfntemperature(&self,)->::core::result::Result<::ridl_rt::sample::Sample<super::Temperature>,"),
"signal accessor returns a Sample of the declared payload",
);
assert!(
d.contains("self.port.read(<super::Cabinas::ridl_rt::contract::Interface>::NUMBER,::ridl_rt::contract::Ordinal(1u32),&mutbuf,)"),
"the accessor calls SignalReader::read"
);
assert!(
d.contains("::ridl_rt::contract::Ordinal(1u32)"),
"the signal's ordinal is read from the descriptor row",
);
assert!(
d.contains("::ridl_rt::sample::Detection::InvalidValue(violation)"),
"a constraint violation becomes Detection::InvalidValue",
);
assert!(
d.contains("::ridl_rt::sample::Detection::Corrupt"),
"malformed bytes become Detection::Corrupt",
);
assert!(
d.contains("<super::CabinTemperatureas::ridl_rt::contract::Signal>::init()"),
"the init value stands in for a payload that failed its check",
);
}
#[test]
fn the_client_subscribes_and_polls_events_through_the_event_source_port() {
let d = dense(&module(&face(), "cabin"));
assert!(
d.contains("pubfnsubscribe_warning(&mutself,)"),
"one subscribe method per event",
);
assert!(
d.contains("self.port.subscribe(<super::Cabinas::ridl_rt::contract::Interface>::NUMBER,&[::ridl_rt::contract::Ordinal(2u32)],)"),
"subscribe calls EventSource::subscribe"
);
assert!(
d.contains("self.port.next("),
"polling calls EventSource::next"
);
assert!(d.contains("pubenumEvent{"), "an occurrence enum");
assert!(
d.contains("Warning(::ridl_rt::sample::Occurrence<super::Warning>),"),
"one variant per event, carrying the declared payload",
);
assert!(
d.contains("::ridl_rt::contract::Ordinal(2u32)=>{Ok(Some(Event::Warning("),
"the poll routes the event's ordinal to its variant",
);
assert!(
d.contains(
"ifoccurrence.iface!=<super::Cabinas::ridl_rt::contract::Interface>::NUMBER{\
returnErr(::ridl_rt::port::ReadError::Contract(\
::ridl_rt::error::Contract::UnknownInteraction,),);}"
),
"the poll checks the interface number before the ordinal",
);
}
#[test]
fn the_client_sends_a_command_and_a_query_through_the_caller_port() {
let d = dense(&module(&face(), "cabin"));
assert!(
d.contains(
"pubfnset_level(&mutself,level:super::Level,)\
->::core::result::Result<SetLevelCorrelation,::ridl_rt::port::SendError>"
),
"the command method returns its own correlation newtype",
);
assert!(
d.contains("self.port.command(<super::Cabinas::ridl_rt::contract::Interface>::NUMBER,::ridl_rt::contract::Ordinal(3u32),bytes,)"),
"the command calls Caller::command"
);
let send_command = between(&d, "pubfnset_level(&mutself", "pubfnaverage(&mutself");
assert!(
send_command
.contains("<super::CabinSetLevelas::ridl_rt::contract::Command>::require(&level)"),
"the consumer evaluates the command's require, through the Command trait",
);
assert!(
d.contains(
"pubfnaverage(&mutself,window:super::Window,)\
->::core::result::Result<AverageCorrelation,::ridl_rt::port::SendError>"
),
"the query method returns its own correlation newtype",
);
assert!(
d.contains("self.port.query(<super::Cabinas::ridl_rt::contract::Interface>::NUMBER,::ridl_rt::contract::Ordinal(4u32),bytes,)"),
"the query calls Caller::query"
);
assert!(
d.contains("pubfnaverage_reply(&mutself,correlation:AverageCorrelation,)"),
"a separate reply method polls the correlation, typed by its query",
);
assert!(
d.contains("self.port.reply("),
"the reply method calls Caller::reply"
);
let send_query = between(&d, "pubfnaverage(&mutself", "pubfnaverage_reply");
assert!(
send_query.contains("<super::CabinAverageas::ridl_rt::contract::Query>::require(&window)"),
"the consumer evaluates the query's require, through the Query trait",
);
let reply = between(&d, "pubfnaverage_reply", "pubfnset_level_ack");
assert!(
reply.contains("::ridl_rt::error::Contract::InvalidValue(violation)"),
"a reply that breaks a typl constraint is an InvalidValue contract error",
);
assert!(
reply.contains("::ridl_rt::error::Transport::Corrupt"),
"malformed reply bytes are a transport corruption",
);
assert!(
d.contains("pubfnset_level_ack(&mutself,correlation:SetLevelCorrelation,)"),
"the acknowledgment method is per command and takes that command's newtype",
);
assert!(
d.contains("self.port.ack(correlation.0)"),
"the acknowledgment method calls Caller::ack through the newtype"
);
}
#[test]
fn the_publisher_writes_signals_and_raises_events() {
let d = dense(&module(&face(), "cabin"));
assert!(
d.contains("pubstructPublisher<W:::ridl_rt::port::SignalWriter+::ridl_rt::port::EventSink"),
"Cabin publisher port bounds",
);
assert!(
d.contains("port:W,"),
"the publisher holds its port by value"
);
assert!(
d.contains("pubfntemperature(&mutself,value:super::Temperature,)"),
"one publish method per signal",
);
assert!(
d.contains("self.port.set(<super::Cabinas::ridl_rt::contract::Interface>::NUMBER,::ridl_rt::contract::Ordinal(1u32),bytes,)"),
"publishing calls SignalWriter::set"
);
assert!(
d.contains("pubfninvalidate_temperature(&mutself,)"),
"one invalidate method per signal",
);
assert!(
d.contains("self.port.invalidate(<super::Cabinas::ridl_rt::contract::Interface>::NUMBER,::ridl_rt::contract::Ordinal(1u32),)"),
"invalidate calls SignalWriter::invalidate"
);
assert!(
d.contains("pubfnwarning(&mutself,value:super::Warning,)"),
"one raise method per event",
);
assert!(
d.contains("self.port.raise(<super::Cabinas::ridl_rt::contract::Interface>::NUMBER,::ridl_rt::contract::Ordinal(2u32),bytes,)"),
"raising calls EventSink::raise"
);
assert!(d.contains("pubfncommit(&mutself)"), "the publisher commits");
assert!(
d.contains("self.port.commit()"),
"commit calls SignalWriter::commit"
);
}
#[test]
fn the_provider_trait_carries_the_settled_method_signatures() {
let d = dense(&module(&face(), "cabin"));
assert!(d.contains("pubtraitProvider{"), "the Provider trait");
assert!(
d.contains("fnset_level(&mutself,level:&super::Level);"),
"the command provider method returns nothing",
);
assert!(
d.contains("fnaverage(&mutself,window:&super::Window)->super::Average;"),
"the query provider method returns its declared reply",
);
}
#[test]
fn the_pipeline_generate_emits_no_face_module() {
let package = ir::compile_fixture("interaction_face.ridl");
let plain = generate(&package).expect("generate").rust_source;
assert!(
!plain.contains("pub mod cabin"),
"the pipeline emits no face module"
);
assert!(
!plain.contains("Publisher"),
"the pipeline emits no publisher"
);
}