Skip to content

Commit

Permalink
Handle floats gracefully when expecting u64 in parser.rs (#803)
Browse files Browse the repository at this point in the history
* Handle floats gracefully when expecting u64 in parser.rs

* Fix linter issues in parser.rs

* Add test gcda files from mozillavpn project

* More linter fixes.

* Test parsing gcov json file with floating point values

The gcov json file comes from gcov 9

Co-authored-by: Marco Castelluccio <[email protected]>
  • Loading branch information
oskirby and marco-c authored Apr 1, 2022
1 parent 9249866 commit dbc39c3
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 1 deletion.
209 changes: 208 additions & 1 deletion src/parser.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use flate2::read::GzDecoder;
use serde::Deserialize;
use serde::{Deserialize, Deserializer};
use std::cmp::Ordering;
use std::collections::{btree_map, hash_map, BTreeMap};
use std::fmt;
Expand Down Expand Up @@ -341,6 +341,7 @@ struct GcovFile {
struct GcovLine {
line_number: u32,
function_name: Option<String>,
#[serde(deserialize_with = "deserialize_counter")]
count: u64,
unexecuted_block: bool,
branches: Vec<GcovBr>,
Expand All @@ -349,6 +350,7 @@ struct GcovLine {
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
struct GcovBr {
#[serde(deserialize_with = "deserialize_counter")]
count: u64,
throw: bool,
fallthrough: bool,
Expand All @@ -365,9 +367,33 @@ struct GcovFunction {
end_column: u32,
blocks: u32,
blocks_executed: u32,
#[serde(deserialize_with = "deserialize_counter")]
execution_count: u64,
}

// JSON sometimes surprises us with floats where we expected integers, use
// a custom deserializer to ensure all the counters are converted to u64.
pub fn deserialize_counter<'de, D>(deserializer: D) -> Result<u64, D::Error>
where
D: Deserializer<'de>,
{
let n: serde_json::Number = Deserialize::deserialize(deserializer)?;
if n.is_f64() {
let value: f64 = n.as_f64().unwrap();
if (value >= 0.0) && (value <= u64::MAX as f64) {
return Ok(value as u64);
}
}

match n.as_u64() {
Some(value) => Ok(value),
None => Err(serde::de::Error::custom(format!(
"Unable to parse u64 from {}",
n
))),
}
}

pub fn parse_gcov_gz(gcov_path: &Path) -> Result<Vec<(String, CovResult)>, ParserError> {
let f = File::open(&gcov_path)
.unwrap_or_else(|_| panic!("Failed to open gcov file {}", gcov_path.display()));
Expand Down Expand Up @@ -1717,6 +1743,187 @@ mod tests {
assert!(func.executed);
}

#[test]
fn test_parser_gcov_gz() {
let results = parse_gcov_gz(Path::new(
"./test/mozillavpn_serverconnection.gcno.gcov.json.gz",
))
.unwrap();
assert_eq!(results.len(), 37);
let (ref source_name, ref result) = results[0];

assert_eq!(source_name, "server/serverconnection.cpp");

assert_eq!(
result.lines,
[
(32, 0),
(33, 0),
(35, 0),
(36, 0),
(37, 0),
(38, 0),
(40, 0),
(41, 0),
(42, 0),
(43, 0),
(44, 0),
(45, 0),
(46, 0),
(48, 0),
(49, 0),
(50, 0),
(51, 0),
(52, 0),
(55, 0),
(56, 0),
(57, 0),
(58, 0),
(59, 0),
(61, 0),
(62, 0),
(63, 0),
(66, 0),
(67, 0),
(68, 0),
(71, 0),
(74, 0),
(75, 0),
(78, 0),
(79, 0),
(82, 0),
(83, 0),
(85, 0),
(86, 0),
(87, 0),
(88, 0),
(90, 0),
(91, 0),
(94, 0),
(95, 0),
(96, 0),
(97, 0),
(101, 0),
(102, 0),
(103, 0),
(104, 0),
(107, 0),
(112, 0),
(113, 0),
(114, 0),
(118, 0),
(119, 0),
(120, 0),
(124, 0),
(125, 0),
(126, 0),
(129, 0),
(130, 0),
(131, 0),
(135, 0),
(136, 0),
(137, 0),
(138, 0),
(139, 0),
(142, 0),
(143, 0),
(144, 0),
(148, 0),
(149, 0),
(150, 0),
(151, 0),
(157, 0),
(158, 0),
(159, 0),
(164, 0),
(169, 0),
(171, 0),
(172, 0),
(175, 0),
(176, 0),
(178, 0),
(179, 0),
(181, 0),
(183, 0),
(184, 0),
(185, 0),
(186, 0),
(188, 0),
(189, 0),
(190, 0),
(193, 0),
(194, 0),
(195, 0),
(196, 0),
(199, 0),
(200, 0),
(202, 0),
(203, 0),
(205, 0),
(206, 0),
(207, 0),
(210, 0),
(216, 0),
(217, 0),
(220, 0),
(221, 0),
(223, 0),
(225, 0),
(226, 0),
(227, 0),
(230, 0),
(231, 0),
(234, 0),
(237, 0),
(238, 0),
(239, 0),
(241, 0),
(242, 0),
(243, 0),
(245, 0),
(247, 0),
(248, 0),
(249, 0),
(251, 0),
(252, 0),
(254, 0),
(255, 0),
(256, 0),
(257, 0),
(258, 0),
(260, 0),
(261, 0),
(262, 0),
(263, 0),
(264, 0),
(267, 0),
(268, 0),
(270, 0),
(271, 0),
(272, 0),
(273, 0),
(274, 0),
(275, 0),
(279, 0)
]
.iter()
.cloned()
.collect()
);

assert_eq!(result.branches, [].iter().cloned().collect());

assert!(result
.functions
.contains_key("ServerConnection::readData()"));
let func = result
.functions
.get("ServerConnection::readData()")
.unwrap();
assert_eq!(func.start, 188);
assert!(!func.executed);
}

#[test]
fn test_parser_jacoco_xml_basic() {
let mut lines: BTreeMap<u32, u64> = BTreeMap::new();
Expand Down
6 changes: 6 additions & 0 deletions src/producer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,12 @@ mod tests {
false,
),
(ItemFormat::Profraw, true, "default_1.profraw", false),
(
ItemFormat::Gcno,
true,
"mozillavpn_serverconnection_1.gcno",
true,
),
];

check_produced(tmp_path, &receiver, expected);
Expand Down
Binary file added test/mozillavpn_serverconnection.gcda
Binary file not shown.
Binary file added test/mozillavpn_serverconnection.gcno
Binary file not shown.
Binary file not shown.

0 comments on commit dbc39c3

Please sign in to comment.