Skip to content

Commit dbc39c3

Browse files
oskirbymarco-c
andauthored
Handle floats gracefully when expecting u64 in parser.rs (#803)
* 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]>
1 parent 9249866 commit dbc39c3

File tree

5 files changed

+214
-1
lines changed

5 files changed

+214
-1
lines changed

src/parser.rs

Lines changed: 208 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use flate2::read::GzDecoder;
2-
use serde::Deserialize;
2+
use serde::{Deserialize, Deserializer};
33
use std::cmp::Ordering;
44
use std::collections::{btree_map, hash_map, BTreeMap};
55
use std::fmt;
@@ -341,6 +341,7 @@ struct GcovFile {
341341
struct GcovLine {
342342
line_number: u32,
343343
function_name: Option<String>,
344+
#[serde(deserialize_with = "deserialize_counter")]
344345
count: u64,
345346
unexecuted_block: bool,
346347
branches: Vec<GcovBr>,
@@ -349,6 +350,7 @@ struct GcovLine {
349350
#[derive(Debug, Deserialize)]
350351
#[allow(dead_code)]
351352
struct GcovBr {
353+
#[serde(deserialize_with = "deserialize_counter")]
352354
count: u64,
353355
throw: bool,
354356
fallthrough: bool,
@@ -365,9 +367,33 @@ struct GcovFunction {
365367
end_column: u32,
366368
blocks: u32,
367369
blocks_executed: u32,
370+
#[serde(deserialize_with = "deserialize_counter")]
368371
execution_count: u64,
369372
}
370373

374+
// JSON sometimes surprises us with floats where we expected integers, use
375+
// a custom deserializer to ensure all the counters are converted to u64.
376+
pub fn deserialize_counter<'de, D>(deserializer: D) -> Result<u64, D::Error>
377+
where
378+
D: Deserializer<'de>,
379+
{
380+
let n: serde_json::Number = Deserialize::deserialize(deserializer)?;
381+
if n.is_f64() {
382+
let value: f64 = n.as_f64().unwrap();
383+
if (value >= 0.0) && (value <= u64::MAX as f64) {
384+
return Ok(value as u64);
385+
}
386+
}
387+
388+
match n.as_u64() {
389+
Some(value) => Ok(value),
390+
None => Err(serde::de::Error::custom(format!(
391+
"Unable to parse u64 from {}",
392+
n
393+
))),
394+
}
395+
}
396+
371397
pub fn parse_gcov_gz(gcov_path: &Path) -> Result<Vec<(String, CovResult)>, ParserError> {
372398
let f = File::open(&gcov_path)
373399
.unwrap_or_else(|_| panic!("Failed to open gcov file {}", gcov_path.display()));
@@ -1717,6 +1743,187 @@ mod tests {
17171743
assert!(func.executed);
17181744
}
17191745

1746+
#[test]
1747+
fn test_parser_gcov_gz() {
1748+
let results = parse_gcov_gz(Path::new(
1749+
"./test/mozillavpn_serverconnection.gcno.gcov.json.gz",
1750+
))
1751+
.unwrap();
1752+
assert_eq!(results.len(), 37);
1753+
let (ref source_name, ref result) = results[0];
1754+
1755+
assert_eq!(source_name, "server/serverconnection.cpp");
1756+
1757+
assert_eq!(
1758+
result.lines,
1759+
[
1760+
(32, 0),
1761+
(33, 0),
1762+
(35, 0),
1763+
(36, 0),
1764+
(37, 0),
1765+
(38, 0),
1766+
(40, 0),
1767+
(41, 0),
1768+
(42, 0),
1769+
(43, 0),
1770+
(44, 0),
1771+
(45, 0),
1772+
(46, 0),
1773+
(48, 0),
1774+
(49, 0),
1775+
(50, 0),
1776+
(51, 0),
1777+
(52, 0),
1778+
(55, 0),
1779+
(56, 0),
1780+
(57, 0),
1781+
(58, 0),
1782+
(59, 0),
1783+
(61, 0),
1784+
(62, 0),
1785+
(63, 0),
1786+
(66, 0),
1787+
(67, 0),
1788+
(68, 0),
1789+
(71, 0),
1790+
(74, 0),
1791+
(75, 0),
1792+
(78, 0),
1793+
(79, 0),
1794+
(82, 0),
1795+
(83, 0),
1796+
(85, 0),
1797+
(86, 0),
1798+
(87, 0),
1799+
(88, 0),
1800+
(90, 0),
1801+
(91, 0),
1802+
(94, 0),
1803+
(95, 0),
1804+
(96, 0),
1805+
(97, 0),
1806+
(101, 0),
1807+
(102, 0),
1808+
(103, 0),
1809+
(104, 0),
1810+
(107, 0),
1811+
(112, 0),
1812+
(113, 0),
1813+
(114, 0),
1814+
(118, 0),
1815+
(119, 0),
1816+
(120, 0),
1817+
(124, 0),
1818+
(125, 0),
1819+
(126, 0),
1820+
(129, 0),
1821+
(130, 0),
1822+
(131, 0),
1823+
(135, 0),
1824+
(136, 0),
1825+
(137, 0),
1826+
(138, 0),
1827+
(139, 0),
1828+
(142, 0),
1829+
(143, 0),
1830+
(144, 0),
1831+
(148, 0),
1832+
(149, 0),
1833+
(150, 0),
1834+
(151, 0),
1835+
(157, 0),
1836+
(158, 0),
1837+
(159, 0),
1838+
(164, 0),
1839+
(169, 0),
1840+
(171, 0),
1841+
(172, 0),
1842+
(175, 0),
1843+
(176, 0),
1844+
(178, 0),
1845+
(179, 0),
1846+
(181, 0),
1847+
(183, 0),
1848+
(184, 0),
1849+
(185, 0),
1850+
(186, 0),
1851+
(188, 0),
1852+
(189, 0),
1853+
(190, 0),
1854+
(193, 0),
1855+
(194, 0),
1856+
(195, 0),
1857+
(196, 0),
1858+
(199, 0),
1859+
(200, 0),
1860+
(202, 0),
1861+
(203, 0),
1862+
(205, 0),
1863+
(206, 0),
1864+
(207, 0),
1865+
(210, 0),
1866+
(216, 0),
1867+
(217, 0),
1868+
(220, 0),
1869+
(221, 0),
1870+
(223, 0),
1871+
(225, 0),
1872+
(226, 0),
1873+
(227, 0),
1874+
(230, 0),
1875+
(231, 0),
1876+
(234, 0),
1877+
(237, 0),
1878+
(238, 0),
1879+
(239, 0),
1880+
(241, 0),
1881+
(242, 0),
1882+
(243, 0),
1883+
(245, 0),
1884+
(247, 0),
1885+
(248, 0),
1886+
(249, 0),
1887+
(251, 0),
1888+
(252, 0),
1889+
(254, 0),
1890+
(255, 0),
1891+
(256, 0),
1892+
(257, 0),
1893+
(258, 0),
1894+
(260, 0),
1895+
(261, 0),
1896+
(262, 0),
1897+
(263, 0),
1898+
(264, 0),
1899+
(267, 0),
1900+
(268, 0),
1901+
(270, 0),
1902+
(271, 0),
1903+
(272, 0),
1904+
(273, 0),
1905+
(274, 0),
1906+
(275, 0),
1907+
(279, 0)
1908+
]
1909+
.iter()
1910+
.cloned()
1911+
.collect()
1912+
);
1913+
1914+
assert_eq!(result.branches, [].iter().cloned().collect());
1915+
1916+
assert!(result
1917+
.functions
1918+
.contains_key("ServerConnection::readData()"));
1919+
let func = result
1920+
.functions
1921+
.get("ServerConnection::readData()")
1922+
.unwrap();
1923+
assert_eq!(func.start, 188);
1924+
assert!(!func.executed);
1925+
}
1926+
17201927
#[test]
17211928
fn test_parser_jacoco_xml_basic() {
17221929
let mut lines: BTreeMap<u32, u64> = BTreeMap::new();

src/producer.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,12 @@ mod tests {
747747
false,
748748
),
749749
(ItemFormat::Profraw, true, "default_1.profraw", false),
750+
(
751+
ItemFormat::Gcno,
752+
true,
753+
"mozillavpn_serverconnection_1.gcno",
754+
true,
755+
),
750756
];
751757

752758
check_produced(tmp_path, &receiver, expected);

test/mozillavpn_serverconnection.gcda

26 KB
Binary file not shown.

test/mozillavpn_serverconnection.gcno

291 KB
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)