From 8dc6e958aa305236054a43c0db74dda9176e0702 Mon Sep 17 00:00:00 2001 From: trinity-1686a <trinity@deuxfleurs.fr> Date: Thu, 24 Mar 2022 19:43:54 +0100 Subject: [PATCH] move isolation in separate module --- crates/tor-circmgr/src/isolation.rs | 329 +++++++++++++++++++++++++++ crates/tor-circmgr/src/lib.rs | 8 +- crates/tor-circmgr/src/mgr.rs | 2 +- crates/tor-circmgr/src/preemptive.rs | 2 +- crates/tor-circmgr/src/usage.rs | 325 +------------------------- 5 files changed, 336 insertions(+), 330 deletions(-) create mode 100644 crates/tor-circmgr/src/isolation.rs diff --git a/crates/tor-circmgr/src/isolation.rs b/crates/tor-circmgr/src/isolation.rs new file mode 100644 index 0000000000..05a0d423e8 --- /dev/null +++ b/crates/tor-circmgr/src/isolation.rs @@ -0,0 +1,329 @@ +//! Types related to stream isolation +use downcast_rs::{impl_downcast, Downcast}; +use dyn_clone::{clone_trait_object, DynClone}; +use std::sync::atomic::{AtomicU64, Ordering}; + +/// A type that can make isolation decisions about streams it is attached to. +/// +/// Types that implement `Isolation` contain properties about a stream that are +/// used to make decisions about whether that stream can share the same circuit +/// as other streams. You may pass in any type implementing `Isolation` when +/// creating a stream via `TorClient::connect_with_prefs`, or constructing a +/// circuit with [`CircMgr::get_or_launch_exit()`](crate::CircMgr::get_or_launch_exit). +/// +/// You typically do not want to implement this trait directly. Instead, most +/// users should implement [`IsolationHelper`]. +/// +// TODO this trait should probably be sealed so the same-type requirement can't be bypassed +pub trait Isolation: + seal::Sealed + Downcast + DynClone + std::fmt::Debug + Send + Sync + 'static +{ + /// Return true if this Isolation is compatible with another. + /// + /// Two streams may share a circuit if and only if they have compatible + /// `Isolation`s. + /// + /// # Requirements + /// + /// For correctness, this relation must be symmetrical and reflexive: + /// `self.compatible(other)` must equal `other.compatible(self)`, and + /// `self.compatible(self)` must be true. + /// + /// For correctness, this function must always give the same result as + /// `self.join(other).is_some()`. + /// + /// This relationship does **not** have to be transitive: it's possible that + /// stream A can share a circuit with either stream B or stream C, but not + /// with both. + fn compatible(&self, other: &dyn Isolation) -> bool; + + /// Join two [`Isolation`] into the intersection of what each allows. + /// + /// A circuit's isolation is the `join` of the isolation values of all of + /// the streams that have _ever_ used that circuit. A circuit's isolation + /// can never be `None`: streams that would cause it to be `None` can't be + /// attached to the circuit. + /// + /// When a stream is added to a circuit, `join` is used to calculate the + /// circuit's new isolation. + /// + /// # Requirements + /// + /// For correctness, this function must be commutative: `self.join(other)` + /// must equal `other.join(self)`. Also, it must be idempotent: + /// `self.join(self)` must equal self. + // + // TODO: (This function probably should be associative too, but we haven't done + // all the math.) + fn join(&self, other: &dyn Isolation) -> Option<Box<dyn Isolation>>; +} + +/// Seal preventing implementation of Isolation not relying on IsolationHelper +mod seal { + /// Seal preventing implementation of Isolation not relying on IsolationHelper + pub trait Sealed {} + impl<T: super::IsolationHelper> Sealed for T {} +} + +impl_downcast!(Isolation); +clone_trait_object!(Isolation); +impl<T: Isolation> From<T> for Box<dyn Isolation> { + fn from(isolation: T) -> Self { + Box::new(isolation) + } +} + +impl<T: IsolationHelper + Clone + std::fmt::Debug + Send + Sync + 'static> Isolation for T { + fn compatible(&self, other: &dyn Isolation) -> bool { + if let Some(other) = other.as_any().downcast_ref() { + self.compatible_same_type(other) + } else { + false + } + } + + fn join(&self, other: &dyn Isolation) -> Option<Box<dyn Isolation>> { + if let Some(other) = other.as_any().downcast_ref() { + self.join_same_type(other) + .map(|res| Box::new(res) as Box<dyn Isolation>) + } else { + None + } + } +} + +/// Trait to help implement [`Isolation`]. +/// +/// You should generally implement this trait whenever you need to implement a +/// new set of stream isolation rules: it takes care of down-casting and type +/// checking for you. +/// +/// When you implement this trait for some type T, isolation objects of that +/// type will be incompatible (unable to share circuits) with objects of _any +/// other type_. (That's usually what you want; if you're defining a new type +/// of Isolation rules, then you probably don't want streams using different +/// rules to share circuits with yours.) +pub trait IsolationHelper: Sized { + /// Returns whether self and other are compatible. + /// + /// Two streams may share a circuit if and only if they have compatible + /// `Isolation`s. + /// + /// (See [`Isolation::compatible`] for more information and requirements.) + fn compatible_same_type(&self, other: &Self) -> bool; + + /// Join self and other into the intersection of what they allows. + /// + /// (See [`Isolation::join`] for more information and requirements.) + fn join_same_type(&self, other: &Self) -> Option<Self>; +} + +/// A token used to isolate unrelated streams on different circuits. +/// +/// When two streams are associated with different isolation tokens, they +/// can never share the same circuit. +/// +/// Tokens created with [`IsolationToken::new`] are all different from +/// one another, and different from tokens created with +/// [`IsolationToken::no_isolation`]. However, tokens created with +/// [`IsolationToken::no_isolation`] are all equal to one another. +/// +/// # Examples +/// +/// Creating distinct isolation tokens: +/// +/// ```rust +/// # use tor_circmgr::IsolationToken; +/// let token_1 = IsolationToken::new(); +/// let token_2 = IsolationToken::new(); +/// +/// assert_ne!(token_1, token_2); +/// +/// // Demonstrating the behaviour of no_isolation() tokens: +/// assert_ne!(token_1, IsolationToken::no_isolation()); +/// assert_eq!(IsolationToken::no_isolation(), IsolationToken::no_isolation()); +/// ``` +/// +/// Using an isolation token to route streams differently over the Tor network: +/// +/// ```ignore +/// use arti_client::StreamPrefs; +/// +/// let token_1 = IsolationToken::new(); +/// let token_2 = IsolationToken::new(); +/// +/// let mut prefs_1 = StreamPrefs::new(); +/// prefs_1.set_isolation(token_1); +/// +/// let mut prefs_2 = StreamPrefs::new(); +/// prefs_2.set_isolation(token_2); +/// +/// // These two connections will come from different source IP addresses. +/// tor_client.connect(("example.com", 80), Some(prefs_1)).await?; +/// tor_client.connect(("example.com", 80), Some(prefs_2)).await?; +/// ``` +// # Semver note +// +// This type is re-exported by `arti-client`: any changes to it must be +// reflected in `arti-client`'s version. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct IsolationToken(u64); + +#[allow(clippy::new_without_default)] +impl IsolationToken { + /// Create a new IsolationToken, unequal to any other token this function + /// has created. + /// + /// # Panics + /// + /// Panics if we have already allocated 2^64 isolation tokens: in that + /// case, we have exhausted the space of possible tokens, and it is + /// no longer possible to ensure isolation. + pub fn new() -> Self { + /// Internal counter used to generate different tokens each time + static COUNTER: AtomicU64 = AtomicU64::new(1); + // Ordering::Relaxed is fine because we don't care about causality, we just want a + // different number each time + let token = COUNTER.fetch_add(1, Ordering::Relaxed); + assert!(token < u64::MAX); + IsolationToken(token) + } + + /// Create a new IsolationToken equal to every other token created + /// with this function, but different from all tokens created with + /// `new`. + /// + /// This can be used when no isolation is wanted for some streams. + pub fn no_isolation() -> Self { + IsolationToken(0) + } +} + +impl IsolationHelper for IsolationToken { + fn compatible_same_type(&self, other: &Self) -> bool { + self == other + } + fn join_same_type(&self, other: &Self) -> Option<Self> { + if self.compatible_same_type(other) { + Some(*self) + } else { + None + } + } +} + +#[cfg(test)] +pub(crate) mod test { + use super::*; + + /// Trait for testing use only. Much like PartialEq, but for type containing an dyn Isolation + /// which is known to be an IsolationToken. + pub(crate) trait IsolationTokenEq { + /// Compare two values, returning true if they are equals and all dyn Isolation they contain + /// are IsolationToken (which are equal too). + fn isol_eq(&self, other: &Self) -> bool; + } + + macro_rules! assert_isoleq { + { $arg1:expr, $arg2:expr } => { + assert!($arg1.isol_eq(&$arg2)) + } + } + pub(crate) use assert_isoleq; + + impl IsolationTokenEq for IsolationToken { + fn isol_eq(&self, other: &Self) -> bool { + self == other + } + } + + impl<T: IsolationTokenEq> IsolationTokenEq for Option<T> { + fn isol_eq(&self, other: &Self) -> bool { + match (self, other) { + (Some(this), Some(other)) => this.isol_eq(other), + (None, None) => true, + _ => false, + } + } + } + + impl<T: IsolationTokenEq + std::fmt::Debug> IsolationTokenEq for Vec<T> { + fn isol_eq(&self, other: &Self) -> bool { + if self.len() != other.len() { + return false; + } + self.iter() + .zip(other.iter()) + .all(|(this, other)| this.isol_eq(other)) + } + } + + impl IsolationTokenEq for dyn Isolation { + fn isol_eq(&self, other: &Self) -> bool { + let this = self.as_any().downcast_ref::<IsolationToken>(); + let other = other.as_any().downcast_ref::<IsolationToken>(); + match (this, other) { + (Some(this), Some(other)) => this == other, + _ => false, + } + } + } + + #[derive(PartialEq, Clone, Copy, Debug)] + struct OtherIsolation(usize); + + impl IsolationHelper for OtherIsolation { + fn compatible_same_type(&self, other: &Self) -> bool { + self == other + } + fn join_same_type(&self, other: &Self) -> Option<Self> { + if self.compatible_same_type(other) { + Some(*self) + } else { + None + } + } + } + + #[test] + fn isolation_token() { + let token_1 = IsolationToken::new(); + let token_2 = IsolationToken::new(); + + assert!(token_1.compatible_same_type(&token_1)); + assert!(token_2.compatible_same_type(&token_2)); + assert!(!token_1.compatible_same_type(&token_2)); + + assert_eq!(token_1.join_same_type(&token_1), Some(token_1)); + assert_eq!(token_2.join_same_type(&token_2), Some(token_2)); + assert_eq!(token_1.join_same_type(&token_2), None); + } + + #[test] + fn isolation_trait() { + let token_1: Box<dyn Isolation> = Box::new(IsolationToken::new()); + let token_2: Box<dyn Isolation> = Box::new(IsolationToken::new()); + let other_1: Box<dyn Isolation> = Box::new(OtherIsolation(0)); + let other_2: Box<dyn Isolation> = Box::new(OtherIsolation(1)); + + assert!(token_1.compatible(token_1.as_ref())); + assert!(token_2.compatible(token_2.as_ref())); + assert!(!token_1.compatible(token_2.as_ref())); + + assert!(other_1.compatible(other_1.as_ref())); + assert!(other_2.compatible(other_2.as_ref())); + assert!(!other_1.compatible(other_2.as_ref())); + + assert!(!token_1.compatible(other_1.as_ref())); + assert!(!other_1.compatible(token_1.as_ref())); + + assert!(token_1.join(token_1.as_ref()).is_some()); + assert!(token_1.join(token_2.as_ref()).is_none()); + + assert!(other_1.join(other_1.as_ref()).is_some()); + assert!(other_1.join(other_2.as_ref()).is_none()); + + assert!(token_1.join(other_1.as_ref()).is_none()); + assert!(other_1.join(token_1.as_ref()).is_none()); + } +} diff --git a/crates/tor-circmgr/src/lib.rs b/crates/tor-circmgr/src/lib.rs index f1897785c4..13e330b0e0 100644 --- a/crates/tor-circmgr/src/lib.rs +++ b/crates/tor-circmgr/src/lib.rs @@ -64,6 +64,7 @@ pub mod build; mod config; mod err; mod impls; +pub mod isolation; mod mgr; pub mod path; mod preemptive; @@ -71,11 +72,8 @@ mod timeouts; mod usage; pub use err::Error; -pub use usage::{IsolationToken, StreamIsolation, StreamIsolationBuilder, TargetPort, TargetPorts}; -/// Types related to stream isolation -pub mod isolation { - pub use crate::usage::{Isolation, IsolationHelper, IsolationToken}; -} +pub use isolation::IsolationToken; +pub use usage::{StreamIsolation, StreamIsolationBuilder, TargetPort, TargetPorts}; pub use config::{ CircMgrConfig, CircuitTiming, CircuitTimingBuilder, PathConfig, PathConfigBuilder, diff --git a/crates/tor-circmgr/src/mgr.rs b/crates/tor-circmgr/src/mgr.rs index e82014e5cc..08ec587d9a 100644 --- a/crates/tor-circmgr/src/mgr.rs +++ b/crates/tor-circmgr/src/mgr.rs @@ -1332,7 +1332,7 @@ fn spawn_expiration_task<B, R>( mod test { #![allow(clippy::unwrap_used)] use super::*; - use crate::usage::test::{assert_isoleq, IsolationTokenEq}; + use crate::isolation::test::{assert_isoleq, IsolationTokenEq}; use crate::usage::{ExitPolicy, SupportedCircUsage}; use crate::{Error, StreamIsolation, TargetCircUsage, TargetPort}; use std::collections::BTreeSet; diff --git a/crates/tor-circmgr/src/preemptive.rs b/crates/tor-circmgr/src/preemptive.rs index 533e61f971..7d8db2f872 100644 --- a/crates/tor-circmgr/src/preemptive.rs +++ b/crates/tor-circmgr/src/preemptive.rs @@ -90,7 +90,7 @@ mod test { use crate::{PreemptiveCircuitConfig, PreemptiveCircuitPredictor, TargetCircUsage, TargetPort}; use std::time::{Duration, Instant}; - use crate::usage::test::{assert_isoleq, IsolationTokenEq}; + use crate::isolation::test::{assert_isoleq, IsolationTokenEq}; #[test] fn predicts_starting_ports() { diff --git a/crates/tor-circmgr/src/usage.rs b/crates/tor-circmgr/src/usage.rs index 0b78b4cb77..91975a36c4 100644 --- a/crates/tor-circmgr/src/usage.rs +++ b/crates/tor-circmgr/src/usage.rs @@ -1,11 +1,8 @@ //! Code related to tracking what activities a circuit can be used for. -use downcast_rs::{impl_downcast, Downcast}; -use dyn_clone::{clone_trait_object, DynClone}; use rand::Rng; use serde::{Deserialize, Serialize}; use std::fmt::{self, Display}; -use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; use tor_error::bad_api_usage; use tracing::debug; @@ -16,6 +13,7 @@ use tor_netdir::Relay; use tor_netdoc::types::policy::PortPolicy; use tor_rtcompat::Runtime; +use crate::isolation::{Isolation, IsolationToken}; use crate::mgr::{abstract_spec_find_supported, AbstractCirc, OpenEntry}; use crate::Result; @@ -102,215 +100,6 @@ impl Display for TargetPorts { } } -/// A type that can make isolation decisions about streams it is attached to. -/// -/// Types that implement `Isolation` contain properties about a stream that are -/// used to make decisions about whether that stream can share the same circuit -/// as other streams. You may pass in any type implementing `Isolation` when -/// creating a stream via `TorClient::connect_with_prefs`, or constructing a -/// circuit with [`CircMgr::get_or_launch_exit()`](crate::CircMgr::get_or_launch_exit). -/// -/// You typically do not want to implement this trait directly. Instead, most -/// users should implement [`IsolationHelper`]. -/// -// TODO this trait should probably be sealed so the same-type requirement can't be bypassed -pub trait Isolation: - seal::Sealed + Downcast + DynClone + std::fmt::Debug + Send + Sync + 'static -{ - /// Return true if this Isolation is compatible with another. - /// - /// Two streams may share a circuit if and only if they have compatible - /// `Isolation`s. - /// - /// # Requirements - /// - /// For correctness, this relation must be symmetrical and reflexive: - /// `self.compatible(other)` must equal `other.compatible(self)`, and - /// `self.compatible(self)` must be true. - /// - /// For correctness, this function must always give the same result as - /// `self.join(other).is_some()`. - /// - /// This relationship does **not** have to be transitive: it's possible that - /// stream A can share a circuit with either stream B or stream C, but not - /// with both. - fn compatible(&self, other: &dyn Isolation) -> bool; - - /// Join two [`Isolation`] into the intersection of what each allows. - /// - /// A circuit's isolation is the `join` of the isolation values of all of - /// the streams that have _ever_ used that circuit. A circuit's isolation - /// can never be `None`: streams that would cause it to be `None` can't be - /// attached to the circuit. - /// - /// When a stream is added to a circuit, `join` is used to calculate the - /// circuit's new isolation. - /// - /// # Requirements - /// - /// For correctness, this function must be commutative: `self.join(other)` - /// must equal `other.join(self)`. Also, it must be idempotent: - /// `self.join(self)` must equal self. - // - // TODO: (This function probably should be associative too, but we haven't done - // all the math.) - fn join(&self, other: &dyn Isolation) -> Option<Box<dyn Isolation>>; -} - -/// Seal preventing implementation of Isolation not relying on IsolationHelper -mod seal { - /// Seal preventing implementation of Isolation not relying on IsolationHelper - pub trait Sealed {} - impl<T: super::IsolationHelper> Sealed for T {} -} - -impl_downcast!(Isolation); -clone_trait_object!(Isolation); -impl<T: Isolation> From<T> for Box<dyn Isolation> { - fn from(isolation: T) -> Self { - Box::new(isolation) - } -} - -impl<T: IsolationHelper + Clone + std::fmt::Debug + Send + Sync + 'static> Isolation for T { - fn compatible(&self, other: &dyn Isolation) -> bool { - if let Some(other) = other.as_any().downcast_ref() { - self.compatible_same_type(other) - } else { - false - } - } - - fn join(&self, other: &dyn Isolation) -> Option<Box<dyn Isolation>> { - if let Some(other) = other.as_any().downcast_ref() { - self.join_same_type(other) - .map(|res| Box::new(res) as Box<dyn Isolation>) - } else { - None - } - } -} - -/// Trait to help implement [`Isolation`]. -/// -/// You should generally implement this trait whenever you need to implement a -/// new set of stream isolation rules: it takes care of down-casting and type -/// checking for you. -/// -/// When you implement this trait for some type T, isolation objects of that -/// type will be incompatible (unable to share circuits) with objects of _any -/// other type_. (That's usually what you want; if you're defining a new type -/// of Isolation rules, then you probably don't want streams using different -/// rules to share circuits with yours.) -pub trait IsolationHelper: Sized { - /// Returns whether self and other are compatible. - /// - /// Two streams may share a circuit if and only if they have compatible - /// `Isolation`s. - /// - /// (See [`Isolation::compatible`] for more information and requirements.) - fn compatible_same_type(&self, other: &Self) -> bool; - - /// Join self and other into the intersection of what they allows. - /// - /// (See [`Isolation::join`] for more information and requirements.) - fn join_same_type(&self, other: &Self) -> Option<Self>; -} - -/// A token used to isolate unrelated streams on different circuits. -/// -/// When two streams are associated with different isolation tokens, they -/// can never share the same circuit. -/// -/// Tokens created with [`IsolationToken::new`] are all different from -/// one another, and different from tokens created with -/// [`IsolationToken::no_isolation`]. However, tokens created with -/// [`IsolationToken::no_isolation`] are all equal to one another. -/// -/// # Examples -/// -/// Creating distinct isolation tokens: -/// -/// ```rust -/// # use tor_circmgr::IsolationToken; -/// let token_1 = IsolationToken::new(); -/// let token_2 = IsolationToken::new(); -/// -/// assert_ne!(token_1, token_2); -/// -/// // Demonstrating the behaviour of no_isolation() tokens: -/// assert_ne!(token_1, IsolationToken::no_isolation()); -/// assert_eq!(IsolationToken::no_isolation(), IsolationToken::no_isolation()); -/// ``` -/// -/// Using an isolation token to route streams differently over the Tor network: -/// -/// ```ignore -/// use arti_client::StreamPrefs; -/// -/// let token_1 = IsolationToken::new(); -/// let token_2 = IsolationToken::new(); -/// -/// let mut prefs_1 = StreamPrefs::new(); -/// prefs_1.set_isolation(token_1); -/// -/// let mut prefs_2 = StreamPrefs::new(); -/// prefs_2.set_isolation(token_2); -/// -/// // These two connections will come from different source IP addresses. -/// tor_client.connect(("example.com", 80), Some(prefs_1)).await?; -/// tor_client.connect(("example.com", 80), Some(prefs_2)).await?; -/// ``` -// # Semver note -// -// This type is re-exported by `arti-client`: any changes to it must be -// reflected in `arti-client`'s version. -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct IsolationToken(u64); - -#[allow(clippy::new_without_default)] -impl IsolationToken { - /// Create a new IsolationToken, unequal to any other token this function - /// has created. - /// - /// # Panics - /// - /// Panics if we have already allocated 2^64 isolation tokens: in that - /// case, we have exhausted the space of possible tokens, and it is - /// no longer possible to ensure isolation. - pub fn new() -> Self { - /// Internal counter used to generate different tokens each time - static COUNTER: AtomicU64 = AtomicU64::new(1); - // Ordering::Relaxed is fine because we don't care about causality, we just want a - // different number each time - let token = COUNTER.fetch_add(1, Ordering::Relaxed); - assert!(token < u64::MAX); - IsolationToken(token) - } - - /// Create a new IsolationToken equal to every other token created - /// with this function, but different from all tokens created with - /// `new`. - /// - /// This can be used when no isolation is wanted for some streams. - pub fn no_isolation() -> Self { - IsolationToken(0) - } -} - -impl IsolationHelper for IsolationToken { - fn compatible_same_type(&self, other: &Self) -> bool { - self == other - } - fn join_same_type(&self, other: &Self) -> Option<Self> { - if self.compatible_same_type(other) { - Some(*self) - } else { - None - } - } -} - /// A set of information about how a stream should be isolated. /// /// If two streams are isolated from one another, they may not share @@ -626,58 +415,13 @@ impl crate::mgr::AbstractSpec for SupportedCircUsage { pub(crate) mod test { #![allow(clippy::unwrap_used)] use super::*; + use crate::isolation::test::{assert_isoleq, IsolationTokenEq}; use crate::path::OwnedPath; use crate::test::OptDummyGuardMgr; use std::convert::TryFrom; use tor_linkspec::ChanTarget; use tor_netdir::testnet; - /// Trait for testing use only. Much like PartialEq, but for type containing an dyn Isolation - /// which is known to be an IsolationToken. - pub(crate) trait IsolationTokenEq { - /// Compare two values, returning true if they are equals and all dyn Isolation they contain - /// are IsolationToken (which are equal too). - fn isol_eq(&self, other: &Self) -> bool; - } - - impl IsolationTokenEq for IsolationToken { - fn isol_eq(&self, other: &Self) -> bool { - self == other - } - } - - impl<T: IsolationTokenEq> IsolationTokenEq for Option<T> { - fn isol_eq(&self, other: &Self) -> bool { - match (self, other) { - (Some(this), Some(other)) => this.isol_eq(other), - (None, None) => true, - _ => false, - } - } - } - - impl<T: IsolationTokenEq + std::fmt::Debug> IsolationTokenEq for Vec<T> { - fn isol_eq(&self, other: &Self) -> bool { - if self.len() != other.len() { - return false; - } - self.iter() - .zip(other.iter()) - .all(|(this, other)| this.isol_eq(other)) - } - } - - impl IsolationTokenEq for dyn Isolation { - fn isol_eq(&self, other: &Self) -> bool { - let this = self.as_any().downcast_ref::<IsolationToken>(); - let other = other.as_any().downcast_ref::<IsolationToken>(); - match (this, other) { - (Some(this), Some(other)) => this == other, - _ => false, - } - } - } - impl IsolationTokenEq for StreamIsolation { fn isol_eq(&self, other: &Self) -> bool { self.stream_token.isol_eq(other.stream_token.as_ref()) @@ -737,13 +481,6 @@ pub(crate) mod test { } } - macro_rules! assert_isoleq { - { $arg1:expr, $arg2:expr } => { - assert!($arg1.isol_eq(&$arg2)) - } - } - pub(crate) use assert_isoleq; - #[test] fn exit_policy() { use tor_netdir::testnet::construct_custom_netdir; @@ -1121,62 +858,4 @@ pub(crate) mod test { let ports = [TargetPort::ipv4(80), TargetPort::ipv6(443)]; assert_eq!(TargetPorts::from(&ports[..]).to_string(), "[80v4,443v6]"); } - - #[test] - fn isolation_token() { - let token_1 = IsolationToken::new(); - let token_2 = IsolationToken::new(); - - assert!(token_1.compatible_same_type(&token_1)); - assert!(token_2.compatible_same_type(&token_2)); - assert!(!token_1.compatible_same_type(&token_2)); - - assert_eq!(token_1.join_same_type(&token_1), Some(token_1)); - assert_eq!(token_2.join_same_type(&token_2), Some(token_2)); - assert_eq!(token_1.join_same_type(&token_2), None); - } - - #[derive(PartialEq, Clone, Copy, Debug)] - struct OtherIsolation(usize); - - impl IsolationHelper for OtherIsolation { - fn compatible_same_type(&self, other: &Self) -> bool { - self == other - } - fn join_same_type(&self, other: &Self) -> Option<Self> { - if self.compatible_same_type(other) { - Some(*self) - } else { - None - } - } - } - - #[test] - fn isolation_trait() { - let token_1: Box<dyn Isolation> = Box::new(IsolationToken::new()); - let token_2: Box<dyn Isolation> = Box::new(IsolationToken::new()); - let other_1: Box<dyn Isolation> = Box::new(OtherIsolation(0)); - let other_2: Box<dyn Isolation> = Box::new(OtherIsolation(1)); - - assert!(token_1.compatible(token_1.as_ref())); - assert!(token_2.compatible(token_2.as_ref())); - assert!(!token_1.compatible(token_2.as_ref())); - - assert!(other_1.compatible(other_1.as_ref())); - assert!(other_2.compatible(other_2.as_ref())); - assert!(!other_1.compatible(other_2.as_ref())); - - assert!(!token_1.compatible(other_1.as_ref())); - assert!(!other_1.compatible(token_1.as_ref())); - - assert!(token_1.join(token_1.as_ref()).is_some()); - assert!(token_1.join(token_2.as_ref()).is_none()); - - assert!(other_1.join(other_1.as_ref()).is_some()); - assert!(other_1.join(other_2.as_ref()).is_none()); - - assert!(token_1.join(other_1.as_ref()).is_none()); - assert!(other_1.join(token_1.as_ref()).is_none()); - } } -- GitLab