diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a029c6b2..f9fb115d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,6 +53,17 @@ jobs: env: RUSTFLAGS: --cfg test_node_semver ${{env.RUSTFLAGS}} + mirror_node_matches_prerelease: + name: mirror_node_matches_prerelease + needs: pre_ci + if: needs.pre_ci.outputs.continue + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo test --package semver --test test_matches_prerelease --features "mirror_node_matches_prerelease" + minimal: name: Minimal versions needs: pre_ci diff --git a/Cargo.toml b/Cargo.toml index 12b7404f..c8dea82a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ rust-version = "1.31" [features] default = ["std"] +mirror_node_matches_prerelease = [] std = [] [dependencies] diff --git a/src/eval.rs b/src/eval.rs index 7d0e9473..b7024d78 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1,9 +1,9 @@ -use crate::{Comparator, Op, Prerelease, Version, VersionReq}; +use crate::{eval_ext, Comparator, Op, Version, VersionReq}; pub(crate) fn matches_req(req: &VersionReq, ver: &Version, prerelease_matches: bool) -> bool { for cmp in &req.comparators { if prerelease_matches { - if !matches_prerelease_impl(cmp, ver) { + if !eval_ext::matches_prerelease_impl(cmp, ver) { return false; } } else if !matches_impl(cmp, ver) { @@ -30,41 +30,6 @@ pub(crate) fn matches_req(req: &VersionReq, ver: &Version, prerelease_matches: b pub(crate) fn matches_comparator(cmp: &Comparator, ver: &Version) -> bool { matches_impl(cmp, ver) && (ver.pre.is_empty() || pre_is_compatible(cmp, ver)) } -// If VersionReq missing Minor, Patch, then filling them with 0 -fn fill_partial_req(cmp: &Comparator) -> Comparator { - let mut cmp = cmp.clone(); - if cmp.minor.is_none() { - cmp.minor = Some(0); - cmp.patch = Some(0); - } else if cmp.patch.is_none() { - cmp.patch = Some(0); - } - cmp -} - -fn matches_prerelease_impl(cmp: &Comparator, ver: &Version) -> bool { - match cmp.op { - Op::Exact | Op::Wildcard => matches_exact_prerelease(cmp, ver), - Op::Greater => matches_greater(cmp, ver), - Op::GreaterEq => { - if matches_exact_prerelease(cmp, ver) { - return true; - } - matches_greater(cmp, ver) - } - Op::Less => matches_less(&fill_partial_req(cmp), ver), - Op::LessEq => { - if matches_exact_prerelease(cmp, ver) { - return true; - } - matches_less(&fill_partial_req(cmp), ver) - } - Op::Tilde => matches_tilde_prerelease(cmp, ver), - Op::Caret => matches_caret_prerelease(cmp, ver), - #[cfg(no_non_exhaustive)] - Op::__NonExhaustive => unreachable!(), - } -} fn matches_impl(cmp: &Comparator, ver: &Version) -> bool { match cmp.op { @@ -80,49 +45,7 @@ fn matches_impl(cmp: &Comparator, ver: &Version) -> bool { } } -fn matches_exact_prerelease(cmp: &Comparator, ver: &Version) -> bool { - if matches_exact(cmp, ver) { - return true; - } - - // If the comparator has a prerelease tag like =3.0.0-alpha.24, - // then it shoud be only exactly match 3.0.0-alpha.24. - if !cmp.pre.is_empty() { - return false; - } - - if !matches_greater(&fill_partial_req(cmp), ver) { - return false; - } - - let mut upper = Comparator { - op: Op::Less, - pre: Prerelease::new("0").unwrap(), - ..cmp.clone() - }; - - match (upper.minor.is_some(), upper.patch.is_some()) { - (true, true) => { - upper.patch = Some(upper.patch.unwrap() + 1); - } - (true, false) => { - // Partial Exact VersionReq eg. =0.24 - upper.minor = Some(upper.minor.unwrap() + 1); - upper.patch = Some(0); - } - (false, false) => { - // Partial Exact VersionReq eg. =0 - upper.major += 1; - upper.minor = Some(0); - upper.patch = Some(0); - } - _ => {} - } - - matches_less(&upper, ver) -} - -fn matches_exact(cmp: &Comparator, ver: &Version) -> bool { +pub(super) fn matches_exact(cmp: &Comparator, ver: &Version) -> bool { if ver.major != cmp.major { return false; } @@ -142,7 +65,7 @@ fn matches_exact(cmp: &Comparator, ver: &Version) -> bool { ver.pre == cmp.pre } -fn matches_greater(cmp: &Comparator, ver: &Version) -> bool { +pub(super) fn matches_greater(cmp: &Comparator, ver: &Version) -> bool { if ver.major != cmp.major { return ver.major > cmp.major; } @@ -168,7 +91,7 @@ fn matches_greater(cmp: &Comparator, ver: &Version) -> bool { ver.pre > cmp.pre } -fn matches_less(cmp: &Comparator, ver: &Version) -> bool { +pub(super) fn matches_less(cmp: &Comparator, ver: &Version) -> bool { if ver.major != cmp.major { return ver.major < cmp.major; } @@ -194,37 +117,6 @@ fn matches_less(cmp: &Comparator, ver: &Version) -> bool { ver.pre < cmp.pre } -fn matches_tilde_prerelease(cmp: &Comparator, ver: &Version) -> bool { - if matches_exact(cmp, ver) { - return true; - } - - if !matches_greater(&fill_partial_req(cmp), ver) { - return false; - } - - let mut upper = Comparator { - op: Op::Less, - pre: Prerelease::new("0").unwrap(), - ..cmp.clone() - }; - - match (upper.minor.is_some(), upper.patch.is_some()) { - (true, _) => { - upper.minor = Some(upper.minor.unwrap() + 1); - upper.patch = Some(0); - } - (false, false) => { - upper.major += 1; - upper.minor = Some(0); - upper.patch = Some(0); - } - _ => {} - } - - matches_less(&upper, ver) -} - fn matches_tilde(cmp: &Comparator, ver: &Version) -> bool { if ver.major != cmp.major { return false; @@ -245,52 +137,6 @@ fn matches_tilde(cmp: &Comparator, ver: &Version) -> bool { ver.pre >= cmp.pre } -fn matches_caret_prerelease(cmp: &Comparator, ver: &Version) -> bool { - if matches_exact(cmp, ver) { - return true; - } - - if !matches_greater(&fill_partial_req(cmp), ver) { - return false; - } - - let mut upper = Comparator { - op: Op::Less, - pre: Prerelease::new("0").unwrap(), - ..cmp.clone() - }; - - match ( - upper.major > 0, - upper.minor.is_some(), - upper.patch.is_some(), - ) { - (true, _, _) | (_, false, false) => { - upper.major += 1; - upper.minor = Some(0); - upper.patch = Some(0); - } - (_, true, false) => { - upper.minor = Some(upper.minor.unwrap() + 1); - upper.patch = Some(0); - } - (_, true, _) if upper.minor.unwrap() > 0 => { - upper.minor = Some(upper.minor.unwrap() + 1); - upper.patch = Some(0); - } - (_, true, _) if upper.minor.unwrap() == 0 => { - if upper.patch.is_none() { - upper.patch = Some(1); - } else { - upper.patch = Some(upper.patch.unwrap() + 1); - } - } - _ => {} - } - - matches_less(&upper, ver) -} - fn matches_caret(cmp: &Comparator, ver: &Version) -> bool { if ver.major != cmp.major { return false; diff --git a/src/eval_ext.rs b/src/eval_ext.rs new file mode 100644 index 00000000..1e9b07c4 --- /dev/null +++ b/src/eval_ext.rs @@ -0,0 +1,269 @@ +use super::eval::{matches_exact, matches_greater, matches_less}; +use crate::{Comparator, Op, Prerelease, Version}; + +pub(super) fn matches_prerelease_impl(cmp: &Comparator, ver: &Version) -> bool { + match cmp.op { + Op::Exact | Op::Wildcard => matches_exact_prerelease(cmp, ver), + Op::Greater => matches_greater(cmp, ver), + Op::GreaterEq => { + if matches_exact_prerelease(cmp, ver) { + return true; + } + matches_greater(cmp, ver) + } + Op::Less => matches_less(&fill_partial_req(cmp), ver), + Op::LessEq => { + if matches_exact_prerelease(cmp, ver) { + return true; + } + matches_less(&fill_partial_req(cmp), ver) + } + Op::Tilde => matches_tilde_prerelease(cmp, ver), + Op::Caret => matches_caret_prerelease(cmp, ver), + #[cfg(no_non_exhaustive)] + Op::__NonExhaustive => unreachable!(), + } +} + +#[cfg(not(feature = "mirror_node_matches_prerelease"))] +fn fill_partial_req(cmp: &Comparator) -> Comparator { + let mut cmp = cmp.clone(); + if cmp.minor.is_none() { + cmp.minor = Some(0); + cmp.patch = Some(0); + } else if cmp.patch.is_none() { + cmp.patch = Some(0); + } + cmp +} + +#[cfg(not(feature = "mirror_node_matches_prerelease"))] +fn matches_exact_prerelease(cmp: &Comparator, ver: &Version) -> bool { + if matches_exact(cmp, ver) { + return true; + } + + // If the comparator has a prerelease tag like =3.0.0-alpha.24, + // then it shoud be only exactly match 3.0.0-alpha.24. + if !cmp.pre.is_empty() { + return false; + } + + if !matches_greater(&fill_partial_req(cmp), ver) { + return false; + } + + let mut upper = Comparator { + op: Op::Less, + pre: Prerelease::new("0").unwrap(), + ..cmp.clone() + }; + + match (upper.minor.is_some(), upper.patch.is_some()) { + (true, true) => { + upper.patch = Some(upper.patch.unwrap() + 1); + } + (true, false) => { + // Partial Exact VersionReq eg. =0.24 + upper.minor = Some(upper.minor.unwrap() + 1); + upper.patch = Some(0); + } + (false, false) => { + // Partial Exact VersionReq eg. =0 + upper.major += 1; + upper.minor = Some(0); + upper.patch = Some(0); + } + _ => {} + } + + matches_less(&upper, ver) +} + +fn matches_tilde_prerelease(cmp: &Comparator, ver: &Version) -> bool { + if matches_exact(cmp, ver) { + return true; + } + + if !matches_greater(&fill_partial_req(cmp), ver) { + return false; + } + + let mut upper = Comparator { + op: Op::Less, + pre: Prerelease::new("0").unwrap(), + ..cmp.clone() + }; + + match (upper.minor.is_some(), upper.patch.is_some()) { + (true, _) => { + upper.minor = Some(upper.minor.unwrap() + 1); + upper.patch = Some(0); + } + (false, false) => { + upper.major += 1; + upper.minor = Some(0); + upper.patch = Some(0); + } + _ => {} + } + + matches_less(&upper, ver) +} + +#[cfg(not(feature = "mirror_node_matches_prerelease"))] +fn matches_caret_prerelease(cmp: &Comparator, ver: &Version) -> bool { + if matches_exact(cmp, ver) { + return true; + } + + if !matches_greater(&fill_partial_req(cmp), ver) { + return false; + } + + let mut upper = Comparator { + op: Op::Less, + pre: Prerelease::new("0").unwrap(), + ..cmp.clone() + }; + + match ( + upper.major > 0, + upper.minor.is_some(), + upper.patch.is_some(), + ) { + (true, _, _) | (_, false, false) => { + upper.major += 1; + upper.minor = Some(0); + upper.patch = Some(0); + } + (_, true, false) => { + upper.minor = Some(upper.minor.unwrap() + 1); + upper.patch = Some(0); + } + (_, true, _) if upper.minor.unwrap() > 0 => { + upper.minor = Some(upper.minor.unwrap() + 1); + upper.patch = Some(0); + } + (_, true, _) if upper.minor.unwrap() == 0 => { + if upper.patch.is_none() { + upper.patch = Some(1); + } else { + upper.patch = Some(upper.patch.unwrap() + 1); + } + } + _ => {} + } + + matches_less(&upper, ver) +} + +#[cfg(feature = "mirror_node_matches_prerelease")] +fn fill_partial_req(cmp: &Comparator) -> Comparator { + let mut cmp = cmp.clone(); + if cmp.minor.is_none() { + cmp.minor = Some(0); + cmp.patch = Some(0); + cmp.pre = Prerelease::new("0").unwrap(); + } else if cmp.patch.is_none() { + cmp.patch = Some(0); + cmp.pre = Prerelease::new("0").unwrap(); + } + cmp +} + +#[cfg(feature = "mirror_node_matches_prerelease")] +fn matches_exact_prerelease(cmp: &Comparator, ver: &Version) -> bool { + let lower = fill_partial_req(cmp); + if matches_exact(&lower, ver) { + return true; + } + + // If the comparator has a prerelease tag like =3.0.0-alpha.24, + // then it shoud be only exactly match 3.0.0-alpha.24. + if !cmp.pre.is_empty() { + return false; + } + + if !matches_greater(&lower, ver) { + return false; + } + + let mut upper = Comparator { + op: Op::Less, + pre: Prerelease::new("0").unwrap(), + ..cmp.clone() + }; + + match (upper.minor.is_some(), upper.patch.is_some()) { + (true, true) => { + upper.patch = Some(upper.patch.unwrap() + 1); + } + (true, false) => { + // Partial Exact VersionReq eg. =0.24 + upper.minor = Some(upper.minor.unwrap() + 1); + upper.patch = Some(0); + } + (false, false) => { + // Partial Exact VersionReq eg. =0 + upper.major += 1; + upper.minor = Some(0); + upper.patch = Some(0); + } + _ => {} + } + + matches_less(&upper, ver) +} + +#[cfg(feature = "mirror_node_matches_prerelease")] +fn matches_caret_prerelease(cmp: &Comparator, ver: &Version) -> bool { + let mut lower = fill_partial_req(cmp); + if lower.major == 0 && lower.pre.is_empty() { + lower.pre = Prerelease::new("0").unwrap(); + } + + if matches_exact(&lower, ver) { + return true; + } + + if !matches_greater(&lower, ver) { + return false; + } + + let mut upper = Comparator { + op: Op::Less, + pre: Prerelease::new("0").unwrap(), + ..cmp.clone() + }; + + match ( + upper.major > 0, + upper.minor.is_some(), + upper.patch.is_some(), + ) { + (true, _, _) | (_, false, false) => { + upper.major += 1; + upper.minor = Some(0); + upper.patch = Some(0); + } + (_, true, false) => { + upper.minor = Some(upper.minor.unwrap() + 1); + upper.patch = Some(0); + } + (_, true, _) if upper.minor.unwrap() > 0 => { + upper.minor = Some(upper.minor.unwrap() + 1); + upper.patch = Some(0); + } + (_, true, _) if upper.minor.unwrap() == 0 => { + if upper.patch.is_none() { + upper.patch = Some(1); + } else { + upper.patch = Some(upper.patch.unwrap() + 1); + } + } + _ => {} + } + + matches_less(&upper, ver) +} diff --git a/src/lib.rs b/src/lib.rs index 42f1b09a..e7d0d883 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,6 +93,7 @@ mod backport; mod display; mod error; mod eval; +mod eval_ext; mod identifier; mod impls; mod parse; diff --git a/tests/node/mod.rs b/tests/node/mod.rs index eb50673d..b3817738 100644 --- a/tests/node/mod.rs +++ b/tests/node/mod.rs @@ -8,8 +8,10 @@ use std::process::Command; pub(super) struct VersionReq(semver::VersionReq); impl VersionReq { + #[allow(dead_code)] pub(super) const STAR: Self = VersionReq(semver::VersionReq::STAR); + #[allow(dead_code)] pub(super) fn matches(&self, version: &Version) -> bool { let out = Command::new("node") .arg("-e") @@ -29,6 +31,27 @@ impl VersionReq { panic!("unexpected output: {}", s); } } + + #[allow(dead_code)] + pub(super) fn matches_prerelease(&self, version: &Version) -> bool { + let out = Command::new("node") + .arg("-e") + .arg(format!( + "console.log(require('semver').satisfies('{}', '{}', {{includePrerelease: true}}))", + version, + self.to_string().replace(',', ""), + )) + .output() + .unwrap(); + if out.stdout == b"true\n" { + true + } else if out.stdout == b"false\n" { + false + } else { + let s = String::from_utf8_lossy(&out.stdout) + String::from_utf8_lossy(&out.stderr); + panic!("unexpected output: {}", s); + } + } } impl Display for VersionReq { diff --git a/tests/test_matches_prerelease.rs b/tests/test_matches_prerelease.rs index 6515357e..161ee030 100644 --- a/tests/test_matches_prerelease.rs +++ b/tests/test_matches_prerelease.rs @@ -1,4 +1,3 @@ -#![cfg(not(test_node_semver))] #![allow( clippy::missing_panics_doc, clippy::shadow_unrelated, @@ -6,16 +5,23 @@ clippy::wildcard_imports )] +mod node; mod util; - use crate::util::*; - +#[cfg(test_node_semver)] +use node::{req, VersionReq}; +#[cfg(not(test_node_semver))] use semver::VersionReq; #[cfg_attr(not(no_track_caller), track_caller)] fn assert_prerelease_match_all(req: &VersionReq, versions: &[&str]) { for string in versions { let parsed = version(string); - assert!(req.matches_prerelease(&parsed), "did not match {}", string); + assert!( + req.matches_prerelease(&parsed), + "{} did not match {}", + req, + string, + ); } } @@ -23,303 +29,430 @@ fn assert_prerelease_match_all(req: &VersionReq, versions: &[&str]) { fn assert_prerelease_match_none(req: &VersionReq, versions: &[&str]) { for string in versions { let parsed = version(string); - assert!(!req.matches_prerelease(&parsed), "matched {}", string); + assert!( + !req.matches_prerelease(&parsed), + "{} matched {}", + req, + string + ); } } #[test] -fn patch_caret() { - let ref r = req("0.0.7"); - // Match >=0.0.7, <0.0.8-0, that's only match 0.0.7 - assert_prerelease_match_all(r, &["0.0.7"]); - // Not Match <0.7.0 - assert_prerelease_match_none(r, &["0.0.6", "0.0.7-0", "0.0.7-pre"]); - // Not Match >=0.8.0-0 - assert_prerelease_match_none(r, &["0.0.8-0", "0.0.8-pre.1", "0.0.8"]); - - let ref r = req("0.0.7-0"); - // Match >=0.0.7-0, <0.0.8-0 - assert_prerelease_match_all(r, &["0.0.7-0", "0.0.7-pre", "0.0.7"]); - // Not Match <0.7.0-0 - assert_prerelease_match_none(r, &["0.0.6-0", "0.0.6-pre", "0.0.6"]); - // Not Match >=0.0.8-0 - assert_prerelease_match_none(r, &["0.0.8-0", "0.0.8-pre.1", "0.0.8"]); -} - -#[test] -fn minor_caret() { - let ref r = req("0.24"); - // Match >= 0.24.0, < 0.25.0-0 - assert_prerelease_match_all(r, &["0.24.0", "0.24.1-0", "0.24.1-pre", "0.24.1"]); - // Not Match < 0.24.0 - assert_prerelease_match_none(r, &["0.1.0", "0.8.0-pre"]); - // Not Match >= 0.25.0-0 - assert_prerelease_match_none(r, &["0.25.0-0", "0.25.0", "0.25.8", "2.0.0-0"]); - - let ref r = req("0.8.0"); - // Match >= 0.8.0, < 0.9.0-0 - assert_prerelease_match_all(r, &["0.8.0", "0.8.1-0", "0.8.1-pre", "0.8.1"]); - // Not Match <0.8.0 - assert_prerelease_match_none(r, &["0.1.0", "0.8.0-pre"]); - // Not Match >=0.9.0-0 - assert_prerelease_match_none(r, &["0.9.0-0", "1.0.0-pre", "2.0.0-0"]); - - let ref r = req("0.8.0-0"); - // Match >= 0.8.0-0, < 0.9.0-0 - assert_prerelease_match_all(r, &["0.8.0-0", "0.8.0", "0.8.1-pre", "0.8.1"]); - // Not Match < 0.8.0-0 - assert_prerelease_match_none(r, &["0.1.0", "0.7.7-pre", "0.7.7"]); - // Not Match >= 0.9.0-0 - assert_prerelease_match_none(r, &["0.9.0-0", "0.9.0", "1.0.0-pre", "2.0.0-0"]); -} - -#[test] -fn major_caret() { - let ref r = req("0"); - // Match >= 0.0.0, < 1.0.0-0 - assert_prerelease_match_all(r, &["0.0.0", "0.0.1-0", "0.0.1-pre", "0.1.1"]); - // Not Match < 0.0.0 - assert_prerelease_match_none(r, &["0.0.0-pre"]); - // Not Match >= 1.0.0-0 - assert_prerelease_match_none(r, &["1.0.0-0", "1.0.0", "1.0.1-pre", "2.0.0-0"]); - - let ref r = req("0.0"); - // Match >= 0.0.0, < 0.1.0-0 - assert_prerelease_match_all(r, &["0.0.1-z0", "0.0.9"]); - // Not Match >= 0.1.0-0 - assert_prerelease_match_none(r, &["0.1.0-0", "0.1.0", "0.1.1-pre", "0.1.1-z0", "1.1.0"]); - - let ref r = req("0.0.0"); - // Match >= 0.0.0, < 0.0.1-0 - assert_prerelease_match_all(r, &["0.0.0"]); - // Not Match >= 0.0.1-0 - assert_prerelease_match_none(r, &["0.0.1-0", "0.0.1", "1.0.1-pre"]); - - let ref r = req("1"); - // Match >= 1.0.0, < 2.0.0-0 - assert_prerelease_match_all(r, &["1.2.3", "1.2.4-0", "1.2.4", "1.8.8"]); - // Not Match < 1.0.0 - assert_prerelease_match_none(r, &["0.0.8", "0.9.0", "1.0.0-pre"]); - // Not Match >= 2.0.0-0 - assert_prerelease_match_none(r, &["2.0.0-0", "2.0.0", "2.0.1"]); - - let ref r = req("1.2"); - // Match >= 1.2.0, < 1.3.0-0 - assert_prerelease_match_all(r, &["1.2.3", "1.2.4-0", "1.2.4", "1.8.8"]); - // Not Match < 1.2.0 - assert_prerelease_match_none(r, &["0.0.8", "0.9.0", "1.0.0-pre"]); - // Not Match >= 1.3.0-0 - assert_prerelease_match_none(r, &["2.0.0-0", "2.0.0", "2.0.1"]); - - let ref r = req("1.2.3"); - // Match >=1.2.3, < 2.0.0-0 - assert_prerelease_match_all(r, &["1.2.3", "1.2.4-0", "1.2.4", "1.8.8"]); - // Not Match < 1.2.3 - assert_prerelease_match_none(r, &["0.8.8", "1.2.0", "1.2.3-pre"]); - // Not Match >= 2.0.0-0 - assert_prerelease_match_none(r, &["2.0.0-0", "2.0.0", "2.0.1"]); - - let ref r = req("1.2.3-0"); - // Match >= 1.2.3-0, < 2.0.0-0 - assert_prerelease_match_all(r, &["1.2.3-pre", "1.2.3", "1.2.4-0", "1.2.4"]); - // Not Match < 1.2.3-0, >= 2.0.0-0 - assert_prerelease_match_none(r, &["1.2.0", "2.0.0-0", "2.0.0"]); -} - -#[test] +#[cfg(not(any(feature = "mirror_node_matches_prerelease", test_node_semver)))] fn test_exact() { - // =I.J.K equivalent to >=I.J.K, =I.J.0, = 4.2.0, < 4.3.0-0 - assert_prerelease_match_all(r, &["4.2.0", "4.2.1", "4.2.4-pre", "4.2.9"]); - // Not Match < 4.2.0 - assert_prerelease_match_none(r, &["0.0.1", "2.1.2-pre", "4.0.0-pre"]); - // Not Match >= 4.3.0-0 - assert_prerelease_match_none(r, &["4.3.0-0", "4.3.0", "5.0.0-0", "5.0.0"]); - - // =I equivalent to >=I.0.0, <(I+1).0.0-0 - let ref r = req("=4"); - // Match >= 4.0.0, < 5.0.0-0 - assert_prerelease_match_all(r, &["4.0.0", "4.2.1", "4.2.4-pre", "4.9.9"]); - // Not Match < 4.0.0 - assert_prerelease_match_none(r, &["0.0.1", "2.1.2-pre", "4.0.0-pre"]); - // Not Match >= 5.0.0-0 - assert_prerelease_match_none(r, &["5.0.0-0", "5.0.0", "5.0.1"]); - // =I.J.K-pre only match I.J.K-pre let ref r = req("=4.2.1-0"); // Only exactly match 4.2.1-0 assert_prerelease_match_all(r, &["4.2.1-0"]); // Not match others - assert_prerelease_match_none(r, &["1.2.3", "4.2.0", "4.2.1-1", "4.2.2", "4.3.5"]); -} + assert_prerelease_match_none(r, &["1.2.3", "4.2.0", "4.2.1-1", "4.2.2"]); -#[test] -fn test_greater() { - // >I.J.K - let ref r = req(">4.2.1"); - assert_prerelease_match_all(r, &["4.2.2", "5.0.0-0", "5.0.0"]); - assert_prerelease_match_none(r, &["0.0.0", "4.2.1-pre", "4.2.1"]); - // >I.J equivalent to >=I.(J+1).0-0 - let ref r = req(">4.2"); - assert_prerelease_match_all(r, &["4.3.0-pre", "4.3.0", "5.0.0"]); - assert_prerelease_match_none(r, &["0.0.0", "4.2.1"]); - // >I equivalent to >=(I+1).0.0-0 - let ref r = req(">4"); - assert_prerelease_match_all(r, &["5.0.0-0", "5.0.0-1", "5.0.0"]); - assert_prerelease_match_none(r, &["0.0.0", "4.2.1"]); + // =I.J.K equivalent to >=I.J.K, =4.2.1, <4.2.2-0")] { + assert_prerelease_match_all(r, &["4.2.1"]); + assert_prerelease_match_none(r, &["1.2.3", "4.2.1-0", "4.2.2-0", "4.2.2"]); + } + + // =I.J equivalent to >=I.J.0, =4.2.0, <4.3.0-0")] { + assert_prerelease_match_all(r, &["4.2.0", "4.2.1", "4.2.9"]); + assert_prerelease_match_none(r, &["0.0.1", "2.1.2-0", "4.2.0-0"]); + assert_prerelease_match_none(r, &["4.3.0-0", "4.3.0", "5.0.0-0", "5.0.0"]); + } + + // =I equivalent to >=I.0.0, <(I+1).0.0-0 + for r in &[req("=4"), req(">=4.0.0, <5.0.0-0")] { + assert_prerelease_match_all(r, &["4.0.0", "4.2.1", "4.2.4-0", "4.9.9"]); + assert_prerelease_match_none(r, &["0.0.1", "2.1.2-0", "4.0.0-0"]); + assert_prerelease_match_none(r, &["5.0.0-0", "5.0.0", "5.0.1"]); + } } #[test] +#[cfg(not(any(feature = "mirror_node_matches_prerelease", test_node_semver)))] fn test_greater_eq() { + // >=I.J.K-0 + let ref r = req(">=4.2.1-0"); + assert_prerelease_match_all(r, &["4.2.1-0", "4.2.1", "5.0.0"]); + assert_prerelease_match_none(r, &["0.0.0", "1.2.3"]); + // >=I.J.K let ref r = req(">=4.2.1"); - assert_prerelease_match_all(r, &["4.2.1", "5.0.0-0", "5.0.0"]); - assert_prerelease_match_none(r, &["0.0.0", "4.2.1-pre"]); + assert_prerelease_match_all(r, &["4.2.1", "5.0.0"]); + assert_prerelease_match_none(r, &["0.0.0", "4.2.1-0"]); + // >=I.J equivalent to >=I.J.0 - let ref r = req(">=4.2"); - assert_prerelease_match_all(r, &["4.2.0", "4.2.1-pre", "4.3.0", "5.0.0"]); - assert_prerelease_match_none(r, &["0.0.0", "4.1.1", "4.2.0-0"]); + for r in &[req(">=4.2"), req(">=4.2.0")] { + assert_prerelease_match_all(r, &["4.2.1-0", "4.2.0", "4.3.0"]); + assert_prerelease_match_none(r, &["0.0.0", "4.1.1", "4.2.0-0"]); + } + // >=I equivalent to >=I.0.0 - let ref r = req(">=4"); - assert_prerelease_match_all(r, &["4.0.0", "4.1.0-1", "5.0.0"]); - assert_prerelease_match_none(r, &["0.0.0", "4.0.0-pre"]); + for r in &[req(">=4"), req(">=4.0.0")] { + assert_prerelease_match_all(r, &["4.0.0", "4.1.0-1", "5.0.0"]); + assert_prerelease_match_none(r, &["0.0.0", "1.2.3", "4.0.0-0"]); + } } #[test] +#[cfg(not(any(feature = "mirror_node_matches_prerelease", test_node_semver)))] fn test_less() { + // 0) — equivalent to >=I.J.K-0, <(I+1).0.0-0 + for r in &[req("^1.2.3-0"), req(">=1.2.3-0, <2.0.0-0")] { + assert_prerelease_match_all(r, &["1.2.3-0", "1.2.3-1", "1.2.3", "1.9.9"]); + assert_prerelease_match_none(r, &["0.0.9", "1.1.1-0", "2.0.0-0", "2.1.1"]); + } + + // ^I.J.K (for I>0) — equivalent to >=I.J.K, <(I+1).0.0-0 + for r in &[req("^1.2.3"), req(">=1.2.3, <2.0.0-0")] { + assert_prerelease_match_all(r, &["1.2.3", "1.9.9"]); + assert_prerelease_match_none( + r, + &["0.0.9", "1.1.1-0", "1.2.3-0", "1.2.3-1", "2.0.0-0", "2.1.1"], + ); + } + + // ^0.J.K-0 (for J>0) — equivalent to >=0.J.K-0, <0.(J+1).0-0 + for r in &[req("^0.2.3-0"), req(">=0.2.3-0, <0.3.0-0")] { + assert_prerelease_match_all(r, &["0.2.3-0", "0.2.3", "0.2.9-0", "0.2.9"]); + assert_prerelease_match_none(r, &["0.0.9", "0.3.0-0", "0.3.11", "1.1.1"]); + } + + // ^0.J.K (for J>0) — equivalent to >=0.J.K-0, <0.(J+1).0-0 + for r in &[req("^0.2.3"), req(">=0.2.3, <0.3.0-0")] { + assert_prerelease_match_all(r, &["0.2.3", "0.2.9-0", "0.2.9"]); + assert_prerelease_match_none(r, &["0.0.9", "0.2.3-0", "0.3.0-0", "0.3.11", "1.1.1"]); + } + + // ^0.0.K-0 — equivalent to >=0.0.K-0, <0.0.(K+1)-0 + for r in &[req("^0.0.3-0"), req(">=0.0.3-0, <0.1.0-0")] { + assert_prerelease_match_all(r, &["0.0.3-0", "0.0.3-1", "0.0.3"]); + assert_prerelease_match_none(r, &["0.0.1", "0.3.0-0", "0.4.0-0", "1.1.1"]); + } + + // ^0.0.K — equivalent to >=0.0.K, <0.0.(K+1)-0 + for r in &[req("^0.0.3"), req(">=0.0.3, <0.1.0-0")] { + assert_prerelease_match_all(r, &["0.0.3"]); + assert_prerelease_match_none( + r, + &["0.0.1", "0.0.3-0", "0.3.0-0", "0.0.3-1", "0.4.0-0", "1.1.1"], + ); + } + + // ^I.J (for I>0 or J>0) — equivalent to >=I.J.0, <(I+1).0.0-0) + for r in &[req("^1.2"), req(">=1.2.0, <2.0.0-0")] { + assert_prerelease_match_all(r, &["1.2.0", "1.9.0-0", "1.9.9"]); + assert_prerelease_match_none(r, &["0.0.1", "0.0.4-0", "1.2.0-0", "2.0.0-0", "4.0.1"]); + } + + // ^0.0 — equivalent to >=0.0.0, <0.1.0-0 + for r in &[req("^0.0"), req(">=0.0.0, <0.1.0-0")] { + assert_prerelease_match_all(r, &["0.0.0", "0.0.1", "0.0.4-0"]); + assert_prerelease_match_none(r, &["0.0.0-0", "0.1.0-0", "0.1.0", "1.1.1"]); + } + + // ^I — equivalent to >=I.0.0, <(I+1).0.0-0 + for r in &[req("^1"), req(">=1.0.0, <2.0.0-0")] { + assert_prerelease_match_all(r, &["1.0.0", "1.0.1"]); + assert_prerelease_match_none(r, &["0.1.0-0", "0.1.0", "1.0.0-0", "2.0.0-0", "3.1.2"]); + } +} + +#[test] +#[cfg(not(any(feature = "mirror_node_matches_prerelease", test_node_semver)))] +fn test_wildcard() { + // I.J.* — equivalent to =I.J + // + // =I.J equivalent to >=I.J.0, = 4.2.0, < 4.3.0-0 + assert_prerelease_match_all(r, &["4.2.0", "4.2.1", "4.2.9"]); + // Not Match < 4.2.0 + assert_prerelease_match_none(r, &["0.0.1", "2.1.2-0", "4.2.0-0"]); + // Not Match >= 4.3.0-0 + assert_prerelease_match_none(r, &["4.3.0-0", "4.3.0", "5.0.0", "5.0.1"]); + } + + // I.* or I.*.* — equivalent to =I + // + // =I equivalent to >=I.0.0, <(I+1).0.0-0 + for r in &[req("4.*"), req("4.*.*"), req("=4")] { + // Match >= 4.0.0, < 5.0.0-0 + assert_prerelease_match_all(r, &["4.0.0", "4.2.1", "4.9.9"]); + // Not Match < 4.0.0 + assert_prerelease_match_none(r, &["0.0.1", "2.1.2-0", "4.0.0-0"]); + // Not Match >= 5.0.0-0 + assert_prerelease_match_none(r, &["5.0.0-0", "5.0.0", "5.0.1"]); + } +} + +// +// These tests below can pass in both implementations +// + +#[test] +fn test_greater() { + // >I.J.K-0 + let ref r = req(">4.2.1-0"); + assert_prerelease_match_all(r, &["4.2.1", "4.2.2", "5.0.0"]); + assert_prerelease_match_none(r, &["0.0.0", "4.2.1-0"]); + + // >I.J.K + let ref r = req(">4.2.1"); + assert_prerelease_match_all(r, &["4.2.2", "5.0.0-0", "5.0.0"]); + assert_prerelease_match_none(r, &["0.0.0", "4.2.1-0", "4.2.1"]); + + // >I.J equivalent to >=I.(J+1).0-0 + for r in &[req(">4.2"), req(">=4.3.0-0")] { + assert_prerelease_match_all(r, &["4.3.0-0", "4.3.0", "5.0.0"]); + assert_prerelease_match_none(r, &["0.0.0", "4.2.1"]); + } + + // >I equivalent to >=(I+1).0.0-0 + for r in &[req(">4"), req(">=5.0.0-0")] { + assert_prerelease_match_all(r, &["5.0.0-0", "5.0.0"]); + assert_prerelease_match_none(r, &["0.0.0", "4.2.1"]); + } } #[test] fn test_less_eq() { // <=I.J.K let ref r = req("<=4.2.1"); - assert_prerelease_match_all(r, &["0.0.0", "4.2.1-pre", "4.2.1"]); + assert_prerelease_match_all(r, &["0.0.0", "4.2.1-0", "4.2.1"]); assert_prerelease_match_none(r, &["4.2.2", "5.0.0-0", "5.0.0"]); + // <=I.J.K-0 + let ref r = req("<=4.2.1-0"); + assert_prerelease_match_all(r, &["0.0.0", "4.2.1-0"]); + assert_prerelease_match_none(r, &["4.2.1", "4.2.2", "5.0.0-0", "5.0.0"]); + // <=I.J equivalent to =I.J.K-0, = 1.2.3-0, < 1.3.0-0")] { + assert_prerelease_match_all(r, &["1.2.3-0", "1.2.3", "1.2.4-0", "1.2.4"]); + assert_prerelease_match_none(r, &["0.0.1", "1.1.0-0"]); + assert_prerelease_match_none(r, &["1.3.0-0", "1.3.0", "1.3.1", "2.0.0"]); + } + // ~I.J.K — equivalent to >=I.J.K, = 1.2.3, < 1.3.0-0 - assert_prerelease_match_all(r, &["1.2.3", "1.2.4-pre", "1.2.4"]); - // Not Match < 1.2.3 - assert_prerelease_match_none(r, &["0.0.1", "1.0.0-pre", "1.1.0-0", "1.1.0"]); - // Not Match >= 1.3.0-0 - assert_prerelease_match_none(r, &["1.3.0-0", "1.3.0", "1.3.1-0", "1.3.1", "2.0.0"]); + for r in &[req("~1.2.3"), req(">= 1.2.3, < 1.3.0-0")] { + assert_prerelease_match_all(r, &["1.2.3", "1.2.4-0", "1.2.4"]); + assert_prerelease_match_none(r, &["0.0.1", "1.1.0-0", "1.2.3-0"]); + assert_prerelease_match_none(r, &["1.3.0-0", "1.3.0", "1.3.1", "2.0.0"]); + } // ~I.J — equivalent to >=I.J.0, = 0.24.0, < 0.25.0-0 - assert_prerelease_match_all(r, &["0.24.0", "0.24.1-pre", "0.24.1", "0.24.9"]); - // Not Match < 0.24.0 - assert_prerelease_match_none(r, &["0.0.1", "0.9.9", "0.24.0-pre"]); - // Not Match >= 0.25.0-0 - assert_prerelease_match_none(r, &["0.25.0-0", "1.1.0", "1.2.3", "2.0.0"]); + for r in &[req("~0.24"), req(">=0.24.0, <0.25.0-0")] { + assert_prerelease_match_all(r, &["0.24.0", "0.24.1-0", "0.24.1", "0.24.9"]); + assert_prerelease_match_none(r, &["0.0.1", "0.9.9", "0.24.0-0"]); + assert_prerelease_match_none(r, &["0.25.0-0", "1.1.0", "1.2.3", "2.0.0"]); + } // ~I — >=I.0.0, <(I+1).0.0-0 - let ref r = req("~1"); - // Match >= 1.0.0, < 2.0.0-0 - assert_prerelease_match_all(r, &["1.0.0", "1.1.0-0", "1.1.0", "1.2.3"]); - // Not Match < 1.0.0 - assert_prerelease_match_none(r, &["0.0.1", "0.9.9", "1.0.0-pre"]); - // Not Match >= 2.0.0-0 - assert_prerelease_match_none(r, &["2.0.0-0", "2.0.0", "2.0.1"]); - - // ~I.J.K-[pre] — equivalent to >=I.J.K-[pre], = 1.2.3-0, < 1.3.0-0 - assert_prerelease_match_all(r, &["1.2.3-0", "1.2.3", "1.2.4-pre", "1.2.4"]); - // Not Match < 1.2.3-0 - assert_prerelease_match_none(r, &["0.0.1", "1.0.0-pre", "1.1.0-0", "1.1.0"]); - // Not Match >= 1.3.0-0 - assert_prerelease_match_none(r, &["1.3.0-0", "1.3.0", "1.3.1-0", "1.3.1", "2.0.0"]); + for r in &[req("~1"), req(">=1.0.0, <2.0.0-0")] { + assert_prerelease_match_all(r, &["1.0.0", "1.1.0-0", "1.1.0"]); + assert_prerelease_match_none(r, &["0.0.1", "0.9.9", "1.0.0-0"]); + assert_prerelease_match_none(r, &["2.0.0-0", "2.0.0", "2.0.1"]); + } } +// +// These tests below are for node semver compatibility test. (with includePrerelease=true, see https://github.com/npm/node-semver?tab=readme-ov-file#functions) +// + #[test] +#[cfg(any(feature = "mirror_node_matches_prerelease", test_node_semver))] +fn test_exact() { + // =I.J.K-pre only match I.J.K-pre + let ref r = req("=4.2.1-0"); + // Only exactly match 4.2.1-0 + assert_prerelease_match_all(r, &["4.2.1-0"]); + // Not match others + assert_prerelease_match_none(r, &["1.2.3", "4.2.0", "4.2.1-1", "4.2.2"]); + + // =I.J.K equivalent to >=I.J.K, =4.2.1, <4.2.2-0")] { + assert_prerelease_match_all(r, &["4.2.1"]); + assert_prerelease_match_none(r, &["1.2.3", "4.2.1-0", "4.2.2-0", "4.2.2"]); + } + + // =I.J equivalent to >=I.J.0-0, =4.2.0-0, <4.3.0-0")] { + assert_prerelease_match_all(r, &["4.2.0-0", "4.2.0", "4.2.1", "4.2.9"]); + assert_prerelease_match_none(r, &["0.0.1", "2.1.2-0"]); + assert_prerelease_match_none(r, &["4.3.0-0", "4.3.0", "5.0.0-0", "5.0.0"]); + } + + // =I equivalent to >=I.0.0-0, <(I+1).0.0-0 + for r in &[req("=4"), req(">=4.0.0-0, <5.0.0-0")] { + assert_prerelease_match_all(r, &["4.0.0-0", "4.0.0", "4.2.1", "4.2.4-0", "4.9.9"]); + assert_prerelease_match_none(r, &["0.0.1", "2.1.2-0"]); + assert_prerelease_match_none(r, &["5.0.0-0", "5.0.0", "5.0.1"]); + } +} + +#[test] +#[cfg(any(feature = "mirror_node_matches_prerelease", test_node_semver))] +fn test_greater_eq() { + // >=I.J.K-0 + let ref r = req(">=4.2.1-0"); + assert_prerelease_match_all(r, &["4.2.1-0", "4.2.1", "5.0.0"]); + assert_prerelease_match_none(r, &["0.0.0", "1.2.3"]); + + // >=I.J.K + let ref r = req(">=4.2.1"); + assert_prerelease_match_all(r, &["4.2.1", "5.0.0"]); + assert_prerelease_match_none(r, &["0.0.0", "4.2.1-0"]); + + // >=I.J equivalent to >=I.J.0-0 + for r in &[req(">=4.2"), req(">=4.2.0-0")] { + assert_prerelease_match_all(r, &["4.2.0-0", "4.2.0", "4.2.1-0", "4.3.0"]); + assert_prerelease_match_none(r, &["0.0.0", "4.1.1"]); + } + + // >=I equivalent to >=I.0.0-0 + for r in &[req(">=4"), req(">=4.0.0-0")] { + assert_prerelease_match_all(r, &["4.0.0-0", "4.0.0", "4.1.0-1", "5.0.0"]); + assert_prerelease_match_none(r, &["0.0.0", "1.2.3"]); + } +} + +#[test] +#[cfg(any(feature = "mirror_node_matches_prerelease", test_node_semver))] +fn test_less() { + // 0) — equivalent to >=I.J.K-0, <(I+1).0.0-0 + for r in &[req("^1.2.3-0"), req(">=1.2.3-0, <2.0.0-0")] { + assert_prerelease_match_all(r, &["1.2.3-0", "1.2.3-1", "1.2.3", "1.9.9"]); + assert_prerelease_match_none(r, &["0.0.9", "1.1.1-0", "2.0.0-0", "2.1.1"]); + } + // ^I.J.K (for I>0) — equivalent to >=I.J.K, <(I+1).0.0-0 - let ref r = req("^1.2.3"); - assert_prerelease_match_all(r, &["1.2.3", "1.2.4-0", "1.8.9"]); - assert_prerelease_match_none(r, &["0.0.9", "1.1.1-0", "1.2.3-0", "2.0.0-pre", "2.1.1"]); + for r in &[req("^1.2.3"), req(">=1.2.3, <2.0.0-0")] { + assert_prerelease_match_all(r, &["1.2.3", "1.9.9"]); + assert_prerelease_match_none( + r, + &["0.0.9", "1.1.1-0", "1.2.3-0", "1.2.3-1", "2.0.0-0", "2.1.1"], + ); + } - // ^0.J.K (for J>0) — equivalent to >=0.J.K, <0.(J+1).0-0 - let ref r = req("^0.2.3"); - assert_prerelease_match_all(r, &["0.2.3", "0.2.9-0", "0.2.9"]); - assert_prerelease_match_none(r, &["0.0.9", "0.2.3-0", "0.3.0-0", "0.3.11", "1.1.1"]); + // ^0.J.K-0 (for J>0) — equivalent to >=0.J.K-0, <0.(J+1).0-0 + // ^0.J.K (for J>0) — equivalent to >=0.J.K-0, <0.(J+1).0-0 + for r in &[req("^0.2.3-0"), req("^0.2.3"), req(">=0.2.3-0, <0.3.0-0")] { + assert_prerelease_match_all(r, &["0.2.3-0", "0.2.3", "0.2.9-0", "0.2.9"]); + assert_prerelease_match_none(r, &["0.0.9", "0.3.0-0", "0.3.11", "1.1.1"]); + } - // ^0.0.K — equivalent to >=0.0.K, <0.0.(K+1)-0 - let ref r = req("^0.0.3"); - assert_prerelease_match_all(r, &["0.0.3"]); - assert_prerelease_match_none( - r, - &["0.0.1", "0.0.4-0", "0.0.9", "0.3.0-0", "0.4.0-0", "1.1.1"], - ); + // ^0.0.K-0 — equivalent to >=0.0.K-0, <0.0.(K+1)-0 + // ^0.0.K — equivalent to >=0.0.K-0, <0.0.(K+1)-0 + for r in &[req("^0.0.3-0"), req("^0.0.3"), req(">=0.0.3-0, <0.1.0-0")] { + assert_prerelease_match_all(r, &["0.0.3-0", "0.0.3-1", "0.0.3"]); + assert_prerelease_match_none(r, &["0.0.1", "0.3.0-0", "0.4.0-0", "1.1.1"]); + } - // ^I.J (for I>0 or J>0) — equivalent to >=I.J.0, <(I+1).0.0-0) - let ref r = req("^1.2"); - assert_prerelease_match_all(r, &["1.2.0", "1.9.0-0", "1.9.9"]); - assert_prerelease_match_none(r, &["0.0.1", "0.0.4-0", "1.2.0-0", "2.0.0-0", "4.0.1"]); + // ^I.J (for I>0 or J>0) — equivalent to >=I.J.0-0, <(I+1).0.0-0) + for r in &[req("^1.2"), req(">=1.2.0-0, <2.0.0-0")] { + assert_prerelease_match_all(r, &["1.2.0-0", "1.2.0", "1.9.0-0", "1.9.9"]); + assert_prerelease_match_none(r, &["0.0.1", "0.0.4-0", "2.0.0-0", "4.0.1"]); + } - // ^0.0 — equivalent to >=0.0.0, <0.1.0-0 - let ref r = req("^0.0"); - assert_prerelease_match_all(r, &["0.0.0", "0.0.1", "0.0.4-0"]); - assert_prerelease_match_none(r, &["0.1.0-0", "0.1.0", "1.1.1"]); + // ^0.0 — equivalent to >=0.0.0-0, <0.1.0-0 + for r in &[req("^0.0"), req(">=0.0.0-0, <0.1.0-0")] { + assert_prerelease_match_all(r, &["0.0.0-0", "0.0.0", "0.0.1", "0.0.4-0"]); + assert_prerelease_match_none(r, &["0.1.0-0", "0.1.0", "1.1.1"]); + } - // ^I — equivalent to >=I.0.0, <(I+1).0.0-0 - let ref r = req("^1"); - assert_prerelease_match_all(r, &["1.0.0", "1.0.1", "1.0.4-0"]); - assert_prerelease_match_none(r, &["0.1.0-0", "0.1.0", "2.0.0-0", "3.1.2"]); + // ^I — equivalent to >=I.0.0-0, <(I+1).0.0-0 + for r in &[req("^1"), req(">=1.0.0-0, <2.0.0-0")] { + assert_prerelease_match_all(r, &["1.0.0-0", "1.0.0", "1.0.1"]); + assert_prerelease_match_none(r, &["0.1.0-0", "0.1.0", "2.0.0-0", "3.1.2"]); + } } #[test] +#[cfg(any(feature = "mirror_node_matches_prerelease", test_node_semver))] fn test_wildcard() { // I.J.* — equivalent to =I.J // - // =I.J equivalent to >=I.J.0, = 4.2.0, < 4.3.0-0 - assert_prerelease_match_all(r, &["4.2.0", "4.2.1", "4.2.4-pre", "4.2.9"]); - // Not Match < 4.2.0 - assert_prerelease_match_none(r, &["0.0.1", "2.1.2-pre", "4.0.0-pre"]); - // Not Match >= 4.3.0-0 - assert_prerelease_match_none(r, &["4.3.0-0", "4.3.0", "5.0.0-0", "5.0.0", "5.0.1"]); + // =I.J equivalent to >=I.J.0-0, =I.0.0, <(I+1).0.0-0 - for r in &[req("4.*"), req("4.*.*")] { - // Match >= 4.0.0, < 5.0.0-0 - assert_prerelease_match_all(r, &["4.0.0", "4.2.1", "4.2.4-pre", "4.9.9"]); - // Not Match < 4.0.0 - assert_prerelease_match_none(r, &["0.0.1", "2.1.2-pre", "4.0.0-pre"]); - // Not Match >= 5.0.0-0 + // =I equivalent to >=I.0.0-0, <(I+1).0.0-0 + for r in &[req("4.*"), req("4.*.*"), req("=4")] { + assert_prerelease_match_all(r, &["4.0.0-0", "4.0.0", "4.2.1", "4.9.9"]); + assert_prerelease_match_none(r, &["0.0.1", "2.1.2-0"]); assert_prerelease_match_none(r, &["5.0.0-0", "5.0.0", "5.0.1"]); } }