1mod cargo;
9mod external;
10mod manifest;
11mod metadata;
12#[cfg(test)]
13#[path = "tests/support.rs"]
14mod test_support;
15
16use bake::{Context, Error, Result};
17
18fn run_before_test_hook(context: &mut Context) -> Result<()> {
19 context.call_if_registered("test:before", &[])?;
20 Ok(())
21}
22
23#[bake::task(name = "test")]
25fn run(
26 context: &mut Context,
27 #[bake(named, default = false, help = "Also test examples and benchmarks.")] all_targets: bool,
28) -> Result<String> {
29 run_before_test_hook(context)?;
30
31 let mut arguments = vec![
32 "test".to_owned(),
33 "--workspace".to_owned(),
34 "--locked".to_owned(),
35 ];
36 if all_targets {
37 arguments.push("--all-targets".to_owned());
38 }
39
40 cargo::run(context, &arguments)?;
41 Ok("Cargo workspace tests passed".to_owned())
42}
43
44#[bake::task(name = "test:coverage")]
51fn run_coverage(
52 context: &mut Context,
53 #[bake(named, default = false, help = "Test every feature together.")] all_features: bool,
54 #[bake(help = "Repeat to select features.")] features: Vec<String>,
55 #[bake(help = "Limit coverage to one workspace package.")] package: Option<String>,
56 #[bake(named, default = false, help = "Also include examples and benchmarks.")]
57 all_targets: bool,
58) -> Result<String> {
59 if all_features && !features.is_empty() {
60 return Err(Error::new(
61 "choose either --all-features true or one or more --features values",
62 ));
63 }
64
65 run_before_test_hook(context)?;
66
67 let mut doc_arguments = vec!["test".to_owned()];
68 append_package_arguments(&mut doc_arguments, package.as_deref());
69 doc_arguments.extend(["--locked".to_owned(), "--doc".to_owned()]);
70 append_feature_arguments(&mut doc_arguments, all_features, &features);
71 cargo::run(context, &doc_arguments)?;
72
73 let mut coverage_arguments = vec!["llvm-cov".to_owned()];
74 append_package_arguments(&mut coverage_arguments, package.as_deref());
75 coverage_arguments.extend([
76 "--locked".to_owned(),
77 "--fail-under-lines".to_owned(),
78 "100".to_owned(),
79 "--show-missing-lines".to_owned(),
80 ]);
81 if all_targets {
82 coverage_arguments.push("--all-targets".to_owned());
83 }
84 append_feature_arguments(&mut coverage_arguments, all_features, &features);
85 cargo::run(context, &coverage_arguments)?;
86
87 Ok("Workspace tests passed with complete line coverage".to_owned())
88}
89
90fn append_feature_arguments(arguments: &mut Vec<String>, all_features: bool, features: &[String]) {
91 if all_features {
92 arguments.push("--all-features".to_owned());
93 } else if !features.is_empty() {
94 arguments.push("--features".to_owned());
95 arguments.push(features.join(","));
96 }
97}
98
99fn append_package_arguments(arguments: &mut Vec<String>, package: Option<&str>) {
100 if let Some(package) = package {
101 arguments.push("--package".to_owned());
102 arguments.push(package.to_owned());
103 } else {
104 arguments.push("--workspace".to_owned());
105 }
106}
107
108#[bake::task(name = "test:external")]
110fn run_external(
111 context: &mut Context,
112 #[bake(named, default = false, help = "Also test examples and benchmarks.")] all_targets: bool,
113) -> Result<String> {
114 let workspace = metadata::read_workspace(context)?;
115 if workspace.external_repositories.is_empty() {
116 return Ok("No downstream repositories are configured for external tests".to_owned());
117 }
118 if workspace.packages.is_empty() {
119 return Err(Error::new(
120 "the current workspace has no crates.io packages to patch into downstream tests",
121 ));
122 }
123
124 let mut checkouts = Vec::with_capacity(workspace.external_repositories.len());
125 for repository in &workspace.external_repositories {
126 let checkout = external::checkout(context, repository)?;
127 manifest::patch_checkout(&checkout, &workspace.packages)?;
128 cargo::update_in(&checkout)?;
129 external::ensure_local_patches_are_selected(&checkout, &workspace.packages)?;
130 checkouts.push(checkout);
131 }
132
133 run_before_test_hook(context)?;
134
135 for checkout in &checkouts {
136 let mut arguments = vec!["test".to_owned(), "--workspace".to_owned()];
137 if all_targets {
138 arguments.push("--all-targets".to_owned());
139 }
140 external::run_cargo(checkout, &arguments)?;
141 }
142
143 let repository_count = checkouts.len();
144 let repository_label = if repository_count == 1 {
145 "repository"
146 } else {
147 "repositories"
148 };
149 Ok(format!(
150 "External tests passed for {repository_count} downstream {repository_label}"
151 ))
152}
153
154#[cfg(test)]
155#[path = "tests/tasks.rs"]
156mod tests;