|
@@ -1,16 +1,17 @@
|
|
|
-use std::path::{Path,PathBuf};
|
|
|
-use std::{process, fs};
|
|
|
-use std::error;
|
|
|
use regex;
|
|
|
-
|
|
|
+use std::collections::BTreeMap;
|
|
|
+use std::error;
|
|
|
+use std::path::{Path, PathBuf};
|
|
|
+use std::time::SystemTime;
|
|
|
+use std::{fs, process};
|
|
|
|
|
|
fn main() {
|
|
|
let test_paths = get_test_paths().expect("no test!");
|
|
|
- println!("Test paths are {:?}",test_paths);
|
|
|
+ println!("Test paths are {:?}", test_paths);
|
|
|
let mut profraws: Vec<PathBuf> = vec![];
|
|
|
|
|
|
- for test_path in &test_paths{
|
|
|
- let test_result_path = run_tests(test_path.as_path()).expect("can't test!");
|
|
|
+ for test_path in &test_paths {
|
|
|
+ let test_result_path = run_tests(test_path.as_path()).expect("can't test!");
|
|
|
profraws.push(test_result_path);
|
|
|
}
|
|
|
|
|
@@ -18,15 +19,28 @@ fn main() {
|
|
|
show_coverage(&test_paths).expect("cannot make a report!");
|
|
|
}
|
|
|
|
|
|
+fn pick_latest(test_paths: &Vec<PathBuf>) -> Result<&PathBuf, Box<dyn error::Error>> {
|
|
|
+ let mut times: BTreeMap<SystemTime, &PathBuf> = BTreeMap::new();
|
|
|
+
|
|
|
+ for path in test_paths {
|
|
|
+ let meta = fs::metadata(path)
|
|
|
+ .expect(&("no metadata for".to_owned() + path.to_str().expect("cannot stringify")));
|
|
|
+ let time = meta.created().expect("a file without creation date");
|
|
|
+ times.insert(time, path);
|
|
|
+ }
|
|
|
+ let (_time, &path) = times.last_key_value().expect("no profdata paths?");
|
|
|
+ return Ok(path);
|
|
|
+}
|
|
|
|
|
|
fn show_coverage(test_paths: &Vec<PathBuf>) -> Result<(), Box<dyn error::Error>> {
|
|
|
assert!(Path::new("default.profdata").exists(), "no profdata!");
|
|
|
|
|
|
let mut args = vec!["report", "--instr-profile", "default.profdata"];
|
|
|
args.extend(
|
|
|
- test_paths.iter()
|
|
|
- .map(|path| ["-object", path.to_str().unwrap()])
|
|
|
- .flatten()
|
|
|
+ test_paths
|
|
|
+ .iter()
|
|
|
+ .map(|path| ["-object", path.to_str().unwrap()])
|
|
|
+ .flatten(),
|
|
|
);
|
|
|
|
|
|
println!("llvm-cov args: {:?}", args);
|
|
@@ -37,16 +51,13 @@ fn show_coverage(test_paths: &Vec<PathBuf>) -> Result<(), Box<dyn error::Error>>
|
|
|
.expect("could not run llvm-cov");
|
|
|
child.wait()?;
|
|
|
return Ok(());
|
|
|
-
|
|
|
}
|
|
|
|
|
|
-fn generate_profdata(paths : Vec<PathBuf>) -> Result<(), Box<dyn error::Error>> {
|
|
|
+fn generate_profdata(paths: Vec<PathBuf>) -> Result<(), Box<dyn error::Error>> {
|
|
|
println!("Generating profdata");
|
|
|
|
|
|
let mut args = vec!["merge"];
|
|
|
- args.extend(
|
|
|
- paths.iter().map(|x| x.to_str().unwrap())
|
|
|
- );
|
|
|
+ args.extend(paths.iter().map(|x| x.to_str().unwrap()));
|
|
|
args.push("-o");
|
|
|
args.push("default.profdata");
|
|
|
|
|
@@ -56,25 +67,34 @@ fn generate_profdata(paths : Vec<PathBuf>) -> Result<(), Box<dyn error::Error>>
|
|
|
.expect("could not process data");
|
|
|
child.wait()?;
|
|
|
|
|
|
- for path in paths{
|
|
|
+ for path in paths {
|
|
|
fs::remove_file(path).expect("cannot remove a file!");
|
|
|
}
|
|
|
|
|
|
return Ok(());
|
|
|
-
|
|
|
}
|
|
|
|
|
|
+/// Run an executable and collect your resulting profile
|
|
|
fn run_tests(test_path: &Path) -> Result<PathBuf, Box<dyn error::Error>> {
|
|
|
let mut child = process::Command::new(test_path.to_str().unwrap())
|
|
|
.spawn()
|
|
|
.expect("could not run tests!");
|
|
|
child.wait()?;
|
|
|
- let newname: String = String::from(test_path.file_name().unwrap().to_str().unwrap()) + ".profraw";
|
|
|
- fs::rename("default.profraw", &newname).expect("cannot rename");
|
|
|
- return Ok(fs::canonicalize(newname).expect("could not make a profdata"));
|
|
|
|
|
|
+ let mut paths: Vec<PathBuf> = Vec::new();
|
|
|
+ for dir in fs::read_dir(".").expect("cannot ls") {
|
|
|
+ let entry = dir.expect("who knows, ls sometimes gets empty entries?");
|
|
|
+ paths.push(entry.path())
|
|
|
+ }
|
|
|
+ let cargo_given_name = pick_latest(&paths)?;
|
|
|
+
|
|
|
+ let newname: String =
|
|
|
+ String::from(test_path.file_name().unwrap().to_str().unwrap()) + ".profraw";
|
|
|
+ fs::rename(cargo_given_name, &newname).expect("cannot rename");
|
|
|
+ return Ok(fs::canonicalize(newname).expect("could not make a profdata"));
|
|
|
}
|
|
|
|
|
|
+/// Collect paths of test executables generated by Cargo
|
|
|
fn get_test_paths() -> Result<Vec<PathBuf>, Box<dyn error::Error>> {
|
|
|
let child = process::Command::new("cargo")
|
|
|
.env("RUSTFLAGS", "-C instrument-coverage")
|
|
@@ -86,7 +106,7 @@ fn get_test_paths() -> Result<Vec<PathBuf>, Box<dyn error::Error>> {
|
|
|
|
|
|
// println!("{}", rawstring);
|
|
|
assert_ne!(rawstring.len(), 0, "empty cargo output!");
|
|
|
-
|
|
|
+
|
|
|
let parentheses = regex::Regex::new(r"\((?P<inside>.*)\)").unwrap();
|
|
|
|
|
|
let mut result: Vec<PathBuf> = vec![];
|
|
@@ -96,7 +116,7 @@ fn get_test_paths() -> Result<Vec<PathBuf>, Box<dyn error::Error>> {
|
|
|
for found_parentheses in parentheses.captures_iter(line) {
|
|
|
let path = Path::new(&found_parentheses["inside"]);
|
|
|
// println!("test path candidate {:?}", path);
|
|
|
- if path.exists(){
|
|
|
+ if path.exists() {
|
|
|
result.push(path.to_path_buf());
|
|
|
// return Ok(path.to_path_buf());
|
|
|
}
|