#![allow(missing_docs)]
#![cfg(not(target_arch = "wasm32"))]
#[allow(dead_code, unreachable_pub)]
#[path = "support/mod.rs"]
mod test_support;
use assert_matches::assert_matches;
use cfg_if::cfg_if;
use telltale_machine::coroutine::Fault;
use telltale_machine::instr::{ImmValue, Instr};
use telltale_machine::{MonitorMode, ProtocolMachine, ProtocolMachineConfig, ProtocolMachineError};
use test_support::PassthroughHandler;
cfg_if! {
if #[cfg(feature = "multi-thread")] {
use telltale_machine::ThreadedProtocolMachine;
}
}
#[test]
fn cooperative_monitor_precheck_catches_mismatched_instr_shape() {
let mut image = test_support::simple_send_recv_image("A", "B", "msg");
image.programs.insert(
"A".to_string(),
vec![Instr::Receive { chan: 0, dst: 0 }, Instr::Halt],
);
let mut machine = ProtocolMachine::new(ProtocolMachineConfig {
monitor_mode: MonitorMode::SessionTypePrecheck,
..ProtocolMachineConfig::default()
});
machine.load_choreography(&image).expect("load image");
let err = machine
.run(&PassthroughHandler, 32)
.expect_err("expected mismatch");
assert_matches!(
err,
ProtocolMachineError::Fault {
fault: Fault::TypeViolation {
ref message,
..
},
..
} if message.contains("[monitor]")
);
}
#[test]
fn cooperative_monitor_precheck_bypasses_control_flow_instrs() {
let mut image = test_support::simple_send_recv_image("A", "B", "msg");
image.programs.insert(
"A".to_string(),
vec![
Instr::Set {
dst: 0,
val: ImmValue::Nat(7),
},
Instr::Halt,
],
);
let mut machine = ProtocolMachine::new(ProtocolMachineConfig {
monitor_mode: MonitorMode::SessionTypePrecheck,
..ProtocolMachineConfig::default()
});
machine.load_choreography(&image).expect("load image");
machine
.run(&PassthroughHandler, 1)
.expect("control-flow step should bypass monitor precheck");
assert_eq!(
machine
.last_sched_step()
.expect("expected at least one scheduled step")
.selected_coro,
0
);
}
#[test]
fn cooperative_monitor_offer_passes_on_send_state() {
let mut image = test_support::simple_send_recv_image("A", "Z", "msg");
image.programs.insert(
"A".to_string(),
vec![
Instr::Offer {
chan: 0,
label: "msg".to_string(),
},
Instr::Halt,
],
);
let mut machine = ProtocolMachine::new(ProtocolMachineConfig {
monitor_mode: MonitorMode::SessionTypePrecheck,
..ProtocolMachineConfig::default()
});
machine.load_choreography(&image).expect("load image");
machine
.run(&PassthroughHandler, 1)
.expect("offer should pass monitor on send state");
}
#[test]
fn cooperative_monitor_offer_rejects_recv_state() {
let mut image = test_support::simple_send_recv_image("Z", "A", "msg");
image.programs.insert(
"A".to_string(),
vec![
Instr::Offer {
chan: 0,
label: "msg".to_string(),
},
Instr::Halt,
],
);
let mut machine = ProtocolMachine::new(ProtocolMachineConfig {
monitor_mode: MonitorMode::SessionTypePrecheck,
..ProtocolMachineConfig::default()
});
machine.load_choreography(&image).expect("load image");
let err = machine
.run(&PassthroughHandler, 1)
.expect_err("offer should fail monitor on recv state");
assert_matches!(
err,
ProtocolMachineError::Fault {
fault: Fault::TypeViolation {
ref message,
..
},
..
} if message.contains("[monitor]") && message.contains("expected Send state")
);
}
#[test]
fn cooperative_monitor_choose_passes_on_recv_state() {
let mut image = test_support::simple_send_recv_image("Z", "A", "msg");
image.programs.insert(
"A".to_string(),
vec![
Instr::Choose {
chan: 0,
table: vec![("msg".to_string(), 1)],
},
Instr::Halt,
],
);
let mut machine = ProtocolMachine::new(ProtocolMachineConfig {
monitor_mode: MonitorMode::SessionTypePrecheck,
..ProtocolMachineConfig::default()
});
machine.load_choreography(&image).expect("load image");
machine
.run(&PassthroughHandler, 1)
.expect("choose should pass monitor on recv state");
}
#[test]
fn cooperative_monitor_choose_rejects_send_state() {
let mut image = test_support::simple_send_recv_image("A", "Z", "msg");
image.programs.insert(
"A".to_string(),
vec![
Instr::Choose {
chan: 0,
table: vec![("msg".to_string(), 1)],
},
Instr::Halt,
],
);
let mut machine = ProtocolMachine::new(ProtocolMachineConfig {
monitor_mode: MonitorMode::SessionTypePrecheck,
..ProtocolMachineConfig::default()
});
machine.load_choreography(&image).expect("load image");
let err = machine
.run(&PassthroughHandler, 1)
.expect_err("choose should fail monitor on send state");
assert_matches!(
err,
ProtocolMachineError::Fault {
fault: Fault::TypeViolation {
ref message,
..
},
..
} if message.contains("[monitor]") && message.contains("expected Recv state")
);
}
#[test]
fn cooperative_out_of_credits_faults_before_dispatch() {
let image = test_support::simple_send_recv_image("A", "B", "msg");
let mut machine = ProtocolMachine::new(ProtocolMachineConfig {
initial_cost_budget: 0,
instruction_cost: 1,
..ProtocolMachineConfig::default()
});
machine.load_choreography(&image).expect("load image");
let err = machine
.run(&PassthroughHandler, 32)
.expect_err("expected out-of-credits");
assert_matches!(
err,
ProtocolMachineError::Fault {
fault: Fault::OutOfCredits,
..
}
);
}
cfg_if! {
if #[cfg(feature = "multi-thread")] {
#[test]
fn threaded_out_of_credits_faults_before_dispatch() {
let image = test_support::simple_send_recv_image("A", "B", "msg");
let mut machine = ThreadedProtocolMachine::with_workers(
ProtocolMachineConfig {
initial_cost_budget: 0,
instruction_cost: 1,
..ProtocolMachineConfig::default()
},
2,
);
machine.load_choreography(&image).expect("load image");
let err = machine
.run(&PassthroughHandler, 32)
.expect_err("expected out-of-credits");
assert_matches!(
err,
ProtocolMachineError::Fault {
fault: Fault::OutOfCredits,
..
}
);
}
#[test]
fn threaded_monitor_precheck_catches_mismatched_instr_shape() {
let mut image = test_support::simple_send_recv_image("A", "B", "msg");
image.programs.insert(
"A".to_string(),
vec![Instr::Receive { chan: 0, dst: 0 }, Instr::Halt],
);
let mut machine = ThreadedProtocolMachine::with_workers(
ProtocolMachineConfig {
monitor_mode: MonitorMode::SessionTypePrecheck,
..ProtocolMachineConfig::default()
},
2,
);
machine.load_choreography(&image).expect("load image");
let err = machine
.run(&PassthroughHandler, 32)
.expect_err("expected mismatch");
assert_matches!(
err,
ProtocolMachineError::Fault {
fault: Fault::TypeViolation {
ref message,
..
},
..
} if message.contains("[monitor]")
);
}
#[test]
fn threaded_monitor_precheck_bypasses_control_flow_instrs() {
let mut image = test_support::simple_send_recv_image("A", "B", "msg");
image.programs.insert(
"A".to_string(),
vec![
Instr::Set {
dst: 0,
val: ImmValue::Nat(7),
},
Instr::Halt,
],
);
let mut machine = ThreadedProtocolMachine::with_workers(
ProtocolMachineConfig {
monitor_mode: MonitorMode::SessionTypePrecheck,
..ProtocolMachineConfig::default()
},
2,
);
machine.load_choreography(&image).expect("load image");
machine.run(&PassthroughHandler, 1)
.expect("control-flow step should bypass monitor precheck");
}
}
}