use color_eyre::Result;
use std::cmp::Ordering;
use crate::models::PackageFormat;
use crate::parse_requires::{Operator, VersionConstraint};
use crate::parse_version::PackageVersion;
use crate::conda_pkg::VERSION_BUILD_SEPARATOR;
use crate::version_compare::*;
use log;
fn normalize_constraint_operand(
operand: &str,
operator: &Operator,
format: PackageFormat,
provided_version: Option<&str>,
) -> (String, bool) {
let mut use_greater_than_equal = false;
let normalized = if operand.ends_with("~~") {
let base = operand.trim_end_matches("~~").trim_end_matches('-');
match operator {
Operator::VersionGreaterThanEqual => {
base.to_string()
}
Operator::VersionGreaterThan => {
let has_revision = if let Some(dash_pos) = base.rfind('-') {
let after_dash = &base[dash_pos + 1..];
!after_dash.is_empty() && after_dash.chars().next().unwrap().is_ascii_digit()
} else {
false
};
if has_revision {
use_greater_than_equal = true;
}
base.to_string()
}
Operator::VersionLessThan => {
compute_next_version(&base)
}
Operator::VersionLessThanEqual => {
base.to_string()
}
_ => {
base.to_string()
}
}
} else if format == PackageFormat::Deb && operand.ends_with('~') {
match operator {
Operator::VersionGreaterThanEqual | Operator::VersionGreaterThan => {
if let Some(provided) = provided_version {
let (provided_upstream, _) = if let Some(dash_pos) = provided.rfind('-') {
let after_dash = &provided[dash_pos + 1..];
if !after_dash.is_empty() && after_dash.chars().next().unwrap().is_ascii_digit() {
(&provided[..dash_pos], true)
} else {
(provided, false)
}
} else {
(provided, false)
};
let constraint_base = operand.trim_end_matches('~');
if provided_upstream.contains('~') {
operand.to_string()
} else if provided_upstream == constraint_base ||
compare_versions(provided_upstream, constraint_base, format)
.map(|cmp| cmp == Ordering::Greater || cmp == Ordering::Equal)
.unwrap_or(false) {
constraint_base.to_string()
} else {
operand.to_string()
}
} else {
operand.trim_end_matches('~').to_string()
}
}
_ => {
operand.to_string()
}
}
} else {
operand.to_string()
};
(normalized, use_greater_than_equal)
}
fn check_conda_version_build_string_constraint(
package_version: &str,
constraint: &VersionConstraint,
format: PackageFormat,
normalized_operand: &str,
use_greater_than_equal: bool,
) -> Option<Option<bool>> {
if !normalized_operand.contains('=') || format != PackageFormat::Conda {
return None;
}
let parts: Vec<&str> = normalized_operand.split('=').collect();
if parts.len() != 2 {
return None;
}
let constraint_version = parts[0];
let build_string_pattern = parts[1];
let mut version_parts = package_version.splitn(2, VERSION_BUILD_SEPARATOR);
let package_version_part = version_parts.next().unwrap_or(package_version);
let package_build_string = version_parts.next();
let version_only_constraint = VersionConstraint {
operator: constraint.operator.clone(),
operand: constraint_version.to_string(),
};
let version_satisfies = check_version_constraint_core(
package_version_part,
&version_only_constraint,
format,
constraint_version,
use_greater_than_equal,
)?;
if !version_satisfies {
return Some(Some(false));
}
let build_string_matches = check_build_string_pattern(package_build_string, build_string_pattern);
Some(Some(build_string_matches))
}
fn should_use_upstream_comparison(
normalized_operand: &str,
format: PackageFormat,
) -> bool {
if !matches!(format, PackageFormat::Rpm | PackageFormat::Pacman | PackageFormat::Apk) {
return false;
}
let has_explicit_revision = if let Some(dash_pos) = normalized_operand.rfind('-') {
let after_dash = &normalized_operand[dash_pos + 1..];
if after_dash.is_empty() {
false
} else {
let first_char = after_dash.chars().next().unwrap();
if first_char.is_ascii_digit() {
true
} else if first_char.is_ascii_alphabetic() {
let lower_after = after_dash.to_lowercase();
let is_prerelease = lower_after.starts_with("rc") ||
lower_after.starts_with("beta") ||
lower_after.starts_with("alpha") ||
lower_after.starts_with("pre") ||
lower_after.starts_with("dev") ||
lower_after.starts_with("snapshot");
!is_prerelease
} else {
false
}
}
} else {
false
};
!has_explicit_revision
&& PackageVersion::parse(normalized_operand)
.map(|ver| ver.revision == "0")
.unwrap_or(false)
}
fn compare_version_with_operator(
package_version: &str,
constraint: &VersionConstraint,
format: PackageFormat,
normalized_operand: &str,
use_upstream_comparison: bool,
use_greater_than_equal: bool,
) -> Option<bool> {
match &constraint.operator {
crate::parse_requires::Operator::VersionGreaterThan => {
if use_greater_than_equal {
if use_upstream_comparison {
compare_upstream_versions_ordering_with_format(package_version, normalized_operand, Some(format))
.map(|cmp| cmp != Ordering::Less)
} else {
compare_versions(package_version, normalized_operand, format)
.map(|cmp| cmp != Ordering::Less)
}
} else {
if use_upstream_comparison {
compare_upstream_versions_ordering_with_format(package_version, normalized_operand, Some(format))
.map(|cmp| cmp == Ordering::Greater)
} else {
compare_versions(package_version, normalized_operand, format)
.map(|cmp| cmp == Ordering::Greater)
}
}
}
crate::parse_requires::Operator::VersionGreaterThanEqual => {
if use_upstream_comparison {
compare_upstream_versions_ordering_with_format(package_version, normalized_operand, Some(format))
.map(|cmp| cmp != Ordering::Less)
} else {
compare_versions(package_version, normalized_operand, format)
.map(|cmp| cmp != Ordering::Less)
}
}
crate::parse_requires::Operator::VersionLessThan => {
if use_upstream_comparison {
compare_upstream_versions_ordering_with_format(package_version, normalized_operand, Some(format))
.map(|cmp| cmp == Ordering::Less)
} else {
compare_versions(package_version, normalized_operand, format)
.map(|cmp| cmp == Ordering::Less)
}
}
crate::parse_requires::Operator::VersionLessThanEqual => {
if use_upstream_comparison {
compare_upstream_versions_ordering_with_format(package_version, normalized_operand, Some(format))
.map(|cmp| cmp != Ordering::Greater)
} else {
compare_versions(package_version, normalized_operand, format)
.map(|cmp| cmp != Ordering::Greater)
}
}
crate::parse_requires::Operator::VersionEqual => {
check_version_equal(package_version, normalized_operand, format)
}
crate::parse_requires::Operator::VersionNotEqual => {
if use_upstream_comparison {
compare_upstream_versions_ordering_with_format(package_version, normalized_operand, Some(format))
.map(|cmp| cmp != Ordering::Equal)
} else {
compare_versions(package_version, normalized_operand, format)
.map(|cmp| cmp != Ordering::Equal)
}
}
crate::parse_requires::Operator::VersionCompatible => {
if format == PackageFormat::Apk {
check_apk_fuzzy_version(package_version, normalized_operand)
} else if format == PackageFormat::Python {
check_python_compatible_release(package_version, normalized_operand, format)
} else {
compare_versions(package_version, normalized_operand, format)
.map(|cmp| cmp != Ordering::Less)
}
}
_ => {
None
}
}
}
fn check_version_equal(
version1: &str,
version2: &str,
format: PackageFormat,
) -> Option<bool> {
match format {
PackageFormat::Rpm | PackageFormat::Pacman | PackageFormat::Apk => {
compare_upstream_versions_with_format(version1, version2, Some(format)).ok()
}
PackageFormat::Deb => {
let v1_base = normalize_version_for_equality(version1, format);
let v2_base = normalize_version_for_equality(version2, format);
compare_versions(v1_base, v2_base, format)
.map(|cmp| cmp == Ordering::Equal)
}
PackageFormat::Conda => {
let v1_version_part = version1.split(VERSION_BUILD_SEPARATOR).next().unwrap_or(version1);
let v2_version_part = version2.split(VERSION_BUILD_SEPARATOR).next().unwrap_or(version2);
compare_versions(v1_version_part, v2_version_part, format)
.map(|cmp| cmp == Ordering::Equal)
}
_ => {
compare_versions(version1, version2, format)
.map(|cmp| cmp == Ordering::Equal)
}
}
}
fn check_version_constraint_core(
package_version: &str,
constraint: &VersionConstraint,
format: PackageFormat,
normalized_operand: &str,
use_greater_than_equal: bool,
) -> Option<bool> {
if let Some(result) = check_conda_version_build_string_constraint(
package_version,
constraint,
format,
normalized_operand,
use_greater_than_equal,
) {
return result;
}
let use_upstream_comparison = should_use_upstream_comparison(normalized_operand, format);
compare_version_with_operator(
package_version,
constraint,
format,
normalized_operand,
use_upstream_comparison,
use_greater_than_equal,
)
}
fn check_build_string_pattern(
package_build_string: Option<&str>,
build_string_pattern: &str,
) -> bool {
if build_string_pattern == "*" {
true
} else if build_string_pattern.starts_with('*') {
let suffix = &build_string_pattern[1..];
if let Some(build_str) = package_build_string {
build_str.ends_with(suffix)
} else {
false
}
} else if build_string_pattern.ends_with('*') {
let prefix = &build_string_pattern[..build_string_pattern.len() - 1];
if let Some(build_str) = package_build_string {
build_str.starts_with(prefix)
} else {
false
}
} else {
if let Some(build_str) = package_build_string {
build_str == build_string_pattern
} else {
false
}
}
}
fn check_version_equal_pattern(
package_version: &str,
constraint: &VersionConstraint,
format: PackageFormat,
normalized_operand: &str,
) -> bool {
if constraint.operand == "*" {
return true;
}
if constraint.operand.contains('=') && format == PackageFormat::Conda {
let parts: Vec<&str> = constraint.operand.split('=').collect();
if parts.len() == 2 {
let version_pattern = parts[0];
let build_string_pattern = parts[1];
let mut version_parts = package_version.splitn(2, VERSION_BUILD_SEPARATOR);
let version_part = version_parts.next().unwrap_or(package_version);
let build_string_part = version_parts.next();
let version_matches = if version_pattern == "*" {
true
} else if version_pattern.ends_with(".*") {
let prefix = &version_pattern[..version_pattern.len() - 2];
version_part.starts_with(prefix) || version_part == prefix
} else if version_pattern.ends_with('*') {
let prefix = &version_pattern[..version_pattern.len() - 1];
version_part.starts_with(prefix)
} else {
check_version_equal(version_part, version_pattern, format).unwrap_or(false)
};
if !version_matches {
return false;
}
let build_string_matches = check_build_string_pattern(build_string_part, build_string_pattern);
return build_string_matches;
}
return false;
}
if constraint.operand.ends_with(".*") {
if format == PackageFormat::Conda {
let prefix_without_dot = &constraint.operand[..constraint.operand.len() - 2];
if package_version == prefix_without_dot {
return true;
}
if let Some(next_char) = package_version.chars().nth(prefix_without_dot.len()) {
return package_version.starts_with(prefix_without_dot)
&& (next_char == '.' || next_char == '-');
}
return false;
} else {
let prefix_with_dot = &constraint.operand[..constraint.operand.len() - 1];
return package_version.starts_with(prefix_with_dot);
}
}
if constraint.operand.ends_with('*') {
let prefix = &constraint.operand[..constraint.operand.len() - 1];
return package_version.starts_with(prefix);
}
check_version_equal(package_version, normalized_operand, format).unwrap_or(false)
}
fn check_version_not_equal_pattern(
package_version: &str,
constraint: &VersionConstraint,
format: PackageFormat,
normalized_operand: &str,
use_greater_than_equal: bool,
) -> bool {
if constraint.operand.ends_with(".*") {
let prefix = &constraint.operand[..constraint.operand.len() - 1];
return !package_version.starts_with(prefix);
}
if constraint.operand.ends_with('*') {
let prefix = &constraint.operand[..constraint.operand.len() - 1];
return !package_version.starts_with(prefix);
}
check_version_constraint_core(
package_version,
constraint,
format,
normalized_operand,
use_greater_than_equal,
)
.unwrap_or(false)
}
pub fn check_version_constraint(
package_version: &str,
constraint: &VersionConstraint,
format: PackageFormat,
) -> Result<bool> {
if constraint.operand.contains("%{") {
log::trace!(
"Ignoring constraint with unexpanded RPM macro: {} {}",
format!("{:?}", constraint.operator),
constraint.operand
);
return Ok(true);
}
let (operand, use_greater_than_equal) = normalize_constraint_operand(
&constraint.operand,
&constraint.operator,
format,
Some(package_version),
);
if matches!(constraint.operator, Operator::VersionLessThan) && constraint.operand.ends_with("~~") {
let base = constraint.operand.trim_end_matches("~~").trim_end_matches('-');
if let (Ok(pkg_ver), Ok(base_ver)) = (PackageVersion::parse(package_version), PackageVersion::parse(&base)) {
if pkg_ver.upstream == base_ver.upstream {
return Ok(false);
}
}
}
let satisfies = match &constraint.operator {
crate::parse_requires::Operator::VersionEqual => {
check_version_equal_pattern(package_version, constraint, format, &operand)
}
crate::parse_requires::Operator::VersionNotEqual => {
check_version_not_equal_pattern(
package_version,
constraint,
format,
&operand,
use_greater_than_equal,
)
}
crate::parse_requires::Operator::IfInstall => {
true
}
_ => {
check_version_constraint_core(
package_version,
constraint,
format,
&operand,
use_greater_than_equal,
)
.unwrap_or(false)
}
};
Ok(satisfies)
}
pub fn check_version_satisfies_constraints(
version: &str,
constraints: &Vec<VersionConstraint>,
format: PackageFormat,
) -> Result<bool> {
let mut or_groups: Vec<Vec<&VersionConstraint>> = Vec::new();
let mut and_constraints: Vec<&VersionConstraint> = Vec::new();
let non_conditional_constraints: Vec<&VersionConstraint> = constraints.iter()
.filter(|c| !matches!(c.operator, Operator::IfInstall))
.collect();
let mut processed = vec![false; non_conditional_constraints.len()];
for i in 0..non_conditional_constraints.len() {
if processed[i] {
continue;
}
let constraint_i = non_conditional_constraints[i];
let mut or_group = vec![constraint_i];
processed[i] = true;
for j in (i + 1)..non_conditional_constraints.len() {
if processed[j] {
continue;
}
let constraint_j = non_conditional_constraints[j];
let are_mutually_exclusive = match (&constraint_i.operator, &constraint_j.operator) {
(Operator::VersionLessThan, Operator::VersionGreaterThan) |
(Operator::VersionLessThan, Operator::VersionGreaterThanEqual) |
(Operator::VersionLessThanEqual, Operator::VersionGreaterThan) |
(Operator::VersionLessThanEqual, Operator::VersionGreaterThanEqual) |
(Operator::VersionGreaterThan, Operator::VersionLessThan) |
(Operator::VersionGreaterThan, Operator::VersionLessThanEqual) |
(Operator::VersionGreaterThanEqual, Operator::VersionLessThan) |
(Operator::VersionGreaterThanEqual, Operator::VersionLessThanEqual) => {
if constraint_i.operand == constraint_j.operand {
true
} else {
are_constraints_logically_mutually_exclusive(
constraint_i,
constraint_j,
format,
)
}
}
_ => false,
};
if are_mutually_exclusive {
let mut can_add = true;
for existing_constraint in &or_group[1..] {
let mutually_exclusive_with_existing = match (&existing_constraint.operator, &constraint_j.operator) {
(Operator::VersionLessThan, Operator::VersionGreaterThan) |
(Operator::VersionLessThan, Operator::VersionGreaterThanEqual) |
(Operator::VersionLessThanEqual, Operator::VersionGreaterThan) |
(Operator::VersionLessThanEqual, Operator::VersionGreaterThanEqual) |
(Operator::VersionGreaterThan, Operator::VersionLessThan) |
(Operator::VersionGreaterThan, Operator::VersionLessThanEqual) |
(Operator::VersionGreaterThanEqual, Operator::VersionLessThan) |
(Operator::VersionGreaterThanEqual, Operator::VersionLessThanEqual) => {
if existing_constraint.operand == constraint_j.operand {
true
} else {
are_constraints_logically_mutually_exclusive(
existing_constraint,
constraint_j,
format,
)
}
}
_ => false,
};
if !mutually_exclusive_with_existing {
can_add = false;
break;
}
}
if can_add {
or_group.push(constraint_j);
processed[j] = true;
}
}
}
if or_group.len() > 1 {
or_groups.push(or_group);
} else {
and_constraints.push(constraint_i);
}
}
for constraint in &and_constraints {
if !check_version_constraint(version, constraint, format)? {
return Ok(false);
}
}
for or_group in &or_groups {
let mut any_satisfied = false;
for constraint in or_group {
if check_version_constraint(version, constraint, format)? {
any_satisfied = true;
break;
}
}
if !any_satisfied {
return Ok(false);
}
}
Ok(true)
}
pub fn are_constraints_logically_mutually_exclusive(
constraint1: &VersionConstraint,
constraint2: &VersionConstraint,
format: PackageFormat,
) -> bool {
if constraint1.operand.contains("%{") || constraint2.operand.contains("%{") {
return false;
}
let normalize_operand_for_comparison = |op: &str| -> (String, bool) {
if op.ends_with("~~") {
let base = op.trim_end_matches("~~");
(base.to_string(), true)
} else {
(op.to_string(), false)
}
};
let (op1_base, op1_is_tilde) = normalize_operand_for_comparison(&constraint1.operand);
let (op2_base, op2_is_tilde) = normalize_operand_for_comparison(&constraint2.operand);
let comparison = compare_versions(&op1_base, &op2_base, format);
match comparison {
Some(std::cmp::Ordering::Less) => {
match (&constraint1.operator, &constraint2.operator) {
(Operator::VersionLessThan, Operator::VersionGreaterThanEqual) |
(Operator::VersionLessThan, Operator::VersionGreaterThan) |
(Operator::VersionLessThanEqual, Operator::VersionGreaterThan) => {
true
}
(Operator::VersionLessThanEqual, Operator::VersionGreaterThanEqual) => {
true
}
(Operator::VersionGreaterThanEqual, Operator::VersionLessThan) |
(Operator::VersionGreaterThanEqual, Operator::VersionLessThanEqual) |
(Operator::VersionGreaterThan, Operator::VersionLessThan) |
(Operator::VersionGreaterThan, Operator::VersionLessThanEqual) => {
false
}
_ => false,
}
}
Some(std::cmp::Ordering::Equal) => {
match (&constraint1.operator, &constraint2.operator) {
(Operator::VersionLessThan, Operator::VersionGreaterThanEqual) |
(Operator::VersionLessThan, Operator::VersionGreaterThan) |
(Operator::VersionLessThanEqual, Operator::VersionGreaterThan) |
(Operator::VersionGreaterThan, Operator::VersionLessThanEqual) |
(Operator::VersionGreaterThan, Operator::VersionLessThan) |
(Operator::VersionGreaterThanEqual, Operator::VersionLessThan) => {
if op1_is_tilde == op2_is_tilde {
matches!((&constraint1.operator, &constraint2.operator),
(Operator::VersionLessThan, Operator::VersionGreaterThan) |
(Operator::VersionGreaterThan, Operator::VersionLessThan))
} else {
false
}
}
(Operator::VersionLessThanEqual, Operator::VersionGreaterThanEqual) => {
false
}
_ => false,
}
}
Some(std::cmp::Ordering::Greater) => {
match (&constraint2.operator, &constraint1.operator) {
(Operator::VersionLessThan, Operator::VersionGreaterThanEqual) |
(Operator::VersionLessThan, Operator::VersionGreaterThan) |
(Operator::VersionLessThanEqual, Operator::VersionGreaterThan) => {
true
}
(Operator::VersionLessThanEqual, Operator::VersionGreaterThanEqual) => {
true
}
(Operator::VersionGreaterThanEqual, Operator::VersionLessThan) |
(Operator::VersionGreaterThanEqual, Operator::VersionLessThanEqual) |
(Operator::VersionGreaterThan, Operator::VersionLessThan) |
(Operator::VersionGreaterThan, Operator::VersionLessThanEqual) => {
false
}
_ => false,
}
}
None => {
false
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_check_version_equal_conda_with_build() {
assert_eq!(
check_version_equal("2.26", "2.26-5", PackageFormat::Conda),
Some(true),
"Constraint '=2.26' should match package version '2.26-5' (build number should be ignored)"
);
assert_eq!(
check_version_equal("2.26-5", "2.26", PackageFormat::Conda),
Some(true),
"Package version '2.26-5' should match constraint '=2.26'"
);
assert_eq!(
check_version_equal("2.26", "2.27-5", PackageFormat::Conda),
Some(false),
"Different versions should not match"
);
assert_eq!(
check_version_equal("2.26-5", "2.26-10", PackageFormat::Conda),
Some(true),
"Same version with different build numbers should match"
);
assert_eq!(
check_version_equal("1.0", "1.0-1", PackageFormat::Conda),
Some(true),
"Version '1.0' should match '1.0-1'"
);
}
#[test]
fn test_conda_version_underscore_normalization() {
let result = compare_versions("1.3_7", "1.3.3", PackageFormat::Conda);
assert_eq!(result, Some(Ordering::Greater),
"1.3_7 should be greater than 1.3.3 (underscore normalized to dot)");
assert_eq!(
check_version_equal("1.3_7", "1.3.7", PackageFormat::Conda),
Some(true),
"1.3_7 should equal 1.3.7 (underscore normalized to dot)"
);
let constraint_ge = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "1.3.3".to_string(),
};
assert!(check_version_constraint("1.3_7", &constraint_ge, PackageFormat::Conda).unwrap(),
"1.3_7 should satisfy >=1.3.3 (underscore normalized to dot)");
assert!(check_version_constraint("1.3_7-r43h6115d3f_0", &constraint_ge, PackageFormat::Conda).unwrap(),
"1.3_7-r43h6115d3f_0 should satisfy >=1.3.3 (version part normalized)");
let constraint_gt = VersionConstraint {
operator: Operator::VersionGreaterThan,
operand: "1.3.3".to_string(),
};
assert!(check_version_constraint("1.3_7", &constraint_gt, PackageFormat::Conda).unwrap(),
"1.3_7 should satisfy >1.3.3");
let constraint_le = VersionConstraint {
operator: Operator::VersionLessThanEqual,
operand: "1.3.3".to_string(),
};
assert!(!check_version_constraint("1.3_7", &constraint_le, PackageFormat::Conda).unwrap(),
"1.3_7 should NOT satisfy <=1.3.3");
let constraint_lt = VersionConstraint {
operator: Operator::VersionLessThan,
operand: "1.3.3".to_string(),
};
assert!(!check_version_constraint("1.3_7", &constraint_lt, PackageFormat::Conda).unwrap(),
"1.3_7 should NOT satisfy <1.3.3");
assert!(!check_version_constraint("1.3_2", &constraint_ge, PackageFormat::Conda).unwrap(),
"1.3_2 should NOT satisfy >=1.3.3");
assert!(check_version_constraint("1.3_2", &constraint_le, PackageFormat::Conda).unwrap(),
"1.3_2 should satisfy <=1.3.3");
assert_eq!(
check_version_equal("1_2_3", "1.2.3", PackageFormat::Conda),
Some(true),
"1_2_3 should equal 1.2.3"
);
assert_eq!(
check_version_equal("1.3_7", "1.3.7", PackageFormat::Conda),
Some(true),
"1.3_7 should equal 1.3.7 (mixed separators normalized)"
);
}
#[test]
fn test_check_version_equal_rpm_with_dot_extension() {
assert_eq!(
check_version_equal("1.14.6-3.fc42", "1.14", PackageFormat::Rpm),
Some(true),
"Constraint '=1.14' should match package version '1.14.6-3.fc42' (dot extension should be allowed)"
);
assert_eq!(
check_version_equal("1.14.6-3.fc42", "1.15", PackageFormat::Rpm),
Some(false),
"Different versions should not match"
);
assert_eq!(
check_version_equal("1.14.6-3.fc42", "1.14.6-5.fc42", PackageFormat::Rpm),
Some(true),
"Same upstream version with different releases should match"
);
assert_eq!(
check_version_equal("9.8.3-bp160.1.34", "9.8.3", PackageFormat::Rpm),
Some(true),
"Constraint '=9.8.3' should match package version '9.8.3-bp160.1.34' (dash extension should be allowed)"
);
}
#[test]
fn test_rpm_version_constraint_with_release() {
let constraint = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "1.0.11".to_string(),
};
let satisfies = check_version_constraint("1.0.11-bp160.1.13", &constraint, PackageFormat::Rpm);
assert!(satisfies.is_ok(), "check_version_constraint should succeed");
assert!(satisfies.unwrap(),
"1.0.11-bp160.1.13 should satisfy >= 1.0.11 in RPM format (version with release >= version without release)");
let constraint2 = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "1.0.11-bp160.1.13".to_string(),
};
let satisfies2 = check_version_constraint("1.0.11", &constraint2, PackageFormat::Rpm);
assert!(satisfies2.is_ok(), "check_version_constraint should succeed");
assert!(!satisfies2.unwrap(),
"1.0.11 should NOT satisfy >= 1.0.11-bp160.1.13 in RPM format (version without release < version with release)");
let constraint3 = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "12.0.0".to_string(),
};
let satisfies3 = check_version_constraint("12.0.0-bp160.1.2", &constraint3, PackageFormat::Rpm);
assert!(satisfies3.is_ok(), "check_version_constraint should succeed");
assert!(satisfies3.unwrap(),
"12.0.0-bp160.1.2 should satisfy >= 12.0.0 in RPM format");
}
#[test]
fn test_rpm_version_constraint_upstream_only_operand() {
let constraint = VersionConstraint {
operator: Operator::VersionLessThanEqual,
operand: "3.1.0".to_string(),
};
let satisfies = check_version_constraint("3.1.0-17.oe2403sp1", &constraint, PackageFormat::Rpm);
assert!(satisfies.is_ok(), "check_version_constraint should succeed");
assert!(satisfies.unwrap(),
"3.1.0-17.oe2403sp1 should satisfy <= 3.1.0 in RPM format (compare upstream only)");
let constraint2 = VersionConstraint {
operator: Operator::VersionLessThanEqual,
operand: "3.0.9".to_string(),
};
let satisfies2 = check_version_constraint("3.1.0-17.oe2403sp1", &constraint2, PackageFormat::Rpm);
assert!(satisfies2.is_ok(), "check_version_constraint should succeed");
assert!(!satisfies2.unwrap(),
"3.1.0-17.oe2403sp1 should NOT satisfy <= 3.0.9 in RPM format");
let constraint3 = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "3.1.0".to_string(),
};
let satisfies3 = check_version_constraint("3.1.0-17.oe2403sp1", &constraint3, PackageFormat::Rpm);
assert!(satisfies3.is_ok(), "check_version_constraint should succeed");
assert!(satisfies3.unwrap(),
"3.1.0-17.oe2403sp1 should satisfy >= 3.1.0 in RPM format (compare upstream only)");
let constraint4 = VersionConstraint {
operator: Operator::VersionLessThan,
operand: "3.1.1".to_string(),
};
let satisfies4 = check_version_constraint("3.1.0-17.oe2403sp1", &constraint4, PackageFormat::Rpm);
assert!(satisfies4.is_ok(), "check_version_constraint should succeed");
assert!(satisfies4.unwrap(),
"3.1.0-17.oe2403sp1 should satisfy < 3.1.1 in RPM format (compare upstream only)");
let constraint5 = VersionConstraint {
operator: Operator::VersionGreaterThan,
operand: "3.0.9".to_string(),
};
let satisfies5 = check_version_constraint("3.1.0-17.oe2403sp1", &constraint5, PackageFormat::Rpm);
assert!(satisfies5.is_ok(), "check_version_constraint should succeed");
assert!(satisfies5.unwrap(),
"3.1.0-17.oe2403sp1 should satisfy > 3.0.9 in RPM format (compare upstream only)");
let constraint6 = VersionConstraint {
operator: Operator::VersionNotEqual,
operand: "3.0.9".to_string(),
};
let satisfies6 = check_version_constraint("3.1.0-17.oe2403sp1", &constraint6, PackageFormat::Rpm);
assert!(satisfies6.is_ok(), "check_version_constraint should succeed");
assert!(satisfies6.unwrap(),
"3.1.0-17.oe2403sp1 should satisfy != 3.0.9 in RPM format (compare upstream only)");
let constraint7 = VersionConstraint {
operator: Operator::VersionNotEqual,
operand: "3.1.0".to_string(),
};
let satisfies7 = check_version_constraint("3.1.0-17.oe2403sp1", &constraint7, PackageFormat::Rpm);
assert!(satisfies7.is_ok(), "check_version_constraint should succeed");
assert!(!satisfies7.unwrap(),
"3.1.0-17.oe2403sp1 should NOT satisfy != 3.1.0 in RPM format (same upstream)");
}
#[test]
fn test_rpm_version_constraint_with_release_operand() {
let constraint = VersionConstraint {
operator: Operator::VersionLessThanEqual,
operand: "3.1.0-16".to_string(),
};
let satisfies = check_version_constraint("3.1.0-17.oe2403sp1", &constraint, PackageFormat::Rpm);
assert!(satisfies.is_ok(), "check_version_constraint should succeed");
assert!(!satisfies.unwrap(),
"3.1.0-17.oe2403sp1 should NOT satisfy <= 3.1.0-16 in RPM format (compare full version)");
let constraint2 = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "3.1.0-18".to_string(),
};
let satisfies2 = check_version_constraint("3.1.0-17.oe2403sp1", &constraint2, PackageFormat::Rpm);
assert!(satisfies2.is_ok(), "check_version_constraint should succeed");
assert!(!satisfies2.unwrap(),
"3.1.0-17.oe2403sp1 should NOT satisfy >= 3.1.0-18 in RPM format (compare full version)");
let constraint3 = VersionConstraint {
operator: Operator::VersionLessThanEqual,
operand: "3.1.0-20".to_string(),
};
let satisfies3 = check_version_constraint("3.1.0-17.oe2403sp1", &constraint3, PackageFormat::Rpm);
assert!(satisfies3.is_ok(), "check_version_constraint should succeed");
assert!(satisfies3.unwrap(),
"3.1.0-17.oe2403sp1 should satisfy <= 3.1.0-20 in RPM format (compare full version)");
}
#[test]
fn test_pacman_apk_version_constraint_upstream_only_operand() {
let constraint = VersionConstraint {
operator: Operator::VersionLessThanEqual,
operand: "3.1.0".to_string(),
};
let satisfies = check_version_constraint("3.1.0-17", &constraint, PackageFormat::Pacman);
assert!(satisfies.is_ok(), "check_version_constraint should succeed");
assert!(satisfies.unwrap(),
"3.1.0-17 should satisfy <= 3.1.0 in Pacman format (compare upstream only)");
let constraint2 = VersionConstraint {
operator: Operator::VersionLessThanEqual,
operand: "5.82".to_string(),
};
let satisfies2 = check_version_constraint("5.82-r0", &constraint2, PackageFormat::Apk);
assert!(satisfies2.is_ok(), "check_version_constraint should succeed");
assert!(satisfies2.unwrap(),
"5.82-r0 should satisfy <= 5.82 in Apk format (compare upstream only)");
}
#[test]
fn test_pacman_version_constraint_without_explicit_epoch() {
let constraint = VersionConstraint {
operator: Operator::VersionEqual,
operand: "11.9.0".to_string(),
};
let satisfies = check_version_constraint("1:11.9.0-1", &constraint, PackageFormat::Pacman);
assert!(satisfies.is_ok(), "check_version_constraint should succeed");
assert!(satisfies.unwrap(),
"Package '1:11.9.0-1' should satisfy constraint 'libvirt(=11.9.0)' in Pacman format");
assert_eq!(
check_version_equal("1:11.9.0-1", "11.9.0", PackageFormat::Pacman),
Some(true),
"check_version_equal: '1:11.9.0-1' should match '11.9.0' in Pacman format"
);
assert_eq!(
check_version_equal("11.9.0", "1:11.9.0-1", PackageFormat::Pacman),
Some(true),
"check_version_equal: '11.9.0' should match '1:11.9.0-1' in Pacman format"
);
assert_eq!(
check_version_equal("2:11.9.0-1", "11.9.0", PackageFormat::Pacman),
Some(true),
"check_version_equal: '2:11.9.0-1' should match '11.9.0' in Pacman format"
);
let constraint_explicit = VersionConstraint {
operator: Operator::VersionEqual,
operand: "1:11.9.0".to_string(),
};
let satisfies_explicit = check_version_constraint("1:11.9.0-1", &constraint_explicit, PackageFormat::Pacman);
assert!(satisfies_explicit.is_ok(), "check_version_constraint should succeed");
assert!(satisfies_explicit.unwrap(),
"Package '1:11.9.0-1' should satisfy constraint '1:11.9.0' (explicit epoch match)");
let constraint_mismatch = VersionConstraint {
operator: Operator::VersionEqual,
operand: "1:11.9.0".to_string(),
};
let satisfies_mismatch = check_version_constraint("2:11.9.0-1", &constraint_mismatch, PackageFormat::Pacman);
assert!(satisfies_mismatch.is_ok(), "check_version_constraint should succeed");
assert!(!satisfies_mismatch.unwrap(),
"Package '2:11.9.0-1' should NOT satisfy constraint '1:11.9.0' (explicit epoch mismatch)");
let constraint_ge = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "11.9.0".to_string(),
};
let satisfies_ge = check_version_constraint("1:11.9.0-1", &constraint_ge, PackageFormat::Pacman);
assert!(satisfies_ge.is_ok(), "check_version_constraint should succeed");
assert!(satisfies_ge.unwrap(),
"Package '1:11.9.0-1' should satisfy constraint '>=11.9.0' in Pacman format");
let constraint_le = VersionConstraint {
operator: Operator::VersionLessThanEqual,
operand: "11.9.0".to_string(),
};
let satisfies_le = check_version_constraint("1:11.9.0-1", &constraint_le, PackageFormat::Pacman);
assert!(satisfies_le.is_ok(), "check_version_constraint should succeed");
assert!(satisfies_le.unwrap(),
"Package '1:11.9.0-1' should satisfy constraint '<=11.9.0' in Pacman format");
}
#[test]
fn test_rpm_version_constraint_without_explicit_epoch() {
let constraint = VersionConstraint {
operator: Operator::VersionEqual,
operand: "11.9.0".to_string(),
};
let satisfies = check_version_constraint("1:11.9.0-1", &constraint, PackageFormat::Rpm);
assert!(satisfies.is_ok(), "check_version_constraint should succeed");
assert!(satisfies.unwrap(),
"Package '1:11.9.0-1' should satisfy constraint '11.9.0' in RPM format");
assert_eq!(
check_version_equal("1:11.9.0-1", "11.9.0", PackageFormat::Rpm),
Some(true),
"check_version_equal: '1:11.9.0-1' should match '11.9.0' in RPM format"
);
}
#[test]
fn test_deb_version_constraint_full_comparison() {
let constraint = VersionConstraint {
operator: Operator::VersionLessThanEqual,
operand: "3.1.0".to_string(),
};
let satisfies = check_version_constraint("3.1.0-1", &constraint, PackageFormat::Deb);
assert!(satisfies.is_ok(), "check_version_constraint should succeed");
assert!(!satisfies.unwrap(),
"3.1.0-1 should NOT satisfy <= 3.1.0 in Debian format (compare full version)");
}
#[test]
fn test_rpm_version_constraint_openeuler_case() {
let constraint = VersionConstraint {
operator: Operator::VersionLessThanEqual,
operand: "3.1.0".to_string(),
};
let satisfies = check_version_constraint("3.1.0-17.oe2403sp1", &constraint, PackageFormat::Rpm);
assert!(satisfies.is_ok(), "check_version_constraint should succeed");
assert!(satisfies.unwrap(),
"3.1.0-17.oe2403sp1 should satisfy <= 3.1.0 in RPM format (openeuler case)");
let constraint2 = VersionConstraint {
operator: Operator::VersionLessThanEqual,
operand: "3.1.0".to_string(),
};
let satisfies2 = check_version_constraint("3.1.0-1.oe2403sp1", &constraint2, PackageFormat::Rpm);
assert!(satisfies2.is_ok(), "check_version_constraint should succeed");
assert!(satisfies2.unwrap(),
"3.1.0-1.oe2403sp1 should satisfy <= 3.1.0 in RPM format");
let constraint3 = VersionConstraint {
operator: Operator::VersionLessThanEqual,
operand: "3.1.0".to_string(),
};
let satisfies3 = check_version_constraint("3.1.1-1.oe2403sp1", &constraint3, PackageFormat::Rpm);
assert!(satisfies3.is_ok(), "check_version_constraint should succeed");
assert!(!satisfies3.unwrap(),
"3.1.1-1.oe2403sp1 should NOT satisfy <= 3.1.0 in RPM format");
}
#[test]
fn test_rpm_version_with_tilde_comparison() {
let result = compare_versions("25.0.2", "25.0.0~rc2", PackageFormat::Rpm);
assert_eq!(
result,
Some(Ordering::Greater),
"25.0.2 should be greater than 25.0.0~rc2 in RPM format, got {:?}", result
);
let result2 = compare_versions("25.0.2-3.fc42", "25.0.0~rc2-1", PackageFormat::Rpm);
assert_eq!(
result2,
Some(Ordering::Greater),
"25.0.2-3.fc42 should be greater than 25.0.0~rc2-1 in RPM format, got {:?}", result2
);
let constraint = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "25.0.0~rc2-1".to_string(),
};
let satisfies = check_version_constraint("25.0.2-3.fc42", &constraint, PackageFormat::Rpm);
assert!(satisfies.is_ok(), "check_version_constraint should succeed");
assert!(satisfies.unwrap(),
"25.0.2-3.fc42 should satisfy >= 25.0.0~rc2-1 in RPM format");
}
#[test]
fn test_rpm_version_constraint_explicit_revision_zero() {
let constraint = VersionConstraint {
operator: Operator::VersionGreaterThan,
operand: "2.1.4-0".to_string(),
};
let satisfies = check_version_constraint("2.1.5", &constraint, PackageFormat::Rpm);
assert!(satisfies.is_ok(), "check_version_constraint should succeed");
assert!(satisfies.unwrap(),
"2.1.5 should satisfy > 2.1.4-0 in RPM format (full version comparison)");
let satisfies2 = check_version_constraint("2.1.4-1", &constraint, PackageFormat::Rpm);
assert!(satisfies2.is_ok(), "check_version_constraint should succeed");
assert!(satisfies2.unwrap(),
"2.1.4-1 should satisfy > 2.1.4-0 in RPM format (full version comparison)");
let satisfies3 = check_version_constraint("2.1.4-0", &constraint, PackageFormat::Rpm);
assert!(satisfies3.is_ok(), "check_version_constraint should succeed");
assert!(!satisfies3.unwrap(),
"2.1.4-0 should NOT satisfy > 2.1.4-0 in RPM format (equal version)");
let satisfies4 = check_version_constraint("2.1.4", &constraint, PackageFormat::Rpm);
assert!(satisfies4.is_ok(), "check_version_constraint should succeed");
assert!(!satisfies4.unwrap(),
"2.1.4 should NOT satisfy > 2.1.4-0 in RPM format (equal version, full comparison)");
let satisfies5 = check_version_constraint("2.1.3", &constraint, PackageFormat::Rpm);
assert!(satisfies5.is_ok(), "check_version_constraint should succeed");
assert!(!satisfies5.unwrap(),
"2.1.3 should NOT satisfy > 2.1.4-0 in RPM format (lesser version)");
let constraint2 = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "2.1.4-0".to_string(),
};
let satisfies6 = check_version_constraint("2.1.4-0", &constraint2, PackageFormat::Rpm);
assert!(satisfies6.is_ok(), "check_version_constraint should succeed");
assert!(satisfies6.unwrap(),
"2.1.4-0 should satisfy >= 2.1.4-0 in RPM format (equal version)");
let satisfies7 = check_version_constraint("2.1.4", &constraint2, PackageFormat::Rpm);
assert!(satisfies7.is_ok(), "check_version_constraint should succeed");
assert!(satisfies7.unwrap(),
"2.1.4 should satisfy >= 2.1.4-0 in RPM format (equal version, full comparison)");
let constraint3 = VersionConstraint {
operator: Operator::VersionLessThan,
operand: "2.1.4-0".to_string(),
};
let satisfies8 = check_version_constraint("2.1.3", &constraint3, PackageFormat::Rpm);
assert!(satisfies8.is_ok(), "check_version_constraint should succeed");
assert!(satisfies8.unwrap(),
"2.1.3 should satisfy < 2.1.4-0 in RPM format (lesser version)");
let satisfies9 = check_version_constraint("2.1.4-0", &constraint3, PackageFormat::Rpm);
assert!(satisfies9.is_ok(), "check_version_constraint should succeed");
assert!(!satisfies9.unwrap(),
"2.1.4-0 should NOT satisfy < 2.1.4-0 in RPM format (equal version)");
let satisfies10 = check_version_constraint("2.1.5", &constraint3, PackageFormat::Rpm);
assert!(satisfies10.is_ok(), "check_version_constraint should succeed");
assert!(!satisfies10.unwrap(),
"2.1.5 should NOT satisfy < 2.1.4-0 in RPM format (greater version)");
}
#[test]
fn test_rpm_version_constraint_explicit_revision_zero_vs_no_revision() {
let constraint_no_rev = VersionConstraint {
operator: Operator::VersionGreaterThan,
operand: "2.1.4".to_string(),
};
let satisfies1 = check_version_constraint("2.1.4-1", &constraint_no_rev, PackageFormat::Rpm);
assert!(satisfies1.is_ok(), "check_version_constraint should succeed");
assert!(!satisfies1.unwrap(),
"2.1.4-1 should NOT satisfy > 2.1.4 in RPM format (upstream-only comparison, equal upstream)");
let satisfies1b = check_version_constraint("2.1.5", &constraint_no_rev, PackageFormat::Rpm);
assert!(satisfies1b.is_ok(), "check_version_constraint should succeed");
assert!(satisfies1b.unwrap(),
"2.1.5 should satisfy > 2.1.4 in RPM format (upstream-only comparison, greater upstream)");
let constraint_no_rev_ge = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "2.1.4".to_string(),
};
let satisfies1c = check_version_constraint("2.1.4-1", &constraint_no_rev_ge, PackageFormat::Rpm);
assert!(satisfies1c.is_ok(), "check_version_constraint should succeed");
assert!(satisfies1c.unwrap(),
"2.1.4-1 should satisfy >= 2.1.4 in RPM format (upstream-only comparison, equal upstream)");
let constraint_explicit_rev = VersionConstraint {
operator: Operator::VersionGreaterThan,
operand: "2.1.4-0".to_string(),
};
let satisfies2 = check_version_constraint("2.1.4-1", &constraint_explicit_rev, PackageFormat::Rpm);
assert!(satisfies2.is_ok(), "check_version_constraint should succeed");
assert!(satisfies2.unwrap(),
"2.1.4-1 should satisfy > 2.1.4-0 in RPM format (full comparison)");
let satisfies3 = check_version_constraint("2.1.4", &constraint_explicit_rev, PackageFormat::Rpm);
assert!(satisfies3.is_ok(), "check_version_constraint should succeed");
assert!(!satisfies3.unwrap(),
"2.1.4 should NOT satisfy > 2.1.4-0 in RPM format (equal version, full comparison)");
}
#[test]
fn test_pacman_version_constraint_explicit_revision_zero() {
let constraint = VersionConstraint {
operator: Operator::VersionGreaterThan,
operand: "2.1.4-0".to_string(),
};
let satisfies = check_version_constraint("2.1.5", &constraint, PackageFormat::Pacman);
assert!(satisfies.is_ok(), "check_version_constraint should succeed");
assert!(satisfies.unwrap(),
"2.1.5 should satisfy > 2.1.4-0 in Pacman format (full version comparison)");
let satisfies2 = check_version_constraint("2.1.4-0", &constraint, PackageFormat::Pacman);
assert!(satisfies2.is_ok(), "check_version_constraint should succeed");
assert!(!satisfies2.unwrap(),
"2.1.4-0 should NOT satisfy > 2.1.4-0 in Pacman format (equal version)");
}
#[test]
fn test_apk_version_constraint_explicit_revision_zero() {
let constraint = VersionConstraint {
operator: Operator::VersionGreaterThan,
operand: "2.1.4-0".to_string(),
};
let satisfies = check_version_constraint("2.1.5", &constraint, PackageFormat::Apk);
assert!(satisfies.is_ok(), "check_version_constraint should succeed");
assert!(satisfies.unwrap(),
"2.1.5 should satisfy > 2.1.4-0 in APK format (full version comparison)");
let satisfies2 = check_version_constraint("2.1.4-0", &constraint, PackageFormat::Apk);
assert!(satisfies2.is_ok(), "check_version_constraint should succeed");
assert!(!satisfies2.unwrap(),
"2.1.4-0 should NOT satisfy > 2.1.4-0 in APK format (equal version)");
}
#[test]
fn test_rpm_version_constraint_explicit_revision_zero_pre_release() {
let constraint = VersionConstraint {
operator: Operator::VersionGreaterThan,
operand: "2.1.4-rc1".to_string(),
};
let satisfies = check_version_constraint("2.1.4", &constraint, PackageFormat::Rpm);
assert!(satisfies.is_ok(), "check_version_constraint should succeed");
assert!(satisfies.unwrap(),
"2.1.4 should satisfy > 2.1.4-rc1 in RPM format (upstream-only comparison, pre-release)");
let satisfies2 = check_version_constraint("2.1.4-rc1", &constraint, PackageFormat::Rpm);
assert!(satisfies2.is_ok(), "check_version_constraint should succeed");
assert!(!satisfies2.unwrap(),
"2.1.4-rc1 should NOT satisfy > 2.1.4-rc1 in RPM format (equal version)");
}
#[test]
fn test_version_equal_star_wildcard() {
let constraint = VersionConstraint {
operator: Operator::VersionEqual,
operand: "6.9.*".to_string(),
};
assert!(check_version_constraint("6.9.10", &constraint, PackageFormat::Conda).unwrap(),
"6.9.10 should match 6.9.*");
assert!(check_version_constraint("6.9.0", &constraint, PackageFormat::Conda).unwrap(),
"6.9.0 should match 6.9.*");
assert!(check_version_constraint("6.9.10.1", &constraint, PackageFormat::Conda).unwrap(),
"6.9.10.1 should match 6.9.*");
assert!(check_version_constraint("6.9.7.1", &constraint, PackageFormat::Conda).unwrap(),
"6.9.7.1 should match 6.9.*");
assert!(!check_version_constraint("6.10.0", &constraint, PackageFormat::Conda).unwrap(),
"6.10.0 should NOT match 6.9.*");
assert!(!check_version_constraint("7.0.0", &constraint, PackageFormat::Conda).unwrap(),
"7.0.0 should NOT match 6.9.*");
let constraint_with_build = VersionConstraint {
operator: Operator::VersionEqual,
operand: "11.2.0.*".to_string(),
};
assert!(check_version_constraint("11.2.0-h5c386dc_0", &constraint_with_build, PackageFormat::Conda).unwrap(),
"11.2.0-h5c386dc_0 should match 11.2.0.* (build string should be ignored)");
assert!(check_version_constraint("11.2.0-h5c386dc_1", &constraint_with_build, PackageFormat::Conda).unwrap(),
"11.2.0-h5c386dc_1 should match 11.2.0.*");
assert!(check_version_constraint("11.2.0-h5c386dc_2", &constraint_with_build, PackageFormat::Conda).unwrap(),
"11.2.0-h5c386dc_2 should match 11.2.0.*");
assert!(check_version_constraint("11.2.0", &constraint_with_build, PackageFormat::Conda).unwrap(),
"11.2.0 (without build string) should match 11.2.0.* (treated as 11.2.0* for Conda)");
assert!(!check_version_constraint("11.2.1-h5c386dc_0", &constraint_with_build, PackageFormat::Conda).unwrap(),
"11.2.1-h5c386dc_0 should NOT match 11.2.0.*");
assert!(check_version_constraint("6.9.10", &constraint, PackageFormat::Python).unwrap(),
"6.9.10 should match 6.9.* in Python format");
assert!(check_version_constraint("6.9.10", &constraint, PackageFormat::Deb).unwrap(),
"6.9.10 should match 6.9.* in Debian format");
}
#[test]
fn test_version_equal_dot_star_pattern_matching() {
let constraint = VersionConstraint {
operator: Operator::VersionEqual,
operand: "1.*".to_string(),
};
assert!(check_version_constraint("1", &constraint, PackageFormat::Conda).unwrap(),
"1 should match 1.* (exact match)");
assert!(check_version_constraint("1.0", &constraint, PackageFormat::Conda).unwrap(),
"1.0 should match 1.*");
assert!(check_version_constraint("1.2.3", &constraint, PackageFormat::Conda).unwrap(),
"1.2.3 should match 1.*");
assert!(check_version_constraint("1.10.20", &constraint, PackageFormat::Conda).unwrap(),
"1.10.20 should match 1.*");
assert!(check_version_constraint("1-build", &constraint, PackageFormat::Conda).unwrap(),
"1-build should match 1.* (with dash)");
assert!(check_version_constraint("1-h5c386dc_0", &constraint, PackageFormat::Conda).unwrap(),
"1-h5c386dc_0 should match 1.* (with dash and build string)");
assert!(!check_version_constraint("10.2", &constraint, PackageFormat::Conda).unwrap(),
"10.2 should NOT match 1.* (next char is '0', not '.' or '-')");
assert!(!check_version_constraint("10", &constraint, PackageFormat::Conda).unwrap(),
"10 should NOT match 1.*");
assert!(!check_version_constraint("11.2.0", &constraint, PackageFormat::Conda).unwrap(),
"11.2.0 should NOT match 1.*");
let constraint_1_2 = VersionConstraint {
operator: Operator::VersionEqual,
operand: "1.2.*".to_string(),
};
assert!(check_version_constraint("1.2", &constraint_1_2, PackageFormat::Conda).unwrap(),
"1.2 should match 1.2.* (exact match)");
assert!(check_version_constraint("1.2.0", &constraint_1_2, PackageFormat::Conda).unwrap(),
"1.2.0 should match 1.2.*");
assert!(check_version_constraint("1.2.xxx", &constraint_1_2, PackageFormat::Conda).unwrap(),
"1.2.xxx should match 1.2.*");
assert!(check_version_constraint("1.2-build", &constraint_1_2, PackageFormat::Conda).unwrap(),
"1.2-build should match 1.2.* (with dash)");
assert!(check_version_constraint("1.2-h5c386dc_0", &constraint_1_2, PackageFormat::Conda).unwrap(),
"1.2-h5c386dc_0 should match 1.2.* (with dash and build string)");
assert!(!check_version_constraint("1.20", &constraint_1_2, PackageFormat::Conda).unwrap(),
"1.20 should NOT match 1.2.* (next char is '0', not '.' or '-')");
assert!(!check_version_constraint("1.20.0", &constraint_1_2, PackageFormat::Conda).unwrap(),
"1.20.0 should NOT match 1.2.*");
let constraint_11_2_0 = VersionConstraint {
operator: Operator::VersionEqual,
operand: "11.2.0.*".to_string(),
};
assert!(check_version_constraint("11.2.0", &constraint_11_2_0, PackageFormat::Conda).unwrap(),
"11.2.0 should match 11.2.0.* (exact match)");
assert!(check_version_constraint("11.2.0.1", &constraint_11_2_0, PackageFormat::Conda).unwrap(),
"11.2.0.1 should match 11.2.0.*");
assert!(check_version_constraint("11.2.0-h5c386dc_0", &constraint_11_2_0, PackageFormat::Conda).unwrap(),
"11.2.0-h5c386dc_0 should match 11.2.0.*");
assert!(!check_version_constraint("11.2.01", &constraint_11_2_0, PackageFormat::Conda).unwrap(),
"11.2.01 should NOT match 11.2.0.*");
}
#[test]
fn test_version_not_equal_star_wildcard() {
let constraint = VersionConstraint {
operator: Operator::VersionNotEqual,
operand: "6.9.*".to_string(),
};
assert!(!check_version_constraint("6.9.10", &constraint, PackageFormat::Conda).unwrap(),
"6.9.10 should NOT match != 6.9.*");
assert!(!check_version_constraint("6.9.0", &constraint, PackageFormat::Conda).unwrap(),
"6.9.0 should NOT match != 6.9.*");
assert!(check_version_constraint("6.9", &constraint, PackageFormat::Conda).unwrap(),
"6.9 should match != 6.9.*");
assert!(check_version_constraint("6.10.0", &constraint, PackageFormat::Conda).unwrap(),
"6.10.0 should match != 6.9.*");
assert!(check_version_constraint("7.0.0", &constraint, PackageFormat::Conda).unwrap(),
"7.0.0 should match != 6.9.*");
}
#[test]
fn test_version_equal_wildcard_any() {
let constraint = VersionConstraint {
operator: Operator::VersionEqual,
operand: "*".to_string(),
};
assert!(check_version_constraint("6.9.10", &constraint, PackageFormat::Conda).unwrap(),
"* should match 6.9.10");
assert!(check_version_constraint("1.0.0", &constraint, PackageFormat::Conda).unwrap(),
"* should match 1.0.0");
assert!(check_version_constraint("2.5.3", &constraint, PackageFormat::Rpm).unwrap(),
"* should match 2.5.3 in RPM format");
assert!(check_version_constraint("3.14.159", &constraint, PackageFormat::Deb).unwrap(),
"* should match 3.14.159 in Debian format");
}
#[test]
fn test_version_equal_star_conda_pattern() {
let constraint = VersionConstraint {
operator: Operator::VersionEqual,
operand: "9*".to_string(),
};
assert!(check_version_constraint("9b", &constraint, PackageFormat::Conda).unwrap(),
"9b should match 9*");
assert!(check_version_constraint("9e", &constraint, PackageFormat::Conda).unwrap(),
"9e should match 9*");
assert!(check_version_constraint("9f", &constraint, PackageFormat::Conda).unwrap(),
"9f should match 9*");
assert!(check_version_constraint("9", &constraint, PackageFormat::Conda).unwrap(),
"9 should match 9*");
assert!(!check_version_constraint("8d", &constraint, PackageFormat::Conda).unwrap(),
"8d should NOT match 9*");
assert!(!check_version_constraint("10", &constraint, PackageFormat::Conda).unwrap(),
"10 should NOT match 9*");
}
#[test]
fn test_conda_version_range_constraints() {
let constraint1 = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "6.9.7.1".to_string(),
};
let constraint2 = VersionConstraint {
operator: Operator::VersionLessThan,
operand: "6.10.0a0".to_string(),
};
assert!(check_version_constraint("6.9.10", &constraint1, PackageFormat::Conda).unwrap(),
"6.9.10 should satisfy >= 6.9.7.1");
assert!(check_version_constraint("6.9.7.1", &constraint1, PackageFormat::Conda).unwrap(),
"6.9.7.1 should satisfy >= 6.9.7.1");
assert!(!check_version_constraint("6.9.7.0", &constraint1, PackageFormat::Conda).unwrap(),
"6.9.7.0 should NOT satisfy >= 6.9.7.1");
assert!(check_version_constraint("6.9.10", &constraint2, PackageFormat::Conda).unwrap(),
"6.9.10 should satisfy < 6.10.0a0");
assert!(check_version_constraint("6.9.99", &constraint2, PackageFormat::Conda).unwrap(),
"6.9.99 should satisfy < 6.10.0a0");
assert!(!check_version_constraint("6.10.0a0", &constraint2, PackageFormat::Conda).unwrap(),
"6.10.0a0 should NOT satisfy < 6.10.0a0");
assert!(!check_version_constraint("6.10.0", &constraint2, PackageFormat::Conda).unwrap(),
"6.10.0 should NOT satisfy < 6.10.0a0");
assert!(check_version_constraint("6.9.10", &constraint1, PackageFormat::Conda).unwrap() &&
check_version_constraint("6.9.10", &constraint2, PackageFormat::Conda).unwrap(),
"6.9.10 should satisfy both >=6.9.7.1 and <6.10.0a0");
}
#[test]
fn test_conda_version_build_string_comparison_operators() {
let constraint_ge = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "1.1.4=*_0".to_string(),
};
assert!(check_version_constraint("1.1.4-hd3eb1b0_0", &constraint_ge, PackageFormat::Conda).unwrap(),
"1.1.4-hd3eb1b0_0 should satisfy >=1.1.4=*_0");
assert!(check_version_constraint("1.1.5-hd3eb1b0_0", &constraint_ge, PackageFormat::Conda).unwrap(),
"1.1.5-hd3eb1b0_0 should satisfy >=1.1.4=*_0 (higher version, matching build)");
assert!(check_version_constraint("1.2.0-hd3eb1b0_0", &constraint_ge, PackageFormat::Conda).unwrap(),
"1.2.0-hd3eb1b0_0 should satisfy >=1.1.4=*_0 (higher version, matching build)");
assert!(!check_version_constraint("1.1.3-hd3eb1b0_0", &constraint_ge, PackageFormat::Conda).unwrap(),
"1.1.3-hd3eb1b0_0 should NOT satisfy >=1.1.4=*_0 (version too low)");
assert!(!check_version_constraint("1.1.4-hd3eb1b0_1", &constraint_ge, PackageFormat::Conda).unwrap(),
"1.1.4-hd3eb1b0_1 should NOT satisfy >=1.1.4=*_0 (build string doesn't end with _0)");
assert!(!check_version_constraint("1.1.5-hd3eb1b0_1", &constraint_ge, PackageFormat::Conda).unwrap(),
"1.1.5-hd3eb1b0_1 should NOT satisfy >=1.1.4=*_0 (build string doesn't match)");
let constraint_gt = VersionConstraint {
operator: Operator::VersionGreaterThan,
operand: "1.1.4=*_0".to_string(),
};
assert!(check_version_constraint("1.1.5-hd3eb1b0_0", &constraint_gt, PackageFormat::Conda).unwrap(),
"1.1.5-hd3eb1b0_0 should satisfy >1.1.4=*_0");
assert!(!check_version_constraint("1.1.4-hd3eb1b0_0", &constraint_gt, PackageFormat::Conda).unwrap(),
"1.1.4-hd3eb1b0_0 should NOT satisfy >1.1.4=*_0 (not greater)");
let constraint_le = VersionConstraint {
operator: Operator::VersionLessThanEqual,
operand: "1.1.4=*_0".to_string(),
};
assert!(check_version_constraint("1.1.4-hd3eb1b0_0", &constraint_le, PackageFormat::Conda).unwrap(),
"1.1.4-hd3eb1b0_0 should satisfy <=1.1.4=*_0");
assert!(check_version_constraint("1.1.3-hd3eb1b0_0", &constraint_le, PackageFormat::Conda).unwrap(),
"1.1.3-hd3eb1b0_0 should satisfy <=1.1.4=*_0");
assert!(!check_version_constraint("1.1.5-hd3eb1b0_0", &constraint_le, PackageFormat::Conda).unwrap(),
"1.1.5-hd3eb1b0_0 should NOT satisfy <=1.1.4=*_0 (version too high)");
let constraint_lt = VersionConstraint {
operator: Operator::VersionLessThan,
operand: "1.1.4=*_0".to_string(),
};
assert!(check_version_constraint("1.1.3-hd3eb1b0_0", &constraint_lt, PackageFormat::Conda).unwrap(),
"1.1.3-hd3eb1b0_0 should satisfy <1.1.4=*_0");
assert!(!check_version_constraint("1.1.4-hd3eb1b0_0", &constraint_lt, PackageFormat::Conda).unwrap(),
"1.1.4-hd3eb1b0_0 should NOT satisfy <1.1.4=*_0 (not less)");
}
#[test]
fn test_conda_version_build_string_patterns() {
let constraint_any_build = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "1.1.4=*".to_string(),
};
assert!(check_version_constraint("1.1.4-hd3eb1b0_0", &constraint_any_build, PackageFormat::Conda).unwrap(),
"1.1.4-hd3eb1b0_0 should satisfy >=1.1.4=* (any build string)");
assert!(check_version_constraint("1.1.4-any_build", &constraint_any_build, PackageFormat::Conda).unwrap(),
"1.1.4-any_build should satisfy >=1.1.4=* (any build string)");
assert!(check_version_constraint("1.1.4", &constraint_any_build, PackageFormat::Conda).unwrap(),
"1.1.4 (no build string) should satisfy >=1.1.4=*");
let constraint_suffix = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "1.1.4=*_0".to_string(),
};
assert!(check_version_constraint("1.1.4-hd3eb1b0_0", &constraint_suffix, PackageFormat::Conda).unwrap(),
"1.1.4-hd3eb1b0_0 should satisfy >=1.1.4=*_0 (ends with _0)");
assert!(check_version_constraint("1.1.4-abc_0", &constraint_suffix, PackageFormat::Conda).unwrap(),
"1.1.4-abc_0 should satisfy >=1.1.4=*_0 (ends with _0)");
assert!(!check_version_constraint("1.1.4-hd3eb1b0_1", &constraint_suffix, PackageFormat::Conda).unwrap(),
"1.1.4-hd3eb1b0_1 should NOT satisfy >=1.1.4=*_0 (doesn't end with _0)");
assert!(!check_version_constraint("1.1.4", &constraint_suffix, PackageFormat::Conda).unwrap(),
"1.1.4 (no build string) should NOT satisfy >=1.1.4=*_0 (pattern requires build string)");
let constraint_prefix = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "1.1.4=hd3*".to_string(),
};
assert!(check_version_constraint("1.1.4-hd3eb1b0_0", &constraint_prefix, PackageFormat::Conda).unwrap(),
"1.1.4-hd3eb1b0_0 should satisfy >=1.1.4=hd3* (starts with hd3)");
assert!(check_version_constraint("1.1.4-hd3xyz", &constraint_prefix, PackageFormat::Conda).unwrap(),
"1.1.4-hd3xyz should satisfy >=1.1.4=hd3* (starts with hd3)");
assert!(!check_version_constraint("1.1.4-abc123", &constraint_prefix, PackageFormat::Conda).unwrap(),
"1.1.4-abc123 should NOT satisfy >=1.1.4=hd3* (doesn't start with hd3)");
let constraint_exact = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "1.1.4=hd3eb1b0_0".to_string(),
};
assert!(check_version_constraint("1.1.4-hd3eb1b0_0", &constraint_exact, PackageFormat::Conda).unwrap(),
"1.1.4-hd3eb1b0_0 should satisfy >=1.1.4=hd3eb1b0_0 (exact match)");
assert!(check_version_constraint("1.1.5-hd3eb1b0_0", &constraint_exact, PackageFormat::Conda).unwrap(),
"1.1.5-hd3eb1b0_0 should satisfy >=1.1.4=hd3eb1b0_0 (higher version, exact build)");
assert!(!check_version_constraint("1.1.4-hd3eb1b0_1", &constraint_exact, PackageFormat::Conda).unwrap(),
"1.1.4-hd3eb1b0_1 should NOT satisfy >=1.1.4=hd3eb1b0_0 (build string mismatch)");
}
#[test]
fn test_conda_version_build_string_specific_case() {
let constraint = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "1.1.4=*_0".to_string(),
};
assert!(check_version_constraint("1.1.4-hd3eb1b0_0", &constraint, PackageFormat::Conda).unwrap(),
"1.1.4-hd3eb1b0_0 should satisfy >=1.1.4=*_0 (specific case from error)");
let constraint_audit = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "1.0.8=*_0".to_string(),
};
assert!(check_version_constraint("1.0.8-hd3eb1b0_0", &constraint_audit, PackageFormat::Conda).unwrap(),
"1.0.8-hd3eb1b0_0 should satisfy >=1.0.8=*_0");
let constraint_libselinux = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "1.33.2=*_0".to_string(),
};
assert!(check_version_constraint("1.33.2-hd3eb1b0_0", &constraint_libselinux, PackageFormat::Conda).unwrap(),
"1.33.2-hd3eb1b0_0 should satisfy >=1.33.2=*_0");
}
#[test]
fn test_version_equal_star_edge_cases() {
let constraint = VersionConstraint {
operator: Operator::VersionEqual,
operand: "1.*".to_string(),
};
assert!(check_version_constraint("1.0", &constraint, PackageFormat::Conda).unwrap(),
"1.0 should match 1.*");
assert!(check_version_constraint("1.10", &constraint, PackageFormat::Conda).unwrap(),
"1.10 should match 1.*");
assert!(check_version_constraint("1.2.3.4", &constraint, PackageFormat::Conda).unwrap(),
"1.2.3.4 should match 1.*");
assert!(!check_version_constraint("10.0", &constraint, PackageFormat::Conda).unwrap(),
"10.0 should NOT match 1.*");
let constraint_no_wildcard = VersionConstraint {
operator: Operator::VersionEqual,
operand: "6.9.10".to_string(),
};
assert!(check_version_constraint("6.9.10", &constraint_no_wildcard, PackageFormat::Conda).unwrap(),
"6.9.10 should match = 6.9.10 (fallback to equality)");
assert!(!check_version_constraint("6.9.11", &constraint_no_wildcard, PackageFormat::Conda).unwrap(),
"6.9.11 should NOT match = 6.9.10 (fallback to equality)");
}
#[test]
fn test_check_single_constraint() {
let constraint = VersionConstraint {
operator: Operator::VersionGreaterThan,
operand: "5.13".to_string(),
};
assert!(check_version_constraint("6.1", &constraint, PackageFormat::Rpm).unwrap());
assert!(!check_version_constraint("5.13", &constraint, PackageFormat::Rpm).unwrap());
assert!(!check_version_constraint("5.0", &constraint, PackageFormat::Rpm).unwrap());
let constraint = VersionConstraint {
operator: Operator::VersionLessThan,
operand: "7.0".to_string(),
};
assert!(check_version_constraint("6.1", &constraint, PackageFormat::Rpm).unwrap());
assert!(!check_version_constraint("7.0", &constraint, PackageFormat::Rpm).unwrap());
assert!(!check_version_constraint("8.0", &constraint, PackageFormat::Rpm).unwrap());
let constraint = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "4.2.5".to_string(),
};
assert!(check_version_constraint("6.1", &constraint, PackageFormat::Rpm).unwrap());
assert!(check_version_constraint("4.2.5", &constraint, PackageFormat::Rpm).unwrap());
assert!(!check_version_constraint("4.2.4", &constraint, PackageFormat::Rpm).unwrap());
let constraint = VersionConstraint {
operator: Operator::VersionLessThanEqual,
operand: "7.0".to_string(),
};
assert!(check_version_constraint("6.1", &constraint, PackageFormat::Rpm).unwrap());
assert!(check_version_constraint("7.0", &constraint, PackageFormat::Rpm).unwrap());
assert!(!check_version_constraint("8.0", &constraint, PackageFormat::Rpm).unwrap());
}
#[test]
fn test_check_single_constraint_version_compatible() {
let constraint = VersionConstraint {
operator: Operator::VersionCompatible,
operand: "3.12".to_string(),
};
assert!(check_version_constraint("3.12.12-r0", &constraint, PackageFormat::Apk).unwrap(),
"3.12.12-r0 should satisfy ~3.12");
assert!(check_version_constraint("3.12.0-r0", &constraint, PackageFormat::Apk).unwrap(),
"3.12.0-r0 should satisfy ~3.12");
assert!(check_version_constraint("3.12", &constraint, PackageFormat::Apk).unwrap(),
"3.12 should satisfy ~3.12");
assert!(check_version_constraint("3.12.15-r1", &constraint, PackageFormat::Apk).unwrap(),
"3.12.15-r1 should satisfy ~3.12");
assert!(!check_version_constraint("3.11.9-r0", &constraint, PackageFormat::Apk).unwrap(),
"3.11.9-r0 should NOT satisfy ~3.12");
assert!(!check_version_constraint("3.10.0-r0", &constraint, PackageFormat::Apk).unwrap(),
"3.10.0-r0 should NOT satisfy ~3.12");
let constraint = VersionConstraint {
operator: Operator::VersionCompatible,
operand: "5.13".to_string(),
};
assert!(check_version_constraint("5.13.0-1.fc42", &constraint, PackageFormat::Rpm).unwrap(),
"5.13.0-1.fc42 should satisfy ~5.13");
assert!(check_version_constraint("5.13.5-2.el8", &constraint, PackageFormat::Rpm).unwrap(),
"5.13.5-2.el8 should satisfy ~5.13");
assert!(check_version_constraint("5.13", &constraint, PackageFormat::Rpm).unwrap(),
"5.13 should satisfy ~5.13");
assert!(!check_version_constraint("5.12.9-1.fc42", &constraint, PackageFormat::Rpm).unwrap(),
"5.12.9-1.fc42 should NOT satisfy ~5.13");
assert!(!check_version_constraint("5.11", &constraint, PackageFormat::Rpm).unwrap(),
"5.11 should NOT satisfy ~5.13");
let constraint = VersionConstraint {
operator: Operator::VersionCompatible,
operand: "2.5".to_string(),
};
assert!(check_version_constraint("2.5.0-1", &constraint, PackageFormat::Deb).unwrap(),
"2.5.0-1 should satisfy ~2.5");
assert!(check_version_constraint("2.5.3-2ubuntu1", &constraint, PackageFormat::Deb).unwrap(),
"2.5.3-2ubuntu1 should satisfy ~2.5");
assert!(check_version_constraint("2.5", &constraint, PackageFormat::Deb).unwrap(),
"2.5 should satisfy ~2.5");
assert!(!check_version_constraint("2.4.9-1", &constraint, PackageFormat::Deb).unwrap(),
"2.4.9-1 should NOT satisfy ~2.5");
assert!(!check_version_constraint("2.3", &constraint, PackageFormat::Deb).unwrap(),
"2.3 should NOT satisfy ~2.5");
let constraint = VersionConstraint {
operator: Operator::VersionCompatible,
operand: "1.2.3".to_string(),
};
assert!(check_version_constraint("1.2.3", &constraint, PackageFormat::Rpm).unwrap(),
"1.2.3 should satisfy ~1.2.3");
assert!(check_version_constraint("1.2.3-1", &constraint, PackageFormat::Rpm).unwrap(),
"1.2.3-1 should satisfy ~1.2.3");
assert!(check_version_constraint("1.2.4", &constraint, PackageFormat::Rpm).unwrap(),
"1.2.4 should satisfy ~1.2.3");
assert!(check_version_constraint("1.2.10", &constraint, PackageFormat::Rpm).unwrap(),
"1.2.10 should satisfy ~1.2.3");
assert!(!check_version_constraint("1.2.2", &constraint, PackageFormat::Rpm).unwrap(),
"1.2.2 should NOT satisfy ~1.2.3");
assert!(!check_version_constraint("1.1.9", &constraint, PackageFormat::Rpm).unwrap(),
"1.1.9 should NOT satisfy ~1.2.3");
}
#[test]
fn test_check_single_constraint_with_tilde_tilde_suffix() {
let constraint = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "0.7.5-~~".to_string(),
};
assert!(check_version_constraint("0.7.5-1+b3", &constraint, PackageFormat::Deb).unwrap(),
"0.7.5-1+b3 should satisfy >= 0.7.5-~~");
assert!(check_version_constraint("0.7.5-1", &constraint, PackageFormat::Deb).unwrap(),
"0.7.5-1 should satisfy >= 0.7.5-~~");
assert!(check_version_constraint("0.7.5", &constraint, PackageFormat::Deb).unwrap(),
"0.7.5 should satisfy >= 0.7.5-~~");
assert!(check_version_constraint("0.7.6", &constraint, PackageFormat::Deb).unwrap(),
"0.7.6 should satisfy >= 0.7.5-~~");
assert!(!check_version_constraint("0.7.4", &constraint, PackageFormat::Deb).unwrap(),
"0.7.4 should NOT satisfy >= 0.7.5-~~");
let constraint = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "0.7.5~~".to_string(),
};
assert!(check_version_constraint("0.7.5-1", &constraint, PackageFormat::Deb).unwrap(),
"0.7.5-1 should satisfy >= 0.7.5~~");
assert!(check_version_constraint("0.7.6", &constraint, PackageFormat::Deb).unwrap(),
"0.7.6 should satisfy >= 0.7.5~~");
assert!(!check_version_constraint("0.7.4", &constraint, PackageFormat::Deb).unwrap(),
"0.7.4 should NOT satisfy >= 0.7.5~~");
let constraint = VersionConstraint {
operator: Operator::VersionGreaterThan,
operand: "0.7.5-~~".to_string(),
};
assert!(check_version_constraint("0.7.6", &constraint, PackageFormat::Deb).unwrap(),
"0.7.6 should satisfy > 0.7.5-~~");
assert!(check_version_constraint("0.7.5-1", &constraint, PackageFormat::Deb).unwrap(),
"0.7.5-1 should satisfy > 0.7.5-~~");
assert!(!check_version_constraint("0.7.5", &constraint, PackageFormat::Deb).unwrap(),
"0.7.5 should NOT satisfy > 0.7.5-~~");
assert!(!check_version_constraint("0.7.4", &constraint, PackageFormat::Deb).unwrap(),
"0.7.4 should NOT satisfy > 0.7.5-~~");
let constraint = VersionConstraint {
operator: Operator::VersionGreaterThan,
operand: "0.6.0-4~~".to_string(),
};
assert!(check_version_constraint("0.6.0-4", &constraint, PackageFormat::Deb).unwrap(),
"0.6.0-4 should satisfy > 0.6.0-4~~ (because > X-Y~~ means >= X-Y for versions with revisions)");
assert!(check_version_constraint("0.6.0-5", &constraint, PackageFormat::Deb).unwrap(),
"0.6.0-5 should satisfy > 0.6.0-4~~");
assert!(check_version_constraint("0.6.1", &constraint, PackageFormat::Deb).unwrap(),
"0.6.1 should satisfy > 0.6.0-4~~");
assert!(!check_version_constraint("0.6.0-3", &constraint, PackageFormat::Deb).unwrap(),
"0.6.0-3 should NOT satisfy > 0.6.0-4~~");
let constraint = VersionConstraint {
operator: Operator::VersionLessThan,
operand: "0.7.5-~~".to_string(),
};
assert!(check_version_constraint("0.7.4", &constraint, PackageFormat::Deb).unwrap(),
"0.7.4 should satisfy < 0.7.5-~~");
assert!(!check_version_constraint("0.7.5", &constraint, PackageFormat::Deb).unwrap(),
"0.7.5 should NOT satisfy < 0.7.5-~~");
assert!(!check_version_constraint("0.7.6", &constraint, PackageFormat::Deb).unwrap(),
"0.7.6 should NOT satisfy < 0.7.5-~~");
let constraint = VersionConstraint {
operator: Operator::VersionLessThanEqual,
operand: "0.7.5-~~".to_string(),
};
assert!(check_version_constraint("0.7.4", &constraint, PackageFormat::Deb).unwrap(),
"0.7.4 should satisfy <= 0.7.5-~~");
assert!(check_version_constraint("0.7.5", &constraint, PackageFormat::Deb).unwrap(),
"0.7.5 should satisfy <= 0.7.5-~~");
assert!(!check_version_constraint("0.7.6", &constraint, PackageFormat::Deb).unwrap(),
"0.7.6 should NOT satisfy <= 0.7.5-~~");
let constraint = VersionConstraint {
operator: Operator::VersionEqual,
operand: "0.7.5-~~".to_string(),
};
assert!(check_version_constraint("0.7.5", &constraint, PackageFormat::Deb).unwrap(),
"0.7.5 should satisfy = 0.7.5-~~");
assert!(!check_version_constraint("0.7.6", &constraint, PackageFormat::Deb).unwrap(),
"0.7.6 should NOT satisfy = 0.7.5-~~");
let constraint = VersionConstraint {
operator: Operator::VersionEqual,
operand: "6.14.0-1017.18~24.04.1".to_string(),
};
assert!(check_version_constraint("6.14.0-1017.18~24.04.1+2", &constraint, PackageFormat::Deb).unwrap(),
"6.14.0-1017.18~24.04.1+2 should satisfy = 6.14.0-1017.18~24.04.1");
assert!(check_version_constraint("6.14.0-1017.18~24.04.1", &constraint, PackageFormat::Deb).unwrap(),
"6.14.0-1017.18~24.04.1 should satisfy = 6.14.0-1017.18~24.04.1");
assert!(!check_version_constraint("6.14.0-1017.18~24.04.2", &constraint, PackageFormat::Deb).unwrap(),
"6.14.0-1017.18~24.04.2 should NOT satisfy = 6.14.0-1017.18~24.04.1");
let constraint = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "5.15.13+dfsg~".to_string(),
};
assert!(check_version_constraint("5.15.13+dfsg-1ubuntu1", &constraint, PackageFormat::Deb).unwrap(),
"5.15.13+dfsg-1ubuntu1 should satisfy >= 5.15.13+dfsg~");
assert!(check_version_constraint("5.15.13+dfsg", &constraint, PackageFormat::Deb).unwrap(),
"5.15.13+dfsg should satisfy >= 5.15.13+dfsg~");
assert!(check_version_constraint("5.15.14+dfsg", &constraint, PackageFormat::Deb).unwrap(),
"5.15.14+dfsg should satisfy >= 5.15.13+dfsg~");
assert!(!check_version_constraint("5.15.12+dfsg", &constraint, PackageFormat::Deb).unwrap(),
"5.15.12+dfsg should NOT satisfy >= 5.15.13+dfsg~");
let constraint = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "0.12.0~".to_string(),
};
assert!(check_version_constraint("0.12.0~rc2-2build3", &constraint, PackageFormat::Deb).unwrap(),
"0.12.0~rc2-2build3 should satisfy >= 0.12.0~");
assert!(check_version_constraint("0.12.0~rc1-1", &constraint, PackageFormat::Deb).unwrap(),
"0.12.0~rc1-1 should satisfy >= 0.12.0~");
assert!(check_version_constraint("0.12.0-1", &constraint, PackageFormat::Deb).unwrap(),
"0.12.0-1 should satisfy >= 0.12.0~ (final version > pre-release)");
assert!(!check_version_constraint("0.11.9-1", &constraint, PackageFormat::Deb).unwrap(),
"0.11.9-1 should NOT satisfy >= 0.12.0~");
let constraint = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "0.0~git20210726.e7812ac~".to_string(),
};
assert!(check_version_constraint("0.0~git20210726.e7812ac-4", &constraint, PackageFormat::Deb).unwrap(),
"0.0~git20210726.e7812ac-4 should satisfy >= 0.0~git20210726.e7812ac~");
assert!(check_version_constraint("0.0~git20210726.e7812ac-1", &constraint, PackageFormat::Deb).unwrap(),
"0.0~git20210726.e7812ac-1 should satisfy >= 0.0~git20210726.e7812ac~");
assert!(check_version_constraint("0.0~git20210726.e7812ac", &constraint, PackageFormat::Deb).unwrap(),
"0.0~git20210726.e7812ac should satisfy >= 0.0~git20210726.e7812ac~");
let constraint = VersionConstraint {
operator: Operator::VersionNotEqual,
operand: "0.7.5-~~".to_string(),
};
assert!(!check_version_constraint("0.7.5", &constraint, PackageFormat::Deb).unwrap(),
"0.7.5 should NOT satisfy != 0.7.5-~~");
assert!(check_version_constraint("0.7.6", &constraint, PackageFormat::Deb).unwrap(),
"0.7.6 should satisfy != 0.7.5-~~");
let constraint = VersionConstraint {
operator: Operator::VersionLessThan,
operand: "2~~".to_string(),
};
assert!(check_version_constraint("1.20", &constraint, PackageFormat::Rpm).unwrap(),
"1.20 should satisfy < 2~~ (which means < 3)");
assert!(check_version_constraint("1.99", &constraint, PackageFormat::Rpm).unwrap(),
"1.99 should satisfy < 2~~ (which means < 3)");
assert!(check_version_constraint("2.2.4", &constraint, PackageFormat::Rpm).unwrap(),
"2.2.4 should satisfy < 2~~ (which means < 3)");
assert!(check_version_constraint("2.2.6", &constraint, PackageFormat::Rpm).unwrap(),
"2.2.6 should satisfy < 2~~ (which means < 3)");
assert!(!check_version_constraint("2", &constraint, PackageFormat::Rpm).unwrap(),
"2 should NOT satisfy < 2~~ (base version is excluded)");
assert!(!check_version_constraint("3", &constraint, PackageFormat::Rpm).unwrap(),
"3 should NOT satisfy < 2~~ (which means < 3)");
assert!(!check_version_constraint("3.0", &constraint, PackageFormat::Rpm).unwrap(),
"3.0 should NOT satisfy < 2~~ (which means < 3)");
let constraint = VersionConstraint {
operator: Operator::VersionLessThan,
operand: "1.20~~".to_string(),
};
assert!(check_version_constraint("1.19", &constraint, PackageFormat::Rpm).unwrap(),
"1.19 should satisfy < 1.20~~ (which means < 1.21)");
assert!(!check_version_constraint("1.20", &constraint, PackageFormat::Rpm).unwrap(),
"1.20 should NOT satisfy < 1.20~~ (base version is excluded)");
assert!(check_version_constraint("1.20.5", &constraint, PackageFormat::Rpm).unwrap(),
"1.20.5 should satisfy < 1.20~~ (which means < 1.21)");
assert!(!check_version_constraint("1.21", &constraint, PackageFormat::Rpm).unwrap(),
"1.21 should NOT satisfy < 1.20~~ (which means < 1.21)");
assert!(!check_version_constraint("1.22", &constraint, PackageFormat::Rpm).unwrap(),
"1.22 should NOT satisfy < 1.20~~ (which means < 1.21)");
let constraint = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "1.2.3-~~".to_string(),
};
assert!(check_version_constraint("1.2.3-1.el8", &constraint, PackageFormat::Rpm).unwrap(),
"1.2.3-1.el8 should satisfy >= 1.2.3-~~");
assert!(check_version_constraint("1.2.4", &constraint, PackageFormat::Rpm).unwrap(),
"1.2.4 should satisfy >= 1.2.3-~~");
assert!(!check_version_constraint("1.2.2", &constraint, PackageFormat::Rpm).unwrap(),
"1.2.2 should NOT satisfy >= 1.2.3-~~");
let constraint = VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "1.2.3-beta-~~".to_string(),
};
assert!(check_version_constraint("1.2.3-beta-1", &constraint, PackageFormat::Deb).unwrap(),
"1.2.3-beta-1 should satisfy >= 1.2.3-beta-~~");
assert!(check_version_constraint("1.2.3-beta", &constraint, PackageFormat::Deb).unwrap(),
"1.2.3-beta should satisfy >= 1.2.3-beta-~~");
}
#[test]
fn test_check_version_satisfies_constraints() {
let constraints = vec![
VersionConstraint {
operator: Operator::VersionCompatible,
operand: "5.82".to_string(),
},
];
assert!(check_version_satisfies_constraints("5.82.0-r0", &constraints, PackageFormat::Apk).unwrap(),
"5.82.0-r0 should satisfy ~5.82");
assert!(check_version_satisfies_constraints("5.82.1-r0", &constraints, PackageFormat::Apk).unwrap(),
"5.82.1-r0 should satisfy ~5.82");
assert!(!check_version_satisfies_constraints("5.81-r0", &constraints, PackageFormat::Apk).unwrap(),
"5.81-r0 should NOT satisfy ~5.82");
let constraints_and = vec![
VersionConstraint {
operator: Operator::VersionGreaterThanEqual,
operand: "5.80".to_string(),
},
VersionConstraint {
operator: Operator::VersionLessThan,
operand: "6.0".to_string(),
},
];
assert!(check_version_satisfies_constraints("5.82-r0", &constraints_and, PackageFormat::Apk).unwrap(),
"5.82-r0 should satisfy >=5.80,<6.0");
assert!(!check_version_satisfies_constraints("5.79-r0", &constraints_and, PackageFormat::Apk).unwrap(),
"5.79-r0 should NOT satisfy >=5.80,<6.0");
assert!(!check_version_satisfies_constraints("6.0-r0", &constraints_and, PackageFormat::Apk).unwrap(),
"6.0-r0 should NOT satisfy >=5.80,<6.0 (upstream-only comparison: 6.0 < 6.0 is false)");
assert!(!check_version_satisfies_constraints("6.0", &constraints_and, PackageFormat::Apk).unwrap(),
"6.0 should NOT satisfy >=5.80,<6.0");
let constraints_or = vec![
VersionConstraint {
operator: Operator::VersionLessThan,
operand: "5.80".to_string(),
},
VersionConstraint {
operator: Operator::VersionGreaterThan,
operand: "5.80".to_string(),
},
];
assert!(check_version_satisfies_constraints("5.82-r0", &constraints_or, PackageFormat::Apk).unwrap(),
"5.82-r0 should satisfy <5.80 OR >5.80");
assert!(check_version_satisfies_constraints("5.79-r0", &constraints_or, PackageFormat::Apk).unwrap(),
"5.79-r0 should satisfy <5.80 OR >5.80");
assert!(!check_version_satisfies_constraints("5.80-r0", &constraints_or, PackageFormat::Apk).unwrap(),
"5.80-r0 should NOT satisfy <5.80 OR >5.80 (upstream-only comparison: 5.80 == 5.80)");
assert!(!check_version_satisfies_constraints("5.80", &constraints_or, PackageFormat::Apk).unwrap(),
"5.80 should NOT satisfy <5.80 OR >5.80 (exactly equal)");
let constraints_mixed = vec![
VersionConstraint {
operator: Operator::VersionLessThan,
operand: "5.80".to_string(),
},
VersionConstraint {
operator: Operator::VersionGreaterThan,
operand: "5.80".to_string(),
},
VersionConstraint {
operator: Operator::VersionLessThan,
operand: "6.0".to_string(),
},
];
assert!(check_version_satisfies_constraints("5.82-r0", &constraints_mixed, PackageFormat::Apk).unwrap(),
"5.82-r0 should satisfy (<5.80 OR >5.80) AND <6.0");
assert!(!check_version_satisfies_constraints("6.1-r0", &constraints_mixed, PackageFormat::Apk).unwrap(),
"6.1-r0 should NOT satisfy (<5.80 OR >5.80) AND <6.0");
}
}