diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ed56c1f..75288eb5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,8 +16,8 @@ jobs: steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - - run: cargo build --verbose - - run: cargo test --features=serde --verbose + - run: cargo build --verbose --workspace + - run: cargo test --features serde --workspace --verbose clippy: name: No warnings from Clippy @@ -30,7 +30,7 @@ jobs: - name: Check Clippy lints env: RUSTFLAGS: -D warnings - run: cargo clippy + run: cargo clippy --workspace check_formatting: name: Source code is formatted @@ -40,7 +40,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: components: rustfmt - - run: cargo fmt --all -- --check + - run: cargo fmt --workspace -- --check check_documentation: name: Documentation builds successfully @@ -51,4 +51,4 @@ jobs: - name: Check documentation env: RUSTDOCFLAGS: -D warnings - run: cargo doc --no-deps --document-private-items + run: cargo doc --workspace --no-deps --document-private-items diff --git a/Cargo.lock b/Cargo.lock index edc73e7e..d896bf8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -574,11 +574,11 @@ dependencies = [ "log", "priority-queue", "proptest", - "ron", "rustc-hash 2.0.0", "serde", "thiserror", "varisat", + "version-range", ] [[package]] @@ -751,18 +751,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -785,6 +785,9 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "syn" @@ -973,6 +976,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ed610a8d5e63d9c0e31300e8fdb55104c5f21e422743a9dc74848fa8317fd2" +[[package]] +name = "version-range" +version = "0.1.0" +dependencies = [ + "proptest", + "ron", + "serde", + "smallvec", +] + [[package]] name = "wait-timeout" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 9f502332..90c42281 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,8 @@ # SPDX-License-Identifier: MPL-2.0 +[workspace] +members = ["version-range"] + [package] name = "pubgrub" version = "0.2.1" @@ -21,18 +24,22 @@ include = ["Cargo.toml", "LICENSE", "README.md", "src/**", "tests/**", "examples [dependencies] indexmap = "2.5.0" +log = "0.4.22" # for debug logs in tests priority-queue = "2.1.0" -thiserror = "1.0" rustc-hash = ">=1.0.0, <3.0.0" serde = { version = "1.0", features = ["derive"], optional = true } -log = "0.4.22" # for debug logs in tests +thiserror = "1.0" +version-range = { version = "0.1.0", path = "version-range" } [dev-dependencies] proptest = "1.5.0" -ron = "=0.9.0-alpha.0" varisat = "0.2.2" criterion = "0.5" env_logger = "0.11.5" +version-range = { version = "0.1.0", path = "version-range", features = ["proptest"] } + +[features] +serde = ["dep:serde", "version-range/serde"] [[bench]] name = "large_case" diff --git a/src/lib.rs b/src/lib.rs index 344ca0cd..4a2281dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -212,7 +212,6 @@ mod error; mod package; -mod range; mod report; mod solver; mod term; @@ -222,7 +221,6 @@ mod version_set; pub use error::{NoSolutionError, PubGrubError}; pub use package::Package; -pub use range::Range; pub use report::{ DefaultStringReportFormatter, DefaultStringReporter, DerivationTree, Derived, External, ReportFormatter, Reporter, @@ -231,6 +229,7 @@ pub use solver::{resolve, Dependencies, DependencyProvider, OfflineDependencyPro pub use term::Term; pub use type_aliases::{DependencyConstraints, Map, SelectedDependencies, Set}; pub use version::{SemanticVersion, VersionParseError}; +pub use version_range::Range; pub use version_set::VersionSet; mod internal; diff --git a/src/solver.rs b/src/solver.rs index 53cb524a..de490927 100644 --- a/src/solver.rs +++ b/src/solver.rs @@ -199,7 +199,7 @@ pub trait DependencyProvider { /// How this provider stores the version requirements for the packages. /// The requirements must be able to process the same kind of version as this dependency provider. /// - /// A common choice is [`Range`][crate::range::Range]. + /// A common choice is [`Range`][version_range::Range]. type VS: VersionSet; /// Type for custom incompatibilities. diff --git a/src/term.rs b/src/term.rs index f67a8109..c4a80fab 100644 --- a/src/term.rs +++ b/src/term.rs @@ -220,15 +220,14 @@ impl Display for Term { #[cfg(test)] pub mod tests { - use proptest::prelude::*; - use super::*; - use crate::Range; + use proptest::prelude::*; + use version_range::Range; pub fn strategy() -> impl Strategy>> { prop_oneof![ - crate::range::tests::strategy().prop_map(Term::Positive), - crate::range::tests::strategy().prop_map(Term::Negative), + version_range::proptest_strategy().prop_map(Term::Negative), + version_range::proptest_strategy().prop_map(Term::Positive), ] } proptest! { diff --git a/src/version_set.rs b/src/version_set.rs index f67afb6b..7aea30a0 100644 --- a/src/version_set.rs +++ b/src/version_set.rs @@ -20,6 +20,8 @@ use std::fmt::{Debug, Display}; +use crate::Range; + /// Trait describing sets of versions. pub trait VersionSet: Debug + Display + Clone + Eq { /// Version type associated with the sets manipulated. @@ -68,3 +70,43 @@ pub trait VersionSet: Debug + Display + Clone + Eq { self == &self.intersection(other) } } + +impl VersionSet for Range { + type V = T; + + fn empty() -> Self { + Range::empty() + } + + fn singleton(v: Self::V) -> Self { + Range::singleton(v) + } + + fn complement(&self) -> Self { + Range::complement(self) + } + + fn intersection(&self, other: &Self) -> Self { + Range::intersection(self, other) + } + + fn contains(&self, v: &Self::V) -> bool { + Range::contains(self, v) + } + + fn full() -> Self { + Range::full() + } + + fn union(&self, other: &Self) -> Self { + Range::union(self, other) + } + + fn is_disjoint(&self, other: &Self) -> bool { + Range::is_disjoint(self, other) + } + + fn subset_of(&self, other: &Self) -> bool { + Range::subset_of(self, other) + } +} diff --git a/version-range/Cargo.lock b/version-range/Cargo.lock new file mode 100644 index 00000000..519e626e --- /dev/null +++ b/version-range/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "version-range" +version = "0.1.0" diff --git a/version-range/Cargo.toml b/version-range/Cargo.toml new file mode 100644 index 00000000..c598477c --- /dev/null +++ b/version-range/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "version-range" +version = "0.1.0" +edition = "2021" +keywords = ["version", "pubgrub", "selector", "ranges"] + +[dependencies] +proptest = { version = "1.5.0", optional = true } +serde = { version = "1.0.210", features = ["derive"], optional = true } +smallvec = { version = "1.13.2" } + +[features] +serde = ["dep:serde", "smallvec/serde"] + +[dev-dependencies] +proptest = "1.5.0" +ron = "=0.9.0-alpha.0" diff --git a/src/range.rs b/version-range/src/lib.rs similarity index 79% rename from src/range.rs rename to version-range/src/lib.rs index 4b305e10..ba5bfb68 100644 --- a/src/range.rs +++ b/version-range/src/lib.rs @@ -1,54 +1,38 @@ // SPDX-License-Identifier: MPL-2.0 -//! Ranges are constraints defining sets of versions. +//! This crate contains a performance-optimized type for version ranges and operations on them. //! -//! Concretely, those constraints correspond to any set of versions -//! representable as the concatenation, union, and complement -//! of the ranges building blocks. +//! A [`Range`] can represent version selectors such as `(>=1, <2) OR (==3) OR (>4)`. Internally, +//! it is an ordered list of contiguous intervals (segments) with inclusive, exclusive or open-ended +//! ends, similar to a `Vec<(Bound, Bound)>`. //! -//! Those building blocks are: -//! - [empty()](Range::empty): the empty set -//! - [full()](Range::full): the set of all possible versions -//! - [singleton(v)](Range::singleton): the set containing only the version v -//! - [higher_than(v)](Range::higher_than): the set defined by `v <= versions` -//! - [strictly_higher_than(v)](Range::strictly_higher_than): the set defined by `v < versions` -//! - [lower_than(v)](Range::lower_than): the set defined by `versions <= v` -//! - [strictly_lower_than(v)](Range::strictly_lower_than): the set defined by `versions < v` -//! - [between(v1, v2)](Range::between): the set defined by `v1 <= versions < v2` +//! [`Range`] is generic over any type that implements [`Ord`] + [`Clone`] and can represent all +//! kinds of slices with ordered coordinates, not just version ranges. While built as a +//! performance-critical piece of [pubgrub](https://github.com/pubgrub-rs/pubgrub), it can be +//! adopted for other domains, too. //! -//! Ranges can be created from any type that implements [`Ord`] + [`Clone`]. +//! You can construct a basic range from one of the following build blocks. All other ranges are +//! concatenation, union, and complement of these basic range. +//! - [empty()](Range::empty): No version +//! - [full()](Range::full): All versions +//! - [singleton(v)](Range::singleton): Only the version v exactly +//! - [higher_than(v)](Range::higher_than): All versions `v <= versions` +//! - [strictly_higher_than(v)](Range::strictly_higher_than): All versions `v < versions` +//! - [lower_than(v)](Range::lower_than): All versions `versions <= v` +//! - [strictly_lower_than(v)](Range::strictly_lower_than): All versions `versions < v` +//! - [between(v1, v2)](Range::between): All versions `v1 <= versions < v2` //! -//! In order to advance the solver front, comparisons of versions sets are necessary in the algorithm. -//! To do those comparisons between two sets S1 and S2 we use the mathematical property that S1 ⊂ S2 if and only if S1 ∩ S2 == S1. -//! We can thus compute an intersection and evaluate an equality to answer if S1 is a subset of S2. -//! But this means that the implementation of equality must be correct semantically. -//! In practice, if equality is derived automatically, this means sets must have unique representations. +//! Note that there are limitations to the equality implementation: Given a `Range`, +//! the segments `(Unbounded, Included(42u32))` and `(Included(0), Included(42u32))` as well as +//! `(Included(1), Included(5))` and `(Included(1), Included(3)) + (Included(4), Included(5))` +//! are reported as unequal, even though the match the same versions: We can't tell that there isn't +//! a version between `0` and `-inf` or `3` and `4` respectively. //! -//! By migrating from a custom representation for discrete sets in v0.2 -//! to a generic bounded representation for continuous sets in v0.3 -//! we are potentially breaking that assumption in two ways: +//! ## Optional features //! -//! 1. Minimal and maximal `Unbounded` values can be replaced by their equivalent if it exists. -//! 2. Simplifying adjacent bounds of discrete sets cannot be detected and automated in the generic intersection code. -//! -//! An example for each can be given when `T` is `u32`. -//! First, we can have both segments `S1 = (Unbounded, Included(42u32))` and `S2 = (Included(0), Included(42u32))` -//! that represent the same segment but are structurally different. -//! Thus, a derived equality check would answer `false` to `S1 == S2` while it's true. -//! -//! Second both segments `S1 = (Included(1), Included(5))` and `S2 = (Included(1), Included(3)) + (Included(4), Included(5))` are equal. -//! But without asking the user to provide a `bump` function for discrete sets, -//! the algorithm is not able to tell that the space between the right `Included(3)` bound and the left `Included(4)` bound is empty. -//! Thus the algorithm is not able to reduce S2 to its canonical S1 form while computing sets operations like intersections in the generic code. -//! -//! This is likely to lead to user facing theoretically correct but practically nonsensical ranges, -//! like (Unbounded, Excluded(0)) or (Excluded(6), Excluded(7)). -//! In general nonsensical inputs often lead to hard to track bugs. -//! But as far as we can tell this should work in practice. -//! So for now this crate only provides an implementation for continuous ranges. -//! With the v0.3 api the user could choose to bring back the discrete implementation from v0.2, as documented in the guide. -//! If doing so regularly fixes bugs seen by users, we will bring it back into the core library. -//! If we do not see practical bugs, or we get a formal proof that the code cannot lead to error states, then we may remove this warning. +//! * `serde`: serialization and deserialization for the version range, given that the version type +//! also supports it. +//! * `proptest`: Exports are proptest strategy for [`Range`]. use std::borrow::Borrow; use std::cmp::Ordering; @@ -56,8 +40,9 @@ use std::fmt::{Debug, Display, Formatter}; use std::ops::Bound::{self, Excluded, Included, Unbounded}; use std::ops::RangeBounds; -use crate::internal::SmallVec; -use crate::VersionSet; +#[cfg(any(feature = "proptest", test))] +use proptest::prelude::*; +use smallvec::{smallvec, SmallVec}; /// A Range represents multiple intervals of a continuous range of monotone increasing /// values. @@ -65,7 +50,7 @@ use crate::VersionSet; #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(transparent))] pub struct Range { - segments: SmallVec>, + segments: SmallVec<[Interval; 2]>, } type Interval = (Bound, Bound); @@ -74,49 +59,49 @@ impl Range { /// Empty set of versions. pub fn empty() -> Self { Self { - segments: SmallVec::empty(), + segments: SmallVec::new(), } } /// Set of all possible versions pub fn full() -> Self { Self { - segments: SmallVec::one((Unbounded, Unbounded)), + segments: smallvec![(Unbounded, Unbounded)], } } /// Set of all versions higher or equal to some version pub fn higher_than(v: impl Into) -> Self { Self { - segments: SmallVec::one((Included(v.into()), Unbounded)), + segments: smallvec![(Included(v.into()), Unbounded)], } } /// Set of all versions higher to some version pub fn strictly_higher_than(v: impl Into) -> Self { Self { - segments: SmallVec::one((Excluded(v.into()), Unbounded)), + segments: smallvec![(Excluded(v.into()), Unbounded)], } } /// Set of all versions lower to some version pub fn strictly_lower_than(v: impl Into) -> Self { Self { - segments: SmallVec::one((Unbounded, Excluded(v.into()))), + segments: smallvec![(Unbounded, Excluded(v.into()))], } } /// Set of all versions lower or equal to some version pub fn lower_than(v: impl Into) -> Self { Self { - segments: SmallVec::one((Unbounded, Included(v.into()))), + segments: smallvec![(Unbounded, Included(v.into()))], } } /// Set of versions greater or equal to `v1` but less than `v2`. pub fn between(v1: impl Into, v2: impl Into) -> Self { Self { - segments: SmallVec::one((Included(v1.into()), Excluded(v2.into()))), + segments: smallvec![(Included(v1.into()), Excluded(v2.into()))], } } @@ -131,7 +116,7 @@ impl Range { pub fn singleton(v: impl Into) -> Self { let v = v.into(); Self { - segments: SmallVec::one((Included(v.clone()), Included(v))), + segments: smallvec![(Included(v.clone()), Included(v))], } } @@ -163,7 +148,7 @@ impl Range { /// Helper function performing the negation of intervals in segments. fn negate_segments(start: Bound, segments: &[Interval]) -> Self { - let mut complement_segments: SmallVec> = SmallVec::empty(); + let mut complement_segments = SmallVec::new(); let mut start = start; for (v1, v2) in segments { complement_segments.push(( @@ -288,7 +273,7 @@ impl Range { }; if valid_segment(&start, &end) { Self { - segments: SmallVec::one((start, end)), + segments: smallvec![(start, end)], } } else { Self::empty() @@ -577,7 +562,7 @@ fn group_adjacent_locations( impl Range { /// Computes the union of this `Range` and another. pub fn union(&self, other: &Self) -> Self { - let mut output: SmallVec> = SmallVec::empty(); + let mut output = SmallVec::new(); let mut accumulator: Option<(&Bound<_>, &Bound<_>)> = None; let mut left_iter = self.segments.iter().peekable(); let mut right_iter = other.segments.iter().peekable(); @@ -635,7 +620,7 @@ impl Range { /// Computes the intersection of two sets of versions. pub fn intersection(&self, other: &Self) -> Self { - let mut output: SmallVec> = SmallVec::empty(); + let mut output = SmallVec::new(); let mut left_iter = self.segments.iter().peekable(); let mut right_iter = other.segments.iter().peekable(); // By the definition of intersection any point that is matched by the output @@ -822,7 +807,7 @@ impl Range { &self, kept_segments: impl Iterator, Option)>, ) -> Range { - let mut segments = SmallVec::Empty; + let mut segments = SmallVec::new(); for (s, e) in kept_segments { segments.push(( s.map_or(Unbounded, |s| self.segments[s].0.clone()), @@ -838,46 +823,6 @@ impl Range { } } -impl VersionSet for Range { - type V = T; - - fn empty() -> Self { - Range::empty() - } - - fn singleton(v: Self::V) -> Self { - Range::singleton(v) - } - - fn complement(&self) -> Self { - Range::complement(self) - } - - fn intersection(&self, other: &Self) -> Self { - Range::intersection(self, other) - } - - fn contains(&self, v: &Self::V) -> bool { - Range::contains(self, v) - } - - fn full() -> Self { - Range::full() - } - - fn union(&self, other: &Self) -> Self { - Range::union(self, other) - } - - fn is_disjoint(&self, other: &Self) -> bool { - Range::is_disjoint(self, other) - } - - fn subset_of(&self, other: &Self) -> bool { - Range::subset_of(self, other) - } -} - // REPORT ###################################################################### impl Display for Range { @@ -928,9 +873,10 @@ impl<'de, V: serde::Deserialize<'de>> serde::Deserialize<'de> for Range { D(V, Option), } - let bounds: SmallVec> = serde::Deserialize::deserialize(deserializer)?; + let bounds: SmallVec<[EitherInterval; 2]> = + serde::Deserialize::deserialize(deserializer)?; - let mut segments = SmallVec::Empty; + let mut segments = SmallVec::new(); for i in bounds { match i { EitherInterval::B(l, r) => segments.push((l, r)), @@ -943,77 +889,76 @@ impl<'de, V: serde::Deserialize<'de>> serde::Deserialize<'de> for Range { } } -// TESTS ####################################################################### - -#[cfg(test)] -pub mod tests { - use proptest::prelude::*; - - use super::*; +/// Generate version sets from a random vector of deltas between randomly inclusive or exclusive +/// bounds. +#[cfg(any(feature = "proptest", test))] +pub fn proptest_strategy() -> impl Strategy> { + ( + any::(), + prop::collection::vec(any::<(u32, bool)>(), 1..10), + ) + .prop_map(|(start_unbounded, deltas)| { + let mut start = if start_unbounded { + Some(Unbounded) + } else { + None + }; + let mut largest: u32 = 0; + let mut last_bound_was_inclusive = false; + let mut segments = SmallVec::new(); + for (delta, inclusive) in deltas { + // Add the offset to the current bound + largest = match largest.checked_add(delta) { + Some(s) => s, + None => { + // Skip this offset, if it would result in a too large bound. + continue; + } + }; - /// Generate version sets from a random vector of deltas between bounds. - /// Each bound is randomly inclusive or exclusive. - pub fn strategy() -> impl Strategy> { - ( - any::(), - prop::collection::vec(any::<(u32, bool)>(), 1..10), - ) - .prop_map(|(start_unbounded, deltas)| { - let mut start = if start_unbounded { - Some(Unbounded) + let current_bound = if inclusive { + Included(largest) } else { - None + Excluded(largest) }; - let mut largest: u32 = 0; - let mut last_bound_was_inclusive = false; - let mut segments = SmallVec::Empty; - for (delta, inclusive) in deltas { - // Add the offset to the current bound - largest = match largest.checked_add(delta) { - Some(s) => s, - None => { - // Skip this offset, if it would result in a too large bound. - continue; - } - }; - let current_bound = if inclusive { - Included(largest) - } else { - Excluded(largest) - }; - - // If we already have a start bound, the next offset defines the complete range. - // If we don't have a start bound, we have to generate one. - if let Some(start_bound) = start.take() { - // If the delta from the start bound is 0, the only authorized configuration is - // Included(x), Included(x) - if delta == 0 && !(matches!(start_bound, Included(_)) && inclusive) { - start = Some(start_bound); - continue; - } - last_bound_was_inclusive = inclusive; - segments.push((start_bound, current_bound)); - } else { - // If the delta from the end bound of the last range is 0 and - // any of the last ending or current starting bound is inclusive, - // we skip the delta because they basically overlap. - if delta == 0 && (last_bound_was_inclusive || inclusive) { - continue; - } - start = Some(current_bound); + // If we already have a start bound, the next offset defines the complete range. + // If we don't have a start bound, we have to generate one. + if let Some(start_bound) = start.take() { + // If the delta from the start bound is 0, the only authorized configuration is + // Included(x), Included(x) + if delta == 0 && !(matches!(start_bound, Included(_)) && inclusive) { + start = Some(start_bound); + continue; + } + last_bound_was_inclusive = inclusive; + segments.push((start_bound, current_bound)); + } else { + // If the delta from the end bound of the last range is 0 and + // any of the last ending or current starting bound is inclusive, + // we skip the delta because they basically overlap. + if delta == 0 && (last_bound_was_inclusive || inclusive) { + continue; } + start = Some(current_bound); } + } - // If we still have a start bound, but didn't have enough deltas to complete another - // segment, we add an unbounded upperbound. - if let Some(start_bound) = start { - segments.push((start_bound, Unbounded)); - } + // If we still have a start bound, but didn't have enough deltas to complete another + // segment, we add an unbounded upperbound. + if let Some(start_bound) = start { + segments.push((start_bound, Unbounded)); + } - Range { segments }.check_invariants() - }) - } + Range { segments }.check_invariants() + }) +} + +#[cfg(test)] +pub mod tests { + use proptest::prelude::*; + + use super::*; fn version_strat() -> impl Strategy { any::() @@ -1025,7 +970,7 @@ pub mod tests { #[cfg(feature = "serde")] #[test] - fn serde_round_trip(range in strategy()) { + fn serde_round_trip(range in proptest_strategy()) { let s = ron::ser::to_string(&range).unwrap(); let r = ron::de::from_str(&s).unwrap(); assert_eq!(range, r); @@ -1034,83 +979,83 @@ pub mod tests { // Testing negate ---------------------------------- #[test] - fn negate_is_different(range in strategy()) { + fn negate_is_different(range in proptest_strategy()) { assert_ne!(range.complement(), range); } #[test] - fn double_negate_is_identity(range in strategy()) { + fn double_negate_is_identity(range in proptest_strategy()) { assert_eq!(range.complement().complement(), range); } #[test] - fn negate_contains_opposite(range in strategy(), version in version_strat()) { + fn negate_contains_opposite(range in proptest_strategy(), version in version_strat()) { assert_ne!(range.contains(&version), range.complement().contains(&version)); } // Testing intersection ---------------------------- #[test] - fn intersection_is_symmetric(r1 in strategy(), r2 in strategy()) { + fn intersection_is_symmetric(r1 in proptest_strategy(), r2 in proptest_strategy()) { assert_eq!(r1.intersection(&r2), r2.intersection(&r1)); } #[test] - fn intersection_with_any_is_identity(range in strategy()) { + fn intersection_with_any_is_identity(range in proptest_strategy()) { assert_eq!(Range::full().intersection(&range), range); } #[test] - fn intersection_with_none_is_none(range in strategy()) { + fn intersection_with_none_is_none(range in proptest_strategy()) { assert_eq!(Range::empty().intersection(&range), Range::empty()); } #[test] - fn intersection_is_idempotent(r1 in strategy(), r2 in strategy()) { + fn intersection_is_idempotent(r1 in proptest_strategy(), r2 in proptest_strategy()) { assert_eq!(r1.intersection(&r2).intersection(&r2), r1.intersection(&r2)); } #[test] - fn intersection_is_associative(r1 in strategy(), r2 in strategy(), r3 in strategy()) { + fn intersection_is_associative(r1 in proptest_strategy(), r2 in proptest_strategy(), r3 in proptest_strategy()) { assert_eq!(r1.intersection(&r2).intersection(&r3), r1.intersection(&r2.intersection(&r3))); } #[test] - fn intesection_of_complements_is_none(range in strategy()) { + fn intesection_of_complements_is_none(range in proptest_strategy()) { assert_eq!(range.complement().intersection(&range), Range::empty()); } #[test] - fn intesection_contains_both(r1 in strategy(), r2 in strategy(), version in version_strat()) { + fn intesection_contains_both(r1 in proptest_strategy(), r2 in proptest_strategy(), version in version_strat()) { assert_eq!(r1.intersection(&r2).contains(&version), r1.contains(&version) && r2.contains(&version)); } // Testing union ----------------------------------- #[test] - fn union_of_complements_is_any(range in strategy()) { + fn union_of_complements_is_any(range in proptest_strategy()) { assert_eq!(range.complement().union(&range), Range::full()); } #[test] - fn union_contains_either(r1 in strategy(), r2 in strategy(), version in version_strat()) { + fn union_contains_either(r1 in proptest_strategy(), r2 in proptest_strategy(), version in version_strat()) { assert_eq!(r1.union(&r2).contains(&version), r1.contains(&version) || r2.contains(&version)); } #[test] - fn is_disjoint_through_intersection(r1 in strategy(), r2 in strategy()) { + fn is_disjoint_through_intersection(r1 in proptest_strategy(), r2 in proptest_strategy()) { let disjoint_def = r1.intersection(&r2) == Range::empty(); assert_eq!(r1.is_disjoint(&r2), disjoint_def); } #[test] - fn subset_of_through_intersection(r1 in strategy(), r2 in strategy()) { + fn subset_of_through_intersection(r1 in proptest_strategy(), r2 in proptest_strategy()) { let disjoint_def = r1.intersection(&r2) == r1; assert_eq!(r1.subset_of(&r2), disjoint_def); } #[test] - fn union_through_intersection(r1 in strategy(), r2 in strategy()) { + fn union_through_intersection(r1 in proptest_strategy(), r2 in proptest_strategy()) { let union_def = r1 .complement() .intersection(&r2.complement()) @@ -1127,17 +1072,17 @@ pub mod tests { } #[test] - fn contains_negation(range in strategy(), version in version_strat()) { + fn contains_negation(range in proptest_strategy(), version in version_strat()) { assert_ne!(range.contains(&version), range.complement().contains(&version)); } #[test] - fn contains_intersection(range in strategy(), version in version_strat()) { + fn contains_intersection(range in proptest_strategy(), version in version_strat()) { assert_eq!(range.contains(&version), range.intersection(&Range::singleton(version)) != Range::empty()); } #[test] - fn contains_bounding_range(range in strategy(), version in version_strat()) { + fn contains_bounding_range(range in proptest_strategy(), version in version_strat()) { if range.contains(&version) { assert!(range.bounding_range().map(|b| b.contains(&version)).unwrap_or(false)); } @@ -1157,14 +1102,14 @@ pub mod tests { } #[test] - fn contains(range in strategy(), versions in proptest::collection::vec(version_strat(), ..30)) { + fn contains(range in proptest_strategy(), versions in proptest::collection::vec(version_strat(), ..30)) { for v in versions { assert_eq!(range.contains(&v), range.segments.iter().any(|s| RangeBounds::contains(s, &v))); } } #[test] - fn contains_many(range in strategy(), mut versions in proptest::collection::vec(version_strat(), ..30)) { + fn contains_many(range in proptest_strategy(), mut versions in proptest::collection::vec(version_strat(), ..30)) { versions.sort(); assert_eq!(versions.len(), range.contains_many(versions.iter()).count()); for (a, b) in versions.iter().zip(range.contains_many(versions.iter())) { @@ -1173,7 +1118,7 @@ pub mod tests { } #[test] - fn simplify(range in strategy(), mut versions in proptest::collection::vec(version_strat(), ..30)) { + fn simplify(range in proptest_strategy(), mut versions in proptest::collection::vec(version_strat(), ..30)) { versions.sort(); let simp = range.simplify(versions.iter());