Skip to content

Commit 605fdaf

Browse files
committed
[nextest-filtering] cache the list of workspace packages
Turn `ParseContext` into a cache store as shown here. In the future, it would be nice to reuse a single `ParseContext` across many places.
1 parent 98390e5 commit 605fdaf

File tree

9 files changed

+115
-85
lines changed

9 files changed

+115
-85
lines changed

cargo-nextest/src/dispatch.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,15 +1548,12 @@ impl App {
15481548
}
15491549

15501550
fn build_filtering_expressions(&self) -> Result<Vec<Filterset>> {
1551-
let pcx = ParseContext {
1552-
graph: self.base.graph(),
1553-
kind: FiltersetKind::Test,
1554-
};
1551+
let pcx = ParseContext::new(self.base.graph());
15551552
let (exprs, all_errors): (Vec<_>, Vec<_>) = self
15561553
.build_filter
15571554
.filterset
15581555
.iter()
1559-
.map(|input| Filterset::parse(input.clone(), &pcx))
1556+
.map(|input| Filterset::parse(input.clone(), &pcx, FiltersetKind::Test))
15601557
.partition_result();
15611558

15621559
if !all_errors.is_empty() {

nextest-filtering/examples/parser.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,12 @@ fn main() {
5353
let args = Args::parse();
5454

5555
let graph = load_graph(args.cargo_metadata);
56-
let cx = nextest_filtering::ParseContext {
57-
graph: &graph,
58-
kind: nextest_filtering::FiltersetKind::Test,
59-
};
60-
match nextest_filtering::Filterset::parse(args.expr, &cx) {
56+
let cx = nextest_filtering::ParseContext::new(&graph);
57+
match nextest_filtering::Filterset::parse(
58+
args.expr,
59+
&cx,
60+
nextest_filtering::FiltersetKind::Test,
61+
) {
6162
Ok(expr) => println!("{expr:?}"),
6263
Err(FiltersetParseErrors { input, errors, .. }) => {
6364
for error in errors {

nextest-filtering/src/compile.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,14 @@ use std::collections::HashSet;
1717
pub(crate) fn compile(
1818
expr: &ParsedExpr,
1919
cx: &ParseContext<'_>,
20+
kind: FiltersetKind,
2021
) -> Result<CompiledExpr, Vec<ParseSingleError>> {
2122
let mut errors = vec![];
22-
check_banned_predicates(expr, cx.kind, &mut errors);
23+
check_banned_predicates(expr, kind, &mut errors);
2324

24-
let in_workspace_packages: Vec<_> = cx
25-
.graph
26-
.resolve_workspace()
27-
.packages(guppy::graph::DependencyDirection::Forward)
28-
.collect();
29-
let mut cache = cx.graph.new_depends_cache();
30-
let expr = compile_expr(expr, &in_workspace_packages, &mut cache, &mut errors);
25+
let workspace_packages = &cx.make_cache().workspace_packages;
26+
let mut cache = cx.graph().new_depends_cache();
27+
let expr = compile_expr(expr, workspace_packages, &mut cache, &mut errors);
3128

3229
if errors.is_empty() {
3330
Ok(expr)

nextest-filtering/src/expression.rs

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ use crate::{
99
},
1010
};
1111
use guppy::{
12-
graph::{cargo::BuildPlatform, PackageGraph},
12+
graph::{cargo::BuildPlatform, PackageGraph, PackageMetadata},
1313
PackageId,
1414
};
1515
use miette::SourceSpan;
1616
use nextest_metadata::{RustBinaryId, RustTestBinaryKind};
1717
use recursion::{Collapsible, CollapsibleExt, MappableFrame, PartiallyApplied};
18-
use std::{collections::HashSet, fmt};
18+
use std::{collections::HashSet, fmt, sync::OnceLock};
1919

2020
/// Matcher for name
2121
///
@@ -268,16 +268,51 @@ impl FiltersetLeaf {
268268
}
269269

270270
/// Inputs to filterset parsing.
271-
#[derive(Copy, Clone, Debug)]
272-
pub struct ParseContext<'a> {
271+
#[derive(Debug)]
272+
pub struct ParseContext<'g> {
273273
/// The package graph.
274-
pub graph: &'a PackageGraph,
274+
graph: &'g PackageGraph,
275275

276-
/// What kind of expression this is.
277-
///
278-
/// In some cases, expressions must restrict themselves to a subset of the full filtering
279-
/// language. This is used to determine what subset of the language is allowed.
280-
pub kind: FiltersetKind,
276+
/// Cached data computed on first access.
277+
cache: OnceLock<ParseContextCache<'g>>,
278+
}
279+
280+
impl<'g> ParseContext<'g> {
281+
/// Creates a new `ParseContext`.
282+
#[inline]
283+
pub fn new(graph: &'g PackageGraph) -> Self {
284+
Self {
285+
graph,
286+
cache: OnceLock::new(),
287+
}
288+
}
289+
290+
/// Returns the package graph.
291+
#[inline]
292+
pub fn graph(&self) -> &'g PackageGraph {
293+
self.graph
294+
}
295+
296+
pub(crate) fn make_cache(&self) -> &ParseContextCache<'g> {
297+
self.cache
298+
.get_or_init(|| ParseContextCache::new(self.graph))
299+
}
300+
}
301+
302+
#[derive(Debug)]
303+
pub(crate) struct ParseContextCache<'g> {
304+
pub(crate) workspace_packages: Vec<PackageMetadata<'g>>,
305+
}
306+
307+
impl<'g> ParseContextCache<'g> {
308+
fn new(graph: &'g PackageGraph) -> Self {
309+
Self {
310+
workspace_packages: graph
311+
.resolve_workspace()
312+
.packages(guppy::graph::DependencyDirection::Forward)
313+
.collect(),
314+
}
315+
}
281316
}
282317

283318
/// The kind of filterset being parsed.
@@ -311,7 +346,11 @@ pub struct EvalContext<'a> {
311346

312347
impl Filterset {
313348
/// Parse a filterset.
314-
pub fn parse(input: String, cx: &ParseContext<'_>) -> Result<Self, FiltersetParseErrors> {
349+
pub fn parse(
350+
input: String,
351+
cx: &ParseContext<'_>,
352+
kind: FiltersetKind,
353+
) -> Result<Self, FiltersetParseErrors> {
315354
let mut errors = Vec::new();
316355
match parse(new_span(&input, &mut errors)) {
317356
Ok(parsed_expr) => {
@@ -321,7 +360,7 @@ impl Filterset {
321360

322361
match parsed_expr {
323362
ExprResult::Valid(parsed) => {
324-
let compiled = crate::compile::compile(&parsed, cx)
363+
let compiled = crate::compile::compile(&parsed, cx, kind)
325364
.map_err(|errors| FiltersetParseErrors::new(input.clone(), errors))?;
326365
Ok(Self {
327366
input,

nextest-filtering/tests/match.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,8 @@ fn mk_pid(c: char) -> PackageId {
2828
}
2929

3030
fn parse(input: &str, graph: &PackageGraph) -> Filterset {
31-
let cx = ParseContext {
32-
graph,
33-
kind: FiltersetKind::Test,
34-
};
35-
let expr = Filterset::parse(input.to_owned(), &cx).unwrap();
31+
let cx = ParseContext::new(graph);
32+
let expr = Filterset::parse(input.to_owned(), &cx, FiltersetKind::Test).unwrap();
3633
eprintln!("expression: {expr:?}");
3734
expr
3835
}
@@ -393,21 +390,22 @@ fn test_expr_with_no_matching_packages() {
393390
}
394391

395392
let graph = load_graph();
396-
let cx = ParseContext {
397-
graph: &graph,
398-
kind: FiltersetKind::Test,
399-
};
393+
let cx = ParseContext::new(&graph);
400394

401-
let errors = Filterset::parse("deps(does-not-exist)".to_owned(), &cx).unwrap_err();
395+
let errors =
396+
Filterset::parse("deps(does-not-exist)".to_owned(), &cx, FiltersetKind::Test).unwrap_err();
402397
assert_error(&errors);
403398

404-
let errors = Filterset::parse("deps(=does-not-exist)".to_owned(), &cx).unwrap_err();
399+
let errors =
400+
Filterset::parse("deps(=does-not-exist)".to_owned(), &cx, FiltersetKind::Test).unwrap_err();
405401
assert_error(&errors);
406402

407-
let errors = Filterset::parse("deps(~does-not-exist)".to_owned(), &cx).unwrap_err();
403+
let errors =
404+
Filterset::parse("deps(~does-not-exist)".to_owned(), &cx, FiltersetKind::Test).unwrap_err();
408405
assert_error(&errors);
409406

410-
let errors = Filterset::parse("deps(/does-not/)".to_owned(), &cx).unwrap_err();
407+
let errors =
408+
Filterset::parse("deps(/does-not/)".to_owned(), &cx, FiltersetKind::Test).unwrap_err();
411409
assert_error(&errors);
412410
}
413411

nextest-runner/src/config/overrides.rs

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -453,11 +453,8 @@ impl CompiledData<PreBuildPlatform> {
453453
errors: &mut Vec<ConfigCompileError>,
454454
) -> Self {
455455
let profile_default_filter = profile_default_filter.and_then(|filter| {
456-
let cx = ParseContext {
457-
graph,
458-
kind: FiltersetKind::DefaultFilter,
459-
};
460-
match Filterset::parse(filter.to_owned(), &cx) {
456+
let cx = ParseContext::new(graph);
457+
match Filterset::parse(filter.to_owned(), &cx, FiltersetKind::DefaultFilter) {
461458
Ok(expr) => Some(CompiledDefaultFilter {
462459
expr: expr.compiled,
463460
profile: profile_name.to_owned(),
@@ -607,20 +604,19 @@ impl CompiledOverride<PreBuildPlatform> {
607604
});
608605
return None;
609606
}
610-
let cx = ParseContext {
611-
graph,
612-
// In the future, based on the settings we may want to have restrictions on the kind
613-
// here.
614-
kind: FiltersetKind::Test,
615-
};
607+
let cx = ParseContext::new(graph);
608+
// In the future, based on the settings we may want to have
609+
// restrictions on the kind here.
610+
let kind = FiltersetKind::Test;
616611

617612
let host_spec = MaybeTargetSpec::new(source.platform.host.as_deref());
618613
let target_spec = MaybeTargetSpec::new(source.platform.target.as_deref());
619614
let filter = source.filter.as_ref().map_or(Ok(None), |filter| {
620-
Some(Filterset::parse(filter.clone(), &cx)).transpose()
615+
Some(Filterset::parse(filter.clone(), &cx, kind)).transpose()
621616
});
622617
let default_filter = source.default_filter.as_ref().map_or(Ok(None), |filter| {
623-
Some(Filterset::parse(filter.clone(), &cx)).transpose()
618+
// XXX: kind should be Filterset::DefaultFilter!
619+
Some(Filterset::parse(filter.clone(), &cx, kind)).transpose()
624620
});
625621

626622
match (host_spec, target_spec, filter, default_filter) {

nextest-runner/src/config/scripts.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -279,14 +279,17 @@ impl CompiledProfileScripts<PreBuildPlatform> {
279279

280280
let host_spec = MaybeTargetSpec::new(source.platform.host.as_deref());
281281
let target_spec = MaybeTargetSpec::new(source.platform.target.as_deref());
282-
let cx = ParseContext {
283-
graph,
284-
// TODO: probably want to restrict the set of expressions here.
285-
kind: FiltersetKind::Test,
286-
};
282+
let cx = ParseContext::new(graph);
287283

288284
let filter_expr = source.filter.as_ref().map_or(Ok(None), |filter| {
289-
Some(Filterset::parse(filter.clone(), &cx)).transpose()
285+
// TODO: probably want to restrict the set of expressions here via
286+
// the `kind` parameter.
287+
Some(Filterset::parse(
288+
filter.clone(),
289+
&cx,
290+
FiltersetKind::DefaultFilter,
291+
))
292+
.transpose()
290293
});
291294

292295
match (host_spec, target_spec, filter_expr) {

nextest-runner/src/list/test_list.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,17 +1175,16 @@ mod tests {
11751175
benches::ignored_bench_foo: benchmark
11761176
"};
11771177

1178-
let cx = ParseContext {
1179-
graph: &PACKAGE_GRAPH_FIXTURE,
1180-
kind: FiltersetKind::Test,
1181-
};
1178+
let cx = ParseContext::new(&PACKAGE_GRAPH_FIXTURE);
11821179

11831180
let test_filter = TestFilterBuilder::new(
11841181
RunIgnored::Default,
11851182
None,
11861183
TestFilterPatterns::default(),
11871184
// Test against the platform() predicate because this is the most important one here.
1188-
vec![Filterset::parse("platform(target)".to_owned(), &cx).unwrap()],
1185+
vec![
1186+
Filterset::parse("platform(target)".to_owned(), &cx, FiltersetKind::Test).unwrap(),
1187+
],
11891188
)
11901189
.unwrap();
11911190
let fake_cwd: Utf8PathBuf = "/fake/cwd".into();

nextest-runner/tests/integration/basic.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -238,11 +238,13 @@ fn test_run() -> Result<()> {
238238
fn test_run_ignored() -> Result<()> {
239239
set_env_vars();
240240

241-
let pcx = ParseContext {
242-
graph: &PACKAGE_GRAPH,
243-
kind: FiltersetKind::Test,
244-
};
245-
let expr = Filterset::parse("not test(test_slow_timeout)".to_owned(), &pcx).unwrap();
241+
let pcx = ParseContext::new(&PACKAGE_GRAPH);
242+
let expr = Filterset::parse(
243+
"not test(test_slow_timeout)".to_owned(),
244+
&pcx,
245+
FiltersetKind::Test,
246+
)
247+
.unwrap();
246248

247249
let test_filter = TestFilterBuilder::new(
248250
RunIgnored::Only,
@@ -336,13 +338,11 @@ fn test_run_ignored() -> Result<()> {
336338
fn test_filter_expr_with_string_filters() -> Result<()> {
337339
set_env_vars();
338340

339-
let pcx = ParseContext {
340-
graph: &PACKAGE_GRAPH,
341-
kind: FiltersetKind::Test,
342-
};
341+
let pcx = ParseContext::new(&PACKAGE_GRAPH);
343342
let expr = Filterset::parse(
344343
"test(test_multiply_two) | test(=tests::call_dylib_add_two)".to_owned(),
345344
&pcx,
345+
FiltersetKind::Test,
346346
)
347347
.expect("filterset is valid");
348348

@@ -410,13 +410,11 @@ fn test_filter_expr_with_string_filters() -> Result<()> {
410410
fn test_filter_expr_without_string_filters() -> Result<()> {
411411
set_env_vars();
412412

413-
let pcx = ParseContext {
414-
graph: &PACKAGE_GRAPH,
415-
kind: FiltersetKind::Test,
416-
};
413+
let pcx = ParseContext::new(&PACKAGE_GRAPH);
417414
let expr = Filterset::parse(
418415
"test(test_multiply_two) | test(=tests::call_dylib_add_two)".to_owned(),
419416
&pcx,
417+
FiltersetKind::Test,
420418
)
421419
.expect("filterset is valid");
422420

@@ -653,11 +651,13 @@ fn test_retries(retries: Option<RetryPolicy>) -> Result<()> {
653651
fn test_termination() -> Result<()> {
654652
set_env_vars();
655653

656-
let pcx = ParseContext {
657-
graph: &PACKAGE_GRAPH,
658-
kind: FiltersetKind::Test,
659-
};
660-
let expr = Filterset::parse("test(/^test_slow_timeout/)".to_owned(), &pcx).unwrap();
654+
let pcx = ParseContext::new(&PACKAGE_GRAPH);
655+
let expr = Filterset::parse(
656+
"test(/^test_slow_timeout/)".to_owned(),
657+
&pcx,
658+
FiltersetKind::Test,
659+
)
660+
.unwrap();
661661
let test_filter = TestFilterBuilder::new(
662662
RunIgnored::Only,
663663
None,

0 commit comments

Comments
 (0)