Don't ignore gcno files that are not associated with any gcda file.

Fixes #42
This commit is contained in:
Marco Castelluccio 2017-10-18 19:25:13 +01:00
Родитель ece5888cd3
Коммит 631b320b42
5 изменённых файлов: 144 добавлений и 82 удалений

Просмотреть файл

@ -72,7 +72,7 @@ fn prova() {
#[derive(Debug,PartialEq)]
enum ItemFormat {
GCDA,
GCNO,
INFO,
}
@ -147,7 +147,7 @@ fn test_mkfifo() {
}
fn dir_producer(directories: &[&String], queue: &WorkQueue) -> Option<Vec<u8>> {
let gcda_ext = Some(OsStr::new("gcda"));
let gcno_ext = Some(OsStr::new("gcno"));
let info_ext = Some(OsStr::new("info"));
let json_ext = Some(OsStr::new("json"));
@ -159,8 +159,8 @@ fn dir_producer(directories: &[&String], queue: &WorkQueue) -> Option<Vec<u8>> {
let path = entry.path();
if path.is_file() {
let ext = path.extension();
let format = if ext == gcda_ext {
ItemFormat::GCDA
let format = if ext == gcno_ext {
ItemFormat::GCNO
} else if ext == info_ext {
ItemFormat::INFO
} else if ext == json_ext && path.file_name().unwrap() == "linked-files-map.json" {
@ -187,14 +187,14 @@ fn dir_producer(directories: &[&String], queue: &WorkQueue) -> Option<Vec<u8>> {
fn check_produced(queue: &WorkQueue, expected: Vec<(ItemFormat,bool,&str)>) {
let mut vec: Vec<Option<WorkItem>> = Vec::new();
for _ in 0..expected.len() {
loop {
let elem = queue.try_pop();
assert!(!elem.is_none());
if elem.is_none() {
break;
}
vec.push(elem.unwrap());
}
assert!(queue.try_pop().is_none());
for elem in &expected {
assert!(vec.iter().any(|x| {
if !x.is_some() {
@ -218,6 +218,27 @@ fn check_produced(queue: &WorkQueue, expected: Vec<(ItemFormat,bool,&str)>) {
}), "Missing {:?}", elem);
}
for v in &vec {
let v = v.as_ref().unwrap();
assert!(expected.iter().any(|x| {
if v.format != x.0 {
return false;
}
match v.item {
ItemType::Content(_) => {
!x.1
},
ItemType::Path(ref p) => {
x.1 && p.ends_with(x.2)
}
}
}), "Unexpected {:?}", v);
}
// Make sure we haven't generated duplicated entries.
assert_eq!(vec.len(), expected.len());
// Assert file exists and file with the same name but with extension .gcno exists.
for x in vec.iter() {
let x = x.as_ref().unwrap();
@ -228,7 +249,7 @@ fn check_produced(queue: &WorkQueue, expected: Vec<(ItemFormat,bool,&str)>) {
},
ItemType::Path(ref p) => {
assert!(p.exists(), "{} doesn't exist", p.display());
if x.format == ItemFormat::GCDA {
if x.format == ItemFormat::GCNO {
let gcno = p.with_file_name(format!("{}.gcno", p.file_stem().unwrap().to_str().unwrap()));
assert!(gcno.exists(), "{} doesn't exist", gcno.display());
}
@ -244,16 +265,17 @@ fn test_dir_producer() {
let mapping = dir_producer(&vec![&"test".to_string()], &queue);
let expected: Vec<(ItemFormat,bool,&str)> = vec![
(ItemFormat::GCDA, true, "grcov/test/Platform.gcda"),
(ItemFormat::GCDA, true, "grcov/test/sub2/RootAccessibleWrap.gcda"),
(ItemFormat::GCDA, true, "grcov/test/nsMaiInterfaceValue.gcda"),
(ItemFormat::GCDA, true, "grcov/test/sub/prova2.gcda"),
(ItemFormat::GCDA, true, "grcov/test/nsMaiInterfaceDocument.gcda"),
(ItemFormat::GCDA, true, "grcov/test/Unified_cpp_netwerk_base0.gcda"),
(ItemFormat::GCDA, true, "grcov/test/prova.gcda"),
(ItemFormat::GCDA, true, "grcov/test/nsGnomeModule.gcda"),
(ItemFormat::GCDA, true, "grcov/test/negative_counts.gcda"),
(ItemFormat::GCDA, true, "grcov/test/64bit_count.gcda"),
(ItemFormat::GCNO, true, "grcov/test/Platform.gcno"),
(ItemFormat::GCNO, true, "grcov/test/sub2/RootAccessibleWrap.gcno"),
(ItemFormat::GCNO, true, "grcov/test/nsMaiInterfaceValue.gcno"),
(ItemFormat::GCNO, true, "grcov/test/sub/prova2.gcno"),
(ItemFormat::GCNO, true, "grcov/test/nsMaiInterfaceDocument.gcno"),
(ItemFormat::GCNO, true, "grcov/test/Unified_cpp_netwerk_base0.gcno"),
(ItemFormat::GCNO, true, "grcov/test/prova.gcno"),
(ItemFormat::GCNO, true, "grcov/test/nsGnomeModule.gcno"),
(ItemFormat::GCNO, true, "grcov/test/negative_counts.gcno"),
(ItemFormat::GCNO, true, "grcov/test/64bit_count.gcno"),
(ItemFormat::GCNO, true, "grcov/test/no_gcda/main.gcno"),
(ItemFormat::INFO, true, "grcov/test/1494603973-2977-7.info"),
(ItemFormat::INFO, true, "grcov/test/prova.info"),
(ItemFormat::INFO, true, "grcov/test/prova_fn_with_commas.info"),
@ -270,8 +292,8 @@ fn test_dir_producer() {
let mapping = dir_producer(&vec![&"test/sub".to_string(), &"test/sub2".to_string()], &queue);
let expected: Vec<(ItemFormat,bool,&str)> = vec![
(ItemFormat::GCDA, true, "grcov/test/sub2/RootAccessibleWrap.gcda"),
(ItemFormat::GCDA, true, "grcov/test/sub/prova2.gcda"),
(ItemFormat::GCNO, true, "grcov/test/sub2/RootAccessibleWrap.gcno"),
(ItemFormat::GCNO, true, "grcov/test/sub/prova2.gcno"),
];
check_produced(&queue, expected);
@ -330,28 +352,29 @@ fn zip_producer(tmp_dir: &Path, zip_files: &[&String], queue: &WorkQueue) -> Opt
else {
let stem = path.file_stem().unwrap().to_str().unwrap();
let gcno_path = path.with_file_name(format!("{}_{}.gcno", stem, 1));
extract_file(&mut gcno_file, &gcno_path);
let physical_gcno_path = path.with_file_name(format!("{}_{}.gcno", stem, 1));
extract_file(&mut gcno_file, &physical_gcno_path);
let gcda_path_in_zip = gcno_path_in_zip.with_extension("gcda");
for (num, gcda_archive) in gcda_archives.iter_mut().enumerate() {
if let Ok(mut gcda_file) = gcda_archive.by_name(gcda_path_in_zip.to_str().unwrap()) {
// Create symlinks.
if num != 0 {
let link_path = path.with_file_name(format!("{}_{}.gcno", stem, num + 1));
fs::hard_link(&gcno_path, &link_path).expect(format!("Failed to create hardlink {}", link_path.display()).as_str());
}
let gcno_path = path.with_file_name(format!("{}_{}.gcno", stem, num + 1));
// Create symlinks.
if num != 0 {
fs::hard_link(&physical_gcno_path, &gcno_path).expect(format!("Failed to create hardlink {}", gcno_path.display()).as_str());
}
if let Ok(mut gcda_file) = gcda_archive.by_name(gcda_path_in_zip.to_str().unwrap()) {
let gcda_path = path.with_file_name(format!("{}_{}.gcda", stem, num + 1));
extract_file(&mut gcda_file, &gcda_path);
queue.push(Some(WorkItem {
format: ItemFormat::GCDA,
item: ItemType::Path(gcda_path),
}));
}
queue.push(Some(WorkItem {
format: ItemFormat::GCNO,
item: ItemType::Path(gcno_path),
}));
}
}
}
@ -379,7 +402,7 @@ fn zip_producer(tmp_dir: &Path, zip_files: &[&String], queue: &WorkQueue) -> Opt
#[test]
fn test_zip_producer() {
// Test extracting multiple gcda archives.
println!("Test extracting multiple gcda archives.");
let queue: Arc<WorkQueue> = Arc::new(MsQueue::new());
let tmp_dir = TempDir::new("grcov").expect("Failed to create temporary directory");
@ -387,16 +410,18 @@ fn test_zip_producer() {
let mapping = zip_producer(&tmp_path, &vec![&"test/gcno.zip".to_string(), &"test/gcda1.zip".to_string(), &"test/gcda2.zip".to_string()], &queue);
let expected: Vec<(ItemFormat,bool,&str)> = vec![
(ItemFormat::GCDA, true, "Platform_1.gcda"),
(ItemFormat::GCDA, true, "sub2/RootAccessibleWrap_1.gcda"),
(ItemFormat::GCDA, true, "nsMaiInterfaceValue_1.gcda"),
(ItemFormat::GCDA, true, "sub/prova2_1.gcda"),
(ItemFormat::GCDA, true, "nsMaiInterfaceDocument_1.gcda"),
(ItemFormat::GCDA, true, "nsGnomeModule_1.gcda"),
(ItemFormat::GCDA, true, "nsMaiInterfaceValue_2.gcda"),
(ItemFormat::GCDA, true, "nsMaiInterfaceDocument_2.gcda"),
(ItemFormat::GCDA, true, "nsGnomeModule_2.gcda"),
(ItemFormat::GCDA, true, "sub/prova2_2.gcda"),
(ItemFormat::GCNO, true, "Platform_1.gcno"),
(ItemFormat::GCNO, true, "Platform_2.gcno"),
(ItemFormat::GCNO, true, "sub2/RootAccessibleWrap_1.gcno"),
(ItemFormat::GCNO, true, "sub2/RootAccessibleWrap_2.gcno"),
(ItemFormat::GCNO, true, "nsMaiInterfaceValue_1.gcno"),
(ItemFormat::GCNO, true, "sub/prova2_1.gcno"),
(ItemFormat::GCNO, true, "nsMaiInterfaceDocument_1.gcno"),
(ItemFormat::GCNO, true, "nsGnomeModule_1.gcno"),
(ItemFormat::GCNO, true, "nsMaiInterfaceValue_2.gcno"),
(ItemFormat::GCNO, true, "nsMaiInterfaceDocument_2.gcno"),
(ItemFormat::GCNO, true, "nsGnomeModule_2.gcno"),
(ItemFormat::GCNO, true, "sub/prova2_2.gcno"),
];
check_produced(&queue, expected);
@ -404,7 +429,8 @@ fn test_zip_producer() {
let mapping: Value = serde_json::from_slice(&mapping.unwrap()).unwrap();
assert_eq!(mapping.get("dist/include/zlib.h").unwrap().as_str().unwrap(), "modules/zlib/src/zlib.h");
// Test extracting gcno with no path mapping.
println!("Test extracting gcno with no path mapping.");
let queue: Arc<WorkQueue> = Arc::new(MsQueue::new());
let tmp_dir = TempDir::new("grcov").expect("Failed to create temporary directory");
@ -412,18 +438,19 @@ fn test_zip_producer() {
let mapping = zip_producer(&tmp_path, &vec![&"test/gcno_no_path_mapping.zip".to_string(), &"test/gcda1.zip".to_string()], &queue);
let expected: Vec<(ItemFormat,bool,&str)> = vec![
(ItemFormat::GCDA, true, "Platform_1.gcda"),
(ItemFormat::GCDA, true, "sub2/RootAccessibleWrap_1.gcda"),
(ItemFormat::GCDA, true, "nsMaiInterfaceValue_1.gcda"),
(ItemFormat::GCDA, true, "sub/prova2_1.gcda"),
(ItemFormat::GCDA, true, "nsMaiInterfaceDocument_1.gcda"),
(ItemFormat::GCDA, true, "nsGnomeModule_1.gcda"),
(ItemFormat::GCNO, true, "Platform_1.gcno"),
(ItemFormat::GCNO, true, "sub2/RootAccessibleWrap_1.gcno"),
(ItemFormat::GCNO, true, "nsMaiInterfaceValue_1.gcno"),
(ItemFormat::GCNO, true, "sub/prova2_1.gcno"),
(ItemFormat::GCNO, true, "nsMaiInterfaceDocument_1.gcno"),
(ItemFormat::GCNO, true, "nsGnomeModule_1.gcno"),
];
check_produced(&queue, expected);
assert!(mapping.is_none());
// Test calling zip_producer with a different order of zip files.
println!("Test calling zip_producer with a different order of zip files.");
let queue: Arc<WorkQueue> = Arc::new(MsQueue::new());
let tmp_dir = TempDir::new("grcov").expect("Failed to create temporary directory");
@ -431,21 +458,24 @@ fn test_zip_producer() {
zip_producer(&tmp_path, &vec![&"test/gcda1.zip".to_string(), &"test/gcno.zip".to_string(), &"test/gcda2.zip".to_string()], &queue);
let expected: Vec<(ItemFormat,bool,&str)> = vec![
(ItemFormat::GCDA, true, "Platform_1.gcda"),
(ItemFormat::GCDA, true, "sub2/RootAccessibleWrap_1.gcda"),
(ItemFormat::GCDA, true, "nsMaiInterfaceValue_1.gcda"),
(ItemFormat::GCDA, true, "sub/prova2_1.gcda"),
(ItemFormat::GCDA, true, "nsMaiInterfaceDocument_1.gcda"),
(ItemFormat::GCDA, true, "nsGnomeModule_1.gcda"),
(ItemFormat::GCDA, true, "nsMaiInterfaceValue_2.gcda"),
(ItemFormat::GCDA, true, "nsMaiInterfaceDocument_2.gcda"),
(ItemFormat::GCDA, true, "nsGnomeModule_2.gcda"),
(ItemFormat::GCDA, true, "sub/prova2_2.gcda"),
(ItemFormat::GCNO, true, "Platform_1.gcno"),
(ItemFormat::GCNO, true, "Platform_2.gcno"),
(ItemFormat::GCNO, true, "sub2/RootAccessibleWrap_1.gcno"),
(ItemFormat::GCNO, true, "sub2/RootAccessibleWrap_2.gcno"),
(ItemFormat::GCNO, true, "nsMaiInterfaceValue_1.gcno"),
(ItemFormat::GCNO, true, "sub/prova2_1.gcno"),
(ItemFormat::GCNO, true, "nsMaiInterfaceDocument_1.gcno"),
(ItemFormat::GCNO, true, "nsGnomeModule_1.gcno"),
(ItemFormat::GCNO, true, "nsMaiInterfaceValue_2.gcno"),
(ItemFormat::GCNO, true, "nsMaiInterfaceDocument_2.gcno"),
(ItemFormat::GCNO, true, "nsGnomeModule_2.gcno"),
(ItemFormat::GCNO, true, "sub/prova2_2.gcno"),
];
check_produced(&queue, expected);
// Test extracting info files.
println!("Test extracting info files.");
let queue: Arc<WorkQueue> = Arc::new(MsQueue::new());
let tmp_dir = TempDir::new("grcov").expect("Failed to create temporary directory");
@ -469,7 +499,8 @@ fn test_zip_producer() {
check_produced(&queue, expected);
// Test extracting both info and gcno/gcda files.
println!("Test extracting both info and gcno/gcda files.");
let queue: Arc<WorkQueue> = Arc::new(MsQueue::new());
let tmp_dir = TempDir::new("grcov").expect("Failed to create temporary directory");
@ -477,12 +508,12 @@ fn test_zip_producer() {
zip_producer(&tmp_path, &vec![&"test/gcno.zip".to_string(), &"test/gcda1.zip".to_string(), &"test/info1.zip".to_string(), &"test/info2.zip".to_string()], &queue);
let expected: Vec<(ItemFormat,bool,&str)> = vec![
(ItemFormat::GCDA, true, "Platform_1.gcda"),
(ItemFormat::GCDA, true, "sub2/RootAccessibleWrap_1.gcda"),
(ItemFormat::GCDA, true, "nsMaiInterfaceValue_1.gcda"),
(ItemFormat::GCDA, true, "sub/prova2_1.gcda"),
(ItemFormat::GCDA, true, "nsMaiInterfaceDocument_1.gcda"),
(ItemFormat::GCDA, true, "nsGnomeModule_1.gcda"),
(ItemFormat::GCNO, true, "Platform_1.gcno"),
(ItemFormat::GCNO, true, "sub2/RootAccessibleWrap_1.gcno"),
(ItemFormat::GCNO, true, "nsMaiInterfaceValue_1.gcno"),
(ItemFormat::GCNO, true, "sub/prova2_1.gcno"),
(ItemFormat::GCNO, true, "nsMaiInterfaceDocument_1.gcno"),
(ItemFormat::GCNO, true, "nsGnomeModule_1.gcno"),
(ItemFormat::INFO, false, "1494603967-2977-2_0.info"),
(ItemFormat::INFO, false, "1494603967-2977-3_0.info"),
(ItemFormat::INFO, false, "1494603967-2977-4_0.info"),
@ -498,6 +529,37 @@ fn test_zip_producer() {
];
check_produced(&queue, expected);
println!("Test extracting gcno with no associated gcda.");
let queue: Arc<WorkQueue> = Arc::new(MsQueue::new());
let tmp_dir = TempDir::new("grcov").expect("Failed to create temporary directory");
let tmp_path = tmp_dir.path().to_owned();
let mapping = zip_producer(&tmp_path, &vec![&"test/no_gcda/main.gcno.zip".to_string(), &"test/no_gcda/empty.gcda.zip".to_string()], &queue);
let expected: Vec<(ItemFormat,bool,&str)> = vec![
(ItemFormat::GCNO, true, "main_1.gcno"),
];
check_produced(&queue, expected);
assert!(mapping.is_none());
println!("Test extracting gcno with an associated gcda file in only one zip file.");
let queue: Arc<WorkQueue> = Arc::new(MsQueue::new());
let tmp_dir = TempDir::new("grcov").expect("Failed to create temporary directory");
let tmp_path = tmp_dir.path().to_owned();
let mapping = zip_producer(&tmp_path, &vec![&"test/no_gcda/main.gcno.zip".to_string(), &"test/no_gcda/empty.gcda.zip".to_string(), &"test/no_gcda/main.gcda.zip".to_string()], &queue);
let expected: Vec<(ItemFormat,bool,&str)> = vec![
(ItemFormat::GCNO, true, "main_1.gcno"),
(ItemFormat::GCNO, true, "main_2.gcno"),
];
check_produced(&queue, expected);
assert!(mapping.is_none());
}
fn producer(tmp_dir: &Path, paths: &[String], queue: &WorkQueue) -> Option<Vec<u8>> {
@ -524,14 +586,14 @@ fn producer(tmp_dir: &Path, paths: &[String], queue: &WorkQueue) -> Option<Vec<u
}
}
fn run_gcov(gcda_path: &PathBuf, branch_enabled: bool, working_dir: &PathBuf) {
fn run_gcov(gcno_path: &PathBuf, branch_enabled: bool, working_dir: &PathBuf) {
let mut command = Command::new("gcov");
let command = if branch_enabled {
command.arg("-b").arg("-c")
} else {
&mut command
};
let status = command.arg(gcda_path)
let status = command.arg(gcno_path)
.arg("-i") // Generate intermediate gcov format, faster to parse.
.current_dir(working_dir)
.stdout(Stdio::null())
@ -543,24 +605,24 @@ fn run_gcov(gcda_path: &PathBuf, branch_enabled: bool, working_dir: &PathBuf) {
} else {*/
let status = status.status()
.expect("Failed to execute gcov process");
assert!(status.success(), "gcov wasn't successfully executed on {}", gcda_path.display());
assert!(status.success(), "gcov wasn't successfully executed on {}", gcno_path.display());
//}
}
fn run_llvm_gcov(gcda_path: &PathBuf, working_dir: &PathBuf) {
fn run_llvm_gcov(gcno_path: &PathBuf, working_dir: &PathBuf) {
let status = Command::new("llvm-cov")
.arg("gcov")
.arg("-l") // Generate unique names for gcov files.
.arg("-b") // Generate function call information.
.arg("-c") // Display branch counts instead of percentages.
.arg(gcda_path)
.arg(gcno_path)
.current_dir(working_dir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.expect("Failed to execute llvm-cov process");
assert!(status.success(), "llvm-cov wasn't successfully executed on {}", gcda_path.display());
assert!(status.success(), "llvm-cov wasn't successfully executed on {}", gcno_path.display());
}
fn parse_lcov<T: Read>(lcov_reader: BufReader<T>, branch_enabled: bool) -> Vec<(String,CovResult)> {
@ -1694,23 +1756,23 @@ fn main() {
while let Some(work_item) = queue.pop() {
let new_results = match work_item.format {
ItemFormat::GCDA => {
let gcda_path = work_item.path();
ItemFormat::GCNO => {
let gcno_path = work_item.path();
if !is_llvm {
let gcov_path = working_dir.join(gcda_path.file_name().unwrap().to_str().unwrap().to_string() + ".gcov");
let gcov_path = working_dir.join(gcno_path.file_name().unwrap().to_str().unwrap().to_string() + ".gcov");
/*if cfg!(unix) {
mkfifo(&gcov_path);
}*/
run_gcov(gcda_path, branch_enabled, &working_dir);
run_gcov(gcno_path, branch_enabled, &working_dir);
let new_results = parse_gcov(&gcov_path);
fs::remove_file(gcov_path).unwrap();
new_results
} else {
run_llvm_gcov(gcda_path, &working_dir);
run_llvm_gcov(gcno_path, &working_dir);
let mut new_results: Vec<(String,CovResult)> = Vec::new();

Двоичные данные
test/no_gcda/empty.gcda.zip Normal file

Двоичный файл не отображается.

Двоичные данные
test/no_gcda/main.gcda.zip Normal file

Двоичный файл не отображается.

Двоичные данные
test/no_gcda/main.gcno Normal file

Двоичный файл не отображается.

Двоичные данные
test/no_gcda/main.gcno.zip Normal file

Двоичный файл не отображается.