use super::relay_state_snapshot::{MessagingStateSnapshot, RelayDispatchQueueRemainingCapacity};
use alloc::collections::btree_map::BTreeMap;
use codec::{Decode, Encode};
use core::marker::PhantomData;
use cumulus_primitives_core::{relay_chain, ParaId};
use scale_info::TypeInfo;
use sp_runtime::RuntimeDebug;
#[derive(Clone, RuntimeDebug)]
pub struct HrmpOutboundLimits {
pub bytes_remaining: u32,
pub messages_remaining: u32,
}
#[derive(Clone, RuntimeDebug)]
pub struct OutboundBandwidthLimits {
pub ump_messages_remaining: u32,
pub ump_bytes_remaining: u32,
pub hrmp_outgoing: BTreeMap<ParaId, HrmpOutboundLimits>,
}
impl OutboundBandwidthLimits {
pub fn from_relay_chain_state(messaging_state: &MessagingStateSnapshot) -> Self {
let RelayDispatchQueueRemainingCapacity { remaining_count, remaining_size } =
messaging_state.relay_dispatch_queue_remaining_capacity;
let hrmp_outgoing = messaging_state
.egress_channels
.iter()
.map(|(id, channel)| {
(
*id,
HrmpOutboundLimits {
bytes_remaining: channel.max_total_size.saturating_sub(channel.total_size),
messages_remaining: channel.max_capacity.saturating_sub(channel.msg_count),
},
)
})
.collect();
Self {
ump_messages_remaining: remaining_count,
ump_bytes_remaining: remaining_size,
hrmp_outgoing,
}
}
}
#[derive(RuntimeDebug)]
pub enum BandwidthUpdateError {
HrmpMessagesOverflow {
recipient: ParaId,
messages_remaining: u32,
messages_submitted: u32,
},
HrmpBytesOverflow {
recipient: ParaId,
bytes_remaining: u32,
bytes_submitted: u32,
},
UmpMessagesOverflow {
messages_remaining: u32,
messages_submitted: u32,
},
UmpBytesOverflow {
bytes_remaining: u32,
bytes_submitted: u32,
},
InvalidHrmpWatermark {
submitted: relay_chain::BlockNumber,
latest: relay_chain::BlockNumber,
},
UpgradeGoAheadAlreadyProcessed,
}
#[derive(RuntimeDebug, Default, Copy, Clone, Encode, Decode, TypeInfo)]
pub struct HrmpChannelUpdate {
pub msg_count: u32,
pub total_bytes: u32,
}
impl HrmpChannelUpdate {
fn is_empty(&self) -> bool {
self.msg_count == 0 && self.total_bytes == 0
}
fn append(
&self,
other: &Self,
recipient: ParaId,
limits: &OutboundBandwidthLimits,
) -> Result<Self, BandwidthUpdateError> {
let limits = limits
.hrmp_outgoing
.get(&recipient)
.expect("limit for declared hrmp channel must be present; qed");
let mut new = *self;
new.msg_count = new.msg_count.saturating_add(other.msg_count);
if new.msg_count > limits.messages_remaining {
return Err(BandwidthUpdateError::HrmpMessagesOverflow {
recipient,
messages_remaining: limits.messages_remaining,
messages_submitted: new.msg_count,
})
}
new.total_bytes = new.total_bytes.saturating_add(other.total_bytes);
if new.total_bytes > limits.bytes_remaining {
return Err(BandwidthUpdateError::HrmpBytesOverflow {
recipient,
bytes_remaining: limits.bytes_remaining,
bytes_submitted: new.total_bytes,
})
}
Ok(new)
}
fn subtract(&mut self, other: &Self) {
self.msg_count -= other.msg_count;
self.total_bytes -= other.total_bytes;
}
}
#[derive(Default, Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
pub struct UsedBandwidth {
pub ump_msg_count: u32,
pub ump_total_bytes: u32,
pub hrmp_outgoing: BTreeMap<ParaId, HrmpChannelUpdate>,
}
impl UsedBandwidth {
fn append(
&self,
other: &Self,
limits: &OutboundBandwidthLimits,
) -> Result<Self, BandwidthUpdateError> {
let mut new = self.clone();
new.ump_msg_count = new.ump_msg_count.saturating_add(other.ump_msg_count);
if new.ump_msg_count > limits.ump_messages_remaining {
return Err(BandwidthUpdateError::UmpMessagesOverflow {
messages_remaining: limits.ump_messages_remaining,
messages_submitted: new.ump_msg_count,
})
}
new.ump_total_bytes = new.ump_total_bytes.saturating_add(other.ump_total_bytes);
if new.ump_total_bytes > limits.ump_bytes_remaining {
return Err(BandwidthUpdateError::UmpBytesOverflow {
bytes_remaining: limits.ump_bytes_remaining,
bytes_submitted: new.ump_total_bytes,
})
}
for (id, channel) in other.hrmp_outgoing.iter() {
let current = new.hrmp_outgoing.entry(*id).or_default();
*current = current.append(channel, *id, limits)?;
}
Ok(new)
}
fn subtract(&mut self, other: &Self) {
self.ump_msg_count -= other.ump_msg_count;
self.ump_total_bytes -= other.ump_total_bytes;
for (id, channel) in other.hrmp_outgoing.iter() {
let entry = self
.hrmp_outgoing
.get_mut(id)
.expect("entry's been inserted earlier with `append`; qed");
entry.subtract(channel);
}
self.hrmp_outgoing.retain(|_, channel| !channel.is_empty());
}
}
#[derive(Encode, Decode, TypeInfo, RuntimeDebug)]
pub struct Ancestor<H> {
used_bandwidth: UsedBandwidth,
para_head_hash: Option<H>,
consumed_go_ahead_signal: Option<relay_chain::UpgradeGoAhead>,
}
impl<H> Ancestor<H> {
pub fn new_unchecked(
used_bandwidth: UsedBandwidth,
consumed_go_ahead_signal: Option<relay_chain::UpgradeGoAhead>,
) -> Self {
Self { used_bandwidth, para_head_hash: None, consumed_go_ahead_signal }
}
pub fn used_bandwidth(&self) -> &UsedBandwidth {
&self.used_bandwidth
}
pub fn para_head_hash(&self) -> Option<&H> {
self.para_head_hash.as_ref()
}
pub fn replace_para_head_hash(&mut self, para_head_hash: H) {
self.para_head_hash.replace(para_head_hash);
}
}
pub enum HrmpWatermarkUpdate {
Head(relay_chain::BlockNumber),
Trunk(relay_chain::BlockNumber),
}
impl HrmpWatermarkUpdate {
pub fn new(
watermark: relay_chain::BlockNumber,
relay_parent_number: relay_chain::BlockNumber,
) -> Self {
if watermark >= relay_parent_number {
HrmpWatermarkUpdate::Head(relay_parent_number)
} else {
HrmpWatermarkUpdate::Trunk(watermark)
}
}
}
#[derive(Default, Encode, Decode, TypeInfo, RuntimeDebug)]
pub struct SegmentTracker<H> {
used_bandwidth: UsedBandwidth,
hrmp_watermark: Option<relay_chain::BlockNumber>,
consumed_go_ahead_signal: Option<relay_chain::UpgradeGoAhead>,
phantom_data: PhantomData<H>,
}
impl<H> SegmentTracker<H> {
pub fn append(
&mut self,
block: &Ancestor<H>,
new_watermark: HrmpWatermarkUpdate,
limits: &OutboundBandwidthLimits,
) -> Result<(), BandwidthUpdateError> {
if self.consumed_go_ahead_signal.is_some() && block.consumed_go_ahead_signal.is_some() {
return Err(BandwidthUpdateError::UpgradeGoAheadAlreadyProcessed)
}
if let Some(watermark) = self.hrmp_watermark.as_ref() {
if let HrmpWatermarkUpdate::Trunk(new) = new_watermark {
if &new <= watermark {
return Err(BandwidthUpdateError::InvalidHrmpWatermark {
submitted: new,
latest: *watermark,
})
}
}
}
self.used_bandwidth = self.used_bandwidth.append(block.used_bandwidth(), limits)?;
if let Some(consumed) = block.consumed_go_ahead_signal.as_ref() {
self.consumed_go_ahead_signal.replace(*consumed);
}
self.hrmp_watermark.replace(match new_watermark {
HrmpWatermarkUpdate::Trunk(w) | HrmpWatermarkUpdate::Head(w) => w,
});
Ok(())
}
pub fn subtract(&mut self, block: &Ancestor<H>) {
self.used_bandwidth.subtract(block.used_bandwidth());
if let Some(consumed) = block.consumed_go_ahead_signal.as_ref() {
let signal_in_segment = self.consumed_go_ahead_signal.take();
assert_eq!(signal_in_segment, Some(*consumed));
}
}
pub fn used_bandwidth(&self) -> &UsedBandwidth {
&self.used_bandwidth
}
pub fn consumed_go_ahead_signal(&self) -> Option<relay_chain::UpgradeGoAhead> {
self.consumed_go_ahead_signal
}
}
pub(crate) fn size_after_included<H: PartialEq>(included_hash: H, segment: &[Ancestor<H>]) -> u32 {
let pivot = segment
.iter()
.position(|ancestor| ancestor.para_head_hash() == Some(&included_hash))
.map(|p| p + 1)
.unwrap_or(0);
(segment.len() - pivot) as u32
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::{vec, vec::Vec};
use assert_matches::assert_matches;
#[test]
fn outbound_limits_constructed_correctly() {
let para_a = ParaId::from(0);
let para_a_channel = relay_chain::AbridgedHrmpChannel {
max_message_size: 15,
msg_count: 5,
max_capacity: 7,
total_size: 50,
max_total_size: 60,
mqc_head: None,
};
let para_b = ParaId::from(1);
let para_b_channel = relay_chain::AbridgedHrmpChannel {
max_message_size: 15,
msg_count: 40,
max_capacity: 50,
total_size: 500,
max_total_size: 500,
mqc_head: None,
};
let relay_dispatch_queue_remaining_capacity =
RelayDispatchQueueRemainingCapacity { remaining_count: 1, remaining_size: 50 };
let messaging_state = MessagingStateSnapshot {
dmq_mqc_head: relay_chain::Hash::zero(),
relay_dispatch_queue_remaining_capacity,
ingress_channels: Vec::new(),
egress_channels: vec![(para_a, para_a_channel), (para_b, para_b_channel)],
};
let limits = OutboundBandwidthLimits::from_relay_chain_state(&messaging_state);
assert_eq!(limits.ump_messages_remaining, 1);
assert_eq!(limits.ump_bytes_remaining, 50);
let para_a_limits = limits.hrmp_outgoing.get(¶_a).expect("channel must be present");
let para_b_limits = limits.hrmp_outgoing.get(¶_b).expect("channel must be present");
assert_eq!(para_a_limits.bytes_remaining, 10);
assert_eq!(para_a_limits.messages_remaining, 2);
assert_eq!(para_b_limits.bytes_remaining, 0);
assert_eq!(para_b_limits.messages_remaining, 10);
}
#[test]
fn hrmp_msg_count_limits() {
let para_0 = ParaId::from(0);
let para_0_limits = HrmpOutboundLimits { bytes_remaining: u32::MAX, messages_remaining: 5 };
let para_1 = ParaId::from(1);
let para_1_limits = HrmpOutboundLimits { bytes_remaining: u32::MAX, messages_remaining: 3 };
let hrmp_outgoing = [(para_0, para_0_limits), (para_1, para_1_limits)].into();
let limits = OutboundBandwidthLimits {
ump_messages_remaining: 0,
ump_bytes_remaining: 0,
hrmp_outgoing,
};
let mut hrmp_update = HrmpChannelUpdate::default();
assert!(hrmp_update.is_empty());
for _ in 0..5 {
hrmp_update = hrmp_update
.append(&HrmpChannelUpdate { msg_count: 1, total_bytes: 10 }, para_0, &limits)
.expect("update is within the limits");
}
assert_matches!(
hrmp_update.append(
&HrmpChannelUpdate { msg_count: 1, total_bytes: 10 },
para_0,
&limits,
),
Err(BandwidthUpdateError::HrmpMessagesOverflow {
recipient,
messages_remaining,
messages_submitted,
}) if recipient == para_0 && messages_remaining == 5 && messages_submitted == 6
);
let mut hrmp_update = HrmpChannelUpdate::default();
hrmp_update = hrmp_update
.append(&HrmpChannelUpdate { msg_count: 2, total_bytes: 10 }, para_1, &limits)
.expect("update is within the limits");
assert_matches!(
hrmp_update.append(
&HrmpChannelUpdate { msg_count: 3, total_bytes: 10 },
para_1,
&limits,
),
Err(BandwidthUpdateError::HrmpMessagesOverflow {
recipient,
messages_remaining,
messages_submitted,
}) if recipient == para_1 && messages_remaining == 3 && messages_submitted == 5
);
}
#[test]
fn hrmp_bytes_limits() {
let para_0 = ParaId::from(0);
let para_0_limits =
HrmpOutboundLimits { bytes_remaining: 25, messages_remaining: u32::MAX };
let hrmp_outgoing = [(para_0, para_0_limits)].into();
let limits = OutboundBandwidthLimits {
ump_messages_remaining: 0,
ump_bytes_remaining: 0,
hrmp_outgoing,
};
let mut hrmp_update = HrmpChannelUpdate::default();
assert!(hrmp_update.is_empty());
for _ in 0..5 {
hrmp_update = hrmp_update
.append(&HrmpChannelUpdate { msg_count: 1, total_bytes: 4 }, para_0, &limits)
.expect("update is within the limits");
}
assert_matches!(
hrmp_update.append(
&HrmpChannelUpdate { msg_count: 1, total_bytes: 6 },
para_0,
&limits,
),
Err(BandwidthUpdateError::HrmpBytesOverflow {
recipient,
bytes_remaining,
bytes_submitted,
}) if recipient == para_0 && bytes_remaining == 25 && bytes_submitted == 26
);
}
#[test]
fn hrmp_limits_with_segment() {
let create_used_hrmp =
|hrmp_outgoing| UsedBandwidth { ump_msg_count: 0, ump_total_bytes: 0, hrmp_outgoing };
let para_0 = ParaId::from(0);
let para_0_limits = HrmpOutboundLimits { bytes_remaining: 30, messages_remaining: 10 };
let para_1 = ParaId::from(1);
let para_1_limits = HrmpOutboundLimits { bytes_remaining: 20, messages_remaining: 3 };
let hrmp_outgoing = [(para_0, para_0_limits), (para_1, para_1_limits)].into();
let limits = OutboundBandwidthLimits {
ump_messages_remaining: 0,
ump_bytes_remaining: 0,
hrmp_outgoing,
};
let mut segment = SegmentTracker::default();
let para_0_update = HrmpChannelUpdate { msg_count: 1, total_bytes: 6 };
let ancestor_0 = Ancestor {
used_bandwidth: create_used_hrmp([(para_0, para_0_update)].into()),
para_head_hash: None::<relay_chain::Hash>,
consumed_go_ahead_signal: None,
};
segment
.append(&ancestor_0, HrmpWatermarkUpdate::Trunk(0), &limits)
.expect("update is within the limits");
for watermark in 1..5 {
let ancestor = Ancestor {
used_bandwidth: create_used_hrmp([(para_0, para_0_update)].into()),
para_head_hash: None::<relay_chain::Hash>,
consumed_go_ahead_signal: None,
};
segment
.append(&ancestor, HrmpWatermarkUpdate::Trunk(watermark), &limits)
.expect("update is within the limits");
}
let para_0_update = HrmpChannelUpdate { msg_count: 1, total_bytes: 1 };
let ancestor_5 = Ancestor {
used_bandwidth: create_used_hrmp([(para_0, para_0_update)].into()),
para_head_hash: None::<relay_chain::Hash>,
consumed_go_ahead_signal: None,
};
assert_matches!(
segment.append(&ancestor_5, HrmpWatermarkUpdate::Trunk(5), &limits),
Err(BandwidthUpdateError::HrmpBytesOverflow {
recipient,
bytes_remaining,
bytes_submitted,
}) if recipient == para_0 && bytes_remaining == 30 && bytes_submitted == 31
);
segment.subtract(&ancestor_0);
segment
.append(&ancestor_5, HrmpWatermarkUpdate::Trunk(5), &limits)
.expect("update is within the limits");
let para_1_update = HrmpChannelUpdate { msg_count: 3, total_bytes: 10 };
let ancestor = Ancestor {
used_bandwidth: create_used_hrmp([(para_1, para_1_update)].into()),
para_head_hash: None::<relay_chain::Hash>,
consumed_go_ahead_signal: None,
};
segment
.append(&ancestor, HrmpWatermarkUpdate::Trunk(6), &limits)
.expect("update is within the limits");
assert_matches!(
segment.append(&ancestor, HrmpWatermarkUpdate::Trunk(7), &limits),
Err(BandwidthUpdateError::HrmpMessagesOverflow {
recipient,
messages_remaining,
messages_submitted,
}) if recipient == para_1 && messages_remaining == 3 && messages_submitted == 6
);
}
#[test]
fn ump_limits_with_segment() {
let create_used_ump = |(ump_msg_count, ump_total_bytes)| UsedBandwidth {
ump_msg_count,
ump_total_bytes,
hrmp_outgoing: BTreeMap::default(),
};
let limits = OutboundBandwidthLimits {
ump_messages_remaining: 5,
ump_bytes_remaining: 50,
hrmp_outgoing: BTreeMap::default(),
};
let mut segment = SegmentTracker::default();
let ancestor_0 = Ancestor {
used_bandwidth: create_used_ump((1, 10)),
para_head_hash: None::<relay_chain::Hash>,
consumed_go_ahead_signal: None,
};
segment
.append(&ancestor_0, HrmpWatermarkUpdate::Trunk(0), &limits)
.expect("update is within the limits");
for watermark in 1..4 {
let ancestor = Ancestor {
used_bandwidth: create_used_ump((1, 10)),
para_head_hash: None::<relay_chain::Hash>,
consumed_go_ahead_signal: None,
};
segment
.append(&ancestor, HrmpWatermarkUpdate::Trunk(watermark), &limits)
.expect("update is within the limits");
}
let ancestor_4 = Ancestor {
used_bandwidth: create_used_ump((1, 30)),
para_head_hash: None::<relay_chain::Hash>,
consumed_go_ahead_signal: None,
};
assert_matches!(
segment.append(&ancestor_4, HrmpWatermarkUpdate::Trunk(4), &limits),
Err(BandwidthUpdateError::UmpBytesOverflow {
bytes_remaining,
bytes_submitted,
}) if bytes_remaining == 50 && bytes_submitted == 70
);
let ancestor = Ancestor {
used_bandwidth: create_used_ump((1, 5)),
para_head_hash: None::<relay_chain::Hash>,
consumed_go_ahead_signal: None,
};
segment
.append(&ancestor, HrmpWatermarkUpdate::Trunk(4), &limits)
.expect("update is within the limits");
assert_matches!(
segment.append(&ancestor, HrmpWatermarkUpdate::Trunk(5), &limits),
Err(BandwidthUpdateError::UmpMessagesOverflow {
messages_remaining,
messages_submitted,
}) if messages_remaining == 5 && messages_submitted == 6
);
}
#[test]
fn segment_hrmp_watermark() {
let mut segment = SegmentTracker::default();
let ancestor = Ancestor {
used_bandwidth: UsedBandwidth::default(),
para_head_hash: None::<relay_chain::Hash>,
consumed_go_ahead_signal: None,
};
let limits = OutboundBandwidthLimits {
ump_messages_remaining: 0,
ump_bytes_remaining: 0,
hrmp_outgoing: BTreeMap::default(),
};
segment
.append(&ancestor, HrmpWatermarkUpdate::Head(0), &limits)
.expect("nothing to compare the watermark with in default segment");
assert_matches!(
segment.append(&ancestor, HrmpWatermarkUpdate::Trunk(0), &limits),
Err(BandwidthUpdateError::InvalidHrmpWatermark {
submitted,
latest,
}) if submitted == 0 && latest == 0
);
for watermark in 1..5 {
segment
.append(&ancestor, HrmpWatermarkUpdate::Trunk(watermark), &limits)
.expect("hrmp watermark is valid");
}
for watermark in 0..5 {
assert_matches!(
segment.append(&ancestor, HrmpWatermarkUpdate::Trunk(watermark), &limits),
Err(BandwidthUpdateError::InvalidHrmpWatermark {
submitted,
latest,
}) if submitted == watermark && latest == 4
);
}
segment
.append(&ancestor, HrmpWatermarkUpdate::Head(4), &limits)
.expect("head updates always valid");
}
#[test]
fn segment_drops_empty_hrmp_channels() {
let create_used_hrmp =
|hrmp_outgoing| UsedBandwidth { ump_msg_count: 0, ump_total_bytes: 0, hrmp_outgoing };
let para_0 = ParaId::from(0);
let para_0_limits =
HrmpOutboundLimits { bytes_remaining: u32::MAX, messages_remaining: u32::MAX };
let para_1 = ParaId::from(1);
let para_1_limits =
HrmpOutboundLimits { bytes_remaining: u32::MAX, messages_remaining: u32::MAX };
let hrmp_outgoing = [(para_0, para_0_limits), (para_1, para_1_limits)].into();
let limits = OutboundBandwidthLimits {
ump_messages_remaining: 0,
ump_bytes_remaining: 0,
hrmp_outgoing,
};
let mut segment = SegmentTracker::default();
let para_0_update = HrmpChannelUpdate { msg_count: 1, total_bytes: 1 };
let ancestor_0 = Ancestor {
used_bandwidth: create_used_hrmp([(para_0, para_0_update)].into()),
para_head_hash: None::<relay_chain::Hash>,
consumed_go_ahead_signal: None,
};
segment
.append(&ancestor_0, HrmpWatermarkUpdate::Head(0), &limits)
.expect("update is within the limits");
let para_1_update = HrmpChannelUpdate { msg_count: 3, total_bytes: 10 };
let ancestor_1 = Ancestor {
used_bandwidth: create_used_hrmp([(para_1, para_1_update)].into()),
para_head_hash: None::<relay_chain::Hash>,
consumed_go_ahead_signal: None,
};
segment
.append(&ancestor_1, HrmpWatermarkUpdate::Head(1), &limits)
.expect("update is within the limits");
assert_eq!(segment.used_bandwidth.hrmp_outgoing.len(), 2);
segment.subtract(&ancestor_0);
assert_eq!(segment.used_bandwidth.hrmp_outgoing.len(), 1);
segment.subtract(&ancestor_1);
assert_eq!(segment.used_bandwidth.hrmp_outgoing.len(), 0);
}
#[test]
fn segment_go_ahead_signal_is_unique() {
let limits = OutboundBandwidthLimits {
ump_messages_remaining: 0,
ump_bytes_remaining: 0,
hrmp_outgoing: BTreeMap::default(),
};
let mut segment = SegmentTracker::default();
let ancestor_0 = Ancestor {
used_bandwidth: UsedBandwidth::default(),
para_head_hash: None::<relay_chain::Hash>,
consumed_go_ahead_signal: Some(relay_chain::UpgradeGoAhead::GoAhead),
};
segment
.append(&ancestor_0, HrmpWatermarkUpdate::Head(0), &limits)
.expect("update is within the limits");
let ancestor_1 = Ancestor {
used_bandwidth: UsedBandwidth::default(),
para_head_hash: None::<relay_chain::Hash>,
consumed_go_ahead_signal: None,
};
segment
.append(&ancestor_1, HrmpWatermarkUpdate::Head(1), &limits)
.expect("update is within the limits");
let ancestor_2 = Ancestor {
used_bandwidth: UsedBandwidth::default(),
para_head_hash: None::<relay_chain::Hash>,
consumed_go_ahead_signal: Some(relay_chain::UpgradeGoAhead::Abort),
};
assert_matches!(
segment.append(&ancestor_2, HrmpWatermarkUpdate::Head(2), &limits),
Err(BandwidthUpdateError::UpgradeGoAheadAlreadyProcessed)
);
segment.subtract(&ancestor_0);
segment
.append(&ancestor_2, HrmpWatermarkUpdate::Head(1), &limits)
.expect("update is within the limits");
}
#[test]
fn size_after_included_works() {
let segment = vec![
Ancestor {
used_bandwidth: Default::default(),
para_head_hash: Some("a"),
consumed_go_ahead_signal: None,
},
Ancestor {
used_bandwidth: Default::default(),
para_head_hash: Some("b"),
consumed_go_ahead_signal: None,
},
Ancestor {
used_bandwidth: Default::default(),
para_head_hash: Some("c"),
consumed_go_ahead_signal: None,
},
];
assert_eq!(size_after_included("a", &segment), 2,);
assert_eq!(size_after_included("b", &segment), 1,);
assert_eq!(size_after_included("c", &segment), 0,);
assert_eq!(size_after_included("d", &segment), 3,);
assert_eq!(size_after_included("x", &[]), 0,);
}
}