use crate::actor::ActorId;
use crate::lab::oracle::{CapabilityKind, OracleViolation, RRefId};
use crate::record::ObligationKind;
use crate::supervision::{EscalationPolicy, RestartPolicy};
use crate::types::{Budget, CancelReason, TaskId};
use crate::util::ArenaIndex;
use super::runner::MetaHarness;
pub const INVARIANT_TASK_LEAK: &str = "task_leak";
pub const INVARIANT_OBLIGATION_LEAK: &str = "obligation_leak";
pub const INVARIANT_QUIESCENCE: &str = "quiescence";
pub const INVARIANT_LOSER_DRAIN: &str = "loser_drain";
pub const INVARIANT_FINALIZER: &str = "finalizer";
pub const INVARIANT_REGION_TREE: &str = "region_tree";
pub const INVARIANT_AMBIENT_AUTHORITY: &str = "ambient_authority";
pub const INVARIANT_DEADLINE_MONOTONE: &str = "deadline_monotone";
pub const INVARIANT_CANCELLATION_PROTOCOL: &str = "cancellation_protocol";
pub const INVARIANT_CANCEL_CORRECTNESS: &str = "cancel_correctness";
pub const INVARIANT_CANCEL_DEBT: &str = "cancel_debt";
pub const INVARIANT_CANCEL_ORDERING: &str = "cancel_signal_ordering";
pub const INVARIANT_RUNTIME_EPOCH: &str = "runtime_epoch";
pub const INVARIANT_CHANNEL_ATOMICITY: &str = "channel_atomicity";
pub const INVARIANT_WAKER_DEDUP: &str = "waker_dedup";
pub const INVARIANT_ACTOR_LEAK: &str = "actor_leak";
pub const INVARIANT_SUPERVISION: &str = "supervision";
pub const INVARIANT_MAILBOX: &str = "mailbox";
pub const INVARIANT_RREF_ACCESS: &str = "rref_access";
pub const INVARIANT_REPLY_LINEARITY: &str = "reply_linearity";
pub const INVARIANT_REGISTRY_LEASE: &str = "registry_lease";
pub const INVARIANT_DOWN_ORDER: &str = "down_order";
pub const INVARIANT_SUPERVISOR_QUIESCENCE: &str = "supervisor_quiescence";
pub const INVARIANT_PRIORITY_INVERSION: &str = "priority_inversion";
#[cfg(feature = "messaging-fabric")]
pub const INVARIANT_FABRIC_PUBLISH: &str = "fabric_publish";
#[cfg(feature = "messaging-fabric")]
pub const INVARIANT_FABRIC_REPLY: &str = "fabric_reply";
#[cfg(feature = "messaging-fabric")]
pub const INVARIANT_FABRIC_QUIESCENCE: &str = "fabric_quiescence";
#[cfg(feature = "messaging-fabric")]
pub const INVARIANT_FABRIC_REDELIVERY: &str = "fabric_redelivery";
pub const ALL_ORACLE_INVARIANTS: &[&str] = &[
INVARIANT_TASK_LEAK,
INVARIANT_QUIESCENCE,
INVARIANT_CANCELLATION_PROTOCOL,
INVARIANT_LOSER_DRAIN,
INVARIANT_OBLIGATION_LEAK,
INVARIANT_AMBIENT_AUTHORITY,
INVARIANT_FINALIZER,
INVARIANT_REGION_TREE,
"region_leak",
INVARIANT_DEADLINE_MONOTONE,
INVARIANT_CANCEL_CORRECTNESS,
INVARIANT_CANCEL_DEBT,
INVARIANT_CANCEL_ORDERING,
INVARIANT_RUNTIME_EPOCH,
INVARIANT_CHANNEL_ATOMICITY,
INVARIANT_WAKER_DEDUP,
INVARIANT_ACTOR_LEAK,
INVARIANT_SUPERVISION,
INVARIANT_MAILBOX,
INVARIANT_RREF_ACCESS,
INVARIANT_REPLY_LINEARITY,
INVARIANT_REGISTRY_LEASE,
INVARIANT_DOWN_ORDER,
INVARIANT_SUPERVISOR_QUIESCENCE,
#[cfg(feature = "messaging-fabric")]
INVARIANT_FABRIC_PUBLISH,
#[cfg(feature = "messaging-fabric")]
INVARIANT_FABRIC_REPLY,
#[cfg(feature = "messaging-fabric")]
INVARIANT_FABRIC_QUIESCENCE,
#[cfg(feature = "messaging-fabric")]
INVARIANT_FABRIC_REDELIVERY,
];
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BuiltinMutation {
TaskLeak,
ObligationLeak,
Quiescence,
LoserDrain,
Finalizer,
RegionTreeMultipleRoots,
AmbientAuthoritySpawnWithoutCapability,
DeadlineMonotoneChildUnbounded,
CancelPropagationMissingChild,
ActorLeak,
SupervisionRestartLimitExceeded,
MailboxCapacityExceeded,
CrossRegionRRefAccess,
#[cfg(feature = "messaging-fabric")]
FabricPublishMissingSubscriberDelivery,
#[cfg(feature = "messaging-fabric")]
FabricReplyUnresolvedOnClose,
#[cfg(feature = "messaging-fabric")]
FabricQuiescenceBusyCellOnClose,
#[cfg(feature = "messaging-fabric")]
FabricRedeliveryBoundExceeded,
}
#[must_use]
pub fn builtin_mutations() -> Vec<BuiltinMutation> {
let mutations = vec![
BuiltinMutation::TaskLeak,
BuiltinMutation::ObligationLeak,
BuiltinMutation::Quiescence,
BuiltinMutation::LoserDrain,
BuiltinMutation::Finalizer,
BuiltinMutation::RegionTreeMultipleRoots,
BuiltinMutation::AmbientAuthoritySpawnWithoutCapability,
BuiltinMutation::DeadlineMonotoneChildUnbounded,
BuiltinMutation::CancelPropagationMissingChild,
BuiltinMutation::ActorLeak,
BuiltinMutation::SupervisionRestartLimitExceeded,
BuiltinMutation::MailboxCapacityExceeded,
BuiltinMutation::CrossRegionRRefAccess,
];
#[cfg(feature = "messaging-fabric")]
let mutations = {
let mut mutations = mutations;
mutations.extend([
BuiltinMutation::FabricPublishMissingSubscriberDelivery,
BuiltinMutation::FabricReplyUnresolvedOnClose,
BuiltinMutation::FabricQuiescenceBusyCellOnClose,
BuiltinMutation::FabricRedeliveryBoundExceeded,
]);
mutations
};
mutations
}
impl BuiltinMutation {
#[must_use]
pub fn name(self) -> &'static str {
match self {
Self::TaskLeak => "mutation_task_leak",
Self::ObligationLeak => "mutation_obligation_leak",
Self::Quiescence => "mutation_quiescence",
Self::LoserDrain => "mutation_loser_drain",
Self::Finalizer => "mutation_finalizer",
Self::RegionTreeMultipleRoots => "mutation_region_tree_multiple_roots",
Self::AmbientAuthoritySpawnWithoutCapability => {
"mutation_ambient_authority_spawn_without_capability"
}
Self::DeadlineMonotoneChildUnbounded => "mutation_deadline_child_unbounded",
Self::CancelPropagationMissingChild => "mutation_cancel_missing_child",
Self::ActorLeak => "mutation_actor_leak",
Self::SupervisionRestartLimitExceeded => "mutation_supervision_restart_limit",
Self::MailboxCapacityExceeded => "mutation_mailbox_capacity_exceeded",
Self::CrossRegionRRefAccess => "mutation_cross_region_rref_access",
#[cfg(feature = "messaging-fabric")]
Self::FabricPublishMissingSubscriberDelivery => {
"mutation_fabric_publish_missing_subscriber_delivery"
}
#[cfg(feature = "messaging-fabric")]
Self::FabricReplyUnresolvedOnClose => "mutation_fabric_reply_unresolved_on_close",
#[cfg(feature = "messaging-fabric")]
Self::FabricQuiescenceBusyCellOnClose => {
"mutation_fabric_quiescence_busy_cell_on_close"
}
#[cfg(feature = "messaging-fabric")]
Self::FabricRedeliveryBoundExceeded => "mutation_fabric_redelivery_bound_exceeded",
}
}
#[must_use]
pub fn invariant(self) -> &'static str {
match self {
Self::TaskLeak => INVARIANT_TASK_LEAK,
Self::ObligationLeak => INVARIANT_OBLIGATION_LEAK,
Self::Quiescence => INVARIANT_QUIESCENCE,
Self::LoserDrain => INVARIANT_LOSER_DRAIN,
Self::Finalizer => INVARIANT_FINALIZER,
Self::RegionTreeMultipleRoots => INVARIANT_REGION_TREE,
Self::AmbientAuthoritySpawnWithoutCapability => INVARIANT_AMBIENT_AUTHORITY,
Self::DeadlineMonotoneChildUnbounded => INVARIANT_DEADLINE_MONOTONE,
Self::CancelPropagationMissingChild => INVARIANT_CANCELLATION_PROTOCOL,
Self::ActorLeak => INVARIANT_ACTOR_LEAK,
Self::SupervisionRestartLimitExceeded => INVARIANT_SUPERVISION,
Self::MailboxCapacityExceeded => INVARIANT_MAILBOX,
Self::CrossRegionRRefAccess => INVARIANT_RREF_ACCESS,
#[cfg(feature = "messaging-fabric")]
Self::FabricPublishMissingSubscriberDelivery => INVARIANT_FABRIC_PUBLISH,
#[cfg(feature = "messaging-fabric")]
Self::FabricReplyUnresolvedOnClose => INVARIANT_FABRIC_REPLY,
#[cfg(feature = "messaging-fabric")]
Self::FabricQuiescenceBusyCellOnClose => INVARIANT_FABRIC_QUIESCENCE,
#[cfg(feature = "messaging-fabric")]
Self::FabricRedeliveryBoundExceeded => INVARIANT_FABRIC_REDELIVERY,
}
}
pub(crate) fn apply_baseline(self, harness: &mut MetaHarness) {
match self {
Self::TaskLeak => baseline_task_leak(harness),
Self::ObligationLeak => baseline_obligation_leak(harness),
Self::Quiescence => baseline_quiescence(harness),
Self::LoserDrain => baseline_loser_drain(harness),
Self::Finalizer => baseline_finalizer(harness),
Self::RegionTreeMultipleRoots => baseline_region_tree(harness),
Self::AmbientAuthoritySpawnWithoutCapability => baseline_ambient_authority(harness),
Self::DeadlineMonotoneChildUnbounded => baseline_deadline_monotone(harness),
Self::CancelPropagationMissingChild => baseline_cancel_propagation(harness),
Self::ActorLeak => baseline_actor_leak(harness),
Self::SupervisionRestartLimitExceeded => baseline_supervision_restart(harness),
Self::MailboxCapacityExceeded => baseline_mailbox_capacity(harness),
Self::CrossRegionRRefAccess => baseline_rref_access(harness),
#[cfg(feature = "messaging-fabric")]
Self::FabricPublishMissingSubscriberDelivery => baseline_fabric_publish(harness),
#[cfg(feature = "messaging-fabric")]
Self::FabricReplyUnresolvedOnClose => baseline_fabric_reply(harness),
#[cfg(feature = "messaging-fabric")]
Self::FabricQuiescenceBusyCellOnClose => baseline_fabric_quiescence(harness),
#[cfg(feature = "messaging-fabric")]
Self::FabricRedeliveryBoundExceeded => baseline_fabric_redelivery(harness),
}
}
pub(crate) fn apply_mutation(self, harness: &mut MetaHarness) {
match self {
Self::TaskLeak => mutation_task_leak(harness),
Self::ObligationLeak => mutation_obligation_leak(harness),
Self::Quiescence => mutation_quiescence(harness),
Self::LoserDrain => mutation_loser_drain(harness),
Self::Finalizer => mutation_finalizer(harness),
Self::RegionTreeMultipleRoots => mutation_region_tree(harness),
Self::AmbientAuthoritySpawnWithoutCapability => mutation_ambient_authority(harness),
Self::DeadlineMonotoneChildUnbounded => mutation_deadline_monotone(harness),
Self::CancelPropagationMissingChild => mutation_cancel_propagation(harness),
Self::ActorLeak => mutation_actor_leak(harness),
Self::SupervisionRestartLimitExceeded => mutation_supervision_restart(harness),
Self::MailboxCapacityExceeded => mutation_mailbox_capacity(harness),
Self::CrossRegionRRefAccess => mutation_rref_access(harness),
#[cfg(feature = "messaging-fabric")]
Self::FabricPublishMissingSubscriberDelivery => mutation_fabric_publish(harness),
#[cfg(feature = "messaging-fabric")]
Self::FabricReplyUnresolvedOnClose => mutation_fabric_reply(harness),
#[cfg(feature = "messaging-fabric")]
Self::FabricQuiescenceBusyCellOnClose => mutation_fabric_quiescence(harness),
#[cfg(feature = "messaging-fabric")]
Self::FabricRedeliveryBoundExceeded => mutation_fabric_redelivery(harness),
}
}
}
fn actor(n: u32) -> ActorId {
ActorId::from_task(TaskId::from_arena(ArenaIndex::new(n, 0)))
}
#[cfg(feature = "messaging-fabric")]
fn fabric_cell_id(
pattern: &str,
membership_epoch: u64,
generation: u64,
) -> crate::messaging::fabric::CellId {
crate::messaging::fabric::CellId::for_partition(
crate::messaging::fabric::CellEpoch::new(membership_epoch, generation),
&crate::messaging::SubjectPattern::new(pattern),
)
}
fn baseline_task_leak(harness: &mut MetaHarness) {
let now = harness.now();
let region = harness.next_region();
let task = harness.next_task();
harness.oracles.task_leak.on_spawn(task, region, now);
harness.oracles.task_leak.on_complete(task, now);
harness.oracles.task_leak.on_region_close(region, now);
}
fn mutation_task_leak(harness: &mut MetaHarness) {
let now = harness.now();
let region = harness.next_region();
let task = harness.next_task();
harness.oracles.task_leak.on_spawn(task, region, now);
harness.oracles.task_leak.on_region_close(region, now);
}
fn baseline_obligation_leak(harness: &mut MetaHarness) {
let now = harness.now();
let region = harness.create_root_region();
let task = harness.create_runtime_task(region);
let obligation = harness
.runtime
.state
.create_obligation(ObligationKind::SendPermit, task, region, None)
.expect("create obligation");
harness
.runtime
.state
.commit_obligation(obligation)
.expect("commit obligation");
harness.close_region(region);
harness
.oracles
.obligation_leak
.snapshot_from_state(&harness.runtime.state, now);
}
fn mutation_obligation_leak(harness: &mut MetaHarness) {
let now = harness.now();
let region = harness.create_root_region();
let task = harness.create_runtime_task(region);
let _obligation = harness
.runtime
.state
.create_obligation(ObligationKind::SendPermit, task, region, None)
.expect("create obligation");
harness.close_region(region);
harness
.runtime
.state
.region(region)
.expect("region must exist to be mutated")
.set_state(crate::record::region::RegionState::Closed);
harness
.oracles
.obligation_leak
.snapshot_from_state(&harness.runtime.state, now);
}
fn baseline_quiescence(harness: &mut MetaHarness) {
let now = harness.now();
let parent = harness.next_region();
let child = harness.next_region();
harness.oracles.quiescence.on_region_create(parent, None);
harness
.oracles
.quiescence
.on_region_create(child, Some(parent));
harness.oracles.quiescence.on_region_close(child, now);
harness.oracles.quiescence.on_region_close(parent, now);
}
fn mutation_quiescence(harness: &mut MetaHarness) {
let now = harness.now();
let parent = harness.next_region();
let child = harness.next_region();
harness.oracles.quiescence.on_region_create(parent, None);
harness
.oracles
.quiescence
.on_region_create(child, Some(parent));
harness.oracles.quiescence.on_region_close(parent, now);
}
fn baseline_loser_drain(harness: &mut MetaHarness) {
let now = harness.now();
let region = harness.next_region();
let winner = harness.next_task();
let loser = harness.next_task();
let race_id = harness
.oracles
.loser_drain
.on_race_start(region, vec![winner, loser], now);
harness.oracles.loser_drain.on_task_complete(winner, now);
harness.oracles.loser_drain.on_task_complete(loser, now);
harness
.oracles
.loser_drain
.on_race_complete(race_id, winner, now);
}
fn mutation_loser_drain(harness: &mut MetaHarness) {
let now = harness.now();
let region = harness.next_region();
let winner = harness.next_task();
let loser = harness.next_task();
let race_id = harness
.oracles
.loser_drain
.on_race_start(region, vec![winner, loser], now);
harness.oracles.loser_drain.on_task_complete(winner, now);
harness
.oracles
.loser_drain
.on_race_complete(race_id, winner, now);
}
fn baseline_finalizer(harness: &mut MetaHarness) {
let now = harness.now();
let region = harness.next_region();
let finalizer = harness.next_finalizer();
harness
.oracles
.finalizer
.on_register(finalizer, region, now);
harness.oracles.finalizer.on_run(finalizer, now);
harness.oracles.finalizer.on_region_close(region, now);
}
fn mutation_finalizer(harness: &mut MetaHarness) {
let now = harness.now();
let region = harness.next_region();
let finalizer = harness.next_finalizer();
harness
.oracles
.finalizer
.on_register(finalizer, region, now);
harness.oracles.finalizer.on_region_close(region, now);
}
fn baseline_region_tree(harness: &mut MetaHarness) {
let now = harness.now();
let root = harness.next_region();
let child = harness.next_region();
harness
.oracles
.region_tree
.on_region_create(root, None, now);
harness
.oracles
.region_tree
.on_region_create(child, Some(root), now);
}
fn mutation_region_tree(harness: &mut MetaHarness) {
let now = harness.now();
let root_a = harness.next_region();
let root_b = harness.next_region();
harness
.oracles
.region_tree
.on_region_create(root_a, None, now);
harness
.oracles
.region_tree
.on_region_create(root_b, None, now);
}
fn baseline_ambient_authority(harness: &mut MetaHarness) {
let now = harness.now();
let region = harness.next_region();
let task = harness.next_task();
let child = harness.next_task();
harness
.oracles
.ambient_authority
.on_task_created(task, region, None, now);
harness
.oracles
.ambient_authority
.on_spawn_effect(task, child, now);
}
fn mutation_ambient_authority(harness: &mut MetaHarness) {
let now = harness.now();
let region = harness.next_region();
let task = harness.next_task();
let child = harness.next_task();
harness
.oracles
.ambient_authority
.on_task_created(task, region, None, now);
harness
.oracles
.ambient_authority
.on_capability_revoked(task, CapabilityKind::Spawn, now);
harness
.oracles
.ambient_authority
.on_spawn_effect(task, child, now);
}
fn baseline_deadline_monotone(harness: &mut MetaHarness) {
let now = harness.now();
let parent = harness.next_region();
let child = harness.next_region();
let parent_budget = Budget::with_deadline_secs(10);
let child_budget = Budget::with_deadline_secs(5);
harness
.oracles
.deadline_monotone
.on_region_create(parent, None, &parent_budget, now);
harness
.oracles
.deadline_monotone
.on_region_create(child, Some(parent), &child_budget, now);
}
fn mutation_deadline_monotone(harness: &mut MetaHarness) {
let now = harness.now();
let parent = harness.next_region();
let child = harness.next_region();
let parent_budget = Budget::with_deadline_secs(10);
let child_budget = Budget::INFINITE;
harness
.oracles
.deadline_monotone
.on_region_create(parent, None, &parent_budget, now);
harness
.oracles
.deadline_monotone
.on_region_create(child, Some(parent), &child_budget, now);
}
fn baseline_cancel_propagation(harness: &mut MetaHarness) {
let now = harness.now();
let parent = harness.next_region();
let child = harness.next_region();
harness
.oracles
.cancellation_protocol
.on_region_create(parent, None);
harness
.oracles
.cancellation_protocol
.on_region_create(child, Some(parent));
harness
.oracles
.cancellation_protocol
.on_region_cancel(parent, CancelReason::shutdown(), now);
harness.oracles.cancellation_protocol.on_region_cancel(
child,
CancelReason::parent_cancelled(),
now,
);
}
fn mutation_cancel_propagation(harness: &mut MetaHarness) {
let now = harness.now();
let parent = harness.next_region();
let child = harness.next_region();
harness
.oracles
.cancellation_protocol
.on_region_create(parent, None);
harness
.oracles
.cancellation_protocol
.on_region_create(child, Some(parent));
harness
.oracles
.cancellation_protocol
.on_region_cancel(parent, CancelReason::shutdown(), now);
}
fn baseline_actor_leak(harness: &mut MetaHarness) {
let now = harness.now();
let region = harness.next_region();
harness.oracles.actor_leak.on_spawn(actor(100), region, now);
harness.oracles.actor_leak.on_stop(actor(100), now);
harness.oracles.actor_leak.on_region_close(region, now);
}
fn mutation_actor_leak(harness: &mut MetaHarness) {
let now = harness.now();
let region = harness.next_region();
harness.oracles.actor_leak.on_spawn(actor(100), region, now);
harness.oracles.actor_leak.on_region_close(region, now);
}
fn baseline_supervision_restart(harness: &mut MetaHarness) {
let now = harness.now();
harness.oracles.supervision.register_supervisor(
actor(200),
RestartPolicy::OneForOne,
2,
EscalationPolicy::Escalate,
);
harness
.oracles
.supervision
.register_child(actor(200), actor(201));
harness
.oracles
.supervision
.on_child_failed(actor(200), actor(201), now, "test error".into());
harness.oracles.supervision.on_restart(actor(201), 1, now);
}
fn mutation_supervision_restart(harness: &mut MetaHarness) {
let now = harness.now();
harness.oracles.supervision.register_supervisor(
actor(200),
RestartPolicy::OneForOne,
2,
EscalationPolicy::Escalate,
);
harness
.oracles
.supervision
.register_child(actor(200), actor(201));
harness
.oracles
.supervision
.on_child_failed(actor(200), actor(201), now, "test error".into());
harness.oracles.supervision.on_restart(actor(201), 3, now);
}
fn baseline_mailbox_capacity(harness: &mut MetaHarness) {
let now = harness.now();
harness
.oracles
.mailbox
.configure_mailbox(actor(300), 2, false);
harness.oracles.mailbox.on_send(actor(300), now);
harness.oracles.mailbox.on_send(actor(300), now);
harness.oracles.mailbox.on_receive(actor(300), now);
harness.oracles.mailbox.on_receive(actor(300), now);
}
fn mutation_mailbox_capacity(harness: &mut MetaHarness) {
let now = harness.now();
harness
.oracles
.mailbox
.configure_mailbox(actor(300), 2, false);
harness.oracles.mailbox.on_send(actor(300), now);
harness.oracles.mailbox.on_send(actor(300), now);
harness.oracles.mailbox.on_send(actor(300), now);
harness.oracles.mailbox.on_receive(actor(300), now);
harness.oracles.mailbox.on_receive(actor(300), now);
harness.oracles.mailbox.on_receive(actor(300), now);
}
fn baseline_rref_access(harness: &mut MetaHarness) {
let now = harness.now();
let region = harness.next_region();
let task = harness.next_task();
let rref = RRefId {
owner_region: region,
alloc_index: 0,
};
harness.oracles.rref_access.on_rref_create(rref, region);
harness.oracles.rref_access.on_task_spawn(task, region);
harness.oracles.rref_access.on_rref_access(rref, task, now);
harness.oracles.rref_access.on_region_close(region, now);
}
fn mutation_rref_access(harness: &mut MetaHarness) {
let now = harness.now();
let region_a = harness.next_region();
let region_b = harness.next_region();
let task = harness.next_task();
let rref = RRefId {
owner_region: region_a,
alloc_index: 0,
};
harness.oracles.rref_access.on_rref_create(rref, region_a);
harness.oracles.rref_access.on_task_spawn(task, region_b);
harness.oracles.rref_access.on_rref_access(rref, task, now);
}
#[cfg(feature = "messaging-fabric")]
fn baseline_fabric_publish(harness: &mut MetaHarness) {
let now = harness.now();
harness
.oracles
.fabric_publish
.register_subscription(1, crate::messaging::SubjectPattern::new("orders.>"));
harness
.oracles
.fabric_publish
.register_subscription(2, crate::messaging::SubjectPattern::new("orders.created"));
let publish_id = harness
.oracles
.fabric_publish
.on_publish_committed(crate::messaging::Subject::new("orders.created"), now);
harness
.oracles
.fabric_publish
.on_subscriber_receive(publish_id, 1);
harness
.oracles
.fabric_publish
.on_subscriber_receive(publish_id, 2);
}
#[cfg(feature = "messaging-fabric")]
fn mutation_fabric_publish(harness: &mut MetaHarness) {
let now = harness.now();
harness
.oracles
.fabric_publish
.register_subscription(1, crate::messaging::SubjectPattern::new("orders.>"));
harness
.oracles
.fabric_publish
.register_subscription(2, crate::messaging::SubjectPattern::new("orders.created"));
let publish_id = harness
.oracles
.fabric_publish
.on_publish_committed(crate::messaging::Subject::new("orders.created"), now);
harness
.oracles
.fabric_publish
.on_subscriber_receive(publish_id, 1);
}
#[cfg(feature = "messaging-fabric")]
fn baseline_fabric_reply(harness: &mut MetaHarness) {
let now = harness.now();
let region = harness.next_region();
harness.oracles.fabric_reply.on_request_started(
"req-fabric-clean",
region,
crate::messaging::DeliveryClass::ObligationBacked,
now,
);
harness
.oracles
.fabric_reply
.on_reply_resolved("req-fabric-clean", now);
harness.oracles.fabric_reply.on_region_close(region, now);
}
#[cfg(feature = "messaging-fabric")]
fn mutation_fabric_reply(harness: &mut MetaHarness) {
let now = harness.now();
let region = harness.next_region();
harness.oracles.fabric_reply.on_request_started(
"req-fabric-open",
region,
crate::messaging::DeliveryClass::ObligationBacked,
now,
);
harness.oracles.fabric_reply.on_region_close(region, now);
}
#[cfg(feature = "messaging-fabric")]
fn baseline_fabric_quiescence(harness: &mut MetaHarness) {
let now = harness.now();
let region = harness.next_region();
let cell = fabric_cell_id("orders.created", 1, 0);
harness
.oracles
.fabric_quiescence
.observe_cell(region, cell, 2, now);
harness
.oracles
.fabric_quiescence
.observe_cell(region, cell, 0, now);
harness
.oracles
.fabric_quiescence
.on_region_close(region, now);
}
#[cfg(feature = "messaging-fabric")]
fn mutation_fabric_quiescence(harness: &mut MetaHarness) {
let now = harness.now();
let region = harness.next_region();
let cell = fabric_cell_id("orders.created", 1, 0);
harness
.oracles
.fabric_quiescence
.observe_cell(region, cell, 1, now);
harness
.oracles
.fabric_quiescence
.on_region_close(region, now);
}
#[cfg(feature = "messaging-fabric")]
fn baseline_fabric_redelivery(harness: &mut MetaHarness) {
let now = harness.now();
harness
.oracles
.fabric_redelivery
.track_message("msg-fabric-clean", 2);
harness
.oracles
.fabric_redelivery
.on_redelivery("msg-fabric-clean", now);
harness
.oracles
.fabric_redelivery
.on_redelivery("msg-fabric-clean", now);
}
#[cfg(feature = "messaging-fabric")]
fn mutation_fabric_redelivery(harness: &mut MetaHarness) {
let now = harness.now();
harness
.oracles
.fabric_redelivery
.track_message("msg-fabric-overflow", 1);
harness
.oracles
.fabric_redelivery
.on_redelivery("msg-fabric-overflow", now);
harness
.oracles
.fabric_redelivery
.on_redelivery("msg-fabric-overflow", now);
}
#[must_use]
pub fn invariant_from_violation(violation: &OracleViolation) -> &'static str {
match violation {
OracleViolation::TaskLeak(_) => INVARIANT_TASK_LEAK,
OracleViolation::ObligationLeak(_) => INVARIANT_OBLIGATION_LEAK,
OracleViolation::Quiescence(_) => INVARIANT_QUIESCENCE,
OracleViolation::LoserDrain(_) => INVARIANT_LOSER_DRAIN,
OracleViolation::Finalizer(_) => INVARIANT_FINALIZER,
OracleViolation::RegionTree(_) => INVARIANT_REGION_TREE,
OracleViolation::RegionLeak(_) => "region_leak",
OracleViolation::AmbientAuthority(_) => INVARIANT_AMBIENT_AUTHORITY,
OracleViolation::DeadlineMonotone(_) => INVARIANT_DEADLINE_MONOTONE,
OracleViolation::CancellationProtocol(_) => INVARIANT_CANCELLATION_PROTOCOL,
OracleViolation::CancelCorrectness(_) => INVARIANT_CANCEL_CORRECTNESS,
OracleViolation::CancelDebt(_) => INVARIANT_CANCEL_DEBT,
OracleViolation::CancelOrdering(_) => INVARIANT_CANCEL_ORDERING,
OracleViolation::RuntimeEpoch(_) => INVARIANT_RUNTIME_EPOCH,
OracleViolation::ChannelAtomicity(_) => INVARIANT_CHANNEL_ATOMICITY,
OracleViolation::WakerDedup(_) => INVARIANT_WAKER_DEDUP,
OracleViolation::ActorLeak(_) => INVARIANT_ACTOR_LEAK,
OracleViolation::Supervision(_) => INVARIANT_SUPERVISION,
OracleViolation::Mailbox(_) => INVARIANT_MAILBOX,
OracleViolation::RRefAccess(_) => INVARIANT_RREF_ACCESS,
OracleViolation::ReplyLinearity(_) => INVARIANT_REPLY_LINEARITY,
OracleViolation::RegistryLease(_) => INVARIANT_REGISTRY_LEASE,
OracleViolation::DownOrder(_) => INVARIANT_DOWN_ORDER,
OracleViolation::SupervisorQuiescence(_) => INVARIANT_SUPERVISOR_QUIESCENCE,
#[cfg(feature = "messaging-fabric")]
OracleViolation::FabricPublish(_) => INVARIANT_FABRIC_PUBLISH,
#[cfg(feature = "messaging-fabric")]
OracleViolation::FabricReply(_) => INVARIANT_FABRIC_REPLY,
#[cfg(feature = "messaging-fabric")]
OracleViolation::FabricQuiescence(_) => INVARIANT_FABRIC_QUIESCENCE,
#[cfg(feature = "messaging-fabric")]
OracleViolation::FabricRedelivery(_) => INVARIANT_FABRIC_REDELIVERY,
OracleViolation::PriorityInversion(_) => INVARIANT_PRIORITY_INVERSION,
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
fn expected_oracle_invariant_count() -> usize {
let mut count = 17;
if cfg!(feature = "messaging-fabric") {
count += 4;
}
count
}
fn expected_builtin_mutation_count() -> usize {
let mut count = 13;
if cfg!(feature = "messaging-fabric") {
count += 4;
}
count
}
#[test]
fn all_oracle_invariants_count() {
assert_eq!(
ALL_ORACLE_INVARIANTS.len(),
expected_oracle_invariant_count()
);
}
#[test]
fn all_oracle_invariants_unique() {
let set: HashSet<&str> = ALL_ORACLE_INVARIANTS.iter().copied().collect();
assert_eq!(set.len(), ALL_ORACLE_INVARIANTS.len());
}
#[test]
fn builtin_mutations_count() {
assert_eq!(builtin_mutations().len(), expected_builtin_mutation_count());
}
#[test]
fn builtin_mutations_stable_order() {
assert_eq!(builtin_mutations(), builtin_mutations());
}
#[test]
fn mutation_names_unique() {
let names: Vec<&str> = builtin_mutations().iter().map(|m| m.name()).collect();
let set: HashSet<&str> = names.iter().copied().collect();
assert_eq!(set.len(), names.len(), "mutation names must be unique");
}
#[test]
fn mutation_invariants_all_in_all_oracle_invariants() {
let all_set: HashSet<&str> = ALL_ORACLE_INVARIANTS.iter().copied().collect();
for m in builtin_mutations() {
assert!(
all_set.contains(m.invariant()),
"mutation {:?} targets unknown invariant {}",
m,
m.invariant()
);
}
}
#[test]
fn mutation_name_matches_variant() {
assert_eq!(BuiltinMutation::TaskLeak.name(), "mutation_task_leak");
assert_eq!(
BuiltinMutation::ObligationLeak.name(),
"mutation_obligation_leak"
);
assert_eq!(BuiltinMutation::Quiescence.name(), "mutation_quiescence");
assert_eq!(BuiltinMutation::LoserDrain.name(), "mutation_loser_drain");
assert_eq!(BuiltinMutation::Finalizer.name(), "mutation_finalizer");
assert_eq!(
BuiltinMutation::RegionTreeMultipleRoots.name(),
"mutation_region_tree_multiple_roots"
);
assert_eq!(
BuiltinMutation::AmbientAuthoritySpawnWithoutCapability.name(),
"mutation_ambient_authority_spawn_without_capability"
);
assert_eq!(
BuiltinMutation::DeadlineMonotoneChildUnbounded.name(),
"mutation_deadline_child_unbounded"
);
assert_eq!(
BuiltinMutation::CancelPropagationMissingChild.name(),
"mutation_cancel_missing_child"
);
assert_eq!(BuiltinMutation::ActorLeak.name(), "mutation_actor_leak");
assert_eq!(
BuiltinMutation::SupervisionRestartLimitExceeded.name(),
"mutation_supervision_restart_limit"
);
assert_eq!(
BuiltinMutation::MailboxCapacityExceeded.name(),
"mutation_mailbox_capacity_exceeded"
);
assert_eq!(
BuiltinMutation::CrossRegionRRefAccess.name(),
"mutation_cross_region_rref_access"
);
#[cfg(feature = "messaging-fabric")]
{
assert_eq!(
BuiltinMutation::FabricPublishMissingSubscriberDelivery.name(),
"mutation_fabric_publish_missing_subscriber_delivery"
);
assert_eq!(
BuiltinMutation::FabricReplyUnresolvedOnClose.name(),
"mutation_fabric_reply_unresolved_on_close"
);
assert_eq!(
BuiltinMutation::FabricQuiescenceBusyCellOnClose.name(),
"mutation_fabric_quiescence_busy_cell_on_close"
);
assert_eq!(
BuiltinMutation::FabricRedeliveryBoundExceeded.name(),
"mutation_fabric_redelivery_bound_exceeded"
);
}
}
#[test]
fn mutation_invariant_mapping() {
assert_eq!(BuiltinMutation::TaskLeak.invariant(), INVARIANT_TASK_LEAK);
assert_eq!(
BuiltinMutation::ObligationLeak.invariant(),
INVARIANT_OBLIGATION_LEAK
);
assert_eq!(
BuiltinMutation::Quiescence.invariant(),
INVARIANT_QUIESCENCE
);
assert_eq!(
BuiltinMutation::LoserDrain.invariant(),
INVARIANT_LOSER_DRAIN
);
assert_eq!(BuiltinMutation::Finalizer.invariant(), INVARIANT_FINALIZER);
assert_eq!(
BuiltinMutation::RegionTreeMultipleRoots.invariant(),
INVARIANT_REGION_TREE
);
assert_eq!(
BuiltinMutation::AmbientAuthoritySpawnWithoutCapability.invariant(),
INVARIANT_AMBIENT_AUTHORITY
);
assert_eq!(
BuiltinMutation::DeadlineMonotoneChildUnbounded.invariant(),
INVARIANT_DEADLINE_MONOTONE
);
assert_eq!(
BuiltinMutation::CancelPropagationMissingChild.invariant(),
INVARIANT_CANCELLATION_PROTOCOL
);
assert_eq!(BuiltinMutation::ActorLeak.invariant(), INVARIANT_ACTOR_LEAK);
assert_eq!(
BuiltinMutation::SupervisionRestartLimitExceeded.invariant(),
INVARIANT_SUPERVISION
);
assert_eq!(
BuiltinMutation::MailboxCapacityExceeded.invariant(),
INVARIANT_MAILBOX
);
assert_eq!(
BuiltinMutation::CrossRegionRRefAccess.invariant(),
INVARIANT_RREF_ACCESS
);
#[cfg(feature = "messaging-fabric")]
{
assert_eq!(
BuiltinMutation::FabricPublishMissingSubscriberDelivery.invariant(),
INVARIANT_FABRIC_PUBLISH
);
assert_eq!(
BuiltinMutation::FabricReplyUnresolvedOnClose.invariant(),
INVARIANT_FABRIC_REPLY
);
assert_eq!(
BuiltinMutation::FabricQuiescenceBusyCellOnClose.invariant(),
INVARIANT_FABRIC_QUIESCENCE
);
assert_eq!(
BuiltinMutation::FabricRedeliveryBoundExceeded.invariant(),
INVARIANT_FABRIC_REDELIVERY
);
}
}
#[test]
fn builtin_mutation_equality_and_hash() {
let a = BuiltinMutation::TaskLeak;
let b = BuiltinMutation::TaskLeak;
let c = BuiltinMutation::Finalizer;
assert_eq!(a, b);
assert_ne!(a, c);
let mut set = HashSet::new();
set.insert(a);
set.insert(b);
assert_eq!(set.len(), 1);
}
#[test]
fn builtin_mutation_debug() {
assert!(format!("{:?}", BuiltinMutation::TaskLeak).contains("TaskLeak"));
}
#[test]
fn builtin_mutation_clone_copy() {
let a = BuiltinMutation::TaskLeak;
let b = a;
assert_eq!(a, b);
}
#[test]
fn baseline_task_leak_no_panic() {
let mut h = MetaHarness::new(42);
BuiltinMutation::TaskLeak.apply_baseline(&mut h);
}
#[test]
fn mutation_task_leak_no_panic() {
let mut h = MetaHarness::new(42);
BuiltinMutation::TaskLeak.apply_mutation(&mut h);
}
#[test]
fn baseline_all_mutations_no_panic() {
for m in builtin_mutations() {
let mut h = MetaHarness::new(42);
m.apply_baseline(&mut h);
}
}
#[test]
fn mutation_all_mutations_no_panic() {
for m in builtin_mutations() {
let mut h = MetaHarness::new(42);
m.apply_mutation(&mut h);
}
}
#[test]
fn baseline_produces_no_violations() {
for m in builtin_mutations() {
let mut h = MetaHarness::new(42);
m.apply_baseline(&mut h);
let v = h.oracles.check_all(h.now());
assert!(
v.is_empty(),
"baseline for {m:?} produced violations: {v:?}"
);
}
}
#[test]
fn mutation_produces_expected_violation() {
for m in builtin_mutations() {
let mut h = MetaHarness::new(42);
m.apply_mutation(&mut h);
let v = h.oracles.check_all(h.now());
let detected = v
.iter()
.any(|vv| invariant_from_violation(vv) == m.invariant());
let inv = m.invariant();
assert!(
detected,
"mutation {m:?} did not trigger expected invariant {inv}; got {v:?}"
);
}
}
}