Skip to content

Commit 2d61669

Browse files
committed
test: fix dep feature behavior in the sat resolver
Behavior of the feature: * if dependency `dep_name` is not optional, its feature `"bar"` is activated. * if dependency `dep_name` is optional: - if this is a weak dependency feature: - feature `"bar"` of dependency `dep_name` is activated if `dep_name` has been activated. - if this is not a weak dependency feature: - feature `dep_name` is activated if it exists. - dependency `dep_name` is activated. - feature `"bar"` of dependency `dep_name` is activated.
1 parent 5a4b4cc commit 2d61669

File tree

2 files changed

+173
-19
lines changed

2 files changed

+173
-19
lines changed

crates/resolver-tests/src/sat.rs

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ fn process_pkg_features(
9898
var_for_is_packages_features_used: &HashMap<PackageId, HashMap<InternedString, varisat::Var>>,
9999
pkg_feature_var_map: &HashMap<InternedString, varisat::Var>,
100100
pkg_features: &FeatureMap,
101+
optional_dependencies: &HashSet<(DepKind, InternedString)>,
101102
compatible_dep_summaries_map: &HashMap<InternedString, HashMap<DepKind, Vec<Summary>>>,
102103
) {
103104
// add clauses for package features
@@ -133,39 +134,76 @@ fn process_pkg_features(
133134
dep_feature: dep_feature_name,
134135
weak,
135136
} => {
137+
// Behavior of the feature:
138+
// * if dependency `dep_name` is not optional, its feature `"bar"` is activated.
139+
// * if dependency `dep_name` is optional:
140+
// - if this is a weak dependency feature:
141+
// - feature `"bar"` of dependency `dep_name` is activated if `dep_name` has been activated.
142+
// - if this is not a weak dependency feature:
143+
// - feature `dep_name` is activated if it exists.
144+
// - dependency `dep_name` is activated.
145+
// - feature `"bar"` of dependency `dep_name` is activated.
146+
136147
// add clauses for each dependency with the provided name (normal/build/dev)
137-
for compatible_dep_summaries in compatible_dep_summaries_map
148+
for (&dep_kind, compatible_dep_summaries) in compatible_dep_summaries_map
138149
.get(&dep_name)
139150
.into_iter()
140-
.flat_map(|map| map.values())
151+
.flatten()
141152
{
142-
let mut dep_clause = Vec::new();
143-
144-
for dep in compatible_dep_summaries {
145-
let dep_feature_var_map =
146-
&var_for_is_packages_features_used[&dep.package_id()];
147-
148-
let Some(dep_feature_var) = dep_feature_var_map.get(&dep_feature_name)
149-
else {
150-
continue;
151-
};
153+
let compatible_vars = compatible_dep_summaries
154+
.iter()
155+
.filter_map(|dep| {
156+
let dep_feature_var_map =
157+
&var_for_is_packages_features_used[&dep.package_id()];
158+
159+
match dep_feature_var_map.get(&dep_feature_name) {
160+
None => None,
161+
Some(&dep_feature_var) => {
162+
let dep_var = var_for_is_packages_used[&dep.package_id()];
163+
Some((dep_var, dep_feature_var))
164+
}
165+
}
166+
})
167+
.collect::<Vec<_>>();
152168

153-
let dep_var = var_for_is_packages_used[&dep.package_id()];
169+
if compatible_vars.is_empty() {
170+
if !weak {
171+
solver.add_clause(&[pkg_feature_var.negative()]);
172+
} else {
173+
for dep in compatible_dep_summaries {
174+
let dep_var = var_for_is_packages_used[&dep.package_id()];
175+
solver.add_clause(&[
176+
pkg_feature_var.negative(),
177+
dep_var.negative(),
178+
]);
179+
}
180+
}
181+
continue;
182+
}
154183

184+
for &(dep_var, dep_feature_var) in &compatible_vars {
155185
solver.add_clause(&[
156186
pkg_feature_var.negative(),
157187
dep_var.negative(),
158188
dep_feature_var.positive(),
159189
]);
160-
161-
if !weak {
162-
dep_clause.push(dep_var.positive());
163-
}
164190
}
165191

166-
if !weak {
167-
dep_clause.push(pkg_feature_var.negative());
192+
if !weak && optional_dependencies.contains(&(dep_kind, dep_name)) {
193+
let dep_clause = compatible_vars
194+
.iter()
195+
.map(|(dep_var, _)| dep_var.positive())
196+
.chain([pkg_feature_var.negative()])
197+
.collect::<Vec<_>>();
198+
168199
solver.add_clause(&dep_clause);
200+
201+
if let Some(other_feature_var) = pkg_feature_var_map.get(&dep_name) {
202+
solver.add_clause(&[
203+
pkg_feature_var.negative(),
204+
other_feature_var.positive(),
205+
]);
206+
}
169207
}
170208
}
171209
}
@@ -298,6 +336,12 @@ impl SatResolver {
298336
let pkg_dependencies = pkg.dependencies();
299337
let pkg_features = pkg.features();
300338

339+
let optional_dependencies = pkg_dependencies
340+
.iter()
341+
.filter(|dep| dep.is_optional())
342+
.map(|dep| (dep.kind(), dep.name_in_toml()))
343+
.collect();
344+
301345
let compatible_dep_summaries_map =
302346
find_all_compatible_dep_summaries(pkg_dependencies, &by_name, false);
303347

@@ -307,6 +351,7 @@ impl SatResolver {
307351
&var_for_is_packages_features_used,
308352
&var_for_is_packages_features_used[&pkg_id],
309353
pkg_features,
354+
&optional_dependencies,
310355
&compatible_dep_summaries_map,
311356
);
312357

crates/resolver-tests/tests/validated.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,47 @@ fn missing_dep_feature() {
177177
assert!(resolve_and_validated(deps, &reg, &mut sat_resolver).is_err());
178178
}
179179

180+
#[test]
181+
fn missing_weak_dep_feature() {
182+
let reg = registry(vec![
183+
pkg("a"),
184+
pkg_dep_with("dep1", vec![dep("a")], &[("f", &["a/a"])]),
185+
pkg_dep_with("dep2", vec!["a".opt()], &[("f", &["a/a"])]),
186+
pkg_dep_with("dep3", vec!["a".opt()], &[("f", &["a?/a"])]),
187+
pkg_dep_with("dep4", vec!["x".opt()], &[("f", &["x?/a"])]),
188+
]);
189+
190+
let deps = vec![dep("dep1").with(&["f"])];
191+
let mut sat_resolver = SatResolver::new(&reg);
192+
assert!(resolve_and_validated(deps, &reg, &mut sat_resolver).is_err());
193+
194+
let deps = vec![dep("dep2").with(&["f"])];
195+
let mut sat_resolver = SatResolver::new(&reg);
196+
assert!(resolve_and_validated(deps, &reg, &mut sat_resolver).is_err());
197+
198+
let deps = vec![dep("dep2").with(&["a", "f"])];
199+
let mut sat_resolver = SatResolver::new(&reg);
200+
assert!(resolve_and_validated(deps, &reg, &mut sat_resolver).is_err());
201+
202+
// Weak dependencies are not supported yet in the dependency resolver
203+
let deps = vec![dep("dep3").with(&["f"])];
204+
assert!(resolve(deps.clone(), &reg).is_err());
205+
assert!(SatResolver::new(&reg).sat_resolve(&deps));
206+
207+
let deps = vec![dep("dep3").with(&["a", "f"])];
208+
let mut sat_resolver = SatResolver::new(&reg);
209+
assert!(resolve_and_validated(deps, &reg, &mut sat_resolver).is_err());
210+
211+
// Weak dependencies are not supported yet in the dependency resolver
212+
let deps = vec![dep("dep4").with(&["f"])];
213+
assert!(resolve(deps.clone(), &reg).is_err());
214+
assert!(SatResolver::new(&reg).sat_resolve(&deps));
215+
216+
let deps = vec![dep("dep4").with(&["x", "f"])];
217+
let mut sat_resolver = SatResolver::new(&reg);
218+
assert!(resolve_and_validated(deps, &reg, &mut sat_resolver).is_err());
219+
}
220+
180221
#[test]
181222
fn conflict_feature_and_sys() {
182223
let reg = registry(vec![
@@ -299,3 +340,71 @@ fn multiple_dep_kinds_with_different_packages() {
299340
let mut sat_resolver = SatResolver::new(&reg);
300341
assert!(resolve_and_validated(deps, &reg, &mut sat_resolver).is_err());
301342
}
343+
344+
#[test]
345+
fn dep_feature_with_shadowing_feature() {
346+
let reg = registry(vec![
347+
pkg_dep_with("a", vec![], &[("b", &[])]),
348+
pkg_dep_with(
349+
"dep",
350+
vec!["a".opt().rename("aa"), "c".opt()],
351+
&[("default", &["aa/b"]), ("aa", &["c"])],
352+
),
353+
]);
354+
355+
let deps = vec!["dep".with(&["default"])];
356+
let mut sat_resolver = SatResolver::new(&reg);
357+
assert!(resolve_and_validated(deps, &reg, &mut sat_resolver).is_err());
358+
}
359+
360+
#[test]
361+
fn dep_feature_not_optional_with_shadowing_feature() {
362+
let reg = registry(vec![
363+
pkg_dep_with("a", vec![], &[("b", &[])]),
364+
pkg_dep_with(
365+
"dep",
366+
vec!["a".rename("aa"), "c".opt()],
367+
&[("default", &["aa/b"]), ("aa", &["c"])],
368+
),
369+
]);
370+
371+
let deps = vec!["dep".with(&["default"])];
372+
let mut sat_resolver = SatResolver::new(&reg);
373+
assert!(resolve_and_validated(deps, &reg, &mut sat_resolver).is_ok());
374+
}
375+
376+
#[test]
377+
fn dep_feature_weak_with_shadowing_feature() {
378+
let reg = registry(vec![
379+
pkg_dep_with("a", vec![], &[("b", &[])]),
380+
pkg_dep_with(
381+
"dep",
382+
vec!["a".opt().rename("aa"), "c".opt()],
383+
&[("default", &["aa?/b"]), ("aa", &["c"])],
384+
),
385+
]);
386+
387+
let deps = vec!["dep".with(&["default"])];
388+
let mut sat_resolver = SatResolver::new(&reg);
389+
assert!(resolve_and_validated(deps, &reg, &mut sat_resolver).is_ok());
390+
}
391+
392+
#[test]
393+
fn dep_feature_duplicate_with_shadowing_feature() {
394+
let reg = registry(vec![
395+
pkg_dep_with("a", vec![], &[("b", &[])]),
396+
pkg_dep_with(
397+
"dep",
398+
vec![
399+
"a".opt().rename("aa"),
400+
dep_kind("a", DepKind::Build).rename("aa"),
401+
"c".opt(),
402+
],
403+
&[("default", &["aa/b"]), ("aa", &["c"])],
404+
),
405+
]);
406+
407+
let deps = vec!["dep".with(&["default"])];
408+
let mut sat_resolver = SatResolver::new(&reg);
409+
assert!(resolve_and_validated(deps, &reg, &mut sat_resolver).is_err());
410+
}

0 commit comments

Comments
 (0)