Skip to content

Commit aece1f4

Browse files
committed
test: refactor resolver test functions
1 parent d7bffc3 commit aece1f4

File tree

3 files changed

+126
-111
lines changed

3 files changed

+126
-111
lines changed

crates/resolver-tests/src/lib.rs

Lines changed: 90 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
#![allow(clippy::print_stderr)]
22

3-
use std::cell::RefCell;
43
use std::cmp::{max, min};
54
use std::collections::{BTreeMap, HashMap, HashSet};
65
use std::fmt;
76
use std::fmt::Write;
8-
use std::rc::Rc;
97
use std::sync::OnceLock;
108
use std::task::Poll;
119
use std::time::Instant;
@@ -19,7 +17,6 @@ use cargo::core::{GitReference, SourceId};
1917
use cargo::sources::source::QueryKind;
2018
use cargo::sources::IndexSummary;
2119
use cargo::util::{CargoResult, GlobalContext, IntoUrl};
22-
use cargo_util_schemas::manifest::RustVersion;
2320

2421
use proptest::collection::{btree_map, vec};
2522
use proptest::prelude::*;
@@ -35,18 +32,17 @@ pub fn resolve(deps: Vec<Dependency>, registry: &[Summary]) -> CargoResult<Vec<P
3532
pub fn resolve_and_validated(
3633
deps: Vec<Dependency>,
3734
registry: &[Summary],
38-
sat_resolve: Option<SatResolve>,
35+
sat_resolver: &mut SatResolver,
3936
) -> CargoResult<Vec<PackageId>> {
4037
let resolve =
4138
resolve_with_global_context_raw(deps.clone(), registry, &GlobalContext::default().unwrap());
4239

4340
match resolve {
4441
Err(e) => {
45-
let sat_resolve = sat_resolve.unwrap_or_else(|| SatResolve::new(registry));
46-
if sat_resolve.sat_resolve(&deps) {
42+
if sat_resolver.sat_resolve(&deps) {
4743
panic!(
48-
"the resolve err but the sat_resolve thinks this will work:\n{}",
49-
sat_resolve.use_packages().unwrap()
44+
"`resolve()` returned an error but the sat resolver thinks this will work:\n{}",
45+
sat_resolver.used_packages().unwrap()
5046
);
5147
}
5248
Err(e)
@@ -73,11 +69,10 @@ pub fn resolve_and_validated(
7369
let out = resolve.sort();
7470
assert_eq!(out.len(), used.len());
7571

76-
let sat_resolve = sat_resolve.unwrap_or_else(|| SatResolve::new(registry));
77-
if !sat_resolve.sat_is_valid_solution(&out) {
72+
if !sat_resolver.sat_is_valid_solution(&out) {
7873
panic!(
79-
"the sat_resolve err but the resolve thinks this will work:\n{:?}",
80-
resolve
74+
"`resolve()` thinks this will work, but the solution is \
75+
invalid according to the sat resolver:\n{resolve:?}",
8176
);
8277
}
8378
Ok(out)
@@ -158,31 +153,35 @@ pub fn resolve_with_global_context_raw(
158153
list: registry,
159154
used: HashSet::new(),
160155
};
161-
let summary = Summary::new(
156+
157+
let root_summary = Summary::new(
162158
pkg_id("root"),
163159
deps,
164160
&BTreeMap::new(),
165161
None::<&String>,
166-
None::<RustVersion>,
162+
None,
167163
)
168164
.unwrap();
165+
169166
let opts = ResolveOpts::everything();
167+
170168
let start = Instant::now();
171169
let mut version_prefs = VersionPreferences::default();
172170
if gctx.cli_unstable().minimal_versions {
173171
version_prefs.version_ordering(VersionOrdering::MinimumVersionsFirst)
174172
}
173+
175174
let resolve = resolver::resolve(
176-
&[(summary, opts)],
175+
&[(root_summary, opts)],
177176
&[],
178177
&mut registry,
179178
&version_prefs,
180179
ResolveVersion::with_rust_version(None),
181180
Some(gctx),
182181
);
183182

184-
// The largest test in our suite takes less then 30 sec.
185-
// So lets fail the test if we have ben running for two long.
183+
// The largest test in our suite takes less then 30 secs.
184+
// So let's fail the test if we have been running for more than 60 secs.
186185
assert!(start.elapsed().as_secs() < 60);
187186
resolve
188187
}
@@ -249,18 +248,15 @@ fn sat_at_most_one_by_key<K: std::hash::Hash + Eq>(
249248
/// find a valid resolution if one exists. The big thing that the real resolver does,
250249
/// that this one does not do is work with features and optional dependencies.
251250
///
252-
/// The SAT library dose not optimize for the newer version,
251+
/// The SAT library does not optimize for the newer version,
253252
/// so the selected packages may not match the real resolver.
254-
#[derive(Clone)]
255-
pub struct SatResolve(Rc<RefCell<SatResolveInner>>);
256-
257-
struct SatResolveInner {
253+
pub struct SatResolver {
258254
solver: varisat::Solver<'static>,
259255
var_for_is_packages_used: HashMap<PackageId, varisat::Var>,
260256
by_name: HashMap<&'static str, Vec<PackageId>>,
261257
}
262258

263-
impl SatResolve {
259+
impl SatResolver {
264260
pub fn new(registry: &[Summary]) -> Self {
265261
let mut cnf = varisat::CnfFormula::new();
266262
// That represents each package version which is set to "true" if the packages in the lock file and "false" if it is unused.
@@ -325,79 +321,81 @@ impl SatResolve {
325321
solver
326322
.solve()
327323
.expect("docs say it can't error in default config");
328-
SatResolve(Rc::new(RefCell::new(SatResolveInner {
324+
325+
SatResolver {
329326
solver,
330327
var_for_is_packages_used,
331328
by_name,
332-
})))
329+
}
333330
}
334-
pub fn sat_resolve(&self, deps: &[Dependency]) -> bool {
335-
let mut s = self.0.borrow_mut();
331+
332+
pub fn sat_resolve(&mut self, root_dependencies: &[Dependency]) -> bool {
336333
let mut assumption = vec![];
337334
let mut this_call = None;
338335

339336
// the starting `deps` need to be satisfied
340-
for dep in deps.iter() {
337+
for dep in root_dependencies {
341338
let empty_vec = vec![];
342-
let matches: Vec<varisat::Lit> = s
339+
let matches: Vec<varisat::Lit> = self
343340
.by_name
344341
.get(dep.package_name().as_str())
345342
.unwrap_or(&empty_vec)
346343
.iter()
347344
.filter(|&p| dep.matches_id(*p))
348-
.map(|p| s.var_for_is_packages_used[p].positive())
345+
.map(|p| self.var_for_is_packages_used[p].positive())
349346
.collect();
350347
if matches.is_empty() {
351348
return false;
352349
} else if matches.len() == 1 {
353350
assumption.extend_from_slice(&matches)
354351
} else {
355352
if this_call.is_none() {
356-
let new_var = s.solver.new_var();
353+
let new_var = self.solver.new_var();
357354
this_call = Some(new_var);
358355
assumption.push(new_var.positive());
359356
}
360357
let mut matches = matches;
361358
matches.push(this_call.unwrap().negative());
362-
s.solver.add_clause(&matches);
359+
self.solver.add_clause(&matches);
363360
}
364361
}
365362

366-
s.solver.assume(&assumption);
363+
self.solver.assume(&assumption);
367364

368-
s.solver
365+
self.solver
369366
.solve()
370367
.expect("docs say it can't error in default config")
371368
}
372-
pub fn sat_is_valid_solution(&self, pids: &[PackageId]) -> bool {
373-
let mut s = self.0.borrow_mut();
369+
370+
pub fn sat_is_valid_solution(&mut self, pids: &[PackageId]) -> bool {
374371
for p in pids {
375-
if p.name().as_str() != "root" && !s.var_for_is_packages_used.contains_key(p) {
372+
if p.name().as_str() != "root" && !self.var_for_is_packages_used.contains_key(p) {
376373
return false;
377374
}
378375
}
379-
let assumption: Vec<_> = s
376+
let assumption: Vec<_> = self
380377
.var_for_is_packages_used
381378
.iter()
382379
.map(|(p, v)| v.lit(pids.contains(p)))
383380
.collect();
384381

385-
s.solver.assume(&assumption);
382+
self.solver.assume(&assumption);
386383

387-
s.solver
384+
self.solver
388385
.solve()
389386
.expect("docs say it can't error in default config")
390387
}
391-
fn use_packages(&self) -> Option<String> {
392-
self.0.borrow().solver.model().map(|lits| {
388+
389+
fn used_packages(&self) -> Option<String> {
390+
self.solver.model().map(|lits| {
393391
let lits: HashSet<_> = lits
394392
.iter()
395393
.filter(|l| l.is_positive())
396394
.map(|l| l.var())
397395
.collect();
398396
let mut out = String::new();
399397
out.push_str("used:\n");
400-
for (p, v) in self.0.borrow().var_for_is_packages_used.iter() {
398+
for (p, v) in self.var_for_is_packages_used.iter() {
401399
if lits.contains(v) {
402400
writeln!(&mut out, " {}", p).unwrap();
403401
}
@@ -409,18 +407,40 @@ impl SatResolve {
409407

410408
pub trait ToDep {
411409
fn to_dep(self) -> Dependency;
410+
fn to_opt_dep(self) -> Dependency;
411+
fn to_dep_with(self, features: &[&'static str]) -> Dependency;
412412
}
413413

414414
impl ToDep for &'static str {
415415
fn to_dep(self) -> Dependency {
416416
Dependency::parse(self, Some("1.0.0"), registry_loc()).unwrap()
417417
}
418+
fn to_opt_dep(self) -> Dependency {
419+
let mut dep = self.to_dep();
420+
dep.set_optional(true);
421+
dep
422+
}
423+
fn to_dep_with(self, features: &[&'static str]) -> Dependency {
424+
let mut dep = self.to_dep();
425+
dep.set_default_features(false);
426+
dep.set_features(features.into_iter().copied());
427+
dep
428+
}
418429
}
419430

420431
impl ToDep for Dependency {
421432
fn to_dep(self) -> Dependency {
422433
self
423434
}
435+
fn to_opt_dep(mut self) -> Dependency {
436+
self.set_optional(true);
437+
self
438+
}
439+
fn to_dep_with(mut self, features: &[&'static str]) -> Dependency {
440+
self.set_default_features(false);
441+
self.set_features(features.into_iter().copied());
442+
self
443+
}
424444
}
425445

426446
pub trait ToPkgId {
@@ -448,8 +468,8 @@ impl<T: AsRef<str>, U: AsRef<str>> ToPkgId for (T, U) {
448468

449469
#[macro_export]
450470
macro_rules! pkg {
451-
($pkgid:expr => [$($deps:expr),+ $(,)* ]) => ({
452-
let d: Vec<Dependency> = vec![$($deps.to_dep()),+];
471+
($pkgid:expr => [$($deps:expr),* $(,)? ]) => ({
472+
let d: Vec<Dependency> = vec![$($deps.to_dep()),*];
453473
$crate::pkg_dep($pkgid, d)
454474
});
455475

@@ -473,18 +493,29 @@ pub fn pkg<T: ToPkgId>(name: T) -> Summary {
473493
pub fn pkg_dep<T: ToPkgId>(name: T, dep: Vec<Dependency>) -> Summary {
474494
let pkgid = name.to_pkgid();
475495
let link = if pkgid.name().ends_with("-sys") {
476-
Some(pkgid.name().as_str())
496+
Some(pkgid.name())
477497
} else {
478498
None
479499
};
480-
Summary::new(
481-
name.to_pkgid(),
482-
dep,
483-
&BTreeMap::new(),
484-
link,
485-
None::<RustVersion>,
486-
)
487-
.unwrap()
500+
Summary::new(name.to_pkgid(), dep, &BTreeMap::new(), link, None).unwrap()
501+
}
502+
503+
pub fn pkg_dep_with<T: ToPkgId>(
504+
name: T,
505+
dep: Vec<Dependency>,
506+
features: &[(&'static str, &[&'static str])],
507+
) -> Summary {
508+
let pkgid = name.to_pkgid();
509+
let link = if pkgid.name().ends_with("-sys") {
510+
Some(pkgid.name())
511+
} else {
512+
None
513+
};
514+
let features = features
515+
.into_iter()
516+
.map(|&(name, values)| (name.into(), values.into_iter().map(|&v| v.into()).collect()))
517+
.collect();
518+
Summary::new(name.to_pkgid(), dep, &features, link, None).unwrap()
488519
}
489520

490521
pub fn pkg_id(name: &str) -> PackageId {
@@ -510,7 +541,7 @@ pub fn pkg_loc(name: &str, loc: &str) -> Summary {
510541
Vec::new(),
511542
&BTreeMap::new(),
512543
link,
513-
None::<RustVersion>,
544+
None,
514545
)
515546
.unwrap()
516547
}
@@ -519,14 +550,7 @@ pub fn remove_dep(sum: &Summary, ind: usize) -> Summary {
519550
let mut deps = sum.dependencies().to_vec();
520551
deps.remove(ind);
521552
// note: more things will need to be copied over in the future, but it works for now.
522-
Summary::new(
523-
sum.package_id(),
524-
deps,
525-
&BTreeMap::new(),
526-
sum.links().map(|a| a.as_str()),
527-
None::<RustVersion>,
528-
)
529-
.unwrap()
553+
Summary::new(sum.package_id(), deps, &BTreeMap::new(), sum.links(), None).unwrap()
530554
}
531555

532556
pub fn dep(name: &str) -> Dependency {
@@ -629,7 +653,7 @@ fn meta_test_deep_pretty_print_registry() {
629653
pkg!(("foo", "1.0.0") => [dep_req("bar", "2")]),
630654
pkg!(("foo", "2.0.0") => [dep_req("bar", "*")]),
631655
pkg!(("bar", "1.0.0") => [dep_req("baz", "=1.0.2"),
632-
dep_req("other", "1")]),
656+
dep_req("other", "1")]),
633657
pkg!(("bar", "2.0.0") => [dep_req("baz", "=1.0.1")]),
634658
pkg!(("baz", "1.0.2") => [dep_req("other", "2")]),
635659
pkg!(("baz", "1.0.1")),
@@ -654,8 +678,8 @@ fn meta_test_deep_pretty_print_registry() {
654678
}
655679

656680
/// This generates a random registry index.
657-
/// Unlike vec((Name, Ver, vec((Name, VerRq), ..), ..)
658-
/// This strategy has a high probability of having valid dependencies
681+
/// Unlike `vec((Name, Ver, vec((Name, VerRq), ..), ..)`,
682+
/// this strategy has a high probability of having valid dependencies.
659683
pub fn registry_strategy(
660684
max_crates: usize,
661685
max_versions: usize,

0 commit comments

Comments
 (0)