use crate::result::CompilerResult;
use crate::test_binary::RelayInstr;
use crate::test_descriptor::ast::{RelayOp, RelayStatement};
use crate::test_descriptor::concrete_test::ConcreteTest;
#[derive(PartialEq, Debug)]
pub struct RelaysTimeline {
pub endpoints: Vec<u32>,
pub operations: Vec<Vec<RelayStatement>>,
}
impl RelaysTimeline {
pub fn new() -> Self {
RelaysTimeline {
endpoints: Vec::new(),
operations: vec![],
}
}
pub fn add_relay_statement(&mut self, statement: &RelayStatement) -> CompilerResult<()> {
let res = CompilerResult::status_only("Add relay statement to relay timeline");
let start_time = statement.time as u32;
let start_idx = match self.endpoints.binary_search(&start_time) {
Ok(exact_idx) => exact_idx,
Err(one_above_idx) => one_above_idx,
};
if start_idx >= self.endpoints.len() {
self.endpoints.push(start_time);
self.operations.push(Vec::new());
}
let start_timeline = self.endpoints[start_idx];
match start_timeline.cmp(&start_time) {
std::cmp::Ordering::Equal => {
self.operations[start_idx].push((*statement).clone());
}
std::cmp::Ordering::Greater => {
self.endpoints.insert(start_idx, start_time);
self.operations
.insert(start_idx, vec![(*statement).clone()]);
}
std::cmp::Ordering::Less => (),
}
res
}
pub fn validate(&self, concrete: &ConcreteTest) -> CompilerResult<()> {
let mut res = CompilerResult::status_only("Validate relay timeline");
let mut active_relays: Vec<String> = Vec::new();
let mut unset_relays: Vec<String> = Vec::new();
for relay_idx in 0..self.endpoints.len() {
for idx in 0..self.operations[relay_idx].len() {
let operation = &self.operations[relay_idx][idx];
match operation.op {
RelayOp::Set => {
if active_relays.contains(&operation.id) {
res.warning((
operation.metadata,
"Tried to set an already active relay",
));
} else {
active_relays.push(operation.id.to_string());
unset_relays.retain(|x| !x.eq(&operation.id));
}
}
RelayOp::Unset => {
if unset_relays.contains(&operation.id) {
res.warning((
operation.metadata,
"Tried to unset an already inactive relay",
));
} else {
unset_relays.push(operation.id.to_string());
active_relays.retain(|x| !x.eq(&operation.id));
}
}
}
}
}
active_relays.sort();
if !active_relays.eq(&concrete.environment.config.safe_state) {
res.error(format!(
"Active relays at end of test did not match safe state {:?}",
active_relays
));
}
res
}
pub fn to_instrs(&self, concrete: &ConcreteTest) -> CompilerResult<Vec<(u32, RelayInstr)>> {
let mut res = CompilerResult::new("Convert relays timeline into a vector of RelayInstr");
let mut relay_instructions: Vec<(u32, RelayInstr)> = Vec::new();
for (time, operations_vec) in self.endpoints.iter().zip(self.operations.iter()) {
for statement in operations_vec {
relay_instructions.push((
*time,
check!(res, RelayInstr::try_from(concrete, statement)),
));
}
}
res.with_value(relay_instructions)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::result::diagnostic::Location;
use crate::result::Status;
use crate::test_builder::TestBuilder;
use crate::test_descriptor::ast::RelayOp;
#[test]
fn construct_relay_timeline() {
let empty_location = Location::from_raw(0, 0, 0);
let endpoints: Vec<u32> = vec![0, 2, 5, 20];
let relay1_set = RelayStatement {
time: 0,
id: "1".to_string(),
op: RelayOp::Set,
metadata: empty_location,
};
let relay2_set = RelayStatement {
time: 2,
id: "2".to_string(),
op: RelayOp::Set,
metadata: empty_location,
};
let relay1_unset = RelayStatement {
time: 20,
id: "1".to_string(),
op: RelayOp::Unset,
metadata: empty_location,
};
let relay2_unset = RelayStatement {
time: 5,
id: "2".to_string(),
op: RelayOp::Unset,
metadata: empty_location,
};
let operations = vec![
vec![relay1_set.clone()],
vec![relay2_set.clone()],
vec![relay2_unset.clone()],
vec![relay1_unset.clone()],
];
let simple_timeline = RelaysTimeline {
endpoints,
operations,
};
let mut timeline_relay = RelaysTimeline::new();
timeline_relay.add_relay_statement(&relay1_set);
timeline_relay.add_relay_statement(&relay2_set);
timeline_relay.add_relay_statement(&relay2_unset);
timeline_relay.add_relay_statement(&relay1_unset);
assert_eq!(timeline_relay, simple_timeline);
}
#[test]
fn validate_one_relay_active() {
let mut testbuilder = TestBuilder::new();
testbuilder.with_relay("relay1");
let concrete = ConcreteTest::try_new(testbuilder.env, testbuilder.test)
.to_option()
.unwrap();
let empty_location = Location::from_raw(0, 0, 0);
let relay1_set = RelayStatement {
time: 0,
id: "1".to_string(),
op: RelayOp::Set,
metadata: empty_location,
};
let mut timeline_relay = RelaysTimeline::new();
timeline_relay.add_relay_statement(&relay1_set);
assert_eq!(
timeline_relay.validate(&concrete).get_status(),
Status::Failed
);
}
#[test]
fn validate_one_relay_inactive() {
let mut testbuilder = TestBuilder::new();
testbuilder.with_relay("relay1");
let concrete = ConcreteTest::try_new(testbuilder.env, testbuilder.test)
.to_option()
.unwrap();
let empty_location = Location::from_raw(0, 0, 0);
let relay1_set = RelayStatement {
time: 0,
id: "1".to_string(),
op: RelayOp::Set,
metadata: empty_location,
};
let relay1_unset = RelayStatement {
time: 0,
id: "1".to_string(),
op: RelayOp::Unset,
metadata: empty_location,
};
let mut timeline_relay = RelaysTimeline::new();
timeline_relay.add_relay_statement(&relay1_set);
timeline_relay.add_relay_statement(&relay1_unset);
assert_ne!(
timeline_relay.validate(&concrete).get_status(),
Status::Failed
);
}
#[test]
fn validate_failed_multiple_relays_active() {
let mut testbuilder = TestBuilder::new();
testbuilder.with_relay("relay1");
let concrete = ConcreteTest::try_new(testbuilder.env, testbuilder.test)
.to_option()
.unwrap();
let empty_location = Location::from_raw(0, 0, 0);
let relay1_set = RelayStatement {
time: 0,
id: "1".to_string(),
op: RelayOp::Set,
metadata: empty_location,
};
let relay1_unset = RelayStatement {
time: 20,
id: "1".to_string(),
op: RelayOp::Unset,
metadata: empty_location,
};
let relay2_set = RelayStatement {
time: 2,
id: "2".to_string(),
op: RelayOp::Set,
metadata: empty_location,
};
let relay3_set = RelayStatement {
time: 2,
id: "3".to_string(),
op: RelayOp::Set,
metadata: empty_location,
};
let mut timeline_relay = RelaysTimeline::new();
timeline_relay.add_relay_statement(&relay1_set);
timeline_relay.add_relay_statement(&relay1_unset);
timeline_relay.add_relay_statement(&relay2_set);
timeline_relay.add_relay_statement(&relay3_set);
assert_eq!(
timeline_relay.validate(&concrete).get_status(),
Status::Failed
);
}
#[test]
fn validate_failed_first_relay_active() {
let mut testbuilder = TestBuilder::new();
testbuilder.with_relay("relay1");
let concrete = ConcreteTest::try_new(testbuilder.env, testbuilder.test)
.to_option()
.unwrap();
let empty_location = Location::from_raw(0, 0, 0);
let relay1_set = RelayStatement {
time: 0,
id: "1".to_string(),
op: RelayOp::Set,
metadata: empty_location,
};
let relay2_set = RelayStatement {
time: 2,
id: "2".to_string(),
op: RelayOp::Set,
metadata: empty_location,
};
let relay2_unset = RelayStatement {
time: 5,
id: "2".to_string(),
op: RelayOp::Unset,
metadata: empty_location,
};
let relay3_set = RelayStatement {
time: 2,
id: "3".to_string(),
op: RelayOp::Set,
metadata: empty_location,
};
let relay3_unset = RelayStatement {
time: 5,
id: "3".to_string(),
op: RelayOp::Unset,
metadata: empty_location,
};
let mut timeline_relay = RelaysTimeline::new();
timeline_relay.add_relay_statement(&relay1_set);
timeline_relay.add_relay_statement(&relay2_set);
timeline_relay.add_relay_statement(&relay2_unset);
timeline_relay.add_relay_statement(&relay3_set);
timeline_relay.add_relay_statement(&relay3_unset);
assert_eq!(
timeline_relay.validate(&concrete).get_status(),
Status::Failed
);
}
#[test]
fn validate_all_relays_set_unset() {
let mut testbuilder = TestBuilder::new();
testbuilder.with_relay("relay1");
let concrete = ConcreteTest::try_new(testbuilder.env, testbuilder.test)
.to_option()
.unwrap();
let empty_location = Location::from_raw(0, 0, 0);
let relay1_set = RelayStatement {
time: 0,
id: "1".to_string(),
op: RelayOp::Set,
metadata: empty_location,
};
let relay1_unset = RelayStatement {
time: 0,
id: "1".to_string(),
op: RelayOp::Unset,
metadata: empty_location,
};
let relay2_set = RelayStatement {
time: 2,
id: "2".to_string(),
op: RelayOp::Set,
metadata: empty_location,
};
let relay2_unset = RelayStatement {
time: 5,
id: "2".to_string(),
op: RelayOp::Unset,
metadata: empty_location,
};
let relay3_set = RelayStatement {
time: 2,
id: "3".to_string(),
op: RelayOp::Set,
metadata: empty_location,
};
let relay3_unset = RelayStatement {
time: 5,
id: "3".to_string(),
op: RelayOp::Unset,
metadata: empty_location,
};
let mut timeline_relay = RelaysTimeline::new();
timeline_relay.add_relay_statement(&relay1_set);
timeline_relay.add_relay_statement(&relay1_unset);
timeline_relay.add_relay_statement(&relay2_set);
timeline_relay.add_relay_statement(&relay2_unset);
timeline_relay.add_relay_statement(&relay3_set);
timeline_relay.add_relay_statement(&relay3_unset);
assert_ne!(
timeline_relay.validate(&concrete).get_status(),
Status::Failed
);
}
}