From 93fd2f2398e49f92a28cf493687274c5f4f067c4 Mon Sep 17 00:00:00 2001 From: Hayley Call Date: Thu, 16 Dec 2021 12:46:05 -0800 Subject: [PATCH 01/10] function to convert source format to cobertura xml, column removed from source format and corresponding tests and uses, todo: fix cobertura test --- src/agent/coverage/src/block.rs | 2 +- src/agent/coverage/src/cobertura.rs | 252 ++++++++++++++++++++++++++ src/agent/coverage/src/lib.rs | 1 + src/agent/coverage/src/source.rs | 266 ++-------------------------- 4 files changed, 264 insertions(+), 257 deletions(-) create mode 100644 src/agent/coverage/src/cobertura.rs diff --git a/src/agent/coverage/src/block.rs b/src/agent/coverage/src/block.rs index ddba3ed6d9..03216eea77 100644 --- a/src/agent/coverage/src/block.rs +++ b/src/agent/coverage/src/block.rs @@ -155,7 +155,7 @@ impl CommandBlockCov { for (line, count) in lines { // Valid lines are always 1-indexed. if line > 0 { - let location = Location::new(line, None, count)?; + let location = Location::new(line, count)?; locations.push(location) } } diff --git a/src/agent/coverage/src/cobertura.rs b/src/agent/coverage/src/cobertura.rs new file mode 100644 index 0000000000..b14c67283e --- /dev/null +++ b/src/agent/coverage/src/cobertura.rs @@ -0,0 +1,252 @@ +use std::time::{SystemTime, UNIX_EPOCH}; +use xml::writer::{EmitterConfig, XmlEvent}; +use anyhow::Context; +use anyhow::Error; +use crate::source::SourceCoverage; +use crate::source::SourceFileCoverage; +use crate::source::SourceCoverageLocation; + + +pub fn cobertura(source_coverage:SourceCoverage) -> Result { + + let mut backing: Vec = Vec::new(); + let mut emitter = EmitterConfig::new() + .perform_indent(true) + .create_writer(&mut backing); + + let unixtime = SystemTime::now() + .duration_since(UNIX_EPOCH) + .context("system time before unix epoch")? + .as_secs(); + + emitter.write( + XmlEvent::start_element("coverage") + .attr("lines-valid","0") + .attr("lines-covered","0") + .attr("line-rate","0") + .attr("branches-valid", "0") + .attr("branches-covered", "0") + .attr("branch-rate", "0") + .attr("timestamp", &format!("{}", unixtime)) + .attr("complexity", "0") + .attr("version", "0.1"), + )?; + emitter.write(XmlEvent::start_element("sources"))?; + emitter.write(XmlEvent::start_element("source"))?; + emitter.write(XmlEvent::characters(""))?; + emitter.write(XmlEvent::end_element())?; // source + emitter.write(XmlEvent::end_element())?; // sources + + emitter.write(XmlEvent::start_element("packages"))?; + emitter.write( + XmlEvent::start_element("package") + .attr("name", "0") + .attr("lines-valid","0") + .attr("lines-covered","0") + .attr("line-rate","0") + .attr("branches-valid", "0") + .attr("branches-covered", "0") + .attr("branch-rate", "0") + .attr("complexity", "0"), + )?; + emitter.write(XmlEvent::start_element("classes"))?; + // loop through files + let files: Vec = source_coverage.files; + for file in files { + emitter.write( + XmlEvent::start_element("class") + .attr("name", "0") + .attr("filename", &file.file) + .attr("lines-valid","0") + .attr("lines-covered","0") + .attr("line-rate","0") + .attr("branches-valid", "0") + .attr("branches-covered", "0") + .attr("branch-rate", "0") + .attr("complexity", "0"), + )?; + // emitter.write(XmlEvent::start_element("methods"))?; + // emitter.write( + // XmlEvent::start_element("method") + // .attr("name", "0") + // .attr("signature", "0") + // .attr("lines-valid","0") + // .attr("lines-covered","0") + // .attr("line-rate","0") + // .attr("branches-valid", "0") + // .attr("branches-covered", "0") + // .attr("branch-rate", "0") + // .attr("complexity", "0"), + // )?; + let locations: Vec = file.locations; + emitter.write(XmlEvent::start_element("lines"))?; + for location in locations { + emitter.write( + XmlEvent::start_element("line") + .attr("number", &location.line.to_string()) + .attr("hits", &location.count.to_string()) + .attr("branch", "false"), + )?; + } + emitter.write(XmlEvent::end_element())?; // line + + emitter.write(XmlEvent::end_element())?; // lines + // emitter.write(XmlEvent::end_element())?; // method + // emitter.write(XmlEvent::end_element())?; // methods + emitter.write(XmlEvent::end_element())?; // class + } + + emitter.write(XmlEvent::end_element())?; // classes + emitter.write(XmlEvent::end_element())?; // package + + emitter.write(XmlEvent::end_element())?; // packages + emitter.write(XmlEvent::end_element())?; // coverage + + + + Ok(String::from_utf8(backing)?) + +} + + + +// #[cfg(test)] + +// mod tests { +// use super::*; +// use anyhow::Result; + +// #[test] +// fn test_source_to_cobertura() -> Result<()> { + +// let sourceCoverageLocation1 = SourceCoverageLocation { +// line:10, +// count:0 +// }; +// let sourceCoverageLocation2 = SourceCoverageLocation { +// line:5, +// count:3 +// }; + +// let coverageLocations_vec1: Vec = Vec::new(); +// coverageLocations_vec1.push(sourceCoverageLocation1); +// coverageLocations_vec1.push(sourceCoverageLocation2); + +// let coverageLocations_vec2: Vec = Vec::new(); +// coverageLocations_vec2.push(sourceCoverageLocation1); +// coverageLocations_vec2.push(sourceCoverageLocation2); + +// let sourceFileCoverage1 = SourceFileCoverage { +// locations:coverageLocations_vec1, +// file:"C:/Users/file1.txt".to_string() +// }; + +// let sourceFileCoverage2 = SourceFileCoverage { +// locations:coverageLocations_vec2, +// file:"C:/Users/file2.txt".to_string() +// }; + +// let fileCoverage_vec1: Vec = Vec::new(); +// fileCoverage_vec1.push(sourceFileCoverage1); +// fileCoverage_vec1.push(sourceFileCoverage2); + +// let sourceCoverage1 = SourceCoverage { +// files:fileCoverage_vec1 +// }; + +// let _result = cobertura (sourceCoverage1); + + // let mut backing_test: Vec = Vec::new(); + // let mut emitter_test = EmitterConfig::new() + // .perform_indent(true) + // .create_writer(&mut backing_test); + + // let unixtime = SystemTime::now() + // .duration_since(UNIX_EPOCH) + // .context("system time before unix epoch")? + // .as_secs(); + + // emitter_test.write( + // XmlEvent::start_element("coverage") + // .attr("lines-valid","0") + // .attr("lines-covered","0") + // .attr("line-rate","0") + // .attr("branches-valid", "0") + // .attr("branches-covered", "0") + // .attr("branch-rate", "0") + // .attr("timestamp", &format!("{}", unixtime)) + // .attr("complexity", "0") + // .attr("version", "0.1"), + // )?; + // emitter_test.write(XmlEvent::start_element("sources"))?; + // emitter_test.write(XmlEvent::start_element("source"))?; + // emitter_test.write(XmlEvent::characters(""))?; + // emitter_test.write(XmlEvent::end_element())?; // source + // emitter_test.write(XmlEvent::end_element())?; // sources + + // emitter_test.write(XmlEvent::start_element("packages"))?; + // emitter_test.write( + // XmlEvent::start_element("package") + // .attr("name", "0") + // .attr("lines-valid","0") + // .attr("lines-covered","0") + // .attr("line-rate","0") + // .attr("branches-valid", "0") + // .attr("branches-covered", "0") + // .attr("branch-rate", "0") + // .attr("complexity", "0"), + // )?; + // emitter_test.write(XmlEvent::start_element("classes"))?; + // emitter_test.write( + // XmlEvent::start_element("class") + // .attr("name", "0") + // .attr("filename", "C:/Users/file1.txt") + // .attr("lines-valid","0") + // .attr("lines-covered","0") + // .attr("line-rate","0") + // .attr("branches-valid", "0") + // .attr("branches-covered", "0") + // .attr("branch-rate", "0") + // .attr("complexity", "0"), + // emitter_test.write(XmlEvent::start_element("lines"))?; + // emitter_test.write( + // XmlEvent::start_element("line") + // .attr("number", "10") + // .attr("hits", "0") + // .attr("branch", "false"), + // )?; + // emitter_test.write( + // XmlEvent::start_element("class") + // .attr("name", "0") + // .attr("filename", "C:/Users/file2.txt") + // .attr("lines-valid","0") + // .attr("lines-covered","0") + // .attr("line-rate","0") + // .attr("branches-valid", "0") + // .attr("branches-covered", "0") + // .attr("branch-rate", "0") + // .attr("complexity", "0"), + // emitter_test.write(XmlEvent::start_element("lines"))?; + // emitter_test.write( + // XmlEvent::start_element("line") + // .attr("number", "5") + // .attr("hits", "3") + // .attr("branch", "false"), + // )?; + // emitter_test.write(XmlEvent::end_element())?; // line + + // emitter_test.write(XmlEvent::end_element())?; // lines + // emitter_test.write(XmlEvent::end_element())?; // class + // emitter_test.write(XmlEvent::end_element())?; // classes + // emitter_test.write(XmlEvent::end_element())?; // package + // emitter_test.write(XmlEvent::end_element())?; // packages + // emitter_test.write(XmlEvent::end_element())?; // coverage + + // assert_eq!(); + // Ok(()) + // } +// } + + + + diff --git a/src/agent/coverage/src/lib.rs b/src/agent/coverage/src/lib.rs index dbbce3494b..a94a98b53d 100644 --- a/src/agent/coverage/src/lib.rs +++ b/src/agent/coverage/src/lib.rs @@ -23,6 +23,7 @@ pub mod demangle; pub mod report; pub mod sancov; pub mod source; +pub mod cobertura; #[cfg(target_os = "linux")] pub mod disasm; diff --git a/src/agent/coverage/src/source.rs b/src/agent/coverage/src/source.rs index cf2530f29d..5aa8a111d6 100644 --- a/src/agent/coverage/src/source.rs +++ b/src/agent/coverage/src/source.rs @@ -23,27 +23,18 @@ pub struct SourceCoverageLocation { /// Line number of entry in `file` (1-indexed). pub line: u32, - /// Optional column offset (0-indexed). - /// - /// When column offsets are present, they should be interpreted as the start - /// of a span bounded by the next in-line column offset (or end-of-line). - pub column: Option, - /// Execution count at location. pub count: u32, } impl SourceCoverageLocation { - pub fn new(line: u32, column: impl Into>, count: u32) -> Result { + pub fn new(line: u32, count: u32) -> Result { if line == 0 { anyhow::bail!("source lines must be 1-indexed"); } - let column = column.into(); - Ok(Self { line, - column, count, }) } @@ -61,27 +52,25 @@ mod tests { #[test] fn test_source_coverage_location() -> Result<()> { - let valid = SourceCoverageLocation::new(5, 4, 1)?; + let valid = SourceCoverageLocation::new(5, 1)?; assert_eq!( valid, SourceCoverageLocation { line: 5, - column: Some(4), count: 1, } ); - let valid_no_col = SourceCoverageLocation::new(5, None, 1)?; + let valid_no_col = SourceCoverageLocation::new(5, 1)?; assert_eq!( valid_no_col, SourceCoverageLocation { line: 5, - column: None, count: 1, } ); - let invalid = SourceCoverageLocation::new(0, 4, 1); + let invalid = SourceCoverageLocation::new(0, 1); assert!(invalid.is_err()); Ok(()) @@ -93,17 +82,17 @@ mod tests { { "file": MAIN_C.to_owned(), "locations": [ - { "line": 4, "column": 4, "count": 1 }, - { "line": 9, "column": 4, "count": 0 }, - { "line": 12, "column": 4, "count": 1 }, + { "line": 4, "count": 1 }, + { "line": 9, "count": 0 }, + { "line": 12, "count": 1 }, ], }, { "file": COMMON_C.to_owned(), "locations": [ - { "line": 5, "column": 4, "count": 0 }, - { "line": 5, "column": 9, "count": 1 }, - { "line": 8, "column": 0, "count": 0 }, + { "line": 5, "count": 0 }, + { "line": 5, "count": 1 }, + { "line": 8, "count": 0 }, ], }, ]))?; @@ -115,17 +104,14 @@ mod tests { locations: vec![ SourceCoverageLocation { line: 4, - column: Some(4), count: 1, }, SourceCoverageLocation { line: 9, - column: Some(4), count: 0, }, SourceCoverageLocation { line: 12, - column: Some(4), count: 1, }, ], @@ -135,17 +121,14 @@ mod tests { locations: vec![ SourceCoverageLocation { line: 5, - column: Some(4), count: 0, }, SourceCoverageLocation { line: 5, - column: Some(9), count: 1, }, SourceCoverageLocation { line: 8, - column: Some(0), count: 0, }, ], @@ -213,233 +196,4 @@ mod tests { Ok(()) } - - #[test] - fn test_source_coverage_no_or_null_columns() -> Result<()> { - let text_null_cols = serde_json::to_string(&json!([ - { - "file": MAIN_C.to_owned(), - "locations": [ - { "line": 4, "column": null, "count": 1 }, - { "line": 9, "column": null, "count": 0 }, - { "line": 12, "column": null, "count": 1 }, - ], - }, - { - "file": COMMON_C.to_owned(), - "locations": [ - { "line": 5, "column": null, "count": 0 }, - { "line": 5, "column": null, "count": 1 }, - { "line": 8, "column": null, "count": 0 }, - ], - }, - ]))?; - - let text_no_cols = serde_json::to_string(&json!([ - { - "file": MAIN_C.to_owned(), - "locations": [ - { "line": 4, "count": 1 }, - { "line": 9, "count": 0 }, - { "line": 12, "count": 1 }, - ], - }, - { - "file": COMMON_C.to_owned(), - "locations": [ - { "line": 5, "count": 0 }, - { "line": 5, "count": 1 }, - { "line": 8, "count": 0 }, - ], - }, - ]))?; - - let coverage = { - let files = vec![ - SourceFileCoverage { - file: MAIN_C.to_owned(), - locations: vec![ - SourceCoverageLocation { - line: 4, - column: None, - count: 1, - }, - SourceCoverageLocation { - line: 9, - column: None, - count: 0, - }, - SourceCoverageLocation { - line: 12, - column: None, - count: 1, - }, - ], - }, - SourceFileCoverage { - file: COMMON_C.to_owned(), - locations: vec![ - SourceCoverageLocation { - line: 5, - column: None, - count: 0, - }, - SourceCoverageLocation { - line: 5, - column: None, - count: 1, - }, - SourceCoverageLocation { - line: 8, - column: None, - count: 0, - }, - ], - }, - ]; - SourceCoverage { files } - }; - - // Serialized with present `column` keys, `null` values. - let ser = serde_json::to_string(&coverage)?; - assert_eq!(ser, text_null_cols); - - // Deserializes when `column` keys are absent. - let de_no_cols: SourceCoverage = serde_json::from_str(&text_no_cols)?; - assert_eq!(de_no_cols, coverage); - - // Deserializes when `column` keys are present but `null`. - let de_null_cols: SourceCoverage = serde_json::from_str(&text_null_cols)?; - assert_eq!(de_null_cols, coverage); - - Ok(()) - } - - #[test] - fn test_source_coverage_partial_columns() -> Result<()> { - let text = serde_json::to_string(&json!([ - { - "file": MAIN_C.to_owned(), - "locations": [ - { "line": 4, "column": 4, "count": 1 }, - { "line": 9, "column": 4, "count": 0 }, - { "line": 12, "column": 4, "count": 1 }, - ], - }, - { - "file": COMMON_C.to_owned(), - "locations": [ - { "line": 5, "column": null, "count": 0 }, - { "line": 5, "column": null, "count": 1 }, - { "line": 8, "column": null, "count": 0 }, - ], - }, - ]))?; - - let coverage = { - let files = vec![ - SourceFileCoverage { - file: MAIN_C.to_owned(), - locations: vec![ - SourceCoverageLocation { - line: 4, - column: Some(4), - count: 1, - }, - SourceCoverageLocation { - line: 9, - column: Some(4), - count: 0, - }, - SourceCoverageLocation { - line: 12, - column: Some(4), - count: 1, - }, - ], - }, - SourceFileCoverage { - file: COMMON_C.to_owned(), - locations: vec![ - SourceCoverageLocation { - line: 5, - column: None, - count: 0, - }, - SourceCoverageLocation { - line: 5, - column: None, - count: 1, - }, - SourceCoverageLocation { - line: 8, - column: None, - count: 0, - }, - ], - }, - ]; - SourceCoverage { files } - }; - - let ser = serde_json::to_string(&coverage)?; - assert_eq!(ser, text); - - let de: SourceCoverage = serde_json::from_str(&text)?; - assert_eq!(de, coverage); - - Ok(()) - } - - #[test] - fn test_source_coverage_mixed_columns() -> Result<()> { - let text = serde_json::to_string(&json!([ - { - "file": MAIN_C.to_owned(), - "locations": [ - { "line": 4, "column": null, "count": 1 }, - { "line": 9, "column": 4, "count": 0 }, - { "line": 12, "column": null, "count": 1 }, - { "line": 13, "column": 7, "count": 0 }, - ], - }, - ]))?; - - let coverage = { - let files = vec![SourceFileCoverage { - file: MAIN_C.to_owned(), - locations: vec![ - SourceCoverageLocation { - line: 4, - column: None, - count: 1, - }, - SourceCoverageLocation { - line: 9, - column: Some(4), - count: 0, - }, - SourceCoverageLocation { - line: 12, - column: None, - count: 1, - }, - SourceCoverageLocation { - line: 13, - column: Some(7), - count: 0, - }, - ], - }]; - SourceCoverage { files } - }; - - let ser = serde_json::to_string(&coverage)?; - assert_eq!(ser, text); - - let de: SourceCoverage = serde_json::from_str(&text)?; - assert_eq!(de, coverage); - - Ok(()) - } } From fc23afce3a340d2eeb62f7959d67647560e780c6 Mon Sep 17 00:00:00 2001 From: Hayley Call Date: Thu, 16 Dec 2021 15:43:06 -0800 Subject: [PATCH 02/10] Fixed test for source to cobertura xml function --- src/agent/coverage/src/cobertura.rs | 497 ++++++++++++++-------------- 1 file changed, 245 insertions(+), 252 deletions(-) diff --git a/src/agent/coverage/src/cobertura.rs b/src/agent/coverage/src/cobertura.rs index b14c67283e..14d405234e 100644 --- a/src/agent/coverage/src/cobertura.rs +++ b/src/agent/coverage/src/cobertura.rs @@ -1,252 +1,245 @@ -use std::time::{SystemTime, UNIX_EPOCH}; -use xml::writer::{EmitterConfig, XmlEvent}; -use anyhow::Context; -use anyhow::Error; -use crate::source::SourceCoverage; -use crate::source::SourceFileCoverage; -use crate::source::SourceCoverageLocation; - - -pub fn cobertura(source_coverage:SourceCoverage) -> Result { - - let mut backing: Vec = Vec::new(); - let mut emitter = EmitterConfig::new() - .perform_indent(true) - .create_writer(&mut backing); - - let unixtime = SystemTime::now() - .duration_since(UNIX_EPOCH) - .context("system time before unix epoch")? - .as_secs(); - - emitter.write( - XmlEvent::start_element("coverage") - .attr("lines-valid","0") - .attr("lines-covered","0") - .attr("line-rate","0") - .attr("branches-valid", "0") - .attr("branches-covered", "0") - .attr("branch-rate", "0") - .attr("timestamp", &format!("{}", unixtime)) - .attr("complexity", "0") - .attr("version", "0.1"), - )?; - emitter.write(XmlEvent::start_element("sources"))?; - emitter.write(XmlEvent::start_element("source"))?; - emitter.write(XmlEvent::characters(""))?; - emitter.write(XmlEvent::end_element())?; // source - emitter.write(XmlEvent::end_element())?; // sources - - emitter.write(XmlEvent::start_element("packages"))?; - emitter.write( - XmlEvent::start_element("package") - .attr("name", "0") - .attr("lines-valid","0") - .attr("lines-covered","0") - .attr("line-rate","0") - .attr("branches-valid", "0") - .attr("branches-covered", "0") - .attr("branch-rate", "0") - .attr("complexity", "0"), - )?; - emitter.write(XmlEvent::start_element("classes"))?; - // loop through files - let files: Vec = source_coverage.files; - for file in files { - emitter.write( - XmlEvent::start_element("class") - .attr("name", "0") - .attr("filename", &file.file) - .attr("lines-valid","0") - .attr("lines-covered","0") - .attr("line-rate","0") - .attr("branches-valid", "0") - .attr("branches-covered", "0") - .attr("branch-rate", "0") - .attr("complexity", "0"), - )?; - // emitter.write(XmlEvent::start_element("methods"))?; - // emitter.write( - // XmlEvent::start_element("method") - // .attr("name", "0") - // .attr("signature", "0") - // .attr("lines-valid","0") - // .attr("lines-covered","0") - // .attr("line-rate","0") - // .attr("branches-valid", "0") - // .attr("branches-covered", "0") - // .attr("branch-rate", "0") - // .attr("complexity", "0"), - // )?; - let locations: Vec = file.locations; - emitter.write(XmlEvent::start_element("lines"))?; - for location in locations { - emitter.write( - XmlEvent::start_element("line") - .attr("number", &location.line.to_string()) - .attr("hits", &location.count.to_string()) - .attr("branch", "false"), - )?; - } - emitter.write(XmlEvent::end_element())?; // line - - emitter.write(XmlEvent::end_element())?; // lines - // emitter.write(XmlEvent::end_element())?; // method - // emitter.write(XmlEvent::end_element())?; // methods - emitter.write(XmlEvent::end_element())?; // class - } - - emitter.write(XmlEvent::end_element())?; // classes - emitter.write(XmlEvent::end_element())?; // package - - emitter.write(XmlEvent::end_element())?; // packages - emitter.write(XmlEvent::end_element())?; // coverage - - - - Ok(String::from_utf8(backing)?) - -} - - - -// #[cfg(test)] - -// mod tests { -// use super::*; -// use anyhow::Result; - -// #[test] -// fn test_source_to_cobertura() -> Result<()> { - -// let sourceCoverageLocation1 = SourceCoverageLocation { -// line:10, -// count:0 -// }; -// let sourceCoverageLocation2 = SourceCoverageLocation { -// line:5, -// count:3 -// }; - -// let coverageLocations_vec1: Vec = Vec::new(); -// coverageLocations_vec1.push(sourceCoverageLocation1); -// coverageLocations_vec1.push(sourceCoverageLocation2); - -// let coverageLocations_vec2: Vec = Vec::new(); -// coverageLocations_vec2.push(sourceCoverageLocation1); -// coverageLocations_vec2.push(sourceCoverageLocation2); - -// let sourceFileCoverage1 = SourceFileCoverage { -// locations:coverageLocations_vec1, -// file:"C:/Users/file1.txt".to_string() -// }; - -// let sourceFileCoverage2 = SourceFileCoverage { -// locations:coverageLocations_vec2, -// file:"C:/Users/file2.txt".to_string() -// }; - -// let fileCoverage_vec1: Vec = Vec::new(); -// fileCoverage_vec1.push(sourceFileCoverage1); -// fileCoverage_vec1.push(sourceFileCoverage2); - -// let sourceCoverage1 = SourceCoverage { -// files:fileCoverage_vec1 -// }; - -// let _result = cobertura (sourceCoverage1); - - // let mut backing_test: Vec = Vec::new(); - // let mut emitter_test = EmitterConfig::new() - // .perform_indent(true) - // .create_writer(&mut backing_test); - - // let unixtime = SystemTime::now() - // .duration_since(UNIX_EPOCH) - // .context("system time before unix epoch")? - // .as_secs(); - - // emitter_test.write( - // XmlEvent::start_element("coverage") - // .attr("lines-valid","0") - // .attr("lines-covered","0") - // .attr("line-rate","0") - // .attr("branches-valid", "0") - // .attr("branches-covered", "0") - // .attr("branch-rate", "0") - // .attr("timestamp", &format!("{}", unixtime)) - // .attr("complexity", "0") - // .attr("version", "0.1"), - // )?; - // emitter_test.write(XmlEvent::start_element("sources"))?; - // emitter_test.write(XmlEvent::start_element("source"))?; - // emitter_test.write(XmlEvent::characters(""))?; - // emitter_test.write(XmlEvent::end_element())?; // source - // emitter_test.write(XmlEvent::end_element())?; // sources - - // emitter_test.write(XmlEvent::start_element("packages"))?; - // emitter_test.write( - // XmlEvent::start_element("package") - // .attr("name", "0") - // .attr("lines-valid","0") - // .attr("lines-covered","0") - // .attr("line-rate","0") - // .attr("branches-valid", "0") - // .attr("branches-covered", "0") - // .attr("branch-rate", "0") - // .attr("complexity", "0"), - // )?; - // emitter_test.write(XmlEvent::start_element("classes"))?; - // emitter_test.write( - // XmlEvent::start_element("class") - // .attr("name", "0") - // .attr("filename", "C:/Users/file1.txt") - // .attr("lines-valid","0") - // .attr("lines-covered","0") - // .attr("line-rate","0") - // .attr("branches-valid", "0") - // .attr("branches-covered", "0") - // .attr("branch-rate", "0") - // .attr("complexity", "0"), - // emitter_test.write(XmlEvent::start_element("lines"))?; - // emitter_test.write( - // XmlEvent::start_element("line") - // .attr("number", "10") - // .attr("hits", "0") - // .attr("branch", "false"), - // )?; - // emitter_test.write( - // XmlEvent::start_element("class") - // .attr("name", "0") - // .attr("filename", "C:/Users/file2.txt") - // .attr("lines-valid","0") - // .attr("lines-covered","0") - // .attr("line-rate","0") - // .attr("branches-valid", "0") - // .attr("branches-covered", "0") - // .attr("branch-rate", "0") - // .attr("complexity", "0"), - // emitter_test.write(XmlEvent::start_element("lines"))?; - // emitter_test.write( - // XmlEvent::start_element("line") - // .attr("number", "5") - // .attr("hits", "3") - // .attr("branch", "false"), - // )?; - // emitter_test.write(XmlEvent::end_element())?; // line - - // emitter_test.write(XmlEvent::end_element())?; // lines - // emitter_test.write(XmlEvent::end_element())?; // class - // emitter_test.write(XmlEvent::end_element())?; // classes - // emitter_test.write(XmlEvent::end_element())?; // package - // emitter_test.write(XmlEvent::end_element())?; // packages - // emitter_test.write(XmlEvent::end_element())?; // coverage - - // assert_eq!(); - // Ok(()) - // } -// } - - - - +use crate::source::SourceCoverage; +use crate::source::SourceCoverageLocation; +use crate::source::SourceFileCoverage; +use anyhow::Context; +use anyhow::Error; +use anyhow::Result; +use std::time::{SystemTime, UNIX_EPOCH}; +use xml::writer::{EmitterConfig, XmlEvent}; + +pub fn cobertura(source_coverage: SourceCoverage) -> Result { + let mut backing: Vec = Vec::new(); + let mut emitter = EmitterConfig::new() + .perform_indent(true) + .create_writer(&mut backing); + + let unixtime = SystemTime::now() + .duration_since(UNIX_EPOCH) + .context("system time before unix epoch")? + .as_secs(); + + emitter.write( + XmlEvent::start_element("coverage") + .attr("lines-valid", "0") + .attr("lines-covered", "0") + .attr("line-rate", "0") + .attr("branches-valid", "0") + .attr("branches-covered", "0") + .attr("branch-rate", "0") + .attr("timestamp", &format!("{}", unixtime)) + .attr("complexity", "0") + .attr("version", "0.1"), + )?; + emitter.write(XmlEvent::start_element("sources"))?; + emitter.write(XmlEvent::start_element("source"))?; + emitter.write(XmlEvent::characters(""))?; + emitter.write(XmlEvent::end_element())?; // source + emitter.write(XmlEvent::end_element())?; // sources + + emitter.write(XmlEvent::start_element("packages"))?; + emitter.write( + XmlEvent::start_element("package") + .attr("name", "0") + .attr("lines-valid", "0") + .attr("lines-covered", "0") + .attr("line-rate", "0") + .attr("branches-valid", "0") + .attr("branches-covered", "0") + .attr("branch-rate", "0") + .attr("complexity", "0"), + )?; + emitter.write(XmlEvent::start_element("classes"))?; + // loop through files + let files: Vec = source_coverage.files; + for file in files { + emitter.write( + XmlEvent::start_element("class") + .attr("name", "0") + .attr("filename", &file.file) + .attr("lines-valid", "0") + .attr("lines-covered", "0") + .attr("line-rate", "0") + .attr("branches-valid", "0") + .attr("branches-covered", "0") + .attr("branch-rate", "0") + .attr("complexity", "0"), + )?; + // emitter.write(XmlEvent::start_element("methods"))?; + // emitter.write( + // XmlEvent::start_element("method") + // .attr("name", "0") + // .attr("signature", "0") + // .attr("lines-valid","0") + // .attr("lines-covered","0") + // .attr("line-rate","0") + // .attr("branches-valid", "0") + // .attr("branches-covered", "0") + // .attr("branch-rate", "0") + // .attr("complexity", "0"), + // )?; + let locations: Vec = file.locations; + emitter.write(XmlEvent::start_element("lines"))?; + for location in locations { + emitter.write( + XmlEvent::start_element("line") + .attr("number", &location.line.to_string()) + .attr("hits", &location.count.to_string()) + .attr("branch", "false"), + )?; + emitter.write(XmlEvent::end_element())?; // line + } + emitter.write(XmlEvent::end_element())?; // lines + + // emitter.write(XmlEvent::end_element())?; // method + // emitter.write(XmlEvent::end_element())?; // methods + + emitter.write(XmlEvent::end_element())?; // class + } + + emitter.write(XmlEvent::end_element())?; // classes + emitter.write(XmlEvent::end_element())?; // package + + emitter.write(XmlEvent::end_element())?; // packages + emitter.write(XmlEvent::end_element())?; // coverage + + Ok(String::from_utf8(backing)?) +} + +#[cfg(test)] + +mod tests { + use super::*; + use anyhow::Result; + + #[test] + fn test_source_to_cobertura() -> Result<()> { + + let mut coverage_locations_vec1: Vec = Vec::new(); + coverage_locations_vec1.push(SourceCoverageLocation {line:5,count:3}); + coverage_locations_vec1.push(SourceCoverageLocation {line:10,count:0}); + + let mut coverage_locations_vec2: Vec = Vec::new(); + coverage_locations_vec2.push(SourceCoverageLocation {line:0,count:0}); + + let mut file_coverage_vec1: Vec = Vec::new(); + file_coverage_vec1.push(SourceFileCoverage {locations:coverage_locations_vec1,file:"C:/Users/file1.txt".to_string()}); + file_coverage_vec1.push(SourceFileCoverage {locations:coverage_locations_vec2, file:"C:/Users/file2.txt".to_string()}); + + let source_coverage_result = cobertura (SourceCoverage{files:file_coverage_vec1}); + + let mut backing_test: Vec = Vec::new(); + let mut _emitter_test = EmitterConfig::new() + .perform_indent(true) + .create_writer(&mut backing_test); + + let unixtime = SystemTime::now() + .duration_since(UNIX_EPOCH) + .context("system time before unix epoch")? + .as_secs(); + + _emitter_test.write( + XmlEvent::start_element("coverage") + .attr("lines-valid","0") + .attr("lines-covered","0") + .attr("line-rate","0") + .attr("branches-valid", "0") + .attr("branches-covered", "0") + .attr("branch-rate", "0") + .attr("timestamp", &format!("{}", unixtime)) + .attr("complexity", "0") + .attr("version", "0.1"), + )?; + _emitter_test.write(XmlEvent::start_element("sources"))?; + _emitter_test.write(XmlEvent::start_element("source"))?; + _emitter_test.write(XmlEvent::characters(""))?; + _emitter_test.write(XmlEvent::end_element())?; // source + _emitter_test.write(XmlEvent::end_element())?; // sources + + _emitter_test.write(XmlEvent::start_element("packages"))?; + + _emitter_test.write( + XmlEvent::start_element("package") + .attr("name", "0") + .attr("lines-valid","0") + .attr("lines-covered","0") + .attr("line-rate","0") + .attr("branches-valid", "0") + .attr("branches-covered", "0") + .attr("branch-rate", "0") + .attr("complexity", "0"), + )?; + _emitter_test.write(XmlEvent::start_element("classes"))?; + + _emitter_test.write( + XmlEvent::start_element("class") + .attr("name", "0") + .attr("filename", "C:/Users/file1.txt") + .attr("lines-valid","0") + .attr("lines-covered","0") + .attr("line-rate","0") + .attr("branches-valid", "0") + .attr("branches-covered", "0") + .attr("branch-rate", "0") + .attr("complexity", "0"), + )?; + _emitter_test.write(XmlEvent::start_element("lines"))?; + + _emitter_test.write( + XmlEvent::start_element("line") + .attr("number", "5") + .attr("hits", "3") + .attr("branch", "false"), + )?; + _emitter_test.write(XmlEvent::end_element())?; // line + + _emitter_test.write( + XmlEvent::start_element("line") + .attr("number", "10") + .attr("hits", "0") + .attr("branch", "false"), + )?; + _emitter_test.write(XmlEvent::end_element())?; // line + + _emitter_test.write(XmlEvent::end_element())?; // lines + + _emitter_test.write(XmlEvent::end_element())?; // class + + _emitter_test.write( + XmlEvent::start_element("class") + .attr("name", "0") + .attr("filename", "C:/Users/file2.txt") + .attr("lines-valid","0") + .attr("lines-covered","0") + .attr("line-rate","0") + .attr("branches-valid", "0") + .attr("branches-covered", "0") + .attr("branch-rate", "0") + .attr("complexity", "0"), + )?; + + _emitter_test.write(XmlEvent::start_element("lines"))?; + + _emitter_test.write( + XmlEvent::start_element("line") + .attr("number", "0") + .attr("hits", "0") + .attr("branch", "false"), + )?; + _emitter_test.write(XmlEvent::end_element())?; // line + + _emitter_test.write(XmlEvent::end_element())?; // lines + _emitter_test.write(XmlEvent::end_element())?; // class + _emitter_test.write(XmlEvent::end_element())?; // classes + _emitter_test.write(XmlEvent::end_element())?; // package + _emitter_test.write(XmlEvent::end_element())?; // packages + _emitter_test.write(XmlEvent::end_element())?; // coverage + + // let check = match source_coverage_result { + // Ok(source_coverage_result) => source_coverage_result, + // Err(err) => return Err(err), + // }; + assert_eq!(source_coverage_result.unwrap(), String::from_utf8(backing_test)?); + + Ok(()) + } +} From 1f81652e926791c1fc94d57073c57ab9a710a195 Mon Sep 17 00:00:00 2001 From: Hayley Call Date: Thu, 16 Dec 2021 16:12:44 -0800 Subject: [PATCH 03/10] Added SourceCoverage columns, associated tests and uses back in, ran rustfmt --- src/agent/coverage/src/block.rs | 2 +- src/agent/coverage/src/cobertura.rs | 58 +++--- src/agent/coverage/src/source.rs | 266 ++++++++++++++++++++++++++-- 3 files changed, 291 insertions(+), 35 deletions(-) diff --git a/src/agent/coverage/src/block.rs b/src/agent/coverage/src/block.rs index 03216eea77..ddba3ed6d9 100644 --- a/src/agent/coverage/src/block.rs +++ b/src/agent/coverage/src/block.rs @@ -155,7 +155,7 @@ impl CommandBlockCov { for (line, count) in lines { // Valid lines are always 1-indexed. if line > 0 { - let location = Location::new(line, count)?; + let location = Location::new(line, None, count)?; locations.push(location) } } diff --git a/src/agent/coverage/src/cobertura.rs b/src/agent/coverage/src/cobertura.rs index 14d405234e..fee899a3f2 100644 --- a/src/agent/coverage/src/cobertura.rs +++ b/src/agent/coverage/src/cobertura.rs @@ -113,19 +113,26 @@ mod tests { #[test] fn test_source_to_cobertura() -> Result<()> { - let mut coverage_locations_vec1: Vec = Vec::new(); - coverage_locations_vec1.push(SourceCoverageLocation {line:5,count:3}); - coverage_locations_vec1.push(SourceCoverageLocation {line:10,count:0}); + coverage_locations_vec1.push(SourceCoverageLocation { line: 5, column: None, count: 3 }); + coverage_locations_vec1.push(SourceCoverageLocation { line: 10, column: None, count: 0 }); let mut coverage_locations_vec2: Vec = Vec::new(); - coverage_locations_vec2.push(SourceCoverageLocation {line:0,count:0}); + coverage_locations_vec2.push(SourceCoverageLocation { line: 0, column: None, count: 0 }); let mut file_coverage_vec1: Vec = Vec::new(); - file_coverage_vec1.push(SourceFileCoverage {locations:coverage_locations_vec1,file:"C:/Users/file1.txt".to_string()}); - file_coverage_vec1.push(SourceFileCoverage {locations:coverage_locations_vec2, file:"C:/Users/file2.txt".to_string()}); - - let source_coverage_result = cobertura (SourceCoverage{files:file_coverage_vec1}); + file_coverage_vec1.push(SourceFileCoverage { + locations: coverage_locations_vec1, + file: "C:/Users/file1.txt".to_string(), + }); + file_coverage_vec1.push(SourceFileCoverage { + locations: coverage_locations_vec2, + file: "C:/Users/file2.txt".to_string(), + }); + + let source_coverage_result = cobertura(SourceCoverage { + files: file_coverage_vec1, + }); let mut backing_test: Vec = Vec::new(); let mut _emitter_test = EmitterConfig::new() @@ -133,15 +140,15 @@ mod tests { .create_writer(&mut backing_test); let unixtime = SystemTime::now() - .duration_since(UNIX_EPOCH) - .context("system time before unix epoch")? - .as_secs(); + .duration_since(UNIX_EPOCH) + .context("system time before unix epoch")? + .as_secs(); _emitter_test.write( XmlEvent::start_element("coverage") - .attr("lines-valid","0") - .attr("lines-covered","0") - .attr("line-rate","0") + .attr("lines-valid", "0") + .attr("lines-covered", "0") + .attr("line-rate", "0") .attr("branches-valid", "0") .attr("branches-covered", "0") .attr("branch-rate", "0") @@ -160,9 +167,9 @@ mod tests { _emitter_test.write( XmlEvent::start_element("package") .attr("name", "0") - .attr("lines-valid","0") - .attr("lines-covered","0") - .attr("line-rate","0") + .attr("lines-valid", "0") + .attr("lines-covered", "0") + .attr("line-rate", "0") .attr("branches-valid", "0") .attr("branches-covered", "0") .attr("branch-rate", "0") @@ -174,9 +181,9 @@ mod tests { XmlEvent::start_element("class") .attr("name", "0") .attr("filename", "C:/Users/file1.txt") - .attr("lines-valid","0") - .attr("lines-covered","0") - .attr("line-rate","0") + .attr("lines-valid", "0") + .attr("lines-covered", "0") + .attr("line-rate", "0") .attr("branches-valid", "0") .attr("branches-covered", "0") .attr("branch-rate", "0") @@ -208,9 +215,9 @@ mod tests { XmlEvent::start_element("class") .attr("name", "0") .attr("filename", "C:/Users/file2.txt") - .attr("lines-valid","0") - .attr("lines-covered","0") - .attr("line-rate","0") + .attr("lines-valid", "0") + .attr("lines-covered", "0") + .attr("line-rate", "0") .attr("branches-valid", "0") .attr("branches-covered", "0") .attr("branch-rate", "0") @@ -238,7 +245,10 @@ mod tests { // Ok(source_coverage_result) => source_coverage_result, // Err(err) => return Err(err), // }; - assert_eq!(source_coverage_result.unwrap(), String::from_utf8(backing_test)?); + assert_eq!( + source_coverage_result.unwrap(), + String::from_utf8(backing_test)? + ); Ok(()) } diff --git a/src/agent/coverage/src/source.rs b/src/agent/coverage/src/source.rs index 5aa8a111d6..cf2530f29d 100644 --- a/src/agent/coverage/src/source.rs +++ b/src/agent/coverage/src/source.rs @@ -23,18 +23,27 @@ pub struct SourceCoverageLocation { /// Line number of entry in `file` (1-indexed). pub line: u32, + /// Optional column offset (0-indexed). + /// + /// When column offsets are present, they should be interpreted as the start + /// of a span bounded by the next in-line column offset (or end-of-line). + pub column: Option, + /// Execution count at location. pub count: u32, } impl SourceCoverageLocation { - pub fn new(line: u32, count: u32) -> Result { + pub fn new(line: u32, column: impl Into>, count: u32) -> Result { if line == 0 { anyhow::bail!("source lines must be 1-indexed"); } + let column = column.into(); + Ok(Self { line, + column, count, }) } @@ -52,25 +61,27 @@ mod tests { #[test] fn test_source_coverage_location() -> Result<()> { - let valid = SourceCoverageLocation::new(5, 1)?; + let valid = SourceCoverageLocation::new(5, 4, 1)?; assert_eq!( valid, SourceCoverageLocation { line: 5, + column: Some(4), count: 1, } ); - let valid_no_col = SourceCoverageLocation::new(5, 1)?; + let valid_no_col = SourceCoverageLocation::new(5, None, 1)?; assert_eq!( valid_no_col, SourceCoverageLocation { line: 5, + column: None, count: 1, } ); - let invalid = SourceCoverageLocation::new(0, 1); + let invalid = SourceCoverageLocation::new(0, 4, 1); assert!(invalid.is_err()); Ok(()) @@ -82,17 +93,17 @@ mod tests { { "file": MAIN_C.to_owned(), "locations": [ - { "line": 4, "count": 1 }, - { "line": 9, "count": 0 }, - { "line": 12, "count": 1 }, + { "line": 4, "column": 4, "count": 1 }, + { "line": 9, "column": 4, "count": 0 }, + { "line": 12, "column": 4, "count": 1 }, ], }, { "file": COMMON_C.to_owned(), "locations": [ - { "line": 5, "count": 0 }, - { "line": 5, "count": 1 }, - { "line": 8, "count": 0 }, + { "line": 5, "column": 4, "count": 0 }, + { "line": 5, "column": 9, "count": 1 }, + { "line": 8, "column": 0, "count": 0 }, ], }, ]))?; @@ -104,14 +115,17 @@ mod tests { locations: vec![ SourceCoverageLocation { line: 4, + column: Some(4), count: 1, }, SourceCoverageLocation { line: 9, + column: Some(4), count: 0, }, SourceCoverageLocation { line: 12, + column: Some(4), count: 1, }, ], @@ -121,14 +135,17 @@ mod tests { locations: vec![ SourceCoverageLocation { line: 5, + column: Some(4), count: 0, }, SourceCoverageLocation { line: 5, + column: Some(9), count: 1, }, SourceCoverageLocation { line: 8, + column: Some(0), count: 0, }, ], @@ -196,4 +213,233 @@ mod tests { Ok(()) } + + #[test] + fn test_source_coverage_no_or_null_columns() -> Result<()> { + let text_null_cols = serde_json::to_string(&json!([ + { + "file": MAIN_C.to_owned(), + "locations": [ + { "line": 4, "column": null, "count": 1 }, + { "line": 9, "column": null, "count": 0 }, + { "line": 12, "column": null, "count": 1 }, + ], + }, + { + "file": COMMON_C.to_owned(), + "locations": [ + { "line": 5, "column": null, "count": 0 }, + { "line": 5, "column": null, "count": 1 }, + { "line": 8, "column": null, "count": 0 }, + ], + }, + ]))?; + + let text_no_cols = serde_json::to_string(&json!([ + { + "file": MAIN_C.to_owned(), + "locations": [ + { "line": 4, "count": 1 }, + { "line": 9, "count": 0 }, + { "line": 12, "count": 1 }, + ], + }, + { + "file": COMMON_C.to_owned(), + "locations": [ + { "line": 5, "count": 0 }, + { "line": 5, "count": 1 }, + { "line": 8, "count": 0 }, + ], + }, + ]))?; + + let coverage = { + let files = vec![ + SourceFileCoverage { + file: MAIN_C.to_owned(), + locations: vec![ + SourceCoverageLocation { + line: 4, + column: None, + count: 1, + }, + SourceCoverageLocation { + line: 9, + column: None, + count: 0, + }, + SourceCoverageLocation { + line: 12, + column: None, + count: 1, + }, + ], + }, + SourceFileCoverage { + file: COMMON_C.to_owned(), + locations: vec![ + SourceCoverageLocation { + line: 5, + column: None, + count: 0, + }, + SourceCoverageLocation { + line: 5, + column: None, + count: 1, + }, + SourceCoverageLocation { + line: 8, + column: None, + count: 0, + }, + ], + }, + ]; + SourceCoverage { files } + }; + + // Serialized with present `column` keys, `null` values. + let ser = serde_json::to_string(&coverage)?; + assert_eq!(ser, text_null_cols); + + // Deserializes when `column` keys are absent. + let de_no_cols: SourceCoverage = serde_json::from_str(&text_no_cols)?; + assert_eq!(de_no_cols, coverage); + + // Deserializes when `column` keys are present but `null`. + let de_null_cols: SourceCoverage = serde_json::from_str(&text_null_cols)?; + assert_eq!(de_null_cols, coverage); + + Ok(()) + } + + #[test] + fn test_source_coverage_partial_columns() -> Result<()> { + let text = serde_json::to_string(&json!([ + { + "file": MAIN_C.to_owned(), + "locations": [ + { "line": 4, "column": 4, "count": 1 }, + { "line": 9, "column": 4, "count": 0 }, + { "line": 12, "column": 4, "count": 1 }, + ], + }, + { + "file": COMMON_C.to_owned(), + "locations": [ + { "line": 5, "column": null, "count": 0 }, + { "line": 5, "column": null, "count": 1 }, + { "line": 8, "column": null, "count": 0 }, + ], + }, + ]))?; + + let coverage = { + let files = vec![ + SourceFileCoverage { + file: MAIN_C.to_owned(), + locations: vec![ + SourceCoverageLocation { + line: 4, + column: Some(4), + count: 1, + }, + SourceCoverageLocation { + line: 9, + column: Some(4), + count: 0, + }, + SourceCoverageLocation { + line: 12, + column: Some(4), + count: 1, + }, + ], + }, + SourceFileCoverage { + file: COMMON_C.to_owned(), + locations: vec![ + SourceCoverageLocation { + line: 5, + column: None, + count: 0, + }, + SourceCoverageLocation { + line: 5, + column: None, + count: 1, + }, + SourceCoverageLocation { + line: 8, + column: None, + count: 0, + }, + ], + }, + ]; + SourceCoverage { files } + }; + + let ser = serde_json::to_string(&coverage)?; + assert_eq!(ser, text); + + let de: SourceCoverage = serde_json::from_str(&text)?; + assert_eq!(de, coverage); + + Ok(()) + } + + #[test] + fn test_source_coverage_mixed_columns() -> Result<()> { + let text = serde_json::to_string(&json!([ + { + "file": MAIN_C.to_owned(), + "locations": [ + { "line": 4, "column": null, "count": 1 }, + { "line": 9, "column": 4, "count": 0 }, + { "line": 12, "column": null, "count": 1 }, + { "line": 13, "column": 7, "count": 0 }, + ], + }, + ]))?; + + let coverage = { + let files = vec![SourceFileCoverage { + file: MAIN_C.to_owned(), + locations: vec![ + SourceCoverageLocation { + line: 4, + column: None, + count: 1, + }, + SourceCoverageLocation { + line: 9, + column: Some(4), + count: 0, + }, + SourceCoverageLocation { + line: 12, + column: None, + count: 1, + }, + SourceCoverageLocation { + line: 13, + column: Some(7), + count: 0, + }, + ], + }]; + SourceCoverage { files } + }; + + let ser = serde_json::to_string(&coverage)?; + assert_eq!(ser, text); + + let de: SourceCoverage = serde_json::from_str(&text)?; + assert_eq!(de, coverage); + + Ok(()) + } } From 220c8cac2f4c016e02aab34a5d3b518539221482 Mon Sep 17 00:00:00 2001 From: Hayley Call Date: Thu, 16 Dec 2021 16:33:39 -0800 Subject: [PATCH 04/10] Improved result resolution in test, removed some commented out code --- src/agent/coverage/src/cobertura.rs | 30 +++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/agent/coverage/src/cobertura.rs b/src/agent/coverage/src/cobertura.rs index fee899a3f2..2f28c5961b 100644 --- a/src/agent/coverage/src/cobertura.rs +++ b/src/agent/coverage/src/cobertura.rs @@ -90,9 +90,6 @@ pub fn cobertura(source_coverage: SourceCoverage) -> Result { } emitter.write(XmlEvent::end_element())?; // lines - // emitter.write(XmlEvent::end_element())?; // method - // emitter.write(XmlEvent::end_element())?; // methods - emitter.write(XmlEvent::end_element())?; // class } @@ -114,11 +111,23 @@ mod tests { #[test] fn test_source_to_cobertura() -> Result<()> { let mut coverage_locations_vec1: Vec = Vec::new(); - coverage_locations_vec1.push(SourceCoverageLocation { line: 5, column: None, count: 3 }); - coverage_locations_vec1.push(SourceCoverageLocation { line: 10, column: None, count: 0 }); + coverage_locations_vec1.push(SourceCoverageLocation { + line: 5, + column: None, + count: 3, + }); + coverage_locations_vec1.push(SourceCoverageLocation { + line: 10, + column: None, + count: 0, + }); let mut coverage_locations_vec2: Vec = Vec::new(); - coverage_locations_vec2.push(SourceCoverageLocation { line: 0, column: None, count: 0 }); + coverage_locations_vec2.push(SourceCoverageLocation { + line: 0, + column: None, + count: 0, + }); let mut file_coverage_vec1: Vec = Vec::new(); file_coverage_vec1.push(SourceFileCoverage { @@ -241,14 +250,7 @@ mod tests { _emitter_test.write(XmlEvent::end_element())?; // packages _emitter_test.write(XmlEvent::end_element())?; // coverage - // let check = match source_coverage_result { - // Ok(source_coverage_result) => source_coverage_result, - // Err(err) => return Err(err), - // }; - assert_eq!( - source_coverage_result.unwrap(), - String::from_utf8(backing_test)? - ); + assert_eq!(source_coverage_result?, String::from_utf8(backing_test)?); Ok(()) } From e56aa0ebbe36da031c3337c47bd577cdea67cdf8 Mon Sep 17 00:00:00 2001 From: Hayley Call Date: Thu, 6 Jan 2022 13:20:42 -0800 Subject: [PATCH 05/10] Removed unrequired elements and attributes according to cobertura/reportgenerator schema --- src/agent/coverage/src/cobertura.rs | 67 +++-------------------------- 1 file changed, 5 insertions(+), 62 deletions(-) diff --git a/src/agent/coverage/src/cobertura.rs b/src/agent/coverage/src/cobertura.rs index 2f28c5961b..3dfe02dde2 100644 --- a/src/agent/coverage/src/cobertura.rs +++ b/src/agent/coverage/src/cobertura.rs @@ -20,34 +20,16 @@ pub fn cobertura(source_coverage: SourceCoverage) -> Result { emitter.write( XmlEvent::start_element("coverage") - .attr("lines-valid", "0") - .attr("lines-covered", "0") .attr("line-rate", "0") - .attr("branches-valid", "0") - .attr("branches-covered", "0") .attr("branch-rate", "0") - .attr("timestamp", &format!("{}", unixtime)) + .attr("lines-covered", "0") + .attr("lines-valid", "0") + .attr("branches-covered", "0") .attr("complexity", "0") .attr("version", "0.1"), + .attr("timestamp", &format!("{}", unixtime)) )?; - emitter.write(XmlEvent::start_element("sources"))?; - emitter.write(XmlEvent::start_element("source"))?; - emitter.write(XmlEvent::characters(""))?; - emitter.write(XmlEvent::end_element())?; // source - emitter.write(XmlEvent::end_element())?; // sources - emitter.write(XmlEvent::start_element("packages"))?; - emitter.write( - XmlEvent::start_element("package") - .attr("name", "0") - .attr("lines-valid", "0") - .attr("lines-covered", "0") - .attr("line-rate", "0") - .attr("branches-valid", "0") - .attr("branches-covered", "0") - .attr("branch-rate", "0") - .attr("complexity", "0"), - )?; emitter.write(XmlEvent::start_element("classes"))?; // loop through files let files: Vec = source_coverage.files; @@ -56,27 +38,11 @@ pub fn cobertura(source_coverage: SourceCoverage) -> Result { XmlEvent::start_element("class") .attr("name", "0") .attr("filename", &file.file) - .attr("lines-valid", "0") - .attr("lines-covered", "0") .attr("line-rate", "0") - .attr("branches-valid", "0") - .attr("branches-covered", "0") .attr("branch-rate", "0") .attr("complexity", "0"), )?; - // emitter.write(XmlEvent::start_element("methods"))?; - // emitter.write( - // XmlEvent::start_element("method") - // .attr("name", "0") - // .attr("signature", "0") - // .attr("lines-valid","0") - // .attr("lines-covered","0") - // .attr("line-rate","0") - // .attr("branches-valid", "0") - // .attr("branches-covered", "0") - // .attr("branch-rate", "0") - // .attr("complexity", "0"), - // )?; + let locations: Vec = file.locations; emitter.write(XmlEvent::start_element("lines"))?; for location in locations { @@ -94,9 +60,6 @@ pub fn cobertura(source_coverage: SourceCoverage) -> Result { } emitter.write(XmlEvent::end_element())?; // classes - emitter.write(XmlEvent::end_element())?; // package - - emitter.write(XmlEvent::end_element())?; // packages emitter.write(XmlEvent::end_element())?; // coverage Ok(String::from_utf8(backing)?) @@ -165,25 +128,7 @@ mod tests { .attr("complexity", "0") .attr("version", "0.1"), )?; - _emitter_test.write(XmlEvent::start_element("sources"))?; - _emitter_test.write(XmlEvent::start_element("source"))?; - _emitter_test.write(XmlEvent::characters(""))?; - _emitter_test.write(XmlEvent::end_element())?; // source - _emitter_test.write(XmlEvent::end_element())?; // sources - - _emitter_test.write(XmlEvent::start_element("packages"))?; - _emitter_test.write( - XmlEvent::start_element("package") - .attr("name", "0") - .attr("lines-valid", "0") - .attr("lines-covered", "0") - .attr("line-rate", "0") - .attr("branches-valid", "0") - .attr("branches-covered", "0") - .attr("branch-rate", "0") - .attr("complexity", "0"), - )?; _emitter_test.write(XmlEvent::start_element("classes"))?; _emitter_test.write( @@ -246,8 +191,6 @@ mod tests { _emitter_test.write(XmlEvent::end_element())?; // lines _emitter_test.write(XmlEvent::end_element())?; // class _emitter_test.write(XmlEvent::end_element())?; // classes - _emitter_test.write(XmlEvent::end_element())?; // package - _emitter_test.write(XmlEvent::end_element())?; // packages _emitter_test.write(XmlEvent::end_element())?; // coverage assert_eq!(source_coverage_result?, String::from_utf8(backing_test)?); From 434ba3035b836377ec9b43097f3c94c412a154e5 Mon Sep 17 00:00:00 2001 From: Hayley Call Date: Fri, 7 Jan 2022 10:22:50 -0800 Subject: [PATCH 06/10] Updated test, fixed missed error --- src/agent/coverage/src/cobertura.rs | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/agent/coverage/src/cobertura.rs b/src/agent/coverage/src/cobertura.rs index 3dfe02dde2..15769d2b5c 100644 --- a/src/agent/coverage/src/cobertura.rs +++ b/src/agent/coverage/src/cobertura.rs @@ -26,8 +26,8 @@ pub fn cobertura(source_coverage: SourceCoverage) -> Result { .attr("lines-valid", "0") .attr("branches-covered", "0") .attr("complexity", "0") - .attr("version", "0.1"), - .attr("timestamp", &format!("{}", unixtime)) + .attr("version", "0.1") + .attr("timestamp", &format!("{}", unixtime)), )?; emitter.write(XmlEvent::start_element("classes"))?; @@ -118,15 +118,14 @@ mod tests { _emitter_test.write( XmlEvent::start_element("coverage") - .attr("lines-valid", "0") - .attr("lines-covered", "0") .attr("line-rate", "0") - .attr("branches-valid", "0") - .attr("branches-covered", "0") .attr("branch-rate", "0") - .attr("timestamp", &format!("{}", unixtime)) + .attr("lines-covered", "0") + .attr("lines-valid", "0") + .attr("branches-covered", "0") .attr("complexity", "0") - .attr("version", "0.1"), + .attr("version", "0.1") + .attr("timestamp", &format!("{}", unixtime)), )?; _emitter_test.write(XmlEvent::start_element("classes"))?; @@ -135,11 +134,7 @@ mod tests { XmlEvent::start_element("class") .attr("name", "0") .attr("filename", "C:/Users/file1.txt") - .attr("lines-valid", "0") - .attr("lines-covered", "0") .attr("line-rate", "0") - .attr("branches-valid", "0") - .attr("branches-covered", "0") .attr("branch-rate", "0") .attr("complexity", "0"), )?; @@ -169,11 +164,7 @@ mod tests { XmlEvent::start_element("class") .attr("name", "0") .attr("filename", "C:/Users/file2.txt") - .attr("lines-valid", "0") - .attr("lines-covered", "0") .attr("line-rate", "0") - .attr("branches-valid", "0") - .attr("branches-covered", "0") .attr("branch-rate", "0") .attr("complexity", "0"), )?; From b4c847f6d3aecab69f082404f09f415cfdddae62 Mon Sep 17 00:00:00 2001 From: Hayley Call Date: Fri, 7 Jan 2022 11:59:39 -0800 Subject: [PATCH 07/10] Improved XML to work best with ReportGenerator format --- src/agent/coverage/src/cobertura.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/agent/coverage/src/cobertura.rs b/src/agent/coverage/src/cobertura.rs index 15769d2b5c..27a6cd0f22 100644 --- a/src/agent/coverage/src/cobertura.rs +++ b/src/agent/coverage/src/cobertura.rs @@ -25,11 +25,21 @@ pub fn cobertura(source_coverage: SourceCoverage) -> Result { .attr("lines-covered", "0") .attr("lines-valid", "0") .attr("branches-covered", "0") + .attr("branches-valid", "0") .attr("complexity", "0") .attr("version", "0.1") .attr("timestamp", &format!("{}", unixtime)), )?; + emitter.write(XmlEvent::start_element("packages"))?; + emitter.write( + XmlEvent::start_element("package") + .attr("name", "0") + .attr("line-rate", "0") + .attr("branch-rate", "0") + .attr("complexity", "0"), + )?; + emitter.write(XmlEvent::start_element("classes"))?; // loop through files let files: Vec = source_coverage.files; @@ -60,6 +70,8 @@ pub fn cobertura(source_coverage: SourceCoverage) -> Result { } emitter.write(XmlEvent::end_element())?; // classes + emitter.write(XmlEvent::end_element())?; // package + emitter.write(XmlEvent::end_element())?; // packages emitter.write(XmlEvent::end_element())?; // coverage Ok(String::from_utf8(backing)?) @@ -123,11 +135,21 @@ mod tests { .attr("lines-covered", "0") .attr("lines-valid", "0") .attr("branches-covered", "0") + .attr("branches-valid", "0") .attr("complexity", "0") .attr("version", "0.1") .attr("timestamp", &format!("{}", unixtime)), )?; + _emitter_test.write(XmlEvent::start_element("packages"))?; + _emitter_test.write( + XmlEvent::start_element("package") + .attr("name", "0") + .attr("line-rate", "0") + .attr("branch-rate", "0") + .attr("complexity", "0"), + )?; + _emitter_test.write(XmlEvent::start_element("classes"))?; _emitter_test.write( @@ -182,6 +204,8 @@ mod tests { _emitter_test.write(XmlEvent::end_element())?; // lines _emitter_test.write(XmlEvent::end_element())?; // class _emitter_test.write(XmlEvent::end_element())?; // classes + _emitter_test.write(XmlEvent::end_element())?; // package + _emitter_test.write(XmlEvent::end_element())?; // packages _emitter_test.write(XmlEvent::end_element())?; // coverage assert_eq!(source_coverage_result?, String::from_utf8(backing_test)?); From 598407d8cf5b77156071858b13e191e78c1b0186 Mon Sep 17 00:00:00 2001 From: Hayley Call Date: Fri, 7 Jan 2022 12:04:45 -0800 Subject: [PATCH 08/10] Format change --- src/agent/coverage/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agent/coverage/src/lib.rs b/src/agent/coverage/src/lib.rs index a94a98b53d..d87e5c42c1 100644 --- a/src/agent/coverage/src/lib.rs +++ b/src/agent/coverage/src/lib.rs @@ -17,13 +17,13 @@ pub mod elf; pub mod block; pub mod cache; +pub mod cobertura; pub mod code; pub mod debuginfo; pub mod demangle; pub mod report; pub mod sancov; pub mod source; -pub mod cobertura; #[cfg(target_os = "linux")] pub mod disasm; From d1cf6b914da13ac64b422ed4ddb4a893e4ba904d Mon Sep 17 00:00:00 2001 From: Hayley Call Date: Fri, 7 Jan 2022 12:22:59 -0800 Subject: [PATCH 09/10] Order matter, don't format --- src/agent/coverage/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agent/coverage/src/lib.rs b/src/agent/coverage/src/lib.rs index d87e5c42c1..a94a98b53d 100644 --- a/src/agent/coverage/src/lib.rs +++ b/src/agent/coverage/src/lib.rs @@ -17,13 +17,13 @@ pub mod elf; pub mod block; pub mod cache; -pub mod cobertura; pub mod code; pub mod debuginfo; pub mod demangle; pub mod report; pub mod sancov; pub mod source; +pub mod cobertura; #[cfg(target_os = "linux")] pub mod disasm; From b2defd551dc263e55542624123758fe7a2a1eaf1 Mon Sep 17 00:00:00 2001 From: Hayley Call Date: Fri, 7 Jan 2022 12:27:44 -0800 Subject: [PATCH 10/10] Undid order, included xml in toml --- src/agent/coverage/Cargo.toml | 1 + src/agent/coverage/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/agent/coverage/Cargo.toml b/src/agent/coverage/Cargo.toml index 1717d5bd87..f04301d1d1 100644 --- a/src/agent/coverage/Cargo.toml +++ b/src/agent/coverage/Cargo.toml @@ -27,6 +27,7 @@ serde = { version = "1.0", features = ["derive"] } symbolic = { version = "8.5", features = ["debuginfo", "demangle", "symcache"] } uuid = { version = "0.8", features = ["guid"] } win-util = { path = "../win-util" } +xml-rs = "0.8" [target.'cfg(target_os = "windows")'.dependencies] pdb = "0.7" diff --git a/src/agent/coverage/src/lib.rs b/src/agent/coverage/src/lib.rs index a94a98b53d..d87e5c42c1 100644 --- a/src/agent/coverage/src/lib.rs +++ b/src/agent/coverage/src/lib.rs @@ -17,13 +17,13 @@ pub mod elf; pub mod block; pub mod cache; +pub mod cobertura; pub mod code; pub mod debuginfo; pub mod demangle; pub mod report; pub mod sancov; pub mod source; -pub mod cobertura; #[cfg(target_os = "linux")] pub mod disasm;