embedded_runner/
collect.rs

1use std::path::PathBuf;
2
3use tokio::io::AsyncWriteExt;
4
5use crate::{cfg::CollectCmdConfig, coverage, RunnerError};
6
7pub async fn run(cfg: CollectCmdConfig) -> Result<(), RunnerError> {
8    let coverages_file = coverage::coverages_filepath();
9    let mut coverages = mantra_schema::coverage::CoverageSchema {
10        version: Some(mantra_schema::SCHEMA_VERSION.to_string()),
11        test_runs: Vec::new(),
12    };
13
14    if !coverages_file.exists() {
15        log::info!("No coverage to collect.");
16        return Ok(());
17    }
18
19    let files = tokio::fs::read_to_string(&coverages_file)
20        .await
21        .expect("Coverages file exists.");
22    for line in files.lines() {
23        let coverage_path = PathBuf::from(line);
24
25        if !coverage_path.exists() {
26            log::error!("Missing coverage file '{}'.", coverage_path.display());
27        } else {
28            let coverage_content = tokio::fs::read_to_string(coverage_path)
29                .await
30                .expect("Coverage file exists.");
31            let mut coverage: mantra_schema::coverage::CoverageSchema =
32                serde_json::from_str(&coverage_content)
33                    .expect("Coverage was serialized with embedded-runner.");
34
35            coverages.test_runs.append(&mut coverage.test_runs);
36        }
37    }
38
39    if coverages.test_runs.is_empty() {
40        log::info!("No coverages found.");
41        return Ok(());
42    }
43
44    let output = match cfg.output {
45        Some(out) => {
46            if out
47                .extension()
48                .and_then(|s| s.to_str())
49                .map(|s| s.to_lowercase())
50                == Some("json".to_string())
51            {
52                out
53            } else {
54                return Err(RunnerError::Setup(
55                    "Output path must point to a JSON file.".to_string(),
56                ));
57            }
58        }
59        None => PathBuf::from("coverage.json"),
60    };
61
62    let combined_coverage =
63        serde_json::to_string(&coverages).expect("Serializing coverage schema.");
64
65    if !output.exists() {
66        let mut file = tokio::fs::File::create(output).await.map_err(|_| {
67            RunnerError::Setup("Could not create combined coverage file.".to_string())
68        })?;
69        let _ = file.write(combined_coverage.as_bytes()).await;
70    } else {
71        let _ = tokio::fs::write(output, combined_coverage).await;
72    }
73
74    // To only collect the newly created coverage files
75    let _ = tokio::fs::remove_file(coverages_file).await;
76
77    Ok(())
78}