Skip to content

Commit 703b520

Browse files
committed
Auto merge of #13977 - linyihai:doc-resolver-tests, r=Eh2406
doc: Add README for resolver-tests ### What does this PR try to resolve? This collect the comments from `@Eh2406` about the resolver-tests and add a README to it. ### How should we test and review this PR? ### Additional information Maybe Fixed #13319 See #11938 (comment)
2 parents 9ba3894 + 861429b commit 703b520

File tree

2 files changed

+25
-1
lines changed

2 files changed

+25
-1
lines changed

crates/resolver-tests/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# resolver-tests
2+
3+
## The aim
4+
5+
This crate aims to test the resolution of Cargo's resolver. It implements a [SAT solver](https://en.wikipedia.org/wiki/SAT_solver) to compare with resolution of Cargo's resolver.
6+
This ensures that Cargo's dependency resolution is proven valid by lowering to [SAT problem](https://en.wikipedia.org/wiki/Boolean_satisfiability_problem).
7+
8+
## About the test
9+
10+
The Cargo's resolver is very sensitive to what order it tries to evaluate constraints. This makes it incredibly difficult
11+
to be sure that a handful of tests actually covers all the important permutations of decision-making. The tests not only needs
12+
to hit all the corner cases, it needs to try all of the orders of evaluation. So we use fuzz testing to cover more permutations.
13+

crates/resolver-tests/src/lib.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub fn resolve(deps: Vec<Dependency>, registry: &[Summary]) -> CargoResult<Vec<P
3131
resolve_with_global_context(deps, registry, &GlobalContext::default().unwrap())
3232
}
3333

34+
// Verify that the resolution of cargo resolver can pass the verification of SAT
3435
pub fn resolve_and_validated(
3536
deps: Vec<Dependency>,
3637
registry: &[Summary],
@@ -198,6 +199,9 @@ fn log_bits(x: usize) -> usize {
198199
(num_bits::<usize>() as u32 - x.leading_zeros()) as usize
199200
}
200201

202+
// At this point is possible to select every version of every package.
203+
// So we need to mark certain versions as incompatible with each other.
204+
// We could add a clause not A, not B for all A and B that are incompatible,
201205
fn sat_at_most_one(solver: &mut impl varisat::ExtendFormula, vars: &[varisat::Var]) {
202206
if vars.len() <= 1 {
203207
return;
@@ -210,6 +214,8 @@ fn sat_at_most_one(solver: &mut impl varisat::ExtendFormula, vars: &[varisat::Va
210214
solver.add_clause(&[vars[1].negative(), vars[2].negative()]);
211215
return;
212216
}
217+
// There are more efficient ways to do it for large numbers of versions.
218+
//
213219
// use the "Binary Encoding" from
214220
// https://www.it.uu.se/research/group/astra/ModRef10/papers/Alan%20M.%20Frisch%20and%20Paul%20A.%20Giannoros.%20SAT%20Encodings%20of%20the%20At-Most-k%20Constraint%20-%20ModRef%202010.pdf
215221
let bits: Vec<varisat::Var> = solver.new_var_iter(log_bits(vars.len())).collect();
@@ -257,6 +263,7 @@ struct SatResolveInner {
257263
impl SatResolve {
258264
pub fn new(registry: &[Summary]) -> Self {
259265
let mut cnf = varisat::CnfFormula::new();
266+
// That represents each package version which is set to "true" if the packages in the lock file and "false" if it is unused.
260267
let var_for_is_packages_used: HashMap<PackageId, varisat::Var> = registry
261268
.iter()
262269
.map(|s| (s.package_id(), cnf.new_var()))
@@ -290,6 +297,10 @@ impl SatResolve {
290297

291298
let empty_vec = vec![];
292299

300+
// Now different versions can conflict, but dependencies might not be selected.
301+
// So we need to add clauses that ensure that if a package is selected for each dependency a version
302+
// that satisfies that dependency is selected.
303+
//
293304
// active packages need each of there `deps` to be satisfied
294305
for p in registry.iter() {
295306
for dep in p.dependencies() {
@@ -681,7 +692,7 @@ pub fn registry_strategy(
681692
vers
682693
});
683694

684-
// each version of each crate can depend on each crate smaller then it.
695+
// each version of each crate can depend on each crate smaller than it.
685696
// In theory shrinkage should be 2, but in practice we get better trees with a larger value.
686697
let max_deps = max_versions * (max_crates * (max_crates - 1)) / shrinkage;
687698

0 commit comments

Comments
 (0)