Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/defs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub enum ItemFormat {
Profraw,
Profdata,
Info,
JacocoXml,
Xml,
}

#[derive(Debug)]
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,13 +309,13 @@ pub fn consumer(
continue;
}
}
ItemFormat::Info | ItemFormat::JacocoXml => {
ItemFormat::Info | ItemFormat::Xml => {
if let ItemType::Content(content) = work_item.item {
if work_item.format == ItemFormat::Info {
try_parse!(parse_lcov(content, branch_enabled), work_item.name)
} else {
let buffer = BufReader::new(Cursor::new(content));
try_parse!(parse_jacoco_xml_report(buffer), work_item.name)
try_parse!(parse_xml_report(buffer), work_item.name)
}
} else {
error!("Invalid content type");
Expand Down
5 changes: 3 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,8 @@ fn main() {
),
OutputType::Files => output_files(results, output_path.as_deref()),
OutputType::Covdir => output_covdir(results, output_path.as_deref(), opt.precision),
OutputType::Html => output_html(
OutputType::Html =>
output_html(
results,
output_path.as_deref(),
num_threads,
Expand All @@ -575,7 +576,7 @@ fn main() {
&opt.abs_link_prefix.clone(),
opt.no_date,
opt.html_resources,
),
),
OutputType::Cobertura => output_cobertura(
source_root.as_deref(),
results,
Expand Down
94 changes: 90 additions & 4 deletions src/parser.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use flate2::read::GzDecoder;
use regex::Regex;
use serde::{Deserialize, Deserializer};
use std::cmp::Ordering;
use std::collections::{btree_map, hash_map, BTreeMap};
Expand Down Expand Up @@ -822,7 +823,88 @@ fn parse_jacoco_report_package<T: BufRead>(
.collect())
}

pub fn parse_jacoco_xml_report<T: Read>(
pub fn parse_pytest_file<T: BufRead>(
parser: &mut Reader<T>,
buf: &mut Vec<u8>,
) -> Result<CovResult, ParserError> {
let condition_re = Regex::new(r"(\d+)/(\d+)").unwrap();

let mut lines: BTreeMap<u32, u64> = BTreeMap::new();
let mut branches: BTreeMap<u32, Vec<bool>> = BTreeMap::new();

loop {
match parser.read_event_into(buf) {
Ok(Event::Start(ref e)) if e.local_name().into_inner() == b"line" => {
let (mut lineno, mut hit, mut condition_coverage) = (None, None, None);
for a in e.attributes() {
let a = a?;
match a.key.into_inner() {
b"number" => lineno = Some(Decoder {}.decode(&a.value)?.parse::<u32>()?),
b"hits" => hit = Some(Decoder {}.decode(&a.value)?.parse::<u64>()?),
b"condition-coverage" => {
condition_coverage = String::from_utf8(a.value.to_vec()).ok()
}
_ => {}
}
}

if let (Some(lineno), Some(hit)) = (lineno, hit) {
lines.insert(lineno, hit);
}

if let (Some(lineno), Some(condition_coverage)) = (lineno, condition_coverage) {
if let Some(captures) = condition_re.captures(&condition_coverage) {
if let (Some(covered), Some(total)) = (captures.get(1), captures.get(2)) {
if let (Ok(covered), Ok(total)) = (
covered.as_str().parse::<u32>(),
total.as_str().parse::<u32>(),
) {
let mut brs: Vec<bool> = vec![false; total as usize];
for i in 0..covered {
brs[i as usize] = true;
}

branches.insert(lineno, brs);
}
}
}
}
}
Ok(Event::End(ref e)) if e.local_name().into_inner() == b"class" => break,
Err(e) => return Err(ParserError::Parse(e.to_string())),
_ => {}
}
}

Ok(CovResult { lines, branches, functions: FxHashMap::default() })
}

pub fn parse_pytest_report_packages<T: BufRead>(
parser: &mut Reader<T>,
buf: &mut Vec<u8>,
) -> Result<Vec<(String, CovResult)>, ParserError> {
let mut results_map: FxHashMap<String, CovResult> = FxHashMap::default();
loop {
match parser.read_event_into(buf) {
Ok(Event::Start(ref e)) if e.local_name().into_inner() == b"class" => {
let file = get_xml_attribute(parser, e, "filename")?;
let result = parse_pytest_file(parser, buf)?;

results_map.insert(
file.to_string(),
result,
);
}
Ok(Event::End(ref e)) if e.local_name().into_inner() == b"packages" => break,
Err(e) => return Err(ParserError::Parse(e.to_string())),
_ => {}
}
}

Ok(results_map.into_iter().collect())
}

pub fn parse_xml_report<T: Read>(
xml_reader: BufReader<T>,
) -> Result<Vec<(String, CovResult)>, ParserError> {
let mut parser = Reader::from_reader(xml_reader);
Expand All @@ -835,6 +917,10 @@ pub fn parse_jacoco_xml_report<T: Read>(

loop {
match parser.read_event_into(&mut buf) {
Ok(Event::Start(ref e)) if e.local_name().into_inner() == b"packages" => {
let mut package_results = parse_pytest_report_packages(&mut parser, &mut buf)?;
results.append(&mut package_results);
}
Ok(Event::Start(ref e)) if e.local_name().into_inner() == b"package" => {
let package = get_xml_attribute(&parser, e, "name")?;
let mut package_results =
Expand Down Expand Up @@ -2032,7 +2118,7 @@ TN:http_3a_2f_2fweb_2dplatform_2etest_3a8000_2freferrer_2dpolicy_2fgen_2fsrcdoc_

let f = File::open("./test/jacoco/basic-report.xml").expect("Failed to open xml file");
let file = BufReader::new(&f);
let results = parse_jacoco_xml_report(file).unwrap();
let results = parse_xml_report(file).unwrap();

assert_eq!(results, expected);
}
Expand Down Expand Up @@ -2079,7 +2165,7 @@ TN:http_3a_2f_2fweb_2dplatform_2etest_3a8000_2freferrer_2dpolicy_2fgen_2fsrcdoc_

let f = File::open("./test/jacoco/inner-classes.xml").expect("Failed to open xml file");
let file = BufReader::new(&f);
let results = parse_jacoco_xml_report(file).unwrap();
let results = parse_xml_report(file).unwrap();

assert_eq!(results, expected);
}
Expand Down Expand Up @@ -2154,7 +2240,7 @@ TN:http_3a_2f_2fweb_2dplatform_2etest_3a8000_2freferrer_2dpolicy_2fgen_2fsrcdoc_
let f =
File::open("./test/jacoco/kotlin-jacoco-report.xml").expect("Failed to open xml file");
let file = BufReader::new(&f);
let results = parse_jacoco_xml_report(file).unwrap();
let results = parse_xml_report(file).unwrap();
assert_eq!(results, expected);
}
}
4 changes: 2 additions & 2 deletions src/path_rewriting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ fn map_partial_path(file_to_paths: &FxHashMap<String, Vec<PathBuf>>, path: PathB

let mut result: Option<&PathBuf> = None;
for option in options {
if option.ends_with(&path) {
if option.ends_with(&path) && !option.ends_with("__init__.py") {
if result.is_some() {
error!("Only one file in the repository should end with {} ({} and {} both end with that).",
path.display(),
Expand Down Expand Up @@ -227,7 +227,7 @@ fn to_globset(dirs: &[impl AsRef<str>]) -> GlobSet {
glob_builder.build().unwrap()
}

const PARTIAL_PATH_EXTENSION: &[&str] = &["java", "kt"];
const PARTIAL_PATH_EXTENSION: &[&str] = &["java", "kt", "py"];

pub fn rewrite_paths(
result_map: CovResultMap,
Expand Down
39 changes: 21 additions & 18 deletions src/producer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ impl Archive {
}
}
"xml" => {
if Archive::check_file(file, &Archive::is_jacoco) {
if Archive::check_file(file, &Archive::is_jacoco_or_pytest_cov) {
let filename = clean_path(path);
self.insert_vec(filename, xmls);
}
Expand All @@ -119,11 +119,11 @@ impl Archive {
&& (&bytes[5..] == b"204" || &bytes[5..] == b"804")
}

fn is_jacoco(reader: &mut dyn Read) -> bool {
fn is_jacoco_or_pytest_cov(reader: &mut dyn Read) -> bool {
let mut bytes: [u8; 256] = [0; 256];
if reader.read_exact(&mut bytes).is_ok() {
return match String::from_utf8(bytes.to_vec()) {
Ok(s) => s.contains("-//JACOCO//DTD"),
Ok(s) => s.contains("-//JACOCO//DTD") || s.contains("<coverage"),
Err(_) => false,
};
}
Expand Down Expand Up @@ -574,7 +574,7 @@ pub fn producer(
);

file_content_producer(&infos.into_inner(), sender, ItemFormat::Info);
file_content_producer(&xmls.into_inner(), sender, ItemFormat::JacocoXml);
file_content_producer(&xmls.into_inner(), sender, ItemFormat::Xml);
llvm_format_producer(
tmp_dir,
&profdatas.into_inner(),
Expand Down Expand Up @@ -753,31 +753,31 @@ mod tests {
(ItemFormat::Gcno, false, "llvm/file_branch", true),
(ItemFormat::Gcno, false, "llvm/reader", true),
(
ItemFormat::JacocoXml,
ItemFormat::Xml,
false,
"jacoco/basic-jacoco.xml",
false,
),
(
ItemFormat::JacocoXml,
ItemFormat::Xml,
false,
"jacoco/inner-classes.xml",
false,
),
(
ItemFormat::JacocoXml,
ItemFormat::Xml,
false,
"jacoco/multiple-top-level-classes.xml",
false,
),
(
ItemFormat::JacocoXml,
ItemFormat::Xml,
false,
"jacoco/full-junit4-report-multiple-top-level-classes.xml",
false,
),
(
ItemFormat::JacocoXml,
ItemFormat::Xml,
false,
"jacoco/kotlin-jacoco-report.xml",
false,
Expand All @@ -789,6 +789,9 @@ mod tests {
"mozillavpn_serverconnection_1.gcno",
true,
),
(
ItemFormat::Xml, false, "pycov/report.xml", false,
)
];

check_produced(tmp_path, &receiver, expected);
Expand Down Expand Up @@ -1169,12 +1172,12 @@ mod tests {

let expected = vec![
(
ItemFormat::JacocoXml,
ItemFormat::Xml,
false,
"jacoco/basic-jacoco.xml",
true,
),
(ItemFormat::JacocoXml, false, "inner-classes.xml", true),
(ItemFormat::Xml, false, "inner-classes.xml", true),
];

check_produced(tmp_path, &receiver, expected);
Expand Down Expand Up @@ -1202,12 +1205,12 @@ mod tests {

let expected = vec![
(
ItemFormat::JacocoXml,
ItemFormat::Xml,
false,
"jacoco/basic-jacoco.xml",
true,
),
(ItemFormat::JacocoXml, false, "inner-classes.xml", true),
(ItemFormat::Xml, false, "inner-classes.xml", true),
(ItemFormat::Info, false, "1494603967-2977-2_0.info", true),
(ItemFormat::Info, false, "1494603967-2977-3_0.info", true),
(ItemFormat::Info, false, "1494603967-2977-4_0.info", true),
Expand Down Expand Up @@ -1649,28 +1652,28 @@ mod tests {
fn test_jacoco_files() {
let mut file = File::open("./test/jacoco/basic-report.xml").ok();
assert!(
Archive::check_file(file.as_mut(), &Archive::is_jacoco),
Archive::check_file(file.as_mut(), &Archive::is_jacoco_or_pytest_cov),
"A Jacoco XML file expected"
);
let mut file =
File::open("./test/jacoco/full-junit4-report-multiple-top-level-classes.xml").ok();
assert!(
Archive::check_file(file.as_mut(), &Archive::is_jacoco),
Archive::check_file(file.as_mut(), &Archive::is_jacoco_or_pytest_cov),
"A Jacoco XML file expected"
);
let mut file = File::open("./test/jacoco/inner-classes.xml").ok();
assert!(
Archive::check_file(file.as_mut(), &Archive::is_jacoco),
Archive::check_file(file.as_mut(), &Archive::is_jacoco_or_pytest_cov),
"A Jacoco XML file expected"
);
let mut file = File::open("./test/jacoco/multiple-top-level-classes.xml").ok();
assert!(
Archive::check_file(file.as_mut(), &Archive::is_jacoco),
Archive::check_file(file.as_mut(), &Archive::is_jacoco_or_pytest_cov),
"A Jacoco XML file expected"
);
let mut file = File::open("./test/jacoco/not_jacoco_file.xml").ok();
assert!(
!Archive::check_file(file.as_mut(), &Archive::is_jacoco),
!Archive::check_file(file.as_mut(), &Archive::is_jacoco_or_pytest_cov),
"Not a Jacoco XML file expected"
);
}
Expand Down
Loading
Loading