workspacer_test_coverage/
test_coverage.rs1crate::ix!();
3
4#[async_trait]
5pub trait RunTestsWithCoverage {
6
7 type Report;
8 type Error;
9
10 async fn run_tests_with_coverage(&self)
11 -> Result<Self::Report, Self::Error>;
12}
13
14#[async_trait]
18impl RunTestsWithCoverage for CrateHandle {
19 type Report = TestCoverageReport;
20 type Error = WorkspaceError;
21
22 async fn run_tests_with_coverage(&self) -> Result<Self::Report, Self::Error> {
23 let workspace_root = self
24 .root_dir_path_buf()
25 .parent()
26 .ok_or_else(|| {
27 error!("Cannot get parent directory for crate_path={:?}", self.root_dir_path_buf());
30 WorkspaceError::IoError {
31 io_error: std::sync::Arc::new(std::io::Error::new(
32 std::io::ErrorKind::NotFound,
33 "No parent directory"
34 )),
35 context: "finding workspace root from crate path".to_string(),
36 }
37 })?
38 .to_path_buf();
39
40 let crate_name = self.name();
42 let coverage_cmd = TestCoverageCommand::run_with_package(&workspace_root, &crate_name).await?;
43 let report = coverage_cmd.generate_report()?;
44 Ok(report)
45 }
46}
47
48#[async_trait]
49impl<P,H:CrateHandleInterface<P>> RunTestsWithCoverage for Workspace<P,H>
50where for<'async_trait> P: From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait
51{
52 type Report = TestCoverageReport;
53 type Error = WorkspaceError;
54
55 async fn run_tests_with_coverage(&self)
57 -> Result<Self::Report, Self::Error>
58 {
59 let workspace_path = self.as_ref(); let test_coverage = TestCoverageCommand::run_in(workspace_path).await?;
62
63 let report = test_coverage.generate_report()?;
64
65 Ok(report)
66 }
67}
68
69#[cfg(test)]
70mod test_run_tests_with_coverage_real {
71 use super::*;
72 use std::path::PathBuf;
73 use tempfile::tempdir;
74 use workspacer_3p::tokio::process::Command;
75 use workspacer_3p::tokio;
76
77 #[derive(Debug)]
82 struct MockWorkspace {
83 path: PathBuf,
84 }
85
86 impl AsRef<std::path::Path> for MockWorkspace {
87 fn as_ref(&self) -> &std::path::Path {
88 &self.path
89 }
90 }
91
92 #[async_trait]
96 impl RunTestsWithCoverage for MockWorkspace {
97 type Report = TestCoverageReport;
98 type Error = WorkspaceError;
99
100 async fn run_tests_with_coverage(&self) -> Result<Self::Report, Self::Error> {
101 let workspace_path = self.as_ref();
102 let test_coverage = TestCoverageCommand::run_in(workspace_path).await?;
103 let report = test_coverage.generate_report()?;
104 Ok(report)
105 }
106 }
107
108 #[traced_test]
109 async fn test_run_tests_with_coverage_succeeds() {
110 trace!("Beginning test_run_tests_with_coverage_succeeds...");
111
112 let tmp_dir = tempdir().expect("Failed to create temp directory");
113 let path = tmp_dir.path();
114
115 let init_status = Command::new("cargo")
116 .arg("init")
117 .arg("--bin")
118 .arg("--vcs")
119 .arg("none")
120 .current_dir(path)
121 .output()
122 .await
123 .expect("Failed to spawn `cargo init`");
124
125 if !init_status.status.success() {
126 warn!("Skipping test because `cargo init` failed with status: {:?}", init_status.status);
127 return;
128 }
129
130 let main_rs = path.join("src").join("main.rs");
132 let code = r#"
133 fn main() {}
134 #[test]
135 fn test_ok() { assert_eq!(2+2,4); }
136 "#;
137 tokio::fs::write(&main_rs, code)
138 .await
139 .expect("Failed to write test code to main.rs");
140
141 let ws = MockWorkspace { path: path.to_path_buf() };
142
143 match ws.run_tests_with_coverage().await {
145 Ok(report) => {
146 info!("Coverage succeeded: {:?}", report);
147 assert!(report.covered_lines() > 0, "Expected at least one covered line");
148 assert!(report.total_coverage() > 0.0, "Expected non-zero coverage");
149 }
150 Err(e) => panic!("Expected coverage to succeed, but got error: {:?}", e),
151 }
152 }
153
154 #[traced_test]
155 async fn test_run_tests_with_coverage_fails() {
156 trace!("Beginning test_run_tests_with_coverage_fails...");
157
158 let tmp_dir = tempdir().expect("Failed to create temp directory");
159 let path = tmp_dir.path();
160
161 let init_status = Command::new("cargo")
162 .arg("init")
163 .arg("--bin")
164 .arg("--vcs")
165 .arg("none")
166 .current_dir(path)
167 .output()
168 .await
169 .expect("Failed to spawn `cargo init`");
170
171 if !init_status.status.success() {
172 warn!("Skipping test because `cargo init` failed with status: {:?}", init_status.status);
173 return;
174 }
175
176 let main_rs = path.join("src").join("main.rs");
178 let code = r#"
179 fn main(){}
180 #[test]
181 fn test_fail(){ assert_eq!(1+1,3); }
182 "#;
183 tokio::fs::write(&main_rs, code)
184 .await
185 .expect("Failed to write test code to main.rs");
186
187 let ws = MockWorkspace { path: path.to_path_buf() };
188
189 match ws.run_tests_with_coverage().await {
191 Ok(report) => panic!("Expected coverage to fail, got success: {:?}", report),
192 Err(e) => {
193 info!("Coverage or test failure as expected: {:?}", e);
194 }
195 }
196 }
197}