#![warn(missing_docs)]
use nargo_types::{Result, Span};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CoverageMetadata {
pub file_path: String,
pub statement_map: HashMap<usize, Span>,
pub branch_map: HashMap<usize, BranchMetadata>,
pub function_map: HashMap<usize, FunctionMetadata>,
pub s: HashMap<usize, u32>,
pub b: HashMap<usize, Vec<u32>>,
pub f: HashMap<usize, u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BranchMetadata {
pub line: usize,
pub r#type: String,
pub locations: Vec<Span>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionMetadata {
pub name: String,
pub line: usize,
pub span: Span,
}
pub struct CoverageInstrumenter {
file_id: String,
next_stmt_id: usize,
metadata: CoverageMetadata,
}
impl CoverageInstrumenter {
pub fn new(file_id: String, file_path: String) -> Self {
Self { file_id, next_stmt_id: 0, metadata: CoverageMetadata { file_path, ..Default::default() } }
}
pub fn instrument(&mut self, _module: &mut ()) -> Result<CoverageMetadata> {
Ok(self.metadata.clone())
}
fn instrument_program(&mut self, _program: &mut ()) {
}
fn instrument_stmt(&mut self, _stmt: ()) -> () {
}
fn instrument_expr(&mut self, _expr: &mut ()) {
}
fn instrument_template(&mut self, _template: &mut ()) {
}
fn instrument_template_node(&mut self, _node: &mut ()) {
}
fn next_id(&mut self) -> usize {
let id = self.next_stmt_id;
self.next_stmt_id += 1;
id
}
fn create_counter_stmt(&self, _id: usize) -> () {
}
fn create_branch_counter_expr(&self, _bid: usize, _index: usize) -> () {
}
fn create_function_counter_stmt(&self, _fid: usize) -> () {
}
}
pub struct LcovReporter;
impl LcovReporter {
pub fn report(metadatas: &[CoverageMetadata]) -> String {
let mut out = String::new();
for meta in metadatas {
out.push_str(&format!("TN:\nSF:{}\n", meta.file_path));
for (id, func) in &meta.function_map {
let hits = meta.f.get(id).cloned().unwrap_or(0);
out.push_str(&format!("FN:{},{}\n", func.line, func.name));
out.push_str(&format!("FNDA:{},{}\n", hits, func.name));
}
out.push_str(&format!("FNF:{}\n", meta.function_map.len()));
out.push_str(&format!("FNH:{}\n", meta.f.values().filter(|&&h| h > 0).count()));
let mut line_hits: HashMap<u32, u32> = HashMap::new();
for (id, span) in &meta.statement_map {
let hits = meta.s.get(id).cloned().unwrap_or(0);
let line = span.start.line;
let entry = line_hits.entry(line).or_insert(0);
*entry = (*entry).max(hits);
}
let mut lines: Vec<_> = line_hits.keys().collect();
lines.sort();
for &line in lines {
let hits = line_hits[&line];
out.push_str(&format!("DA:{},{}\n", line, hits));
}
out.push_str(&format!("LF:{}\n", line_hits.len()));
out.push_str(&format!("LH:{}\n", line_hits.values().filter(|&&h| h > 0).count()));
let mut branch_count = 0;
let mut branch_hit = 0;
for (bid, branch) in &meta.branch_map {
let hits = meta.b.get(bid);
for (idx, &h) in hits.map(|v| v.as_slice()).unwrap_or(&[]).iter().enumerate() {
out.push_str(&format!("BRDA:{},0,{},{}\n", branch.line, idx, if h > 0 { h.to_string() } else { "-".to_string() }));
branch_count += 1;
if h > 0 {
branch_hit += 1;
}
}
}
out.push_str(&format!("BRF:{}\n", branch_count));
out.push_str(&format!("BRH:{}\n", branch_hit));
out.push_str("end_of_record\n");
}
out
}
}